Forráskód Böngészése

Changed character sample to use custom sandbox scene instead of seymour (still in progress).
Updates to SceneLoader to support wildcards on node URLs for matching multiple nodes in a scene (via asterisk at end of URL).
Other general cleanup in SceneLoader.
Changed and cleaned up how rigid body meshes are created (they are now loaded from GPB during creation).
Added default value for font size in Font::drawText.
Added some additional supported material parameter binding constants.
Fixed issue with defines when loading materials from properties files.
Fixed issue with different transform spaces being used in specular calculations in diffuse-specular shaders (other shaders still need to be updated similarly).

Steve Grenier 14 éve
szülő
commit
cb4d6cead0

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

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

+ 13 - 4
gameplay/res/shaders/colored-specular.fsh

@@ -5,12 +5,17 @@ precision highp float;
 // Uniforms
 // Uniforms
 uniform vec3 u_lightColor;                      // Light color
 uniform vec3 u_lightColor;                      // Light color
 uniform vec3 u_ambientColor;                    // Ambient 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
 uniform vec4 u_diffuseColor;                    // Diffuse color
+#endif
 
 
 // Inputs
 // Inputs
-varying vec3 v_normalVector;                    // NormalVector in view space.
+varying vec3 v_normalVector;                    // NormalVector in view space
 varying vec3 v_cameraDirection;                 // Camera direction
 varying vec3 v_cameraDirection;                 // Camera direction
+#if defined(VERTEX_COLOR)
+varying vec4 v_color;							// Vertex color
+#endif
 
 
 // Global variables
 // Global variables
 vec4 _baseColor;                                // Base color
 vec4 _baseColor;                                // Base color
@@ -103,8 +108,12 @@ void applyLight()
 
 
 void main()
 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
     // Apply light
     applyLight();
     applyLight();

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

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

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

@@ -30,7 +30,7 @@ void lighting(vec3 normalVector, vec3 cameraDirection, vec3 lightDirection, floa
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
     _diffuseColor = u_lightColor * _baseColor.rgb * diffuseIntensity;
 
 
     // Specular
     // Specular
-    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    vec3 halfVector = normalize(lightDirection + cameraDirection);
     float specularIntensity = attenuation * max(0.0, pow(dot(normalVector, halfVector), u_specularExponent));
     float specularIntensity = attenuation * max(0.0, pow(dot(normalVector, halfVector), u_specularExponent));
     specularIntensity = max(0.0, specularIntensity);
     specularIntensity = max(0.0, specularIntensity);
     _specularColor = u_lightColor * _baseColor.rgb * specularIntensity;
     _specularColor = u_lightColor * _baseColor.rgb * specularIntensity;

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

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

+ 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)
 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;
     float scale = (float)size / _size;
     char* cursor = NULL;
     char* cursor = NULL;
     if (rightToLeft)
     if (rightToLeft)
@@ -288,6 +290,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)
 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;
     float scale = (float)size / _size;
     char* token = const_cast<char*>(text);
     char* token = const_cast<char*>(text);
     const int length = strlen(text);
     const int length = strlen(text);

+ 5 - 5
gameplay/src/Font.h

@@ -131,9 +131,9 @@ public:
      * @param x The viewport x position to draw text at.
      * @param x The viewport x position to draw text at.
      * @param y The viewport y position to draw text at.
      * @param y The viewport y position to draw text at.
      * @param color The color of text.
      * @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.
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
@@ -142,13 +142,13 @@ public:
      * @param text The text to draw.
      * @param text The text to draw.
      * @param clip The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param clip The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param color The color of text.
      * @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 justify Justification of text within the viewport.
      * @param wrap Wraps text to fit within the width of the viewport if true.
      * @param wrap Wraps text to fit within the width of the viewport if true.
      * @param rightToLeft
      * @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.
      * 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");
     const char* fragmentShaderPath = passProperties->getString("fragmentShader");
     assert(fragmentShaderPath);
     assert(fragmentShaderPath);
     const char* defines = passProperties->getString("defines");
     const char* defines = passProperties->getString("defines");
-    std::string define = "";
+    std::string define;
     if (defines != NULL)
     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
     // Create the pass

+ 5 - 0
gameplay/src/Mesh.cpp

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

+ 10 - 0
gameplay/src/Mesh.h

@@ -123,6 +123,15 @@ public:
      */
      */
     static Mesh* createBoundingBox(const BoundingBox& box);
     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.
      * Gets the vertex format for the mesh.
      *
      *
@@ -295,6 +304,7 @@ private:
      */
      */
     Mesh(const Mesh& copy);
     Mesh(const Mesh& copy);
 
 
+    std::string _url;
     const VertexFormat _vertexFormat;
     const VertexFormat _vertexFormat;
     unsigned int _vertexCount;
     unsigned int _vertexCount;
     VertexBufferHandle _vertexBuffer;
     VertexBufferHandle _vertexBuffer;

+ 8 - 3
gameplay/src/Model.cpp

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

+ 11 - 2
gameplay/src/Model.h

@@ -109,6 +109,15 @@ public:
      */
      */
     Material* setMaterial(const char* materialPath, int partIndex = -1);
     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.
      * Returns the MeshSkin.
      * 
      * 
@@ -146,13 +155,13 @@ private:
      * Destructor. Hidden use release() instead.
      * Destructor. Hidden use release() instead.
      */
      */
     ~Model();
     ~Model();
-
+
     /**
     /**
      * Sets the MeshSkin for this model.
      * Sets the MeshSkin for this model.
      * 
      * 
      * @param skin 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.
      * Sets the node that is associated with this model.

+ 31 - 7
gameplay/src/Node.cpp

@@ -310,7 +310,7 @@ const Matrix& Node::getWorldMatrix() const
 const Matrix& Node::getWorldViewMatrix() const
 const Matrix& Node::getWorldViewMatrix() const
 {
 {
     static Matrix worldView;
     static Matrix worldView;
-    
+
     Matrix::multiply(getViewMatrix(), getWorldMatrix(), &worldView);
     Matrix::multiply(getViewMatrix(), getWorldMatrix(), &worldView);
 
 
     return worldView;
     return worldView;
@@ -319,18 +319,21 @@ const Matrix& Node::getWorldViewMatrix() const
 const Matrix& Node::getInverseTransposeWorldViewMatrix() const
 const Matrix& Node::getInverseTransposeWorldViewMatrix() const
 {
 {
     static Matrix invTransWorldView;
     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);
     Matrix::multiply(getViewMatrix(), getWorldMatrix(), &invTransWorldView);
     invTransWorldView.invert();
     invTransWorldView.invert();
     invTransWorldView.transpose();
     invTransWorldView.transpose();
-
     return invTransWorldView;
     return invTransWorldView;
 }
 }
 
 
+const Matrix& Node::getInverseTransposeWorldMatrix() const
+{
+    static Matrix invTransWorld;
+    invTransWorld = getWorldMatrix();
+    invTransWorld.invert();
+    invTransWorld.transpose();
+    return invTransWorld;
+}
+
 const Matrix& Node::getViewMatrix() const
 const Matrix& Node::getViewMatrix() const
 {
 {
     Scene* scene = getScene();
     Scene* scene = getScene();
@@ -437,6 +440,8 @@ Vector3 Node::getForwardVectorView() const
     Vector3 vector;
     Vector3 vector;
     getWorldMatrix().getForwardVector(&vector);
     getWorldMatrix().getForwardVector(&vector);
     getViewMatrix().transformVector(&vector);
     getViewMatrix().transformVector(&vector);
+    //getForwardVector(&vector);
+    //getWorldViewMatrix().transformVector(&vector);
     return vector;
     return vector;
 }
 }
 
 
@@ -459,6 +464,25 @@ Vector3 Node::getActiveCameraTranslationWorld() const
     return Vector3::zero();
     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()
 void Node::hierarchyChanged()
 {
 {
     // When our hierarchy changes our world transform is affected, so we must dirty it.
     // When our hierarchy changes our world transform is affected, so we must dirty it.

+ 17 - 1
gameplay/src/Node.h

@@ -171,10 +171,19 @@ public:
      */
      */
     const Matrix& getWorldViewMatrix() const;
     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.
      * 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.
      * @return The inverse world view matrix of this node.
      */
      */
@@ -259,6 +268,13 @@ public:
      */
      */
     Vector3 getActiveCameraTranslationWorld() const;
     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.
      * Returns the pointer to this node's camera.
      *
      *

+ 151 - 104
gameplay/src/Package.cpp

@@ -3,7 +3,6 @@
 #include "FileSystem.h"
 #include "FileSystem.h"
 #include "MeshPart.h"
 #include "MeshPart.h"
 #include "Scene.h"
 #include "Scene.h"
-#include "SceneLoader.h"
 #include "Joint.h"
 #include "Joint.h"
 
 
 #define GPB_PACKAGE_VERSION_MAJOR 1
 #define GPB_PACKAGE_VERSION_MAJOR 1
@@ -299,11 +298,6 @@ bool Package::readMatrix(float* m)
 }
 }
 
 
 Scene* Package::loadScene(const char* id)
 Scene* Package::loadScene(const char* id)
-{
-    return loadScene(id, NULL);
-}
-
-Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesWithMeshRB)
 {
 {
     clearLoadSession();
     clearLoadSession();
 
 
@@ -335,7 +329,7 @@ Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesW
         // Read each child directly into the scene
         // Read each child directly into the scene
         for (unsigned int i = 0; i < childrenCount; i++)
         for (unsigned int i = 0; i < childrenCount; i++)
         {
         {
-            Node* node = readNode(scene, NULL, nodesWithMeshRB);
+            Node* node = readNode(scene, NULL);
             if (node)
             if (node)
             {
             {
                 scene->addNode(node);
                 scene->addNode(node);
@@ -397,17 +391,12 @@ Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesW
 }
 }
 
 
 Node* Package::loadNode(const char* id)
 Node* Package::loadNode(const char* id)
-{
-    return loadNode(id, false);
-}
-
-Node* Package::loadNode(const char* id, bool loadWithMeshRBSupport)
 {
 {
     assert(id);
     assert(id);
 
 
     clearLoadSession();
     clearLoadSession();
 
 
-    Node* node = loadNode(id, NULL, NULL, loadWithMeshRBSupport);
+    Node* node = loadNode(id, NULL, NULL);
    
    
     if (node)
     if (node)
     {
     {
@@ -417,7 +406,7 @@ Node* Package::loadNode(const char* id, bool loadWithMeshRBSupport)
     return node;
     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);
     assert(id);
 
 
@@ -443,22 +432,13 @@ Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext,
             return NULL;
             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;
     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();
     const char* id = getIdFromOffset();
 
 
@@ -509,7 +489,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vecto
         // Read each child
         // Read each child
         for (unsigned int i = 0; i < childrenCount; i++)
         for (unsigned int i = 0; i < childrenCount; i++)
         {
         {
-            Node* child = readNode(sceneContext, nodeContext, nodesWithMeshRB);
+            Node* child = readNode(sceneContext, nodeContext);
             if (child)
             if (child)
             {
             {
                 node->addChild(child);
                 node->addChild(child);
@@ -534,23 +514,8 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vecto
         SAFE_RELEASE(light);
         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
     // Read model
-    Model* model = readModel(sceneContext, nodeContext, loadWithMeshRBSupport, node->getId());
+    Model* model = readModel(sceneContext, nodeContext, node->getId());
     if (model)
     if (model)
     {
     {
         node->setModel(model);
         node->setModel(model);
@@ -681,14 +646,14 @@ Light* Package::readLight()
     return light;
     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
     // Read mesh
     Mesh* mesh = NULL;
     Mesh* mesh = NULL;
     std::string xref = readString(_file);
     std::string xref = readString(_file);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     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)
         if (mesh)
         {
         {
             Model* model = Model::create(mesh);
             Model* model = Model::create(mesh);
@@ -816,7 +781,7 @@ void Package::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             {
             {
                 jointId = jointId.substr(1, jointId.length() - 1);
                 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)
                 if (n && n->getType() == Node::JOINT)
                 {
                 {
                     Joint* joint = static_cast<Joint*>(n);
                     Joint* joint = static_cast<Joint*>(n);
@@ -992,12 +957,12 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
 
 
 Mesh* Package::loadMesh(const char* id)
 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);
     long position = ftell(_file);
 
 
     // Seek to the specified Mesh
     // Seek to the specified Mesh
@@ -1007,6 +972,56 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
         return NULL;
         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
     // Read vertex format/elements
     unsigned int vertexElementCount;
     unsigned int vertexElementCount;
     if (fread(&vertexElementCount, 4, 1, _file) != 1 || vertexElementCount < 1)
     if (fread(&vertexElementCount, 4, 1, _file) != 1 || vertexElementCount < 1)
@@ -1027,60 +1042,42 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
         vertexElements[i].size = vSize;
         vertexElements[i].size = vSize;
     }
     }
 
 
-    // Create VertexFormat
-    VertexFormat vertexFormat(vertexElements, vertexElementCount);
+    MeshData* meshData = new MeshData(VertexFormat(vertexElements, vertexElementCount));
+
     SAFE_DELETE_ARRAY(vertexElements);
     SAFE_DELETE_ARRAY(vertexElements);
 
 
     // Read vertex data
     // Read vertex data
     unsigned int vertexByteCount;
     unsigned int vertexByteCount;
     if (fread(&vertexByteCount, 4, 1, _file) != 1 || vertexByteCount == 0)
     if (fread(&vertexByteCount, 4, 1, _file) != 1 || vertexByteCount == 0)
     {
     {
+        SAFE_DELETE(meshData);
         return NULL;
         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;
         return NULL;
     }
     }
 
 
     // Read mesh bounds (bounding box and bounding sphere)
     // 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;
         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;
         return NULL;
     }
     }
-    mesh->setVertexData(vertexData, 0, vertexCount);
-    if (loadWithMeshRBSupport)
-        SceneLoader::addMeshRigidBodyData(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
     // Read mesh parts
     unsigned int meshPartCount;
     unsigned int meshPartCount;
     if (fread(&meshPartCount, 4, 1, _file) != 1)
     if (fread(&meshPartCount, 4, 1, _file) != 1)
     {
     {
-        SAFE_RELEASE(mesh);
+        SAFE_DELETE(meshData);
         return NULL;
         return NULL;
     }
     }
     for (unsigned int i = 0; i < meshPartCount; ++i)
     for (unsigned int i = 0; i < meshPartCount; ++i)
@@ -1091,23 +1088,18 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
             fread(&iFormat, 4, 1, _file) != 1 ||
             fread(&iFormat, 4, 1, _file) != 1 ||
             fread(&iByteCount, 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;
             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;
         unsigned int indexSize = 0;
-        switch (indexFormat)
+        switch (partData->indexFormat)
         {
         {
         case Mesh::INDEX8:
         case Mesh::INDEX8:
             indexSize = 1;
             indexSize = 1;
@@ -1119,23 +1111,53 @@ Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char*
             indexSize = 4;
             indexSize = 4;
             break;
             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;
             return NULL;
         }
         }
-        part->setIndexData(indexData, 0, indexCount);
-        if (loadWithMeshRBSupport)
-            SceneLoader::addMeshRigidBodyData(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)
 Font* Package::loadFont(const char* id)
@@ -1265,8 +1287,8 @@ const char* Package::getObjectID(unsigned int index) const
     return (index >= _referenceCount ? NULL : _references[index].id.c_str());
     return (index >= _referenceCount ? NULL : _references[index].id.c_str());
 }
 }
 
 
-Package::Reference::Reference() :
-    type(0), offset(0)
+Package::Reference::Reference()
+    : type(0), offset(0)
 {
 {
 }
 }
 
 
@@ -1274,4 +1296,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
 class Package : public Ref
 {
 {
-    friend class SceneLoader;
+    friend class PhysicsController;
 
 
 public:
 public:
 
 
@@ -118,6 +118,31 @@ private:
         std::vector<Matrix> inverseBindPoseMatrices;
         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);
     Package(const char* path);
 
 
     /**
     /**
@@ -173,46 +198,22 @@ private:
      */
      */
     Reference* seekToFirstType(unsigned int type);
     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.
      * Internal method to load a node.
      *
      *
      * Only one of node or scene should be passed as non-NULL (or neither).
      * 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.
      * Loads a mesh with the specified ID from the package.
      *
      *
      * @param id The ID of the mesh to load.
      * @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.
      * @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.
      * @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.
      * Reads an unsigned int from the current file position.
@@ -287,7 +288,7 @@ private:
      * 
      * 
      * @return A pointer to new node or NULL if there was an error.
      * @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.
      * Reads a camera from the current file position.
@@ -308,7 +309,25 @@ private:
      * 
      * 
      * @return A pointer to a new model or NULL if there was an error.
      * @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.
      * Reads a mesh skin from the current file position.
@@ -356,6 +375,7 @@ private:
     void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
     void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
 
 
 private:
 private:
+
     std::string _path;
     std::string _path;
     unsigned int _referenceCount;
     unsigned int _referenceCount;
     Reference* _references;
     Reference* _references;

+ 56 - 25
gameplay/src/PhysicsController.cpp

@@ -3,7 +3,7 @@
 #include "MeshPart.h"
 #include "MeshPart.h"
 #include "PhysicsController.h"
 #include "PhysicsController.h"
 #include "PhysicsMotionState.h"
 #include "PhysicsMotionState.h"
-#include "SceneLoader.h"
+#include "Package.h"
 
 
 // The initial capacity of the Bullet debug drawer's vertex batch.
 // The initial capacity of the Bullet debug drawer's vertex batch.
 #define INITIAL_CAPACITY 280
 #define INITIAL_CAPACITY 280
@@ -403,23 +403,51 @@ btCollisionShape* PhysicsController::createSphere(float radius, const btVector3&
     // Create the sphere shape and add it to the cache.
     // Create the sphere shape and add it to the cache.
     btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
     btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
     _shapes.push_back(new PhysicsCollisionShape(sphere));
     _shapes.push_back(new PhysicsCollisionShape(sphere));
-    
+
     return sphere;
     return sphere;
 }
 }
 
 
 btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
 btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
 {
 {
-    // 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.
     // Copy the scaled vertex position data to the rigid body's local buffer.
     Matrix m;
     Matrix m;
     Matrix::createScale(body->_node->getScaleX(), body->_node->getScaleY(), body->_node->getScaleZ(), &m);
     Matrix::createScale(body->_node->getScaleX(), body->_node->getScaleY(), body->_node->getScaleZ(), &m);
-    unsigned int vertexCount = data->mesh->getVertexCount();
-    body->_vertexData = new float[vertexCount * 3];
+    body->_vertexData = new float[data->vertexCount * 3];
     Vector3 v;
     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)]),
         v.set(*((float*)&data->vertexData[i * vertexStride + 0 * sizeof(float)]),
               *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
               *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
@@ -427,19 +455,20 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
         v *= m;
         v *= m;
         memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
         memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
     }
     }
-    
+
     btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
     btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
 
 
-    if (data->mesh->getPartCount() > 0)
+    unsigned int partCount = data->parts.size();
+    if (partCount > 0)
     {
     {
         PHY_ScalarType indexType = PHY_UCHAR;
         PHY_ScalarType indexType = PHY_UCHAR;
         int indexStride = 0;
         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:
             case Mesh::INDEX8:
                 indexType = PHY_UCHAR;
                 indexType = PHY_UCHAR;
@@ -455,17 +484,16 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
                 break;
                 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.
             // Create a btIndexedMesh object for the current mesh part.
             btIndexedMesh indexedMesh;
             btIndexedMesh indexedMesh;
             indexedMesh.m_indexType = indexType;
             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_triangleIndexBase = (const unsigned char*)body->_indexData[i];
             indexedMesh.m_triangleIndexStride = indexStride;
             indexedMesh.m_triangleIndexStride = indexStride;
             indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
             indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
@@ -479,8 +507,8 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
     else
     else
     {
     {
         // Generate index data for the mesh locally in the rigid body.
         // 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;
             indexData[i] = i;
         }
         }
@@ -489,8 +517,8 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
         // Create a single btIndexedMesh object for the mesh interface.
         // Create a single btIndexedMesh object for the mesh interface.
         btIndexedMesh indexedMesh;
         btIndexedMesh indexedMesh;
         indexedMesh.m_indexType = PHY_INTEGER;
         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_triangleIndexBase = body->_indexData[0];
         indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
         indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
         indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
         indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
@@ -504,6 +532,9 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
     btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
     btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
     _shapes.push_back(new PhysicsCollisionShape(shape));
     _shapes.push_back(new PhysicsCollisionShape(shape));
 
 
+    // Free the temporary mesh data now that it's stored in physics system
+    SAFE_DELETE(data);
+
     return shape;
     return shape;
 }
 }
 
 

+ 16 - 0
gameplay/src/RenderState.cpp

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

+ 10 - 0
gameplay/src/RenderState.h

@@ -57,6 +57,11 @@ public:
          */
          */
         WORLD_VIEW_PROJECTION_MATRIX,
         WORLD_VIEW_PROJECTION_MATRIX,
 
 
+        /**
+         * Binds a node's InverseTransposeWorl matrix.
+         */
+        INVERSE_TRANSPOSE_WORLD_MATRIX,
+
         /**
         /**
          * Binds a node's InverseTransposeWorldView matrix.
          * Binds a node's InverseTransposeWorldView matrix.
          */
          */
@@ -67,6 +72,11 @@ public:
          */
          */
         CAMERA_WORLD_POSITION,
         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.
          * 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;
     _viewport = viewport;
 }
 }
 
 
-const Vector3& Scene::getAmbientColor()
+const Vector3& Scene::getAmbientColor() const
 {
 {
     return _ambientColor;
     return _ambientColor;
 }
 }

+ 1 - 1
gameplay/src/Scene.h

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

+ 284 - 281
gameplay/src/SceneLoader.cpp

@@ -9,9 +9,7 @@ namespace gameplay
 // Static member variables.
 // Static member variables.
 std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 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;
 
 
 Scene* SceneLoader::load(const char* filePath)
 Scene* SceneLoader::load(const char* filePath)
 {
 {
@@ -35,27 +33,11 @@ Scene* SceneLoader::load(const char* filePath)
         SAFE_DELETE(properties);
         SAFE_DELETE(properties);
         return NULL;
         return NULL;
     }
     }
-    
 
 
     // Build the node URL/property and animation reference tables and load the referenced files.
     // Build the node URL/property and animation reference tables and load the referenced files.
     buildReferenceTables(sceneProperties);
     buildReferenceTables(sceneProperties);
     loadReferencedFiles();
     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.
     // Load the main scene data from GPB and apply the global scene properties.
     Scene* scene = loadMainSceneData(sceneProperties);
     Scene* scene = loadMainSceneData(sceneProperties);
     if (!scene)
     if (!scene)
@@ -66,8 +48,18 @@ Scene* SceneLoader::load(const char* filePath)
 
 
     // First apply the node url properties. Following that,
     // First apply the node url properties. Following that,
     // apply the normal node properties and create the animations.
     // 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);
     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);
     createAnimations(scene);
 
 
     // Find the physics properties object.
     // Find the physics properties object.
@@ -98,65 +90,21 @@ Scene* SceneLoader::load(const char* filePath)
     // Clean up the .scene file's properties object.
     // Clean up the .scene file's properties object.
     SAFE_DELETE(properties);
     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.
     // Clear all temporary data stores.
     _propertiesFromFile.clear();
     _propertiesFromFile.clear();
     _animations.clear();
     _animations.clear();
-    _nodeProperties.clear();
-    _nodesWithMeshRB.clear();
+    _sceneNodes.clear();
 
 
     return scene;
     return scene;
 }
 }
 
 
-void SceneLoader::addMeshRigidBodyData(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;
-    }
-
-    (*_meshRigidBodyData)[id].mesh = mesh;
-    (*_meshRigidBodyData)[id].vertexData = new unsigned char[vertexByteCount];
-    memcpy((*_meshRigidBodyData)[id].vertexData, vertexData, vertexByteCount);
-}
-
-void SceneLoader::addMeshRigidBodyData(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;
-    }
-
-    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)
 void SceneLoader::addSceneAnimation(const char* animationID, const char* targetID, const char* url)
 {
 {
     // Calculate the file and id from the given url.
     // Calculate the file and id from the given url.
     std::string file;
     std::string file;
     std::string id;
     std::string id;
     splitURL(url, &file, &id);
     splitURL(url, &file, &id);
-    
+
     // If there is a file that needs to be loaded later, add an 
     // If there is a file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
     // empty entry to the properties table to signify it.
     if (file.length() > 0 && _propertiesFromFile.count(file) == 0)
     if (file.length() > 0 && _propertiesFromFile.count(file) == 0)
@@ -166,166 +114,154 @@ void SceneLoader::addSceneAnimation(const char* animationID, const char* targetI
     _animations.push_back(SceneAnimation(animationID, targetID, file, id));
     _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.
     // Calculate the file and id from the given url.
     std::string file;
     std::string file;
     std::string id;
     std::string id;
     splitURL(url, &file, &id);
     splitURL(url, &file, &id);
-    
+
     // If there is a non-GPB file that needs to be loaded later, add an 
     // If there is a non-GPB file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
     // empty entry to the properties table to signify it.
     if (file.length() > 0 && file.find(".gpb") == file.npos && _propertiesFromFile.count(file) == 0)
     if (file.length() > 0 && file.find(".gpb") == file.npos && _propertiesFromFile.count(file) == 0)
         _propertiesFromFile[file] = NULL;
         _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.
     // 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;
                 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);
-                }
+        }
+    }
+}
 
 
-                break;
-            case SceneNodeProperty::PARTICLE:
+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;
+        }
+
+        // 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
         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.
             // Check to make sure the referenced properties object was loaded properly.
-            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
+            Properties* p = _propertiesFromFile[snp._file];
             if (!p)
             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 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)
                 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
             else
@@ -336,11 +272,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.
             // 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;
             const char* name = NULL;
             if (np && (name = np->getString("rigidbodymodel")))
             if (np && (name = np->getString("rigidbodymodel")))
             {
             {
-                Node* modelNode = scene->findNode(name);
+                Node* modelNode = node->getScene()->findNode(name);
                 if (!modelNode)
                 if (!modelNode)
                     WARN_VARG("Node '%s' does not exist; attempting to use its model for rigid body creation.", name);
                     WARN_VARG("Node '%s' does not exist; attempting to use its model for rigid body creation.", name);
                 else
                 else
@@ -358,9 +294,48 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
                 }
                 }
             }
             }
             else if (!node->getModel())
             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
             else
                 node->setPhysicsRigidBody(p);
                 node->setPhysicsRigidBody(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;
         }
         }
     }
     }
 }
 }
@@ -369,65 +344,126 @@ void SceneLoader::applyNodeUrls(Scene* scene)
 {
 {
     // Apply all URL node properties so that when we go to apply
     // Apply all URL node properties so that when we go to apply
     // the other node properties, the node is in the scene.
     // 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
                     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);
                             scene->addNode(node);
                             SAFE_RELEASE(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
                 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.
             // Remove the node property since we are done applying it.
-            _nodeProperties.erase(_nodeProperties.begin() + i);
+            sceneNode._properties.erase(sceneNode._properties.begin() + j);
         }
         }
-        else
-            i++;
     }
     }
 }
 }
 
 
@@ -446,27 +482,40 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 continue;
                 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())
             while (name = ns->getNextProperty())
             {
             {
                 if (strcmp(name, "url") == 0)
                 if (strcmp(name, "url") == 0)
                 {
                 {
-                    addSceneNodeProperty(SceneNodeProperty::URL, ns->getId(), ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::URL, ns->getString());
                 }
                 }
                 else if (strcmp(name, "audio") == 0)
                 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)
                 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)
                 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)
                 else if (strcmp(name, "rigidbodymodel") == 0)
                 {
                 {
@@ -474,15 +523,15 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 }
                 else if (strcmp(name, "translate") == 0)
                 else if (strcmp(name, "translate") == 0)
                 {
                 {
-                    addSceneNodeProperty(SceneNodeProperty::TRANSLATE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::TRANSLATE);
                 }
                 }
                 else if (strcmp(name, "rotate") == 0)
                 else if (strcmp(name, "rotate") == 0)
                 {
                 {
-                    addSceneNodeProperty(SceneNodeProperty::ROTATE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::ROTATE);
                 }
                 }
                 else if (strcmp(name, "scale") == 0)
                 else if (strcmp(name, "scale") == 0)
                 {
                 {
-                    addSceneNodeProperty(SceneNodeProperty::SCALE, ns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::SCALE);
                 }
                 }
                 else
                 else
                 {
                 {
@@ -533,52 +582,16 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         }
         }
         else
         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());
             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
-                        _nodesWithMeshRB.push_back(_nodeProperties[i]._nodeID);
-                }
-            }
-        }
-    }
-}
-
 void SceneLoader::createAnimations(const Scene* scene)
 void SceneLoader::createAnimations(const Scene* scene)
 {
 {
     // Create the scene animations.
     // 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
         // If the target node doesn't exist in the scene, then we
         // can't do anything so we skip to the next animation.
         // can't do anything so we skip to the next animation.
@@ -610,17 +623,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)
 PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
 {
     PhysicsGenericConstraint* physicsConstraint;
     PhysicsGenericConstraint* physicsConstraint;
@@ -711,8 +713,9 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
         WARN_VARG("Failed to load scene GPB file '%s'.", path);
         WARN_VARG("Failed to load scene GPB file '%s'.", path);
         return NULL;
         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)
     if (!scene)
     {
     {
         WARN_VARG("Failed to load scene from '%s'.", path);
         WARN_VARG("Failed to load scene from '%s'.", path);
@@ -818,7 +821,7 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             
             
             // If the constraint failed to load, continue on to the next one.
             // If the constraint failed to load, continue on to the next one.
             if (!physicsConstraint)
             if (!physicsConstraint)
-                    continue;
+                continue;
 
 
             // If the breaking impulse was specified, apply it to the constraint.
             // If the breaking impulse was specified, apply it to the constraint.
             if (constraint->getString("breakingImpulse"))
             if (constraint->getString("breakingImpulse"))

+ 28 - 26
gameplay/src/SceneLoader.h

@@ -15,8 +15,6 @@ namespace gameplay
  */
  */
 class SceneLoader
 class SceneLoader
 {
 {
-    friend class Package;
-    friend class PhysicsController;
     friend class Scene;
     friend class Scene;
 
 
 private:
 private:
@@ -27,13 +25,6 @@ private:
     // ------------------------------------------------------------------------
     // ------------------------------------------------------------------------
     // Helper structures and functions for SceneLoader::load(const char*).
     // Helper structures and functions for SceneLoader::load(const char*).
 
 
-    struct MeshRigidBodyData
-    {
-        Mesh* mesh;
-        unsigned char* vertexData;
-        std::vector<unsigned char*> indexData;
-    };
-
     struct SceneAnimation
     struct SceneAnimation
     {
     {
         SceneAnimation(const char* animationID, const char* targetID, std::string file, std::string id)
         SceneAnimation(const char* animationID, const char* targetID, std::string file, std::string id)
@@ -47,27 +38,43 @@ private:
 
 
     struct SceneNodeProperty
     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;
         Type _type;
-        const char* _nodeID;
         std::string _file;
         std::string _file;
         std::string _id;
         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 id, Mesh* mesh, unsigned char* vertexData, unsigned int vertexByteCount);
-    static void addMeshRigidBodyData(std::string id, unsigned char* indexData, unsigned int indexByteCount);
     static void addSceneAnimation(const char* animationID, const char* targetID, const char* url);
     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 applyNodeUrls(Scene* scene);
     static void buildReferenceTables(Properties* sceneProperties);
     static void buildReferenceTables(Properties* sceneProperties);
     static void calculateNodesWithMeshRigidBodies(const Properties* sceneProperties);
     static void calculateNodesWithMeshRigidBodies(const Properties* sceneProperties);
     static void createAnimations(const Scene* scene);
     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* loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
     static PhysicsConstraint* loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
     static PhysicsConstraint* loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
     static Scene* loadMainSceneData(const Properties* sceneProperties);
     static Scene* loadMainSceneData(const Properties* sceneProperties);
@@ -85,14 +92,9 @@ private:
     // Holds the animations declared in the .scene file.
     // Holds the animations declared in the .scene file.
     static std::vector<SceneAnimation> _animations;
     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;
 };
 };
 
 
 }
 }