zoukankan      html  css  js  c++  java
  • Python>>>创建一个简单的3D场景(2)

    使用trackball 来旋转场景

    工程文件下载

    效果如下:

    当鼠标点击和在窗口移动时,x,y的值以左上角为原点,

    首先变换成左下角为原点。然后整个窗口map到【-1,1】,【-1,1】

    的平面上。

    x  = (x*2.0 - width)/width
    dx = (2.*dx)/width
    y = (y*2.0 - height)/height
    dy = (2.*dy)/height

    trackball 原理,在viewport上“增加”一个半球体,x,y值都和平面上的一样。

    从p1移动到p2

    计算出移动轴

    和角度。

    
    

    trackball代码

    __docformat__ = 'restructuredtext'
    __version__ = '1.0'
    
    import math
    import OpenGL.GL as gl
    from OpenGL.GL import GLfloat
    
    
    # Some useful functions on vectors
    # -----------------------------------------------------------------------------
    def _v_add(v1, v2):
        return [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]]
    def _v_sub(v1, v2):
        return [v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]]
    def _v_mul(v, s):
        return [v[0]*s, v[1]*s, v[2]*s]
    def _v_dot(v1, v2):
        return v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]
    def _v_cross(v1, v2):
        return [(v1[1]*v2[2]) - (v1[2]*v2[1]),
                (v1[2]*v2[0]) - (v1[0]*v2[2]),
                (v1[0]*v2[1]) - (v1[1]*v2[0])]
    def _v_length(v):
        return math.sqrt(_v_dot(v,v))
    def _v_normalize(v):
        try:                      return _v_mul(v,1.0/_v_length(v))
        except ZeroDivisionError: return v
    
    # Some useful functions on quaternions
    # -----------------------------------------------------------------------------
    def _q_add(q1,q2):
        t1 = _v_mul(q1, q2[3])
        t2 = _v_mul(q2, q1[3])
        t3 = _v_cross(q2, q1)
        tf = _v_add(t1, t2)
        tf = _v_add(t3, tf)
        tf.append(q1[3]*q2[3]-_v_dot(q1,q2))
        return tf
    def _q_mul(q, s):
        return [q[0]*s, q[1]*s, q[2]*s, q[3]*s]
    def _q_dot(q1, q2):
        return q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2] + q1[3]*q2[3]
    def _q_length(q):
        return math.sqrt(_q_dot(q,q))
    def _q_normalize(q):
        try:                      return _q_mul(q,1.0/_q_length(q))
        except ZeroDivisionError: return q
    def _q_from_axis_angle(v, phi):
        q = _v_mul(_v_normalize(v), math.sin(phi/2.0))
        q.append(math.cos(phi/2.0))
        return q
    def _q_rotmatrix(q):
        m = [0.0]*16
        m[0*4+0] = 1.0 - 2.0*(q[1]*q[1] + q[2]*q[2])
        m[0*4+1] = 2.0 * (q[0]*q[1] - q[2]*q[3])
        m[0*4+2] = 2.0 * (q[2]*q[0] + q[1]*q[3])
        m[0*4+3] = 0.0
        m[1*4+0] = 2.0 * (q[0]*q[1] + q[2]*q[3])
        m[1*4+1] = 1.0 - 2.0*(q[2]*q[2] + q[0]*q[0])
        m[1*4+2] = 2.0 * (q[1]*q[2] - q[0]*q[3])
        m[1*4+3] = 0.0
        m[2*4+0] = 2.0 * (q[2]*q[0] - q[1]*q[3])
        m[2*4+1] = 2.0 * (q[1]*q[2] + q[0]*q[3])
        m[2*4+2] = 1.0 - 2.0*(q[1]*q[1] + q[0]*q[0])
        m[3*4+3] = 1.0
        return m
    
    
    
    class Trackball(object):
        ''' Virtual trackball for 3D scene viewing. '''
    
        def __init__(self, theta=0, phi=0, zoom=1, distance=3):
            ''' Build a new trackball with specified view '''
    
            self._rotation = [0,0,0,1]
            self.zoom = zoom
            self.distance = distance
            self._count = 0
            self._matrix=None
            self._RENORMCOUNT = 97
            self._TRACKBALLSIZE = 0.8
            self._set_orientation(theta,phi)
            self._x = 0.0
            self._y = 0.0
    
        def drag_to (self, x, y, dx, dy):
            ''' Move trackball view from x,y to x+dx,y+dy. '''
            viewport = gl.glGetIntegerv(gl.GL_VIEWPORT)
            width,height = float(viewport[2]), float(viewport[3])
            x  = (x*2.0 - width)/width
            dx = (2.*dx)/width
            y  = (y*2.0 - height)/height
            dy = (2.*dy)/height
            q = self._rotate(x,y,dx,dy)
            self._rotation = _q_add(q,self._rotation)
            self._count += 1
            if self._count > self._RENORMCOUNT:
                self._rotation = _q_normalize(self._rotation)
                self._count = 0
            m = _q_rotmatrix(self._rotation)
            self._matrix = (GLfloat*len(m))(*m)
    
        def zoom_to (self, x, y, dx, dy):
            ''' Zoom trackball by a factor dy '''
            viewport = gl.glGetIntegerv(gl.GL_VIEWPORT)
            height = float(viewport[3])
            self.zoom = self.zoom-5*dy/height
    
        def pan_to (self, x, y, dx, dy):
            ''' Pan trackball by a factor dx,dy '''
            self.x += dx*0.1
            self.y += dy*0.1
    
    
        def push(self):
            viewport = gl.glGetIntegerv(gl.GL_VIEWPORT)
            gl.glMatrixMode(gl.GL_PROJECTION)
            gl.glPushMatrix()
            gl.glLoadIdentity ()
            aspect = viewport[2]/float(viewport[3])
            aperture = 35.0
            near = 0.1
            far = 100.0
            top = math.tan(aperture*3.14159/360.0) * near * self._zoom
            bottom = -top
            left = aspect * bottom
            right = aspect * top
            gl.glFrustum (left, right, bottom, top, near, far)
            gl.glMatrixMode (gl.GL_MODELVIEW)
            gl.glPushMatrix()
            gl.glLoadIdentity ()
            # gl.glTranslate (0.0, 0, -self._distance)
            gl.glTranslate (self._x, self._y, -self._distance)
            gl.glMultMatrixf (self._matrix)
    
        def pop(void):
            gl.glMatrixMode(gl.GL_MODELVIEW)
            gl.glPopMatrix()
            gl.glMatrixMode(gl.GL_PROJECTION)
            gl.glPopMatrix()
    
        def _get_matrix(self):
            return self._matrix
        matrix = property(_get_matrix,
                         doc='''Model view matrix transformation (read-only)''')
    
        def _get_zoom(self):
            return self._zoom
        def _set_zoom(self, zoom):
            self._zoom = zoom
            if self._zoom < .25: self._zoom = .25
            if self._zoom > 10: self._zoom = 10
        zoom = property(_get_zoom, _set_zoom,
                         doc='''Zoom factor''')
    
        def _get_distance(self):
            return self._distance
        def _set_distance(self, distance):
            self._distance = distance
            if self._distance < 1: self._distance= 1
        distance = property(_get_distance, _set_distance,
                            doc='''Scene distance from point of view''')
    
        def _get_theta(self):
            self._theta, self._phi = self._get_orientation()
            return self._theta
        def _set_theta(self, theta):
            self._set_orientation(math.fmod(theta,360.0),
                                  math.fmod(self._phi,360.0))
        theta = property(_get_theta, _set_theta,
                         doc='''Angle (in degrees) around the z axis''')
    
    
        def _get_phi(self):
            self._theta, self._phi = self._get_orientation()
            return self._phi
        def _set_phi(self, phi):
            self._set_orientation(math.fmod(self._theta,360.),
                                  math.fmod(phi,360.0))
        phi = property(_get_phi, _set_phi,
                         doc='''Angle around x axis''')
    
    
        def _get_orientation(self):
            ''' Return current computed orientation (theta,phi). ''' 
    
            q0,q1,q2,q3 = self._rotation
            ax = math.atan(2*(q0*q1+q2*q3)/(1-2*(q1*q1+q2*q2)))*180.0/math.pi
            az = math.atan(2*(q0*q3+q1*q2)/(1-2*(q2*q2+q3*q3)))*180.0/math.pi
            return -az,ax
    
        def _set_orientation(self, theta, phi):
            ''' Computes rotation corresponding to theta and phi. ''' 
    
            self._theta = theta
            self._phi = phi
            angle = self._theta*(math.pi/180.0)
            sine = math.sin(0.5*angle)
            xrot = [1*sine, 0, 0, math.cos(0.5*angle)]
            angle = self._phi*(math.pi/180.0)
            sine = math.sin(0.5*angle);
            zrot = [0, 0, sine, math.cos(0.5*angle)]
            self._rotation = _q_add(xrot, zrot)
            m = _q_rotmatrix(self._rotation)
            self._matrix = (GLfloat*len(m))(*m)
    
    
        def _project(self, r, x, y):
            ''' Project an x,y pair onto a sphere of radius r OR a hyperbolic sheet
                if we are away from the center of the sphere.
            '''
    
            d = math.sqrt(x*x + y*y)
            if (d < r * 0.70710678118654752440):    # Inside sphere
                z = math.sqrt(r*r - d*d)
            else:                                   # On hyperbola
                t = r / 1.41421356237309504880
                z = t*t / d
            return z
    
    
        def _rotate(self, x, y, dx, dy): 
            ''' Simulate a track-ball.
    
                Project the points onto the virtual trackball, then figure out the
                axis of rotation, which is the cross product of x,y and x+dx,y+dy.
    
                Note: This is a deformed trackball-- this is a trackball in the
                center, but is deformed into a hyperbolic sheet of rotation away
                from the center.  This particular function was chosen after trying
                out several variations.
            '''
    
            if not dx and not dy:
                return [ 0.0, 0.0, 0.0, 1.0]
            last = [x, y,       self._project(self._TRACKBALLSIZE, x, y)]
            new  = [x+dx, y+dy, self._project(self._TRACKBALLSIZE, x+dx, y+dy)]
            a = _v_cross(new, last)
            d = _v_sub(last, new)
            t = _v_length(d) / (2.0*self._TRACKBALLSIZE)
            if (t > 1.0): t = 1.0
            if (t < -1.0): t = -1.0
            phi = 2.0 * math.asin(t)
            return _q_from_axis_angle(a,phi)
    
    
        def __str__(self):
            phi = str(self.phi)
            theta = str(self.theta)
            zoom = str(self.zoom)
            return 'Trackball(phi=%s,theta=%s,zoom=%s)' % (phi,theta,zoom)
    
        def __repr__(self):
            phi = str(self.phi)
            theta = str(self.theta)
            zoom = str(self.zoom)
            return 'Trackball(phi=%s,theta=%s,zoom=%s)' % (phi,theta,zoom)
  • 相关阅读:
    Qt 学习之路 2(51):布尔表达式树模型
    Qt 学习之路 2(50):自定义可编辑模型
    Qt 学习之路 2(49):自定义只读模型
    Qt 学习之路 2(48):QSortFilterProxyModel
    spring boot 定时任务
    mybatis 操作总结
    MySQL 中常用的函数
    Maven dependencies 与 dependencyManagement 区别
    springboot 校验机制 @Validated @Valid
    Fork JOIN 分而治之
  • 原文地址:https://www.cnblogs.com/legion/p/6273565.html
Copyright © 2011-2022 走看看