def __init__(self, length = 0.4, cyradius = 0.005, coradius = 0.075, mid=0.80, parent=None): """ Constructor. @param cyradius The radius of the cylinder used in the arrow shaft @param coradius The radius of the cone used in the arrow tip @param mid The "mid point" or the percentage [0,1] along the length where the cylinder and cone join @param parent The parent SceneGraphNode instance. """ super(GnomonNode, self).__init__(self.__description__, parent) self.__length = length self.__o = Vector3D(); self.__x = Vector3D(length, 0, 0) self.__y = Vector3D(0, length, 0) self.__z = Vector3D(0, 0, length) self.__mid = mid self.__cyradius = cyradius self.__coradius = coradius self._updateGeometry()
def paint_enter(self): """ Implements the GLNodeAdapter's paint_enter method for an OpenGL Render operation. """ GL.glPushAttrib(GL.GL_ENABLE_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_LINE_BIT | GL.GL_CURRENT_BIT) GL.glPushClientAttrib(GL.GL_CLIENT_VERTEX_ARRAY_BIT) GL.glMatrixMode(GL.GL_MODELVIEW) GL.glPushMatrix() GL.glMultMatrixf(self._node.matrix().data()) GL.glColor(self._node.color.getRgbF()) # The positive Z-axis is the default direction for Cylinder Quadrics in OpenGL. # If our vector is not parallel to the z-axis, e.g. (0, 0, Z), then rotate it. # 1) Get a normal from the z-v plane # 2) Get the angle inbetween z-v on the plane (see vector dot product) # 3) Rotate the normal by that angle. v = self._node.axis m = Matrix4x4.identity() if v.x != 0 or v.y != 0: zaxis = Vector3D(0,0,1) angle = zaxis.angle(v) normal = zaxis.crossproduct(v, True) m *= Matrix4x4.rotation(angle, normal) # The positive Z-axis is the default direction fo Cylinder Quadrics in OpenGL. # If our z is negative, we need to flip the cylinder if v.z < 0: yaxis = Vector3D(1,0,0) m *= Matrix4x4.rotation(math.radians(180), yaxis) GL.glMultMatrixf(m.data()) q = self.__quadric r = self._node.radius h = self._node.length sl = self._node.slices st = self._node.stacks lp = self._node.loops GLU.gluQuadricDrawStyle (q, GLU.GLU_FILL) GLU.gluQuadricNormals (q, GLU.GLU_SMOOTH) GLU.gluQuadricOrientation(q, GLU.GLU_OUTSIDE) self._glcylinder(q, r, h, sl, st, lp)
def __init__(self, direction=None): """ Constructor. @param direction The Translation direction vector. """ super(TranslationComponent, self).__init__() self.__direction = None self.blockSignals(True) self.direction = direction or Vector3D(0, 0, 0) self.blockSignals(False) self._updateMatrix()
def __init__(self, length = 0.5, width = 0.5, normal = Vector3D(0,1,0), parent=None): """ Constructor. @param length The length of one dimension of the plane. @param width The length of one dimension of the plane. @param normal The surface normal of the plane. @param parent The parent SceneGraphNode instance. """ super(PlaneNode, self).__init__(self.__description__, parent) self.__length = length self.__width = width self.__normal = normal
def __init__(self, radius = 1.0, length = 1, axis=Vector3D(0,1,0), parent=None): """ Constructor. @param radius The radius of the cylinder base. @param length The length of the cylinder. @param axis The axis of the cylinder. @param parent The parent SceneGraphNode instance. """ super(CylinderNode, self).__init__(self.__description__, parent) self.__radius = radius self.__length = length self.__axis = axis
def __init__(self, spacing=0.5, count = 14, normal=Vector3D(0,1,0), parent=None): """ Constructor. @param count The number of lines in the grid. @param space The spaceing between lines. @param length The length of one dimension of the plane. @param width The length of one dimension of the plane. @param normal The surface normal of the plane. @param parent The parent SceneGraphNode instance. """ super(GridNode, self).__init__(spacing * count, spacing * count, normal, parent) self.__spacing = spacing self.__count = count
def __init__(self, angle=0, axis=None, point=None): """ Constructor. @param angle The initial rotation angle (degrees). @param axis The initial rotation axis. @param point The initial rotation origin point. """ super(RotationComponent, self).__init__() self.__angle = None self.__axis = None self.__point = None self.blockSignals(True) self.angle = angle self.axis = axis or Vector3D(0, 0, 0) self.point = point or Point3D(0, 0, 0) self.blockSignals(False) self._updateMatrix()
def __init__(self, radius=1.0, length=1, axis=Vector3D(0, 1, 0), slices=32, stacks=1, loops=1, parent=None): """ Constructor. @param radius The radius of the cylinder base. @param length The length of the cylinder. @param axis The axis of the cylinder. @param slices The number of subdivisions around the z-axis (similar to lines of longitude). @param stacks The number of subdivisions along the z-axis (similar to lines of latitude). @param loops The number of concentric rings about the origin into which the cylinder's base is subdivided. @param parent The parent SceneGraphNode instance. """ super(QuadricCylinderNode, self).__init__(radius, length, axis, parent) self.__stacks = stacks self.__slices = slices self.__loops = loops
class CameraNode(ObjectNode, VirtualScreen): """ The Camera Node provides Camera implementation of a AbstractSceneGraphItem. """ # Additional Meta Information __category__ = "Camera Node" __icon__ = ":/icons/camera.png" __description__ = "Camera" __instantiable__ = True # Camera default values __camera_target__ = Point3D(0, 0, 0) __camera_position__ = Point3D(21, 28, 21) __camera_upvector__ = Vector3D(0, 1, 0) __camera_fov__ = 30.0 # (degrees) __camera_znear__ = 1.0 __camera_zfar__ = 1000 __camera_swidth__ = 1.0 __camera_sheight__ = 1.0 # The rotation speed; The amount of rotation (in degrees) that will occur # from dragging the mouse across the entire width of the screen. __camera_max_screen_rotation__ = 180.0 def __init__(self, position=None, target=None, up=None, fov=None, znear=None, zfar=None, swidth=None, sheight=None, parent=None): """ Constructor. @param position The position of the camera (in world space). If None, it will default to CameraNode.__camera_target__. @param target The camera target point (in world space). If None, it will default to CameraNode.__camera_position__. @param up The camera up point (in world space). If None, it will default to CameraNode.__camera_upvector__. @param fov The field of view (in degrees). If None, it will default to CameraNode.__camera_fov__. @param znear The distance from position to the near clipping plane. If None, it will default to CameraNode.__camera_znear__. @param zfar The distance from position to the far clipping plane. If None, it will default to CameraNode.__camera_zfar__. @param swidth The initial screen width (in pixels). If None, it will default to CameraNode.__camera_swidth__. @param sheight The initial screen height (in pixels). If None, it will default to CameraNode.__camera_sheight__. @param parent The parent AbstractSceneGraphItem instance. """ super(CameraNode, self).__init__("camera", parent) # Register the 'reset' data self._restore['_znear'] = znear or self.__camera_znear__ self._restore['_zfar'] = zfar or self.__camera_zfar__ self._restore['_screenwidth'] = swidth or self.__camera_swidth__ self._restore['_screenheight'] = sheight or self.__camera_sheight__ self._restore['_fov'] = fov or self.__camera_fov__ self._restore['_position'] = (position or self.__camera_position__).duplicate() self._restore['_target'] = (target or self.__camera_target__).duplicate() self._restore['_up'] = (up or self.__camera_upvector__).duplicate() self._restore['_viewport'] = self._generateViewport(self._restore['_fov'], self._restore['_screenwidth'], self._restore['_screenheight'], self._restore['_znear']) self.__projectionmatrix = None self.reset() self._updateProjectionMatrix() @property def znear(self): return self._znear; @property def zfar(self): return self._zfar; @property def screenwidth(self): return self._screenwidth; @property def screenheight(self): return self._screenheight; @property def fov(self): return self._fov; @property def position(self): return self._position; @property def target(self): return self._target; @property def up(self): return self._up; @property def viewport(self): return self._viewport; def _generateProjectionMatrix(self): """ Generates a LookAt matrix from internal data. @returns A Matrix4x4 representation of camera's LookAt component. """ return Matrix4x4.lookAt(self._position, self._target, self._up, False) def _generateViewport(self, fov, width, height, znear): """ Generates a viewport from the dimensions. @param fov The Field of View angle (in degrees). @param width The screen width (in pixels). @param height The screen height (in pixels). @param znear The z position of the near clipping plane. @returns A tuple of (left, right, bottom, top) of the viewing frustrum at znear. """ # Figure out the viewport rectangle that contains the conocircle along it's shortest # dimensions; maintain the ratio of the otherdimension. if width < height: znear_width = conicwidth(fov, znear) znear_height = znear_width * height / width else: znear_height = conicwidth(fov, znear) znear_width = znear_height * width / height # Center the viewport zleft = - 0.5 * znear_width zright = 0.5 * znear_width zbottom = - 0.5 * znear_height ztop = 0.5 * znear_height return (zleft, zright, zbottom, ztop) def _updateProjectionMatrix(self): """ Internal method to manually update the cached transformation matrix from internal data. """ self.__projectionmatrix = self._generateProjectionMatrix() def projectionMatrix(self): """ Returns the component pre-calcaulted transformation matrix. @returns A Matrix4x4 representation of the transformation component. """ return self.__projectionmatrix def resize(self, width, height): """ Resizes the virtual screen. """ self._viewport = self._generateViewport(self._fov, width, height, self._znear) self._screenminsize = min(width, height) self._screenwidth = width self._screenheight = height self._rotationspeed = self.__camera_max_screen_rotation__ / self._screenminsize self._pixelradians = math.radians(self._rotationspeed) def tumble(self, hdelta, vdelta): """ This is equivalent of rotating the camera around it's target point while maintaining the direction vector; to reflect the change in view the camera will be rotated by the additive inverse of the deltas. @param hdelta The horizontal delta (the up vector rotation) @param vdelta The vertical delta (the right vector rotation) @see Maya Rotation defintions (http://download.autodesk.com/global/docs/maya2013/en_us/index.html?url=files/Viewing_the_scene_Tumble_track_dolly_or_tilt_the_view.htm,topicNumber=d30e15213) @see Rotation algorithm (http://gamedev.stackexchange.com/questions/20758/how-can-i-orbit-a-camera-about-its-target-point) """ # Get the current look at vector (e.g inverse direction) idirection = self._position - self._target # Rotate the y-axis (aka 'up' vector) which is the plane normal coming out of the x-z plane. # The x-z plane is typically the horizontal plane and the yaw angle is measured by the horizontal delta parameter. hrotation = - hdelta * self._pixelradians M = Matrix4x4.rotation( hrotation, self.__camera_upvector__ ) idirection = M * idirection self._up = M * self._up right = (self._up ^ idirection).normalized() # Now rotate the x-axis (aka 'right' vector) which is the plane normal coming out of the y-z plane. # The y-z plane is typically the vertical plane and the pitch angle is measured by the vertical delta parameter. vrotation = - vdelta * self._pixelradians M = Matrix4x4.rotation( vrotation, right ) idirection = M * idirection self._up = M * self._up # Update the camera's position self._position = self._target + idirection self._updateProjectionMatrix() def roll(self, delta): """ Executes a rotation operation around the z-axis This is equivalent of rotating the camera around it's z-axis; to reflect the change in view the camera will be rotated by the additive inverse of the delta. @param delta The rotation delta (the change in rotating around the direction vector) """ # Get the current look at vector (e.g inverse direction) direction = self._target - self._position # Rotate the z-axis (aka 'look at' vector) which is the plane normal coming out of the x-y plane. # Since we're rotating the x-y plane, the concept of vertical or horizontal delta doesn't apply; it's # a straight up spin of the look at vector rotation = - delta * self._pixelradians M = Matrix4x4.rotation( rotation, direction ) self._up = M * self._up self._updateProjectionMatrix() def track( self, hdelta, vdelta ): """ Executes a translate operation the x-y axis. This is equivalent of moving the scene along the x-y axis; to reflect the change in view the camera will be translated by the additive inverse of the deltas. @param hdelta The horizontal delta (the change in distance along the right vector) @param vdelta The vertical delta (the change in distance along the up vector) """ # We need to calculation a ratio of pixels to world units for speed. # Get the direction vector (normalized) and the distance. direction = self._target - self._position distance = direction.length() direction = direction.normalized() # Get the view width at the point along the z-axis and build a pixels to unit ratio viewwidth = conicwidth(self._fov, distance) factor = viewwidth / self._screenminsize # Update the right vector right = direction ^ self._up # Calcaulte the x-y axis translation vector translation = right * (- hdelta * factor) + self._up * (- vdelta * factor) # Move the Position and Target self._position += translation self._target += translation self._updateProjectionMatrix() def dolly( self, delta ): """ Executes a translate operation the z-axis. This is equivalent of moving the camera along the z-axis; to reflect the change in view the camera will be translated by the additive inverse of the delta. @param delta The translation delta (the change in distance along the direction vector) """ # We need to calculation a ratio of pixels to world units for speed. # Get the direction vector (normalized) and the distance. direction = self._target - self._position distance = direction.length() direction = direction.normalized() # Get the view width at the point along the z-axis and build a pixels to unit ratio viewwidth = conicwidth(self._fov, distance) factor = viewwidth / self._screenminsize # Calculate the translation along the direction vector translation = direction * (- delta * factor) # Move the Position only; the look at point reamins unchanged. self._position += translation self._updateProjectionMatrix() def zoom( self, delta ): """ Executes a zoom operation. This is equivalent of zooming into the scene without moving the camera; to reflect the change in view the camera fov will be scaled by the additive inverse of the delta. @param delta The fov delta (the change in fov, in degrees) @see http://gamedev.stackexchange.com/questions/30357/3d-zooming-technique-to-maintain-the-relative-position-of-an-object-on-screen """ # Clamp the __camera_fov__ (in degrees) to 1 and 180 self._fov = sorted((1, self._fov - delta, 180))[1] self._viewport = self._generateViewport(self._fov, self._screenwidth, self._screenheight, self._znear)