Tuesday merger of bf-blender into orange branch.
authorTon Roosendaal <ton@blender.org>
Tue, 10 Jan 2006 22:10:14 +0000 (22:10 +0000)
committerTon Roosendaal <ton@blender.org>
Tue, 10 Jan 2006 22:10:14 +0000 (22:10 +0000)
139 files changed:
extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.h [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/CollisionMargin.h [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.h [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/EmptyShape.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/EmptyShape.h [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/OptimizedBvh.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/OptimizedBvh.h [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/TriangleCallback.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.cpp [new file with mode: 0644]
extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.h [new file with mode: 0644]
extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.cpp [new file with mode: 0644]
extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.h [new file with mode: 0644]
release/scripts/3ds_export.py [new file with mode: 0644]
release/scripts/3ds_import.py [new file with mode: 0644]
release/scripts/blenderLipSynchro.py [new file with mode: 0644]
release/scripts/flt_export.py [new file with mode: 0755]
release/scripts/flt_filewalker.py [new file with mode: 0644]
release/scripts/flt_import.py [new file with mode: 0755]
release/scripts/md2_export.py [new file with mode: 0644]
release/scripts/md2_import.py [new file with mode: 0644]
release/scripts/mirror_bone_weights.py [new file with mode: 0644]
release/scripts/widgetwizard.py [new file with mode: 0644]
release/scripts/xsi_export.py [new file with mode: 0644]
source/blender/blenkernel/BKE_global.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/CCGSubSurf.c
source/blender/blenkernel/intern/CCGSubSurf.h
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/displist.c
source/blender/blenkernel/intern/ipo.c
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/subsurf_ccg.c
source/blender/blenkernel/intern/text.c
source/blender/makesdna/DNA_ipo_types.h
source/blender/makesdna/DNA_meshdata_types.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/python/api2_2x/Bone.c
source/blender/python/api2_2x/Draw.c
source/blender/python/api2_2x/Draw.h
source/blender/python/api2_2x/Ipo.c
source/blender/python/api2_2x/Object.c
source/blender/python/api2_2x/Scene.c
source/blender/python/api2_2x/doc/Draw.py
source/blender/python/api2_2x/doc/Lamp.py
source/blender/python/api2_2x/doc/Object.py
source/blender/python/api2_2x/matrix.c
source/blender/renderconverter/intern/convertBlenderScene.c
source/blender/src/booleanops_mesh.c
source/blender/src/buttons_editing.c
source/blender/src/buttons_scene.c
source/blender/src/drawview.c
source/blender/src/editaction.c
source/blender/src/editconstraint.c
source/blender/src/editipo.c
source/blender/src/editipo_lib.c
source/blender/src/editobject.c
source/blender/src/editsima.c
source/blender/src/fluidsim.c
source/blender/src/header_image.c
source/blender/src/header_info.c
source/blender/src/interface.c
source/blender/src/space.c
source/blender/src/toolbox.c
source/gameengine/BlenderRoutines/BL_KetsjiEmbedStart.cpp
source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp
source/gameengine/Converter/BL_ArmatureObject.cpp
source/gameengine/Converter/BL_ArmatureObject.h
source/gameengine/Converter/BL_BlenderDataConversion.cpp
source/gameengine/Converter/BL_MeshDeformer.cpp
source/gameengine/Converter/BL_MeshDeformer.h
source/gameengine/Converter/BL_SkinDeformer.cpp
source/gameengine/Converter/BL_SkinDeformer.h
source/gameengine/Converter/BlenderWorldInfo.cpp
source/gameengine/Converter/BlenderWorldInfo.h
source/gameengine/Converter/KX_BlenderSceneConverter.cpp
source/gameengine/Converter/KX_BlenderSceneConverter.h
source/gameengine/Converter/KX_IpoConvert.cpp
source/gameengine/Converter/KX_IpoConvert.h
source/gameengine/Converter/Makefile
source/gameengine/Converter/SConscript
source/gameengine/Expressions/Value.cpp
source/gameengine/Expressions/Value.h
source/gameengine/GameLogic/SCA_IObject.cpp
source/gameengine/GameLogic/SCA_LogicManager.cpp
source/gameengine/GameLogic/SCA_LogicManager.h
source/gameengine/GameLogic/SCA_PythonController.cpp
source/gameengine/GamePlayer/ghost/GPG_Application.cpp
source/gameengine/GamePlayer/ghost/GPG_Application.h
source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
source/gameengine/Ketsji/BL_Material.cpp [new file with mode: 0644]
source/gameengine/Ketsji/BL_Material.h [new file with mode: 0644]
source/gameengine/Ketsji/BL_Shader.cpp [new file with mode: 0644]
source/gameengine/Ketsji/BL_Shader.h [new file with mode: 0644]
source/gameengine/Ketsji/BL_Texture.cpp [new file with mode: 0644]
source/gameengine/Ketsji/BL_Texture.h [new file with mode: 0644]
source/gameengine/Ketsji/KX_BlenderMaterial.cpp [new file with mode: 0644]
source/gameengine/Ketsji/KX_BlenderMaterial.h [new file with mode: 0644]
source/gameengine/Ketsji/KX_BulletPhysicsController.h
source/gameengine/Ketsji/KX_Camera.cpp
source/gameengine/Ketsji/KX_Camera.h
source/gameengine/Ketsji/KX_ConvertPhysicsObjects.cpp
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Ketsji/KX_ISceneConverter.h
source/gameengine/Ketsji/KX_KetsjiEngine.cpp
source/gameengine/Ketsji/KX_KetsjiEngine.h
source/gameengine/Ketsji/KX_MaterialIpoController.cpp [new file with mode: 0644]
source/gameengine/Ketsji/KX_MaterialIpoController.h [new file with mode: 0644]
source/gameengine/Ketsji/KX_MeshProxy.cpp
source/gameengine/Ketsji/KX_PolygonMaterial.h
source/gameengine/Ketsji/KX_PythonInit.cpp
source/gameengine/Ketsji/KX_RadarSensor.cpp
source/gameengine/Ketsji/KX_Scene.cpp
source/gameengine/Ketsji/KX_Scene.h
source/gameengine/Ketsji/KX_VertexProxy.cpp
source/gameengine/Ketsji/KX_VertexProxy.h
source/gameengine/Ketsji/KX_WorldInfo.h
source/gameengine/Ketsji/Makefile
source/gameengine/Ketsji/SConscript
source/gameengine/Physics/Bullet/CcdPhysicsController.cpp
source/gameengine/Rasterizer/RAS_BucketManager.cpp
source/gameengine/Rasterizer/RAS_CameraData.h
source/gameengine/Rasterizer/RAS_IPolygonMaterial.cpp
source/gameengine/Rasterizer/RAS_IPolygonMaterial.h
source/gameengine/Rasterizer/RAS_IRasterizer.h
source/gameengine/Rasterizer/RAS_MaterialBucket.cpp
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/ARB_multitexture.h [new file with mode: 0644]
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.h
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.h
source/gameengine/Rasterizer/RAS_OpenGLRasterizer/glext.h [new file with mode: 0644]
source/gameengine/Rasterizer/RAS_TexVert.cpp
source/gameengine/Rasterizer/RAS_TexVert.h
source/nan_definitions.mk

diff --git a/extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.cpp b/extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.cpp
new file mode 100644 (file)
index 0000000..0c94b88
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+//#define DISABLE_BVH
+
+
+#include "CollisionShapes/BvhTriangleMeshShape.h"
+#include "CollisionShapes/OptimizedBvh.h"
+
+///Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy optimization.
+///Uses an interface to access the triangles to allow for sharing graphics/physics triangles.
+BvhTriangleMeshShape::BvhTriangleMeshShape(StridingMeshInterface* meshInterface)
+:TriangleMeshShape(meshInterface)
+{
+       //construct bvh from meshInterface
+#ifndef DISABLE_BVH
+
+       m_bvh = new OptimizedBvh();
+       m_bvh->Build(meshInterface);
+
+#endif //DISABLE_BVH
+
+}
+
+BvhTriangleMeshShape::~BvhTriangleMeshShape()
+{
+       delete m_bvh;
+}
+
+//perform bvh tree traversal and report overlapping triangles to 'callback'
+void   BvhTriangleMeshShape::ProcessAllTriangles(TriangleCallback* callback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const
+{
+
+#ifdef DISABLE_BVH
+       //brute force traverse all triangles
+       TriangleMeshShape::ProcessAllTriangles(callback,aabbMin,aabbMax);
+#else
+
+       //first get all the nodes
+
+       
+       struct  MyNodeOverlapCallback : public NodeOverlapCallback
+       {
+               StridingMeshInterface*  m_meshInterface;
+               TriangleCallback*               m_callback;
+               SimdVector3                             m_triangle[3];
+
+
+               MyNodeOverlapCallback(TriangleCallback* callback,StridingMeshInterface* meshInterface)
+                       :m_callback(callback),
+                       m_meshInterface(meshInterface)
+               {
+               }
+                               
+               virtual void ProcessNode(const OptimizedBvhNode* node)
+               {
+                       const unsigned char *vertexbase;
+                       int numverts;
+                       PHY_ScalarType type;
+                       int stride;
+                       const unsigned char *indexbase;
+                       int indexstride;
+                       int numfaces;
+                       PHY_ScalarType indicestype;
+                       
+
+                       m_meshInterface->getLockedReadOnlyVertexIndexBase(
+                               &vertexbase,
+                               numverts,
+                               type,
+                               stride,
+                               &indexbase,
+                               indexstride,
+                               numfaces,
+                               indicestype,
+                               node->m_subPart);
+
+                       int* gfxbase = (int*)(indexbase+node->m_triangleIndex*indexstride);
+                       
+                       const SimdVector3& meshScaling = m_meshInterface->getScaling();
+                       for (int j=2;j>=0;j--)
+                       {
+                               
+                               int graphicsindex = gfxbase[j];
+#ifdef DEBUG_TRIANGLE_MESH
+                               printf("%d ,",graphicsindex);
+#endif //DEBUG_TRIANGLE_MESH
+                               float* graphicsbase = (float*)(vertexbase+graphicsindex*stride);
+
+                               m_triangle[j] = SimdVector3(
+                                       graphicsbase[0]*meshScaling.getX(),
+                                       graphicsbase[1]*meshScaling.getY(),
+                                       graphicsbase[2]*meshScaling.getZ());
+#ifdef DEBUG_TRIANGLE_MESH
+                               printf("triangle vertices:%f,%f,%f\n",triangle[j].x(),triangle[j].y(),triangle[j].z());
+#endif //DEBUG_TRIANGLE_MESH
+                       }
+
+                       m_callback->ProcessTriangle(m_triangle);
+                       m_meshInterface->unLockReadOnlyVertexBase(node->m_subPart);
+               }
+
+       };
+
+       MyNodeOverlapCallback   myNodeCallback(callback,m_meshInterface);
+
+       m_bvh->ReportAabbOverlappingNodex(&myNodeCallback,aabbMin,aabbMax);
+
+
+#endif//DISABLE_BVH
+
+
+}
+
+
+void   BvhTriangleMeshShape::setLocalScaling(const SimdVector3& scaling)
+{
+       if ((getLocalScaling() -scaling).length2() > SIMD_EPSILON)
+       {
+               TriangleMeshShape::setLocalScaling(scaling);
+               delete m_bvh;
+               m_bvh = new OptimizedBvh();
+               m_bvh->Build(m_meshInterface);
+               //rebuild the bvh...
+       }
+}
diff --git a/extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.h b/extern/bullet/Bullet/CollisionShapes/BvhTriangleMeshShape.h
new file mode 100644 (file)
index 0000000..86cd1e5
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+#ifndef BVH_TRIANGLE_MESH_SHAPE_H
+#define BVH_TRIANGLE_MESH_SHAPE_H
+
+#include "CollisionShapes/TriangleMeshShape.h"
+#include "CollisionShapes/OptimizedBvh.h"
+
+///Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy optimization.
+///Uses an interface to access the triangles to allow for sharing graphics/physics triangles.
+class BvhTriangleMeshShape : public TriangleMeshShape
+{
+
+       OptimizedBvh*   m_bvh;
+       
+       
+public:
+       BvhTriangleMeshShape(StridingMeshInterface* meshInterface);
+
+       virtual ~BvhTriangleMeshShape();
+
+       
+       /*
+       virtual int     GetShapeType() const
+       {
+               return TRIANGLE_MESH_SHAPE_PROXYTYPE;
+       }
+       */
+
+
+
+       virtual void    ProcessAllTriangles(TriangleCallback* callback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const;
+
+
+       //debugging
+       virtual char*   GetName()const {return "BVHTRIANGLEMESH";}
+
+
+       virtual void    setLocalScaling(const SimdVector3& scaling);
+       
+
+
+};
+
+#endif //BVH_TRIANGLE_MESH_SHAPE_H
diff --git a/extern/bullet/Bullet/CollisionShapes/CollisionMargin.h b/extern/bullet/Bullet/CollisionShapes/CollisionMargin.h
new file mode 100644 (file)
index 0000000..41009dc
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef COLLISION_MARGIN_H
+#define COLLISION_MARGIN_H
+
+//used by Gjk and some other algorithms
+
+#define CONVEX_DISTANCE_MARGIN 0.04f// 0.1f//;//0.01f
+
+
+
+#endif //COLLISION_MARGIN_H
+
diff --git a/extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.cpp b/extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.cpp
new file mode 100644 (file)
index 0000000..dbaaaf2
--- /dev/null
@@ -0,0 +1,86 @@
+#include "ConvexTriangleCallback.h"
+#include "NarrowPhaseCollision/PersistentManifold.h"
+#include "NarrowPhaseCollision/ManifoldContactAddResult.h"
+#include "NarrowPhaseCollision/GjkPairDetector.h"
+#include "NarrowPhaseCollision/MinkowskiPenetrationDepthSolver.h"
+
+
+
+#include "TriangleShape.h"
+
+//m_manifoldPtr = m_dispatcher->GetNewManifold(proxy0->m_clientObject,proxy1->m_clientObject);
+       //m_dispatcher->ReleaseManifold( m_manifoldPtr );
+
+ConvexTriangleCallback::ConvexTriangleCallback(PersistentManifold* manifold,ConvexShape* convexShape,const SimdTransform&convexTransform,const SimdTransform& triangleMeshTransform)
+:m_triangleMeshTransform(triangleMeshTransform),
+       m_convexTransform(convexTransform),
+       m_convexShape(convexShape),
+       m_manifoldPtr(manifold),
+       m_triangleCount(0)
+{
+}
+
+ConvexTriangleCallback::~ConvexTriangleCallback()
+{
+  
+}
+  
+
+void   ConvexTriangleCallback::ClearCache()
+{
+       m_manifoldPtr->ClearManifold();
+};
+
+
+
+void ConvexTriangleCallback::ProcessTriangle(SimdVector3* triangle)
+{
+
+       //triangle, convex
+       TriangleShape tm(triangle[0],triangle[1],triangle[2]);  
+       tm.SetMargin(m_collisionMarginTriangle);
+       GjkPairDetector::ClosestPointInput input;
+       input.m_transformA = m_triangleMeshTransform;
+       input.m_transformB = m_convexTransform;
+       
+       ManifoldContactAddResult output(m_triangleMeshTransform,m_convexTransform,m_manifoldPtr);
+       
+       
+       VoronoiSimplexSolver simplexSolver;
+       MinkowskiPenetrationDepthSolver penetrationDepthSolver;
+       
+       GjkPairDetector gjkDetector(&tm,m_convexShape,&simplexSolver,&penetrationDepthSolver);
+
+       gjkDetector.SetMinkowskiA(&tm);
+       gjkDetector.SetMinkowskiB(m_convexShape);
+       input.m_maximumDistanceSquared = tm.GetMargin()+ m_convexShape->GetMargin() + m_manifoldPtr->GetManifoldMargin();
+       input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared;
+
+       input.m_maximumDistanceSquared = 1e30f;//?
+       
+               
+       gjkDetector.GetClosestPoints(input,output);
+
+
+}
+
+
+
+void   ConvexTriangleCallback::Update(float collisionMarginTriangle)
+{
+       m_triangleCount = 0;
+       m_collisionMarginTriangle = collisionMarginTriangle;
+
+       SimdTransform boxInTriangleSpace;
+       boxInTriangleSpace = m_triangleMeshTransform.inverse() * m_convexTransform;
+
+       m_convexShape->GetAabb(boxInTriangleSpace,m_aabbMin,m_aabbMax);
+
+       float extraMargin = CONVEX_DISTANCE_MARGIN;
+
+       SimdVector3 extra(extraMargin,extraMargin,extraMargin);
+
+       m_aabbMax += extra;
+       m_aabbMin -= extra;
+       
+}
diff --git a/extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.h b/extern/bullet/Bullet/CollisionShapes/ConvexTriangleCallback.h
new file mode 100644 (file)
index 0000000..8e0e446
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef CONVEX_TRIANGLE_CALLBACK_H
+#define CONVEX_TRIANGLE_CALLBACK_H
+
+#include "TriangleCallback.h"
+class ConvexShape;
+class PersistentManifold;
+#include "SimdTransform.h"
+///ConvexTriangleCallback processes the narrowphase convex-triangle collision detection
+class ConvexTriangleCallback: public TriangleCallback
+{
+       SimdVector3     m_aabbMin;
+       SimdVector3     m_aabbMax ;
+
+       SimdTransform   m_triangleMeshTransform;
+       SimdTransform   m_convexTransform;
+
+//     bool m_useContinuous;
+       float m_collisionMarginTriangle;
+       
+public:
+int    m_triangleCount;
+       
+       ConvexShape*    m_convexShape;
+
+       PersistentManifold*     m_manifoldPtr;
+
+       ConvexTriangleCallback(PersistentManifold* manifold,ConvexShape* convexShape,const SimdTransform&convexTransform,const SimdTransform& triangleMeshTransform);
+
+       void    Update(float collisionMarginTriangle);
+
+       virtual ~ConvexTriangleCallback();
+
+       virtual void ProcessTriangle(SimdVector3* triangle);
+       
+       void ClearCache();
+
+       inline const SimdVector3& GetAabbMin() const
+       {
+               return m_aabbMin;
+       }
+       inline const SimdVector3& GetAabbMax() const
+       {
+               return m_aabbMax;
+       }
+
+};
+
+
+#endif //CONVEX_TRIANGLE_CALLBACK_H
\ No newline at end of file
diff --git a/extern/bullet/Bullet/CollisionShapes/EmptyShape.cpp b/extern/bullet/Bullet/CollisionShapes/EmptyShape.cpp
new file mode 100644 (file)
index 0000000..04954dd
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+ */
+
+#include "EmptyShape.h"
+
+
+#include "CollisionShape.h"
+
+
+EmptyShape::EmptyShape()
+{
+}
+
+
+EmptyShape::~EmptyShape()
+{
+}
+
+
+       ///GetAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version
+void EmptyShape::GetAabb(const SimdTransform& t,SimdVector3& aabbMin,SimdVector3& aabbMax) const
+{
+       SimdVector3 margin(GetMargin(),GetMargin(),GetMargin());
+
+       aabbMin = t.getOrigin() - margin;
+
+       aabbMax = t.getOrigin() + margin;
+
+}
+
+void   EmptyShape::CalculateLocalInertia(SimdScalar mass,SimdVector3& inertia)
+{
+       assert(0);
+}
+
+       
+       
diff --git a/extern/bullet/Bullet/CollisionShapes/EmptyShape.h b/extern/bullet/Bullet/CollisionShapes/EmptyShape.h
new file mode 100644 (file)
index 0000000..7ecbb05
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+ */
+
+#ifndef EMPTY_SHAPE_H
+#define EMPTY_SHAPE_H
+
+#include "CollisionShape.h"
+
+#include "SimdVector3.h"
+#include "SimdTransform.h"
+#include "SimdMatrix3x3.h"
+#include <vector>
+#include "CollisionShapes/CollisionMargin.h"
+
+
+
+
+/// EmptyShape is a collision shape without actual collision detection. It can be replaced by another shape during runtime
+class EmptyShape       : public CollisionShape
+{
+public:
+       EmptyShape();
+
+       virtual ~EmptyShape();
+
+
+       ///GetAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version
+       void GetAabb(const SimdTransform& t,SimdVector3& aabbMin,SimdVector3& aabbMax) const;
+
+
+       virtual void    setLocalScaling(const SimdVector3& scaling)
+       {
+               m_localScaling = scaling;
+       }
+       virtual const SimdVector3& getLocalScaling() const 
+       {
+               return m_localScaling;
+       }
+
+       virtual void    CalculateLocalInertia(SimdScalar mass,SimdVector3& inertia);
+       
+       virtual int     GetShapeType() const { return EMPTY_SHAPE_PROXYTYPE;}
+
+       virtual void    SetMargin(float margin)
+       {
+               m_collisionMargin = margin;
+       }
+       virtual float   GetMargin() const
+       {
+               return m_collisionMargin;
+       }
+       virtual char*   GetName()const
+       {
+               return "Empty";
+       }
+
+
+private:
+       SimdScalar      m_collisionMargin;
+protected:
+       SimdVector3     m_localScaling;
+
+};
+
+
+
+#endif //EMPTY_SHAPE_H
diff --git a/extern/bullet/Bullet/CollisionShapes/OptimizedBvh.cpp b/extern/bullet/Bullet/CollisionShapes/OptimizedBvh.cpp
new file mode 100644 (file)
index 0000000..fe3a518
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#include "OptimizedBvh.h"
+#include "StridingMeshInterface.h"
+#include "AabbUtil2.h"
+
+
+
+void OptimizedBvh::Build(StridingMeshInterface* triangles)
+{
+       int countTriangles = 0;
+
+       
+
+       // NodeArray    triangleNodes;
+
+       struct  NodeTriangleCallback : public InternalTriangleIndexCallback
+       {
+               NodeArray&      m_triangleNodes;
+
+               NodeTriangleCallback(NodeArray& triangleNodes)
+                       :m_triangleNodes(triangleNodes)
+               {
+
+               }
+
+               virtual void InternalProcessTriangleIndex(SimdVector3* triangle,int partId,int  triangleIndex)
+               {
+
+                       OptimizedBvhNode node;
+                       node.m_aabbMin = SimdVector3(1e30f,1e30f,1e30f); 
+                       node.m_aabbMax = SimdVector3(-1e30f,-1e30f,-1e30f); 
+                       node.m_aabbMin.setMin(triangle[0]);
+                       node.m_aabbMax.setMax(triangle[0]);
+                       node.m_aabbMin.setMin(triangle[1]);
+                       node.m_aabbMax.setMax(triangle[1]);
+                       node.m_aabbMin.setMin(triangle[2]);
+                       node.m_aabbMax.setMax(triangle[2]);
+
+                       node.m_escapeIndex = -1;
+                       node.m_leftChild = 0;
+                       node.m_rightChild = 0;
+
+
+                       //for child nodes
+                       node.m_subPart = partId;
+                       node.m_triangleIndex = triangleIndex;
+
+                       
+                       m_triangleNodes.push_back(node);
+               }
+       };
+
+       
+
+       NodeTriangleCallback    callback(m_leafNodes);
+
+       SimdVector3 aabbMin(-1e30f,-1e30f,-1e30f);
+       SimdVector3 aabbMax(1e30f,1e30f,1e30f);
+
+       triangles->InternalProcessAllTriangles(&callback,aabbMin,aabbMax);
+
+       //now we have an array of leafnodes in m_leafNodes
+
+       m_contiguousNodes = new OptimizedBvhNode[2*m_leafNodes.size()];
+       m_curNodeIndex = 0;
+
+       m_rootNode1 = BuildTree(m_leafNodes,0,m_leafNodes.size());
+
+
+       ///create the leafnodes first
+//     OptimizedBvhNode* leafNodes = new OptimizedBvhNode;
+}
+
+
+OptimizedBvhNode*      OptimizedBvh::BuildTree (NodeArray&     leafNodes,int startIndex,int endIndex)
+{
+
+       int numIndices =endIndex-startIndex;
+       assert(numIndices>0);
+
+       int curIndex = m_curNodeIndex;
+
+       if (numIndices==1)
+       {
+               return new (&m_contiguousNodes[m_curNodeIndex++]) OptimizedBvhNode(leafNodes[startIndex]);
+       }
+       //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'.
+       
+       int splitAxis = CalcSplittingAxis(leafNodes,startIndex,endIndex);
+
+       int splitIndex = SortAndCalcSplittingIndex(leafNodes,startIndex,endIndex,splitAxis);
+
+       OptimizedBvhNode* internalNode = &m_contiguousNodes[m_curNodeIndex++];
+       
+       internalNode->m_aabbMax.setValue(-1e30f,-1e30f,-1e30f);
+       internalNode->m_aabbMin.setValue(1e30f,1e30f,1e30f);
+       
+       for (int i=startIndex;i<endIndex;i++)
+       {
+               internalNode->m_aabbMax.setMax(leafNodes[i].m_aabbMax);
+               internalNode->m_aabbMin.setMin(leafNodes[i].m_aabbMin);
+       }
+
+       
+
+       //internalNode->m_escapeIndex;
+       internalNode->m_leftChild = BuildTree(leafNodes,startIndex,splitIndex);
+       internalNode->m_rightChild = BuildTree(leafNodes,splitIndex,endIndex);
+
+       internalNode->m_escapeIndex  = m_curNodeIndex - curIndex;
+       return internalNode;
+}
+
+int    OptimizedBvh::SortAndCalcSplittingIndex(NodeArray&      leafNodes,int startIndex,int endIndex,int splitAxis)
+{
+       int splitIndex =startIndex;
+       int numIndices = endIndex - startIndex;
+
+       SimdVector3 means(0.f,0.f,0.f);
+       for (int i=startIndex;i<endIndex;i++)
+       {
+               SimdVector3 center = 0.5f*(leafNodes[i].m_aabbMax+leafNodes[i].m_aabbMin);
+               means+=center;
+       }
+       means *= (1.f/(float)numIndices);
+       
+       float splitValue = means[splitAxis];
+       
+       //sort leafNodes so all values larger then splitValue comes first, and smaller values start from 'splitIndex'.
+       for (int i=startIndex;i<endIndex;i++)
+       {
+               SimdVector3 center = 0.5f*(leafNodes[i].m_aabbMax+leafNodes[i].m_aabbMin);
+               if (center[splitAxis] > splitValue)
+               {
+                       //swap
+                       OptimizedBvhNode tmp = leafNodes[i];
+                       leafNodes[i] = leafNodes[splitIndex];
+                       leafNodes[splitIndex] = tmp;
+                       splitIndex++;
+               }
+       }
+       if ((splitIndex==startIndex) || (splitIndex == (endIndex-1)))
+       {
+               splitIndex = startIndex+ (numIndices>>1);
+       }
+       return splitIndex;
+}
+
+
+int    OptimizedBvh::CalcSplittingAxis(NodeArray&      leafNodes,int startIndex,int endIndex)
+{
+       SimdVector3 means(0.f,0.f,0.f);
+       int numIndices = endIndex-startIndex;
+
+       for (int i=startIndex;i<endIndex;i++)
+       {
+               SimdVector3 center = 0.5f*(leafNodes[i].m_aabbMax+leafNodes[i].m_aabbMin);
+               means+=center;
+       }
+       means *= (1.f/(float)numIndices);
+               
+       SimdVector3 variance(0.f,0.f,0.f);
+
+       for (int i=startIndex;i<endIndex;i++)
+       {
+               SimdVector3 center = 0.5f*(leafNodes[i].m_aabbMax+leafNodes[i].m_aabbMin);
+               SimdVector3 diff2 = center-means;
+               diff2 = diff2 * diff2;
+               variance += diff2;
+       }
+       variance *= (1.f/       ((float)numIndices-1)   );
+       
+       int biggestAxis = variance.maxAxis();
+       return biggestAxis;
+
+}
+
+
+       
+void   OptimizedBvh::ReportAabbOverlappingNodex(NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const
+{
+       if (aabbMin.length() > 1000.f)
+       {
+               for (int i=0;i<m_leafNodes.size();i++)
+               {
+                       const OptimizedBvhNode& node = m_leafNodes[i];
+                       nodeCallback->ProcessNode(&node);
+               }
+       } else
+       {
+               //WalkTree(m_rootNode1,nodeCallback,aabbMin,aabbMax);
+               WalkStacklessTree(m_rootNode1,nodeCallback,aabbMin,aabbMax);
+       }
+}
+
+void   OptimizedBvh::WalkTree(OptimizedBvhNode* rootNode,NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const
+{
+       bool aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMin,rootNode->m_aabbMax);
+       if (aabbOverlap)
+       {
+               bool isLeafNode = (!rootNode->m_leftChild && !rootNode->m_rightChild);
+               if (isLeafNode)
+               {
+                       nodeCallback->ProcessNode(rootNode);
+               } else
+               {
+                       WalkTree(rootNode->m_leftChild,nodeCallback,aabbMin,aabbMax);
+                       WalkTree(rootNode->m_rightChild,nodeCallback,aabbMin,aabbMax);
+               }
+       }
+
+}
+
+int maxIterations = 0;
+
+void   OptimizedBvh::WalkStacklessTree(OptimizedBvhNode* rootNode,NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const
+{
+       int curIndex = 0;
+       int walkIterations = 0;
+
+       while (curIndex < m_curNodeIndex)
+       {
+               //catch bugs in tree data
+               assert (walkIterations < m_curNodeIndex);
+
+               walkIterations++;
+               bool aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMin,rootNode->m_aabbMax);
+               bool isLeafNode = (!rootNode->m_leftChild && !rootNode->m_rightChild);
+               
+               if (isLeafNode && aabbOverlap)
+               {
+                       nodeCallback->ProcessNode(rootNode);
+               } 
+               
+               if (aabbOverlap || isLeafNode)
+               {
+                       rootNode++;
+                       curIndex++;
+               } else
+               {
+                       int escapeIndex = rootNode->m_escapeIndex;
+                       rootNode += escapeIndex;
+                       curIndex += escapeIndex;
+               }
+               
+       }
+
+       if (maxIterations < walkIterations)
+               maxIterations = walkIterations;
+
+}
+
+
+void   OptimizedBvh::ReportSphereOverlappingNodex(NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const
+{
+
+}
+
diff --git a/extern/bullet/Bullet/CollisionShapes/OptimizedBvh.h b/extern/bullet/Bullet/CollisionShapes/OptimizedBvh.h
new file mode 100644 (file)
index 0000000..911bb9a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#ifndef OPTIMIZED_BVH_H
+#define OPTIMIZED_BVH_H
+#include "SimdVector3.h"
+#include <vector>
+
+class StridingMeshInterface;
+
+/// OptimizedBvhNode contains both internal and leaf node information.
+/// It hasn't been optimized yet for storage. Some obvious optimizations are:
+/// Removal of the pointers (can already be done, they are not used for traversal)
+/// and storing aabbmin/max as quantized integers.
+/// 'subpart' doesn't need an integer either. It allows to re-use graphics triangle
+/// meshes stored in a non-uniform way (like batches/subparts of triangle-fans
+struct OptimizedBvhNode
+{
+
+       SimdVector3     m_aabbMin;
+       SimdVector3     m_aabbMax;
+
+//these 2 pointers are obsolete, the stackless traversal just uses the escape index
+       OptimizedBvhNode*       m_leftChild;
+       OptimizedBvhNode*       m_rightChild;
+
+       int     m_escapeIndex;
+
+       //for child nodes
+       int     m_subPart;
+       int     m_triangleIndex;
+
+};
+
+class NodeOverlapCallback
+{
+public:
+       virtual void ProcessNode(const OptimizedBvhNode* node) = 0;
+};
+
+typedef std::vector<OptimizedBvhNode>  NodeArray;
+
+
+///OptimizedBvh store an AABB tree that can be quickly traversed on CPU (and SPU,GPU in future)
+class OptimizedBvh
+{
+       OptimizedBvhNode*       m_rootNode1;
+       
+       OptimizedBvhNode*       m_contiguousNodes;
+       int                                     m_curNodeIndex;
+
+       int                                     m_numNodes;
+
+       NodeArray                       m_leafNodes;
+
+public:
+       OptimizedBvh() :m_rootNode1(0), m_numNodes(0) { }
+       
+       void    Build(StridingMeshInterface* triangles);
+
+       OptimizedBvhNode*       BuildTree       (NodeArray&     leafNodes,int startIndex,int endIndex);
+
+       int     CalcSplittingAxis(NodeArray&    leafNodes,int startIndex,int endIndex);
+
+       int     SortAndCalcSplittingIndex(NodeArray&    leafNodes,int startIndex,int endIndex,int splitAxis);
+       
+       void    WalkTree(OptimizedBvhNode* rootNode,NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const;
+       
+       void    WalkStacklessTree(OptimizedBvhNode* rootNode,NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const;
+       
+
+       //OptimizedBvhNode*     GetRootNode() { return m_rootNode1;}
+
+       int                                     GetNumNodes() { return m_numNodes;}
+
+       void    ReportAabbOverlappingNodex(NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const;
+
+       void    ReportSphereOverlappingNodex(NodeOverlapCallback* nodeCallback,const SimdVector3& aabbMin,const SimdVector3& aabbMax) const;
+
+
+};
+
+
+#endif //OPTIMIZED_BVH_H
\ No newline at end of file
diff --git a/extern/bullet/Bullet/CollisionShapes/TriangleCallback.cpp b/extern/bullet/Bullet/CollisionShapes/TriangleCallback.cpp
new file mode 100644 (file)
index 0000000..6fc6935
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#include "TriangleCallback.h"
+
+TriangleCallback::~TriangleCallback()
+{
+
+}
+
+
+InternalTriangleIndexCallback::~InternalTriangleIndexCallback()
+{
+
+}
\ No newline at end of file
diff --git a/extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.cpp b/extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.cpp
new file mode 100644 (file)
index 0000000..711a912
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#include "TriangleIndexVertexArray.h"
+
+TriangleIndexVertexArray::TriangleIndexVertexArray(int numTriangleIndices,int* triangleIndexBase,int triangleIndexStride,int numVertices,float* vertexBase,int vertexStride)
+:m_numTriangleIndices(numTriangleIndices),
+m_triangleIndexBase(triangleIndexBase),
+m_triangleIndexStride(triangleIndexStride),
+m_numVertices(numVertices),
+m_vertexBase(vertexBase),
+m_vertexStride(vertexStride)
+{
+}
+
+void   TriangleIndexVertexArray::getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart)
+{
+       numverts = m_numVertices;
+       (*vertexbase) = (unsigned char *)m_vertexBase;
+       type = PHY_FLOAT;
+       vertexStride = m_vertexStride;
+
+       numfaces = m_numTriangleIndices;
+       (*indexbase) = (unsigned char *)m_triangleIndexBase;
+       indexstride = m_triangleIndexStride;
+       indicestype = PHY_INTEGER;
+}
+
+void   TriangleIndexVertexArray::getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) const
+{
+       numverts = m_numVertices;
+       (*vertexbase) = (unsigned char *)m_vertexBase;
+       type = PHY_FLOAT;
+       vertexStride = m_vertexStride;
+
+       numfaces = m_numTriangleIndices;
+       (*indexbase) = (unsigned char *)m_triangleIndexBase;
+       indexstride = m_triangleIndexStride;
+       indicestype = PHY_INTEGER;
+}
+
diff --git a/extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.h b/extern/bullet/Bullet/CollisionShapes/TriangleIndexVertexArray.h
new file mode 100644 (file)
index 0000000..4784dc3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#include "StridingMeshInterface.h"
+
+
+class TriangleIndexVertexArray : public StridingMeshInterface
+{
+
+       int                     m_numTriangleIndices;
+       int*            m_triangleIndexBase;
+       int                     m_triangleIndexStride;
+       int                     m_numVertices;
+       float*          m_vertexBase;
+       int                     m_vertexStride;
+       
+public:
+
+       TriangleIndexVertexArray(int numTriangleIndices,int* triangleIndexBase,int triangleIndexStride,int numVertices,float* vertexBase,int vertexStride);
+       
+       virtual void    getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0);
+
+       virtual void    getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const;
+
+       /// unLockVertexBase finishes the access to a subpart of the triangle mesh
+       /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished
+       virtual void    unLockVertexBase(int subpart) {}
+
+       virtual void    unLockReadOnlyVertexBase(int subpart) const {}
+
+       /// getNumSubParts returns the number of seperate subparts
+       /// each subpart has a continuous array of vertices and indices
+       virtual int             getNumSubParts() const { return 1;}
+       
+       virtual void    preallocateVertices(int numverts){}
+       virtual void    preallocateIndices(int numindices){}
+
+};
\ No newline at end of file
diff --git a/extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.cpp b/extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.cpp
new file mode 100644 (file)
index 0000000..bb45e52
--- /dev/null
@@ -0,0 +1,33 @@
+
+#include "ManifoldContactAddResult.h"
+#include "NarrowPhaseCollision/PersistentManifold.h"
+
+ManifoldContactAddResult::ManifoldContactAddResult(SimdTransform transA,SimdTransform transB,PersistentManifold* manifoldPtr)
+               :m_manifoldPtr(manifoldPtr)
+{
+       m_transAInv = transA.inverse();
+       m_transBInv = transB.inverse();
+
+}
+
+
+void ManifoldContactAddResult::AddContactPoint(const SimdVector3& normalOnBInWorld,const SimdVector3& pointInWorld,float depth)
+{
+       if (depth > m_manifoldPtr->GetManifoldMargin())
+               return;
+
+
+       SimdVector3 pointA = pointInWorld + normalOnBInWorld * depth;
+       SimdVector3 localA = m_transAInv(pointA );
+       SimdVector3 localB = m_transBInv(pointInWorld);
+       ManifoldPoint newPt(localA,localB,normalOnBInWorld,depth);
+
+       int insertIndex = m_manifoldPtr->GetCacheEntry(newPt);
+       if (insertIndex >= 0)
+       {
+               m_manifoldPtr->ReplaceContactPoint(newPt,insertIndex);
+       } else
+       {
+               m_manifoldPtr->AddManifoldPoint(newPt);
+       }
+}
\ No newline at end of file
diff --git a/extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.h b/extern/bullet/Bullet/NarrowPhaseCollision/ManifoldContactAddResult.h
new file mode 100644 (file)
index 0000000..1d019f9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies.
+ * Erwin Coumans makes no representations about the suitability 
+ * of this software for any purpose.  
+ * It is provided "as is" without express or implied warranty.
+*/
+
+#ifndef MANIFOLD_CONTACT_ADD_RESULT_H
+#define MANIFOLD_CONTACT_ADD_RESULT_H
+
+#include "NarrowPhaseCollision/DiscreteCollisionDetectorInterface.h"
+class PersistentManifold;
+
+class ManifoldContactAddResult : public DiscreteCollisionDetectorInterface::Result
+{
+       PersistentManifold* m_manifoldPtr;
+       SimdTransform   m_transAInv;
+       SimdTransform   m_transBInv;
+
+public:
+
+       ManifoldContactAddResult(SimdTransform transA,SimdTransform transB,PersistentManifold* manifoldPtr);
+
+       virtual void AddContactPoint(const SimdVector3& normalOnBInWorld,const SimdVector3& pointInWorld,float depth);
+
+};
+
+#endif //MANIFOLD_CONTACT_ADD_RESULT_H
diff --git a/release/scripts/3ds_export.py b/release/scripts/3ds_export.py
new file mode 100644 (file)
index 0000000..ebe06c3
--- /dev/null
@@ -0,0 +1,673 @@
+#!BPY
+
+""" 
+Name: '3D Studio (.3ds)...'
+Blender: 237
+Group: 'Export'
+Tooltip: 'Export to 3DS file format (.3ds).'
+"""
+
+__author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes"]
+__url__ = ("blender", "elysiun", "http://www.gametutorials.com")
+__version__ = "0.82"
+__bpydoc__ = """\
+
+3ds Exporter
+
+This script Exports a 3ds file and the materials into blender for editing.
+
+Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen).
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb 
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+######################################################
+# Importing modules
+######################################################
+
+import Blender
+from Blender import NMesh, Scene, Object, Material
+import struct
+
+
+######################################################
+# Data Structures
+######################################################
+
+#Some of the chunks that we will export
+#----- Primary Chunk, at the beginning of each file
+PRIMARY= long("0x4D4D",16)
+
+#------ Main Chunks
+OBJECTINFO   =      long("0x3D3D",16);      #This gives the version of the mesh and is found right before the material and object information
+VERSION      =      long("0x0002",16);      #This gives the version of the .3ds file
+EDITKEYFRAME=      long("0xB000",16);      #This is the header for all of the key frame info
+
+#------ sub defines of OBJECTINFO
+MATERIAL=45055         #0xAFFF                         // This stored the texture info
+OBJECT=16384           #0x4000                         // This stores the faces, vertices, etc...
+
+#>------ sub defines of MATERIAL
+MATNAME    =      long("0xA000",16);      # This holds the material name
+MATAMBIENT   =      long("0xA010",16);      # Ambient color of the object/material
+MATDIFFUSE   =      long("0xA020",16);      # This holds the color of the object/material
+MATSPECULAR   =      long("0xA030",16);      # SPecular color of the object/material
+MATSHINESS   =      long("0xA040",16);      # ??
+MATMAP       =      long("0xA200",16);      # This is a header for a new material
+MATMAPFILE    =      long("0xA300",16);      # This holds the file name of the texture
+
+RGB1=  long("0x0011",16)
+RGB2=  long("0x0012",16)
+
+#>------ sub defines of OBJECT
+OBJECT_MESH  =      long("0x4100",16);      # This lets us know that we are reading a new object
+OBJECT_LIGHT =      long("0x4600",16);      # This lets un know we are reading a light object
+OBJECT_CAMERA=      long("0x4700",16);      # This lets un know we are reading a camera object
+
+#>------ sub defines of CAMERA
+OBJECT_CAM_RANGES=   long("0x4720",16);      # The camera range values
+
+#>------ sub defines of OBJECT_MESH
+OBJECT_VERTICES =   long("0x4110",16);      # The objects vertices
+OBJECT_FACES    =   long("0x4120",16);      # The objects faces
+OBJECT_MATERIAL =   long("0x4130",16);      # This is found if the object has a material, either texture map or color
+OBJECT_UV       =   long("0x4140",16);      # The UV texture coordinates
+OBJECT_TRANS_MATRIX  =   long("0x4160",16); # The Object Matrix
+
+#==============================================#
+# Strips the slashes from the back of a string #
+#==============================================#
+def stripPath(path):
+       return path.split('/')[-1].split('\\')[-1]
+
+#==================================================#
+# New name based on old with a different extension #
+#==================================================#
+def newFName(ext):
+       return Blender.Get('filename')[: -len(Blender.Get('filename').split('.', -1)[-1]) ] + ext
+
+
+#the chunk class
+class chunk:
+       ID=0
+       size=0
+
+       def __init__(self):
+               self.ID=0
+               self.size=0
+
+       def get_size(self):
+               self.size=6
+
+       def write(self, file):
+               #write header
+               data=struct.pack(\
+               "<HI",\
+               self.ID,\
+               self.size)
+               file.write(data)
+
+       def dump(self):
+               print "ID: ", self.ID
+               print "ID in hex: ", hex(self.ID)
+               print "size: ", self.size
+
+
+
+#may want to add light, camera, keyframe chunks.
+class vert_chunk(chunk):
+       verts=[]
+
+       def __init__(self):
+               self.verts=[]
+               self.ID=OBJECT_VERTICES
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=2 #for the number of verts short
+               temp_size += 12 * len(self.verts)  #3 floats x 4 bytes each
+               self.size+=temp_size
+               #~ print "vert_chunk size: ", self.size
+               return self.size
+       
+       def write(self, file):
+               chunk.write(self, file)
+               #write header
+               data=struct.pack("<H", len(self.verts))
+               file.write(data)
+               #write verts
+               for vert in self.verts:
+                       data=struct.pack("<3f",vert[0],vert[1], vert[2])
+                       file.write(data)
+
+class obj_material_chunk(chunk):
+       name=""
+       faces=[]
+
+       def __init__(self):
+               self.name=""
+               self.faces=[]
+               self.ID=OBJECT_MATERIAL
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=(len(self.name)+1)
+               temp_size+=2
+               for face in self.faces:
+                       temp_size+=2
+               self.size+=temp_size
+               #~ print "obj material chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               name_length=len(self.name)+1
+               binary_format="<"+str(name_length)+"s"
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+               binary_format="<H"
+               #~ print "Nr of faces: ", len(self.faces)
+               data=struct.pack(binary_format, len(self.faces))
+               file.write(data)
+               for face in self.faces:
+                       data=struct.pack(binary_format, face)
+                       file.write(data)
+
+class face_chunk(chunk):
+       faces=[]
+       num_faces=0
+       m_chunks=[]
+
+       def __init__(self):
+               self.faces=[]
+               self.ID=OBJECT_FACES
+               self.num_faces=0
+               self.m_chunks=[]
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size = 2 #num faces info
+               temp_size += 8 * len(self.faces)  #4 short ints x 2 bytes each
+               for m in self.m_chunks:
+                       temp_size+=m.get_size()
+               self.size += temp_size
+               #~ print "face_chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               data=struct.pack("<H", len(self.faces))
+               file.write(data)
+               #write faces
+               for face in self.faces:
+                       data=struct.pack("<4H", face[0],face[1], face[2], 0) # The last zero is only used by 3d studio
+                       file.write(data)
+               #write materials
+               for m in self.m_chunks:
+                       m.write(file)
+
+class uv_chunk(chunk):
+       uv=[]
+       num_uv=0
+
+       def __init__(self):
+               self.uv=[]
+               self.ID=OBJECT_UV
+               self.num_uv=0
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=2 #for num UV
+               for this_uv in self.uv:
+                       temp_size+=8  #2 floats at 4 bytes each
+               self.size+=temp_size
+               #~ print "uv chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               
+               #write header
+               data=struct.pack("<H", len(self.uv))
+               file.write(data)
+               
+               #write verts
+               for this_uv in self.uv:
+                       data=struct.pack("<2f", this_uv[0], this_uv[1])
+                       file.write(data)
+
+class mesh_chunk(chunk):
+       v_chunk=vert_chunk()
+       f_chunk=face_chunk()
+       uv_chunk=uv_chunk()
+
+       def __init__(self):
+               self.v_chunk=vert_chunk()
+               self.f_chunk=face_chunk()
+               self.uv_chunk=uv_chunk()
+               self.ID=OBJECT_MESH
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.v_chunk.get_size()
+               temp_size+=self.f_chunk.get_size()
+               temp_size+=self.uv_chunk.get_size()
+               self.size+=temp_size
+               #~ print "object mesh chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write stuff
+               self.v_chunk.write(file)
+               self.f_chunk.write(file)
+               self.uv_chunk.write(file)
+
+class object_chunk(chunk):
+       name=""
+       mesh_chunks=[]
+
+       def __init__(self):
+               self.name=""
+               self.mesh_chunks=[]
+               self.ID=OBJECT
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=len(self.name)+1 #+1 for null character
+               for mesh in self.mesh_chunks:
+                       temp_size+=mesh.get_size()
+               self.size+=temp_size
+               #~ print "object chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               
+               binary_format = "<%ds" % (len(self.name)+1)
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+               #write stuff
+               for mesh in self.mesh_chunks:
+                       mesh.write(file)
+
+class object_info_chunk(chunk):
+       obj_chunks=[]
+       mat_chunks=[]
+
+       def __init__(self):
+               self.obj_chunks=[]
+               self.mat_chunks=[]
+               self.ID=OBJECTINFO
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=0
+               for mat in self.mat_chunks:
+                       temp_size+=mat.get_size()
+               for obj in self.obj_chunks:
+                       temp_size+=obj.get_size()
+               self.size+=temp_size
+               #~ print "object info size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write all the materials
+               for mat in self.mat_chunks:
+                       mat.write(file)
+               #write all the objects
+               for obj in self.obj_chunks:
+                       obj.write(file)
+
+
+
+class version_chunk(chunk):
+       version=3
+
+       def __init__(self):
+               self.ID=VERSION
+               self.version=3 #that the document that I'm using
+
+       def get_size(self):
+               chunk.get_size(self)
+               self.size += 4 #bytes for the version info
+               #~ print "version chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write header and version
+               data=struct.pack("<I", self.version)
+               file.write(data)
+
+class rgb_chunk(chunk):
+       col=[]
+
+       def __init__(self):
+               self.col=[]
+
+       def get_size(self):
+               chunk.get_size(self)
+               self.size+=3 #color size
+               #~ print "rgb chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               for c in self.col:
+                       file.write( struct.pack("<c", chr(int(255*c))) )
+
+
+class rgb1_chunk(rgb_chunk):
+
+       def __init__(self):
+               self.ID=RGB1
+
+class rgb2_chunk(rgb_chunk):
+
+       def __init__(self):
+               self.ID=RGB2
+
+class material_ambient_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATAMBIENT
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material ambient size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_diffuse_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATDIFFUSE
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material diffuse size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_specular_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATSPECULAR
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material specular size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_name_chunk(chunk):
+       name=""
+
+       def __init__(self):
+               self.ID=MATNAME
+               self.name=""
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=(len(self.name)+1)
+               self.size+=temp_size
+               #~ print "material name size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               name_length=len(self.name)+1
+               binary_format="<"+str(name_length)+"s"
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+
+class material_chunk(chunk):
+       matname_chunk=None
+       matambient_chunk=None
+       matdiffuse_chunk=None
+       matspecular_chunk=None
+
+       def __init__(self):
+               self.ID=MATERIAL
+               self.matname_chunk=material_name_chunk()
+               self.matambient_chunk=material_ambient_chunk()
+               self.matdiffuse_chunk=material_diffuse_chunk()
+               self.matspecular_chunk=material_specular_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.matname_chunk.get_size()
+               temp_size+=self.matambient_chunk.get_size()
+               temp_size+=self.matdiffuse_chunk.get_size()
+               temp_size+=self.matspecular_chunk.get_size()
+               self.size+=temp_size
+               #~ print "material chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name chunk
+               self.matname_chunk.write(file)
+               #write material colors
+               self.matambient_chunk.write(file)
+               self.matdiffuse_chunk.write(file)
+               self.matspecular_chunk.write(file)
+
+class primary_chunk(chunk):
+       version=None
+       obj_info=None
+
+       def __init__(self):
+               self.version=version_chunk()
+               self.obj_info=object_info_chunk()
+               self.ID=PRIMARY
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.version.get_size()
+               temp_size+=self.obj_info.get_size()
+               self.size+=temp_size
+               #~ print "primary chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write version chunk
+               self.version.write(file)
+               #write object_info chunk
+               self.obj_info.write(file)
+
+def read_chunk(file, chunk):
+               chunk.ID, chunk.size = \
+               struct.unpack(\
+               chunk.binary_format, \
+               file.read(struct.calcsize(chunk.binary_format))  )
+       
+def read_string(file):
+       s=""
+       index=0
+       
+       #read in the characters till we get a null character
+       data=struct.unpack("c", file.read(struct.calcsize("c")))
+       s=s+(data[0])
+       #print "string: ",s
+       while(ord(s[index])!=0):
+               index+=1
+               data=struct.unpack("c", file.read(struct.calcsize("c")))
+               s=s+(data[0])
+               #print "string: ",s
+       return str(s)
+
+######################################################
+# EXPORT
+######################################################
+def save_3ds(filename):
+       # Time the export
+       time1 = Blender.sys.time()
+
+       exported_materials = {}
+
+       #fill the chunks full of data
+       primary=primary_chunk()
+       #get all the objects in this scene
+       object_list = [ ob for ob in Blender.Object.GetSelected() if ob.getType() == 'Mesh' ]
+       #fill up the data structures with objects
+       for obj in object_list:
+               #create a new object chunk
+               primary.obj_info.obj_chunks.append(object_chunk())
+               #get the mesh data
+               blender_mesh = obj.getData()
+               blender_mesh.transform(obj.getMatrix())
+               #set the object name
+               primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].name=obj.getName()
+
+               matrix = obj.getMatrix()
+
+               #make a new mesh chunk object
+               mesh=mesh_chunk()
+               
+               mesh.v_chunk.verts = blender_mesh.verts
+               
+               dummy = None # just incase...
+               
+               for m in blender_mesh.materials:
+                       mesh.f_chunk.m_chunks.append(obj_material_chunk())
+                       mesh.f_chunk.m_chunks[len(mesh.f_chunk.m_chunks)-1].name = m.name
+
+                       # materials should only be exported once
+                       try:
+                               dummy = exported_materials[m.name]
+                               
+                               
+                       except KeyError:
+                               material = material_chunk()
+                               material.matname_chunk.name=m.name
+                               material.matambient_chunk.col1.col = m.mirCol
+                               material.matambient_chunk.col2.col = m.mirCol
+                               material.matdiffuse_chunk.col1.col = m.rgbCol
+                               material.matdiffuse_chunk.col2.col = m.rgbCol
+                               material.matspecular_chunk.col1.col = m.specCol
+                               material.matspecular_chunk.col2.col = m.specCol
+                               
+                               primary.obj_info.mat_chunks.append(material)
+                               
+                               exported_materials[m.name] = None
+               
+               del dummy # unpolute the namespace
+               
+               valid_faces = [f for f in blender_mesh.faces if len(f) > 2]
+               facenr=0
+               #fill in faces
+               for face in valid_faces:
+                       
+                       #is this a tri or a quad
+                       num_fv=len(face.v)
+                       
+                       
+                       #it's a tri
+                       if num_fv==3:
+                               mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index))
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                       
+                       else: #it's a quad                                      
+                               mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index))  # 0,1,2
+                               mesh.f_chunk.faces.append((face[2].index, face[3].index, face[0].index))  # 2,3,0
+                               #first tri
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                               #other tri
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                       
+
+               #fill in the UV info
+               if blender_mesh.hasVertexUV():
+                       for vert in blender_mesh.verts:
+                               mesh.uv_chunk.uv.append((vert.uvco[0], vert.uvco[1]))
+
+               elif blender_mesh.hasFaceUV():
+                       for face in valid_faces:
+                               # Tri or quad.
+                               for uv_coord in face.uv:
+                                       mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1]))
+
+               #filled in our mesh, lets add it to the file
+               primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].mesh_chunks.append(mesh)
+
+       #check the size
+       primary.get_size()
+       #open the files up for writing
+       file = open( filename, "wb" )
+       #recursively write the stuff to file
+       primary.write(file)
+       file.close()
+       print "3ds export time: %.2f" % (Blender.sys.time() - time1)
+       
+
+Blender.Window.FileSelector(save_3ds, "Export 3DS", newFName('3ds'))
diff --git a/release/scripts/3ds_import.py b/release/scripts/3ds_import.py
new file mode 100644 (file)
index 0000000..017ec74
--- /dev/null
@@ -0,0 +1,662 @@
+#!BPY
+
+""" 
+Name: '3D Studio (.3ds)...'
+Blender: 237
+Group: 'Import'
+Tooltip: 'Import from 3DS file format. (.3ds)'
+"""
+
+__author__ = ["Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Campbell Barton"]
+__url__ = ("blender", "elysiun", "http://www.gametutorials.com")
+__version__ = "0.92"
+__bpydoc__ = """\
+
+3ds Importer
+
+This script imports a 3ds file and the materials into Blender for editing.
+
+Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen).
+
+Changes:
+
+0.92<br>
+- Added support for diffuse, alpha, spec, bump maps in a single material
+
+0.9<br>
+- Reorganized code into object/material block functions<br>
+- Use of Matrix() to copy matrix data<br>
+- added support for material transparency<br>
+
+0.81a (fork- not 0.9) Campbell Barton 2005-06-08<br>
+- Simplified import code<br>
+- Never overwrite data<br>
+- Faster list handling<br>
+- Leaves import selected<br>
+
+0.81 Damien McGinnes 2005-01-09<br>
+- handle missing images better<br>
+
+0.8 Damien McGinnes 2005-01-08<br>
+- copies sticky UV coords to face ones<br>
+- handles images better<br>
+- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script
+
+"""
+
+# $Id$
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+# Importing modules
+
+import Blender
+from Blender import NMesh, Scene, Object, Material, Image, Texture
+
+import sys, struct, string
+
+import os
+
+######################################################
+# Data Structures
+######################################################
+#----- Primary Chunk, 
+PRIMARY                        =       long("0x4D4D",16)       # should be aat the beginning of each file
+VERSION                        =       long("0x0002",16)       #This gives the version of the .3ds file
+EDITOR_BLOCK   =       long("0x3D3D",16)       #this is the Editor Data block, contains objects, materials
+KEYFRAME_BLOCK =       long("0xB000",16)       #This is the header for all of the key frame info
+
+#------ sub defines of EDITOR_BLOCK
+MATERIAL_BLOCK =       long("0xAFFF",16)       #This stores the Material info
+OBJECT_BLOCK   =       long("0x4000",16)       #This stores the Object,Camera,Light
+
+#------ sub defines of OBJECT_BLOCK
+OBJECT_MESH            =       long("0x4100",16)       # This lets us know that we are reading a new object
+OBJECT_LIGHT   =       long("0x4600",16)       # This lets un know we are reading a light object
+OBJECT_CAMERA  =       long("0x4700",16)       # This lets un know we are reading a camera object
+
+#------ sub defines of OBJECT_MESH
+MESH_VERTICES  =       long("0x4110",16)       # The objects vertices
+MESH_FACES             =       long("0x4120",16)       # The objects faces
+MESH_MATERIAL  =       long("0x4130",16)       # This is found if the object has a material, either texture map or color
+MESH_UV                        =       long("0x4140",16)       # The UV texture coordinates
+MESH_TRANS_MATRIX      =       long("0x4160",16)       # The Object Matrix
+MESH_COLOR             =       long("0x4165",16)       # The color of the object
+MESH_TEXTURE_INFO      =       long("0x470",16)        # Info about the Object Texture
+
+#------ sub defines of OBJECT_CAMERA
+CAMERA_CONE            =       long("0x4710",16)       # The camera see cone
+CAMERA_RANGES  =       long("0x4720",16)       # The camera range values
+
+#------ sub defines of OBJECT_LIGHT
+LIGHT_SPOTLIGHT        =       long("0x4610",16)       # A spotlight
+LIGHT_ATTENUATE        =       long("0x4625",16)       # Light attenuation values
+
+
+#------ sub defines of MATERIAL_BLOCK
+MAT_NAME               =       long("0xA000",16)       # This holds the material name
+MAT_AMBIENT            =       long("0xA010",16)       # Ambient color of the object/material
+MAT_DIFFUSE            =       long("0xA020",16)       # This holds the color of the object/material
+MAT_SPECULAR   =       long("0xA030",16)       # SPecular color of the object/material
+MAT_SHINESS            =       long("0xA040",16)       # ??
+MAT_TRANSPARENCY=      long("0xA050",16)       # Transparency value of material
+MAT_SELF_ILLUM =       long("0xA080",16)       # Self Illumination value of material
+MAT_WIRE               =       long("0xA085",16)       # Only render's wireframe
+
+MAT_TEXTURE_MAP        =       long("0xA200",16)       # This is a header for a new texture map
+MAT_SPECULAR_MAP=      long("0xA204",16)       # This is a header for a new specular map
+MAT_OPACITY_MAP        =       long("0xA210",16)       # This is a header for a new opacity map
+MAT_REFLECTION_MAP=    long("0xA220",16)       # This is a header for a new reflection map
+MAT_BUMP_MAP   =       long("0xA230",16)       # This is a header for a new bump map
+MAT_MAP_FILENAME=      long("0xA300",16)       # This holds the file name of the texture
+#lots more to add here for maps
+
+######################################################
+# Globals
+######################################################
+TEXTURE_DICT={}
+MATERIAL_DICT={}
+
+
+######################################################
+# Chunk Class
+######################################################
+class chunk:
+       ID=0
+       length=0
+       bytes_read=0
+
+       binary_format="<HI"
+
+       def __init__(self):
+               self.ID=0
+               self.length=0
+               self.bytes_read=0
+
+       def dump(self):
+               print "ID in hex: ", hex(self.ID)
+               print "length: ", self.length
+               print "bytes_read: ", self.bytes_read
+               
+
+######################################################
+# Helper functions
+######################################################
+def read_chunk(file, chunk):
+       temp_data=file.read(struct.calcsize(chunk.binary_format))
+       data=struct.unpack(chunk.binary_format, temp_data)
+       chunk.ID=data[0]
+       chunk.length=data[1]
+       chunk.bytes_read=6
+
+def skip_to_end(file, skip_chunk):
+       buffer_size=skip_chunk.length-skip_chunk.bytes_read
+       binary_format=str(buffer_size)+"c"
+       temp_data=file.read(struct.calcsize(binary_format))
+       skip_chunk.bytes_read+=buffer_size
+
+def read_string(file):
+       s=""
+       index=0
+       #read the first character
+       temp_data=file.read(1)
+       data=struct.unpack("c", temp_data)
+       s=s+(data[0])
+       #read in the characters till we get a null character
+       while(ord(s[index])!=0):
+               index+=1
+               temp_data=file.read(1)
+               data=struct.unpack("c", temp_data)
+               s=s+(data[0])
+       the_string=s[:-1]  #remove the null character from the string
+       return str(the_string)
+
+def getUniqueName(name):
+       newName = name
+       uniqueInt = 0
+       while 1:
+               try:
+                       ob = Object.Get(newName)
+                       # Okay, this is working, so lets make a new name
+                       newName = '%s.%d' % (name, uniqueInt)
+                       uniqueInt +=1
+               except AttributeError:
+                       if newName not in NMesh.GetNames():
+                               return newName
+                       else:
+                               newName = '%s.%d' % (name, uniqueInt)
+                               uniqueInt +=1
+
+def add_texture_to_material(image, texture, material, mapto):
+       if mapto=="DIFFUSE":
+               map=Texture.MapTo.COL
+       elif mapto=="SPECULAR":
+               map=Texture.MapTo.SPEC
+       elif mapto=="OPACITY":
+               map=Texture.MapTo.ALPHA
+       elif mapto=="BUMP":
+               map=Texture.MapTo.NOR
+       else:
+               print "/tError:  Cannot map to ", mapto
+               return
+       
+       texture.setImage(image)
+       texture_list=material.getTextures()
+       index=0
+       for tex in texture_list:
+               if tex==None:
+                       material.setTexture(index,texture,Texture.TexCo.OBJECT,map)
+                       return
+               else:
+                       index+=1
+               if index>10:
+                       print "/tError: Cannot add diffuse map.  Too many textures"
+       
+######################################################
+# Process an object (tri-mesh, Camera, or Light)
+######################################################
+def process_object_block(file, previous_chunk, object_list):
+       # Localspace variable names, faster.
+       STRUCT_SIZE_2FLOAT = struct.calcsize("2f")
+       STRUCT_SIZE_3FLOAT = struct.calcsize("3f")
+       STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize("H")
+       STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize("4H")
+       STRUCT_SIZE_4x3MAT = struct.calcsize("ffffffffffff")
+       
+       #spare chunks
+       new_chunk=chunk()
+       temp_chunk=chunk()
+       
+       global TEXURE_DICT
+       global MATERIAL_DICT
+       
+       #don't know which one we're making, so let's have a place for one of each
+       new_mesh=None
+       new_light=None
+       new_camera=None
+       
+       #all objects have a name (first thing)
+       tempName = str(read_string(file))
+       obj_name = getUniqueName( tempName )
+       previous_chunk.bytes_read += (len(tempName)+1)
+       
+       while (previous_chunk.bytes_read<previous_chunk.length):
+               read_chunk(file, new_chunk)
+               
+               if (new_chunk.ID==OBJECT_MESH):
+                       new_mesh=Blender.NMesh.New(obj_name)
+                       while (new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               
+                               if (temp_chunk.ID==MESH_VERTICES):
+                                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                       data=struct.unpack("H", temp_data)
+                                       temp_chunk.bytes_read+=2
+                                       num_verts=data[0]
+                                       for counter in range (num_verts):
+                                               temp_data=file.read(STRUCT_SIZE_3FLOAT)
+                                               temp_chunk.bytes_read += STRUCT_SIZE_3FLOAT
+                                               data=struct.unpack("3f", temp_data)
+                                               v=NMesh.Vert(data[0],data[1],data[2])
+                                               new_mesh.verts.append(v)
+               
+                               elif (temp_chunk.ID==MESH_FACES):
+                                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                       data=struct.unpack("H", temp_data)
+                                       temp_chunk.bytes_read+=2
+                                       num_faces=data[0]
+                                       for counter in range(num_faces):
+                                               temp_data=file.read(STRUCT_SIZE_4UNSIGNED_SHORT)
+                                               temp_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each
+                                               data=struct.unpack("4H", temp_data)
+                                               #insert the mesh info into the faces, don't worry about data[3] it is a 3D studio thing
+                                               f = NMesh.Face( [new_mesh.verts[data[i]] for i in xrange(3)] )
+                                               f.uv = [ tuple(new_mesh.verts[data[i]].uvco[:2]) for  i in xrange(3) ]
+                                               new_mesh.faces.append(f)
+               
+                               elif (temp_chunk.ID==MESH_MATERIAL):
+                                       material_name=""
+                                       material_name=str(read_string(file))
+                                       temp_chunk.bytes_read += len(material_name)+1 # remove 1 null character.
+                                       material_found=0
+                                       for mat in Material.Get():
+                                               if(mat.name==material_name):
+                                                       if len(new_mesh.materials) >= 15:
+                                                               print "\tCant assign more than 16 materials per mesh, keep going..."
+                                                               break
+                                                       else:
+                                                               meshHasMat = 0
+                                                               for myMat in new_mesh.materials:
+                                                                       if myMat.name == mat.name:
+                                                                               meshHasMat = 1
+                                                               if meshHasMat == 0:
+                                                                       new_mesh.addMaterial(mat)
+                                                                       material_found=1
+                                                                       #figure out what material index this is for the mesh
+                                                                       for mat_counter in range(len(new_mesh.materials)):
+                                                                               if new_mesh.materials[mat_counter].name == material_name:
+                                                                                       mat_index=mat_counter                                                   
+                                                               break # get out of this for loop so we don't accidentally set material_found back to 0
+                                               else:
+                                                       material_found=0
+                                                       
+                                       if material_found == 1:
+                                               #read the number of faces using this material
+                                               temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                               data=struct.unpack("H", temp_data)
+                                               temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
+                                               num_faces_using_mat=data[0]
+                                               #list of faces using mat
+                                               for face_counter in range(num_faces_using_mat):
+                                                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                                       temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
+                                                       data=struct.unpack("H", temp_data)
+                                                       new_mesh.faces[data[0]].materialIndex = mat_index       
+                                                       try:
+                                                               mname = MATERIAL_DICT[mat.name]
+                                                               new_mesh.faces[data[0]].image = TEXTURE_DICT[mname]
+                                                       except:
+                                                               continue
+                                       else:
+                                               #read past the information about the material you couldn't find
+                                               skip_to_end(file,temp_chunk)    
+               
+                               elif (new_chunk.ID == MESH_UV):
+                                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                       data=struct.unpack("H", temp_data)
+                                       temp_chunk.bytes_read+=2
+                                       num_uv=data[0]
+               
+                                       for counter in range(num_uv):
+                                               temp_data=file.read(STRUCT_SIZE_2FLOAT)
+                                               temp_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each
+                                               data=struct.unpack("2f", temp_data)
+                                               #insert the insert the UV coords in the vertex data
+                                               new_mesh.verts[counter].uvco = data
+                               
+                               elif (new_chunk.ID == MESH_TRANS_MATRIX):
+                                       temp_data=file.read(STRUCT_SIZE_4x3MAT)
+                                       data = list( struct.unpack("ffffffffffff", temp_data) )
+                                       temp_chunk.bytes_read += STRUCT_SIZE_4x3MAT 
+                                       new_matrix = Blender.Mathutils.Matrix(\
+                                       data[:3] + [0],\
+                                       data[3:6] + [0],\
+                                       data[6:9] + [0],\
+                                       data[9:] + [1])
+                                       new_mesh.setMatrix(new_matrix)
+                               else:
+                                       skip_to_end(file, temp_chunk)
+                               
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+               
+               elif (new_chunk.ID==OBJECT_LIGHT):
+                       skip_to_end(file,new_chunk)
+
+               elif (new_chunk.ID==OBJECT_CAMERA):
+                       skip_to_end(file,new_chunk)
+                       
+               else: #don't know what kind of object it is
+                       skip_to_end(file,new_chunk)
+               
+               if new_mesh!=None:
+                       object_list.append(NMesh.PutRaw(new_mesh))
+               if new_light!=None:
+                       object_list.append(new_light)
+               if new_camera!=None:
+                       object_list.append(new_camera)
+               
+               previous_chunk.bytes_read+=new_chunk.bytes_read
+
+######################################################
+# Process a Material
+######################################################
+def process_material_block(file, previous_chunk):
+       # Localspace variable names, faster.
+       STRUCT_SIZE_3BYTE = struct.calcsize("3B")
+       STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize("H")
+       
+       #spare chunks
+       new_chunk=chunk()
+       temp_chunk=chunk()
+       
+       global TEXURE_DICT
+       global MATERIAL_DICT
+       
+       new_material=Blender.Material.New()
+       
+       while (previous_chunk.bytes_read<previous_chunk.length):
+               #read the next chunk
+               read_chunk(file, new_chunk)
+               
+               if (new_chunk.ID==MAT_NAME):
+                       material_name=""
+                       material_name=str(read_string(file))
+                       new_chunk.bytes_read+=(len(material_name)+1) #plus one for the null character that ended the string
+                       new_material.setName(material_name)
+                       MATERIAL_DICT[material_name] = new_material.name
+               
+               elif (new_chunk.ID==MAT_AMBIENT):
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(STRUCT_SIZE_3BYTE)
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       new_material.mirCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MAT_DIFFUSE):
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(STRUCT_SIZE_3BYTE)
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       new_material.rgbCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MAT_SPECULAR):
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(STRUCT_SIZE_3BYTE)
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       new_material.specCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MAT_TEXTURE_MAP):
+                       new_texture=Blender.Texture.New('Diffuse')
+                       new_texture.setType('Image')
+                       while (new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               
+                               if (temp_chunk.ID==MAT_MAP_FILENAME):
+                                       texture_name=""
+                                       texture_name=str(read_string(file))
+                                       try:
+                                               img = Image.Load(texture_name)
+                                               TEXTURE_DICT[new_material.name]=img
+                                       except IOError:
+                                               fname = os.path.join( os.path.dirname(FILENAME), texture_name)
+                                               try:
+                                                       img = Image.Load(fname)
+                                                       TEXTURE_DICT[new_material.name]=img
+                                               except IOError:
+                                                       print "\tERROR: failed to load image ",texture_name
+                                                       TEXTURE_DICT[new_material.name] = None # Dummy
+                                                       img=Blender.Image.New(fname,1,1,24) #blank image
+                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
+                                       
+                               else:
+                                       skip_to_end(file, temp_chunk)
+                               
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+                       
+                       #add the map to the material in the right channel
+                       add_texture_to_material(img, new_texture, new_material, "DIFFUSE")
+                               
+               elif (new_chunk.ID==MAT_SPECULAR_MAP):
+                       new_texture=Blender.Texture.New('Specular')
+                       new_texture.setType('Image')
+                       while (new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               
+                               if (temp_chunk.ID==MAT_MAP_FILENAME):
+                                       texture_name=""
+                                       texture_name=str(read_string(file))
+                                       try:
+                                               img = Image.Load(texture_name)
+                                               TEXTURE_DICT[new_material.name]=img
+                                       except IOError:
+                                               fname = os.path.join( os.path.dirname(FILENAME), texture_name)
+                                               try:
+                                                       img = Image.Load(fname)
+                                                       TEXTURE_DICT[new_material.name]=img
+                                               except IOError:
+                                                       print "\tERROR: failed to load image ",texture_name
+                                                       TEXTURE_DICT[new_material.name] = None # Dummy
+                                                       img=Blender.Image.New(fname,1,1,24) #blank image
+                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
+                               else:
+                                       skip_to_end(file, temp_chunk)
+                               
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+                               
+                       #add the map to the material in the right channel
+                       add_texture_to_material(img, new_texture, new_material, "SPECULAR")
+       
+               elif (new_chunk.ID==MAT_OPACITY_MAP):
+                       new_texture=Blender.Texture.New('Opacity')
+                       new_texture.setType('Image')
+                       while (new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               
+                               if (temp_chunk.ID==MAT_MAP_FILENAME):
+                                       texture_name=""
+                                       texture_name=str(read_string(file))
+                                       try:
+                                               img = Image.Load(texture_name)
+                                               TEXTURE_DICT[new_material.name]=img
+                                       except IOError:
+                                               fname = os.path.join( os.path.dirname(FILENAME), texture_name)
+                                               try:
+                                                       img = Image.Load(fname)
+                                                       TEXTURE_DICT[new_material.name]=img
+                                               except IOError:
+                                                       print "\tERROR: failed to load image ",texture_name
+                                                       TEXTURE_DICT[new_material.name] = None # Dummy
+                                                       img=Blender.Image.New(fname,1,1,24) #blank image
+                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
+                               else:
+                                       skip_to_end(file, temp_chunk)
+                               
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+
+                       #add the map to the material in the right channel
+                       add_texture_to_material(img, new_texture, new_material, "OPACITY")
+
+               elif (new_chunk.ID==MAT_BUMP_MAP):
+                       new_texture=Blender.Texture.New('Bump')
+                       new_texture.setType('Image')
+                       while (new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               
+                               if (temp_chunk.ID==MAT_MAP_FILENAME):
+                                       texture_name=""
+                                       texture_name=str(read_string(file))
+                                       try:
+                                               img = Image.Load(texture_name)
+                                               TEXTURE_DICT[new_material.name]=img
+                                       except IOError:
+                                               fname = os.path.join( os.path.dirname(FILENAME), texture_name)
+                                               try:
+                                                       img = Image.Load(fname)
+                                                       TEXTURE_DICT[new_material.name]=img
+                                               except IOError:
+                                                       print "\tERROR: failed to load image ",texture_name
+                                                       TEXTURE_DICT[new_material.name] = None # Dummy
+                                                       img=Blender.Image.New(fname,1,1,24) #blank image        
+                                       new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed
+                               else:
+                                       skip_to_end(file, temp_chunk)
+                               
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+                               
+                       #add the map to the material in the right channel
+                       add_texture_to_material(img, new_texture, new_material, "BUMP")
+                       
+               elif (new_chunk.ID==MAT_TRANSPARENCY):
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                       data=struct.unpack("H", temp_data)
+                       temp_chunk.bytes_read+=2
+                       new_material.setAlpha(1-(float(data[0])/100))
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+                       
+               else:
+                       skip_to_end(file,new_chunk)
+               
+               previous_chunk.bytes_read+=new_chunk.bytes_read
+
+######################################################
+# process a main chunk
+######################################################
+def process_main_chunk(file,previous_chunk,new_object_list):
+       
+       #spare chunks
+       new_chunk=chunk()
+       temp_chunk=chunk()
+
+       #Go through the main chunk
+       while (previous_chunk.bytes_read<previous_chunk.length):
+               read_chunk(file, new_chunk)
+               
+               if (new_chunk.ID==VERSION):
+                       temp_data=file.read(struct.calcsize("I"))
+                       data=struct.unpack("I", temp_data)
+                       version=data[0]
+                       new_chunk.bytes_read+=4 #read the 4 bytes for the version number
+                       if (version>3): #this loader works with version 3 and below, but may not with 4 and above
+                               print "\tNon-Fatal Error:  Version greater than 3, may not load correctly: ", version
+
+               elif (new_chunk.ID==EDITOR_BLOCK):
+                       while(new_chunk.bytes_read<new_chunk.length):
+                               read_chunk(file, temp_chunk)
+                               if (temp_chunk.ID==MATERIAL_BLOCK):
+                                       process_material_block(file, temp_chunk)
+                               elif (temp_chunk.ID==OBJECT_BLOCK):
+                                       process_object_block(file, temp_chunk,new_object_list)
+                               else:
+                                       skip_to_end(file,temp_chunk)
+                                       
+                               new_chunk.bytes_read+=temp_chunk.bytes_read
+               else: 
+                       skip_to_end(file,new_chunk)
+       
+               previous_chunk.bytes_read+=new_chunk.bytes_read
+
+#***********************************************
+# main entry point for loading 3ds files
+#***********************************************
+def load_3ds (filename):
+       current_chunk=chunk()
+       print "--------------------------------"
+       print 'Importing "%s"' % filename
+       time1 = Blender.sys.time()  #for timing purposes
+       file=open(filename,"rb")
+       new_object_list = []
+       
+       global FILENAME
+       FILENAME=filename
+       
+       read_chunk(file, current_chunk)
+       
+       if (current_chunk.ID!=PRIMARY):
+               print "\tFatal Error:  Not a valid 3ds file: ", filename
+               file.close()
+               return
+       
+       process_main_chunk(file, current_chunk, new_object_list)
+       
+       # Select all new objects.
+       for ob in new_object_list: ob.sel = 1
+       
+       print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))
+       file.close()
+
+#***********************************************
+# MAIN
+#***********************************************
+def my_callback(filename):
+       load_3ds(filename)
+
+Blender.Window.FileSelector(my_callback, "Import 3DS", '*.3ds')
+
+# For testing compatibility
+'''
+TIME = Blender.sys.time()
+import os
+for _3ds in os.listdir('/3ds/'):
+       if _3ds.lower().endswith('3ds'):
+               print _3ds
+               newScn = Scene.New(_3ds)
+               newScn.makeCurrent()
+               my_callback('/3ds/' + _3ds)
+
+print "TOTAL TIME: ", Blender.sys.time() - TIME
+'''
diff --git a/release/scripts/blenderLipSynchro.py b/release/scripts/blenderLipSynchro.py
new file mode 100644 (file)
index 0000000..8c66e67
--- /dev/null
@@ -0,0 +1,602 @@
+#!BPY
+
+"""
+Name: 'BlenderLipSynchro'
+Blender: 239
+Group: 'Animation'
+Tooltip: 'Import phonemes from Papagayo or JLipSync for lip synchronisation'
+"""
+
+__author__ = "Dienben: Benoit Foucque dienben_mail@yahoo.fr"
+__url__ = ("blenderLipSynchro Blog, http://blenderlipsynchro.blogspot.com/",
+"Papagayo (Python), http://www.lostmarble.com/papagayo/index.shtml",
+"JLipSync (Java), http://jlipsync.sourceforge.net/")
+__version__ = "1.2"
+__bpydoc__ = """\
+Description:
+
+This script imports Voice Export made by Papagayo or JLipSync and maps the export with your shapes.
+
+Usage:
+
+Import a Papagayo or JLipSync voice export file and link it with your shapes.
+
+Note:<br>
+- Naturally, you need files exported from one of the supported lip synching
+programs. Check their sites to learn more and download them.
+
+"""
+
+# --------------------------------------------------------------------------
+# BlenderLipSynchro
+# --------------------------------------------------------------------------
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# -------------------------------------------------------------------------- 
+
+
+
+#il y a 3 etapes
+#la deuxieme on charge le dictionnaire de correspondance
+#la troisieme on fait le choix des correpondance
+#la quatrieme on construit les cles a partir du fichiers frame
+
+#there are 3 stage
+#the second one load the mapping dictionnary 
+#the tird make the mapping
+#the fourth make the key in the IPO Curve
+
+#voici mes differents imports
+#the imports
+import os
+import Blender
+
+from Blender import Ipo
+from Blender.Draw import *
+from Blender.BGL import *
+
+
+
+#ici commencent mes fonctions
+#here begin my functions
+#cette fonction trace l'interface graphique
+#this functions draw the User interface
+def trace():
+    #voici mes variables pouvant etre modifie
+    #my variables
+    global fichier_dico,repertoire_dictionaire,iponame,repertoire_phoneme,fichier_text,nbr_phoneme
+    global let01, let02, let03, let04,let05, let06, let07, let08, let09, let10
+    global let11, let12, let13, let14,let15, let16, let17, let18, let19, let20
+    global let21, let22, let23, let24
+
+    global let01selectkey,let02selectkey,let03selectkey,let04selectkey,let05selectkey
+    global let06selectkey,let07selectkey,let08selectkey,let09selectkey,let10selectkey,let11selectkey
+    global let12selectkey,let13selectkey,let14selectkey,let15selectkey,let16selectkey,let17selectkey
+    global let18selectkey,let19selectkey,let20selectkey,let21selectkey,let22selectkey,let23selectkey
+    global let24selectkey
+    
+    glClearColor(0.4,0.5,0.6 ,0.0)
+    glClear(GL_COLOR_BUFFER_BIT)
+
+    glColor3d(1,1,1)
+    glRasterPos2i(87, 375)
+    Text("Blendersynchro V 1.1")
+    glColor3d(1,1,1)
+    glRasterPos2i(84, 360)
+    Text("Programation: Dienben")
+
+    glColor3d(0,0,0)
+    glRasterPos2i(13, 342)
+    Text("Papagayo File importer")
+    glColor3d(0,0,0)
+    glRasterPos2i(13, 326)
+    Text("Thanks to Chris Clawson and Liubomir Kovatchev")
+
+    glColor3d(1,1,1)
+    glRasterPos2i(5, 320)
+    Text("_______________________________________________________")
+    glColor3d(0,0,0)
+    glRasterPos2i(6, 318)
+    Text("_______________________________________________________")
+
+    
+    if (etape==1):
+        #cette etape permet de choisi la correspondance entre les phonemes et les cles
+        #this stage offer the possibility to choose the mapping between phonems and shapes
+
+        glColor3d(1,1,1)
+        glRasterPos2i(140, 300)
+        Text("Objet: "+Blender.Object.GetSelected()[0].getName() )
+
+        glColor3d(1,1,1)
+        glRasterPos2i(5, 215)
+        Text("Assign phonems to shapes:")
+
+        #on mesure la taille de la liste de phonemes
+        #this is the lenght of the phonem list
+        nbr_phoneme=len(liste_phoneme)
+
+        #on dessine les listes de choix
+        #we draw the choice list
+
+        let01 = String(" ", 4, 5, 185, 30, 16, liste_phoneme[0], 3)
+        glColor3d(0,0,0)
+        glRasterPos2i(40, 188)
+        Text("=")
+        let01selectkey = Menu(key_menu, 50, 50, 185, 70, 16, let01selectkey.val)
+
+
+       let02 = String(" ", 4, 150, 185, 30, 16, liste_phoneme[1], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(185, 188)
+       Text("=")
+       let02selectkey = Menu(key_menu, 51, 195, 185, 70, 16, let02selectkey.val)
+
+
+       let03 = String(" ", 4, 5, 165, 30, 16, liste_phoneme[2], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(40, 168)
+       Text("=")
+       let03selectkey = Menu(key_menu, 52, 50, 165, 70, 16, let03selectkey.val)
+
+
+       let04 = String(" ", 4, 150, 165, 30, 16, liste_phoneme[3], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(185, 168)
+       Text("=")
+       let04selectkey = Menu(key_menu, 53, 195, 165, 70, 16, let04selectkey.val)
+
+
+       let05 = String(" ", 4, 5, 145, 30, 16, liste_phoneme[4], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(40, 148)
+       Text("=")
+       let05selectkey = Menu(key_menu, 54, 50, 145, 70, 16, let05selectkey.val)
+
+
+       let06 = String(" ", 4, 150, 145, 30, 16, liste_phoneme[5], 2)
+       glColor3d(0,0,0)                                
+       glRasterPos2i(185, 148)
+       Text("=")
+       let06selectkey = Menu(key_menu, 55, 195, 145, 70, 16, let06selectkey.val)
+
+
+       let07 = String(" ", 4, 5, 125, 30, 16, liste_phoneme[6], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(40, 128)
+       Text("=")
+       let07selectkey = Menu(key_menu, 56, 50, 125, 70, 16, let07selectkey.val)
+
+       #
+       let08 = String(" ", 4, 150, 125, 30, 16, liste_phoneme[7], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(185, 128)
+       Text("=")
+       let08selectkey = Menu(key_menu, 57, 195, 125, 70, 16,let08selectkey.val)
+
+
+       let09 = String(" ", 4, 5, 105, 30, 16, liste_phoneme[8], 2)
+       glColor3d(0,0,0)
+       glRasterPos2i(40, 108)
+       Text("=")
+       let09selectkey = Menu(key_menu, 58, 50, 105, 70, 16,let09selectkey.val)
+
+
+       let10 = String(" ", 4, 150, 105, 30, 16, liste_phoneme[9], 2)
+       glColor3d(0,0,0)                                
+       glRasterPos2i(185, 108)
+       Text("=")
+       let10selectkey = Menu(key_menu, 59, 195, 105, 70, 16, let10selectkey.val)
+       
+       #soft_type = 0:Papagayo
+       #soft_type = 1:JLipSync
+       if (soft_type==1):
+               let11 = String(" ", 4, 5, 85, 30, 16, liste_phoneme[10], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(40, 88)
+               Text("=")
+               let11selectkey = Menu(key_menu, 60, 50, 85, 70, 16, let11selectkey.val)
+
+               let12 = String(" ", 4, 150, 85, 30, 16, liste_phoneme[11], 2)
+               glColor3d(0,0,0)                                
+               Text("=")
+               let12selectkey = Menu(key_menu, 61, 195, 85, 70, 16, let12selectkey.val)
+       
+               let13 = String(" ", 4, 5, 65, 30, 16, liste_phoneme[12], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(40, 68)
+               Text("=")
+               let13selectkey = Menu(key_menu, 62, 50, 65, 70, 16, let13selectkey.val)
+       
+               let14 = String(" ", 4, 150, 65, 30, 16, liste_phoneme[13], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(185, 68)
+               Text("=")
+               let14selectkey = Menu(key_menu, 63, 195, 65, 70, 16, let14selectkey.val)
+       
+               let15 = String(" ", 4, 5, 45, 30, 16, liste_phoneme[14], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(40, 48)
+               Text("=")
+               let15selectkey = Menu(key_menu, 64, 50, 45, 70, 16, let15selectkey.val)
+       
+               let16 = String(" ", 4, 150, 45, 30, 16, liste_phoneme[15], 2)
+               glColor3d(0,0,0)                                
+               glRasterPos2i(185, 48)
+               Text("=")
+               let16selectkey = Menu(key_menu, 65, 195, 45, 70, 16, let16selectkey.val)
+       
+               let17 = String(" ", 4, 295, 185, 30, 16, liste_phoneme[16], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(330, 188)
+               Text("=")
+               let17selectkey = Menu(key_menu, 66, 340, 185, 70, 16, let17selectkey.val)
+       
+               let18 = String(" ", 4, 440, 185, 70, 16, liste_phoneme[17], 8)
+               glColor3d(0,0,0)
+               glRasterPos2i(515, 188)
+               Text("=")
+               let18selectkey = Menu(key_menu, 67, 525, 185, 70, 16, let18selectkey.val)
+       
+               let19 = String(" ", 4, 295, 165, 30, 16, liste_phoneme[18], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(330, 168)
+               Text("=")
+               let19selectkey = Menu(key_menu, 68, 340, 165, 70, 16, let19selectkey.val)
+       
+               let20 = String(" ", 4, 440, 165, 70, 16, liste_phoneme[19], 8)
+               glColor3d(0,0,0)
+               glRasterPos2i(515, 168)
+               Text("=")
+               let20selectkey = Menu(key_menu, 69, 525, 165, 70, 16, let20selectkey.val)
+       
+               let21 = String(" ", 4, 295, 145, 30, 16, liste_phoneme[20], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(330, 148)
+               Text("=")
+               let21selectkey = Menu(key_menu, 70, 340, 145, 70, 16, let21selectkey.val)
+       
+               let22 = String(" ", 4, 440, 145, 70, 16, liste_phoneme[21], 8)
+               glColor3d(0,0,0)                                
+               glRasterPos2i(515, 148)
+               Text("=")
+               let22selectkey = Menu(key_menu, 71, 525, 145, 70, 16, let22selectkey.val)
+       
+               let23 = String(" ", 4, 295, 125, 30, 16, liste_phoneme[22], 2)
+               glColor3d(0,0,0)
+               glRasterPos2i(330, 128)
+               Text("=")
+               let23selectkey = Menu(key_menu, 72, 340, 125, 70, 16,let23selectkey.val)
+       
+               let24 = String(" ", 4, 440, 125, 70, 16, liste_phoneme[23], 8)
+               glColor3d(0,0,0)
+               glRasterPos2i(515, 128)
+               Text("=")
+               let24selectkey = Menu(key_menu, 73, 525, 125, 70, 16, let24selectkey.val)
+       
+       
+       Button("Import Text", 3, 155, 5, 145, 22)
+       Button("Choose Voice Export", 4, 120, 250, 250, 22)
+
+       
+    if (etape==2):
+        glColor3d(1,1,1)
+        glRasterPos2i(125, 200)
+        Text("Operation Completed")
+        
+    if (etape==0):
+        glColor3d(1,1,1)
+        glRasterPos2i(125, 200)
+        Text("Please select a Mesh'Object and Create all the IPO Curves for your Shapes")
+        
+    if (etape==3):
+       Button("Papagayo", 5, 155, 250, 250, 22)
+       Button("JlipSync", 6, 155, 225, 250, 22)
+
+    glColor3d(1,1,1)
+    glRasterPos2i(6, 40)
+    Text("_______________________________________________________")
+    glColor3d(0,0,0)
+    glRasterPos2i(6, 38)
+    Text("_______________________________________________________")
+       
+    Button("Exit", 1, 305, 5, 80, 22)
+    
+    
+       
+       
+#cette fonction sur evenement quite en cas d'ESC
+#this functions catch the ESC event and quit
+def event(evt,val):
+       if (evt == ESCKEY and not val): Exit()
+
+#cette fonction gere les evenements
+#the event functions
+def bevent(evt):
+       global etape,soft_type,liste_phoneme
+
+       if (evt == 1):
+               Exit()
+       
+               
+        elif (evt == 4):
+            #c'est le choix de l'export pamela
+            #we choose the papagayo export
+            Blender.Window.FileSelector(selectionner_export_papagayo,"Choose Export")
+            
+            
+        elif (evt == 3):
+            #c'est l'import Papagayo
+            #we import 
+            lecture_chaine(mon_fichier_export_pamela,dico_phoneme_export_pamela)
+            construction_dico_correspondance()
+            construction_lipsynchro()
+            #on change d'etape
+            #we change the stage
+            etape=2
+         
+        elif (evt == 5):
+            #we choose papagayo
+            soft_type=0
+            liste_phoneme=liste_phoneme_papagayo
+            etape=1
+         
+        elif (evt == 6):
+            #we choose jlipsync
+            soft_type=1
+            liste_phoneme=liste_phoneme_jlipsinch
+            etape=1
+               
+        Blender.Redraw()
+
+#cette fonction recupere le nom et le chemin du fichier dictionnaire
+#we catch the name and the path of the dictionnary
+def selectionner_export_papagayo(filename):
+    global mon_fichier_export_pamela
+    #debut
+    mon_fichier_export_pamela=filename
+    
+#fonction de lecture de la liste frame phoneme
+#we read the frame and phonems
+def lecture_chaine(fichier,liste):
+    mon_fichier=open(fichier)
+        
+    #je lis la premiere ligne qui contiens la version de moho
+    #first, we read the moho version
+    mon_fichier.readline()
+
+    #je lis jusqu'a la fin
+    #then we read until the end of the file
+    while 1:
+        ma_ligne=mon_fichier.readline()
+        if ma_ligne=='':
+            break
+        decoup=ma_ligne.split()
+        liste[decoup[0]]=decoup[1]
+    print liste
+
+
+
+
+#fonction qui construit la liste dictionnaire simple
+#we make the dictionnary
+def construction_dictionnaire_phoneme():
+    index_liste=0
+     #je transforme mon dictionnaire en list de tulpes
+     #we transform the list in tulpes
+    ma_liste=dico_phoneme.items()
+    #je parcours ma liste a la recherche d'elements non existant
+    #we read the list to find non existing elements
+    print dico_phoneme
+    for index in range(len(ma_liste)):
+        if ma_liste[index][1] not in liste_phoneme:
+            liste_phoneme[index_liste:index_liste]=[ma_liste[index][1]]
+            index_liste=index_liste+1
+    print liste_phoneme
+     
+
+
+#cette fonction recupere les courbes cible 
+#this functon catch the IPO curve
+def recuperation_courbe():
+       global key_menu,dico_key
+
+       #on recupere le nom des shapes
+       #we catch the shapes
+       key=Blender.Object.GetSelected()[0].getData().getKey().getBlocks()
+       for n in range(len(key)):
+               #on vire la première cle (en effet basic n'est pas une cle en tant que telle)
+               #we threw away the basic shapes
+               if (n>0):
+                       key_menu=key_menu+key[n].name + " %x" + str(n-1) + "|"
+                       dico_key[str(n-1)]=Blender.Object.GetSelected()[0].getData().getKey().getIpo().getCurves()[n-1]
+       
+       
+       print "dico_key"
+       print dico_key
+       print 'end dico_key'
+
+#cette fonction construit un dictionnaire de correspondance entre les phonemes prononces et les cles a utiliser
+#we make the dictionnary for the mapping between shapes and phonems
+def construction_dico_correspondance():
+       global dico_correspondance
+       #je parcours les phonemes
+       #we read the phonems
+       dico_correspondance[liste_phoneme[0]]=dico_key[str(let01selectkey.val)]
+       dico_correspondance[liste_phoneme[1]]=dico_key[str(let02selectkey.val)]
+       dico_correspondance[liste_phoneme[2]]=dico_key[str(let03selectkey.val)]
+       dico_correspondance[liste_phoneme[3]]=dico_key[str(let04selectkey.val)]
+       dico_correspondance[liste_phoneme[4]]=dico_key[str(let05selectkey.val)]
+       dico_correspondance[liste_phoneme[5]]=dico_key[str(let06selectkey.val)]
+       dico_correspondance[liste_phoneme[6]]=dico_key[str(let07selectkey.val)]
+       dico_correspondance[liste_phoneme[7]]=dico_key[str(let08selectkey.val)]
+       dico_correspondance[liste_phoneme[8]]=dico_key[str(let09selectkey.val)]
+       dico_correspondance[liste_phoneme[9]]=dico_key[str(let10selectkey.val)]
+       
+       if (soft_type==1):
+               dico_correspondance[liste_phoneme[10]]=dico_key[str(let11selectkey.val)]
+               dico_correspondance[liste_phoneme[11]]=dico_key[str(let12selectkey.val)]
+               dico_correspondance[liste_phoneme[12]]=dico_key[str(let13selectkey.val)]
+               dico_correspondance[liste_phoneme[13]]=dico_key[str(let14selectkey.val)]
+               dico_correspondance[liste_phoneme[14]]=dico_key[str(let15selectkey.val)]
+               dico_correspondance[liste_phoneme[15]]=dico_key[str(let16selectkey.val)]
+               dico_correspondance[liste_phoneme[16]]=dico_key[str(let17selectkey.val)]
+               dico_correspondance[liste_phoneme[17]]=dico_key[str(let18selectkey.val)]
+               dico_correspondance[liste_phoneme[18]]=dico_key[str(let19selectkey.val)]
+               dico_correspondance[liste_phoneme[19]]=dico_key[str(let20selectkey.val)]
+               dico_correspondance[liste_phoneme[20]]=dico_key[str(let21selectkey.val)]
+               dico_correspondance[liste_phoneme[21]]=dico_key[str(let22selectkey.val)]
+               dico_correspondance[liste_phoneme[22]]=dico_key[str(let23selectkey.val)]
+               dico_correspondance[liste_phoneme[23]]=dico_key[str(let24selectkey.val)]
+               
+       print dico_correspondance
+
+
+#cette fonction ajoute un points a la cle donnee a la frame donnee
+#we add a point to the IPO curve Target
+def ajoute_point(cle,frame,valeur):
+    cle.setInterpolation('Linear')
+    cle.addBezier((frame,valeur))
+    cle.Recalc()
+
+#cette fonction parcours le dictionnaire des frame à ajouter et construit les points
+#we add all the point to the IPO Curve
+def construction_lipsynchro():
+    print "je construit"
+    doublet_old=""
+    #construction de la liste des frame
+    cpt=0
+    liste_frame=[]
+    for frame in dico_phoneme_export_pamela:
+        liste_frame.append(int(frame))
+        cpt=cpt+1
+    liste_frame.sort()
+    print "listeframe"
+    print liste_frame
+    print "fini"
+    
+    for doublet in liste_frame:
+        ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],doublet,1)
+        if (doublet_old==""):
+            ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],(doublet-2),0)
+        if (doublet_old!=''):
+            if (dico_correspondance[dico_phoneme_export_pamela[str(doublet)]]!=dico_correspondance[dico_phoneme_export_pamela[doublet_old]]):
+                print "doublet:"+str(doublet)
+                print "doublet old:"+doublet_old                
+                ajoute_point(dico_correspondance[dico_phoneme_export_pamela[doublet_old]],(int(doublet_old)+2),0)
+                ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],(doublet-2),0)
+        doublet_old=str(doublet)
+        
+
+#end of my functions we begin the execution       
+#je commence l execution-----------------------------------------------------------------------------------------------
+#voici mes variables
+
+#declaration et instanciation
+#decleration and instanciation
+#ce sont les repertoires
+repertoire_dictionaire=Create('C:/')
+repertoire_phoneme=Create('c:/')
+
+#ce sont ls fichiers
+fichier_dico=Create("sample.mot")
+fichier_text=Create("")
+
+#voici mon objet de travail
+objet_travail=Create(0)
+
+#my soft type
+soft_type=1
+
+#voici la liste des phoneme effectivement utilise
+#the phonems'list
+liste_phoneme_papagayo=['AI','E','O','U','FV','L','WQ','MBP','etc','rest']
+liste_phoneme_jlipsinch=['A','B','C','Closed','D','E','F','G','I','K','L','M','N','O','P','Q','R','S','SH','T','TH','U','V','W']
+
+liste_phoneme=liste_phoneme_jlipsinch
+#voici mon dictionnaire des frames o
+dico_phoneme_export_pamela = Create(0)
+dico_phoneme_export_pamela={}
+
+
+
+#voici mes cle
+key_menu=""
+dico_key={}
+
+#voici mes ipo
+dico_bloc={}
+iponame = Create(0)
+
+#voici mon dictionnaire de correspondance
+dico_correspondance={}
+
+try:
+       #on verifie est bien une mesh et qu'il a des courbes
+       if ((Blender.Object.GetSelected()[0].getType()=='Mesh')):
+               #on verifie que l'objet a bien toute ses Courbes
+               if (len(Blender.Object.GetSelected()[0].getData().getKey().getBlocks())-1==Blender.Object.GetSelected()[0].getData().getKey().getIpo().getNcurves()):
+                       etape=3
+                       #on lance la creation du dictionnaire
+                       recuperation_courbe()
+               else:
+                       print "not the good number of IPO Curve"
+                       etape = 0
+       else:
+               print "error: bad object Type:"
+               print Blender.Object.GetSelected()[0].getType()
+               etape = 0
+except:
+       print 'error: exception'
+       etape = 0
+
+
+#voici le fichier dictionnaire
+mon_fichier_dico=""
+
+#voici le fichier export pamela
+mon_fichier_export_pamela=""
+
+
+let01selectkey = Create(0)
+let02selectkey = Create(0)
+let03selectkey = Create(0)
+let04selectkey = Create(0)
+let05selectkey = Create(0)
+let06selectkey = Create(0)
+let07selectkey = Create(0)
+let08selectkey = Create(0)
+let09selectkey = Create(0)
+let10selectkey = Create(0)
+let11selectkey = Create(0)
+let12selectkey = Create(0)
+let13selectkey = Create(0)
+let14selectkey = Create(0)
+let15selectkey = Create(0)
+let16selectkey = Create(0)
+let17selectkey = Create(0)
+let18selectkey = Create(0)
+let19selectkey = Create(0)
+let20selectkey = Create(0)
+let21selectkey = Create(0)
+let22selectkey = Create(0)
+let23selectkey = Create(0)
+let24selectkey = Create(0)
+
+
+Register (trace,event,bevent)
diff --git a/release/scripts/flt_export.py b/release/scripts/flt_export.py
new file mode 100755 (executable)
index 0000000..b5c5797
--- /dev/null
@@ -0,0 +1,723 @@
+#!BPY
+
+# flt_export.py is an OpenFlight exporter for blender.
+# Copyright (C) 2005 Greg MacDonald
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+""" Registration info for Blender menus:
+Name: 'OpenFlight (.flt)...'
+Blender: 237
+Group: 'Export'
+Tip: 'Export to OpenFlight v16.0 (.flt)'
+"""
+
+__author__ = "Greg MacDonald"
+__version__ = "1.2 10/20/05"
+__url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/")
+__bpydoc__ = """\
+This script exports v16.0 OpenFlight files.  OpenFlight is a
+registered trademark of MultiGen-Paradigm, Inc.
+
+Run from "File->Export" menu. 
+
+Options are available from Blender's "Scripts Config Editor," accessible through
+the "Scripts->System" menu from the scripts window.
+
+Features:<br>
+* Heirarchy retained.<br>
+* Normals retained.<br>
+* First texture exported.<br>
+* Diffuse material color is exported as the face color, material color, or both
+depending on the option settings.<br>
+* Double sided faces are exported as two faces.<br>
+* Object transforms exported.
+
+Things To Be Aware Of:<br>
+* Object names are exported, not mesh or data names.
+* Material indices that don't have a material associated with them will confuse the
+exporter. If a warning appears about this, correct it by deleting the offending
+material indices in Blender.
+
+What's Not Handled:<br>
+* Animations.<br>
+* Vetex colors.<br>
+"""
+
+import Blender
+from flt_filewalker import FltOut
+
+class ExporterOptions:
+       def __init__(self):
+               self.defaults = { 'Diffuse Color To OpenFlight Material': False,
+                                                 'Diffuse Color To OpenFlight Face': True}
+               
+               d = Blender.Registry.GetKey('flt_export', True)
+               
+               if d == None or d.keys() != self.defaults.keys():
+                       d = self.defaults
+                       Blender.Registry.SetKey('flt_export', d, True)
+               
+               self.verbose = 1
+               self.tolerance = 0.001
+               self.use_mat_color = d['Diffuse Color To OpenFlight Material']
+               self.use_face_color = d['Diffuse Color To OpenFlight Face']
+               
+options = ExporterOptions()
+
+FLOAT_TOLERANCE = options.tolerance
+
+identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
+
+def is_identity(m):
+       for i in xrange(4):
+               for j in xrange(4):
+                       if abs(m[i][j] - identity_matrix[i][j]) > FLOAT_TOLERANCE:
+                               return False
+       return True
+
+class MaterialDesc:
+       def __init__(self):
+               self.name = 'Blender'
+
+               # Colors, List of 3 floats.
+               self.diffuse = [1.0, 1.0, 1.0]
+               self.specular = [1.0, 1.0, 1.0]
+
+               # Scalars
+               self.ambient = 0.1 # [0.0, 1.0]
+               self.emissive = 0.0 # [0.0, 1.0]
+               self.shininess = 32.0 # Range is [0.0, 128.0]
+               self.alpha = 1.0 # Range is [0.0, 1.0]
+
+class VertexDesc:
+       def __init__(self, co=None, no=None, uv=None):
+               if co: self.x, self.y, self.z = tuple(co)
+               else: self.x = self.y = self.z = 0.0
+               if no: self.nx, self.ny, self.nz = tuple(no)
+               else: self.nx = self.ny = self.nz = 0.0
+               if uv: self.u, self.v = tuple(uv)
+               else: self.u = self.v = 0.0
+
+class GlobalResourceRepository:
+       def new_face_name(self):
+               self.face_name += 1
+               return 'f%i' % (self.face_name-1)
+
+       def vertex_count(self):
+               return len(self.vertex_lst)
+
+       def request_vertex_desc(self, i):
+               return self.vertex_lst[i]
+
+       def request_vertex_index(self, desc):
+               match = None
+               for i, v in enumerate(self.vertex_lst):
+                       if\
+                       abs(v.x - desc.x) > FLOAT_TOLERANCE or\
+                       abs(v.y - desc.y) > FLOAT_TOLERANCE or\
+                       abs(v.z - desc.z) > FLOAT_TOLERANCE or\
+                       abs(v.nx - desc.nx) > FLOAT_TOLERANCE or\
+                       abs(v.ny - desc.ny) > FLOAT_TOLERANCE or\
+                       abs(v.nz - desc.nz) > FLOAT_TOLERANCE or\
+                       abs(v.u - desc.u) > FLOAT_TOLERANCE or\
+                       abs(v.v - desc.v) > FLOAT_TOLERANCE:
+                               pass
+                       else:
+                               match = i
+                               break
+
+               if match != None:
+                       return match
+               else:
+                       self.vertex_lst.append(desc)
+                       return len(self.vertex_lst) - 1
+
+       def request_texture_index(self, filename):
+               match = None
+               for i in xrange(len(self.texture_lst)):
+                       if self.texture_lst[i] != filename:
+                               continue
+                       match = i
+                       break
+               if match != None:
+                       return match
+               else:
+                       self.texture_lst.append(filename)
+                       return len(self.texture_lst) - 1
+
+       def request_texture_filename(self, index):
+               return self.texture_lst[index]
+
+       def texture_count(self):
+               return len(self.texture_lst)
+
+       def request_material_index(self, desc):
+               match = None
+               for i in xrange(len(self.material_lst)):
+                       if self.material_lst[i].diffuse != desc.diffuse:
+                               continue
+                       if self.material_lst[i].specular != desc.specular:
+                               continue
+                       if self.material_lst[i].ambient != desc.ambient:
+                               continue
+                       if self.material_lst[i].emissive != desc.emissive:
+                               continue
+                       if self.material_lst[i].shininess != desc.shininess:
+                               continue
+                       if self.material_lst[i].alpha != desc.alpha:
+                               continue
+                       match = i
+                       break
+
+               if match != None:
+                       return i
+               else:
+                       self.material_lst.append(desc)
+                       return len(self.material_lst) - 1
+
+       def request_material_desc(self, index):
+               return self.material_lst[index]
+
+       def material_count(self):
+               return len(self.material_lst)
+
+       # Returns not actual index but one that includes intensity information.
+       # color_index = 127*intensity + 128*actual_index
+       def request_color_index(self, col):
+               r,g,b = tuple(col)
+               m = max(r, g, b)
+               if m > 0.0:
+                       intensity = m / 1.0
+                       r = int(round(r/m * 255.0))
+                       g = int(round(g/m * 255.0))
+                       b = int(round(b/m * 255.0))
+                       brightest = [r, g, b]
+               else:
+                       brightest = [255, 255, 255]
+                       intensity = 0.0
+
+               match = None
+               for i in xrange(len(self.color_lst)):
+                       if self.color_lst[i] != brightest:
+                               continue
+
+                       match = i
+                       break
+
+               if match != None:
+                       index = match
+               else:
+                       length = len(self.color_lst)
+                       if length <= 1024:
+                               self.color_lst.append(brightest)
+                               index = length
+                       else:
+                               if options.verbose >= 1:
+                                       print 'Warning: Exceeded max color limit.'
+                               index = 0
+
+               color_index = int(round(127.0*intensity)) + 128*index
+               return color_index
+
+       # Returns color from actual index.
+       def request_max_color(self, index):
+               return self.color_lst[index]
+
+       def color_count(self):
+               return len(self.color_lst)
+
+       def __init__(self):
+               self.vertex_lst = []
+               self.texture_lst = []
+               self.material_lst = []
+               self.color_lst = [[255, 255, 255]]
+               self.face_name = 0
+
+class Node:
+       # Gathers info from blender needed for export.
+       # The =[0] is a trick to emulate c-like static function variables
+       # that are persistant between calls.
+       def blender_export(self, level=[0]):
+               if self.object:
+                       if options.verbose >= 2:
+                               print '\t' * level[0], self.name, self.object.getType()
+
+               level[0] += 1
+               
+               for child in self.children:
+                       child.blender_export()
+                       
+               level[0] -= 1
+
+       # Exports this node's info to file.
+       def write(self):
+               pass
+
+       def write_matrix(self):
+               if self.matrix and not is_identity(self.matrix):
+                       self.header.fw.write_short(49)   # Matrix opcode
+                       self.header.fw.write_ushort(68)  # Length of record
+                       for i in xrange(4):
+                               for j in xrange(4):
+                                       self.header.fw.write_float(self.matrix[i][j])
+
+       def write_push(self):
+               self.header.fw.write_short(10)
+               self.header.fw.write_ushort(4)
+
+       def write_pop(self):
+               self.header.fw.write_short(11)
+               self.header.fw.write_ushort(4)
+
+       def write_longid(self, name):
+               length = len(name)
+               if length >= 8:
+                       self.header.fw.write_short(33)              # Long ID opcode
+                       self.header.fw.write_ushort(length+5)       # Length of record
+                       self.header.fw.write_string(name, length+1) # name + zero terminator
+
+       # Initialization sets up basic tree structure.
+       def __init__(self, parent, header, object, object_lst):
+               self.header = header
+               self.object = object
+               if object:
+                       self.name = self.object.getName()
+                       self.matrix = self.object.getMatrix('localspace')
+               else:
+                       self.name = 'no name'
+                       self.matrix = None
+
+               self.children = []
+               self.parent = parent
+               if parent:
+                       parent.children.append(self)
+
+               left_over = object_lst[:]
+               self.child_objects = []
+
+               # Add children to child list and remove from left_over list.
+               
+               # Pop is faster then remove
+               i = len(object_lst)
+               while i:
+                       i-=1
+                       if object_lst[i].parent == object:
+                               self.child_objects.append(left_over.pop(i))
+                       
+               # Spawn children.
+               self.has_object_child = False # For Database class.
+               for child in self.child_objects:                        
+                       if child.getType() == 'Mesh':
+                               BlenderMesh(self, header, child, left_over)
+                               self.has_object_child = True
+                       else: # Treat all non meshes as emptys
+                               BlenderEmpty(self, header, child, left_over)
+
+class FaceDesc:
+       def __init__(self):
+               self.vertex_index_lst = []
+               self.texture_index = -1
+               self.material_index = -1
+               self.color_index = 127
+       
+class BlenderMesh(Node):
+       def blender_export(self):
+               Node.blender_export(self)
+
+               mesh = self.object.getData()
+               mesh_hasuv = mesh.hasFaceUV()
+               # Gather materials and textures.
+               tex_index_lst = []
+               mat_index_lst = []
+               color_index_lst = []
+               materials = mesh.getMaterials()
+               
+               if not materials:
+                       materials = [Blender.Material.New()]
+               
+               for mat in materials:
+                       # Gather Color.
+                       if options.use_face_color:
+                               color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol()))
+                       else:
+                               color_index_lst.append(127) # white
+                       # Gather Texture.
+                       mtex_lst = mat.getTextures()
+
+                       index = -1
+                       mtex = mtex_lst[0] # Not doing multi-texturing at the moment.
+                       if mtex != None:
+                               tex = mtex_lst[0].tex
+                               if tex != None:
+                                       image = tex.getImage()
+                                       if image != None:
+                                               filename = image.getFilename()
+                                               index = self.header.GRR.request_texture_index(filename)
+
+                       tex_index_lst.append(index)
+
+                       # Gather Material
+                       mat_desc = MaterialDesc()
+                       mat_desc.name = mat.getName()
+                       mat_desc.alpha = mat.getAlpha()
+                       mat_desc.shininess = mat.getSpec() * 64.0   # 2.0 => 128.0
+                       if options.use_mat_color:
+                               mat_desc.diffuse = mat.getRGBCol()
+                       else:
+                               mat_desc.diffuse = [1.0, 1.0, 1.0]
+
+                       mat_desc.specular = mat.getSpecCol()
+                       amb = mat.getAmb()
+                       mat_desc.ambient = [amb, amb, amb]
+                       emit = mat.getEmit()
+                       mat_desc.emissive = [emit, emit, emit]
+
+                       mat_index_lst.append(self.header.GRR.request_material_index(mat_desc))
+
+               # Faces described as lists of indices into the GRR's vertex_lst.
+               for face in mesh.faces:
+                       
+                       face_v = face.v # Faster access
+                       
+                       # Create vertex description list for each face.
+                       if mesh_hasuv:
+                               vertex_lst = [VertexDesc(v.co, v.no, face.uv[i]) for i, v in enumerate(face_v)]
+                       else:
+                               vertex_lst = [VertexDesc(v.co, v.no) for i, v in enumerate(face_v)]
+                       
+                       index_lst = []
+                       for vert_desc in vertex_lst:
+                               index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
+                       
+                       face_desc = FaceDesc()
+                       face_desc.vertex_index_lst = index_lst
+                       
+                       if face.materialIndex < len(materials):
+                               face_desc.color_index    = color_index_lst[face.materialIndex]
+                               face_desc.texture_index  = tex_index_lst[face.materialIndex]
+                               face_desc.material_index = mat_index_lst[face.materialIndex]
+                       else:
+                               if options.verbose >=1:
+                                       print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.'
+
+                       self.face_lst.append(face_desc)
+                       
+                       # Export double sided face as 2 faces with opposite orientations.
+                       if mesh_hasuv and face.mode & Blender.NMesh.FaceModes['TWOSIDE']:
+                               # Create vertex description list for each face. they have a face mode, so we know they have a UV too.
+                               vertex_lst = [VertexDesc(v.co, -v.no, face.uv[i]) for i, v in enumerate(face_v)]
+                               vertex_lst.reverse() # Reversing flips the face.
+                               
+                               index_lst = []
+                               for vert_desc in vertex_lst:
+                                       index_lst.append(self.header.GRR.request_vertex_index(vert_desc))
+                               
+                               face_desc = FaceDesc()
+                               face_desc.vertex_index_lst = index_lst
+                               if face.materialIndex < len(materials):
+                                       face_desc.color_index = color_index_lst[face.materialIndex]
+                                       face_desc.texture_index = tex_index_lst[face.materialIndex]
+                                       face_desc.material_index = mat_index_lst[face.materialIndex]
+                               else:
+                                       if options.verbose >=1:
+                                               print 'Error: No material for material index. Delete abandoned material indices in Blender.'
+       
+                               self.face_lst.append(face_desc)
+
+       def write_faces(self):
+               for face_desc in self.face_lst:
+                       face_name = self.header.GRR.new_face_name()
+                       
+                       self.header.fw.write_short(5)                                   # Face opcode
+                       self.header.fw.write_ushort(80)                                 # Length of record
+                       self.header.fw.write_string(face_name, 8)                       # ASCII ID
+                       self.header.fw.write_int(-1)                                    # IR color code
+                       self.header.fw.write_short(0)                                   # Relative priority
+                       self.header.fw.write_char(0)                                    # Draw type
+                       self.header.fw.write_char(0)                                    # Draw textured white.
+                       self.header.fw.write_ushort(0)                                  # Color name index
+                       self.header.fw.write_ushort(0)                                  # Alt color name index
+                       self.header.fw.write_char(0)                                    # Reserved
+                       self.header.fw.write_char(1)                                    # Template
+                       self.header.fw.write_short(-1)                                  # Detail tex pat index
+                       self.header.fw.write_short(face_desc.texture_index)             # Tex pattern index
+                       self.header.fw.write_short(face_desc.material_index)            # material index
+                       self.header.fw.write_short(0)                                   # SMC code
+                       self.header.fw.write_short(0)                                   # Feature code
+                       self.header.fw.write_int(0)                                     # IR material code
+                       self.header.fw.write_ushort(0)                                  # transparency 0 = opaque
+                       self.header.fw.write_uchar(0)                                   # LOD generation control
+                       self.header.fw.write_uchar(0)                                   # line style index
+                       self.header.fw.write_int(0x00000000)                            # Flags
+                       self.header.fw.write_uchar(2)                                   # Light mode
+                       self.header.fw.pad(7)                                           # Reserved
+                       self.header.fw.write_uint(-1)                                   # Packed color
+                       self.header.fw.write_uint(-1)                                   # Packed alt color
+                       self.header.fw.write_short(-1)                                  # Tex map index
+                       self.header.fw.write_short(0)                                   # Reserved
+                       self.header.fw.write_uint(face_desc.color_index)                # Color index
+                       self.header.fw.write_uint(127)                                  # Alt color index
+                       self.header.fw.write_short(0)                                   # Reserved
+                       self.header.fw.write_short(-1)                                  # Shader index
+
+                       self.write_longid(face_name)
+
+                       self.write_push()
+
+                       # Vertex list record
+                       self.header.fw.write_short(72)                        # Vertex list opcode
+                       num_verts = len(face_desc.vertex_index_lst)
+                       self.header.fw.write_ushort(4*num_verts+4)            # Length of record
+
+                       for vert_index in face_desc.vertex_index_lst:
+                               # Offset into vertex palette
+                               self.header.fw.write_int(vert_index*64+8)
+
+                       self.write_pop()
+
+       def write(self):
+               if self.open_flight_type == 'Object':
+                       self.header.fw.write_short(4)               # Object opcode
+                       self.header.fw.write_ushort(28)             # Length of record
+                       self.header.fw.write_string(self.name, 8)   # ASCII ID
+                       self.header.fw.pad(16)
+       
+                       self.write_longid(self.name)
+                       
+                       self.write_matrix()
+                       
+                       if self.face_lst != []:
+                               self.write_push()
+               
+                               self.write_faces()
+               
+                               self.write_pop()
+               else:
+                       self.header.fw.write_short(2)               # Group opcode
+                       self.header.fw.write_ushort(44)             # Length of record
+                       self.header.fw.write_string(self.name, 8)   # ASCII ID
+                       self.header.fw.pad(32)
+       
+                       self.write_longid(self.name)
+                       
+                       # Because a group can contain faces as well as children.
+                       self.write_push() 
+                       
+                       self.write_faces()
+                       
+                       for child in self.children:
+                               child.write()
+                       
+                       self.write_pop()
+
+       def __init__(self, parent, header, object, object_lst):
+               Node.__init__(self, parent, header, object, object_lst)
+               self.face_lst = []
+               
+               if self.children:
+                       self.open_flight_type= 'Group'
+               else: # Empty list.
+                       self.open_flight_type = 'Object'
+                       
+
+class BlenderEmpty(Node):
+       def write(self):
+               self.header.fw.write_short(2)               # Group opcode
+               self.header.fw.write_ushort(44)             # Length of record
+               self.header.fw.write_string(self.name, 8)   # ASCII ID
+               self.header.fw.pad(32)
+
+               self.write_longid(self.name)
+               
+               self.write_matrix()
+               
+               if self.children: # != []
+                       self.write_push()
+       
+                       for child in self.children:
+                               child.write()
+                               
+                       self.write_pop()
+
+class Database(Node):
+       def write_header(self):
+               if options.verbose >= 2:
+                       print 'Writing header.'
+               self.fw.write_short(1)          # Header opcode
+               self.fw.write_ushort(324)       # Length of record
+               self.fw.write_string('db', 8)   # ASCII ID
+               self.fw.write_int(1600)         # Revision Number
+               self.fw.pad(44)
+               self.fw.write_short(1)          # Unit multiplier.
+               self.fw.write_char(0)           # Units, 0 = meters
+               self.fw.write_char(0)           # texwhite on new faces 0 = false
+               self.fw.write_uint(0x80000000)  # misc flags set to saving vertex normals
+               self.fw.pad(24)
+               self.fw.write_int(0)            # projection type, 0 = flat earth
+               self.fw.pad(30)
+               self.fw.write_short(1)          # double precision
+               self.fw.pad(140)
+               self.fw.write_int(0)            # ellipsoid model, 0 = WSG 1984
+               self.fw.pad(52)
+
+       def write_vert_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing vertex palette.'
+               # Write record for vertex palette
+               self.fw.write_short(67)                             # Vertex palette opcode.
+               self.fw.write_short(8)                              # Length of record
+               self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything.
+
+               # Write records for individual vertices.
+               for i in xrange(self.GRR.vertex_count()):
+                       desc = self.GRR.request_vertex_desc(i)
+                       self.fw.write_short(70)                         # Vertex with color normal and uv opcode.
+                       self.fw.write_ushort(64)                        # Length of record
+                       self.fw.write_ushort(0)                         # Color name index
+                       self.fw.write_short(0x2000)                     # Flags set to no color
+                       self.fw.write_double(desc.x)
+                       self.fw.write_double(desc.y)
+                       self.fw.write_double(desc.z)
+                       self.fw.write_float(desc.nx)
+                       self.fw.write_float(desc.ny)
+                       self.fw.write_float(desc.nz)
+                       self.fw.write_float(desc.u)
+                       self.fw.write_float(desc.v)
+                       self.fw.pad(12)
+
+       def write_tex_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing texture palette.'
+               # Write record for texture palette
+               for i in xrange(self.GRR.texture_count()):
+                       self.fw.write_short(64)                                         # Texture palette opcode.
+                       self.fw.write_short(216)                                        # Length of record
+                       self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename
+                       self.fw.write_int(i)                                            # Texture index
+                       self.fw.write_int(0)                                            # X
+                       self.fw.write_int(0)                                            # Y
+
+       def write_mat_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing material palette.'
+               for i in xrange(self.GRR.material_count()):
+                       desc = self.GRR.request_material_desc(i)
+                       self.fw.write_short(113)                # Material palette opcode.
+                       self.fw.write_short(84)                 # Length of record
+                       self.fw.write_int(i)                    # Material index
+                       self.fw.write_string(desc.name, 12)     # Material name
+                       self.fw.write_uint(0x80000000)          # Flags
+                       self.fw.write_float(desc.ambient[0])    # Ambient color.
+                       self.fw.write_float(desc.ambient[1])    # Ambient color.
+                       self.fw.write_float(desc.ambient[2])    # Ambient color.
+                       self.fw.write_float(desc.diffuse[0])    # Diffuse color.
+                       self.fw.write_float(desc.diffuse[1])    # Diffuse color.
+                       self.fw.write_float(desc.diffuse[2])    # Diffuse color.
+                       self.fw.write_float(desc.specular[0])   # Specular color.
+                       self.fw.write_float(desc.specular[1])   # Specular color.
+                       self.fw.write_float(desc.specular[2])   # Specular color.
+                       self.fw.write_float(desc.emissive[0])   # Emissive color.
+                       self.fw.write_float(desc.emissive[1])   # Emissive color.
+                       self.fw.write_float(desc.emissive[2])   # Emissive color.
+                       self.fw.write_float(desc.shininess)
+                       self.fw.write_float(desc.alpha)
+                       self.fw.write_int(0)                    # Reserved
+
+       def write_col_pal(self):
+               if options.verbose >= 2:
+                       print 'Writing color palette.'
+               self.fw.write_short(32)                     # Color palette opcode.
+               self.fw.write_short(4228)                   # Length of record
+               self.fw.pad(128)
+               count = self.GRR.color_count()
+               for i in xrange(count):
+                       col = self.GRR.request_max_color(i)
+                       self.fw.write_uchar(255)                  # alpha
+                       self.fw.write_uchar(col[2])               # b
+                       self.fw.write_uchar(col[1])               # g
+                       self.fw.write_uchar(col[0])               # r
+               self.fw.pad(max(4096-count*4, 0))
+
+       def write(self):
+               self.write_header()
+               self.write_vert_pal()
+               self.write_tex_pal()
+               self.write_mat_pal()
+               self.write_col_pal()
+
+               # Wrap everything in a group if it has an object child.
+               if self.has_object_child:
+                       self.header.fw.write_short(2)          # Group opcode
+                       self.header.fw.write_ushort(44)        # Length of record
+                       self.header.fw.write_string('g1', 8)   # ASCII ID
+                       self.header.fw.pad(32)
+               
+               self.write_push()
+
+               for child in self.children:
+                       child.write()
+
+               self.write_pop()
+
+       def __init__(self, scene, fw):
+               self.fw = fw
+               self.scene = scene
+               self.all_objects = scene.getChildren()
+               self.GRR = GlobalResourceRepository()
+
+               Node.__init__(self, None, self, None, self.all_objects)
+
+def fs_callback(filename):
+       Blender.Window.WaitCursor(True)
+       
+       if Blender.sys.exists(filename):
+               r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No')
+               if r != 1:
+                       if options.verbose >= 1:
+                               print 'Export cancelled.'
+                       return
+       
+       time1 = Blender.sys.time() # Start timing
+       
+       fw = FltOut(filename)
+
+       db = Database(Blender.Scene.GetCurrent(), fw)
+       
+       if options.verbose >= 1:
+               print 'Pass 1: Exporting from Blender.\n'
+       
+       db.blender_export()
+       
+       if options.verbose >= 1:
+               print 'Pass 2: Writing %s\n' % filename
+               
+       db.write()
+
+       fw.close_file()
+       if options.verbose >= 1:
+               print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1)
+               
+       Blender.Window.WaitCursor(False)
+
+if options.verbose >= 1:
+       print '\nOpenFlight Exporter'
+       print 'Version:', __version__
+       print 'Author: Greg MacDonald'
+       print __url__[2]
+       print
+       
+fname = Blender.sys.makename(ext=".flt")
+Blender.Window.FileSelector(fs_callback, "Export OpenFlight v16.0", fname)
diff --git a/release/scripts/flt_filewalker.py b/release/scripts/flt_filewalker.py
new file mode 100644 (file)
index 0000000..f1f6ecd
--- /dev/null
@@ -0,0 +1,278 @@
+#!BPY
+
+# flt_filewalker.py is an utility module for OpenFlight IO scripts for blender.
+# Copyright (C) 2005 Greg MacDonald
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+import Blender
+from struct import *
+import re
+
+class FltIn:
+    def begin_record(self):
+        if self.repeat == True:
+            self.repeat = False
+        else:
+            self.position += self.length
+        try:
+            self.file.seek(self.position)
+            input = self.file.read(4)
+        except:
+            print 'Parse Error!'
+            return False
+            
+        if not input:
+            self.close_file()
+            return False
+            
+        self.opcode = unpack('>h', input[:2])[0]
+        self.length = unpack('>H', input[-2:])[0]
+        
+        self.next_position = self.position + self.length
+        
+        return True
+
+    def repeat_record(self):
+        self.repeat = True
+
+    def get_opcode(self):
+        return self.opcode
+
+    def get_level(self):
+        return self.level
+
+    def up_level(self):
+        self.level += 1
+
+    def down_level(self):
+        self.level -= 1
+
+    def read_string(self, length):
+        s = ''
+        if self.file.tell() + length <= self.next_position:
+            start = self.file.tell()
+            for i in range(length):
+                char = self.file.read(1)
+                if char == '\x00':
+                    break
+                s = s + char
+            
+            self.file.seek(start+length)
+#        else:
+#            print 'Warning: string truncated'
+
+        return s
+    
+    def read_int(self):
+        if self.file.tell() + 4 <= self.next_position:
+            return unpack('>i', self.file.read(4))[0]
+        else:
+            #print 'Warning: int truncated'
+            return 0
+
+    def read_uint(self):
+        if self.file.tell() + 4 <= self.next_position:
+            return unpack('>I', self.file.read(4))[0]
+        else:
+            #print 'Warning: uint truncated'
+            return 0
+
+    def read_double(self):
+        if self.file.tell() + 8 <= self.next_position:
+            return unpack('>d', self.file.read(8))[0]
+        else:
+            #print 'Warning: double truncated'
+            return 0.0
+
+    def read_float(self):
+        if self.file.tell() + 4 <= self.next_position:
+            return unpack('>f', self.file.read(4))[0]
+        else:
+            #print 'Warning: float truncated'
+            return 0.0
+
+    def read_ushort(self):
+        if self.file.tell() + 2 <= self.next_position:
+            return unpack('>H', self.file.read(2))[0]
+        else:
+            #print 'Warning: ushort truncated'
+            return 0
+
+    def read_short(self):
+        if self.file.tell() + 2 <= self.next_position:
+            return unpack('>h', self.file.read(2))[0]
+        else:
+            #print 'Warning: short trunated'
+            return 0
+
+    def read_uchar(self):
+        if self.file.tell() + 1 <= self.next_position:
+            return unpack('>B', self.file.read(1))[0]
+        else:
+            #print 'Warning: uchar truncated'
+            return 0
+
+    def read_char(self):
+        if self.file.tell() + 1 <= self.next_position:
+            return unpack('>b', self.file.read(1))[0]
+        else:
+            #print 'Warning: char truncated'
+            return 0
+
+    def read_ahead(self, i):
+        if self.file.tell() + i <= self.next_position:
+            self.file.seek(i, 1)
+#        else:
+#            print 'Warning: attempt to seek past record'      
+
+    def get_length(self):
+        return self.length
+
+    def close_file(self):
+        self.file.close()
+
+    def __init__(self, filename):
+        self.file = open(filename, 'rb')
+        self.position = 0
+        self.next_position = 100000
+        self.opcode = 0
+        self.length = 0
+        self.level = 0
+        self.repeat = False # Repeat the last record.
+        
+class FltOut:
+    # Length includes terminating null
+    def write_string(self, string, length):
+        if len(string) > length - 1:
+            str_len = length - 1
+        else:
+            str_len = len(string)
+
+        pad_len = length - str_len
+
+        self.file.write(string[:str_len])
+
+        self.pad(pad_len)
+
+    def write_int(self, a):
+        self.file.write( pack('>i', a) )
+
+    def write_uint(self, a):
+        self.file.write( pack('>I', a) )
+
+    def write_double(self, a):
+        self.file.write( pack('>d', a) )
+
+    def write_float(self, a):
+        self.file.write( pack('>f', a) )
+
+    def write_ushort(self, a):
+        self.file.write( pack('>H', a) )
+
+    def write_short(self, a):
+        self.file.write( pack('>h', a) )
+
+    def write_uchar(self, a):
+        self.file.write( pack('>B', a) )
+
+    def write_char(self, a):
+        self.file.write( pack('>b', a) )
+
+    def pad(self, reps):
+        for i in range(reps):
+            self.file.write('\x00')
+
+    def close_file(self):
+        self.file.close()
+
+    def __init__(self, filename):
+        self.file = open(filename, 'wb')
+
+class FileFinder:
+    def add_file_to_search_path(self, filename):
+        dir = Blender.sys.dirname(filename)
+        if dir != None and dir != '':
+            self.search_dirs.append(dir)
+
+    def strip_path(self, full_path):
+        # One of my flt files had a windows path with unix seperation. Basename
+        # returned the whole path + filename, which isn't expected. So my
+        # attempt to fix it is to replace all / or \ with the platform specific
+        # dir seperator.
+        #
+        # note: \\\\ is actually just one \ indirected twice, once for python
+        #       then again for re.sub
+        if Blender.sys.sep == '\\':
+            full_path = re.sub('/', '\\\\', full_path)
+        elif Blender.sys.sep == '/':
+            full_path = re.sub('\\\\', '/', full_path)
+            
+        filename = Blender.sys.basename(full_path)
+        return filename
+
+    def find(self, full_path):
+        if full_path == '':
+            return None
+
+        # Seperate out the path.
+        dirname = Blender.sys.dirname(full_path)
+
+        # Try it first.
+        if Blender.sys.exists(full_path):
+            if not dirname in self.search_dirs:
+                self.search_dirs.append(dirname)
+            return full_path
+
+        # Maybe it's relative.
+        for path in self.search_dirs:
+            rel_full_path = Blender.sys.join(path, full_path)
+            if Blender.sys.exists(rel_full_path):
+                return rel_full_path
+
+        # Search previous directories that have worked.
+        filename = self.strip_path(full_path)
+        for path in self.search_dirs:
+            t = Blender.sys.join(path, filename)
+            if Blender.sys.exists(t):
+                return t
+
+        # Ask user where it is.
+        self.user_input = Blender.Draw.PupStrInput(filename + "? ", '', 100)
+        if self.user_input != None:
+            t = Blender.sys.join(self.user_input, filename)
+            if Blender.sys.exists(t):
+                user_dirname = Blender.sys.dirname(t)
+                if not user_dirname in self.search_dirs:
+                    self.search_dirs.append(user_dirname)
+                return t
+
+        # Couldn't find it.
+        return None
+
+    def __init__(self):
+        self.user_input = ''
+        self.current_file = ''
+        self.search_dirs = []
+        
+        dir = Blender.Get('texturesdir')
+        if dir != None and dir != '':
+            self.search_dirs.append(dir)
+        
+        dir = Blender.sys.dirname(Blender.Get('filename'))
+        if dir != None and dir != '':
+            print dir
+            self.search_dirs.append(dir)
+  
\ No newline at end of file
diff --git a/release/scripts/flt_import.py b/release/scripts/flt_import.py
new file mode 100755 (executable)
index 0000000..2553f34
--- /dev/null
@@ -0,0 +1,1803 @@
+#!BPY
+
+# flt_import.py is an OpenFlight importer for blender.
+# Copyright (C) 2005 Greg MacDonald
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+""" Registration info for Blender menus:
+Name: 'OpenFlight (.flt)...'
+Blender: 238
+Group: 'Import'
+Tip: 'Import OpenFlight (.flt)'
+"""
+
+__author__ = "Greg MacDonald"
+__version__ = "1.2 10/20/05"
+__url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/")
+__bpydoc__ = """\
+This script imports OpenFlight files into Blender. OpenFlight is a
+registered trademark of MultiGen-Paradigm, Inc.
+
+Run from "File->Import" menu.
+
+Options are available from Blender's "Scripts Config Editor," accessible through
+the "Scripts->System" menu from the scripts window.
+
+All options are toggle switches that let the user choose what is imported. Most
+are straight-forward, but one option could be a source of confusion. The 
+"Diffuse Color From Face" option when set pulls the diffuse color from the face
+colors. Otherwise the diffuse color comes from the material. What may be
+confusing is that this options only works if the "Diffuse Color" option is set.
+
+New Features:<br>
+* Importer is 14 times faster.<br>
+* External triangle module is no longer required, but make sure the importer
+has a 3d View screen open while its running or triangulation won't work.<br>
+* Should be able to import all versions of flight files.
+
+Features:<br>
+* Heirarchy retained.<br>
+* First texture imported.<br>
+* Colors imported from face or material.<br>
+* LOD seperated out into different layers.<br>
+* Asks for location of unfound textures or external references.<br>
+* Searches Blender's texture directory in the user preferences panel.<br>
+* Triangles with more than 4 verts are triangulated if the Triangle python
+module is installed.<br>
+* Matrix transforms imported.<br>
+* External references to whole files are imported.
+
+Things To Be Aware Of:<br>
+* Each new color and face attribute creates a new material and there are only a maximum of 16
+materials per object.<br>
+* For triangulated faces, normals must be recomputed outward manually by typing
+CTRL+N in edit mode.<br>
+* You can change options only after an initial import.<br>
+* External references are imported as geometry and will be exported that way.<br>
+* A work around for not using the Triangle python module is to simply to 
+triangulate in Creator before importing. This is only necessary if your
+model contains 5 or more vertices.<br>
+* You have to manually blend the material color with the texture color.
+
+What's Not Handled:<br>
+* Special texture repeating modes.<br>
+* Replications and instancing.<br>
+* Comment and attribute fields.<br>
+* Light points.<br>
+* Animations.<br>
+* External references to a node within a file.<br>
+* Multitexturing.<br>
+* Vetex colors.<br>
+"""
+
+import Blender
+import os
+
+from flt_filewalker import FltIn, FileFinder
+
+def col_to_gray(c):
+    return 0.3*c[0] + 0.59*c[1] + 0.11*c[2]
+
+class ImporterOptions:
+    def __init__(self):
+        self.defaults = { 'Texture': True,
+                          'Diffuse Color': True,
+                          'Specular Color': False,
+                          'Emissive Intensity': False,
+                          'Alpha': True,
+                          'Ambient Intensity': False,
+                          'Shininess': True,
+                          'Diffuse Color From Face': True}
+
+        d = Blender.Registry.GetKey('flt_import', True)
+
+        if d == None or d.keys() != self.defaults.keys():
+            d = self.defaults
+            Blender.Registry.SetKey('flt_import', d, True)
+
+        self.verbose = 1
+        self.get_texture = d['Texture']
+        self.get_diffuse = d['Diffuse Color']
+        self.get_specular = d['Specular Color']
+        self.get_emissive = d['Emissive Intensity']
+        self.get_alpha = d['Alpha']
+        self.get_ambient = d['Ambient Intensity']
+        self.get_shininess = d['Shininess']
+        self.color_from_face = d['Diffuse Color From Face']
+
+options = ImporterOptions()
+
+# Contributed by Campbell Barton:
+# http://en.wikibooks.org/wiki/Blending_Into_Python:_Cookbook#Expanded_Scanfill_function
+msg_once = False
+def scanFillPoints(pointList):
+    global msg_once
+    
+    screen_info = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D)
+    if screen_info == []:
+        if not msg_once:
+            msg = 'Error: A 3D View window must be open while the script is running for triangulation to occur.'
+            Blender.Draw.PupMenu(msg)
+            print msg
+            print
+            msg_once = True
+        return None
+        
+    Blender.Window.EditMode(0)
+    nme = Blender.NMesh.GetRaw('.scanfill')
+    if nme:
+        nme.verts = []
+        nme.edges = []
+        nme. faces = []
+    else:
+        nme = Blender.NMesh.New('.scanfill')
+        
+    for p in pointList:
+        v = Blender.NMesh.Vert( p[0], p[1], p[2] )
+        nme.verts.append(v)
+        v.sel = 1
+
+        if len(nme.verts) >= 2:
+            nme.addEdge(nme.verts[-2], nme.verts[-1])
+
+    nme.addEdge(nme.verts[0], nme.verts[-1])
+
+    nme.update()
+    
+    scn = Blender.Scene.GetCurrent()
+
+    actOb = scn.getActiveObject()
+    if actOb:
+        actSel = actOb.sel
+    else:
+        actSel = 0
+
+    try:
+        ob = Blender.Object.Get('.scanfill')
+    except AttributeError:
+        ob = Blender.Object.New('Mesh')
+        ob.setName('.scanfill')
+        ob.link(nme)
+    
+    scn.link(ob)
+    scn.layers = range(1,20)
+    ob.sel = 1
+    Blender.Window.EditMode(1)
+
+    winid = screen_info[0]['id']
+    Blender.Window.SetKeyQualifiers(Blender.Window.Qual.SHIFT)
+    Blender.Window.QAdd(winid, Blender.Draw.FKEY,1)
+    Blender.Window.QHandle(winid)
+    Blender.Window.SetKeyQualifiers(0)
+
+    Blender.Window.EditMode(0)
+    scn.unlink(ob)
+
+    # Select the old active object.
+    if actOb:
+        actOb.sel = actSel
+
+    # Return the scanfilled faces.
+    return ob.getData()
+
+class MaterialDesc:
+    # Was going to use int(f*1000.0) instead of round(f,3), but for some reason
+    # round produces better results, as in less dups.
+    def make_key(self):
+        key = []
+        if options.get_texture:
+            if self.tex0:
+                key.append(self.tex0.getName())
+            else:
+                key.append(None)
+        
+        if options.get_alpha:
+            key.append(round(self.alpha, 3))
+        else:
+            key.append(None)
+            
+        if options.get_shininess:
+            key.append(round(self.shininess, 3))
+        else:
+            key.append(None)
+        
+        if options.get_emissive:
+            key.append(round(self.emissive, 3))
+        else:
+            key.append(None)
+        
+        if options.get_ambient:
+            key.append(round(self.ambient, 3))
+        else:
+            key.append(None)
+        
+        if options.get_specular:
+            for n in self.specular:
+                key.append(round(n, 3))
+        else:
+            key.extend([None, None, None])
+        
+        if options.get_diffuse:
+            for n in self.diffuse:
+                key.append(round(n, 3))
+        else:
+            key.extend([None, None, None])
+        
+#        key.extend(self.face_props.values())
+        
+        return tuple(key)
+
+    def __init__(self):
+        self.name = 'Material'
+        # Colors, List of 3 floats.
+        self.diffuse = [1.0, 1.0, 1.0]
+        self.specular = [1.0, 1.0, 1.0]
+
+        # Scalars
+        self.ambient = 0.0 # [0.0, 1.0]
+        self.emissive = 0.0 # [0.0, 1.0]
+        self.shininess = 0.5 # Range is [0.0, 2.0]
+        self.alpha = 1.0 # Range is [0.0, 1.0]
+
+        self.tex0 = None
+        
+        # OpenFlight Face attributes
+        self.face_props = dict.fromkeys(['comment', 'ir color', 'priority', 
+                            'draw type', 'texture white', 'template billboard',
+                            'smc', 'fid', 'ir material', 'lod generation control',
+                            'flags', 'light mode'])
+
+class VertexDesc:
+    def make_key(self):
+        return (round(self.x,3), round(self.y, 3), round(self.z, 3))
+        
+    def __init__(self):
+        self.x = 0.0
+        self.y = 0.0
+        self.z = 0.0
+        self.nx = 0.0
+        self.ny = 1.0
+        self.nz = 0.0
+        self.u = 0.0
+        self.v = 0.0
+        self.r = 1.0
+        self.g = 1.0
+        self.b = 1.0
+        self.a = 1.0        
+
+class LightPointAppDesc:
+    def make_key(self):
+        d = dict(self.props)
+        del d['id']
+        del d['type']
+        
+        if d['directionality'] != 0: # not omni
+            d['nx'] = 0.0
+            d['ny'] = 0.0
+            d['nz'] = 0.0
+        
+        return tuple(d.values())
+        
+    def __init__(self):
+        self.props = dict()
+        self.props.update({'type': 'LPA'})
+        self.props.update({'id': 'ap'})
+        # Attribs not found in inline lightpoint.
+        self.props.update({'visibility range': 0.0})
+        self.props.update({'fade range ratio': 0.0})
+        self.props.update({'fade in duration': 0.0})
+        self.props.update({'fade out duration': 0.0})
+        self.props.update({'LOD range ratio': 0.0})
+        self.props.update({'LOD scale': 0.0})
+
+class GlobalResourceRepository:
+    def request_lightpoint_app(self, desc):
+        match = self.light_point_app.get(desc.make_key())
+        
+        if match:
+            return match.getName()
+        else:
+            # Create empty and fill with properties.
+            name = desc.props['type'] + ': ' + desc.props['id']
+            object = Blender.Object.New('Empty', name)
+            
+            scene.link(object)
+    
+            # Attach properties
+            for name, value in desc.props.items():
+                object.addProperty(name, value)
+            
+            self.light_point_app.update({desc.make_key(): object})
+            
+            return object.getName()
+            
+    def request_vert(self, desc):
+        match = self.vert_dict.get(desc.make_key())
+
+        if match:
+            return match
+        else:
+            vert = Blender.NMesh.Vert(desc.x, desc.y, desc.z)
+
+            vert.no[0] = desc.nx
+            vert.no[1] = desc.ny
+            vert.no[2] = desc.nz
+
+            self.vert_dict.update({desc.make_key(): vert})
+            return vert
+
+    def request_mat(self, mat_desc):
+        match = self.mat_dict.get(mat_desc.make_key())
+        if match: return match
+        
+        mat = Blender.Material.New(mat_desc.name)
+
+        if mat_desc.tex0 != None:
+            mat.setTexture(0, mat_desc.tex0, Blender.Texture.TexCo.UV)
+
+        mat.setAlpha(mat_desc.alpha)
+        mat.setSpec(mat_desc.shininess)
+        mat.setHardness(255)
+        mat.setEmit(mat_desc.emissive)
+        mat.setAmb(mat_desc.ambient)
+        mat.setSpecCol(mat_desc.specular)
+        mat.setRGBCol(mat_desc.diffuse)
+        
+        # Create a text object to store openflight face attribs until
+        # user properties can be set on materials.
+#        t = Blender.Text.New('FACE: ' + mat.getName())
+#
+#        for name, value in mat_desc.face_props.items():
+#            t.write(name + '\n' + str(value) + '\n\n')    
+                
+        self.mat_dict.update({mat_desc.make_key(): mat})
+
+        return mat
+        
+    def request_image(self, filename_with_path):
+        if not options.get_texture: return None
+
+        img = self.img_dict.get(filename_with_path)
+        if img: return img
+            
+        img =  Blender.Image.Load(filename_with_path)
+        self.img_dict.update({filename_with_path: img})
+        return img
+        
+    def request_texture(self, image):
+        if not options.get_texture:
+            return None
+
+        tex = self.tex_dict.get(image.filename)
+        if tex: return tex
+        
+        tex = Blender.Texture.New(Blender.sys.basename(image.filename))
+        tex.setImage(image)
+        tex.setType('Image')
+        self.tex_dict.update({image.filename: tex})
+        return tex
+        
+    def __init__(self):
+        # material
+        self.mat_dict = dict()
+        mat_lst = Blender.Material.Get()
+        for mat in mat_lst:
+            mat_desc = MaterialDesc()
+            mapto_lst = mat.getTextures()
+            if mapto_lst[0]:
+                mat_desc.tex0 = mapto_lst[0].tex
+            else:
+                mat_desc.tex0 = None
+            mat_desc.alpha = mat.getAlpha()
+            mat_desc.shininess = mat.getSpec()
+            mat_desc.emissive = mat.getEmit()
+            mat_desc.ambient = mat.getAmb()
+            mat_desc.specular = mat.getSpecCol()
+            mat_desc.diffuse = mat.getRGBCol()
+            
+            self.mat_dict.update({mat_desc.make_key(): mat})
+            
+        # texture
+        self.tex_dict = dict()
+        tex_lst = Blender.Texture.Get()
+        
+        for tex in tex_lst:
+            img = tex.getImage()
+            # Only interested in textures with images.
+            if img:
+                self.tex_dict.update({img.filename: tex})
+                
+        # image
+        img_lst = Blender.Image.Get()
+        self.img_dict = dict()
+        for img in img_lst:
+            self.img_dict.update({img.filename: img})
+            
+        # vertex
+        self.vert_dict = dict()
+        
+        # light point
+        self.light_point_app = dict()
+        
+# Globals
+GRR = GlobalResourceRepository()
+FF = FileFinder()
+current_layer = 0x1
+scene = Blender.Scene.getCurrent()
+
+# Opcodes that indicate its time to return control to parent.
+throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63]
+do_not_report_opcodes = [76, 78, 79, 80, 81, 82, 94, 83, 33, 112, 100, 101, 102, 97, 31, 103, 104, 117, 118, 120, 121, 124, 125]
+
+opcode_name = { 0: 'db',
+                1: 'head',
+                2: 'grp',
+                4: 'obj',
+                5: 'face',
+                10: 'push',
+                11: 'pop',
+                14: 'dof',
+                19: 'push sub',
+                20: 'pop sub',
+                21: 'push ext',
+                22: 'pop ext',
+                23: 'cont',
+                31: 'comment',
+                32: 'color pal',
+                33: 'long id',
+                49: 'matrix',
+                50: 'vector',
+                52: 'multi-tex',
+                53: 'uv lst',
+                55: 'bsp',
+                60: 'rep',
+                61: 'inst ref',
+                62: 'inst def',
+                63: 'ext ref',
+                64: 'tex pal',
+                67: 'vert pal',
+                68: 'vert w col',
+                69: 'vert w col & norm',
+                70: 'vert w col, norm & uv',
+                71: 'vert w col & uv',
+                72: 'vert lst',
+                73: 'lod',
+                74: 'bndin box',
+                76: 'rot edge',
+                78: 'trans',
+                79: 'scl',
+                80: 'rot pnt',
+                81: 'rot and/or scale pnt',
+                82: 'put',
+                83: 'eyepoint & trackplane pal',
+                84: 'mesh',
+                85: 'local vert pool',
+                86: 'mesh prim',
+                87: 'road seg',
+                88: 'road zone',
+                89: 'morph vert lst',
+                90: 'link pal',
+                91: 'snd',
+                92: 'rd path',
+                93: 'snd pal',
+                94: 'gen matrix',
+                95: 'txt',
+                96: 'sw',
+                97: 'line styl pal',
+                98: 'clip reg',
+                100: 'ext',
+                101: 'light src',
+                102: 'light src pal',
+                103: 'reserved',
+                104: 'reserved',
+                105: 'bndin sph',
+                106: 'bndin cyl',
+                107: 'bndin hull',
+                108: 'bndin vol cntr',
+                109: 'bndin vol orient',
+                110: 'rsrvd',
+                111: 'light pnt',
+                112: 'tex map pal',
+                113: 'mat pal',
+                114: 'name tab',
+                115: 'cat',
+                116: 'cat dat',
+                117: 'rsrvd',
+                118: 'rsrvd',
+                119: 'bounding hist',
+                120: 'rsrvd',
+                121: 'rsrvd',
+                122: 'push attrib',
+                123: 'pop attrib',
+                124: 'rsrvd',
+                125: 'rsrvd',
+                126: 'curv',
+                127: 'road const',
+                128: 'light pnt appear pal',
+                129: 'light pnt anim pal',
+                130: 'indexed lp',
+                131: 'lp sys',
+                132: 'indx str',
+                133: 'shdr pal'}
+
+class Handler:
+    def in_throw_back_lst(self, opcode):
+        return opcode in self.throw_back_lst
+        
+    def handle(self, opcode):
+        return self.handler[opcode]()
+    
+    def handles(self, opcode):
+        return opcode in self.handler.keys()
+    
+    def throws_back_all_unhandled(self):
+        return self.throw_back_unhandled
+        
+    def set_throw_back_lst(self, a):
+        self.throw_back_lst = a
+        
+    def set_throw_back_all_unhandled(self):
+        self.throw_back_unhandled = True
+        
+    def set_only_throw_back_specified(self):
+        self.throw_back_unhandled = False
+        
+    def set_handler(self, d):
+        self.handler = d
+        
+    def __init__(self):
+        # Dictionary of opcodes to handler methods.
+        self.handler = dict()
+        # Send all opcodes not handled to the parent node.
+        self.throw_back_unhandled = False
+        # If throw_back_unhandled is False then only throw back
+        # if the opcodes in throw_back are encountered.
+        self.throw_back_lst = []
+        
+class Node:
+    def blender_import(self):
+        if self.opcode in opcode_name and options.verbose >= 2:
+            for i in range(self.get_level()):
+                print ' ',
+            print opcode_name[self.opcode],
+            print '-', self.props['id'],
+            print '-', self.props['comment'],
+
+            print
+
+        for child in self.children:
+            child.blender_import()
+        
+        # Import comment.
+#        if self.props['comment'] != '':
+#            name = 'COMMENT: ' + self.props['id']
+#            t = Blender.Text.New(name)
+#            t.write(self.props['comment'])
+#            self.props['comment'] = name
+        
+    # Always ignore extensions and anything in between them.
+    def parse_push_extension(self):
+        self.saved_handler = self.active_handler
+        self.active_handler = self.extension_handler
+        return True
+    
+    def parse_pop_extension(self):
+        self.active_handler = self.saved_handler
+        return True
+    
+    def parse_push(self):
+        self.header.fw.up_level()
+        # Ignore unknown children.
+        self.ignore_unhandled = True
+        # Don't do child records that might overwrite parent info. ex: longid
+        self.active_handler = self.child_handler
+        return True
+        
+    def parse_pop(self):
+        self.header.fw.down_level()
+        
+        if self.header.fw.get_level() == self.level:
+            return False
+        
+        return True
+    
+    def parse(self):
+        while self.header.fw.begin_record():
+            opcode = self.header.fw.get_opcode()
+
+            # Print out info on opcode and tree level.
+            if options.verbose >= 3:
+                p = ''
+                for i in range(self.header.fw.get_level()):
+                    p = p + '  '
+                if opcode in opcode_name:
+                    p = p + opcode_name[opcode]
+                else:
+                    if options.verbose >= 1:
+                        print 'undocumented opcode', opcode
+                    continue
+                            
+            if self.global_handler.handles(opcode):
+                if options.verbose >= 3:
+                    print p + ' handled globally'
+                if self.global_handler.handle(opcode) == False:
+                    break
+                    
+            elif self.active_handler.handles(opcode):
+                if options.verbose >= 4:
+                    print p + ' handled'
+                if self.active_handler.handle(opcode) == False:
+                    break
+                    
+            else:
+                if self.active_handler.throws_back_all_unhandled():
+                    if options.verbose >= 3:
+                        print p + ' handled elsewhere'              
+                    self.header.fw.repeat_record()
+                    break
+
+                elif self.active_handler.in_throw_back_lst(opcode):
+                    if options.verbose >= 3:
+                        print p + ' handled elsewhere'              
+                    self.header.fw.repeat_record()
+                    break
+
+                else:
+                    if options.verbose >= 3:
+                        print p + ' ignored'
+                    elif options.verbose >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name:
+                        print opcode_name[opcode], 'not handled'                        
+                    
+    def get_level(self):
+        return self.level
+        
+    def parse_long_id(self):
+        self.props['id'] = self.header.fw.read_string(self.header.fw.get_length()-4)
+        return True
+
+    def parse_comment(self):
+        self.props['comment'] = self.header.fw.read_string(self.header.fw.get_length()-4)
+        return True
+
+    def __init__(self, parent, header):
+        self.root_handler = Handler()
+        self.child_handler = Handler()
+        self.extension_handler = Handler()
+        self.global_handler = Handler()
+        
+        self.global_handler.set_handler({21: self.parse_push_extension})
+        self.active_handler = self.root_handler
+        
+        # used by parse_*_extension
+        self.extension_handler.set_handler({22: self.parse_pop_extension})
+        self.saved_handler = None
+        
+        self.header = header
+        self.children = []
+
+        self.parent = parent
+
+        if parent:
+            parent.children.append(self)
+
+        self.level = self.header.fw.get_level()
+        self.opcode = self.header.fw.get_opcode()
+
+        self.props = {'id': 'unnamed', 'comment': '', 'type': 'untyped'}
+
+class VertexPalette(Node):
+    def blender_import(self):
+        for vert_desc in self.vert_desc_lst:
+            vert = GRR.request_vert(vert_desc)
+            self.blender_verts.append(vert)
+
+    def parse_vertex_common(self):
+        # Add this vertex to an offset to index dictionary.
+        self.index_lst.append( (self.offset, self.next_index) )
+        self.next_index += 1
+        # Get ready for next record.
+        self.offset += self.header.fw.get_length()
+
+        v = VertexDesc()
+
+        self.header.fw.read_ahead(2)
+        v.flags = self.header.fw.read_short()
+
+        v.x = self.header.fw.read_double()
+        v.y = self.header.fw.read_double()
+        v.z = self.header.fw.read_double()
+
+        return v
+
+    def parse_vertex_post_common(self, v):
+        if not v.flags & 0x2000: # 0x2000 = no color
+            if v.flags & 0x1000: # 0x1000 = packed color
+                v.a = self.header.fw.read_uchar()
+                v.b = self.header.fw.read_uchar()
+                v.g = self.header.fw.read_uchar()
+                v.r = self.header.fw.read_uchar()
+            else:
+                self.header.fw.read_ahead(4)
+
+            color_index = self.header.fw.read_uint()
+            color = self.header.get_color(color_index)
+            v.r = color[0]
+            v.g = color[1]
+            v.b = color[2]
+            v.a = color[3]
+
+        self.vert_desc_lst.append(v)
+        
+        return True
+
+    def parse_vertex_c(self):
+        v = self.parse_vertex_common()
+
+        self.parse_vertex_post_common(v)
+        
+        return True
+
+    def parse_vertex_cn(self):
+        v = self.parse_vertex_common()
+
+        v.nx = self.header.fw.read_float()
+        v.ny = self.header.fw.read_float()
+        v.nz = self.header.fw.read_float()
+
+        self.parse_vertex_post_common(v)
+        
+        return True
+
+    def parse_vertex_cuv(self):
+        v = self.parse_vertex_common()
+
+        v.u = self.header.fw.read_float()
+        v.v = self.header.fw.read_float()
+
+        self.parse_vertex_post_common(v)
+        
+        return True
+
+    def parse_vertex_cnuv(self):
+        v = self.parse_vertex_common()
+
+        v.nx = self.header.fw.read_float()
+        v.ny = self.header.fw.read_float()
+        v.nz = self.header.fw.read_float()
+
+        v.u = self.header.fw.read_float()
+        v.v = self.header.fw.read_float()
+
+        self.parse_vertex_post_common(v)
+        
+        return True
+
+    def parse(self):
+        Node.parse(self)
+
+        self.index = dict(self.index_lst)
+        del self.index_lst
+
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        self.root_handler.set_handler({68: self.parse_vertex_c,
+                                       69: self.parse_vertex_cn,
+                                       70: self.parse_vertex_cnuv,
+                                       71: self.parse_vertex_cuv})
+        self.root_handler.set_throw_back_all_unhandled()
+
+        self.vert_desc_lst = []
+        self.blender_verts = []
+        self.offset = 8
+        # Used to create a map from byte offset to vertex index.
+        self.index = dict()
+        self.index_lst = []
+        self.next_index = 0
+
+class InterNode(Node):
+    def blender_import(self):
+#        name = self.props['type'] + ': ' + self.props['id']
+        name = self.props['id']
+        if self.isMesh:
+            self.object = Blender.Object.New('Mesh', name)
+            self.mesh = self.object.getData()
+        else:
+            self.object = Blender.Object.New('Empty', name)
+
+        if self.parent:
+            self.parent.object.makeParent([self.object])
+
+        scene.link(self.object)
+
+        self.object.Layer = current_layer
+
+        Node.blender_import(self)
+
+        if self.isMesh:
+            self.mesh.update(recalc_normals=1)
+            
+        if self.matrix:
+            self.object.setMatrix(self.matrix)
+        
+        # Attach properties
+        #for name, value in self.props.items():
+        #    self.object.addProperty(name, value)
+        
+    def parse_face(self):
+        child = Face(self)
+        child.parse()
+        return True
+
+    def parse_group(self):
+        child = Group(self)
+        child.parse()
+        return True
+
+    def move_to_next_layer(self):
+        global current_layer
+        current_layer = current_layer << 1
+        if current_layer > 0x80000:
+            current_layer = 1
+
+    def parse_lod(self):
+        child = LOD(self)
+        child.parse()
+        return True
+
+    def parse_unhandled(self):
+        child = Unhandled(self)
+        child.parse()
+        return True
+
+    def parse_object(self):
+        child = Object(self)
+        child.parse()
+        return True
+    
+    def parse_xref(self):
+        child = XRef(self)
+        child.parse()
+        return True
+
+    def parse_indexed_light_point(self):
+        child = IndexedLightPoint(self)
+        child.parse()
+        return True
+        
+    def parse_inline_light_point(self):
+        child = InlineLightPoint(self)
+        child.parse()
+        return True
+        
+    def parse_matrix(self):
+        m = []
+        for i in range(4):
+            m.append([])
+            for j in range(4):
+                f = self.header.fw.read_float()
+                m[i].append(f)
+        self.matrix = Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3])
+        
+    def __init__(self):
+        self.object = None
+        self.mesh = None
+        self.isMesh = False
+        self.matrix = None
+        
+class Face(Node):
+    def blender_import_face(self, indices, material_index, image):
+        mesh = self.parent.mesh
+        face = Blender.NMesh.Face()
+        
+        # Add vertices to face.
+        for i in indices:
+            # Add uv info to face.
+            vert_desc = self.header.vert_pal.vert_desc_lst[i]
+            if vert_desc.u != None and vert_desc.v != None:
+                mesh.hasFaceUV(True)
+                face.uv.append((vert_desc.u, vert_desc.v))
+            
+            # Add vert to face.
+            vert = self.header.vert_pal.blender_verts[i]
+            face.v.append(vert)
+            
+            # Add vert to mesh.
+            if not vert in mesh.verts:
+                mesh.verts.append(vert)
+            
+        if image:
+            face.image = image           
+        face.materialIndex = material_index
+        face.smooth = True
+        
+        mesh.addFace(face)
+        
+    def parse_comment(self):
+        self.comment = self.header.fw.read_string(self.header.fw.get_length()-4)
+        return True
+        
+    # returns a tuple (material, image) where material is the blender material and
+    # image is the blender image or None.
+    def create_blender_material(self):
+         # Create face material.
+        mat_desc = MaterialDesc()
+        
+        if self.mat_index != -1:
+            if not self.mat_index in self.header.mat_desc_pal:
+                if options.verbose >= 1:
+                    print 'Warning: Material index', self.mat_index, 'not in material palette.'
+            else:
+                mat_pal_desc = self.header.mat_desc_pal[self.mat_index]
+                mat_desc.alpha = mat_pal_desc.alpha * self.alpha # combine face and mat alphas
+                mat_desc.ambient = mat_pal_desc.ambient
+                mat_desc.diffuse = mat_pal_desc.diffuse
+                mat_desc.specular = mat_pal_desc.specular
+                mat_desc.emissive = mat_pal_desc.emissive
+                mat_desc.shininess = mat_pal_desc.shininess
+        else:
+            # if no material get alpha from just face.
+            mat_desc.alpha = self.alpha
+
+        # Color.
+        if options.color_from_face:
+            color = None
+            if not self.props['flags'] & 0x40000000:
+                if self.props['flags'] & 0x10000000: # packed color
+                    color = self.packed_color
+                else:
+                    color = self.header.get_color(self.color_index)
+
+            if color:
+                r = float(color[0])/255.0
+                g = float(color[1])/255.0
+                b = float(color[2])/255.0
+                mat_desc.diffuse = [r, g, b]
+
+        # Texture
+        image = None
+        if self.tex_index != -1 and self.tex_index in self.header.bl_tex_pal:
+            mat_desc.tex0 = self.header.bl_tex_pal[self.tex_index]
+            if mat_desc.tex0:
+                mat_desc.name = FF.strip_path(self.header.tex_pal[self.tex_index])
+                image = mat_desc.tex0.image
+
+        # OpenFlight Face Attributes
+        mat_desc.face_props = self.props
+        
+        # Get material.
+        mat = GRR.request_mat(mat_desc)
+        
+        # Add material to mesh.
+        mesh = self.parent.mesh
+        try:
+            mesh.addMaterial(mat)
+        except AttributeError:
+            pass
+        except RuntimeError:
+            if options.verbose >= 1:
+                print 'Warning: Too many materials per mesh object. Only a maximum of 16 ' + \
+                      'allowed. Using 16th material instead.'
+            mat = mesh.materials[-1]
+
+        # Return where it is in the mesh for faces.
+        material_index = mesh.materials.index(mat)
+        
+        return (material_index, image)
+        
+    def triangulate(self):
+        point_lst = []
+        for i in self.indices:
+            vert_desc = self.header.vert_pal.vert_desc_lst[i]
+            point_lst.append(self.header.vert_pal.blender_verts[i].co)
+        
+        mesh = scanFillPoints(point_lst)
+        if not mesh:
+            return []
+            
+        # mesh.verts and vert_lst should be in the same order unless blender rearranged them during triangulation, unlikely.
+        tri_lst = []
+        for f in mesh.faces:
+            tri = []
+            for vert in f.v:
+                i = mesh.verts.index(vert)
+                tri.append(self.indices[i])
+            tri_lst.append(tri)
+            
+        return tri_lst
+        
+    def blender_import(self):
+        vert_count = len(self.indices)
+        if vert_count == 0:
+            if options.verbose >= 2:
+                print 'Warning: Ignoring face with no vertices.'
+            return
+
+        material = self.create_blender_material()
+        
+        if vert_count > 4:
+            tri_lst = self.triangulate()
+        else:
+            tri_lst = [self.indices]
+            
+        for tri in tri_lst:
+            self.blender_import_face(tri, material[0], material[1])
+        
+        # Store comment info in parent.
+        if self.comment != '':
+            if self.parent.props['comment'] != '':
+                self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment
+            else:
+                self.parent.props['comment'] = self.comment
+        
+    def parse_vertex_list(self):
+        length = self.header.fw.get_length()
+        fw = self.header.fw
+        vert_pal = self.header.vert_pal
+
+        count = (length-4)/4
+
+        for i in range(count):
+            byte_offset = fw.read_int()
+            if byte_offset in vert_pal.index:
+                index = vert_pal.index[byte_offset]
+                self.indices.append(index)
+            elif options.verbose >= 1:
+                print 'Warning: Unable to map byte offset %s' + \
+                      ' to vertex index.' % byte_offset
+      
+        return True
+
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        self.root_handler.set_handler({31: self.parse_comment,
+                                       10: self.parse_push})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({72: self.parse_vertex_list,
+                                        10: self.parse_push,
+                                        11: self.parse_pop})
+        
+        if parent:
+            parent.isMesh = True
+
+        self.indices = []
+        
+        self.comment = ''
+        self.props = dict.fromkeys(['ir color', 'priority', 
+                                    'draw type', 'texture white', 'template billboard',
+                                    'smc', 'fid', 'ir material', 'lod generation control',
+                                    'flags', 'light mode'])
+        
+        self.header.fw.read_ahead(8) # face id
+        # Load face.
+        self.props['ir color'] = self.header.fw.read_int()
+        self.props['priority'] = self.header.fw.read_short()
+        self.props['draw type'] = self.header.fw.read_char()
+        self.props['texture white'] = self.header.fw.read_char()
+        self.header.fw.read_ahead(4) # color name indices
+        self.header.fw.read_ahead(1) # reserved
+        self.props['template billboard'] = self.header.fw.read_uchar()
+        self.detail_tex_index = self.header.fw.read_short()
+        self.tex_index = self.header.fw.read_short()
+        self.mat_index = self.header.fw.read_short()
+        self.props['smc'] = self.header.fw.read_short()
+        self.props['fid'] = self.header.fw.read_short()
+        self.props['ir material'] = self.header.fw.read_int()
+        self.alpha = 1.0 - float(self.header.fw.read_ushort()) / 65535.0
+        self.props['lod generation control'] = self.header.fw.read_uchar()
+        self.header.fw.read_ahead(1) # line style index
+        self.props['flags'] = self.header.fw.read_int()
+        self.props['light mode'] = self.header.fw.read_uchar()
+        self.header.fw.read_ahead(7)
+        a = self.header.fw.read_uchar()
+        b = self.header.fw.read_uchar()
+        g = self.header.fw.read_uchar()
+        r = self.header.fw.read_uchar()
+        self.packed_color = [r, g, b, a]
+        a = self.header.fw.read_uchar()
+        b = self.header.fw.read_uchar()
+        g = self.header.fw.read_uchar()
+        r = self.header.fw.read_uchar()
+        self.alt_packed_color = [r, g, b, a]
+        self.tex_map_index = self.header.fw.read_short()
+        self.header.fw.read_ahead(2)
+        self.color_index = self.header.fw.read_uint()
+        self.alt_color_index = self.header.fw.read_uint()
+        #self.header.fw.read_ahead(2)
+        #self.shader_index = self.header.fw.read_short()
+
+class Object(InterNode):
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+       
+        self.child_handler.set_handler({5: self.parse_face,
+                                        #130: self.parse_indexed_light_point,
+                                        #111: self.parse_inline_light_point,
+                                        10: self.parse_push,
+                                        11: self.parse_pop})
+
+        self.props['type'] = 'Object'
+        self.props['id'] = self.header.fw.read_string(8)
+
+class Group(InterNode):
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({5: self.parse_face,
+                                        #130: self.parse_indexed_light_point,
+                                        #111: self.parse_inline_light_point,
+                                        2: self.parse_group,
+                                        73: self.parse_lod,
+                                        4: self.parse_object,
+                                        10: self.parse_push,
+                                        11: self.parse_pop,
+                                        96: self.parse_unhandled,
+                                        14: self.parse_unhandled,
+                                        91: self.parse_unhandled,
+                                        98: self.parse_unhandled,
+                                        63: self.parse_xref})
+        self.props = dict.fromkeys(['type', 'id', 'comment', 'priority', 'flags', 'special1',
+                                    'special2', 'significance', 'layer code', 'loop count',
+                                    'loop duration', 'last frame duration'])
+        
+        self.props['type'] = 'Group'
+        self.props['comment'] = ''
+        self.props['id'] = self.header.fw.read_string(8)
+        self.props['priority'] = self.header.fw.read_short()
+        self.header.fw.read_ahead(2)
+        self.props['flags'] = self.header.fw.read_int()
+        self.props['special1'] = self.header.fw.read_short()
+        self.props['special2'] = self.header.fw.read_short()
+        self.props['significance'] = self.header.fw.read_short()
+        self.props['layer code'] = self.header.fw.read_char()
+        self.header.fw.read_ahead(5)
+        self.props['loop count'] = self.header.fw.read_int()
+        self.props['loop duration'] = self.header.fw.read_float()
+        self.props['last frame duration'] = self.header.fw.read_float()               
+
+class XRef(InterNode):
+    def parse(self):
+        if self.xref:
+            self.xref.parse()
+        Node.parse(self)
+
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+
+        self.root_handler.set_handler({49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        xref_filename = self.header.fw.read_string(200)
+        filename = FF.find(xref_filename)
+
+        self.props['type'] = 'XRef'
+        
+        if filename != None:
+            self.xref = Database(filename, self)
+            self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(filename))[0]
+        else:
+            self.xref = None
+            self.props['id'] = 'X: broken'
+
+class LOD(InterNode):
+    def blender_import(self):
+        self.move_to_next_layer()
+        InterNode.blender_import(self)
+
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({2: self.parse_group,
+                                        73: self.parse_lod,
+                                        4: self.parse_object,
+                                        10: self.parse_push,
+                                        11: self.parse_pop,
+                                        96: self.parse_unhandled, # switch
+                                        14: self.parse_unhandled, # DOF
+                                        91: self.parse_unhandled, # sound
+                                        98: self.parse_unhandled, # clip
+                                        63: self.parse_xref})
+
+        self.props['type'] = 'LOD'
+        self.props['id'] = self.header.fw.read_string(8)
+
+class InlineLightPoint(InterNode):
+    # return dictionary: lp_app name => index list
+    def group_points(self, props):
+        
+        name_to_indices = {}
+        
+        for i in self.indices:
+            vert_desc = self.header.vert_pal.vert_desc_lst[i]
+            app_desc = LightPointAppDesc()
+            app_desc.props.update(props)
+            # add vertex normal and color
+            app_desc.props.update({'nx': vert_desc.nx})
+            app_desc.props.update({'ny': vert_desc.ny})
+            app_desc.props.update({'nz': vert_desc.nz})
+            
+            app_desc.props.update({'r': vert_desc.r})
+            app_desc.props.update({'g': vert_desc.g})
+            app_desc.props.update({'b': vert_desc.b})
+            app_desc.props.update({'a': vert_desc.a})
+            
+            app_name = GRR.request_lightpoint_app(app_desc)
+
+            if name_to_indices.get(app_name):
+                name_to_indices[app_name].append(i)
+            else:
+                name_to_indices.update({app_name: [i]})
+            
+        return name_to_indices
+        
+    def blender_import(self):
+        name = self.props['type'] + ': ' + self.props['id']
+
+        name_to_indices = self.group_points(self.app_props)
+
+        for app_name, indices in name_to_indices.items():
+            self.object = Blender.Object.New('Mesh', name)
+            self.mesh = self.object.getData()
+
+            if self.parent:
+                self.parent.object.makeParent([self.object])
+                
+            for i in indices:
+                vert = self.header.vert_pal.blender_verts[i]
+                self.mesh.verts.append(vert)
+            
+            scene.link(self.object)
+    
+            self.object.Layer = current_layer
+            
+            if self.matrix:
+                self.object.setMatrix(self.matrix)
+                
+            # Import comment.
+            if self.props['comment'] != '':
+                name = 'COMMENT: ' + self.props['id']
+                t = Blender.Text.New(name)
+                t.write(self.props['comment'])
+                self.props['comment'] = name
+                
+            # Attach properties.
+            self.props.update({'appearance': app_name})
+            for name, value in self.props.items():
+                self.object.addProperty(name, value)
+            
+            self.mesh.update()
+            
+    def parse_vertex_list(self):
+        length = self.header.fw.get_length()
+        fw = self.header.fw
+        vert_pal = self.header.vert_pal
+
+        count = (length-4)/4
+
+        for i in range(count):
+            byte_offset = fw.read_int()
+            if byte_offset in vert_pal.index:
+                index = vert_pal.index[byte_offset]
+                self.indices.append(index)
+            elif options.verbose >= 1:
+                print 'Warning: Unable to map byte offset %s' + \
+                      ' to vertex index.' % byte_offset
+      
+        return True
+        
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({72: self.parse_vertex_list,
+                                        10: self.parse_push,
+                                        11: self.parse_pop})
+
+        self.indices = []
+                
+        self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance'])
+        self.app_props = dict()
+        
+        self.props['comment'] = ''
+        self.props['type'] = 'Light Point'
+        self.props['id'] = self.header.fw.read_string(8)
+        
+        self.app_props.update({'smc': self.header.fw.read_short()})
+        self.app_props.update({'fid': self.header.fw.read_short()})
+        self.app_props.update({'back color: a': self.header.fw.read_uchar()})
+        self.app_props.update({'back color: b': self.header.fw.read_uchar()})
+        self.app_props.update({'back color: g': self.header.fw.read_uchar()})
+        self.app_props.update({'back color: r': self.header.fw.read_uchar()})
+        self.app_props.update({'display mode': self.header.fw.read_int()})
+        self.app_props.update({'intensity': self.header.fw.read_float()})
+        self.app_props.update({'back intensity': self.header.fw.read_float()})
+        self.app_props.update({'minimum defocus': self.header.fw.read_float()})
+        self.app_props.update({'maximum defocus': self.header.fw.read_float()})
+        self.app_props.update({'fading mode': self.header.fw.read_int()})
+        self.app_props.update({'fog punch mode': self.header.fw.read_int()})
+        self.app_props.update({'directional mode': self.header.fw.read_int()})
+        self.app_props.update({'range mode': self.header.fw.read_int()})
+        self.app_props.update({'min pixel size': self.header.fw.read_float()})
+        self.app_props.update({'max pixel size': self.header.fw.read_float()})
+        self.app_props.update({'actual size': self.header.fw.read_float()})
+        self.app_props.update({'trans falloff pixel size': self.header.fw.read_float()})
+        self.app_props.update({'trans falloff exponent': self.header.fw.read_float()})
+        self.app_props.update({'trans falloff scalar': self.header.fw.read_float()})
+        self.app_props.update({'trans falloff clamp': self.header.fw.read_float()})
+        self.app_props.update({'fog scalar': self.header.fw.read_float()})
+        self.app_props.update({'fog intensity': self.header.fw.read_float()})
+        self.app_props.update({'size threshold': self.header.fw.read_float()})
+        self.app_props.update({'directionality': self.header.fw.read_int()})
+        self.app_props.update({'horizontal lobe angle': self.header.fw.read_float()})
+        self.app_props.update({'vertical lobe angle': self.header.fw.read_float()})
+        self.app_props.update({'lobe roll angle': self.header.fw.read_float()})
+        self.app_props.update({'dir falloff exponent': self.header.fw.read_float()})
+        self.app_props.update({'dir ambient intensity': self.header.fw.read_float()})
+        self.header.fw.read_ahead(12) # Animation settings.        
+        self.app_props.update({'significance': self.header.fw.read_float()})
+        self.props['draw order'] = self.header.fw.read_int()
+        self.app_props.update({'flags': self.header.fw.read_int()})
+        #self.fw.read_ahead(12) # More animation settings.                
+        
+class IndexedLightPoint(InterNode):
+    # return dictionary: lp_app name => index list
+    def group_points(self, props):
+        
+        name_to_indices = {}
+        
+        for i in self.indices:
+            vert_desc = self.header.vert_pal.vert_desc_lst[i]
+            app_desc = LightPointAppDesc()
+            app_desc.props.update(props)
+            # add vertex normal and color
+            app_desc.props.update({'nx': vert_desc.nx})
+            app_desc.props.update({'ny': vert_desc.ny})
+            app_desc.props.update({'nz': vert_desc.nz})
+            
+            app_desc.props.update({'r': vert_desc.r})
+            app_desc.props.update({'g': vert_desc.g})
+            app_desc.props.update({'b': vert_desc.b})
+            app_desc.props.update({'a': vert_desc.a})
+            
+            app_name = GRR.request_lightpoint_app(app_desc)
+
+            if name_to_indices.get(app_name):
+                name_to_indices[app_name].append(i)
+            else:
+                name_to_indices.update({app_name: [i]})
+            
+        return name_to_indices
+        
+    def blender_import(self):
+        name = self.props['type'] + ': ' + self.props['id']
+        
+        name_to_indices = self.group_points(self.header.lightpoint_appearance_pal[self.index])
+        
+        for app_name, indices in name_to_indices.items():        
+            self.object = Blender.Object.New('Mesh', name)
+            self.mesh = self.object.getData()
+            
+            if self.parent:
+                self.parent.object.makeParent([self.object])
+                
+            for i in indices:
+                vert = self.header.vert_pal.blender_verts[i]
+                self.mesh.verts.append(vert)
+            
+            scene.link(self.object)
+    
+            self.object.Layer = current_layer
+            
+            if self.matrix:
+                self.object.setMatrix(self.matrix)
+                
+            # Import comment.
+            if self.props['comment'] != '':
+                name = 'COMMENT: ' + self.props['id']
+                t = Blender.Text.New(name)
+                t.write(self.props['comment'])
+                self.props['comment'] = name
+                
+            # Attach properties.
+            self.props.update({'appearance': app_name})
+            for name, value in self.props.items():
+                self.object.addProperty(name, value)
+            
+            self.mesh.update()
+            
+    def parse_vertex_list(self):
+        length = self.header.fw.get_length()
+        fw = self.header.fw
+        vert_pal = self.header.vert_pal
+
+        count = (length-4)/4
+
+        for i in range(count):
+            byte_offset = fw.read_int()
+            if byte_offset in vert_pal.index:
+                index = vert_pal.index[byte_offset]
+                self.indices.append(index)
+            elif options.verbose >= 1:
+                print 'Warning: Unable to map byte offset %s' + \
+                      ' to vertex index.' % byte_offset
+      
+        return True
+        
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({72: self.parse_vertex_list,
+                                        10: self.parse_push,
+                                        11: self.parse_pop})
+
+        self.indices = []
+        
+        self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance'])
+        self.props['comment'] = ''
+        self.props['type'] = 'Light Point'
+        self.props['id'] = self.header.fw.read_string(8)
+        self.index = self.header.fw.read_int()
+        self.header.fw.read_ahead(4) # animation index
+        self.props['draw order'] = self.header.fw.read_int()        
+
+class Unhandled(InterNode):
+    def __init__(self, parent):
+        Node.__init__(self, parent, parent.header)
+        InterNode.__init__(self)
+        
+        self.root_handler.set_handler({33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       10: self.parse_push,
+                                       49: self.parse_matrix})
+        self.root_handler.set_throw_back_lst(throw_back_opcodes)
+        
+        self.child_handler.set_handler({2: self.parse_group,
+                                        73: self.parse_lod,
+                                        4: self.parse_object,
+                                        10: self.parse_push,
+                                        11: self.parse_pop,
+                                        96: self.parse_unhandled, # switch
+                                        14: self.parse_unhandled, # DOF
+                                        91: self.parse_unhandled, # sound
+                                        98: self.parse_unhandled, # clip
+                                        63: self.parse_xref})
+
+        self.props['id'] = self.header.fw.read_string(8)
+
+class Database(InterNode):
+    def blender_import(self):
+        self.tex_pal = dict(self.tex_pal_lst)
+        del self.tex_pal_lst
+
+        # Setup Textures
+        bl_tex_pal_lst = []
+        for i in self.tex_pal.keys():
+            path_filename = FF.find(self.tex_pal[i])
+            if path_filename != None:
+                img = GRR.request_image(path_filename)
+                if img:
+                    tex = GRR.request_texture(img)
+                    tex.setName(FF.strip_path(self.tex_pal[i]))
+                    bl_tex_pal_lst.append( (i, tex) )
+                else:
+                    bl_tex_pal_lst.append( (i, None) )
+            elif options.verbose >= 1:
+                print 'Warning: Unable to find', self.tex_pal[i]
+
+        self.bl_tex_pal = dict(bl_tex_pal_lst)
+
+        # Setup Materials
+        self.mat_desc_pal = dict(self.mat_desc_pal_lst)
+
+        InterNode.blender_import(self)
+
+    def parse_appearance_palette(self):
+        props = dict()
+        self.fw.read_ahead(4) # reserved
+        props.update({'id': self.fw.read_string(256)})
+        index = self.fw.read_int()
+        props.update({'smc': self.fw.read_short()})
+        props.update({'fid': self.fw.read_short()})
+        props.update({'back color: a': self.fw.read_uchar()})
+        props.update({'back color: b': self.fw.read_uchar()})
+        props.update({'back color: g': self.fw.read_uchar()})
+        props.update({'back color: r': self.fw.read_uchar()})
+        props.update({'display mode': self.fw.read_int()})
+        props.update({'intensity': self.fw.read_float()})
+        props.update({'back intensity': self.fw.read_float()})
+        props.update({'minimum defocus': self.fw.read_float()})
+        props.update({'maximum defocus': self.fw.read_float()})
+        props.update({'fading mode': self.fw.read_int()})
+        props.update({'fog punch mode': self.fw.read_int()})
+        props.update({'directional mode': self.fw.read_int()})
+        props.update({'range mode': self.fw.read_int()})
+        props.update({'min pixel size': self.fw.read_float()})
+        props.update({'max pixel size': self.fw.read_float()})
+        props.update({'actual size': self.fw.read_float()})
+        props.update({'trans falloff pixel size': self.fw.read_float()})
+        props.update({'trans falloff exponent': self.fw.read_float()})
+        props.update({'trans falloff scalar': self.fw.read_float()})
+        props.update({'trans falloff clamp': self.fw.read_float()})
+        props.update({'fog scalar': self.fw.read_float()})
+        props.update({'fog intensity': self.fw.read_float()})
+        props.update({'size threshold': self.fw.read_float()})
+        props.update({'directionality': self.fw.read_int()})
+        props.update({'horizontal lobe angle': self.fw.read_float()})
+        props.update({'vertical lobe angle': self.fw.read_float()})
+        props.update({'lobe roll angle': self.fw.read_float()})
+        props.update({'dir falloff exponent': self.fw.read_float()})
+        props.update({'dir ambient intensity': self.fw.read_float()})
+        props.update({'significance': self.fw.read_float()})
+        props.update({'flags': self.fw.read_int()})
+        props.update({'visibility range': self.fw.read_float()})
+        props.update({'fade range ratio': self.fw.read_float()})
+        props.update({'fade in duration': self.fw.read_float()})
+        props.update({'fade out duration': self.fw.read_float()})
+        props.update({'LOD range ratio': self.fw.read_float()})
+        props.update({'LOD scale': self.fw.read_float()})
+        
+        self.lightpoint_appearance_pal.update({index: props})
+        
+    def parse_header(self):
+        self.props['type'] = 'Header'
+        self.props['comment'] = ''
+        self.props['id'] = self.fw.read_string(8)
+        self.props['version'] = self.fw.read_int()
+        self.fw.read_ahead(46)
+        self.props['units'] = self.fw.read_char()
+        self.props['set white'] = bool(self.fw.read_char())
+        self.props['flags'] = self.fw.read_int()
+        self.fw.read_ahead(24)
+        self.props['projection type'] = self.fw.read_int()
+        self.fw.read_ahead(36)
+        self.props['sw x'] = self.fw.read_double()
+        self.props['sw y'] = self.fw.read_double()
+        self.props['dx'] = self.fw.read_double()
+        self.props['dy'] = self.fw.read_double()
+        self.fw.read_ahead(24)
+        self.props['sw lat'] = self.fw.read_double()
+        self.props['sw lon'] = self.fw.read_double()
+        self.props['ne lat'] = self.fw.read_double()
+        self.props['ne lon'] = self.fw.read_double()
+        self.props['origin lat'] = self.fw.read_double()
+        self.props['origin lon'] = self.fw.read_double()
+        self.props['lambert lat1'] = self.fw.read_double()
+        self.props['lambert lat2'] = self.fw.read_double()
+        self.fw.read_ahead(16)
+        self.props['ellipsoid model'] = self.fw.read_int()
+        self.fw.read_ahead(4)
+        self.props['utm zone'] = self.fw.read_short()
+        self.fw.read_ahead(6)
+        self.props['dz'] = self.fw.read_double()
+        self.props['radius'] = self.fw.read_double()
+        self.fw.read_ahead(8)
+        self.props['major axis'] = self.fw.read_double()
+        self.props['minor axis'] = self.fw.read_double()
+        
+        if options.verbose >= 1:
+            print 'OpenFlight Version:', float(self.props['version']) / 100.0
+            print
+            
+        return True
+
+    def parse_mat_palette(self):
+        mat_desc = MaterialDesc()
+        index = self.fw.read_int()
+
+        name = self.fw.read_string(12)
+        if len(mat_desc.name) > 0:
+            mat_desc.name = name
+
+        flag = self.fw.read_int()
+        # skip material if not used
+        if not flag & 0x80000000:
+            return True
+
+        ambient_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
+        mat_desc.diffuse = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
+        mat_desc.specular = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
+        emissive_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()]
+
+        mat_desc.shininess = self.fw.read_float() / 64.0 # [0.0, 128.0] => [0.0, 2.0]
+        mat_desc.alpha = self.fw.read_float()
+
+        # Convert ambient and emissive colors into intensitities.
+        mat_desc.ambient = col_to_gray(ambient_col)
+        mat_desc.emissive = col_to_gray(emissive_col)
+
+        self.mat_desc_pal_lst.append( (index, mat_desc) )
+        
+        return True
+    
+    def get_color(self, color_index):
+        index = color_index / 128
+        intensity = float(color_index - 128.0 * index) / 127.0
+
+        if index >= 0 and index <= 1023:
+            brightest = self.col_pal[index]
+            r = int(brightest[0] * intensity)
+            g = int(brightest[1] * intensity)
+            b = int(brightest[2] * intensity)
+            #a = int(brightest[3] * intensity)
+            a = int(brightest[3])
+            
+            color = [r, g, b, a]
+            
+        return color
+    
+    def parse_color_palette(self):
+        self.header.fw.read_ahead(128)
+        for i in range(1024):
+            a = self.header.fw.read_uchar()
+            b = self.header.fw.read_uchar()
+            g = self.header.fw.read_uchar()
+            r = self.header.fw.read_uchar()
+            self.col_pal.append((r, g, b, a))
+        return True
+        
+    def parse_vertex_palette(self):
+        self.vert_pal = VertexPalette(self)
+        self.vert_pal.parse()
+        return True
+        
+    def parse_texture_palette(self):
+        name = self.fw.read_string(200)
+        index = self.fw.read_int()
+        self.tex_pal_lst.append( (index, name) )
+        return True
+        
+    def __init__(self, filename, parent=None):
+        if options.verbose >= 1:
+            print 'Parsing:', filename
+            print
+        
+        self.fw = FltIn(filename)
+        Node.__init__(self, parent, self)
+        InterNode.__init__(self)
+        
+        self.root_handler.set_handler({1: self.parse_header,
+                                       67: self.parse_vertex_palette,
+                                       33: self.parse_long_id,
+                                       31: self.parse_comment,
+                                       64: self.parse_texture_palette,
+                                       32: self.parse_color_palette,
+                                       113: self.parse_mat_palette,
+                                       128: self.parse_appearance_palette,
+                                       10: self.parse_push})
+        if parent:
+            self.root_handler.set_throw_back_lst(throw_back_opcodes)
+
+        self.child_handler.set_handler({#130: self.parse_indexed_light_point,
+                                        #111: self.parse_inline_light_point,
+                                        2: self.parse_group,
+                                        73: self.parse_lod,
+                                        4: self.parse_object,
+                                        10: self.parse_push,
+                                        11: self.parse_pop,
+                                        96: self.parse_unhandled,
+                                        14: self.parse_unhandled,
+                                        91: self.parse_unhandled,
+                                        98: self.parse_unhandled,
+                                        63: self.parse_xref})
+        
+        self.vert_pal = None
+        self.lightpoint_appearance_pal = dict()
+        self.tex_pal = dict()
+        self.tex_pal_lst = []
+        self.bl_tex_pal = dict()
+        self.col_pal = []
+        self.mat_desc_pal_lst = []
+        self.mat_desc_pal = dict()
+        self.props = dict.fromkeys(['id', 'type', 'comment', 'version', 'units', 'set white',
+            'flags', 'projection type', 'sw x', 'sw y', 'dx', 'dy', 'dz', 'sw lat',
+            'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1',
+            'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis'])
+
+def select_file(filename):
+    Blender.Window.WaitCursor(True)
+
+    if filename[-4:] != '.flt':
+        msg = 'Error: Not a flight file.'
+        Blender.Draw.PupMenu(msg)
+        print msg
+        print
+        return
+
+    if not Blender.sys.exists(filename):
+        msg = 'Error: File ' + filename + ' does not exist.'
+        Blender.Draw.PupMenu(msg)
+        return
+
+    FF.add_file_to_search_path(filename)
+    
+    if options.verbose >= 1:
+        print 'Pass 1: Loading.'
+        print
+
+    load_time = Blender.sys.time()    
+    db = Database(filename)
+    db.parse()
+    load_time = Blender.sys.time() - load_time
+
+    if options.verbose >= 1:
+        print
+        print 'Pass 2: Importing to Blender.'
+        print
+
+    import_time = Blender.sys.time()
+    db.blender_import()
+    import_time = Blender.sys.time() - import_time
+    
+    Blender.Window.ViewLayer(range(1,21))
+    Blender.Window.RedrawAll()
+        
+    if options.verbose >= 1:
+        print 'Done.'
+        print
+        print 'Time to parse file: %.3f seconds' % load_time
+        print 'Time to import to blender: %.3f seconds' % import_time
+        print 'Total time: %.3f seconds' % (load_time + import_time)
+    
+    Blender.Window.WaitCursor(False)
+
+
+if options.verbose >= 1:
+    print
+    print 'OpenFlight Importer'
+    print 'Version:', __version__
+    print 'Author: Greg MacDonald'
+    print __url__[2]
+    print
+
+Blender.Window.EditMode(0)
+
+winid = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D)[0]['id']
+
+Blender.Window.FileSelector(select_file, "Import OpenFlight", "*.flt")
diff --git a/release/scripts/md2_export.py b/release/scripts/md2_export.py
new file mode 100644 (file)
index 0000000..d02f110
--- /dev/null
@@ -0,0 +1,1016 @@
+#!BPY
+
+"""
+Name: 'MD2 (.md2)'
+Blender: 239
+Group: 'Export'
+Tooltip: 'Export to Quake file format (.md2).'
+"""
+
+__author__ = 'Bob Holcomb'
+__version__ = '0.16'
+__url__ = ["Bob's site, http://bane.servebeer.com",
+     "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"]
+__email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"]
+__bpydoc__ = """\
+This script Exports a Quake 2 file (MD2).
+
+ Additional help from: Shadwolf, Skandal, Rojo, Cambo<br>
+ Thanks Guys!
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C): Bob Holcomb
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+import Blender
+from Blender import *
+from Blender.Draw import *
+from Blender.BGL import *
+from Blender.Window import *
+
+import struct, string
+from types import *
+
+
+
+######################################################
+# GUI Loader
+######################################################
+
+# Export globals
+g_filename=Create("/home/bob/work/blender_scripts/md2/test-export.md2")
+g_frame_filename=Create("default")
+
+g_filename_search=Create("model")
+g_frame_search=Create("default")
+
+user_frame_list=[]
+
+#Globals
+g_scale=Create(1.0)
+
+# Events
+EVENT_NOEVENT=1
+EVENT_SAVE_MD2=2
+EVENT_CHOOSE_FILENAME=3
+EVENT_CHOOSE_FRAME=4
+EVENT_EXIT=100
+
+######################################################
+# Callbacks for Window functions
+######################################################
+def filename_callback(input_filename):
+       global g_filename
+       g_filename.val=input_filename
+
+def frame_callback(input_frame):
+       global g_frame_filename
+       g_frame_filename.val=input_frame
+
+def draw_gui():
+       global g_scale
+       global g_filename
+       global g_frame_filename
+       global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_FRAME,EVENT_EXIT
+
+       ########## Titles
+       glClear(GL_COLOR_BUFFER_BIT)
+       glRasterPos2d(8, 103)
+       Text("MD2 Export")
+
+       ######### Parameters GUI Buttons
+       g_filename = String("MD2 file to save: ", EVENT_NOEVENT, 10, 55, 210, 18,
+                            g_filename.val, 255, "MD2 file to save")
+       ########## MD2 File Search Button
+       Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18)
+
+       g_frame_filename = String("Frame List file to load: ", EVENT_NOEVENT, 10, 35, 210, 18,
+                                g_frame_filename.val, 255, "Frame List to load-overrides MD2 defaults")
+       ########## Texture Search Button
+       Button("Search",EVENT_CHOOSE_FRAME,220,35,80,18)
+
+       ########## Scale slider-default is 1/8 which is a good scale for md2->blender
+       g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18,
+                    1.0, 0.001, 10.0, 1, "Scale factor for obj Model");
+
+       ######### Draw and Exit Buttons
+       Button("Export",EVENT_SAVE_MD2 , 10, 10, 80, 18)
+       Button("Exit",EVENT_EXIT , 170, 10, 80, 18)
+
+def event(evt, val):   
+       if (evt == QKEY and not val):
+               Exit()
+
+def bevent(evt):
+       global g_filename
+       global g_frame_filename
+       global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_EXIT
+
+       ######### Manages GUI events
+       if (evt==EVENT_EXIT):
+               Blender.Draw.Exit()
+       elif (evt==EVENT_CHOOSE_FILENAME):
+               FileSelector(filename_callback, "MD2 File Selection")
+       elif (evt==EVENT_CHOOSE_FRAME):
+               FileSelector(frame_callback, "Frame Selection")
+       elif (evt==EVENT_SAVE_MD2):
+               if (g_filename.val == "model"):
+                       save_md2("blender.md2")
+                       Blender.Draw.Exit()
+                       return
+               else:
+                       save_md2(g_filename.val)
+                       Blender.Draw.Exit()
+                       return
+
+Register(draw_gui, event, bevent)
+
+######################################################
+# MD2 Model Constants
+######################################################
+MD2_MAX_TRIANGLES=4096
+MD2_MAX_VERTICES=2048
+MD2_MAX_TEXCOORDS=2048
+MD2_MAX_FRAMES=512
+MD2_MAX_SKINS=32
+MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128)
+
+MD2_FRAME_NAME_LIST=(("stand",1,40),
+                                       ("run",41,46),
+                                       ("attack",47,54),
+                                       ("pain1",55,58),
+                                       ("pain2",59,62),
+                                       ("pain3",63,66),
+                                       ("jump",67,72),
+                                       ("flip",73,84),
+                                       ("salute", 85,95),
+                                       ("taunt",96,112),
+                                       ("wave",113,123),
+                                       ("point",124,135),
+                                       ("crstnd",136,154),
+                                       ("crwalk",155,160),
+                                       ("crattack",161,169),
+                                       ("crpain",170,173),
+                                       ("crdeath",174,178),
+                                       ("death1",179,184),
+                                       ("death2",185,190),
+                                       ("death3",191,198))
+                                       #198 frames
+
+######################################################
+# MD2 data structures
+######################################################
+class md2_point:
+       vertices=[]
+       lightnormalindex=0
+       binary_format="<3BB"
+       def __init__(self):
+               self.vertices=[0]*3
+               self.lightnormalindex=0
+       def save(self, file):
+               temp_data=[0]*4
+               temp_data[0]=self.vertices[0]
+               temp_data[1]=self.vertices[1]
+               temp_data[2]=self.vertices[2]
+               temp_data[3]=self.lightnormalindex
+               data=struct.pack(self.binary_format, temp_data[0], temp_data[1], temp_data[2], temp_data[3])
+               file.write(data)
+       def dump(self):
+               print "MD2 Point Structure"
+               print "vertex X: ", self.vertices[0]
+               print "vertex Y: ", self.vertices[1]
+               print "vertex Z: ", self.vertices[2]
+               print "lightnormalindex: ",self.lightnormalindex
+               print ""
+               
+class md2_face:
+       vertex_index=[]
+       texture_index=[]
+       binary_format="<3h3h"
+       def __init__(self):
+               self.vertex_index = [ 0, 0, 0 ]
+               self.texture_index = [ 0, 0, 0]
+       def save(self, file):
+               temp_data=[0]*6
+               #swap vertices around so they draw right
+               temp_data[0]=self.vertex_index[0]
+               temp_data[1]=self.vertex_index[2]
+               temp_data[2]=self.vertex_index[1]
+               #swap texture vertices around so they draw right
+               temp_data[3]=self.texture_index[0]
+               temp_data[4]=self.texture_index[2]
+               temp_data[5]=self.texture_index[1]
+               data=struct.pack(self.binary_format,temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5])
+               file.write(data)
+       def dump (self):
+               print "MD2 Face Structure"
+               print "vertex 1 index: ", self.vertex_index[0]
+               print "vertex 2 index: ", self.vertex_index[1]
+               print "vertex 3 index: ", self.vertex_index[2]
+               print "texture 1 index: ", self.texture_index[0]
+               print "texture 2 index: ", self.texture_index[1]
+               print "texture 3 index: ", self.texture_index[2]
+               print ""
+               
+class md2_tex_coord:
+       u=0
+       v=0
+       binary_format="<2h"
+       def __init__(self):
+               self.u=0
+               self.v=0
+       def save(self, file):
+               temp_data=[0]*2
+               temp_data[0]=self.u
+               temp_data[1]=self.v
+               data=struct.pack(self.binary_format, temp_data[0], temp_data[1])
+               file.write(data)
+       def dump (self):
+               print "MD2 Texture Coordinate Structure"
+               print "texture coordinate u: ",self.u
+               print "texture coordinate v: ",self.v
+               print ""
+               
+class md2_GL_command:
+       s=0.0
+       t=0.0
+       vert_index=0
+       binary_format="<2fi"
+       
+       def __init__(self):
+               self.s=0.0
+               self.t=0.0
+               vert_index=0
+       def save(self,file):
+               temp_data=[0]*3
+               temp_data[0]=float(self.s)
+               temp_data[1]=float(self.t)
+               temp_data[2]=self.vert_index
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2])
+               file.write(data)
+       def dump (self):
+               print "MD2 OpenGL Command"
+               print "s: ", self.s
+               print "t: ", self.t
+               print "Vertex Index: ", self.vert_index
+               print ""
+
+class md2_GL_cmd_list:
+       num=0
+       cmd_list=[]
+       binary_format="<i"
+       
+       def __init__(self):
+               self.num=0
+               self.cmd_list=[]
+       
+       def save(self,file):
+               data=struct.pack(self.binary_format, self.num)
+               file.write(data)
+               for cmd in self.cmd_list:
+                       cmd.save(file)
+       def dump(self):
+               print "MD2 OpenGL Command List"
+               print "number: ", self.num
+               for cmd in self.cmd_list:
+                       cmd.dump()
+               print ""
+
+class md2_skin:
+       name=""
+       binary_format="<64s"
+       def __init__(self):
+               self.name=""
+       def save(self, file):
+               temp_data=self.name
+               data=struct.pack(self.binary_format, temp_data)
+               file.write(data)
+       def dump (self):
+               print "MD2 Skin"
+               print "skin name: ",self.name
+               print ""
+               
+class md2_frame:
+       scale=[]
+       translate=[]
+       name=[]
+       vertices=[]
+       binary_format="<3f3f16s"
+
+       def __init__(self):
+               self.scale=[0.0]*3
+               self.translate=[0.0]*3
+               self.name=""
+               self.vertices=[]
+       def save(self, file):
+               temp_data=[0]*7
+               temp_data[0]=float(self.scale[0])
+               temp_data[1]=float(self.scale[1])
+               temp_data[2]=float(self.scale[2])
+               temp_data[3]=float(self.translate[0])
+               temp_data[4]=float(self.translate[1])
+               temp_data[5]=float(self.translate[2])
+               temp_data[6]=self.name
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6])
+               file.write(data)
+       def dump (self):
+               print "MD2 Frame"
+               print "scale x: ",self.scale[0]
+               print "scale y: ",self.scale[1]
+               print "scale z: ",self.scale[2]
+               print "translate x: ",self.translate[0]
+               print "translate y: ",self.translate[1]
+               print "translate z: ",self.translate[2]
+               print "name: ",self.name
+               print ""
+               
+class md2_obj:
+       #Header Structure
+       ident=0                         #int 0  This is used to identify the file
+       version=0                       #int 1  The version number of the file (Must be 8)
+       skin_width=0            #int 2  The skin width in pixels
+       skin_height=0           #int 3  The skin height in pixels
+       frame_size=0            #int 4  The size in bytes the frames are
+       num_skins=0                     #int 5  The number of skins associated with the model
+       num_vertices=0          #int 6  The number of vertices (constant for each frame)
+       num_tex_coords=0        #int 7  The number of texture coordinates
+       num_faces=0                     #int 8  The number of faces (polygons)
+       num_GL_commands=0       #int 9  The number of gl commands
+       num_frames=0            #int 10 The number of animation frames
+       offset_skins=0          #int 11 The offset in the file for the skin data
+       offset_tex_coords=0     #int 12 The offset in the file for the texture data
+       offset_faces=0          #int 13 The offset in the file for the face data
+       offset_frames=0         #int 14 The offset in the file for the frames data
+       offset_GL_commands=0#int 15     The offset in the file for the gl commands data
+       offset_end=0            #int 16 The end of the file offset
+       binary_format="<17i"  #little-endian (<), 17 integers (17i)
+       #md2 data objects
+       tex_coords=[]
+       faces=[]
+       frames=[]
+       skins=[]
+       GL_commands=[]
+       
+       def __init__ (self):
+               self.tex_coords=[]
+               self.faces=[]
+               self.frames=[]
+               self.skins=[]
+       def save(self, file):
+               temp_data=[0]*17
+               temp_data[0]=self.ident
+               temp_data[1]=self.version
+               temp_data[2]=self.skin_width
+               temp_data[3]=self.skin_height
+               temp_data[4]=self.frame_size
+               temp_data[5]=self.num_skins
+               temp_data[6]=self.num_vertices
+               temp_data[7]=self.num_tex_coords
+               temp_data[8]=self.num_faces
+               temp_data[9]=self.num_GL_commands
+               temp_data[10]=self.num_frames
+               temp_data[11]=self.offset_skins
+               temp_data[12]=self.offset_tex_coords
+               temp_data[13]=self.offset_faces
+               temp_data[14]=self.offset_frames
+               temp_data[15]=self.offset_GL_commands
+               temp_data[16]=self.offset_end
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6],temp_data[7],temp_data[8],temp_data[9],temp_data[10],temp_data[11],temp_data[12],temp_data[13],temp_data[14],temp_data[15],temp_data[16])
+               file.write(data)
+               #write the skin data
+               for skin in self.skins:
+                       skin.save(file)
+               #save the texture coordinates
+               for tex_coord in self.tex_coords:
+                       tex_coord.save(file)
+               #save the face info
+               for face in self.faces:
+                       face.save(file)
+               #save the frames
+               for frame in self.frames:
+                       frame.save(file)
+                       for vert in frame.vertices:
+                               vert.save(file)
+               #save the GL command List
+               for cmd in self.GL_commands:
+                       cmd.save(file)
+       def dump (self):
+               print "Header Information"
+               print "ident: ", self.ident
+               print "version: ", self.version
+               print "skin width: ", self.skin_width
+               print "skin height: ", self.skin_height
+               print "frame size: ", self.frame_size
+               print "number of skins: ", self.num_skins
+               print "number of texture coordinates: ", self.num_tex_coords
+               print "number of faces: ", self.num_faces
+               print "number of frames: ", self.num_frames
+               print "number of vertices: ", self.num_vertices
+               print "number of GL commands: ",self.num_GL_commands
+               print "offset skins: ", self.offset_skins
+               print "offset texture coordinates: ", self.offset_tex_coords
+               print "offset faces: ", self.offset_faces
+               print "offset frames: ",self.offset_frames
+               print "offset GL Commands: ",self.offset_GL_commands
+               print "offset end: ",self.offset_end
+               print ""
+
+######################################################
+# Validation
+######################################################
+def validation(object):
+       global user_frame_list
+       
+       #get access to the mesh data
+       mesh=object.getData(False, True) #get the object (not just name) and the Mesh, not NMesh
+
+       #check it's composed of only tri's      
+       result=0
+       for face in mesh.faces:
+               if len(face.verts)!=3:
+                       #select the face for future triangulation
+                       face.sel=1
+                       if result==0:  #first time we have this problem, don't pop-up a window every time it finds a quad
+                               print "Model not made entirely of triangles"
+                               result=Blender.Draw.PupMenu("Model not made entirely out of Triangles-Convert?%t|YES|NO")
+       if result==1:
+               mesh.quadToTriangle(0) #use closest verticies in breaking a quad
+       elif result==2:
+               return False #user will fix (I guess)
+
+       #check it has UV coordinates
+       if mesh.vertexUV==True:
+               print "Vertex UV not supported"
+               result=Blender.Draw.PupMenu("Vertex UV not suppored-Use Sticky UV%t|OK")
+               return False
+                       
+       elif mesh.faceUV==True:
+               for face in mesh.faces:
+                       if(len(face.uv)==3):
+                               pass
+                       else:
+                               print "Models vertices do not all have UV"
+                               result=Blender.Draw.PupMenu("Models vertices do not all have UV%t|OK")
+                               return False
+       
+       else:
+               print "Model does not have UV (face or vertex)"
+               result=Blender.Draw.PupMenu("Model does not have UV (face or vertex)%t|OK")
+               return False
+
+       #check it has only 1 associated texture map
+       last_face=""
+       last_face=mesh.faces[0].image
+       if last_face=="":
+               print "Model does not have a texture Map"
+               result=Blender.Draw.PupMenu("Model does not have a texture Map%t|OK")
+               return False
+
+       for face in mesh.faces:
+               mesh_image=face.image
+               if not mesh_image:
+                       print "Model has a face without a texture Map"
+                       result=Blender.Draw.PupMenu("Model has a face without a texture Map%t|OK")
+                       return False
+               if mesh_image!=last_face:
+                       print "Model has more than 1 texture map assigned"
+                       result=Blender.Draw.PupMenu("Model has more than 1 texture map assigned%t|OK")
+                       return False
+               
+       size=mesh_image.getSize()
+       #is this really what the user wants
+       if (size[0]!=256 or size[1]!=256):
+               print "Texture map size is non-standard (not 256x256), it is: ",size[0],"x",size[1]
+               result=Blender.Draw.PupMenu("Texture map size is non-standard (not 256x256), it is: "+size[0]+"x"+size[1]+": Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+
+       #verify frame list data
+       user_frame_list=get_frame_list()        
+       temp=user_frame_list[len(user_frame_list)-1]
+       temp_num_frames=temp[2]
+       
+       #verify tri/vert/frame counts are within MD2 standard
+       face_count=len(mesh.faces)
+       vert_count=len(mesh.verts)      
+       frame_count=temp_num_frames
+       
+       if face_count>MD2_MAX_TRIANGLES:
+               print "Number of triangles exceeds MD2 standard: ", face_count,">",MD2_MAX_TRIANGLES
+               result=Blender.Draw.PupMenu("Number of triangles exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       if vert_count>MD2_MAX_VERTICES:
+               print "Number of verticies exceeds MD2 standard",vert_count,">",MD2_MAX_VERTICES
+               result=Blender.Draw.PupMenu("Number of verticies exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       if frame_count>MD2_MAX_FRAMES:
+               print "Number of frames exceeds MD2 standard of",frame_count,">",MD2_MAX_FRAMES
+               result=Blender.Draw.PupMenu("Number of frames exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       #model is OK
+       return True
+
+######################################################
+# Fill MD2 data structure
+######################################################
+def fill_md2(md2, object):
+       global user_frame_list
+       #get a Mesh, not NMesh
+       mesh=object.getData(False, True)        
+       
+       #load up some intermediate data structures
+       tex_list={}
+       tex_count=0
+       #create the vertex list from the first frame
+       Blender.Set("curframe", 1)
+       
+       #header information
+       md2.ident=844121161
+       md2.version=8   
+       md2.num_vertices=len(mesh.verts)
+       md2.num_faces=len(mesh.faces)
+
+       #get the skin information
+       #use the first faces' image for the texture information
+       mesh_image=mesh.faces[0].image
+       size=mesh_image.getSize()
+       md2.skin_width=size[0]
+       md2.skin_height=size[1]
+       md2.num_skins=1
+       #add a skin node to the md2 data structure
+       md2.skins.append(md2_skin())
+       md2.skins[0].name=Blender.sys.basename(mesh_image.getFilename())
+
+       #put texture information in the md2 structure
+       #build UV coord dictionary (prevents double entries-saves space)
+       for face in mesh.faces:
+               for i in range(0,3):
+                       t=(face.uv[i])
+                       tex_key=(t[0],t[1])
+                       if not tex_list.has_key(tex_key):
+                               tex_list[tex_key]=tex_count
+                               tex_count+=1
+       md2.num_tex_coords=tex_count #each vert has its own UV coord
+
+       for this_tex in range (0, md2.num_tex_coords):
+               md2.tex_coords.append(md2_tex_coord())
+       for coord, index in tex_list.iteritems():
+               #md2.tex_coords.append(md2_tex_coord())
+               md2.tex_coords[index].u=int(coord[0]*md2.skin_width)
+               md2.tex_coords[index].v=int((1-coord[1])*md2.skin_height)
+
+       #put faces in the md2 structure
+       #for each face in the model
+       for this_face in range(0, md2.num_faces):
+               md2.faces.append(md2_face())
+               for i in range(0,3):
+                       #blender uses indexed vertexes so this works very well
+                       md2.faces[this_face].vertex_index[i]=mesh.faces[this_face].verts[i].index
+                       #lookup texture index in dictionary
+                       uv_coord=(mesh.faces[this_face].uv[i])
+                       tex_key=(uv_coord[0],uv_coord[1])
+                       tex_index=tex_list[tex_key]
+                       md2.faces[this_face].texture_index[i]=tex_index
+
+       #compute GL commands
+       md2.num_GL_commands=build_GL_commands(md2)
+
+       #get the frame data
+       #calculate 1 frame size  + (1 vert size*num_verts)
+       md2.frame_size=40+(md2.num_vertices*4) #in bytes
+       
+       #get the frame list
+       user_frame_list=get_frame_list()
+       if user_frame_list=="default":
+               md2.num_frames=198
+       else:
+               temp=user_frame_list[len(user_frame_list)-1]  #last item
+               md2.num_frames=temp[2] #last frame number
+       
+       #fill in each frame with frame info and all the vertex data for that frame
+       for frame_counter in range(0,md2.num_frames):
+               #add a frame
+               md2.frames.append(md2_frame())
+               #update the mesh objects vertex positions for the animation
+               Blender.Set("curframe", frame_counter)  #set blender to the correct frame
+               mesh.getFromObject(object.name)  #update the mesh to make verts current
+               
+#each frame has a scale and transform value that gets the vertex value between 0-255
+#since the scale and transform are the same for the all the verts in the frame, we only need
+#to figure this out once per frame
+               
+               #we need to start with the bounding box
+               bounding_box=object.getBoundBox() #uses the object, not the mesh data
+               #initialize with the first vertex for both min and max.  X and Y are swapped for MD2 format
+               point=bounding_box[0]
+               frame_min_x=point[1]
+               frame_max_x=point[1]
+               frame_min_y=point[0]
+               frame_max_y=point[0]
+               frame_min_z=point[2]
+               frame_max_z=point[2]
+               #find min/max values
+               for point in bounding_box:
+                       if frame_min_x>point[1]: frame_min_x=point[1]
+                       if frame_max_x<point[1]: frame_max_x=point[1]
+                       if frame_min_y>point[0]: frame_min_y=point[0]
+                       if frame_max_y<point[0]: frame_max_y=point[0]
+                       if frame_min_z>point[2]: frame_min_z=point[2]
+                       if frame_max_z<point[2]: frame_max_z=point[2]
+
+               #the scale is the difference between the min and max (on that axis) / 255
+               frame_scale_x=(frame_max_x-frame_min_x)/255
+               frame_scale_y=(frame_max_y-frame_min_y)/255
+               frame_scale_z=(frame_max_z-frame_min_z)/255
+               
+               #translate value of the mesh to center it on the origin
+               frame_trans_x=frame_min_x
+               frame_trans_y=frame_min_y
+               frame_trans_z=frame_min_z
+               
+               #fill in the data
+               md2.frames[frame_counter].scale=(-frame_scale_x, frame_scale_y, frame_scale_z)
+               md2.frames[frame_counter].translate=(-frame_trans_x, frame_trans_y, frame_trans_z)
+               
+               #now for the vertices
+               for vert_counter in range(0, md2.num_vertices):
+                       #add a vertex to the md2 structure
+                       md2.frames[frame_counter].vertices.append(md2_point())
+                       #figure out the new coords based on scale and transform
+                       #then translates the point so it's not less than 0
+                       #then scale it so it's between 0..255
+                       new_x=int((mesh.verts[vert_counter].co[1]-frame_trans_x)/frame_scale_x)
+                       new_y=int((mesh.verts[vert_counter].co[0]-frame_trans_y)/frame_scale_y)
+                       new_z=int((mesh.verts[vert_counter].co[2]-frame_trans_z)/frame_scale_z)
+                       #put them in the structure
+                       md2.frames[frame_counter].vertices[vert_counter].vertices=(new_x, new_y, new_z)
+
+                       #need to add the lookup table check here
+                       md2.frames[frame_counter].vertices[vert_counter].lightnormalindex=0
+                       
+       #output all the frame names-user_frame_list is loaded during the validation
+       for frame_set in user_frame_list:
+               for counter in range(frame_set[1]-1, frame_set[2]):
+                       md2.frames[counter].name=frame_set[0]+"_"+str(counter-frame_set[1]+2)
+
+       #compute these after everthing is loaded into a md2 structure
+       header_size=17*4 #17 integers, and each integer is 4 bytes
+       skin_size=64*md2.num_skins #64 char per skin * number of skins
+       tex_coord_size=4*md2.num_tex_coords #2 short * number of texture coords
+       face_size=12*md2.num_faces #3 shorts for vertex index, 3 shorts for tex index
+       frames_size=(((12+12+16)+(4*md2.num_vertices)) * md2.num_frames) #frame info+verts per frame*num frames
+       GL_command_size=md2.num_GL_commands*4 #each is an int or float, so 4 bytes per
+       
+       #fill in the info about offsets
+       md2.offset_skins=0+header_size
+       md2.offset_tex_coords=md2.offset_skins+skin_size
+       md2.offset_faces=md2.offset_tex_coords+tex_coord_size
+       md2.offset_frames=md2.offset_faces+face_size
+       md2.offset_GL_commands=md2.offset_frames+frames_size
+       md2.offset_end=md2.offset_GL_commands+GL_command_size
+
+######################################################
+# Get Frame List
+######################################################
+def get_frame_list():
+       global g_frame_filename
+       frame_list=[]
+
+       if g_frame_filename.val=="default":
+               return MD2_FRAME_NAME_LIST
+
+       else:
+       #check for file
+               if (Blender.sys.exists(g_frame_filename.val)==1):
+                       #open file and read it in
+                       file=open(g_frame_filename.val,"r")
+                       lines=file.readlines()
+                       file.close()
+
+                       #check header (first line)
+                       if lines[0]<>"# MD2 Frame Name List\n":
+                               print "its not a valid file"
+                               result=Blender.Draw.PupMenu("This is not a valid frame definition file-using default%t|OK")
+                               return MD2_FRAME_NAME_LIST
+                       else:
+                               #read in the data
+                               num_frames=0
+                               for counter in range(1, len(lines)):
+                                       current_line=lines[counter]
+                                       if current_line[0]=="#":
+                                               #found a comment
+                                               pass
+                                       else:
+                                               data=current_line.split()
+                                               frame_list.append([data[0],num_frames+1, num_frames+int(data[1])])
+                                               num_frames+=int(data[1])
+                               return frame_list
+               else:
+                       print "Cannot find file"
+                       result=Blender.Draw.PupMenu("Cannot find frame definion file-using default%t|OK")
+                       return MD2_FRAME_NAME_LIST
+
+######################################################
+# Tri-Strip/Tri-Fan functions
+######################################################
+def find_strip_length(md2, start_tri, start_vert):
+       #variables shared between fan and strip functions
+       global used
+       global strip_vert
+       global strip_st
+       global strip_tris
+       global strip_count
+
+       m1=m2=0
+       st1=st2=0
+       
+       used[start_tri]=2
+
+       last=start_tri
+
+       strip_vert[0]=md2.faces[last].vertex_index[start_vert%3]
+       strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3]
+       strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3]
+
+       strip_st[0]=md2.faces[last].texture_index[start_vert%3]
+       strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3]
+       strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       strip_tris[0]=start_tri
+       strip_count=1
+
+       m1=md2.faces[last].vertex_index[(start_vert+2)%3]
+       st1=md2.faces[last].texture_index[(start_vert+2)%3]
+       m2=md2.faces[last].vertex_index[(start_vert+1)%3]
+       st2=md2.faces[last].texture_index[(start_vert+1)%3]
+       
+       #look for matching triangle
+       check=start_tri+1
+       
+       for tri_counter in range(start_tri+1, md2.num_faces):
+               
+               for k in range(0,3):
+                       if md2.faces[check].vertex_index[k]!=m1:
+                               continue
+                       if md2.faces[check].texture_index[k]!=st1:
+                               continue
+                       if md2.faces[check].vertex_index[(k+1)%3]!=m2:
+                               continue
+                       if md2.faces[check].texture_index[(k+1)%3]!=st2:
+                               continue
+                       
+                       #if we can't use this triangle, this tri_strip is done
+                       if (used[tri_counter]!=0):
+                               for clear_counter in range(start_tri+1, md2.num_faces):
+                                       if used[clear_counter]==2:
+                                               used[clear_counter]=0
+                               return strip_count
+
+                       #new edge
+                       if (strip_count & 1):
+                               m2=md2.faces[check].vertex_index[(k+2)%3]
+                               st2=md2.faces[check].texture_index[(k+2)%3]
+                       else:
+                               m1=md2.faces[check].vertex_index[(k+2)%3]
+                               st1=md2.faces[check].texture_index[(k+2)%3]
+                       
+                       strip_vert[strip_count+2]=md2.faces[tri_counter].vertex_index[(k+2)%3]
+                       strip_st[strip_count+2]=md2.faces[tri_counter].texture_index[(k+2)%3]
+                       strip_tris[strip_count]=tri_counter
+                       strip_count+=1
+       
+                       used[tri_counter]=2
+               check+=1
+       return strip_count
+
+def find_fan_length(md2, start_tri, start_vert):
+       #variables shared between fan and strip functions
+       global used
+       global strip_vert
+       global strip_st
+       global strip_tris
+       global strip_count
+
+       m1=m2=0
+       st1=st2=0
+       
+       used[start_tri]=2
+
+       last=start_tri
+
+       strip_vert[0]=md2.faces[last].vertex_index[start_vert%3]
+       strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3]
+       strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3]
+       
+       strip_st[0]=md2.faces[last].texture_index[start_vert%3]
+       strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3]
+       strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       strip_tris[0]=start_tri
+       strip_count=1
+
+       m1=md2.faces[last].vertex_index[(start_vert+0)%3]
+       st1=md2.faces[last].texture_index[(start_vert+0)%3]
+       m2=md2.faces[last].vertex_index[(start_vert+2)%3]
+       st2=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       #look for matching triangle     
+       check=start_tri+1
+       for tri_counter in range(start_tri+1, md2.num_faces):
+               for k in range(0,3):
+                       if md2.faces[check].vertex_index[k]!=m1:
+                               continue
+                       if md2.faces[check].texture_index[k]!=st1:
+                               continue
+                       if md2.faces[check].vertex_index[(k+1)%3]!=m2:
+                               continue
+                       if md2.faces[check].texture_index[(k+1)%3]!=st2:
+                               continue
+                       
+                       #if we can't use this triangle, this tri_strip is done
+                       if (used[tri_counter]!=0):
+                               for clear_counter in range(start_tri+1, md2.num_faces):
+                                       if used[clear_counter]==2:
+                                               used[clear_counter]=0
+                               return strip_count
+
+                       #new edge
+                       m2=md2.faces[check].vertex_index[(k+2)%3]
+                       st2=md2.faces[check].texture_index[(k+2)%3]
+                       
+                       strip_vert[strip_count+2]=m2
+                       strip_st[strip_count+2]=st2
+                       strip_tris[strip_count]=tri_counter
+                       strip_count+=1
+       
+                       used[tri_counter]=2
+               check+=1
+       return strip_count
+
+
+######################################################
+# Globals for GL command list calculations
+######################################################
+used=[]
+strip_vert=0
+strip_st=0
+strip_tris=0
+strip_count=0
+
+######################################################
+# Build GL command List
+######################################################
+def build_GL_commands(md2):
+       #variables shared between fan and strip functions
+       global used
+       used=[0]*md2.num_faces
+       global strip_vert
+       strip_vert=[0]*128
+       global strip_st
+       strip_st=[0]*128
+       global strip_tris
+       strip_tris=[0]*128
+       global strip_count
+       strip_count=0
+       
+       #variables
+       num_commands=0
+       start_vert=0
+       fan_length=strip_length=0
+       length=best_length=0
+       best_type=0
+       best_vert=[0]*1024
+       best_st=[0]*1024
+       best_tris=[0]*1024
+       s=0.0
+       t=0.0
+       
+       for face_counter in range(0,md2.num_faces):
+               if used[face_counter]!=0: #don't evaluate a tri that's been used
+                       #print "found a used triangle: ", face_counter
+                       pass
+               else:
+                       best_length=0 #restart the counter
+                       #for each vertex index in this face
+                       for start_vert in range(0,3):
+                               fan_length=find_fan_length(md2, face_counter, start_vert)
+                               if (fan_length>best_length):
+                                       best_type=1
+                                       best_length=fan_length
+                                       for index in range (0, best_length+2):
+                                               best_st[index]=strip_st[index]
+                                               best_vert[index]=strip_vert[index]
+                                       for index in range(0, best_length):
+                                               best_tris[index]=strip_tris[index]
+                               
+                               strip_length=find_strip_length(md2, face_counter, start_vert)
+                               if (strip_length>best_length): 
+                                       best_type=0
+                                       best_length=strip_length
+                                       for index in range (0, best_length+2):
+                                               best_st[index]=strip_st[index]
+                                               best_vert[index]=strip_vert[index]
+                                       for index in range(0, best_length):
+                                               best_tris[index]=strip_tris[index]
+                       
+                       #mark the tris on the best strip/fan as used
+                       for used_counter in range (0, best_length):
+                               used[best_tris[used_counter]]=1
+       
+                       temp_cmdlist=md2_GL_cmd_list()
+                       #push the number of commands into the command stream
+                       if best_type==1:
+                               temp_cmdlist.num=best_length+2
+                               num_commands+=1
+                       else:   
+                               temp_cmdlist.num=(-(best_length+2))
+                               num_commands+=1
+                       for command_counter in range (0, best_length+2):
+                               #emit a vertex into the reorder buffer
+                               cmd=md2_GL_command()
+                               index=best_st[command_counter]
+                               #calc and put S/T coords in the structure
+                               s=md2.tex_coords[index].u
+                               t=md2.tex_coords[index].v
+                               s=(s+0.5)/md2.skin_width
+                               t=(t+0.5)/md2.skin_height
+                               cmd.s=s
+                               cmd.t=t
+                               cmd.vert_index=best_vert[command_counter]
+                               temp_cmdlist.cmd_list.append(cmd)
+                               num_commands+=3
+                       md2.GL_commands.append(temp_cmdlist)
+       
+       #end of list
+       temp_cmdlist=md2_GL_cmd_list()  
+       temp_cmdlist.num=0
+       md2.GL_commands.append(temp_cmdlist)  
+       num_commands+=1
+