lighting calc checks chunks to see if they really need another pass, and splits up work into batches to avoid memory errors

This commit is contained in:
David Vierra 2010-09-18 01:52:11 -10:00
parent 5908c3ed62
commit e888709165

View File

@ -1712,21 +1712,84 @@ class MCInfdevOldLevel(MCLevel):
print "Saved {0} chunks".format(dirtyChunkCount); print "Saved {0} chunks".format(dirtyChunkCount);
def generateLights(self, dirtyChunks = None): def generateLights(self, dirtyChunks = None):
""" dirtyChunks may be an iterable yielding (xPos,zPos) tuples """ """ dirtyChunks may be an iterable yielding (xPos,zPos) tuples
if none, generate lights for all chunks that need lighting
"""
activeBlocks = set(); activeBlocks = set();
#doneBlocks = set() #doneBlocks = set()
la = array(self.materials.lightAbsorption) la = array(self.materials.lightAbsorption)
startTime = datetime.now(); startTime = datetime.now();
print "Initializing lights..."
if dirtyChunks is None: if dirtyChunks is None:
dirtyChunks = filter(lambda x: x.needsLighting, self._presentChunks.values()); dirtyChunks = filter(lambda x: x.needsLighting, self._presentChunks.values());
else: else:
dirtyChunks = [self._presentChunks[c] for c in dirtyChunks if c in self._presentChunks]; dirtyChunks = [self._presentChunks[c] for c in dirtyChunks if c in self._presentChunks];
dirtyChunks = sorted(list(dirtyChunks), key=lambda x:x.chunkPosition)
#at 150k per loaded chunk,
maxLightingChunks = 4000
print "Asked to light {0} chunks".format(len(dirtyChunks))
chunkLists = [dirtyChunks];
def reverseChunkPosition(x):
cx,cz = x.chunkPosition;
return cz,cx
def splitChunkLists(chunkLists):
newChunkLists = []
for l in chunkLists:
#list is already sorted on x position, so this splits into left and right
smallX = l[:len(l)/2]
bigX = l[len(l)/2:]
#sort halves on z position
smallX = sorted(smallX, key=reverseChunkPosition)
bigX = sorted(bigX, key=reverseChunkPosition)
#add quarters to list
newChunkLists.append(smallX[:len(smallX)/2])
newChunkLists.append(smallX[len(smallX)/2:])
newChunkLists.append(bigX[:len(bigX)/2])
newChunkLists.append(bigX[len(bigX)/2:])
return newChunkLists
while len(chunkLists[0]) > maxLightingChunks:
chunkLists = splitChunkLists(chunkLists);
if len(chunkLists) > 1:
print "Using {0} batches to conserve memory.".format(len(chunkLists))
i=0
for dc in chunkLists:
i+=1;
print "Batch {0}/{1}".format(i, len(chunkLists))
dc = sorted(dc, key=lambda x:x.chunkPosition)
self._generateLights(dc)
for ch in dc:
ch.compress();
timeDelta = datetime.now()-startTime;
print "Completed in {0}, {1} per chunk".format(timeDelta, dirtyChunks and timeDelta/len(dirtyChunks) or 0)
return;
def _generateLights(self, dirtyChunks):
conserveMemory = False
#[d.genFastLights() for d in dirtyChunks] #[d.genFastLights() for d in dirtyChunks]
dirtyChunks = set(dirtyChunks) dirtyChunks = set(dirtyChunks)
for ch in list(dirtyChunks): #grab all adjoining chunks and relight them, too! for ch in list(dirtyChunks):
#relight all blocks in neighboring chunks in case their light source disappeared.
cx,cz = ch.chunkPosition cx,cz = ch.chunkPosition
for dx,dz in itertools.product( (-1, 0, 1), (-1, 0, 1) ): for dx,dz in itertools.product( (-1, 0, 1), (-1, 0, 1) ):
if (cx+dx,cz+dz) in self._presentChunks: if (cx+dx,cz+dz) in self._presentChunks:
@ -1739,11 +1802,14 @@ class MCInfdevOldLevel(MCLevel):
chunk.load(); chunk.load();
chunk.chunkChanged(); chunk.chunkChanged();
#print chunk; #print chunk;
assert chunk.dirty and chunk.needsCompression and chunk.needsLighting assert chunk.dirty and chunk.needsLighting
#chunk.SkyLight[:] = 0 #chunk.SkyLight[:] = 0
#chunk.BlockLight[:] = 0 #chunk.BlockLight[:] = 0
chunk.BlockLight[:] = self.materials.lightEmission[chunk.Blocks]; chunk.BlockLight[:] = self.materials.lightEmission[chunk.Blocks];
if conserveMemory:
chunk.compress();
zeroChunk = ZeroChunk(128) zeroChunk = ZeroChunk(128)
@ -1751,13 +1817,21 @@ class MCInfdevOldLevel(MCLevel):
#for chunk in dirtyChunks: #for chunk in dirtyChunks:
la[18] = 0; #for normal light dispersal, leaves absorb the same as empty air. la[18] = 0; #for normal light dispersal, leaves absorb the same as empty air.
startingDirtyChunks = dirtyChunks
oldLeftEdge = zeros( (1, 16, 128), 'uint8');
oldBottomEdge = zeros( (16, 1, 128), 'uint8');
oldChunk = zeros( (16, 16, 128), 'uint8');
print "Dispersing light..." print "Dispersing light..."
for light in ("BlockLight", "SkyLight"): for light in ("BlockLight", "SkyLight"):
print light; print light;
zerochunkLight = getattr(zeroChunk, light); zerochunkLight = getattr(zeroChunk, light);
newDirtyChunks = list(startingDirtyChunks);
for i in range(14): for i in range(14):
print "Pass ", i print "Pass ", i, ":", len(newDirtyChunks), "chunks"
""" """
propagate light! propagate light!
for each of the six cardinal directions, figure a new light value for for each of the six cardinal directions, figure a new light value for
@ -1767,6 +1841,11 @@ class MCInfdevOldLevel(MCLevel):
we calculate all chunks one step before moving to the next step, to ensure all gaps at chunk edges are filled. we calculate all chunks one step before moving to the next step, to ensure all gaps at chunk edges are filled.
we do an extra cycle because lights sent across edges may lag by one cycle. we do an extra cycle because lights sent across edges may lag by one cycle.
""" """
dirtyChunks = sorted(set(newDirtyChunks), key=lambda x:x.chunkPosition)
newDirtyChunks = list();
for chunk in dirtyChunks: for chunk in dirtyChunks:
#xxx code duplication #xxx code duplication
(cx,cz) = chunk.chunkPosition (cx,cz) = chunk.chunkPosition
@ -1781,11 +1860,15 @@ class MCInfdevOldLevel(MCLevel):
neighboringChunks[dir] = self.getChunk(cx+dx,cz+dz) neighboringChunks[dir] = self.getChunk(cx+dx,cz+dz)
assert neighboringChunks[dir].root_tag != None assert neighboringChunks[dir].root_tag != None
chunkLa = la[chunk.Blocks]+1; chunkLa = la[chunk.Blocks]+1;
chunkLight = getattr(chunk,light); chunkLight = getattr(chunk,light);
oldChunk[:] = chunkLight[:]
nc = neighboringChunks[FaceXDecreasing] nc = neighboringChunks[FaceXDecreasing]
ncLight = getattr(nc,light); ncLight = getattr(nc,light);
oldLeftEdge[:] = ncLight[15:16,:,0:128] #save the old left edge
#left edge #left edge
newlight = (chunkLight[0:1,:,:128]-la[nc.Blocks[15:16,:,0:128]])-1 newlight = (chunkLight[0:1,:,:128]-la[nc.Blocks[15:16,:,0:128]])-1
@ -1808,7 +1891,6 @@ class MCInfdevOldLevel(MCLevel):
chunkLight[15:16,:,0:128] = maximum(chunkLight[15:16,:,0:128], newlight) chunkLight[15:16,:,0:128] = maximum(chunkLight[15:16,:,0:128], newlight)
#right edge #right edge
nc = neighboringChunks[FaceXIncreasing] nc = neighboringChunks[FaceXIncreasing]
@ -1836,10 +1918,15 @@ class MCInfdevOldLevel(MCLevel):
zerochunkLight[:] = 0; zerochunkLight[:] = 0;
#check if the left edge changed and dirty or compress the chunk appropriately
if (oldLeftEdge != ncLight[15:16,:,:128]).any():
#chunk is dirty
newDirtyChunks.append(nc)
#bottom edge #bottom edge
nc = neighboringChunks[FaceZDecreasing] nc = neighboringChunks[FaceZDecreasing]
ncLight = getattr(nc,light); ncLight = getattr(nc,light);
oldBottomEdge[:] = ncLight[:,15:16,:128] # save the old bottom edge
newlight = (chunkLight[:,0:1,:128]-la[nc.Blocks[:,15:16,:128]])-1 newlight = (chunkLight[:,0:1,:128]-la[nc.Blocks[:,15:16,:128]])-1
newlight[newlight>15]=0; newlight[newlight>15]=0;
@ -1889,7 +1976,9 @@ class MCInfdevOldLevel(MCLevel):
zerochunkLight[:] = 0; zerochunkLight[:] = 0;
if (oldBottomEdge != ncLight[:,15:16,:128]).any():
newDirtyChunks.append(nc)
newlight = (chunkLight[:,:,0:127]-chunkLa[:,:,1:128]) newlight = (chunkLight[:,:,0:127]-chunkLa[:,:,1:128])
newlight[newlight>15]=0; newlight[newlight>15]=0;
chunkLight[:,:,1:128] = maximum(chunkLight[:,:,1:128], newlight) chunkLight[:,:,1:128] = maximum(chunkLight[:,:,1:128], newlight)
@ -1899,14 +1988,12 @@ class MCInfdevOldLevel(MCLevel):
chunkLight[:,:,0:127] = maximum(chunkLight[:,:,0:127], newlight) chunkLight[:,:,0:127] = maximum(chunkLight[:,:,0:127], newlight)
zerochunkLight[:] = 0; zerochunkLight[:] = 0;
if (oldChunk != chunkLight).any():
timeDelta = datetime.now()-startTime; newDirtyChunks.append(chunk);
print "Completed in {0}, {1} per chunk".format(timeDelta, dirtyChunks and timeDelta/len(dirtyChunks) or 0) for ch in startingDirtyChunks:
for ch in dirtyChunks:
ch.needsLighting = False; ch.needsLighting = False;
return;
""" """
#fill all sky-facing blocks with full light #fill all sky-facing blocks with full light
for x,z in itertools.product(range(16), for x,z in itertools.product(range(16),
@ -2075,7 +2162,9 @@ class MCInfdevOldLevel(MCLevel):
else: else:
blocks[:] = blockType blocks[:] = blockType
chunk.Data[slices] = blockData chunk.Data[slices] = blockData
chunk.chunkChanged(needsLighting); chunk.chunkChanged(needsLighting);
chunk.compress();
def createChunksInRange(self, box): def createChunksInRange(self, box):
@ -2188,6 +2277,7 @@ class MCInfdevOldLevel(MCLevel):
data[:] = (sourceData[:,:,:] & 0xf) data[:] = (sourceData[:,:,:] & 0xf)
chunk.chunkChanged(); chunk.chunkChanged();
#chunk.compress(); #xxx find out why this trashes changes to tile entities
def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, copyAir = True, copyWater = True): def copyBlocksFrom(self, sourceLevel, sourceBox, destinationPoint, copyAir = True, copyWater = True):
(x,y,z) = destinationPoint; (x,y,z) = destinationPoint;