Beispiel #1
0
    def __init__(self, editorSession, *args, **kwargs):
        super(BrushTool, self).__init__(editorSession, *args, **kwargs)
        self.toolWidget = load_ui("editortools/brush.ui")
        self.brushMode = None
        self.brushLoader = None

        self.brushModesByName = {cls.name: cls() for cls in BrushModeClasses}
        modes = self.brushModesByName.values()
        modes.sort(key=lambda m: m.name)
        self.toolWidget.brushModeInput.setModes(modes)
        BrushModeSetting.connectAndCall(self.modeSettingChanged)

        self.cursorWorldScene = None
        self.cursorNode = TranslateNode()

        self.toolWidget.xSpinSlider.setMinimum(1)
        self.toolWidget.ySpinSlider.setMinimum(1)
        self.toolWidget.zSpinSlider.setMinimum(1)

        self.toolWidget.xSpinSlider.valueChanged.connect(self.setX)
        self.toolWidget.ySpinSlider.valueChanged.connect(self.setY)
        self.toolWidget.zSpinSlider.valueChanged.connect(self.setZ)

        self.toolWidget.brushShapeInput.shapeChanged.connect(self.updateCursor)
        self.toolWidget.brushShapeInput.shapeOptionsChanged.connect(
            self.updateCursor)

        self.fillBlock = editorSession.worldEditor.blocktypes["stone"]

        self.brushSize = BrushSizeSetting.value(QtGui.QVector3D(
            5, 5, 5)).toTuple()  # calls updateCursor

        self.toolWidget.xSpinSlider.setValue(self.brushSize[0])
        self.toolWidget.ySpinSlider.setValue(self.brushSize[1])
        self.toolWidget.zSpinSlider.setValue(self.brushSize[2])
Beispiel #2
0
def entityModelNode(ref, model, modelTex, chunk):
    modelVerts = numpy.array(model.vertices)
    modelVerts.shape = modelVerts.shape[0] // 4, 4, modelVerts.shape[1]
    # scale down
    modelVerts[..., :3] *= 1 / 16.
    modelVerts[..., 1] = -modelVerts[..., 1] + 1.5 + 1 / 64.
    modelVerts[..., 0] = -modelVerts[..., 0]

    vertexBuffer = QuadVertexArrayBuffer(len(modelVerts),
                                         lights=False,
                                         textures=True)
    vertexBuffer.vertex[:] = modelVerts[..., :3]
    vertexBuffer.texcoord[:] = modelVerts[..., 3:5]

    node = VertexNode([vertexBuffer])
    rotateNode = RotateNode(ref.Rotation[0], (0., 1., 0.))
    rotateNode.addChild(node)
    translateNode = TranslateNode(
        (ref.Position - (chunk.cx << 4, 0, chunk.cz << 4)))
    translateNode.addChild(rotateNode)

    textureNode = BindTextureNode(
        modelTex, (1. / model.texWidth, 1. / model.texHeight, 1))
    textureNode.addChild(translateNode)
    return textureNode
Beispiel #3
0
    def __init__(self):
        super(Rotate3DNode, self).__init__()
        self.anchorNode = TranslateNode()
        self.rotXNode = RotateNode(axis=(1, 0, 0))
        self.rotYNode = RotateNode(axis=(0, 1, 0))
        self.rotZNode = RotateNode(axis=(0, 0, 1))
        self.recenterNode = TranslateNode()

        super(Rotate3DNode, self).addChild(self.anchorNode)
        self.anchorNode.addChild(self.rotZNode)
        self.rotZNode.addChild(self.rotYNode)
        self.rotYNode.addChild(self.rotXNode)
        self.rotXNode.addChild(self.recenterNode)
Beispiel #4
0
    def __init__(self):
        super(Rotate3DNode, self).__init__()
        self.anchorNode = TranslateNode()
        self.rotXNode = RotateNode(axis=(1, 0, 0))
        self.rotYNode = RotateNode(axis=(0, 1, 0))
        self.rotZNode = RotateNode(axis=(0, 0, 1))
        self.recenterNode = TranslateNode()

        super(Rotate3DNode, self).addChild(self.anchorNode)
        self.anchorNode.addChild(self.rotZNode)
        self.rotZNode.addChild(self.rotYNode)
        self.rotYNode.addChild(self.rotXNode)
        self.rotXNode.addChild(self.recenterNode)
Beispiel #5
0
    def __init__(self, editorSession, *args, **kwargs):
        super(BrushTool, self).__init__(editorSession, *args, **kwargs)
        self.toolWidget = load_ui("editortools/brush.ui")
        self.brushMode = None
        self.brushLoader = None

        self.brushModesByName = {cls.name:cls() for cls in BrushModeClasses}
        modes = self.brushModesByName.values()
        modes.sort(key=lambda m: m.name)
        self.toolWidget.brushModeInput.setModes(modes)
        BrushModeSetting.connectAndCall(self.modeSettingChanged)

        self.cursorWorldScene = None
        self.cursorNode = TranslateNode()

        self.toolWidget.xSpinSlider.setMinimum(1)
        self.toolWidget.ySpinSlider.setMinimum(1)
        self.toolWidget.zSpinSlider.setMinimum(1)

        self.toolWidget.xSpinSlider.valueChanged.connect(self.setX)
        self.toolWidget.ySpinSlider.valueChanged.connect(self.setY)
        self.toolWidget.zSpinSlider.valueChanged.connect(self.setZ)

        self.toolWidget.brushShapeInput.shapeChanged.connect(self.updateCursor)
        self.toolWidget.brushShapeInput.shapeOptionsChanged.connect(self.updateCursor)

        self.fillBlock = editorSession.worldEditor.blocktypes["stone"]

        self.brushSize = BrushSizeSetting.value(QtGui.QVector3D(5, 5, 5)).toTuple()  # calls updateCursor

        self.toolWidget.xSpinSlider.setValue(self.brushSize[0])
        self.toolWidget.ySpinSlider.setValue(self.brushSize[1])
        self.toolWidget.zSpinSlider.setValue(self.brushSize[2])
Beispiel #6
0
def entityModelNode(ref, model, modelTex, chunk):
    modelVerts = numpy.array(model.vertices)
    modelVerts.shape = modelVerts.shape[0]//4, 4, modelVerts.shape[1]
    # scale down
    modelVerts[..., :3] *= 1/16.
    modelVerts[..., 1] = -modelVerts[..., 1] + 1.5 + 1/64.
    modelVerts[..., 0] = -modelVerts[..., 0]

    vertexBuffer = QuadVertexArrayBuffer(len(modelVerts), lights=False, textures=True)
    vertexBuffer.vertex[:] = modelVerts[..., :3]
    vertexBuffer.texcoord[:] = modelVerts[..., 3:5]

    node = VertexNode([vertexBuffer])
    rotateNode = RotateNode(ref.Rotation[0], (0., 1., 0.))
    rotateNode.addChild(node)
    translateNode = TranslateNode((ref.Position - (chunk.cx << 4, 0, chunk.cz << 4)))
    translateNode.addChild(rotateNode)

    textureNode = BindTextureNode(modelTex, (1./model.texWidth, 1./model.texHeight, 1))
    textureNode.addChild(translateNode)
    return textureNode
Beispiel #7
0
class Rotate3DNode(Node):
    def __init__(self):
        super(Rotate3DNode, self).__init__()
        self.anchorNode = TranslateNode()
        self.rotXNode = RotateNode(axis=(1, 0, 0))
        self.rotYNode = RotateNode(axis=(0, 1, 0))
        self.rotZNode = RotateNode(axis=(0, 0, 1))
        self.recenterNode = TranslateNode()

        super(Rotate3DNode, self).addChild(self.anchorNode)
        self.anchorNode.addChild(self.rotZNode)
        self.rotZNode.addChild(self.rotYNode)
        self.rotYNode.addChild(self.rotXNode)
        self.rotXNode.addChild(self.recenterNode)

    def addChild(self, node):
        self.recenterNode.addChild(node)

    def removeChild(self, node):
        self.recenterNode.removeChild(node)

    def setRotation(self, rots):
        rx, ry, rz = rots
        self.rotXNode.degrees = rx
        self.rotYNode.degrees = ry
        self.rotZNode.degrees = rz

    def setAnchor(self, point):
        self.anchorNode.translateOffset = point
        self.recenterNode.translateOffset = -point
Beispiel #8
0
class Rotate3DNode(Node):
    def __init__(self):
        super(Rotate3DNode, self).__init__()
        self.anchorNode = TranslateNode()
        self.rotXNode = RotateNode(axis=(1, 0, 0))
        self.rotYNode = RotateNode(axis=(0, 1, 0))
        self.rotZNode = RotateNode(axis=(0, 0, 1))
        self.recenterNode = TranslateNode()

        super(Rotate3DNode, self).addChild(self.anchorNode)
        self.anchorNode.addChild(self.rotZNode)
        self.rotZNode.addChild(self.rotYNode)
        self.rotYNode.addChild(self.rotXNode)
        self.rotXNode.addChild(self.recenterNode)

    def addChild(self, node):
        self.recenterNode.addChild(node)

    def removeChild(self, node):
        self.recenterNode.removeChild(node)

    def setRotation(self, rots):
        rx, ry, rz = rots
        self.rotXNode.degrees = rx
        self.rotYNode.degrees = ry
        self.rotZNode.degrees = rz

    def setAnchor(self, point):
        self.anchorNode.translateOffset = point
        self.recenterNode.translateOffset = -point
Beispiel #9
0
def chestEntityModelNode(ref, model, modelTex, chunk, facing, largeX, largeZ):
    modelVerts = numpy.array(model.vertices)
    modelVerts.shape = modelVerts.shape[0]//4, 4, modelVerts.shape[1]
    # scale down
    modelVerts[..., :3] *= 1/16.
    # modelVerts[..., 1] = -modelVerts[..., 1]
    # modelVerts[..., 0] = -modelVerts[..., 0]

    vertexBuffer = QuadVertexArrayBuffer(len(modelVerts), lights=False, textures=True)
    vertexBuffer.vertex[:] = modelVerts[..., :3]
    vertexBuffer.texcoord[:] = modelVerts[..., 3:5]

    node = VertexNode([vertexBuffer])
    rotations = {
        "north": 180,
        "east": 270,
        "south": 0,
        "west": 90
    }
    decenterTranslateNode = TranslateNode((-0.5, -0.5, -0.5))
    decenterTranslateNode.addChild(node)

    rotateNode = RotateNode(rotations[facing], (0., 1., 0.))
    # rotateNode = RotateNode(0, (0., 1., 0.))
    rotateNode.addChild(decenterTranslateNode)
    dx = dz = 0
    if largeX and facing == "north":
        dx = 1.
    if largeZ and facing == "east":
        dz = -1.
    recenterTranslateNode = TranslateNode((0.5+dx, 0.5, 0.5+dz))
    recenterTranslateNode.addChild(rotateNode)
    x, y, z = (ref.Position - (chunk.cx << 4, 0, chunk.cz << 4))
    scaleNode = ScaleNode((1., -1., -1.))
    scaleNode.addChild(recenterTranslateNode)
    posTranslateNode = TranslateNode((x, y+1., z+1.))
    posTranslateNode.addChild(scaleNode)

    textureNode = BindTextureNode(modelTex, (1./model.texWidth, 1./model.texHeight, 1))
    textureNode.addChild(posTranslateNode)
    return textureNode
Beispiel #10
0
class BrushTool(EditorTool):
    name = "Brush"
    iconName = "brush"
    maxBrushSize = 512

    def __init__(self, editorSession, *args, **kwargs):
        super(BrushTool, self).__init__(editorSession, *args, **kwargs)
        self.toolWidget = load_ui("editortools/brush.ui")
        self.brushMode = None
        self.brushLoader = None

        self.brushModesByName = {cls.name:cls() for cls in BrushModeClasses}
        modes = self.brushModesByName.values()
        modes.sort(key=lambda m: m.name)
        self.toolWidget.brushModeInput.setModes(modes)
        BrushModeSetting.connectAndCall(self.modeSettingChanged)

        self.cursorWorldScene = None
        self.cursorNode = TranslateNode()

        self.toolWidget.xSpinSlider.setMinimum(1)
        self.toolWidget.ySpinSlider.setMinimum(1)
        self.toolWidget.zSpinSlider.setMinimum(1)

        self.toolWidget.xSpinSlider.valueChanged.connect(self.setX)
        self.toolWidget.ySpinSlider.valueChanged.connect(self.setY)
        self.toolWidget.zSpinSlider.valueChanged.connect(self.setZ)

        self.toolWidget.brushShapeInput.shapeChanged.connect(self.updateCursor)
        self.toolWidget.brushShapeInput.shapeOptionsChanged.connect(self.updateCursor)

        self.fillBlock = editorSession.worldEditor.blocktypes["stone"]

        self.brushSize = BrushSizeSetting.value(QtGui.QVector3D(5, 5, 5)).toTuple()  # calls updateCursor

        self.toolWidget.xSpinSlider.setValue(self.brushSize[0])
        self.toolWidget.ySpinSlider.setValue(self.brushSize[1])
        self.toolWidget.zSpinSlider.setValue(self.brushSize[2])

    @property
    def hoverDistance(self):
        return self.toolWidget.hoverSpinSlider.value()

    _brushSize = (0, 0, 0)

    @property
    def brushSize(self):
        return self._brushSize

    @brushSize.setter
    def brushSize(self, value):
        self._brushSize = value
        BrushSizeSetting.setValue(QtGui.QVector3D(*self.brushSize))
        self.updateCursor()

    def setX(self, val):
        x, y, z = self.brushSize
        x = float(val)
        self.brushSize = x, y, z

    def setY(self, val):
        x, y, z = self.brushSize
        y = float(val)
        self.brushSize = x, y, z

    def setZ(self, val):
        x, y, z = self.brushSize
        z = float(val)
        self.brushSize = x, y, z

    def setBlocktypes(self, types):
        if len(types) == 0:
            return

        self.fillBlock = types[0]
        self.updateCursor()

    def mousePress(self, event):
        pos = event.blockPosition
        vector = (event.blockFace.vector * self.hoverDistance)
        command = BrushCommand(self.editorSession, [pos + vector], self.options)
        self.editorSession.pushCommand(command)

    def mouseMove(self, event):
        if event.blockPosition:
            vector = (event.blockFace.vector * self.hoverDistance)
            assert isinstance(vector, Vector), "vector isa %s" % type(vector)
            self.cursorNode.translateOffset = event.blockPosition + vector

    @property
    def options(self):
        options = {'brushSize': self.brushSize,
                   'brushShape': self.brushShape,
                   'brushMode': self.brushMode}
        options.update(self.brushMode.getOptions())
        return options

    def modeSettingChanged(self, value):
        self.brushMode = self.brushModesByName[value]
        stack = self.toolWidget.modeOptionsStack
        while stack.count():
            stack.removeWidget(stack.widget(0))
        widget = self.brushMode.createOptionsWidget(self)
        if widget:
            stack.addWidget(widget)

    @property
    def brushShape(self):
        return self.toolWidget.brushShapeInput.currentShape

    def updateCursor(self):
        log.info("Updating brush cursor")
        if self.cursorWorldScene:
            self.brushLoader.timer.stop()
            self.cursorNode.removeChild(self.cursorWorldScene)
            self.cursorNode.removeChild(self.cursorBoxNode)

        cursorLevel = self.brushMode.createCursorLevel(self)
        cursorBox = self.brushMode.brushBoxForPoint((0, 0, 0), self.options)

        self.cursorBoxNode = SelectionBoxNode()
        self.cursorBoxNode.selectionBox = cursorBox
        self.cursorBoxNode.filled = False

        self.cursorWorldScene = worldscene.WorldScene(cursorLevel, self.editorSession.textureAtlas)
        self.cursorWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer
        self.cursorNode.addChild(self.cursorWorldScene)
        self.cursorNode.addChild(self.cursorBoxNode)

        self.brushLoader = WorldLoader(self.cursorWorldScene)
        self.brushLoader.startLoader()
Beispiel #11
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)
Beispiel #12
0
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)
Beispiel #13
0
def chestEntityModelNode(ref, model, modelTex, chunk, facing, largeX, largeZ):
    modelVerts = numpy.array(model.vertices)
    modelVerts.shape = modelVerts.shape[0] // 4, 4, modelVerts.shape[1]
    # scale down
    modelVerts[..., :3] *= 1 / 16.
    # modelVerts[..., 1] = -modelVerts[..., 1]
    # modelVerts[..., 0] = -modelVerts[..., 0]

    vertexBuffer = QuadVertexArrayBuffer(len(modelVerts),
                                         lights=False,
                                         textures=True)
    vertexBuffer.vertex[:] = modelVerts[..., :3]
    vertexBuffer.texcoord[:] = modelVerts[..., 3:5]

    node = VertexNode([vertexBuffer])
    rotations = {"north": 180, "east": 270, "south": 0, "west": 90}
    decenterTranslateNode = TranslateNode((-0.5, -0.5, -0.5))
    decenterTranslateNode.addChild(node)

    rotateNode = RotateNode(rotations[facing], (0., 1., 0.))
    # rotateNode = RotateNode(0, (0., 1., 0.))
    rotateNode.addChild(decenterTranslateNode)
    dx = dz = 0
    if largeX and facing == "north":
        dx = 1.
    if largeZ and facing == "east":
        dz = -1.
    recenterTranslateNode = TranslateNode((0.5 + dx, 0.5, 0.5 + dz))
    recenterTranslateNode.addChild(rotateNode)
    x, y, z = (ref.Position - (chunk.cx << 4, 0, chunk.cz << 4))
    scaleNode = ScaleNode((1., -1., -1.))
    scaleNode.addChild(recenterTranslateNode)
    posTranslateNode = TranslateNode((x, y + 1., z + 1.))
    posTranslateNode.addChild(scaleNode)

    textureNode = BindTextureNode(
        modelTex, (1. / model.texWidth, 1. / model.texHeight, 1))
    textureNode.addChild(posTranslateNode)
    return textureNode
Beispiel #14
0
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)
Beispiel #15
0
    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 = scenenode.Node()

        self.sceneHolderNode = 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)
Beispiel #16
0
 def __init__(self):
     super(WorkplaneNode, self).__init__()
     self.translateNode = TranslateNode()
     self.addChild(self.translateNode)
     self.axis = 1
Beispiel #17
0
class WorkplaneNode(scenenode.Node):
    def __init__(self):
        super(WorkplaneNode, self).__init__()
        self.translateNode = TranslateNode()
        self.addChild(self.translateNode)
        self.axis = 1

    vertexNode = None

    _axis = 1

    @property
    def axis(self):
        return self._axis

    @axis.setter
    def axis(self, axis):
        self._axis = axis
        self.dirty = True

        gridSize = 64
        left = -gridSize // 2
        right = gridSize // 2

        gridArrayBuffer = VertexArrayBuffer((gridSize * 4, ),
                                            GL.GL_LINES,
                                            textures=False,
                                            lights=False)

        gridArrayBuffer.rgba[:] = 255, 255, 255, 100

        # y=0, move by translating
        gridArrayBuffer.vertex[:, axis] = 0

        axis1 = (axis - 1) % 3
        axis2 = (axis + 1) % 3

        # left edge
        gridArrayBuffer.vertex[0:gridSize * 2:2, axis2] = left
        gridArrayBuffer.vertex[0:gridSize * 2:2, axis1] = range(left, right)

        # right edge
        gridArrayBuffer.vertex[1:gridSize * 2:2, axis2] = right - 1
        gridArrayBuffer.vertex[1:gridSize * 2:2, axis1] = range(left, right)

        # bottom edge
        gridArrayBuffer.vertex[gridSize * 2::2, axis1] = left
        gridArrayBuffer.vertex[gridSize * 2::2, axis2] = range(left, right)

        # top edge
        gridArrayBuffer.vertex[gridSize * 2 + 1::2, axis1] = right - 1
        gridArrayBuffer.vertex[gridSize * 2 + 1::2, axis2] = range(left, right)

        if self.vertexNode:
            self.translateNode.removeChild(self.vertexNode)
        self.vertexNode = VertexNode([gridArrayBuffer])
        self.translateNode.addChild(self.vertexNode)

    @property
    def position(self):
        return self.translateNode.translateOffset

    @position.setter
    def position(self, value):
        self.translateNode.translateOffset = value
Beispiel #18
0
 def __init__(self):
     super(WorkplaneNode, self).__init__()
     self.translateNode = TranslateNode()
     self.addChild(self.translateNode)
     self.axis = 1
Beispiel #19
0
class WorkplaneNode(scenenode.Node):
    def __init__(self):
        super(WorkplaneNode, self).__init__()
        self.translateNode = TranslateNode()
        self.addChild(self.translateNode)
        self.axis = 1

    vertexNode = None

    _axis = 1

    @property
    def axis(self):
        return self._axis

    @axis.setter
    def axis(self, axis):
        self._axis = axis
        self.dirty = True

        gridSize = 64
        left = -gridSize // 2
        right = gridSize // 2

        gridArrayBuffer = VertexArrayBuffer((gridSize * 4,), GL.GL_LINES, textures=False, lights=False)

        gridArrayBuffer.rgba[:] = 255, 255, 255, 100

        # y=0, move by translating
        gridArrayBuffer.vertex[:, axis] = 0

        axis1 = (axis - 1) % 3
        axis2 = (axis + 1) % 3

        # left edge
        gridArrayBuffer.vertex[0 : gridSize * 2 : 2, axis2] = left
        gridArrayBuffer.vertex[0 : gridSize * 2 : 2, axis1] = range(left, right)

        # right edge
        gridArrayBuffer.vertex[1 : gridSize * 2 : 2, axis2] = right - 1
        gridArrayBuffer.vertex[1 : gridSize * 2 : 2, axis1] = range(left, right)

        # bottom edge
        gridArrayBuffer.vertex[gridSize * 2 :: 2, axis1] = left
        gridArrayBuffer.vertex[gridSize * 2 :: 2, axis2] = range(left, right)

        # top edge
        gridArrayBuffer.vertex[gridSize * 2 + 1 :: 2, axis1] = right - 1
        gridArrayBuffer.vertex[gridSize * 2 + 1 :: 2, axis2] = range(left, right)

        if self.vertexNode:
            self.translateNode.removeChild(self.vertexNode)
        self.vertexNode = VertexNode([gridArrayBuffer])
        self.translateNode.addChild(self.vertexNode)

    @property
    def position(self):
        return self.translateNode.translateOffset

    @position.setter
    def position(self, value):
        self.translateNode.translateOffset = value
Beispiel #20
0
class BrushTool(EditorTool):
    name = "Brush"
    iconName = "brush"
    maxBrushSize = 512

    def __init__(self, editorSession, *args, **kwargs):
        super(BrushTool, self).__init__(editorSession, *args, **kwargs)
        self.toolWidget = load_ui("editortools/brush.ui")
        self.brushMode = None
        self.brushLoader = None

        self.brushModesByName = {cls.name: cls() for cls in BrushModeClasses}
        modes = self.brushModesByName.values()
        modes.sort(key=lambda m: m.name)
        self.toolWidget.brushModeInput.setModes(modes)
        BrushModeSetting.connectAndCall(self.modeSettingChanged)

        self.cursorWorldScene = None
        self.cursorNode = TranslateNode()

        self.toolWidget.xSpinSlider.setMinimum(1)
        self.toolWidget.ySpinSlider.setMinimum(1)
        self.toolWidget.zSpinSlider.setMinimum(1)

        self.toolWidget.xSpinSlider.valueChanged.connect(self.setX)
        self.toolWidget.ySpinSlider.valueChanged.connect(self.setY)
        self.toolWidget.zSpinSlider.valueChanged.connect(self.setZ)

        self.toolWidget.brushShapeInput.shapeChanged.connect(self.updateCursor)
        self.toolWidget.brushShapeInput.shapeOptionsChanged.connect(
            self.updateCursor)

        self.fillBlock = editorSession.worldEditor.blocktypes["stone"]

        self.brushSize = BrushSizeSetting.value(QtGui.QVector3D(
            5, 5, 5)).toTuple()  # calls updateCursor

        self.toolWidget.xSpinSlider.setValue(self.brushSize[0])
        self.toolWidget.ySpinSlider.setValue(self.brushSize[1])
        self.toolWidget.zSpinSlider.setValue(self.brushSize[2])

    @property
    def hoverDistance(self):
        return self.toolWidget.hoverSpinSlider.value()

    _brushSize = (0, 0, 0)

    @property
    def brushSize(self):
        return self._brushSize

    @brushSize.setter
    def brushSize(self, value):
        self._brushSize = value
        BrushSizeSetting.setValue(QtGui.QVector3D(*self.brushSize))
        self.updateCursor()

    def setX(self, val):
        x, y, z = self.brushSize
        x = float(val)
        self.brushSize = x, y, z

    def setY(self, val):
        x, y, z = self.brushSize
        y = float(val)
        self.brushSize = x, y, z

    def setZ(self, val):
        x, y, z = self.brushSize
        z = float(val)
        self.brushSize = x, y, z

    def setBlocktypes(self, types):
        if len(types) == 0:
            return

        self.fillBlock = types[0]
        self.updateCursor()

    def mousePress(self, event):
        pos = event.blockPosition
        vector = (event.blockFace.vector * self.hoverDistance)
        command = BrushCommand(self.editorSession, [pos + vector],
                               self.options)
        self.editorSession.pushCommand(command)

    def mouseMove(self, event):
        if event.blockPosition:
            vector = (event.blockFace.vector * self.hoverDistance)
            assert isinstance(vector, Vector), "vector isa %s" % type(vector)
            self.cursorNode.translateOffset = event.blockPosition + vector

    @property
    def options(self):
        options = {
            'brushSize': self.brushSize,
            'brushShape': self.brushShape,
            'brushMode': self.brushMode
        }
        options.update(self.brushMode.getOptions())
        return options

    def modeSettingChanged(self, value):
        self.brushMode = self.brushModesByName[value]
        stack = self.toolWidget.modeOptionsStack
        while stack.count():
            stack.removeWidget(stack.widget(0))
        widget = self.brushMode.createOptionsWidget(self)
        if widget:
            stack.addWidget(widget)

    @property
    def brushShape(self):
        return self.toolWidget.brushShapeInput.currentShape

    def updateCursor(self):
        log.info("Updating brush cursor")
        if self.cursorWorldScene:
            self.brushLoader.timer.stop()
            self.cursorNode.removeChild(self.cursorWorldScene)
            self.cursorNode.removeChild(self.cursorBoxNode)

        cursorLevel = self.brushMode.createCursorLevel(self)
        cursorBox = self.brushMode.brushBoxForPoint((0, 0, 0), self.options)

        self.cursorBoxNode = SelectionBoxNode()
        self.cursorBoxNode.selectionBox = cursorBox
        self.cursorBoxNode.filled = False

        self.cursorWorldScene = worldscene.WorldScene(
            cursorLevel, self.editorSession.textureAtlas)
        self.cursorWorldScene.depthOffsetNode.depthOffset = DepthOffset.PreviewRenderer
        self.cursorNode.addChild(self.cursorWorldScene)
        self.cursorNode.addChild(self.cursorBoxNode)

        self.brushLoader = WorldLoader(self.cursorWorldScene)
        self.brushLoader.startLoader()
Beispiel #21
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)
Beispiel #22
0
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 = scenenode.Node()

        self.sceneHolderNode = 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):
        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()
                        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.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.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)