def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.toolWidget.setLayout( Column(self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None editorSession.dimensionChanged.connect(self.dimensionDidChange)
def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.optionsButton = QtGui.QPushButton(self.tr("Options")) self.optionsMenu = QtGui.QMenu() self.optionsButton.setMenu(self.optionsMenu) classicSelectionAction = self.optionsMenu.addAction( self.tr("Classic Selection")) stickySelectionAction = self.optionsMenu.addAction( self.tr("Sticky Selection")) self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget(addShapes=[ChunkShape()]) self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.shapeInput.shapeOptionsChanged.connect(self.shapeDidChange) self.toolWidget.setLayout( Column(self.optionsButton, self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenenode.Node("selectOverlay") self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None classicSelectionAction.setCheckable(True) classicSelectionAction.toggled.connect(ClassicSelectionOption.setValue) classicSelectionAction.setChecked(ClassicSelectionOption.value()) ClassicSelectionOption.connectAndCall(self.setClassicSelection) stickySelectionAction.setCheckable(True) stickySelectionAction.toggled.connect(StickySelectionOption.setValue) stickySelectionAction.setChecked(StickySelectionOption.value()) StickySelectionOption.connectAndCall(self.setStickySelection) editorSession.dimensionChanged.connect(self.dimensionDidChange) editorSession.revisionChanged.connect(self.revisionDidChange)
def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.deselectButton = QtGui.QPushButton(self.tr("Deselect")) self.deselectButton.clicked.connect(self.deselect) self.deleteSelectionButton = QtGui.QPushButton(self.tr("Delete")) self.deleteSelectionButton.clicked.connect(self.deleteSelection) self.deleteBlocksButton = QtGui.QPushButton(self.tr("Delete Blocks")) self.deleteBlocksButton.clicked.connect(self.deleteBlocks) self.deleteEntitiesButton = QtGui.QPushButton(self.tr("Delete Entities")) self.deleteEntitiesButton.clicked.connect(self.deleteEntities) self.fillButton = QtGui.QPushButton(self.tr("Fill")) self.fillButton.clicked.connect(self.fill) self.replaceButton = QtGui.QPushButton(self.tr("Replace")) self.replaceButton.clicked.connect(self.replace) self.toolWidget.setLayout(Column(self.coordInput, self.shapeInput, self.deselectButton, self.deleteSelectionButton, self.deleteBlocksButton, self.deleteEntitiesButton, self.fillButton, self.replaceButton, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.selectionNode.dimension = editorSession.currentDimension # xxx dimensionDidChange self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None
def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.toolWidget.setLayout(Column(self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None editorSession.dimensionChanged.connect(self.dimensionDidChange)
def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.optionsButton = QtGui.QPushButton(self.tr("Options")) self.optionsMenu = QtGui.QMenu() self.optionsButton.setMenu(self.optionsMenu) classicSelectionAction = self.optionsMenu.addAction(self.tr("Classic Selection")) stickySelectionAction = self.optionsMenu.addAction(self.tr("Sticky Selection")) self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget(addShapes=[ChunkShape()]) self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.shapeInput.shapeOptionsChanged.connect(self.shapeDidChange) self.toolWidget.setLayout(Column(self.optionsButton, self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenenode.Node("selectOverlay") self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None classicSelectionAction.setCheckable(True) classicSelectionAction.toggled.connect(ClassicSelectionOption.setValue) classicSelectionAction.setChecked(ClassicSelectionOption.value()) ClassicSelectionOption.connectAndCall(self.setClassicSelection) stickySelectionAction.setCheckable(True) stickySelectionAction.toggled.connect(StickySelectionOption.setValue) stickySelectionAction.setChecked(StickySelectionOption.value()) StickySelectionOption.connectAndCall(self.setStickySelection) editorSession.dimensionChanged.connect(self.dimensionDidChange) editorSession.revisionChanged.connect(self.revisionDidChange)
def createToolWidget(self): toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [TreeGen()] self.currentType = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() for gt in self.generatorTypes: self.generatorTypeInput.addItem(gt.displayName, gt) self.generatorTypeInput.currentIndexChanged.connect( self.generatorTypeChanged) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) column.append(self.generatorTypeInput) column.append(self.optionsHolder) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenegraph.Node() self.sceneHolderNode = scenegraph.TranslateNode() self.overlayNode.addChild(self.sceneHolderNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.schematicBounds = None
def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.livePreview = False self.blockPreview = False self.glPreview = True toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [pluginClass(self) for pluginClass in _pluginClasses] self.currentGenerator = None if len(self.generatorTypes): self.currentGenerator = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() self.generatorTypesChanged() self.generatorTypeInput.currentIndexChanged.connect(self.currentTypeChanged) self.livePreviewCheckbox = QtGui.QCheckBox("Live Preview") self.livePreviewCheckbox.setChecked(self.livePreview) self.livePreviewCheckbox.toggled.connect(self.livePreviewToggled) self.blockPreviewCheckbox = QtGui.QCheckBox("Block Preview") self.blockPreviewCheckbox.setChecked(self.blockPreview) self.blockPreviewCheckbox.toggled.connect(self.blockPreviewToggled) self.glPreviewCheckbox = QtGui.QCheckBox("GL Preview") self.glPreviewCheckbox.setChecked(self.glPreview) self.glPreviewCheckbox.toggled.connect(self.glPreviewToggled) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.generateButton = QtGui.QPushButton(self.tr("Generate")) self.generateButton.clicked.connect(self.generateClicked) column.append(self.generatorTypeInput) column.append(self.livePreviewCheckbox) column.append(self.blockPreviewCheckbox) column.append(self.glPreviewCheckbox) column.append(self.optionsHolder) column.append(self.generateButton) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenegraph.Node() self.sceneHolderNode = scenegraph.TranslateNode() self.overlayNode.addChild(self.sceneHolderNode) self.previewNode = None self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.loader = None self.previewBounds = None self.schematicBounds = None self.currentSchematic = None self.currentTypeChanged(0) # Name of last selected generator plugin is saved after unloading # so it can be reselected if it is immediately reloaded self._lastTypeName = None _GeneratePlugins.instance.pluginAdded.connect(self.addPlugin) _GeneratePlugins.instance.pluginRemoved.connect(self.removePlugin)
class GenerateTool(EditorTool): name = "Generate" iconName = "generate" instantDisplayChunks = 32 def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.livePreview = False self.blockPreview = False self.glPreview = True toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [pluginClass(self) for pluginClass in _pluginClasses] self.currentGenerator = None if len(self.generatorTypes): self.currentGenerator = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() self.generatorTypesChanged() self.generatorTypeInput.currentIndexChanged.connect(self.currentTypeChanged) self.livePreviewCheckbox = QtGui.QCheckBox("Live Preview") self.livePreviewCheckbox.setChecked(self.livePreview) self.livePreviewCheckbox.toggled.connect(self.livePreviewToggled) self.blockPreviewCheckbox = QtGui.QCheckBox("Block Preview") self.blockPreviewCheckbox.setChecked(self.blockPreview) self.blockPreviewCheckbox.toggled.connect(self.blockPreviewToggled) self.glPreviewCheckbox = QtGui.QCheckBox("GL Preview") self.glPreviewCheckbox.setChecked(self.glPreview) self.glPreviewCheckbox.toggled.connect(self.glPreviewToggled) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.generateButton = QtGui.QPushButton(self.tr("Generate")) self.generateButton.clicked.connect(self.generateClicked) column.append(self.generatorTypeInput) column.append(self.livePreviewCheckbox) column.append(self.blockPreviewCheckbox) column.append(self.glPreviewCheckbox) column.append(self.optionsHolder) column.append(self.generateButton) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenegraph.Node() self.sceneHolderNode = scenegraph.TranslateNode() self.overlayNode.addChild(self.sceneHolderNode) self.previewNode = None self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.loader = None self.previewBounds = None self.schematicBounds = None self.currentSchematic = None self.currentTypeChanged(0) # Name of last selected generator plugin is saved after unloading # so it can be reselected if it is immediately reloaded self._lastTypeName = None _GeneratePlugins.instance.pluginAdded.connect(self.addPlugin) _GeneratePlugins.instance.pluginRemoved.connect(self.removePlugin) def removePlugin(self, cls): self.generatorTypes[:] = [gt for gt in self.generatorTypes if not isinstance(gt, cls)] self.generatorTypesChanged() if self.currentGenerator not in self.generatorTypes: lastTypeName = self.currentGenerator.__class__.__name__ self.currentTypeChanged(0) # resets self._lastTypeName self._lastTypeName = lastTypeName def addPlugin(self, cls): self.generatorTypes.append(cls(self)) self.generatorTypesChanged() if self._lastTypeName is not None: if cls.__name__ == self._lastTypeName: self.currentTypeChanged(len(self.generatorTypes)-1) def generatorTypesChanged(self): for gt in self.generatorTypes: if hasattr(gt, 'displayName'): displayName = gt.displayName else: displayName = gt.__class__.__name__ self.generatorTypeInput.addItem(displayName, gt) def livePreviewToggled(self, value): self.livePreview = value def blockPreviewToggled(self, value): self.blockPreview = value if value: if self.currentSchematic: self.displaySchematic(self.currentSchematic, self.schematicBounds.origin) else: self.updateBlockPreview() else: self.clearSchematic() def glPreviewToggled(self, value): self.glPreview = value if value: self.updateNodePreview() # xxx cache previewNode? else: self.clearNode() def currentTypeChanged(self, index): # user selected generator type after old type was unloaded, so forget the old type self._lastTypeName = None self.optionsHolder.removeWidget(self.optionsHolder.widget(0)) if index < len(self.generatorTypes): self.currentGenerator = self.generatorTypes[index] self.optionsHolder.addWidget(self.currentGenerator.getOptionsWidget()) self.updatePreview() else: self.currentGenerator = None self.clearSchematic() self.clearNode() def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) def boundsDidChange(self, bounds): # box still being resized if not self.currentGenerator: return if not self.livePreview: return self.previewBounds = bounds self.currentGenerator.boundsChanged(bounds) self.updatePreview() def boundsDidChangeDone(self, bounds, newSelection): # box finished resize if not self.currentGenerator: return self.previewBounds = bounds self.schematicBounds = bounds self.currentGenerator.boundsChanged(bounds) self.updatePreview() def clearNode(self): if self.previewNode: self.overlayNode.removeChild(self.previewNode) self.previewNode = None def updatePreview(self): if self.blockPreview: self.updateBlockPreview() else: self.clearSchematic() if self.glPreview: self.updateNodePreview() else: self.clearNode() def updateBlockPreview(self): bounds = self.previewBounds if bounds is not None and bounds.volume > 0: self.generateNextSchematic(bounds) else: self.clearSchematic() def updateNodePreview(self): bounds = self.previewBounds if self.currentGenerator is None: return if bounds is not None and bounds.volume > 0: node = self.currentGenerator.getPreviewNode(bounds) if node is not None: self.clearNode() if isinstance(node, list): nodes = node node = scenegraph.Node() for c in nodes: node.addChild(c) self.overlayNode.addChild(node) self.previewNode = node def generateNextSchematic(self, bounds): if bounds is None: self.clearSchematic() return if self.currentGenerator is None: return try: schematic = self.currentGenerator.generate(bounds, self.editorSession.worldEditor.blocktypes) self.currentSchematic = schematic self.displaySchematic(schematic, bounds.origin) except Exception as e: log.exception("Error while running generator %s: %s", self.currentGenerator, e) QtGui.QMessageBox.warning(qApp.mainWindow, "Error while running generator", "An error occurred while running the generator: \n %s.\n\n" "Traceback: %s" % (e, traceback.format_exc())) self.livePreview = False def displaySchematic(self, schematic, offset): if schematic is not None: dim = schematic.getDimension() if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.loader.timer.stop() self.loader = None atlas = self.editorSession.textureAtlas self.worldScene = WorldScene(dim, atlas) self.sceneHolderNode.translateOffset = offset self.sceneHolderNode.addChild(self.worldScene) self.loader = WorldLoader(self.worldScene) if dim.chunkCount() <= self.instantDisplayChunks: exhaust(self.loader.work()) else: self.loader.timer.start() else: self.clearSchematic() def clearSchematic(self): if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.worldScene = None self.loader.timer.stop() self.loader = None def generateClicked(self): if self.currentGenerator is None: return if self.schematicBounds is None: log.info("schematicBounds is None, not generating") return if self.currentSchematic is None: log.info("Generating new schematic for import") currentSchematic = self.currentGenerator.generate(self.schematicBounds, self.editorSession.worldEditor.blocktypes) else: log.info("Importing previously generated schematic.") currentSchematic = self.currentSchematic command = GenerateCommand(self, self.schematicBounds) try: with command.begin(): if currentSchematic is not None: task = self.editorSession.currentDimension.importSchematicIter(currentSchematic, self.schematicBounds.origin) showProgress(self.tr("Importing generated object..."), task) else: task = self.currentGenerator.generateInWorld(self.schematicBounds, self.editorSession.currentDimension) showProgress(self.tr("Generating object in world..."), task) except Exception as e: log.exception("Error while importing or generating in world: %r" % e) command.undo() else: self.editorSession.pushCommand(command)
def __init__(self, pendingImport, textureAtlas): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport dim = pendingImport.sourceDim # positionTranslateNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.positionTranslateNode = TranslateNode() self.rotateNode = Rotate3DNode() self.addChild(self.positionTranslateNode) self.positionTranslateNode.addChild(self.rotateNode) self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) # worldSceneTranslateNode is contained by positionTranslateNode, and # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode # can translate by the current position. self.worldSceneTranslateNode = TranslateNode() self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer # transformedWorldTranslateNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedWorldTranslateNode = TranslateNode() self.transformedWorldScene = None self.addChild(self.transformedWorldTranslateNode) self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin self.worldSceneTranslateNode.addChild(self.worldScene) self.rotateNode.addChild(self.worldSceneTranslateNode) # handleNode displays a bounding box that can be moved around, and responds # to mouse events. box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.updateTransformedScene() self.basePosition = pendingImport.basePosition self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader( self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader() self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation)
class PendingImportNode(Node, QtCore.QObject): __node_id_counter = 0 def __init__(self, pendingImport, textureAtlas): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport dim = pendingImport.sourceDim # positionTranslateNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.positionTranslateNode = TranslateNode() self.rotateNode = Rotate3DNode() self.addChild(self.positionTranslateNode) self.positionTranslateNode.addChild(self.rotateNode) self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) # worldSceneTranslateNode is contained by positionTranslateNode, and # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode # can translate by the current position. self.worldSceneTranslateNode = TranslateNode() self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer # transformedWorldTranslateNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedWorldTranslateNode = TranslateNode() self.transformedWorldScene = None self.addChild(self.transformedWorldTranslateNode) self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin self.worldSceneTranslateNode.addChild(self.worldScene) self.rotateNode.addChild(self.worldSceneTranslateNode) # handleNode displays a bounding box that can be moved around, and responds # to mouse events. box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.updateTransformedScene() self.basePosition = pendingImport.basePosition self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader( self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader() self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) # Emitted when the user finishes dragging the box handle and releases the mouse # button. Arguments are (newPosition, oldPosition). importMoved = QtCore.Signal(object, object) def handleBoundsChangedDone(self, bounds, oldBounds): point = self.getBaseFromTransformed(bounds.origin) oldPoint = self.getBaseFromTransformed(oldBounds.origin) if point != oldPoint: self.importMoved.emit(point, oldPoint) def handleBoundsChanged(self, bounds): point = self.getBaseFromTransformed(bounds.origin) if self.basePosition != point: self.basePosition = point def getBaseFromTransformed(self, point): offset = self.pendingImport.basePosition - self.pendingImport.importPos return point + offset def setPreviewRotation(self, rots): self.rotateNode.visible = True self.worldSceneTranslateNode.visible = True self.transformedWorldTranslateNode.visible = False self.rotateNode.setRotation(rots) def setRotation(self, rots): self.updateTransformedScene() self.updateBoxHandle() self.rotateNode.setRotation(rots) def updateTransformedScene(self): if self.pendingImport.transformedDim is not None: log.info("Showing transformed scene") self.rotateNode.visible = False self.worldSceneTranslateNode.visible = False self.transformedWorldTranslateNode.visible = True if self.transformedWorldScene: self.transformedWorldTranslateNode.removeChild( self.transformedWorldScene) self.transformedWorldScene = WorldScene( self.pendingImport.transformedDim, self.textureAtlas) self.transformedWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer self.transformedWorldTranslateNode.addChild( self.transformedWorldScene) self.updateTransformedSceneOffset() cPos = list(self.pendingImport.transformedDim.chunkPositions()) self.loader = WorldLoader(self.transformedWorldScene, cPos) self.loader.startLoader() else: log.info("Hiding transformed scene") self.rotateNode.visible = True self.worldSceneTranslateNode.visible = True self.transformedWorldTranslateNode.visible = False if self.transformedWorldScene: self.transformedWorldTranslateNode.removeChild( self.transformedWorldScene) self.transformedWorldScene = None def updateTransformedSceneOffset(self): self.transformedWorldTranslateNode.translateOffset = self.transformedPosition - self.pendingImport.importDim.bounds.origin @property def transformedPosition(self): return self.basePosition + self.pendingImport.importPos - self.pendingImport.basePosition @property def basePosition(self): return self.positionTranslateNode.translateOffset @basePosition.setter def basePosition(self, value): if value == self.positionTranslateNode.translateOffset: return self.positionTranslateNode.translateOffset = Vector(*value) self.updateTransformedSceneOffset() self.updateBoxHandle() def setPosition(self, pos): self.basePosition = pos def updateBoxHandle(self): if self.transformedWorldScene is None: bounds = BoundingBox(self.basePosition, self.pendingImport.bounds.size) else: origin = self.transformedPosition bounds = BoundingBox(origin, self.pendingImport.importBounds.size) #if self.handleNode.bounds.size != bounds.size: self.handleNode.bounds = bounds # --- Mouse events --- # inherit from BoxHandle? def mouseMove(self, event): self.handleNode.mouseMove(event) def mousePress(self, event): self.handleNode.mousePress(event) def mouseRelease(self, event): self.handleNode.mouseRelease(event)
def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.livePreview = False self.blockPreview = False self.glPreview = True toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [pluginClass(self) for pluginClass in _pluginClasses] self.currentGenerator = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() for gt in self.generatorTypes: self.generatorTypeInput.addItem(gt.displayName, gt) self.generatorTypeInput.currentIndexChanged.connect(self.generatorTypeChanged) self.livePreviewCheckbox = QtGui.QCheckBox("Live Preview") self.livePreviewCheckbox.setChecked(self.livePreview) self.livePreviewCheckbox.toggled.connect(self.livePreviewToggled) self.blockPreviewCheckbox = QtGui.QCheckBox("Block Preview") self.blockPreviewCheckbox.setChecked(self.blockPreview) self.blockPreviewCheckbox.toggled.connect(self.blockPreviewToggled) self.glPreviewCheckbox = QtGui.QCheckBox("GL Preview") self.glPreviewCheckbox.setChecked(self.glPreview) self.glPreviewCheckbox.toggled.connect(self.glPreviewToggled) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.generateButton = QtGui.QPushButton(self.tr("Generate")) self.generateButton.clicked.connect(self.generateClicked) column.append(self.generatorTypeInput) column.append(self.livePreviewCheckbox) column.append(self.blockPreviewCheckbox) column.append(self.glPreviewCheckbox) column.append(self.optionsHolder) column.append(self.generateButton) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenegraph.Node() self.sceneHolderNode = scenegraph.TranslateNode() self.overlayNode.addChild(self.sceneHolderNode) self.previewNode = None self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.previewBounds = None self.schematicBounds = None self.currentSchematic = None if len(self.generatorTypes): self.generatorTypeChanged(0)
class PendingImportNode(Node, QtCore.QObject): __node_id_counter = 0 def __init__(self, pendingImport, textureAtlas, hasHandle=True): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. hasHandle: bool True if this import node should have a user-interactive BoxHandle associated with it. This is False for the extra copies displayed by a repeated clone. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport self.hasHandle = hasHandle dim = pendingImport.sourceDim self.transformedPosition = Vector(0, 0, 0) # worldScene is contained by rotateNode, and # translates the world scene back to 0, 0, 0 so the rotateNode can # rotate it around the anchor, and the plainSceneNode can translate # it to the current position. self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.worldSceneTranslate = Translate( -self.pendingImport.selection.origin) self.worldScene.addState(self.worldSceneTranslate) # rotateNode is used to rotate the non-transformed preview during live rotation self.rotateNode = Rotate3DNode() self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) self.rotateNode.addChild(self.worldScene) self.scaleNode = Scale3DNode() self.scaleNode.setAnchor(self.pendingImport.selection.size * 0.5) self.scaleNode.addChild(self.rotateNode) # plainSceneNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.plainSceneNode = Node("plainScene") self.positionTranslate = Translate() self.plainSceneNode.addState(self.positionTranslate) self.plainSceneNode.addChild(self.scaleNode) self.addChild(self.plainSceneNode) # transformedSceneNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedSceneNode = Node("transformedScene") self.transformedSceneTranslate = Translate() self.transformedSceneNode.addState(self.transformedSceneTranslate) self.transformedWorldScene = None self.addChild(self.transformedSceneNode) box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) if hasHandle: # handleNode displays a bounding box that can be moved around, and responds # to mouse events. self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.boxNode = None else: # boxNode displays a plain, non-movable bounding box self.boxNode = SelectionBoxNode() self.boxNode.wireColor = (1, 1, 1, .2) self.boxNode.filled = False self.handleNode = None self.addChild(self.boxNode) self.updateTransformedScene() self.basePosition = pendingImport.basePosition if hasHandle: self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect( self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader( self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader(0.1 if self.hasHandle else 0.0) self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) self.pendingImport.scaleChanged.connect(self.setScale) # Emitted when the user finishes dragging the box handle and releases the mouse # button. Arguments are (newPosition, oldPosition). importMoved = QtCore.Signal(object, object) # Emitted while the user is dragging the box handle. Argument is the box origin. importIsMoving = QtCore.Signal(object) def handleBoundsChangedDone(self, bounds, oldBounds): point = self.getBaseFromTransformed(bounds.origin) oldPoint = self.getBaseFromTransformed(oldBounds.origin) if point != oldPoint: self.importMoved.emit(point, oldPoint) def handleBoundsChanged(self, bounds): log.info("handleBoundsChanged: %s", bounds) self.setPreviewBasePosition(bounds.origin) def setPreviewBasePosition(self, origin): point = self.getBaseFromTransformed(origin) if self.basePosition != point: self.basePosition = point self.importIsMoving.emit(point) def getBaseFromTransformed(self, point): return point - self.pendingImport.transformOffset def setPreviewRotation(self, rots): self.plainSceneNode.visible = True self.transformedSceneNode.visible = False self.rotateNode.setRotation(rots) def setRotation(self, rots): self.updateTransformedScene() self.updateBoxHandle() self.rotateNode.setRotation(rots) def setPreviewScale(self, scale): self.plainSceneNode.visible = True self.transformedSceneNode.visible = False self.scaleNode.setScale(scale) def setScale(self, scale): self.updateTransformedScene() self.updateBoxHandle() self.scaleNode.setScale(scale) def updateTransformedScene(self): if self.pendingImport.transformedDim is not None: log.info("Showing transformed scene") self.plainSceneNode.visible = False self.transformedSceneNode.visible = True if self.transformedWorldScene: self.transformedSceneNode.removeChild( self.transformedWorldScene) self.transformedWorldScene = WorldScene( self.pendingImport.transformedDim, self.textureAtlas) self.transformedWorldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.transformedSceneNode.addChild(self.transformedWorldScene) self.updateTransformedPosition() cPos = list(self.pendingImport.transformedDim.chunkPositions()) self.loader = WorldLoader(self.transformedWorldScene, cPos) # ALERT!: self.hasHandle is overloaded with the meaning: # "not the first clone in a repeated clone" self.loader.startLoader(0.1 if self.hasHandle else 0.0) else: log.info("Hiding transformed scene") self.plainSceneNode.visible = True self.transformedSceneNode.visible = False if self.transformedWorldScene: self.transformedSceneNode.removeChild( self.transformedWorldScene) self.transformedWorldScene = None def updateTransformedPosition(self): self.transformedPosition = self.basePosition + self.pendingImport.transformOffset self.transformedSceneTranslate.translateOffset = self.transformedPosition - self.pendingImport.importDim.bounds.origin def updateBoxHandle(self): if self.transformedWorldScene is None: bounds = BoundingBox(self.basePosition, self.pendingImport.bounds.size) else: origin = self.transformedPosition bounds = BoundingBox(origin, self.pendingImport.importBounds.size) #if self.handleNode.bounds.size != bounds.size: if self.hasHandle: self.handleNode.bounds = bounds else: self.boxNode.selectionBox = bounds @property def basePosition(self): return self.positionTranslate.translateOffset @basePosition.setter def basePosition(self, value): value = Vector(*value) if value == self.positionTranslate.translateOffset: return self.positionTranslate.translateOffset = value self.updateTransformedPosition() self.updateBoxHandle() def setPosition(self, pos): self.basePosition = pos # --- Mouse events --- # inherit from BoxHandle? def mouseMove(self, event): if not self.hasHandle: return self.handleNode.mouseMove(event) def mousePress(self, event): if not self.hasHandle: return self.handleNode.mousePress(event) def mouseRelease(self, event): if not self.hasHandle: return self.handleNode.mouseRelease(event)
class GenerateTool(EditorTool): name = "Generate" iconName = "generate" instantDisplayChunks = 32 modifiesWorld = True def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.livePreview = False self.blockPreview = False self.glPreview = True toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [ pluginClass(self) for pluginClass in GeneratePlugins.registeredPlugins ] self.currentGenerator = None if len(self.generatorTypes): self.currentGenerator = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() self.generatorTypesChanged() self.generatorTypeInput.currentIndexChanged.connect( self.currentTypeChanged) self.livePreviewCheckbox = QtGui.QCheckBox("Live Preview") self.livePreviewCheckbox.setChecked(self.livePreview) self.livePreviewCheckbox.toggled.connect(self.livePreviewToggled) self.blockPreviewCheckbox = QtGui.QCheckBox("Block Preview") self.blockPreviewCheckbox.setChecked(self.blockPreview) self.blockPreviewCheckbox.toggled.connect(self.blockPreviewToggled) self.glPreviewCheckbox = QtGui.QCheckBox("GL Preview") self.glPreviewCheckbox.setChecked(self.glPreview) self.glPreviewCheckbox.toggled.connect(self.glPreviewToggled) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.generateButton = QtGui.QPushButton(self.tr("Generate")) self.generateButton.clicked.connect(self.generateClicked) column.append(self.generatorTypeInput) column.append(self.livePreviewCheckbox) column.append(self.blockPreviewCheckbox) column.append(self.glPreviewCheckbox) column.append(self.optionsHolder) column.append(self.generateButton) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenenode.Node("generateOverlay") self.sceneHolderNode = Node("sceneHolder") self.sceneTranslate = Translate() self.sceneHolderNode.addState(self.sceneTranslate) self.overlayNode.addChild(self.sceneHolderNode) self.previewNode = None self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.loader = None self.previewBounds = None self.schematicBounds = None self.currentSchematic = None self.currentTypeChanged(0) # Name of last selected generator plugin is saved after unloading # so it can be reselected if it is immediately reloaded self._lastTypeName = None GeneratePlugins.pluginAdded.connect(self.addPlugin) GeneratePlugins.pluginRemoved.connect(self.removePlugin) def removePlugin(self, cls): log.info("Removing plugin %s", cls.__name__) self.generatorTypes[:] = [ gt for gt in self.generatorTypes if not isinstance(gt, cls) ] self.generatorTypesChanged() if self.currentGenerator not in self.generatorTypes: lastTypeName = self.currentGenerator.__class__.__name__ self.currentTypeChanged(0) # resets self._lastTypeName self._lastTypeName = lastTypeName def addPlugin(self, cls): log.info("Adding plugin %s", cls.__name__) self.generatorTypes.append(cls(self)) self.generatorTypesChanged() if self._lastTypeName is not None: if cls.__name__ == self._lastTypeName: self.currentTypeChanged(len(self.generatorTypes) - 1) def generatorTypesChanged(self): self.generatorTypeInput.clear() for gt in self.generatorTypes: if hasattr(gt, 'displayName'): displayName = gt.displayName else: displayName = gt.__class__.__name__ self.generatorTypeInput.addItem(displayName, gt) def livePreviewToggled(self, value): self.livePreview = value def blockPreviewToggled(self, value): self.blockPreview = value if value: if self.currentSchematic: self.displaySchematic(self.currentSchematic, self.schematicBounds.origin) else: self.updateBlockPreview() else: self.clearSchematic() def glPreviewToggled(self, value): self.glPreview = value if value: self.updateNodePreview() # xxx cache previewNode? else: self.clearNode() def currentTypeChanged(self, index): # user selected generator type after old type was unloaded, so forget the old type self._lastTypeName = None self.optionsHolder.removeWidget(self.optionsHolder.widget(0)) if index < len(self.generatorTypes): self.currentGenerator = self.generatorTypes[index] self.optionsHolder.addWidget( self.currentGenerator.getOptionsWidget()) self.updatePreview() else: self.currentGenerator = None self.clearSchematic() self.clearNode() log.info("Chose generator %s", repr(self.currentGenerator)) def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) def boundsDidChange(self, bounds): # box still being resized if not self.currentGenerator: return if not self.livePreview: return self.previewBounds = bounds self.currentGenerator.boundsChanged(bounds) self.updatePreview() def boundsDidChangeDone(self, bounds, oldBounds): # box finished resize if not self.currentGenerator: return self.previewBounds = bounds self.schematicBounds = bounds self.currentGenerator.boundsChanged(bounds) self.updatePreview() def clearNode(self): if self.previewNode: self.overlayNode.removeChild(self.previewNode) self.previewNode = None def updatePreview(self): if self.blockPreview: self.updateBlockPreview() else: self.clearSchematic() if self.glPreview: self.updateNodePreview() else: self.clearNode() def updateBlockPreview(self): bounds = self.previewBounds if bounds is not None and bounds.volume > 0: self.generateNextSchematic(bounds) else: self.clearSchematic() def updateNodePreview(self): bounds = self.previewBounds if self.currentGenerator is None: return if bounds is not None and bounds.volume > 0: try: node = self.currentGenerator.getPreviewNode(bounds) except Exception: log.exception( "Error while getting scene nodes from generator:") else: if node is not None: self.clearNode() if isinstance(node, list): nodes = node node = scenenode.Node("generatePreviewHolder") for c in nodes: node.addChild(c) self.overlayNode.addChild(node) self.previewNode = node def generateNextSchematic(self, bounds): if bounds is None: self.clearSchematic() return if self.currentGenerator is None: return try: schematic = self.currentGenerator.generate( bounds, self.editorSession.worldEditor.blocktypes) self.currentSchematic = schematic self.displaySchematic(schematic, bounds.origin) except Exception as e: log.exception("Error while running generator %s: %s", self.currentGenerator, e) QtGui.QMessageBox.warning( QtGui.qApp.mainWindow, "Error while running generator", "An error occurred while running the generator: \n %s.\n\n" "Traceback: %s" % (e, traceback.format_exc())) self.livePreview = False def displaySchematic(self, schematic, offset): if schematic is not None: dim = schematic.getDimension() if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.loader.timer.stop() self.loader = None atlas = self.editorSession.textureAtlas self.worldScene = WorldScene(dim, atlas) self.sceneTranslate.translateOffset = offset self.sceneHolderNode.addChild(self.worldScene) self.loader = WorldLoader(self.worldScene) if dim.chunkCount() <= self.instantDisplayChunks: exhaust(self.loader.work()) else: self.loader.startLoader() else: self.clearSchematic() def clearSchematic(self): if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.worldScene = None self.loader.timer.stop() self.loader = None def generateClicked(self): if self.currentGenerator is None: return if self.schematicBounds is None: log.info("schematicBounds is None, not generating") return if self.currentSchematic is None: log.info("Generating new schematic for import") currentSchematic = self.currentGenerator.generate( self.schematicBounds, self.editorSession.worldEditor.blocktypes) else: log.info("Importing previously generated schematic.") currentSchematic = self.currentSchematic command = GenerateCommand(self, self.schematicBounds) try: with command.begin(): if currentSchematic is not None: task = self.editorSession.currentDimension.importSchematicIter( currentSchematic, self.schematicBounds.origin) showProgress(self.tr("Importing generated object..."), task) else: task = self.currentGenerator.generateInWorld( self.schematicBounds, self.editorSession.currentDimension) showProgress(self.tr("Generating object in world..."), task) except Exception as e: log.exception("Error while importing or generating in world: %r" % e) command.undo() else: self.editorSession.pushCommand(command)
class SelectionTool(EditorTool): name = "Select" iconName = "select_blocks" def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.toolWidget.setLayout( Column(self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None editorSession.dimensionChanged.connect(self.dimensionDidChange) def dimensionDidChange(self): self.selectionNode.dimension = self.editorSession.currentDimension def shapeDidChange(self): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection( self.currentSelection) def toolActive(self): self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .5 def toolInactive(self): self.faceHoverNode.visible = False self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): return not self.selectionNode.filled @hideSelectionWalls.setter def hideSelectionWalls(self, value): self.selectionNode.filled = not value @property def currentSelection(self): return self.editorSession.currentSelection @currentSelection.setter def currentSelection(self, value): self.editorSession.currentSelection = value def coordInputChanged(self, box): self.currentSelection = self.createShapedSelection(box) def selectionDidChange(self, value): self.coordInput.boundingBox = value self.updateNodes() def updateNodes(self): box = self.currentSelection if box: self.boxHandleNode.bounds = BoundingBox(box.origin, box.size) self.selectionNode.visible = True self.selectionNode.selection = box else: self.boxHandleNode.bounds = None self.selectionNode.visible = False self.faceHoverNode.visible = False def boxHandleResized(self, box): if box is not None: self.selectionNode.selection = self.createShapedSelection(box) def boxHandleResizedDone(self, box, newSelection): if box is not None: selection = self.createShapedSelection(box) command = SelectCommand(self.editorSession, selection) if not newSelection: command.setText(self.tr("Resize Selection")) self.editorSession.undoStack.push(command) self.updateNodes() def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.mouseDrag(event) def mouseDrag(self, event): # Update cursor self.cursorNode.point = event.blockPosition self.cursorNode.face = event.blockFace self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) selectionColor = (0.8, 0.8, 1.0) alpha = 0.33 showPreviousSelection = True def createShapedSelection(self, box): if self.shapeInput.currentShape is shapewidget.Square: return box else: return selection.ShapedSelection( box, self.shapeInput.currentShape.shapeFunc)
class PendingImportNode(Node, QtCore.QObject): __node_id_counter = 0 def __init__(self, pendingImport, textureAtlas): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport dim = pendingImport.sourceDim # positionTranslateNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.positionTranslateNode = TranslateNode() self.rotateNode = Rotate3DNode() self.addChild(self.positionTranslateNode) self.positionTranslateNode.addChild(self.rotateNode) self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) # worldSceneTranslateNode is contained by positionTranslateNode, and # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode # can translate by the current position. self.worldSceneTranslateNode = TranslateNode() self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer # transformedWorldTranslateNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedWorldTranslateNode = TranslateNode() self.transformedWorldScene = None self.addChild(self.transformedWorldTranslateNode) self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin self.worldSceneTranslateNode.addChild(self.worldScene) self.rotateNode.addChild(self.worldSceneTranslateNode) # handleNode displays a bounding box that can be moved around, and responds # to mouse events. box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.updateTransformedScene() self.basePosition = pendingImport.basePosition self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader(self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader() self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) # Emitted when the user finishes dragging the box handle and releases the mouse # button. Arguments are (newPosition, oldPosition). importMoved = QtCore.Signal(object, object) def handleBoundsChangedDone(self, bounds, oldBounds): point = self.getBaseFromTransformed(bounds.origin) oldPoint = self.getBaseFromTransformed(oldBounds.origin) if point != oldPoint: self.importMoved.emit(point, oldPoint) def handleBoundsChanged(self, bounds): point = self.getBaseFromTransformed(bounds.origin) if self.basePosition != point: self.basePosition = point def getBaseFromTransformed(self, point): offset = self.pendingImport.basePosition - self.pendingImport.importPos return point + offset def setPreviewRotation(self, rots): self.rotateNode.visible = True self.worldSceneTranslateNode.visible = True self.transformedWorldTranslateNode.visible = False self.rotateNode.setRotation(rots) def setRotation(self, rots): self.updateTransformedScene() self.updateBoxHandle() self.rotateNode.setRotation(rots) def updateTransformedScene(self): if self.pendingImport.transformedDim is not None: log.info("Showing transformed scene") self.rotateNode.visible = False self.worldSceneTranslateNode.visible = False self.transformedWorldTranslateNode.visible = True if self.transformedWorldScene: self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) self.transformedWorldScene = WorldScene(self.pendingImport.transformedDim, self.textureAtlas) self.transformedWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer self.transformedWorldTranslateNode.addChild(self.transformedWorldScene) self.updateTransformedSceneOffset() cPos = list(self.pendingImport.transformedDim.chunkPositions()) self.loader = WorldLoader(self.transformedWorldScene, cPos) self.loader.startLoader() else: log.info("Hiding transformed scene") self.rotateNode.visible = True self.worldSceneTranslateNode.visible = True self.transformedWorldTranslateNode.visible = False if self.transformedWorldScene: self.transformedWorldTranslateNode.removeChild(self.transformedWorldScene) self.transformedWorldScene = None def updateTransformedSceneOffset(self): self.transformedWorldTranslateNode.translateOffset = self.transformedPosition - self.pendingImport.importDim.bounds.origin @property def transformedPosition(self): return self.basePosition + self.pendingImport.importPos - self.pendingImport.basePosition @property def basePosition(self): return self.positionTranslateNode.translateOffset @basePosition.setter def basePosition(self, value): if value == self.positionTranslateNode.translateOffset: return self.positionTranslateNode.translateOffset = Vector(*value) self.updateTransformedSceneOffset() self.updateBoxHandle() def setPosition(self, pos): self.basePosition = pos def updateBoxHandle(self): if self.transformedWorldScene is None: bounds = BoundingBox(self.basePosition, self.pendingImport.bounds.size) else: origin = self.transformedPosition bounds = BoundingBox(origin, self.pendingImport.importBounds.size) #if self.handleNode.bounds.size != bounds.size: self.handleNode.bounds = bounds # --- Mouse events --- # inherit from BoxHandle? def mouseMove(self, event): self.handleNode.mouseMove(event) def mousePress(self, event): self.handleNode.mousePress(event) def mouseRelease(self, event): self.handleNode.mouseRelease(event)
class SelectionTool(EditorTool): name = "Select" iconName = "select_blocks" def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.optionsButton = QtGui.QPushButton(self.tr("Options")) self.optionsMenu = QtGui.QMenu() self.optionsButton.setMenu(self.optionsMenu) classicSelectionAction = self.optionsMenu.addAction(self.tr("Classic Selection")) stickySelectionAction = self.optionsMenu.addAction(self.tr("Sticky Selection")) self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget(addShapes=[ChunkShape()]) self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.shapeInput.shapeOptionsChanged.connect(self.shapeDidChange) self.toolWidget.setLayout(Column(self.optionsButton, self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenenode.Node("selectOverlay") self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None classicSelectionAction.setCheckable(True) classicSelectionAction.toggled.connect(ClassicSelectionOption.setValue) classicSelectionAction.setChecked(ClassicSelectionOption.value()) ClassicSelectionOption.connectAndCall(self.setClassicSelection) stickySelectionAction.setCheckable(True) stickySelectionAction.toggled.connect(StickySelectionOption.setValue) stickySelectionAction.setChecked(StickySelectionOption.value()) StickySelectionOption.connectAndCall(self.setStickySelection) editorSession.dimensionChanged.connect(self.dimensionDidChange) editorSession.revisionChanged.connect(self.revisionDidChange) def revisionDidChange(self, changes): chunks = changes.chunks[self.editorSession.currentDimension.dimName] for cx, cz in chunks: self.selectionNode.discardChunk(cx, cz) def dimensionDidChange(self): self.selectionNode.dimension = self.editorSession.currentDimension def shapeDidChange(self): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection(self.currentSelection) def toolActive(self): self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .5 def toolInactive(self): self.faceHoverNode.visible = False self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): return not self.selectionNode.filled @hideSelectionWalls.setter def hideSelectionWalls(self, value): self.selectionNode.filled = not value @property def currentSelection(self): return self.editorSession.currentSelection @currentSelection.setter def currentSelection(self, value): self.editorSession.currentSelection = value def coordInputChanged(self, box): self.currentSelection = self.createShapedSelection(box) def selectionDidChange(self, value): self.coordInput.boundingBox = value self.updateNodes() def updateNodes(self): box = self.currentSelection if box: self.boxHandleNode.bounds = BoundingBox(box.origin, box.size) self.selectionNode.visible = True self.selectionNode.selection = box else: self.boxHandleNode.bounds = None self.selectionNode.visible = False self.faceHoverNode.visible = False lastResizeTime = time.time() def boxHandleResized(self, box): if box is not None: self.selectionNode.selection = self.createShapedSelection(box) if time.time() - self.lastResizeTime > 0.025: self.selectionNode.loadImmediateChunks(0.025) self.lastResizeTime = time.time() def boxHandleResizedDone(self, box, oldBox): if box is not None: selection = self.createShapedSelection(box) command = SelectCommand(self.editorSession, selection) if oldBox is not None: command.setText(self.tr("Resize Selection")) self.editorSession.undoStack.push(command) self.updateNodes() self.selectionNode.loadImmediateChunks() def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.mouseDrag(event) def mouseDrag(self, event): # Update cursor self.cursorNode.point = event.blockPosition self.cursorNode.face = event.blockFace self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) selectionColor = (0.8, 0.8, 1.0) alpha = 0.33 showPreviousSelection = True def createShapedSelection(self, box): return self.shapeInput.currentShape.createShapedSelection(box, self.editorSession.currentDimension) def setClassicSelection(self, enabled): self.boxHandleNode.classicSelection = enabled def setStickySelection(self, enabled): self.boxHandleNode.stickySelection = enabled
def __init__(self, pendingImport, textureAtlas, hasHandle=True): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. hasHandle: bool True if this import node should have a user-interactive BoxHandle associated with it. This is False for the extra copies displayed by a repeated clone. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport self.hasHandle = hasHandle dim = pendingImport.sourceDim self.transformedPosition = Vector(0, 0, 0) # worldScene is contained by rotateNode, and # translates the world scene back to 0, 0, 0 so the rotateNode can # rotate it around the anchor, and the plainSceneNode can translate # it to the current position. self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.worldSceneTranslate = Translate( -self.pendingImport.selection.origin) self.worldScene.addState(self.worldSceneTranslate) # rotateNode is used to rotate the non-transformed preview during live rotation self.rotateNode = Rotate3DNode() self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) self.rotateNode.addChild(self.worldScene) self.scaleNode = Scale3DNode() self.scaleNode.setAnchor(self.pendingImport.selection.size * 0.5) self.scaleNode.addChild(self.rotateNode) # plainSceneNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.plainSceneNode = Node("plainScene") self.positionTranslate = Translate() self.plainSceneNode.addState(self.positionTranslate) self.plainSceneNode.addChild(self.scaleNode) self.addChild(self.plainSceneNode) # transformedSceneNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedSceneNode = Node("transformedScene") self.transformedSceneTranslate = Translate() self.transformedSceneNode.addState(self.transformedSceneTranslate) self.transformedWorldScene = None self.addChild(self.transformedSceneNode) box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) if hasHandle: # handleNode displays a bounding box that can be moved around, and responds # to mouse events. self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.boxNode = None else: # boxNode displays a plain, non-movable bounding box self.boxNode = SelectionBoxNode() self.boxNode.wireColor = (1, 1, 1, .2) self.boxNode.filled = False self.handleNode = None self.addChild(self.boxNode) self.updateTransformedScene() self.basePosition = pendingImport.basePosition if hasHandle: self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect( self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader( self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader(0.1 if self.hasHandle else 0.0) self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) self.pendingImport.scaleChanged.connect(self.setScale)
class SelectionTool(EditorTool): name = "Select" iconName = "select_blocks" def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.deselectButton = QtGui.QPushButton(self.tr("Deselect")) self.deselectButton.clicked.connect(self.deselect) self.deleteSelectionButton = QtGui.QPushButton(self.tr("Delete")) self.deleteSelectionButton.clicked.connect(self.deleteSelection) self.deleteBlocksButton = QtGui.QPushButton(self.tr("Delete Blocks")) self.deleteBlocksButton.clicked.connect(self.deleteBlocks) self.deleteEntitiesButton = QtGui.QPushButton(self.tr("Delete Entities")) self.deleteEntitiesButton.clicked.connect(self.deleteEntities) self.fillButton = QtGui.QPushButton(self.tr("Fill")) self.fillButton.clicked.connect(self.fill) self.replaceButton = QtGui.QPushButton(self.tr("Replace")) self.replaceButton.clicked.connect(self.replace) self.toolWidget.setLayout(Column(self.coordInput, self.shapeInput, self.deselectButton, self.deleteSelectionButton, self.deleteBlocksButton, self.deleteEntitiesButton, self.fillButton, self.replaceButton, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.selectionNode.dimension = editorSession.currentDimension # xxx dimensionDidChange self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None def shapeDidChange(self): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection(self.currentSelection) def toolActive(self): self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .5 def toolInactive(self): self.faceHoverNode.visible = False self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): return not self.selectionNode.filled @hideSelectionWalls.setter def hideSelectionWalls(self, value): self.selectionNode.filled = not value @property def currentSelection(self): return self.editorSession.currentSelection @currentSelection.setter def currentSelection(self, value): self.editorSession.currentSelection = value self.boxHandleNode.bounds = None if value is None else BoundingBox(value.origin, value.size) def coordInputChanged(self, box): self.currentSelection = self.createShapedSelection(box) def selectionDidChange(self, value): self.coordInput.boundingBox = value self.updateNodes() def updateNodes(self): box = self.currentSelection if box: self.selectionNode.visible = True self.selectionNode.selection = box else: self.selectionNode.visible = False self.faceHoverNode.visible = False def deleteSelection(self): command = SimpleRevisionCommand(self.editorSession, "Delete") with command.begin(): fillTask = self.editorSession.currentDimension.fillBlocksIter(self.editorSession.currentSelection, "air") entitiesTask = RemoveEntitiesOperation(self.editorSession.currentDimension, self.editorSession.currentSelection) task = ComposeOperations(fillTask, entitiesTask) showProgress("Deleting...", task) self.editorSession.pushCommand(command) def deleteBlocks(self): pass def deleteEntities(self): pass def fill(self): fillCommand(self.editorSession) def replace(self): replaceCommand(self.editorSession) def boxHandleResized(self, box): if box is not None: self.selectionNode.selection = self.createShapedSelection(box) def boxHandleResizedDone(self, box, newSelection): if box is not None: selection = self.createShapedSelection(box) command = SelectCommand(self, selection) if not newSelection: command.setText(self.tr("Resize Selection")) self.editorSession.undoStack.push(command) self.updateNodes() def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.mouseDrag(event) def mouseDrag(self, event): # Update cursor self.cursorNode.point = event.blockPosition self.cursorNode.face = event.blockFace self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) def deselect(self): editor = self.editorSession command = SelectCommand(self, None) command.setText(self.tr("Deselect")) editor.undoStack.push(command) selectionColor = (0.8, 0.8, 1.0) alpha = 0.33 showPreviousSelection = True def createShapedSelection(self, box): if self.shapeInput.currentShape is shapewidget.Square: return box else: return selection.ShapedSelection(box, self.shapeInput.currentShape.shapeFunc)
class SelectionTool(EditorTool): name = "Select" iconName = "select_blocks" def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.optionsButton = QtGui.QPushButton(self.tr("Options")) self.optionsMenu = QtGui.QMenu() self.optionsButton.setMenu(self.optionsMenu) classicSelectionAction = self.optionsMenu.addAction( self.tr("Classic Selection")) stickySelectionAction = self.optionsMenu.addAction( self.tr("Sticky Selection")) self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget(addShapes=[ChunkShape()]) self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.shapeInput.shapeOptionsChanged.connect(self.shapeDidChange) self.toolWidget.setLayout( Column(self.optionsButton, self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenenode.Node("selectOverlay") self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None classicSelectionAction.setCheckable(True) classicSelectionAction.toggled.connect(ClassicSelectionOption.setValue) classicSelectionAction.setChecked(ClassicSelectionOption.value()) ClassicSelectionOption.connectAndCall(self.setClassicSelection) stickySelectionAction.setCheckable(True) stickySelectionAction.toggled.connect(StickySelectionOption.setValue) stickySelectionAction.setChecked(StickySelectionOption.value()) StickySelectionOption.connectAndCall(self.setStickySelection) editorSession.dimensionChanged.connect(self.dimensionDidChange) editorSession.revisionChanged.connect(self.revisionDidChange) def revisionDidChange(self, changes): chunks = changes.chunks[self.editorSession.currentDimension.dimName] for cx, cz in chunks: self.selectionNode.discardChunk(cx, cz) def dimensionDidChange(self): self.selectionNode.dimension = self.editorSession.currentDimension def shapeDidChange(self): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection( self.currentSelection) def toolActive(self): self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .5 def toolInactive(self): self.faceHoverNode.visible = False self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): return not self.selectionNode.filled @hideSelectionWalls.setter def hideSelectionWalls(self, value): self.selectionNode.filled = not value @property def currentSelection(self): return self.editorSession.currentSelection @currentSelection.setter def currentSelection(self, value): self.editorSession.currentSelection = value def coordInputChanged(self, box): self.currentSelection = self.createShapedSelection(box) def selectionDidChange(self, value): self.coordInput.boundingBox = value self.updateNodes() def updateNodes(self): box = self.currentSelection if box: self.boxHandleNode.bounds = BoundingBox(box.origin, box.size) self.selectionNode.visible = True self.selectionNode.selection = box else: self.boxHandleNode.bounds = None self.selectionNode.visible = False self.faceHoverNode.visible = False self.selectionNode.selection = None lastResizeTime = time.time() def boxHandleResized(self, box): if box is not None: self.selectionNode.selection = self.createShapedSelection(box) if time.time() - self.lastResizeTime > 0.025: self.selectionNode.loadImmediateChunks(0.025) self.lastResizeTime = time.time() else: self.selectionNode.selection = None def boxHandleResizedDone(self, box, oldBox): if box is not None: selection = self.createShapedSelection(box) command = SelectCommand(self.editorSession, selection) if oldBox is not None: command.setText(self.tr("Resize Selection")) self.editorSession.pushCommand(command) self.updateNodes() self.selectionNode.loadImmediateChunks() else: self.selectionNode.selection = None def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.mouseDrag(event) def mouseDrag(self, event): # Update cursor self.cursorNode.point = event.blockPosition self.cursorNode.face = event.blockFace self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) selectionColor = (0.8, 0.8, 1.0) alpha = 0.33 showPreviousSelection = True def createShapedSelection(self, box): return self.shapeInput.currentShape.createShapedSelection( box, self.editorSession.currentDimension) def setClassicSelection(self, enabled): self.boxHandleNode.classicSelection = enabled def setStickySelection(self, enabled): self.boxHandleNode.stickySelection = enabled
def __init__(self, pendingImport, textureAtlas): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport dim = pendingImport.sourceDim # positionTranslateNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.positionTranslateNode = TranslateNode() self.rotateNode = Rotate3DNode() self.addChild(self.positionTranslateNode) self.positionTranslateNode.addChild(self.rotateNode) self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) # worldSceneTranslateNode is contained by positionTranslateNode, and # serves to translate the world scene back to 0, 0, 0 so the positionTranslateNode # can translate by the current position. self.worldSceneTranslateNode = TranslateNode() self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer # transformedWorldTranslateNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedWorldTranslateNode = TranslateNode() self.transformedWorldScene = None self.addChild(self.transformedWorldTranslateNode) self.worldSceneTranslateNode.translateOffset = -self.pendingImport.selection.origin self.worldSceneTranslateNode.addChild(self.worldScene) self.rotateNode.addChild(self.worldSceneTranslateNode) # handleNode displays a bounding box that can be moved around, and responds # to mouse events. box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.updateTransformedScene() self.basePosition = pendingImport.basePosition self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader(self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader() self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation)
class PendingImportNode(Node, QtCore.QObject): __node_id_counter = 0 def __init__(self, pendingImport, textureAtlas, hasHandle=True): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. hasHandle: bool True if this import node should have a user-interactive BoxHandle associated with it. This is False for the extra copies displayed by a repeated clone. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport self.hasHandle = hasHandle dim = pendingImport.sourceDim self.transformedPosition = Vector(0, 0, 0) # worldScene is contained by rotateNode, and # translates the world scene back to 0, 0, 0 so the rotateNode can # rotate it around the anchor, and the plainSceneNode can translate # it to the current position. self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.worldSceneTranslate = Translate(-self.pendingImport.selection.origin) self.worldScene.addState(self.worldSceneTranslate) # rotateNode is used to rotate the non-transformed preview during live rotation self.rotateNode = Rotate3DNode() self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) self.rotateNode.addChild(self.worldScene) self.scaleNode = Scale3DNode() self.scaleNode.setAnchor(self.pendingImport.selection.size * 0.5) self.scaleNode.addChild(self.rotateNode) # plainSceneNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.plainSceneNode = Node("plainScene") self.positionTranslate = Translate() self.plainSceneNode.addState(self.positionTranslate) self.plainSceneNode.addChild(self.scaleNode) self.addChild(self.plainSceneNode) # transformedSceneNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedSceneNode = Node("transformedScene") self.transformedSceneTranslate = Translate() self.transformedSceneNode.addState(self.transformedSceneTranslate) self.transformedWorldScene = None self.addChild(self.transformedSceneNode) box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) if hasHandle: # handleNode displays a bounding box that can be moved around, and responds # to mouse events. self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.boxNode = None else: # boxNode displays a plain, non-movable bounding box self.boxNode = SelectionBoxNode() self.boxNode.wireColor = (1, 1, 1, .2) self.boxNode.filled = False self.handleNode = None self.addChild(self.boxNode) self.updateTransformedScene() self.basePosition = pendingImport.basePosition if hasHandle: self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader(self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader(0.1 if self.hasHandle else 0.0) self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) self.pendingImport.scaleChanged.connect(self.setScale) # Emitted when the user finishes dragging the box handle and releases the mouse # button. Arguments are (newPosition, oldPosition). importMoved = QtCore.Signal(object, object) # Emitted while the user is dragging the box handle. Argument is the box origin. importIsMoving = QtCore.Signal(object) def handleBoundsChangedDone(self, bounds, oldBounds): point = self.getBaseFromTransformed(bounds.origin) oldPoint = self.getBaseFromTransformed(oldBounds.origin) if point != oldPoint: self.importMoved.emit(point, oldPoint) def handleBoundsChanged(self, bounds): log.info("handleBoundsChanged: %s", bounds) self.setPreviewBasePosition(bounds.origin) def setPreviewBasePosition(self, origin): point = self.getBaseFromTransformed(origin) if self.basePosition != point: self.basePosition = point self.importIsMoving.emit(point) def getBaseFromTransformed(self, point): return point - self.pendingImport.transformOffset def setPreviewRotation(self, rots): self.plainSceneNode.visible = True self.transformedSceneNode.visible = False self.rotateNode.setRotation(rots) def setRotation(self, rots): self.updateTransformedScene() self.updateBoxHandle() self.rotateNode.setRotation(rots) def setPreviewScale(self, scale): self.plainSceneNode.visible = True self.transformedSceneNode.visible = False self.scaleNode.setScale(scale) def setScale(self, scale): self.updateTransformedScene() self.updateBoxHandle() self.scaleNode.setScale(scale) def updateTransformedScene(self): if self.pendingImport.transformedDim is not None: log.info("Showing transformed scene") self.plainSceneNode.visible = False self.transformedSceneNode.visible = True if self.transformedWorldScene: self.transformedSceneNode.removeChild(self.transformedWorldScene) self.transformedWorldScene = WorldScene(self.pendingImport.transformedDim, self.textureAtlas) self.transformedWorldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.transformedSceneNode.addChild(self.transformedWorldScene) self.updateTransformedPosition() cPos = list(self.pendingImport.transformedDim.chunkPositions()) self.loader = WorldLoader(self.transformedWorldScene, cPos) # ALERT!: self.hasHandle is overloaded with the meaning: # "not the first clone in a repeated clone" self.loader.startLoader(0.1 if self.hasHandle else 0.0) else: log.info("Hiding transformed scene") self.plainSceneNode.visible = True self.transformedSceneNode.visible = False if self.transformedWorldScene: self.transformedSceneNode.removeChild(self.transformedWorldScene) self.transformedWorldScene = None def updateTransformedPosition(self): self.transformedPosition = self.basePosition + self.pendingImport.transformOffset self.transformedSceneTranslate.translateOffset = self.transformedPosition - self.pendingImport.importDim.bounds.origin def updateBoxHandle(self): if self.transformedWorldScene is None: bounds = BoundingBox(self.basePosition, self.pendingImport.bounds.size) else: origin = self.transformedPosition bounds = BoundingBox(origin, self.pendingImport.importBounds.size) #if self.handleNode.bounds.size != bounds.size: if self.hasHandle: self.handleNode.bounds = bounds else: self.boxNode.selectionBox = bounds @property def basePosition(self): return self.positionTranslate.translateOffset @basePosition.setter def basePosition(self, value): value = Vector(*value) if value == self.positionTranslate.translateOffset: return self.positionTranslate.translateOffset = value self.updateTransformedPosition() self.updateBoxHandle() def setPosition(self, pos): self.basePosition = pos # --- Mouse events --- # inherit from BoxHandle? def mouseMove(self, event): if not self.hasHandle: return self.handleNode.mouseMove(event) def mousePress(self, event): if not self.hasHandle: return self.handleNode.mousePress(event) def mouseRelease(self, event): if not self.hasHandle: return self.handleNode.mouseRelease(event)
def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.livePreview = False self.blockPreview = False self.glPreview = True toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [ pluginClass(self) for pluginClass in GeneratePlugins.registeredPlugins ] self.currentGenerator = None if len(self.generatorTypes): self.currentGenerator = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() self.generatorTypesChanged() self.generatorTypeInput.currentIndexChanged.connect( self.currentTypeChanged) self.livePreviewCheckbox = QtGui.QCheckBox("Live Preview") self.livePreviewCheckbox.setChecked(self.livePreview) self.livePreviewCheckbox.toggled.connect(self.livePreviewToggled) self.blockPreviewCheckbox = QtGui.QCheckBox("Block Preview") self.blockPreviewCheckbox.setChecked(self.blockPreview) self.blockPreviewCheckbox.toggled.connect(self.blockPreviewToggled) self.glPreviewCheckbox = QtGui.QCheckBox("GL Preview") self.glPreviewCheckbox.setChecked(self.glPreview) self.glPreviewCheckbox.toggled.connect(self.glPreviewToggled) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) self.generateButton = QtGui.QPushButton(self.tr("Generate")) self.generateButton.clicked.connect(self.generateClicked) column.append(self.generatorTypeInput) column.append(self.livePreviewCheckbox) column.append(self.blockPreviewCheckbox) column.append(self.glPreviewCheckbox) column.append(self.optionsHolder) column.append(self.generateButton) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenenode.Node("generateOverlay") self.sceneHolderNode = Node("sceneHolder") self.sceneTranslate = Translate() self.sceneHolderNode.addState(self.sceneTranslate) self.overlayNode.addChild(self.sceneHolderNode) self.previewNode = None self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.loader = None self.previewBounds = None self.schematicBounds = None self.currentSchematic = None self.currentTypeChanged(0) # Name of last selected generator plugin is saved after unloading # so it can be reselected if it is immediately reloaded self._lastTypeName = None GeneratePlugins.pluginAdded.connect(self.addPlugin) GeneratePlugins.pluginRemoved.connect(self.removePlugin)
def __init__(self, pendingImport, textureAtlas, hasHandle=True): """ A scenegraph node displaying an object that will be imported later, including live and deferred views of the object with transformed items, and a BoxHandle for moving the item. Parameters ---------- pendingImport: PendingImport The object to be imported. The PendingImportNode responds to changes in this object's position, rotation, and scale. textureAtlas: TextureAtlas The textures and block models used to render the preview of the object. hasHandle: bool True if this import node should have a user-interactive BoxHandle associated with it. This is False for the extra copies displayed by a repeated clone. Attributes ---------- basePosition: Vector The pre-transform position of the pending import. This is equal to `self.pendingImport.basePosition` except when the node is currently being dragged. transformedPosition: Vector The post-transform position of the pending import. This is equal to `self.pendingImport.importPos` except when the node is currently being dragged, scaled, or rotated. """ super(PendingImportNode, self).__init__() self.textureAtlas = textureAtlas self.pendingImport = pendingImport self.hasHandle = hasHandle dim = pendingImport.sourceDim self.transformedPosition = Vector(0, 0, 0) # worldScene is contained by rotateNode, and # translates the world scene back to 0, 0, 0 so the rotateNode can # rotate it around the anchor, and the plainSceneNode can translate # it to the current position. self.worldScene = WorldScene(dim, textureAtlas, bounds=pendingImport.selection) self.worldScene.depthOffset.depthOffset = DepthOffsets.PreviewRenderer self.worldSceneTranslate = Translate(-self.pendingImport.selection.origin) self.worldScene.addState(self.worldSceneTranslate) # rotateNode is used to rotate the non-transformed preview during live rotation self.rotateNode = Rotate3DNode() self.rotateNode.setAnchor(self.pendingImport.selection.size * 0.5) self.rotateNode.addChild(self.worldScene) self.scaleNode = Scale3DNode() self.scaleNode.setAnchor(self.pendingImport.selection.size * 0.5) self.scaleNode.addChild(self.rotateNode) # plainSceneNode contains the non-transformed preview of the imported # object, including its world scene. This preview will be rotated model-wise # while the user is dragging the rotate controls. self.plainSceneNode = Node("plainScene") self.positionTranslate = Translate() self.plainSceneNode.addState(self.positionTranslate) self.plainSceneNode.addChild(self.scaleNode) self.addChild(self.plainSceneNode) # transformedSceneNode contains the transformed preview of the imported # object, including a world scene that displays the object wrapped by a # DimensionTransform. self.transformedSceneNode = Node("transformedScene") self.transformedSceneTranslate = Translate() self.transformedSceneNode.addState(self.transformedSceneTranslate) self.transformedWorldScene = None self.addChild(self.transformedSceneNode) box = BoundingBox(pendingImport.importPos, pendingImport.importBounds.size) if hasHandle: # handleNode displays a bounding box that can be moved around, and responds # to mouse events. self.handleNode = BoxHandle() self.handleNode.bounds = box self.handleNode.resizable = False self.boxNode = None else: # boxNode displays a plain, non-movable bounding box self.boxNode = SelectionBoxNode() self.boxNode.wireColor = (1, 1, 1, .2) self.boxNode.filled = False self.handleNode = None self.addChild(self.boxNode) self.updateTransformedScene() self.basePosition = pendingImport.basePosition if hasHandle: self.handleNode.boundsChanged.connect(self.handleBoundsChanged) self.handleNode.boundsChangedDone.connect(self.handleBoundsChangedDone) self.addChild(self.handleNode) # loads the non-transformed world scene asynchronously. self.loader = WorldLoader(self.worldScene, list(pendingImport.selection.chunkPositions())) self.loader.startLoader(0.1 if self.hasHandle else 0.0) self.pendingImport.positionChanged.connect(self.setPosition) self.pendingImport.rotationChanged.connect(self.setRotation) self.pendingImport.scaleChanged.connect(self.setScale)
class SelectionTool(EditorTool): name = "Select" iconName = "select_blocks" def __init__(self, editorSession, *args, **kwargs): """ :type editorSession: EditorSession """ super(SelectionTool, self).__init__(editorSession, *args, **kwargs) toolWidget = QtGui.QWidget() editorSession.selectionChanged.connect(self.selectionDidChange) self.toolWidget = toolWidget self.coordInput = SelectionCoordinateWidget() self.coordInput.boxChanged.connect(self.coordInputChanged) self.shapeInput = ShapeWidget() self.shapeInput.shapeChanged.connect(self.shapeDidChange) self.toolWidget.setLayout(Column(self.coordInput, self.shapeInput, None)) self.cursorNode = SelectionCursor() self.overlayNode = scenegraph.Node() self.faceHoverNode = SelectionFaceNode() self.selectionNode = SelectionScene() self.overlayNode.addChild(self.selectionNode) self.overlayNode.addChild(self.faceHoverNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boxHandleResized) self.boxHandleNode.boundsChangedDone.connect(self.boxHandleResizedDone) self.overlayNode.addChild(self.boxHandleNode) self.newSelectionNode = None editorSession.dimensionChanged.connect(self.dimensionDidChange) def dimensionDidChange(self): self.selectionNode.dimension = self.editorSession.currentDimension def shapeDidChange(self): if self.currentSelection is not None: self.currentSelection = self.createShapedSelection(self.currentSelection) def toolActive(self): self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .5 def toolInactive(self): self.faceHoverNode.visible = False self.boxHandleNode.boxNode.wireColor = 1, 1, 1, .33 @property def hideSelectionWalls(self): return not self.selectionNode.filled @hideSelectionWalls.setter def hideSelectionWalls(self, value): self.selectionNode.filled = not value @property def currentSelection(self): return self.editorSession.currentSelection @currentSelection.setter def currentSelection(self, value): self.editorSession.currentSelection = value def coordInputChanged(self, box): self.currentSelection = self.createShapedSelection(box) def selectionDidChange(self, value): self.coordInput.boundingBox = value self.updateNodes() def updateNodes(self): box = self.currentSelection if box: self.boxHandleNode.bounds = BoundingBox(box.origin, box.size) self.selectionNode.visible = True self.selectionNode.selection = box else: self.boxHandleNode.bounds = None self.selectionNode.visible = False self.faceHoverNode.visible = False def boxHandleResized(self, box): if box is not None: self.selectionNode.selection = self.createShapedSelection(box) def boxHandleResizedDone(self, box, newSelection): if box is not None: selection = self.createShapedSelection(box) command = SelectCommand(self.editorSession, selection) if not newSelection: command.setText(self.tr("Resize Selection")) self.editorSession.undoStack.push(command) self.updateNodes() def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.mouseDrag(event) def mouseDrag(self, event): # Update cursor self.cursorNode.point = event.blockPosition self.cursorNode.face = event.blockFace self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) selectionColor = (0.8, 0.8, 1.0) alpha = 0.33 showPreviousSelection = True def createShapedSelection(self, box): if self.shapeInput.currentShape is shapewidget.Square: return box else: return selection.ShapedSelection(box, self.shapeInput.currentShape.shapeFunc)
class GenerateTool(EditorTool): name = "Generate" iconName = "generate" instantDisplayChunks = 32 def __init__(self, *args, **kwargs): EditorTool.__init__(self, *args, **kwargs) self.createToolWidget() def createToolWidget(self): toolWidget = QtGui.QWidget() self.toolWidget = toolWidget column = [] self.generatorTypes = [TreeGen()] self.currentType = self.generatorTypes[0] self.generatorTypeInput = QtGui.QComboBox() for gt in self.generatorTypes: self.generatorTypeInput.addItem(gt.displayName, gt) self.generatorTypeInput.currentIndexChanged.connect( self.generatorTypeChanged) self.optionsHolder = QtGui.QStackedWidget() self.optionsHolder.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding) column.append(self.generatorTypeInput) column.append(self.optionsHolder) self.toolWidget.setLayout(Column(*column)) self.overlayNode = scenegraph.Node() self.sceneHolderNode = scenegraph.TranslateNode() self.overlayNode.addChild(self.sceneHolderNode) self.boxHandleNode = BoxHandle() self.boxHandleNode.boundsChanged.connect(self.boundsDidChange) self.boxHandleNode.boundsChangedDone.connect(self.boundsDidChangeDone) self.overlayNode.addChild(self.boxHandleNode) self.worldScene = None self.schematicBounds = None def generatorTypeChanged(self, index): self.currentType = self.generatorTypes[index] self.optionsHolder.removeWidget(self.optionsHolder.widget(0)) self.optionsHolder.addWidget(self.currentType.getOptionsWidget()) def mousePress(self, event): self.boxHandleNode.mousePress(event) def mouseMove(self, event): self.boxHandleNode.mouseMove(event) def mouseRelease(self, event): self.boxHandleNode.mouseRelease(event) def boundsDidChange(self, bounds): if bounds is not None and bounds.volume: node = self.currentType.getPreviewNode() if node is not None: pass else: if self.schematicBounds is None or self.schematicBounds.size != bounds.size: schematic = self.currentType.generatePreview( bounds, self.editorSession.worldEditor.blocktypes) self.displaySchematic(schematic, bounds.origin) else: self.sceneHolderNode.translateOffset = bounds.origin self.schematicBounds = bounds def boundsDidChangeDone(self, bounds, newSelection): if bounds is not None and bounds.volume: if self.schematicBounds is None or self.schematicBounds.size != bounds.size: schematic = self.currentType.generate( bounds, self.editorSession.worldEditor.blocktypes) offset = bounds.origin self.displaySchematic(schematic, offset) else: self.sceneHolderNode.translateOffset = bounds.origin else: self.displaySchematic(None, None) self.schematicBounds = bounds def displaySchematic(self, schematic, offset): if schematic is not None: dim = schematic.getDimension() if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.loader.timer.stop() self.loader = None atlas = self.editorSession.textureAtlas self.worldScene = WorldScene(dim, atlas) self.sceneHolderNode.translateOffset = offset self.sceneHolderNode.addChild(self.worldScene) self.loader = WorldLoader(self.worldScene) if dim.chunkCount() <= self.instantDisplayChunks: for _ in self.loader.work(): pass else: self.loader.timer.start() else: if self.worldScene: self.sceneHolderNode.removeChild(self.worldScene) self.worldScene = None self.loader.timer.stop() self.loader = None