Exemplo n.º 1
0
class NodeConnector:
    def __init__(self, socketA, socketB):
        self.connectorID = uuid4()
        self.socketA = socketA
        self.socketB = socketB
        self.line = LineNodePath(ShowBaseGlobal.aspect2d,
                                 thickness=2,
                                 colorVec=(0.8, 0.8, 0.8, 1))
        self.draw()

    def update(self):
        self.line.reset()
        self.draw()

    def draw(self):
        self.line.moveTo(self.socketA.plug.getPos(ShowBaseGlobal.aspect2d))
        self.line.drawTo(self.socketB.plug.getPos(ShowBaseGlobal.aspect2d))
        self.line.create()

    def has(self, socket):
        """Returns True if one of the sockets this connector connects is
        the given socket"""
        return socket == self.socketA or socket == self.socketB

    def connects(self, a, b):
        """Returns True if this connector connects socket a and b"""
        return (a == self.socketA or a
                == self.socketB) and (b == self.socketA or b == self.socketB)

    def disconnect(self):
        self.line.reset()
        self.socketA.setConnected(False)
        self.socketB.setConnected(False)
Exemplo n.º 2
0
def draw_lines(lines: LineNodePath, *paths: dict, origin=None, relative=True):
    if origin is None:
        origin = lines.getCurrentPosition()
    lines.reset()
    for path in paths:
        if 'color' in path:
            lines.setColor(*path['color'])
        points = path['points']
        lines.moveTo(*origin)
        for point in points:
            if relative:
                point += origin
            lines.drawTo(*point)
    lines.create()
Exemplo n.º 3
0
class Fisherman(Toon.Toon):
    numFishTypes = 3

    def __init__(self, id, toNpcId=20001, fAutonomous=1):
        # Default NPC ID is Flippy
        Toon.Toon.__init__(self)
        self.id = id
        self.fAutonomous = fAutonomous
        npcInfo = NPCToons.NPCToonDict[toNpcId]
        dnaList = npcInfo[2]
        gender = npcInfo[3]
        dna = ToonDNA.ToonDNA()
        dna.newToonFromProperties(*dnaList)
        self.setDNA(dna)
        self.reparentTo(render)
        self.angleNP = self.find('**/actorGeom')
        # Create pole
        self.pole = Actor.Actor()
        self.pole.loadModel('phase_4/models/props/fishing-pole-mod')
        self.pole.loadAnims({'cast': 'phase_4/models/props/fishing-pole-chan'})
        # Get the top of the pole.
        self.ptop = self.pole.find('**/joint_attachBill')
        self.pole.pose('cast', 0)
        # Prepare Pole
        self.poleNode = []
        self.holdPole()
        self.createCastTrack()
        self.castIval = None
        # Prepare actor
        self.setupNeutralBlend()
        self.targetInterval = None
        # Start automatic casting or create cast button
        if self.fAutonomous:
            self.castButton = None
            self.targetButton = None
            self.startCasting()
        else:
            # Starts casting mode when mouse enters button region
            self.castButton = DirectButton(text='CAST',
                                           relief=None,
                                           scale=0.1,
                                           pos=(0, 0, -0.2))
            self.castButton.bind(DGG.ENTER, self.showCancelFrame)
            # A big screen encompassing frame to catch the button releases
            self.cancelFrame = DirectFrame(parent=self.castButton,
                                           frameSize=(-1, 1, -1, 1),
                                           relief=None,
                                           state='normal')
            # Make sure this is on top of all the other widgets
            self.cancelFrame.setBin('gui-popup', 0)
            self.cancelFrame.bind(DGG.B1PRESS, self.startAdjustingCastTask)
            self.cancelFrame.bind(DGG.B1RELEASE, self.finishCast)
            self.cancelFrame.hide()
            # Create bob
            self.bob = loader.loadModel('phase_4/models/props/fishing_bob')
            self.bobSpot = Point3(0)
            # Parameters to control bob motion
            self.vZeroMax = 30.0
            self.angleMax = 30.0
            # Ripple effect
            self.ripples = Ripples.Ripples(self.angleNP)
            self.ripples.hide()
            # Target
            self.buttonFrame = DirectFrame()
            self.target = base.distributedFishingTarget.fishingTargetNode

            self.fishPivot = self.attachNewNode('fishPivot')
            self.fish = loader.loadModel('models/misc/smiley')
            self.fish.reparentTo(self.fishPivot)
            self.fish.setScale(0.3, 1, 0.3)
            self.wiggleIval = None
            self.circleIval = None
            self.initFish()

            self.targetButton = DirectButton(parent=self.buttonFrame,
                                             text='MOVE',
                                             relief=DGG.RAISED,
                                             scale=0.1,
                                             pos=(0, 0, -0.9),
                                             command=self.moveTarget)
            self.targetTypeButton = DirectCheckButton(parent=self.buttonFrame,
                                                      text='MOVING',
                                                      relief=DGG.RAISED,
                                                      scale=0.085,
                                                      pos=(0.4, 0, -0.895),
                                                      command=self.setfMove)
            self.fMovingTarget = 0
            self.targetModeButton = DirectCheckButton(
                parent=self.buttonFrame,
                text='dTASK',
                relief=DGG.RAISED,
                scale=0.085,
                pos=(0.8, 0, -0.895),
                command=self.setfTargetMode)
            self.fTargetMode = 0
            # Vector line
            self.line = LineNodePath(render2d)
            self.line.setColor(VBase4(1, 0, 0, 1))
            self.line.moveTo(0, 0, 0)
            self.line.drawTo(1, 0, 0)
            self.line.create()
            self.moveTarget()

    def showCancelFrame(self, event):
        # Also display cancel frame to catch clicks outside of the popup
        self.cancelFrame.show()

    def startAdjustingCastTask(self, event):
        # Start task to adjust power of cast
        self.getMouse()
        self.initMouseX = self.mouseX
        self.initMouseY = self.mouseY
        self.initMouseX = 0
        self.initMouseY = -0.2
        self.line.lineSegs.setVertex(0, self.initMouseX, 0, self.initMouseY)
        self.angleNP.setH(0)
        self.__hideBob()
        taskMgr.remove('distCheck')
        # Position and scale cancel frame to fill entire window
        self.cancelFrame.setPos(render2d, 0, 0, 0)
        self.cancelFrame.setScale(render2d, 1, 1, 1)
        self.castTrack.finish()
        self.castTrack.setT(0)
        self.castTrack.start(startT=0, endT=0.4)
        self.castIval = Sequence(
            Wait(0.4),
            Func(taskMgr.add, self.adjustingCastTask, 'adjustCastTask'))
        self.castIval.start()

    def adjustingCastTask(self, state):
        self.getMouse()
        deltaX = self.mouseX - self.initMouseX
        deltaY = self.mouseY - self.initMouseY
        self.line.lineSegs.setVertex(1, self.mouseX, 0, self.mouseY)
        dist = math.sqrt(deltaX * deltaX + deltaY * deltaY)
        delta = (dist / 0.5)
        if deltaY > 0:
            delta *= -1
        p = max(min(delta, 1.0), 0.0)
        self.power = p
        self.castTrack.setT(0.4 + p * 0.5)
        self.bobSpot = Point3(0, 6.5 + p * 25.0, -1.9)
        # Calc angle
        if deltaY == 0:
            angle = 0
        else:
            angle = rad2Deg(math.atan(deltaX / deltaY))
        self.angleNP.setH(-angle)
        return Task.cont

    def stopAdjustingCastTask(self):
        taskMgr.remove('adjustingTask')

    def setupNeutralBlend(self):
        self.stop()
        self.loop('neutral')
        self.enableBlend()
        self.pose('cast', 0)
        self.setControlEffect('neutral', 0.2)
        self.setControlEffect('cast', 0.8)

    def startCasting(self):
        if self.fAutonomous:
            self.castIval = Sequence(
                ActorInterval(self, 'cast'), Func(self.catchFish),
                Parallel(
                    ActorInterval(self, 'neutral', loop=1, duration=100),
                    Sequence(
                        Wait(random.random() * 20.0),
                        Func(self.startCasting),
                    )))
        else:
            self.castIval = Sequence(
                ActorInterval(self, 'cast'), Func(self.catchFish),
                ActorInterval(self, 'neutral', loop=1, duration=100))
        self.castIval.play()

    def stopCasting(self):
        if self.castIval:
            self.castIval.pause()
        if self.targetInterval:
            self.stopTargetInterval()
        taskMgr.remove('distCheck')

    def catchFish(self):
        fishNum = int(round(random.random() * self.numFishTypes))
        messenger.send('caughtFish', sentArgs=[self.id, fishNum])

    def setupNeutralBlend(self):
        self.stop()
        self.loop('neutral')
        self.enableBlend()
        self.pose('cast', 0)
        self.setControlEffect('neutral', 0.2)
        self.setControlEffect('cast', 0.8)

    def getPole(self):
        toonTrack = Sequence(
            # Blend in neutral anim
            Func(self.setupNeutralBlend),
            # Pull out pole
            Func(self.holdPole),
            Parallel(
                ActorInterval(self, 'cast', playRate=0.5, duration=27. / 12.),
                ActorInterval(self.pole,
                              'cast',
                              playRate=0.5,
                              duration=27. / 12.),
                LerpScaleInterval(self.pole,
                                  duration=2.0,
                                  scale=1.0,
                                  startScale=0.01)),
        )
        toonTrack.play()

    def putAwayPole(self):
        Sequence(
            Parallel(
                ActorInterval(self,
                              'cast',
                              duration=1.0,
                              startTime=1.0,
                              endTime=0.0),
                ActorInterval(self.pole,
                              'cast',
                              duration=1.0,
                              startTime=1.0,
                              endTime=0.0),
                LerpScaleInterval(self.pole,
                                  duration=0.5,
                                  scale=0.01,
                                  startScale=1.0)),
            Func(self.dropPole)).start()

    def holdPole(self):
        if self.poleNode != []:
            self.dropPole()

        # One node, instanced to each of the toon's three right hands,
        # will hold the pole.
        np = NodePath('pole-holder')
        hands = self.getRightHands()
        for h in hands:
            self.poleNode.append(np.instanceTo(h))

        self.pole.reparentTo(self.poleNode[0])

    def dropPole(self):
        self.__hideBob()
        #self.__hideLine()
        if self.pole != None:
            self.pole.clearMat()
            self.pole.detachNode()

        for pn in self.poleNode:
            pn.removeNode()
        self.poleNode = []

    def createCastTrack(self):
        self.castTrack = Sequence(
            Parallel(
                ActorInterval(self, 'cast', duration=2.0, startTime=1.0),
                ActorInterval(self.pole, 'cast', duration=2.0, startTime=1.0),
            ))

    def cast(self):
        self.castTrack.start()

    def finishCast(self, event=None):
        #self.line.lineSegs.setVertex(1,self.initMouseX,0,self.initMouseY)
        self.cancelFrame.hide()
        if not self.castTrack:
            self.createCastTrack()
        self.castIval.finish()
        taskMgr.remove('adjustCastTask')
        taskMgr.remove('moveBobTask')
        self.castTrack.pause()
        #self.castTrack.start(self.castTrack.getT())
        p = self.power
        startT = 0.9 + (1 - p) * 0.3
        self.castTrack.start(startT)
        self.bobStartPos = Point3(0.017568, 7.90371, 6.489)
        Sequence(Wait(1.2 - startT), Func(self.startMoveBobTask)).start()

    def startMoveBobTask(self):
        self.__showBob()
        self.bobStartT = globalClock.getFrameTime()
        taskMgr.add(self.moveBobTask, 'moveBobTask')

    def moveBobTask(self, state):
        # Accel due to gravity
        g = 32.2
        # Elapsed time of cast
        t = globalClock.getFrameTime() - self.bobStartT
        # Scale bob velocity and angle based on power of cast
        vZero = self.power * self.vZeroMax
        angle = deg2Rad(self.power * self.angleMax)
        # How far has bob moved from start point?
        deltaY = vZero * math.cos(angle) * t
        deltaZ = vZero * math.sin(angle) * t - (g * t * t) / 2.0
        deltaPos = Point3(0, deltaY, deltaZ)
        # Current bob position
        pos = self.bobStartPos + deltaPos
        # Have we reached end condition?
        if pos[2] < -1.9:
            pos.setZ(-1.9)
            self.ripples.reparentTo(self.angleNP)
            self.ripples.setPos(pos)
            self.ripples.play()
            returnVal = Task.done
            if self.fTargetMode:
                taskMgr.add(self.distCheck, 'distCheck')
            else:
                self.distCheck()
        else:
            returnVal = Task.cont
        self.bob.setPos(pos)
        return returnVal

    def distCheck(self, state=None):
        # Check to see if we hit the target
        bPos = self.bob.getPos()
        # Check target
        returnVal = self.__distCheck(bPos, self.target)
        if returnVal == Task.done:
            return returnVal
        # Check fish
        return self.__distCheck(bPos, self.fish)

    def __distCheck(self, bPos, target):
        def flashTarget():
            self.stopTargetInterval()
            self.target.getChild(0).setColor(0, 0, 1)

        def flashFish():
            taskMgr.remove('turnTask')
            self.fish.lerpScale(Point3(0.01), 0.5, task='flashFish')

        tPos = target.getPos(self.angleNP)
        tDist = Vec3(tPos - bPos)
        tDist.setZ(0)
        dist = tDist.length()
        if target == self.target:
            flashFunc = flashTarget
            moveFunc = self.moveTarget
        else:
            flashFunc = flashFish
            moveFunc = self.turnFish
        if dist < 2.5:
            fBite = (random.random() < 0.4) or (not self.fTargetMode)
            delay = self.fTargetMode * 0.25
            if fBite:
                print('BITE')
                Sequence(
                    Wait(random.random() * delay),
                    Func(flashFunc),
                    Func(self.catchFish),
                    Wait(2.0),
                    Func(moveFunc),
                ).play()
            else:
                print('MISS')

                def moveIt():
                    moveFunc(targetPos=target.getPos())

                Sequence(Wait(random.random() * delay), Func(moveIt)).play()
            return Task.done
        return Task.cont

    def stopTargetInterval(self):
        if self.targetInterval:
            self.targetInterval.pause()
        self.targetInterval = None

    def __showBob(self):
        # Put the bob in the water and make it gently float.
        self.__hideBob()
        self.bob.reparentTo(self.angleNP)
        self.bob.setPos(self.ptop, 0, 0, 0)

    def __hideBob(self):
        if self.bob != None:
            self.bob.detachNode()

    def getMouse(self):
        if (base.mouseWatcherNode.hasMouse()):
            self.mouseX = base.mouseWatcherNode.getMouseX()
            self.mouseY = base.mouseWatcherNode.getMouseY()
        else:
            self.mouseX = 0
            self.mouseY = 0

    def setfMove(self, fMoving):
        self.fMovingTarget = fMoving

    def setfTargetMode(self, fTargetMode):
        self.fTargetMode = fTargetMode

    def moveTarget(self, targetPos=None):
        base.distributedFishingTarget.sendUpdate('bobEnter', [])


#        self.stopTargetInterval()
#        self.target.clearColor()
#        if not targetPos:
#            x = -87.0 + random.random() * 15.0
#            y = 25.0 + random.random() * 20.0
#            z = -4.8
#            self.targetPos = Point3(x,y,z)
#        else:
#            self.targetPos.assign(targetPos)
#        if self.fMovingTarget:
#            self.makeTargetInterval()
#        else:
#            #self.target.setPos(self.targetPos)

    def initFish(self):
        x = -10.0 + random.random() * 20.0
        y = 00.0 + random.random() * 30.0
        z = -1.6
        self.fishPivot.setPos(x, y, z)
        self.turningRadius = 5.0 + random.random() * 5.0
        self.fish.setPos(self.turningRadius, 0, -0.4)
        if self.wiggleIval:
            self.wiggleIval.pause()
        self.wiggleIval = Sequence(
            self.fish.hprInterval(0.5,
                                  hpr=Point3(10, 0, 0),
                                  startHpr=Point3(-10, 0, 0),
                                  blendType='easeInOut'),
            self.fish.hprInterval(0.5,
                                  hpr=Point3(-10, 0, 0),
                                  startHpr=Point3(10, 0, 0),
                                  blendType='easeInOut'))
        self.wiggleIval.loop()
        if self.circleIval:
            self.circleIval.pause()
        self.circleIval = self.fishPivot.hprInterval(20,
                                                     Point3(360, 0, 0),
                                                     startHpr=Point3(0))
        self.circleIval.loop()
        taskMgr.remove('turnTask')
        taskMgr.doMethodLater(3.0 + random.random() * 3.0, self.turnFish,
                              'turnTask')
        taskMgr.remove('fishBoundsCheck')
        taskMgr.add(self.fishBoundsCheck, 'fishBoundsCheck')

    def fishBoundsCheck(self, state):
        pos = self.fish.getPos(self)
        if pos[0] < -20:
            self.fishPivot.setX(self.fishPivot.getX() + 40.0)
        elif pos[0] > 20:
            self.fishPivot.setX(self.fishPivot.getX() - 40.0)
        if pos[1] < -10:
            self.fishPivot.setY(self.fishPivot.getY() + 50.0)
        elif pos[1] > 40:
            self.fishPivot.setY(self.fishPivot.getY() - 50.0)
        return Task.cont

    def turnFish(self, state=None, targetPos=None):
        self.fish.setScale(0.3, 1, 0.3)
        if self.circleIval:
            self.circleIval.pause()
        newTurningRadius = 5.0 + random.random() * 5.0
        fRightTurn = random.random() < 0.5
        if fRightTurn:
            newTurningRadius *= -1.0
        offset = self.turningRadius - newTurningRadius
        self.fishPivot.setX(self.fishPivot, offset)
        self.turningRadius = newTurningRadius
        self.fish.setX(self.turningRadius)
        currH = self.fishPivot.getH() % 360.0
        if fRightTurn:
            self.circleIval = self.fishPivot.hprInterval(
                20, Point3(currH - 360, 0, 0), startHpr=Point3(currH, 0, 0))
        else:
            self.circleIval = self.fishPivot.hprInterval(
                20, Point3(currH + 360, 0, 0), startHpr=Point3(currH, 0, 0))
        self.circleIval.loop()
        taskMgr.doMethodLater(3.0 + random.random() * 3.0, self.turnFish,
                              'turnTask')
        return Task.done

    def makeTargetInterval(self):
        x = -10.0 + random.random() * 20.0
        y = 0.0 + random.random() * 30.0
        z = -1.6
        self.targetEndPos = Point3(x, y, z)
        dist = Vec3(self.targetEndPos - self.targetPos).length()
        dur = dist / 1.5
        dur = dur * (0.75 + random.random() * 0.5)
        self.targetInterval = Sequence(
            self.target.posInterval(dur,
                                    self.targetEndPos,
                                    startPos=self.targetPos,
                                    blendType='easeInOut'),
            self.target.posInterval(dur,
                                    self.targetPos,
                                    startPos=self.targetEndPos,
                                    blendType='easeInOut'),
            name='moveInterval')
        offsetDur = dur / random.randint(1, 4)
        amplitude = 0.1 + random.random() * 1.0
        self.targetInterval.loop()

    def destroy(self):
        if not self.fAutonomous:
            self.castButton.destroy()
            self.buttonFrame.destroy()
            self.line.removeNode()
            taskMgr.remove('turnTask')
            taskMgr.remove('fishBoundsCheck')
        self.stopCasting()
        self.removeNode()
Exemplo n.º 4
0
class main(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        # Disable the default camera movements
        self.disableMouse()

        #
        # VIEW SETTINGS
        #
        self.win.setClearColor((0.16, 0.16, 0.16, 1))
        render.setAntialias(AntialiasAttrib.MAuto)
        render2d.setAntialias(AntialiasAttrib.MAuto)

        #
        # NODE VIEW
        #
        self.viewNP = aspect2d.attachNewNode("viewNP")
        self.viewNP.setScale(0.5)

        #
        # NODE MANAGER
        #
        self.nodeMgr = NodeManager(self.viewNP)

        #
        # NODE RELATED EVENTS
        #
        # Add nodes
        self.accept("addNode", self.nodeMgr.addNode)
        # Remove nodes
        self.accept("removeNode", self.nodeMgr.removeNode)
        self.accept("x", self.nodeMgr.removeNode)
        self.accept("delete", self.nodeMgr.removeNode)
        # Selecting
        self.accept("selectNode", self.nodeMgr.selectNode)
        # Deselecting
        self.accept("mouse3", self.nodeMgr.deselectAll)
        # Node Drag and Drop
        self.accept("dragNodeStart", self.setDraggedNode)
        self.accept("dragNodeMove", self.updateNodeMove)
        self.accept("dragNodeStop", self.updateNodeStop)
        # Duplicate/Copy nodes
        self.accept("shift-d", self.nodeMgr.copyNodes)
        self.accept("copyNodes", self.nodeMgr.copyNodes)
        # Refresh node logics
        self.accept("ctlr-r", self.nodeMgr.updateAllLeaveNodes)
        self.accept("refreshNodes", self.nodeMgr.updateAllLeaveNodes)

        #
        # SOCKET RELATED EVENTS
        #
        self.accept("updateConnectedNodes", self.nodeMgr.updateConnectedNodes)
        # Socket connection with drag and drop
        self.accept("startPlug", self.nodeMgr.setStartPlug)
        self.accept("endPlug", self.nodeMgr.setEndPlug)
        self.accept("connectPlugs", self.nodeMgr.connectPlugs)
        self.accept("cancelPlug", self.nodeMgr.cancelPlug)
        # Draw line while connecting sockets
        self.accept("startLineDrawing", self.startLineDrawing)
        self.accept("stopLineDrawing", self.stopLineDrawing)

        #
        # PROJECT MANAGEMENT
        #
        self.accept("new", self.newProject)
        self.accept("save", self.saveProject)
        self.accept("load", self.loadProject)
        self.accept("quit", exit)

        #
        # EDITOR VIEW
        #
        # Zooming
        self.accept("zoom", self.zoom)
        self.accept("zoom_reset", self.zoomReset)
        self.accept("wheel_up", self.zoom, [True])
        self.accept("wheel_down", self.zoom, [False])

        # Drag view
        self.mouseSpeed = 1
        self.mousePos = None
        self.startCameraMovement = False
        self.accept("mouse2", self.setMoveCamera, [True])
        self.accept("mouse2-up", self.setMoveCamera, [False])

        # Box select
        # accept the 1st mouse button events to start and stop the draw
        self.accept("mouse1", self.startBoxDraw)
        self.accept("mouse1-up", self.stopBoxDraw)
        # variables to store the start and current pos of the mousepointer
        self.startPos = LPoint2f(0, 0)
        self.lastPos = LPoint2f(0, 0)
        # variables for the to be drawn box
        self.boxCardMaker = CardMaker("SelectionBox")
        self.boxCardMaker.setColor(1, 1, 1, 0.25)
        self.box = None

        #
        # WINDOW RELATED EVENTS
        #
        self.screenSize = base.getSize()
        self.accept("window-event", self.windowEventHandler)

        #
        # MENU BAR
        #
        self.menuBar = MenuBar()

        #
        # TASKS
        #
        # Task for handling dragging of the camera/view
        self.taskMgr.add(self.updateCam, "task_camActualisation", priority=-4)

    # ------------------------------------------------------------------
    # PROJECT FUNCTIONS
    # ------------------------------------------------------------------
    def newProject(self):
        self.nodeMgr.cleanup()

    def saveProject(self):
        Save(self.nodeList, self.connections)

    def loadProject(self):
        self.nodeMgr.cleanup()
        Load(self.nodeMgr)

    # ------------------------------------------------------------------
    # CAMERA SPECIFIC FUNCTIONS
    # ------------------------------------------------------------------
    def setMoveCamera(self, moveCamera):
        """Start dragging around the editor area/camera"""
        # store the mouse position if weh have a mouse
        if base.mouseWatcherNode.hasMouse():
            x = base.mouseWatcherNode.getMouseX()
            y = base.mouseWatcherNode.getMouseY()
            self.mousePos = Point2(x, y)
        # set the variable according to if we want to move the camera or not
        self.startCameraMovement = moveCamera

    def updateCam(self, task):
        """Task that will move the editor area/camera around according
        to mouse movements"""
        # variables to store the mouses current x and y position
        x = 0.0
        y = 0.0
        if base.mouseWatcherNode.hasMouse():
            # get the mouse position
            x = base.mouseWatcherNode.getMouseX()
            y = base.mouseWatcherNode.getMouseY()
        if base.mouseWatcherNode.hasMouse() \
        and self.mousePos is not None \
        and self.startCameraMovement:
            # Move the viewer node aspect independent
            wp = self.win.getProperties()
            aspX = 1.0
            aspY = 1.0
            wpXSize = wp.getXSize()
            wpYSize = wp.getYSize()
            if wpXSize > wpYSize:
                aspX = wpXSize / float(wpYSize)
            else:
                aspY = wpYSize / float(wpXSize)
            mouseMoveX = (self.mousePos.getX() - x) / self.viewNP.getScale(
            ).getX() * self.mouseSpeed * aspX
            mouseMoveY = (self.mousePos.getY() - y) / self.viewNP.getScale(
            ).getZ() * self.mouseSpeed * aspY
            self.mousePos = Point2(x, y)

            self.viewNP.setX(self.viewNP, -mouseMoveX)
            self.viewNP.setZ(self.viewNP, -mouseMoveY)

            self.nodeMgr.updateConnections()

        # continue the task until it got manually stopped
        return task.cont

    def zoom(self, zoomIn):
        """Zoom the editor in or out dependent on the value in zoomIn"""
        zoomFactor = 0.05
        maxZoomIn = 2
        maxZoomOut = 0.1
        if zoomIn:
            s = self.viewNP.getScale()
            if s.getX() - zoomFactor < maxZoomIn and s.getY(
            ) - zoomFactor < maxZoomIn and s.getZ() - zoomFactor < maxZoomIn:
                self.viewNP.setScale(s.getX() + zoomFactor,
                                     s.getY() + zoomFactor,
                                     s.getZ() + zoomFactor)
        else:
            s = self.viewNP.getScale()
            if s.getX() - zoomFactor > maxZoomOut and s.getY(
            ) - zoomFactor > maxZoomOut and s.getZ() - zoomFactor > maxZoomOut:
                self.viewNP.setScale(s.getX() - zoomFactor,
                                     s.getY() - zoomFactor,
                                     s.getZ() - zoomFactor)
        self.nodeMgr.updateConnections()

    def zoomReset(self):
        """Set the zoom level back to the default"""
        self.viewNP.setScale(0.5)
        self.nodeMgr.updateConnections()

    # ------------------------------------------------------------------
    # DRAG LINE
    # ------------------------------------------------------------------
    def startLineDrawing(self, startPos):
        """Start a task that will draw a line from the given start
        position to the cursor"""
        self.line = LineNodePath(render2d,
                                 thickness=2,
                                 colorVec=(0.8, 0.8, 0.8, 1))
        self.line.moveTo(startPos)
        t = self.taskMgr.add(self.drawLineTask, "drawLineTask")
        t.startPos = startPos

    def drawLineTask(self, task):
        """Draws a line from a given start position to the cursor"""
        mwn = base.mouseWatcherNode
        if mwn.hasMouse():
            pos = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])

            self.line.reset()
            self.line.moveTo(task.startPos)
            self.line.drawTo(pos)
            self.line.create()
        return task.cont

    def stopLineDrawing(self):
        """Stop the task that draws a line to the cursor"""
        taskMgr.remove("drawLineTask")
        if self.line is not None:
            self.line.reset()
            self.line = None

    # ------------------------------------------------------------------
    # EDITOR NODE DRAGGING UPDATE
    # ------------------------------------------------------------------
    def setDraggedNode(self, node):
        """This will set the node that is currently dragged around
        as well as update other selected nodes which will be moved
        in addition to the main dragged node"""
        self.draggedNode = node
        self.tempNodePositions = {}
        for node in self.nodeMgr.selectedNodes:
            self.tempNodePositions[node] = node.frame.getPos(render2d)

    def updateNodeMove(self, mouseA, mouseB):
        """Will be called as long as a node is beeing dragged around"""
        for node in self.nodeMgr.selectedNodes:
            if node is not self.draggedNode and node in self.tempNodePositions.keys(
            ):
                editVec = Vec3(self.tempNodePositions[node] - mouseA)
                newPos = mouseB + editVec
                node.frame.setPos(render2d, newPos)
        self.nodeMgr.updateConnections()

    def updateNodeStop(self, node=None):
        """Will be called when a node dragging stopped"""
        self.draggedNode = None
        self.tempNodePositions = {}
        self.nodeMgr.updateConnections()

    # ------------------------------------------------------------------
    # BASIC WINDOW HANDLING
    # ------------------------------------------------------------------
    def windowEventHandler(self, window=None):
        """Custom handler for window events.
        We mostly use this for resizing."""

        # call showBase windowEvent which would otherwise get overridden and breaking the app
        self.windowEvent(window)

        if window != self.win:
            # This event isn't about our window.
            return

        if window is not None:  # window is none if panda3d is not started
            if self.screenSize == base.getSize():
                return
            self.screenSize = base.getSize()

            # Resize all editor frames
            self.menuBar.resizeFrame()

    # ------------------------------------------------------------------
    # SELECTION BOX
    # ------------------------------------------------------------------
    def startBoxDraw(self):
        """Start drawing the box"""
        if self.mouseWatcherNode.hasMouse():
            # get the mouse position
            self.startPos = LPoint2f(self.mouseWatcherNode.getMouse())
        taskMgr.add(self.dragBoxDrawTask, "dragBoxDrawTask")

    def stopBoxDraw(self):
        """Stop the draw box task and remove the box"""
        if not taskMgr.hasTaskNamed("dragBoxDrawTask"): return
        taskMgr.remove("dragBoxDrawTask")
        if self.startPos is None or self.lastPos is None: return
        self.nodeMgr.deselectAll()
        if self.box is not None:
            for node in self.nodeMgr.getAllNodes():
                # store some view scales for calculations
                viewXScale = self.viewNP.getScale().getX()
                viewZScale = self.viewNP.getScale().getZ()

                # calculate the node edges
                nodeLeft = node.getLeft() * viewXScale
                nodeRight = node.getRight() * viewXScale
                nodeBottom = node.getBottom() * viewZScale
                nodeTop = node.getTop() * viewZScale

                # calculate bounding box edges
                left = min(self.lastPos.getX(), self.startPos.getX())
                right = max(self.lastPos.getX(), self.startPos.getX())
                top = max(self.lastPos.getY(), self.startPos.getY())
                bottom = min(self.lastPos.getY(), self.startPos.getY())

                # check for hits
                xGood = yGood = False
                if left < nodeLeft and right > nodeLeft:
                    xGood = True
                elif left < nodeRight and right > nodeRight:
                    xGood = True
                if top > nodeTop and bottom < nodeTop:
                    yGood = True
                elif top > nodeBottom and bottom < nodeBottom:
                    yGood = True

                # check if we have any hits
                if xGood and yGood:
                    self.nodeMgr.selectNode(node, True, True)

            # Cleanup the selection box
            self.box.removeNode()
            self.startPos = None
            self.lastPos = None

    def dragBoxDrawTask(self, task):
        """This task will track the mouse position and actualize the box's size
        according to the first click position of the mouse"""
        if self.mouseWatcherNode.hasMouse():
            if self.startPos is None:
                self.startPos = LPoint2f(self.mouseWatcherNode.getMouse())
            # get the current mouse position
            self.lastPos = LPoint2f(self.mouseWatcherNode.getMouse())
        else:
            return task.cont

        # check if we already have a box
        if self.box != None:
            # if so, remove that old box
            self.box.removeNode()
        # set the box's size
        self.boxCardMaker.setFrame(self.lastPos.getX(), self.startPos.getX(),
                                   self.startPos.getY(), self.lastPos.getY())
        # generate, setup and draw the box
        node = self.boxCardMaker.generate()
        self.box = render2d.attachNewNode(node)
        self.box.setBin("gui-popup", 25)
        self.box.setTransparency(TransparencyAttrib.M_alpha)

        # run until the task is manually stopped
        return task.cont
Exemplo n.º 5
0
class NodeEditor(DirectObject):
    def __init__(self, parent, customNodeMap={}, customExporterMap={}):

        DirectObject.__init__(self)

        fn = Filename.fromOsSpecific(os.path.dirname(__file__))
        fn.makeTrueCase()
        self.icon_dir = str(fn) + "/"
        loadPrcFileData("", f"model-path {self.icon_dir}")

        #
        # NODE VIEW
        #
        self.viewNP = aspect2d.attachNewNode("viewNP")
        self.viewNP.setScale(0.5)

        #
        # NODE MANAGER
        #
        self.nodeMgr = NodeManager(self.viewNP, customNodeMap)

        # Drag view
        self.mouseSpeed = 1
        self.mousePos = None
        self.startCameraMovement = False

        # Box select
        # variables to store the start and current pos of the mousepointer
        self.startPos = LPoint2f(0, 0)
        self.lastPos = LPoint2f(0, 0)
        # variables for the to be drawn box
        self.boxCardMaker = CardMaker("SelectionBox")
        self.boxCardMaker.setColor(1, 1, 1, 0.25)
        self.box = None

        #
        # MENU BAR
        #
        self.mainView = MainView(parent, customNodeMap, customExporterMap)

        self.enable_editor()

    # ------------------------------------------------------------------
    # FRAME COMPATIBILITY FUNCTIONS
    # ------------------------------------------------------------------
    def is_dirty(self):
        """
        This method returns True if an unsaved state of the editor is given
        """
        return len(self.nodeMgr.getAllNodes()) > 0

    def enable_editor(self):
        """
        Enable the editor.
        """
        self.enable_events()

        # Task for handling dragging of the camera/view
        taskMgr.add(self.updateCam,
                    "NodeEditor_task_camActualisation",
                    priority=-4)

        self.viewNP.show()
        self.nodeMgr.showConnections()

    def disable_editor(self):
        """
        Disable the editor.
        """
        self.ignore_all()
        taskMgr.remove("NodeEditor_task_camActualisation")

        self.viewNP.hide()
        self.nodeMgr.hideConnections()

    def do_exception_save(self):
        """
        Save content of editor if the application crashes
        """
        Save(self.nodeMgr.nodeList, self.nodeMgr.connections, True)

    # ------------------------------------------------------------------
    # NODE EDITOR RELATED EVENTS
    # ------------------------------------------------------------------
    def enable_events(self):
        # Add nodes
        self.accept("addNode", self.nodeMgr.addNode)
        # Remove nodes
        self.accept("NodeEditor_removeNode", self.nodeMgr.removeNode)
        self.accept("x", self.nodeMgr.removeNode)
        self.accept("delete", self.nodeMgr.removeNode)
        # Selecting
        self.accept("selectNode", self.nodeMgr.selectNode)
        # Deselecting
        self.accept("mouse3", self.nodeMgr.deselectAll)
        # Node Drag and Drop
        self.accept("dragNodeStart", self.setDraggedNode)
        self.accept("dragNodeMove", self.updateNodeMove)
        self.accept("dragNodeStop", self.updateNodeStop)
        # Duplicate/Copy nodes
        self.accept("shift-d", self.nodeMgr.copyNodes)
        self.accept("NodeEditor_copyNodes", self.nodeMgr.copyNodes)
        # Refresh node logics
        self.accept("ctlr-r", self.nodeMgr.updateAllLeaveNodes)
        self.accept("NodeEditor_refreshNodes",
                    self.nodeMgr.updateAllLeaveNodes)

        #
        # SOCKET RELATED EVENTS
        #
        self.accept("updateConnectedNodes", self.nodeMgr.updateConnectedNodes)
        # Socket connection with drag and drop
        self.accept("startPlug", self.nodeMgr.setStartPlug)
        self.accept("endPlug", self.nodeMgr.setEndPlug)
        self.accept("connectPlugs", self.nodeMgr.connectPlugs)
        self.accept("cancelPlug", self.nodeMgr.cancelPlug)
        # Draw line while connecting sockets
        self.accept("startLineDrawing", self.startLineDrawing)
        self.accept("stopLineDrawing", self.stopLineDrawing)

        #
        # CONNECTION RELATED EVENTS
        #
        self.accept("NodeEditor_updateConnections",
                    self.nodeMgr.updateConnections)

        #
        # PROJECT MANAGEMENT
        #
        self.accept("NodeEditor_new", self.newProject)
        self.accept("NodeEditor_save", self.saveProject)
        self.accept("NodeEditor_load", self.loadProject)
        self.accept("quit_app", exit)

        #
        # EXPORTERS
        #
        self.accept("NodeEditor_customSave", self.customExport)

        #
        # EDITOR VIEW
        #
        # Zooming
        self.accept("NodeEditor_zoom", self.zoom)
        self.accept("NodeEditor_zoom_reset", self.zoomReset)
        self.accept("wheel_up", self.zoom, [True])
        self.accept("wheel_down", self.zoom, [False])

        # Drag view
        self.accept("mouse2", self.setMoveCamera, [True])
        self.accept("mouse2-up", self.setMoveCamera, [False])

        # Box select
        # accept the 1st mouse button events to start and stop the draw
        self.accept("mouse1", self.startBoxDraw)
        self.accept("mouse1-up", self.stopBoxDraw)

    # ------------------------------------------------------------------
    # PROJECT FUNCTIONS
    # ------------------------------------------------------------------
    def newProject(self):
        self.nodeMgr.cleanup()

    def saveProject(self):
        Save(self.nodeMgr.nodeList, self.nodeMgr.connections)

    def loadProject(self):
        self.nodeMgr.cleanup()
        Load(self.nodeMgr)

    def customExport(self, exporter):
        exporter(self.nodeMgr.nodeList, self.nodeMgr.connections)

    # ------------------------------------------------------------------
    # CAMERA SPECIFIC FUNCTIONS
    # ------------------------------------------------------------------
    def setMoveCamera(self, moveCamera):
        """Start dragging around the editor area/camera"""
        # store the mouse position if weh have a mouse
        if base.mouseWatcherNode.hasMouse():
            x = base.mouseWatcherNode.getMouseX()
            y = base.mouseWatcherNode.getMouseY()
            self.mousePos = Point2(x, y)
        # set the variable according to if we want to move the camera or not
        self.startCameraMovement = moveCamera

    def updateCam(self, task):
        """Task that will move the editor area/camera around according
        to mouse movements"""
        # variables to store the mouses current x and y position
        x = 0.0
        y = 0.0
        if base.mouseWatcherNode.hasMouse():
            # get the mouse position
            x = base.mouseWatcherNode.getMouseX()
            y = base.mouseWatcherNode.getMouseY()
        if base.mouseWatcherNode.hasMouse() \
        and self.mousePos is not None \
        and self.startCameraMovement:
            # Move the viewer node aspect independent
            wp = base.win.getProperties()
            aspX = 1.0
            aspY = 1.0
            wpXSize = wp.getXSize()
            wpYSize = wp.getYSize()
            if wpXSize > wpYSize:
                aspX = wpXSize / float(wpYSize)
            else:
                aspY = wpYSize / float(wpXSize)
            mouseMoveX = (self.mousePos.getX() - x) / self.viewNP.getScale(
            ).getX() * self.mouseSpeed * aspX
            mouseMoveY = (self.mousePos.getY() - y) / self.viewNP.getScale(
            ).getZ() * self.mouseSpeed * aspY
            self.mousePos = Point2(x, y)

            self.viewNP.setX(self.viewNP, -mouseMoveX)
            self.viewNP.setZ(self.viewNP, -mouseMoveY)

            self.nodeMgr.updateConnections()

        # continue the task until it got manually stopped
        return task.cont

    def zoom(self, zoomIn):
        """Zoom the editor in or out dependent on the value in zoomIn"""
        zoomFactor = 0.05
        maxZoomIn = 2
        maxZoomOut = 0.1
        if zoomIn:
            s = self.viewNP.getScale()
            if s.getX() - zoomFactor < maxZoomIn and s.getY(
            ) - zoomFactor < maxZoomIn and s.getZ() - zoomFactor < maxZoomIn:
                self.viewNP.setScale(s.getX() + zoomFactor,
                                     s.getY() + zoomFactor,
                                     s.getZ() + zoomFactor)
        else:
            s = self.viewNP.getScale()
            if s.getX() - zoomFactor > maxZoomOut and s.getY(
            ) - zoomFactor > maxZoomOut and s.getZ() - zoomFactor > maxZoomOut:
                self.viewNP.setScale(s.getX() - zoomFactor,
                                     s.getY() - zoomFactor,
                                     s.getZ() - zoomFactor)
        self.nodeMgr.updateConnections()

    def zoomReset(self):
        """Set the zoom level back to the default"""
        self.viewNP.setScale(0.5)
        self.nodeMgr.updateConnections()

    # ------------------------------------------------------------------
    # DRAG LINE
    # ------------------------------------------------------------------
    def startLineDrawing(self, startPos):
        """Start a task that will draw a line from the given start
        position to the cursor"""
        self.line = LineNodePath(render2d,
                                 thickness=2,
                                 colorVec=(0.8, 0.8, 0.8, 1))
        self.line.moveTo(startPos)
        t = taskMgr.add(self.drawLineTask, "drawLineTask")
        t.startPos = startPos

    def drawLineTask(self, task):
        """Draws a line from a given start position to the cursor"""
        mwn = base.mouseWatcherNode
        if mwn.hasMouse():
            pos = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1])

            self.line.reset()
            self.line.moveTo(task.startPos)
            self.line.drawTo(pos)
            self.line.create()
        return task.cont

    def stopLineDrawing(self):
        """Stop the task that draws a line to the cursor"""
        taskMgr.remove("drawLineTask")
        if self.line is not None:
            self.line.reset()
            self.line = None

    # ------------------------------------------------------------------
    # EDITOR NODE DRAGGING UPDATE
    # ------------------------------------------------------------------
    def setDraggedNode(self, node):
        """This will set the node that is currently dragged around
        as well as update other selected nodes which will be moved
        in addition to the main dragged node"""
        self.draggedNode = node
        self.draggedNode.disable()
        self.tempNodePositions = {}
        for node in self.nodeMgr.selectedNodes:
            self.tempNodePositions[node] = node.frame.getPos(render2d)

    def updateNodeMove(self, mouseA, mouseB):
        """Will be called as long as a node is beeing dragged around"""
        for node in self.nodeMgr.selectedNodes:
            if node is not self.draggedNode and node in self.tempNodePositions.keys(
            ):
                editVec = Vec3(self.tempNodePositions[node] - mouseA)
                newPos = mouseB + editVec
                node.frame.setPos(render2d, newPos)
        self.nodeMgr.updateConnections()

    def updateNodeStop(self, node=None):
        """Will be called when a node dragging stopped"""
        self.draggedNode.enable()
        self.draggedNode = None
        self.tempNodePositions = {}
        self.nodeMgr.updateConnections()

    # ------------------------------------------------------------------
    # SELECTION BOX
    # ------------------------------------------------------------------
    def startBoxDraw(self):
        """Start drawing the box"""
        if base.mouseWatcherNode.hasMouse():
            # get the mouse position
            self.startPos = LPoint2f(base.mouseWatcherNode.getMouse())
        taskMgr.add(self.dragBoxDrawTask, "dragBoxDrawTask")

    def stopBoxDraw(self):
        """Stop the draw box task and remove the box"""
        if not taskMgr.hasTaskNamed("dragBoxDrawTask"): return
        taskMgr.remove("dragBoxDrawTask")
        if self.startPos is None or self.lastPos is None: return
        self.nodeMgr.deselectAll()
        if self.box is not None:
            for node in self.nodeMgr.getAllNodes():
                # store some view scales for calculations
                viewXScale = self.viewNP.getScale().getX()
                viewZScale = self.viewNP.getScale().getZ()

                # calculate the node edges
                nodeLeft = node.getLeft() * viewXScale
                nodeRight = node.getRight() * viewXScale
                nodeBottom = node.getBottom() * viewZScale
                nodeTop = node.getTop() * viewZScale

                # calculate bounding box edges
                left = min(self.lastPos.getX(), self.startPos.getX())
                right = max(self.lastPos.getX(), self.startPos.getX())
                top = max(self.lastPos.getY(), self.startPos.getY())
                bottom = min(self.lastPos.getY(), self.startPos.getY())

                # check for hits
                xGood = yGood = False
                if left < nodeLeft and right > nodeLeft:
                    xGood = True
                elif left < nodeRight and right > nodeRight:
                    xGood = True
                if top > nodeTop and bottom < nodeTop:
                    yGood = True
                elif top > nodeBottom and bottom < nodeBottom:
                    yGood = True

                # check if we have any hits
                if xGood and yGood:
                    self.nodeMgr.selectNode(node, True, True)

            # Cleanup the selection box
            self.box.removeNode()
            self.startPos = None
            self.lastPos = None

    def dragBoxDrawTask(self, task):
        """This task will track the mouse position and actualize the box's size
        according to the first click position of the mouse"""
        if base.mouseWatcherNode.hasMouse():
            if self.startPos is None:
                self.startPos = LPoint2f(base.mouseWatcherNode.getMouse())
            # get the current mouse position
            self.lastPos = LPoint2f(base.mouseWatcherNode.getMouse())
        else:
            return task.cont

        # check if we already have a box
        if self.box != None:
            # if so, remove that old box
            self.box.removeNode()
        # set the box's size
        self.boxCardMaker.setFrame(self.lastPos.getX(), self.startPos.getX(),
                                   self.startPos.getY(), self.lastPos.getY())
        # generate, setup and draw the box
        node = self.boxCardMaker.generate()
        self.box = render2d.attachNewNode(node)
        self.box.setBin("gui-popup", 25)
        self.box.setTransparency(TransparencyAttrib.M_alpha)

        # run until the task is manually stopped
        return task.cont