Ver código fonte

Merge git://github.com/bjowi/assimp

acgessler 12 anos atrás
pai
commit
29a7126589

+ 3 - 4
Readme.md

@@ -1,4 +1,4 @@
-Open Asset Import Library (_assimp_) 
+Open Asset Import Library (assimp) 
 ========
 ========
 
 
 
 
@@ -29,8 +29,9 @@ __Note__: this `README` refers to the file structure used by release packages, w
 The library provides importers for a lot of file formats, including:
 The library provides importers for a lot of file formats, including:
 
 
 - 3DS
 - 3DS
-- BLEND 
+- BLEND (Blender 3D)
 - DAE (Collada)
 - DAE (Collada)
+- FBX
 - IFC-STEP 
 - IFC-STEP 
 - ASE
 - ASE
 - DXF
 - DXF
@@ -121,5 +122,3 @@ For the formal details, see the `LICENSE` file.
 
 
 
 
 ------------------------------
 ------------------------------
-
-(This repository is a mirror of the SVN repository located [here](https://assimp.svn.sourceforge.net/svnroot/assimp). Thanks to [klickverbot](https://github.com/klickverbot) for setting this up!)

+ 4 - 4
assimp.pc.in

@@ -1,10 +1,10 @@
 prefix=@CMAKE_INSTALL_PREFIX@
 prefix=@CMAKE_INSTALL_PREFIX@
-exec_prefix=@CMAKE_INSTALL_PREFIX@/@BIN_INSTALL_DIR@
-libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
-includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@/assimp
+exec_prefix=@CMAKE_INSTALL_PREFIX@/@ASSIMP_BIN_INSTALL_DIR@
+libdir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_LIB_INSTALL_DIR@
+includedir=@CMAKE_INSTALL_PREFIX@/@ASSIMP_INCLUDE_INSTALL_DIR@/assimp
 
 
 Name: @CMAKE_PROJECT_NAME@
 Name: @CMAKE_PROJECT_NAME@
 Description: Import various well-known 3D model formats in an uniform manner.
 Description: Import various well-known 3D model formats in an uniform manner.
 Version: @PROJECT_VERSION@
 Version: @PROJECT_VERSION@
 Libs: -L${libdir} -lassimp@ASSIMP_LIBRARY_SUFFIX@
 Libs: -L${libdir} -lassimp@ASSIMP_LIBRARY_SUFFIX@
-Cflags: -I${includedir} 
+Cflags: -I${includedir}

+ 11 - 11
port/PyAssimp/README.md

@@ -1,8 +1,8 @@
 PyAssimp Readme
 PyAssimp Readme
 ===============
 ===============
 
 
--- a simple Python wrapper for Assimp using ctypes to access
-the library. Requires Python >= 2.6.
+A simple Python wrapper for Assimp using `ctypes` to access the library.
+Requires Python >= 2.6.
 
 
 Python 3 support is mostly here, but not well tested.
 Python 3 support is mostly here, but not well tested.
 
 
@@ -12,10 +12,10 @@ particular, only loading of models is currently supported (no export).
 USAGE
 USAGE
 -----
 -----
 
 
-To get started with pyAssimp, examine the sample.py script in scripts/, which
-illustrates the basic usage. All Assimp data structures are wrapped using
+To get started with pyAssimp, examine the `sample.py` script in `scripts/`,
+which illustrates the basic usage. All Assimp data structures are wrapped using
 ctypes. All the data+length fields in Assimp's data structures (such as
 ctypes. All the data+length fields in Assimp's data structures (such as
-'aiMesh::mNumVertices','aiMesh::mVertices') are replaced by simple python
+`aiMesh::mNumVertices`, `aiMesh::mVertices`) are replaced by simple python
 lists, so you can call len() on them to get their respective size and access
 lists, so you can call len() on them to get their respective size and access
 members using [].
 members using [].
 
 
@@ -57,18 +57,18 @@ release(scene)
 INSTALL
 INSTALL
 -------
 -------
 
 
-Install pyassimp by running:
+Install `pyassimp` by running:
 
 
 > python setup.py install
 > python setup.py install
 
 
-PyAssimp requires a assimp dynamic library (DLL on windows,
-so on linux :-) in order to work. The default search directories 
+PyAssimp requires a assimp dynamic library (`DLL` on windows,
+`.so` on linux :-) in order to work. The default search directories 
 are:
 are:
 
 
 - the current directory
 - the current directory
-- on linux additionally: /usr/lib and /usr/local/lib
+- on linux additionally: `/usr/lib` and `/usr/local/lib`
 
 
 To build that library, refer to the Assimp master INSTALL
 To build that library, refer to the Assimp master INSTALL
-instructions. To look in more places, edit ./pyassimp/helper.py.
-There's an 'additional_dirs' list waiting for your entries.
+instructions. To look in more places, edit `./pyassimp/helper.py`.
+There's an `additional_dirs` list waiting for your entries.
 
 

+ 6 - 38
port/PyAssimp/pyassimp/core.py

@@ -346,60 +346,28 @@ def _get_properties(properties, length):
     for p in [properties[i] for i in range(length)]:
     for p in [properties[i] for i in range(length)]:
         #the name
         #the name
         p = p.contents
         p = p.contents
-        key = str(p.mKey.data)
+        key = str(p.mKey.data).split('.')[1]
 
 
         #the data
         #the data
         from ctypes import POINTER, cast, c_int, c_float, sizeof
         from ctypes import POINTER, cast, c_int, c_float, sizeof
         if p.mType == 1:
         if p.mType == 1:
             arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents
             arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents
-            value = numpy.array([x for x in arr])
+            value = [x for x in arr]
         elif p.mType == 3: #string can't be an array
         elif p.mType == 3: #string can't be an array
             value = cast(p.mData, POINTER(structs.String)).contents.data
             value = cast(p.mData, POINTER(structs.String)).contents.data
         elif p.mType == 4:
         elif p.mType == 4:
             arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents
             arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents
-            value = numpy.array([x for x in arr])
+            value = [x for x in arr]
         else:
         else:
             value = p.mData[:p.mDataLength]
             value = p.mData[:p.mDataLength]
 
 
+        if len(value) == 1:
+            [value] = value
+
         result[key] = value
         result[key] = value
 
 
     return result
     return result
 
 
-def aiGetMaterialFloatArray(material, key):
-    AI_SUCCESS = 0
-    from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
-    out = structs.Color4D()
-    max = c_uint(sizeof(structs.Color4D))
-    r=_assimp_lib.dll.aiGetMaterialFloatArray(pointer(material), 
-                                            key[0], 
-                                            key[1], 
-                                            key[2], 
-                                            byref(out), 
-                                            byref(max))
-                                            
-    if (r != AI_SUCCESS):    
-        raise AssimpError("aiGetMaterialFloatArray failed!")
-      
-    out._init()
-    return [out[i] for i in range(max.value)]
-    
-def aiGetMaterialString(material, key):
-    AI_SUCCESS = 0
-    from ctypes import byref, pointer, cast, c_float, POINTER, sizeof, c_uint
-    out = structs.String()
-    r=_assimp_lib.dll.aiGetMaterialString(pointer(material), 
-                                            key[0], 
-                                            key[1], 
-                                            key[2], 
-                                            byref(out))
-                                            
-    if (r != AI_SUCCESS):    
-        raise AssimpError("aiGetMaterialString failed!")
-        
-    return str(out.data)
-
-    
-    
 def decompose_matrix(matrix):
 def decompose_matrix(matrix):
     if not isinstance(matrix, structs.Matrix4x4):
     if not isinstance(matrix, structs.Matrix4x4):
         raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!")
         raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!")

+ 7 - 4
port/PyAssimp/pyassimp/helper.py

@@ -9,6 +9,7 @@ import ctypes
 from ctypes import POINTER
 from ctypes import POINTER
 import operator
 import operator
 import numpy
 import numpy
+from numpy import linalg
 
 
 import logging;logger = logging.getLogger("pyassimp")
 import logging;logger = logging.getLogger("pyassimp")
 
 
@@ -47,12 +48,14 @@ def transform(vector3, matrix4x4):
 def get_bounding_box(scene):
 def get_bounding_box(scene):
     bb_min = [1e10, 1e10, 1e10] # x,y,z
     bb_min = [1e10, 1e10, 1e10] # x,y,z
     bb_max = [-1e10, -1e10, -1e10] # x,y,z
     bb_max = [-1e10, -1e10, -1e10] # x,y,z
-    return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max)
+    return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, linalg.inv(scene.rootnode.transformation))
 
 
-def get_bounding_box_for_node(node, bb_min, bb_max):
+def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
+
+    transformation = numpy.dot(transformation, node.transformation)
     for mesh in node.meshes:
     for mesh in node.meshes:
         for v in mesh.vertices:
         for v in mesh.vertices:
-            v = transform(v, node.transformation)
+            v = transform(v, transformation)
             bb_min[0] = min(bb_min[0], v[0])
             bb_min[0] = min(bb_min[0], v[0])
             bb_min[1] = min(bb_min[1], v[1])
             bb_min[1] = min(bb_min[1], v[1])
             bb_min[2] = min(bb_min[2], v[2])
             bb_min[2] = min(bb_min[2], v[2])
@@ -62,7 +65,7 @@ def get_bounding_box_for_node(node, bb_min, bb_max):
 
 
 
 
     for child in node.children:
     for child in node.children:
-        bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max)
+        bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation)
 
 
     return bb_min, bb_max
     return bb_min, bb_max
 
 

+ 410 - 0
port/PyAssimp/scripts/advanced_3d_viewer.py

@@ -0,0 +1,410 @@
+#!/usr/bin/env python
+#-*- coding: UTF-8 -*-
+
+""" This program loads a model with PyASSIMP, and display it.
+
+It make a large use of shaders to illustrate a 'modern' OpenGL pipeline.
+
+Based on:
+- pygame + mouselook code from http://3dengine.org/Spectator_%28PyOpenGL%29
+ - http://www.lighthouse3d.com/tutorials
+ - http://www.songho.ca/opengl/gl_transform.html
+ - http://code.activestate.com/recipes/325391/
+ - ASSIMP's C++ SimpleOpenGL viewer
+"""
+import sys
+
+import logging
+logger = logging.getLogger("underworlds.3d_viewer")
+gllogger = logging.getLogger("OpenGL")
+gllogger.setLevel(logging.WARNING)
+logging.basicConfig(level=logging.INFO)
+
+import OpenGL
+#OpenGL.ERROR_CHECKING=False
+#OpenGL.ERROR_LOGGING = False
+#OpenGL.ERROR_ON_COPY = True
+#OpenGL.FULL_LOGGING = True
+from OpenGL.GL import *
+from OpenGL.error import GLError
+from OpenGL.GLU import *
+from OpenGL.GLUT import *
+from OpenGL.arrays import vbo
+from OpenGL.GL import shaders
+
+import pygame
+
+import math, random
+import numpy
+from numpy import linalg
+
+from pyassimp import core as pyassimp
+from pyassimp.postprocess import *
+from pyassimp.helper import *
+
+class DefaultCamera:
+    def __init__(self, w, h, fov):
+        self.clipplanenear = 0.001
+        self.clipplanefar = 100000.0
+        self.aspect = w/h
+        self.horizontalfov = fov * math.pi/180
+        self.transformation = [[ 0.68, -0.32, 0.65, 7.48],
+                               [ 0.73,  0.31, -0.61, -6.51],
+                               [-0.01,  0.89,  0.44,  5.34],
+                               [ 0.,    0.,    0.,    1.  ]]
+        self.lookat = [0.0,0.0,-1.0]
+
+    def __str__(self):
+        return "Default camera"
+
+class PyAssimp3DViewer:
+
+    base_name = "PyASSIMP 3D viewer"
+
+    def __init__(self, model, w=1024, h=768, fov=75):
+
+        pygame.init()
+        pygame.display.set_caption(self.base_name)
+        pygame.display.set_mode((w,h), pygame.OPENGL | pygame.DOUBLEBUF)
+
+        self.prepare_shaders()
+
+        self.cameras = [DefaultCamera(w,h,fov)]
+        self.current_cam_index = 0
+
+        self.load_model(model)
+
+        # for FPS computation
+        self.frames = 0
+        self.last_fps_time = glutGet(GLUT_ELAPSED_TIME)
+
+
+        self.cycle_cameras()
+
+    def prepare_shaders(self):
+
+        phong_weightCalc = """
+        float phong_weightCalc(
+            in vec3 light_pos, // light position
+            in vec3 frag_normal // geometry normal
+        ) {
+            // returns vec2( ambientMult, diffuseMult )
+            float n_dot_pos = max( 0.0, dot(
+                frag_normal, light_pos
+            ));
+            return n_dot_pos;
+        }
+        """
+
+        vertex = shaders.compileShader( phong_weightCalc +
+        """
+        uniform vec4 Global_ambient;
+        uniform vec4 Light_ambient;
+        uniform vec4 Light_diffuse;
+        uniform vec3 Light_location;
+        uniform vec4 Material_ambient;
+        uniform vec4 Material_diffuse;
+        attribute vec3 Vertex_position;
+        attribute vec3 Vertex_normal;
+        varying vec4 baseColor;
+        void main() {
+            gl_Position = gl_ModelViewProjectionMatrix * vec4(
+                Vertex_position, 1.0
+            );
+            vec3 EC_Light_location = gl_NormalMatrix * Light_location;
+            float diffuse_weight = phong_weightCalc(
+                normalize(EC_Light_location),
+                normalize(gl_NormalMatrix * Vertex_normal)
+            );
+            baseColor = clamp(
+            (
+                // global component
+                (Global_ambient * Material_ambient)
+                // material's interaction with light's contribution
+                // to the ambient lighting...
+                + (Light_ambient * Material_ambient)
+                // material's interaction with the direct light from
+                // the light.
+                + (Light_diffuse * Material_diffuse * diffuse_weight)
+            ), 0.0, 1.0);
+        }""", GL_VERTEX_SHADER)
+
+        fragment = shaders.compileShader("""
+        varying vec4 baseColor;
+        void main() {
+            gl_FragColor = baseColor;
+        }
+        """, GL_FRAGMENT_SHADER)
+
+        self.shader = shaders.compileProgram(vertex,fragment)
+        self.set_shader_accessors( (
+            'Global_ambient',
+            'Light_ambient','Light_diffuse','Light_location',
+            'Material_ambient','Material_diffuse',
+        ), (
+            'Vertex_position','Vertex_normal',
+        ), self.shader)
+
+    def set_shader_accessors(self, uniforms, attributes, shader):
+        # add accessors to the shaders uniforms and attributes
+        for uniform in uniforms:
+            location = glGetUniformLocation( shader,  uniform )
+            if location in (None,-1):
+                logger.warning('No uniform: %s'%( uniform ))
+            setattr( shader, uniform, location )
+
+        for attribute in attributes:
+            location = glGetAttribLocation( shader, attribute )
+            if location in (None,-1):
+                logger.warning('No attribute: %s'%( attribute ))
+            setattr( shader, attribute, location )
+
+
+    def prepare_gl_buffers(self, mesh):
+
+        mesh.gl = {}
+
+        # Fill the buffer for vertex and normals positions
+        v = numpy.array(mesh.vertices, 'f')
+        n = numpy.array(mesh.normals, 'f')
+
+        mesh.gl["vbo"] = vbo.VBO(numpy.hstack((v,n)))
+
+        # Fill the buffer for vertex positions
+        mesh.gl["faces"] = glGenBuffers(1)
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"])
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
+                    mesh.faces,
+                    GL_STATIC_DRAW)
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0)
+
+    
+    def load_model(self, path, postprocess = aiProcessPreset_TargetRealtime_MaxQuality):
+        logger.info("Loading model:" + path + "...")
+
+        if postprocess:
+            self.scene = pyassimp.load(path, postprocess)
+        else:
+            self.scene = pyassimp.load(path)
+        logger.info("Done.")
+
+        scene = self.scene
+        #log some statistics
+        logger.info("  meshes: %d" % len(scene.meshes))
+        logger.info("  total faces: %d" % sum([len(mesh.faces) for mesh in scene.meshes]))
+        logger.info("  materials: %d" % len(scene.materials))
+        self.bb_min, self.bb_max = get_bounding_box(self.scene)
+        logger.info("  bounding box:" + str(self.bb_min) + " - " + str(self.bb_max))
+
+        self.scene_center = [(a + b) / 2. for a, b in zip(self.bb_min, self.bb_max)]
+
+        for index, mesh in enumerate(scene.meshes):
+            self.prepare_gl_buffers(mesh)
+
+        # Finally release the model
+        pyassimp.release(scene)
+
+        logger.info("Ready for 3D rendering!")
+
+    def cycle_cameras(self):
+        if not self.cameras:
+            logger.info("No camera in the scene")
+            return None
+        self.current_cam_index = (self.current_cam_index + 1) % len(self.cameras)
+        self.current_cam = self.cameras[self.current_cam_index]
+        self.set_camera(self.current_cam)
+        logger.info("Switched to camera <%s>" % self.current_cam)
+
+    def set_camera_projection(self, camera = None):
+
+        if not camera:
+            camera = self.cameras[self.current_cam_index]
+
+        znear = camera.clipplanenear
+        zfar = camera.clipplanefar
+        aspect = camera.aspect
+        fov = camera.horizontalfov
+
+        glMatrixMode(GL_PROJECTION)
+        glLoadIdentity()
+
+        # Compute gl frustrum
+        tangent = math.tan(fov/2.)
+        h = znear * tangent
+        w = h * aspect
+
+        # params: left, right, bottom, top, near, far
+        glFrustum(-w, w, -h, h, znear, zfar)
+        # equivalent to:
+        #gluPerspective(fov * 180/math.pi, aspect, znear, zfar)
+        glMatrixMode(GL_MODELVIEW)
+        glLoadIdentity()
+
+
+    def set_camera(self, camera):
+
+        self.set_camera_projection(camera)
+
+        glMatrixMode(GL_MODELVIEW)
+        glLoadIdentity()
+
+        cam = transform([0.0, 0.0, 0.0], camera.transformation)
+        at = transform(camera.lookat, camera.transformation)
+        gluLookAt(cam[0], cam[2], -cam[1],
+                   at[0],  at[2],  -at[1],
+                       0,      1,       0)
+
+    def render(self, wireframe = False, twosided = False):
+
+        glEnable(GL_DEPTH_TEST)
+        glDepthFunc(GL_LEQUAL)
+
+
+        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE if wireframe else GL_FILL)
+        glDisable(GL_CULL_FACE) if twosided else glEnable(GL_CULL_FACE)
+
+        shader = self.shader
+
+        glUseProgram(shader)
+        glUniform4f( shader.Global_ambient, .4,.2,.2,.1 )
+        glUniform4f( shader.Light_ambient, .4,.4,.4, 1.0 )
+        glUniform4f( shader.Light_diffuse, 1,1,1,1 )
+        glUniform3f( shader.Light_location, 2,2,10 )
+
+        self.recursive_render(self.scene.rootnode, shader)
+
+
+        glUseProgram( 0 )
+
+    def recursive_render(self, node, shader):
+        """ Main recursive rendering method.
+        """
+
+        # save model matrix and apply node transformation
+        glPushMatrix()
+        m = node.transformation.transpose() # OpenGL row major
+        glMultMatrixf(m)
+
+        for mesh in node.meshes:
+
+            stride = 24 # 6 * 4 bytes
+
+            glUniform4f( shader.Material_diffuse, *mesh.material.properties["diffuse"] )
+            glUniform4f( shader.Material_ambient, *mesh.material.properties["ambient"] )
+
+            vbo = mesh.gl["vbo"]
+            vbo.bind()
+
+            glEnableVertexAttribArray( shader.Vertex_position )
+            glEnableVertexAttribArray( shader.Vertex_normal )
+
+            glVertexAttribPointer(
+                shader.Vertex_position,
+                3, GL_FLOAT,False, stride, vbo
+            )
+
+            glVertexAttribPointer(
+                shader.Vertex_normal,
+                3, GL_FLOAT,False, stride, vbo+12
+            )
+
+            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.gl["faces"])
+            glDrawElements(GL_TRIANGLES, len(mesh.faces) * 3, GL_UNSIGNED_INT, None)
+
+
+            vbo.unbind()
+            glDisableVertexAttribArray( shader.Vertex_position )
+
+            glDisableVertexAttribArray( shader.Vertex_normal )
+
+
+            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
+
+        for child in node.children:
+            self.recursive_render(child, shader)
+
+        glPopMatrix()
+
+
+    def loop(self):
+
+        pygame.display.flip()
+        pygame.event.pump()
+        self.keys = [k for k, pressed in enumerate(pygame.key.get_pressed()) if pressed]
+
+        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
+
+        # Compute FPS
+        gl_time = glutGet(GLUT_ELAPSED_TIME)
+        self.frames += 1
+        if gl_time - self.last_fps_time >= 1000:
+            current_fps = self.frames * 1000 / (gl_time - self.last_fps_time)
+            pygame.display.set_caption(self.base_name + " - %.0f fps" % current_fps)
+            self.frames = 0
+            self.last_fps_time = gl_time
+
+
+        return True
+
+    def controls_3d(self,
+                    mouse_button=1, \
+                    up_key=pygame.K_UP, \
+                    down_key=pygame.K_DOWN, \
+                    left_key=pygame.K_LEFT, \
+                    right_key=pygame.K_RIGHT):
+        """ The actual camera setting cycle """
+        mouse_dx,mouse_dy = pygame.mouse.get_rel()
+        if pygame.mouse.get_pressed()[mouse_button]:
+            look_speed = .2
+            buffer = glGetDoublev(GL_MODELVIEW_MATRIX)
+            c = (-1 * numpy.mat(buffer[:3,:3]) * \
+                numpy.mat(buffer[3,:3]).T).reshape(3,1)
+            # c is camera center in absolute coordinates, 
+            # we need to move it back to (0,0,0) 
+            # before rotating the camera
+            glTranslate(c[0],c[1],c[2])
+            m = buffer.flatten()
+            glRotate(mouse_dx * look_speed, m[1],m[5],m[9])
+            glRotate(mouse_dy * look_speed, m[0],m[4],m[8])
+            
+            # compensate roll
+            glRotated(-math.atan2(-m[4],m[5]) * \
+                57.295779513082320876798154814105 ,m[2],m[6],m[10])
+            glTranslate(-c[0],-c[1],-c[2])
+
+        # move forward-back or right-left
+        if up_key in self.keys:
+            fwd = .1
+        elif down_key in self.keys:
+            fwd = -.1
+        else:
+            fwd = 0
+
+        if left_key in self.keys:
+            strafe = .1
+        elif right_key in self.keys:
+            strafe = -.1
+        else:
+            strafe = 0
+
+        if abs(fwd) or abs(strafe):
+            m = glGetDoublev(GL_MODELVIEW_MATRIX).flatten()
+            glTranslate(fwd*m[2],fwd*m[6],fwd*m[10])
+            glTranslate(strafe*m[0],strafe*m[4],strafe*m[8])
+
+if __name__ == '__main__':
+    if not len(sys.argv) > 1:
+        print("Usage: " + __file__ + " <model>")
+        sys.exit(2)
+
+    app = PyAssimp3DViewer(model = sys.argv[1], w = 1024, h = 768, fov = 75)
+
+    while app.loop():
+        app.render()
+        app.controls_3d(0)
+        if pygame.K_f in app.keys: pygame.display.toggle_fullscreen()
+        if pygame.K_s in app.keys: app.screenshot()
+        if pygame.K_v in app.keys: app.check_visibility()
+        if pygame.K_TAB in app.keys: app.cycle_cameras()
+        if pygame.K_ESCAPE in app.keys:
+            break

+ 47 - 88
port/PyAssimp/scripts/opengl_viewer.py → port/PyAssimp/scripts/simple_opengl_viewer.py

@@ -1,14 +1,21 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #-*- coding: UTF-8 -*-
 #-*- coding: UTF-8 -*-
 
 
-""" This program demonstrate the use of pyassimp to render
-objects in OpenGL.
+""" This program demonstrates the use of pyassimp to load and
+render objects with OpenGL.
 
 
-It loads a 3D model with ASSIMP and display it.
+'c' cycles between cameras (if any available)
+'q' to quit
+
+This example mixes 'old' OpenGL fixed-function pipeline with 
+Vertex Buffer Objects.
 
 
 Materials are supported but textures are currently ignored.
 Materials are supported but textures are currently ignored.
 
 
-Half-working keyboard + mouse navigation is supported.
+For a more advanced example (with shaders + keyboard/mouse 
+controls), check scripts/sdl_viewer.py
+
+Author: Séverin Lemaignan, 2012
 
 
 This sample is based on several sources, including:
 This sample is based on several sources, including:
  - http://www.lighthouse3d.com/tutorials
  - http://www.lighthouse3d.com/tutorials
@@ -21,9 +28,8 @@ import os, sys
 from OpenGL.GLUT import *
 from OpenGL.GLUT import *
 from OpenGL.GLU import *
 from OpenGL.GLU import *
 from OpenGL.GL import *
 from OpenGL.GL import *
-from OpenGL.arrays import ArrayDatatype
 
 
-import logging;logger = logging.getLogger("assimp_opengl")
+import logging;logger = logging.getLogger("pyassimp_opengl")
 logging.basicConfig(level=logging.INFO)
 logging.basicConfig(level=logging.INFO)
 
 
 import math
 import math
@@ -40,20 +46,14 @@ width = 900
 
 
 class GLRenderer():
 class GLRenderer():
     def __init__(self):
     def __init__(self):
-        self.scene = None
 
 
-        self.drot = 0.0
-        self.dp = 0.0
+        self.scene = None
 
 
-        self.angle = 0.0
-        self.x = 1.0
-        self.z = 3.0
-        self.lx = 0.0
-        self.lz = 0.0
         self.using_fixed_cam = False
         self.using_fixed_cam = False
         self.current_cam_index = 0
         self.current_cam_index = 0
 
 
-        self.x_origin = -1 # x position of the mouse when pressing left btn
+        # store the global scene rotation
+        self.angle = 0.
 
 
         # for FPS calculation
         # for FPS calculation
         self.prev_time = 0
         self.prev_time = 0
@@ -61,6 +61,10 @@ class GLRenderer():
         self.frames = 0
         self.frames = 0
 
 
     def prepare_gl_buffers(self, mesh):
     def prepare_gl_buffers(self, mesh):
+        """ Creates 3 buffer objets for each mesh, 
+        to store the vertices, the normals, and the faces
+        indices.
+        """
 
 
         mesh.gl = {}
         mesh.gl = {}
 
 
@@ -90,8 +94,7 @@ class GLRenderer():
         glBindBuffer(GL_ARRAY_BUFFER,0)
         glBindBuffer(GL_ARRAY_BUFFER,0)
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0)
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0)
 
 
-    
-    def load_dae(self, path, postprocess = None):
+    def load_model(self, path, postprocess = None):
         logger.info("Loading model:" + path + "...")
         logger.info("Loading model:" + path + "...")
 
 
         if postprocess:
         if postprocess:
@@ -129,9 +132,11 @@ class GLRenderer():
 
 
         if not self.using_fixed_cam:
         if not self.using_fixed_cam:
             glLoadIdentity()
             glLoadIdentity()
-            gluLookAt(self.x ,1., self.z, # pos
-                    self.x + self.lx - 1.0, 1., self.z + self.lz - 3.0, # look at
-                    0.,1.,0.) # up vector
+
+            gluLookAt(0.,0.,3.,
+                      0.,0.,-5.,
+                      0.,1.,0.)
+
 
 
 
 
     def set_camera(self, camera):
     def set_camera(self, camera):
@@ -178,13 +183,13 @@ class GLRenderer():
         tmp = max(x_max, y_max)
         tmp = max(x_max, y_max)
         z_max = self.bb_max[2] - self.bb_min[2]
         z_max = self.bb_max[2] - self.bb_min[2]
         tmp = max(z_max, tmp)
         tmp = max(z_max, tmp)
-        
+
         if not restore:
         if not restore:
             tmp = 1. / tmp
             tmp = 1. / tmp
 
 
         logger.info("Scaling the scene by %.03f" % tmp)
         logger.info("Scaling the scene by %.03f" % tmp)
         glScalef(tmp, tmp, tmp)
         glScalef(tmp, tmp, tmp)
-    
+
         # center the model
         # center the model
         direction = -1 if not restore else 1
         direction = -1 if not restore else 1
         glTranslatef( direction * self.scene_center[0], 
         glTranslatef( direction * self.scene_center[0], 
@@ -192,25 +197,23 @@ class GLRenderer():
                       direction * self.scene_center[2] )
                       direction * self.scene_center[2] )
 
 
         return x_max, y_max, z_max
         return x_max, y_max, z_max
- 
+
     def apply_material(self, mat):
     def apply_material(self, mat):
-        """ Apply an OpenGL, using one OpenGL list per material to cache 
+        """ Apply an OpenGL, using one OpenGL display list per material to cache 
         the operation.
         the operation.
         """
         """
 
 
         if not hasattr(mat, "gl_mat"): # evaluate once the mat properties, and cache the values in a glDisplayList.
         if not hasattr(mat, "gl_mat"): # evaluate once the mat properties, and cache the values in a glDisplayList.
-    
-            diffuse = mat.properties.get("$clr.diffuse", numpy.array([0.8, 0.8, 0.8, 1.0]))
-            specular = mat.properties.get("$clr.specular", numpy.array([0., 0., 0., 1.0]))
-            ambient = mat.properties.get("$clr.ambient", numpy.array([0.2, 0.2, 0.2, 1.0]))
-            emissive = mat.properties.get("$clr.emissive", numpy.array([0., 0., 0., 1.0]))
-            shininess = min(mat.properties.get("$mat.shininess", 1.0), 128)
-            wireframe = mat.properties.get("$mat.wireframe", 0)
-            twosided = mat.properties.get("$mat.twosided", 1)
-    
-            from OpenGL.raw import GL
-            setattr(mat, "gl_mat", GL.GLuint(0))
-            mat.gl_mat = glGenLists(1)
+
+            diffuse = numpy.array(mat.properties.get("diffuse", [0.8, 0.8, 0.8, 1.0]))
+            specular = numpy.array(mat.properties.get("specular", [0., 0., 0., 1.0]))
+            ambient = numpy.array(mat.properties.get("ambient", [0.2, 0.2, 0.2, 1.0]))
+            emissive = numpy.array(mat.properties.get("emissive", [0., 0., 0., 1.0]))
+            shininess = min(mat.properties.get("shininess", 1.0), 128)
+            wireframe = mat.properties.get("wireframe", 0)
+            twosided = mat.properties.get("twosided", 1)
+
+            setattr(mat, "gl_mat", glGenLists(1))
             glNewList(mat.gl_mat, GL_COMPILE)
             glNewList(mat.gl_mat, GL_COMPILE)
     
     
             glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse)
             glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse)
@@ -239,6 +242,8 @@ class GLRenderer():
         self.lz = -math.cos(self.angle)
         self.lz = -math.cos(self.angle)
         self.set_default_camera()
         self.set_default_camera()
 
 
+        self.angle = (gl_time - self.prev_time) * 0.1
+
         self.prev_time = gl_time
         self.prev_time = gl_time
 
 
         # Compute FPS
         # Compute FPS
@@ -290,9 +295,10 @@ class GLRenderer():
         """ GLUT callback to redraw OpenGL surface
         """ GLUT callback to redraw OpenGL surface
         """
         """
         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
-    
+
+        glRotatef(self.angle,0.,1.,0.)
         self.recursive_render(self.scene.rootnode)
         self.recursive_render(self.scene.rootnode)
-    
+
         glutSwapBuffers()
         glutSwapBuffers()
         self.do_motion()
         self.do_motion()
         return
         return
@@ -307,42 +313,6 @@ class GLRenderer():
         if key == 'q':
         if key == 'q':
             sys.exit(0)
             sys.exit(0)
 
 
-    def onspecialkeypress(self, key, x, y):
-
-        fraction = 0.05
-
-        if key == GLUT_KEY_UP:
-            self.dp = 0.5
-        if key == GLUT_KEY_DOWN:
-            self.dp = -0.5
-        if key == GLUT_KEY_LEFT:
-            self.drot = -0.01
-        if key == GLUT_KEY_RIGHT:
-            self.drot = 0.01
-
-    def onspecialkeyrelease(self, key, x, y):
-
-        if key == GLUT_KEY_UP:
-            self.dp = 0.
-        if key == GLUT_KEY_DOWN:
-            self.dp = 0.
-        if key == GLUT_KEY_LEFT:
-            self.drot = 0.0
-        if key == GLUT_KEY_RIGHT:
-            self.drot = 0.0
-
-    def onclick(self, button, state, x, y):
-        if button == GLUT_LEFT_BUTTON:
-            if state == GLUT_UP:
-                self.drot = 0
-                self.x_origin = -1
-            else: # GLUT_DOWN
-                self.x_origin = x
-
-    def onmousemove(self, x, y):
-        if self.x_origin >= 0:
-            self.drot = (x - self.x_origin) * 0.001
-
     def render(self, filename=None, fullscreen = False, autofit = True, postprocess = None):
     def render(self, filename=None, fullscreen = False, autofit = True, postprocess = None):
         """
         """
 
 
@@ -364,7 +334,8 @@ class GLRenderer():
                 print("Fullscreen mode not available!")
                 print("Fullscreen mode not available!")
                 sys.exit(1)
                 sys.exit(1)
 
 
-        self.load_dae(filename, postprocess = postprocess)
+        self.load_model(filename, postprocess = postprocess)
+
 
 
         glClearColor(0.1,0.1,0.1,1.)
         glClearColor(0.1,0.1,0.1,1.)
         #glShadeModel(GL_SMOOTH)
         #glShadeModel(GL_SMOOTH)
@@ -374,16 +345,10 @@ class GLRenderer():
         glEnable(GL_CULL_FACE)
         glEnable(GL_CULL_FACE)
         glEnable(GL_DEPTH_TEST)
         glEnable(GL_DEPTH_TEST)
 
 
-        #lightZeroPosition = [10.,4.,10.,1.]
-        #lightZeroColor = [0.8,1.0,0.8,1.0] #green tinged
-        #glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition)
-        #glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor)
-        #glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1)
-        #glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05)
         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)
         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)
         glEnable(GL_NORMALIZE)
         glEnable(GL_NORMALIZE)
         glEnable(GL_LIGHT0)
         glEnable(GL_LIGHT0)
-    
+
         glutDisplayFunc(self.display)
         glutDisplayFunc(self.display)
 
 
 
 
@@ -399,14 +364,8 @@ class GLRenderer():
 
 
         glPushMatrix()
         glPushMatrix()
 
 
-        # Register GLUT callbacks for keyboard and mouse
         glutKeyboardFunc(self.onkeypress)
         glutKeyboardFunc(self.onkeypress)
-        glutSpecialFunc(self.onspecialkeypress)
         glutIgnoreKeyRepeat(1)
         glutIgnoreKeyRepeat(1)
-        glutSpecialUpFunc(self.onspecialkeyrelease)
-
-        glutMouseFunc(self.onclick)
-        glutMotionFunc(self.onmousemove)
 
 
         glutMainLoop()
         glutMainLoop()