Sfoglia il codice sorgente

- PhysicsCharacter changes.
- Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-sgrenier

Steve Grenier 14 anni fa
parent
commit
67ecff296c
60 ha cambiato i file con 1212 aggiunte e 430 eliminazioni
  1. 1 0
      gameplay-encoder/src/DAESceneEncoder.cpp
  2. 16 15
      gameplay-encoder/src/FBXSceneEncoder.cpp
  3. 26 1
      gameplay-encoder/src/GPBFile.cpp
  4. 4 0
      gameplay-encoder/src/GPBFile.h
  5. 0 2
      gameplay-encoder/src/Mesh.cpp
  6. 2 4
      gameplay-encoder/src/Mesh.h
  7. 245 0
      gameplay-newproject.sh
  8. 27 17
      gameplay/src/Animation.cpp
  9. 8 0
      gameplay/src/Animation.h
  10. 100 36
      gameplay/src/AnimationClip.cpp
  11. 60 25
      gameplay/src/AnimationClip.h
  12. 51 18
      gameplay/src/AnimationController.cpp
  13. 10 10
      gameplay/src/AnimationController.h
  14. 23 0
      gameplay/src/AnimationTarget.cpp
  15. 2 0
      gameplay/src/AnimationTarget.h
  16. 43 6
      gameplay/src/AudioBuffer.cpp
  17. 4 2
      gameplay/src/Base.h
  18. 1 1
      gameplay/src/BoundingBox.h
  19. 1 1
      gameplay/src/BoundingBox.inl
  20. 1 1
      gameplay/src/BoundingSphere.h
  21. 1 1
      gameplay/src/BoundingSphere.inl
  22. 2 1
      gameplay/src/DebugNew.cpp
  23. 10 10
      gameplay/src/Font.cpp
  24. 1 1
      gameplay/src/Font.h
  25. 1 1
      gameplay/src/Matrix.cpp
  26. 9 9
      gameplay/src/Matrix.h
  27. 6 6
      gameplay/src/Matrix.inl
  28. 22 2
      gameplay/src/ParticleEmitter.cpp
  29. 7 0
      gameplay/src/ParticleEmitter.h
  30. 1 1
      gameplay/src/PhysicsConstraint.cpp
  31. 177 66
      gameplay/src/PhysicsController.cpp
  32. 41 4
      gameplay/src/PhysicsController.h
  33. 3 3
      gameplay/src/PhysicsGenericConstraint.cpp
  34. 8 8
      gameplay/src/PhysicsGenericConstraint.inl
  35. 3 3
      gameplay/src/PhysicsHingeConstraint.cpp
  36. 4 5
      gameplay/src/PhysicsMotionState.cpp
  37. 26 54
      gameplay/src/PhysicsRigidBody.cpp
  38. 2 20
      gameplay/src/PhysicsRigidBody.h
  39. 6 5
      gameplay/src/PhysicsRigidBody.inl
  40. 2 2
      gameplay/src/PhysicsSocketConstraint.cpp
  41. 2 2
      gameplay/src/PhysicsSpringConstraint.cpp
  42. 1 1
      gameplay/src/Plane.h
  43. 1 1
      gameplay/src/Plane.inl
  44. 63 20
      gameplay/src/PlatformQNX.cpp
  45. 12 11
      gameplay/src/Properties.cpp
  46. 1 1
      gameplay/src/Quaternion.h
  47. 1 1
      gameplay/src/Quaternion.inl
  48. 1 1
      gameplay/src/Ray.h
  49. 1 1
      gameplay/src/Ray.inl
  50. 6 4
      gameplay/src/SceneLoader.cpp
  51. 3 0
      gameplay/src/SceneLoader.h
  52. 62 10
      gameplay/src/SpriteBatch.cpp
  53. 24 2
      gameplay/src/SpriteBatch.h
  54. 2 1
      gameplay/src/Texture.cpp
  55. 14 5
      gameplay/src/Vector2.h
  56. 10 5
      gameplay/src/Vector2.inl
  57. 17 8
      gameplay/src/Vector3.h
  58. 10 5
      gameplay/src/Vector3.inl
  59. 14 5
      gameplay/src/Vector4.h
  60. 10 5
      gameplay/src/Vector4.inl

+ 1 - 0
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -627,6 +627,7 @@ bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChann
                     Quaternion rotation;
                     Vector3 translation;
                     matrix.decompose(&scale, &rotation, &translation);
+                    rotation.normalize();
 
                     size_t k = i * 10;
                     floats[k+0] = scale.x;

+ 16 - 15
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -15,7 +15,7 @@ using namespace gameplay;
  * 
  * @return The aspect ratio from the camera.
  */
-float getAspectRatio(KFbxCamera* fbxCamera);
+static float getAspectRatio(KFbxCamera* fbxCamera);
 
 /**
  * Returns the field of view Y from the given camera.
@@ -24,7 +24,7 @@ float getAspectRatio(KFbxCamera* fbxCamera);
  * 
  * @return The field of view Y.
  */
-float getFieldOfView(KFbxCamera* fbxCamera);
+static float getFieldOfView(KFbxCamera* fbxCamera);
 
 /**
  * Loads the texture coordinates from given mesh's polygon part into the vertex.
@@ -34,7 +34,7 @@ float getFieldOfView(KFbxCamera* fbxCamera);
  * @param posInPoly The position in the polygon.
  * @param vertex The vertex to copy the texture coordinates to.
  */
-void loadTextureCoords(KFbxMesh* fbxMesh, int polyIndex, int posInPoly, Vertex* vertex);
+static void loadTextureCoords(KFbxMesh* fbxMesh, int polyIndex, int posInPoly, Vertex* vertex);
 
 /**
  * Loads the normal from the mesh and adds it to the given vertex.
@@ -43,7 +43,7 @@ void loadTextureCoords(KFbxMesh* fbxMesh, int polyIndex, int posInPoly, Vertex*
  * @param vertexIndex The vertex index in the mesh.
  * @param vertex The vertex to copy to.
  */
-void loadNormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadNormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
 
 /**
  * Loads the tangent from the mesh and adds it to the given vertex.
@@ -52,7 +52,7 @@ void loadNormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
  * @param vertexIndex The index of the vertex within fbxMesh.
  * @param vertex The vertex to copy to.
  */
-void loadTangent(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadTangent(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
 
 /**
  * Loads the binormal from the mesh and adds it to the given vertex.
@@ -61,7 +61,7 @@ void loadTangent(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
  * @param vertexIndex The index of the vertex within fbxMesh.
  * @param vertex The vertex to copy to.
  */
-void loadBinormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadBinormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
 
 /**
  * Loads the vertex diffuse color from the mesh and adds it to the given vertex.
@@ -70,7 +70,7 @@ void loadBinormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
  * @param vertexIndex The index of the vertex within fbxMesh.
  * @param vertex The vertex to copy to.
  */
-void loadVertexColor(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+static void loadVertexColor(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
 
 /**
  * Loads the blend weight and blend indices data into the vertex.
@@ -78,7 +78,7 @@ void loadVertexColor(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
  * @param vertexWeights List of vertex weights. The x member contains the blendIndices. The y member contains the blendWeights.
  * @param vertex The vertex to copy the blend data to.
  */
-void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
+static void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
 
 /**
  * Loads the blend weights and blend indices from the given mesh.
@@ -90,17 +90,17 @@ void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
  * 
  * @return True if this mesh has a mesh skin, false otherwise.
  */
-bool loadBlendWeights(KFbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
+static bool loadBlendWeights(KFbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
 
 /**
  * Copies from an FBX matrix to a float[16] array.
  */
-void copyMatrix(const KFbxMatrix& fbxMatrix, float* matrix);
+static void copyMatrix(const KFbxMatrix& fbxMatrix, float* matrix);
 
 /**
  * Copies from an FBX matrix to a gameplay matrix.
  */
-void copyMatrix(const KFbxMatrix& fbxMatrix, Matrix& matrix);
+static void copyMatrix(const KFbxMatrix& fbxMatrix, Matrix& matrix);
 
 /**
  * Finds the min and max start time and stop time of the given animation curve.
@@ -114,7 +114,7 @@ void copyMatrix(const KFbxMatrix& fbxMatrix, Matrix& matrix);
  * @param stopTime The max stop time. (in/out)
  * @param frameRate The frame rate. (in/out)
  */
-void findMinMaxTime(KFbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
+static void findMinMaxTime(KFbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
 
 /**
  * Appends a key frame of the given node's transform at the given time.
@@ -124,7 +124,7 @@ void findMinMaxTime(KFbxAnimCurve* animCurve, float* startTime, float* stopTime,
  * @param keyTimes The list of key times to append to.
  * @param keyValues The list of key values to append to.
  */
-void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes, std::vector<float>* keyValues);
+static void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes, std::vector<float>* keyValues);
 
 /**
  * Decomposes the given node's matrix transform at the given time and copies to scale, rotation and translation.
@@ -135,7 +135,7 @@ void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes,
  * @param rotation The rotation to copy to.
  * @param translation The translation to copy to.
  */
-void decompose(KFbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
+static void decompose(KFbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
 
 /**
  * Creates an animation channel that targets the given node and target attribute using the given key times and key values.
@@ -147,7 +147,7 @@ void decompose(KFbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotati
  * 
  * @return The newly created animation channel.
  */
-AnimationChannel* createAnimationChannel(KFbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
+static AnimationChannel* createAnimationChannel(KFbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
 
 void addScaleChannel(Animation* animation, KFbxNode* fbxNode, float startTime, float stopTime);
 
@@ -1274,6 +1274,7 @@ void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes,
     Quaternion rotation;
     Vector3 translation;
     matrix.decompose(&scale, &rotation, &translation);
+    rotation.normalize();
 
     keyTimes->push_back(time);
     keyValues->push_back(scale.x);

+ 26 - 1
gameplay-encoder/src/GPBFile.cpp

@@ -253,8 +253,13 @@ void GPBFile::adjust()
         }
     }
 
+    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
+    {
+        computeBounds(*i);
+    }
+
     // try to convert joint transform animations into rotation animations
-    //optimizeTransformAnimations(); // TODO: Fix bounding sphere before re-enabling this
+    //optimizeTransformAnimations();
 
     // TODO:
     // remove ambient _lights
@@ -270,6 +275,26 @@ void GPBFile::adjust()
     //   This can be merged into one animation. Same for scale animations.
 }
 
+void GPBFile::computeBounds(Node* node)
+{
+    assert(node);
+    if (Model* model = node->getModel())
+    {
+        if (Mesh* mesh = model->getMesh())
+        {
+            mesh->computeBounds();
+        }
+        if (MeshSkin* skin = model->getSkin())
+        {
+            skin->computeBounds();
+        }
+    }
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        computeBounds(child);
+    }
+}
+
 void GPBFile::optimizeTransformAnimations()
 {
     const unsigned int animationCount = _animations.getAnimationCount();

+ 4 - 0
gameplay-encoder/src/GPBFile.h

@@ -99,6 +99,10 @@ public:
     void adjust();
 
 private:
+    /**
+     * Computes the bounds of all meshes in the node hierarchy.
+     */
+    void computeBounds(Node* node);
     void optimizeTransformAnimations();
 
     /**

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

@@ -273,7 +273,6 @@ void Mesh::writeBinaryVertices(FILE* file)
     }
 
     // Write bounds
-    computeBounds();
     write(&bounds.min.x, 3, file);
     write(&bounds.max.x, 3, file);
     write(&bounds.center.x, 3, file);
@@ -302,7 +301,6 @@ void Mesh::writeText(FILE* file)
     fprintf(file, "</vertices>\n");
 
     // write bounds
-    computeBounds();
     fprintf(file, "<bounds>\n");
     fprintf(file, "<min>\n");
     writeVectorText(bounds.min, file);

+ 2 - 4
gameplay-encoder/src/Mesh.h

@@ -65,16 +65,14 @@ public:
      */
     void generateHeightmap(const char* filename);
 
+    void computeBounds();
+
     Model* model;
     std::vector<Vertex> vertices;
     std::vector<MeshPart*> parts;
     BoundingVolume bounds;
     std::map<Vertex, unsigned int> vertexLookupTable;
 
-private:
-
-    void computeBounds();
-
 private:
     std::vector<VertexElement> _vertexFormat;
 

+ 245 - 0
gameplay-newproject.sh

@@ -0,0 +1,245 @@
+#/bin/bash
+# ********************************************************************
+#
+# generate-project.sh
+#
+# This script generates a set of gameplay project files.
+# The new project will be based of the gameplay-template project and 
+# it will be generated with the name and location that is specified
+# as input parameters.
+#
+# IMPORTANT: This script must be run from the root of the gameplay
+# source tree.
+#
+# ********************************************************************
+
+echo
+echo "1. Enter a name for the new project."
+echo
+echo "   This name will be given to the project"
+echo "   executable and a folder with this name"
+echo "   will be created to store all project files."
+echo
+read -p "Project Name: " "" projName 
+if [[ "$projName" == "" ]]; then
+	echo
+	echo "ERROR: No project name specified."
+	echo
+	exit -1;
+fi
+echo 
+
+echo
+echo "2. Enter a game title."
+echo
+echo "   On some platforms, this title is used to"
+echo "   identify the game during installation and"
+echo "   on shortcuts/icons."
+echo
+read -p "Title: " "" title 
+if [[ "$title" == "" ]]; then
+	echo
+	echo "ERROR: No game title specified."
+	echo
+	exit -1;
+fi
+echo 
+
+echo
+echo "3. Enter a short game description."
+echo
+read -p "Description: " "" desc
+if [[ "$desc" == "" ]]; then
+	desc=$title
+fi
+echo 
+
+echo
+echo "4. Enter a unique identifier for your project."
+echo
+echo "   This should be a human readable package name,"
+echo "   containing at least two words separated by a"
+echo "   period (eg. com.surname.gamename)."
+echo
+read -p "Unique ID: " "" uuid
+if [[ "$uuid" == "" ]]; then
+	echo
+	echo "ERROR: No uuid specified."
+	echo
+	exit -1;
+fi
+echo 
+
+echo
+echo "5. Enter author name."
+echo
+echo "   On BlackBerry targets, this is used for"
+echo "   signing and must match the developer name"
+echo "   of your development certificate."
+echo
+read -p "Author: " "" author
+if [[ "$author" == "" ]]; then
+	echo
+	echo "ERROR: No author specified."
+	echo
+	exit -1;
+fi
+echo 
+
+echo
+echo "6. Enter your game's main class name."
+echo
+echo "   Your initial game header and source file"
+echo "   will be given this name and a class with"
+echo "   this name will be created in these files."
+echo
+read -p "Class name: " "" className
+if [[ "$className" == "" ]]; then
+	echo
+	echo "ERROR: No class name specified."
+	echo
+	exit -1;
+fi
+echo 
+
+echo
+echo "7. Enter the project path."
+echo
+echo "   This can be a relative path, absolute path,"
+echo "   or empty for the current folder. Note that"
+echo "   a project folder named $projName will also"
+echo "   be created inside this folder."
+echo
+read -p "Path: " "" location
+if [[ "$location" == "" ]]; then
+	projPath=$projName
+else
+	projPath="$location/$projName"
+fi
+echo
+
+
+# Verify Path and eliminate double '//'
+projPath=`echo "$projPath" | sed 's_//_/_g'`
+if [ -e $projPath ]; then
+	echo
+	echo "ERROR: Path '$projPath' already exists, aborting."
+	echo
+	exit -2
+fi
+
+# Generate relative path from project folder to gameplay folder
+gpPathAbs=`pwd`
+common_path=$projPath
+back=
+while [ "${gpPathAbs#$common_path}" = "${gpPathAbs}" ]; do
+	common_path=$(dirname $common_path)
+	back="../${back}"
+done
+gpPath=${back}${gpPathAbs#$common_path/}
+
+# Make required source folder directories
+mkdir -p "$projPath/src"
+mkdir -p "$projPath/res"
+
+# Below does copy, then uses 'sed' with -i for inplace editing
+# Alternative below uses sed to do a input then output skipping the copy
+# sed "s/TEMPLATE_PROJECT/$projectName/g" "gameplay-template/gameplay-template.vcxproj" > "$projPath/$projName.vcxproj"
+
+#############################################
+# Copy Microsoft Virtual Studio project files
+cp "gameplay-template/gameplay-template.vcxproj" "$projPath/$projName.vcxproj"
+sed -i "" "s*TEMPLATE_PROJECT*$projectName*g" "$projPath/$projName.vcxproj"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/$projName.vcxproj"
+sed -i "" "s*GAMEPLAY_PATH*$gpPath*g" "$projPath/$projName.vcxproj"
+
+cp "gameplay-template/gameplay-template.vcxproj.filters" "$projPath/$projName.vcxproj.filters"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/$projName.vcxproj.filters"
+
+cp "gameplay-template/gameplay-template.vcxproj.user" "$projPath/$projName.vcxproj.user"
+sed -i "" "s*GAMEPLAY_PATH*$gpPath*g" "$projPath/$projName.vcxproj.user"
+
+
+#############################################
+# Copy Apple XCode project files
+mkdir -p "$projPath/$projName.xcodeproj"
+cp "gameplay-template/gameplay-template.xcodeproj/project.pbxproj" "$projPath/$projName.xcodeproj/project.pbxproj"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/$projName.xcodeproj/project.pbxproj"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/$projName.xcodeproj/project.pbxproj"
+sed -i "" "s*GAMEPLAY_PATH*$gpPath*g" "$projPath/$projName.xcodeproj/project.pbxproj"
+
+cp "gameplay-template/TEMPLATE_PROJECT-macos.plist" "$projPath/$projName-macos.plist"
+sed -i "" "s*TEMPLATE_UUID*$uuid*g" "$projPath/$projName-macos.plist"
+sed -i "" "s*TEMPLATE_AUTHOR*$author*g" "$projPath/$projName-macos.plist"
+
+cp "gameplay-template/TEMPLATE_PROJECT-ios.plist" "$projPath/$projName-ios.plist"
+sed -i "" "s*TEMPLATE_UUID*$uuid*g" "$projPath/$projName-ios.plist"
+sed -i "" "s*TEMPLATE_AUTHOR*$author*g" "$projPath/$projName-ios.plist"
+
+#############################################
+# Copy BlackBerry NDK project files
+cp "gameplay-template/template.cproject" "$projPath/.cproject"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/.cproject"
+sed -i "" "s*TEMPLATE_UUID*$uuid*g" "$projPath/.cproject"
+sed -i "" "s*GAMEPLAY_PATH*$gpPath*g" "$projPath/.cproject"
+
+cp "gameplay-template/template.project" "$projPath/.project"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/.project"
+
+cp "gameplay-template/template.bar-descriptor.xml" "$projPath/bar-descriptor.xml"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/bar-descriptor.xml"
+sed -i "" "s*TEMPLATE_TITLE*$title*g" "$projPath/bar-descriptor.xml"
+sed -i "" "s*TEMPLATE_UUID*$uuid*g" "$projPath/bar-descriptor.xml"
+sed -i "" "s*TEMPLATE_AUTHOR*$author*g" "$projPath/bar-descriptor.xml"
+sed -i "" "s*TEMPLATE_DESCRIPTION*$desc*g" "$projPath/bar-descriptor.xml"
+
+#############################################
+# Copy Android NDK project files
+mkdir -p "$projPath/android"
+mkdir -p "$projPath/android/jni"
+mkdir -p "$projPath/android/res/values"
+
+cp "gameplay-template/android/template.AndroidManifest.xml" "$projPath/android/AndroidManifest.xml"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/android/AndroidManifest.xml"
+sed -i "" "s*TEMPLATE_UUID*$uuid*g" "$projPath/android/AndroidManifest.xml"
+
+cp "gameplay-template/android/template.build.xml" "$projPath/android/build.xml"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/android/build.xml"
+
+# Does not exist
+#cp "gameplay-template/android/template.project" "$projPath/android/.project"
+#sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/android/.project"
+
+#cp "gameplay-template/android/template.classpath" "$projPath/android/.classpath"
+
+cp "gameplay-template/android/jni/Application.mk" "$projPath/android/jni/Application.mk"
+
+cp "gameplay-template/android/jni/template.Android.mk" "$projPath/android/jni/Android.mk"
+sed -i "" "s*TEMPLATE_PROJECT*$projName*g" "$projPath/android/jni/Android.mk"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/android/jni/Android.mk"
+
+#cp "gameplay-template/android/jni/main.cpp" "$projPath/android/jni/main.cpp"
+
+cp "gameplay-template/android/res/values/template.strings.xml" "$projPath/android/res/values/strings.xml"
+sed -i "" "s*TEMPLATE_TITLE*$title*g" "$projPath/android/res/values/strings.xml"
+
+
+#############################################
+# Copy source files
+#############################################
+cp "gameplay-template/src/TemplateGame.h" "$projPath/src/$className.h"
+cp "gameplay-template/src/TemplateGame.cpp" "$projPath/src/$className.cpp"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/src/$className.h"
+sed -i "" "s*TemplateGame*$className*g" "$projPath/src/$className.cpp"
+
+# Copy resource files
+cp "gameplay-template/res/"* "$projPath/res/"
+#cp "gameplay-template/res/shaders/colored."* "$projPath/res/"
+
+# Copy icon
+cp "gameplay-template/icon.png" "$projPath/icon.png"
+
+# Open the new project folder
+open $projPath
+
+exit 0

+ 27 - 17
gameplay/src/Animation.cpp

@@ -31,7 +31,8 @@ Animation::~Animation()
 {
     if (_defaultClip)
     {
-        _defaultClip->stop();
+        if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+            _controller->unschedule(_defaultClip);
         SAFE_RELEASE(_defaultClip);
     }
 
@@ -42,7 +43,8 @@ Animation::~Animation()
         while (clipIter != _clips->end())
         {   
             AnimationClip* clip = *clipIter;
-            clip->stop();
+            if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+                _controller->unschedule(clip);
             SAFE_RELEASE(clip);
             clipIter++;
         }
@@ -65,7 +67,6 @@ Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int p
 Animation::Channel::~Channel()
 {
     SAFE_DELETE(_curve);
-    _animation->removeChannel(this);
     SAFE_RELEASE(_animation);
 }
 
@@ -120,10 +121,10 @@ AnimationClip* Animation::getClip(const char* id)
     }
 }
 
-void Animation::play(const char* id)
+void Animation::play(const char* clipId)
 {
     // If id is NULL, play the default clip.
-    if (id == NULL)
+    if (clipId == NULL)
     {
         if (_defaultClip == NULL)
             createDefaultClip();
@@ -133,32 +134,41 @@ void Animation::play(const char* id)
     else
     {
         // Find animation clip.. and play.
-        AnimationClip* clip = findClip(id);
+        AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
-        {
             clip->play();
-        }
     }
 }
 
-void Animation::stop(const char* id)
+void Animation::stop(const char* clipId)
 {
     // If id is NULL, play the default clip.
-    if (id == NULL)
+    if (clipId == NULL)
     {
-        if (_defaultClip == NULL)
-            createDefaultClip();
-
-        _defaultClip->stop();
+        if (_defaultClip)
+            _defaultClip->stop();
     }
     else
     {
         // Find animation clip.. and play.
-        AnimationClip* clip = findClip(id);
+        AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
-        {
             clip->stop();
-        }
+    }
+}
+
+void Animation::pause(const char * clipId)
+{
+    if (clipId == NULL)
+    {
+        if (_defaultClip)
+            _defaultClip->pause();
+    }
+    else
+    {
+        AnimationClip* clip = findClip(clipId);
+        if (clip != NULL)
+            clip->pause();
     }
 }
 

+ 8 - 0
gameplay/src/Animation.h

@@ -82,6 +82,13 @@ public:
      */
     void stop(const char* clipId = NULL);
 
+    /** 
+     * Pauses the AnimationClip with the specified name.
+     *
+     * @param clipId The ID of the AnimationClip to pause. If NULL, pauses the default clip.
+     */
+    void pause(const char* clipId = NULL);
+
 private:
 
     /**
@@ -92,6 +99,7 @@ private:
      */
     class Channel
     {
+        friend class AnimationController;
         friend class AnimationClip;
         friend class Animation;
         friend class AnimationTarget;

+ 100 - 36
gameplay/src/AnimationClip.cpp

@@ -36,10 +36,31 @@ AnimationClip::~AnimationClip()
     SAFE_RELEASE(_crossFadeToClip);
     SAFE_DELETE(_beginListeners);
     SAFE_DELETE(_endListeners);
-    SAFE_DELETE(_listeners);
+
+    if (_listeners)
+    {
+        *_listenerItr = _listeners->begin();
+        while (*_listenerItr != _listeners->end())
+        {
+            ListenerEvent* lEvt = **_listenerItr;
+            SAFE_DELETE(lEvt);
+            ++*_listenerItr;
+        }
+        SAFE_DELETE(_listeners);
+    }
     SAFE_DELETE(_listenerItr);
 }
 
+AnimationClip::ListenerEvent::ListenerEvent(Listener* listener, unsigned long eventTime)
+{
+    _listener = listener;
+    _eventTime = eventTime;
+}
+
+AnimationClip::ListenerEvent::~ListenerEvent()
+{
+}
+
 const char* AnimationClip::getID() const
 {
     return _id.c_str();
@@ -108,6 +129,11 @@ unsigned long AnimationClip::getActiveDuration() const
     return _activeDuration;
 }
 
+unsigned long AnimationClip::getDuration() const
+{
+    return _duration;
+}
+
 void AnimationClip::setSpeed(float speed)
 {
     _speed = speed;
@@ -137,13 +163,26 @@ void AnimationClip::play()
 {
     if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
     {
-        onEnd();
-        _animation->_controller->unschedule(this);
+        // If paused, reset the bit and return.
+        if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+        {
+            resetClipStateBit(CLIP_IS_PAUSED_BIT);
+            return;
+        }
+
+        // If the clip is set to be removed, reset the flag.
+        if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+            resetClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
+
+        // Set the state bit to restart.
+        setClipStateBit(CLIP_IS_RESTARTED_BIT);
+    }
+    else
+    {
+        setClipStateBit(CLIP_IS_PLAYING_BIT);
+        _animation->_controller->schedule(this);
     }
     
-    setClipStateBit(CLIP_IS_PLAYING_BIT);
-    
-    _animation->_controller->schedule(this);
     _timeStarted = Game::getGameTime();
 }
 
@@ -151,8 +190,20 @@ void AnimationClip::stop()
 {
     if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
     {
-        onEnd();
-        _animation->_controller->unschedule(this);
+        // Reset the restarted and paused bits. 
+        resetClipStateBit(CLIP_IS_RESTARTED_BIT);
+        resetClipStateBit(CLIP_IS_PAUSED_BIT);
+
+        // Mark the clip to removed from the AnimationController.
+        setClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
+    }
+}
+
+void AnimationClip::pause()
+{
+    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    {
+        setClipStateBit(CLIP_IS_PAUSED_BIT);
     }
 }
 
@@ -200,39 +251,40 @@ void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
 {
     assert(listener);
-    assert(eventTime < _duration);
+    assert(eventTime < _activeDuration);
 
-    listener->_listenerTime = eventTime;
+    ListenerEvent* listenerEvent = new ListenerEvent(listener, eventTime);
 
     if (!_listeners)
     {
-        _listeners = new std::list<Listener*>;
-        _listeners->push_front(listener);
+        _listeners = new std::list<ListenerEvent*>;
+        _listeners->push_front(listenerEvent);
 
-        _listenerItr = new std::list<Listener*>::iterator;
+        _listenerItr = new std::list<ListenerEvent*>::iterator;
         if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
             *_listenerItr = _listeners->begin();
     }
     else
     {
-        for (std::list<Listener*>::iterator itr = _listeners->begin(); itr != _listeners->begin(); itr++)
+        for (std::list<ListenerEvent*>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         {
-            if (eventTime < (*itr)->_listenerTime)
+            if (eventTime < (*itr)->_eventTime)
             {
-                itr = _listeners->insert(itr, listener);
+                itr = _listeners->insert(itr, listenerEvent);
 
                 // If playing, update the iterator if we need to.
                 // otherwise, it will just be set the next time the clip gets played.
                 if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
                 {
                     unsigned long currentTime = _elapsedTime % _duration;
-                    if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_listenerTime)) || 
-                        (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_listenerTime)))
+                    if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
+                        (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
                         *_listenerItr = itr;
                 }
                 return;
             }
         }
+        _listeners->push_back(listenerEvent);
     }
 }
 
@@ -254,8 +306,21 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
 
 bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
 {
-    if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
+    if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+    {
+        return false;
+    }
+    else if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    {   // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point
+        // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the 
+        // running clips on the AnimationController.
+        onEnd();
+        return true;
+    }
+    else if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
+    {
         onBegin();
+    }
     else
     {
         _elapsedTime += elapsedTime * _speed;
@@ -302,17 +367,17 @@ bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*
     {
         if (_speed >= 0.0f)
         {
-            while (*_listenerItr != _listeners->end() && currentTime >= (**_listenerItr)->_listenerTime)
+            while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime)
             {
-                (**_listenerItr)->animationEvent(this, Listener::DEFAULT);
+                (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 ++*_listenerItr;
             }
         }
         else
         {
-            while (*_listenerItr != _listeners->begin() && currentTime <= (**_listenerItr)->_listenerTime)
+            while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime)
             {
-                (**_listenerItr)->animationEvent(this, Listener::DEFAULT);
+                (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 --*_listenerItr;
             }
         }
@@ -387,20 +452,9 @@ bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*
     }
 
     // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
-    if (!isClipStateBitSet(CLIP_IS_STARTED_BIT))
+    if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT) || !isClipStateBitSet(CLIP_IS_STARTED_BIT))
     {
         onEnd();
-        // Notify end listeners if any.
-        if (_endListeners)
-        {
-            std::vector<Listener*>::iterator listener = _endListeners->begin();
-            while (listener != _endListeners->end())
-            {
-                (*listener)->animationEvent(this, Listener::END);
-                listener++;
-            }
-        }
-
         return true;
     }
 
@@ -441,8 +495,18 @@ void AnimationClip::onBegin()
 void AnimationClip::onEnd()
 {
     _blendWeight = 1.0f;
-    _timeStarted = 0;
     resetClipStateBit(CLIP_ALL_BITS);
+
+    // Notify end listeners if any.
+    if (_endListeners)
+    {
+        std::vector<Listener*>::iterator listener = _endListeners->begin();
+        while (listener != _endListeners->end())
+        {
+            (*listener)->animationEvent(this, Listener::END);
+            listener++;
+        }
+    }
 }
 
 bool AnimationClip::isClipStateBitSet(char bit) const

+ 60 - 25
gameplay/src/AnimationClip.h

@@ -37,7 +37,6 @@ public:
     public:
 
         Listener() 
-            : _listenerTime(0L)
         {
         }
 
@@ -66,9 +65,6 @@ public:
          * Handles when animation event occurs.
          */
         virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
-
-    private:
-        unsigned long _listenerTime;
     };
 
     /**
@@ -138,6 +134,13 @@ public:
      */
     unsigned long getActiveDuration() const;
 
+    /**
+     * Gets the AnimationClip's duration.
+     *
+     * @return the AnimationClip's duration.
+     */
+    unsigned long getDuration() const;
+
     /**
      * Set the AnimationClip's running speed. 
      *
@@ -183,6 +186,11 @@ public:
      */
     void stop();
 
+    /**
+     * Pauses the AnimationClip.
+     */
+    void pause();
+
     /**
      * Fades this clip out, and the specified clip in over the given duration.
      *
@@ -217,12 +225,39 @@ public:
     void addListener(AnimationClip::Listener* listener, unsigned long eventTime);
 
 private:
+    /**
+     * State bits.
+     */
     static const char CLIP_IS_PLAYING_BIT = 0x01;             // Bit representing whether AnimationClip is a running clip in AnimationController
     static const char CLIP_IS_STARTED_BIT = 0x02;             // Bit representing whether the AnimationClip has actually been started (ie: received first call to update())
     static const char CLIP_IS_FADING_OUT_STARTED_BIT = 0x04;  // Bit representing that a cross fade has started.
     static const char CLIP_IS_FADING_OUT_BIT = 0x08;          // Bit representing whether the clip is fading out.
     static const char CLIP_IS_FADING_IN_BIT = 0x10;           // Bit representing whether the clip is fading out.
-    static const char CLIP_ALL_BITS = 0x1F;                   // Bit mask for all the state bits.
+    static const char CLIP_IS_MARKED_FOR_REMOVAL_BIT = 0x20;  // Bit representing whether the clip has ended and should be removed from the AnimationController.
+    static const char CLIP_IS_RESTARTED_BIT = 0x40;           // Bit representing if the clip should be restarted by the AnimationController.
+    static const char CLIP_IS_PAUSED_BIT = 0x80;              // Bit representing if the clip is currently paused.
+    static const char CLIP_ALL_BITS = 0xFF;                   // Bit mask for all the state bits.
+
+    /**
+     * ListenerEvent.
+     *
+     * Internal structure used for storing the event time at which an AnimationClip::Listener should be called back.
+     */
+    struct ListenerEvent
+    {
+        /** 
+         * Constructor.
+         */
+        ListenerEvent(Listener* listener, unsigned long eventTime);
+
+        /**
+         * Destructor.
+         */
+        ~ListenerEvent();
+
+        Listener* _listener;        // This listener to call back when this event is triggered.
+        unsigned long _eventTime;   // The time at which the listener will be called back at during the playback of the AnimationClip.
+    };
 
     /**
      * Constructor.
@@ -274,26 +309,26 @@ private:
      */
     void resetClipStateBit(char bit);
 
-    std::string _id;                                // AnimationClip ID.
-    Animation* _animation;                          // The Animation this clip is created from.
-    unsigned long _startTime;                       // Start time of the clip.
-    unsigned long _endTime;                         // End time of the clip.
-    unsigned long _duration;                        // The total duration.
-    char _stateBits;                                // Bit flag used to keep track of the clip's current state.
-    float _repeatCount;                             // The clip's repeat count.
-    unsigned long _activeDuration;                  // The active duration of the clip.
-    float _speed;                                   // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.
-    unsigned long _timeStarted;                     // The game time when this clip was actually started.
-    long _elapsedTime;                              // Time elapsed while the clip is running.
-    AnimationClip* _crossFadeToClip;                // The clip to cross fade to.
-    unsigned long _crossFadeOutElapsed;             // The amount of time that has elapsed for the crossfade.
-    unsigned long _crossFadeOutDuration;            // The duration of the cross fade.
-    float _blendWeight;                             // The clip's blendweight.
-    std::vector<AnimationValue*> _values;           // AnimationValue holder.
-    std::vector<Listener*>* _beginListeners;        // Collection of begin listeners on the clip.
-    std::vector<Listener*>* _endListeners;          // Collection of end listeners on the clip.
-    std::list<Listener*>* _listeners;               // Ordered collection of listeners on the clip.
-    std::list<Listener*>::iterator* _listenerItr;   // Iterator that points to the next listener event to be triggered.
+    std::string _id;                                    // AnimationClip ID.
+    Animation* _animation;                              // The Animation this clip is created from.
+    unsigned long _startTime;                           // Start time of the clip.
+    unsigned long _endTime;                             // End time of the clip.
+    unsigned long _duration;                            // The total duration.
+    char _stateBits;                                    // Bit flag used to keep track of the clip's current state.
+    float _repeatCount;                                 // The clip's repeat count.
+    unsigned long _activeDuration;                      // The active duration of the clip.
+    float _speed;                                       // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.
+    unsigned long _timeStarted;                         // The game time when this clip was actually started.
+    long _elapsedTime;                                  // Time elapsed while the clip is running.
+    AnimationClip* _crossFadeToClip;                    // The clip to cross fade to.
+    unsigned long _crossFadeOutElapsed;                 // The amount of time that has elapsed for the crossfade.
+    unsigned long _crossFadeOutDuration;                // The duration of the cross fade.
+    float _blendWeight;                                 // The clip's blendweight.
+    std::vector<AnimationValue*> _values;               // AnimationValue holder.
+    std::vector<Listener*>* _beginListeners;            // Collection of begin listeners on the clip.
+    std::vector<Listener*>* _endListeners;              // Collection of end listeners on the clip.
+    std::list<ListenerEvent*>* _listeners;              // Ordered collection of listeners on the clip.
+    std::list<ListenerEvent*>::iterator* _listenerItr;  // Iterator that points to the next listener event to be triggered.
 };
 
 }

+ 51 - 18
gameplay/src/AnimationController.cpp

@@ -13,7 +13,14 @@ AnimationController::AnimationController()
 
 AnimationController::~AnimationController()
 {
-    destroyAllAnimations();
+    std::vector<Animation*>::iterator itr = _animations.begin();
+    for ( ; itr != _animations.end(); itr++)
+    {
+        Animation* temp = *itr;
+        SAFE_RELEASE(temp);
+    }
+
+    _animations.clear();
 }
 
 Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
@@ -111,12 +118,9 @@ void AnimationController::stopAllAnimations()
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = *clipIter;
-        clipIter++;
         clip->stop();
+        clipIter++;
     }
-    _runningClips.clear();
-
-    _state = IDLE;
 }
 
 Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, Properties* animationProperties)
@@ -269,7 +273,13 @@ void AnimationController::initialize()
 
 void AnimationController::finalize()
 {
-    stopAllAnimations();
+    std::list<AnimationClip*>::iterator itr = _runningClips.begin();
+    for ( ; itr != _runningClips.end(); itr++)
+    {
+        AnimationClip* clip = *itr;
+        SAFE_RELEASE(clip);
+    }
+    _runningClips.clear();
     _state = STOPPED;
 }
 
@@ -326,7 +336,15 @@ void AnimationController::update(long elapsedTime)
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = (*clipIter);
-        if (clip->update(elapsedTime, &_activeTargets))
+        if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
+        {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
+            // move it from where it is in the running clips list to the back.
+            clip->onEnd();
+            clip->setClipStateBit(AnimationClip::CLIP_IS_PLAYING_BIT);
+            _runningClips.push_back(clip);
+            clipIter = _runningClips.erase(clipIter);
+        }
+        else if (clip->update(elapsedTime, &_activeTargets))
         {
             SAFE_RELEASE(clip);
             clipIter = _runningClips.erase(clipIter);
@@ -358,30 +376,45 @@ void AnimationController::addAnimation(Animation* animation)
 
 void AnimationController::destroyAnimation(Animation* animation)
 {
-    std::vector<Animation*>::iterator itr = _animations.begin();
+    assert(animation);
 
-    while (itr != _animations.end())
+    std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
+    for (; cItr != animation->_channels.end(); cItr++)
     {
-        if (animation == *itr)
+        Animation::Channel* channel = *cItr;
+        channel->_target->deleteChannel(channel);
+    }
+
+    std::vector<Animation*>::iterator aItr = _animations.begin();
+    while (aItr != _animations.end())
+    {
+        if (animation == *aItr)
         {
-            Animation* animation = *itr;
-            _animations.erase(itr);
-            SAFE_RELEASE(animation);
+            Animation* temp = *aItr;
+            SAFE_RELEASE(temp);
+            _animations.erase(aItr);
             return;
         }
-        itr++;
+        aItr++;
     }
 }
 
 void AnimationController::destroyAllAnimations()
 {
-    std::vector<Animation*>::iterator itr = _animations.begin();
+    std::vector<Animation*>::iterator aItr = _animations.begin();
     
-    while (itr != _animations.end())
+    while (aItr != _animations.end())
     {
-        Animation* animation = *itr;
+        Animation* animation = *aItr;
+        std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
+        for (; cItr != animation->_channels.end(); cItr++)
+        {
+            Animation::Channel* channel = *cItr;
+            channel->_target->deleteChannel(channel);
+        }
+
         SAFE_RELEASE(animation);
-        itr++;
+        aItr++;
     }
 
     _animations.clear();

+ 10 - 10
gameplay/src/AnimationController.h

@@ -110,6 +110,16 @@ public:
      * Stops all AnimationClips currently playing on the AnimationController.
      */
     void stopAllAnimations();
+
+    /**
+     * Removes the given animation from this AnimationTarget.
+     */
+    void destroyAnimation(Animation* animation);
+
+    /**
+     * Removes all animations from the AnimationTarget.
+     */ 
+    void destroyAllAnimations();
        
 private:
 
@@ -193,16 +203,6 @@ private:
      * Adds an animation on this AnimationTarget.
      */ 
     void addAnimation(Animation* animation);
-
-    /**
-     * Removes the given animation from this AnimationTarget.
-     */
-    void destroyAnimation(Animation* animation);
-
-    /**
-     * Removes all animations from the AnimationTarget.
-     */ 
-    void destroyAllAnimations();
     
     State _state;                               // The current state of the AnimationController.
     std::list<AnimationClip*> _runningClips;    // A list of running AnimationClips.

+ 23 - 0
gameplay/src/AnimationTarget.cpp

@@ -19,6 +19,7 @@ AnimationTarget::~AnimationTarget()
         while (itr != _animationChannels->end())
         {
             Animation::Channel* channel = (*itr);
+            channel->_animation->removeChannel(channel);
             SAFE_DELETE(channel);
             itr++;
         }
@@ -95,6 +96,28 @@ int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
     return -1;
 }
 
+void AnimationTarget::deleteChannel(Animation::Channel* channel)
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        for ( ; itr != _animationChannels->end(); itr++)
+        {
+            Animation::Channel* temp = *itr;
+            if (channel == temp)
+            {
+                SAFE_DELETE(channel);
+                _animationChannels->erase(itr);
+
+                if (_animationChannels->empty())
+                    SAFE_DELETE(_animationChannels);
+
+                return;
+            }
+        }
+    }
+}
+
 }
 
 

+ 2 - 0
gameplay/src/AnimationTarget.h

@@ -67,6 +67,8 @@ protected:
 
     void addChannel(Animation::Channel* animation);
 
+    void deleteChannel(Animation::Channel* channel);
+
     TargetType _targetType;             // The type of target this is.
 
     char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.

+ 43 - 6
gameplay/src/AudioBuffer.cpp

@@ -179,6 +179,12 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
         return false;
     
+    unsigned int section_size;
+    section_size  = stream[7]<<24;
+    section_size |= stream[6]<<16;
+    section_size |= stream[5]<<8;
+    section_size |= stream[4];
+
     // Check for a valid pcm format.
     if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
     {
@@ -216,7 +222,6 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     bits  = stream[1]<<8;
     bits |= stream[0];
     
-
     // Now convert the given channel count and bit depth into an OpenAL format. 
     ALuint format = 0;
     if (bits == 8)
@@ -239,13 +244,45 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         return false;
     }
     
-    // Read the data chunk, which will hold the decoded sample data 
-    if (fread(stream, 1, 4, file) != 4 || memcmp(stream, "data", 4) != 0)
+    // Check against the size of the format header as there may be more data that we need to read
+    if (section_size > 16)
     {
-        LOG_ERROR("WAV file has no data.");
-        return false;
+    	unsigned int length = section_size - 16;
+
+    	// extension size is 2 bytes
+    	if (fread(stream, 1, length, file) != length)
+    		return false;
     }
-    
+
+    if (fread(stream, 1, 4, file) != 4)
+    	return false;
+
+    // read the next chunk, could be fact section or the data section
+    if (memcmp(stream, "fact", 4) == 0)
+    {
+    	if (fread(stream, 1, 4, file) != 4)
+    		return false;
+
+    	section_size  = stream[3]<<24;
+    	section_size |= stream[2]<<16;
+    	section_size |= stream[1]<<8;
+    	section_size |= stream[0];
+
+    	// read in the rest of the fact section
+    	if (fread(stream, 1, section_size, file) != section_size)
+    		return false;
+
+    	if (fread(stream, 1, 4, file) != 4)
+    		return false;
+    }
+
+    // should now be the data section which holds the decoded sample data
+    if (memcmp(stream, "data", 4) != 0)
+    {
+    	LOG_ERROR("WAV file has no data.");
+    	return false;
+    }
+
     // Read how much data is remaining and buffer it up.
     unsigned int dataSize;
     fread(&dataSize, sizeof(int), 1, file);

+ 4 - 2
gameplay/src/Base.h

@@ -17,6 +17,7 @@
 #include <string>
 #include <vector>
 #include <list>
+#include <set>
 #include <stack>
 #include <map>
 #include <algorithm>
@@ -103,13 +104,14 @@ extern void printError(const char* format, ...);
 // Bullet Physics
 #include <btBulletDynamicsCommon.h>
 #include <BulletCollision/CollisionDispatch/btGhostObject.h>
+#define BV(v) (btVector3((v).x, (v).y, (v).z))
+#define BQ(q) (btQuaternion((q).x, (q).y, (q).z, (q).w))
 
 // Debug new for memory leak detection
 #include "DebugNew.h"
 
 // Object deletion macro
 #define SAFE_DELETE(x) \
-    if (x) \
     { \
         delete x; \
         x = NULL; \
@@ -117,7 +119,6 @@ extern void printError(const char* format, ...);
 
 // Array deletion macro
 #define SAFE_DELETE_ARRAY(x) \
-    if (x) \
     { \
         delete[] x; \
         x = NULL; \
@@ -146,6 +147,7 @@ extern void printError(const char* format, ...);
 #define MATH_PIOVER4                M_PI_4
 #define MATH_PIX2                   6.28318530717958647693f
 #define MATH_EPSILON                0.000001f
+#define MATH_CLAMP(x, lo, hi)       ((x < lo) ? lo : ((x > hi) ? hi : x))
 #ifndef M_1_PI
 #define M_1_PI                      0.31830988618379067154
 #endif

+ 1 - 1
gameplay/src/BoundingBox.h

@@ -191,7 +191,7 @@ public:
  * @param box The bounding box to transform.
  * @return The resulting transformed bounding box.
  */
-inline BoundingBox operator*(const Matrix& matrix, const BoundingBox& box);
+inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box);
 
 }
 

+ 1 - 1
gameplay/src/BoundingBox.inl

@@ -9,7 +9,7 @@ inline BoundingBox& BoundingBox::operator*=(const Matrix& matrix)
     return *this;
 }
 
-inline BoundingBox operator*(const Matrix& matrix, const BoundingBox& box)
+inline const BoundingBox operator*(const Matrix& matrix, const BoundingBox& box)
 {
     BoundingBox b(box);
     b.transform(matrix);

+ 1 - 1
gameplay/src/BoundingSphere.h

@@ -183,7 +183,7 @@ private:
  * @param sphere The bounding sphere to transform.
  * @return The resulting transformed bounding sphere.
  */
-inline BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere);
+inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere);
 
 }
 

+ 1 - 1
gameplay/src/BoundingSphere.inl

@@ -9,7 +9,7 @@ inline BoundingSphere& BoundingSphere::operator*=(const Matrix& matrix)
     return *this;
 }
 
-inline BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere)
+inline const BoundingSphere operator*(const Matrix& matrix, const BoundingSphere& sphere)
 {
     BoundingSphere s(sphere);
     s.transform(matrix);

+ 2 - 1
gameplay/src/DebugNew.cpp

@@ -109,7 +109,8 @@ void* debugAlloc(std::size_t size, const char* file, int line)
 
 void debugFree(void* p)
 {
-    assert(p);
+    if (p == 0)
+        return;
 
     // Backup passed in pointer to access memory allocation record
     void* mem = ((unsigned char*)p) - sizeof(MemoryAllocationRecord);

+ 10 - 10
gameplay/src/Font.cpp

@@ -179,10 +179,10 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
     if (size == 0)
         size = _size;
     float scale = (float)size / _size;
-    char* cursor = NULL;
+    const char* cursor = NULL;
     if (rightToLeft)
     {
-        cursor = const_cast<char*>(text);
+        cursor = text;
     }
 
     int xPos = x, yPos = y;
@@ -293,7 +293,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
     if (size == 0)
         size = _size;
     float scale = (float)size / _size;
-    char* token = const_cast<char*>(text);
+    const char* token = text;
     const int length = strlen(text);
     int yPos = area.y;
 
@@ -309,7 +309,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         hAlign = ALIGN_LEFT;
     }
 
-    token = const_cast<char*>(text);
+    token = text;
 
     // For alignments other than top-left, need to calculate the y position to begin drawing from
     // and the starting x position of each line.  For right-to-left text, need to determine
@@ -480,12 +480,12 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         xPos = *xPositionsIt++;
     }
 
-    token = const_cast<char*>(text);
+    token = text;
     
     int iteration = 1;
     unsigned int lineLength;
     unsigned int currentLineLength = 0;
-    char* lineStart;
+    const char* lineStart;
     std::vector<unsigned int>::const_iterator lineLengthsIt;
     if (rightToLeft)
     {
@@ -655,7 +655,7 @@ void Font::measureText(const char* text, unsigned int size, unsigned int* width,
 {
     float scale = (float)size / _size;
     const int length = strlen(text);
-    char* token = const_cast<char*>(text);
+    const char* token = text;
 
     *width = 0;
     *height = 0;
@@ -695,7 +695,7 @@ void Font::measureText(const char* text, const Rectangle& viewport, unsigned int
         hAlign = ALIGN_LEFT;
     }
 
-    char* token = const_cast<char*>(text);
+    const char* token = text;
     std::vector<bool> emptyLines;
     std::vector<Vector2> lines;
 
@@ -1031,7 +1031,7 @@ unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigne
 
 unsigned int Font::getReversedTokenLength(const char* token, const char* bufStart)
 {
-    char* cursor = const_cast<char*>(token);
+    const char* cursor = token;
     char c = cursor[0];
     unsigned int length = 0;
 
@@ -1050,7 +1050,7 @@ unsigned int Font::getReversedTokenLength(const char* token, const char* bufStar
     return length;
 }
 
-bool Font::handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+bool Font::handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
                       std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd)
 {
     char delimiter = *token[0];

+ 1 - 1
gameplay/src/Font.h

@@ -198,7 +198,7 @@ private:
     unsigned int getReversedTokenLength(const char* token, const char* bufStart);
 
     // Returns false if EOF was reached, true otherwise.
-    bool handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+    bool handleDelimiters(const char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
                           std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd);
     void addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
                      std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft);

+ 1 - 1
gameplay/src/Matrix.cpp

@@ -460,7 +460,7 @@ bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translatio
     // Now calculate the rotation from the resulting matrix (axes).
     float trace = xaxis.x + yaxis.y + zaxis.z + 1.0f;
 
-    if (trace > MATH_TOLERANCE)
+    if (trace > MATH_EPSILON)
     {
         float s = 0.5f / sqrt(trace);
         rotation->w = 0.25f / s;

+ 9 - 9
gameplay/src/Matrix.h

@@ -831,7 +831,7 @@ public:
      * @param m The matrix to add.
      * @return The matrix sum.
      */
-    inline Matrix operator+(const Matrix& m) const;
+    inline const Matrix operator+(const Matrix& m) const;
     
     /**
      * Adds the given matrix to this matrix.
@@ -842,14 +842,14 @@ public:
     inline Matrix& operator+=(const Matrix& m);
 
     /**
-     * Calculates the sum of this matrix with the given matrix.
+     * Calculates the difference of this matrix with the given matrix.
      * 
      * Note: this does not modify this matrix.
      * 
-     * @param m The matrix to add.
-     * @return The matrix sum.
+     * @param m The matrix to subtract.
+     * @return The matrix difference.
      */
-    inline Matrix operator-(const Matrix& m) const;
+    inline const Matrix operator-(const Matrix& m) const;
 
     /**
      * Subtracts the given matrix from this matrix.
@@ -866,7 +866,7 @@ public:
      * 
      * @return The negation of this matrix.
      */
-    inline Matrix operator-() const;
+    inline const Matrix operator-() const;
 
     /**
      * Calculates the matrix product of this matrix with the given matrix.
@@ -876,7 +876,7 @@ public:
      * @param m The matrix to multiply by.
      * @return The matrix product.
      */
-    inline Matrix operator*(const Matrix& m) const;
+    inline const Matrix operator*(const Matrix& m) const;
 
     /**
      * Right-multiplies this matrix by the given matrix.
@@ -907,7 +907,7 @@ inline Vector3& operator*=(Vector3& v, const Matrix& m);
  * @param v The vector to transform.
  * @return The resulting transformed vector.
  */
-inline Vector3 operator*(const Matrix& m, const Vector3& v);
+inline const Vector3 operator*(const Matrix& m, const Vector3& v);
 
 /**
  * Transforms the given vector by the given matrix.
@@ -929,7 +929,7 @@ inline Vector4& operator*=(Vector4& v, const Matrix& m);
  * @param v The vector to transform.
  * @return The resulting transformed vector.
  */
-inline Vector4 operator*(const Matrix& m, const Vector4& v);
+inline const Vector4 operator*(const Matrix& m, const Vector4& v);
 
 }
 

+ 6 - 6
gameplay/src/Matrix.inl

@@ -3,7 +3,7 @@
 namespace gameplay
 {
 
-inline Matrix Matrix::operator+(const Matrix& m) const
+inline const Matrix Matrix::operator+(const Matrix& m) const
 {
     Matrix result(*this);
     result.add(m);
@@ -16,7 +16,7 @@ inline Matrix& Matrix::operator+=(const Matrix& m)
     return *this;
 }
 
-inline Matrix Matrix::operator-(const Matrix& m) const
+inline const Matrix Matrix::operator-(const Matrix& m) const
 {
     Matrix result(*this);
     result.subtract(m);
@@ -29,14 +29,14 @@ inline Matrix& Matrix::operator-=(const Matrix& m)
     return *this;
 }
 
-inline Matrix Matrix::operator-() const
+inline const Matrix Matrix::operator-() const
 {
     Matrix m(*this);
     m.negate();
     return m;
 }
 
-inline Matrix Matrix::operator*(const Matrix& m) const
+inline const Matrix Matrix::operator*(const Matrix& m) const
 {
     Matrix result(*this);
     result.multiply(m);
@@ -55,7 +55,7 @@ inline Vector3& operator*=(Vector3& v, const Matrix& m)
     return v;
 }
 
-inline Vector3 operator*(const Matrix& m, const Vector3& v)
+inline const Vector3 operator*(const Matrix& m, const Vector3& v)
 {
     Vector3 x;
     m.transformVector(v, &x);
@@ -68,7 +68,7 @@ inline Vector4& operator*=(Vector4& v, const Matrix& m)
     return v;
 }
 
-inline Vector4 operator*(const Matrix& m, const Vector4& v)
+inline const Vector4 operator*(const Matrix& m, const Vector4& v)
 {
     Vector4 x;
     m.transformVector(v, &x);

+ 22 - 2
gameplay/src/ParticleEmitter.cpp

@@ -235,6 +235,24 @@ bool ParticleEmitter::isStarted() const
     return _started;
 }
 
+bool ParticleEmitter::isActive() const
+{
+    if (_started)
+        return true;
+
+    bool active = false;
+    for (unsigned int i = 0; i < _particleCount; i++)
+    {
+        if (_particles[i]._energy > 0)
+        {
+            active = true;
+            break;
+        }
+    }
+
+    return active;
+}
+
 void ParticleEmitter::emit(unsigned int particleCount)
 {
     // Limit particleCount so as not to go over _particleCountMax.
@@ -867,7 +885,8 @@ void ParticleEmitter::draw()
             {
                 Particle* p = &_particles[i];
                 _spriteBatch->draw(p->_position.x, p->_position.y, p->_position.z, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color);
+                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color,
+                                   true);
             }
         }
         else
@@ -879,7 +898,8 @@ void ParticleEmitter::draw()
             {
                 Particle* p = &_particles[i];
                 _spriteBatch->draw(p->_position, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color, pivot, p->_angle);
+                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color, pivot, p->_angle,
+                                   true);
             }
         }
 

+ 7 - 0
gameplay/src/ParticleEmitter.h

@@ -212,6 +212,13 @@ public:
      */
     bool isStarted() const;
 
+    /**
+     * Gets whether this ParticleEmitter is currently active (i.e. if any of its particles are alive).
+     * 
+     * @return Whether this ParticleEmitter is currently active.
+     */
+    bool isActive() const;
+
     /**
      * Generates an arbitrary number of particles all at once.  Each newly emitted
      * particle has its properties assigned within the ranges defined by its ParticleEmitter.

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -118,7 +118,7 @@ btTransform PhysicsConstraint::getTransformOffset(const Node* node, const Vector
     
     t = offsetByCenterOfMass(node, t);
 
-    return btTransform(btQuaternion(r.x, r.y, r.z, r.w), btVector3(t.x, t.y, t.z));
+    return btTransform(BQ(r), BV(t));
 }
 
 Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)

+ 177 - 66
gameplay/src/PhysicsController.cpp

@@ -11,6 +11,11 @@
 namespace gameplay
 {
 
+const int PhysicsController::DIRTY         = 0x01;
+const int PhysicsController::COLLISION     = 0x02;
+const int PhysicsController::REGISTERED    = 0x04;
+const int PhysicsController::REMOVE        = 0x08;
+
 PhysicsController::PhysicsController()
   : _collisionConfiguration(NULL), _dispatcher(NULL),
     _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _ghostPairCallback(NULL),
@@ -115,7 +120,7 @@ void PhysicsController::setGravity(const Vector3& gravity)
     _gravity = gravity;
 
     if (_world)
-        _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
+        _world->setGravity(BV(_gravity));
 }
 
 void PhysicsController::drawDebug(const Matrix& viewProjection)
@@ -125,6 +130,100 @@ void PhysicsController::drawDebug(const Matrix& viewProjection)
     _debugDrawer->end();
 }
 
+PhysicsRigidBody* PhysicsController::rayTest(const Ray& ray, Vector3* hitPoint, float* hitFraction)
+{
+    btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(ray.getDirection()));
+    _world->rayTest(BV(ray.getOrigin()), BV(ray.getDirection()), callback);
+    if (callback.hasHit())
+    {
+        if (hitPoint)
+            hitPoint->set(callback.m_hitPointWorld.x(), callback.m_hitPointWorld.y(), callback.m_hitPointWorld.z());
+
+        if (hitFraction)
+            *hitFraction = callback.m_closestHitFraction;
+
+        return getRigidBody(callback.m_collisionObject);
+    }
+
+    return NULL;
+}
+
+btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
+    const btCollisionObject* b, int partIdB, int indexB)
+{
+    // Get pointers to the PhysicsRigidBody objects.
+    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getRigidBody(a);
+    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getRigidBody(b);
+    
+    // If the given rigid body pair has collided in the past, then
+    // we notify the listeners only if the pair was not colliding
+    // during the previous frame. Otherwise, it's a new pair, so add a
+    // new entry to the cache with the appropriate listeners and notify them.
+    PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+    if (_collisionStatus.count(pair) > 0)
+    {
+        const CollisionInfo& collisionInfo = _collisionStatus[pair];
+        if ((collisionInfo._status & COLLISION) == 0)
+        {
+            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = collisionInfo._listeners.begin();
+            for (; iter != collisionInfo._listeners.end(); iter++)
+            {
+                if ((collisionInfo._status & REMOVE) == 0)
+                {
+                    (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
+                        Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+                }
+            }
+        }
+    }
+    else
+    {
+        CollisionInfo& collisionInfo = _collisionStatus[pair];
+
+        // Initialized the status for the new entry.
+        collisionInfo._status = 0;
+
+        // Add the appropriate listeners.
+        PhysicsRigidBody::CollisionPair p1(pair._rbA, NULL);
+        if (_collisionStatus.count(p1) > 0)
+        {
+            const CollisionInfo& ci = _collisionStatus[p1];
+            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = ci._listeners.begin();
+            for (; iter != ci._listeners.end(); iter++)
+            {
+                collisionInfo._listeners.push_back(*iter);
+            }
+        }
+        PhysicsRigidBody::CollisionPair p2(pair._rbB, NULL);
+        if (_collisionStatus.count(p2) > 0)
+        {
+            const CollisionInfo& ci = _collisionStatus[p2];
+            std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = ci._listeners.begin();
+            for (; iter != ci._listeners.end(); iter++)
+            {
+                collisionInfo._listeners.push_back(*iter);
+            }
+        }
+
+        std::vector<PhysicsRigidBody::Listener*>::iterator iter = collisionInfo._listeners.begin();
+        for (; iter != collisionInfo._listeners.end(); iter++)
+        {
+            if ((collisionInfo._status & REMOVE) == 0)
+            {
+                (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
+                    Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+            }
+        }
+    }
+
+    // Update the collision status cache (we remove the dirty bit
+    // set in the controller's update so that this particular collision pair's
+    // status is not reset to 'no collision' when the controller's update completes).
+    _collisionStatus[pair]._status &= ~DIRTY;
+    _collisionStatus[pair]._status |= COLLISION;
+    return 0.0f;
+}
+
 void PhysicsController::initialize()
 {
     _collisionConfiguration = new btDefaultCollisionConfiguration();
@@ -134,7 +233,7 @@ void PhysicsController::initialize()
 
     // Create the world.
     _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
-    _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
+    _world->setGravity(BV(_gravity));
 
     // Register ghost pair callback so bullet detects collisions with ghost objects (used for character collisions).
     _ghostPairCallback = bullet_new<btGhostPairCallback>();
@@ -222,81 +321,75 @@ void PhysicsController::update(long elapsedTime)
     // During collision processing, if a collision occurs, the status is 
     // set to COLLISION and the DIRTY bit is cleared. Then, after collision processing 
     // is finished, if a given status is still dirty, the COLLISION bit is cleared.
+    //
+    // If an entry was marked for removal in the last frame, remove it now.
 
-    // Dirty all the collision listeners' collision status caches.
-    for (unsigned int i = 0; i < _bodies.size(); i++)
+    // Dirty the collision status cache entries.
+    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    for (; iter != _collisionStatus.end();)
     {
-        if (_bodies[i]->_listeners)
+        if ((iter->second._status & REMOVE) != 0)
         {
-            for (unsigned int k = 0; k < _bodies[i]->_listeners->size(); k++)
-            {
-                std::map<PhysicsRigidBody::CollisionPair, int>::iterator iter = (*_bodies[i]->_listeners)[k]->_collisionStatus.begin();
-                for (; iter != (*_bodies[i]->_listeners)[k]->_collisionStatus.end(); iter++)
-                {
-                    iter->second |= PhysicsRigidBody::Listener::DIRTY;
-                }
-            }
+            std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
+            iter++;
+            _collisionStatus.erase(eraseIter);
+        }
+        else
+        {
+            iter->second._status |= DIRTY;
+            iter++;
         }
     }
 
-    // Go through the physics rigid bodies and update the collision listeners.
-    unsigned int size = _bodies.size();
-    unsigned int listenerCount = 0;
-    PhysicsRigidBody* body = NULL;
-    PhysicsRigidBody::Listener* listener = NULL;
-    PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
-    for (unsigned int i = 0; i < size; i++)
+    // Go through the collision status cache and perform all registered collision tests.
+    iter = _collisionStatus.begin();
+    for (; iter != _collisionStatus.end(); iter++)
     {
-        body = _bodies[i];
-        if (body->_listeners)
+        // If this collision pair was one that was registered for listening, then perform the collision test.
+        // (In the case where we register for all collisions with a rigid body, there will be a lot
+        // of collision pairs in the status cache that we did not explicitly register for.)
+        if ((iter->second._status & REGISTERED) != 0 && (iter->second._status & REMOVE) == 0)
         {
-            listenerCount = body->_listeners->size();
-            for (unsigned int k = 0; k < listenerCount; k++)
-            {
-                listener = (*body->_listeners)[k];
-                std::map<PhysicsRigidBody::CollisionPair, int>::iterator iter = listener->_collisionStatus.begin();
-                for (; iter != listener->_collisionStatus.end(); iter++)
-                {
-                    // If this collision pair was one that was registered for listening, then perform the collision test.
-                    // (In the case where we register for all collisions with a rigid body, there will be a lot
-                    // of collision pairs in the status cache that we did not explicitly register for.)
-                    if ((iter->second & PhysicsRigidBody::Listener::REGISTERED) != 0)
-                    {
-                        if (iter->first._rbB)
-                            physicsController->_world->contactPairTest(iter->first._rbA->_body, iter->first._rbB->_body, *listener);
-                        else
-                            physicsController->_world->contactTest(iter->first._rbA->_body, *listener);
-                    }
-                }   
-            }
+            if (iter->first._rbB)
+                _world->contactPairTest(iter->first._rbA->_body, iter->first._rbB->_body, *this);
+            else
+                _world->contactTest(iter->first._rbA->_body, *this);
         }
     }
 
-    // Go through all the collision listeners and update their collision status caches.
-    for (unsigned int i = 0; i < _bodies.size(); i++)
+    // Update all the collision status cache entries.
+    iter = _collisionStatus.begin();
+    for (; iter != _collisionStatus.end(); iter++)
     {
-        if (_bodies[i]->_listeners)
+        if ((iter->second._status & DIRTY) != 0)
         {
-            for (unsigned int k = 0; k < _bodies[i]->_listeners->size(); k++)
+            if ((iter->second._status & COLLISION) != 0 && iter->first._rbB)
             {
-                std::map<PhysicsRigidBody::CollisionPair, int>::iterator iter = (*_bodies[i]->_listeners)[k]->_collisionStatus.begin();
-                for (; iter != (*_bodies[i]->_listeners)[k]->_collisionStatus.end(); iter++)
+                unsigned int size = iter->second._listeners.size();
+                for (unsigned int i = 0; i < size; i++)
                 {
-                    if ((iter->second & PhysicsRigidBody::Listener::DIRTY) != 0)
-                    {
-                        if ((iter->second & PhysicsRigidBody::Listener::COLLISION) != 0 && iter->first._rbB)
-                        {
-                            (*_bodies[i]->_listeners)[k]->collisionEvent(PhysicsRigidBody::Listener::NOT_COLLIDING, iter->first, Vector3::zero());
-                        }
-
-                        iter->second &= ~PhysicsRigidBody::Listener::COLLISION;
-                    }
+                    iter->second._listeners[i]->collisionEvent(PhysicsRigidBody::Listener::NOT_COLLIDING, iter->first);
                 }
             }
+
+            iter->second._status &= ~COLLISION;
         }
     }
 }
-    
+
+void PhysicsController::addCollisionListener(PhysicsRigidBody::Listener* listener, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+{
+    PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+
+    // Make sure the status of the entry is initialized properly.
+    if (_collisionStatus.count(pair) == 0)
+        _collisionStatus[pair]._status = 0;
+
+    // Add the listener and ensure the status includes that this collision pair is registered.
+    _collisionStatus[pair]._listeners.push_back(listener);
+    if ((_collisionStatus[pair]._status & PhysicsController::REGISTERED) == 0)
+        _collisionStatus[pair]._status |= PhysicsController::REGISTERED;
+}
 
 void PhysicsController::addRigidBody(PhysicsRigidBody* body)
 {
@@ -330,9 +423,27 @@ void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
             else
                 _shapes[i]->release();
 
-            return;
+            break;
+        }
+    }
+
+    // Remove the rigid body from the controller's list.
+    for (unsigned int i = 0; i < _bodies.size(); i++)
+    {
+        if (_bodies[i] == rigidBody)
+        {
+            _bodies.erase(_bodies.begin() + i);
+            break;
         }
     }
+
+    // Find all references to the rigid body in the collision status cache and mark them for removal.
+    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    for (; iter != _collisionStatus.end(); iter++)
+    {
+        if (iter->first._rbA == rigidBody || iter->first._rbB == rigidBody)
+            iter->second._status |= REMOVE;
+    }
 }
 
 PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* collisionObject)
@@ -347,9 +458,9 @@ PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* colli
     return NULL;
 }
 
-btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const btVector3& scale)
+btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const Vector3& scale)
 {
-    btVector3 halfExtents(scale.x() * 0.5 * abs(max.x - min.x), scale.y() * 0.5 * abs(max.y - min.y), scale.z() * 0.5 * abs(max.z - min.z));
+    btVector3 halfExtents(scale.x * 0.5 * abs(max.x - min.x), scale.y * 0.5 * abs(max.y - min.y), scale.z * 0.5 * abs(max.z - min.z));
 
     // Return the box shape from the cache if it already exists.
     for (unsigned int i = 0; i < _shapes.size(); i++)
@@ -395,15 +506,15 @@ btCollisionShape* PhysicsController::createCapsule(float radius, float height)
     return capsule;
 }
 
-btCollisionShape* PhysicsController::createSphere(float radius, const btVector3& scale)
+btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
 {
     // Since sphere shapes depend only on the radius, the best we can do is take
     // the largest dimension and apply that as the uniform scale to the rigid body.
-    float uniformScale = scale.x();
-    if (uniformScale < scale.y())
-        uniformScale = scale.y();
-    if (uniformScale < scale.z())
-        uniformScale = scale.z();
+    float uniformScale = scale.x;
+    if (uniformScale < scale.y)
+        uniformScale = scale.y;
+    if (uniformScale < scale.z)
+        uniformScale = scale.z;
     
     // Return the sphere shape from the cache if it already exists.
     for (unsigned int i = 0; i < _shapes.size(); i++)

+ 41 - 4
gameplay/src/PhysicsController.h

@@ -15,7 +15,7 @@ namespace gameplay
 /**
  * Defines a class for controlling game physics.
  */
-class PhysicsController
+class PhysicsController : public btCollisionWorld::ContactResultCallback
 {
     friend class Game;
     friend class PhysicsConstraint;
@@ -182,7 +182,7 @@ public:
      * @param gravity The gravity vector.
      */
     void setGravity(const Vector3& gravity);
-    
+   
     /**
      * Draws debugging information (rigid body outlines, etc.) using the given view projection matrix.
      * 
@@ -190,8 +190,41 @@ public:
      */
     void drawDebug(const Matrix& viewProjection);
 
+    /**
+     * Gets the first rigid body that the given ray intersects.
+     * 
+     * @param ray The ray to test intersection with.
+     * @param hitPoint Optional Vector3 point that is populated with the world-space point of intersection.
+     * @param hitDistance Optional float pointer that is populated with the distance along the ray
+     *      (as a fraction between 0-1) where the intersection occurred.
+     *
+     * @return The first rigid body that the ray intersects, or NULL if no intersection was found.
+     */
+    PhysicsRigidBody* rayTest(const Ray& ray, Vector3* hitPoint = NULL, float* hitFraction = NULL);
+
+protected:
+
+    /**
+     * Internal function used for Bullet integration (do not use or override).
+     */
+    btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB);    
+
 private:
 
+    // Internal constants for the collision status cache.
+    static const int DIRTY;
+    static const int COLLISION;
+    static const int REGISTERED;
+    static const int REMOVE;
+
+    // Represents the collision listeners and status for a given collision pair (used by the collision status cache).
+    struct CollisionInfo
+    {
+        std::vector<PhysicsRigidBody::Listener*> _listeners;
+        int _status;
+    };
+
+    // Wraps Bullet collision shapes (used for implementing shape caching).
     struct PhysicsCollisionShape : public Ref
     {
         PhysicsCollisionShape(btCollisionShape* shape) : _shape(shape) {}
@@ -235,6 +268,9 @@ private:
      */
     void update(long elapsedTime);
 
+    // Adds the given collision listener for the two given rigid bodies.
+    void addCollisionListener(PhysicsRigidBody::Listener* listener, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+
     // Adds the given rigid body to the world.
     void addRigidBody(PhysicsRigidBody* body);
     
@@ -245,13 +281,13 @@ private:
     PhysicsRigidBody* getRigidBody(const btCollisionObject* collisionObject);
     
     // Creates a box collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createBox(const Vector3& min, const Vector3& max, const btVector3& scale);
+    btCollisionShape* createBox(const Vector3& min, const Vector3& max, const Vector3& scale);
 
     // Creates a capsule collision shape to be used in the creation of a rigid body.
     btCollisionShape* createCapsule(float radius, float height);
 
     // Creates a sphere collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createSphere(float radius, const btVector3& scale);
+    btCollisionShape* createSphere(float radius, const Vector3& scale);
 
     // Creates a triangle mesh collision shape to be used in the creation of a rigid body.
     btCollisionShape* createMesh(PhysicsRigidBody* body, const Vector3& scale);
@@ -310,6 +346,7 @@ private:
     std::vector<Listener*>* _listeners;
     std::vector<PhysicsRigidBody*> _bodies;
     Vector3 _gravity;
+    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo> _collisionStatus;  
 };
 
 }

+ 3 - 3
gameplay/src/PhysicsGenericConstraint.cpp

@@ -45,13 +45,13 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
         b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-        btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
-        btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), btVector3(tB.x, tB.y, tB.z));
+        btTransform frameInA(BQ(rotationOffsetA), BV(tA));
+        btTransform frameInB(BQ(rotationOffsetB), BV(tB));
         _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
     }
     else
     {
-        btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
+        btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         _constraint = new btGeneric6DofConstraint(*a->_body, frameInA, true);
     }
 }

+ 8 - 8
gameplay/src/PhysicsGenericConstraint.inl

@@ -45,42 +45,42 @@ inline const Vector3& PhysicsGenericConstraint::getTranslationOffsetB() const
 
 inline void PhysicsGenericConstraint::setAngularLowerLimit(const Vector3& limits)
 {
-    ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(btVector3(limits.x, limits.y, limits.z));
+    ((btGeneric6DofConstraint*)_constraint)->setAngularLowerLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setAngularUpperLimit(const Vector3& limits)
 {
-    ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(btVector3(limits.x, limits.y, limits.z));
+    ((btGeneric6DofConstraint*)_constraint)->setAngularUpperLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setLinearLowerLimit(const Vector3& limits)
 {
-    ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(btVector3(limits.x, limits.y, limits.z));
+    ((btGeneric6DofConstraint*)_constraint)->setLinearLowerLimit(BV(limits));
 }
     
 inline void PhysicsGenericConstraint::setLinearUpperLimit(const Vector3& limits)
 {
-    ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(btVector3(limits.x, limits.y, limits.z));
+    ((btGeneric6DofConstraint*)_constraint)->setLinearUpperLimit(BV(limits));
 }
 
 inline void PhysicsGenericConstraint::setRotationOffsetA(const Quaternion& rotationOffset)
 {
-    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setRotation(btQuaternion(rotationOffset.x, rotationOffset.y, rotationOffset.z, rotationOffset.w));
+    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setRotation(BQ(rotationOffset));
 }
 
 inline void PhysicsGenericConstraint::setRotationOffsetB(const Quaternion& rotationOffset)
 {
-    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setRotation(btQuaternion(rotationOffset.x, rotationOffset.y, rotationOffset.z, rotationOffset.w));
+    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setRotation(BQ(rotationOffset));
 }
 
 inline void PhysicsGenericConstraint::setTranslationOffsetA(const Vector3& translationOffset)
 {
-    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setOrigin(btVector3(translationOffset.x, translationOffset.y, translationOffset.z));
+    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetA().setOrigin(BV(translationOffset));
 }
 
 inline void PhysicsGenericConstraint::setTranslationOffsetB(const Vector3& translationOffset)
 {
-    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setOrigin(btVector3(translationOffset.x, translationOffset.y, translationOffset.z));
+    static_cast<btGeneric6DofConstraint*>(_constraint)->getFrameOffsetB().setOrigin(BV(translationOffset));
 }
 
 }

+ 3 - 3
gameplay/src/PhysicsHingeConstraint.cpp

@@ -27,13 +27,13 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
         b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-        btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
-        btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), btVector3(tB.x, tB.y, tB.z));
+        btTransform frameInA(BQ(rotationOffsetA), BV(tA));
+        btTransform frameInB(BQ(rotationOffsetB), BV(tB));
         _constraint = new btHingeConstraint(*a->_body, *b->_body, frameInA, frameInB);
     }
     else
     {
-        btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
+        btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         _constraint = new btHingeConstraint(*a->_body, frameInA);
     }
 }

+ 4 - 5
gameplay/src/PhysicsMotionState.cpp

@@ -10,7 +10,7 @@ PhysicsMotionState::PhysicsMotionState(Node* node, const Vector3* centerOfMassOf
     if (centerOfMassOffset)
     {
         // Store the center of mass offset.
-        _centerOfMassOffset.setOrigin(btVector3(centerOfMassOffset->x, centerOfMassOffset->y, centerOfMassOffset->z));
+        _centerOfMassOffset.setOrigin(BV(*centerOfMassOffset));
     }
 
     updateTransformFromNode();
@@ -50,17 +50,16 @@ void PhysicsMotionState::updateTransformFromNode() const
     {
         // When there is a center of mass offset, we modify the initial world transformation
         // so that when physics is initially applied, the object is in the correct location.
-        btQuaternion orientation(rotation.x, rotation.y, rotation.z, rotation.w);
-        btTransform offset = btTransform(orientation, btVector3(0.0f, 0.0f, 0.0f)) * _centerOfMassOffset.inverse();
+        btTransform offset = btTransform(BQ(rotation), btVector3(0.0f, 0.0f, 0.0f)) * _centerOfMassOffset.inverse();
 
         btVector3 origin(m.m[12] + _centerOfMassOffset.getOrigin().getX() + offset.getOrigin().getX(), 
                          m.m[13] + _centerOfMassOffset.getOrigin().getY() + offset.getOrigin().getY(), 
                          m.m[14] + _centerOfMassOffset.getOrigin().getZ() + offset.getOrigin().getZ());
-        _worldTransform = btTransform(orientation, origin);
+        _worldTransform = btTransform(BQ(rotation), origin);
     }
     else
     {
-        _worldTransform = btTransform(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w), btVector3(m.m[12], m.m[13], m.m[14]));
+        _worldTransform = btTransform(BQ(rotation), btVector3(m.m[12], m.m[13], m.m[14]));
     }
 }
 

+ 26 - 54
gameplay/src/PhysicsRigidBody.cpp

@@ -10,10 +10,6 @@
 namespace gameplay
 {
 
-const int PhysicsRigidBody::Listener::DIRTY         = 0x01;
-const int PhysicsRigidBody::Listener::COLLISION     = 0x02;
-const int PhysicsRigidBody::Listener::REGISTERED    = 0x04;
-
 // Internal values used for creating mesh, heightfield, and capsule rigid bodies.
 #define SHAPE_MESH ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 1))
 #define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 2))
@@ -28,33 +24,40 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, floa
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
 {
+    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
+    Vector3 scale;
+    node->getWorldMatrix().getScale(&scale);
+
     switch (type)
     {
         case SHAPE_BOX:
         {
             const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-            _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
+            _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, scale);
             break;
         }
         case SHAPE_SPHERE:
         {
             const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
-            _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
+            _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, scale);
             break;
         }
         case SHAPE_MESH:
         {
-            _shape = Game::getInstance()->getPhysicsController()->createMesh(this, node->getScale());
+            _shape = Game::getInstance()->getPhysicsController()->createMesh(this, scale);
             break;
         }
     }
 
     // Use the center of the bounding sphere as the center of mass offset.
     Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
+    c.x *= scale.x;
+    c.y *= scale.y;
+    c.z *= scale.z;
     c.negate();
 
-    // Create the Bullet rigid body.
-    if (c.lengthSquared() > MATH_EPSILON)
+    // Create the Bullet rigid body (we don't apply center of mass offsets on mesh rigid bodies).
+    if (c.lengthSquared() > MATH_EPSILON && type != SHAPE_MESH)
         _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
     else
         _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
@@ -151,11 +154,18 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float
         _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
         _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
 {
+    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
+    Vector3 scale;
+    node->getWorldMatrix().getScale(&scale);
+
     // Create the capsule collision shape.
     _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
 
     // Use the center of the bounding sphere as the center of mass offset.
     Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
+    c.x *= scale.x;
+    c.y *= scale.y;
+    c.z *= scale.z;
     c.negate();
 
     // Create the Bullet rigid body.
@@ -205,13 +215,7 @@ PhysicsRigidBody::~PhysicsRigidBody()
 
 void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
 {
-    if (!_listeners)
-        _listeners = new std::vector<Listener*>();
-
-    CollisionPair pair(this, body);
-    listener->_collisionStatus[pair] = PhysicsRigidBody::Listener::REGISTERED;
-
-    _listeners->push_back(listener);
+    Game::getInstance()->getPhysicsController()->addCollisionListener(listener, this, body);
 }
 
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
@@ -222,9 +226,9 @@ void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativeP
     {
         _body->activate();
         if (relativePosition)
-            _body->applyForce(btVector3(force.x, force.y, force.z), btVector3(relativePosition->x, relativePosition->y, relativePosition->z));
+            _body->applyForce(BV(force), BV(*relativePosition));
         else
-            _body->applyCentralForce(btVector3(force.x, force.y, force.z));
+            _body->applyCentralForce(BV(force));
     }
 }
 
@@ -238,10 +242,10 @@ void PhysicsRigidBody::applyImpulse(const Vector3& impulse, const Vector3* relat
 
         if (relativePosition)
         {
-            _body->applyImpulse(btVector3(impulse.x, impulse.y, impulse.z), btVector3(relativePosition->x, relativePosition->y, relativePosition->z));
+            _body->applyImpulse(BV(impulse), BV(*relativePosition));
         }
         else
-            _body->applyCentralImpulse(btVector3(impulse.x, impulse.y, impulse.z));
+            _body->applyCentralImpulse(BV(impulse));
     }
 }
 
@@ -252,7 +256,7 @@ void PhysicsRigidBody::applyTorque(const Vector3& torque)
     if (torque.lengthSquared() > MATH_EPSILON)
     {
         _body->activate();
-        _body->applyTorque(btVector3(torque.x, torque.y, torque.z));
+        _body->applyTorque(BV(torque));
     }
 }
 
@@ -263,7 +267,7 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
     if (torque.lengthSquared() > MATH_EPSILON)
     {
         _body->activate();
-        _body->applyTorqueImpulse(btVector3(torque.x, torque.y, torque.z));
+        _body->applyTorqueImpulse(BV(torque));
     }
 }
 
@@ -567,38 +571,6 @@ PhysicsRigidBody::Listener::~Listener()
     // Unused
 }
 
-btScalar PhysicsRigidBody::Listener::addSingleResult(btManifoldPoint& cp, 
-                                                     const btCollisionObject* a, int partIdA, int indexA, 
-                                                     const btCollisionObject* b, int partIdB, int indexB)
-{
-    // Get pointers to the PhysicsRigidBody objects.
-    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getRigidBody(a);
-    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getRigidBody(b);
-    
-    // If the given rigid body pair has collided in the past, then
-    // we notify the listener only if the pair was not colliding
-    // during the previous frame. Otherwise, it's a new pair, so notify the listener.
-    CollisionPair pair(rbA, rbB);
-    if (_collisionStatus.count(pair) > 0)
-    {
-        if ((_collisionStatus[pair] & COLLISION) == 0)
-            collisionEvent(COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
-                Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
-    }
-    else
-    {
-        collisionEvent(COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
-            Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
-    }
-
-    // Update the collision status cache (we remove the dirty bit
-    // set in the controller's update so that this particular collision pair's
-    // status is not reset to 'no collision' when the controller's update completes).
-    _collisionStatus[pair] &= ~DIRTY;
-    _collisionStatus[pair] |= COLLISION;
-    return 0.0f;
-}
-
 btScalar PhysicsRigidBody::CollidesWithCallback::addSingleResult(btManifoldPoint& cp, 
                                                                  const btCollisionObject* a, int partIdA, int indexA, 
                                                                  const btCollisionObject* b, int partIdB, int indexB)

+ 2 - 20
gameplay/src/PhysicsRigidBody.h

@@ -70,7 +70,7 @@ public:
     /**
      * Collision listener interface.
      */
-    class Listener : public btCollisionWorld::ContactResultCallback
+    class Listener
     {
         friend class PhysicsRigidBody;
         friend class PhysicsController;
@@ -106,24 +106,6 @@ public:
          * @param contactPointB The contact point with the second rigid body (in world space).
          */
         virtual void collisionEvent(EventType type, const CollisionPair& collisionPair, const Vector3& contactPointA = Vector3(), const Vector3& contactPointB = Vector3()) = 0;
-
-        /**
-         * Internal function used for Bullet integration (do not use or override).
-         */
-        btScalar addSingleResult(btManifoldPoint& cp, 
-                                 const btCollisionObject* a, int partIdA, int indexA, 
-                                 const btCollisionObject* b, int partIdB, int indexB);
-
-        std::map<CollisionPair, int> _collisionStatus;  // Holds the collision status for each pair of rigid bodies. 
-        
-    private:
-
-        // Internal constant.
-        static const int DIRTY;
-        // Internal constant.
-        static const int COLLISION;
-        // Internal constant.
-        static const int REGISTERED;
     };
 
     /**
@@ -236,7 +218,7 @@ public:
      * 
      * @return The node.
      */
-    inline const Node* getNode() const;
+    inline Node* getNode();
 
     /**
      * Gets the rigid body's restitution.

+ 6 - 5
gameplay/src/PhysicsRigidBody.inl

@@ -1,4 +1,5 @@
 #include "PhysicsRigidBody.h"
+#include "Base.h"
 
 namespace gameplay
 {
@@ -58,7 +59,7 @@ inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
     return *_linearVelocity;
 }
 
-inline const Node* PhysicsRigidBody::getNode() const
+inline Node* PhysicsRigidBody::getNode()
 {
     return _node;
 }
@@ -85,12 +86,12 @@ inline bool PhysicsRigidBody::isDynamic() const
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
 {
-    _body->setAngularVelocity(btVector3(velocity.x, velocity.y, velocity.z));
+    _body->setAngularVelocity(BV(velocity));
 }
 
 inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 {
-    _body->setAnisotropicFriction(btVector3(friction.x, friction.y, friction.z));
+    _body->setAnisotropicFriction(BV(friction));
 }
 
 inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
@@ -105,7 +106,7 @@ inline void PhysicsRigidBody::setFriction(float friction)
 
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
 {
-    _body->setGravity(btVector3(gravity.x, gravity.y, gravity.z));
+    _body->setGravity(BV(gravity));
 }
 
 inline void PhysicsRigidBody::setKinematic(bool kinematic)
@@ -124,7 +125,7 @@ inline void PhysicsRigidBody::setKinematic(bool kinematic)
 
 inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 {
-    _body->setLinearVelocity(btVector3(velocity.x, velocity.y, velocity.z));
+    _body->setLinearVelocity(BV(velocity));
 }
 
 inline void PhysicsRigidBody::setRestitution(float restitution)

+ 2 - 2
gameplay/src/PhysicsSocketConstraint.cpp

@@ -38,11 +38,11 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
         b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, btVector3(tA.x, tA.y, tA.z), btVector3(tB.x, tB.y, tB.z));
+        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, BV(tA), BV(tB));
     }
     else
     {
-        _constraint = new btPoint2PointConstraint(*a->_body, btVector3(tA.x, tA.y, tA.z));
+        _constraint = new btPoint2PointConstraint(*a->_body, BV(tA));
     }
 }
 

+ 2 - 2
gameplay/src/PhysicsSpringConstraint.cpp

@@ -32,8 +32,8 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quat
     b->getNode()->getWorldMatrix().getScale(&sB);
     Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-    btTransform frameInA(btQuaternion(rotationOffsetA.x, rotationOffsetA.y, rotationOffsetA.z, rotationOffsetA.w), btVector3(tA.x, tA.y, tA.z));
-    btTransform frameInB(btQuaternion(rotationOffsetB.x, rotationOffsetB.y, rotationOffsetB.z, rotationOffsetB.w), btVector3(tB.x, tB.y, tB.z));
+    btTransform frameInA(BQ(rotationOffsetA), BV(tA));
+    btTransform frameInB(BQ(rotationOffsetB), BV(tB));
     _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
 }
 

+ 1 - 1
gameplay/src/Plane.h

@@ -220,7 +220,7 @@ private:
  * @param plane The plane to transform.
  * @return The resulting transformed plane.
  */
-inline Plane operator*(const Matrix& matrix, const Plane& plane);
+inline const Plane operator*(const Matrix& matrix, const Plane& plane);
 
 }
 

+ 1 - 1
gameplay/src/Plane.inl

@@ -9,7 +9,7 @@ inline Plane& Plane::operator*=(const Matrix& matrix)
     return *this;
 }
 
-inline Plane operator*(const Matrix& matrix, const Plane& plane)
+inline const Plane operator*(const Matrix& matrix, const Plane& plane)
 {
     Plane p(plane);
     p.transform(matrix);

+ 63 - 20
gameplay/src/PlatformQNX.cpp

@@ -504,10 +504,7 @@ Platform* Platform::create(Game* game)
     int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2;
     int screenSwapInterval = WINDOW_VSYNC ? 1 : 0;
     int screenTransparency = SCREEN_TRANSPARENCY_NONE;
-
-    // Hard-coded to fullscreen.
-    __screenWindowSize[0] = -1;
-    __screenWindowSize[1] = -1;
+    int angle = atoi(getenv("ORIENTATION"));
 
     // Hard-coded to (0,0).
     int windowPosition[] =
@@ -574,25 +571,71 @@ Platform* Platform::create(Game* game)
         goto error;
     }
 
-    if (__screenWindowSize[0] > 0 && __screenWindowSize[1] > 0)
-    {
-        rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_SIZE, __screenWindowSize);
-        if (rc)
-        {
-            perror("screen_set_window_property_iv(SCREEN_PROPERTY_SIZE)");
-            goto error;
-        }
-    }
-    else
+	screen_display_t screen_display;
+    rc = screen_get_window_property_pv(__screenWindow, SCREEN_PROPERTY_DISPLAY, (void **)&screen_display);
+    if (rc)
     {
-        rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_SIZE, __screenWindowSize);
-        if (rc)
-        {
-            perror("screen_get_window_property_iv(SCREEN_PROPERTY_SIZE)");
-            goto error;
-        }
+    	perror("screen_get_window_property_pv(SCREEN_PROPERTY_DISPLAY)");
+    	goto error;
     }
 
+	screen_display_mode_t screen_mode;
+	rc = screen_get_display_property_pv(screen_display, SCREEN_PROPERTY_MODE, (void**)&screen_mode);
+	if (rc)
+	{
+		perror("screen_get_display_property_pv(SCREEN_PROPERTY_MODE)");
+		goto error;
+	}
+
+    int size[2];
+	rc = screen_get_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, size);
+	if (rc)
+	{
+		perror("screen_get_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+		goto error;
+	}
+
+	__screenWindowSize[0] = size[0];
+	__screenWindowSize[1] = size[1];
+
+	if ((angle == 0) || (angle == 180))
+	{
+		if (((screen_mode.width > screen_mode.height) && (size[0] < size[1])) ||
+			((screen_mode.width < screen_mode.height) && (size[0] > size[1])))
+		{
+			__screenWindowSize[1] = size[0];
+			__screenWindowSize[0] = size[1];
+		}
+	}
+	else if ((angle == 90) || (angle == 270))
+	{
+		if (((screen_mode.width > screen_mode.height) && (size[0] > size[1])) ||
+			((screen_mode.width < screen_mode.height) && (size[0] < size[1])))
+		{
+			__screenWindowSize[1] = size[0];
+			__screenWindowSize[0] = size[1];
+		}
+	}
+	else
+	{
+		perror("Navigator returned an unexpected orientation angle.");
+		goto error;
+	}
+
+	rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_BUFFER_SIZE, __screenWindowSize);
+	if (rc)
+	{
+		perror("screen_set_window_property_iv(SCREEN_PROPERTY_BUFFER_SIZE)");
+		goto error;
+	}
+
+	rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_ROTATION, &angle);
+	if (rc)
+	{
+		perror("screen_set_window_property_iv(SCREEN_PROPERTY_ROTATION)");
+		goto error;
+	}
+
     if (windowPosition[0] != 0 || windowPosition[1] != 0)
     {
         rc = screen_set_window_property_iv(__screenWindow, SCREEN_PROPERTY_POSITION, windowPosition);

+ 12 - 11
gameplay/src/Properties.cpp

@@ -477,34 +477,35 @@ bool Properties::exists(const char* name) const
     return _properties.find(name) != _properties.end();
 }
 
-bool isStringNumeric(const char* str)
+const bool isStringNumeric(const char* str)
 {
-    char* ptr = const_cast<char*>(str);
+    // The first character may be '-'
+    if (*str == '-')
+        str++;
 
-    // First character must be a digit
-    if (!isdigit(*ptr))
+    // The first character after the sign must be a digit
+    if (!isdigit(*str))
         return false;
-    ptr++;
+    str++;
 
     // All remaining characters must be digits, with a single decimal (.) permitted
     unsigned int decimalCount = 0;
-    while (*ptr)
+    while (*str)
     {
-        if (!isdigit(*ptr))
+        if (!isdigit(*str))
         {
-            if (*ptr == '.' && decimalCount == 0)
+            if (*str == '.' && decimalCount == 0)
             {
                 // Max of 1 decimal allowed
                 decimalCount++;
             }
             else
             {
-                // Not a number
+                return false;
             }
         }
-        ptr++;
+        str++;
     }
-    
     return true;
 }
 

+ 1 - 1
gameplay/src/Quaternion.h

@@ -340,7 +340,7 @@ public:
      * @param q The quaternion to multiply.
      * @return The quaternion product.
      */
-    inline Quaternion operator*(const Quaternion& q) const;
+    inline const Quaternion operator*(const Quaternion& q) const;
 
     /**
      * Multiplies this quaternion with the given quaternion.

+ 1 - 1
gameplay/src/Quaternion.inl

@@ -3,7 +3,7 @@
 namespace gameplay
 {
 
-inline Quaternion Quaternion::operator*(const Quaternion& q) const
+inline const Quaternion Quaternion::operator*(const Quaternion& q) const
 {
     Quaternion result(*this);
     result.multiply(q);

+ 1 - 1
gameplay/src/Ray.h

@@ -167,7 +167,7 @@ private:
  * @param ray The ray to transform.
  * @return The resulting transformed ray.
  */
-inline Ray operator*(const Matrix& matrix, const Ray& ray);
+inline const Ray operator*(const Matrix& matrix, const Ray& ray);
 
 }
 

+ 1 - 1
gameplay/src/Ray.inl

@@ -9,7 +9,7 @@ inline Ray& Ray::operator*=(const Matrix& matrix)
     return *this;
 }
 
-inline Ray operator*(const Matrix& matrix, const Ray& ray)
+inline const Ray operator*(const Matrix& matrix, const Ray& ray)
 {
     Ray r(ray);
     r.transform(matrix);

+ 6 - 4
gameplay/src/SceneLoader.cpp

@@ -10,6 +10,7 @@ namespace gameplay
 std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
+std::string SceneLoader::_path;
 
 Scene* SceneLoader::load(const char* filePath)
 {
@@ -34,6 +35,8 @@ Scene* SceneLoader::load(const char* filePath)
         return NULL;
     }
 
+    // Get the path to the main GPB.
+    _path = sceneProperties->getString("path");
     // Build the node URL/property and animation reference tables and load the referenced files.
     buildReferenceTables(sceneProperties);
     loadReferencedFiles();
@@ -706,11 +709,10 @@ PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint
 Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
 {
     // Load the main scene from the specified path.
-    const char* path = sceneProperties->getString("path");
-    Package* package = Package::create(path);
+    Package* package = Package::create(_path.c_str());
     if (!package)
     {
-        WARN_VARG("Failed to load scene GPB file '%s'.", path);
+        WARN_VARG("Failed to load scene GPB file '%s'.", _path.c_str());
         return NULL;
     }
 
@@ -718,7 +720,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Scene* scene = package->loadScene(sceneID);
     if (!scene)
     {
-        WARN_VARG("Failed to load scene from '%s'.", path);
+        WARN_VARG("Failed to load scene from '%s'.", _path.c_str());
         SAFE_RELEASE(package);
         return NULL;
     }

+ 3 - 0
gameplay/src/SceneLoader.h

@@ -95,6 +95,9 @@ private:
     // Holds all the nodes+properties declared in the .scene file.
     static std::vector<SceneNode> _sceneNodes;
 
+
+    // The path of the main GPB for the scene being loaded.
+    static std::string _path;
 };
 
 }

+ 62 - 10
gameplay/src/SpriteBatch.cpp

@@ -69,6 +69,7 @@ SpriteBatch::SpriteBatch(const SpriteBatch& copy)
 SpriteBatch::~SpriteBatch()
 {
     SAFE_DELETE(_batch);
+    SAFE_RELEASE(__spriteEffect);
 }
 
 SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
@@ -200,23 +201,33 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 }
 
 void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
-                       const Vector2& rotationPoint, float rotationAngle)
+                       const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
-    // Expand dst by scale into 4 points.
-    float x2 = dst.x + width;
-    float y2 = dst.y + height;
+    float x = dst.x;
+    float y = dst.y;
+
+    // Treat the given position as the center if the user specified it as such.
+    if (positionIsCenter)
+    {
+        x -= 0.5f * width;
+        y -= 0.5f * height;
+    }
+
+    // Expand the destination position by scale into 4 points.
+    float x2 = x + width;
+    float y2 = y + height;
     
-    Vector2 upLeft(dst.x, dst.y);
-    Vector2 upRight(x2, dst.y);
-    Vector2 downLeft(dst.x, y2);
+    Vector2 upLeft(x, y);
+    Vector2 upRight(x2, y);
+    Vector2 downLeft(x, y2);
     Vector2 downRight(x2, y2);
 
     // Rotate points around rotationAxis by rotationAngle.
     Vector2 pivotPoint(rotationPoint);
     pivotPoint.x *= width;
     pivotPoint.y *= height;
-    pivotPoint.x += dst.x;
-    pivotPoint.y += dst.y;
+    pivotPoint.x += x;
+    pivotPoint.y += y;
     upLeft.rotate(pivotPoint, rotationAngle);
     upRight.rotate(pivotPoint, rotationAngle);
     downLeft.rotate(pivotPoint, rotationAngle);
@@ -234,13 +245,54 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
     _batch->add(v, 4, indices, 4);
 }
 
+void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
+    float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
+{
+    // Calculate the vertex positions.
+    Vector3 p[4];
+    p[0] = position - 0.5f * width * right;
+    p[1] = position + 0.5f * width * right;
+    p[2] = p[0] + height * forward;
+    p[3] = p[1] + height * forward;
+
+    // Calculate the rotation point.
+    Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+
+    // Rotate all points the specified amount about the given point (about the up vector).
+    Vector3 u;
+    Vector3::cross(right, forward, &u);
+    Matrix rotation;
+    Matrix::createRotation(u, rotationAngle, &rotation);
+    p[0] = (rotation * (p[0] - rp)) + rp;
+    p[1] = (rotation * (p[1] - rp)) + rp;
+    p[2] = (rotation * (p[2] - rp)) + rp;
+    p[3] = (rotation * (p[3] - rp)) + rp;
+
+    // Add the sprite vertex data to the batch.
+    static SpriteVertex v[4];
+    ADD_SPRITE_VERTEX(v[0], p[0].x, p[0].y, p[0].z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], p[1].x, p[1].y, p[1].z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], p[2].x, p[2].y, p[2].z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], p[3].x, p[3].y, p[3].z, u2, v2, color.x, color.y, color.z, color.w);
+    
+    static const unsigned short indices[4] = { 0, 1, 2, 3 };
+    _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
+}
+
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
 {
     draw(x, y, 0, width, height, u1, v1, u2, v2, color);
 }
 
-void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
+void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
 {
+    // Treat the given position as the center if the user specified it as such.
+    if (positionIsCenter)
+    {
+        x -= 0.5f * width;
+        y -= 0.5f * height;
+    }
+
     // Write sprite vertex data.
     float x2 = x + width;
     float y2 = y + height;

+ 24 - 2
gameplay/src/SpriteBatch.h

@@ -140,9 +140,30 @@ public:
      * @param rotationPoint The point to rotate around, relative to dst's x and y values.
      *                      (e.g. Use Vector2(0.5f, 0.5f) to rotate around the quad's center.)
      * @param rotationAngle The rotation angle.
+     * @param positionIsCenter Specified whether the given destination is to be the center of the sprite or not (if not, it is treated as the bottom-left).
      */
     void draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
-              const Vector2& rotationPoint, float rotationAngle);
+              const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
+    
+    /**
+     * Draws a single sprite, rotated about the implied up vector.
+     * 
+     * @param position The destination position.
+     * @param right The right vector of the sprite quad (should be normalized).
+     * @param forward The forward vector of the sprite quad (should be normalized).
+     * @param width The width of the sprite.
+     * @param height The height of the sprite.
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param rotationPoint The point to rotate around, relative to dst's x and y values.
+     *                      (e.g. Use Vector2(0.5f, 0.5f) to rotate around the quad's center.)
+     * @param rotationAngle The rotation angle.
+     */
+    void draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height, 
+        float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle);
 
     /**
      * Draws a single sprite.
@@ -172,8 +193,9 @@ public:
      * @param u2 Texture coordinate.
      * @param v2 Texture coordinate.
      * @param color The color to tint the sprite. Use white for no tint.
+     * @param positionIsCenter Specified whether the given destination is to be the center of the sprite or not (if not, it is treated as the bottom-left).
      */
-    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color);
+    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter = false);
 
     /**
      * Ends sprite drawing.

+ 2 - 1
gameplay/src/Texture.cpp

@@ -68,7 +68,8 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
             if (tolower(ext[1]) == 'p' && tolower(ext[2]) == 'n' && tolower(ext[3]) == 'g')
             {
                 Image* image = Image::create(path);
-                texture = create(image, generateMipmaps);
+                if (image)
+                    texture = create(image, generateMipmaps);
                 SAFE_RELEASE(image);
             }
             break;

+ 14 - 5
gameplay/src/Vector2.h

@@ -323,7 +323,7 @@ public:
      * @param v The vector to add.
      * @return The vector sum.
      */
-    inline Vector2 operator+(const Vector2& v) const;
+    inline const Vector2 operator+(const Vector2& v) const;
 
     /**
      * Adds the given vector to this vector.
@@ -341,7 +341,7 @@ public:
      * @param v The vector to add.
      * @return The vector sum.
      */
-    inline Vector2 operator-(const Vector2& v) const;
+    inline const Vector2 operator-(const Vector2& v) const;
 
     /**
      * Subtracts the given vector from this vector.
@@ -358,7 +358,7 @@ public:
      * 
      * @return The negation of this vector.
      */
-    inline Vector2 operator-() const;
+    inline const Vector2 operator-() const;
 
     /**
      * Calculates the scalar product of this vector with the given value.
@@ -368,7 +368,7 @@ public:
      * @param x The value to scale by.
      * @return The scaled vector.
      */
-    inline Vector2 operator*(float x) const;
+    inline const Vector2 operator*(float x) const;
 
     /**
      * Scales this vector by the given value.
@@ -395,6 +395,15 @@ public:
      * @return True if this vector is equal to the given vector, false otherwise.
      */
     inline bool operator==(const Vector2& v) const;
+
+    /**
+     * Determines if this vector is not equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is not equal to the given vector, false otherwise.
+     */
+    inline bool operator!=(const Vector2& v) const;
 };
 
 /**
@@ -404,7 +413,7 @@ public:
  * @param v The vector to scale.
  * @return The scaled vector.
  */
-inline Vector2 operator*(float x, const Vector2& v);
+inline const Vector2 operator*(float x, const Vector2& v);
 
 }
 

+ 10 - 5
gameplay/src/Vector2.inl

@@ -3,7 +3,7 @@
 namespace gameplay
 {
 
-inline Vector2 Vector2::operator+(const Vector2& v) const
+inline const Vector2 Vector2::operator+(const Vector2& v) const
 {
     Vector2 result(*this);
     result.add(v);
@@ -16,7 +16,7 @@ inline Vector2& Vector2::operator+=(const Vector2& v)
     return *this;
 }
 
-inline Vector2 Vector2::operator-(const Vector2& v) const
+inline const Vector2 Vector2::operator-(const Vector2& v) const
 {
     Vector2 result(*this);
     result.subtract(v);
@@ -29,14 +29,14 @@ inline Vector2& Vector2::operator-=(const Vector2& v)
     return *this;
 }
 
-inline Vector2 Vector2::operator-() const
+inline const Vector2 Vector2::operator-() const
 {
     Vector2 result(*this);
     result.negate();
     return result;
 }
 
-inline Vector2 Vector2::operator*(float x) const
+inline const Vector2 Vector2::operator*(float x) const
 {
     Vector2 result(*this);
     result.scale(x);
@@ -63,7 +63,12 @@ inline bool Vector2::operator==(const Vector2& v) const
     return x==v.x && y==v.y;
 }
 
-inline Vector2 operator*(float x, const Vector2& v)
+inline bool Vector2::operator!=(const Vector2& v) const
+{
+    return x!=v.x || y!=v.y;
+}
+
+inline const Vector2 operator*(float x, const Vector2& v)
 {
     Vector2 result(v);
     result.scale(x);

+ 17 - 8
gameplay/src/Vector3.h

@@ -353,7 +353,7 @@ public:
      * @param v The vector to add.
      * @return The vector sum.
      */
-    inline Vector3 operator+(const Vector3& v) const;
+    inline const Vector3 operator+(const Vector3& v) const;
 
     /**
      * Adds the given vector to this vector.
@@ -364,14 +364,14 @@ public:
     inline Vector3& operator+=(const Vector3& v);
 
     /**
-     * Calculates the sum of this vector with the given vector.
+     * Calculates the difference of this vector with the given vector.
      * 
      * Note: this does not modify this vector.
      * 
-     * @param v The vector to add.
-     * @return The vector sum.
+     * @param v The vector to subtract.
+     * @return The vector difference.
      */
-    inline Vector3 operator-(const Vector3& v) const;
+    inline const Vector3 operator-(const Vector3& v) const;
 
     /**
      * Subtracts the given vector from this vector.
@@ -388,7 +388,7 @@ public:
      * 
      * @return The negation of this vector.
      */
-    inline Vector3 operator-() const;
+    inline const Vector3 operator-() const;
 
     /**
      * Calculates the scalar product of this vector with the given value.
@@ -398,7 +398,7 @@ public:
      * @param x The value to scale by.
      * @return The scaled vector.
      */
-    inline Vector3 operator*(float x) const;
+    inline const Vector3 operator*(float x) const;
 
     /**
      * Scales this vector by the given value.
@@ -425,6 +425,15 @@ public:
      * @return True if this vector is equal to the given vector, false otherwise.
      */
     inline bool operator==(const Vector3& v) const;
+
+    /**
+     * Determines if this vector is not equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is not equal to the given vector, false otherwise.
+     */
+    inline bool operator!=(const Vector3& v) const;
 };
 
 /**
@@ -434,7 +443,7 @@ public:
  * @param v The vector to scale.
  * @return The scaled vector.
  */
-inline Vector3 operator*(float x, const Vector3& v);
+inline const Vector3 operator*(float x, const Vector3& v);
 
 }
 

+ 10 - 5
gameplay/src/Vector3.inl

@@ -4,7 +4,7 @@
 namespace gameplay
 {
 
-inline Vector3 Vector3::operator+(const Vector3& v) const
+inline const Vector3 Vector3::operator+(const Vector3& v) const
 {
     Vector3 result(*this);
     result.add(v);
@@ -17,7 +17,7 @@ inline Vector3& Vector3::operator+=(const Vector3& v)
     return *this;
 }
 
-inline Vector3 Vector3::operator-(const Vector3& v) const
+inline const Vector3 Vector3::operator-(const Vector3& v) const
 {
     Vector3 result(*this);
     result.subtract(v);
@@ -30,14 +30,14 @@ inline Vector3& Vector3::operator-=(const Vector3& v)
     return *this;
 }
 
-inline Vector3 Vector3::operator-() const
+inline const Vector3 Vector3::operator-() const
 {
     Vector3 result(*this);
     result.negate();
     return result;
 }
 
-inline Vector3 Vector3::operator*(float x) const
+inline const Vector3 Vector3::operator*(float x) const
 {
     Vector3 result(*this);
     result.scale(x);
@@ -68,7 +68,12 @@ inline bool Vector3::operator==(const Vector3& v) const
     return x==v.x && y==v.y && z==v.z;
 }
 
-inline Vector3 operator*(float x, const Vector3& v)
+inline bool Vector3::operator!=(const Vector3& v) const
+{
+    return x!=v.x || y!=v.y || z!=v.z;
+}
+
+inline const Vector3 operator*(float x, const Vector3& v)
 {
     Vector3 result(v);
     result.scale(x);

+ 14 - 5
gameplay/src/Vector4.h

@@ -348,7 +348,7 @@ public:
      * @param v The vector to add.
      * @return The vector sum.
      */
-    inline Vector4 operator+(const Vector4& v) const;
+    inline const Vector4 operator+(const Vector4& v) const;
 
     /**
      * Adds the given vector to this vector.
@@ -366,7 +366,7 @@ public:
      * @param v The vector to add.
      * @return The vector sum.
      */
-    inline Vector4 operator-(const Vector4& v) const;
+    inline const Vector4 operator-(const Vector4& v) const;
 
     /**
      * Subtracts the given vector from this vector.
@@ -383,7 +383,7 @@ public:
      * 
      * @return The negation of this vector.
      */
-    inline Vector4 operator-() const;
+    inline const Vector4 operator-() const;
 
     /**
      * Calculates the scalar product of this vector with the given value.
@@ -393,7 +393,7 @@ public:
      * @param x The value to scale by.
      * @return The scaled vector.
      */
-    inline Vector4 operator*(float x) const;
+    inline const Vector4 operator*(float x) const;
 
     /**
      * Scales this vector by the given value.
@@ -420,6 +420,15 @@ public:
      * @return True if this vector is equal to the given vector, false otherwise.
      */
     inline bool operator==(const Vector4& v) const;
+
+    /**
+     * Determines if this vector is not equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is not equal to the given vector, false otherwise.
+     */
+    inline bool operator!=(const Vector4& v) const;
 };
 
 /**
@@ -429,7 +438,7 @@ public:
  * @param v The vector to scale.
  * @return The scaled vector.
  */
-inline Vector4 operator*(float x, const Vector4& v);
+inline const Vector4 operator*(float x, const Vector4& v);
 
 }
 

+ 10 - 5
gameplay/src/Vector4.inl

@@ -4,7 +4,7 @@
 namespace gameplay
 {
 
-inline Vector4 Vector4::operator+(const Vector4& v) const
+inline const Vector4 Vector4::operator+(const Vector4& v) const
 {
     Vector4 result(*this);
     result.add(v);
@@ -17,7 +17,7 @@ inline Vector4& Vector4::operator+=(const Vector4& v)
     return *this;
 }
 
-inline Vector4 Vector4::operator-(const Vector4& v) const
+inline const Vector4 Vector4::operator-(const Vector4& v) const
 {
     Vector4 result(*this);
     result.subtract(v);
@@ -30,14 +30,14 @@ inline Vector4& Vector4::operator-=(const Vector4& v)
     return *this;
 }
 
-inline Vector4 Vector4::operator-() const
+inline const Vector4 Vector4::operator-() const
 {
     Vector4 result(*this);
     result.negate();
     return result;
 }
 
-inline Vector4 Vector4::operator*(float x) const
+inline const Vector4 Vector4::operator*(float x) const
 {
     Vector4 result(*this);
     result.scale(x);
@@ -75,7 +75,12 @@ inline bool Vector4::operator==(const Vector4& v) const
     return x==v.x && y==v.y && z==v.z && w==v.w;
 }
 
-inline Vector4 operator*(float x, const Vector4& v)
+inline bool Vector4::operator!=(const Vector4& v) const
+{
+    return x!=v.x || y!=v.y || z!=v.z || w!=v.w;
+}
+
+inline const Vector4 operator*(float x, const Vector4& v)
 {
     Vector4 result(v);
     result.scale(x);