class CameraController(DirectObject): def __init__(self, base, r, theta, phi): DirectObject.__init__(self) self.base = base # Parameters self.rotateMag = 0.5 self.moveMag = 50 self.zoomMag = 100 # Camera properties self.r = r self.theta = theta self.phi = phi self.target = NodePath("target") self.target.reparentTo(self.base.render) self.base.camera.reparentTo(self.target) self.followingObject = None # Controls self.mouseDown1 = False self.mouseDown2 = False self.mouseDown3 = False self.mousePrevX = 0.0 self.mousePrevY = 0.0 self.accept("mouse1", self.onMouse1, [True]) self.accept("mouse1-up", self.onMouse1, [False]) self.accept("mouse2", self.onMouse2, [True]) self.accept("mouse2-up", self.onMouse2, [False]) self.accept("mouse3", self.onMouse3, [True]) self.accept("mouse3-up", self.onMouse3, [False]) # Run task that updates camera self.base.taskMgr.add(self.updateCamera, "UpdateCameraTask", priority=1) def setTarget(self, parent): self.target.reparentTo(parent) def follow(self, obj): self.followingObject = obj def stopFollowing(self): self.followingObject = None def zoom(self, dR): self.r += dR if self.r < 0.0: self.r = 0.0 def rotateTheta(self, dTheta): self.theta += dTheta if self.theta < 0.0: self.theta += 2 * math.pi if self.theta > 2 * math.pi: self.theta -= 2 * math.pi def rotatePhi(self, dPhi): self.phi += dPhi if self.phi < 0.1: self.phi = 0.1 if self.phi > math.pi-0.1: self.phi = math.pi-0.1 def onMouse1(self, down): if not self.mouseDown2 and not self.mouseDown3: if down and self.base.mouseWatcherNode.hasMouse(): self.mouseDown1 = True self.mousePrevX = self.base.mouseWatcherNode.getMouseX() self.mousePrevY = self.base.mouseWatcherNode.getMouseY() else: self.mouseDown1 = False def onMouse2(self, down): if not self.mouseDown1 and not self.mouseDown3: if down and self.base.mouseWatcherNode.hasMouse(): self.mouseDown2 = True self.mousePrevX = self.base.mouseWatcherNode.getMouseX() self.mousePrevY = self.base.mouseWatcherNode.getMouseY() else: self.mouseDown2 = False def onMouse3(self, down): if not self.mouseDown1 and not self.mouseDown2: if down and self.base.mouseWatcherNode.hasMouse(): self.mouseDown3 = True self.mousePrevX = self.base.mouseWatcherNode.getMouseX() self.mousePrevY = self.base.mouseWatcherNode.getMouseY() else: self.mouseDown3 = False def updateCamera(self, task): if self.base.mouseWatcherNode.hasMouse(): # Register camera controls mouseX = self.base.mouseWatcherNode.getMouseX() mouseY = self.base.mouseWatcherNode.getMouseY() dX = self.mousePrevX - mouseX dY = self.mousePrevY - mouseY if self.mouseDown1: self.rotateTheta(dX * math.pi * self.rotateMag) self.rotatePhi(-dY * math.pi * self.rotateMag) if self.mouseDown2 and self.followingObject is None: vecX = self.target.getRelativeVector(self.base.camera, Vec3.right()) vecY = self.target.getRelativeVector(self.base.camera, Vec3.forward()) vecY.setZ(0.0) vecY.normalize() offset = (vecX * dX * self.moveMag) + (vecY * dY * self.moveMag) self.target.setPos(self.target, offset) if self.followingObject is not None: self.target.setPos(self.followingObject.getPos()) if self.mouseDown3: self.zoom(dY * self.zoomMag) self.mousePrevX = mouseX self.mousePrevY = mouseY # Update camera position position = Point3(0.0, 0.0, 0.0) position.setX(self.r * math.cos(self.theta) * math.sin(self.phi)) position.setY(self.r * math.sin(self.theta) * math.sin(self.phi)) position.setZ(self.r * math.cos(self.phi)) self.base.camera.setPos(position) self.base.camera.lookAt(self.target) return task.cont
class CameraController(DirectObject): def __init__(self, base, r, theta, phi): DirectObject.__init__(self) self.base = base # Parameters self.rotateMag = 0.5 self.moveMag = 50 self.zoomMag = 15 # Camera properties self.r = r self.theta = theta self.phi = phi self.target = NodePath("target") self.target.reparentTo(self.base.render) self.base.camera.reparentTo(self.target) # Controls self.mouseDown1 = False self.mouseDown2 = False self.mouseDown3 = False self.mousePrevX = 0.0 self.mousePrevY = 0.0 self.updateCamera(0, 0) def setTarget(self, parent): self.target.reparentTo(parent) def rotateTheta(self, value): self._rotateTheta(value) self.updateCamera(self.mousePrevX, self.mousePrevY) def rotatePhi(self, value): self._rotatePhi_for_scroll(math.radians(value)) self.updateCamera(self.mousePrevX, self.mousePrevY) def mouse_press(self, button, x, y): self.mouseDown1 = True if button == Qt.LeftButton else self.mouseDown1 self.mouseDown2 = True if button == Qt.RightButton else self.mouseDown2 self.mouseDown3 = True if button == Qt.MiddleButton else self.mouseDown3 self.mousePrevX = x self.mousePrevY = y self.updateCamera(x, y) def mouse_release(self, button, x, y): self.mouseDown1 = False if button == Qt.LeftButton else self.mouseDown1 self.mouseDown2 = False if button == Qt.RightButton else self.mouseDown2 self.mouseDown3 = False if button == Qt.MiddleButton else self.mouseDown3 self.mousePrevX = x self.mousePrevY = y def mouse_move(self, button, x, y): self.updateCamera(x, y) def zoom_event(self, delta): self.zoom(delta) self.updateCamera(self.mousePrevX, self.mousePrevY) def updateCamera(self, x, y): factor = 0.01 dX = (self.mousePrevX - x) * factor dY = (self.mousePrevY - y) * factor if self.mouseDown1: self._rotateTheta(dX * math.pi * self.rotateMag) self._rotatePhi(-dY * math.pi * self.rotateMag) if self.mouseDown2: vecX = self.target.getRelativeVector(self.base.camera, Vec3.right()) vecY = self.target.getRelativeVector(self.base.camera, Vec3.forward()) vecY.setZ(0.0) vecY.normalize() offset = (vecX * dX * self.moveMag) + (vecY * dY * self.moveMag) self.target.setPos(self.target, offset) if self.mouseDown3: self.zoom(dY * self.zoomMag) self.mousePrevX = x self.mousePrevY = y # Update camera position position = Point3(0.0, 0.0, 0.0) position.setX(self.r * math.cos(self.theta) * math.sin(self.phi)) position.setY(self.r * math.sin(self.theta) * math.sin(self.phi)) position.setZ(self.r * math.cos(self.phi)) self.base.camera.setPos(position) self.base.camera.lookAt(self.target) def zoom(self, delta): self.r += delta * 0.2 if self.r < 0.0: self.r = 0.0 def _rotateTheta(self, dTheta): self.theta += dTheta if self.theta < 0.0: self.theta += 2 * math.pi if self.theta > 2 * math.pi: self.theta -= 2 * math.pi def _rotatePhi(self, dPhi): self.phi += dPhi if self.phi < 0.1: self.phi = 0.1 if self.phi > math.pi - 0.1: self.phi = math.pi - 0.1 def _rotatePhi_for_scroll(self, dPhi): self.phi += dPhi if self.phi < 0.1: self.phi = 0.1 if self.phi > math.pi - 0.1: self.phi = self.phi - math.pi
class TQGraphicsNodePath: """ Anything that fundamentally is a only a graphics object in this engine should have these properties. Kwargs: TQGraphicsNodePath_creation_parent_node : this is assigned in the constructor, and after makeObject and the return of the p3d Nodepath, each TQGraphicsNodePath object has to call set_p3d_node """ def __init__(self, **kwargs): """ """ self.TQGraphicsNodePath_creation_parent_node = None self.p3d_nodepath = NodePath("empty") self.p3d_nodepath_changed_post_init_p = False self.p3d_parent_nodepath = NodePath("empty") self.node_p3d = None self.p3d_nodepath.reparentTo(self.p3d_parent_nodepath) self.apply_kwargs(**kwargs) def apply_kwargs(self, **kwargs): """ """ if 'color' in kwargs: TQGraphicsNodePath.setColor(self, kwargs.get('color')) else: TQGraphicsNodePath.setColor(self, Vec4(1., 1., 1., 1.)) def attach_to_render(self): """ """ # assert self.p3d_nodepath # self.p3d_nodepath.reparentTo(render) assert self.p3d_nodepath self.reparentTo(engine.tq_graphics_basics.tq_render) def attach_to_aspect2d(self): """ """ # assert self.p3d_nodepath # self.p3d_nodepath.reparentTo(aspect2d) assert self.p3d_nodepath self.reparentTo(engine.tq_graphics_basics.tq_aspect2d) def _set_p3d_nodepath_plain_post_init(p3d_nodepath): """ """ self.p3d_nodepath = p3d_nodepath self.p3d_nodepath_changed_post_init_p = True @staticmethod def from_p3d_nodepath(p3d_nodepath, **tq_graphics_nodepath_kwargs): """ factory """ go = TQGraphicsNodePath(**tq_graphics_nodepath_kwargs) go.set_p3d_nodepath(p3d_nodepath) return go def set_parent_node_for_nodepath_creation( self, TQGraphicsNodePath_creation_parent_node): """ when calling attachNewNode_p3d, a new node pat is generated. E.g.: To attach a line to render (3d world) is different than attaching it to aspect2d (2d GUI plane), since the aspect2d children are not directly affected by camera movements Args: - TQGraphicsNodePath_creation_parent_node : the NodePath to attach the TQGraphicsNodePath to (e.g. render or aspect2d in p3d) """ # self.set_parent_node_for_nodepath_creation(self.TQGraphicsNodePath_creation_parent_node) self.TQGraphicsNodePath_creation_parent_node = TQGraphicsNodePath_creation_parent_node def set_p3d_nodepath(self, p3d_nodepath, remove_old_nodepath=True): """ """ self.p3d_nodepath = p3d_nodepath def get_p3d_nodepath(self): """ """ return self.p3d_nodepath def set_render_above_all(self, p): """ set render order to be such that it renders normally (false), or above all (true) Args: p: True or False to enable or disable the 'above all' rendering mode """ try: if p == True: self.p3d_nodepath.setBin("fixed", 0) self.p3d_nodepath.setDepthTest(False) self.p3d_nodepath.setDepthWrite(False) else: self.p3d_nodepath.setBin("default", 0) self.p3d_nodepath.setDepthTest(True) self.p3d_nodepath.setDepthWrite(True) except NameError: # if p3d_nodepath is not yet defined print("NameError in set_render_above_all()") def remove(self): """ """ self.p3d_nodepath.removeNode() def setPos(self, *args, **kwargs): """ """ return self.p3d_nodepath.setPos(*args, **kwargs) def getPos(self): """ """ return self.p3d_nodepath.getPos() def setScale(self, *args, **kwargs): """ """ return self.p3d_nodepath.setScale(*args, **kwargs) def getScale(self): """ """ return self.p3d_nodepath.getScale() def setMat(self, *args, **kwargs): """ """ return self.p3d_nodepath.setMat(*args, **kwargs) def setMat_normal(self, mat4x4_normal_np): """ normal convention (numpy array), i.e. convert to forrowvecs convention for p3d setMat call """ return self.p3d_nodepath.setMat(math_utils.to_forrowvecs(mat4x4_normal_np)) def getMat(self, *args, **kwargs): """ """ return self.p3d_nodepath.getMat(*args, **kwargs) def getMat_normal(self, *args, **kwargs): """ """ return math_utils.from_forrowvecs(self.p3d_nodepath.getMat(*args, **kwargs)) def setTexture(self, *args, **kwargs): """ """ return self.p3d_nodepath.setTexture(*args, **kwargs) def setColor(self, *args, **kwargs): """ """ return self.p3d_nodepath.setColor(*args, **kwargs) def setTwoSided(self, *args, **kwargs): """ """ return self.p3d_nodepath.setTwoSided(*args, **kwargs) def setRenderModeWireframe(self, *args, **kwargs): """ """ return self.p3d_nodepath.setRenderModeWireframe(*args, **kwargs) def reparentTo(self, *args, **kwargs): """ """ new_args = list(args) new_args[0] = new_args[0].p3d_nodepath return self.p3d_nodepath.reparentTo(*new_args, **kwargs) def reparentTo_p3d(self, *args, **kwargs): """ input a p3d nodepath directly """ # new_args = list(args) # new_args[0] = new_args[0].p3d_nodepath return self.p3d_nodepath.reparentTo(*args, **kwargs) def get_node_p3d(self): """ """ # return self.p3d_nodepath.node() return self.node_p3d def set_node_p3d(self, node_p3d): """ not available in p3d NodePath class """ self.node_p3d = node_p3d def setRenderModeFilled(self, *args, **kwargs): """ """ return self.p3d_nodepath.setRenderModeFilled(*args, **kwargs) def setLightOff(self, *args, **kwargs): """ """ return self.p3d_nodepath.setLightOff(*args, **kwargs) def show(self): """ """ return self.p3d_nodepath.show() def hide(self): """ """ return self.p3d_nodepath.hide() def setRenderModeThickness(self, *args, **kwargs): """ """ return self.p3d_nodepath.setRenderModeThickness(*args, **kwargs) def removeNode(self): """ """ return self.p3d_nodepath.removeNode() def setHpr(self, *args, **kwargs): """ """ return self.p3d_nodepath.setHpr(*args, **kwargs) def showBounds(self): """ """ return self.p3d_nodepath.showBounds() def setCollideMask(self, *args, **kwargs): """ """ return self.p3d_nodepath.setCollideMask(*args, **kwargs) def getParent_p3d(self, *args, **kwargs): """ """ return self.p3d_nodepath.getParent(*args, **kwargs) def wrtReparentTo(self, *args, **kwargs): """ """ return self.p3d_nodepath.wrtReparentTo(*args, **kwargs) def setBin(self, *args, **kwargs): """ """ return self.p3d_nodepath.setBin(*args, **kwargs) def setDepthTest(self, *args, **kwargs): """ """ return self.p3d_nodepath.setDepthTest(*args, **kwargs) def setDepthWrite(self, *args, **kwargs): """ """ return self.p3d_nodepath.setDepthWrite(*args, **kwargs) def get_children_p3d(self, *args, **kwargs): """ """ return self.p3d_nodepath.get_children(*args, **kwargs) def attachNewNode_p3d(self, *args, **kwargs): """ """ return self.p3d_nodepath.attachNewNode(*args, **kwargs) def lookAt(self, *args, **kwargs): """ """ return self.p3d_nodepath.lookAt(*args, **kwargs) def setLight(self, *args, **kwargs): """ """ return self.p3d_nodepath.setLight(*args, **kwargs) def setAntialias(self, *args, **kwargs): """ """ return self.p3d_nodepath.setAntialias(*args, **kwargs) def getRelativeVector(self, *args, **kwargs): """ """ return self.p3d_nodepath.getRelativeVector(*args, **kwargs) def setTransparency(self, *args, **kwargs): """ """ return self.p3d_nodepath.setTransparency(*args, **kwargs) def getHpr(self, *args, **kwargs): """ """ return self.p3d_nodepath.getHpr(*args, **kwargs) def setHpr(self, *args, **kwargs): """ """ return self.p3d_nodepath.setHpr(*args, **kwargs) def getTightBounds(self, *args, **kwargs): """ """ return self.p3d_nodepath.getTightBounds(*args, **kwargs) def showTightBounds(self, *args, **kwargs): """ """ return self.p3d_nodepath.showTightBounds(*args, **kwargs)