def loadSections(self): selection = self.renderSelection if selection is None: self.loadTimer.setInterval(333) return else: self.loadTimer.setInterval(0) for cx, cz in selection.chunkPositions(): if self.groupNode.containsChunkNode((cx, cz)): continue vertexArrays = [] for cy in selection.sectionPositions(cx, cz): box = SectionBox(cx, cy, cz).expand(1) mask = selection.box_mask(box) if mask is not None: vertexArrays.extend(self.buildSection(mask, cy)) if len(vertexArrays): chunkNode = ChunkNode((cx, cz)) vertexNode = VertexNode(vertexArrays) chunkNode.addChild(vertexNode) self.groupNode.addChunkNode(chunkNode) yield self.loadTimer.setInterval(333)
def advanceToChunk(ray, dimension, maxDistance): point, vector = ray inBounds = point in dimension.bounds for pos, face in _cast(point, vector, maxDistance, 16): x, y, z = pos x >>= 4 y >>= 4 z >>= 4 if inBounds and pos not in dimension.bounds: raise RayBoundsError("Ray exited dimension bounds.") inBounds = pos in dimension.bounds if dimension.containsChunk(x, z): chunk = dimension.getChunk(x, z) section = chunk.getSection(y) if section is not None: # if (section.Blocks == 0).all(): # log.warn("Empty section found!!") # continue box = SectionBox(x, y, z) if point in box: return point ixs = rayIntersectsBox(box, ray) if ixs: hitPoint = ixs[0][0] return hitPoint raise RayBoundsError("Ray exited dimension bounds.")
def __iter__(self): cx, cz = self.chunkUpdate.chunk.chunkPosition sectionBounds = SectionBox(cx, self.y, cz) bounds = self.chunkUpdate.chunkInfo.worldScene.bounds if bounds: sectionBounds = sectionBounds.intersect(bounds) modelMesh = BlockModelMesh(self) with profiler.context("BlockModelMesh"): modelMesh.createVertexArrays() self.blockMeshes.append(modelMesh) yield
def box_mask(self, box): """ :param box: :type box: BoundingBox :return: :rtype: """ #if self.volume > 40: # import pdb; pdb.set_trace() mask = numpy.zeros(shape=box.size, dtype=bool) for cx, cz in box.chunkPositions(): try: chunk = self.dimension.getChunk(cx, cz) except ChunkNotPresent: continue for cy in box.sectionPositions(cx, cz): section = chunk.getSection(cy) if section is None: continue sectionBox = box.intersect(SectionBox(cx, cy, cz)) if sectionBox.volume == 0: continue slices = numpy.s_[sectionBox.miny & 0xf:(sectionBox.miny & 0xf) + sectionBox.height, sectionBox.minz & 0xf:(sectionBox.minz & 0xf) + sectionBox.length, sectionBox.minx & 0xf:(sectionBox.minx & 0xf) + sectionBox.width, ] maskSlices = numpy.s_[sectionBox.miny - box.miny:sectionBox.maxy - box.miny, sectionBox.minz - box.minz:sectionBox.maxz - box.minz, sectionBox.minx - box.minx:sectionBox.maxx - box.minx, ] blocks = section.Blocks mask[maskSlices] = blocks[slices] != 0 return mask
def copyBlocksIter(destDim, sourceDim, sourceSelection, destinationPoint, blocksToCopy=None, entities=True, create=False, biomes=False, updateLights=False): """ Copy blocks and entities from the `sourceBox` area of `sourceDim` to `destDim` starting at `destinationPoint`. :param sourceDim: WorldEditorDimension :param destDim: WorldEditorDimension Optional parameters: - `blocksToCopy`: list of blockIDs to copy. - `entities`: True to copy Entities and TileEntities, False otherwise. - `create`: True to create new chunks in destLevel, False otherwise. - `biomes`: True to copy biome data, False otherwise. """ (lx, ly, lz) = sourceSelection.size # needs work xxx log.info(u"Copying {0} blocks from {1} to {2}" .format(ly * lz * lx, sourceSelection, destinationPoint)) startTime = time.time() destBox = BoundingBox(destinationPoint, sourceSelection.size) chunkCount = destBox.chunkCount i = 0 entitiesCopied = 0 tileEntitiesCopied = 0 entitiesSeen = 0 tileEntitiesSeen = 0 if updateLights: allChangedX = [] allChangedY = [] allChangedZ = [] makeSourceMask = sourceMaskFunc(blocksToCopy) copyOffset = destBox.origin - sourceSelection.origin # Visit each chunk in the source area # Visit each section in this chunk # Find the chunks and sections of the destination area corresponding to this section # Compute slices for Blocks array and mask # Use slices and mask to copy Blocks and Data # Copy entities and tile entities from this chunk. sourceBiomeMask = None convertBlocks = blocktypes.blocktypeConverter(destDim.blocktypes, sourceDim.blocktypes) for sourceCpos in sourceSelection.chunkPositions(): # Visit each chunk if not sourceDim.containsChunk(*sourceCpos): continue sourceChunk = sourceDim.getChunk(*sourceCpos) i += 1 yield (i, chunkCount) if i % 20 == 0: log.info("Copying: Chunk {0}/{1}...".format(i, chunkCount)) # Use sourceBiomeMask to accumulate a list of columns over all sections whose biomes should be copied. sourceBiomes = None if biomes and hasattr(sourceChunk, 'Biomes'): sourceBiomes = sourceChunk.Biomes sourceBiomeMask = numpy.zeros_like(sourceBiomes) for sourceCy in sourceChunk.sectionPositions(): # Visit each section sourceSection = sourceChunk.getSection(sourceCy) if sourceSection is None: continue selectionMask = sourceSelection.section_mask(sourceCpos[0], sourceCy, sourceCpos[1]) if selectionMask is None: continue typeMask = makeSourceMask(sourceSection.Blocks) sourceMask = selectionMask & typeMask # Update sourceBiomeMask if sourceBiomes is not None: sourceBiomeMask |= sourceMask.any(axis=0) # Find corresponding destination area(s) sectionBox = SectionBox(sourceCpos[0], sourceCy, sourceCpos[1]) destBox = BoundingBox(sectionBox.origin + copyOffset, sectionBox.size) for destCpos in destBox.chunkPositions(): if not create and not destDim.containsChunk(*destCpos): continue destChunk = destDim.getChunk(*destCpos, create=True) for destCy in destBox.sectionPositions(*destCpos): # Compute slices for source and dest arrays destSectionBox = SectionBox(destCpos[0], destCy, destCpos[1]) intersect = destSectionBox.intersect(destBox) if intersect.volume == 0: continue destSection = destChunk.getSection(destCy, create=True) if destSection is None: continue destSlices = ( slice(intersect.miny - (destCy << 4), intersect.maxy - (destCy << 4)), slice(intersect.minz - (destCpos[1] << 4), intersect.maxz - (destCpos[1] << 4)), slice(intersect.minx - (destCpos[0] << 4), intersect.maxx - (destCpos[0] << 4)), ) sourceIntersect = BoundingBox(intersect.origin - copyOffset, intersect.size) sourceSlices = ( slice(sourceIntersect.miny - (sourceCy << 4), sourceIntersect.maxy - (sourceCy << 4)), slice(sourceIntersect.minz - (sourceCpos[1] << 4), sourceIntersect.maxz - (sourceCpos[1] << 4)), slice(sourceIntersect.minx - (sourceCpos[0] << 4), sourceIntersect.maxx - (sourceCpos[0] << 4)), ) # Read blocks sourceBlocks = sourceSection.Blocks[sourceSlices] sourceData = sourceSection.Data[sourceSlices] sourceMaskPart = sourceMask[sourceSlices] # Convert blocks convertedSourceBlocks, convertedSourceData = convertBlocks(sourceBlocks, sourceData) convertedSourceBlocksMasked = convertedSourceBlocks[sourceMaskPart] # Find blocks that need direct lighting update - block opacity or brightness changed oldBrightness = destDim.blocktypes.brightness[destSection.Blocks[destSlices][sourceMaskPart]] newBrightness = destDim.blocktypes.brightness[convertedSourceBlocksMasked] oldOpacity = destDim.blocktypes.opacity[destSection.Blocks[destSlices][sourceMaskPart]] newOpacity = destDim.blocktypes.opacity[convertedSourceBlocksMasked] changedLight = (oldBrightness != newBrightness) | (oldOpacity != newOpacity) # Write blocks destSection.Blocks[destSlices][sourceMaskPart] = convertedSourceBlocks[sourceMaskPart] destSection.Data[destSlices][sourceMaskPart] = convertedSourceData[sourceMaskPart] if updateLights: # Find coordinates of lighting updates (changedFlat,) = changedLight.nonzero() # Since convertedSourceBlocksMasked is a 1d array, changedFlat is an index # into this array. Thus, changedFlat is also an index into the nonzero values # of sourceMaskPart. if len(changedFlat): x, y, z = sourceMaskPart.nonzero() changedX = x[changedFlat].astype('i4') changedY = y[changedFlat].astype('i4') changedZ = z[changedFlat].astype('i4') changedX += intersect.minx changedY += intersect.miny changedZ += intersect.minz if updateLights == "all": allChangedX.append(changedX) allChangedY.append(changedY) allChangedZ.append(changedZ) else: # log.info("Updating section lights in %s blocks... (ob %s)", # changedFlat.shape, # oldBrightness.shape) relight.updateLightsByCoord(destDim, changedX, changedY, changedZ) destChunk.dirty = True # Copy biomes if sourceBiomes is not None: bx, bz = sourceBiomeMask.nonzero() wbx = bx + (sourceCpos[0] << 4) wbz = bz + (sourceCpos[1] << 4) destDim.setBlocks(wbx, 1, wbz, Biomes=sourceBiomes[bx, bz]) # Copy entities and tile entities if entities: entitiesSeen += len(sourceChunk.Entities) for entity in sourceChunk.Entities: if entity.Position in sourceSelection: entitiesCopied += 1 newEntity = entity.copyWithOffset(copyOffset) destDim.addEntity(newEntity) tileEntitiesSeen += len(sourceChunk.TileEntities) for tileEntity in sourceChunk.TileEntities: if tileEntity.Position in sourceSelection: tileEntitiesCopied += 1 newEntity = tileEntity.copyWithOffset(copyOffset) destDim.addTileEntity(newEntity) duration = time.time() - startTime log.info("Duration: %0.3fs, %d/%d chunks, %0.2fms per chunk (%0.2f chunks per second)", duration, i, sourceSelection.chunkCount, 1000 * duration/i, i/duration) log.info("Copied %d/%d entities and %d/%d tile entities", entitiesCopied, entitiesSeen, tileEntitiesCopied, tileEntitiesSeen) if updateLights == "all": log.info("Updating all at once for %d sections (%d cells)", len(allChangedX), sum(len(a) for a in allChangedX)) startTime = time.time() for i in range(len(allChangedX)): x = allChangedX[i] y = allChangedY[i] z = allChangedZ[i] relight.updateLightsByCoord(destDim, x, y, z) i = i or 1 duration = time.time() - startTime duration = duration or 1 log.info("Lighting complete.") log.info("Duration: %0.3fs, %d sections, %0.2fms per section (%0.2f sections per second)", duration, i, 1000 * duration/i, i/duration)
def copyBlocksIter(destDim, sourceDim, sourceSelection, destinationPoint, blocksToCopy=None, entities=True, create=False, biomes=False): """ Copy blocks and entities from the `sourceBox` area of `sourceDim` to `destDim` starting at `destinationPoint`. :param sourceDim: WorldEditorDimension :param destDim: WorldEditorDimension Optional parameters: - `blocksToCopy`: list of blockIDs to copy. - `entities`: True to copy Entities and TileEntities, False otherwise. - `create`: True to create new chunks in destLevel, False otherwise. - `biomes`: True to copy biome data, False otherwise. """ (lx, ly, lz) = sourceSelection.size # needs work xxx log.info(u"Copying {0} blocks from {1} to {2}".format( ly * lz * lx, sourceSelection, destinationPoint)) startTime = datetime.now() destBox = BoundingBox(destinationPoint, sourceSelection.size) chunkCount = destBox.chunkCount i = 0 entitiesCopied = 0 tileEntitiesCopied = 0 entitiesSeen = 0 tileEntitiesSeen = 0 makeSourceMask = sourceMaskFunc(blocksToCopy) copyOffset = destBox.origin - sourceSelection.origin # Visit each chunk in the source area # Visit each section in this chunk # Find the chunks and sections of the destination area corresponding to this section # Compute slices for Blocks array and mask # Use slices and mask to copy Blocks and Data # Copy entities and tile entities from this chunk. sourceBiomeMask = None convertBlocks = blocktypes.blocktypeConverter(destDim.blocktypes, sourceDim.blocktypes) for sourceCpos in sourceSelection.chunkPositions(): # Visit each chunk if not sourceDim.containsChunk(*sourceCpos): continue sourceChunk = sourceDim.getChunk(*sourceCpos) i += 1 yield (i, chunkCount) if i % 100 == 0: log.info("Copying: Chunk {0}...".format(i)) # Use sourceBiomeMask to accumulate a list of columns over all sections whose biomes should be copied. sourceBiomes = None if biomes and hasattr(sourceChunk, 'Biomes'): sourceBiomes = sourceChunk.Biomes sourceBiomeMask = numpy.zeros_like(sourceBiomes) for sourceCy in sourceChunk.sectionPositions(): # Visit each section sourceSection = sourceChunk.getSection(sourceCy) if sourceSection is None: continue selectionMask = sourceSelection.section_mask( sourceCpos[0], sourceCy, sourceCpos[1]) if selectionMask is None: continue typeMask = makeSourceMask(sourceSection.Blocks) sourceMask = selectionMask & typeMask # Update sourceBiomeMask if sourceBiomes is not None: sourceBiomeMask |= sourceMask.any(axis=0) # Find corresponding destination area(s) sectionBox = SectionBox(sourceCpos[0], sourceCy, sourceCpos[1], sourceSection) destBox = BoundingBox(sectionBox.origin + copyOffset, sectionBox.size) for destCpos in destBox.chunkPositions(): if not create and not destDim.containsChunk(*destCpos): continue destChunk = destDim.getChunk(*destCpos, create=True) for destCy in destBox.sectionPositions(*destCpos): # Compute slices for source and dest arrays destSectionBox = SectionBox(destCpos[0], destCy, destCpos[1]) intersect = destSectionBox.intersect(destBox) if intersect.volume == 0: continue destSection = destChunk.getSection(destCy, create=True) if destSection is None: continue # Recompute destSectionBox and intersect using the shape of destSection.Blocks # after destChunk is loaded to work with odd shaped FakeChunkDatas XXXXXXXXXXXX destSectionBox = SectionBox(destCpos[0], destCy, destCpos[1], destSection) intersect = destSectionBox.intersect(destBox) if intersect.volume == 0: continue destSlices = ( slice(intersect.miny - (destCy << 4), intersect.maxy - (destCy << 4)), slice(intersect.minz - (destCpos[1] << 4), intersect.maxz - (destCpos[1] << 4)), slice(intersect.minx - (destCpos[0] << 4), intersect.maxx - (destCpos[0] << 4)), ) sourceIntersect = BoundingBox( intersect.origin - copyOffset, intersect.size) sourceSlices = ( slice(sourceIntersect.miny - (sourceCy << 4), sourceIntersect.maxy - (sourceCy << 4)), slice(sourceIntersect.minz - (sourceCpos[1] << 4), sourceIntersect.maxz - (sourceCpos[1] << 4)), slice(sourceIntersect.minx - (sourceCpos[0] << 4), sourceIntersect.maxx - (sourceCpos[0] << 4)), ) # Read blocks sourceBlocks = sourceSection.Blocks[sourceSlices] sourceData = sourceSection.Data[sourceSlices] sourceMaskPart = sourceMask[sourceSlices] # Convert blocks convertedSourceBlocks, convertedSourceData = convertBlocks( sourceBlocks, sourceData) # Write blocks destSection.Blocks[destSlices][ sourceMaskPart] = convertedSourceBlocks[sourceMaskPart] destSection.Data[destSlices][ sourceMaskPart] = convertedSourceData[sourceMaskPart] destChunk.dirty = True # Copy biomes if sourceBiomes is not None: bx, bz = sourceBiomeMask.nonzero() wbx = bx + (sourceCpos[0] << 4) wbz = bz + (sourceCpos[1] << 4) destDim.setBlocks(wbx, 1, wbz, Biomes=sourceBiomes[bx, bz]) # Copy entities and tile entities if entities: entitiesSeen += len(sourceChunk.Entities) for entity in sourceChunk.Entities: if entity.Position in sourceSelection: entitiesCopied += 1 newEntity = entity.copyWithOffset(copyOffset) destDim.addEntity(newEntity) tileEntitiesSeen += len(sourceChunk.TileEntities) for tileEntity in sourceChunk.TileEntities: if tileEntity.Position in sourceSelection: tileEntitiesCopied += 1 newEntity = tileEntity.copyWithOffset(copyOffset) destDim.addTileEntity(newEntity) log.info("Duration: {0}".format(datetime.now() - startTime)) log.info("Copied %d/%d entities and %d/%d tile entities", entitiesCopied, entitiesSeen, tileEntitiesCopied, tileEntitiesSeen)
def areaBlocksOrData(self, arrayName): """ Return the blocks in an 18-wide cube centered on this section. Only retrieves blocks from the 6 sections neighboring this one along a major axis, so the corners are empty. That's fine since they aren't needed for any calculation we do. :return: Array of blocks in this chunk and six of its neighbors. :rtype: numpy.ndarray(shape=(18, 18, 18), dtype='uint16') """ chunk = self.chunkUpdate.chunk chunkWidth, chunkLength, chunkHeight = self.chunkSection.Blocks.shape cy = self.chunkSection.Y areaBlocks = numpy.empty( (chunkWidth + 2, chunkLength + 2, chunkHeight + 2), numpy.uint16 if arrayName == "Blocks" else numpy.uint8) areaBlocks[(0, -1), :, :] = 0 areaBlocks[:, (0, -1)] = 0 areaBlocks[:, :, (0, -1)] = 0 mask = None bounds = self.chunkUpdate.chunkInfo.worldScene.bounds if bounds: cx, cz = self.chunkUpdate.chunk.chunkPosition sectionBox = SectionBox(cx, cy, cz).expand(1) areaBox = BoundingBox( sectionBox.origin, (chunkWidth + 2, chunkHeight + 2, chunkLength + 2)) mask = bounds.box_mask(areaBox) if mask is None: return areaBlocks areaBlocks[1:-1, 1:-1, 1:-1] = getattr(self.chunkSection, arrayName) neighboringChunks = self.chunkUpdate.neighboringChunks if faces.FaceXDecreasing in neighboringChunks: ncs = neighboringChunks[faces.FaceXDecreasing].getSection(cy) if ncs: areaBlocks[1:-1, 1:-1, :1] = getattr(ncs, arrayName)[:, :, -1:] if faces.FaceXIncreasing in neighboringChunks: ncs = neighboringChunks[faces.FaceXIncreasing].getSection(cy) if ncs: areaBlocks[1:-1, 1:-1, -1:] = getattr(ncs, arrayName)[:, :, :1] if faces.FaceZDecreasing in neighboringChunks: ncs = neighboringChunks[faces.FaceZDecreasing].getSection(cy) if ncs: areaBlocks[1:-1, :1, 1:-1] = getattr(ncs, arrayName)[:chunkWidth, -1:, :chunkHeight] if faces.FaceZIncreasing in neighboringChunks: ncs = neighboringChunks[faces.FaceZIncreasing].getSection(cy) if ncs: areaBlocks[1:-1, -1:, 1:-1] = getattr( ncs, arrayName)[:chunkWidth, :1, :chunkHeight] aboveSection = chunk.getSection(self.chunkSection.Y + 1) if aboveSection: areaBlocks[-1:, 1:-1, 1:-1] = getattr(aboveSection, arrayName)[:1, :, :] belowSection = chunk.getSection(self.chunkSection.Y - 1) if belowSection: areaBlocks[:1, 1:-1, 1:-1] = getattr(belowSection, arrayName)[-1:, :, :] if mask is not None: areaBlocks[~mask] = 0 return areaBlocks