Browse Source

Merge pull request #161 from blackberry-gaming/next-sgrenier

Next sgrenier
Sean Paul Taylor 14 years ago
parent
commit
b3e1eba79b
46 changed files with 877 additions and 584 deletions
  1. 1 1
      gameplay-encoder/gameplay-binary.txt
  2. 1 1
      gameplay-encoder/src/FBXSceneEncoder.cpp
  3. 2 2
      gameplay-encoder/src/Mesh.cpp
  4. 2 2
      gameplay-encoder/src/MeshSkin.cpp
  5. 2 0
      gameplay/gameplay.vcxproj
  6. 7 1
      gameplay/gameplay.vcxproj.filters
  7. 17 5
      gameplay/res/shaders/colored-specular.fsh
  8. 11 2
      gameplay/res/shaders/colored-specular.vsh
  9. 4 1
      gameplay/res/shaders/colored.fsh
  10. 5 2
      gameplay/res/shaders/diffuse-specular.fsh
  11. 9 11
      gameplay/res/shaders/diffuse-specular.vsh
  12. 4 1
      gameplay/res/shaders/diffuse.fsh
  13. 1 0
      gameplay/src/Base.h
  14. 2 1
      gameplay/src/Camera.cpp
  15. 4 0
      gameplay/src/Font.cpp
  16. 5 5
      gameplay/src/Font.h
  17. 7 7
      gameplay/src/Material.cpp
  18. 5 0
      gameplay/src/Mesh.cpp
  19. 10 0
      gameplay/src/Mesh.h
  20. 8 3
      gameplay/src/Model.cpp
  21. 11 2
      gameplay/src/Model.h
  22. 31 7
      gameplay/src/Node.cpp
  23. 17 1
      gameplay/src/Node.h
  24. 151 104
      gameplay/src/Package.cpp
  25. 49 29
      gameplay/src/Package.h
  26. 1 1
      gameplay/src/PhysicsConstraint.cpp
  27. 86 29
      gameplay/src/PhysicsController.cpp
  28. 47 4
      gameplay/src/PhysicsController.h
  29. 1 0
      gameplay/src/PhysicsFixedConstraint.cpp
  30. 1 1
      gameplay/src/PhysicsGenericConstraint.cpp
  31. 1 1
      gameplay/src/PhysicsHingeConstraint.cpp
  32. 1 0
      gameplay/src/PhysicsMotionState.cpp
  33. 1 0
      gameplay/src/PhysicsMotionState.h
  34. 0 1
      gameplay/src/PhysicsRigidBody.cpp
  35. 15 0
      gameplay/src/PhysicsRigidBody.h
  36. 11 1
      gameplay/src/PhysicsRigidBody.inl
  37. 1 1
      gameplay/src/PhysicsSocketConstraint.cpp
  38. 1 1
      gameplay/src/PhysicsSpringConstraint.cpp
  39. 3 5
      gameplay/src/Properties.cpp
  40. 16 0
      gameplay/src/RenderState.cpp
  41. 10 0
      gameplay/src/RenderState.h
  42. 1 1
      gameplay/src/Scene.cpp
  43. 1 1
      gameplay/src/Scene.h
  44. 284 323
      gameplay/src/SceneLoader.cpp
  45. 28 26
      gameplay/src/SceneLoader.h
  46. 1 0
      gameplay/src/gameplay.h

+ 1 - 1
gameplay-encoder/gameplay-binary.txt

@@ -198,9 +198,9 @@ Reference
 34->Mesh
                 vertexFormat            VertexElement[] { enum VertexUsage usage, unint size }
                 vertices                byte[]
-                parts                   MeshPart[]
                 boundingBox             BoundingBox { float[3] min, float[3] max }
                 boundingSphere          BoundingSphere { float[3] center, float radius }
+                parts                   MeshPart[]
 ------------------------------------------------------------------------------------------------------
 35->MeshPart
                 primitiveType           enum PrimitiveType

+ 1 - 1
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -269,7 +269,7 @@ void FBXSceneEncoder::loadAnimationChannels(KFbxAnimLayer* animLayer, KFbxNode*
     // TODO: Ignore properties that are not animated (scale, rotation, translation)
     // This should result in only one animation channel per animated node.
 
-    float startTime = FLT_MAX, stopTime = -1.0f, frameRate = FLT_MIN;
+    float startTime = FLT_MAX, stopTime = -1.0f, frameRate = -FLT_MAX;
     bool tx = false, ty = false, tz = false, rx = false, ry = false, rz = false, sx = false, sy = false, sz = false;
     KFbxAnimCurve* animCurve = NULL;
     animCurve = fbxNode->LclTranslation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_T_X);

+ 2 - 2
gameplay-encoder/src/Mesh.cpp

@@ -156,7 +156,7 @@ void Mesh::generateHeightmap(const char* filename)
     float* heights = new float[width * height];
     int index = 0;
     float minHeight = FLT_MAX;
-    float maxHeight = FLT_MIN;
+    float maxHeight = -FLT_MAX;
     for (int z = minZ; z <= maxZ; z++)
     {
         rayOrigin.z = (float)z;
@@ -390,7 +390,7 @@ void Mesh::computeBounds()
     }
 
     bounds.min.x = bounds.min.y = bounds.min.z = FLT_MAX;
-    bounds.max.x = bounds.max.y = bounds.max.z = FLT_MIN;
+    bounds.max.x = bounds.max.y = bounds.max.z = -FLT_MAX;
     bounds.center.x = bounds.center.y = bounds.center.z = 0.0f;
     bounds.radius = 0.0f;
 

+ 2 - 2
gameplay-encoder/src/MeshSkin.cpp

@@ -225,7 +225,7 @@ void MeshSkin::computeBounds()
         vertices.clear();
         BoundingVolume jointBounds;
         jointBounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
-        jointBounds.max.set(FLT_MIN, FLT_MIN, FLT_MIN);
+        jointBounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
         for (unsigned int j = 0; j < vertexCount; ++j)
         {
             const Vertex& v = _mesh->getVertex(j);
@@ -354,7 +354,7 @@ void MeshSkin::computeBounds()
     Matrix temp;
     Matrix* jointTransforms = new Matrix[jointCount];
     _mesh->bounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
-    _mesh->bounds.max.set(FLT_MIN, FLT_MIN, FLT_MIN);
+    _mesh->bounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
     _mesh->bounds.center.set(0, 0, 0);
     _mesh->bounds.radius = 0;
     Vector3 skinnedPos;

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -61,6 +61,7 @@
     <ClCompile Include="src\Node.cpp" />
     <ClCompile Include="src\Package.cpp" />
     <ClCompile Include="src\ParticleEmitter.cpp" />
+    <ClCompile Include="src\PhysicsCharacter.cpp" />
     <ClCompile Include="src\PhysicsConstraint.cpp" />
     <ClCompile Include="src\PhysicsController.cpp" />
     <ClCompile Include="src\PhysicsFixedConstraint.cpp" />
@@ -148,6 +149,7 @@
     <ClInclude Include="src\Node.h" />
     <ClInclude Include="src\Package.h" />
     <ClInclude Include="src\ParticleEmitter.h" />
+    <ClInclude Include="src\PhysicsCharacter.h" />
     <ClInclude Include="src\PhysicsConstraint.h" />
     <ClInclude Include="src\PhysicsController.h" />
     <ClInclude Include="src\PhysicsFixedConstraint.h" />

+ 7 - 1
gameplay/gameplay.vcxproj.filters

@@ -264,6 +264,9 @@
     <ClCompile Include="src\TextBox.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\PhysicsCharacter.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -518,6 +521,9 @@
     <ClInclude Include="src\TextBox.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\PhysicsCharacter.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -652,4 +658,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

+ 17 - 5
gameplay/res/shaders/colored-specular.fsh

@@ -5,12 +5,17 @@ precision highp float;
 // Uniforms
 uniform vec3 u_lightColor;                      // Light color
 uniform vec3 u_ambientColor;                    // Ambient color
-uniform float u_specularExponent;               // Specular exponent or shininess property.
+uniform float u_specularExponent;               // Specular exponent or shininess property
+#if !defined(VERTEX_COLOR)
 uniform vec4 u_diffuseColor;                    // Diffuse color
+#endif
 
 // Inputs
-varying vec3 v_normalVector;                    // NormalVector in view space.
+varying vec3 v_normalVector;                    // NormalVector in view space
 varying vec3 v_cameraDirection;                 // Camera direction
+#if defined(VERTEX_COLOR)
+varying vec4 v_color;							// Vertex color
+#endif
 
 // Global variables
 vec4 _baseColor;                                // Base color
@@ -24,7 +29,10 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = abs(dot(normalVector, lightDirection));
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.25f; // simulate light bounce at partial intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 
@@ -103,8 +111,12 @@ void applyLight()
 
 void main()
 {
-    // Fetch diffuse color from texture.
-    _baseColor = u_diffuseColor;
+    // Set base diffuse color
+#if defined(VERTEX_COLOR)
+	_baseColor = v_color;
+#else
+	_baseColor = u_diffuseColor;
+#endif
 
     // Apply light
     applyLight();

+ 11 - 2
gameplay/res/shaders/colored-specular.vsh

@@ -7,11 +7,16 @@ uniform vec3 u_cameraPosition;                      // Position of the camera.
 // Inputs
 attribute vec4 a_position;                          // Vertex Position (x, y, z, w)
 attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
-attribute vec2 a_texCoord;                          // Vertex Texture Coordinate (u, v)
+#if defined(VERTEX_COLOR)
+attribute vec4 a_color;
+#endif
 
 // Outputs
-varying vec3 v_normalVector;                        // NormalVector in view space.
+varying vec3 v_normalVector;                        // NormalVector in view space
 varying vec3 v_cameraDirection;                     // Camera direction
+#if defined(VERTEX_COLOR)
+varying vec4 v_color;								// Vertex color
+#endif
 
 #if defined(SKINNING)
 
@@ -189,6 +194,10 @@ void main()
     vec4 positionWorldSpace = u_worldMatrix * position;
     v_cameraDirection = u_cameraPosition - positionWorldSpace.xyz;
 
+#if defined(VERTEX_COLOR)
+	v_color = a_color;
+#endif
+
     // Apply light.
     applyLight(position);
 }

+ 4 - 1
gameplay/res/shaders/colored.fsh

@@ -21,7 +21,10 @@ void lighting(vec3 normalVector, vec3 lightDirection, float attenuation)
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.5f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 }

+ 5 - 2
gameplay/res/shaders/diffuse-specular.fsh

@@ -25,12 +25,15 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.25f; // simulate light bounce at partial intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 
     // Specular
-    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    vec3 halfVector = normalize(lightDirection + cameraDirection);
     float specularIntensity = attenuation * max(0.0, pow(dot(normalVector, halfVector), u_specularExponent));
     specularIntensity = max(0.0, specularIntensity);
     _specularColor = u_lightColor * _baseColor.rgb * specularIntensity;

+ 9 - 11
gameplay/res/shaders/diffuse-specular.vsh

@@ -1,8 +1,8 @@
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;           // Matrix to transform a position to clip space.
 uniform mat4 u_inverseTransposeWorldViewMatrix;     // Matrix to transform a normal to view space.
-uniform mat4 u_worldMatrix;                         // Matrix to tranform a position to world space.
-uniform vec3 u_cameraPosition;                      // Position of the camera.
+uniform mat4 u_worldViewMatrix;                     // Matrix to tranform a position to view space.
+uniform vec3 u_cameraPosition;                      // Position of the camera in view space.
 
 // Inputs
 attribute vec4 a_position;                          // Vertex Position (x, y, z, w)
@@ -117,7 +117,6 @@ vec3 getNormal()
 
 #if defined(POINT_LIGHT)
 
-uniform mat4 u_worldViewMatrix;                         // Matrix to tranform a position to view space.
 uniform vec3 u_pointLightPosition;                      // Position
 uniform float u_pointLightRangeInverse;                 // Inverse of light range.
 varying vec4 v_vertexToPointLightDirection;             // Light direction w.r.t current vertex.
@@ -141,7 +140,6 @@ void applyLight(vec4 position)
 
 #elif defined(SPOT_LIGHT)
 
-uniform mat4 u_worldViewMatrix;                         // Matrix to tranform a position to view space.
 uniform vec3 u_spotLightPosition;                       // Position
 uniform float u_spotLightRangeInverse;                  // Inverse of light range.
 varying vec3 v_vertexToSpotLightDirection;              // Light direction w.r.t current vertex.
@@ -179,18 +177,18 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * position;
 
     // Transform normal to view space.
-    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz,
-                                                u_inverseTransposeWorldViewMatrix[1].xyz,
-                                                u_inverseTransposeWorldViewMatrix[2].xyz);
-    v_normalVector = inverseTransposeWorldViewMatrix * normal;
+    mat3 normalMatrix = mat3(u_inverseTransposeWorldViewMatrix[0].xyz,
+                             u_inverseTransposeWorldViewMatrix[1].xyz,
+                             u_inverseTransposeWorldViewMatrix[2].xyz);
+    v_normalVector = normalMatrix * normal;
 
     // Compute the camera direction.
-    vec4 positionWorldSpace = u_worldMatrix * position;
+    vec4 positionWorldSpace = u_worldViewMatrix * position;
     v_cameraDirection = u_cameraPosition - positionWorldSpace.xyz;
 
     // Apply light.
     applyLight(position);
 
-    // Pass on the texture coordinates to Fragment shader.
+	// Pass on the texture coordinates to Fragment shader.
     v_texCoord = a_texCoord;
-}
+}

+ 4 - 1
gameplay/res/shaders/diffuse.fsh

@@ -22,7 +22,10 @@ void lighting(vec3 normalVector, vec3 lightDirection, float attenuation)
     _ambientColor = _baseColor.rgb * u_ambientColor;
 
     // Diffuse
-    float diffuseIntensity = attenuation * max(0.0, dot(normalVector, lightDirection));
+	float ddot = dot(normalVector, lightDirection);
+	if (ddot < 0)
+		ddot = abs(ddot) * 0.5f; // simulate light bounce at half intensity
+    float diffuseIntensity = attenuation * ddot;
     diffuseIntensity = max(0.0, diffuseIntensity);
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 }

+ 1 - 0
gameplay/src/Base.h

@@ -103,6 +103,7 @@ extern void printError(const char* format, ...);
 
 // Bullet Physics
 #include <btBulletDynamicsCommon.h>
+#include <BulletCollision/CollisionDispatch/btGhostObject.h>
 #define BV(v) (btVector3((v).x, (v).y, (v).z))
 #define BQ(q) (btQuaternion((q).x, (q).y, (q).z, (q).w))
 

+ 2 - 1
gameplay/src/Camera.cpp

@@ -2,6 +2,8 @@
 #include "Camera.h"
 #include "Game.h"
 #include "Node.h"
+#include "Game.h"
+#include "PhysicsController.h"
 
 // Camera dirty bits
 #define CAMERA_DIRTY_VIEW 1
@@ -341,7 +343,6 @@ void Camera::pickRay(const Viewport* viewport, float x, float y, Ray* dst)
     dst->set(nearPoint, direction);
 }
 
-
 void Camera::transformChanged(Transform* transform, long cookie)
 {
     _dirtyBits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;

+ 4 - 0
gameplay/src/Font.cpp

@@ -176,6 +176,8 @@ void Font::begin()
 
 void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 {
+    if (size == 0)
+        size = _size;
     float scale = (float)size / _size;
     const char* cursor = NULL;
 
@@ -289,6 +291,8 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 
 void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft)
 {
+    if (size == 0)
+        size = _size;
     float scale = (float)size / _size;
     const char* token = text;
     const int length = strlen(text);

+ 5 - 5
gameplay/src/Font.h

@@ -132,9 +132,9 @@ public:
      * @param x The viewport x position to draw text at.
      * @param y The viewport y position to draw text at.
      * @param color The color of text.
-     * @param size The size to draw text.
+     * @param size The size to draw text (0 for default size).
      */
-    void drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft = false);
+    void drawText(const char* text, int x, int y, const Vector4& color, unsigned int size = 0, bool rightToLeft = false);
 
     /**
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
@@ -143,13 +143,13 @@ public:
      * @param text The text to draw.
      * @param clip The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param color The color of text.
-     * @param size The size to draw text.
+     * @param size The size to draw text (0 for default size).
      * @param justify Justification of text within the viewport.
      * @param wrap Wraps text to fit within the width of the viewport if true.
      * @param rightToLeft
      */
-    void drawText(const char* text, const Rectangle& clip, const Vector4& color, unsigned int size,
-                  Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+    void drawText(const char* text, const Rectangle& clip, const Vector4& color,
+        unsigned int size = 0, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
     /**
      * Measures a string's width and height without alignment, wrapping or clipping.

+ 7 - 7
gameplay/src/Material.cpp

@@ -212,17 +212,17 @@ bool Material::loadPass(Technique* technique, Properties* passProperties)
     const char* fragmentShaderPath = passProperties->getString("fragmentShader");
     assert(fragmentShaderPath);
     const char* defines = passProperties->getString("defines");
-    std::string define = "";
+    std::string define;
     if (defines != NULL)
     {
-        char* token = strtok((char*)defines, ";");
-        while (token)
+        define = defines;
+        define.insert(0, "#define ");
+        unsigned int pos;
+        while ((pos = define.find(';')) != std::string::npos)
         {
-            define += "#define ";
-            define += token;
-            define += "\n";
-            token = strtok(NULL, ";");
+            define.replace(pos, 1, "\n#define ");
         }
+        define += "\n";
     }
 
     // Create the pass

+ 5 - 0
gameplay/src/Mesh.cpp

@@ -235,6 +235,11 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
     return mesh;
 }
 
+const char* Mesh::getUrl() const
+{
+    return _url.c_str();
+}
+
 const VertexFormat& Mesh::getVertexFormat() const
 {
     return _vertexFormat;

+ 10 - 0
gameplay/src/Mesh.h

@@ -123,6 +123,15 @@ public:
      */
     static Mesh* createBoundingBox(const BoundingBox& box);
 
+    /**
+     * Returns a URL from which the mesh was loaded from.
+     *
+     * For meshes loaded from a Package, this URL will point
+     * to the file and ID of the mesh within the package. For
+     * all other meshes, an empty string will be returned.
+     */
+    const char* getUrl() const;
+
     /**
      * Gets the vertex format for the mesh.
      *
@@ -295,6 +304,7 @@ private:
      */
     Mesh(const Mesh& copy);
 
+    std::string _url;
     const VertexFormat _vertexFormat;
     unsigned int _vertexCount;
     VertexBufferHandle _vertexBuffer;

+ 8 - 3
gameplay/src/Model.cpp

@@ -190,6 +190,11 @@ Material* Model::setMaterial(const char* materialPath, int partIndex)
     return material;
 }
 
+bool Model::hasPartMaterial(unsigned int partIndex) const
+{
+    return (partIndex >= 0 && partIndex < _partCount && _partMaterials && _partMaterials[partIndex]);
+}
+
 MeshSkin* Model::getSkin()
 {
     return _skin;
@@ -276,15 +281,15 @@ void Model::draw(bool wireframe)
             MeshPart* part = _mesh->getPart(i);
 
             // Get the material for this mesh part.
-            Material* material;
-            if (_partMaterials && i < _partCount && _partMaterials[i])
+            Material* material = getMaterial(i);
+            /*if (_partMaterials && i < _partCount && _partMaterials[i])
             {
                 material = _partMaterials[i]; // Use part material
             }
             else
             {
                 material = _material; // Use shared material
-            }
+            }*/
 
             if (material)
             {

+ 11 - 2
gameplay/src/Model.h

@@ -109,6 +109,15 @@ public:
      */
     Material* setMaterial(const char* materialPath, int partIndex = -1);
 
+    /**
+     * Determines if a custom (non-shared) material is set for the specified part index.
+     *
+     * @param partIndex MeshPart index.
+     *
+     * @return True if a custom MeshPart material is set for the specified index, false otherwise.
+     */
+    bool hasPartMaterial(unsigned int partIndex) const;
+
     /**
      * Returns the MeshSkin.
      * 
@@ -146,13 +155,13 @@ private:
      * Destructor. Hidden use release() instead.
      */
     ~Model();
-
+
     /**
      * Sets the MeshSkin for this model.
      * 
      * @param skin The MeshSkin for this model.
      */
-    void setSkin(MeshSkin* skin);
+    void setSkin(MeshSkin* skin);
 
     /**
      * Sets the node that is associated with this model.

+ 31 - 7
gameplay/src/Node.cpp

@@ -317,7 +317,7 @@ const Matrix& Node::getWorldMatrix() const
 const Matrix& Node::getWorldViewMatrix() const
 {
     static Matrix worldView;
-    
+
     Matrix::multiply(getViewMatrix(), getWorldMatrix(), &worldView);
 
     return worldView;
@@ -326,18 +326,21 @@ const Matrix& Node::getWorldViewMatrix() const
 const Matrix& Node::getInverseTransposeWorldViewMatrix() const
 {
     static Matrix invTransWorldView;
-
-    // Assume the matrix is always dirty since the camera is moving
-    // almost every frame in most games.
-    //    
-    // TODO: Optimize here to NOT calculate the inverse transpose if the matrix is orthogonal.
     Matrix::multiply(getViewMatrix(), getWorldMatrix(), &invTransWorldView);
     invTransWorldView.invert();
     invTransWorldView.transpose();
-
     return invTransWorldView;
 }
 
+const Matrix& Node::getInverseTransposeWorldMatrix() const
+{
+    static Matrix invTransWorld;
+    invTransWorld = getWorldMatrix();
+    invTransWorld.invert();
+    invTransWorld.transpose();
+    return invTransWorld;
+}
+
 const Matrix& Node::getViewMatrix() const
 {
     Scene* scene = getScene();
@@ -444,6 +447,8 @@ Vector3 Node::getForwardVectorView() const
     Vector3 vector;
     getWorldMatrix().getForwardVector(&vector);
     getViewMatrix().transformVector(&vector);
+    //getForwardVector(&vector);
+    //getWorldViewMatrix().transformVector(&vector);
     return vector;
 }
 
@@ -466,6 +471,25 @@ Vector3 Node::getActiveCameraTranslationWorld() const
     return Vector3::zero();
 }
 
+Vector3 Node::getActiveCameraTranslationView() const
+{
+    Scene* scene = getScene();
+    if (scene)
+    {
+        Camera* camera = scene->getActiveCamera();
+        if (camera)
+        {
+            Node* cameraNode = camera->getNode();
+            if (cameraNode)
+            {
+                return cameraNode->getTranslationView();
+            }
+        }
+    }
+
+    return Vector3::zero();
+}
+
 void Node::hierarchyChanged()
 {
     // When our hierarchy changes our world transform is affected, so we must dirty it.

+ 17 - 1
gameplay/src/Node.h

@@ -173,10 +173,19 @@ public:
      */
     const Matrix& getWorldViewMatrix() const;
 
+    /**
+     * Gets the inverse transpose world matrix corresponding to this node.
+     *
+     * This matrix is typically used to transform normal vectors into world space.
+     *
+     * @return The inverse world matrix of this node.
+     */
+    const Matrix& getInverseTransposeWorldMatrix() const;
+
     /**
      * Gets the inverse transpose world view matrix corresponding to this node.
      *
-     * This matrix is typically used to transform normal vectors.
+     * This matrix is typically used to transform normal vectors into view space.
      *
      * @return The inverse world view matrix of this node.
      */
@@ -261,6 +270,13 @@ public:
      */
     Vector3 getActiveCameraTranslationWorld() const;
 
+    /**
+     * Returns the view-space translation vector of the currently active camera for this node's scene.
+     *
+     * @return The translation vector of the scene's active camera, in view-space.
+     */
+    Vector3 getActiveCameraTranslationView() const;
+
     /**
      * Returns the pointer to this node's camera.
      *

+ 151 - 104
gameplay/src/Package.cpp

@@ -3,7 +3,6 @@
 #include "FileSystem.h"
 #include "MeshPart.h"
 #include "Scene.h"
-#include "SceneLoader.h"
 #include "Joint.h"
 
 #define GPB_PACKAGE_VERSION_MAJOR 1
@@ -319,11 +318,6 @@ bool Package::readMatrix(float* m)
 }
 
 Scene* Package::loadScene(const char* id)
-{
-    return loadScene(id, NULL);
-}
-
-Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesWithMeshRB)
 {
     clearLoadSession();
 
@@ -355,7 +349,7 @@ Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesW
         // Read each child directly into the scene
         for (unsigned int i = 0; i < childrenCount; i++)
         {
-            Node* node = readNode(scene, NULL, nodesWithMeshRB);
+            Node* node = readNode(scene, NULL);
             if (node)
             {
                 scene->addNode(node);
@@ -417,17 +411,12 @@ Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesW
 }
 
 Node* Package::loadNode(const char* id)
-{
-    return loadNode(id, false);
-}
-
-Node* Package::loadNode(const char* id, bool loadWithMeshRBSupport)
 {
     assert(id);
 
     clearLoadSession();
 
-    Node* node = loadNode(id, NULL, NULL, loadWithMeshRBSupport);
+    Node* node = loadNode(id, NULL, NULL);
    
     if (node)
     {
@@ -437,7 +426,7 @@ Node* Package::loadNode(const char* id, bool loadWithMeshRBSupport)
     return node;
 }
 
-Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport)
+Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
 {
     assert(id);
 
@@ -463,22 +452,13 @@ Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext,
             return NULL;
         }
 
-        if (loadWithMeshRBSupport)
-        {
-            std::vector<std::string> nodesWithMeshRBSupport;
-            nodesWithMeshRBSupport.push_back(id);
-            node = readNode(sceneContext, nodeContext, &nodesWithMeshRBSupport);
-        }
-        else
-        {
-            node = readNode(sceneContext, nodeContext, NULL);
-        }
+        node = readNode(sceneContext, nodeContext);
     }
 
     return node;
 }
 
-Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vector<std::string>* nodesWithMeshRB)
+Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
 {
     const char* id = getIdFromOffset();
 
@@ -529,7 +509,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vecto
         // Read each child
         for (unsigned int i = 0; i < childrenCount; i++)
         {
-            Node* child = readNode(sceneContext, nodeContext, nodesWithMeshRB);
+            Node* child = readNode(sceneContext, nodeContext);
             if (child)
             {
                 node->addChild(child);
@@ -554,23 +534,8 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vecto
         SAFE_RELEASE(light);
     }
 
-    // Check if this node's id is in the list of nodes to be loaded with
-    // mesh rigid body support so that when we load the model we keep the proper data.
-    bool loadWithMeshRBSupport = false;
-    if (nodesWithMeshRB)
-    {
-        for (unsigned int i = 0; i < nodesWithMeshRB->size(); i++)
-        {
-            if (strcmp((*nodesWithMeshRB)[i].c_str(), id) == 0)
-            {
-                loadWithMeshRBSupport = true;
-                break;
-            }
-        }
-    }
-
     // Read model
-    Model* model = readModel(sceneContext, nodeContext, loadWithMeshRBSupport, node->getId());
+    Model* model = readModel(sceneContext, nodeContext, node->getId());
     if (model)
     {
         node->setModel(model);
@@ -701,14 +666,14 @@ Light* Package::readLight()
     return light;
 }
 
-Model* Package::readModel(Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport, const char* nodeId)
+Model* Package::readModel(Scene* sceneContext, Node* nodeContext, const char* nodeId)
 {
     // Read mesh
     Mesh* mesh = NULL;
     std::string xref = readString(_file);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
-        mesh = loadMesh(xref.c_str() + 1, loadWithMeshRBSupport, nodeId);
+        mesh = loadMesh(xref.c_str() + 1, nodeId);
         if (mesh)
         {
             Model* model = Model::create(mesh);
@@ -836,7 +801,7 @@ void Package::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             {
                 jointId = jointId.substr(1, jointId.length() - 1);
 
-                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext, false);
+                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext);
                 if (n && n->getType() == Node::JOINT)
                 {
                     Joint* joint = static_cast<Joint*>(n);
@@ -1012,12 +977,12 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
 
 Mesh* Package::loadMesh(const char* id)
 {
-    return loadMesh(id, false, NULL);
+    return loadMesh(id, false);
 }
 
-Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char* nodeId)
+Mesh* Package::loadMesh(const char* id, const char* nodeId)
 {
-    // save the file position
+    // Save the file position
     long position = ftell(_file);
 
     // Seek to the specified Mesh
@@ -1027,6 +992,56 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
         return NULL;
     }
 
+    // Read mesh data
+    MeshData* meshData = readMeshData();
+    if (meshData == NULL)
+    {
+        return NULL;
+    }
+
+    // Create Mesh
+    Mesh* mesh = Mesh::createMesh(meshData->vertexFormat, meshData->vertexCount, false);
+    if (mesh == NULL)
+    {
+        LOG_ERROR_VARG("Failed to create mesh: %s", id);
+        SAFE_DELETE_ARRAY(meshData);
+        return NULL;
+    }
+
+    mesh->_url = _path;
+    mesh->_url += "#";
+    mesh->_url += id;
+
+    mesh->setVertexData(meshData->vertexData, 0, meshData->vertexCount);
+
+    mesh->_boundingBox.set(meshData->boundingBox);
+    mesh->_boundingSphere.set(meshData->boundingSphere);
+
+    // Create mesh parts
+    for (unsigned int i = 0; i < meshData->parts.size(); ++i)
+    {
+        MeshPartData* partData = meshData->parts[i];
+
+        MeshPart* part = mesh->addPart(partData->primitiveType, partData->indexFormat, partData->indexCount, false);
+        if (part == NULL)
+        {
+            LOG_ERROR_VARG("Failed to create mesh part (i=%d): %s", i, id);
+            SAFE_DELETE(meshData);
+            return NULL;
+        }
+        part->setIndexData(partData->indexData, 0, partData->indexCount);
+    }
+
+    SAFE_DELETE(meshData);
+
+    // Restore file pointer
+    fseek(_file, position, SEEK_SET);
+
+    return mesh;
+}
+
+Package::MeshData* Package::readMeshData()
+{
     // Read vertex format/elements
     unsigned int vertexElementCount;
     if (fread(&vertexElementCount, 4, 1, _file) != 1 || vertexElementCount < 1)
@@ -1047,60 +1062,42 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
         vertexElements[i].size = vSize;
     }
 
-    // Create VertexFormat
-    VertexFormat vertexFormat(vertexElements, vertexElementCount);
+    MeshData* meshData = new MeshData(VertexFormat(vertexElements, vertexElementCount));
+
     SAFE_DELETE_ARRAY(vertexElements);
 
     // Read vertex data
     unsigned int vertexByteCount;
     if (fread(&vertexByteCount, 4, 1, _file) != 1 || vertexByteCount == 0)
     {
+        SAFE_DELETE(meshData);
         return NULL;
     }
-    unsigned char* vertexData = new unsigned char[vertexByteCount];
-    if (fread(vertexData, 1, vertexByteCount, _file) != vertexByteCount)
+    meshData->vertexCount = vertexByteCount / meshData->vertexFormat.getVertexSize();
+    meshData->vertexData = new unsigned char[vertexByteCount];
+    if (fread(meshData->vertexData, 1, vertexByteCount, _file) != vertexByteCount)
     {
-        LOG_ERROR_VARG("Failed to read %d vertex data bytes for mesh: %s", vertexByteCount, id);
+        SAFE_DELETE(meshData);
         return NULL;
     }
 
     // Read mesh bounds (bounding box and bounding sphere)
-    Vector3 boundsMin, boundsMax, boundsCenter;
-    float boundsRadius = 0.0f;
-    if (fread(&boundsMin.x, 4, 3, _file) != 3 || fread(&boundsMax.x, 4, 3, _file) != 3)
-    {
-        LOG_ERROR_VARG("Failed to read bounding box for mesh: %s", id);
-        return NULL;
-    }
-    if (fread(&boundsCenter.x, 4, 3, _file) != 3 || fread(&boundsRadius, 4, 1, _file) != 1)
+    if (fread(&meshData->boundingBox.min.x, 4, 3, _file) != 3 || fread(&meshData->boundingBox.max.x, 4, 3, _file) != 3)
     {
-        LOG_ERROR_VARG("Failed to read bounding sphere for mesh: %s", id);
+        SAFE_DELETE(meshData);
         return NULL;
     }
-
-    // Create Mesh
-    int vertexCount = vertexByteCount / vertexFormat.getVertexSize();
-    Mesh* mesh = Mesh::createMesh(vertexFormat, vertexCount, false);
-    if (mesh == NULL)
+    if (fread(&meshData->boundingSphere.center.x, 4, 3, _file) != 3 || fread(&meshData->boundingSphere.radius, 4, 1, _file) != 1)
     {
-        LOG_ERROR_VARG("Failed to create mesh: %s", id);
-        SAFE_DELETE_ARRAY(vertexData);
+        SAFE_DELETE(meshData);
         return NULL;
     }
-    mesh->setVertexData(vertexData, 0, vertexCount);
-    if (loadWithMeshRBSupport)
-        SceneLoader::addMeshRigidBodyData(_path, nodeId, mesh, vertexData, vertexByteCount);
-    SAFE_DELETE_ARRAY(vertexData);
-
-    // Set mesh bounding volumes
-    mesh->_boundingBox.set(boundsMin, boundsMax);
-    mesh->_boundingSphere.set(boundsCenter, boundsRadius);
 
     // Read mesh parts
     unsigned int meshPartCount;
     if (fread(&meshPartCount, 4, 1, _file) != 1)
     {
-        SAFE_RELEASE(mesh);
+        SAFE_DELETE(meshData);
         return NULL;
     }
     for (unsigned int i = 0; i < meshPartCount; ++i)
@@ -1111,23 +1108,18 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
             fread(&iFormat, 4, 1, _file) != 1 ||
             fread(&iByteCount, 4, 1, _file) != 1)
         {
-            LOG_ERROR_VARG("Failed to read mesh part (i=%d): %s", i, id);
-            SAFE_RELEASE(mesh);
+            SAFE_DELETE(meshData);
             return NULL;
         }
 
-        unsigned char* indexData = new unsigned char[iByteCount];
-        if (fread(indexData, 1, iByteCount, _file) != iByteCount)
-        {
-            LOG_ERROR_VARG("Failed to read %d index data bytes for mesh part (i=%d): %s", iByteCount, i, id);
-            SAFE_DELETE_ARRAY(indexData);
-            SAFE_RELEASE(mesh);
-            return NULL;
-        }
+        MeshPartData* partData = new MeshPartData();
+        meshData->parts.push_back(partData);
 
-        Mesh::IndexFormat indexFormat = (Mesh::IndexFormat)iFormat;
+        partData->primitiveType = (Mesh::PrimitiveType)pType;
+        partData->indexFormat = (Mesh::IndexFormat)iFormat;
+        
         unsigned int indexSize = 0;
-        switch (indexFormat)
+        switch (partData->indexFormat)
         {
         case Mesh::INDEX8:
             indexSize = 1;
@@ -1139,23 +1131,53 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
             indexSize = 4;
             break;
         }
-        unsigned int indexCount = iByteCount / indexSize;
-        MeshPart* part = mesh->addPart((Mesh::PrimitiveType)pType, indexFormat, indexCount, false);
-        if (part == NULL)
+
+        partData->indexCount = iByteCount / indexSize;
+
+        partData->indexData = new unsigned char[iByteCount];
+        if (fread(partData->indexData, 1, iByteCount, _file) != iByteCount)
         {
-            LOG_ERROR_VARG("Failed to create mesh part (i=%d): %s", i, id);
-            SAFE_DELETE_ARRAY(indexData);
-            SAFE_RELEASE(mesh);
+            SAFE_DELETE(meshData);
             return NULL;
         }
-        part->setIndexData(indexData, 0, indexCount);
-        if (loadWithMeshRBSupport)
-            SceneLoader::addMeshRigidBodyData(_path, nodeId, indexData, iByteCount);
-        SAFE_DELETE_ARRAY(indexData);
     }
 
-    fseek(_file, position, SEEK_SET);
-    return mesh;
+    return meshData;
+}
+
+Package::MeshData* Package::readMeshData(const char* url)
+{
+    assert(url);
+
+    unsigned int len = strlen(url);
+    if (len == 0)
+        return NULL;
+
+    // Parse URL (formatted as 'package#id')
+    std::string urlstring(url);
+    unsigned int pos = urlstring.find('#');
+    if (pos == std::string::npos)
+        return NULL;
+
+    std::string file = urlstring.substr(0, pos);
+    std::string id = urlstring.substr(pos + 1);
+
+    // Load package
+    Package* pkg = Package::create(file.c_str());
+    if (pkg == NULL)
+        return NULL;
+
+    // Seek to mesh with specified ID in package
+    Reference* ref = pkg->seekTo(id.c_str(), PACKAGE_TYPE_MESH);
+    if (ref == NULL)
+        return NULL;
+
+    // Read mesh data from current file position
+    MeshData* meshData = pkg->readMeshData();
+
+    SAFE_RELEASE(pkg);
+
+    return meshData;
 }
 
 Font* Package::loadFont(const char* id)
@@ -1285,8 +1307,8 @@ const char* Package::getObjectID(unsigned int index) const
     return (index >= _referenceCount ? NULL : _references[index].id.c_str());
 }
 
-Package::Reference::Reference() :
-    type(0), offset(0)
+Package::Reference::Reference()
+    : type(0), offset(0)
 {
 }
 
@@ -1294,4 +1316,29 @@ Package::Reference::~Reference()
 {
 }
 
+Package::MeshPartData::MeshPartData() :
+    indexCount(0), indexData(NULL)
+{
+}
+
+Package::MeshPartData::~MeshPartData()
+{
+    SAFE_DELETE_ARRAY(indexData);
+}
+
+Package::MeshData::MeshData(const VertexFormat& vertexFormat)
+    : vertexFormat(vertexFormat), vertexCount(0), vertexData(NULL)
+{
+}
+
+Package::MeshData::~MeshData()
+{
+    SAFE_DELETE_ARRAY(vertexData);
+
+    for (unsigned int i = 0; i < parts.size(); ++i)
+    {
+        SAFE_DELETE(parts[i]);
+    }
+}
+
 }

+ 49 - 29
gameplay/src/Package.h

@@ -15,7 +15,7 @@ namespace gameplay
  */
 class Package : public Ref
 {
-    friend class SceneLoader;
+    friend class PhysicsController;
 
 public:
 
@@ -118,6 +118,31 @@ private:
         std::vector<Matrix> inverseBindPoseMatrices;
     };
 
+    struct MeshPartData
+    {
+        MeshPartData();
+        ~MeshPartData();
+
+        Mesh::PrimitiveType primitiveType;
+        Mesh::IndexFormat indexFormat;
+        unsigned int indexCount;
+        unsigned char* indexData;
+    };
+
+    struct MeshData
+    {
+        MeshData(const VertexFormat& vertexFormat);
+        ~MeshData();
+
+        VertexFormat vertexFormat;
+        unsigned int vertexCount;
+        unsigned char* vertexData;
+        BoundingBox boundingBox;
+        BoundingSphere boundingSphere;
+        Mesh::PrimitiveType primitiveType;
+        std::vector<MeshPartData*> parts;
+    };
+
     Package(const char* path);
 
     /**
@@ -173,46 +198,22 @@ private:
      */
     Reference* seekToFirstType(unsigned int type);
 
-    /**
-     * Loads the scene with the specified ID from the package, and loads the specified nodes with mesh rigid body support.
-     * If id is NULL then the first scene found is loaded.
-     * 
-     * @param id The ID of the scene to load (NULL to load the first scene).
-     * @param nodesWithMeshRB A list of the IDs of the nodes within the scene that 
-     *      should be loaded with support for triangle mesh rigid bodies.
-     * 
-     * @return The loaded scene, or NULL if the scene could not be loaded.
-     */
-    Scene* loadScene(const char* id, const std::vector<std::string>* nodesWithMeshRB);
-
-    /**
-     * Loads a node with the specified ID from the package, optionally with mesh rigid body support.
-     *
-     * @param id The ID of the node to load in the package.
-     * @param loadWithMeshRBSupport Whether or not to load the node with mesh rigid body support.
-     * 
-     * @return The loaded node, or NULL if the node could not be loaded.
-     */
-    Node* loadNode(const char* id, bool loadWithMeshRBSupport);
-
     /**
      * Internal method to load a node.
      *
      * Only one of node or scene should be passed as non-NULL (or neither).
      */
-    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport);
+    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext);
 
     /**
      * Loads a mesh with the specified ID from the package.
      *
      * @param id The ID of the mesh to load.
-     * @param loadWithMeshRBSupport Whether to load the mesh with 
-     *      support for triangle mesh rigid bodies or not.
      * @param nodeId The id of the mesh's model's parent node.
      * 
      * @return The loaded mesh, or NULL if the mesh could not be loaded.
      */
-    Mesh* loadMesh(const char* id, bool loadWithMeshRBSupport, const char* nodeId);
+    Mesh* loadMesh(const char* id, const char* nodeId);
 
     /**
      * Reads an unsigned int from the current file position.
@@ -299,7 +300,7 @@ private:
      * 
      * @return A pointer to new node or NULL if there was an error.
      */
-    Node* readNode(Scene* sceneContext, Node* nodeContext, const std::vector<std::string>* nodesWithMeshRB);
+    Node* readNode(Scene* sceneContext, Node* nodeContext);
 
     /**
      * Reads a camera from the current file position.
@@ -320,7 +321,25 @@ private:
      * 
      * @return A pointer to a new model or NULL if there was an error.
      */
-    Model* readModel(Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport, const char* nodeId);
+    Model* readModel(Scene* sceneContext, Node* nodeContext, const char* nodeId);
+
+    /**
+     * Reads mesh data from the current file position.
+     */
+    MeshData* readMeshData();
+
+    /**
+     * Reads mesh data for the specified URL.
+     *
+     * The specified URL should be formatted as 'package#id', where
+     * 'package' is the package file containing the mesh and 'id' is the ID
+     * of the mesh to read data for.
+     *
+     * @param url The URL to read mesh data from.
+     *
+     * @return The mesh rigid body data.
+     */
+    static MeshData* readMeshData(const char* url);
 
     /**
      * Reads a mesh skin from the current file position.
@@ -368,6 +387,7 @@ private:
     void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
 
 private:
+
     std::string _path;
     unsigned int _referenceCount;
     Reference* _references;

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -1,5 +1,5 @@
+#include "Base.h"
 #include "PhysicsConstraint.h"
-
 #include "Game.h"
 #include "Node.h"
 #include "PhysicsMotionState.h"

+ 86 - 29
gameplay/src/PhysicsController.cpp

@@ -3,7 +3,7 @@
 #include "MeshPart.h"
 #include "PhysicsController.h"
 #include "PhysicsMotionState.h"
-#include "SceneLoader.h"
+#include "Package.h"
 
 // The initial capacity of the Bullet debug drawer's vertex batch.
 #define INITIAL_CAPACITY 280
@@ -18,8 +18,8 @@ const int PhysicsController::REMOVE        = 0x08;
 
 PhysicsController::PhysicsController()
   : _collisionConfiguration(NULL), _dispatcher(NULL),
-    _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _debugDrawer(NULL), 
-    _status(PhysicsController::Listener::DEACTIVATED), _listeners(NULL),
+    _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _ghostPairCallback(NULL),
+    _debugDrawer(NULL), _status(PhysicsController::Listener::DEACTIVATED), _listeners(NULL),
     _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0))
 {
     // Default gravity is 9.8 along the negative Y axis.
@@ -27,6 +27,7 @@ PhysicsController::PhysicsController()
 
 PhysicsController::~PhysicsController()
 {
+    SAFE_DELETE(_ghostPairCallback);
     SAFE_DELETE(_debugDrawer);
     SAFE_DELETE(_listeners);
 }
@@ -39,6 +40,16 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
+PhysicsCharacter* PhysicsController::createCharacter(Node* node, float radius, float height, const Vector3& center)
+{
+    return new PhysicsCharacter(node, radius, height, center);
+}
+
+void PhysicsController::destroyCharacter(PhysicsCharacter* character)
+{
+    SAFE_DELETE(character);
+}
+
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
     checkConstraintRigidBodies(a, b);
@@ -129,12 +140,20 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
     _debugDrawer->end();
 }
 
-PhysicsRigidBody* PhysicsController::rayTest(const Ray& ray, float distance)
+PhysicsRigidBody* PhysicsController::rayTest(const Ray& ray, float distance, Vector3* hitPoint, float* hitFraction)
 {
     btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(distance * ray.getDirection()));
     _world->rayTest(BV(ray.getOrigin()), BV(distance * ray.getDirection()), callback);
     if (callback.hasHit())
+    {
+        if (hitPoint)
+            hitPoint->set(callback.m_hitPointWorld.x(), callback.m_hitPointWorld.y(), callback.m_hitPointWorld.z());
+
+        if (hitFraction)
+            *hitFraction = callback.m_closestHitFraction;
+
         return getRigidBody(callback.m_collisionObject);
+    }
 
     return NULL;
 }
@@ -226,6 +245,10 @@ void PhysicsController::initialize()
     _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
     _world->setGravity(BV(_gravity));
 
+    // Register ghost pair callback so bullet detects collisions with ghost objects (used for character collisions).
+    _ghostPairCallback = bullet_new<btGhostPairCallback>();
+    _world->getPairCache()->setInternalGhostPairCallback(_ghostPairCallback);
+
     // Set up debug drawing.
     _debugDrawer = new DebugDrawer();
     _world->setDebugDrawer(_debugDrawer);
@@ -235,6 +258,7 @@ void PhysicsController::finalize()
 {
     // Clean up the world and its various components.
     SAFE_DELETE(_world);
+    SAFE_DELETE(_ghostPairCallback);
     SAFE_DELETE(_solver);
     SAFE_DELETE(_overlappingPairCache);
     SAFE_DELETE(_dispatcher);
@@ -519,23 +543,52 @@ btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& s
     // Create the sphere shape and add it to the cache.
     btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
     _shapes.push_back(new PhysicsCollisionShape(sphere));
-    
+
     return sphere;
 }
 
 btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Vector3& scale)
 {
-    // Retrieve the mesh rigid body data from the loaded scene.
-    const SceneLoader::MeshRigidBodyData* data = SceneLoader::getMeshRigidBodyData(body->_node->getId());
+    assert(body);
+
+    // Retrieve the mesh rigid body data from the node's mesh.
+    Model* model = body->_node ? body->_node->getModel() : NULL;
+    Mesh* mesh = model ? model->getMesh() : NULL;
+    if (mesh == NULL)
+    {
+        LOG_ERROR("Cannot create mesh rigid body for node without model/mesh.");
+        return NULL;
+    }
+
+    // Only support meshes with triangle list primitive types
+    if (mesh->getPrimitiveType() != Mesh::TRIANGLES)
+    {
+        LOG_ERROR("Cannot create mesh rigid body for mesh without TRIANGLES primitive type.");
+        return NULL;
+    }
+
+    // The mesh must have a valid URL (i.e. it must have been loaded from a Package)
+    // in order to fetch mesh data for computing mesh rigid body.
+    if (strlen(mesh->getUrl()) == 0)
+    {
+        LOG_ERROR("Cannot create mesh rigid body for mesh without valid URL.");
+        return NULL;
+    }
+
+    Package::MeshData* data = Package::readMeshData(mesh->getUrl());
+    if (data == NULL)
+    {
+        return NULL;
+    }
 
     // Copy the scaled vertex position data to the rigid body's local buffer.
     Matrix m;
     Matrix::createScale(scale, &m);
-    unsigned int vertexCount = data->mesh->getVertexCount();
+    unsigned int vertexCount = data->vertexCount;
     body->_vertexData = new float[vertexCount * 3];
     Vector3 v;
-    int vertexStride = data->mesh->getVertexFormat().getVertexSize();
-    for (unsigned int i = 0; i < vertexCount; i++)
+    int vertexStride = data->vertexFormat.getVertexSize();
+    for (unsigned int i = 0; i < data->vertexCount; i++)
     {
         v.set(*((float*)&data->vertexData[i * vertexStride + 0 * sizeof(float)]),
               *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
@@ -543,19 +596,20 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         v *= m;
         memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
     }
-    
+
     btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
 
-    if (data->mesh->getPartCount() > 0)
+    unsigned int partCount = data->parts.size();
+    if (partCount > 0)
     {
         PHY_ScalarType indexType = PHY_UCHAR;
         int indexStride = 0;
-        MeshPart* meshPart = NULL;
-        for (unsigned int i = 0; i < data->mesh->getPartCount(); i++)
+        Package::MeshPartData* meshPart = NULL;
+        for (unsigned int i = 0; i < partCount; i++)
         {
-            meshPart = data->mesh->getPart(i);
+            meshPart = data->parts[i];
 
-            switch (meshPart->getIndexFormat())
+            switch (meshPart->indexFormat)
             {
             case Mesh::INDEX8:
                 indexType = PHY_UCHAR;
@@ -571,17 +625,16 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
                 break;
             }
 
-            // Copy the index data to the rigid body's local buffer.
-            unsigned int indexDataSize = meshPart->getIndexCount() * indexStride;
-            unsigned char* indexData = new unsigned char[indexDataSize];
-            memcpy(indexData, data->indexData[i], indexDataSize);
-            body->_indexData.push_back(indexData);
+            // Move the index data into the rigid body's local buffer.
+            // Set it to NULL in the MeshPartData so it is not released when the data is freed.
+            body->_indexData.push_back(meshPart->indexData);
+            meshPart->indexData = NULL;
 
             // Create a btIndexedMesh object for the current mesh part.
             btIndexedMesh indexedMesh;
             indexedMesh.m_indexType = indexType;
-            indexedMesh.m_numTriangles = meshPart->getIndexCount() / 3;
-            indexedMesh.m_numVertices = meshPart->getIndexCount();
+            indexedMesh.m_numTriangles = meshPart->indexCount / 3; // assume TRIANGLES primitive type
+            indexedMesh.m_numVertices = meshPart->indexCount;
             indexedMesh.m_triangleIndexBase = (const unsigned char*)body->_indexData[i];
             indexedMesh.m_triangleIndexStride = indexStride*3;
             indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
@@ -595,8 +648,8 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
     else
     {
         // Generate index data for the mesh locally in the rigid body.
-        unsigned int* indexData = new unsigned int[data->mesh->getVertexCount()];
-        for (unsigned int i = 0; i < data->mesh->getVertexCount(); i++)
+        unsigned int* indexData = new unsigned int[data->vertexCount];
+        for (unsigned int i = 0; i < data->vertexCount; i++)
         {
             indexData[i] = i;
         }
@@ -605,8 +658,8 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         // Create a single btIndexedMesh object for the mesh interface.
         btIndexedMesh indexedMesh;
         indexedMesh.m_indexType = PHY_INTEGER;
-        indexedMesh.m_numTriangles = data->mesh->getVertexCount() / 3;
-        indexedMesh.m_numVertices = data->mesh->getVertexCount();
+        indexedMesh.m_numTriangles = data->vertexCount / 3; // assume TRIANGLES primitive type
+        indexedMesh.m_numVertices = data->vertexCount;
         indexedMesh.m_triangleIndexBase = body->_indexData[0];
         indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
         indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
@@ -620,6 +673,9 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
     btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
     _shapes.push_back(new PhysicsCollisionShape(shape));
 
+    // Free the temporary mesh data now that it's stored in physics system
+    SAFE_DELETE(data);
+
     return shape;
 }
 
@@ -696,6 +752,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
+    material->getStateBlock()->setDepthTest(true);
 
     VertexFormat::Element elements[] =
     {
@@ -721,9 +778,9 @@ void PhysicsController::DebugDrawer::begin(const Matrix& viewProjection)
 
 void PhysicsController::DebugDrawer::end()
 {
+    _meshBatch->end();
     _meshBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_viewProjection);
     _meshBatch->draw();
-    _meshBatch->end();
 }
 
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
@@ -780,4 +837,4 @@ int	PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
 }
 
-}
+}

+ 47 - 4
gameplay/src/PhysicsController.h

@@ -8,6 +8,7 @@
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
 #include "PhysicsRigidBody.h"
+#include "PhysicsCharacter.h"
 
 namespace gameplay
 {
@@ -18,8 +19,10 @@ namespace gameplay
 class PhysicsController : public btCollisionWorld::ContactResultCallback
 {
     friend class Game;
+    friend Camera;
     friend class PhysicsConstraint;
     friend class PhysicsRigidBody;
+    friend class PhysicsCharacter;
 
 public:
 
@@ -59,6 +62,42 @@ public:
      */
     void addStatusListener(PhysicsController::Listener* listener);
 
+    /**
+     * Creates a new PhysicsCharacter.
+     *
+     * The created character is added to the physics world and automatically receives
+     * physics updates to handle interactions and collisions between the character
+     * and other physics objects in the world. The character will continue to receive
+     * updates until it is destroyed via the destroyCharacter(PhysicsCharacter*) method.
+     *
+     * The node may point to any node in the scene that you wish to control as a character.
+     * When a PhysicsCharacter is created for a particular node, the game will normally
+     * perform all movement directly through the PhysicsCharacter interface and not through
+     * the node itself.
+     *
+     * The radius, height and center parameters define a capsule volume that is used
+     * to represent the character in the physics world. All collision handling is 
+     * performed using this capsule.
+     *
+     * Note that PhysicsCharacter should not be mixed with rigid bodies. Therefore, you 
+     * should ensure that the node (and any of its children) used to create the
+     * PhysicsCharacter does not have any rigid bodies assigned. Doing so will cause
+     * unexpected results.
+     *
+     * @param node Scene node that represents the character.
+     * @param radius Radius of capsule volume used for character collisions.
+     * @param height Height of the capsule volume used for character collisions.
+     * @param center Center point of the capsule volume for the character.
+     */
+    PhysicsCharacter* createCharacter(Node* node, float radius, float height, const Vector3& center = Vector3::zero());
+
+    /**
+     * Destroys a PhysicsCharacter and removes it from the physics world.
+     *
+     * @param character PhysicsCharacter to destroy.
+     */
+    void destroyCharacter(PhysicsCharacter* character);
+
     /**
      * Creates a fixed constraint.
      * 
@@ -181,7 +220,7 @@ public:
      * @param gravity The gravity vector.
      */
     void setGravity(const Vector3& gravity);
-    
+   
     /**
      * Draws debugging information (rigid body outlines, etc.) using the given view projection matrix.
      * 
@@ -194,9 +233,12 @@ public:
      * 
      * @param ray The ray to test intersection with.
      * @param distance How far along the given ray to test for intersections.
-     * @return The first rigid body that the ray intersects.
+     * @param hitPoint Optional Vector3 point that is populated with the world-space point of intersection.
+     * @param hitFraction Optional float pointer that is populated with the distance along the ray
+     *      (as a fraction between 0-1) where the intersection occurred.
+     * @return The first rigid body that the ray intersects, or NULL if no intersection was found.
      */
-    PhysicsRigidBody* rayTest(const Ray& ray, float distance);
+    PhysicsRigidBody* rayTest(const Ray& ray, float distance, Vector3* hitPoint = NULL, float* hitFraction = NULL);
 
 protected:
 
@@ -335,6 +377,7 @@ private:
     btBroadphaseInterface* _overlappingPairCache;
     btSequentialImpulseConstraintSolver* _solver;
     btDynamicsWorld* _world;
+    btGhostPairCallback* _ghostPairCallback;
     std::vector<PhysicsCollisionShape*> _shapes;
     DebugDrawer* _debugDrawer;
     Listener::EventType _status;
@@ -346,4 +389,4 @@ private:
 
 }
 
-#endif
+#endif

+ 1 - 0
gameplay/src/PhysicsFixedConstraint.cpp

@@ -1,3 +1,4 @@
+#include "Base.h"
 #include "PhysicsFixedConstraint.h"
 
 namespace gameplay

+ 1 - 1
gameplay/src/PhysicsGenericConstraint.cpp

@@ -1,5 +1,5 @@
+#include "Base.h"
 #include "PhysicsGenericConstraint.h"
-
 #include "Node.h"
 #include "PhysicsMotionState.h"
 #include "PhysicsRigidBody.h"

+ 1 - 1
gameplay/src/PhysicsHingeConstraint.cpp

@@ -1,5 +1,5 @@
+#include "Base.h"
 #include "PhysicsHingeConstraint.h"
-
 #include "Node.h"
 
 namespace gameplay

+ 1 - 0
gameplay/src/PhysicsMotionState.cpp

@@ -1,3 +1,4 @@
+#include "Base.h"
 #include "PhysicsMotionState.h"
 
 namespace gameplay

+ 1 - 0
gameplay/src/PhysicsMotionState.h

@@ -15,6 +15,7 @@ namespace gameplay
 class PhysicsMotionState : public btMotionState
 {
     friend class PhysicsRigidBody;
+    friend class PhysicsCharacter;
     friend class PhysicsConstraint;
 
 protected:

+ 0 - 1
gameplay/src/PhysicsRigidBody.cpp

@@ -477,7 +477,6 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     return body;
 }
 
-
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
     // This function is only supported for heightfield rigid bodies.

+ 15 - 0
gameplay/src/PhysicsRigidBody.h

@@ -18,6 +18,7 @@ class PhysicsConstraint;
 class PhysicsRigidBody : public Transform::Listener
 {
     friend class Node;
+    friend class PhysicsCharacter;
     friend class PhysicsConstraint;
     friend class PhysicsController;
     friend class PhysicsFixedConstraint;
@@ -233,6 +234,20 @@ public:
      */
     inline bool isKinematic() const;
 
+    /**
+     * Gets whether the rigid body is a static rigid body or not.
+     *
+     * @return Whether the rigid body is static.
+     */
+    inline bool isStatic() const;
+
+    /**
+     * Gets whether the rigid body is a dynamic rigid body or not.
+     *
+     * @return Whether the rigid body is dynamic.
+     */
+    inline bool isDynamic() const;
+
     /**
      * Sets the rigid body's angular velocity.
      * 

+ 11 - 1
gameplay/src/PhysicsRigidBody.inl

@@ -71,7 +71,17 @@ inline float PhysicsRigidBody::getRestitution() const
 
 inline bool PhysicsRigidBody::isKinematic() const
 {
-    return (_body->getCollisionFlags() & btCollisionObject::CF_KINEMATIC_OBJECT) != 0;
+    return _body->isKinematicObject();
+}
+
+inline bool PhysicsRigidBody::isStatic() const
+{
+    return _body->isStaticObject();
+}
+
+inline bool PhysicsRigidBody::isDynamic() const
+{
+    return !_body->isStaticOrKinematicObject();
 }
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)

+ 1 - 1
gameplay/src/PhysicsSocketConstraint.cpp

@@ -1,5 +1,5 @@
+#include "Base.h"
 #include "PhysicsSocketConstraint.h"
-
 #include "Node.h"
 
 namespace gameplay

+ 1 - 1
gameplay/src/PhysicsSpringConstraint.cpp

@@ -1,5 +1,5 @@
+#include "Base.h"
 #include "PhysicsSpringConstraint.h"
-
 #include "Node.h"
 #include "PhysicsRigidBody.h"
 

+ 3 - 5
gameplay/src/Properties.cpp

@@ -567,12 +567,10 @@ const char* Properties::getString(const char* name) const
 
 bool Properties::getBool(const char* name) const
 {
-    if (exists(name))
+    const char* valueString = getString(name);
+    if (valueString)
     {
-        if (_properties.find(name)->second == "true")
-        {
-            return true;
-        }
+        return (strcmp(valueString, "true") == 0);
     }
 
     return false;

+ 16 - 0
gameplay/src/RenderState.cpp

@@ -130,6 +130,10 @@ void RenderState::setParameterAutoBinding(const char* name, const char* autoBind
     {
         value = WORLD_VIEW_PROJECTION_MATRIX;
     }
+    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
+    {
+        value = INVERSE_TRANSPOSE_WORLD_MATRIX;
+    }
     else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
     {
         value = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX;
@@ -138,6 +142,10 @@ void RenderState::setParameterAutoBinding(const char* name, const char* autoBind
     {
         value = CAMERA_WORLD_POSITION;
     }
+    else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
+    {
+        value = CAMERA_VIEW_POSITION;
+    }
     else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
     {
         value = MATRIX_PALETTE;
@@ -218,6 +226,10 @@ void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBind
         getParameter(uniformName)->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
         break;
 
+    case INVERSE_TRANSPOSE_WORLD_MATRIX:
+        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
+        break;
+
     case INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX:
         getParameter(uniformName)->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
         break;
@@ -226,6 +238,10 @@ void RenderState::applyAutoBinding(const char* uniformName, AutoBinding autoBind
         getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
         break;
 
+    case CAMERA_VIEW_POSITION:
+        getParameter(uniformName)->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
+        break;
+
     case MATRIX_PALETTE:
         {
             Model* model = _nodeBinding->getModel();

+ 10 - 0
gameplay/src/RenderState.h

@@ -57,6 +57,11 @@ public:
          */
         WORLD_VIEW_PROJECTION_MATRIX,
 
+        /**
+         * Binds a node's InverseTransposeWorl matrix.
+         */
+        INVERSE_TRANSPOSE_WORLD_MATRIX,
+
         /**
          * Binds a node's InverseTransposeWorldView matrix.
          */
@@ -67,6 +72,11 @@ public:
          */
         CAMERA_WORLD_POSITION,
 
+        /**
+         * Binds the view-space position (Vector3) of the active camera for the node's scene.
+         */
+        CAMERA_VIEW_POSITION,
+
         /**
          * Binds the matrix palette of MeshSkin attached to a node's model.
          */

+ 1 - 1
gameplay/src/Scene.cpp

@@ -278,7 +278,7 @@ void Scene::setViewport(const Viewport& viewport)
     _viewport = viewport;
 }
 
-const Vector3& Scene::getAmbientColor()
+const Vector3& Scene::getAmbientColor() const
 {
     return _ambientColor;
 }

+ 1 - 1
gameplay/src/Scene.h

@@ -151,7 +151,7 @@ public:
      * 
      * @return The ambient color of the scene.
      */
-    const Vector3& getAmbientColor();
+    const Vector3& getAmbientColor() const;
 
     /**
      * Sets the ambient color of the scene.

+ 284 - 323
gameplay/src/SceneLoader.cpp

@@ -9,9 +9,7 @@ namespace gameplay
 // Static member variables.
 std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
-std::vector<SceneLoader::SceneNodeProperty> SceneLoader::_nodeProperties;
-std::vector<std::string> SceneLoader::_nodesWithMeshRB;
-std::map<std::string, SceneLoader::MeshRigidBodyData>* SceneLoader::_meshRigidBodyData = NULL;
+std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
 std::string SceneLoader::_path;
 
 Scene* SceneLoader::load(const char* filePath)
@@ -39,26 +37,10 @@ Scene* SceneLoader::load(const char* filePath)
 
     // Get the path to the main GPB.
     _path = sceneProperties->getString("path");
-    
     // Build the node URL/property and animation reference tables and load the referenced files.
     buildReferenceTables(sceneProperties);
     loadReferencedFiles();
 
-    // Calculate the node IDs that need to be loaded with mesh rigid body support.
-    calculateNodesWithMeshRigidBodies(sceneProperties);
-
-    // Set up for storing the mesh rigid body data.
-    if (_nodesWithMeshRB.size() > 0)
-    {
-        // We do not currently support loading more than one scene simultaneously.
-        if (_meshRigidBodyData)
-        {
-            WARN("Attempting to load multiple scenes simultaneously; mesh rigid bodies will not load properly.");
-        }
-
-        _meshRigidBodyData = new std::map<std::string, MeshRigidBodyData>();
-    }
-
     // Load the main scene data from GPB and apply the global scene properties.
     Scene* scene = loadMainSceneData(sceneProperties);
     if (!scene)
@@ -69,8 +51,18 @@ Scene* SceneLoader::load(const char* filePath)
 
     // First apply the node url properties. Following that,
     // apply the normal node properties and create the animations.
+    // We apply rigid body properties after all other node properties
+    // so that the transform (SRT) properties get applied before
+    // processing rigid bodies.
     applyNodeUrls(scene);
-    applyNodeProperties(scene, sceneProperties);
+    applyNodeProperties(scene, sceneProperties, 
+        SceneNodeProperty::AUDIO | 
+        SceneNodeProperty::MATERIAL | 
+        SceneNodeProperty::PARTICLE |
+        SceneNodeProperty::ROTATE |
+        SceneNodeProperty::SCALE |
+        SceneNodeProperty::TRANSLATE);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::RIGIDBODY);
     createAnimations(scene);
 
     // Find the physics properties object.
@@ -101,95 +93,21 @@ Scene* SceneLoader::load(const char* filePath)
     // Clean up the .scene file's properties object.
     SAFE_DELETE(properties);
 
-    // Clean up mesh rigid body data.
-    if (_meshRigidBodyData)
-    {
-        std::map<std::string, MeshRigidBodyData>::iterator iter = _meshRigidBodyData->begin();
-        for (; iter != _meshRigidBodyData->end(); iter++)
-        {
-            for (unsigned int i = 0; i < iter->second.indexData.size(); i++)
-            {
-                SAFE_DELETE_ARRAY(iter->second.indexData[i]);
-            }
-
-            SAFE_DELETE_ARRAY(iter->second.vertexData);
-        }
-
-        SAFE_DELETE(_meshRigidBodyData);
-    }
-
     // Clear all temporary data stores.
     _propertiesFromFile.clear();
     _animations.clear();
-    _nodeProperties.clear();
-    _nodesWithMeshRB.clear();
+    _sceneNodes.clear();
 
     return scene;
 }
 
-void SceneLoader::addMeshRigidBodyData(std::string package, std::string id, Mesh* mesh, unsigned char* vertexData, unsigned int vertexByteCount)
-{
-    if (!_meshRigidBodyData)
-    {
-        WARN("Attempting to add mesh rigid body data outside of scene loading; ignoring request.");
-        return;
-    }
-
-    // If the node with the mesh rigid body is renamed, we need to find the new id.
-    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
-    {
-        if (_nodeProperties[i]._type == SceneNodeProperty::URL &&
-            _nodeProperties[i]._id == id)
-        {
-            if ((package == _path && _nodeProperties[i]._file.size() == 0) ||
-                (package == _nodeProperties[i]._file))
-            {
-                id = _nodeProperties[i]._nodeID;
-                break;
-            }
-        }
-    }
-
-    (*_meshRigidBodyData)[id].mesh = mesh;
-    (*_meshRigidBodyData)[id].vertexData = new unsigned char[vertexByteCount];
-    memcpy((*_meshRigidBodyData)[id].vertexData, vertexData, vertexByteCount);
-}
-
-void SceneLoader::addMeshRigidBodyData(std::string package, std::string id, unsigned char* indexData, unsigned int indexByteCount)
-{
-    if (!_meshRigidBodyData)
-    {
-        WARN("Attempting to add mesh rigid body data outside of scene loading; ignoring request.");
-        return;
-    }
-
-    // If the node with the mesh rigid body is renamed, we need to find the new id.
-    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
-    {
-        if (_nodeProperties[i]._type == SceneNodeProperty::URL &&
-            _nodeProperties[i]._id == id)
-        {
-            if ((package == _path && _nodeProperties[i]._file.size() == 0) ||
-                (package == _nodeProperties[i]._file))
-            {
-                id = _nodeProperties[i]._nodeID;
-                break;
-            }
-        }
-    }
-
-    unsigned char* indexDataCopy = new unsigned char[indexByteCount];
-    memcpy(indexDataCopy, indexData, indexByteCount);
-    (*_meshRigidBodyData)[id].indexData.push_back(indexDataCopy);
-}
-
 void SceneLoader::addSceneAnimation(const char* animationID, const char* targetID, const char* url)
 {
     // Calculate the file and id from the given url.
     std::string file;
     std::string id;
     splitURL(url, &file, &id);
-    
+
     // If there is a file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
     if (file.length() > 0 && _propertiesFromFile.count(file) == 0)
@@ -199,166 +117,154 @@ void SceneLoader::addSceneAnimation(const char* animationID, const char* targetI
     _animations.push_back(SceneAnimation(animationID, targetID, file, id));
 }
 
-void SceneLoader::addSceneNodeProperty(SceneNodeProperty::Type type, const char* nodeID, const char* url)
+void SceneLoader::addSceneNodeProperty(SceneNode& sceneNode, SceneNodeProperty::Type type, const char* url, int index)
 {
     // Calculate the file and id from the given url.
     std::string file;
     std::string id;
     splitURL(url, &file, &id);
-    
+
     // If there is a non-GPB file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
     if (file.length() > 0 && file.find(".gpb") == file.npos && _propertiesFromFile.count(file) == 0)
         _propertiesFromFile[file] = NULL;
 
+    SceneNodeProperty prop(type, file, id, index);
+
+    // Parse for wildcharacter character (only supported on the URL attribute)
+    if (type == SceneNodeProperty::URL)
+    {
+        if (id.length() > 1 && id.at(id.length()-1) == '*')
+        {
+            prop._id = id.substr(0, id.length()-1);
+            sceneNode._exactMatch = false;
+        }
+    }
+
     // Add the node property to the list of node properties to be resolved later.
-    _nodeProperties.push_back(SceneNodeProperty(type, nodeID, file, id));
+    sceneNode._properties.push_back(prop);
 }
 
-void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* sceneProperties)
+void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags)
 {
-    // Apply all of the remaining scene node properties except rigid body (we apply that last).
-    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
+    for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
     {
-        // If the referenced node doesn't exist in the scene, then we
-        // can't do anything so we skip to the next scene node property.
-        Node* node = scene->findNode(_nodeProperties[i]._nodeID);
-        if (!node)
-        {
-            WARN_VARG("Attempting to set a property for node '%s', which does not exist in the scene.", _nodeProperties[i]._nodeID);
-            continue;
-        }
+        SceneNode& sceneNode = _sceneNodes[i];
 
-        if (_nodeProperties[i]._type == SceneNodeProperty::AUDIO ||
-            _nodeProperties[i]._type == SceneNodeProperty::MATERIAL ||
-            _nodeProperties[i]._type == SceneNodeProperty::PARTICLE ||
-            _nodeProperties[i]._type == SceneNodeProperty::RIGIDBODY)
+        if (sceneNode._exactMatch)
         {
-            // Check to make sure the referenced properties object was loaded properly.
-            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
-            if (!p)
+            // Find the node matching the specified ID exactly
+            Node* node = scene->findNode(sceneNode._nodeID);
+            if (!node)
             {
-                WARN_VARG("The referenced node data in file '%s' failed to load.", _nodeProperties[i]._file.c_str());
+                WARN_VARG("Attempting to set a property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
                 continue;
             }
 
-            // If a specific namespace within the file was specified, load that namespace.
-            if (_nodeProperties[i]._id.size() > 0)
+            for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
             {
-                p = p->getNamespace(_nodeProperties[i]._id.c_str());
-                if (!p)
-                {
-                    WARN_VARG("The referenced node data at '%s#%s' failed to load.", _nodeProperties[i]._file.c_str(), _nodeProperties[i]._id.c_str());
-                    continue;
-                }
+                SceneNodeProperty& snp = sceneNode._properties[j];
+                if ((typeFlags & snp._type) == snp._type)
+                    applyNodeProperty(sceneNode, node, sceneProperties, snp);
             }
-            else
+        }
+        else
+        {
+            // Find all nodes matching the specified ID
+            std::vector<Node*> nodes;
+            unsigned int nodeCount = scene->findNodes(sceneNode._nodeID, nodes, true, false);
+            if (nodeCount == 0)
+                continue;
+            
+            for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
             {
-                // Otherwise, use the first namespace.
-                p->rewind();
-                p = p->getNextNamespace();
-            }
+                SceneNodeProperty& snp = sceneNode._properties[j];
+                if ((typeFlags & snp._type) == 0)
+                    continue;
 
-            switch (_nodeProperties[i]._type)
-            {
-            case SceneNodeProperty::AUDIO:
-            {
-                AudioSource* audioSource = AudioSource::create(p);
-                node->setAudioSource(audioSource);
-                SAFE_RELEASE(audioSource);
-                break;
+                for (unsigned int k = 0; k < nodeCount; ++k)
+                    applyNodeProperty(sceneNode, nodes[k], sceneProperties, snp);
             }
-            case SceneNodeProperty::MATERIAL:
-                if (!node->getModel())
-                    WARN_VARG("Attempting to set a material on node '%s', which has no model.", _nodeProperties[i]._nodeID);
-                else
-                {
-                    Material* material = Material::create(p);
-                    node->getModel()->setMaterial(material);
-                    SAFE_RELEASE(material);
-                }
+        }
+    }
+}
+
+void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp)
+{
+    if (snp._type == SceneNodeProperty::AUDIO ||
+        snp._type == SceneNodeProperty::MATERIAL ||
+        snp._type == SceneNodeProperty::PARTICLE ||
+        snp._type == SceneNodeProperty::RIGIDBODY)
+    {
+        // Check to make sure the referenced properties object was loaded properly.
+        Properties* p = _propertiesFromFile[snp._file];
+        if (!p)
+        {
+            WARN_VARG("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+            return;
+        }
 
-                break;
-            case SceneNodeProperty::PARTICLE:
+        // If a specific namespace within the file was specified, load that namespace.
+        if (snp._id.size() > 0)
+        {
+            p = p->getNamespace(snp._id.c_str());
+            if (!p)
             {
-                ParticleEmitter* particleEmitter = ParticleEmitter::create(p);
-                node->setParticleEmitter(particleEmitter);
-                SAFE_RELEASE(particleEmitter);
-                break;
-            }
-            case SceneNodeProperty::RIGIDBODY:
-                // Process this last in a separate loop to allow scale, translate, rotate to be applied first.
-                break;
-            default:
-                // This cannot happen.
-                break;
+                WARN_VARG("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                return;
             }
         }
         else
         {
-            Properties* np = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
-            const char* name = NULL;
-
-            switch (_nodeProperties[i]._type)
-            {
-            case SceneNodeProperty::TRANSLATE:
-            {
-                Vector3 t;
-                if (np && np->getVector3("translate", &t))
-                    node->setTranslation(t);
-                break;
-            }
-            case SceneNodeProperty::ROTATE:
-            {
-                Quaternion r;
-                if (np && np->getQuaternionFromAxisAngle("rotate", &r))
-                    node->setRotation(r);
-                break;
-            }
-            case SceneNodeProperty::SCALE:
-            {
-                Vector3 s;
-                if (np && np->getVector3("scale", &s))
-                    node->setScale(s);
-                break;
-            }
-            default:
-                WARN_VARG("Unsupported node property type: %d.", _nodeProperties[i]._type);
-                break;
-            }
+            // Otherwise, use the first namespace.
+            p->rewind();
+            p = p->getNextNamespace();
         }
-    }
 
-    // Process rigid body properties.
-    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
-    {
-        if (_nodeProperties[i]._type == SceneNodeProperty::RIGIDBODY)
+        switch (snp._type)
         {
-            // If the referenced node doesn't exist in the scene, then we
-            // can't do anything so we skip to the next scene node property.
-            Node* node = scene->findNode(_nodeProperties[i]._nodeID);
-            if (!node)
+        case SceneNodeProperty::AUDIO:
+        {
+            AudioSource* audioSource = AudioSource::create(p);
+            node->setAudioSource(audioSource);
+            SAFE_RELEASE(audioSource);
+            break;
+        }
+        case SceneNodeProperty::MATERIAL:
+            if (!node->getModel())
+                WARN_VARG("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
+            else
             {
-                WARN_VARG("Attempting to set a property for node '%s', which does not exist in the scene.", _nodeProperties[i]._nodeID);
-                continue;
+                Material* material = Material::create(p);
+                node->getModel()->setMaterial(material, snp._index);
+                SAFE_RELEASE(material);
             }
-
+            break;
+        case SceneNodeProperty::PARTICLE:
+        {
+            ParticleEmitter* particleEmitter = ParticleEmitter::create(p);
+            node->setParticleEmitter(particleEmitter);
+            SAFE_RELEASE(particleEmitter);
+            break;
+        }
+        case SceneNodeProperty::RIGIDBODY:
+        {
             // Check to make sure the referenced properties object was loaded properly.
-            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
+            Properties* p = _propertiesFromFile[snp._file];
             if (!p)
             {
-                WARN_VARG("The referenced node data in file '%s' failed to load.", _nodeProperties[i]._file.c_str());
-                continue;
+                WARN_VARG("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+                return;
             }
 
             // If a specific namespace within the file was specified, load that namespace.
-            if (_nodeProperties[i]._id.size() > 0)
+            if (snp._id.size() > 0)
             {
-                p = p->getNamespace(_nodeProperties[i]._id.c_str());
+                p = p->getNamespace(snp._id.c_str());
                 if (!p)
                 {
-                    WARN_VARG("The referenced node data at '%s#%s' failed to load.", _nodeProperties[i]._file.c_str(), _nodeProperties[i]._id.c_str());
-                    continue;
+                    WARN_VARG("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                    return;
                 }
             }
             else
@@ -369,11 +275,11 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
             }
 
             // If the scene file specifies a rigid body model, use it for creating the rigid body.
-            Properties* np = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
+            Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
             const char* name = NULL;
             if (np && (name = np->getString("rigidbodymodel")))
             {
-                Node* modelNode = scene->findNode(name);
+                Node* modelNode = node->getScene()->findNode(name);
                 if (!modelNode)
                     WARN_VARG("Node '%s' does not exist; attempting to use its model for rigid body creation.", name);
                 else
@@ -391,9 +297,48 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
                 }
             }
             else if (!node->getModel())
-                WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", _nodeProperties[i]._nodeID);
+                WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", sceneNode._nodeID);
             else
                 node->setRigidBody(p);
+            break;
+        }
+        default:
+            // This cannot happen.
+            break;
+        }
+    }
+    else
+    {
+        // Handle Scale, Rotate and Translate
+        Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
+        const char* name = NULL;
+
+        switch (snp._type)
+        {
+        case SceneNodeProperty::TRANSLATE:
+        {
+            Vector3 t;
+            if (np && np->getVector3("translate", &t))
+                node->setTranslation(t);
+            break;
+        }
+        case SceneNodeProperty::ROTATE:
+        {
+            Quaternion r;
+            if (np && np->getQuaternionFromAxisAngle("rotate", &r))
+                node->setRotation(r);
+            break;
+        }
+        case SceneNodeProperty::SCALE:
+        {
+            Vector3 s;
+            if (np && np->getVector3("scale", &s))
+                node->setScale(s);
+            break;
+        }
+        default:
+            WARN_VARG("Unsupported node property type: %d.", snp._type);
+            break;
         }
     }
 }
@@ -402,65 +347,126 @@ void SceneLoader::applyNodeUrls(Scene* scene)
 {
     // Apply all URL node properties so that when we go to apply
     // the other node properties, the node is in the scene.
-    for (unsigned int i = 0; i < _nodeProperties.size(); )
+    for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
     {
-        if (_nodeProperties[i]._type == SceneNodeProperty::URL)
+        SceneNode& sceneNode = _sceneNodes[i];
+
+        // Iterate backwards over the properties list so we can remove properties as we go
+        // without danger of indexing out of bounds.
+        for (int j = sceneNode._properties.size() - 1; j >= 0; --j)
         {
-            // Make sure that the ID we are using to insert the node into the scene with is unique.
-            if (scene->findNode(_nodeProperties[i]._nodeID) != NULL)
-                WARN_VARG("Attempting to insert or rename a node to an ID that already exists: ID='%s'", _nodeProperties[i]._nodeID);
-            else
+            SceneNodeProperty& snp = sceneNode._properties[j];
+            if (snp._type != SceneNodeProperty::URL)
+                continue;
+
+            if (snp._file.empty())
             {
-                // If a file was specified, load the node from file and then insert it into the scene with the new ID.
-                if (_nodeProperties[i]._file.size() > 0)
+                // The node is from the main GPB and should just be renamed.
+
+                // TODO: Should we do all nodes with this case first to allow users to stitch in nodes with
+                // IDs equal to IDs that were in the original GPB file but were changed in the scene file?
+                if (sceneNode._exactMatch)
                 {
-                    Package* tmpPackage = Package::create(_nodeProperties[i]._file.c_str());
-                    if (!tmpPackage)
-                        WARN_VARG("Failed to load GPB file '%s' for node stitching.", _nodeProperties[i]._file.c_str());
+                    Node* node = scene->findNode(snp._id.c_str());
+                    if (node)
+                    {
+                        node->setId(sceneNode._nodeID);
+                    }
                     else
                     {
-                        bool loadWithMeshRBSupport = false;
-                        for (unsigned int j = 0; j < _nodesWithMeshRB.size(); j++)
+                        WARN_VARG("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
+                    }
+                }
+                else
+                {
+                    // Search for nodes using a partial match
+                    std::string partialMatch = snp._id;
+                    std::vector<Node*> nodes;
+                    unsigned int nodeCount = scene->findNodes(snp._id.c_str(), nodes, true, false);
+                    if (nodeCount > 0)
+                    {
+                        for (unsigned int k = 0; k < nodeCount; ++k)
                         {
-                            if (_nodeProperties[i]._id == _nodesWithMeshRB[j])
-                            {
-                                loadWithMeshRBSupport = true;
-                                break;
-                            }
+                            // Construct a new node ID using _nodeID plus the remainder of the partial match.
+                            Node* node = nodes[k];
+                            std::string newID(sceneNode._nodeID);
+                            newID += (node->getId() + snp._id.length());
+                            node->setId(newID.c_str());
                         }
+                    }
+                    else
+                    {
+                        WARN_VARG("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
+                    }
+                }
+            }
+            else
+            {
+                // An external file was referenced, so load the node from file and then insert it into the scene with the new ID.
 
-                        Node* node = tmpPackage->loadNode(_nodeProperties[i]._id.c_str(), loadWithMeshRBSupport);
-                        if (!node)
-                            WARN_VARG("Could not load node '%s' in GPB file '%s'.", _nodeProperties[i]._id.c_str(), _nodeProperties[i]._file.c_str());
-                        else
+                // TODO: Revisit this to determine if we should cache Package objects for the duration of the scene
+                // load to prevent constantly creating/destroying the same externally referenced packages each time
+                // a url with a file is encountered.
+                Package* tmpPackage = Package::create(snp._file.c_str());
+                if (tmpPackage)
+                {
+                    if (sceneNode._exactMatch)
+                    {
+                        Node* node = tmpPackage->loadNode(snp._id.c_str());
+                        if (node)
                         {
-                            node->setId(_nodeProperties[i]._nodeID);
+                            node->setId(sceneNode._nodeID);
                             scene->addNode(node);
                             SAFE_RELEASE(node);
                         }
-                        
-                        SAFE_RELEASE(tmpPackage);
+                        else
+                        {
+                            WARN_VARG("Could not load node '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                        }
+                    }
+                    else
+                    {
+                        // Search for nodes in the package using a partial match
+                        std::string partialMatch = snp._id;
+                        unsigned int objectCount = tmpPackage->getObjectCount();
+                        unsigned int matchCount = 0;
+                        for (unsigned int k = 0; k < objectCount; ++k)
+                        {
+                            const char* objid = tmpPackage->getObjectID(k);
+                            if (strstr(objid, snp._id.c_str()) == objid)
+                            {
+                                // This object ID matches (starts with).
+                                // Try to load this object as a Node.
+                                Node* node = tmpPackage->loadNode(objid);
+                                if (node)
+                                {
+                                    // Construct a new node ID using _nodeID plus the remainder of the partial match.
+                                    std::string newID(sceneNode._nodeID);
+                                    newID += (node->getId() + snp._id.length());
+                                    node->setId(newID.c_str());
+                                    scene->addNode(node);
+                                    SAFE_RELEASE(node);
+                                    matchCount++;
+                                }
+                            }
+                        }
+                        if (matchCount == 0)
+                        {
+                            WARN_VARG("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                        }
                     }
+
+                    SAFE_RELEASE(tmpPackage);
                 }
                 else
                 {
-                    // TODO: Should we do all nodes with this case first to allow users to stitch in nodes with
-                    // IDs equal to IDs that were in the original GPB file but were changed in the scene file?
-
-                    // Otherwise, the node is from the main GPB and should just be renamed.
-                    Node* node = scene->findNode(_nodeProperties[i]._id.c_str());
-                    if (!node)
-                        WARN_VARG("Could not find node '%s' in main scene GPB file.", _nodeProperties[i]._id.c_str());
-                    else
-                        node->setId(_nodeProperties[i]._nodeID);
+                    WARN_VARG("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
                 }
             }
 
             // Remove the node property since we are done applying it.
-            _nodeProperties.erase(_nodeProperties.begin() + i);
+            sceneNode._properties.erase(sceneNode._properties.begin() + j);
         }
-        else
-            i++;
     }
 }
 
@@ -479,27 +485,40 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 continue;
             }
 
+            // Add a SceneNode to the end of the list
+            _sceneNodes.resize(_sceneNodes.size() + 1);
+            SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
+            sceneNode._nodeID = ns->getId();
+
             while (name = ns->getNextProperty())
             {
                 if (strcmp(name, "url") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::URL, ns->getId(), ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::URL, ns->getString());
                 }
                 else if (strcmp(name, "audio") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::AUDIO, ns->getId(), ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::AUDIO, ns->getString());
                 }
-                else if (strcmp(name, "material") == 0)
+                else if (strncmp(name, "material", 8) == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::MATERIAL, ns->getId(), ns->getString());
+                    int materialIndex = -1;
+                    name = strchr(name, '[');
+                    if (name && strlen(name) >= 3)
+                    {
+                        std::string indexString(name);
+                        indexString = indexString.substr(1, indexString.size()-2);
+                        materialIndex = (unsigned int)atoi(indexString.c_str());
+                    }
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::MATERIAL, ns->getString(), materialIndex);
                 }
                 else if (strcmp(name, "particle") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::PARTICLE, ns->getId(), ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
                 }
                 else if (strcmp(name, "rigidbody") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::RIGIDBODY, ns->getId(), ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::RIGIDBODY, ns->getString());
                 }
                 else if (strcmp(name, "rigidbodymodel") == 0)
                 {
@@ -507,15 +526,15 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 else if (strcmp(name, "translate") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::TRANSLATE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::TRANSLATE);
                 }
                 else if (strcmp(name, "rotate") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::ROTATE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::ROTATE);
                 }
                 else if (strcmp(name, "scale") == 0)
                 {
-                    addSceneNodeProperty(SceneNodeProperty::SCALE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::SCALE);
                 }
                 else
                 {
@@ -566,64 +585,16 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         }
         else
         {
+            // TODO: Should we ignore these items? They could be used for generic properties file inheritence.
             WARN_VARG("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
         }
     }
 }
 
-void SceneLoader::calculateNodesWithMeshRigidBodies(const Properties* sceneProperties)
-{
-    const char* name = NULL;
-
-    // Make a list of all nodes with triangle mesh rigid bodies.
-    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
-    {
-        if (_nodeProperties[i]._type == SceneNodeProperty::RIGIDBODY)
-        {
-            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
-            if (p)
-            {
-                if (_nodeProperties[i]._id.size() > 0)
-                {
-                    p = p->getNamespace(_nodeProperties[i]._id.c_str());
-                }
-                else
-                {
-                    p = p->getNextNamespace();
-                }
-
-                if (p && strcmp(p->getNamespace(), "rigidbody") == 0 &&
-                    strcmp(p->getString("type"), "MESH") == 0)
-                {
-                    // If the node specifies a rigidbodymodel, then use
-                    // that node's ID; otherwise, use its ID.
-                    Properties* p = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
-                    if (p && (name = p->getString("rigidbodymodel")))
-                        _nodesWithMeshRB.push_back(name);
-                    else
-                    {
-                        const char* id = _nodeProperties[i]._nodeID;
-                        for (unsigned int j = 0; j < _nodeProperties.size(); j++)
-                        {
-                            if (_nodeProperties[j]._type == SceneNodeProperty::URL &&
-                                _nodeProperties[j]._nodeID == _nodeProperties[i]._nodeID)
-                            {
-                                id = _nodeProperties[j]._id.c_str();
-                                break;
-                            }
-                        }
-                        _nodesWithMeshRB.push_back(id);
-                    }
-                }
-            }
-        }
-    }
-}
-
 void SceneLoader::createAnimations(const Scene* scene)
 {
     // Create the scene animations.
-    for (unsigned int i = 0; i < _animations.size(); i++)
+    for (unsigned int i = 0, count = _animations.size(); i < count; i++)
     {
         // If the target node doesn't exist in the scene, then we
         // can't do anything so we skip to the next animation.
@@ -655,17 +626,6 @@ void SceneLoader::createAnimations(const Scene* scene)
     }
 }
 
-const SceneLoader::MeshRigidBodyData* SceneLoader::getMeshRigidBodyData(std::string id)
-{
-    if (!_meshRigidBodyData)
-    {
-        WARN("Attempting to get mesh rigid body data, but none has been loaded; ignoring request.");
-        return NULL;
-    }
-
-    return (_meshRigidBodyData->count(id) > 0) ? &(*_meshRigidBodyData)[id] : NULL;
-}
-
 PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
     PhysicsGenericConstraint* physicsConstraint;
@@ -755,8 +715,9 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
         WARN_VARG("Failed to load scene GPB file '%s'.", _path.c_str());
         return NULL;
     }
-    
-    Scene* scene = package->loadScene(NULL, &_nodesWithMeshRB);
+
+    const char* sceneID = strlen(sceneProperties->getId()) == 0 ? NULL : sceneProperties->getId();
+    Scene* scene = package->loadScene(sceneID);
     if (!scene)
     {
         WARN_VARG("Failed to load scene from '%s'.", _path.c_str());
@@ -862,7 +823,7 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             
             // If the constraint failed to load, continue on to the next one.
             if (!physicsConstraint)
-                    continue;
+                continue;
 
             // If the breaking impulse was specified, apply it to the constraint.
             if (constraint->getString("breakingImpulse"))

+ 28 - 26
gameplay/src/SceneLoader.h

@@ -15,8 +15,6 @@ namespace gameplay
  */
 class SceneLoader
 {
-    friend class Package;
-    friend class PhysicsController;
     friend class Scene;
 
 private:
@@ -27,13 +25,6 @@ private:
     // ------------------------------------------------------------------------
     // Helper structures and functions for SceneLoader::load(const char*).
 
-    struct MeshRigidBodyData
-    {
-        Mesh* mesh;
-        unsigned char* vertexData;
-        std::vector<unsigned char*> indexData;
-    };
-
     struct SceneAnimation
     {
         SceneAnimation(const char* animationID, const char* targetID, std::string file, std::string id)
@@ -47,27 +38,43 @@ private:
 
     struct SceneNodeProperty
     {
-        enum Type { AUDIO, MATERIAL, PARTICLE, RIGIDBODY, TRANSLATE, ROTATE, SCALE, URL };
-
-        SceneNodeProperty(Type type, const char* nodeID, std::string file, std::string id)
-            : _type(type), _nodeID(nodeID), _file(file), _id(id) {}
+        enum Type
+        {
+            AUDIO = 1,
+            MATERIAL = 2,
+            PARTICLE = 4,
+            RIGIDBODY = 8,
+            TRANSLATE = 16,
+            ROTATE = 32,
+            SCALE = 64,
+            URL = 128
+        };
+
+        SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }
 
         Type _type;
-        const char* _nodeID;
         std::string _file;
         std::string _id;
+        int _index;
+    };
+
+    struct SceneNode
+    {
+        SceneNode() : _nodeID(""), _exactMatch(true) { }
+
+        const char* _nodeID;
+        bool _exactMatch;
+        std::vector<SceneNodeProperty> _properties;
     };
 
-    static void addMeshRigidBodyData(std::string package, std::string id, Mesh* mesh, unsigned char* vertexData, unsigned int vertexByteCount);
-    static void addMeshRigidBodyData(std::string package, std::string id, unsigned char* indexData, unsigned int indexByteCount);
     static void addSceneAnimation(const char* animationID, const char* targetID, const char* url);
-    static void addSceneNodeProperty(SceneNodeProperty::Type type, const char* nodeID, const char* url = NULL);
-    static void applyNodeProperties(const Scene* scene, const Properties* sceneProperties);
+    static void addSceneNodeProperty(SceneNode& sceneNode, SceneNodeProperty::Type type, const char* url = NULL, int index = 0);
+    static void applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags);
+    static void applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp);
     static void applyNodeUrls(Scene* scene);
     static void buildReferenceTables(Properties* sceneProperties);
     static void calculateNodesWithMeshRigidBodies(const Properties* sceneProperties);
     static void createAnimations(const Scene* scene);
-    static const MeshRigidBodyData* getMeshRigidBodyData(std::string id);
     static PhysicsConstraint* loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
     static PhysicsConstraint* loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
     static Scene* loadMainSceneData(const Properties* sceneProperties);
@@ -85,14 +92,9 @@ private:
     // Holds the animations declared in the .scene file.
     static std::vector<SceneAnimation> _animations;
 
-    // Holds all the node properties declared in the .scene file.
-    static std::vector<SceneNodeProperty> _nodeProperties;
-
-    // Holds the node IDs that need to be loaded with mesh rigid body support.
-    static std::vector<std::string> _nodesWithMeshRB;
+    // Holds all the nodes+properties declared in the .scene file.
+    static std::vector<SceneNode> _sceneNodes;
 
-    // Stores the mesh data needed for triangle mesh rigid body support.
-    static std::map<std::string, MeshRigidBodyData>* _meshRigidBodyData;
 
     // The path of the main GPB for the scene being loaded.
     static std::string _path;

+ 1 - 0
gameplay/src/gameplay.h

@@ -67,6 +67,7 @@
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
 #include "PhysicsRigidBody.h"
+#include "PhysicsCharacter.h"
 
 
 // UI