2
0
Эх сурвалжийг харах

Merge pull request #111 from blackberry-gaming/next

Next
Sean Paul Taylor 13 жил өмнө
parent
commit
1b8beb7353
100 өөрчлөгдсөн 4968 нэмэгдсэн , 3564 устгасан
  1. 131 80
      gameplay-encoder/src/DAESceneEncoder.cpp
  2. 3 1
      gameplay-encoder/src/DAESceneEncoder.h
  3. 77 14
      gameplay-encoder/src/DAEUtil.cpp
  4. 18 0
      gameplay-encoder/src/DAEUtil.h
  5. 2 2
      gameplay-encoder/src/GPBDecoder.cpp
  6. 1 0
      gameplay-encoder/src/Scene.cpp
  7. 3 9
      gameplay-encoder/src/TTFFontEncoder.cpp
  8. 0 8
      gameplay-encoder/src/TTFFontEncoder.h
  9. 1 1
      gameplay-encoder/src/main.cpp
  10. 2 0
      gameplay/gameplay.vcxproj
  11. 6 0
      gameplay/gameplay.vcxproj.filters
  12. 12 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  13. 41 39
      gameplay/src/AbsoluteLayout.cpp
  14. 47 15
      gameplay/src/Animation.cpp
  15. 39 7
      gameplay/src/AnimationClip.cpp
  16. 7 0
      gameplay/src/AnimationController.cpp
  17. 142 17
      gameplay/src/AnimationTarget.cpp
  18. 20 1
      gameplay/src/AnimationTarget.h
  19. 7 4
      gameplay/src/AnimationValue.cpp
  20. 74 35
      gameplay/src/AudioBuffer.cpp
  21. 13 11
      gameplay/src/AudioController.cpp
  22. 2 0
      gameplay/src/AudioListener.cpp
  23. 39 39
      gameplay/src/AudioSource.cpp
  24. 58 68
      gameplay/src/Base.h
  25. 1 1
      gameplay/src/BoundingBox.cpp
  26. 66 66
      gameplay/src/Bundle.cpp
  27. 30 30
      gameplay/src/Button.cpp
  28. 7 7
      gameplay/src/Camera.cpp
  29. 13 18
      gameplay/src/CheckBox.cpp
  30. 1 1
      gameplay/src/CheckBox.h
  31. 336 289
      gameplay/src/Container.cpp
  32. 7 5
      gameplay/src/Container.h
  33. 999 835
      gameplay/src/Control.cpp
  34. 31 16
      gameplay/src/Control.h
  35. 0 4
      gameplay/src/Curve.cpp
  36. 5 5
      gameplay/src/Effect.cpp
  37. 6 5
      gameplay/src/FileSystem.cpp
  38. 6 2
      gameplay/src/FlowLayout.cpp
  39. 0 3
      gameplay/src/FlowLayout.h
  40. 340 253
      gameplay/src/Font.cpp
  41. 70 6
      gameplay/src/Font.h
  42. 419 282
      gameplay/src/Form.cpp
  43. 36 0
      gameplay/src/Form.h
  44. 1 1
      gameplay/src/FrameBuffer.cpp
  45. 27 23
      gameplay/src/Frustum.cpp
  46. 9 0
      gameplay/src/Frustum.h
  47. 4 2
      gameplay/src/Game.cpp
  48. 2 2
      gameplay/src/Image.cpp
  49. 70 64
      gameplay/src/Label.cpp
  50. 3 1
      gameplay/src/Label.h
  51. 54 43
      gameplay/src/Layout.cpp
  52. 23 1
      gameplay/src/Layout.h
  53. 14 14
      gameplay/src/Light.cpp
  54. 6 6
      gameplay/src/Material.cpp
  55. 10 10
      gameplay/src/MaterialParameter.cpp
  56. 29 29
      gameplay/src/Matrix.cpp
  57. 4 4
      gameplay/src/MeshBatch.cpp
  58. 1 1
      gameplay/src/MeshBatch.inl
  59. 6 6
      gameplay/src/MeshSkin.cpp
  60. 2 2
      gameplay/src/Model.cpp
  61. 19 8
      gameplay/src/Node.cpp
  62. 34 23
      gameplay/src/ParticleEmitter.cpp
  63. 8 7
      gameplay/src/ParticleEmitter.h
  64. 2 2
      gameplay/src/Pass.cpp
  65. 3 3
      gameplay/src/PhysicsCharacter.cpp
  66. 9 9
      gameplay/src/PhysicsCollisionShape.cpp
  67. 1 1
      gameplay/src/PhysicsConstraint.cpp
  68. 20 20
      gameplay/src/PhysicsController.cpp
  69. 3 3
      gameplay/src/PhysicsGhostObject.cpp
  70. 5 5
      gameplay/src/PhysicsRigidBody.cpp
  71. 2 2
      gameplay/src/Plane.cpp
  72. 0 7
      gameplay/src/Platform.h
  73. 16 9
      gameplay/src/PlatformAndroid.cpp
  74. 0 6
      gameplay/src/PlatformMacOSX.mm
  75. 51 57
      gameplay/src/PlatformQNX.cpp
  76. 37 44
      gameplay/src/PlatformWin32.cpp
  77. 39 44
      gameplay/src/PlatformiOS.mm
  78. 24 24
      gameplay/src/Properties.cpp
  79. 3 3
      gameplay/src/Properties.h
  80. 11 11
      gameplay/src/Quaternion.cpp
  81. 1 0
      gameplay/src/Quaternion.h
  82. 14 13
      gameplay/src/RadioButton.cpp
  83. 1 1
      gameplay/src/RadioButton.h
  84. 4 4
      gameplay/src/RenderState.cpp
  85. 4 4
      gameplay/src/Scene.cpp
  86. 51 50
      gameplay/src/SceneLoader.cpp
  87. 1 1
      gameplay/src/SceneLoader.h
  88. 4 4
      gameplay/src/ScreenDisplayer.h
  89. 140 0
      gameplay/src/ScrollLayout.cpp
  90. 72 0
      gameplay/src/ScrollLayout.h
  91. 26 20
      gameplay/src/Slider.cpp
  92. 3 2
      gameplay/src/Slider.h
  93. 157 113
      gameplay/src/SpriteBatch.cpp
  94. 68 2
      gameplay/src/SpriteBatch.h
  95. 2 2
      gameplay/src/Technique.cpp
  96. 67 46
      gameplay/src/TextBox.cpp
  97. 2 1
      gameplay/src/TextBox.h
  98. 9 9
      gameplay/src/Texture.cpp
  99. 655 610
      gameplay/src/Theme.cpp
  100. 17 6
      gameplay/src/ThemeStyle.cpp

+ 131 - 80
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -261,13 +261,7 @@ void DAESceneEncoder::write(const std::string& filepath, const EncoderArguments&
     daeElement* scene = NULL;
     daeElement* scene = NULL;
     if (domScene && domScene->getInstance_visual_scene())
     if (domScene && domScene->getInstance_visual_scene())
     {
     {
-        scene = domScene->getInstance_visual_scene()->getUrl().getElement();
-        if (scene->getElementType() != COLLADA_TYPE::VISUAL_SCENE)
-        {
-            // This occured once where Maya exported a Node and Scene element with the same ID.
-            fprintf(stderr,"Error: instance_visual_scene does not reference visual_scene for file:%s\n", filepath.c_str());
-            return;
-        }
+        scene = getVisualScene(domScene);
         if (scene)
         if (scene)
         {
         {
             if (nodeId == NULL)
             if (nodeId == NULL)
@@ -366,89 +360,114 @@ void DAESceneEncoder::loadAnimations(const domCOLLADA* dom)
     }
     }
 }
 }
 
 
-void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef)
+void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef, const char* altId)
 {
 {
-    // <channel> points to one <sampler>
-    // <sampler> points to multiple <input> elements
-
-    Animation* animation = new Animation();
-    const char* str = animationRef->getId();
-    if (str)
+    // Animations can contain other animations.
+    const domAnimation_Array& animationArray = animationRef->getAnimation_array();
+    unsigned int animationCount = animationArray.getCount();
+    
+    if (animationCount == 1)
     {
     {
-        animation->setId(str);
+        // DAE_FBX nests 1 animation within another animation for some reason.
+        loadAnimation(animationArray.get(0), animationRef->getId());
+    }
+    else if ( animationCount > 1)
+    {
+        loadAnimation(animationArray.get(0));
     }
     }
 
 
+    // <channel> points to one <sampler>
+    // <sampler> points to multiple <input> elements
+
     // <channel>
     // <channel>
     const domChannel_Array& channelArray = animationRef->getChannel_array();
     const domChannel_Array& channelArray = animationRef->getChannel_array();
     size_t channelArrayCount = channelArray.getCount();
     size_t channelArrayCount = channelArray.getCount();
-    for (size_t i = 0; i < channelArrayCount; ++i)
+    if (channelArrayCount > 0)
     {
     {
-        AnimationChannel* animationChannel = new AnimationChannel();
-
-        const domChannelRef& channelRef = channelArray.get(i);
-
-        // <sampler>
-        const domSamplerRef sampler = getSampler(channelRef);
-        assert(sampler);
+        Animation* animation = new Animation();
+        const char* str = animationRef->getId();
+        if (str)
+        {
+            animation->setId(str);
+        }
+        else if (altId)
+        {
+            animation->setId(altId);
+        }
 
 
-        // <input>
-        const domInputLocal_Array& inputArray = sampler->getInput_array();
-        size_t inputArrayCount = inputArray.getCount();
-        for (size_t j = 0; j < inputArrayCount; ++j)
+        for (size_t i = 0; i < channelArrayCount; ++i)
         {
         {
-            const domInputLocalRef& inputLocal = inputArray.get(j);
+            AnimationChannel* animationChannel = new AnimationChannel();
 
 
-            // <source>
-            const domSourceRef source = getSource(inputLocal, animationRef);
+            const domChannelRef& channelRef = channelArray.get(i);
 
 
-            std::string semantic = inputLocal->getSemantic();
-            if (equals(semantic, "INTERPOLATION"))
-            {
-                // Interpolation source is a list of strings
-                loadInterpolation(source, animationChannel);
-            }
-            else
+            // <sampler>
+            const domSamplerRef sampler = getSampler(channelRef);
+            assert(sampler);
+
+            // <input>
+            const domInputLocal_Array& inputArray = sampler->getInput_array();
+            size_t inputArrayCount = inputArray.getCount();
+            for (size_t j = 0; j < inputArrayCount; ++j)
             {
             {
-                // The other sources are lists of floats.
-                std::vector<float> floats;
-                copyFloats(source->getFloat_array(), &floats);
-                if (equals(semantic, "INPUT"))
-                {
-                    // TODO: Ensure param name is TIME?
-                    for (std::vector<float>::iterator k = floats.begin(); k != floats.end(); ++k)
-                    {
-                        // Convert seconds to milliseconds
-                        *k = *k * 1000.0f;
-                    }
-                    animationChannel->setKeyTimes(floats);
-                }
-                else if (equals(semantic, "OUTPUT"))
+                const domInputLocalRef& inputLocal = inputArray.get(j);
+
+                // <source>
+                const domSourceRef source = getSource(inputLocal, animationRef);
+
+                std::string semantic = inputLocal->getSemantic();
+                if (equals(semantic, "INTERPOLATION"))
                 {
                 {
-                    animationChannel->setKeyValues(floats);
+                    // Interpolation source is a list of strings
+                    loadInterpolation(source, animationChannel);
                 }
                 }
-                else if (equals(semantic, "IN_TANGENT"))
+                else
                 {
                 {
-                    animationChannel->setTangentsIn(floats);
+                    // The other sources are lists of floats.
+                    std::vector<float> floats;
+                    copyFloats(source->getFloat_array(), &floats);
+                    if (equals(semantic, "INPUT"))
+                    {
+                        // TODO: Ensure param name is TIME?
+                        for (std::vector<float>::iterator k = floats.begin(); k != floats.end(); ++k)
+                        {
+                            // Convert seconds to milliseconds
+                            *k = *k * 1000.0f;
+                        }
+                        animationChannel->setKeyTimes(floats);
+                    }
+                    else if (equals(semantic, "OUTPUT"))
+                    {
+                        animationChannel->setKeyValues(floats);
+                    }
+                    else if (equals(semantic, "IN_TANGENT"))
+                    {
+                        animationChannel->setTangentsIn(floats);
+                    }
+                    else if (equals(semantic, "OUT_TANGENT"))
+                    {
+                        animationChannel->setTangentsOut(floats);
+                    }
                 }
                 }
-                else if (equals(semantic, "OUT_TANGENT"))
+            }
+            
+            // get target attribute enum value
+            if (loadTarget(channelRef, animationChannel))
+            {
+                if (animationChannel->getKeyTimes().size() > 0)
                 {
                 {
-                    animationChannel->setTangentsOut(floats);
+                    animation->add(animationChannel);
                 }
                 }
             }
             }
         }
         }
-        // get target attribute enum value
-        if (loadTarget(channelRef, animationChannel))
+        if (animation->getAnimationChannelCount() > 0)
         {
         {
-            animation->add(animationChannel);
+            _gamePlayFile.addAnimation(animation);
+        }
+        else
+        {
+            delete animation;
         }
         }
-    }
-    if (animation->getAnimationChannelCount() > 0)
-    {
-        _gamePlayFile.addAnimation(animation);
-    }
-    else
-    {
-        delete animation;
     }
     }
 }
 }
 
 
@@ -685,6 +704,11 @@ void DAESceneEncoder::loadScene(const domVisual_scene* visualScene)
 
 
     const domNode_Array& nodes = visualScene->getNode_array();
     const domNode_Array& nodes = visualScene->getNode_array();
     scene->setId(visualScene->getId());
     scene->setId(visualScene->getId());
+    if (scene->getId().length() == 0)
+    {
+        scene->setId("__SCENE__");
+    }
+
     size_t childCount = nodes.getCount();
     size_t childCount = nodes.getCount();
     for (size_t i = 0; i < childCount; ++i)
     for (size_t i = 0; i < childCount; ++i)
     {
     {
@@ -989,16 +1013,21 @@ void DAESceneEncoder::loadControllerInstance(const domNode* n, Node* node)
                     domInstance_controller::domSkeleton_Array& skeletons = instanceControllerRef->getSkeleton_array();
                     domInstance_controller::domSkeleton_Array& skeletons = instanceControllerRef->getSkeleton_array();
                     if (skeletons.getCount() == 0)
                     if (skeletons.getCount() == 0)
                     {
                     {
-                        warning("No skeletons found for instance controller: ");
-                        delete model;
-                        continue;
+                        domNode* rootJoint = getRootJointNode(skinElement);
+                        if (rootJoint)
+                        {
+                            loadSkeleton(rootJoint, model->getSkin());
+                            node->setModel(model);
+                        }
+                    }
+                    else
+                    {
+                        // Load the skeleton for this skin
+                        domInstance_controller::domSkeletonRef skeleton = getSkeleton(instanceControllerRef);
+                        assert(skeleton);
+                        loadSkeleton(skeleton, model->getSkin());
+                        node->setModel(model);
                     }
                     }
-                    // Load the skeleton for this skin
-                    domInstance_controller::domSkeletonRef skeleton = getSkeleton(instanceControllerRef);
-                    assert(skeleton);
-                    loadSkeleton(skeleton, model->getSkin());
-
-                    node->setModel(model);
                 }
                 }
             }
             }
         }
         }
@@ -1182,21 +1211,33 @@ Light* DAESceneEncoder::loadLight(const domLight* lightRef)
             {
             {
                 light->setQuadraticAttenuation((float)quadAtt->getValue());
                 light->setQuadraticAttenuation((float)quadAtt->getValue());
             }
             }
+
+            // When Maya exports DAE_FBX, the ambient lights are converted into point lights but with not attenuation elements.
+            // If this point light has no attenuation then assume it is ambient.
+            if (!(constAtt.cast() && linearAtt.cast() && quadAtt.cast()))
+            {
+                light->setAmbientLight();
+            }
         }
         }
     }
     }
     _gamePlayFile.addLight(light);
     _gamePlayFile.addLight(light);
     return light;
     return light;
 }
 }
 
 
+
 void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin)
 void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin)
 {
 {
     xsAnyURI skeletonUri = skeletonElement->getValue();
     xsAnyURI skeletonUri = skeletonElement->getValue();
     daeString skeletonId = skeletonUri.getID();
     daeString skeletonId = skeletonUri.getID();
     daeSIDResolver resolver(skeletonUri.getElement(), skeletonId);
     daeSIDResolver resolver(skeletonUri.getElement(), skeletonId);
     domNode* rootNode = daeSafeCast<domNode>(resolver.getElement());
     domNode* rootNode = daeSafeCast<domNode>(resolver.getElement());
-    
+    loadSkeleton(rootNode, skin);
+}
+
+void DAESceneEncoder::loadSkeleton(domNode* rootNode, MeshSkin* skin)
+{
     // Get the lookup scene id (sid) and joint index.
     // Get the lookup scene id (sid) and joint index.
-    std::string id = std::string(skeletonId);
+    std::string id = std::string(rootNode->getId());
 
 
     // Has the skeleton (root joint) been loaded yet?
     // Has the skeleton (root joint) been loaded yet?
     Node* skeleton = (Node*)_gamePlayFile.getFromRefTable(id);
     Node* skeleton = (Node*)_gamePlayFile.getFromRefTable(id);
@@ -1674,6 +1715,7 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
             {
             {
                 maxOffset = offset;
                 maxOffset = offset;
             }
             }
+            int polyIndexInt = (int) polyInts.get(poly + offset);
             unsigned int polyIndex = (unsigned int) polyInts.get(poly + offset);
             unsigned int polyIndex = (unsigned int) polyInts.get(poly + offset);
 
 
             switch (polygonInputs[k]->type)
             switch (polygonInputs[k]->type)
@@ -1774,8 +1816,17 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
                 {
                 {
                     // TODO: This assumes (s, t) are first
                     // TODO: This assumes (s, t) are first
                     unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride();
                     unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride();
-                    vertex.texCoord.x = (float)source.get(polyIndex * stride);
-                    vertex.texCoord.y = (float)source.get(polyIndex * stride + 1);
+                    if (polyIndexInt < 0)
+                    {
+                        unsigned int i = (unsigned int)((int)polygonInputs[k]->accessor->getCount()) + polyIndexInt;
+                        vertex.texCoord.x = (float)source.get(i * stride);
+                        vertex.texCoord.y = (float)source.get(i * stride + 1);
+                    }
+                    else
+                    {
+                        vertex.texCoord.x = (float)source.get(polyIndex * stride);
+                        vertex.texCoord.y = (float)source.get(polyIndex * stride + 1);
+                    }
                 }
                 }
                 else
                 else
                 {
                 {

+ 3 - 1
gameplay-encoder/src/DAESceneEncoder.h

@@ -111,14 +111,16 @@ private:
      * Loads a COLLADA animation element.
      * Loads a COLLADA animation element.
      * 
      * 
      * @param animationRef The animation dom element to load from.
      * @param animationRef The animation dom element to load from.
+     * @param altId   The id string to use if the animation doesn't have an id.
      */
      */
-    void loadAnimation(const domAnimationRef animationRef);
+    void loadAnimation(const domAnimationRef animationRef, const char* altId = NULL);
 
 
     Camera* loadCamera(const domCamera* cameraRef);
     Camera* loadCamera(const domCamera* cameraRef);
     Light* loadLight(const domLight* lightRef);
     Light* loadLight(const domLight* lightRef);
     Model* loadSkin(const domSkin* skinElement);
     Model* loadSkin(const domSkin* skinElement);
     Model* loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial);
     Model* loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial);
 
 
+    void loadSkeleton(domNode* rootNode, MeshSkin* skin);
     void loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin);
     void loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin);
     
     
     /**
     /**

+ 77 - 14
gameplay-encoder/src/DAEUtil.cpp

@@ -13,7 +13,16 @@ namespace gameplay
  * 
  * 
  * @return The index in skeletonArray or -1 if not found.
  * @return The index in skeletonArray or -1 if not found.
  */
  */
-int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, const domNodeRef& node);
+static int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, const domNodeRef& node);
+
+/**
+ * Gets all of the animation channels that target the given node and appends them to the list.
+ * 
+ * @param animationRef The animation to search in.
+ * @param nodeIdSlash The node's id with a forward slash appended to it.
+ * @param channels The list of channels to append to.
+ */
+static void getAnimationChannels(const domAnimationRef& animationRef, const std::string& nodeIdSlash, std::list<domChannelRef>& channels);
 
 
 void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels)
 void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels)
 {
 {
@@ -33,19 +42,7 @@ void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& chan
         for (size_t j = 0; j < animationCount; ++j)
         for (size_t j = 0; j < animationCount; ++j)
         {
         {
             domAnimationRef& animationRef = animationArray.get(j);
             domAnimationRef& animationRef = animationArray.get(j);
-            domChannel_Array& channelArray = animationRef->getChannel_array();
-            size_t channelArrayCount = channelArray.getCount();
-            for (size_t k = 0; k < channelArrayCount; ++k)
-            {
-                domChannelRef& channel = channelArray.get(k);
-                const char* target = channel->getTarget();
-
-                // TODO: Assumes only one target per channel?
-                if (startsWith(target, nodeIdSlash.c_str()))
-                {
-                    channels.push_back(channel);
-                }
-            }
+            getAnimationChannels(animationRef, nodeIdSlash, channels);
         }
         }
     }
     }
 
 
@@ -262,6 +259,20 @@ const domInstance_controller::domSkeletonRef getSkeleton(const domInstance_contr
     return NULL;
     return NULL;
 }
 }
 
 
+domNode* getRootJointNode(const domSkin* skin)
+{
+    std::vector<std::string> names;
+    getJointNames(skin, names);
+    daeSIDResolver resolver(const_cast<domSkin*>(skin)->getDocument()->getDomRoot(), names[0].c_str());
+    daeElement* element = resolver.getElement();
+    if (element && element->getElementType() == COLLADA_TYPE::NODE)
+    {
+        domNode* node = daeSafeCast<domNode>(resolver.getElement());
+        return node;
+    }
+    return NULL;
+}
+
 bool equalKeyTimes(const domSource* s1, const domSource* s2)
 bool equalKeyTimes(const domSource* s1, const domSource* s2)
 {
 {
     // TODO: shouldn't assume that the source has a float array.
     // TODO: shouldn't assume that the source has a float array.
@@ -355,4 +366,56 @@ int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, con
     return -1;
     return -1;
 }
 }
 
 
+void getAnimationChannels(const domAnimationRef& animationRef, const std::string& nodeIdSlash, std::list<domChannelRef>& channels)
+{
+    domChannel_Array& channelArray = animationRef->getChannel_array();
+    size_t channelArrayCount = channelArray.getCount();
+    for (size_t k = 0; k < channelArrayCount; ++k)
+    {
+        domChannelRef& channel = channelArray.get(k);
+        const char* target = channel->getTarget();
+
+        // TODO: Assumes only one target per channel?
+        if (startsWith(target, nodeIdSlash.c_str()))
+        {
+            channels.push_back(channel);
+        }
+    }
+
+    // This animation could have child animations.
+    const domAnimation_Array& animationArray = animationRef->getAnimation_array();
+    unsigned int animationCount = animationArray.getCount();
+    for (size_t i = 0; i < animationCount; ++i)
+    {
+        getAnimationChannels(animationArray[i], nodeIdSlash, channels);
+    }
+}
+
+domVisual_scene* getVisualScene(const domCOLLADA::domSceneRef& domScene)
+{
+    daeElement* scene = domScene->getInstance_visual_scene()->getUrl().getElement();
+    if (scene->getElementType() == COLLADA_TYPE::VISUAL_SCENE)
+    {
+        return static_cast<domVisual_scene*>(scene);
+    }
+
+    // DAE_FBX sometimes doesn't export an ID. In that case, see if there is only one visual scene and use that.
+    // Most of the time there is only one visual scene.
+    domCOLLADA* root = (domCOLLADA*)domScene->getDocument()->getDomRoot();
+    domLibrary_visual_scenes_Array& visualSceneLibrary = root->getLibrary_visual_scenes_array();
+    size_t visualSceneLibraryCount = visualSceneLibrary.getCount();
+    for (size_t i = 0; i < visualSceneLibraryCount; ++i)
+    {
+        domLibrary_visual_scenesRef scenesRef = visualSceneLibrary.get(i);
+        domVisual_scene_Array visualScenes = scenesRef->getVisual_scene_array();
+        size_t visualSceneCount = visualScenes.getCount();
+        for (size_t j = 0; j < visualSceneCount; ++j)
+        {
+            domVisual_sceneRef visualScene = visualScenes.get(j);
+            return visualScene.cast();
+        }
+    }
+    return NULL;
+}
+
 }
 }

+ 18 - 0
gameplay-encoder/src/DAEUtil.h

@@ -77,6 +77,15 @@ const domName_arrayRef getSourceNameArray(const domSourceRef& source);
  */
  */
 const domInstance_controller::domSkeletonRef getSkeleton(const domInstance_controllerRef& instanceController);
 const domInstance_controller::domSkeletonRef getSkeleton(const domInstance_controllerRef& instanceController);
 
 
+/**
+ * Returns the root joint node of the given skin.
+ * 
+ * @param skin The COLLADA skin to get the root joint for.
+ * 
+ * @return The COLLADA node or NULL if not found.
+ */
+domNode* getRootJointNode(const domSkin* skin);
+
 /**
 /**
  * Returns true if the two given animation channels have equal key time input source.
  * Returns true if the two given animation channels have equal key time input source.
  * 
  * 
@@ -114,6 +123,15 @@ void moveChannelAndSouresToAnimation(domChannelRef& channel, domAnimationRef& an
  */
  */
 bool isEmptyAnimation(domAnimationRef& animation);
 bool isEmptyAnimation(domAnimationRef& animation);
 
 
+/**
+ * Gets the visual scene from the given COLLADA dom scene.
+ * 
+ * @param COLLADA dom scene.
+ * 
+ * @return The visual scene or NULL if not found.
+ */
+domVisual_scene* getVisualScene(const domCOLLADA::domSceneRef& domScene);
+
 }
 }
 
 
 #endif
 #endif

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

@@ -62,7 +62,7 @@ void GPBDecoder::readRefs()
 {
 {
     fprintf(_outFile, "<RefTable>\n");
     fprintf(_outFile, "<RefTable>\n");
     // read number of refs
     // read number of refs
-    unsigned int refCount;
+    unsigned int refCount = 0;
     assert(read(&refCount));
     assert(read(&refCount));
     for (size_t i = 0; i < refCount; ++i)
     for (size_t i = 0; i < refCount; ++i)
     {
     {
@@ -74,7 +74,7 @@ void GPBDecoder::readRefs()
 void GPBDecoder::readRef()
 void GPBDecoder::readRef()
 {
 {
     std::string xref = readString(_file);
     std::string xref = readString(_file);
-    unsigned int type, offset;
+    unsigned int type = 0, offset = 0;
     assert(read(&type));
     assert(read(&type));
     assert(read(&offset));
     assert(read(&offset));
     
     

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

@@ -9,6 +9,7 @@ Scene::Scene(void) : _cameraNode(NULL)
     _ambientColor[0] = 0.0f;
     _ambientColor[0] = 0.0f;
     _ambientColor[1] = 0.0f;
     _ambientColor[1] = 0.0f;
     _ambientColor[2] = 0.0f;
     _ambientColor[2] = 0.0f;
+    setId("scene");
 }
 }
 
 
 Scene::~Scene(void)
 Scene::~Scene(void)

+ 3 - 9
gameplay-encoder/src/TTFFontEncoder.cpp

@@ -5,7 +5,7 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned char* srcBitmap, int srcWidth, int srcHeight)
+static void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned char* srcBitmap, int srcWidth, int srcHeight)
 {
 {
     // offset dst bitmap by x,y.
     // offset dst bitmap by x,y.
     dstBitmap +=  (x + (y * dstWidth));
     dstBitmap +=  (x + (y * dstWidth));
@@ -18,17 +18,12 @@ void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned c
     }
     }
 }
 }
 
 
-void writeUint(FILE* fp, unsigned int i)
+static void writeUint(FILE* fp, unsigned int i)
 {
 {
     fwrite(&i, sizeof(unsigned int), 1, fp);
     fwrite(&i, sizeof(unsigned int), 1, fp);
 }
 }
 
 
-void writeFloat(FILE* fp, float f)
-{
-    fwrite(&f, sizeof(float), 1, fp);
-}
-
-void writeString(FILE* fp, const char* str)
+static void writeString(FILE* fp, const char* str)
 {
 {
     unsigned int len = strlen(str);
     unsigned int len = strlen(str);
     fwrite(&len, sizeof(unsigned int), 1, fp);
     fwrite(&len, sizeof(unsigned int), 1, fp);
@@ -40,7 +35,6 @@ void writeString(FILE* fp, const char* str)
 
 
 int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview = false)
 int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview = false)
 {
 {
- 
     Glyph glyphArray[END_INDEX - START_INDEX];
     Glyph glyphArray[END_INDEX - START_INDEX];
     
     
     // Initialize freetype library.
     // Initialize freetype library.

+ 0 - 8
gameplay-encoder/src/TTFFontEncoder.h

@@ -18,14 +18,6 @@ public:
     float uvCoords[4];
     float uvCoords[4];
 };
 };
 
 
-void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned char* srcBitmap, int srcWidth, int srcHeight);
-
-void writeUint(FILE* fp, unsigned int i);
-
-void writeFloat(FILE* fp, float f);
-
-void writeString(FILE* fp, const char* str);
-
 int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview);
 int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview);
 
 
 }
 }

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

@@ -7,7 +7,7 @@
 
 
 using namespace gameplay;
 using namespace gameplay;
 
 
-std::string getFileName(const std::string& filepath)
+static std::string getFileName(const std::string& filepath)
 {
 {
     size_t index1 = filepath.find_last_of('\\');
     size_t index1 = filepath.find_last_of('\\');
     size_t index2 = filepath.find_last_of('/');
     size_t index2 = filepath.find_last_of('/');

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -90,6 +90,7 @@
     <ClCompile Include="src\RenderTarget.cpp" />
     <ClCompile Include="src\RenderTarget.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\SceneLoader.cpp" />
     <ClCompile Include="src\SceneLoader.cpp" />
+    <ClCompile Include="src\ScrollLayout.cpp" />
     <ClCompile Include="src\Slider.cpp" />
     <ClCompile Include="src\Slider.cpp" />
     <ClCompile Include="src\SpriteBatch.cpp" />
     <ClCompile Include="src\SpriteBatch.cpp" />
     <ClCompile Include="src\Technique.cpp" />
     <ClCompile Include="src\Technique.cpp" />
@@ -181,6 +182,7 @@
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\ScreenDisplayer.h" />
     <ClInclude Include="src\ScreenDisplayer.h" />
+    <ClInclude Include="src\ScrollLayout.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\SpriteBatch.h" />
     <ClInclude Include="src\SpriteBatch.h" />
     <ClInclude Include="src\Technique.h" />
     <ClInclude Include="src\Technique.h" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -279,6 +279,9 @@
     <ClCompile Include="src\FlowLayout.cpp">
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="src\ScrollLayout.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
     <ClInclude Include="src\Animation.h">
@@ -554,6 +557,9 @@
     <ClInclude Include="src\FlowLayout.h">
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="src\ScrollLayout.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
     <None Include="res\shaders\bumped-specular.vsh">

+ 12 - 0
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -28,6 +28,10 @@
 		42554EA2152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */; };
 		42554EA2152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */; };
 		42554EA3152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
 		42554EA3152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
 		42554EA4152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
 		42554EA4152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
+		4265D9241559A3C300581EB0 /* ScrollLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4265D9221559A3C300581EB0 /* ScrollLayout.cpp */; };
+		4265D9251559A3C300581EB0 /* ScrollLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4265D9221559A3C300581EB0 /* ScrollLayout.cpp */; };
+		4265D9261559A3C300581EB0 /* ScrollLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4265D9231559A3C300581EB0 /* ScrollLayout.h */; };
+		4265D9271559A3C300581EB0 /* ScrollLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4265D9231559A3C300581EB0 /* ScrollLayout.h */; };
 		426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AE153F4BB300844500 /* FlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 426878AB153F4BB300844500 /* FlowLayout.h */; };
 		426878AE153F4BB300844500 /* FlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 426878AB153F4BB300844500 /* FlowLayout.h */; };
@@ -404,6 +408,8 @@
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
 		42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionShape.cpp; path = src/PhysicsCollisionShape.cpp; sourceTree = SOURCE_ROOT; };
 		42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionShape.cpp; path = src/PhysicsCollisionShape.cpp; sourceTree = SOURCE_ROOT; };
 		42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionShape.h; path = src/PhysicsCollisionShape.h; sourceTree = SOURCE_ROOT; };
 		42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionShape.h; path = src/PhysicsCollisionShape.h; sourceTree = SOURCE_ROOT; };
+		4265D9221559A3C300581EB0 /* ScrollLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScrollLayout.cpp; path = src/ScrollLayout.cpp; sourceTree = SOURCE_ROOT; };
+		4265D9231559A3C300581EB0 /* ScrollLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScrollLayout.h; path = src/ScrollLayout.h; sourceTree = SOURCE_ROOT; };
 		426878AA153F4BB300844500 /* FlowLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FlowLayout.cpp; path = src/FlowLayout.cpp; sourceTree = SOURCE_ROOT; };
 		426878AA153F4BB300844500 /* FlowLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FlowLayout.cpp; path = src/FlowLayout.cpp; sourceTree = SOURCE_ROOT; };
 		426878AB153F4BB300844500 /* FlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlowLayout.h; path = src/FlowLayout.h; sourceTree = SOURCE_ROOT; };
 		426878AB153F4BB300844500 /* FlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlowLayout.h; path = src/FlowLayout.h; sourceTree = SOURCE_ROOT; };
 		4271C08D15337C8200B89DA7 /* Layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Layout.cpp; path = src/Layout.cpp; sourceTree = SOURCE_ROOT; };
 		4271C08D15337C8200B89DA7 /* Layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Layout.cpp; path = src/Layout.cpp; sourceTree = SOURCE_ROOT; };
@@ -856,6 +862,8 @@
 				428390971489D6E800E2B2F5 /* SceneLoader.cpp */,
 				428390971489D6E800E2B2F5 /* SceneLoader.cpp */,
 				428390981489D6E800E2B2F5 /* SceneLoader.h */,
 				428390981489D6E800E2B2F5 /* SceneLoader.h */,
 				4251B12E152D049B002F6199 /* ScreenDisplayer.h */,
 				4251B12E152D049B002F6199 /* ScreenDisplayer.h */,
+				4265D9221559A3C300581EB0 /* ScrollLayout.cpp */,
+				4265D9231559A3C300581EB0 /* ScrollLayout.h */,
 				5BD52646150F822A004C9099 /* Slider.cpp */,
 				5BD52646150F822A004C9099 /* Slider.cpp */,
 				5BD52647150F822A004C9099 /* Slider.h */,
 				5BD52647150F822A004C9099 /* Slider.h */,
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
@@ -1064,6 +1072,7 @@
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
+				4265D9261559A3C300581EB0 /* ScrollLayout.h in Headers */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1161,6 +1170,7 @@
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
+				4265D9271559A3C300581EB0 /* ScrollLayout.h in Headers */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1317,6 +1327,7 @@
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4265D9241559A3C300581EB0 /* ScrollLayout.cpp in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -1409,6 +1420,7 @@
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4265D9251559A3C300581EB0 /* ScrollLayout.cpp in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 41 - 39
gameplay/src/AbsoluteLayout.cpp

@@ -5,54 +5,56 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    static AbsoluteLayout* __instance;
 
 
-    AbsoluteLayout::AbsoluteLayout()
-    {
-    }
+static AbsoluteLayout* __instance;
 
 
-    AbsoluteLayout::AbsoluteLayout(const AbsoluteLayout& copy)
-    {
-    }
+AbsoluteLayout::AbsoluteLayout()
+{
+}
 
 
-    AbsoluteLayout::~AbsoluteLayout()
-    {
-    }
+AbsoluteLayout::AbsoluteLayout(const AbsoluteLayout& copy)
+{
+}
+
+AbsoluteLayout::~AbsoluteLayout()
+{
+    __instance = NULL;
+}
 
 
-    AbsoluteLayout* AbsoluteLayout::create()
+AbsoluteLayout* AbsoluteLayout::create()
+{
+    if (!__instance)
     {
     {
-        if (!__instance)
-        {
-            __instance = new AbsoluteLayout();
-        }
-        else
-        {
-            __instance->addRef();
-        }
-
-        return __instance;
+        __instance = new AbsoluteLayout();
     }
     }
-
-    Layout::Type AbsoluteLayout::getType()
+    else
     {
     {
-        return Layout::LAYOUT_ABSOLUTE;
+        __instance->addRef();
     }
     }
 
 
-    void AbsoluteLayout::update(const Container* container)
+    return __instance;
+}
+
+Layout::Type AbsoluteLayout::getType()
+{
+    return Layout::LAYOUT_ABSOLUTE;
+}
+
+void AbsoluteLayout::update(const Container* container)
+{
+    GP_ASSERT(container);
+
+    // An AbsoluteLayout does nothing to modify the layout of Controls.
+    std::vector<Control*> controls = container->getControls();
+    unsigned int controlsCount = controls.size();
+    for (unsigned int i = 0; i < controlsCount; i++)
     {
     {
-        // An AbsoluteLayout does nothing to modify the layout of Controls.
-        std::vector<Control*> controls = container->getControls();
-        unsigned int controlsCount = controls.size();
-        for (unsigned int i = 0; i < controlsCount; i++)
-        {
-            Control* control = controls[i];
-
-            align(control, container);
-
-            if (control->isDirty() || control->isContainer())
-            {
-                control->update(container->getClip());
-            }
-        }
+        Control* control = controls[i];
+        GP_ASSERT(control);
+
+        align(control, container);
+        control->update(container->getClip(), Vector2::zero());
     }
     }
+}
+
 }
 }

+ 47 - 15
gameplay/src/Animation.cpp

@@ -21,7 +21,7 @@ Animation::Animation(const char* id, AnimationTarget* target, int propertyId, un
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, type);
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     release();
     release();
-    assert(getRefCount() == 1);
+    GP_ASSERT(getRefCount() == 1);
 }
 }
 
 
 Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
 Animation::Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
@@ -30,7 +30,7 @@ Animation::Animation(const char* id, AnimationTarget* target, int propertyId, un
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     release();
     release();
-    assert(getRefCount() == 1);
+    GP_ASSERT(getRefCount() == 1);
 }
 }
 
 
 Animation::Animation(const char* id)
 Animation::Animation(const char* id)
@@ -45,7 +45,10 @@ Animation::~Animation()
     if (_defaultClip)
     if (_defaultClip)
     {
     {
         if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
         if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+        {
+            GP_ASSERT(_controller);
             _controller->unschedule(_defaultClip);
             _controller->unschedule(_defaultClip);
+        }
         SAFE_RELEASE(_defaultClip);
         SAFE_RELEASE(_defaultClip);
     }
     }
 
 
@@ -56,8 +59,12 @@ Animation::~Animation()
         while (clipIter != _clips->end())
         while (clipIter != _clips->end())
         {   
         {   
             AnimationClip* clip = *clipIter;
             AnimationClip* clip = *clipIter;
+            GP_ASSERT(clip);
             if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
             if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
+            {
+                GP_ASSERT(_controller);
                 _controller->unschedule(clip);
                 _controller->unschedule(clip);
+            }
             SAFE_RELEASE(clip);
             SAFE_RELEASE(clip);
             clipIter++;
             clipIter++;
         }
         }
@@ -69,8 +76,12 @@ Animation::~Animation()
 Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
 Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
     : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
     : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
 {
 {
+    GP_ASSERT(_animation);
+    GP_ASSERT(_target);
+    GP_ASSERT(_curve);
+
     // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
     // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
-    assert(_target->getAnimationPropertyComponentCount(propertyId));
+    GP_ASSERT(_target->getAnimationPropertyComponentCount(propertyId));
     _curve->addRef();
     _curve->addRef();
     _target->addChannel(this);
     _target->addChannel(this);
     _animation->addRef();
     _animation->addRef();
@@ -79,6 +90,10 @@ Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int p
 Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
 Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
     : _animation(animation), _target(target), _propertyId(copy._propertyId), _curve(copy._curve), _duration(copy._duration)
     : _animation(animation), _target(target), _propertyId(copy._propertyId), _curve(copy._curve), _duration(copy._duration)
 {
 {
+    GP_ASSERT(_curve);
+    GP_ASSERT(_target);
+    GP_ASSERT(_animation);
+
     _curve->addRef();
     _curve->addRef();
     _target->addChannel(this);
     _target->addChannel(this);
     _animation->addRef();
     _animation->addRef();
@@ -107,16 +122,15 @@ unsigned long Animation::getDuration() const
 
 
 void Animation::createClips(const char* url)
 void Animation::createClips(const char* url)
 {
 {
-    assert(url);
-
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    assert(properties);
+    GP_ASSERT(properties);
 
 
     Properties* pAnimation = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
     Properties* pAnimation = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-    assert(pAnimation);
+    GP_ASSERT(pAnimation);
     
     
     int frameCount = pAnimation->getInt("frameCount");
     int frameCount = pAnimation->getInt("frameCount");
-    assert(frameCount > 0);
+    if (frameCount <= 0)
+        GP_ERROR("The animation's frame count must be greater than 0.");
 
 
     createClips(pAnimation, (unsigned int)frameCount);
     createClips(pAnimation, (unsigned int)frameCount);
 
 
@@ -171,7 +185,7 @@ void Animation::play(const char* clipId)
     }
     }
     else
     else
     {
     {
-        // Find animation clip.. and play.
+        // Find animation clip and play.
         AnimationClip* clip = findClip(clipId);
         AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
         if (clip != NULL)
             clip->play();
             clip->play();
@@ -188,7 +202,7 @@ void Animation::stop(const char* clipId)
     }
     }
     else
     else
     {
     {
-        // Find animation clip.. and play.
+        // Find animation clip and play.
         AnimationClip* clip = findClip(clipId);
         AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
         if (clip != NULL)
             clip->stop();
             clip->stop();
@@ -214,6 +228,7 @@ bool Animation::targets(AnimationTarget* target) const
 {
 {
     for (std::vector<Animation::Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr)
     for (std::vector<Animation::Channel*>::const_iterator itr = _channels.begin(); itr != _channels.end(); ++itr)
     {
     {
+        GP_ASSERT(*itr);
         if ((*itr)->_target == target)
         if ((*itr)->_target == target)
         {
         {
             return true;
             return true;
@@ -229,7 +244,7 @@ void Animation::createDefaultClip()
 
 
 void Animation::createClips(Properties* animationProperties, unsigned int frameCount)
 void Animation::createClips(Properties* animationProperties, unsigned int frameCount)
 {
 {
-    assert(animationProperties);
+    GP_ASSERT(animationProperties);
     
     
     Properties* pClip = animationProperties->getNextNamespace();
     Properties* pClip = animationProperties->getNextNamespace();
     
     
@@ -272,6 +287,7 @@ void Animation::addClip(AnimationClip* clip)
     if (_clips == NULL)
     if (_clips == NULL)
         _clips = new std::vector<AnimationClip*>;
         _clips = new std::vector<AnimationClip*>;
 
 
+    GP_ASSERT(clip);
     _clips->push_back(clip);
     _clips->push_back(clip);
 }
 }
 
 
@@ -284,9 +300,10 @@ AnimationClip* Animation::findClip(const char* id) const
         for (unsigned int i = 0; i < clipCount; i++)
         for (unsigned int i = 0; i < clipCount; i++)
         {
         {
             clip = _clips->at(i);
             clip = _clips->at(i);
+            GP_ASSERT(clip);
             if (clip->_id.compare(id) == 0)
             if (clip->_id.compare(id) == 0)
             {
             {
-                return _clips->at(i);
+                return clip;
             }
             }
         }
         }
     }
     }
@@ -295,10 +312,15 @@ AnimationClip* Animation::findClip(const char* id) const
 
 
 Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, unsigned int type)
 Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, unsigned int type)
 {
 {
+    GP_ASSERT(target);
+    GP_ASSERT(keyTimes);
+    GP_ASSERT(keyValues);
+
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    assert(propertyComponentCount > 0);
+    GP_ASSERT(propertyComponentCount > 0);
 
 
     Curve* curve = Curve::create(keyCount, propertyComponentCount);
     Curve* curve = Curve::create(keyCount, propertyComponentCount);
+    GP_ASSERT(curve);
     if (target->_targetType == AnimationTarget::TRANSFORM)
     if (target->_targetType == AnimationTarget::TRANSFORM)
         setTransformRotationOffset(curve, propertyId);
         setTransformRotationOffset(curve, propertyId);
 
 
@@ -332,10 +354,15 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
 
 Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
 Animation::Channel* Animation::createChannel(AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, unsigned int type)
 {
 {
+    GP_ASSERT(target);
+    GP_ASSERT(keyTimes);
+    GP_ASSERT(keyValues);
+
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    assert(propertyComponentCount > 0);
+    GP_ASSERT(propertyComponentCount > 0);
 
 
     Curve* curve = Curve::create(keyCount, propertyComponentCount);
     Curve* curve = Curve::create(keyCount, propertyComponentCount);
+    GP_ASSERT(curve);
     if (target->_targetType == AnimationTarget::TRANSFORM)
     if (target->_targetType == AnimationTarget::TRANSFORM)
         setTransformRotationOffset(curve, propertyId);
         setTransformRotationOffset(curve, propertyId);
     
     
@@ -369,6 +396,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
 
 void Animation::addChannel(Channel* channel)
 void Animation::addChannel(Channel* channel)
 {
 {
+    GP_ASSERT(channel);
     _channels.push_back(channel);
     _channels.push_back(channel);
     
     
     if (channel->_duration > _duration)
     if (channel->_duration > _duration)
@@ -395,6 +423,8 @@ void Animation::removeChannel(Channel* channel)
 
 
 void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
 void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
 {
 {
+    GP_ASSERT(curve);
+
     switch (propertyId)
     switch (propertyId)
     {
     {
     case Transform::ANIMATE_ROTATE:
     case Transform::ANIMATE_ROTATE:
@@ -411,13 +441,15 @@ void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId
 
 
 Animation* Animation::clone(Channel* channel, AnimationTarget* target)
 Animation* Animation::clone(Channel* channel, AnimationTarget* target)
 {
 {
+    GP_ASSERT(channel);
+
     Animation* animation = new Animation(getId());
     Animation* animation = new Animation(getId());
 
 
     Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
     Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
     animation->addChannel(channelCopy);
     animation->addChannel(channelCopy);
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     animation->release();
     animation->release();
-    assert(animation->getRefCount() == 1);
+    GP_ASSERT(animation->getRefCount() == 1);
     return animation;
     return animation;
 }
 }
 
 

+ 39 - 7
gameplay/src/AnimationClip.cpp

@@ -14,11 +14,14 @@ AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long
       _elapsedTime(0), _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
       _elapsedTime(0), _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
       _beginListeners(NULL), _endListeners(NULL), _listeners(NULL), _listenerItr(NULL)
       _beginListeners(NULL), _endListeners(NULL), _listeners(NULL), _listenerItr(NULL)
 {
 {
-    assert(0 <= startTime && startTime <= animation->_duration && 0 <= endTime && endTime <= animation->_duration);
+    GP_ASSERT(_animation);
+    GP_ASSERT(0 <= startTime && startTime <= _animation->_duration && 0 <= endTime && endTime <= _animation->_duration);
     
     
-    unsigned int channelCount = _animation->_channels.size();    
+    unsigned int channelCount = _animation->_channels.size();
     for (unsigned int i = 0; i < channelCount; i++)
     for (unsigned int i = 0; i < channelCount; i++)
     {
     {
+        GP_ASSERT(_animation->_channels[i]);
+        GP_ASSERT(_animation->_channels[i]->getCurve());
         _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
         _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
     }
     }
 }
 }
@@ -88,7 +91,7 @@ unsigned long AnimationClip::getElaspedTime() const
 
 
 void AnimationClip::setRepeatCount(float repeatCount)
 void AnimationClip::setRepeatCount(float repeatCount)
 {
 {
-    assert(repeatCount == REPEAT_INDEFINITE || repeatCount > 0.0f);
+    GP_ASSERT(repeatCount == REPEAT_INDEFINITE || repeatCount > 0.0f);
 
 
     _repeatCount = repeatCount;
     _repeatCount = repeatCount;
 
 
@@ -180,6 +183,8 @@ void AnimationClip::play()
     else
     else
     {
     {
         setClipStateBit(CLIP_IS_PLAYING_BIT);
         setClipStateBit(CLIP_IS_PLAYING_BIT);
+        GP_ASSERT(_animation);
+        GP_ASSERT(_animation->_controller);
         _animation->_controller->schedule(this);
         _animation->_controller->schedule(this);
     }
     }
     
     
@@ -209,7 +214,7 @@ void AnimationClip::pause()
 
 
 void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 {
 {
-    assert(clip);
+    GP_ASSERT(clip);
 
 
     // Check if the given clip is fading into this clip.
     // Check if the given clip is fading into this clip.
     // We should reset the clip from fading out, and this one from fading in 
     // We should reset the clip from fading out, and this one from fading in 
@@ -250,8 +255,8 @@ void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 
 
 void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
 void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
 {
 {
-    assert(listener);
-    assert(eventTime < _activeDuration);
+    GP_ASSERT(listener);
+    GP_ASSERT(eventTime < _activeDuration);
 
 
     ListenerEvent* listenerEvent = new ListenerEvent(listener, eventTime);
     ListenerEvent* listenerEvent = new ListenerEvent(listener, eventTime);
 
 
@@ -268,6 +273,7 @@ void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long
     {
     {
         for (std::list<ListenerEvent*>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         for (std::list<ListenerEvent*>::iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         {
         {
+            GP_ASSERT(*itr);
             if (eventTime < (*itr)->_eventTime)
             if (eventTime < (*itr)->_eventTime)
             {
             {
                 itr = _listeners->insert(itr, listenerEvent);
                 itr = _listeners->insert(itr, listenerEvent);
@@ -277,6 +283,7 @@ void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long
                 if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
                 if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
                 {
                 {
                     unsigned long currentTime = _elapsedTime % _duration;
                     unsigned long currentTime = _elapsedTime % _duration;
+                    GP_ASSERT(**_listenerItr);
                     if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
                     if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
                         (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
                         (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
                         *_listenerItr = itr;
                         *_listenerItr = itr;
@@ -293,6 +300,7 @@ void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
     if (!_beginListeners)
     if (!_beginListeners)
         _beginListeners = new std::vector<Listener*>;
         _beginListeners = new std::vector<Listener*>;
 
 
+    GP_ASSERT(listener);
     _beginListeners->push_back(listener);
     _beginListeners->push_back(listener);
 }
 }
 
 
@@ -301,6 +309,7 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
     if (!_endListeners)
     if (!_endListeners)
         _endListeners = new std::vector<Listener*>;
         _endListeners = new std::vector<Listener*>;
 
 
+    GP_ASSERT(listener);
     _endListeners->push_back(listener);
     _endListeners->push_back(listener);
 }
 }
 
 
@@ -365,10 +374,17 @@ bool AnimationClip::update(unsigned long elapsedTime)
     // Notify any listeners of Animation events.
     // Notify any listeners of Animation events.
     if (_listeners)
     if (_listeners)
     {
     {
+        GP_ASSERT(_listenerItr);
+        GP_ASSERT(**_listenerItr);
+
         if (_speed >= 0.0f)
         if (_speed >= 0.0f)
         {
         {
             while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime)
             while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime)
             {
             {
+                GP_ASSERT(_listenerItr);
+                GP_ASSERT(**_listenerItr);
+                GP_ASSERT((**_listenerItr)->_listener);
+
                 (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 ++*_listenerItr;
                 ++*_listenerItr;
             }
             }
@@ -377,6 +393,10 @@ bool AnimationClip::update(unsigned long elapsedTime)
         {
         {
             while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime)
             while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime)
             {
             {
+                GP_ASSERT(_listenerItr);
+                GP_ASSERT(**_listenerItr);
+                GP_ASSERT((**_listenerItr)->_listener);
+
                 (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 (**_listenerItr)->_listener->animationEvent(this, Listener::DEFAULT);
                 --*_listenerItr;
                 --*_listenerItr;
             }
             }
@@ -384,12 +404,18 @@ bool AnimationClip::update(unsigned long elapsedTime)
     }
     }
 
 
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
+    GP_ASSERT(_animation);
+    GP_ASSERT(_animation->_duration > 0);
     float percentComplete = (float)(_startTime + currentTime) / (float) _animation->_duration;
     float percentComplete = (float)(_startTime + currentTime) / (float) _animation->_duration;
     
     
     if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT))
     if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT))
     {
     {
+        GP_ASSERT(_crossFadeToClip);
+        GP_ASSERT(_crossFadeOutDuration > 0);
+
         if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin.
         if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin.
         {
         {
+            GP_ASSERT(_crossFadeToClip);
             _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * abs(_speed); 
             _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * abs(_speed); 
             resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
             resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
         }
         }
@@ -436,10 +462,14 @@ bool AnimationClip::update(unsigned long elapsedTime)
     for (unsigned int i = 0; i < channelCount; i++)
     for (unsigned int i = 0; i < channelCount; i++)
     {
     {
         channel = _animation->_channels[i];
         channel = _animation->_channels[i];
+        GP_ASSERT(channel);
         target = channel->_target;
         target = channel->_target;
+        GP_ASSERT(target);
         value = _values[i];
         value = _values[i];
+        GP_ASSERT(value);
 
 
         // Evaluate the point on Curve
         // Evaluate the point on Curve
+        GP_ASSERT(channel->getCurve());
         channel->getCurve()->evaluate(percentComplete, value->_value);
         channel->getCurve()->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.
         // Set the animation value on the target property.
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
@@ -474,12 +504,13 @@ void AnimationClip::onBegin()
             *_listenerItr = _listeners->end();
             *_listenerItr = _listeners->end();
     }
     }
     
     
-    // Notify begin listeners.. if any.
+    // Notify begin listeners if any.
     if (_beginListeners)
     if (_beginListeners)
     {
     {
         std::vector<Listener*>::iterator listener = _beginListeners->begin();
         std::vector<Listener*>::iterator listener = _beginListeners->begin();
         while (listener != _beginListeners->end())
         while (listener != _beginListeners->end())
         {
         {
+            GP_ASSERT(*listener);
             (*listener)->animationEvent(this, Listener::BEGIN);
             (*listener)->animationEvent(this, Listener::BEGIN);
             listener++;
             listener++;
         }
         }
@@ -497,6 +528,7 @@ void AnimationClip::onEnd()
         std::vector<Listener*>::iterator listener = _endListeners->begin();
         std::vector<Listener*>::iterator listener = _endListeners->begin();
         while (listener != _endListeners->end())
         while (listener != _endListeners->end())
         {
         {
+            GP_ASSERT(*listener);
             (*listener)->animationEvent(this, Listener::END);
             (*listener)->animationEvent(this, Listener::END);
             listener++;
             listener++;
         }
         }

+ 7 - 0
gameplay/src/AnimationController.cpp

@@ -21,6 +21,7 @@ void AnimationController::stopAllAnimations()
     while (clipIter != _runningClips.end())
     while (clipIter != _runningClips.end())
     {
     {
         AnimationClip* clip = *clipIter;
         AnimationClip* clip = *clipIter;
+        GP_ASSERT(clip);
         clip->stop();
         clip->stop();
         clipIter++;
         clipIter++;
     }
     }
@@ -68,6 +69,7 @@ void AnimationController::schedule(AnimationClip* clip)
         _state = RUNNING;
         _state = RUNNING;
     }
     }
 
 
+    GP_ASSERT(clip);
     clip->addRef();
     clip->addRef();
     _runningClips.push_back(clip);
     _runningClips.push_back(clip);
 }
 }
@@ -96,11 +98,14 @@ void AnimationController::update(long elapsedTime)
     if (_state != RUNNING)
     if (_state != RUNNING)
         return;
         return;
 
 
+    Transform::suspendTransformChanged();
+
     // Loop through running clips and call update() on them.
     // Loop through running clips and call update() on them.
     std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
     std::list<AnimationClip*>::iterator clipIter = _runningClips.begin();
     while (clipIter != _runningClips.end())
     while (clipIter != _runningClips.end())
     {
     {
         AnimationClip* clip = (*clipIter);
         AnimationClip* clip = (*clipIter);
+        GP_ASSERT(clip);
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         if (clip->isClipStateBitSet(AnimationClip::CLIP_IS_RESTARTED_BIT))
         {   // If the CLIP_IS_RESTARTED_BIT is set, we should end the clip and 
         {   // 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.
             // move it from where it is in the running clips list to the back.
@@ -120,6 +125,8 @@ void AnimationController::update(long elapsedTime)
         }
         }
     }
     }
 
 
+    Transform::resumeTransformChanged();
+
     if (_runningClips.empty())
     if (_runningClips.empty())
         _state = IDLE;
         _state = IDLE;
 }
 }

+ 142 - 17
gameplay/src/AnimationTarget.cpp

@@ -20,6 +20,8 @@ AnimationTarget::~AnimationTarget()
         while (itr != _animationChannels->end())
         while (itr != _animationChannels->end())
         {
         {
             Animation::Channel* channel = (*itr);
             Animation::Channel* channel = (*itr);
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
             channel->_animation->removeChannel(channel);
             channel->_animation->removeChannel(channel);
             SAFE_DELETE(channel);
             SAFE_DELETE(channel);
             itr++;
             itr++;
@@ -31,8 +33,8 @@ AnimationTarget::~AnimationTarget()
 
 
 Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
 Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
 {
 {
-    assert(type != Curve::BEZIER && type != Curve::HERMITE);
-    assert(keyCount >= 1 && keyTimes && keyValues);
+    GP_ASSERT(type != Curve::BEZIER && type != Curve::HERMITE);
+    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues);
 
 
     Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, type);
     Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, type);
 
 
@@ -41,7 +43,7 @@ Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsi
 
 
 Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
 Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
 {
 {
-    assert(keyCount >= 1 && keyTimes && keyValues && keyInValue && keyOutValue);
+    GP_ASSERT(keyCount >= 1 && keyTimes && keyValues && keyInValue && keyOutValue);
     Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
     Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
 
 
     return animation;
     return animation;
@@ -49,10 +51,8 @@ Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsi
 
 
 Animation* AnimationTarget::createAnimation(const char* id, const char* url)
 Animation* AnimationTarget::createAnimation(const char* id, const char* url)
 {
 {
-    assert(url);
-    
     Properties* p = Properties::create(url);
     Properties* p = Properties::create(url);
-    assert(p);
+    GP_ASSERT(p);
 
 
     Animation* animation = createAnimation(id, (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace());
     Animation* animation = createAnimation(id, (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace());
 
 
@@ -63,7 +63,11 @@ Animation* AnimationTarget::createAnimation(const char* id, const char* url)
 
 
 Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
 Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
 {
 {
+    GP_ASSERT(from);
+    GP_ASSERT(to);
+
     const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
     const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
     float* keyValues = new float[2 * propertyComponentCount];
     float* keyValues = new float[2 * propertyComponentCount];
 
 
     memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
     memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
@@ -83,10 +87,16 @@ Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId
 
 
 Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
 Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
 {
 {
+    GP_ASSERT(from);
+    GP_ASSERT(by);
+
     const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
     const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    GP_ASSERT(propertyComponentCount > 0);
     float* keyValues = new float[2 * propertyComponentCount];
     float* keyValues = new float[2 * propertyComponentCount];
 
 
     memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
     memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
+
+    convertByValues(propertyId, propertyComponentCount, from, by);
     memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
     memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
 
 
     unsigned long* keyTimes = new unsigned long[2];
     unsigned long* keyTimes = new unsigned long[2];
@@ -103,27 +113,55 @@ Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId
 
 
 Animation* AnimationTarget::createAnimation(const char* id, Properties* animationProperties)
 Animation* AnimationTarget::createAnimation(const char* id, Properties* animationProperties)
 {
 {
-    assert(animationProperties);
-    assert(std::strcmp(animationProperties->getNamespace(), "animation") == 0);
+    GP_ASSERT(animationProperties);
+    if (std::strcmp(animationProperties->getNamespace(), "animation") != 0)
+    {
+        GP_ERROR("Invalid animation namespace '%s'.", animationProperties->getNamespace());
+        return NULL;
+    }
     
     
     const char* propertyIdStr = animationProperties->getString("property");
     const char* propertyIdStr = animationProperties->getString("property");
-    assert(propertyIdStr);
+    if (propertyIdStr == NULL)
+    {
+        GP_ERROR("Attribute \'property\' must be specified for an animation.");
+        return NULL;
+    }
     
     
     // Get animation target property id
     // Get animation target property id
     int propertyId = AnimationTarget::getPropertyId(_targetType, propertyIdStr);
     int propertyId = AnimationTarget::getPropertyId(_targetType, propertyIdStr);
-    assert(propertyId != -1);
+    if (propertyId == -1)
+    {
+        GP_ERROR("Property ID is invalid.");
+        return NULL;
+    }
     
     
     unsigned int keyCount = animationProperties->getInt("keyCount");
     unsigned int keyCount = animationProperties->getInt("keyCount");
-    assert(keyCount > 0);
+    if (keyCount == 0)
+    {
+        GP_ERROR("Attribute \'keyCount\' must be specified for an animation.");
+        return NULL;
+    }
 
 
     const char* keyTimesStr = animationProperties->getString("keyTimes");
     const char* keyTimesStr = animationProperties->getString("keyTimes");
-    assert(keyTimesStr);
+    if (keyTimesStr == NULL)
+    {
+        GP_ERROR("Attribute \'keyTimes\' must be specified for an animation.");
+        return NULL;
+    }
     
     
     const char* keyValuesStr = animationProperties->getString("keyValues");
     const char* keyValuesStr = animationProperties->getString("keyValues");
-    assert(keyValuesStr);
+    if (keyValuesStr == NULL)
+    {
+        GP_ERROR("Attribute \'keyValues\' must be specified for an animation.");
+        return NULL;
+    }
     
     
     const char* curveStr = animationProperties->getString("curve");
     const char* curveStr = animationProperties->getString("curve");
-    assert(curveStr);
+    if (curveStr == NULL)
+    {
+        GP_ERROR("Attribute \'curve\' must be specified for an animation.");
+        return NULL;
+    }
     
     
     char delimeter = ' ';
     char delimeter = ' ';
     unsigned int startOffset = 0;
     unsigned int startOffset = 0;
@@ -148,7 +186,7 @@ Animation* AnimationTarget::createAnimation(const char* id, Properties* animatio
     endOffset = (unsigned int)std::string::npos;
     endOffset = (unsigned int)std::string::npos;
     
     
     int componentCount = getAnimationPropertyComponentCount(propertyId);
     int componentCount = getAnimationPropertyComponentCount(propertyId);
-    assert(componentCount > 0);
+    GP_ASSERT(componentCount > 0);
     
     
     unsigned int components = keyCount * componentCount;
     unsigned int components = keyCount * componentCount;
     
     
@@ -232,7 +270,11 @@ Animation* AnimationTarget::createAnimation(const char* id, Properties* animatio
     if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
     if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
     {
     {
         int frameCount = animationProperties->getInt("frameCount");
         int frameCount = animationProperties->getInt("frameCount");
-        assert(frameCount > 0);
+        if (frameCount <= 0)
+        {
+            GP_ERROR("Frame count must be greater than zero for a clip.");
+            return animation;
+        }
         animation->createClips(animationProperties, (unsigned int) frameCount);
         animation->createClips(animationProperties, (unsigned int) frameCount);
     }
     }
 
 
@@ -247,6 +289,7 @@ void AnimationTarget::destroyAnimation(const char* id)
         return;
         return;
 
 
     // Remove this target's channel from animation, and from the target's list of channels.
     // Remove this target's channel from animation, and from the target's list of channels.
+    GP_ASSERT(channel->_animation);
     channel->_animation->removeChannel(channel);
     channel->_animation->removeChannel(channel);
     removeChannel(channel);
     removeChannel(channel);
 
 
@@ -258,6 +301,7 @@ Animation* AnimationTarget::getAnimation(const char* id) const
     if (_animationChannels)
     if (_animationChannels)
     {
     {
         std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
         std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        GP_ASSERT(*itr);
 
 
         if (id == NULL)
         if (id == NULL)
             return (*itr)->_animation;
             return (*itr)->_animation;
@@ -266,6 +310,8 @@ Animation* AnimationTarget::getAnimation(const char* id) const
         for (; itr != _animationChannels->end(); itr++)
         for (; itr != _animationChannels->end(); itr++)
         {
         {
             channel = (Animation::Channel*)(*itr);
             channel = (Animation::Channel*)(*itr);
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
             if (channel->_animation->_id.compare(id) == 0)
             if (channel->_animation->_id.compare(id) == 0)
             {
             {
                 return channel->_animation;
                 return channel->_animation;
@@ -278,6 +324,8 @@ Animation* AnimationTarget::getAnimation(const char* id) const
 
 
 int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
 int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
 {
 {
+    GP_ASSERT(propertyIdStr);
+    
     if (type == AnimationTarget::TRANSFORM)
     if (type == AnimationTarget::TRANSFORM)
     {
     {
         if (strcmp(propertyIdStr, "ANIMATE_SCALE") == 0)
         if (strcmp(propertyIdStr, "ANIMATE_SCALE") == 0)
@@ -341,6 +389,7 @@ void AnimationTarget::addChannel(Animation::Channel* channel)
     if (_animationChannels == NULL)
     if (_animationChannels == NULL)
         _animationChannels = new std::vector<Animation::Channel*>;
         _animationChannels = new std::vector<Animation::Channel*>;
 
 
+    GP_ASSERT(channel);
     _animationChannels->push_back(channel);
     _animationChannels->push_back(channel);
 }
 }
 
 
@@ -370,6 +419,7 @@ Animation::Channel* AnimationTarget::getChannel(const char* id) const
     if (_animationChannels)
     if (_animationChannels)
     {
     {
         std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
         std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        GP_ASSERT(*itr);
 
 
         if (id == NULL)
         if (id == NULL)
             return (*itr);
             return (*itr);
@@ -378,6 +428,7 @@ Animation::Channel* AnimationTarget::getChannel(const char* id) const
         for (; itr != _animationChannels->end(); itr++)
         for (; itr != _animationChannels->end(); itr++)
         {
         {
             channel = (Animation::Channel*)(*itr);
             channel = (Animation::Channel*)(*itr);
+            GP_ASSERT(channel);
             if (channel->_animation->_id.compare(id) == 0)
             if (channel->_animation->_id.compare(id) == 0)
             {
             {
                 return channel;
                 return channel;
@@ -395,7 +446,8 @@ void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &conte
         for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
         for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
         {
         {
             Animation::Channel* channel = *it;
             Animation::Channel* channel = *it;
-            assert(channel->_animation);
+            GP_ASSERT(channel);
+            GP_ASSERT(channel->_animation);
 
 
             Animation* animation = context.findClonedAnimation(channel->_animation);
             Animation* animation = context.findClonedAnimation(channel->_animation);
             if (animation != NULL)
             if (animation != NULL)
@@ -413,6 +465,79 @@ void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &conte
     }
     }
 }
 }
 
 
+void AnimationTarget::convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by)
+{
+    if (_targetType == AnimationTarget::TRANSFORM)
+    {    
+        switch(propertyId)
+        {
+            case Transform::ANIMATE_SCALE:
+            case Transform::ANIMATE_SCALE_UNIT:
+            case Transform::ANIMATE_SCALE_X:
+            case Transform::ANIMATE_SCALE_Y:
+            case Transform::ANIMATE_SCALE_Z:
+            {
+                convertScaleByValues(from, by, componentCount);
+                break;
+            }
+            case Transform::ANIMATE_TRANSLATE:
+            case Transform::ANIMATE_TRANSLATE_X:
+            case Transform::ANIMATE_TRANSLATE_Y:
+            case Transform::ANIMATE_TRANSLATE_Z:
+            {
+                convertByValues(from, by, componentCount);
+                break;
+            }
+            case Transform::ANIMATE_ROTATE:
+            {
+                convertQuaternionByValues(from, by);
+                break;
+            }
+            case Transform::ANIMATE_ROTATE_TRANSLATE:
+            {
+                convertQuaternionByValues(from, by);
+                convertByValues(from + 4, by + 4, 3);
+                break;
+            }   
+            case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+            {
+                convertScaleByValues(from, by, 3);
+                convertQuaternionByValues(from + 3, by + 3);
+                convertByValues(from + 7, by + 7, 3);
+                break;
+            }
+        }
+    }
+    else
+    {
+        convertByValues(from, by, componentCount);
+    }
+}
+
+void AnimationTarget::convertQuaternionByValues(float* from, float* by)
+{
+    Quaternion qFrom(from);
+    Quaternion qBy(by);
+    qBy.multiply(qFrom);
+    memcpy(by, (float*)&qBy, sizeof(float) * 4);
+}
+
+void AnimationTarget::convertScaleByValues(float* from, float* by, unsigned int componentCount)
+{
+    for (unsigned int i = 0; i < componentCount; i++)
+    {
+        by[i] *= from[i];
+    }
+}
+
+void AnimationTarget::convertByValues(float* from, float* by, unsigned int componentCount)
+{
+    for (unsigned int i = 0; i < componentCount; i++)
+    {
+        by[i] += from[i];
+    }
+}
+
 }
 }
 
 
 
 

+ 20 - 1
gameplay/src/AnimationTarget.h

@@ -19,7 +19,6 @@ class AnimationTarget
 {
 {
     friend class Animation;
     friend class Animation;
     friend class AnimationClip;
     friend class AnimationClip;
-    friend class AnimationController;
 
 
 public:
 public:
 
 
@@ -221,6 +220,26 @@ private:
      */
      */
     static int getPropertyId(TargetType type, const char* propertyIdStr);
     static int getPropertyId(TargetType type, const char* propertyIdStr);
 
 
+    /**
+     * Converts by-value animations to to-value animations.
+     */
+    void convertByValues(unsigned int propertyId, unsigned int componentCount, float* from, float* by);
+
+    /**
+     * Converts a Quaternion by-value into a to-value.
+     */
+    void convertQuaternionByValues(float* from, float* by);
+
+    /**
+     * Converts a Scale by-value into a to-value.
+     */
+    void convertScaleByValues(float* from, float* by, unsigned int componentCount);
+
+    /**
+     * Converts a by-value into a to-value.
+     */
+    void convertByValues(float* from, float* by, unsigned int componentCount);
+
     std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
     std::vector<Animation::Channel*>* _animationChannels;   // Collection of all animation channels that target the AnimationTarget
     
     
 };
 };

+ 7 - 4
gameplay/src/AnimationValue.cpp

@@ -7,6 +7,7 @@ namespace gameplay
 AnimationValue::AnimationValue(unsigned int componentCount)
 AnimationValue::AnimationValue(unsigned int componentCount)
   : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
   : _componentCount(componentCount), _componentSize(componentCount * sizeof(float))
 {
 {
+    GP_ASSERT(_componentCount > 0);
     _value = new float[_componentCount];
     _value = new float[_componentCount];
 }
 }
 
 
@@ -17,28 +18,30 @@ AnimationValue::~AnimationValue()
 
 
 float AnimationValue::getFloat(unsigned int index) const
 float AnimationValue::getFloat(unsigned int index) const
 {
 {
-    assert(index < _componentCount);
+    GP_ASSERT(index < _componentCount);
+    GP_ASSERT(_value);
 
 
     return _value[index];
     return _value[index];
 }
 }
 
 
 void AnimationValue::setFloat(unsigned int index, float value)
 void AnimationValue::setFloat(unsigned int index, float value)
 {
 {
-    assert(index < _componentCount);
+    GP_ASSERT(index < _componentCount);
+    GP_ASSERT(_value);
 
 
     _value[index] = value;
     _value[index] = value;
 }
 }
 
 
 void AnimationValue::getFloat(float* value, unsigned int offset, unsigned int length) const
 void AnimationValue::getFloat(float* value, unsigned int offset, unsigned int length) const
 {
 {
-    assert(value && offset < _componentCount && (offset + length) <= _componentCount);
+    GP_ASSERT(_value && value && offset < _componentCount && (offset + length) <= _componentCount);
 
 
     memcpy(value + offset, _value, length * sizeof(float));
     memcpy(value + offset, _value, length * sizeof(float));
 }
 }
 
 
 void AnimationValue::setFloat(float* value, unsigned int offset, unsigned int length)
 void AnimationValue::setFloat(float* value, unsigned int offset, unsigned int length)
 {
 {
-    assert(value && offset < _componentCount && (offset + length) <= _componentCount);
+    GP_ASSERT(_value && value && offset < _componentCount && (offset + length) <= _componentCount);
 
 
     memcpy(_value, value + offset, length * sizeof(float));
     memcpy(_value, value + offset, length * sizeof(float));
 }
 }

+ 74 - 35
gameplay/src/AudioBuffer.cpp

@@ -28,14 +28,14 @@ AudioBuffer::~AudioBuffer()
 
 
     if (_alBuffer)
     if (_alBuffer)
     {
     {
-        alDeleteBuffers(1, &_alBuffer);
+        AL_CHECK( alDeleteBuffers(1, &_alBuffer) );
         _alBuffer = 0;
         _alBuffer = 0;
     }
     }
 }
 }
 
 
 AudioBuffer* AudioBuffer::create(const char* path)
 AudioBuffer* AudioBuffer::create(const char* path)
 {
 {
-    assert(path);
+    GP_ASSERT(path);
 
 
     // Search the cache for a stream from this file.
     // Search the cache for a stream from this file.
     unsigned int bufferCount = (unsigned int)__buffers.size();
     unsigned int bufferCount = (unsigned int)__buffers.size();
@@ -43,6 +43,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     for (unsigned int i = 0; i < bufferCount; i++)
     for (unsigned int i = 0; i < bufferCount; i++)
     {
     {
         buffer = __buffers[i];
         buffer = __buffers[i];
+        GP_ASSERT(buffer);
         if (buffer->_filePath.compare(path) == 0)
         if (buffer->_filePath.compare(path) == 0)
         {
         {
             buffer->addRef();
             buffer->addRef();
@@ -51,15 +52,13 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     }
 
 
     ALuint alBuffer;
     ALuint alBuffer;
-    ALCenum al_error;
 
 
     // Load audio data into a buffer.
     // Load audio data into a buffer.
-    alGenBuffers(1, &alBuffer);
-    al_error = alGetError();
-    if (al_error != AL_NO_ERROR)
+    AL_CHECK( alGenBuffers(1, &alBuffer) );
+    if (AL_LAST_ERROR())
     {
     {
-        LOG_ERROR_VARG("AudioBuffer alGenBuffers AL error: %d", al_error);
-        alDeleteBuffers(1, &alBuffer);
+        GP_ERROR("Failed to create OpenAL buffer; alGenBuffers error: %d", AL_LAST_ERROR());
+        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
         return NULL;
         return NULL;
     }
     }
     
     
@@ -67,7 +66,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     FILE* file = FileSystem::openFile(path, "rb");
     FILE* file = FileSystem::openFile(path, "rb");
     if (!file)
     if (!file)
     {
     {
-        LOG_ERROR_VARG("Invalid audio buffer file: %s", path);
+        GP_ERROR("Failed to load audio file %s.", path);
         goto cleanup;
         goto cleanup;
     }
     }
     
     
@@ -75,7 +74,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     char header[12];
     char header[12];
     if (fread(header, 1, 12, file) != 12)
     if (fread(header, 1, 12, file) != 12)
     {
     {
-        LOG_ERROR_VARG("Invalid audio buffer file: %s", path);
+        GP_ERROR("Invalid header for audio file %s.", path);
         goto cleanup;
         goto cleanup;
     }
     }
     
     
@@ -84,7 +83,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     {
     {
         if (!AudioBuffer::loadWav(file, alBuffer))
         if (!AudioBuffer::loadWav(file, alBuffer))
         {
         {
-            LOG_ERROR_VARG("Invalid wave file: %s", path);
+            GP_ERROR("Invalid wave file: %s", path);
             goto cleanup;
             goto cleanup;
         }
         }
     }
     }
@@ -92,13 +91,14 @@ AudioBuffer* AudioBuffer::create(const char* path)
     {
     {
         if (!AudioBuffer::loadOgg(file, alBuffer))
         if (!AudioBuffer::loadOgg(file, alBuffer))
         {
         {
-            LOG_ERROR_VARG("Invalid ogg file: %s", path);
+            GP_ERROR("Invalid ogg file: %s", path);
             goto cleanup;
             goto cleanup;
         }
         }
     }
     }
     else
     else
     {
     {
-        LOG_ERROR_VARG("Unsupported audio file: %s", path);
+        GP_ERROR("Unsupported audio file: %s", path);
+        goto cleanup;
     }
     }
     
     
     fclose(file);
     fclose(file);
@@ -115,17 +115,21 @@ cleanup:
     if (file)
     if (file)
         fclose(file);
         fclose(file);
     if (alBuffer)
     if (alBuffer)
-        alDeleteBuffers(1, &alBuffer);
+        AL_CHECK( alDeleteBuffers(1, &alBuffer) );
     return NULL;
     return NULL;
 }
 }
 
 
 bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
 bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
 {
 {
+    GP_ASSERT(file);
     unsigned char stream[12];
     unsigned char stream[12];
     
     
     // Verify the wave fmt magic value meaning format.
     // Verify the wave fmt magic value meaning format.
     if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
     if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
+    {
+        GP_ERROR("Failed to verify the magic value for the wave file format.");
         return false;
         return false;
+    }
     
     
     unsigned int section_size;
     unsigned int section_size;
     section_size  = stream[7]<<24;
     section_size  = stream[7]<<24;
@@ -136,21 +140,27 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     // Check for a valid pcm format.
     // Check for a valid pcm format.
     if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
     if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
     {
     {
-        LOG_ERROR("Unsupported audio file, not PCM format.");
+        GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
         return false;
         return false;
     }
     }
     
     
-    // Get the channel count (16-bit little-endian)
+    // Get the channel count (16-bit little-endian).
     int channels;
     int channels;
     if (fread(stream, 1, 2, file) != 2)
     if (fread(stream, 1, 2, file) != 2)
+    {
+        GP_ERROR("Failed to read the wave file's channel count.");
         return false;
         return false;
+    }
     channels  = stream[1]<<8;
     channels  = stream[1]<<8;
     channels |= stream[0];
     channels |= stream[0];
     
     
-    // Get the sample frequency (32-bit little-endian) 
+    // Get the sample frequency (32-bit little-endian).
     ALuint frequency;
     ALuint frequency;
     if (fread(stream, 1, 4, file) != 4)
     if (fread(stream, 1, 4, file) != 4)
+    {
+        GP_ERROR("Failed to read the wave file's sample frequency.");
         return false;
         return false;
+    }
 
 
     frequency  = stream[3]<<24;
     frequency  = stream[3]<<24;
     frequency |= stream[2]<<16;
     frequency |= stream[2]<<16;
@@ -161,12 +171,18 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     // We don't need that info, so just read and ignore it. 
     // We don't need that info, so just read and ignore it. 
     // We could use this later if we need to know the duration.
     // We could use this later if we need to know the duration.
     if (fread(stream, 1, 6, file) != 6)
     if (fread(stream, 1, 6, file) != 6)
+    {
+        GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
         return false;
         return false;
+    }
     
     
-    // Get the bit depth (16-bit little-endian)
+    // Get the bit depth (16-bit little-endian).
     int bits;
     int bits;
     if (fread(stream, 1, 2, file) != 2)
     if (fread(stream, 1, 2, file) != 2)
+    {
+        GP_ERROR("Failed to read the wave file's bit depth.");
         return false;
         return false;
+    }
     bits  = stream[1]<<8;
     bits  = stream[1]<<8;
     bits |= stream[0];
     bits |= stream[0];
     
     
@@ -188,68 +204,91 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     }
     }
     else
     else
     {
     {
-        LOG_ERROR_VARG("Incompatible format: (%d, %d)", channels, bits);
+        GP_ERROR("Incompatible wave file format: (%d, %d)", channels, bits);
         return false;
         return false;
     }
     }
     
     
-    // Check against the size of the format header as there may be more data that we need to read
+    // Check against the size of the format header as there may be more data that we need to read.
     if (section_size > 16)
     if (section_size > 16)
     {
     {
         unsigned int length = section_size - 16;
         unsigned int length = section_size - 16;
 
 
-        // extension size is 2 bytes
+        // Extension size is 2 bytes.
         if (fread(stream, 1, length, file) != length)
         if (fread(stream, 1, length, file) != length)
+        {
+            GP_ERROR("Failed to read extension size from wave file.");
             return false;
             return false;
+        }
     }
     }
 
 
+    // Read in the type of the next section of the file.
     if (fread(stream, 1, 4, file) != 4)
     if (fread(stream, 1, 4, file) != 4)
+    {
+        GP_ERROR("Failed to read next section type (fact or data) from wave file.");
         return false;
         return false;
+    }
 
 
-    // read the next chunk, could be fact section or the data section
+    // Read the fact section if it is there.
     if (memcmp(stream, "fact", 4) == 0)
     if (memcmp(stream, "fact", 4) == 0)
     {
     {
         if (fread(stream, 1, 4, file) != 4)
         if (fread(stream, 1, 4, file) != 4)
+        {
+            GP_ERROR("Failed to read fact section size from wave file.");
             return false;
             return false;
+        }
 
 
         section_size  = stream[3]<<24;
         section_size  = stream[3]<<24;
         section_size |= stream[2]<<16;
         section_size |= stream[2]<<16;
         section_size |= stream[1]<<8;
         section_size |= stream[1]<<8;
         section_size |= stream[0];
         section_size |= stream[0];
 
 
-        // read in the rest of the fact section
+        // Read in the fact section.
         if (fread(stream, 1, section_size, file) != section_size)
         if (fread(stream, 1, section_size, file) != section_size)
+        {
+            GP_ERROR("Failed to read fact section from wave file.");
             return false;
             return false;
+        }
 
 
+        // Read in the type of the next section of the file.
         if (fread(stream, 1, 4, file) != 4)
         if (fread(stream, 1, 4, file) != 4)
+        {
+            GP_ERROR("Failed to read next section type (should be data) from wave file.");
             return false;
             return false;
+        }
     }
     }
 
 
-    // should now be the data section which holds the decoded sample data
+    // Should now be the data section which holds the decoded sample data.
     if (memcmp(stream, "data", 4) != 0)
     if (memcmp(stream, "data", 4) != 0)
     {
     {
-        LOG_ERROR("WAV file has no data.");
+        GP_ERROR("Failed to load wave file; file appears to have no data.");
         return false;
         return false;
     }
     }
 
 
     // Read how much data is remaining and buffer it up.
     // Read how much data is remaining and buffer it up.
     unsigned int dataSize;
     unsigned int dataSize;
-    fread(&dataSize, sizeof(int), 1, file);
+    if (fread(&dataSize, sizeof(int), 1, file) != 1)
+    {
+        GP_ERROR("Failed to read size of data section from wave file.");
+        return false;
+    }
 
 
     char* data = new char[dataSize];
     char* data = new char[dataSize];
     if (fread(data, sizeof(char), dataSize, file) != dataSize)
     if (fread(data, sizeof(char), dataSize, file) != dataSize)
     {
     {
-        LOG_ERROR("WAV file missing data.");
+        GP_ERROR("Failed to load wave file; file is missing data.");
         SAFE_DELETE_ARRAY(data);
         SAFE_DELETE_ARRAY(data);
         return false;
         return false;
     }
     }
 
 
-    alBufferData(buffer, format, data, dataSize, frequency);
+    AL_CHECK( alBufferData(buffer, format, data, dataSize, frequency) );
     SAFE_DELETE_ARRAY(data);
     SAFE_DELETE_ARRAY(data);
     return true;
     return true;
 }
 }
     
     
 bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
 bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
 {
 {
+    GP_ASSERT(file);
+
     OggVorbis_File ogg_file;
     OggVorbis_File ogg_file;
     vorbis_info* info;
     vorbis_info* info;
     ALenum format;
     ALenum format;
@@ -262,18 +301,18 @@ bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
     if ((result = ov_open(file, &ogg_file, NULL, 0)) < 0)
     if ((result = ov_open(file, &ogg_file, NULL, 0)) < 0)
     {
     {
         fclose(file);
         fclose(file);
-        LOG_ERROR("Could not open Ogg stream.");
+        GP_ERROR("Failed to open ogg file.");
         return false;
         return false;
     }
     }
 
 
     info = ov_info(&ogg_file, -1);
     info = ov_info(&ogg_file, -1);
-
+    GP_ASSERT(info);
     if (info->channels == 1)
     if (info->channels == 1)
         format = AL_FORMAT_MONO16;
         format = AL_FORMAT_MONO16;
     else
     else
         format = AL_FORMAT_STEREO16;
         format = AL_FORMAT_STEREO16;
 
 
-    // size = #samples * #channels * 2 (for 16 bit)
+    // size = #samples * #channels * 2 (for 16 bit).
     unsigned int data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;
     unsigned int data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;
     char* data = new char[data_size];
     char* data = new char[data_size];
 
 
@@ -287,7 +326,7 @@ bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
         else if (result < 0)
         else if (result < 0)
         {
         {
             SAFE_DELETE_ARRAY(data);
             SAFE_DELETE_ARRAY(data);
-            LOG_ERROR("OGG file missing data.");
+            GP_ERROR("Failed to read ogg file; file is missing data.");
             return false;
             return false;
         }
         }
         else
         else
@@ -299,16 +338,16 @@ bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)
     if (size == 0)
     if (size == 0)
     {
     {
         SAFE_DELETE_ARRAY(data);
         SAFE_DELETE_ARRAY(data);
-        LOG_ERROR("Unable to read OGG data.");
+        GP_ERROR("Filed to read ogg file; unable to read any data.");
         return false;
         return false;
     }
     }
 
 
-    alBufferData(buffer, format, data, data_size, info->rate);
+    AL_CHECK( alBufferData(buffer, format, data, data_size, info->rate) );
 
 
     SAFE_DELETE_ARRAY(data);
     SAFE_DELETE_ARRAY(data);
     ov_clear(&ogg_file);
     ov_clear(&ogg_file);
 
 
-    // ov_clear actually closes the file pointer as well
+    // ov_clear actually closes the file pointer as well.
     file = 0;
     file = 0;
 
 
     return true;
     return true;

+ 13 - 11
gameplay/src/AudioController.cpp

@@ -18,19 +18,19 @@ AudioController::~AudioController()
 
 
 void AudioController::initialize()
 void AudioController::initialize()
 {
 {
-    _alcDevice = alcOpenDevice (NULL);
+    _alcDevice = alcOpenDevice(NULL);
     if (!_alcDevice)
     if (!_alcDevice)
     {
     {
-        LOG_ERROR("AudioController::initialize() error. Unable to open OpenAL device.\n");
-        return;  
+        GP_ERROR("Unable to open OpenAL device.\n");
+        return;
     }
     }
-        
+    
     _alcContext = alcCreateContext(_alcDevice, NULL);
     _alcContext = alcCreateContext(_alcDevice, NULL);
     ALCenum alcErr = alcGetError(_alcDevice);
     ALCenum alcErr = alcGetError(_alcDevice);
     if (!_alcContext || alcErr != ALC_NO_ERROR)
     if (!_alcContext || alcErr != ALC_NO_ERROR)
     {
     {
-        alcCloseDevice (_alcDevice);
-        LOG_ERROR_VARG("AudioController::initialize() error. Unable to create OpenAL context. Error: %d\n", alcErr);
+        alcCloseDevice(_alcDevice);
+        GP_ERROR("Unable to create OpenAL context. Error: %d\n", alcErr);
         return;
         return;
     }
     }
     
     
@@ -38,7 +38,7 @@ void AudioController::initialize()
     alcErr = alcGetError(_alcDevice);
     alcErr = alcGetError(_alcDevice);
     if (alcErr != ALC_NO_ERROR)
     if (alcErr != ALC_NO_ERROR)
     {
     {
-        LOG_ERROR_VARG("AudioController::initialize() error. Unable to make OpenAL context current. Error: %d\n", alcErr);
+        GP_ERROR("Unable to make OpenAL context current. Error: %d\n", alcErr);
     }
     }
 }
 }
 
 
@@ -65,6 +65,7 @@ void AudioController::pause()
     AudioSource* source = NULL;
     AudioSource* source = NULL;
     while (itr != _playingSources.end())
     while (itr != _playingSources.end())
     {
     {
+        GP_ASSERT(*itr);
         source = *itr;
         source = *itr;
         _pausingSource = source;
         _pausingSource = source;
         source->pause();
         source->pause();
@@ -83,6 +84,7 @@ void AudioController::resume()
     AudioSource* source = NULL;
     AudioSource* source = NULL;
     while (itr != _playingSources.end())
     while (itr != _playingSources.end())
     {
     {
+        GP_ASSERT(*itr);
         source = *itr;
         source = *itr;
         source->resume();
         source->resume();
         itr++;
         itr++;
@@ -94,10 +96,10 @@ void AudioController::update(long elapsedTime)
     AudioListener* listener = AudioListener::getInstance();
     AudioListener* listener = AudioListener::getInstance();
     if (listener)
     if (listener)
     {
     {
-        alListenerf(AL_GAIN, listener->getGain());
-        alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation());
-        alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity());
-        alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition());
+        AL_CHECK( alListenerf(AL_GAIN, listener->getGain()) );
+        AL_CHECK( alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation()) );
+        AL_CHECK( alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity()) );
+        AL_CHECK( alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition()) );
     }
     }
 }
 }
 
 

+ 2 - 0
gameplay/src/AudioListener.cpp

@@ -89,6 +89,7 @@ void AudioListener::setCamera(Camera* c)
         // Disconnect our current camera.
         // Disconnect our current camera.
         if (_camera)
         if (_camera)
         {
         {
+            GP_ASSERT(_camera->getNode());
             _camera->getNode()->removeListener(this);
             _camera->getNode()->removeListener(this);
             SAFE_RELEASE(_camera);
             SAFE_RELEASE(_camera);
         }
         }
@@ -98,6 +99,7 @@ void AudioListener::setCamera(Camera* c)
 
 
         if (_camera)
         if (_camera)
         {
         {
+            GP_ASSERT(_camera->getNode());
             _camera->addRef();
             _camera->addRef();
             _camera->getNode()->addListener(this);
             _camera->getNode()->addListener(this);
         }
         }

+ 39 - 39
gameplay/src/AudioSource.cpp

@@ -12,18 +12,19 @@ namespace gameplay
 AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
 AudioSource::AudioSource(AudioBuffer* buffer, ALuint source) 
     : _alSource(source), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
     : _alSource(source), _buffer(buffer), _looped(true), _gain(1.0f), _pitch(1.0f), _node(NULL)
 {
 {
-    alSourcei(_alSource, AL_BUFFER, buffer->_alBuffer);
-    alSourcei(_alSource, AL_LOOPING, _looped);
-    alSourcef(_alSource, AL_PITCH, _pitch);
-    alSourcef(_alSource, AL_GAIN, _gain);
-    alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity);
+    GP_ASSERT(buffer);
+    AL_CHECK( alSourcei(_alSource, AL_BUFFER, buffer->_alBuffer) );
+    AL_CHECK( alSourcei(_alSource, AL_LOOPING, _looped) );
+    AL_CHECK( alSourcef(_alSource, AL_PITCH, _pitch) );
+    AL_CHECK( alSourcef(_alSource, AL_GAIN, _gain) );
+    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (const ALfloat*)&_velocity) );
 }
 }
 
 
 AudioSource::~AudioSource()
 AudioSource::~AudioSource()
 {
 {
     if (_alSource)
     if (_alSource)
     {
     {
-        alDeleteSources(1, &_alSource);
+        AL_CHECK( alDeleteSources(1, &_alSource) );
         _alSource = 0;
         _alSource = 0;
     }
     }
     SAFE_RELEASE(_buffer);
     SAFE_RELEASE(_buffer);
@@ -31,14 +32,14 @@ AudioSource::~AudioSource()
 
 
 AudioSource* AudioSource::create(const char* url)
 AudioSource* AudioSource::create(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     // Load from a .audio file.
     // Load from a .audio file.
     std::string pathStr = url;
     std::string pathStr = url;
     if (pathStr.find(".audio") != pathStr.npos)
     if (pathStr.find(".audio") != pathStr.npos)
     {
     {
         Properties* properties = Properties::create(url);
         Properties* properties = Properties::create(url);
-        assert(properties);
+        GP_ASSERT(properties);
         if (properties == NULL)
         if (properties == NULL)
         {
         {
             return NULL;
             return NULL;
@@ -57,11 +58,11 @@ AudioSource* AudioSource::create(const char* url)
     // Load the audio source.
     // Load the audio source.
     ALuint alSource = 0;
     ALuint alSource = 0;
 
 
-    alGenSources(1, &alSource);
-    if (alGetError() != AL_NO_ERROR)
+    AL_CHECK( alGenSources(1, &alSource) );
+    if (AL_LAST_ERROR())
     {
     {
         SAFE_RELEASE(buffer);
         SAFE_RELEASE(buffer);
-        LOG_ERROR("AudioSource::createAudioSource - Error generating audio source.");
+        GP_ERROR("Error generating audio source.");
         return NULL;
         return NULL;
     }
     }
     
     
@@ -71,17 +72,17 @@ AudioSource* AudioSource::create(const char* url)
 AudioSource* AudioSource::create(Properties* properties)
 AudioSource* AudioSource::create(Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    assert(properties);
+    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
     if (!properties || !(strcmp(properties->getNamespace(), "audio") == 0))
     {
     {
-        WARN("Failed to load audio source from properties object: must be non-null object and have namespace equal to \'audio\'.");
+        GP_ERROR("Failed to load audio source from properties object: must be non-null object and have namespace equal to \'audio\'.");
         return NULL;
         return NULL;
     }
     }
 
 
     const char* path = properties->getString("path");
     const char* path = properties->getString("path");
     if (path == NULL)
     if (path == NULL)
     {
     {
-        WARN("Audio file failed to load; the file path was not specified.");
+        GP_ERROR("Audio file failed to load; the file path was not specified.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -89,20 +90,20 @@ AudioSource* AudioSource::create(Properties* properties)
     AudioSource* audio = AudioSource::create(path);
     AudioSource* audio = AudioSource::create(path);
     if (audio == NULL)
     if (audio == NULL)
     {
     {
-        WARN_VARG("Audio file '%s' failed to load properly.", path);
+        GP_ERROR("Audio file '%s' failed to load properly.", path);
         return NULL;
         return NULL;
     }
     }
 
 
     // Set any properties that the user specified in the .audio file.
     // Set any properties that the user specified in the .audio file.
-    if (properties->getString("looped") != NULL)
+    if (properties->exists("looped"))
     {
     {
         audio->setLooped(properties->getBool("looped"));
         audio->setLooped(properties->getBool("looped"));
     }
     }
-    if (properties->getString("gain") != NULL)
+    if (properties->exists("gain"))
     {
     {
         audio->setGain(properties->getFloat("gain"));
         audio->setGain(properties->getFloat("gain"));
     }
     }
-    if (properties->getString("pitch") != NULL)
+    if (properties->exists("pitch"))
     {
     {
         audio->setPitch(properties->getFloat("pitch"));
         audio->setPitch(properties->getFloat("pitch"));
     }
     }
@@ -118,7 +119,7 @@ AudioSource* AudioSource::create(Properties* properties)
 AudioSource::State AudioSource::getState() const
 AudioSource::State AudioSource::getState() const
 {
 {
     ALint state;
     ALint state;
-    alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
+    AL_CHECK( alGetSourcei(_alSource, AL_SOURCE_STATE, &state) );
 
 
     switch (state)
     switch (state)
     {
     {
@@ -136,29 +137,28 @@ AudioSource::State AudioSource::getState() const
 
 
 void AudioSource::play()
 void AudioSource::play()
 {
 {
-    alSourcePlay(_alSource);
+    AL_CHECK( alSourcePlay(_alSource) );
 
 
     // Add the source to the controller's list of currently playing sources.
     // Add the source to the controller's list of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
     AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
     if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
     if (audioController->_playingSources.find(this) == audioController->_playingSources.end())
         audioController->_playingSources.insert(this);
         audioController->_playingSources.insert(this);
 }
 }
 
 
 void AudioSource::pause()
 void AudioSource::pause()
 {
 {
-    alSourcePause(_alSource);
+    AL_CHECK( alSourcePause(_alSource) );
 
 
     // Remove the source from the controller's set of currently playing sources
     // Remove the source from the controller's set of currently playing sources
     // if the source is being paused by the user and not the controller itself.
     // if the source is being paused by the user and not the controller itself.
     AudioController* audioController = Game::getInstance()->getAudioController();
     AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
     if (audioController->_pausingSource != this)
     if (audioController->_pausingSource != this)
     {
     {
         std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
         std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
         if (iter != audioController->_playingSources.end())
         if (iter != audioController->_playingSources.end())
-        {
-            WARN("\n\nRemoving an audio source from the list of playing sources...\n\n\n");
             audioController->_playingSources.erase(iter);
             audioController->_playingSources.erase(iter);
-        }
     }
     }
 }
 }
 
 
@@ -172,10 +172,11 @@ void AudioSource::resume()
 
 
 void AudioSource::stop()
 void AudioSource::stop()
 {
 {
-    alSourceStop(_alSource);
+    AL_CHECK( alSourceStop(_alSource) );
 
 
     // Remove the source from the controller's set of currently playing sources.
     // Remove the source from the controller's set of currently playing sources.
     AudioController* audioController = Game::getInstance()->getAudioController();
     AudioController* audioController = Game::getInstance()->getAudioController();
+    GP_ASSERT(audioController);
     std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
     std::set<AudioSource*>::iterator iter = audioController->_playingSources.find(this);
     if (iter != audioController->_playingSources.end())
     if (iter != audioController->_playingSources.end())
         audioController->_playingSources.erase(iter);
         audioController->_playingSources.erase(iter);
@@ -183,7 +184,7 @@ void AudioSource::stop()
 
 
 void AudioSource::rewind()
 void AudioSource::rewind()
 {
 {
-    alSourceRewind(_alSource);
+    AL_CHECK( alSourceRewind(_alSource) );
 }
 }
 
 
 bool AudioSource::isLooped() const
 bool AudioSource::isLooped() const
@@ -193,13 +194,10 @@ bool AudioSource::isLooped() const
 
 
 void AudioSource::setLooped(bool looped)
 void AudioSource::setLooped(bool looped)
 {
 {
-    alGetError();
-    alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE);
-
-    ALCenum error = alGetError();
-    if (error != AL_NO_ERROR)
+    AL_CHECK( alSourcei(_alSource, AL_LOOPING, (looped) ? AL_TRUE : AL_FALSE) );
+    if (AL_LAST_ERROR())
     {
     {
-        LOG_ERROR_VARG("AudioSource::setLooped Error: %d", error);
+        GP_ERROR("Failed to set audio source's looped attribute with error: %d", AL_LAST_ERROR());
     }
     }
     _looped = looped;
     _looped = looped;
 }
 }
@@ -211,7 +209,7 @@ float AudioSource::getGain() const
 
 
 void AudioSource::setGain(float gain)
 void AudioSource::setGain(float gain)
 {
 {
-    alSourcef(_alSource, AL_GAIN, gain);
+    AL_CHECK( alSourcef(_alSource, AL_GAIN, gain) );
     _gain = gain;
     _gain = gain;
 }
 }
 
 
@@ -222,7 +220,7 @@ float AudioSource::getPitch() const
 
 
 void AudioSource::setPitch(float pitch)
 void AudioSource::setPitch(float pitch)
 {
 {
-    alSourcef(_alSource, AL_PITCH, pitch);
+    AL_CHECK( alSourcef(_alSource, AL_PITCH, pitch) );
     _pitch = pitch;
     _pitch = pitch;
 }
 }
 
 
@@ -233,7 +231,7 @@ const Vector3& AudioSource::getVelocity() const
 
 
 void AudioSource::setVelocity(const Vector3& velocity)
 void AudioSource::setVelocity(const Vector3& velocity)
 {
 {
-    alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity);
+    AL_CHECK( alSourcefv(_alSource, AL_VELOCITY, (ALfloat*)&velocity) );
     _velocity = velocity;
     _velocity = velocity;
 }
 }
 
 
@@ -269,17 +267,19 @@ void AudioSource::transformChanged(Transform* transform, long cookie)
     if (_node)
     if (_node)
     {
     {
         Vector3 translation = _node->getTranslationWorld();
         Vector3 translation = _node->getTranslationWorld();
-        alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&translation.x);
+        AL_CHECK( alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&translation.x) );
     }
     }
 }
 }
 
 
 AudioSource* AudioSource::clone(NodeCloneContext &context) const
 AudioSource* AudioSource::clone(NodeCloneContext &context) const
 {
 {
+    GP_ASSERT(_buffer);
+
     ALuint alSource = 0;
     ALuint alSource = 0;
-    alGenSources(1, &alSource);
-    if (alGetError() != AL_NO_ERROR)
+    AL_CHECK( alGenSources(1, &alSource) );
+    if (AL_LAST_ERROR())
     {
     {
-        LOG_ERROR("AudioSource::createAudioSource - Error generating audio source.");
+        GP_ERROR("Error generating audio source.");
         return NULL;
         return NULL;
     }
     }
     AudioSource* audioClone = new AudioSource(_buffer, alSource);
     AudioSource* audioClone = new AudioSource(_buffer, alSource);

+ 58 - 68
gameplay/src/Base.h

@@ -51,50 +51,42 @@ namespace gameplay
 extern void printError(const char* format, ...);
 extern void printError(const char* format, ...);
 }
 }
 
 
-#ifdef __ANDROID__
-#include <android/log.h>
-#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__))
-
-// System Errors
-#define LOG_ERROR(x) \
-    { \
-        LOGI(x); \
-        assert(#x == 0); \
-    }
-#define LOG_ERROR_VARG(x, ...) \
-    { \
-        LOGI(x, __VA_ARGS__); \
-        assert(#x == 0); \
-    }
-
-// Warning macro
-#ifdef WARN
-#undef WARN
+// Assert macros.
+#ifdef _DEBUG
+#ifdef WIN32
+#define GP_FORCE_ASSERTION_FAILURE do { __debugbreak(); } while (0)
+#else
+#define GP_FORCE_ASSERTION_FAILURE do { assert(0); } while (0)
 #endif
 #endif
-#define WARN(x) LOGI(x)
-#define WARN_VARG(x, ...) LOGI(x, __VA_ARGS__)
 
 
+#define GP_ASSERT(expression) do { \
+    if (!(expression)) \
+    { \
+        printError("%s -- Assertion \'" #expression "\' failed.\n", __FUNCTION__); \
+        GP_FORCE_ASSERTION_FAILURE; \
+    } } while (0)
 #else
 #else
+#define GP_FORCE_ASSERTION_FAILURE do { (void)sizeof(int); } while (0)
+#define GP_ASSERT(expression) do { (void)sizeof(expression); } while (0)
+#endif
 
 
-// System Errors
-#define LOG_ERROR(x) \
+// Error macro.
+#define GP_ERROR(...) do \
     { \
     { \
-        printError(x); \
-        assert(#x == 0); \
-    }
-#define LOG_ERROR_VARG(x, ...) \
+        printError("%s -- ", __FUNCTION__); \
+        printError(__VA_ARGS__); \
+        printError("\n"); \
+        GP_FORCE_ASSERTION_FAILURE; \
+        std::exit(-1); \
+    } while (0)
+
+// Warning macro.
+#define GP_WARN(...) do \
     { \
     { \
-        printError(x, __VA_ARGS__); \
-        assert(#x == 0); \
-    }
-
-// Warning macro
-#ifdef WARN
-#undef WARN
-#endif
-#define WARN(x) printError(x)
-#define WARN_VARG(x, ...) printError(x, __VA_ARGS__)
-#endif
+        printError("%s -- ", __FUNCTION__); \
+        printError(__VA_ARGS__); \
+        printError("\n"); \
+    } while (0)
 
 
 // Bullet Physics
 // Bullet Physics
 #include <btBulletDynamicsCommon.h>
 #include <btBulletDynamicsCommon.h>
@@ -256,11 +248,7 @@ typedef GLuint RenderBufferHandle;
     { \
     { \
         gl_code; \
         gl_code; \
         __gl_error_code = glGetError(); \
         __gl_error_code = glGetError(); \
-        if (__gl_error_code != GL_NO_ERROR) \
-        { \
-            LOG_ERROR_VARG(#gl_code ": %d", (int)__gl_error_code); \
-        } \
-        assert(__gl_error_code == GL_NO_ERROR); \
+        GP_ASSERT(__gl_error_code == GL_NO_ERROR); \
     }
     }
 #endif
 #endif
 
 
@@ -280,7 +268,7 @@ typedef GLuint RenderBufferHandle;
         __gl_error_code = glGetError(); \
         __gl_error_code = glGetError(); \
         if (__gl_error_code != GL_NO_ERROR) \
         if (__gl_error_code != GL_NO_ERROR) \
         { \
         { \
-            LOG_ERROR_VARG(#gl_code ": %d", (int)__gl_error_code); \
+            GP_ERROR(#gl_code ": %d", (int)__gl_error_code); \
         } \
         } \
     }
     }
 
 
@@ -292,6 +280,32 @@ extern GLenum __gl_error_code;
  */
  */
 #define GL_LAST_ERROR() __gl_error_code
 #define GL_LAST_ERROR() __gl_error_code
 
 
+/**
+ * Executes the specified AL code and checks the AL error afterwards
+ * to ensure it succeeded.
+ *
+ * The AL_LAST_ERROR macro can be used afterwards to check whether a AL error was
+ * encountered executing the specified code.
+ */
+#define AL_CHECK( al_code ) \
+    { \
+        while (alGetError() != AL_NO_ERROR) ; \
+        al_code; \
+        __al_error_code = alGetError(); \
+        if (__al_error_code != AL_NO_ERROR) \
+        { \
+            GP_ERROR(#al_code ": %d", (int)__al_error_code); \
+        } \
+    }
+
+// Global variable to hold AL errors
+extern ALenum __al_error_code;
+
+/**
+ * Accesses the most recently set global AL error.
+ */
+#define AL_LAST_ERROR() __al_error_code
+
 
 
 #if defined(WIN32)
 #if defined(WIN32)
     #pragma warning( disable : 4172 )
     #pragma warning( disable : 4172 )
@@ -302,28 +316,4 @@ extern GLenum __gl_error_code;
     #pragma warning( disable : 4996 )
     #pragma warning( disable : 4996 )
 #endif
 #endif
 
 
-#ifdef __ANDROID__
-#include <android_native_app_glue.h>
-extern void amain(struct android_app* state);
-#endif
-
-
-// Assert has special behavior on Windows (for Visual Studio).
-#ifdef WIN32
-#ifdef assert
-#undef assert
-#endif
-#ifdef _DEBUG
-#define assert(expression) do { \
-    if (!(expression)) \
-    { \
-        printError("Assertion \'" #expression "\' failed."); \
-        __debugbreak(); \
-    } } while (0)
-
-#else
-#define assert(expression) do { (void)sizeof(expression); } while (0)
-#endif
-#endif
-
 #endif
 #endif

+ 1 - 1
gameplay/src/BoundingBox.cpp

@@ -32,7 +32,7 @@ const BoundingBox& BoundingBox::empty()
 
 
 void BoundingBox::getCorners(Vector3* dst) const
 void BoundingBox::getCorners(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     // Near face, specified counter-clockwise looking towards the origin from the positive z-axis.
     // Near face, specified counter-clockwise looking towards the origin from the positive z-axis.
     // Left-top-front.
     // Left-top-front.

+ 66 - 66
gameplay/src/Bundle.cpp

@@ -96,7 +96,7 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
 template <class T>
 template <class T>
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
 {
 {
-    assert(sizeof(T) >= readSize);
+    GP_ASSERT(sizeof(T) >= readSize);
 
 
     if (!read(length))
     if (!read(length))
     {
     {
@@ -122,7 +122,7 @@ std::string readString(FILE* fp)
     }
     }
 
 
     // Sanity check to detect if string length is far too big
     // Sanity check to detect if string length is far too big
-    assert(length < BUNDLE_MAX_STRING_LENGTH);
+    GP_ASSERT(length < BUNDLE_MAX_STRING_LENGTH);
 
 
     std::string str;
     std::string str;
     if (length > 0)
     if (length > 0)
@@ -154,7 +154,7 @@ Bundle* Bundle::create(const char* path)
     FILE* fp = FileSystem::openFile(path, "rb");
     FILE* fp = FileSystem::openFile(path, "rb");
     if (!fp)
     if (!fp)
     {
     {
-        WARN_VARG("Failed to open file: '%s'.", path);
+        GP_WARN("Failed to open file: '%s'.", path);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -162,7 +162,7 @@ Bundle* Bundle::create(const char* path)
     char sig[9];
     char sig[9];
     if (fread(sig, 1, 9, fp) != 9 || memcmp(sig, "«GPB»\r\n\x1A\n", 9) != 0)
     if (fread(sig, 1, 9, fp) != 9 || memcmp(sig, "«GPB»\r\n\x1A\n", 9) != 0)
     {
     {
-        LOG_ERROR_VARG("Invalid bundle header: %s", path);
+        GP_ERROR("Invalid bundle header: %s", path);
         fclose(fp);
         fclose(fp);
         return NULL;
         return NULL;
     }
     }
@@ -171,7 +171,7 @@ Bundle* Bundle::create(const char* path)
     unsigned char ver[2];
     unsigned char ver[2];
     if (fread(ver, 1, 2, fp) != 2 || ver[0] != BUNDLE_VERSION_MAJOR || ver[1] != BUNDLE_VERSION_MINOR)
     if (fread(ver, 1, 2, fp) != 2 || ver[0] != BUNDLE_VERSION_MAJOR || ver[1] != BUNDLE_VERSION_MINOR)
     {
     {
-        LOG_ERROR_VARG("Unsupported version (%d.%d) for bundle: %s (expected %d.%d)", (int)ver[0], (int)ver[1], path, BUNDLE_VERSION_MAJOR, BUNDLE_VERSION_MINOR);
+        GP_ERROR("Unsupported version (%d.%d) for bundle: %s (expected %d.%d)", (int)ver[0], (int)ver[1], path, BUNDLE_VERSION_MAJOR, BUNDLE_VERSION_MINOR);
         fclose(fp);
         fclose(fp);
         return NULL;
         return NULL;
     }
     }
@@ -257,20 +257,20 @@ Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
     Reference* ref = find(id);
     Reference* ref = find(id);
     if (ref == NULL)
     if (ref == NULL)
     {
     {
-        LOG_ERROR_VARG("No object with name '%s' in bundle '%s'.", id, _path.c_str());
+        GP_ERROR("No object with name '%s' in bundle '%s'.", id, _path.c_str());
         return NULL;
         return NULL;
     }
     }
 
 
     if (ref->type != type)
     if (ref->type != type)
     {
     {
-        LOG_ERROR_VARG("Object '%s' in bundle '%s' has type %d (expected type %d).", id, _path.c_str(), (int)ref->type, (int)type);
+        GP_ERROR("Object '%s' in bundle '%s' has type %d (expected type %d).", id, _path.c_str(), (int)ref->type, (int)type);
         return NULL;
         return NULL;
     }
     }
 
 
     // Seek to the offset of this object
     // Seek to the offset of this object
     if (fseek(_file, ref->offset, SEEK_SET) != 0)
     if (fseek(_file, ref->offset, SEEK_SET) != 0)
     {
     {
-        LOG_ERROR_VARG("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
+        GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
         return NULL;
         return NULL;
     }
     }
 
 
@@ -288,7 +288,7 @@ Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
             // Found a match
             // Found a match
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             {
             {
-                LOG_ERROR_VARG("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
                 return NULL;
             }
             }
             return ref;
             return ref;
@@ -363,7 +363,7 @@ Scene* Bundle::loadScene(const char* id)
     {
     {
         Node* node = scene->findNode(xref.c_str() + 1, true);
         Node* node = scene->findNode(xref.c_str() + 1, true);
         Camera* camera = node->getCamera();
         Camera* camera = node->getCamera();
-        assert(camera);
+        GP_ASSERT(camera);
         scene->setActiveCamera(camera);
         scene->setActiveCamera(camera);
     }
     }
 
 
@@ -372,19 +372,19 @@ Scene* Bundle::loadScene(const char* id)
     if (!read(&red))
     if (!read(&red))
     {
     {
         SAFE_RELEASE(scene);
         SAFE_RELEASE(scene);
-        LOG_ERROR_VARG("Failed to read scene ambient %s color in pakcage %s", "red", _path.c_str());
+        GP_ERROR("Failed to read scene ambient %s color in pakcage %s", "red", _path.c_str());
         return NULL;
         return NULL;
     }
     }
     if (!read(&green))
     if (!read(&green))
     {
     {
         SAFE_RELEASE(scene);
         SAFE_RELEASE(scene);
-        LOG_ERROR_VARG("Failed to read scene ambient %s color in pakcage %s", "green", _path.c_str());
+        GP_ERROR("Failed to read scene ambient %s color in pakcage %s", "green", _path.c_str());
         return NULL;
         return NULL;
     }
     }
     if (!read(&blue))
     if (!read(&blue))
     {
     {
         SAFE_RELEASE(scene);
         SAFE_RELEASE(scene);
-        LOG_ERROR_VARG("Failed to read scene ambient %s color in pakcage %s", "blue", _path.c_str());
+        GP_ERROR("Failed to read scene ambient %s color in pakcage %s", "blue", _path.c_str());
         return NULL;
         return NULL;
     }
     }
     scene->setAmbientColor(red, green, blue);
     scene->setAmbientColor(red, green, blue);
@@ -398,7 +398,7 @@ Scene* Bundle::loadScene(const char* id)
             // Found a match
             // Found a match
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             {
             {
-                LOG_ERROR_VARG("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
                 return NULL;
             }
             }
             readAnimations(scene);
             readAnimations(scene);
@@ -417,7 +417,7 @@ Node* Bundle::loadNode(const char* id)
 
 
 Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     clearLoadSession();
     clearLoadSession();
 
 
@@ -435,7 +435,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
         {
         {
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
             {
             {
-                LOG_ERROR_VARG("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
+                GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 SAFE_DELETE(_trackedNodes);
                 SAFE_DELETE(_trackedNodes);
                 return NULL;
                 return NULL;
             }
             }
@@ -444,7 +444,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
             unsigned int animationCount;
             unsigned int animationCount;
             if (!read(&animationCount))
             if (!read(&animationCount))
             {
             {
-                LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationCount", "Animations");
+                GP_ERROR("Failed to read %s for %s: %s", "animationCount", "Animations");
                 SAFE_DELETE(_trackedNodes);
                 SAFE_DELETE(_trackedNodes);
                 return NULL;
                 return NULL;
             }
             }
@@ -457,7 +457,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                 unsigned int animationChannelCount;
                 unsigned int animationChannelCount;
                 if (!read(&animationChannelCount))
                 if (!read(&animationChannelCount))
                 {
                 {
-                    LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationChannelCount", "animation", id.c_str());
+                    GP_ERROR("Failed to read %s for %s: %s", "animationChannelCount", "animation", id.c_str());
                     SAFE_DELETE(_trackedNodes);
                     SAFE_DELETE(_trackedNodes);
                     return NULL;
                     return NULL;
                 }
                 }
@@ -469,7 +469,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                     std::string targetId = readString(_file);
                     std::string targetId = readString(_file);
                     if (targetId.empty())
                     if (targetId.empty())
                     {
                     {
-                        LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetId", "animation", id.c_str());
+                        GP_ERROR("Failed to read %s for %s: %s", "targetId", "animation", id.c_str());
                         SAFE_DELETE(_trackedNodes);
                         SAFE_DELETE(_trackedNodes);
                         return NULL;
                         return NULL;
                     }
                     }
@@ -482,7 +482,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                         unsigned int targetAttribute;
                         unsigned int targetAttribute;
                         if (!read(&targetAttribute))
                         if (!read(&targetAttribute))
                         {
                         {
-                            LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetAttribute", "animation", id.c_str());
+                            GP_ERROR("Failed to read %s for %s: %s", "targetAttribute", "animation", id.c_str());
                             SAFE_DELETE(_trackedNodes);
                             SAFE_DELETE(_trackedNodes);
                             return NULL;
                             return NULL;
                         }
                         }
@@ -490,7 +490,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                         AnimationTarget* target = iter->second;
                         AnimationTarget* target = iter->second;
                         if (!target)
                         if (!target)
                         {
                         {
-                            LOG_ERROR_VARG("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id.c_str());
+                            GP_ERROR("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id.c_str());
                             SAFE_DELETE(_trackedNodes);
                             SAFE_DELETE(_trackedNodes);
                             return NULL;
                             return NULL;
                         }
                         }
@@ -516,7 +516,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 
 
 Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
 Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     Node* node = NULL;
     Node* node = NULL;
 
 
@@ -734,7 +734,7 @@ Camera* Bundle::readCamera()
     unsigned char cameraType;
     unsigned char cameraType;
     if (!read(&cameraType))
     if (!read(&cameraType))
     {
     {
-        LOG_ERROR_VARG("Failed to load camera type in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load camera type in bundle '%s'.", _path.c_str());
     }
     }
 
 
     if (cameraType == 0)
     if (cameraType == 0)
@@ -746,21 +746,21 @@ Camera* Bundle::readCamera()
     float aspectRatio;
     float aspectRatio;
     if (!read(&aspectRatio))
     if (!read(&aspectRatio))
     {
     {
-        LOG_ERROR_VARG("Failed to load camera aspectRatio in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load camera aspectRatio in bundle '%s'.", _path.c_str());
     }
     }
 
 
     // near plane
     // near plane
     float nearPlane;
     float nearPlane;
     if (!read(&nearPlane))
     if (!read(&nearPlane))
     {
     {
-        LOG_ERROR_VARG("Failed to load camera near plane in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load camera near plane in bundle '%s'.", _path.c_str());
     }
     }
 
 
     // far plane
     // far plane
     float farPlane;
     float farPlane;
     if (!read(&farPlane))
     if (!read(&farPlane))
     {
     {
-        LOG_ERROR_VARG("Failed to load camera far plane in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load camera far plane in bundle '%s'.", _path.c_str());
     }
     }
 
 
     Camera* camera = NULL;
     Camera* camera = NULL;
@@ -770,7 +770,7 @@ Camera* Bundle::readCamera()
         float fieldOfView;
         float fieldOfView;
         if (!read(&fieldOfView))
         if (!read(&fieldOfView))
         {
         {
-            LOG_ERROR_VARG("Failed to load camera field of view in bundle '%s'.", _path.c_str());
+            GP_ERROR("Failed to load camera field of view in bundle '%s'.", _path.c_str());
         }
         }
 
 
         camera = Camera::createPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
         camera = Camera::createPerspective(fieldOfView, aspectRatio, nearPlane, farPlane);
@@ -781,20 +781,20 @@ Camera* Bundle::readCamera()
         float zoomX;
         float zoomX;
         if (!read(&zoomX))
         if (!read(&zoomX))
         {
         {
-            LOG_ERROR_VARG("Failed to load camera zoomX in bundle '%s'.", _path.c_str());
+            GP_ERROR("Failed to load camera zoomX in bundle '%s'.", _path.c_str());
         }
         }
 
 
         float zoomY;
         float zoomY;
         if (!read(&zoomY))
         if (!read(&zoomY))
         {
         {
-            LOG_ERROR_VARG("Failed to load camera zoomY in bundle '%s'.", _path.c_str());
+            GP_ERROR("Failed to load camera zoomY in bundle '%s'.", _path.c_str());
         }
         }
 
 
         camera = Camera::createOrthographic(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
         camera = Camera::createOrthographic(zoomX, zoomY, aspectRatio, nearPlane, farPlane);
     }
     }
     else
     else
     {
     {
-        LOG_ERROR_VARG("Failed to load camera type in bundle '%s'. Invalid camera type.", _path.c_str());
+        GP_ERROR("Failed to load camera type in bundle '%s'. Invalid camera type.", _path.c_str());
     }
     }
     return camera;
     return camera;
 }
 }
@@ -804,7 +804,7 @@ Light* Bundle::readLight()
     unsigned char type;
     unsigned char type;
     if (!read(&type))
     if (!read(&type))
     {
     {
-        LOG_ERROR_VARG("Failed to load light %s in bundle '%s'.", "type", _path.c_str());
+        GP_ERROR("Failed to load light %s in bundle '%s'.", "type", _path.c_str());
     }
     }
 
 
     if (type == 0)
     if (type == 0)
@@ -816,7 +816,7 @@ Light* Bundle::readLight()
     float red, blue, green;
     float red, blue, green;
     if (!read(&red) || !read(&blue) || !read(&green))
     if (!read(&red) || !read(&blue) || !read(&green))
     {
     {
-        LOG_ERROR_VARG("Failed to load light %s in bundle '%s'.", "color", _path.c_str());
+        GP_ERROR("Failed to load light %s in bundle '%s'.", "color", _path.c_str());
     }
     }
     Vector3 color(red, blue, green);
     Vector3 color(red, blue, green);
 
 
@@ -830,7 +830,7 @@ Light* Bundle::readLight()
         float range;
         float range;
         if (!read(&range))
         if (!read(&range))
         {
         {
-            LOG_ERROR_VARG("Failed to load point light %s in bundle '%s'.", "point", _path.c_str());
+            GP_ERROR("Failed to load point light %s in bundle '%s'.", "point", _path.c_str());
         }
         }
         light = Light::createPoint(color, range);
         light = Light::createPoint(color, range);
     }
     }
@@ -839,13 +839,13 @@ Light* Bundle::readLight()
         float range, innerAngle, outerAngle;
         float range, innerAngle, outerAngle;
         if (!read(&range) || !read(&innerAngle) || !read(&outerAngle))
         if (!read(&range) || !read(&innerAngle) || !read(&outerAngle))
         {
         {
-            LOG_ERROR_VARG("Failed to load spot light %s in bundle '%s'.", "spot", _path.c_str());
+            GP_ERROR("Failed to load spot light %s in bundle '%s'.", "spot", _path.c_str());
         }
         }
         light = Light::createSpot(color, range, innerAngle, outerAngle);
         light = Light::createSpot(color, range, innerAngle, outerAngle);
     }
     }
     else
     else
     {
     {
-        LOG_ERROR_VARG("Failed to load light %s in bundle '%s'.", "type", _path.c_str());
+        GP_ERROR("Failed to load light %s in bundle '%s'.", "type", _path.c_str());
     }
     }
     return light;
     return light;
 }
 }
@@ -867,7 +867,7 @@ Model* Bundle::readModel(const char* nodeId)
             unsigned char hasSkin;
             unsigned char hasSkin;
             if (!read(&hasSkin))
             if (!read(&hasSkin))
             {
             {
-                LOG_ERROR_VARG("Failed to load hasSkin in bundle '%s'.", _path.c_str());
+                GP_ERROR("Failed to load hasSkin in bundle '%s'.", _path.c_str());
                 return NULL;
                 return NULL;
             }
             }
             if (hasSkin)
             if (hasSkin)
@@ -882,7 +882,7 @@ Model* Bundle::readModel(const char* nodeId)
             unsigned int materialCount;
             unsigned int materialCount;
             if (!read(&materialCount))
             if (!read(&materialCount))
             {
             {
-                LOG_ERROR_VARG("Failed to load materialCount in bundle '%s'.", _path.c_str());
+                GP_ERROR("Failed to load materialCount in bundle '%s'.", _path.c_str());
                 return NULL;
                 return NULL;
             }
             }
             if (materialCount > 0)
             if (materialCount > 0)
@@ -904,7 +904,7 @@ MeshSkin* Bundle::readMeshSkin()
     float bindShape[16];
     float bindShape[16];
     if (!readMatrix(bindShape))
     if (!readMatrix(bindShape))
     {
     {
-        LOG_ERROR_VARG("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
         SAFE_DELETE(meshSkin);
         SAFE_DELETE(meshSkin);
         return NULL;
         return NULL;
     }
     }
@@ -917,7 +917,7 @@ MeshSkin* Bundle::readMeshSkin()
     unsigned int jointCount;
     unsigned int jointCount;
     if (!read(&jointCount))
     if (!read(&jointCount))
     {
     {
-        LOG_ERROR_VARG("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
         SAFE_DELETE(meshSkin);
         SAFE_DELETE(meshSkin);
         SAFE_DELETE(skinData);
         SAFE_DELETE(skinData);
         return NULL;
         return NULL;
@@ -940,20 +940,20 @@ MeshSkin* Bundle::readMeshSkin()
     unsigned int jointsBindPosesCount;
     unsigned int jointsBindPosesCount;
     if (!read(&jointsBindPosesCount))
     if (!read(&jointsBindPosesCount))
     {
     {
-        LOG_ERROR_VARG("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
+        GP_ERROR("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
         SAFE_DELETE(meshSkin);
         SAFE_DELETE(meshSkin);
         SAFE_DELETE(skinData);
         SAFE_DELETE(skinData);
         return NULL;
         return NULL;
     }
     }
     if (jointsBindPosesCount > 0)
     if (jointsBindPosesCount > 0)
     {
     {
-        assert(jointCount * 16 == jointsBindPosesCount);
+        GP_ASSERT(jointCount * 16 == jointsBindPosesCount);
         float m[16];
         float m[16];
         for (unsigned int i = 0; i < jointCount; i++)
         for (unsigned int i = 0; i < jointCount; i++)
         {
         {
             if (!readMatrix(m))
             if (!readMatrix(m))
             {
             {
-                LOG_ERROR_VARG("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
+                GP_ERROR("Failed to load MeshSkin in bundle '%s'.", _path.c_str());
                 SAFE_DELETE(meshSkin);
                 SAFE_DELETE(meshSkin);
                 SAFE_DELETE(skinData);
                 SAFE_DELETE(skinData);
                 return NULL;
                 return NULL;
@@ -1025,7 +1025,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                         Reference* ref = find(nodeID.c_str());
                         Reference* ref = find(nodeID.c_str());
                         if (ref == NULL)
                         if (ref == NULL)
                         {
                         {
-                            LOG_ERROR_VARG("No object with name '%s' in bundle '%s'.", nodeID.c_str(), _path.c_str());
+                            GP_ERROR("No object with name '%s' in bundle '%s'.", nodeID.c_str(), _path.c_str());
                             break;
                             break;
                         }
                         }
 
 
@@ -1070,7 +1070,7 @@ void Bundle::readAnimation(Scene* scene)
     unsigned int animationChannelCount;
     unsigned int animationChannelCount;
     if (!read(&animationChannelCount))
     if (!read(&animationChannelCount))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationChannelCount", "animation", animationId.c_str());
+        GP_ERROR("Failed to read %s for %s: %s", "animationChannelCount", "animation", animationId.c_str());
         return;
         return;
     }
     }
 
 
@@ -1088,7 +1088,7 @@ void Bundle::readAnimations(Scene* scene)
     unsigned int animationCount;
     unsigned int animationCount;
     if (!read(&animationCount))
     if (!read(&animationCount))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationCount", "Animations");
+        GP_ERROR("Failed to read %s for %s: %s", "animationCount", "Animations");
         return;
         return;
     }
     }
 
 
@@ -1106,7 +1106,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
     std::string targetId = readString(_file);
     std::string targetId = readString(_file);
     if (targetId.empty())
     if (targetId.empty())
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetId", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "targetId", "animation", id);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1114,7 +1114,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
     unsigned int targetAttribute;
     unsigned int targetAttribute;
     if (!read(&targetAttribute))
     if (!read(&targetAttribute))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "targetAttribute", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "targetAttribute", "animation", id);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1129,7 +1129,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
         target = scene->findNode(targetId.c_str());
         target = scene->findNode(targetId.c_str());
         if (!target)
         if (!target)
         {
         {
-            LOG_ERROR_VARG("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id);
+            GP_ERROR("Failed to read %s for %s: %s", "animation target", targetId.c_str(), id);
             return NULL;
             return NULL;
         }
         }
     }
     }
@@ -1155,35 +1155,35 @@ Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id
     // read key times
     // read key times
     if (!readArray(&keyTimesCount, &keyTimes, sizeof(unsigned int)))
     if (!readArray(&keyTimesCount, &keyTimes, sizeof(unsigned int)))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "keyTimes", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "keyTimes", "animation", id);
         return NULL;
         return NULL;
     }
     }
     
     
     // read key values
     // read key values
     if (!readArray(&valuesCount, &values))
     if (!readArray(&valuesCount, &values))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "values", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "values", "animation", id);
         return NULL;
         return NULL;
     }
     }
     
     
     // read tangentsIn
     // read tangentsIn
     if (!readArray(&tangentsInCount, &tangentsIn))
     if (!readArray(&tangentsInCount, &tangentsIn))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "tangentsIn", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "tangentsIn", "animation", id);
         return NULL;
         return NULL;
     }
     }
     
     
     // read tangent_out
     // read tangent_out
     if (!readArray(&tangentsOutCount, &tangentsOut))
     if (!readArray(&tangentsOutCount, &tangentsOut))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "tangentsOut", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "tangentsOut", "animation", id);
         return NULL;
         return NULL;
     }
     }
     
     
     // read interpolations
     // read interpolations
     if (!readArray(&interpolationCount, &interpolation, sizeof(unsigned int)))
     if (!readArray(&interpolationCount, &interpolation, sizeof(unsigned int)))
     {
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "interpolation", "animation", id);
+        GP_ERROR("Failed to read %s for %s: %s", "interpolation", "animation", id);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1193,7 +1193,7 @@ Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id
     // TODO: Handle other target attributes later.
     // TODO: Handle other target attributes later.
     if (targetAttribute > 0)
     if (targetAttribute > 0)
     {
     {
-        assert(keyTimes.size() > 0 && values.size() > 0);
+        GP_ASSERT(keyTimes.size() > 0 && values.size() > 0);
         if (animation == NULL)
         if (animation == NULL)
         {
         {
             // TODO: This code currently assumes LINEAR only
             // TODO: This code currently assumes LINEAR only
@@ -1210,7 +1210,7 @@ Animation* Bundle::readAnimationChannelData(Animation* animation, const char* id
 
 
 Mesh* Bundle::loadMesh(const char* id)
 Mesh* Bundle::loadMesh(const char* id)
 {
 {
-    return loadMesh(id, false);
+    return loadMesh(id, NULL);
 }
 }
 
 
 Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
 Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
@@ -1236,7 +1236,7 @@ Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
     Mesh* mesh = Mesh::createMesh(meshData->vertexFormat, meshData->vertexCount, false);
     Mesh* mesh = Mesh::createMesh(meshData->vertexFormat, meshData->vertexCount, false);
     if (mesh == NULL)
     if (mesh == NULL)
     {
     {
-        LOG_ERROR_VARG("Failed to create mesh: %s", id);
+        GP_ERROR("Failed to create mesh: %s", id);
         SAFE_DELETE_ARRAY(meshData);
         SAFE_DELETE_ARRAY(meshData);
         return NULL;
         return NULL;
     }
     }
@@ -1258,7 +1258,7 @@ Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
         MeshPart* part = mesh->addPart(partData->primitiveType, partData->indexFormat, partData->indexCount, false);
         MeshPart* part = mesh->addPart(partData->primitiveType, partData->indexFormat, partData->indexCount, false);
         if (part == NULL)
         if (part == NULL)
         {
         {
-            LOG_ERROR_VARG("Failed to create mesh part (i=%d): %s", i, id);
+            GP_ERROR("Failed to create mesh part (i=%d): %s", i, id);
             SAFE_DELETE(meshData);
             SAFE_DELETE(meshData);
             return NULL;
             return NULL;
         }
         }
@@ -1380,7 +1380,7 @@ Bundle::MeshData* Bundle::readMeshData()
 
 
 Bundle::MeshData* Bundle::readMeshData(const char* url)
 Bundle::MeshData* Bundle::readMeshData(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     unsigned int len = strlen(url);
     unsigned int len = strlen(url);
     if (len == 0)
     if (len == 0)
@@ -1426,7 +1426,7 @@ Font* Bundle::loadFont(const char* id)
     std::string family = readString(_file);
     std::string family = readString(_file);
     if (family.empty())
     if (family.empty())
     {
     {
-        LOG_ERROR_VARG("Failed to read font family for font: %s", id);
+        GP_ERROR("Failed to read font family for font: %s", id);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1435,7 +1435,7 @@ Font* Bundle::loadFont(const char* id)
     if (fread(&style, 4, 1, _file) != 1 ||
     if (fread(&style, 4, 1, _file) != 1 ||
         fread(&size, 4, 1, _file) != 1)
         fread(&size, 4, 1, _file) != 1)
     {
     {
-        LOG_ERROR_VARG("Failed to read style and/or size for font: %s", id);
+        GP_ERROR("Failed to read style and/or size for font: %s", id);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1446,13 +1446,13 @@ Font* Bundle::loadFont(const char* id)
     unsigned int glyphCount;
     unsigned int glyphCount;
     if (fread(&glyphCount, 4, 1, _file) != 1 || glyphCount == 0)
     if (fread(&glyphCount, 4, 1, _file) != 1 || glyphCount == 0)
     {
     {
-        LOG_ERROR_VARG("Failed to read glyph count for font: %s", id);
+        GP_ERROR("Failed to read glyph count for font: %s", id);
         return NULL;
         return NULL;
     }
     }
     Font::Glyph* glyphs = new Font::Glyph[glyphCount];
     Font::Glyph* glyphs = new Font::Glyph[glyphCount];
     if (fread(glyphs, sizeof(Font::Glyph), glyphCount, _file) != glyphCount)
     if (fread(glyphs, sizeof(Font::Glyph), glyphCount, _file) != glyphCount)
     {
     {
-        LOG_ERROR_VARG("Failed to read %d glyphs for font: %s", glyphCount, id);
+        GP_ERROR("Failed to read %d glyphs for font: %s", glyphCount, id);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
         return NULL;
     }
     }
@@ -1463,20 +1463,20 @@ Font* Bundle::loadFont(const char* id)
         fread(&height, 4, 1, _file) != 1 ||
         fread(&height, 4, 1, _file) != 1 ||
         fread(&textureByteCount, 4, 1, _file) != 1)
         fread(&textureByteCount, 4, 1, _file) != 1)
     {
     {
-        LOG_ERROR_VARG("Failed to read texture attributes for font: %s", id);
+        GP_ERROR("Failed to read texture attributes for font: %s", id);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
         return NULL;
     }
     }
     if (textureByteCount != (width * height))
     if (textureByteCount != (width * height))
     {
     {
-        LOG_ERROR_VARG("Invalid texture byte for font: %s", id);
+        GP_ERROR("Invalid texture byte for font: %s", id);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
         return NULL;
     }
     }
     unsigned char* textureData = new unsigned char[textureByteCount];
     unsigned char* textureData = new unsigned char[textureByteCount];
     if (fread(textureData, 1, textureByteCount, _file) != textureByteCount)
     if (fread(textureData, 1, textureByteCount, _file) != textureByteCount)
     {
     {
-        LOG_ERROR_VARG("Failed to read %d texture bytes for font: %s", textureByteCount, id);
+        GP_ERROR("Failed to read %d texture bytes for font: %s", textureByteCount, id);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(textureData);
         SAFE_DELETE_ARRAY(textureData);
         return NULL;
         return NULL;
@@ -1490,7 +1490,7 @@ Font* Bundle::loadFont(const char* id)
 
 
     if (texture == NULL)
     if (texture == NULL)
     {
     {
-        LOG_ERROR_VARG("Failed to create texture for font: %s", id);
+        GP_ERROR("Failed to create texture for font: %s", id);
         SAFE_DELETE_ARRAY(glyphs);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
         return NULL;
     }
     }

+ 30 - 30
gameplay/src/Button.cpp

@@ -3,41 +3,41 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    Button::Button()
-    {
-    }
 
 
-    Button::~Button()
-    {
-    }
+Button::Button()
+{
+}
 
 
-    Button* Button::create(Theme::Style* style, Properties* properties)
-    {
-        Button* button = new Button();
-        button->initialize(style, properties);
+Button::~Button()
+{
+}
 
 
-        return button;
+Button* Button::create(Theme::Style* style, Properties* properties)
+{
+    Button* button = new Button();
+    button->initialize(style, properties);
+
+    return button;
+}
+
+bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!isEnabled())
+    {
+        return false;
     }
     }
 
 
-    bool Button::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    switch (evt)
     {
     {
-        if (!isEnabled())
-        {
-            return false;
-        }
-
-        switch (evt)
-        {
-        case Touch::TOUCH_PRESS:
-            _state = Control::ACTIVE;
-            _dirty = true;
-            break;
-        case Touch::TOUCH_RELEASE:
-            _dirty = true;
-            setState(Control::NORMAL);
-            break;
-        }
-
-        return Control::touchEvent(evt, x, y, contactIndex);
+    case Touch::TOUCH_PRESS:
+        setState(Control::ACTIVE);
+        break;
+    case Touch::TOUCH_RELEASE:
+        setState(Control::NORMAL);
+        break;
     }
     }
+
+    return Control::touchEvent(evt, x, y, contactIndex);
+}
+
 }
 }

+ 7 - 7
gameplay/src/Camera.cpp

@@ -53,14 +53,14 @@ Camera::Type Camera::getCameraType() const
 
 
 float Camera::getFieldOfView() const
 float Camera::getFieldOfView() const
 {
 {
-    assert(_type == Camera::PERSPECTIVE);
+    GP_ASSERT(_type == Camera::PERSPECTIVE);
 
 
     return _fieldOfView;
     return _fieldOfView;
 }
 }
 
 
 void Camera::setFieldOfView(float fieldOfView)
 void Camera::setFieldOfView(float fieldOfView)
 {
 {
-    assert(_type == Camera::PERSPECTIVE);
+    GP_ASSERT(_type == Camera::PERSPECTIVE);
 
 
     _fieldOfView = fieldOfView;
     _fieldOfView = fieldOfView;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
@@ -68,14 +68,14 @@ void Camera::setFieldOfView(float fieldOfView)
 
 
 float Camera::getZoomX() const
 float Camera::getZoomX() const
 {
 {
-    assert(_type == Camera::ORTHOGRAPHIC);
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     return _zoom[0];
     return _zoom[0];
 }
 }
 
 
 void Camera::setZoomX(float zoomX)
 void Camera::setZoomX(float zoomX)
 {
 {
-    assert(_type == Camera::ORTHOGRAPHIC);
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     _zoom[0] = zoomX;
     _zoom[0] = zoomX;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
@@ -83,14 +83,14 @@ void Camera::setZoomX(float zoomX)
 
 
 float Camera::getZoomY() const
 float Camera::getZoomY() const
 {
 {
-    assert(_type == Camera::ORTHOGRAPHIC);
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     return _zoom[1];
     return _zoom[1];
 }
 }
 
 
 void Camera::setZoomY(float zoomY)
 void Camera::setZoomY(float zoomY)
 {
 {
-    assert(_type == Camera::ORTHOGRAPHIC);
+    GP_ASSERT(_type == Camera::ORTHOGRAPHIC);
 
 
     _zoom[1] = zoomY;
     _zoom[1] = zoomY;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
     _dirtyBits |= CAMERA_DIRTY_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;
@@ -332,7 +332,7 @@ Camera* Camera::clone(NodeCloneContext &context) const
     {
     {
         cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
         cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
     }
     }
-    assert(cameraClone);
+    GP_ASSERT(cameraClone);
 
 
     if (Node* node = context.findClonedNode(getNode()))
     if (Node* node = context.findClonedNode(getNode()))
     {
     {

+ 13 - 18
gameplay/src/CheckBox.cpp

@@ -21,6 +21,8 @@ CheckBox::~CheckBox()
 
 
 CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 {
 {
+    GP_ASSERT(properties);
+
     CheckBox* checkBox = new CheckBox();
     CheckBox* checkBox = new CheckBox();
     checkBox->initialize(style, properties);
     checkBox->initialize(style, properties);
     properties->getVector2("imageSize", &checkBox->_imageSize);
     properties->getVector2("imageSize", &checkBox->_imageSize);
@@ -39,6 +41,7 @@ void CheckBox::setChecked(bool checked)
     if (_checked != checked)
     if (_checked != checked)
     {
     {
         _checked = checked;
         _checked = checked;
+        _dirty = true;
         notifyListeners(Control::Listener::VALUE_CHANGED);
         notifyListeners(Control::Listener::VALUE_CHANGED);
     }
     }
 }
 }
@@ -57,7 +60,7 @@ void CheckBox::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     if ((eventFlags & Control::Listener::TEXT_CHANGED) == Control::Listener::TEXT_CHANGED)
     {
     {
-        assert("TEXT_CHANGED event is not applicable to CheckBox.");
+        GP_ERROR("TEXT_CHANGED event is not applicable to CheckBox.");
         eventFlags &= ~Control::Listener::TEXT_CHANGED;
         eventFlags &= ~Control::Listener::TEXT_CHANGED;
     }
     }
 
 
@@ -77,19 +80,11 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
         {
         {
             if (_state == Control::ACTIVE)
             if (_state == Control::ACTIVE)
             {
             {
-                if (x > 0 && x <= _clipBounds.width &&
-                    y > 0 && y <= _clipBounds.height)
+                if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                    y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 {
                 {
                     _checked = !_checked;
                     _checked = !_checked;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
                     notifyListeners(Control::Listener::VALUE_CHANGED);
-
-                    // Animate between icons.  Old fades out, then the new fades in.
-                    /*
-                    AnimationController* animationController = Game::getInstance()->getAnimationController();
-                    float from[1] = { 1.0f };
-                    float to[1] = { 0.0f };
-                    animationController->createAnimationFromTo("CheckBox::toggle", this, CheckBox::ANIMATE_SPRITE_ALPHA, from, to, Curve::QUADRATIC_IN_OUT, 200L);
-                    */
                 }
                 }
             }
             }
         }
         }
@@ -99,9 +94,9 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
     return Button::touchEvent(evt, x, y, contactIndex);
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void CheckBox::update(const Rectangle& clip)
+void CheckBox::update(const Rectangle& clip, const Vector2& offset)
 {
 {
-    Label::update(clip);
+    Label::update(clip, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())
@@ -138,10 +133,11 @@ void CheckBox::update(const Rectangle& clip)
 
 
 void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
 {
+    GP_ASSERT(spriteBatch);
+    GP_ASSERT(_image);
+
     // Left, v-center.
     // Left, v-center.
     // TODO: Set an alignment for icons.
     // TODO: Set an alignment for icons.
-    const Theme::Border& border = getBorder(_state);
-    const Theme::Padding padding = getPadding();
     
     
     const Rectangle& region = _image->getRegion();
     const Rectangle& region = _image->getRegion();
     const Theme::UVs& uvs = _image->getUVs();
     const Theme::UVs& uvs = _image->getUVs();
@@ -158,10 +154,9 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
         size.set(_imageSize);
         size.set(_imageSize);
     }
     }
 
 
-    Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
-        clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+    Vector2 pos(_viewportBounds.x, _viewportBounds.y + _viewportBounds.height * 0.5f - size.y * 0.5f);
 
 
-    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _clip);
+    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
 }
 }
 
 
 }
 }

+ 1 - 1
gameplay/src/CheckBox.h

@@ -120,7 +120,7 @@ protected:
      *
      *
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
      */
      */
-    void update(const Rectangle& clip);
+    void update(const Rectangle& clip, const Vector2& offset = Vector2::zero());
 
 
     /**
     /**
      * Draw the checkbox icon associated with this control.
      * Draw the checkbox icon associated with this control.

+ 336 - 289
gameplay/src/Container.cpp

@@ -4,400 +4,447 @@
 #include "AbsoluteLayout.h"
 #include "AbsoluteLayout.h"
 #include "FlowLayout.h"
 #include "FlowLayout.h"
 #include "VerticalLayout.h"
 #include "VerticalLayout.h"
+#include "ScrollLayout.h"
 #include "Label.h"
 #include "Label.h"
 #include "Button.h"
 #include "Button.h"
 #include "CheckBox.h"
 #include "CheckBox.h"
 #include "RadioButton.h"
 #include "RadioButton.h"
 #include "Slider.h"
 #include "Slider.h"
 #include "TextBox.h"
 #include "TextBox.h"
+#include "Game.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    Container::Container() : _layout(NULL)
-    {
-    }
 
 
-    Container::Container(const Container& copy)
+Container::Container() : _layout(NULL)
+{
+}
+
+Container::Container(const Container& copy)
+{
+}
+
+Container::~Container()
+{
+    std::vector<Control*>::iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
     {
     {
+        SAFE_RELEASE((*it));
     }
     }
 
 
-    Container::~Container()
-    {
-        std::vector<Control*>::iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
-        {
-            SAFE_RELEASE((*it));
-        }
+    SAFE_RELEASE(_layout);
+}
 
 
-        SAFE_RELEASE(_layout);
+Container* Container::create(Layout::Type type)
+{
+    Layout* layout = NULL;
+    switch (type)
+    {
+    case Layout::LAYOUT_ABSOLUTE:
+        layout = AbsoluteLayout::create();
+        break;
+    case Layout::LAYOUT_FLOW:
+        layout = FlowLayout::create();
+        break;
+    case Layout::LAYOUT_VERTICAL:
+        layout = VerticalLayout::create();
+        break;
+    case Layout::LAYOUT_SCROLL:
+        layout = ScrollLayout::create();
+        break;
     }
     }
 
 
-    Container* Container::create(Layout::Type type)
-    {
-        Layout* layout = NULL;
-        switch (type)
-        {
-        case Layout::LAYOUT_ABSOLUTE:
-            layout = AbsoluteLayout::create();
-            break;
-        case Layout::LAYOUT_FLOW:
-            layout = FlowLayout::create();
-            break;
-        case Layout::LAYOUT_VERTICAL:
-            layout = VerticalLayout::create();
-            break;
-        }
+    Container* container = new Container();
+    container->_layout = layout;
 
 
-        Container* container = new Container();
-        container->_layout = layout;
+    return container;
+}
 
 
-        return container;
-    }
+Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
+{
+    GP_ASSERT(properties);
 
 
-    Container* Container::create(Theme::Style* style, Properties* properties, Theme* theme)
-    {
-        const char* layoutString = properties->getString("layout");
-        Container* container = Container::create(getLayoutType(layoutString));
-        container->initialize(style, properties);
-        container->addControls(theme, properties);
+    const char* layoutString = properties->getString("layout");
+    Container* container = Container::create(getLayoutType(layoutString));
+    container->initialize(style, properties);
+    container->addControls(theme, properties);
 
 
-        return container;
-    }
+    return container;
+}
 
 
-    void Container::addControls(Theme* theme, Properties* properties)
-    {
-        // Add all the controls to this container.
-        Properties* controlSpace = properties->getNextNamespace();
-        while (controlSpace != NULL)
-        {
-            Control* control = NULL;
+void Container::addControls(Theme* theme, Properties* properties)
+{
+    GP_ASSERT(theme);
+    GP_ASSERT(properties);
 
 
-            const char* controlStyleName = controlSpace->getString("style");
-            Theme::Style* controlStyle = NULL;
-            if (controlStyleName)
-            {
-                 controlStyle = theme->getStyle(controlStyleName);
-            }
-            assert(controlStyle);
+    // Add all the controls to this container.
+    Properties* controlSpace = properties->getNextNamespace();
+    while (controlSpace != NULL)
+    {
+        Control* control = NULL;
 
 
-            std::string controlName(controlSpace->getNamespace());
-            std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
-            if (controlName == "LABEL")
-            {
-                control = Label::create(controlStyle, controlSpace);
-            }
-            else if (controlName == "BUTTON")
-            {
-                control = Button::create(controlStyle, controlSpace);
-            }
-            else if (controlName == "CHECKBOX")
-            {
-                control = CheckBox::create(controlStyle, controlSpace);
-            }
-            else if (controlName == "RADIOBUTTON")
-            {
-                control = RadioButton::create(controlStyle, controlSpace);
-            }
-            else if (controlName == "CONTAINER")
-            {
-                control = Container::create(controlStyle, controlSpace, theme);
-            }
-            else if (controlName == "SLIDER")
-            {
-                control = Slider::create(controlStyle, controlSpace);
-            }
-            else if (controlName == "TEXTBOX")
-            {
-                control = TextBox::create(controlStyle, controlSpace);
-            }
+        const char* controlStyleName = controlSpace->getString("style");
+        Theme::Style* controlStyle = NULL;
+        GP_ASSERT(controlStyleName);
+        controlStyle = theme->getStyle(controlStyleName);
+        GP_ASSERT(controlStyle);
 
 
-            // Add the new control to the form.
-            if (control)
-            {
-                addControl(control);
-            }
+        std::string controlName(controlSpace->getNamespace());
+        std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);
+        if (controlName == "LABEL")
+        {
+            control = Label::create(controlStyle, controlSpace);
+        }
+        else if (controlName == "BUTTON")
+        {
+            control = Button::create(controlStyle, controlSpace);
+        }
+        else if (controlName == "CHECKBOX")
+        {
+            control = CheckBox::create(controlStyle, controlSpace);
+        }
+        else if (controlName == "RADIOBUTTON")
+        {
+            control = RadioButton::create(controlStyle, controlSpace);
+        }
+        else if (controlName == "CONTAINER")
+        {
+            control = Container::create(controlStyle, controlSpace, theme);
+        }
+        else if (controlName == "SLIDER")
+        {
+            control = Slider::create(controlStyle, controlSpace);
+        }
+        else if (controlName == "TEXTBOX")
+        {
+            control = TextBox::create(controlStyle, controlSpace);
+        }
+        else
+        {
+            GP_ERROR("Failed to create control; unrecognized control name \'%s\'.", controlName.c_str());
+        }
 
 
-            // Get the next control.
-            controlSpace = properties->getNextNamespace();
+        // Add the new control to the form.
+        if (control)
+        {
+            addControl(control);
         }
         }
-    }
 
 
-    Layout* Container::getLayout()
-    {
-        return _layout;
+        // Get the next control.
+        controlSpace = properties->getNextNamespace();
     }
     }
+}
 
 
-    unsigned int Container::addControl(Control* control)
-    {
-        _controls.push_back(control);
+Layout* Container::getLayout()
+{
+    return _layout;
+}
 
 
-        return _controls.size() - 1;
-    }
+unsigned int Container::addControl(Control* control)
+{
+    GP_ASSERT(control);
+    _controls.push_back(control);
 
 
-    void Container::insertControl(Control* control, unsigned int index)
-    {
-        std::vector<Control*>::iterator it = _controls.begin() + index;
-        _controls.insert(it, control);
-    }
+    return _controls.size() - 1;
+}
 
 
-    void Container::removeControl(unsigned int index)
-    {
-        std::vector<Control*>::iterator it = _controls.begin() + index;
-        _controls.erase(it);
-    }
+void Container::insertControl(Control* control, unsigned int index)
+{
+    GP_ASSERT(control);
+    std::vector<Control*>::iterator it = _controls.begin() + index;
+    _controls.insert(it, control);
+}
 
 
-    void Container::removeControl(const char* id)
+void Container::removeControl(unsigned int index)
+{
+    std::vector<Control*>::iterator it = _controls.begin() + index;
+    _controls.erase(it);
+}
+
+void Container::removeControl(const char* id)
+{
+    std::vector<Control*>::iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
     {
     {
-        std::vector<Control*>::iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
+        Control* c = *it;
+        if (strcmp(id, c->getID()) == 0)
         {
         {
-            Control* c = *it;
-            if (strcmp(id, c->getID()) == 0)
-            {
-                _controls.erase(it);
-            }
+            _controls.erase(it);
+            return;
         }
         }
     }
     }
+}
 
 
-    void Container::removeControl(Control* control)
+void Container::removeControl(Control* control)
+{
+    GP_ASSERT(control);
+    std::vector<Control*>::iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
     {
     {
-        std::vector<Control*>::iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
+        if (*it == control)
         {
         {
-            if (*it == control)
-            {
-                _controls.erase(it);
-            }
+            _controls.erase(it);
+            return;
         }
         }
     }
     }
+}
 
 
-    Control* Container::getControl(unsigned int index) const
-    {
-        std::vector<Control*>::const_iterator it = _controls.begin() + index;
-        return *it;
-    }
+Control* Container::getControl(unsigned int index) const
+{
+    std::vector<Control*>::const_iterator it = _controls.begin() + index;
+    return *it;
+}
 
 
-    Control* Container::getControl(const char* id) const
+Control* Container::getControl(const char* id) const
+{
+    GP_ASSERT(id);
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
     {
     {
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
+        Control* c = *it;
+        GP_ASSERT(c);
+        if (strcmp(id, c->getID()) == 0)
         {
         {
-            Control* c = *it;
-            if (strcmp(id, c->getID()) == 0)
-            {
-                return c;
-            }
-            else if (c->isContainer())
+            return c;
+        }
+        else if (c->isContainer())
+        {
+            Control* cc = ((Container*)c)->getControl(id);
+            if (cc)
             {
             {
-                Control* cc = ((Container*)c)->getControl(id);
-                if (cc)
-                {
-                    return cc;
-                }
+                return cc;
             }
             }
         }
         }
-
-        return NULL;
     }
     }
 
 
-    std::vector<Control*> Container::getControls() const
-    {
-        return _controls;
-    }
+    return NULL;
+}
 
 
-    Animation* Container::getAnimation(const char* id) const
-    {
-        std::vector<Control*>::const_iterator itr = _controls.begin();
-        std::vector<Control*>::const_iterator end = _controls.end();
+const std::vector<Control*>& Container::getControls() const
+{
+    return _controls;
+}
+
+Animation* Container::getAnimation(const char* id) const
+{
+    std::vector<Control*>::const_iterator itr = _controls.begin();
+    std::vector<Control*>::const_iterator end = _controls.end();
         
         
-        Control* control = NULL;
-        for (; itr != end; itr++)
+    Control* control = NULL;
+    for (; itr != end; itr++)
+    {
+        control = *itr;
+        GP_ASSERT(control);
+        Animation* animation = control->getAnimation(id);
+        if (animation)
+            return animation;
+
+        if (control->isContainer())
         {
         {
-            control = *itr;
-            Animation* animation = control->getAnimation(id);
+            animation = ((Container*)control)->getAnimation(id);
             if (animation)
             if (animation)
                 return animation;
                 return animation;
-
-            if (control->isContainer())
-            {
-                animation = ((Container*)control)->getAnimation(id);
-                if (animation)
-                    return animation;
-            }
         }
         }
-
-        return NULL;
     }
     }
 
 
-    void Container::update(const Rectangle& clip)
-    {
-        // Update this container's viewport.
-        Control::update(clip);
+    return NULL;
+}
 
 
-        _layout->update(this);
-    }
+void Container::update(const Rectangle& clip, const Vector2& offset)
+{
+    // Update this container's viewport.
+    Control::update(clip, offset);
 
 
-    void Container::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
-    {
-        // First draw our own border.
-        Control::drawBorder(spriteBatch, clip);
+    GP_ASSERT(_layout);
+    _layout->update(this);
+}
 
 
-        // Now call drawBorder on all controls within this container.
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
-        {
-            Control* control = *it;
-            control->drawBorder(spriteBatch, _clip);
-        }
+void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+{
+    if (_skin && needsClear)
+    {
+        GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
+        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        float clearY = targetHeight - _clearBounds.y - _clearBounds.height;
+        GL_ASSERT( glScissor(_clearBounds.x, clearY,
+            _clearBounds.width, _clearBounds.height) );
+        GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
+        GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
+
+        needsClear = false;
     }
     }
 
 
-    void Container::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
+    Control::drawBorder(spriteBatch, clip);
+
+    std::vector<Control*>::const_iterator it;
+    Rectangle boundsUnion = Rectangle::empty();
+    for (it = _controls.begin(); it < _controls.end(); it++)
     {
     {
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         {
         {
-            Control* control = *it;
-            control->drawImages(spriteBatch, _clip);
+            control->draw(spriteBatch, _viewportClipBounds, needsClear, targetHeight);
+            Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
         }
         }
-
-        _dirty = false;
     }
     }
 
 
-    void Container::drawText(const Rectangle& clip)
-    {
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
-        {
-            Control* control = *it;
-            control->drawText(_clip);
-        }
+    _dirty = false;
+}
 
 
-        _dirty = false;
+bool Container::isDirty()
+{
+    if (_dirty)
+    {
+        return true;
     }
     }
-
-    bool Container::isDirty()
+    else
     {
     {
-        if (_dirty)
-        {
-            return true;
-        }
-        else
+        std::vector<Control*>::const_iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
         {
         {
-            std::vector<Control*>::const_iterator it;
-            for (it = _controls.begin(); it < _controls.end(); it++)
+            GP_ASSERT(*it);
+            if ((*it)->isDirty())
             {
             {
-                if ((*it)->isDirty())
-                {
-                    return true;
-                }
+                return true;
             }
             }
         }
         }
-
-        return false;
     }
     }
 
 
-    bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    return false;
+}
+
+bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!isEnabled())
     {
     {
-        if (!isEnabled())
-        {
-            return false;
-        }
+        return false;
+    }
 
 
-        bool eventConsumed = false;
+    bool eventConsumed = false;
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+    float xPos = border.left + padding.left;
+    float yPos = border.top + padding.top;
 
 
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
-        float xPos = border.left + padding.left;
-        float yPos = border.top + padding.top;
+    Vector2* offset = NULL;
+    if (_layout->getType() == Layout::LAYOUT_SCROLL)
+    {
+        offset = &((ScrollLayout*)_layout)->_scrollPosition;
+    }
 
 
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (!control->isEnabled())
         {
         {
-            Control* control = *it;
-            if (!control->isEnabled())
-            {
-                continue;
-            }
-
-            const Rectangle& bounds = control->getClipBounds();
-            if (control->getState() != Control::NORMAL ||
-                (evt == Touch::TOUCH_PRESS &&
-                 x >= xPos + bounds.x &&
-                 x <= xPos + bounds.x + bounds.width &&
-                 y >= yPos + bounds.y &&
-                 y <= yPos + bounds.y + bounds.height))
-            {
-                // Pass on the event's clip relative to the control.
-                eventConsumed |= control->touchEvent(evt, x - xPos - bounds.x, y - yPos - bounds.y, contactIndex);
-            }
+            continue;
         }
         }
 
 
-        if (!isEnabled())
+        const Rectangle& bounds = control->getBounds();
+        float boundsX = bounds.x;
+        float boundsY = bounds.y;
+        if (offset)
         {
         {
-            return (_consumeTouchEvents | eventConsumed);
+            boundsX += offset->x;
+            boundsY += offset->y;
         }
         }
 
 
-        switch (evt)
+        if (control->getState() != Control::NORMAL ||
+            (evt == Touch::TOUCH_PRESS &&
+                x >= xPos + boundsX &&
+                x <= xPos + boundsX + bounds.width &&
+                y >= yPos + boundsY &&
+                y <= yPos + boundsY + bounds.height))
         {
         {
-        case Touch::TOUCH_PRESS:
-            setState(Control::FOCUS);
-            break;
-        case Touch::TOUCH_RELEASE:
-            setState(Control::NORMAL);
-            break;
+            // Pass on the event's clip relative to the control.
+            eventConsumed |= control->touchEvent(evt, x - xPos - boundsX, y - yPos - boundsY, contactIndex);
         }
         }
-
-        return (_consumeTouchEvents | eventConsumed);
     }
     }
 
 
-    void Container::keyEvent(Keyboard::KeyEvent evt, int key)
+    if (!isEnabled())
     {
     {
-        std::vector<Control*>::const_iterator it;
-        for (it = _controls.begin(); it < _controls.end(); it++)
-        {
-            Control* control = *it;
-            if (!control->isEnabled())
-            {
-                continue;
-            }
-
-            if (control->isContainer() || control->getState() == Control::FOCUS)
-            {
-                control->keyEvent(evt, key);
-            }
-        }
+        return (_consumeTouchEvents | eventConsumed);
     }
     }
 
 
-    bool Container::isContainer()
+    switch (evt)
     {
     {
-        return true;
+    case Touch::TOUCH_PRESS:
+        setState(Control::FOCUS);
+        break;
+    case Touch::TOUCH_RELEASE:
+        setState(Control::NORMAL);
+        break;
     }
     }
 
 
-    Layout::Type Container::getLayoutType(const char* layoutString)
+    if (!eventConsumed)
     {
     {
-        if (!layoutString)
+        // Pass the event on to the layout.
+        if (_layout->touchEvent(evt, x - xPos, y - yPos, contactIndex))
         {
         {
-            return Layout::LAYOUT_ABSOLUTE;
+            _dirty = true;
         }
         }
+    }
 
 
-        std::string layoutName(layoutString);
-        std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
-        if (layoutName == "LAYOUT_ABSOLUTE")
-        {
-            return Layout::LAYOUT_ABSOLUTE;
-        }
-        else if (layoutName == "LAYOUT_VERTICAL")
-        {
-            return Layout::LAYOUT_VERTICAL;
-        }
-        else if (layoutName == "LAYOUT_FLOW")
+    return (_consumeTouchEvents | eventConsumed);
+}
+
+void Container::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (!control->isEnabled())
         {
         {
-            return Layout::LAYOUT_FLOW;
+            continue;
         }
         }
-        else
+
+        if (control->isContainer() || control->getState() == Control::FOCUS)
         {
         {
-            // Default.
-            return Layout::LAYOUT_ABSOLUTE;
+            control->keyEvent(evt, key);
         }
         }
     }
     }
 }
 }
+
+bool Container::isContainer()
+{
+    return true;
+}
+
+Layout::Type Container::getLayoutType(const char* layoutString)
+{
+    if (!layoutString)
+    {
+        return Layout::LAYOUT_ABSOLUTE;
+    }
+
+    std::string layoutName(layoutString);
+    std::transform(layoutName.begin(), layoutName.end(), layoutName.begin(), (int(*)(int))toupper);
+    if (layoutName == "LAYOUT_ABSOLUTE")
+    {
+        return Layout::LAYOUT_ABSOLUTE;
+    }
+    else if (layoutName == "LAYOUT_VERTICAL")
+    {
+        return Layout::LAYOUT_VERTICAL;
+    }
+    else if (layoutName == "LAYOUT_FLOW")
+    {
+        return Layout::LAYOUT_FLOW;
+    }
+    else if (layoutName == "LAYOUT_SCROLL")
+    {
+        return Layout::LAYOUT_SCROLL;
+    }
+    else
+    {
+        // Default.
+        return Layout::LAYOUT_ABSOLUTE;
+    }
+}
+
+}

+ 7 - 5
gameplay/src/Container.h

@@ -112,7 +112,7 @@ public:
      *
      *
      * @return The vector of the controls within this container.
      * @return The vector of the controls within this container.
      */
      */
-    std::vector<Control*> getControls() const;
+    const std::vector<Control*>& getControls() const;
 
 
     /**
     /**
      * Gets the first animation in the control with the specified ID.
      * Gets the first animation in the control with the specified ID.
@@ -158,7 +158,7 @@ protected:
      *
      *
      * @param clip The clipping rectangle of this container's parent container.
      * @param clip The clipping rectangle of this container's parent container.
      */
      */
-    virtual void update(const Rectangle& clip);
+    virtual void update(const Rectangle& clip, const Vector2& offset);
 
 
     /**
     /**
      * Draws the themed border and background of this container and all its controls.
      * Draws the themed border and background of this container and all its controls.
@@ -166,7 +166,7 @@ protected:
      * @param spriteBatch The sprite batch containing this container's border images.
      * @param spriteBatch The sprite batch containing this container's border images.
      * @param clip The clipping rectangle of this container's parent container.
      * @param clip The clipping rectangle of this container's parent container.
      */
      */
-    void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+    //void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
 
 
     /**
     /**
      * Draws the icons of all controls within this container.
      * Draws the icons of all controls within this container.
@@ -174,14 +174,14 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this container's parent container.
      * @param clip The clipping rectangle of this container's parent container.
      */
      */
-    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+    //virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
 
     /**
     /**
      * Draws the text of all controls within this container.
      * Draws the text of all controls within this container.
      *
      *
      * @param clip The clipping rectangle of this container's parent container.
      * @param clip The clipping rectangle of this container's parent container.
      */
      */
-    virtual void drawText(const Rectangle& clip);
+    //virtual void drawText(const Rectangle& clip);
 
 
     /**
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
      * Touch callback on touch events.  Controls return true if they consume the touch event.
@@ -244,6 +244,8 @@ protected:
 private:
 private:
 
 
     Container(const Container& copy);
     Container(const Container& copy);
+
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
 };
 };
 
 
 }
 }

+ 999 - 835
gameplay/src/Control.cpp

@@ -4,1116 +4,1280 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    Control::Control()
-        : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _clip(Rectangle::empty()),
-            _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
-    {
-    }
 
 
-    Control::Control(const Control& copy)
-    {
-    }
+Control::Control()
+    : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
+        _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
+{
+}
+
+Control::Control(const Control& copy)
+{
+}
 
 
-    Control::~Control()
+Control::~Control()
+{
+    if (_listeners)
     {
     {
-        if (_listeners)
+        for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
         {
         {
-            for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
-            {
-                std::list<Listener*>* list = itr->second;
-                SAFE_DELETE(list);
-            }
-            SAFE_DELETE(_listeners);
+            std::list<Listener*>* list = itr->second;
+            SAFE_DELETE(list);
         }
         }
+        SAFE_DELETE(_listeners);
+    }
 
 
-        if (_styleOverridden)
-        {
-            SAFE_DELETE(_style);
-        }
+    if (_styleOverridden)
+    {
+        SAFE_DELETE(_style);
     }
     }
+}
+
+void Control::initialize(Theme::Style* style, Properties* properties)
+{
+    GP_ASSERT(properties);
+    _style = style;
+
+    // Properties not defined by the style.
+    _alignment = getAlignment(properties->getString("alignment"));
+    _autoWidth = properties->getBool("autoWidth");
+    _autoHeight = properties->getBool("autoHeight");
 
 
-    void Control::initialize(Theme::Style* style, Properties* properties)
+    Vector2 position;
+    Vector2 size;
+    if (properties->exists("position"))
+    {
+        properties->getVector2("position", &position);
+    }
+    else
+    {
+        position.x = properties->getFloat("x");
+        position.y = properties->getFloat("y");
+    }
+        
+    if (properties->exists("size"))
+    {
+        properties->getVector2("size", &size);
+    }
+    else
     {
     {
-        _style = style;
+        size.x = properties->getFloat("width");
+        size.y = properties->getFloat("height");
+    }
+    setBounds(Rectangle(position.x, position.y, size.x, size.y));
 
 
-        // Properties not defined by the style.
-        _alignment = getAlignment(properties->getString("alignment"));
-        _autoWidth = properties->getBool("autoWidth");
-        _autoHeight = properties->getBool("autoHeight");
+    _state = Control::getState(properties->getString("state"));
 
 
-        Vector2 position;
-        Vector2 size;
-        if (properties->exists("position"))
+    const char* id = properties->getId();
+    if (id)
+        _id = id;
+
+    // Potentially override themed properties for all states.
+    overrideThemedProperties(properties, STATE_ALL);
+
+    // Override themed properties on specific states.
+    Properties* innerSpace = properties->getNextNamespace();
+    while (innerSpace != NULL)
+    {
+        std::string spaceName(innerSpace->getNamespace());
+        std::transform(spaceName.begin(), spaceName.end(), spaceName.begin(), (int(*)(int))toupper);
+        if (spaceName == "STATENORMAL")
         {
         {
-            properties->getVector2("position", &position);
+            overrideThemedProperties(innerSpace, NORMAL);
         }
         }
-        else
+        else if (spaceName == "STATEFOCUS")
         {
         {
-            position.x = properties->getFloat("x");
-            position.y = properties->getFloat("y");
+            overrideThemedProperties(innerSpace, FOCUS);
         }
         }
-        
-        if (properties->exists("size"))
+        else if (spaceName == "STATEACTIVE")
         {
         {
-            properties->getVector2("size", &size);
+            overrideThemedProperties(innerSpace, ACTIVE);
         }
         }
-        else
+        else if (spaceName == "STATEDISABLED")
         {
         {
-            size.x = properties->getFloat("width");
-            size.y = properties->getFloat("height");
+            overrideThemedProperties(innerSpace, DISABLED);
         }
         }
-        _bounds.set(position.x, position.y, size.x, size.y);
-
-        _state = Control::getState(properties->getString("state"));
-
-        const char* id = properties->getId();
-        if (id)
-            _id = id;
-
-        // Potentially override themed properties for all states.
-        overrideThemedProperties(properties, STATE_ALL);
-
-        // Override themed properties on specific states.
-        Properties* stateSpace = properties->getNextNamespace();
-        while (stateSpace != NULL)
+        else if (spaceName == "MARGIN")
         {
         {
-            std::string stateName(stateSpace->getNamespace());
-            std::transform(stateName.begin(), stateName.end(), stateName.begin(), (int(*)(int))toupper);
-            if (stateName == "STATENORMAL")
-            {
-                overrideThemedProperties(stateSpace, NORMAL);
-            }
-            else if (stateName == "STATEFOCUS")
-            {
-                overrideThemedProperties(stateSpace, FOCUS);
-            }
-            else if (stateName == "STATEACTIVE")
-            {
-                overrideThemedProperties(stateSpace, ACTIVE);
-            }
-            else if (stateName == "STATEDISABLED")
-            {
-                overrideThemedProperties(stateSpace, DISABLED);
-            }
-
-            stateSpace = properties->getNextNamespace();
+            setMargin(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
+                innerSpace->getFloat("left"), innerSpace->getFloat("right"));
+        }
+        else if (spaceName == "PADDING")
+        {
+            setPadding(innerSpace->getFloat("top"), innerSpace->getFloat("bottom"),
+                innerSpace->getFloat("left"), innerSpace->getFloat("right"));
         }
         }
-    }
 
 
-    const char* Control::getID() const
-    {
-        return _id.c_str();
+        innerSpace = properties->getNextNamespace();
     }
     }
+}
+
+const char* Control::getID() const
+{
+    return _id.c_str();
+}
 
 
-    void Control::setPosition(float x, float y)
+void Control::setPosition(float x, float y)
+{
+    if (x != _bounds.x || y != _bounds.y)
     {
     {
         _bounds.x = x;
         _bounds.x = x;
         _bounds.y = y;
         _bounds.y = y;
         _dirty = true;
         _dirty = true;
     }
     }
+}
 
 
-    void Control::setSize(float width, float height)
+void Control::setSize(float width, float height)
+{
+    if (width != _bounds.width || height != _bounds.height)
     {
     {
         _bounds.width = width;
         _bounds.width = width;
         _bounds.height = height;
         _bounds.height = height;
         _dirty = true;
         _dirty = true;
     }
     }
+}
 
 
-    void Control::setBounds(const Rectangle& bounds)
+void Control::setBounds(const Rectangle& bounds)
+{
+    if (bounds != _bounds)
     {
     {
         _bounds.set(bounds);
         _bounds.set(bounds);
+        _dirty = true;
     }
     }
+}
 
 
-    const Rectangle& Control::getBounds() const
-    {
-        return _bounds;
-    }
+const Rectangle& Control::getBounds() const
+{
+    return _bounds;
+}
 
 
-    float Control::getX() const
-    {
-        return _bounds.x;
-    }
+float Control::getX() const
+{
+    return _bounds.x;
+}
 
 
-    float Control::getY() const
-    {
-        return _bounds.y;
-    }
+float Control::getY() const
+{
+    return _bounds.y;
+}
 
 
-    float Control::getWidth() const
-    {
-        return _bounds.width;
-    }
+float Control::getWidth() const
+{
+    return _bounds.width;
+}
 
 
-    float Control::getHeight() const
-    {
-        return _bounds.height;
-    }
+float Control::getHeight() const
+{
+    return _bounds.height;
+}
 
 
-    void Control::setAlignment(Alignment alignment)
-    {
-        _alignment = alignment;
-    }
+void Control::setAlignment(Alignment alignment)
+{
+    _alignment = alignment;
+}
 
 
-    Control::Alignment Control::getAlignment() const
-    {
-        return _alignment;
-    }
+Control::Alignment Control::getAlignment() const
+{
+    return _alignment;
+}
 
 
-    void Control::setAutoWidth(bool autoWidth)
+void Control::setAutoWidth(bool autoWidth)
+{
+    if (_autoWidth != autoWidth)
     {
     {
         _autoWidth = autoWidth;
         _autoWidth = autoWidth;
+        _dirty = true;
     }
     }
+}
 
 
-    bool Control::getAutoWidth() const
-    {
-        return _autoWidth;
-    }
+bool Control::getAutoWidth() const
+{
+    return _autoWidth;
+}
 
 
-    void Control::setAutoHeight(bool autoHeight)
+void Control::setAutoHeight(bool autoHeight)
+{
+    if (_autoHeight != autoHeight)
     {
     {
         _autoHeight = autoHeight;
         _autoHeight = autoHeight;
-    }
-
-    void Control::setOpacity(float opacity, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
-
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setOpacity(opacity);
-        }
-        
         _dirty = true;
         _dirty = true;
     }
     }
+}
 
 
-    float Control::getOpacity(State state) const
-    {
-        return getOverlay(state)->getOpacity();
-    }
+void Control::setOpacity(float opacity, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+        overlays[i]->setOpacity(opacity);
+    }
+        
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setBorder(top, bottom, left, right);
-        }
+float Control::getOpacity(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getOpacity();
+}
 
 
-        _dirty = true;
-    }
+void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Theme::Border& Control::getBorder(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getBorder();
+        overlays[i]->setBorder(top, bottom, left, right);
     }
     }
 
 
-    void Control::setSkinRegion(const Rectangle& region, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
-        }
+const Theme::Border& Control::getBorder(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getBorder();
+}
 
 
-        _dirty = true;
-    }
+void Control::setSkinRegion(const Rectangle& region, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Rectangle& Control::getSkinRegion(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getSkinRegion();
+        overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
     }
     }
 
 
-    const Theme::UVs& Control::getSkinUVs(Theme::Skin::SkinArea area, State state) const
-    {
-        return getOverlay(state)->getSkinUVs(area);
-    }
+    _dirty = true;
+}
 
 
-    void Control::setSkinColor(const Vector4& color, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+const Rectangle& Control::getSkinRegion(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getSkinRegion();
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setSkinColor(color);
-        }
+const Theme::UVs& Control::getSkinUVs(Theme::Skin::SkinArea area, State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getSkinUVs(area);
+}
 
 
-        _dirty = true;
-    }
+void Control::setSkinColor(const Vector4& color, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Vector4& Control::getSkinColor(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getSkinColor();
+        overlays[i]->setSkinColor(color);
     }
     }
 
 
-    void Control::setMargin(float top, float bottom, float left, float right)
-    {
-        _style->setMargin(top, bottom, left, right);
-        _dirty = true;
-    }
+    _dirty = true;
+}
 
 
-    const Theme::Margin& Control::getMargin() const
-    {
-        return _style->getMargin();
-    }
+const Vector4& Control::getSkinColor(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getSkinColor();
+}
 
 
-    void Control::setPadding(float top, float bottom, float left, float right)
-    {
-        _style->setPadding(top, bottom, left, right);
-        _dirty = true;
-    }
-    
-    const Theme::Padding& Control::getPadding() const
-    {
-        return _style->getPadding();
-    }
+void Control::setMargin(float top, float bottom, float left, float right)
+{
+    GP_ASSERT(_style);
+    overrideStyle();
+    _style->setMargin(top, bottom, left, right);
+    _dirty = true;
+}
 
 
-    void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+const Theme::Margin& Control::getMargin() const
+{
+    GP_ASSERT(_style);
+    return _style->getMargin();
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
-        }
+void Control::setPadding(float top, float bottom, float left, float right)
+{
+    GP_ASSERT(_style);
+    overrideStyle();
+    _style->setPadding(top, bottom, left, right);
+    _dirty = true;
+}
+    
+const Theme::Padding& Control::getPadding() const
+{
+    GP_ASSERT(_style);
+    return _style->getPadding();
+}
 
 
-        _dirty = true;
-    }
+void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Rectangle& Control::getImageRegion(const char* id, State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getImageRegion(id);
+        overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
     }
     }
 
 
-    void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setImageColor(id, color);
-        }
+const Rectangle& Control::getImageRegion(const char* id, State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getImageRegion(id);
+}
 
 
-        _dirty = true;
-    }
+void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Vector4& Control::getImageColor(const char* id, State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getImageColor(id);
+        overlays[i]->setImageColor(id, color);
     }
     }
 
 
-    const Theme::UVs& Control::getImageUVs(const char* id, State state) const
-    {
-        return getOverlay(state)->getImageUVs(id);
-    }
+    _dirty = true;
+}
 
 
-    void Control::setCursorRegion(const Rectangle& region, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+const Vector4& Control::getImageColor(const char* id, State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getImageColor(id);
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
-        }
+const Theme::UVs& Control::getImageUVs(const char* id, State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getImageUVs(id);
+}
 
 
-        _dirty = true;
-    }
+void Control::setCursorRegion(const Rectangle& region, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Rectangle& Control::getCursorRegion(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getCursorRegion();
+        overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
     }
     }
 
 
-    void Control::setCursorColor(const Vector4& color, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setCursorColor(color);
-        }
+const Rectangle& Control::getCursorRegion(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getCursorRegion();
+}
 
 
-        _dirty = true;
-    }
+void Control::setCursorColor(const Vector4& color, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Vector4& Control::getCursorColor(State state)
-    {
-        return getOverlay(state)->getCursorColor();
-    }
-    
-    const Theme::UVs& Control::getCursorUVs(State state)
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getCursorUVs();
+        overlays[i]->setCursorColor(color);
     }
     }
 
 
-    void Control::setFont(Font* font, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setFont(font);
-        }
+const Vector4& Control::getCursorColor(State state)
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getCursorColor();
+}
+    
+const Theme::UVs& Control::getCursorUVs(State state)
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getCursorUVs();
+}
 
 
-        _dirty = true;
-    }
+void Control::setFont(Font* font, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    Font* Control::getFont(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getFont();
+        overlays[i]->setFont(font);
     }
     }
 
 
-    void Control::setFontSize(unsigned int fontSize, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setFontSize(fontSize);
-        }
+Font* Control::getFont(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getFont();
+}
 
 
-        _dirty = true;
-    }
+void Control::setFontSize(unsigned int fontSize, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    unsigned int Control::getFontSize(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getFontSize();
+        overlays[i]->setFontSize(fontSize);
     }
     }
 
 
-    void Control::setTextColor(const Vector4& color, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setTextColor(color);
-        }
+unsigned int Control::getFontSize(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getFontSize();
+}
 
 
-        _dirty = true;
-    }
+void Control::setTextColor(const Vector4& color, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Vector4& Control::getTextColor(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getTextColor();
+        overlays[i]->setTextColor(color);
     }
     }
 
 
-    void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setTextAlignment(alignment);
-        }
+const Vector4& Control::getTextColor(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getTextColor();
+}
 
 
-        _dirty = true;
-    }
+void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    Font::Justify Control::getTextAlignment(State state) const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return getOverlay(state)->getTextAlignment();
+        overlays[i]->setTextAlignment(alignment);
     }
     }
 
 
-    void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
-
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setTextRightToLeft(rightToLeft);
-        }
+    _dirty = true;
+}
 
 
-        _dirty = true;
-    }
+Font::Justify Control::getTextAlignment(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getTextAlignment();
+}
 
 
-    bool Control::getTextRightToLeft(State state) const
-    {
-        return getOverlay(state)->getTextRightToLeft();
-    }
+void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-    const Rectangle& Control::getClipBounds() const
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
     {
     {
-        return _clipBounds;
+        overlays[i]->setTextRightToLeft(rightToLeft);
     }
     }
 
 
-    const Rectangle& Control::getClip() const
-    {
-        return _clip;
-    }
+    _dirty = true;
+}
 
 
-    void Control::setStyle(Theme::Style* style)
-    {
-        if (style != _style)
-        {
-            _dirty = true;
-        }
+bool Control::getTextRightToLeft(State state) const
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getTextRightToLeft();
+}
 
 
-        _style = style;
-    }
+const Rectangle& Control::getClipBounds() const
+{
+    return _clipBounds;
+}
+
+const Rectangle& Control::getClip() const
+{
+    return _viewportClipBounds;
+}
 
 
-    Theme::Style* Control::getStyle() const
+void Control::setStyle(Theme::Style* style)
+{
+    if (style != _style)
     {
     {
-        return _style;
+        _dirty = true;
     }
     }
 
 
-    void Control::setState(State state)
-    {
-        _state = state;
+    _style = style;
+}
+
+Theme::Style* Control::getStyle() const
+{
+    return _style;
+}
+
+void Control::setState(State state)
+{
+    if (getOverlay(_state) != getOverlay(state))
         _dirty = true;
         _dirty = true;
+
+    _state = state;
+}
+
+Control::State Control::getState() const
+{
+    return _state;
+}
+
+void Control::disable()
+{
+    _state = DISABLED;
+    _dirty = true;
+}
+
+void Control::enable()
+{
+    _state = NORMAL;
+    _dirty = true;
+}
+
+bool Control::isEnabled()
+{
+    return _state != DISABLED;
+}
+
+Theme::Style::OverlayType Control::getOverlayType() const
+{
+    switch (_state)
+    {
+    case Control::NORMAL:
+        return Theme::Style::OVERLAY_NORMAL;
+    case Control::FOCUS:
+        return Theme::Style::OVERLAY_FOCUS;
+    case Control::ACTIVE:
+        return Theme::Style::OVERLAY_ACTIVE;
+    case Control::DISABLED:
+        return Theme::Style::OVERLAY_DISABLED;
+    default:
+        return Theme::Style::OVERLAY_NORMAL;
     }
     }
+}
+
+void Control::setConsumeTouchEvents(bool consume)
+{
+    _consumeTouchEvents = consume;
+}
+    
+bool Control::getConsumeTouchEvents()
+{
+    return _consumeTouchEvents;
+}
+
+void Control::addListener(Control::Listener* listener, int eventFlags)
+{
+    GP_ASSERT(listener);
 
 
-    Control::State Control::getState() const
+    if ((eventFlags & Listener::PRESS) == Listener::PRESS)
     {
     {
-        return _state;
+        addSpecificListener(listener, Listener::PRESS);
     }
     }
 
 
-    void Control::disable()
+    if ((eventFlags & Listener::RELEASE) == Listener::RELEASE)
     {
     {
-        _state = DISABLED;
-        _dirty = true;
+        addSpecificListener(listener, Listener::RELEASE);
     }
     }
 
 
-    void Control::enable()
+    if ((eventFlags & Listener::CLICK) == Listener::CLICK)
     {
     {
-        _state = NORMAL;
-        _dirty = true;
+        addSpecificListener(listener, Listener::CLICK);
     }
     }
 
 
-    bool Control::isEnabled()
+    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
     {
     {
-        return _state != DISABLED;
+        addSpecificListener(listener, Listener::VALUE_CHANGED);
     }
     }
 
 
-    Theme::Style::OverlayType Control::getOverlayType() const
+    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     {
     {
-        switch (_state)
-        {
-        case Control::NORMAL:
-            return Theme::Style::OVERLAY_NORMAL;
-        case Control::FOCUS:
-            return Theme::Style::OVERLAY_FOCUS;
-        case Control::ACTIVE:
-            return Theme::Style::OVERLAY_ACTIVE;
-        case Control::DISABLED:
-            return Theme::Style::OVERLAY_DISABLED;
-        default:
-            return Theme::Style::OVERLAY_NORMAL;
-        }
+        addSpecificListener(listener, Listener::TEXT_CHANGED);
     }
     }
+}
+
+void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
+{
+    GP_ASSERT(listener);
 
 
-    void Control::setConsumeTouchEvents(bool consume)
+    if (!_listeners)
     {
     {
-        _consumeTouchEvents = consume;
+        _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
     }
     }
-    
-    bool Control::getConsumeTouchEvents()
+
+    std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
+    if (itr == _listeners->end())
     {
     {
-        return _consumeTouchEvents;
+        _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
+        itr = _listeners->find(eventType);
     }
     }
 
 
-    void Control::addListener(Control::Listener* listener, int eventFlags)
+    std::list<Listener*>* listenerList = itr->second;
+    listenerList->push_back(listener);
+}
+
+bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    if (!isEnabled())
     {
     {
-        if ((eventFlags & Listener::PRESS) == Listener::PRESS)
-        {
-            addSpecificListener(listener, Listener::PRESS);
-        }
+        return false;
+    }
 
 
-        if ((eventFlags & Listener::RELEASE) == Listener::RELEASE)
-        {
-            addSpecificListener(listener, Listener::RELEASE);
-        }
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS:
+        notifyListeners(Listener::PRESS);
+        break;
+            
+    case Touch::TOUCH_RELEASE:
+        // Always trigger Listener::RELEASE
+        notifyListeners(Listener::RELEASE);
 
 
-        if ((eventFlags & Listener::CLICK) == Listener::CLICK)
+        // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
+        if (x > 0 && x <= _bounds.width &&
+            y > 0 && y <= _bounds.height)
         {
         {
-            addSpecificListener(listener, Listener::CLICK);
+            notifyListeners(Listener::CLICK);
         }
         }
+        break;
+    }
 
 
-        if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
-        {
-            addSpecificListener(listener, Listener::VALUE_CHANGED);
-        }
+    return _consumeTouchEvents;
+}
 
 
-        if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
-        {
-            addSpecificListener(listener, Listener::TEXT_CHANGED);
-        }
-    }
+void Control::keyEvent(Keyboard::KeyEvent evt, int key)
+{
+}
 
 
-    void Control::addSpecificListener(Control::Listener* listener, Listener::EventType eventType)
+void Control::notifyListeners(Listener::EventType eventType)
+{
+    if (_listeners)
     {
     {
-        if (!_listeners)
-        {
-            _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
-        }
-
         std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
         std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
-        if (itr == _listeners->end())
+        if (itr != _listeners->end())
         {
         {
-            _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
-            itr = _listeners->find(eventType);
+            std::list<Listener*>* listenerList = itr->second;
+            for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); listenerItr++)
+            {
+                GP_ASSERT(*listenerItr);
+                (*listenerItr)->controlEvent(this, eventType);
+            }
         }
         }
-
-        std::list<Listener*>* listenerList = itr->second;
-        listenerList->push_back(listener);
     }
     }
+}
 
 
-    bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-    {
-        if (!isEnabled())
-        {
-            return false;
-        }
+void Control::update(const Rectangle& clip, const Vector2& offset)
+{
+    _clearBounds.set(_absoluteClipBounds);
 
 
-        switch (evt)
-        {
-        case Touch::TOUCH_PRESS:
-            notifyListeners(Listener::PRESS);
-            break;
-            
-        case Touch::TOUCH_RELEASE:
-            // Always trigger Listener::RELEASE
-            notifyListeners(Listener::RELEASE);
+    // Calculate the clipped bounds.
+    float x = _bounds.x + offset.x;
+    float y = _bounds.y + offset.y;
+    float width = _bounds.width;
+    float height = _bounds.height;
 
 
-            // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
-            if (x > 0 && x <= _clipBounds.width &&
-                y > 0 && y <= _clipBounds.height)
-            {
-                notifyListeners(Listener::CLICK);
-            }
-            break;
-        }
+    float clipX2 = clip.x + clip.width;
+    float x2 = clip.x + x + width;
+    if (x2 > clipX2)
+        width -= x2 - clipX2;
 
 
-        return _consumeTouchEvents;
-    }
+    float clipY2 = clip.y + clip.height;
+    float y2 = clip.y + y + height;
+    if (y2 > clipY2)
+        height -= y2 - clipY2;
 
 
-    void Control::keyEvent(Keyboard::KeyEvent evt, int key)
+    if (x < 0)
     {
     {
+        width += x;
+        x = -x;
     }
     }
-
-    void Control::notifyListeners(Listener::EventType eventType)
+    else
     {
     {
-        if (_listeners)
-        {
-            std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
-            if (itr != _listeners->end())
-            {
-                std::list<Listener*>* listenerList = itr->second;
-                for (std::list<Listener*>::iterator listenerItr = listenerList->begin(); listenerItr != listenerList->end(); listenerItr++)
-                {
-                    (*listenerItr)->controlEvent(this, eventType);
-                }
-            }
-        }
+        x = 0;
     }
     }
 
 
-    void Control::update(const Rectangle& clip)
+    if (y < 0)
+    {
+        height += y;
+        y = -y;
+    }
+    else
     {
     {
-        // Calculate the bounds.
-        float x = clip.x + _bounds.x;
-        float y = clip.y + _bounds.y;
-        float width = _bounds.width;
-        float height = _bounds.height;
+        y = 0;
+    }
 
 
-        float clipX2 = clip.x + clip.width;
-        float x2 = x + width;
-        if (x2 > clipX2)
-            width = clipX2 - x;
+    _clipBounds.set(x, y, width, height);
 
 
-        float clipY2 = clip.y + clip.height;
-        float y2 = y + height;
-        if (y2 > clipY2)
-            height = clipY2 - y;
+    // Calculate the absolute bounds.
+    x = _bounds.x + offset.x + clip.x;
+    y = _bounds.y + offset.y + clip.y;
+    _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
 
-        _clipBounds.set(_bounds.x, _bounds.y, width, height);
+    // Calculate the absolute viewport bounds.
+    // Absolute bounds minus border and padding.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
 
 
-        // Calculate the clipping viewport.
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
+    x += border.left + padding.left;
+    y += border.top + padding.top;
+    width = _bounds.width - border.left - padding.left - border.right - padding.right;
+    height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
 
-        x +=  border.left + padding.left;
-        y +=  border.top + padding.top;
-        width = _bounds.width - border.left - padding.left - border.right - padding.right;
-        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
+    _viewportBounds.set(x, y, width, height);
 
 
-        _textBounds.set(x, y, width, height);
+    // Calculate the clip area.
+    // Absolute bounds, minus border and padding,
+    // clipped to the parent container's clip area.
+    clipX2 = clip.x + clip.width;
+    x2 = x + width;
+    if (x2 > clipX2)
+        width = clipX2 - x;
 
 
-        clipX2 = clip.x + clip.width;
-        x2 = x + width;
-        if (x2 > clipX2)
-            width = clipX2 - x;
+    clipY2 = clip.y + clip.height;
+    y2 = y + height;
+    if (y2 > clipY2)
+        height = clipY2 - y;
 
 
-        clipY2 = clip.y + clip.height;
-        y2 = y + height;
-        if (y2 > clipY2)
-            height = clipY2 - y;
+    if (x < clip.x)
+    {
+        float dx = clip.x - x;
+        width -= dx;
+        x = clip.x;
+    }
 
 
-        if (x < clip.x)
-            x = clip.x;
+    if (y < clip.y)
+    {
+        float dy = clip.y - y;
+        height -= dy;
+        y = clip.y;
+    }
+ 
+    _viewportClipBounds.set(x, y, width, height);
 
 
-        if (y < clip.y)
-            y = clip.y;
+    _absoluteClipBounds.set(x - border.left - padding.left, y - border.top - padding.top,
+        width + border.left + padding.left + border.right + padding.right,
+        height + border.top + padding.top + border.bottom + padding.bottom);
+    if (_clearBounds.isEmpty())
+    {
+        _clearBounds.set(_absoluteClipBounds);
+    }
 
 
-        _clip.set(x, y, width, height);
+    // Cache themed attributes for performance.
+    _skin = getSkin(_state);
+    _opacity = getOpacity(_state);
+}
 
 
-        _skin = getSkin(_state);
-        _opacity = getOpacity(_state);
+void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
+{
+    if (!spriteBatch || !_skin || _bounds.width <= 0 || _bounds.height <= 0)
+        return;
+
+    // Get the border and background images for this control's current state.
+    const Theme::UVs& topLeft = _skin->getUVs(Theme::Skin::TOP_LEFT);
+    const Theme::UVs& top = _skin->getUVs(Theme::Skin::TOP);
+    const Theme::UVs& topRight = _skin->getUVs(Theme::Skin::TOP_RIGHT);
+    const Theme::UVs& left = _skin->getUVs(Theme::Skin::LEFT);
+    const Theme::UVs& center = _skin->getUVs(Theme::Skin::CENTER);
+    const Theme::UVs& right = _skin->getUVs(Theme::Skin::RIGHT);
+    const Theme::UVs& bottomLeft = _skin->getUVs(Theme::Skin::BOTTOM_LEFT);
+    const Theme::UVs& bottom = _skin->getUVs(Theme::Skin::BOTTOM);
+    const Theme::UVs& bottomRight = _skin->getUVs(Theme::Skin::BOTTOM_RIGHT);
+
+    // Calculate screen-space positions.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+    Vector4 skinColor = _skin->getColor();
+    skinColor.w *= _opacity;
+
+    float midWidth = _bounds.width - border.left - border.right;
+    float midHeight = _bounds.height - border.top - border.bottom;
+    float midX = _absoluteBounds.x + border.left;
+    float midY = _absoluteBounds.y + border.top;
+    float rightX = _absoluteBounds.x + _bounds.width - border.right;
+    float bottomY = _absoluteBounds.y + _bounds.height - border.bottom;
+
+    // Draw themed border sprites.
+    if (!border.left && !border.right && !border.top && !border.bottom)
+    {
+        // No border, just draw the image.
+        spriteBatch->draw(_absoluteBounds.x, _absoluteBounds.y, _bounds.width, _bounds.height, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+    }
+    else
+    {
+        if (border.left && border.top)
+            spriteBatch->draw(_absoluteBounds.x, _absoluteBounds.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, skinColor, clip);
+        if (border.top)
+            spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, skinColor, clip);
+        if (border.right && border.top)
+            spriteBatch->draw(rightX, _absoluteBounds.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
+        if (border.left)
+            spriteBatch->draw(_absoluteBounds.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
+        if (border.left && border.right && border.top && border.bottom)
+            spriteBatch->draw(_absoluteBounds.x + border.left, _absoluteBounds.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
+                center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+        if (border.right)
+            spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
+        if (border.bottom && border.left)
+            spriteBatch->draw(_absoluteBounds.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, skinColor, clip);
+        if (border.bottom)
+            spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, skinColor, clip);
+        if (border.bottom && border.right)
+            spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, skinColor, clip);
     }
     }
+}
 
 
-    void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
-    {
-        if (!_skin || _bounds.width <= 0 || _bounds.height <= 0)
-            return;
+void Control::drawImages(SpriteBatch* spriteBatch, const Rectangle& position)
+{
+}
 
 
-        Vector2 pos(clip.x + _bounds.x, clip.y + _bounds.y);
+void Control::drawText(const Rectangle& position)
+{
+}
 
 
-        // Get the border and background images for this control's current state.
-        const Theme::UVs& topLeft = _skin->getUVs(Theme::Skin::TOP_LEFT);
-        const Theme::UVs& top = _skin->getUVs(Theme::Skin::TOP);
-        const Theme::UVs& topRight = _skin->getUVs(Theme::Skin::TOP_RIGHT);
-        const Theme::UVs& left = _skin->getUVs(Theme::Skin::LEFT);
-        const Theme::UVs& center = _skin->getUVs(Theme::Skin::CENTER);
-        const Theme::UVs& right = _skin->getUVs(Theme::Skin::RIGHT);
-        const Theme::UVs& bottomLeft = _skin->getUVs(Theme::Skin::BOTTOM_LEFT);
-        const Theme::UVs& bottom = _skin->getUVs(Theme::Skin::BOTTOM);
-        const Theme::UVs& bottomRight = _skin->getUVs(Theme::Skin::BOTTOM_RIGHT);
+void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+{
+    if (needsClear)
+    {
+        GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
+        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        GL_ASSERT( glScissor(_clearBounds.x, targetHeight - _clearBounds.y - _clearBounds.height,
+            _clearBounds.width, _clearBounds.height) );
+        GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
+        GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
+    }
 
 
-        // Calculate screen-space positions.
-        const Theme::Border& border = getBorder(_state);
-        const Theme::Padding& padding = getPadding();
-        Vector4 skinColor = _skin->getColor();
-        skinColor.w *= _opacity;
+    drawBorder(spriteBatch, clip);
+    drawImages(spriteBatch, clip);
+    drawText(clip);
+    _dirty = false;
+}
 
 
-        float midWidth = _bounds.width - border.left - border.right;
-        float midHeight = _bounds.height - border.top - border.bottom;
-        float midX = pos.x + border.left;
-        float midY = pos.y + border.top;
-        float rightX = pos.x + _bounds.width - border.right;
-        float bottomY = pos.y + _bounds.height - border.bottom;
+bool Control::isDirty()
+{
+    return _dirty;
+}
 
 
-        // Draw themed border sprites.
-        if (!border.left && !border.right && !border.top && !border.bottom)
-        {
-            // No border, just draw the image.
-            spriteBatch->draw(pos.x, pos.y, _bounds.width, _bounds.height, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
-        }
-        else
-        {
-            if (border.left && border.top)
-                spriteBatch->draw(pos.x, pos.y, border.left, border.top, topLeft.u1, topLeft.v1, topLeft.u2, topLeft.v2, skinColor, clip);
-            if (border.top)
-                spriteBatch->draw(pos.x + border.left, pos.y, midWidth, border.top, top.u1, top.v1, top.u2, top.v2, skinColor, clip);
-            if (border.right && border.top)
-                spriteBatch->draw(rightX, pos.y, border.right, border.top, topRight.u1, topRight.v1, topRight.u2, topRight.v2, skinColor, clip);
-            if (border.left)
-                spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
-            if (border.left && border.right && border.top && border.bottom)
-                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
-                    center.u1, center.v1, center.u2, center.v2, skinColor, clip);
-            if (border.right)
-                spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
-            if (border.bottom && border.left)
-                spriteBatch->draw(pos.x, bottomY, border.left, border.bottom, bottomLeft.u1, bottomLeft.v1, bottomLeft.u2, bottomLeft.v2, skinColor, clip);
-            if (border.bottom)
-                spriteBatch->draw(midX, bottomY, midWidth, border.bottom, bottom.u1, bottom.v1, bottom.u2, bottom.v2, skinColor, clip);
-            if (border.bottom && border.right)
-                spriteBatch->draw(rightX, bottomY, border.right, border.bottom, bottomRight.u1, bottomRight.v1, bottomRight.u2, bottomRight.v2, skinColor, clip);
-        }
-    }
+bool Control::isContainer()
+{
+    return false;
+}
 
 
-    void Control::drawImages(SpriteBatch* spriteBatch, const Rectangle& position)
+Control::State Control::getState(const char* state)
+{
+    if (!state)
     {
     {
+        return NORMAL;
     }
     }
 
 
-    void Control::drawText(const Rectangle& position)
+    if (strcmp(state, "NORMAL") == 0)
     {
     {
+        return NORMAL;
     }
     }
-
-    bool Control::isDirty()
+    else if (strcmp(state, "ACTIVE") == 0)
     {
     {
-        return _dirty;
+        return ACTIVE;
     }
     }
+    else if (strcmp(state, "FOCUS") == 0)
+    {
+        return FOCUS;
+    }
+    else if (strcmp(state, "DISABLED") == 0)
+    {
+        return DISABLED;
+    }
+
+    return NORMAL;
+}
+
+Theme::ThemeImage* Control::getImage(const char* id, State state)
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    Theme::ImageList* imageList = overlay->getImageList();
+    GP_ASSERT(imageList);
+    return imageList->getImage(id);
+}
 
 
-    bool Control::isContainer()
+// Implementation of AnimationHandler
+unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
+{
+    switch(propertyId)
     {
     {
-        return false;
+    case ANIMATE_POSITION:
+    case ANIMATE_SIZE:
+        return 2;
+
+    case ANIMATE_POSITION_X:
+    case ANIMATE_POSITION_Y:
+    case ANIMATE_SIZE_WIDTH:
+    case ANIMATE_SIZE_HEIGHT:
+    case ANIMATE_OPACITY:
+        return 1;
+
+    default:
+        return -1;
     }
     }
+}
+
+void Control::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+{
+    GP_ASSERT(value);
+
+    switch(propertyId)
+    {
+    case ANIMATE_POSITION:
+        value->setFloat(0, _bounds.x);
+        value->setFloat(1, _bounds.y);
+        break;
+    case ANIMATE_SIZE:
+        value->setFloat(0, _bounds.width);
+        value->setFloat(1, _bounds.height);
+        break;
+    case ANIMATE_POSITION_X:
+        value->setFloat(0, _bounds.x);
+        break;
+    case ANIMATE_POSITION_Y:
+        value->setFloat(0, _bounds.y);
+        break;
+    case ANIMATE_SIZE_WIDTH:
+        value->setFloat(0, _bounds.width);
+        break;
+    case ANIMATE_SIZE_HEIGHT:
+        value->setFloat(0, _bounds.height);
+        break;
+    case ANIMATE_OPACITY:
+    default:
+        break;
+    }
+}
 
 
-    Control::State Control::getState(const char* state)
+void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+{
+    GP_ASSERT(value);
+
+    switch(propertyId)
     {
     {
-        if (!state)
-        {
-            return NORMAL;
-        }
+    case ANIMATE_POSITION:
+        _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
+        _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(1));
+        _dirty = true;
+        break;
+    case ANIMATE_POSITION_X:
+        _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
+        _dirty = true;
+        break;
+    case ANIMATE_POSITION_Y:
+        _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(0));
+        _dirty = true;
+        break;
+    case ANIMATE_SIZE:
+        _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
+        _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(1));
+        _dirty = true;
+        break;
+    case ANIMATE_SIZE_WIDTH:
+        _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
+        _dirty = true;
+        break;
+    case ANIMATE_SIZE_HEIGHT:
+        _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(0));
+        _dirty = true;
+        break;
+    case ANIMATE_OPACITY:
+        _dirty = true;
+    default:
+        break;
+    }
+}
+    
 
 
-        if (strcmp(state, "NORMAL") == 0)
-        {
-            return NORMAL;
-        }
-        else if (strcmp(state, "ACTIVE") == 0)
-        {
-            return ACTIVE;
-        }
-        else if (strcmp(state, "FOCUS") == 0)
-        {
-            return FOCUS;
-        }
-        else if (strcmp(state, "DISABLED") == 0)
-        {
-            return DISABLED;
-        }
+Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays)
+{
+    GP_ASSERT(overlays);
+    GP_ASSERT(_style);
 
 
-        return NORMAL;
+    unsigned int index = 0;
+    if ((overlayTypes & NORMAL) == NORMAL)
+    {
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
     }
     }
 
 
-    Theme::ThemeImage* Control::getImage(const char* id, State state)
+    if ((overlayTypes & FOCUS) == FOCUS)
     {
     {
-        return getOverlay(state)->getImageList()->getImage(id);
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
     }
     }
 
 
-    // Implementation of AnimationHandler
-    unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
+    if ((overlayTypes & ACTIVE) == ACTIVE)
     {
     {
-        switch(propertyId)
-        {
-        case ANIMATE_POSITION:
-        case ANIMATE_SIZE:
-            return 2;
-
-        case ANIMATE_POSITION_X:
-        case ANIMATE_POSITION_Y:
-        case ANIMATE_SIZE_WIDTH:
-        case ANIMATE_SIZE_HEIGHT:
-        case ANIMATE_OPACITY:
-            return 1;
-
-        default:
-            return -1;
-        }
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
     }
     }
 
 
-    void Control::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+    if ((overlayTypes & DISABLED) == DISABLED)
     {
     {
-        switch(propertyId)
-        {
-        case ANIMATE_POSITION:
-            value->setFloat(0, _bounds.x);
-            value->setFloat(1, _bounds.y);
-            break;
-        case ANIMATE_SIZE:
-            value->setFloat(0, _bounds.width);
-            value->setFloat(1, _bounds.height);
-            break;
-        case ANIMATE_POSITION_X:
-            value->setFloat(0, _bounds.x);
-            break;
-        case ANIMATE_POSITION_Y:
-            value->setFloat(0, _bounds.y);
-            break;
-        case ANIMATE_SIZE_WIDTH:
-            value->setFloat(0, _bounds.width);
-            break;
-        case ANIMATE_SIZE_HEIGHT:
-            value->setFloat(0, _bounds.height);
-            break;
-        case ANIMATE_OPACITY:
-        default:
-            break;
-        }
+        overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
     }
     }
 
 
-    void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+    return overlays;
+}
+
+Theme::Style::Overlay* Control::getOverlay(State state) const
+{
+    GP_ASSERT(_style);
+
+    switch(state)
     {
     {
-        switch(propertyId)
-        {
-        case ANIMATE_POSITION:
-            _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
-            _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(1));
-            _dirty = true;
-            break;
-        case ANIMATE_POSITION_X:
-            _bounds.x = Curve::lerp(blendWeight, _bounds.x, value->getFloat(0));
-            _dirty = true;
-            break;
-        case ANIMATE_POSITION_Y:
-            _bounds.y = Curve::lerp(blendWeight, _bounds.y, value->getFloat(0));
-            _dirty = true;
-            break;
-        case ANIMATE_SIZE:
-            _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
-            _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(1));
-            _dirty = true;
-            break;
-        case ANIMATE_SIZE_WIDTH:
-            _bounds.width = Curve::lerp(blendWeight, _bounds.width, value->getFloat(0));
-            _dirty = true;
-            break;
-        case ANIMATE_SIZE_HEIGHT:
-            _bounds.height = Curve::lerp(blendWeight, _bounds.height, value->getFloat(0));
-            _dirty = true;
-            break;
-        case ANIMATE_OPACITY:
-            _dirty = true;
-        default:
-            break;
-        }
+    case Control::NORMAL:
+        return _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
+    case Control::FOCUS:
+        return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
+    case Control::ACTIVE:
+        return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
+    case Control::DISABLED:
+        return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
+    default:
+        return NULL;
     }
     }
-    
-    Theme::Style::Overlay** Control::getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays)
+}
+
+void Control::overrideStyle()
+{
+    if (_styleOverridden)
     {
     {
-        unsigned int index = 0;
-        if ((overlayTypes & NORMAL) == NORMAL)
-        {
-            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
-        }
+        return;
+    }
 
 
-        if ((overlayTypes & FOCUS) == FOCUS)
-        {
-            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
-        }
+    // Copy the style.
+    GP_ASSERT(_style);
+    _style = new Theme::Style(*_style);
+    _styleOverridden = true;
+}
 
 
-        if ((overlayTypes & ACTIVE) == ACTIVE)
-        {
-            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
-        }
+void Control::overrideThemedProperties(Properties* properties, unsigned char states)
+{
+    GP_ASSERT(properties);
+    GP_ASSERT(_style);
+    GP_ASSERT(_style->_theme);
 
 
-        if ((overlayTypes & DISABLED) == DISABLED)
-        {
-            overlays[index++] = _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
-        }
+    Theme::ImageList* imageList = NULL;
+    Theme::ThemeImage* cursor = NULL;
+    Theme::Skin* skin = NULL;
+    _style->_theme->lookUpSprites(properties, &imageList, &cursor, &skin);
 
 
-        return overlays;
+    if (imageList)
+    {
+        setImageList(imageList, states);
     }
     }
 
 
-    Theme::Style::Overlay* Control::getOverlay(State state) const
+    if (cursor)
     {
     {
-        switch(state)
-        {
-        case Control::NORMAL:
-            return _style->getOverlay(Theme::Style::OVERLAY_NORMAL);
-        case Control::FOCUS:
-            return _style->getOverlay(Theme::Style::OVERLAY_FOCUS);
-        case Control::ACTIVE:
-            return _style->getOverlay(Theme::Style::OVERLAY_ACTIVE);
-        case Control::DISABLED:
-            return _style->getOverlay(Theme::Style::OVERLAY_DISABLED);
-        default:
-            return NULL;
-        }
+        setCursor(cursor, states);
     }
     }
 
 
-    void Control::overrideStyle()
+    if (skin)
     {
     {
-        if (_styleOverridden)
-        {
-            return;
-        }
+        setSkin(skin, states);
+    }
 
 
-        // Copy the style.
-        WARN_VARG("%d", sizeof(Theme::Style::Overlay));
-        _style = new Theme::Style(*_style);
-        _styleOverridden = true;
+    if (properties->exists("font"))
+    {
+        Font* font = Font::create(properties->getString("font"));
+        setFont(font, states);
+        font->release();
     }
     }
 
 
-    void Control::overrideThemedProperties(Properties* properties, unsigned char states)
+    if (properties->exists("fontSize"))
     {
     {
-        Theme::ImageList* imageList = NULL;
-        Theme::ThemeImage* cursor = NULL;
-        Theme::Skin* skin = NULL;
-        _style->_theme->lookUpSprites(properties, &imageList, &cursor, &skin);
+        setFontSize(properties->getInt("fontSize"), states);
+    }
 
 
-        if (imageList)
-        {
-            setImageList(imageList, states);
-        }
+    if (properties->exists("textColor"))
+    {
+        Vector4 textColor(0, 0, 0, 1);
+        properties->getColor("textColor", &textColor);
+        setTextColor(textColor, states);
+    }
 
 
-        if (cursor)
-        {
-            setCursor(cursor, states);
-        }
+    if (properties->exists("textAlignment"))
+    {
+        setTextAlignment(Font::getJustify(properties->getString("textAlignment")), states);
+    }
 
 
-        if (skin)
-        {
-            setSkin(skin, states);
-        }
+    if (properties->exists("rightToLeft"))
+    {
+        setTextRightToLeft(properties->getBool("rightToLeft"), states);
+    }
 
 
-        if (properties->exists("font"))
-        {
-            Font* font = Font::create(properties->getString("font"));
-            setFont(font, states);
-            font->release();
-        }
+    if (properties->exists("opacity"))
+    {
+        setOpacity(properties->getFloat("opacity"), states);
+    }
+}
 
 
-        if (properties->exists("fontSize"))
-        {
-            setFontSize(properties->getInt("fontSize"), states);
-        }
+void Control::setImageList(Theme::ImageList* imageList, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-        if (properties->exists("textColor"))
-        {
-            Vector4 textColor(0, 0, 0, 1);
-            properties->getColor("textColor", &textColor);
-            setTextColor(textColor, states);
-        }
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
+    {
+        overlays[i]->setImageList(imageList);
+    }
 
 
-        if (properties->exists("textAlignment"))
-        {
-            setTextAlignment(Font::getJustify(properties->getString("textAlignment")), states);
-        }
+    _dirty = true;
+}
 
 
-        if (properties->exists("rightToLeft"))
-        {
-            setTextRightToLeft(properties->getBool("rightToLeft"), states);
-        }
+void Control::setCursor(Theme::ThemeImage* cursor, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-        if (properties->exists("opacity"))
-        {
-            setOpacity(properties->getFloat("opacity"), states);
-        }
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
+    {
+        overlays[i]->setCursor(cursor);
     }
     }
 
 
-    void Control::setImageList(Theme::ImageList* imageList, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setImageList(imageList);
-        }
+void Control::setSkin(Theme::Skin* skin, unsigned char states)
+{
+    overrideStyle();
+    Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
+    getOverlays(states, overlays);
 
 
-        _dirty = true;
+    for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
+    {
+        overlays[i]->setSkin(skin);
     }
     }
 
 
-    void Control::setCursor(Theme::ThemeImage* cursor, unsigned char states)
-    {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
+    _dirty = true;
+}
 
 
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setCursor(cursor);
-        }
+Theme::Skin* Control::getSkin(State state)
+{
+    Theme::Style::Overlay* overlay = getOverlay(state);
+    GP_ASSERT(overlay);
+    return overlay->getSkin();
+}
 
 
-        _dirty = true;
+Control::Alignment Control::getAlignment(const char* alignment)
+{
+    if (!alignment)
+    {
+        return Control::ALIGN_TOP_LEFT;
     }
     }
 
 
-    void Control::setSkin(Theme::Skin* skin, unsigned char states)
+    if (strcmp(alignment, "ALIGN_LEFT") == 0)
     {
     {
-        overrideStyle();
-        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
-        getOverlays(states, overlays);
-
-        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
-        {
-            overlays[i]->setSkin(skin);
-        }
-
-        _dirty = true;
+        return Control::ALIGN_LEFT;
     }
     }
-
-    Theme::Skin* Control::getSkin(State state)
+    else if (strcmp(alignment, "ALIGN_HCENTER") == 0)
     {
     {
-        return getOverlay(state)->getSkin();
+        return Control::ALIGN_HCENTER;
     }
     }
-
-    Control::Alignment Control::getAlignment(const char* alignment)
+    else if (strcmp(alignment, "ALIGN_RIGHT") == 0)
+    {
+        return Control::ALIGN_RIGHT;
+    }
+    else if (strcmp(alignment, "ALIGN_TOP") == 0)
+    {
+        return Control::ALIGN_TOP;
+    }
+    else if (strcmp(alignment, "ALIGN_VCENTER") == 0)
+    {
+        return Control::ALIGN_VCENTER;
+    }
+    else if (strcmp(alignment, "ALIGN_BOTTOM") == 0)
+    {
+        return Control::ALIGN_BOTTOM;
+    }
+    else if (strcmp(alignment, "ALIGN_TOP_LEFT") == 0)
     {
     {
-        if (!alignment)
-        {
-            return Control::ALIGN_TOP_LEFT;
-        }
-
-        if (strcmp(alignment, "ALIGN_LEFT") == 0)
-        {
-            return Control::ALIGN_LEFT;
-        }
-        else if (strcmp(alignment, "ALIGN_HCENTER") == 0)
-        {
-            return Control::ALIGN_HCENTER;
-        }
-        else if (strcmp(alignment, "ALIGN_RIGHT") == 0)
-        {
-            return Control::ALIGN_RIGHT;
-        }
-        else if (strcmp(alignment, "ALIGN_TOP") == 0)
-        {
-            return Control::ALIGN_TOP;
-        }
-        else if (strcmp(alignment, "ALIGN_VCENTER") == 0)
-        {
-            return Control::ALIGN_VCENTER;
-        }
-        else if (strcmp(alignment, "ALIGN_BOTTOM") == 0)
-        {
-            return Control::ALIGN_BOTTOM;
-        }
-        else if (strcmp(alignment, "ALIGN_TOP_LEFT") == 0)
-        {
-            return Control::ALIGN_TOP_LEFT;
-        }
-        else if (strcmp(alignment, "ALIGN_VCENTER_LEFT") == 0)
-        {
-            return Control::ALIGN_VCENTER_LEFT;
-        }
-        else if (strcmp(alignment, "ALIGN_BOTTOM_LEFT") == 0)
-        {
-            return Control::ALIGN_BOTTOM_LEFT;
-        }
-        else if (strcmp(alignment, "ALIGN_TOP_HCENTER") == 0)
-        {
-            return Control::ALIGN_TOP_HCENTER;
-        }
-        else if (strcmp(alignment, "ALIGN_VCENTER_HCENTER") == 0)
-        {
-            return Control::ALIGN_VCENTER_HCENTER;
-        }
-        else if (strcmp(alignment, "ALIGN_BOTTOM_HCENTER") == 0)
-        {
-            return Control::ALIGN_BOTTOM_HCENTER;
-        }
-        else if (strcmp(alignment, "ALIGN_TOP_RIGHT") == 0)
-        {
-            return Control::ALIGN_TOP_RIGHT;
-        }
-        else if (strcmp(alignment, "ALIGN_VCENTER_RIGHT") == 0)
-        {
-            return Control::ALIGN_VCENTER_RIGHT;
-        }
-        else if (strcmp(alignment, "ALIGN_BOTTOM_RIGHT") == 0)
-        {
-            return Control::ALIGN_BOTTOM_RIGHT;
-        }
-
-        // Default.
         return Control::ALIGN_TOP_LEFT;
         return Control::ALIGN_TOP_LEFT;
     }
     }
+    else if (strcmp(alignment, "ALIGN_VCENTER_LEFT") == 0)
+    {
+        return Control::ALIGN_VCENTER_LEFT;
+    }
+    else if (strcmp(alignment, "ALIGN_BOTTOM_LEFT") == 0)
+    {
+        return Control::ALIGN_BOTTOM_LEFT;
+    }
+    else if (strcmp(alignment, "ALIGN_TOP_HCENTER") == 0)
+    {
+        return Control::ALIGN_TOP_HCENTER;
+    }
+    else if (strcmp(alignment, "ALIGN_VCENTER_HCENTER") == 0)
+    {
+        return Control::ALIGN_VCENTER_HCENTER;
+    }
+    else if (strcmp(alignment, "ALIGN_BOTTOM_HCENTER") == 0)
+    {
+        return Control::ALIGN_BOTTOM_HCENTER;
+    }
+    else if (strcmp(alignment, "ALIGN_TOP_RIGHT") == 0)
+    {
+        return Control::ALIGN_TOP_RIGHT;
+    }
+    else if (strcmp(alignment, "ALIGN_VCENTER_RIGHT") == 0)
+    {
+        return Control::ALIGN_VCENTER_RIGHT;
+    }
+    else if (strcmp(alignment, "ALIGN_BOTTOM_RIGHT") == 0)
+    {
+        return Control::ALIGN_BOTTOM_RIGHT;
+    }
+    else
+    {
+        GP_ERROR("Failed to get corresponding control alignment for unsupported value \'%s\'.", alignment);
+    }
+
+    // Default.
+    return Control::ALIGN_TOP_LEFT;
+}
+
 }
 }

+ 31 - 16
gameplay/src/Control.h

@@ -24,6 +24,7 @@ class Control : public Ref, public AnimationTarget
     friend class AbsoluteLayout;
     friend class AbsoluteLayout;
     friend class VerticalLayout;
     friend class VerticalLayout;
     friend class FlowLayout;
     friend class FlowLayout;
+    friend class ScrollLayout;
 
 
 public:
 public:
 
 
@@ -192,7 +193,7 @@ public:
      * @param width The width.
      * @param width The width.
      * @param height The height.
      * @param height The height.
      */
      */
-    void setSize(float width, float height);
+    virtual void setSize(float width, float height);
 
 
     /**
     /**
      * Set the bounds of this control, relative to its parent container and including its
      * Set the bounds of this control, relative to its parent container and including its
@@ -200,7 +201,7 @@ public:
      *
      *
      * @param bounds The new bounds to set.
      * @param bounds The new bounds to set.
      */
      */
-    void setBounds(const Rectangle& bounds);
+    virtual void setBounds(const Rectangle& bounds);
 
 
     /**
     /**
      * Get the bounds of this control, relative to its parent container and including its
      * Get the bounds of this control, relative to its parent container and including its
@@ -257,7 +258,7 @@ public:
      *
      *
      * @param autoWidth Whether to size this control to fit horizontally within its parent container.
      * @param autoWidth Whether to size this control to fit horizontally within its parent container.
      */
      */
-    void setAutoWidth(bool autoWidth);
+    virtual void setAutoWidth(bool autoWidth);
 
 
     /**
     /**
      * Get whether this control's width is set to automatically adjust to
      * Get whether this control's width is set to automatically adjust to
@@ -272,7 +273,7 @@ public:
      *
      *
      * @param autoHeight Whether to size this control to fit vertically within its parent container.
      * @param autoHeight Whether to size this control to fit vertically within its parent container.
      */
      */
-    void setAutoHeight(bool autoHeight);
+    virtual void setAutoHeight(bool autoHeight);
 
 
     /**
     /**
      * Get whether this control's height is set to automatically adjust to
      * Get whether this control's height is set to automatically adjust to
@@ -735,14 +736,16 @@ protected:
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
+     * @param offset Layout-computed positioning offset to add to the control's position.
      */
      */
-    virtual void update(const Rectangle& clip);
+    virtual void update(const Rectangle& clip, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
      *
      *
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
+     * @param offset Layout-computed positioning offset to add to the control's position.
      */
      */
     virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
     virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
 
@@ -750,6 +753,7 @@ protected:
      * Draw this control's text.
      * Draw this control's text.
      *
      *
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
+     * @param offset Layout-computed positioning offset to add to the control's position.
      */
      */
     virtual void drawText(const Rectangle& clip);
     virtual void drawText(const Rectangle& clip);
 
 
@@ -784,8 +788,9 @@ protected:
     /**
     /**
      * Get a Theme::ThemeImage from its ID, for a given state.
      * Get a Theme::ThemeImage from its ID, for a given state.
      *
      *
-     * @param id The ID of the image to retrieve
+     * @param id The ID of the image to retrieve.
      * @param state The state to get this image from.
      * @param state The state to get this image from.
+     *
      * @return The requested Theme::ThemeImage, or NULL if none was found.
      * @return The requested Theme::ThemeImage, or NULL if none was found.
      */
      */
     Theme::ThemeImage* getImage(const char* id, State state);
     Theme::ThemeImage* getImage(const char* id, State state);
@@ -819,25 +824,32 @@ protected:
      * Position, relative to parent container's clipping window, and desired size.
      * Position, relative to parent container's clipping window, and desired size.
      */
      */
     Rectangle _bounds;
     Rectangle _bounds;
-    
+
     /**
     /**
-     * The position and size of this control, relative to parent container's bounds, including border and padding, after clipping.
+     * Position, relative to parent container's clipping window, including border and padding, after clipping.
      */
      */
     Rectangle _clipBounds;
     Rectangle _clipBounds;
-    
+
     /**
     /**
-     * The position and size of this control's text area, before clipping.  Used for text alignment.
+     * Absolute bounds, including border and padding, before clipping.
      */
      */
-    Rectangle _textBounds;
-    
+    Rectangle _absoluteBounds;
+
     /**
     /**
-     * Clipping window of this control's content, after clipping.
+     * Absolute bounds, including border and padding, after clipping.
      */
      */
-    Rectangle _clip;
-    
+    Rectangle _absoluteClipBounds;
+
     /**
     /**
-     * Flag for whether the Control is dirty.
+     * Absolute bounds of content area (i.e. without border and padding), before clipping.
      */
      */
+    Rectangle _viewportBounds;
+
+    /**
+     * Absolute bounds of content area (i.e. without border and padding), after clipping.
+     */
+    Rectangle _viewportClipBounds;
+
     bool _dirty;
     bool _dirty;
     
     
     /**
     /**
@@ -907,9 +919,12 @@ private:
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
      */
      */
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
     
     
     bool _styleOverridden;
     bool _styleOverridden;
     Theme::Skin* _skin;
     Theme::Skin* _skin;
+    Rectangle _clearBounds;         // Previous frame's absolute clip bounds, to be cleared if necessary.
 };
 };
 
 
 }
 }

+ 0 - 4
gameplay/src/Curve.cpp

@@ -1123,13 +1123,9 @@ void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) c
 {
 {
     // Evaluate.
     // Evaluate.
     if (s >= 0)
     if (s >= 0)
-    {
         Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
         Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
-    }
     else
     else
         Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
         Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
-
-    //((Quaternion*) dst)->normalize();
 }
 }
 
 
 int Curve::determineIndex(float time) const
 int Curve::determineIndex(float time) const

+ 5 - 5
gameplay/src/Effect.cpp

@@ -79,7 +79,7 @@ Effect* Effect::createFromFile(const char* vshPath, const char* fshPath, const c
 
 
     if (effect == NULL)
     if (effect == NULL)
     {
     {
-        LOG_ERROR_VARG("Failed to create effect from shaders: %s, %s", vshPath, fshPath);
+        GP_ERROR("Failed to create effect from shaders: %s, %s", vshPath, fshPath);
     }
     }
     else
     else
     {
     {
@@ -130,7 +130,7 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
             GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) );
             GL_ASSERT( glGetShaderInfoLog(vertexShader, length, NULL, infoLog) );
             infoLog[length-1] = '\0';
             infoLog[length-1] = '\0';
         }
         }
-        LOG_ERROR_VARG("Compile failed for vertex shader (%s): %s", vshPath == NULL ? "NULL" : vshPath, infoLog == NULL ? "" : infoLog);
+        GP_ERROR("Compile failed for vertex shader (%s): %s", vshPath == NULL ? "NULL" : vshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);
         SAFE_DELETE_ARRAY(infoLog);
 
 
         // Clean up.
         // Clean up.
@@ -162,7 +162,7 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
             GL_ASSERT( glGetShaderInfoLog(fragmentShader, length, NULL, infoLog) );
             GL_ASSERT( glGetShaderInfoLog(fragmentShader, length, NULL, infoLog) );
             infoLog[length-1] = '\0';
             infoLog[length-1] = '\0';
         }
         }
-        LOG_ERROR_VARG("Compile failed for fragment shader (%s): %s", fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog);
+        GP_ERROR("Compile failed for fragment shader (%s): %s", fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);
         SAFE_DELETE_ARRAY(infoLog);
 
 
         // Clean up.
         // Clean up.
@@ -193,7 +193,7 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
             GL_ASSERT( glGetProgramInfoLog(program, length, NULL, infoLog) );
             GL_ASSERT( glGetProgramInfoLog(program, length, NULL, infoLog) );
             infoLog[length-1] = '\0';
             infoLog[length-1] = '\0';
         }
         }
-        LOG_ERROR_VARG("Linking program failed (%s,%s): %s", vshPath == NULL ? "NULL" : vshPath, fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog);
+        GP_ERROR("Linking program failed (%s,%s): %s", vshPath == NULL ? "NULL" : vshPath, fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);
         SAFE_DELETE_ARRAY(infoLog);
 
 
         // Clean up.
         // Clean up.
@@ -387,7 +387,7 @@ void Effect::setValue(Uniform* uniform, const Vector4* values, unsigned int coun
 
 
 void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 void Effect::setValue(Uniform* uniform, const Texture::Sampler* sampler)
 {
 {
-    assert(uniform->_type == GL_SAMPLER_2D);
+    GP_ASSERT(uniform->_type == GL_SAMPLER_2D);
 
 
     GL_ASSERT( glActiveTexture(GL_TEXTURE0 + uniform->_index) );
     GL_ASSERT( glActiveTexture(GL_TEXTURE0 + uniform->_index) );
 
 

+ 6 - 5
gameplay/src/FileSystem.cpp

@@ -11,6 +11,7 @@
 #endif
 #endif
 
 
 #ifdef __ANDROID__
 #ifdef __ANDROID__
+#include <android/asset_manager.h>
 extern AAssetManager* __assetManager;
 extern AAssetManager* __assetManager;
 #endif
 #endif
 
 
@@ -49,7 +50,7 @@ void makepath(std::string path, int mode)
             // Directory does not exist.
             // Directory does not exist.
             if (mkdir(dirPath.c_str(), 0777) != 0)
             if (mkdir(dirPath.c_str(), 0777) != 0)
             {
             {
-                WARN_VARG("Failed to create directory: '%s'", dirPath.c_str());
+                GP_WARN("Failed to create directory: '%s'", dirPath.c_str());
                 return;
                 return;
             }
             }
         }
         }
@@ -169,7 +170,7 @@ FILE* FileSystem::openFile(const char* path, const char* mode)
             FILE* file = fopen(fullPath.c_str(), "wb");
             FILE* file = fopen(fullPath.c_str(), "wb");
         
         
             int ret = fwrite(data, sizeof(unsigned char), length, file);
             int ret = fwrite(data, sizeof(unsigned char), length, file);
-            assert(ret == length);
+            GP_ASSERT(ret == length);
             fclose(file);
             fclose(file);
         }
         }
     }
     }
@@ -198,7 +199,7 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     FILE* file = openFile(filePath, "rb");
     FILE* file = openFile(filePath, "rb");
     if (file == NULL)
     if (file == NULL)
     {
     {
-        LOG_ERROR_VARG("Failed to load file: %s", filePath);
+        GP_ERROR("Failed to load file: %s", filePath);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -210,10 +211,10 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     // Read entire file contents.
     // Read entire file contents.
     char* buffer = new char[size + 1];
     char* buffer = new char[size + 1];
     int read = (int)fread(buffer, 1, size, file);
     int read = (int)fread(buffer, 1, size, file);
-    assert(read == size);
+    GP_ASSERT(read == size);
     if (read != size)
     if (read != size)
     {
     {
-        LOG_ERROR_VARG("Read error for file: %s (%d < %d)", filePath, (int)read, (int)size);
+        GP_ERROR("Read error for file: %s (%d < %d)", filePath, (int)read, (int)size);
         SAFE_DELETE_ARRAY(buffer);
         SAFE_DELETE_ARRAY(buffer);
         return NULL;
         return NULL;
     }
     }

+ 6 - 2
gameplay/src/FlowLayout.cpp

@@ -41,7 +41,8 @@ Layout::Type FlowLayout::getType()
 
 
 void FlowLayout::update(const Container* container)
 void FlowLayout::update(const Container* container)
 {
 {
-    const Rectangle& containerBounds = container->getClipBounds();
+    GP_ASSERT(container);
+    const Rectangle& containerBounds = container->getBounds();
     const Theme::Border& containerBorder = container->getBorder(container->getState());
     const Theme::Border& containerBorder = container->getBorder(container->getState());
     const Theme::Padding& containerPadding = container->getPadding();
     const Theme::Padding& containerPadding = container->getPadding();
 
 
@@ -58,6 +59,9 @@ void FlowLayout::update(const Container* container)
     for (unsigned int i = 0; i < controlsCount; i++)
     for (unsigned int i = 0; i < controlsCount; i++)
     {
     {
         Control* control = controls.at(i);
         Control* control = controls.at(i);
+        GP_ASSERT(control);
+
+        //align(control, container);
 
 
         const Rectangle& bounds = control->getBounds();
         const Rectangle& bounds = control->getBounds();
         const Theme::Margin& margin = control->getMargin();
         const Theme::Margin& margin = control->getMargin();
@@ -76,7 +80,7 @@ void FlowLayout::update(const Container* container)
         control->setPosition(xPosition, yPosition);
         control->setPosition(xPosition, yPosition);
         if (control->isDirty() || control->isContainer())
         if (control->isDirty() || control->isContainer())
         {
         {
-            control->update(container->getClip());
+            control->update(container->getClip(), Vector2::zero());
         }
         }
 
 
         xPosition += bounds.width + margin.right;
         xPosition += bounds.width + margin.right;

+ 0 - 3
gameplay/src/FlowLayout.h

@@ -6,9 +6,6 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-/**
- *  Defines a layout that arranges components in a left-to-right or right-to-left flow. 
- */
 class FlowLayout : public Layout
 class FlowLayout : public Layout
 {
 {
     friend class Form;
     friend class Form;

+ 340 - 253
gameplay/src/Font.cpp

@@ -124,7 +124,7 @@ Font* Font::create(const char* family, Style style, unsigned int size, Glyph* gl
         __fontEffect = Effect::createFromSource(FONT_VSH, FONT_FSH);
         __fontEffect = Effect::createFromSource(FONT_VSH, FONT_FSH);
         if (__fontEffect == NULL)
         if (__fontEffect == NULL)
         {
         {
-            LOG_ERROR("Failed to create effect for font.");
+            GP_ERROR("Failed to create effect for font.");
             SAFE_RELEASE(texture);
             SAFE_RELEASE(texture);
             return NULL;
             return NULL;
         }
         }
@@ -142,7 +142,7 @@ Font* Font::create(const char* family, Style style, unsigned int size, Glyph* gl
 
 
     if (batch == NULL)
     if (batch == NULL)
     {
     {
-        LOG_ERROR("Failed to create batch for font.");
+        GP_ERROR("Failed to create batch for font.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -174,6 +174,235 @@ void Font::begin()
     _batch->begin();
     _batch->begin();
 }
 }
 
 
+Font::Text* Font::createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify,
+    bool wrap, bool rightToLeft, const Rectangle* clip)
+{
+    if (size == 0)
+        size = _size;
+    float scale = (float)size / _size;
+    const int length = strlen(text);
+    int yPos = area.y;
+    const float areaHeight = area.height - size;
+    std::vector<int> xPositions;
+    std::vector<unsigned int> lineLengths;
+
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
+
+    Text* batch = new Text(text);
+
+    int xPos = area.x;
+    std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
+    if (xPositionsIt != xPositions.end())
+    {
+        xPos = *xPositionsIt++;
+    }
+
+    const char* token = text;
+    int iteration = 1;
+    unsigned int lineLength;
+    unsigned int currentLineLength = 0;
+    const char* lineStart;
+    std::vector<unsigned int>::const_iterator lineLengthsIt;
+    if (rightToLeft)
+    {
+        lineStart = token;
+        lineLengthsIt = lineLengths.begin();
+        lineLength = *lineLengthsIt++;
+        token += lineLength - 1;
+        iteration = -1;
+    }
+
+    while (token[0] != 0)
+    {
+        // Handle delimiters until next token.
+        if (!handleDelimiters(&token, size, iteration, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+        {
+            break;
+        }
+
+        bool truncated = false;
+        unsigned int tokenLength;
+        unsigned int tokenWidth;
+        unsigned int startIndex;
+        if (rightToLeft)
+        {
+            tokenLength = getReversedTokenLength(token, text);
+            currentLineLength += tokenLength;
+            token -= (tokenLength - 1);
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = -1;
+            startIndex = tokenLength - 1;
+        }
+        else
+        {
+            tokenLength = strcspn(token, " \r\n\t");
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = 1;
+            startIndex = 0;
+        }
+
+        // Wrap if necessary.
+        if (wrap && (xPos + (int)tokenWidth > area.x + area.width || (rightToLeft && currentLineLength > lineLength)))
+        {
+            yPos += (int)size;
+            currentLineLength = tokenLength;
+
+            if (xPositionsIt != xPositions.end())
+            {
+                xPos = *xPositionsIt++;
+            }
+            else
+            {
+                xPos = area.x;
+            }
+        }
+
+        bool draw = true;
+        if (yPos < area.y)
+        {
+            // Skip drawing until line break or wrap.
+            draw = false;
+        }
+        else if (yPos > area.y + areaHeight)
+        {
+            // Truncate below area's vertical limit.
+            break;
+        }
+
+        for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
+        {
+            char c = token[i];
+            int glyphIndex = c - 32; // HACK for ASCII
+        
+            if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
+            {
+                Glyph& g = _glyphs[glyphIndex];
+
+                if (xPos + (int)(g.width*scale) > area.x + area.width)
+                {
+                    // Truncate this line and go on to the next one.
+                    truncated = true;
+                    break;
+                }
+                else if (xPos >= area.x)
+                {
+                    // Draw this character.
+                    if (draw)
+                    {
+                        if (clip)
+                        {
+                            _batch->addSprite(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color, *clip, &batch->_vertices[batch->_vertexCount]);
+                        }
+                        else
+                        {
+                            _batch->addSprite(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color, &batch->_vertices[batch->_vertexCount]);
+                        }
+
+                        if (batch->_vertexCount == 0)
+                        {
+                            // Simply copy values directly into the start of the index array
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 1;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 2;
+                            batch->_indices[batch->_vertexCount] = batch->_vertexCount + 3;
+                            batch->_vertexCount += 4;
+                            batch->_indexCount += 4;
+                        }
+                        else
+                        {
+                            // Create a degenerate triangle to connect separate triangle strips
+                            // by duplicating the previous and next vertices.
+                            batch->_indices[batch->_indexCount] = batch->_indices[batch->_indexCount - 1];
+                            batch->_indices[batch->_indexCount + 1] = batch->_vertexCount;
+            
+                            // Loop through all indices and insert them, their their value offset by
+                            // 'vertexCount' so that they are relative to the first newly insertted vertex
+                            for (unsigned int i = 0; i < 4; ++i)
+                            {
+                                batch->_indices[batch->_indexCount + 2 + i] = i + batch->_vertexCount;
+                            }
+
+                            batch->_indexCount += 6;
+                            batch->_vertexCount += 4;
+                        }
+
+                    }
+                }
+                xPos += (int)(g.width)*scale + (size >> 3);
+            }
+        }
+
+        if (!truncated)
+        {
+            if (rightToLeft)
+            {
+                if (token == lineStart)
+                {
+                    token += lineLength;
+                    
+                    // Now handle delimiters going forwards.
+                    if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                    {
+                        break;
+                    }
+
+                    if (lineLengthsIt != lineLengths.end())
+                    {
+                        lineLength = *lineLengthsIt++;
+                    }
+                    lineStart = token;
+                    token += lineLength-1;
+                }
+                else
+                {
+                    token--;
+                }
+            }
+            else
+            {
+                token += tokenLength;
+            }
+        }
+        else
+        {
+            if (rightToLeft)
+            {
+                token = lineStart + lineLength;
+                
+                if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                {
+                    break;
+                }
+
+                if (lineLengthsIt != lineLengths.end())
+                {
+                    lineLength = *lineLengthsIt++;
+                }
+                lineStart = token;
+                token += lineLength-1;
+            }
+            else
+            {
+                // Skip the rest of this line.
+                unsigned int tokenLength = strcspn(token, "\n");
+
+                if (tokenLength > 0)
+                {                
+                    // Get first token of next line.
+                    token += tokenLength;
+                }
+            }
+        }
+    }
+
+    return batch;
+}
+
+void Font::drawText(Text* text)
+{
+    _batch->draw(text->_vertices, text->_vertexCount, text->_indices, text->_indexCount);
+}
+
 void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 {
 {
     if (size == 0)
     if (size == 0)
@@ -207,7 +436,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
                 switch (delimiter)
                 switch (delimiter)
                 {
                 {
                 case ' ':
                 case ' ':
-                    xPos += (float)size*0.5f;
+                    xPos += size >> 1;
                     break;
                     break;
                 case '\r':
                 case '\r':
                 case '\n':
                 case '\n':
@@ -215,7 +444,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
                     xPos = x;
                     xPos = x;
                     break;
                     break;
                 case '\t':
                 case '\t':
-                    xPos += ((float)size*0.5f)*4;
+                    xPos += (size >> 1)*4;
                     break;
                     break;
                 case 0:
                 case 0:
                     done = true;
                     done = true;
@@ -256,7 +485,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
             switch (c)
             switch (c)
             {
             {
             case ' ':
             case ' ':
-                xPos += (float)size*0.5f;
+                xPos += size >> 1;
                 break;
                 break;
             case '\r':
             case '\r':
             case '\n':
             case '\n':
@@ -264,7 +493,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
                 xPos = x;
                 xPos = x;
                 break;
                 break;
             case '\t':
             case '\t':
-                xPos += ((float)size*0.5f)*4;
+                xPos += (size >> 1)*4;
                 break;
                 break;
             default:
             default:
                 int index = c - 32; // HACK for ASCII
                 int index = c - 32; // HACK for ASCII
@@ -272,9 +501,10 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
                 {
                 {
                     Glyph& g = _glyphs[index];
                     Glyph& g = _glyphs[index];
                     _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
                     _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
-                    xPos += g.width * scale + ((float)size*0.125f);
+                    xPos += floor(g.width * scale + (float)(size >> 3));
                     break;
                     break;
                 }
                 }
+                break;
             }
             }
         }
         }
 
 
@@ -294,185 +524,13 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
     if (size == 0)
     if (size == 0)
         size = _size;
         size = _size;
     float scale = (float)size / _size;
     float scale = (float)size / _size;
-    const char* token = text;
     const int length = strlen(text);
     const int length = strlen(text);
     int yPos = area.y;
     int yPos = area.y;
     const float areaHeight = area.height - size;
     const float areaHeight = area.height - size;
-
-    Justify vAlign = static_cast<Justify>(justify & 0xF0);
-    if (vAlign == 0)
-    {
-        vAlign = ALIGN_TOP;
-    }
-
-    Justify hAlign = static_cast<Justify>(justify & 0x0F);
-    if (hAlign == 0)
-    {
-        hAlign = ALIGN_LEFT;
-    }
-
-    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
-    // the number of characters on each line.
     std::vector<int> xPositions;
     std::vector<int> xPositions;
     std::vector<unsigned int> lineLengths;
     std::vector<unsigned int> lineLengths;
-    if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
-    {
-        int lineWidth = 0;
-        int delimWidth = 0;
-
-        if (wrap)
-        {
-            // Go a word at a time.
-            bool reachedEOF = false;
-            unsigned int lineLength = 0;
-            while (token[0] != 0)
-            {
-                unsigned int tokenWidth = 0;
-
-                // Handle delimiters until next token.
-                char delimiter = token[0];
-                while (delimiter == ' ' ||
-                       delimiter == '\t' ||
-                       delimiter == '\r' ||
-                       delimiter == '\n' ||
-                       delimiter == 0)
-                {
-                    switch (delimiter)
-                    {
-                        case ' ':
-                            delimWidth += (float)size*0.5f;
-                            lineLength++;
-                            break;
-                        case '\r':
-                        case '\n':
-                            yPos += size;
-
-                            if (lineWidth > 0)
-                            {
-                                addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
-                            }
-
-                            lineWidth = 0;
-                            lineLength = 0;
-                            delimWidth = 0;
-                            break;
-                        case '\t':
-                            delimWidth += ((float)size*0.5f)*4;
-                            lineLength++;
-                            break;
-                        case 0:
-                            reachedEOF = true;
-                            break;
-                    }
-
-                    if (reachedEOF)
-                    {
-                        break;
-                    }
-
-                    token++;
-                    delimiter = token[0];
-                }
-
-                if (reachedEOF || token == NULL)
-                {
-                    break;
-                }
-
-                unsigned int tokenLength = strcspn(token, " \r\n\t");
-                tokenWidth += getTokenWidth(token, tokenLength, size, scale);
-
-                // Wrap if necessary.
-                if (lineWidth + tokenWidth + delimWidth > area.width)
-                {
-                    yPos += size;
-
-                    // Push position of current line.
-                    if (lineLength)
-                    {
-                        addLineInfo(area, lineWidth, lineLength-1, hAlign, &xPositions, &lineLengths, rightToLeft);
-                    }
-                    else
-                    {
-                        addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
-                    }
-
-                    // Move token to the next line.
-                    lineWidth = 0;
-                    lineLength = 0;
-                    delimWidth = 0;
-                }
-                else
-                {
-                    lineWidth += delimWidth;
-                    delimWidth = 0;
-                }
-
-                lineWidth += tokenWidth;
-                lineLength += tokenLength;
-                token += tokenLength;
-            }
-
-            // Final calculation of vertical position.
-            int textHeight = yPos - area.y;
-            int vWhiteSpace = areaHeight - textHeight;
-            if (vAlign == ALIGN_VCENTER)
-            {
-                yPos = area.y + vWhiteSpace / 2;
-            }
-            else if (vAlign == ALIGN_BOTTOM)
-            {
-                yPos = area.y + vWhiteSpace;
-            }
-
-            // Calculation of final horizontal position.
-            addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
-        }
-        else
-        {
-            // Go a line at a time.
-            while (token[0] != 0)
-            {
-                char delimiter = token[0];
-                while (delimiter == '\n')
-                {
-                    yPos += size;
-                    ++token;
-                    delimiter = token[0];
-                }
-
-                unsigned int tokenLength = strcspn(token, "\n");
-                if (tokenLength == 0)
-                {
-                    tokenLength = strlen(token);
-                }
-
-                int lineWidth = getTokenWidth(token, tokenLength, size, scale);
-                addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
-
-                token += tokenLength;
-            }
-
-            int textHeight = yPos - area.y;
-            int vWhiteSpace = areaHeight - textHeight;
-            if (vAlign == ALIGN_VCENTER)
-            {
-                yPos = area.y + vWhiteSpace / 2;
-            }
-            else if (vAlign == ALIGN_BOTTOM)
-            {
-                yPos = area.y + vWhiteSpace;
-            }
-        }
 
 
-        if (vAlign == ALIGN_TOP)
-        {
-            yPos = area.y;
-        }
-    }
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
 
 
     // Now we have the info we need in order to render.
     // Now we have the info we need in order to render.
     int xPos = area.x;
     int xPos = area.x;
@@ -482,8 +540,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         xPos = *xPositionsIt++;
         xPos = *xPositionsIt++;
     }
     }
 
 
-    token = text;
-    
+    const char* token = text;
     int iteration = 1;
     int iteration = 1;
     unsigned int lineLength;
     unsigned int lineLength;
     unsigned int currentLineLength = 0;
     unsigned int currentLineLength = 0;
@@ -528,11 +585,9 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         }
         }
 
 
         // Wrap if necessary.
         // Wrap if necessary.
-        if (wrap &&
-            xPos + tokenWidth > area.x + area.width ||
-            (rightToLeft && currentLineLength > lineLength))
+        if (wrap && (xPos + (int)tokenWidth > area.x + area.width || (rightToLeft && currentLineLength > lineLength)))
         {
         {
-            yPos += size;
+            yPos += (int)size;
             currentLineLength = tokenLength;
             currentLineLength = tokenLength;
 
 
             if (xPositionsIt != xPositions.end())
             if (xPositionsIt != xPositions.end())
@@ -587,7 +642,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                         }
                         }
                     }
                     }
                 }
                 }
-                xPos += g.width*scale + ((float)size*0.125f);
+                xPos += (int)(g.width)*scale + (size >> 3);
             }
             }
         }
         }
 
 
@@ -729,7 +784,7 @@ void Font::measureText(const char* text, const Rectangle& clip, unsigned int siz
                 switch (delimiter)
                 switch (delimiter)
                 {
                 {
                     case ' ':
                     case ' ':
-                        delimWidth += (float)size*0.5f;
+                        delimWidth += size >> 1;
                         break;
                         break;
                     case '\r':
                     case '\r':
                     case '\n':
                     case '\n':
@@ -765,7 +820,7 @@ void Font::measureText(const char* text, const Rectangle& clip, unsigned int siz
                         delimWidth = 0;
                         delimWidth = 0;
                         break;
                         break;
                     case '\t':
                     case '\t':
-                        delimWidth += ((float)size*0.5f)*4;
+                        delimWidth += (size >> 1)*4;
                         break;
                         break;
                     case 0:
                     case 0:
                         reachedEOF = true;
                         reachedEOF = true;
@@ -1010,29 +1065,10 @@ void Font::measureText(const char* text, const Rectangle& clip, unsigned int siz
     }
     }
 }
 }
 
 
-unsigned int Font::getIndexAtLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                      Justify justify, bool wrap, bool rightToLeft)
+void Font::getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
+        std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths)
 {
 {
-    return getIndexOrLocation(text, area, size, inLocation, outLocation, -1, justify, wrap, rightToLeft);
-}
-
-void Font::getLocationAtIndex(const char* text, const Rectangle& clip, unsigned int size, Vector2* outLocation, const unsigned int destIndex,
-                              Justify justify, bool wrap, bool rightToLeft)
-{
-    getIndexOrLocation(text, clip, size, *outLocation, outLocation, (const int)destIndex, justify, wrap, rightToLeft);
-}
-
-unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                      const int destIndex, Justify justify, bool wrap, bool rightToLeft)
-{
-    unsigned int charIndex = 0;
-
-    // Essentially need to measure text until we reach inLocation.
     float scale = (float)size / _size;
     float scale = (float)size / _size;
-    const char* token = text;
-    const int length = strlen(text);
-    int yPos = area.y;
-    const float areaHeight = area.height - size;
 
 
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
     if (vAlign == 0)
     if (vAlign == 0)
@@ -1046,13 +1082,12 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
         hAlign = ALIGN_LEFT;
         hAlign = ALIGN_LEFT;
     }
     }
 
 
-    token = text;
+    const char* token = text;
+    const float areaHeight = area.height - size;
 
 
     // For alignments other than top-left, need to calculate the y position to begin drawing from
     // 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
     // and the starting x position of each line.  For right-to-left text, need to determine
     // the number of characters on each line.
     // the number of characters on each line.
-    std::vector<int> xPositions;
-    std::vector<unsigned int> lineLengths;
     if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
     if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
     {
     {
         int lineWidth = 0;
         int lineWidth = 0;
@@ -1078,16 +1113,16 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                     switch (delimiter)
                     switch (delimiter)
                     {
                     {
                         case ' ':
                         case ' ':
-                            delimWidth += (float)size*0.5f;
+                            delimWidth += size >> 1;
                             lineLength++;
                             lineLength++;
                             break;
                             break;
                         case '\r':
                         case '\r':
                         case '\n':
                         case '\n':
-                            yPos += size;
+                            *yPosition += size;
 
 
                             if (lineWidth > 0)
                             if (lineWidth > 0)
                             {
                             {
-                                addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                                addLineInfo(area, lineWidth, lineLength, hAlign, xPositions, lineLengths, rightToLeft);
                             }
                             }
 
 
                             lineWidth = 0;
                             lineWidth = 0;
@@ -1095,7 +1130,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                             delimWidth = 0;
                             delimWidth = 0;
                             break;
                             break;
                         case '\t':
                         case '\t':
-                            delimWidth += ((float)size*0.5f)*4;
+                            delimWidth += (size >> 1)*4;
                             lineLength++;
                             lineLength++;
                             break;
                             break;
                         case 0:
                         case 0:
@@ -1123,16 +1158,16 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                 // Wrap if necessary.
                 // Wrap if necessary.
                 if (lineWidth + tokenWidth + delimWidth > area.width)
                 if (lineWidth + tokenWidth + delimWidth > area.width)
                 {
                 {
-                    yPos += size;
+                    *yPosition += size;
 
 
                     // Push position of current line.
                     // Push position of current line.
                     if (lineLength)
                     if (lineLength)
                     {
                     {
-                        addLineInfo(area, lineWidth, lineLength-1, hAlign, &xPositions, &lineLengths, rightToLeft);
+                        addLineInfo(area, lineWidth, lineLength-1, hAlign, xPositions, lineLengths, rightToLeft);
                     }
                     }
                     else
                     else
                     {
                     {
-                        addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                        addLineInfo(area, lineWidth, tokenLength, hAlign, xPositions, lineLengths, rightToLeft);
                     }
                     }
 
 
                     // Move token to the next line.
                     // Move token to the next line.
@@ -1152,19 +1187,19 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
             }
             }
 
 
             // Final calculation of vertical position.
             // Final calculation of vertical position.
-            int textHeight = yPos - area.y;
+            int textHeight = *yPosition - area.y;
             int vWhiteSpace = areaHeight - textHeight;
             int vWhiteSpace = areaHeight - textHeight;
             if (vAlign == ALIGN_VCENTER)
             if (vAlign == ALIGN_VCENTER)
             {
             {
-                yPos = area.y + vWhiteSpace / 2;
+                *yPosition = area.y + vWhiteSpace / 2;
             }
             }
             else if (vAlign == ALIGN_BOTTOM)
             else if (vAlign == ALIGN_BOTTOM)
             {
             {
-                yPos = area.y + vWhiteSpace;
+                *yPosition = area.y + vWhiteSpace;
             }
             }
 
 
             // Calculation of final horizontal position.
             // Calculation of final horizontal position.
-            addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+            addLineInfo(area, lineWidth, lineLength, hAlign, xPositions, lineLengths, rightToLeft);
         }
         }
         else
         else
         {
         {
@@ -1174,7 +1209,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                 char delimiter = token[0];
                 char delimiter = token[0];
                 while (delimiter == '\n')
                 while (delimiter == '\n')
                 {
                 {
-                    yPos += size;
+                    *yPosition += size;
                     ++token;
                     ++token;
                     delimiter = token[0];
                     delimiter = token[0];
                 }
                 }
@@ -1186,30 +1221,57 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                 }
                 }
 
 
                 int lineWidth = getTokenWidth(token, tokenLength, size, scale);
                 int lineWidth = getTokenWidth(token, tokenLength, size, scale);
-                addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
+                addLineInfo(area, lineWidth, tokenLength, hAlign, xPositions, lineLengths, rightToLeft);
 
 
                 token += tokenLength;
                 token += tokenLength;
             }
             }
 
 
-            int textHeight = yPos - area.y;
+            int textHeight = *yPosition - area.y;
             int vWhiteSpace = areaHeight - textHeight;
             int vWhiteSpace = areaHeight - textHeight;
             if (vAlign == ALIGN_VCENTER)
             if (vAlign == ALIGN_VCENTER)
             {
             {
-                yPos = area.y + vWhiteSpace / 2;
+                *yPosition = area.y + vWhiteSpace / 2;
             }
             }
             else if (vAlign == ALIGN_BOTTOM)
             else if (vAlign == ALIGN_BOTTOM)
             {
             {
-                yPos = area.y + vWhiteSpace;
+                *yPosition = area.y + vWhiteSpace;
             }
             }
         }
         }
 
 
         if (vAlign == ALIGN_TOP)
         if (vAlign == ALIGN_TOP)
         {
         {
-            yPos = area.y;
+            *yPosition = area.y;
         }
         }
     }
     }
+}
+
+int Font::getIndexAtLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                                      Justify justify, bool wrap, bool rightToLeft)
+{
+    return getIndexOrLocation(text, area, size, inLocation, outLocation, -1, justify, wrap, rightToLeft);
+}
+
+void Font::getLocationAtIndex(const char* text, const Rectangle& clip, unsigned int size, Vector2* outLocation, const unsigned int destIndex,
+                              Justify justify, bool wrap, bool rightToLeft)
+{
+    getIndexOrLocation(text, clip, size, *outLocation, outLocation, (const int)destIndex, justify, wrap, rightToLeft);
+}
+
+int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                                      const int destIndex, Justify justify, bool wrap, bool rightToLeft)
+{
+    unsigned int charIndex = 0;
+
+    // Essentially need to measure text until we reach inLocation.
+    float scale = (float)size / _size;
+    const int length = strlen(text);
+    int yPos = area.y;
+    const float areaHeight = area.height - size;
+    std::vector<int> xPositions;
+    std::vector<unsigned int> lineLengths;
+
+    getMeasurementInfo(text, area, size, justify, wrap, rightToLeft, &xPositions, &yPos, &lineLengths);
 
 
-    // Now we have the info we need in order to render.
     int xPos = area.x;
     int xPos = area.x;
     std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
     std::vector<int>::const_iterator xPositionsIt = xPositions.begin();
     if (xPositionsIt != xPositions.end())
     if (xPositionsIt != xPositions.end())
@@ -1217,7 +1279,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
         xPos = *xPositionsIt++;
         xPos = *xPositionsIt++;
     }
     }
 
 
-    token = text;
+    const char* token = text;
     
     
     int iteration = 1;
     int iteration = 1;
     unsigned int lineLength;
     unsigned int lineLength;
@@ -1248,11 +1310,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
         }
         }
 
 
         currentLineLength += delimLength;
         currentLineLength += delimLength;
-        if (result == 0)
-        {
-            break;
-        }
-        else if (result == 2)
+        if (result == 0 || result == 2)
         {
         {
             outLocation->x = xPos;
             outLocation->x = xPos;
             outLocation->y = yPos;
             outLocation->y = yPos;
@@ -1261,7 +1319,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
 
 
         if (destIndex == (int)charIndex ||
         if (destIndex == (int)charIndex ||
             (destIndex == -1 &&
             (destIndex == -1 &&
-             inLocation.x >= xPos && inLocation.x < floor(xPos + ((float)size*0.125f)) &&
+             inLocation.x >= xPos && inLocation.x < floor(xPos + (float)(size >> 3)) &&
              inLocation.y >= yPos && inLocation.y < yPos + size))
              inLocation.y >= yPos && inLocation.y < yPos + size))
         {
         {
             outLocation->x = xPos;
             outLocation->x = xPos;
@@ -1292,9 +1350,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
         }
         }
 
 
         // Wrap if necessary.
         // Wrap if necessary.
-        if (wrap &&
-            xPos + tokenWidth > area.x + area.width ||
-            (rightToLeft && currentLineLength > lineLength))
+        if (wrap && (xPos + (int)tokenWidth > area.x + area.width || (rightToLeft && currentLineLength > lineLength)))
         {
         {
             yPos += size;
             yPos += size;
             currentLineLength = tokenLength;
             currentLineLength = tokenLength;
@@ -1334,7 +1390,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                 // Check against inLocation.
                 // Check against inLocation.
                 if (destIndex == (int)charIndex ||
                 if (destIndex == (int)charIndex ||
                     (destIndex == -1 &&
                     (destIndex == -1 &&
-                    inLocation.x >= xPos && inLocation.x < floor(xPos + g.width*scale + ((float)size*0.125f)) &&
+                    inLocation.x >= xPos && inLocation.x < floor(xPos + g.width*scale + (float)(size >> 3)) &&
                     inLocation.y >= yPos && inLocation.y < yPos + size))
                     inLocation.y >= yPos && inLocation.y < yPos + size))
                 {
                 {
                     outLocation->x = xPos;
                     outLocation->x = xPos;
@@ -1342,7 +1398,7 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
                     return charIndex;
                     return charIndex;
                 }
                 }
 
 
-                xPos += g.width*scale + ((float)size*0.125f);
+                xPos += floor(g.width*scale + (float)(size >> 3));
                 charIndex++;
                 charIndex++;
             }
             }
         }
         }
@@ -1413,9 +1469,18 @@ unsigned int Font::getIndexOrLocation(const char* text, const Rectangle& area, u
         }
         }
     }
     }
 
 
-    outLocation->x = xPos;
-    outLocation->y = yPos;
-    return charIndex;
+
+    if (destIndex == (int)charIndex ||
+        (destIndex == -1 &&
+         inLocation.x >= xPos && inLocation.x < floor(xPos + (float)(size >> 3)) &&
+         inLocation.y >= yPos && inLocation.y < yPos + size))
+    {
+        outLocation->x = xPos;
+        outLocation->y = yPos;
+        return charIndex;
+    }
+    
+    return -1;
 }
 }
 
 
 unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale)
 unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale)
@@ -1428,17 +1493,17 @@ unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigne
         switch (c)
         switch (c)
         {
         {
         case ' ':
         case ' ':
-            tokenWidth += (float)size*0.5f;
+            tokenWidth += size >> 1;
             break;
             break;
         case '\t':
         case '\t':
-            tokenWidth += ((float)size*0.5f)*4;
+            tokenWidth += (size >> 1)*4;
             break;
             break;
         default:
         default:
             int glyphIndex = c - 32;
             int glyphIndex = c - 32;
             if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
             if (glyphIndex >= 0 && glyphIndex < (int)_glyphCount)
             {
             {
                 Glyph& g = _glyphs[glyphIndex];
                 Glyph& g = _glyphs[glyphIndex];
-                tokenWidth += g.width * scale + ((float)size*0.125f);
+                tokenWidth += floor(g.width * scale + (float)(size >> 3));
             }
             }
             break;
             break;
         }
         }
@@ -1481,8 +1546,8 @@ int Font::handleDelimiters(const char** token, const unsigned int size, const in
             delimiter == 0)
             delimiter == 0)
     {
     {
         if ((stopAtPosition &&
         if ((stopAtPosition &&
-            stopAtPosition->x >= *xPos && stopAtPosition->x < floor(*xPos + ((float)size*0.5f)) &&
-            stopAtPosition->y >= *yPos && stopAtPosition->y < *yPos + size) ||
+            stopAtPosition->x >= *xPos && stopAtPosition->x < *xPos + ((int)size >> 1) &&
+            stopAtPosition->y >= *yPos && stopAtPosition->y < *yPos + (int)size) ||
             (currentIndex >= 0 && destIndex >= 0 && currentIndex + (int)*lineLength == destIndex))
             (currentIndex >= 0 && destIndex >= 0 && currentIndex + (int)*lineLength == destIndex))
         {
         {
             // Success + stopAtPosition was reached.
             // Success + stopAtPosition was reached.
@@ -1492,7 +1557,7 @@ int Font::handleDelimiters(const char** token, const unsigned int size, const in
         switch (delimiter)
         switch (delimiter)
         {
         {
             case ' ':
             case ' ':
-                *xPos += (float)size*0.5f;
+                *xPos += size >> 1;
                 (*lineLength)++;
                 (*lineLength)++;
                 if (charIndex)
                 if (charIndex)
                 {
                 {
@@ -1524,7 +1589,7 @@ int Font::handleDelimiters(const char** token, const unsigned int size, const in
                 }
                 }
                 break;
                 break;
             case '\t':
             case '\t':
-                *xPos += ((float)size*0.5f)*4;
+                *xPos += (size >> 1)*4;
                 (*lineLength)++;
                 (*lineLength)++;
                 if (charIndex)
                 if (charIndex)
                 {
                 {
@@ -1635,9 +1700,31 @@ Font::Justify Font::getJustify(const char* justify)
     {
     {
         return Font::ALIGN_BOTTOM_RIGHT;
         return Font::ALIGN_BOTTOM_RIGHT;
     }
     }
+    else
+    {
+        GP_ERROR("Failed to get corresponding font justification for unsupported value \'%s\'.", justify);
+    }
 
 
     // Default.
     // Default.
     return Font::ALIGN_TOP_LEFT;
     return Font::ALIGN_TOP_LEFT;
 }
 }
 
 
+Font::Text::Text(const char* text) : _text(text), _vertexCount(0), _vertices(NULL), _indexCount(0), _indices(NULL)
+{
+    const int length = strlen(text);
+    _vertices = new SpriteBatch::SpriteVertex[length * 4];
+    _indices = new unsigned short[((length - 1) * 6) + 4];
+}
+
+Font::Text::~Text()
+{
+    SAFE_DELETE_ARRAY(_vertices);
+    SAFE_DELETE_ARRAY(_indices);
+}
+
+const char* Font::Text::getText()
+{
+    return _text.c_str();
+}
+
 }
 }

+ 70 - 6
gameplay/src/Font.h

@@ -76,6 +76,40 @@ public:
         float uvs[4];
         float uvs[4];
     };
     };
 
 
+    /**
+     * Vertex coordinates, UVs and indices can be computed and stored in a Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     */
+    class Text
+    {
+        friend class Font;
+
+    public:
+        /**
+         * Constructor.
+         */
+        Text(const char* text);
+
+        /**
+         * Destructor.
+         */
+        ~Text();
+
+        /**
+         * Get the string that will be drawn from this Text object.
+         */
+        const char* getText();
+
+    private:
+        std::string _text;
+        unsigned int _vertexCount;
+        SpriteBatch::SpriteVertex* _vertices;
+        unsigned int _indexCount;
+        unsigned short* _indices;
+        Vector4 _color;
+    };
+
     /**
     /**
      * Creates a font from the given bundle.
      * Creates a font from the given bundle.
      *
      *
@@ -139,7 +173,7 @@ public:
 
 
     /**
     /**
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
-     * Clips text outside the viewport.  Optionally wraps text to fit within the width of the viewport.
+     * Clips text outside the viewport. Optionally wraps text to fit within the width of the viewport.
      *
      *
      * @param text The text to draw.
      * @param text The text to draw.
      * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
@@ -153,6 +187,33 @@ public:
     void drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0, 
     void drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0, 
                   Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
                   Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
 
 
+    /**
+     * Draw a string from a precomputed Text object.
+     *
+     * @param text The text to draw.
+     */
+    void drawText(Text* text);
+
+    /**
+     * Create an immutable Text object from a given string.
+     * Vertex coordinates, UVs and indices will be computed and stored in the Text object.
+     * For static text labels that do not change frequently, this means these computations
+     * need not be performed every frame.
+     *
+     * @param text The text to draw.
+     * @param area The viewport area to draw within.  Text will be clipped outside this rectangle.
+     * @param color The color of text.
+     * @param size The size to draw text (0 for default size).
+     * @param justify Justification of text within the viewport.
+     * @param wrap Wraps text to fit within the width of the viewport if true.
+     * @param rightToLeft Whether to draw text from right to left.
+     * @param clip A region to clip text within after applying justification to the viewport area.
+     *
+     * @return A Text object.
+     */
+    Text* createText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
+                     Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
+
     /**
     /**
      * Measures a string's width and height without alignment, wrapping or clipping.
      * Measures a string's width and height without alignment, wrapping or clipping.
      *
      *
@@ -179,10 +240,10 @@ public:
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
 
     /**
     /**
-     * Get an index into a string corresponding to the character nearest the given location within the clip region.
+     * Get an character index into a string corresponding to the character nearest the given location within the clip region.
      */
      */
-    unsigned int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                    Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+    int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                           Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
 
     /**
     /**
      * Get the location of the character at the given index.
      * Get the location of the character at the given index.
@@ -225,8 +286,11 @@ private:
      */
      */
     ~Font();
     ~Font();
 
 
-    unsigned int getIndexOrLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
-                                    const int destIndex = -1, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
+    void getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
+                            std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths);
+
+    int getIndexOrLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
+                           const int destIndex = -1, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
 
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
     unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
 
 

+ 419 - 282
gameplay/src/Form.cpp

@@ -1,6 +1,8 @@
 #include "Base.h"
 #include "Base.h"
 #include "Form.h"
 #include "Form.h"
 #include "AbsoluteLayout.h"
 #include "AbsoluteLayout.h"
+#include "FlowLayout.h"
+#include "ScrollLayout.h"
 #include "VerticalLayout.h"
 #include "VerticalLayout.h"
 #include "Game.h"
 #include "Game.h"
 #include "Theme.h"
 #include "Theme.h"
@@ -11,388 +13,523 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    static std::vector<Form*> __forms;
 
 
-    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL)
-    {
-    }
+static std::vector<Form*> __forms;
+
+Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _spriteBatch(NULL)
+{
+}
 
 
-    Form::Form(const Form& copy)
+Form::Form(const Form& copy)
+{
+}
+
+Form::~Form()
+{
+    SAFE_RELEASE(_quad);
+    SAFE_RELEASE(_node);
+    SAFE_RELEASE(_frameBuffer);
+    SAFE_RELEASE(_theme);
+    SAFE_DELETE(_spriteBatch);
+
+    // Remove this Form from the global list.
+    std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
+    if (it != __forms.end())
     {
     {
+        __forms.erase(it);
     }
     }
+}
 
 
-    Form::~Form()
+Form* Form::create(const char* url)
+{
+    // Load Form from .form file.
+    Properties* properties = Properties::create(url);
+    if (properties == NULL)
     {
     {
-        SAFE_RELEASE(_quad);
-        SAFE_RELEASE(_node);
-        SAFE_RELEASE(_frameBuffer);
-        SAFE_RELEASE(_theme);
-
-        // Remove this Form from the global list.
-        std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
-        if (it != __forms.end())
-        {
-            __forms.erase(it);
-        }
+        GP_ASSERT(properties);
+        return NULL;
     }
     }
 
 
-    Form* Form::create(const char* url)
+    // Check if the Properties is valid and has a valid namespace.
+    Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
+    assert(formProperties);
+    if (!formProperties || !(strcmp(formProperties->getNamespace(), "form") == 0))
     {
     {
-        // Load Form from .form file.
-        assert(url);
-
-        Properties* properties = Properties::create(url);
-        assert(properties);
-        if (properties == NULL)
-            return NULL;
-
-        // Check if the Properties is valid and has a valid namespace.
-        Properties* formProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-        assert(formProperties);
-        if (!formProperties || !(strcmp(formProperties->getNamespace(), "form") == 0))
-        {
-            SAFE_DELETE(properties);
-            return NULL;
-        }
+        GP_ASSERT(formProperties);
+        SAFE_DELETE(properties);
+        return NULL;
+    }
 
 
-        // Create new form with given ID, theme and layout.
-        const char* themeFile = formProperties->getString("theme");
-        const char* layoutString = formProperties->getString("layout");
+    // Create new form with given ID, theme and layout.
+    const char* themeFile = formProperties->getString("theme");
+    const char* layoutString = formProperties->getString("layout");
         
         
-        Layout* layout;
-        switch (getLayoutType(layoutString))
-        {
-        case Layout::LAYOUT_ABSOLUTE:
-            layout = AbsoluteLayout::create();
-            break;
-        case Layout::LAYOUT_FLOW:
-            break;
-        case Layout::LAYOUT_VERTICAL:
-            layout = VerticalLayout::create();
-            break;
-        }
-
-        assert(themeFile);
-        Theme* theme = Theme::create(themeFile);
-        assert(theme);
+    Layout* layout;
+    switch (getLayoutType(layoutString))
+    {
+    case Layout::LAYOUT_ABSOLUTE:
+        layout = AbsoluteLayout::create();
+        break;
+    case Layout::LAYOUT_FLOW:
+        layout = FlowLayout::create();
+        break;
+    case Layout::LAYOUT_VERTICAL:
+        layout = VerticalLayout::create();
+        break;
+    case Layout::LAYOUT_SCROLL:
+        layout = ScrollLayout::create();
+        break;
+    default:
+        GP_ERROR("Unsupported layout type \'%d\'.", getLayoutType(layoutString));
+    }
 
 
-        Form* form = new Form();
-        form->_layout = layout;
-        form->_theme = theme;
+    Theme* theme = Theme::create(themeFile);
+    GP_ASSERT(theme);
 
 
-        //Theme* theme = form->_theme;
-        const char* styleName = formProperties->getString("style");
-        form->initialize(theme->getStyle(styleName), formProperties);
+    Form* form = new Form();
+    form->_layout = layout;
+    form->_theme = theme;
 
 
-        if (form->_autoWidth)
-        {
-            form->_bounds.width = Game::getInstance()->getWidth();
-        }
+    const char* styleName = formProperties->getString("style");
+    form->initialize(theme->getStyle(styleName), formProperties);
 
 
-        if (form->_autoHeight)
-        {
-            form->_bounds.height = Game::getInstance()->getHeight();
-        }
+    // Add all the controls to the form.
+    form->addControls(theme, formProperties);
 
 
-        // Add all the controls to the form.
-        form->addControls(theme, formProperties);
+    Game* game = Game::getInstance();
+    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
 
-        SAFE_DELETE(properties);
+    SAFE_DELETE(properties);
 
 
-        __forms.push_back(form);
+    __forms.push_back(form);
 
 
-        return form;
-    }
+    return form;
+}
 
 
-    Form* Form::getForm(const char* id)
+Form* Form::getForm(const char* id)
+{
+    std::vector<Form*>::const_iterator it;
+    for (it = __forms.begin(); it < __forms.end(); it++)
     {
     {
-        std::vector<Form*>::const_iterator it;
-        for (it = __forms.begin(); it < __forms.end(); it++)
+        Form* f = *it;
+        GP_ASSERT(f);
+        if (strcmp(id, f->getID()) == 0)
         {
         {
-            Form* f = *it;
-            if (strcmp(id, f->getID()) == 0)
-            {
-                return f;
-            }
+            return f;
         }
         }
-        
-        return NULL;
     }
     }
+        
+    return NULL;
+}
 
 
-    void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
+void Form::setSize(float width, float height)
+{
+    if (_autoWidth)
     {
     {
-        Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
-        initializeQuad(mesh);
-        SAFE_RELEASE(mesh);
+        width = Game::getInstance()->getWidth();
     }
     }
 
 
-    void Form::setQuad(float x, float y, float width, float height)
+    if (_autoHeight)
     {
     {
-        Mesh* mesh = Mesh::createQuad(x, y, width, height);
-        initializeQuad(mesh);
-        SAFE_RELEASE(mesh);
+        height = Game::getInstance()->getHeight();
     }
     }
 
 
-    void Form::setNode(Node* node)
+    if (width != _bounds.width || height != _bounds.height)
     {
     {
-        _node = node;
+        // Width and height must be powers of two to create a texture.
+        int w = width;
+        int h = height;
+
+        if (!((w & (w - 1)) == 0))
+        {
+            w = nextHighestPowerOfTwo(w);
+        }
 
 
-        if (_node && !_quad)
+        if (!((h & (h - 1)) == 0))
         {
         {
-            // Set this Form up to be 3D by initializing a quad, projection matrix and viewport.
-            setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
+            h = nextHighestPowerOfTwo(h);
+        }
 
 
-            Matrix::createOrthographicOffCenter(0, _bounds.width, _bounds.height, 0, 0, 1, &_projectionMatrix);
-            _theme->setProjectionMatrix(_projectionMatrix);
-            
-            _node->setModel(_quad);
+        _u2 = width / (float)w;
+        _v1 = height / (float)h;
+
+        // Create framebuffer if necessary.
+        if (!_frameBuffer)
+        {
+            _frameBuffer = FrameBuffer::create(_id.c_str());
+            GP_ASSERT(_frameBuffer);
         }
         }
+     
+        // Re-create render target.
+        RenderTarget* rt = RenderTarget::create(_id.c_str(), w, h);
+        GP_ASSERT(rt);
+        _frameBuffer->setRenderTarget(rt);
+        SAFE_RELEASE(rt);
+
+        // Re-create projection matrix.
+        Matrix::createOrthographicOffCenter(0, width, height, 0, 0, 1, &_projectionMatrix);
+
+        // Re-create sprite batch.
+        SAFE_DELETE(_spriteBatch);
+        _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
+        GP_ASSERT(_spriteBatch);
+
+        _bounds.width = width;
+        _bounds.height = height;
+        _dirty = true;
     }
     }
+}
+
+void Form::setBounds(const Rectangle& bounds)
+{
+    setPosition(bounds.x, bounds.y);
+    setSize(bounds.width, bounds.height);
+}
 
 
-    void Form::update()
+void Form::setAutoWidth(bool autoWidth)
+{
+    if (_autoWidth != autoWidth)
     {
     {
-        if (isDirty())
+        _autoWidth = autoWidth;
+        _dirty = true;
+
+        if (_autoWidth)
         {
         {
-            Container::update(Rectangle(0, 0, _bounds.width, _bounds.height));
+            setSize(_bounds.width, Game::getInstance()->getWidth());
         }
         }
     }
     }
+}
 
 
-    void Form::draw()
+void Form::setAutoHeight(bool autoHeight)
+{
+    if (_autoHeight != autoHeight)
     {
     {
-        // If this form has a node then it's a 3D form.  The contents will be rendered
-        // into a framebuffer which will be used to texture a quad.  The quad will be
-        // given the same dimensions as the form and must be transformed appropriately
-        // by the user, unless they call setQuad() themselves.
+        _autoHeight = autoHeight;
+        _dirty = true;
 
 
-        // On the other hand, if this form has not been set on a node it will render
-        // directly to the display.
-
-        if (_node)
+        if (_autoHeight)
         {
         {
-            // Check whether this form has changed since the last call to draw()
-            // and if so, render into the framebuffer.
-            if (isDirty())
-            {
-                _frameBuffer->bind();
+            setSize(_bounds.width, Game::getInstance()->getHeight());
+        }
+    }
+}
 
 
-                Game* game = Game::getInstance();
-                Rectangle prevViewport = game->getViewport();
-                
-                game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
+void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
+{
+    Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
 
 
-                draw(_theme->getSpriteBatch(), _clip);
+    initializeQuad(mesh);
+    SAFE_RELEASE(mesh);
+}
 
 
-                // Rebind the default framebuffer and game viewport.
-                FrameBuffer::bindDefault();
+void Form::setQuad(float x, float y, float width, float height)
+{
+    float x2 = x + width;
+    float y2 = y + height;
 
 
-                // restore the previous game viewport
-                game->setViewport(prevViewport);
-            }
+    float vertices[] =
+    {
+        x, y2, 0,   0, _v1,
+        x, y, 0,    0, 0,
+        x2, y2, 0,  _u2, _v1,
+        x2, y, 0,   _u2, 0
+    };
 
 
-            _quad->draw();
-        }
-        else
-        {
-            draw(_theme->getSpriteBatch(), _clip);
-        }
+    VertexFormat::Element elements[] =
+    {
+        VertexFormat::Element(VertexFormat::POSITION, 3),
+        VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
+    };
+    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
+    assert(mesh);
+
+    mesh->setPrimitiveType(Mesh::TRIANGLE_STRIP);
+    mesh->setVertexData(vertices, 0, 4);
+
+    initializeQuad(mesh);
+    SAFE_RELEASE(mesh);
+}
+
+void Form::setNode(Node* node)
+{
+    _node = node;
+        
+    if (_node)
+    {
+        // Set this Form up to be 3D by initializing a quad.
+        setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
+        _node->setModel(_quad);
     }
     }
+}
 
 
-    void Form::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
+void Form::update()
+{
+    if (isDirty())
     {
     {
-        std::vector<Control*>::const_iterator it;
+        Container::update(Rectangle(0, 0, _bounds.width, _bounds.height), Vector2::zero());
+    }
+}
 
 
-        // Batch for all themed border and background sprites.
-        spriteBatch->begin();
+void Form::draw()
+{
+    /*
+    The first time a form is drawn, its contents are rendered into a framebuffer.
+    The framebuffer will only be drawn into again when the contents of the form change.
 
 
-        // Batch each font individually.
-        std::set<Font*>::const_iterator fontIter;
-        for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
-        {
-            Font* font = *fontIter;
-            if (font)
-            {
-                font->begin();
-            }
-        }
+    If this form has a node then it's a 3D form and the framebuffer will be used
+    to texture a quad.  The quad will be given the same dimensions as the form and
+    must be transformed appropriately by the user, unless they call setQuad() themselves.
 
 
-        // Draw the form's border and background.
-        // We don't pass the form's position to itself or it will be applied twice!
-        Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
+    On the other hand, if this form has not been set on a node, SpriteBatch will be used
+    to render the contents of the frambuffer directly to the display.
+    */
 
 
-        for (it = _controls.begin(); it < _controls.end(); it++)
-        {
-            Control* control = *it;
+    // Check whether this form has changed since the last call to draw()
+    // and if so, render into the framebuffer.
+    if (isDirty())
+    {
+        GP_ASSERT(_frameBuffer);
+        _frameBuffer->bind();
 
 
-            // Draw this control's border and background.
-            control->drawBorder(spriteBatch, clip);
+        Game* game = Game::getInstance();
+        Rectangle prevViewport = game->getViewport();
+        game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
 
 
-            // Add all themed foreground sprites (checkboxes etc.) to the same batch.
-            control->drawImages(spriteBatch, clip);
+        GP_ASSERT(_theme);
+        _theme->setProjectionMatrix(_projectionMatrix);
+        draw(_theme->getSpriteBatch(), _viewportClipBounds);
+        _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
 
-            control->drawText(clip);
-        }
+        // Rebind the default framebuffer and game viewport.
+        FrameBuffer::bindDefault();
 
 
-        // Done all batching.
-        spriteBatch->end();
+        // restore the previous game viewport
+        game->setViewport(prevViewport);
+    }
 
 
-        for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
+    if (_node)
+    {
+        GP_ASSERT(_quad);
+        _quad->draw();
+    }
+    else
+    {
+        if (!_spriteBatch)
         {
         {
-            Font* font = *fontIter;
-            if (font)
-            {
-                font->end();
-            }
+            _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
+            GP_ASSERT(_spriteBatch);
         }
         }
 
 
-        _dirty = false;
+        _spriteBatch->begin();
+        _spriteBatch->draw(_bounds.x, _bounds.y, 0, _bounds.width, _bounds.height, 0, _v1, _u2, 0, Vector4::one());
+        _spriteBatch->end();
     }
     }
+}
 
 
-    void Form::initializeQuad(Mesh* mesh)
-    {
-        // Release current model.
-        SAFE_RELEASE(_quad);
+void Form::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
+{
+    GP_ASSERT(spriteBatch);
 
 
-        // Create the model
-        _quad = Model::create(mesh);
+    std::vector<Control*>::const_iterator it;
 
 
-        // Create the material
-        Material* material = _quad->setMaterial("res/shaders/textured.vsh", "res/shaders/textured.fsh");
+    // Batch each font individually.
+    std::set<Font*>::const_iterator fontIter;
+    for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
+    {
+        Font* font = *fontIter;
+        if (font)
+        {
+            font->begin();
+        }
+    }
 
 
-        // Set the common render state block for the material
-        RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
-        stateBlock->setDepthWrite(true);
-        material->setStateBlock(stateBlock);
+    // Batch for all themed border and background sprites.
+    spriteBatch->begin();
 
 
-        // Bind the WorldViewProjection matrix
-        material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
+    // Draw the form's border and background.
+    // We don't pass the form's position to itself or it will be applied twice!
+    Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
 
 
-        // Create a FrameBuffer if necessary.
-        if (!_frameBuffer)
-        {
-            _frameBuffer = FrameBuffer::create(_id.c_str());
-        }
+    Rectangle boundsUnion = Rectangle::empty();
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
 
 
-        // Use the FrameBuffer to texture the quad.
-        if (!_frameBuffer->getRenderTarget())
+        if (_skin || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         {
         {
-            RenderTarget* rt = RenderTarget::create(_id.c_str(), _bounds.width, _bounds.height);
-            _frameBuffer->setRenderTarget(rt);
-            SAFE_RELEASE(rt);
+            control->draw(spriteBatch, clip, _skin == NULL, _bounds.height);
+            Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
         }
         }
+    }
 
 
-        Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
-        sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
-        material->getParameter("u_texture")->setValue(sampler);
-        material->getParameter("u_textureRepeat")->setValue(Vector2::one());
-        material->getParameter("u_textureTransform")->setValue(Vector2::zero());
+    // Done all batching.
+    spriteBatch->end();
 
 
-        SAFE_RELEASE(sampler);
+    for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
+    {
+        Font* font = *fontIter;
+        if (font)
+        {
+            font->end();
+        }
     }
     }
 
 
-    bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+    _dirty = false;
+}
+
+void Form::initializeQuad(Mesh* mesh)
+{
+    // Release current model.
+    SAFE_RELEASE(_quad);
+
+    // Create the model.
+    _quad = Model::create(mesh);
+
+    // Create the material.
+    Material* material = _quad->setMaterial("res/shaders/textured.vsh", "res/shaders/textured.fsh");
+    GP_ASSERT(material);
+
+    // Set the common render state block for the material.
+    GP_ASSERT(_theme);
+    GP_ASSERT(_theme->getSpriteBatch());
+    RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
+    GP_ASSERT(stateBlock);
+    stateBlock->setDepthWrite(true);
+    material->setStateBlock(stateBlock);
+
+    // Bind the WorldViewProjection matrix.
+    material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
+
+    // Bind the texture.
+    Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
+    GP_ASSERT(sampler);
+    sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
+    material->getParameter("u_diffuseTexture")->setValue(sampler);
+    material->getParameter("u_diffuseColor")->setValue(Vector4::one());
+
+    SAFE_RELEASE(sampler);
+}
+
+bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    // Check for a collision with each Form in __forms.
+    // Pass the event on.
+    std::vector<Form*>::const_iterator it;
+    for (it = __forms.begin(); it < __forms.end(); it++)
     {
     {
-        // Check for a collision with each Form in __forms.
-        // Pass the event on.
-        std::vector<Form*>::const_iterator it;
-        for (it = __forms.begin(); it < __forms.end(); it++)
-        {
-            Form* form = *it;
+        Form* form = *it;
+        GP_ASSERT(form);
 
 
-            if (form->isEnabled())
+        if (form->isEnabled())
+        {
+            Node* node = form->_node;
+            if (node)
             {
             {
-                Node* node = form->_node;
-                if (node)
-                {
-                    Scene* scene = node->getScene();
-                    Camera* camera = scene->getActiveCamera();
+                Scene* scene = node->getScene();
+                GP_ASSERT(scene);
+                Camera* camera = scene->getActiveCamera();
 
 
-                    if (camera)
+                if (camera)
+                {
+                    // Get info about the form's position.
+                    Matrix m = node->getMatrix();
+                    Vector3 min(0, 0, 0);
+                    m.transformPoint(&min);
+
+                    // Unproject point into world space.
+                    Ray ray;
+                    camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
+
+                    // Find the quad's plane.
+                    // We know its normal is the quad's forward vector.
+                    Vector3 normal = node->getForwardVectorWorld();
+
+                    // To get the plane's distance from the origin,
+                    // we'll find the distance from the plane defined
+                    // by the quad's forward vector and one of its points
+                    // to the plane defined by the same vector and the origin.
+                    const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
+                    const float d = -(a*min.x) - (b*min.y) - (c*min.z);
+                    const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
+                    Plane plane(normal, -distance);
+
+                    // Check for collision with plane.
+                    float collisionDistance = ray.intersects(plane);
+                    if (collisionDistance != Ray::INTERSECTS_NONE)
                     {
                     {
-                        // Get info about the form's position.
-                        Matrix m = node->getMatrix();
-                        Vector3 min(0, 0, 0);
-                        m.transformPoint(&min);
-
-                        // Unproject point into world space.
-                        Ray ray;
-                        camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
-
-                        // Find the quad's plane.
-                        // We know its normal is the quad's forward vector.
-                        Vector3 normal = node->getForwardVectorWorld();
-
-                        // To get the plane's distance from the origin,
-                        // we'll find the distance from the plane defined
-                        // by the quad's forward vector and one of its points
-                        // to the plane defined by the same vector and the origin.
-                        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-                        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-                        const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
-                        Plane plane(normal, -distance);
-
-                        // Check for collision with plane.
-                        float collisionDistance = ray.intersects(plane);
-                        if (collisionDistance != Ray::INTERSECTS_NONE)
+                        // Multiply the ray's direction vector by collision distance
+                        // and add that to the ray's origin.
+                        Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
+
+                        // Project this point into the plane.
+                        m.invert();
+                        m.transformPoint(&point);
+
+                        // Pass the touch event on.
+                        const Rectangle& bounds = form->getBounds();
+                        if (form->getState() == Control::FOCUS ||
+                            (evt == Touch::TOUCH_PRESS &&
+                                point.x >= bounds.x &&
+                                point.x <= bounds.x + bounds.width &&
+                                point.y >= bounds.y &&
+                                point.y <= bounds.y + bounds.height))
                         {
                         {
-                            // Multiply the ray's direction vector by collision distance
-                            // and add that to the ray's origin.
-                            Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
-
-                            // Project this point into the plane.
-                            m.invert();
-                            m.transformPoint(&point);
-
-                            // Pass the touch event on.
-                            const Rectangle& bounds = form->getClipBounds();
-                            if (form->getState() == Control::FOCUS ||
-                                (evt == Touch::TOUCH_PRESS &&
-                                 point.x >= bounds.x &&
-                                 point.x <= bounds.x + bounds.width &&
-                                 point.y >= bounds.y &&
-                                 point.y <= bounds.y + bounds.height))
+                            if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
                             {
                             {
-                               if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
-                               {
-                                   return true;
-                               }
+                                return true;
                             }
                             }
                         }
                         }
                     }
                     }
                 }
                 }
-                else
+            }
+            else
+            {
+                // Simply compare with the form's bounds.
+                const Rectangle& bounds = form->getBounds();
+                if (form->getState() == Control::FOCUS ||
+                    (evt == Touch::TOUCH_PRESS &&
+                        x >= bounds.x &&
+                        x <= bounds.x + bounds.width &&
+                        y >= bounds.y &&
+                        y <= bounds.y + bounds.height))
                 {
                 {
-                    // Simply compare with the form's bounds.
-                    const Rectangle& bounds = form->getClipBounds();
-                    if (form->getState() == Control::FOCUS ||
-                        (evt == Touch::TOUCH_PRESS &&
-                         x >= bounds.x &&
-                         x <= bounds.x + bounds.width &&
-                         y >= bounds.y &&
-                         y <= bounds.y + bounds.height))
+                    // Pass on the event's position relative to the form.
+                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
                     {
                     {
-                        // Pass on the event's position relative to the form.
-                        if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
-                        {
-                            return true;
-                        }
+                        return true;
                     }
                     }
                 }
                 }
             }
             }
         }
         }
-
-        return false;
     }
     }
 
 
-    void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
+    return false;
+}
+
+void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    std::vector<Form*>::const_iterator it;
+    for (it = __forms.begin(); it < __forms.end(); it++)
     {
     {
-        std::vector<Form*>::const_iterator it;
-        for (it = __forms.begin(); it < __forms.end(); it++)
+        Form* form = *it;
+        GP_ASSERT(form);
+        if (form->isEnabled())
         {
         {
-            Form* form = *it;
-            if (form->isEnabled())
-            {
-                form->keyEvent(evt, key);
-            }
+            form->keyEvent(evt, key);
         }
         }
     }
     }
-}
+}
+
+int Form::nextHighestPowerOfTwo(int x)
+{
+    x--;
+    x |= x >> 1;
+    x |= x >> 2;
+    x |= x >> 4;
+    x |= x >> 8;
+    x |= x >> 16;
+    return x + 1;
+}
+
+}

+ 36 - 0
gameplay/src/Form.h

@@ -67,6 +67,35 @@ public:
      */
      */
     static Form* getForm(const char* id);
     static Form* getForm(const char* id);
 
 
+    /**
+     * Set the desired size of this form.
+     *
+     * @param width The width.
+     * @param height The height.
+     */
+    virtual void setSize(float width, float height);
+
+    /**
+     * Set the bounds of this form.
+     *
+     * @param bounds The new bounds to set.
+     */
+    virtual void setBounds(const Rectangle& bounds);
+
+    /**
+     * Set this form's width to that of the display.
+     *
+     * @param autoWidth Whether to set this form's width to that of the display.
+     */
+    virtual void setAutoWidth(bool autoWidth);
+
+    /**
+     * Set this form's height to that of the display.
+     *
+     * @param autoHeight Whether to set this form's height to that of the display.
+     */
+    virtual void setAutoHeight(bool autoHeight);
+
     /**
     /**
      * Create a 3D quad to texture with this Form.
      * Create a 3D quad to texture with this Form.
      *
      *
@@ -157,11 +186,18 @@ private:
      */
      */
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 
 
+    static int nextHighestPowerOfTwo(int x);
+
     Theme* _theme;              // The Theme applied to this Form.
     Theme* _theme;              // The Theme applied to this Form.
     Model* _quad;               // Quad for rendering this Form in world-space.
     Model* _quad;               // Quad for rendering this Form in world-space.
     Node* _node;                // Node for transforming this Form in world-space.
     Node* _node;                // Node for transforming this Form in world-space.
     FrameBuffer* _frameBuffer;  // FBO the Form is rendered into for texturing the quad.
     FrameBuffer* _frameBuffer;  // FBO the Form is rendered into for texturing the quad.
     Matrix _projectionMatrix;   // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
     Matrix _projectionMatrix;   // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
+    Matrix _defaultProjectionMatrix;
+    SpriteBatch* _spriteBatch;
+
+    float _u2;
+    float _v1;
 };
 };
 
 
 }
 }

+ 1 - 1
gameplay/src/FrameBuffer.cpp

@@ -127,7 +127,7 @@ unsigned int FrameBuffer::getMaxRenderTargets()
 
 
 void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
 void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index)
 {
 {
-    assert(index < __maxRenderTargets);
+    GP_ASSERT(index < __maxRenderTargets);
 
 
     if (_renderTargets[index] == target)
     if (_renderTargets[index] == target)
     {
     {

+ 27 - 23
gameplay/src/Frustum.cpp

@@ -62,7 +62,7 @@ void Frustum::getMatrix(Matrix* dst) const
 
 
 void Frustum::getCorners(Vector3* corners) const
 void Frustum::getCorners(Vector3* corners) const
 {
 {
-    assert(corners);
+    GP_ASSERT(corners);
 
 
     Plane::intersection(_near, _left, _top, &corners[0]);
     Plane::intersection(_near, _left, _top, &corners[0]);
     Plane::intersection(_near, _left, _bottom, &corners[1]);
     Plane::intersection(_near, _left, _bottom, &corners[1]);
@@ -74,6 +74,24 @@ void Frustum::getCorners(Vector3* corners) const
     Plane::intersection(_far, _left, _top, &corners[7]);
     Plane::intersection(_far, _left, _top, &corners[7]);
 }
 }
 
 
+bool Frustum::intersects(const Vector3& point) const
+{
+    if (_near.distance(point) <= 0)
+        return false;
+    if (_far.distance(point) <= 0)
+        return false;
+    if (_left.distance(point) <= 0)
+        return false;
+    if (_right.distance(point) <= 0)
+        return false;
+    if (_top.distance(point) <= 0)
+        return false;
+    if (_bottom.distance(point) <= 0)
+        return false;
+
+    return true;
+}
+
 bool Frustum::intersects(const BoundingSphere& sphere) const
 bool Frustum::intersects(const BoundingSphere& sphere) const
 {
 {
     return sphere.intersects(*this);
     return sphere.intersects(*this);
@@ -105,23 +123,14 @@ void Frustum::set(const Frustum& frustum)
     _matrix.set(frustum._matrix);
     _matrix.set(frustum._matrix);
 }
 }
 
 
-void updatePlane(const Matrix& matrix, Plane* dst)
+void Frustum::updatePlanes()
 {
 {
-    assert(dst);
-
-    dst->setNormal(Vector3(matrix.m[3] + matrix.m[2], matrix.m[7] + matrix.m[6], matrix.m[11] + matrix.m[10]));
-    dst->setDistance(matrix.m[15] + matrix.m[14]);
-
-    Vector3 normal = dst->getNormal();
-    if (!normal.isZero())
-    {
-        float normalizeFactor = 1.0f / sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
-        if (normalizeFactor != 1.0f)
-        {
-            dst->setNormal(Vector3(normal.x * normalizeFactor, normal.y * normalizeFactor, normal.z * normalizeFactor));
-            dst->setDistance(dst->getDistance() * normalizeFactor);
-        }
-    }
+    _near.set(Vector3(_matrix.m[3] + _matrix.m[2], _matrix.m[7] + _matrix.m[6], _matrix.m[11] + _matrix.m[10]), _matrix.m[15] + _matrix.m[14]);
+    _far.set(Vector3(_matrix.m[3] - _matrix.m[2], _matrix.m[7] - _matrix.m[6], _matrix.m[11] - _matrix.m[10]), _matrix.m[15] - _matrix.m[14]);
+    _bottom.set(Vector3(_matrix.m[3] + _matrix.m[1], _matrix.m[7] + _matrix.m[5], _matrix.m[11] + _matrix.m[9]), _matrix.m[15] + _matrix.m[13]);
+    _top.set(Vector3(_matrix.m[3] - _matrix.m[1], _matrix.m[7] - _matrix.m[5], _matrix.m[11] - _matrix.m[9]), _matrix.m[15] - _matrix.m[13]);
+    _left.set(Vector3(_matrix.m[3] + _matrix.m[0], _matrix.m[7] + _matrix.m[4], _matrix.m[11] + _matrix.m[8]), _matrix.m[15] + _matrix.m[12]);
+    _right.set(Vector3(_matrix.m[3] - _matrix.m[0], _matrix.m[7] - _matrix.m[4], _matrix.m[11] - _matrix.m[8]), _matrix.m[15] - _matrix.m[12]);
 }
 }
 
 
 void Frustum::set(const Matrix& matrix)
 void Frustum::set(const Matrix& matrix)
@@ -129,12 +138,7 @@ void Frustum::set(const Matrix& matrix)
     _matrix.set(matrix);
     _matrix.set(matrix);
 
 
     // Update the planes.
     // Update the planes.
-    updatePlane(matrix, &_near);
-    updatePlane(matrix, &_far);
-    updatePlane(matrix, &_bottom);
-    updatePlane(matrix, &_top);
-    updatePlane(matrix, &_left);
-    updatePlane(matrix, &_right);
+    updatePlanes();
 }
 }
 
 
 }
 }

+ 9 - 0
gameplay/src/Frustum.h

@@ -113,6 +113,15 @@ public:
      */
      */
     void getCorners(Vector3* corners) const;
     void getCorners(Vector3* corners) const;
 
 
+    /**
+     * Tests whether this frustum instersects the specified point.
+     *
+     * @param point The point to test intersection with.
+     *
+     * @return true if the specified point intersects this frustum; false otherwise.
+     */
+    bool intersects(const Vector3& point) const;
+
     /**
     /**
      * Tests whether this frustum intersects the specified bounding sphere.
      * Tests whether this frustum intersects the specified bounding sphere.
      *
      *

+ 4 - 2
gameplay/src/Game.cpp

@@ -5,6 +5,7 @@
 
 
 // Extern global variables
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
 GLenum __gl_error_code = GL_NO_ERROR;
+ALenum __al_error_code = AL_NO_ERROR;
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -19,7 +20,7 @@ Game::Game()
       _clearDepth(1.0f), _clearStencil(0),
       _clearDepth(1.0f), _clearStencil(0),
       _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
       _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
 {
 {
-    assert(__gameInstance == NULL);
+    GP_ASSERT(__gameInstance == NULL);
     __gameInstance = this;
     __gameInstance = this;
     _timeEvents = new std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >();
     _timeEvents = new std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >();
 }
 }
@@ -41,6 +42,7 @@ Game::~Game()
 
 
 Game* Game::getInstance()
 Game* Game::getInstance()
 {
 {
+    GP_ASSERT(__gameInstance);
     return __gameInstance;
     return __gameInstance;
 }
 }
 
 
@@ -288,7 +290,7 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 
 
 void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
 void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
 {
 {
-    assert(timeListener);
+    GP_ASSERT(timeListener);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
     _timeEvents->push(timeEvent);
     _timeEvents->push(timeEvent);
 }
 }

+ 2 - 2
gameplay/src/Image.cpp

@@ -18,7 +18,7 @@ Image* Image::create(const char* path)
     unsigned char sig[8];
     unsigned char sig[8];
     if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     {
     {
-        LOG_ERROR_VARG("Texture is not a valid PNG: %s", path);
+        GP_ERROR("Texture is not a valid PNG: %s", path);
         fclose(fp);
         fclose(fp);
         return NULL;
         return NULL;
     }
     }
@@ -73,7 +73,7 @@ Image* Image::create(const char* path)
         break;
         break;
 
 
     default:
     default:
-        LOG_ERROR_VARG("Unsupported PNG color type (%d) for texture: %s", (int)colorType, path);
+        GP_ERROR("Unsupported PNG color type (%d) for texture: %s", (int)colorType, path);
         fclose(fp);
         fclose(fp);
         png_destroy_read_struct(&png, &info, NULL);
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
         return NULL;

+ 70 - 64
gameplay/src/Label.cpp

@@ -3,87 +3,93 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    Label::Label() : _text(""), _font(NULL)
-    {
-    }
 
 
-    Label::Label(const Label& copy)
-    {
-    }
+Label::Label() : _text(""), _font(NULL)
+{
+}
 
 
-    Label::~Label()
-    {
-    }
+Label::Label(const Label& copy)
+{
+}
 
 
-    Label* Label::create(Theme::Style* style, Properties* properties)
-    {
-        Label* label = new Label();
-        label->initialize(style, properties);
+Label::~Label()
+{
+}
 
 
-        return label;
-    }
+Label* Label::create(Theme::Style* style, Properties* properties)
+{
+    Label* label = new Label();
+    label->initialize(style, properties);
+    label->_consumeTouchEvents = false;
 
 
-    void Label::initialize(Theme::Style* style, Properties* properties)
-    {
-        Control::initialize(style, properties);
+    return label;
+}
 
 
-        const char* text = properties->getString("text");
-        if (text)
-        {
-            _text = text;
-        }
-    }
+void Label::initialize(Theme::Style* style, Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    Control::initialize(style, properties);
 
 
-    void Label::addListener(Control::Listener* listener, int eventFlags)
+    const char* text = properties->getString("text");
+    if (text)
     {
     {
-        if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
-        {
-            assert("TEXT_CHANGED event is not applicable to this control.");
-            eventFlags &= ~Listener::TEXT_CHANGED;
-        }
-
-        if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
-        {
-            assert("VALUE_CHANGED event is not applicable to this control.");
-            eventFlags &= ~Listener::VALUE_CHANGED;
-        }
-
-        Control::addListener(listener, eventFlags);
+        _text = text;
     }
     }
-    
-    void Label::setText(const char* text)
+}
+
+void Label::addListener(Control::Listener* listener, int eventFlags)
+{
+    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     {
     {
-        if (text)
-        {
-            _text = text;
-        }
+        GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
     }
-
-    const char* Label::getText()
+    if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
     {
     {
-        return _text.c_str();
+        GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
     }
     }
 
 
-    void Label::update(const Rectangle& clip)
-    {
-        Control::update(clip);
+    _consumeTouchEvents = true;
 
 
-        _font = getFont(_state);
-        _textColor = getTextColor(_state);
-        _textColor.w *= getOpacity(_state);
-    }
+    Control::addListener(listener, eventFlags);
+}
+    
+void Label::setText(const char* text)
+{
+    assert(text);
 
 
-    void Label::drawText(const Rectangle& clip)
-    {
-        if (_text.size() <= 0)
-            return;
+    _text = text;
+    _dirty = true;
+}
 
 
-        // Draw the text.
-        if (_font)
-        {
-            _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_clip);
-        }
+const char* Label::getText()
+{
+    return _text.c_str();
+}
 
 
-        _dirty = false;
+void Label::update(const Rectangle& clip, const Vector2& offset)
+{
+    Control::update(clip, offset);
+
+    _textBounds.set(_viewportBounds);
+
+    _font = getFont(_state);
+    _textColor = getTextColor(_state);
+    _textColor.w *= getOpacity(_state);
+}
+
+void Label::drawText(const Rectangle& clip)
+{
+    if (_text.size() <= 0)
+        return;
+
+    // Draw the text.
+    if (_font)
+    {
+        _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
     }
     }
+
+    _dirty = false;
+}
+
 }
 }

+ 3 - 1
gameplay/src/Label.h

@@ -91,8 +91,9 @@ protected:
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
      * @param clip The clipping rectangle of this label's parent container.
      * @param clip The clipping rectangle of this label's parent container.
+     * @param offset The scroll offset of this label's parent container.
      */
      */
-    void update(const Rectangle& clip);
+    void update(const Rectangle& clip, const Vector2& offset);
 
 
     /**
     /**
      * Draw this label's text.
      * Draw this label's text.
@@ -115,6 +116,7 @@ protected:
      * The text color being used to display the label.
      * The text color being used to display the label.
      */
      */
     Vector4 _textColor;
     Vector4 _textColor;
+    Rectangle _textBounds;  // The position and size of this control's text area, before clipping.  Used for text alignment.
 
 
 private:
 private:
 
 

+ 54 - 43
gameplay/src/Layout.cpp

@@ -5,50 +5,61 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    void Layout::align(Control* control, const Container* container)
-    {
-        if (control->_alignment != Control::ALIGN_TOP_LEFT ||
-            control->_autoWidth || control->_autoHeight)
+
+void Layout::align(Control* control, const Container* container)
+{
+    GP_ASSERT(control);
+    GP_ASSERT(container);
+
+    if (control->_alignment != Control::ALIGN_TOP_LEFT ||
+        control->_autoWidth || control->_autoHeight)
+    {
+        Rectangle controlBounds = control->getBounds();
+        const Theme::Margin& controlMargin = control->getMargin();
+        const Rectangle& containerBounds = container->getBounds();
+        const Theme::Border& containerBorder = container->getBorder(container->getState());
+        const Theme::Padding& containerPadding = container->getPadding();
+
+        float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right;
+        float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
+
+        if (control->_autoWidth)
+        {
+            controlBounds.width = clipWidth;
+        }
+
+        if (control->_autoHeight)
+        {
+            controlBounds.height = clipHeight;
+        }
+
+        // Vertical alignment
+        if ((control->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
         {
         {
-            Rectangle controlBounds = control->getBounds();
-            const Rectangle& containerBounds = container->getClipBounds();
-            const Theme::Border& containerBorder = container->getBorder(container->getState());
-            const Theme::Padding& containerPadding = container->getPadding();
-
-            float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right;
-            float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
-
-            if (control->_autoWidth)
-            {
-                controlBounds.width = clipWidth;
-            }
-
-            if (control->_autoHeight)
-            {
-                controlBounds.height = clipHeight;
-            }
-
-            // Vertical alignment
-            if ((control->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
-            {
-                controlBounds.y = clipHeight - controlBounds.height;
-            }
-            else if ((control->_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
-            {
-                controlBounds.y = clipHeight * 0.5f - controlBounds.height * 0.5f;
-            }
-
-            // Horizontal alignment
-            if ((control->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
-            {
-                controlBounds.x = clipWidth - controlBounds.width;
-            }
-            else if ((control->_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
-            {
-                controlBounds.x = clipWidth * 0.5f - controlBounds.width * 0.5f;
-            }
-
-            control->setBounds(controlBounds);
+            controlBounds.y = clipHeight - controlBounds.height;
         }
         }
+        else if ((control->_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
+        {
+            controlBounds.y = clipHeight * 0.5f - controlBounds.height * 0.5f;
+        }
+
+        // Horizontal alignment
+        if ((control->_alignment & Control::ALIGN_RIGHT) == Control::ALIGN_RIGHT)
+        {
+            controlBounds.x = clipWidth - controlBounds.width - controlMargin.right;
+        }
+        else if ((control->_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
+        {
+            controlBounds.x = clipWidth * 0.5f - controlBounds.width * 0.5f;
+        }
+
+        control->setBounds(controlBounds);
     }
     }
+}
+
+bool Layout::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    return false;
+}
+
 }
 }

+ 23 - 1
gameplay/src/Layout.h

@@ -2,6 +2,7 @@
 #define LAYOUT_H_
 #define LAYOUT_H_
 
 
 #include "Ref.h"
 #include "Ref.h"
+#include "Touch.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -42,7 +43,15 @@ public:
          * Absolute layout: Controls are not modified at all by this layout.
          * Absolute layout: Controls are not modified at all by this layout.
          * They must be positioned and sized manually.
          * They must be positioned and sized manually.
          */
          */
-        LAYOUT_ABSOLUTE
+        LAYOUT_ABSOLUTE,
+
+        /**
+         * Scroll layout: Controls may be placed outside the bounds of the container.
+         * The user can then touch and drag to scroll.  By default controls are placed
+         * based on absolute positions in the .form file, but vertical or horizontal
+         * automatic positioning is an available option.
+         */
+        LAYOUT_SCROLL
     };
     };
 
 
     /**
     /**
@@ -67,6 +76,19 @@ protected:
      * @param container The container to align the control within.
      * @param container The container to align the control within.
      */
      */
     virtual void align(Control* control, const Container* container);
     virtual void align(Control* control, const Container* container);
+
+    /**
+     * Touch callback on touch events.  Coordinates are given relative to the container's
+     * content area.
+     *
+     * @param evt The touch event that occurred.
+     * @param x The x position of the touch in pixels. Left edge is zero.
+     * @param y The y position of the touch in pixels. Top edge is zero.
+     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
+     *
+     * @see Touch::TouchEvent
+     */
+    virtual bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 };
 };
 
 
 }
 }

+ 14 - 14
gameplay/src/Light.cpp

@@ -81,7 +81,7 @@ const Vector3& Light::getColor() const
     case SPOT:
     case SPOT:
         return _spot->color;
         return _spot->color;
     default:
     default:
-        assert(0);
+        GP_ASSERT(0);
         return Vector3::zero();
         return Vector3::zero();
 
 
     }
     }
@@ -105,7 +105,7 @@ void Light::setColor(const Vector3& color)
 
 
 float Light::getRange()  const
 float Light::getRange()  const
 {
 {
-    assert(_type != DIRECTIONAL);
+    GP_ASSERT(_type != DIRECTIONAL);
 
 
     switch (_type)
     switch (_type)
     {
     {
@@ -114,14 +114,14 @@ float Light::getRange()  const
     case SPOT:
     case SPOT:
         return _spot->range;
         return _spot->range;
     default:
     default:
-        assert(0);
+        GP_ASSERT(0);
         return 0.0f;
         return 0.0f;
     }
     }
 }
 }
     
     
 void Light::setRange(float range)
 void Light::setRange(float range)
 {
 {
-    assert(_type != DIRECTIONAL);
+    GP_ASSERT(_type != DIRECTIONAL);
 
 
     switch (_type)
     switch (_type)
     {
     {
@@ -138,7 +138,7 @@ void Light::setRange(float range)
 
 
 float Light::getRangeInverse() const
 float Light::getRangeInverse() const
 {
 {
-    assert(_type != DIRECTIONAL);
+    GP_ASSERT(_type != DIRECTIONAL);
 
 
     switch (_type)
     switch (_type)
     {
     {
@@ -147,21 +147,21 @@ float Light::getRangeInverse() const
     case SPOT:
     case SPOT:
         return _spot->rangeInverse;
         return _spot->rangeInverse;
     default:
     default:
-        assert(0);
+        GP_ASSERT(0);
         return 0.0f;
         return 0.0f;
     }
     }
 }
 }
     
     
 float Light::getInnerAngle()  const
 float Light::getInnerAngle()  const
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     return _spot->innerAngle;
     return _spot->innerAngle;
 }
 }
 
 
 void Light::setInnerAngle(float innerAngle)
 void Light::setInnerAngle(float innerAngle)
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     _spot->innerAngle = innerAngle;
     _spot->innerAngle = innerAngle;
     _spot->innerAngleCos = cos(innerAngle);
     _spot->innerAngleCos = cos(innerAngle);
@@ -169,14 +169,14 @@ void Light::setInnerAngle(float innerAngle)
     
     
 float Light::getOuterAngle()  const
 float Light::getOuterAngle()  const
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     return _spot->outerAngle;
     return _spot->outerAngle;
 }
 }
 
 
 void Light::setOuterAngle(float outerAngle)
 void Light::setOuterAngle(float outerAngle)
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     _spot->outerAngle = outerAngle;
     _spot->outerAngle = outerAngle;
     _spot->outerAngleCos = cos(outerAngle);
     _spot->outerAngleCos = cos(outerAngle);
@@ -184,14 +184,14 @@ void Light::setOuterAngle(float outerAngle)
     
     
 float Light::getInnerAngleCos()  const
 float Light::getInnerAngleCos()  const
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     return _spot->innerAngleCos;
     return _spot->innerAngleCos;
 }
 }
     
     
 float Light::getOuterAngleCos()  const
 float Light::getOuterAngleCos()  const
 {
 {
-    assert(_type == SPOT);
+    GP_ASSERT(_type == SPOT);
 
 
     return _spot->outerAngleCos;
     return _spot->outerAngleCos;
 }
 }
@@ -211,9 +211,9 @@ Light* Light::clone(NodeCloneContext &context) const
         lightClone = createSpot(getColor(), getRange(), getInnerAngle(), getOuterAngle());
         lightClone = createSpot(getColor(), getRange(), getInnerAngle(), getOuterAngle());
         break;
         break;
     default:
     default:
-        assert(false);
+        GP_ASSERT(false);
     }
     }
-    assert(lightClone);
+    GP_ASSERT(lightClone);
 
 
     if (Node* node = context.findClonedNode(getNode()))
     if (Node* node = context.findClonedNode(getNode()))
     {
     {

+ 6 - 6
gameplay/src/Material.cpp

@@ -34,11 +34,11 @@ Material::~Material()
 
 
 Material* Material::create(const char* url)
 Material* Material::create(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     // Load the material properties from file
     // Load the material properties from file
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    assert(properties);
+    GP_ASSERT(properties);
     if (properties == NULL)
     if (properties == NULL)
     {
     {
         return NULL;
         return NULL;
@@ -53,7 +53,7 @@ Material* Material::create(const char* url)
 Material* Material::create(Properties* materialProperties)
 Material* Material::create(Properties* materialProperties)
 {
 {
     // Check if the Properties is valid and has a valid namespace.
     // Check if the Properties is valid and has a valid namespace.
-    assert(materialProperties);
+    GP_ASSERT(materialProperties);
     if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
     if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
     {
     {
         return NULL;
         return NULL;
@@ -151,7 +151,7 @@ unsigned int Material::getTechniqueCount() const
 
 
 Technique* Material::getTechnique(unsigned int index) const
 Technique* Material::getTechnique(unsigned int index) const
 {
 {
-    assert(index < _techniques.size());
+    GP_ASSERT(index < _techniques.size());
 
 
     return _techniques[index];
     return _techniques[index];
 }
 }
@@ -227,9 +227,9 @@ bool Material::loadPass(Technique* technique, Properties* passProperties)
 {
 {
     // Fetch shader info required to create the effect of this technique.
     // Fetch shader info required to create the effect of this technique.
     const char* vertexShaderPath = passProperties->getString("vertexShader");
     const char* vertexShaderPath = passProperties->getString("vertexShader");
-    assert(vertexShaderPath);
+    GP_ASSERT(vertexShaderPath);
     const char* fragmentShaderPath = passProperties->getString("fragmentShader");
     const char* fragmentShaderPath = passProperties->getString("fragmentShader");
-    assert(fragmentShaderPath);
+    GP_ASSERT(fragmentShaderPath);
     const char* defines = passProperties->getString("defines");
     const char* defines = passProperties->getString("defines");
     std::string define;
     std::string define;
     if (defines != NULL)
     if (defines != NULL)

+ 10 - 10
gameplay/src/MaterialParameter.cpp

@@ -232,7 +232,7 @@ void MaterialParameter::bind(Effect* effect)
         if (!_uniform)
         if (!_uniform)
         {
         {
             // This parameter was not found in the specified effect, so do nothing.
             // This parameter was not found in the specified effect, so do nothing.
-            WARN_VARG("Warning: Material parameter '%s' not found in effect '%s'.", _name.c_str(), effect->getId());
+            GP_WARN("Warning: Material parameter '%s' not found in effect '%s'.", _name.c_str(), effect->getId());
             return;
             return;
         }
         }
     }
     }
@@ -246,7 +246,7 @@ void MaterialParameter::bind(Effect* effect)
         }
         }
         else
         else
         {
         {
-            assert(_value.floatPtrValue);
+            GP_ASSERT(_value.floatPtrValue);
             effect->setValue(_uniform, _value.floatPtrValue, _count);
             effect->setValue(_uniform, _value.floatPtrValue, _count);
         }
         }
         break;
         break;
@@ -257,32 +257,32 @@ void MaterialParameter::bind(Effect* effect)
         }
         }
         else
         else
         {
         {
-            assert(_value.intPtrValue);
+            GP_ASSERT(_value.intPtrValue);
             effect->setValue(_uniform, _value.intPtrValue, _count);
             effect->setValue(_uniform, _value.intPtrValue, _count);
         }
         }
         break;
         break;
     case MaterialParameter::VECTOR2:
     case MaterialParameter::VECTOR2:
-        assert(_value.floatPtrValue);
+        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector2*>(_value.floatPtrValue), _count);
         effect->setValue(_uniform, reinterpret_cast<Vector2*>(_value.floatPtrValue), _count);
         break;
         break;
     case MaterialParameter::VECTOR3:
     case MaterialParameter::VECTOR3:
-        assert(_value.floatPtrValue);
+        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector3*>(_value.floatPtrValue), _count);
         effect->setValue(_uniform, reinterpret_cast<Vector3*>(_value.floatPtrValue), _count);
         break;
         break;
     case MaterialParameter::VECTOR4:
     case MaterialParameter::VECTOR4:
-        assert(_value.floatPtrValue);
+        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Vector4*>(_value.floatPtrValue), _count);
         effect->setValue(_uniform, reinterpret_cast<Vector4*>(_value.floatPtrValue), _count);
         break;
         break;
     case MaterialParameter::MATRIX:
     case MaterialParameter::MATRIX:
-        assert(_value.floatPtrValue);
+        GP_ASSERT(_value.floatPtrValue);
         effect->setValue(_uniform, reinterpret_cast<Matrix*>(_value.floatPtrValue), _count);
         effect->setValue(_uniform, reinterpret_cast<Matrix*>(_value.floatPtrValue), _count);
         break;
         break;
     case MaterialParameter::SAMPLER:
     case MaterialParameter::SAMPLER:
-        assert(_value.samplerValue);
+        GP_ASSERT(_value.samplerValue);
         effect->setValue(_uniform, _value.samplerValue);
         effect->setValue(_uniform, _value.samplerValue);
         break;
         break;
     case MaterialParameter::METHOD:
     case MaterialParameter::METHOD:
-        assert(_value.method);
+        GP_ASSERT(_value.method);
         _value.method->setValue(effect);
         _value.method->setValue(effect);
         break;
         break;
     }
     }
@@ -383,7 +383,7 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
 
 
 void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
 {
-    assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
+    GP_ASSERT(blendWeight >= 0.0f && blendWeight <= 1.0f);
 
 
     switch (propertyId)
     switch (propertyId)
     {
     {

+ 29 - 29
gameplay/src/Matrix.cpp

@@ -68,7 +68,7 @@ void Matrix::createLookAt(float eyePositionX, float eyePositionY, float eyePosit
                           float targetPositionX, float targetPositionY, float targetPositionZ,
                           float targetPositionX, float targetPositionY, float targetPositionZ,
                           float upX, float upY, float upZ, Matrix* dst)
                           float upX, float upY, float upZ, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     Vector3 eye(eyePositionX, eyePositionY, eyePositionZ);
     Vector3 eye(eyePositionX, eyePositionY, eyePositionZ);
     Vector3 target(targetPositionX, targetPositionY, targetPositionZ);
     Vector3 target(targetPositionX, targetPositionY, targetPositionZ);
@@ -111,7 +111,7 @@ void Matrix::createLookAt(float eyePositionX, float eyePositionY, float eyePosit
 void Matrix::createPerspective(float fieldOfView, float aspectRatio,
 void Matrix::createPerspective(float fieldOfView, float aspectRatio,
                                      float zNearPlane, float zFarPlane, Matrix* dst)
                                      float zNearPlane, float zFarPlane, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     float f_n = 1.0f / (zFarPlane - zNearPlane);
     float f_n = 1.0f / (zFarPlane - zNearPlane);
     float factor = 1.0f / tanf(MATH_DEG_TO_RAD(fieldOfView) * 0.5f);
     float factor = 1.0f / tanf(MATH_DEG_TO_RAD(fieldOfView) * 0.5f);
@@ -135,7 +135,7 @@ void Matrix::createOrthographic(float width, float height, float zNearPlane, flo
 void Matrix::createOrthographicOffCenter(float left, float right, float bottom, float top,
 void Matrix::createOrthographicOffCenter(float left, float right, float bottom, float top,
                                          float zNearPlane, float zFarPlane, Matrix* dst)
                                          float zNearPlane, float zFarPlane, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     float r_l = 1.0f / (right - left);
     float r_l = 1.0f / (right - left);
     float t_b = 1.0f / (top - bottom);
     float t_b = 1.0f / (top - bottom);
@@ -153,7 +153,7 @@ void Matrix::createOrthographicOffCenter(float left, float right, float bottom,
 
 
 void Matrix::createScale(const Vector3& scale, Matrix* dst)
 void Matrix::createScale(const Vector3& scale, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -164,7 +164,7 @@ void Matrix::createScale(const Vector3& scale, Matrix* dst)
 
 
 void Matrix::createScale(float xScale, float yScale, float zScale, Matrix* dst)
 void Matrix::createScale(float xScale, float yScale, float zScale, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -176,7 +176,7 @@ void Matrix::createScale(float xScale, float yScale, float zScale, Matrix* dst)
 
 
 void Matrix::createRotation(const Quaternion& q, Matrix* dst)
 void Matrix::createRotation(const Quaternion& q, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     float x2 = q.x + q.x;
     float x2 = q.x + q.x;
     float y2 = q.y + q.y;
     float y2 = q.y + q.y;
@@ -215,7 +215,7 @@ void Matrix::createRotation(const Quaternion& q, Matrix* dst)
 
 
 void Matrix::createRotation(const Vector3& axis, float angle, Matrix* dst)
 void Matrix::createRotation(const Vector3& axis, float angle, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     float x = axis.x;
     float x = axis.x;
     float y = axis.y;
     float y = axis.y;
@@ -274,7 +274,7 @@ void Matrix::createRotation(const Vector3& axis, float angle, Matrix* dst)
 
 
 void Matrix::createRotationX(float angle, Matrix* dst)
 void Matrix::createRotationX(float angle, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -289,7 +289,7 @@ void Matrix::createRotationX(float angle, Matrix* dst)
 
 
 void Matrix::createRotationY(float angle, Matrix* dst)
 void Matrix::createRotationY(float angle, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -304,7 +304,7 @@ void Matrix::createRotationY(float angle, Matrix* dst)
 
 
 void Matrix::createRotationZ(float angle, Matrix* dst)
 void Matrix::createRotationZ(float angle, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -319,7 +319,7 @@ void Matrix::createRotationZ(float angle, Matrix* dst)
 
 
 void Matrix::createTranslation(const Vector3& translation, Matrix* dst)
 void Matrix::createTranslation(const Vector3& translation, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -330,7 +330,7 @@ void Matrix::createTranslation(const Vector3& translation, Matrix* dst)
 
 
 void Matrix::createTranslation(float xTranslation, float yTranslation, float zTranslation, Matrix* dst)
 void Matrix::createTranslation(float xTranslation, float yTranslation, float zTranslation, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
     memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
 
 
@@ -346,7 +346,7 @@ void Matrix::add(float scalar)
 
 
 void Matrix::add(float scalar, Matrix* dst)
 void Matrix::add(float scalar, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->m[0]  = m[0]  + scalar;
     dst->m[0]  = m[0]  + scalar;
     dst->m[1]  = m[1]  + scalar;
     dst->m[1]  = m[1]  + scalar;
@@ -373,7 +373,7 @@ void Matrix::add(const Matrix& m)
 
 
 void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
 void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->m[0]  = m1.m[0]  + m2.m[0];
     dst->m[0]  = m1.m[0]  + m2.m[0];
     dst->m[1]  = m1.m[1]  + m2.m[1];
     dst->m[1]  = m1.m[1]  + m2.m[1];
@@ -535,7 +535,7 @@ void Matrix::getTranslation(Vector3* translation) const
 
 
 void Matrix::getUpVector(Vector3* dst) const
 void Matrix::getUpVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->x = m[4];
     dst->x = m[4];
     dst->y = m[5];
     dst->y = m[5];
@@ -544,7 +544,7 @@ void Matrix::getUpVector(Vector3* dst) const
 
 
 void Matrix::getDownVector(Vector3* dst) const
 void Matrix::getDownVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
     dst->x = -m[4];
     dst->x = -m[4];
     dst->y = -m[5];
     dst->y = -m[5];
     dst->z = -m[6];
     dst->z = -m[6];
@@ -552,7 +552,7 @@ void Matrix::getDownVector(Vector3* dst) const
 
 
 void Matrix::getLeftVector(Vector3* dst) const
 void Matrix::getLeftVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->x = -m[0];
     dst->x = -m[0];
     dst->y = -m[1];
     dst->y = -m[1];
@@ -561,7 +561,7 @@ void Matrix::getLeftVector(Vector3* dst) const
 
 
 void Matrix::getRightVector(Vector3* dst) const
 void Matrix::getRightVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->x = m[0];
     dst->x = m[0];
     dst->y = m[1];
     dst->y = m[1];
@@ -570,7 +570,7 @@ void Matrix::getRightVector(Vector3* dst) const
 
 
 void Matrix::getForwardVector(Vector3* dst) const
 void Matrix::getForwardVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->x = -m[8];
     dst->x = -m[8];
     dst->y = -m[9];
     dst->y = -m[9];
@@ -579,7 +579,7 @@ void Matrix::getForwardVector(Vector3* dst) const
 
 
 void Matrix::getBackVector(Vector3* dst) const
 void Matrix::getBackVector(Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->x = m[8];
     dst->x = m[8];
     dst->y = m[9];
     dst->y = m[9];
@@ -657,7 +657,7 @@ void Matrix::multiply(float scalar, Matrix* dst) const
 
 
 void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
 void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->m[0]  = m.m[0]  * scalar;
     dst->m[0]  = m.m[0]  * scalar;
     dst->m[1]  = m.m[1]  * scalar;
     dst->m[1]  = m.m[1]  * scalar;
@@ -684,7 +684,7 @@ void Matrix::multiply(const Matrix& m)
 
 
 void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
 void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     // Support the case where m1 or m2 is the same array as dst.
     // Support the case where m1 or m2 is the same array as dst.
     float product[16];
     float product[16];
@@ -792,7 +792,7 @@ void Matrix::rotateZ(float angle)
 
 
 void Matrix::rotateZ(float angle, Matrix* dst) const
 void Matrix::rotateZ(float angle, Matrix* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     Matrix r;
     Matrix r;
     createRotationZ(angle, &r);
     createRotationZ(angle, &r);
@@ -816,7 +816,7 @@ void Matrix::scale(float xScale, float yScale, float zScale)
 
 
 void Matrix::scale(float xScale, float yScale, float zScale, Matrix* dst) const
 void Matrix::scale(float xScale, float yScale, float zScale, Matrix* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     Matrix s;
     Matrix s;
     createScale(xScale, yScale, zScale, &s);
     createScale(xScale, yScale, zScale, &s);
@@ -855,7 +855,7 @@ void Matrix::set(float m11, float m12, float m13, float m14, float m21, float m2
 
 
 void Matrix::set(const float* m)
 void Matrix::set(const float* m)
 {
 {
-    assert(m);
+    GP_ASSERT(m);
     memcpy(this->m, m, MATRIX_SIZE);
     memcpy(this->m, m, MATRIX_SIZE);
 }
 }
 
 
@@ -921,7 +921,7 @@ void Matrix::transformVector(const Vector3& vector, Vector3* dst) const
 
 
 void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) const
 void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
     
     
     dst->set(
     dst->set(
         x * m[0] + y * m[4] + z * m[8] + w * m[12],
         x * m[0] + y * m[4] + z * m[8] + w * m[12],
@@ -936,7 +936,7 @@ void Matrix::transformVector(Vector4* vector) const
 
 
 void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     dst->set(
     dst->set(
         vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
         vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
@@ -952,7 +952,7 @@ void Matrix::translate(float x, float y, float z)
 
 
 void Matrix::translate(float x, float y, float z, Matrix* dst) const
 void Matrix::translate(float x, float y, float z, Matrix* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     Matrix t;
     Matrix t;
     createTranslation(x, y, z, &t);
     createTranslation(x, y, z, &t);
@@ -976,7 +976,7 @@ void Matrix::transpose()
 
 
 void Matrix::transpose(Matrix* dst) const
 void Matrix::transpose(Matrix* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
     
     
     float t[16] = {
     float t[16] = {
         m[0], m[4], m[8], m[12],
         m[0], m[4], m[8], m[12],

+ 4 - 4
gameplay/src/MeshBatch.cpp

@@ -36,7 +36,7 @@ MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveTy
 
 
 MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity, unsigned int growSize)
 MeshBatch* MeshBatch::create(const VertexFormat& vertexFormat, Mesh::PrimitiveType primitiveType, Material* material, bool indexed, unsigned int initialCapacity, unsigned int growSize)
 {
 {
-    assert(material);
+    GP_ASSERT(material);
 
 
     MeshBatch* batch = new MeshBatch(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
     MeshBatch* batch = new MeshBatch(vertexFormat, primitiveType, material, indexed, initialCapacity, growSize);
 
 
@@ -73,7 +73,7 @@ void MeshBatch::setCapacity(unsigned int capacity)
 
 
 bool MeshBatch::resize(unsigned int capacity)
 bool MeshBatch::resize(unsigned int capacity)
 {
 {
-    assert(capacity > 0);
+    GP_ASSERT(capacity > 0);
     if (capacity == 0)
     if (capacity == 0)
         return false;
         return false;
 
 
@@ -103,7 +103,7 @@ bool MeshBatch::resize(unsigned int capacity)
         vertexCapacity = capacity + 2;
         vertexCapacity = capacity + 2;
         break;
         break;
     default:
     default:
-        assert(0); // unexpected
+        GP_ASSERT(0); // unexpected
         break;
         break;
     }
     }
 
 
@@ -112,7 +112,7 @@ bool MeshBatch::resize(unsigned int capacity)
     // for now, which is the same number of vertices as indices.
     // for now, which is the same number of vertices as indices.
     unsigned int indexCapacity = vertexCapacity;
     unsigned int indexCapacity = vertexCapacity;
 
 
-    assert(indexCapacity <= USHRT_MAX);
+    GP_ASSERT(indexCapacity <= USHRT_MAX);
     if (indexCapacity > USHRT_MAX)
     if (indexCapacity > USHRT_MAX)
         return false;
         return false;
 
 

+ 1 - 1
gameplay/src/MeshBatch.inl

@@ -11,7 +11,7 @@ Material* MeshBatch::getMaterial() const
 template <class T>
 template <class T>
 void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 void MeshBatch::add(T* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
 {
-    assert(sizeof(T) == _vertexFormat.getVertexSize());
+    GP_ASSERT(sizeof(T) == _vertexFormat.getVertexSize());
     
     
     unsigned int newVertexCount = _vertexCount + vertexCount;
     unsigned int newVertexCount = _vertexCount + vertexCount;
     unsigned int newIndexCount = _indexCount + indexCount;
     unsigned int newIndexCount = _indexCount + indexCount;

+ 6 - 6
gameplay/src/MeshSkin.cpp

@@ -37,13 +37,13 @@ unsigned int MeshSkin::getJointCount() const
 
 
 Joint* MeshSkin::getJoint(unsigned int index) const
 Joint* MeshSkin::getJoint(unsigned int index) const
 {
 {
-    assert(index < _joints.size());
+    GP_ASSERT(index < _joints.size());
     return _joints[index];
     return _joints[index];
 }
 }
 
 
 Joint* MeshSkin::getJoint(const char* id) const
 Joint* MeshSkin::getJoint(const char* id) const
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     for (unsigned int i = 0, count = _joints.size(); i < count; ++i)
     for (unsigned int i = 0, count = _joints.size(); i < count; ++i)
     {
     {
@@ -66,7 +66,7 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
         const unsigned int jointCount = getJointCount();
         const unsigned int jointCount = getJointCount();
         skin->setJointCount(jointCount);
         skin->setJointCount(jointCount);
 
 
-        assert(skin->_rootNode == NULL);
+        GP_ASSERT(skin->_rootNode == NULL);
         
         
         // Check if the root node has already been cloned.
         // Check if the root node has already been cloned.
         if (Node* rootNode = context.findClonedNode(_rootNode))
         if (Node* rootNode = context.findClonedNode(_rootNode))
@@ -80,7 +80,7 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
         }
         }
         
         
         Node* node = skin->_rootNode->findNode(_rootJoint->getId());
         Node* node = skin->_rootNode->findNode(_rootJoint->getId());
-        assert(node);
+        GP_ASSERT(node);
         skin->_rootJoint = static_cast<Joint*>(node);
         skin->_rootJoint = static_cast<Joint*>(node);
         for (unsigned int i = 0; i < jointCount; ++i)
         for (unsigned int i = 0; i < jointCount; ++i)
         {
         {
@@ -92,7 +92,7 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
                 if (strcmp(skin->_rootJoint->getId(), oldJoint->getId()) == 0)
                 if (strcmp(skin->_rootJoint->getId(), oldJoint->getId()) == 0)
                     newJoint = static_cast<Joint*>(skin->_rootJoint);
                     newJoint = static_cast<Joint*>(skin->_rootJoint);
             }
             }
-            assert(newJoint);
+            GP_ASSERT(newJoint);
             skin->setJoint(newJoint, i);
             skin->setJoint(newJoint, i);
         }
         }
     }
     }
@@ -128,7 +128,7 @@ void MeshSkin::setJointCount(unsigned int jointCount)
 
 
 void MeshSkin::setJoint(Joint* joint, unsigned int index)
 void MeshSkin::setJoint(Joint* joint, unsigned int index)
 {
 {
-    assert(index < _joints.size());
+    GP_ASSERT(index < _joints.size());
 
 
     if (_joints[index])
     if (_joints[index])
     {
     {

+ 2 - 2
gameplay/src/Model.cpp

@@ -51,7 +51,7 @@ unsigned int Model::getMeshPartCount() const
 
 
 Material* Model::getMaterial(int partIndex)
 Material* Model::getMaterial(int partIndex)
 {
 {
-    assert(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
+    GP_ASSERT(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
 
 
     Material* m = NULL;
     Material* m = NULL;
 
 
@@ -75,7 +75,7 @@ Material* Model::getMaterial(int partIndex)
 
 
 void Model::setMaterial(Material* material, int partIndex)
 void Model::setMaterial(Material* material, int partIndex)
 {
 {
-    assert(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
+    GP_ASSERT(partIndex == -1 || (partIndex >= 0 && partIndex < (int)getMeshPartCount()));
 
 
     Material* oldMaterial = NULL;
     Material* oldMaterial = NULL;
 
 

+ 19 - 8
gameplay/src/Node.cpp

@@ -87,7 +87,7 @@ Node::Type Node::getType() const
 
 
 void Node::addChild(Node* child)
 void Node::addChild(Node* child)
 {
 {
-    assert(child);
+    GP_ASSERT(child);
 
 
     if (child->_parent == this)
     if (child->_parent == this)
     {
     {
@@ -285,7 +285,7 @@ unsigned int Node::getChildCount() const
 
 
 Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     // If the node has a model with a mesh skin, search the skin's hierarchy as well.
     // If the node has a model with a mesh skin, search the skin's hierarchy as well.
     Node* rootNode = NULL;
     Node* rootNode = NULL;
@@ -329,7 +329,7 @@ Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 
 
 unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 {
 {
-    assert(id);
+    GP_ASSERT(id);
     
     
     unsigned int count = 0;
     unsigned int count = 0;
 
 
@@ -601,11 +601,22 @@ void Node::transformChanged()
     _dirtyBits |= NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS;
     _dirtyBits |= NODE_DIRTY_WORLD | NODE_DIRTY_BOUNDS;
 
 
     // Notify our children that their transform has also changed (since transforms are inherited).
     // Notify our children that their transform has also changed (since transforms are inherited).
-    Joint* rootJoint = NULL;
     Node* n = getFirstChild();
     Node* n = getFirstChild();
     while (n)
     while (n)
     {
     {
-        n->transformChanged();
+        if (Transform::isTransformChangedSuspended())
+        {
+            // If the DIRTY_NOTIFY bit is not set
+            if (!n->isDirty(Transform::DIRTY_NOTIFY))
+            {
+                n->transformChanged();
+                suspendTransformChange(n);
+            }
+        }
+        else
+        {
+            n->transformChanged();
+        }
         n = n->getNextSibling();
         n = n->getNextSibling();
     }
     }
 
 
@@ -1016,10 +1027,10 @@ PhysicsCollisionObject* Node::setCollisionObject(const char* url)
 {
 {
     // Load the collision object properties from file.
     // Load the collision object properties from file.
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    assert(properties);
+    GP_ASSERT(properties);
     if (properties == NULL)
     if (properties == NULL)
     {
     {
-        WARN_VARG("Failed to load collision object file: %s", url);
+        GP_WARN("Failed to load collision object file: %s", url);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1039,7 +1050,7 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 0))
         strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
     {
-        WARN("Failed to load collision object from properties object: must be non-null object and have namespace equal to \'character\', \'ghostObject\', or \'rigidBody\'.");
+        GP_WARN("Failed to load collision object from properties object: must be non-null object and have namespace equal to \'character\', \'ghostObject\', or \'rigidBody\'.");
         return NULL;
         return NULL;
     }
     }
 
 

+ 34 - 23
gameplay/src/ParticleEmitter.cpp

@@ -34,11 +34,6 @@ ParticleEmitter::ParticleEmitter(SpriteBatch* batch, unsigned int particleCountM
 
 
     _spriteBatch->getStateBlock()->setDepthWrite(false);
     _spriteBatch->getStateBlock()->setDepthWrite(false);
     _spriteBatch->getStateBlock()->setDepthTest(true);
     _spriteBatch->getStateBlock()->setDepthTest(true);
-    /*
-    _spriteBatch->getStateBlock()->setBlend(true);
-    _spriteBatch->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
-    _spriteBatch->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
-    */
 }
 }
 
 
 ParticleEmitter::~ParticleEmitter()
 ParticleEmitter::~ParticleEmitter()
@@ -50,24 +45,24 @@ ParticleEmitter::~ParticleEmitter()
 
 
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlending textureBlending, unsigned int particleCountMax)
 {
 {
-    assert(textureFile);
+    GP_ASSERT(textureFile);
 
 
     Texture* texture = NULL;
     Texture* texture = NULL;
-    texture = Texture::create(textureFile, true);    
+    texture = Texture::create(textureFile, false);
 
 
     if (!texture)
     if (!texture)
     {
     {
-        LOG_ERROR_VARG("Error creating ParticleEmitter: Could not read texture file: %s", textureFile);
+        GP_ERROR("Error creating ParticleEmitter: Could not read texture file: %s", textureFile);
         return NULL;
         return NULL;
     }
     }
 
 
     // Use default SpriteBatch material.
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);
     texture->release(); // batch owns the texture.
     texture->release(); // batch owns the texture.
-    assert(batch);
+    GP_ASSERT(batch);
 
 
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
     ParticleEmitter* emitter = new ParticleEmitter(batch, particleCountMax);
-    assert(emitter);
+    GP_ASSERT(emitter);
 
 
     // By default assume only one frame which uses the entire texture.
     // By default assume only one frame which uses the entire texture.
     emitter->setTextureBlending(textureBlending);
     emitter->setTextureBlending(textureBlending);
@@ -84,12 +79,12 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
 
 
 ParticleEmitter* ParticleEmitter::create(const char* url)
 ParticleEmitter* ParticleEmitter::create(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
     if (!properties)
     if (!properties)
     {
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: Could not load file: %s", url);
+        GP_ERROR("Error loading ParticleEmitter: Could not load file: %s", url);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -103,14 +98,14 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 {
 {
     if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
     if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
     {
     {
-        LOG_ERROR("Error loading ParticleEmitter: No 'particle' namespace found");
+        GP_ERROR("Error loading ParticleEmitter: No 'particle' namespace found");
         return NULL;
         return NULL;
     }
     }
 
 
     Properties* sprite = properties->getNextNamespace();
     Properties* sprite = properties->getNextNamespace();
     if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
     if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
     {
     {
-        LOG_ERROR("Error loading ParticleEmitter: No 'sprite' namespace found");
+        GP_ERROR("Error loading ParticleEmitter: No 'sprite' namespace found");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -119,7 +114,7 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     const char* texturePath = sprite->getString("path");
     const char* texturePath = sprite->getString("path");
     if (strlen(texturePath) == 0)
     if (strlen(texturePath) == 0)
     {
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: No texture path specified: %s", texturePath);
+        GP_ERROR("Error loading ParticleEmitter: No texture path specified: %s", texturePath);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -282,6 +277,7 @@ void ParticleEmitter::emit(unsigned int particleCount)
     for (unsigned int i = 0; i < particleCount; i++)
     for (unsigned int i = 0; i < particleCount; i++)
     {
     {
         Particle* p = &_particles[_particleCount];
         Particle* p = &_particles[_particleCount];
+        p->_visible = true;
 
 
         generateColor(_colorStart, _colorStartVar, &p->_colorStart);
         generateColor(_colorStart, _colorStartVar, &p->_colorStart);
         generateColor(_colorEnd, _colorEndVar, &p->_colorEnd);
         generateColor(_colorEnd, _colorEndVar, &p->_colorEnd);
@@ -784,14 +780,19 @@ void ParticleEmitter::update(long elapsedTime)
         // How many particles should we emit this frame?
         // How many particles should we emit this frame?
         unsigned int emitCount = _timeRunning / _timePerEmission;
         unsigned int emitCount = _timeRunning / _timePerEmission;
             
             
-        if ((int)_timePerEmission > 0)
+        if (emitCount)
         {
         {
-            _timeRunning %= (int)_timePerEmission;
-        }
+            if ((int)_timePerEmission > 0)
+            {
+                _timeRunning %= (int)_timePerEmission;
+            }
 
 
-        emit(emitCount);
+            emit(emitCount);
+        }
     }
     }
 
 
+    const Frustum& frustum = _node->getScene()->getActiveCamera()->getFrustum();
+
     // Now update all currently living particles.
     // Now update all currently living particles.
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     {
     {
@@ -817,6 +818,12 @@ void ParticleEmitter::update(long elapsedTime)
             p->_position.y += p->_velocity.y * elapsedSecs;
             p->_position.y += p->_velocity.y * elapsedSecs;
             p->_position.z += p->_velocity.z * elapsedSecs;
             p->_position.z += p->_velocity.z * elapsedSecs;
 
 
+            if (!frustum.intersects(p->_position))
+            {
+                p->_visible = false;
+                continue;
+            }
+
             p->_angle += p->_rotationPerParticleSpeed * elapsedSecs;
             p->_angle += p->_rotationPerParticleSpeed * elapsedSecs;
 
 
             // Simple linear interpolation of color and size.
             // Simple linear interpolation of color and size.
@@ -896,10 +903,11 @@ void ParticleEmitter::draw()
         _spriteBatch->begin();
         _spriteBatch->begin();
 
 
         // 2D Rotation.
         // 2D Rotation.
-        Vector2 pivot(0.5f, 0.5f);
+        static const Vector2 pivot(0.5f, 0.5f);
 
 
         // 3D Rotation so that particles always face the camera.
         // 3D Rotation so that particles always face the camera.
         const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
         const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
+
         Vector3 right;
         Vector3 right;
         cameraWorldMatrix.getRightVector(&right);
         cameraWorldMatrix.getRightVector(&right);
         Vector3 up;
         Vector3 up;
@@ -909,9 +917,12 @@ void ParticleEmitter::draw()
         {
         {
             Particle* p = &_particles[i];
             Particle* p = &_particles[i];
 
 
-            _spriteBatch->draw(p->_position, right, up, 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);
+            if (p->_visible)
+            {
+                _spriteBatch->draw(p->_position, right, up, 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);
+            }
         }
         }
 
 
         // Render.
         // Render.

+ 8 - 7
gameplay/src/ParticleEmitter.h

@@ -615,6 +615,13 @@ public:
      */
      */
     void draw();
     void draw();
 
 
+    /**
+     * Gets a BlendMode enum from a corresponding string.
+     */
+    static TextureBlending getTextureBlendingFromString(const char* src);
+
+    void setTextureBlending(TextureBlending blending);
+
 private:
 private:
 
 
     /**
     /**
@@ -656,13 +663,6 @@ private:
      */
      */
     void generateColor(const Vector4& base, const Vector4& variance, Vector4* dst);
     void generateColor(const Vector4& base, const Vector4& variance, Vector4* dst);
 
 
-    /**
-     * Gets a BlendMode enum from a corresponding string.
-     */
-    static TextureBlending getTextureBlendingFromString(const char* src);
-
-    void setTextureBlending(TextureBlending blending);
-
     /**
     /**
      * Defines the data for a single particle in the system.
      * Defines the data for a single particle in the system.
      */
      */
@@ -687,6 +687,7 @@ private:
         float _size;
         float _size;
         unsigned int _frame;
         unsigned int _frame;
         float _timeOnCurrentFrame;
         float _timeOnCurrentFrame;
+        bool _visible;
     };
     };
 
 
     unsigned int _particleCountMax;
     unsigned int _particleCountMax;

+ 2 - 2
gameplay/src/Pass.cpp

@@ -10,7 +10,7 @@ namespace gameplay
 Pass::Pass(const char* id, Technique* technique, Effect* effect) :
 Pass::Pass(const char* id, Technique* technique, Effect* effect) :
     _id(id ? id : ""), _technique(technique), _effect(effect), _vaBinding(NULL)
     _id(id ? id : ""), _technique(technique), _effect(effect), _vaBinding(NULL)
 {
 {
-    assert(technique);
+    GP_ASSERT(technique);
 
 
     RenderState::_parent = _technique;
     RenderState::_parent = _technique;
 }
 }
@@ -25,7 +25,7 @@ Pass* Pass::create(const char* id, Technique* technique, const char* vshPath, co
 {
 {
     // Attempt to create/load the effect
     // Attempt to create/load the effect
     Effect* effect = Effect::createFromFile(vshPath, fshPath, defines);
     Effect* effect = Effect::createFromFile(vshPath, fshPath, defines);
-    assert(effect);
+    GP_ASSERT(effect);
     if (effect == NULL)
     if (effect == NULL)
     {
     {
         return NULL;
         return NULL;

+ 3 - 3
gameplay/src/PhysicsCharacter.cpp

@@ -87,10 +87,10 @@ PhysicsCharacter::~PhysicsCharacter()
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    assert(properties);
+    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
     if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
     {
     {
-        WARN("Failed to load physics character from properties object: must be non-null object and have namespace equal to \'character\'.");
+        GP_WARN("Failed to load physics character from properties object: must be non-null object and have namespace equal to \'character\'.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -98,7 +98,7 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     if (shape == NULL)
     {
     {
-        WARN("Failed to create collision shape during physics character creation.");
+        GP_WARN("Failed to create collision shape during physics character creation.");
         return NULL;
         return NULL;
     }
     }
 
 

+ 9 - 9
gameplay/src/PhysicsCollisionShape.cpp

@@ -118,13 +118,13 @@ PhysicsCollisionShape::Definition& PhysicsCollisionShape::Definition::operator=(
 PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
 PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    assert(properties);
+    GP_ASSERT(properties);
     if (!properties || 
     if (!properties || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "ghostObject") == 0 || 
         strcmp(properties->getNamespace(), "rigidBody") == 0))
         strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
     {
-        WARN("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to \'character\', \'ghostObject\', or \'rigidBody\'.");
+        GP_WARN("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to \'character\', \'ghostObject\', or \'rigidBody\'.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -158,7 +158,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 type = SHAPE_CAPSULE;
                 type = SHAPE_CAPSULE;
             else
             else
             {
             {
-                WARN_VARG("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
+                GP_WARN("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
                 return NULL;
                 return NULL;
             }
             }
 
 
@@ -194,7 +194,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
 
 
     if (!typeSpecified)
     if (!typeSpecified)
     {
     {
-        WARN("Missing 'type' specifier for collision shape definition.");
+        GP_WARN("Missing 'type' specifier for collision shape definition.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -259,7 +259,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
             Mesh* nodeMesh = node->getModel() ? node->getModel()->getMesh() : NULL;
             if (nodeMesh == NULL)
             if (nodeMesh == NULL)
             {
             {
-                WARN("Cannot create mesh rigid body for node without mode/mesh.");
+                GP_WARN("Cannot create mesh rigid body for node without mode/mesh.");
                 return NULL;
                 return NULL;
             }
             }
 
 
@@ -275,7 +275,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                 case Mesh::LINE_STRIP:
                 case Mesh::LINE_STRIP:
                 case Mesh::POINTS:
                 case Mesh::POINTS:
                 case Mesh::TRIANGLE_STRIP:
                 case Mesh::TRIANGLE_STRIP:
-                    WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
+                    GP_WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
                     SAFE_DELETE(shape);
                     SAFE_DELETE(shape);
                     break;
                     break;
             }
             }
@@ -285,7 +285,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         case SHAPE_HEIGHTFIELD:
         case SHAPE_HEIGHTFIELD:
             if (imagePath == NULL)
             if (imagePath == NULL)
             {
             {
-                WARN("Heightfield rigid body requires an image path.");
+                GP_WARN("Heightfield rigid body requires an image path.");
             }
             }
             else
             else
             {
             {
@@ -301,7 +301,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
                     case Image::RGBA:
                     case Image::RGBA:
                         break;
                         break;
                     default:
                     default:
-                        WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
+                        GP_WARN("Heightmap: pixel format is not supported: %d", image->getFormat());
                         return NULL;
                         return NULL;
                 }
                 }
 
 
@@ -310,7 +310,7 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
             }
             }
             break;
             break;
         default:
         default:
-            WARN("Unsupported value for physics collision shape type.");
+            GP_WARN("Unsupported value for physics collision shape type.");
             break;
             break;
     }
     }
 
 

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -145,7 +145,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
         else
         else
         {
         {
             // Warn the user that the model has no bounding volume.
             // Warn the user that the model has no bounding volume.
-            WARN_VARG("Model \'%s\' has no bounding volume - center of mass is defaulting to local coordinate origin.", model->getNode()->getId());
+            GP_WARN("Model \'%s\' has no bounding volume - center of mass is defaulting to local coordinate origin.", model->getNode()->getId());
             model->getNode()->getWorldMatrix().transformPoint(&center);
             model->getNode()->getWorldMatrix().transformPoint(&center);
         }
         }
     }
     }

+ 20 - 20
gameplay/src/PhysicsController.cpp

@@ -160,22 +160,22 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
     {
     {
     public:
     public:
 
 
-	    SweepTestCallback(PhysicsCollisionObject* me)
+        SweepTestCallback(PhysicsCollisionObject* me)
             : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), me(me)
             : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)), me(me)
-	    {
-	    }
+        {
+        }
 
 
-	    btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
-	    {
+        btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
+        {
             PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
             PhysicsCollisionObject* object = reinterpret_cast<PhysicsCollisionObject*>(convexResult.m_hitCollisionObject->getUserPointer());
 
 
-		    if (object == me)
-			    return 1.0f;
+            if (object == me)
+                return 1.0f;
 
 
-		    return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
-	    }
+            return ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
+        }
 
 
-	    PhysicsCollisionObject* me;
+        PhysicsCollisionObject* me;
     };
     };
 
 
     PhysicsCollisionShape* shape = object->getCollisionShape();
     PhysicsCollisionShape* shape = object->getCollisionShape();
@@ -507,7 +507,7 @@ void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
         break;
         break;
 
 
     default:
     default:
-        assert(0); // unexpected (new type?)
+        GP_ASSERT(0); // unexpected (new type?)
         break;
         break;
     }
     }
 }
 }
@@ -529,7 +529,7 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
             break;
             break;
 
 
         default:
         default:
-            assert(0); // unexpected (new type?)
+            GP_ASSERT(0); // unexpected (new type?)
             break;
             break;
         }
         }
     }
     }
@@ -851,7 +851,7 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* i
             pixelSize = 4;
             pixelSize = 4;
             break;
             break;
         default:
         default:
-            LOG_ERROR("Unsupported pixel format for heightmap image.");
+            GP_ERROR("Unsupported pixel format for heightmap image.");
             return NULL;
             return NULL;
     }
     }
 
 
@@ -914,7 +914,7 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* i
 
 
 PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3& scale)
 PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3& scale)
 {
 {
-    assert(mesh);
+    GP_ASSERT(mesh);
 
 
     // Only support meshes with triangle list primitive types
     // Only support meshes with triangle list primitive types
     bool triMesh = true;
     bool triMesh = true;
@@ -936,7 +936,7 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
 
 
     if (!triMesh)
     if (!triMesh)
     {
     {
-        LOG_ERROR("Mesh rigid bodies are currently only supported on meshes with TRIANGLES primitive type.");
+        GP_ERROR("Mesh rigid bodies are currently only supported on meshes with TRIANGLES primitive type.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -944,7 +944,7 @@ PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3&
     // in order to fetch mesh data for computing mesh rigid body.
     // in order to fetch mesh data for computing mesh rigid body.
     if (strlen(mesh->getUrl()) == 0)
     if (strlen(mesh->getUrl()) == 0)
     {
     {
-        LOG_ERROR("Cannot create mesh rigid body for mesh without valid URL.");
+        GP_ERROR("Cannot create mesh rigid body for mesh without valid URL.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -1122,13 +1122,13 @@ bool PhysicsController::checkConstraintRigidBodies(PhysicsRigidBody* a, PhysicsR
 {
 {
     if (!a->supportsConstraints())
     if (!a->supportsConstraints())
     {
     {
-        WARN_VARG("Rigid body '%s' does not support constraints; unexpected behavior may occur.", a->_node->getId());
+        GP_WARN("Rigid body '%s' does not support constraints; unexpected behavior may occur.", a->_node->getId());
         return false;
         return false;
     }
     }
     
     
     if (b && !b->supportsConstraints())
     if (b && !b->supportsConstraints())
     {
     {
-        WARN_VARG("Rigid body '%s' does not support constraints; unexpected behavior may occur.", b->_node->getId());
+        GP_WARN("Rigid body '%s' does not support constraints; unexpected behavior may occur.", b->_node->getId());
         return false;
         return false;
     }
     }
 
 
@@ -1247,12 +1247,12 @@ void PhysicsController::DebugDrawer::drawContactPoint(const btVector3& pointOnB,
 
 
 void PhysicsController::DebugDrawer::reportErrorWarning(const char* warningString)
 void PhysicsController::DebugDrawer::reportErrorWarning(const char* warningString)
 {
 {
-    WARN(warningString);
+    GP_WARN(warningString);
 }
 }
 
 
 void PhysicsController::DebugDrawer::draw3dText(const btVector3& location, const char* textString)
 void PhysicsController::DebugDrawer::draw3dText(const btVector3& location, const char* textString)
 {
 {
-    WARN("Physics debug drawing: 3D text is not supported.");
+    GP_WARN("Physics debug drawing: 3D text is not supported.");
 }
 }
 
 
 void PhysicsController::DebugDrawer::setDebugMode(int mode)
 void PhysicsController::DebugDrawer::setDebugMode(int mode)

+ 3 - 3
gameplay/src/PhysicsGhostObject.cpp

@@ -42,10 +42,10 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    assert(properties);
+    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
     if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
     {
     {
-        WARN("Failed to load ghost object from properties object: must be non-null object and have namespace equal to \'ghost\'.");
+        GP_WARN("Failed to load ghost object from properties object: must be non-null object and have namespace equal to \'ghost\'.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -53,7 +53,7 @@ PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* propertie
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     if (shape == NULL)
     {
     {
-        WARN("Failed to create collision shape during ghost object creation.");
+        GP_WARN("Failed to create collision shape during ghost object creation.");
         return NULL;
         return NULL;
     }
     }
 
 

+ 5 - 5
gameplay/src/PhysicsRigidBody.cpp

@@ -144,10 +144,10 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // Check if the properties is valid and has a valid namespace.
-    assert(properties);
+    GP_ASSERT(properties);
     if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
     if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
     {
     {
-        WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to \'rigidBody\'.");
+        GP_WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to \'rigidBody\'.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -155,7 +155,7 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     PhysicsCollisionShape::Definition* shape = PhysicsCollisionShape::Definition::create(node, properties);
     if (shape == NULL)
     if (shape == NULL)
     {
     {
-        WARN("Failed to create collision shape during rigid body creation.");
+        GP_WARN("Failed to create collision shape during rigid body creation.");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -227,7 +227,7 @@ float PhysicsRigidBody::getHeight(float x, float y) const
     // This function is only supported for heightfield rigid bodies.
     // This function is only supported for heightfield rigid bodies.
     if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
     {
-        WARN("Attempting to get the height of a non-heightfield rigid body.");
+        GP_WARN("Attempting to get the height of a non-heightfield rigid body.");
         return 0.0f;
         return 0.0f;
     }
     }
 
 
@@ -248,7 +248,7 @@ float PhysicsRigidBody::getHeight(float x, float y) const
     // Check that the x, y position is within the bounds.
     // Check that the x, y position is within the bounds.
     if (x < 0.0f || x > w || y < 0.0f || y > h)
     if (x < 0.0f || x > w || y < 0.0f || y > h)
     {
     {
-        WARN_VARG("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, w, h);
+        GP_WARN("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, w, h);
         return 0.0f;
         return 0.0f;
     }
     }
 
 

+ 2 - 2
gameplay/src/Plane.cpp

@@ -251,7 +251,7 @@ void Plane::transform(const Matrix& matrix)
         float nx = _normal.x * inverted.m[0] + _normal.y * inverted.m[1] + _normal.z * inverted.m[2] + _distance * inverted.m[3];
         float nx = _normal.x * inverted.m[0] + _normal.y * inverted.m[1] + _normal.z * inverted.m[2] + _distance * inverted.m[3];
         float ny = _normal.x * inverted.m[4] + _normal.y * inverted.m[5] + _normal.z * inverted.m[6] + _distance * inverted.m[7];
         float ny = _normal.x * inverted.m[4] + _normal.y * inverted.m[5] + _normal.z * inverted.m[6] + _distance * inverted.m[7];
         float nz = _normal.x * inverted.m[8] + _normal.y * inverted.m[9] + _normal.z * inverted.m[10] + _distance * inverted.m[11];
         float nz = _normal.x * inverted.m[8] + _normal.y * inverted.m[9] + _normal.z * inverted.m[10] + _distance * inverted.m[11];
-        float d = _normal.x * inverted.m[12]+ _normal.y * inverted.m[13] + _normal.z * inverted.m[14]+ _distance * inverted.m[15];
+        float d = _normal.x * inverted.m[12]+ _normal.y * inverted.m[13] + _normal.z * inverted.m[14] + _distance * inverted.m[15];
         float factor = 1.0f / sqrt(nx * nx + ny * ny + nz * nz);
         float factor = 1.0f / sqrt(nx * nx + ny * ny + nz * nz);
 
 
         _normal.x = nx * factor;
         _normal.x = nx * factor;
@@ -271,7 +271,7 @@ void Plane::normalize()
 
 
     if (normalizeFactor != 1.0f)
     if (normalizeFactor != 1.0f)
     {
     {
-        _normal.x*= normalizeFactor;
+        _normal.x *= normalizeFactor;
         _normal.y *= normalizeFactor;
         _normal.y *= normalizeFactor;
         _normal.z *= normalizeFactor;
         _normal.z *= normalizeFactor;
         _distance *= normalizeFactor;
         _distance *= normalizeFactor;

+ 0 - 7
gameplay/src/Platform.h

@@ -89,13 +89,6 @@ public:
      */
      */
     static void setVsync(bool enable);
     static void setVsync(bool enable);
 
 
-    /**
-     * Gets the orientation angle the device is currently in.
-     * 
-     * @return The orientation angle.
-     */
-    static int getOrientationAngle();
-
     /**
     /**
      * Set if multi-touch is enabled on the platform
      * Set if multi-touch is enabled on the platform
      */
      */

+ 16 - 9
gameplay/src/PlatformAndroid.cpp

@@ -169,6 +169,9 @@ static bool initEGL()
     
     
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_WIDTH, &__width);
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
     eglQuerySurface(__eglDisplay, __eglSurface, EGL_HEIGHT, &__height);
+
+    if (__width < __height)
+        __orientationAngle = 0;
     
     
     // Set vsync.
     // Set vsync.
     eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
     eglSwapInterval(__eglDisplay, WINDOW_VSYNC ? 1 : 0);
@@ -866,11 +869,6 @@ void Platform::setVsync(bool enable)
     __vsync = enable;
     __vsync = enable;
 }
 }
 
 
-int Platform::getOrientationAngle()
-{
-    return __orientationAngle;
-}
-
 void Platform::setMultiTouch(bool enabled)
 void Platform::setMultiTouch(bool enabled)
 {
 {
     __multiTouch = enabled;
     __multiTouch = enabled;
@@ -888,10 +886,19 @@ void Platform::getAccelerometerValues(float* pitch, float* roll)
     
     
     // By default, android accelerometer values are oriented to the portrait mode.
     // By default, android accelerometer values are oriented to the portrait mode.
     // flipping the x and y to get the desired landscape mode values.
     // flipping the x and y to get the desired landscape mode values.
-    tx = -__sensorEvent.acceleration.y;
-    ty = __sensorEvent.acceleration.x;
-    tz = -__sensorEvent.acceleration.z;
-    
+    if (__orientationAngle == 90)
+    {
+        tx = -__sensorEvent.acceleration.y;
+        ty = __sensorEvent.acceleration.x;
+    }
+    else
+    {
+        // 0
+        tx = __sensorEvent.acceleration.x;
+        ty = __sensorEvent.acceleration.y;
+    }
+    tz = __sensorEvent.acceleration.z;
+
     if (pitch != NULL)
     if (pitch != NULL)
         *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
         *pitch = -atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
     if (roll != NULL)
     if (roll != NULL)

+ 0 - 6
gameplay/src/PlatformMacOSX.mm

@@ -566,7 +566,6 @@ extern void printError(const char* format, ...)
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     vfprintf(stderr, format, argptr);
-    fprintf(stderr, "\n");
     va_end(argptr);
     va_end(argptr);
 }
 }
     
     
@@ -662,11 +661,6 @@ void Platform::setVsync(bool enable)
     __vsync = enable;
     __vsync = enable;
 }
 }
 
 
-int Platform::getOrientationAngle()
-{
-    return 0;
-}
-
 void Platform::setMultiTouch(bool enabled)
 void Platform::setMultiTouch(bool enabled)
 {
 {
 }
 }

+ 51 - 57
gameplay/src/PlatformQNX.cpp

@@ -404,7 +404,6 @@ extern void printError(const char* format, ...)
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     vfprintf(stderr, format, argptr);
-    fprintf(stderr, "\n");
     va_end(argptr);
     va_end(argptr);
 }
 }
 
 
@@ -509,7 +508,7 @@ Platform* Platform::create(Game* game)
 
 
     int rc = 0;
     int rc = 0;
     int screenFormat = SCREEN_FORMAT_RGBA8888;
     int screenFormat = SCREEN_FORMAT_RGBA8888;
-#if defined(__QNXNTO__) && defined(__X86__)
+#ifdef __X86__
     int screenUsage = SCREEN_USAGE_OPENGL_ES2;
     int screenUsage = SCREEN_USAGE_OPENGL_ES2;
 #else
 #else
     int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
     int screenUsage = SCREEN_USAGE_DISPLAY|SCREEN_USAGE_OPENGL_ES2; // Physical device copy directly into physical display
@@ -768,7 +767,7 @@ void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEven
 {
 {
     if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
     if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
     {
     {
-        Game::getInstance()->touchEvent(touchEvent, x, y, 0);
+        Platform::touchEventInternal(touchEvent, x, y, 0);
     }
     }
 }
 }
 
 
@@ -799,7 +798,7 @@ int Platform::enterMessagePump()
         while (true)
         while (true)
         {
         {
             rc = bps_get_event(&event, 1);
             rc = bps_get_event(&event, 1);
-            assert(rc == BPS_SUCCESS);
+            GP_ASSERT(rc == BPS_SUCCESS);
 
 
             if (event == NULL)
             if (event == NULL)
                 break;
                 break;
@@ -957,20 +956,20 @@ int Platform::enterMessagePump()
                     break;
                     break;
                 case NAVIGATOR_WINDOW_STATE:
                 case NAVIGATOR_WINDOW_STATE:
                 {
                 {
-                	navigator_window_state_t state = navigator_event_get_window_state(event);
-                	switch (state)
-                	{
-                	case NAVIGATOR_WINDOW_FULLSCREEN:
-                		_game->resume();
-                		suspended = false;
-                		break;
-                	case NAVIGATOR_WINDOW_THUMBNAIL:
-                	case NAVIGATOR_WINDOW_INVISIBLE:
-                		_game->pause();
-                		suspended = true;
-                		break;
-                	}
-                	break;
+                    navigator_window_state_t state = navigator_event_get_window_state(event);
+                    switch (state)
+                    {
+                    case NAVIGATOR_WINDOW_FULLSCREEN:
+                        _game->resume();
+                        suspended = false;
+                        break;
+                    case NAVIGATOR_WINDOW_THUMBNAIL:
+                    case NAVIGATOR_WINDOW_INVISIBLE:
+                        _game->pause();
+                        suspended = true;
+                        break;
+                    }
+                    break;
                 }
                 }
                 case NAVIGATOR_EXIT:
                 case NAVIGATOR_EXIT:
                     _game->exit();
                     _game->exit();
@@ -1065,11 +1064,6 @@ void Platform::setVsync(bool enable)
     __vsync = enable;
     __vsync = enable;
 }
 }
 
 
-int Platform::getOrientationAngle()
-{
-    return __orientationAngle;
-}
-
 void Platform::setMultiTouch(bool enabled)
 void Platform::setMultiTouch(bool enabled)
 {
 {
     __multiTouch = enabled;
     __multiTouch = enabled;
@@ -1082,40 +1076,40 @@ bool Platform::isMultiTouch()
 
 
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
-	switch(__orientationAngle)
-	{
-	// Landscape based device adjusting for landscape game mode
-	case 0:
-		if (pitch)
-			*pitch = __pitch;
-		if (roll)
-			*roll = -__roll;
-		break;
-	case 180:
-		if (pitch)
-			*pitch = -__pitch;
-		if (roll)
-			*roll = __roll;
-		break;
-
-	// Portrait based device adjusting for landscape game mode
-	case 90:
-		if (pitch)
-			*pitch = -__roll;
-		if (roll)
-			*roll = -__pitch;
-		break;
-
-	case  270:
-		if (pitch)
-			*pitch = __roll;
-		if (roll)
-			*roll = __pitch;
-		break;
-
-	default:
-		break;
-	}
+    switch(__orientationAngle)
+    {
+    // Landscape based device adjusting for landscape game mode
+    case 0:
+        if (pitch)
+            *pitch = __pitch;
+        if (roll)
+            *roll = -__roll;
+        break;
+    case 180:
+        if (pitch)
+            *pitch = -__pitch;
+        if (roll)
+            *roll = __roll;
+        break;
+
+    // Portrait based device adjusting for landscape game mode
+    case 90:
+        if (pitch)
+            *pitch = -__roll;
+        if (roll)
+            *roll = -__pitch;
+        break;
+
+    case  270:
+        if (pitch)
+            *pitch = __roll;
+        if (roll)
+            *roll = __pitch;
+        break;
+
+    default:
+        break;
+    }
 }
 }
 
 
 void Platform::swapBuffers()
 void Platform::swapBuffers()

+ 37 - 44
gameplay/src/PlatformWin32.cpp

@@ -247,10 +247,10 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
 
 
 void UpdateCapture(LPARAM lParam)
 void UpdateCapture(LPARAM lParam)
 {
 {
-	if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
-		SetCapture(__hwnd);
-	else
-		ReleaseCapture();
+    if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
+        SetCapture(__hwnd);
+    else
+        ReleaseCapture();
 }
 }
 
 
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
@@ -290,7 +290,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int x = GET_X_LPARAM(lParam);
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
 
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         {
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
@@ -306,29 +306,29 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         {
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
         }
         }
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         return 0;
         return 0;
     }
     }
     case WM_RBUTTONDOWN:
     case WM_RBUTTONDOWN:
-		UpdateCapture(wParam);
-		lx = GET_X_LPARAM(lParam);
+        UpdateCapture(wParam);
+        lx = GET_X_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
         break;
         break;
 
 
     case WM_RBUTTONUP:
     case WM_RBUTTONUP:
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         break;
         break;
 
 
     case WM_MBUTTONDOWN:
     case WM_MBUTTONDOWN:
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         break;
         break;
 
 
     case WM_MBUTTONUP:
     case WM_MBUTTONUP:
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
-		UpdateCapture(wParam);
+        UpdateCapture(wParam);
         break;
         break;
 
 
     case WM_MOUSEMOVE:
     case WM_MOUSEMOVE:
@@ -336,30 +336,30 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int x = GET_X_LPARAM(lParam);
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
 
-		// Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
-		if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
-		{
-			if ((wParam & MK_LBUTTON) == MK_LBUTTON)
-			{
-				// Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
-				gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
-				return 0;
-			}
-			else if ((wParam & MK_RBUTTON) == MK_RBUTTON)
-			{
-				// Update the pitch and roll by adding the scaled deltas.
-				__roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
-				__pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
-
-				// Clamp the values to the valid range.
-				__roll = max(min(__roll, 90.0f), -90.0f);
-				__pitch = max(min(__pitch, 90.0f), -90.0f);
-
-				// Update the last X/Y values.
-				lx = x;
-				ly = y;
-			}
-		}
+        // Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
+        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
+        {
+            if ((wParam & MK_LBUTTON) == MK_LBUTTON)
+            {
+                // Mouse move events should be interpreted as touch move only if left mouse is held and the game did not consume the mouse event.
+                gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
+                return 0;
+            }
+            else if ((wParam & MK_RBUTTON) == MK_RBUTTON)
+            {
+                // Update the pitch and roll by adding the scaled deltas.
+                __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
+                __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
+
+                // Clamp the values to the valid range.
+                __roll = max(min(__roll, 90.0f), -90.0f);
+                __pitch = max(min(__pitch, 90.0f), -90.0f);
+
+                // Update the last X/Y values.
+                lx = x;
+                ly = y;
+            }
+        }
         break;
         break;
     }
     }
 
 
@@ -416,14 +416,12 @@ extern void printError(const char* format, ...)
 {
 {
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
-    fprintf(stderr, "\n");
     int sz = vfprintf(stderr, format, argptr);
     int sz = vfprintf(stderr, format, argptr);
     if (sz > 0)
     if (sz > 0)
     {
     {
-        char* buf = new char[sz + 2];
+        char* buf = new char[sz + 1];
         vsprintf(buf, format, argptr);
         vsprintf(buf, format, argptr);
-        buf[sz] = '\n';
-        buf[sz+1] = 0;
+        buf[sz] = 0;
         OutputDebugStringA(buf);
         OutputDebugStringA(buf);
         SAFE_DELETE_ARRAY(buf);
         SAFE_DELETE_ARRAY(buf);
     }
     }
@@ -637,11 +635,6 @@ void Platform::setVsync(bool enable)
     __vsync = enable;
     __vsync = enable;
 }
 }
 
 
-int Platform::getOrientationAngle()
-{
-    return 0;
-}
-
 void Platform::setMultiTouch(bool enabled)
 void Platform::setMultiTouch(bool enabled)
 {
 {
 }
 }

+ 39 - 44
gameplay/src/PlatformiOS.mm

@@ -45,7 +45,7 @@ int getKey(unichar keyCode);
 {
 {
     EAGLContext* context;	
     EAGLContext* context;	
     CADisplayLink* displayLink;
     CADisplayLink* displayLink;
-	GLuint defaultFramebuffer;
+    GLuint defaultFramebuffer;
     GLuint colorRenderbuffer;
     GLuint colorRenderbuffer;
     GLuint depthRenderbuffer;
     GLuint depthRenderbuffer;
     GLint framebufferWidth;
     GLint framebufferWidth;
@@ -87,11 +87,11 @@ int getKey(unichar keyCode);
 - (id) initWithFrame:(CGRect)frame
 - (id) initWithFrame:(CGRect)frame
 {
 {
     if ((self = [super initWithFrame:frame]))
     if ((self = [super initWithFrame:frame]))
-	{
+    {
         // A system version of 3.1 or greater is required to use CADisplayLink. 
         // A system version of 3.1 or greater is required to use CADisplayLink. 
-		NSString *reqSysVer = @"3.1";
-		NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
-		if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
+        NSString *reqSysVer = @"3.1";
+        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
+        if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
         {
         {
             // Log the system version
             // Log the system version
             NSLog(@"System Version: %@", currSysVer);
             NSLog(@"System Version: %@", currSysVer);
@@ -113,12 +113,12 @@ int getKey(unichar keyCode);
         self.contentScaleFactor = scale;
         self.contentScaleFactor = scale;
         layer.contentsScale = scale;
         layer.contentsScale = scale;
         
         
-		context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+        context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
         if (!context || ![EAGLContext setCurrentContext:context])
         if (!context || ![EAGLContext setCurrentContext:context])
-		{
-			[self release];
-			return nil;
-		}
+        {
+            [self release];
+            return nil;
+        }
 
 
         if (!defaultFramebuffer)
         if (!defaultFramebuffer)
         {
         {
@@ -135,7 +135,7 @@ int getKey(unichar keyCode);
         depthRenderbuffer = 0;
         depthRenderbuffer = 0;
         framebufferWidth = 0;
         framebufferWidth = 0;
         framebufferHeight = 0;
         framebufferHeight = 0;
-		swapInterval = 1;        
+        swapInterval = 1;        
         updating = FALSE;
         updating = FALSE;
         
         
         [self createFramebuffer];
         [self createFramebuffer];
@@ -156,11 +156,11 @@ int getKey(unichar keyCode);
     _game->exit();
     _game->exit();
     [self deleteFramebuffer];
     [self deleteFramebuffer];
     
     
-	if ([EAGLContext currentContext] == context)
+    if ([EAGLContext currentContext] == context)
     {
     {
         [EAGLContext setCurrentContext:nil];
         [EAGLContext setCurrentContext:nil];
     }
     }
-	[context release];
+    [context release];
     [super dealloc];
     [super dealloc];
 }
 }
 
 
@@ -228,15 +228,15 @@ int getKey(unichar keyCode);
 
 
 - (void)setSwapInterval:(NSInteger)interval
 - (void)setSwapInterval:(NSInteger)interval
 {
 {
-	if (interval >= 1)
-	{
-		swapInterval = interval;		
-		if (updating)
-		{
-			[self stopUpdating];
-			[self startUpdating];
-		}
-	}
+    if (interval >= 1)
+    {
+        swapInterval = interval;		
+        if (updating)
+        {
+            [self stopUpdating];
+            [self startUpdating];
+        }
+    }
 }
 }
 
 
 - (int)swapInterval 
 - (int)swapInterval 
@@ -255,25 +255,25 @@ int getKey(unichar keyCode);
 
 
 - (void)startUpdating
 - (void)startUpdating
 {
 {
-	if (!updating)
-	{
+    if (!updating)
+    {
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         [displayLink setFrameInterval:swapInterval];
         [displayLink setFrameInterval:swapInterval];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
         _game->resume();
         _game->resume();
-		updating = TRUE;
-	}
+        updating = TRUE;
+    }
 }
 }
 
 
 - (void)stopUpdating
 - (void)stopUpdating
 {
 {
-	if (updating)
-	{
+    if (updating)
+    {
         _game->pause();
         _game->pause();
-		[displayLink invalidate];
+        [displayLink invalidate];
         displayLink = nil;
         displayLink = nil;
-		updating = FALSE;
-	}
+        updating = FALSE;
+    }
 }
 }
 
 
 - (void)update:(id)sender
 - (void)update:(id)sender
@@ -487,9 +487,10 @@ int getKey(unichar keyCode);
             break;
             break;
 
 
         case UIInterfaceOrientationPortrait:
         case UIInterfaceOrientationPortrait:
+            tx = accelerometerData.acceleration.x;
+            ty = accelerometerData.acceleration.y;
             break;
             break;
         }
         }
-        
         tz = accelerometerData.acceleration.z;  
         tz = accelerometerData.acceleration.z;  
         
         
         p = atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
         p = atan(ty / sqrt(tx * tx + tz * tz)) * 180.0f * M_1_PI;
@@ -504,27 +505,27 @@ int getKey(unichar keyCode);
 
 
 - (void)applicationWillResignActive:(UIApplication*)application
 - (void)applicationWillResignActive:(UIApplication*)application
 {    
 {    
-	[viewController stopUpdating];
+    [viewController stopUpdating];
 }
 }
 
 
 - (void)applicationDidEnterBackground:(UIApplication*)application 
 - (void)applicationDidEnterBackground:(UIApplication*)application 
 {
 {
-	[viewController stopUpdating];
+    [viewController stopUpdating];
 }
 }
 
 
 - (void)applicationWillEnterForeground:(UIApplication*)application 
 - (void)applicationWillEnterForeground:(UIApplication*)application 
 {	
 {	
-	[viewController startUpdating];
+    [viewController startUpdating];
 }
 }
 
 
 - (void)applicationDidBecomeActive:(UIApplication*)application 
 - (void)applicationDidBecomeActive:(UIApplication*)application 
 {
 {
-	[viewController startUpdating];
+    [viewController startUpdating];
 }
 }
 
 
 - (void)applicationWillTerminate:(UIApplication*)application 
 - (void)applicationWillTerminate:(UIApplication*)application 
 {	
 {	
-	[viewController stopUpdating];
+    [viewController stopUpdating];
 }
 }
 
 
 - (void)dealloc 
 - (void)dealloc 
@@ -533,7 +534,7 @@ int getKey(unichar keyCode);
     [viewController release];
     [viewController release];
     [window release];
     [window release];
     [motionManager release];
     [motionManager release];
-	[super dealloc];
+    [super dealloc];
 }
 }
 
 
 @end
 @end
@@ -775,7 +776,6 @@ extern void printError(const char* format, ...)
     va_list argptr;
     va_list argptr;
     va_start(argptr, format);
     va_start(argptr, format);
     vfprintf(stderr, format, argptr);
     vfprintf(stderr, format, argptr);
-    fprintf(stderr, "\n");
     va_end(argptr);
     va_end(argptr);
 }
 }
 
 
@@ -845,11 +845,6 @@ void Platform::setVsync(bool enable)
     __vsync = enable;
     __vsync = enable;
 }
 }
 
 
-int Platform::getOrientationAngle()
-{
-    return 0;
-}
-
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 void Platform::getAccelerometerValues(float* pitch, float* roll)
 {
 {
     [__appDelegate getAccelerometerPitch:pitch roll:roll];
     [__appDelegate getAccelerometerPitch:pitch roll:roll];

+ 24 - 24
gameplay/src/Properties.cpp

@@ -48,11 +48,11 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
 
 
 Properties* Properties::create(const char* url)
 Properties* Properties::create(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     if (!url || strlen(url) == 0)
     if (!url || strlen(url) == 0)
     {
     {
-        WARN("Attempting to create a Properties object from an empty URL!");
+        GP_WARN("Attempting to create a Properties object from an empty URL!");
         return NULL;
         return NULL;
     }
     }
 
 
@@ -119,7 +119,7 @@ Properties* Properties::create(const char* url)
                 iter = properties->getNextNamespace();
                 iter = properties->getNextNamespace();
                 if (iter == NULL)
                 if (iter == NULL)
                 {
                 {
-                    WARN_VARG("Failed to load Properties object from URL '%s'.", url);
+                    GP_WARN("Failed to load Properties object from URL '%s'.", url);
                     return NULL;
                     return NULL;
                 }
                 }
             }
             }
@@ -173,7 +173,7 @@ void Properties::readProperties(FILE* file)
                 name = strtok(line, " =\t");
                 name = strtok(line, " =\t");
                 if (name == NULL)
                 if (name == NULL)
                 {
                 {
-                    LOG_ERROR("Error parsing properties file: value without name.");
+                    GP_ERROR("Error parsing properties file: value without name.");
                     return;
                     return;
                 }
                 }
 
 
@@ -181,7 +181,7 @@ void Properties::readProperties(FILE* file)
                 value = strtok(NULL, "=");
                 value = strtok(NULL, "=");
                 if (value == NULL)
                 if (value == NULL)
                 {
                 {
-                    LOG_ERROR("Error parsing properties file: name without value.");
+                    GP_ERROR("Error parsing properties file: name without value.");
                 }
                 }
 
 
                 // Remove white-space from value.
                 // Remove white-space from value.
@@ -214,7 +214,7 @@ void Properties::readProperties(FILE* file)
                 name = trimWhiteSpace(name);
                 name = trimWhiteSpace(name);
                 if (name == NULL)
                 if (name == NULL)
                 {
                 {
-                    LOG_ERROR("Error parsing properties file: unknown error.");
+                    GP_ERROR("Error parsing properties file: unknown error.");
                 }
                 }
                 else if (name[0] == '}')
                 else if (name[0] == '}')
                 {
                 {
@@ -541,7 +541,7 @@ const char* Properties::getId() const
 
 
 bool Properties::exists(const char* name) const
 bool Properties::exists(const char* name) const
 {
 {
-    assert(name);
+    GP_ASSERT(name);
     return _properties.find(name) != _properties.end();
     return _properties.find(name) != _properties.end();
 }
 }
 
 
@@ -654,7 +654,7 @@ int Properties::getInt(const char* name) const
         scanned = sscanf(valueString, "%d", &value);
         scanned = sscanf(valueString, "%d", &value);
         if (scanned != 1)
         if (scanned != 1)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             return 0;
             return 0;
         }
         }
         return value;
         return value;
@@ -673,7 +673,7 @@ float Properties::getFloat(const char* name) const
         scanned = sscanf(valueString, "%f", &value);
         scanned = sscanf(valueString, "%f", &value);
         if (scanned != 1)
         if (scanned != 1)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             return 0.0f;
             return 0.0f;
         }
         }
         return value;
         return value;
@@ -692,7 +692,7 @@ long Properties::getLong(const char* name) const
         scanned = sscanf(valueString, "%ld", &value);
         scanned = sscanf(valueString, "%ld", &value);
         if (scanned != 1)
         if (scanned != 1)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             return 0L;
             return 0L;
         }
         }
         return value;
         return value;
@@ -703,7 +703,7 @@ long Properties::getLong(const char* name) const
 
 
 bool Properties::getMatrix(const char* name, Matrix* out) const
 bool Properties::getMatrix(const char* name, Matrix* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -716,7 +716,7 @@ bool Properties::getMatrix(const char* name, Matrix* out) const
 
 
         if (scanned != 16)
         if (scanned != 16)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->setIdentity();
             out->setIdentity();
             return false;
             return false;
         }
         }
@@ -731,7 +731,7 @@ bool Properties::getMatrix(const char* name, Matrix* out) const
 
 
 bool Properties::getVector2(const char* name, Vector2* out) const
 bool Properties::getVector2(const char* name, Vector2* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -741,7 +741,7 @@ bool Properties::getVector2(const char* name, Vector2* out) const
         scanned = sscanf(valueString, "%f,%f", &x, &y);
         scanned = sscanf(valueString, "%f,%f", &x, &y);
         if (scanned != 2)
         if (scanned != 2)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f);
             out->set(0.0f, 0.0f);
             return false;
             return false;
         }
         }
@@ -756,7 +756,7 @@ bool Properties::getVector2(const char* name, Vector2* out) const
 
 
 bool Properties::getVector3(const char* name, Vector3* out) const
 bool Properties::getVector3(const char* name, Vector3* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -766,7 +766,7 @@ bool Properties::getVector3(const char* name, Vector3* out) const
         scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
         scanned = sscanf(valueString, "%f,%f,%f", &x, &y, &z);
         if (scanned != 3)
         if (scanned != 3)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }
@@ -781,7 +781,7 @@ bool Properties::getVector3(const char* name, Vector3* out) const
 
 
 bool Properties::getVector4(const char* name, Vector4* out) const
 bool Properties::getVector4(const char* name, Vector4* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -791,7 +791,7 @@ bool Properties::getVector4(const char* name, Vector4* out) const
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &w);
         if (scanned != 4)
         if (scanned != 4)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }
@@ -806,7 +806,7 @@ bool Properties::getVector4(const char* name, Vector4* out) const
 
 
 bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
 bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -816,7 +816,7 @@ bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) c
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
         scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
         if (scanned != 4)
         if (scanned != 4)
         {
         {
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f, 1.0f);
             out->set(0.0f, 0.0f, 0.0f, 1.0f);
             return false;
             return false;
         }
         }
@@ -831,7 +831,7 @@ bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) c
 
 
 bool Properties::getColor(const char* name, Vector3* out) const
 bool Properties::getColor(const char* name, Vector3* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -840,7 +840,7 @@ bool Properties::getColor(const char* name, Vector3* out) const
             valueString[0] != '#')
             valueString[0] != '#')
         {
         {
             // Not a color string.
             // Not a color string.
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f);
             out->set(0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }
@@ -859,7 +859,7 @@ bool Properties::getColor(const char* name, Vector3* out) const
 
 
 bool Properties::getColor(const char* name, Vector4* out) const
 bool Properties::getColor(const char* name, Vector4* out) const
 {
 {
-    assert(out);
+    GP_ASSERT(out);
 
 
     const char* valueString = getString(name);
     const char* valueString = getString(name);
     if (valueString)
     if (valueString)
@@ -868,7 +868,7 @@ bool Properties::getColor(const char* name, Vector4* out) const
             valueString[0] != '#')
             valueString[0] != '#')
         {
         {
             // Not a color string.
             // Not a color string.
-            LOG_ERROR_VARG("Error parsing property: %s", name);
+            GP_ERROR("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
             return false;
             return false;
         }
         }

+ 3 - 3
gameplay/src/Properties.h

@@ -95,7 +95,7 @@ namespace gameplay
         // Print the name and ID of the current namespace.
         // Print the name and ID of the current namespace.
         const char* spacename = properties->getNamespace();
         const char* spacename = properties->getNamespace();
         const char* id = properties->getId();
         const char* id = properties->getId();
-        WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
+        GP_WARN("Namespace: %s  ID: %s\n{", spacename, id);
  
  
         // Print all properties in this namespace.
         // Print all properties in this namespace.
         const char* name = properties->getNextProperty();
         const char* name = properties->getNextProperty();
@@ -103,10 +103,10 @@ namespace gameplay
         while (name != NULL)
         while (name != NULL)
         {
         {
             value = properties->getString(name);
             value = properties->getString(name);
-            WARN_VARG("%s = %s", name, value);
+            GP_WARN("%s = %s", name, value);
             name = properties->getNextProperty();
             name = properties->getNextProperty();
         }
         }
-        WARN("}\n");
+        GP_WARN("}\n");
  
  
         // Print the properties of every namespace within this one.
         // Print the properties of every namespace within this one.
         Properties* space = properties->getNextNamespace();
         Properties* space = properties->getNextNamespace();

+ 11 - 11
gameplay/src/Quaternion.cpp

@@ -62,14 +62,14 @@ bool Quaternion::isZero() const
 
 
 void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
 void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     m.getRotation(dst);
     m.getRotation(dst);
 }
 }
 
 
 void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst)
 void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst)
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     float halfAngle = angle * 0.5f;
     float halfAngle = angle * 0.5f;
     float sinHalfAngle = sinf(halfAngle);
     float sinHalfAngle = sinf(halfAngle);
@@ -151,7 +151,7 @@ void Quaternion::normalize()
 
 
 void Quaternion::normalize(Quaternion* dst) const
 void Quaternion::normalize(Quaternion* dst) const
 {
 {
-    assert(dst);
+    GP_ASSERT(dst);
 
 
     if (this != dst)
     if (this != dst)
     {
     {
@@ -189,7 +189,7 @@ void Quaternion::set(float x, float y, float z, float w)
 
 
 void Quaternion::set(float* array)
 void Quaternion::set(float* array)
 {
 {
-    assert(array);
+    GP_ASSERT(array);
 
 
     x = array[0];
     x = array[0];
     y = array[1];
     y = array[1];
@@ -225,7 +225,7 @@ void Quaternion::setIdentity()
 
 
 float Quaternion::toAxisAngle(Vector3* axis) const
 float Quaternion::toAxisAngle(Vector3* axis) const
 {
 {
-    assert(axis);
+    GP_ASSERT(axis);
 
 
     Quaternion q(x, y, z, w);
     Quaternion q(x, y, z, w);
     q.normalize();
     q.normalize();
@@ -239,8 +239,8 @@ float Quaternion::toAxisAngle(Vector3* axis) const
 
 
 void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
 void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
 {
 {
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
+    GP_ASSERT(dst);
+    GP_ASSERT(!(t < 0.0f || t > 1.0f));
 
 
     if (t == 0.0f)
     if (t == 0.0f)
     {
     {
@@ -268,8 +268,8 @@ void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quat
 
 
 void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
 void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
 {
 {
-    assert(dst);
-    assert(!(t < 0.0f || t > 1.0f));
+    GP_ASSERT(dst);
+    GP_ASSERT(!(t < 0.0f || t > 1.0f));
 
 
     Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
     Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
     Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
     Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
@@ -285,8 +285,8 @@ void Quaternion::slerp(float q1x, float q1y, float q1z, float q1w, float q2x, fl
     // It contains no division operations, no trig, no inverse trig
     // It contains no division operations, no trig, no inverse trig
     // and no sqrt. Not only does this code tolerate small constraint
     // and no sqrt. Not only does this code tolerate small constraint
     // errors in the input quaternions, it actually corrects for them.
     // errors in the input quaternions, it actually corrects for them.
-    assert(dstx && dsty && dstz && dstw);
-    assert(!(t < 0.0f || t > 1.0f));
+    GP_ASSERT(dstx && dsty && dstz && dstw);
+    GP_ASSERT(!(t < 0.0f || t > 1.0f));
 
 
     if (t == 0.0f)
     if (t == 0.0f)
     {
     {

+ 1 - 0
gameplay/src/Quaternion.h

@@ -41,6 +41,7 @@ class Matrix;
 class Quaternion
 class Quaternion
 {
 {
     friend class Curve;
     friend class Curve;
+    friend class Transform;
 
 
 public:
 public:
 
 

+ 14 - 13
gameplay/src/RadioButton.cpp

@@ -26,6 +26,8 @@ RadioButton::~RadioButton()
 
 
 RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
 RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
 {
 {
+    GP_ASSERT(properties);
+
     RadioButton* radioButton = new RadioButton();
     RadioButton* radioButton = new RadioButton();
     radioButton->initialize(style, properties);
     radioButton->initialize(style, properties);
 
 
@@ -67,8 +69,7 @@ void RadioButton::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     {
     {
-        assert("TEXT_CHANGED event is not applicable to RadioButton.");
-        eventFlags &= ~Listener::TEXT_CHANGED;
+        GP_ERROR("TEXT_CHANGED event is not applicable to RadioButton.");
     }
     }
 
 
     Control::addListener(listener, eventFlags);
     Control::addListener(listener, eventFlags);
@@ -87,8 +88,8 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
         {
         {
             if (_state == Control::ACTIVE)
             if (_state == Control::ACTIVE)
             {
             {
-                if (x > 0 && x <= _clipBounds.width &&
-                    y > 0 && y <= _clipBounds.height)
+                if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                    y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
                 {
                 {
                     if (!_selected)
                     if (!_selected)
                     {
                     {
@@ -111,6 +112,7 @@ void RadioButton::clearSelected(const std::string& groupId)
     for (it = __radioButtons.begin(); it < __radioButtons.end(); it++)
     for (it = __radioButtons.begin(); it < __radioButtons.end(); it++)
     {
     {
         RadioButton* radioButton = *it;
         RadioButton* radioButton = *it;
+        GP_ASSERT(radioButton);
         if (groupId == radioButton->_groupId)
         if (groupId == radioButton->_groupId)
         {
         {
             radioButton->_selected = false;
             radioButton->_selected = false;
@@ -120,9 +122,9 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
     }
 }
 }
 
 
-void RadioButton::update(const Rectangle& clip)
+void RadioButton::update(const Rectangle& clip, const Vector2& offset)
 {
 {
-    Label::update(clip);
+    Label::update(clip, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())
@@ -159,11 +161,11 @@ void RadioButton::update(const Rectangle& clip)
 
 
 void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
 {
+    GP_ASSERT(spriteBatch);
+    GP_ASSERT(_image);
+
     // Left, v-center.
     // Left, v-center.
-    // TODO: Set an alignment for radio button images.
-    const Theme::Border& border = getBorder(_state);
-    const Theme::Padding padding = getPadding();
-    
+    // TODO: Set an alignment for radio button images.   
     const Rectangle& region = _image->getRegion();
     const Rectangle& region = _image->getRegion();
     const Theme::UVs& uvs = _image->getUVs();
     const Theme::UVs& uvs = _image->getUVs();
     Vector4 color = _image->getColor();
     Vector4 color = _image->getColor();
@@ -179,10 +181,9 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
         size.set(_imageSize);
         size.set(_imageSize);
     }
     }
 
 
-    Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
-        clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+    Vector2 pos(_viewportBounds.x, _viewportBounds.y + _viewportBounds.height * 0.5f - size.y * 0.5f);
 
 
-    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _clip);
+    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
 }
 }
 
 
 }
 }

+ 1 - 1
gameplay/src/RadioButton.h

@@ -114,7 +114,7 @@ protected:
      *
      *
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
      */
      */
-    void update(const Rectangle& clip);
+    void update(const Rectangle& clip, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.

+ 4 - 4
gameplay/src/RenderState.cpp

@@ -52,7 +52,7 @@ void RenderState::finalize()
 
 
 MaterialParameter* RenderState::getParameter(const char* name) const
 MaterialParameter* RenderState::getParameter(const char* name) const
 {
 {
-    assert(name);
+    GP_ASSERT(name);
 
 
     MaterialParameter* param;
     MaterialParameter* param;
 
 
@@ -492,13 +492,13 @@ RenderState::Blend parseBlend(const char* value)
     if (upper == "SRC_ALPHA_SATURATE")
     if (upper == "SRC_ALPHA_SATURATE")
         return RenderState::BLEND_SRC_ALPHA_SATURATE;
         return RenderState::BLEND_SRC_ALPHA_SATURATE;
 
 
-    WARN_VARG("Warning: Unrecognized blend value (%s), defaulting to BLEND_ONE.", value);
+    GP_WARN("Warning: Unrecognized blend value (%s), defaulting to BLEND_ONE.", value);
     return RenderState::BLEND_ONE;
     return RenderState::BLEND_ONE;
 }
 }
 
 
 void RenderState::StateBlock::setState(const char* name, const char* value)
 void RenderState::StateBlock::setState(const char* name, const char* value)
 {
 {
-    assert(name && value);
+    GP_ASSERT(name && value);
 
 
     if (strcmp(name, "blend") == 0)
     if (strcmp(name, "blend") == 0)
     {
     {
@@ -526,7 +526,7 @@ void RenderState::StateBlock::setState(const char* name, const char* value)
     }
     }
     else
     else
     {
     {
-        WARN_VARG("Warning: Invalid render state: %s", name);
+        GP_WARN("Warning: Invalid render state: %s", name);
     }
     }
 }
 }
 
 

+ 4 - 4
gameplay/src/Scene.cpp

@@ -59,7 +59,7 @@ void Scene::setId(const char* id)
 
 
 Node* Scene::findNode(const char* id, bool recursive, bool exactMatch) const
 Node* Scene::findNode(const char* id, bool recursive, bool exactMatch) const
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     // Search immediate children first.
     // Search immediate children first.
     for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling())
     for (Node* child = getFirstNode(); child != NULL; child = child->getNextSibling())
@@ -89,7 +89,7 @@ Node* Scene::findNode(const char* id, bool recursive, bool exactMatch) const
 
 
 unsigned int Scene::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 unsigned int Scene::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 {
 {
-    assert(id);
+    GP_ASSERT(id);
 
 
     unsigned int count = 0;
     unsigned int count = 0;
 
 
@@ -129,7 +129,7 @@ Node* Scene::addNode(const char* id)
 
 
 void Scene::addNode(Node* node)
 void Scene::addNode(Node* node)
 {
 {
-    assert(node);
+    GP_ASSERT(node);
 
 
     if (node->_scene == this)
     if (node->_scene == this)
     {
     {
@@ -180,7 +180,7 @@ void Scene::addNode(Node* node)
 
 
 void Scene::removeNode(Node* node)
 void Scene::removeNode(Node* node)
 {
 {
-    assert(node);
+    GP_ASSERT(node);
 
 
     if (node->_scene != this)
     if (node->_scene != this)
         return;
         return;

+ 51 - 50
gameplay/src/SceneLoader.cpp

@@ -13,23 +13,23 @@ std::string SceneLoader::_path;
 
 
 Scene* SceneLoader::load(const char* url)
 Scene* SceneLoader::load(const char* url)
 {
 {
-    assert(url);
+    GP_ASSERT(url);
 
 
     // Load the scene properties from file.
     // Load the scene properties from file.
     Properties* properties = Properties::create(url);
     Properties* properties = Properties::create(url);
-    assert(properties);
+    GP_ASSERT(properties);
     if (properties == NULL)
     if (properties == NULL)
     {
     {
-        WARN_VARG("Failed to load scene file: %s", url);
+        GP_WARN("Failed to load scene file: %s", url);
         return NULL;
         return NULL;
     }
     }
 
 
     // Check if the properties object is valid and has a valid namespace.
     // Check if the properties object is valid and has a valid namespace.
     Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
     Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-    assert(sceneProperties);
+    GP_ASSERT(sceneProperties);
     if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
     if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
     {
     {
-        WARN("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
+        GP_WARN("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
         SAFE_DELETE(properties);
         SAFE_DELETE(properties);
         return NULL;
         return NULL;
     }
     }
@@ -159,7 +159,7 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
             Node* node = scene->findNode(sceneNode._nodeID);
             Node* node = scene->findNode(sceneNode._nodeID);
             if (!node)
             if (!node)
             {
             {
-                WARN_VARG("Attempting to set a property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
+                GP_WARN("Attempting to set a property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
                 continue;
                 continue;
             }
             }
 
 
@@ -167,7 +167,7 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
             {
             {
                 SceneNodeProperty& snp = sceneNode._properties[j];
                 SceneNodeProperty& snp = sceneNode._properties[j];
                 if (typeFlags & snp._type)
                 if (typeFlags & snp._type)
-                    applyNodeProperty(sceneNode, node, sceneProperties, snp);
+                    applyNodeProperty(sceneNode, node, sceneProperties, snp, scene);
             }
             }
         }
         }
         else
         else
@@ -185,13 +185,13 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
                     continue;
                     continue;
 
 
                 for (unsigned int k = 0; k < nodeCount; ++k)
                 for (unsigned int k = 0; k < nodeCount; ++k)
-                    applyNodeProperty(sceneNode, nodes[k], sceneProperties, snp);
+                    applyNodeProperty(sceneNode, nodes[k], sceneProperties, snp, scene);
             }
             }
         }
         }
     }
     }
 }
 }
 
 
-void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp)
+void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp, const Scene* scene)
 {
 {
     if (snp._type == SceneNodeProperty::AUDIO ||
     if (snp._type == SceneNodeProperty::AUDIO ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::MATERIAL ||
@@ -204,7 +204,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         Properties* p = _propertiesFromFile[snp._file];
         Properties* p = _propertiesFromFile[snp._file];
         if (!p)
         if (!p)
         {
         {
-            WARN_VARG("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+            GP_WARN("The referenced node data in file '%s' failed to load.", snp._file.c_str());
             return;
             return;
         }
         }
 
 
@@ -214,7 +214,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             p = p->getNamespace(snp._id.c_str());
             p = p->getNamespace(snp._id.c_str());
             if (!p)
             if (!p)
             {
             {
-                WARN_VARG("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                GP_WARN("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
                 return;
                 return;
             }
             }
         }
         }
@@ -236,7 +236,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         }
         }
         case SceneNodeProperty::MATERIAL:
         case SceneNodeProperty::MATERIAL:
             if (!node->getModel())
             if (!node->getModel())
-                WARN_VARG("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
+                GP_WARN("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
             else
             else
             {
             {
                 Material* material = Material::create(p);
                 Material* material = Material::create(p);
@@ -259,7 +259,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             Properties* p = _propertiesFromFile[snp._file];
             Properties* p = _propertiesFromFile[snp._file];
             if (!p)
             if (!p)
             {
             {
-                WARN_VARG("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+                GP_WARN("The referenced node data in file '%s' failed to load.", snp._file.c_str());
                 return;
                 return;
             }
             }
 
 
@@ -269,7 +269,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 p = p->getNamespace(snp._id.c_str());
                 p = p->getNamespace(snp._id.c_str());
                 if (!p)
                 if (!p)
                 {
                 {
-                    WARN_VARG("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
+                    GP_WARN("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
                     return;
                     return;
                 }
                 }
             }
             }
@@ -283,15 +283,15 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             // Check to make sure the type of the namespace used to load the physics collision object is correct.
             // Check to make sure the type of the namespace used to load the physics collision object is correct.
             if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
             if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
             {
             {
-                WARN_VARG("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_WARN("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
             }
             }
             else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
             else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
             {
             {
-                WARN_VARG("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_WARN("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
             }
             }
             else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidBody") != 0)
             else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidBody") != 0)
             {
             {
-                WARN_VARG("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_WARN("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
             }
             }
             else
             else
             {
             {
@@ -300,21 +300,21 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                 const char* name = NULL;
                 const char* name = NULL;
                 if (np && (name = np->getString("rigidBodyModel")))
                 if (np && (name = np->getString("rigidBodyModel")))
                 {
                 {
-                    Node* modelNode = node->getScene()->findNode(name);
+                    Node* modelNode = scene->findNode(name);
                     if (!modelNode)
                     if (!modelNode)
-                        WARN_VARG("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
+                        GP_WARN("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
                     else
                     else
                     {
                     {
                         if (!modelNode->getModel())
                         if (!modelNode->getModel())
-                            WARN_VARG("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
+                            GP_WARN("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
                         else
                         else
                         {
                         {
                             // Temporarily set rigidBody model on model so it's used during collision object creation.
                             // Temporarily set rigidBody model on model so it's used during collision object creation.
                             Model* model = node->getModel();
                             Model* model = node->getModel();
-                            assert(model);
                         
                         
                             // Up ref count to prevent node from releasing the model when we swap it.
                             // Up ref count to prevent node from releasing the model when we swap it.
-                            model->addRef(); 
+                            if (model)
+                                model->addRef(); 
                         
                         
                             // Create collision object with new rigidBodyModel set.
                             // Create collision object with new rigidBodyModel set.
                             node->setModel(modelNode->getModel());
                             node->setModel(modelNode->getModel());
@@ -324,7 +324,8 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                             node->setModel(model);
                             node->setModel(model);
                         
                         
                             // Decrement temporarily added reference.
                             // Decrement temporarily added reference.
-                            model->release();
+                            if (model)
+                                model->release();
                         }
                         }
                     }
                     }
                 }
                 }
@@ -378,7 +379,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             break;
             break;
         }
         }
         default:
         default:
-            WARN_VARG("Unsupported node property type: %d.", snp._type);
+            GP_WARN("Unsupported node property type: %d.", snp._type);
             break;
             break;
         }
         }
     }
     }
@@ -415,7 +416,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     }
                     else
                     else
                     {
                     {
-                        WARN_VARG("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
+                        GP_WARN("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
                     }
                     }
                 }
                 }
                 else
                 else
@@ -437,7 +438,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     }
                     }
                     else
                     else
                     {
                     {
-                        WARN_VARG("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
+                        GP_WARN("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
                     }
                     }
                 }
                 }
             }
             }
@@ -462,7 +463,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                         }
                         else
                         else
                         {
                         {
-                            WARN_VARG("Could not load node '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                            GP_WARN("Could not load node '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
                         }
                         }
                     }
                     }
                     else
                     else
@@ -493,7 +494,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                         }
                         if (matchCount == 0)
                         if (matchCount == 0)
                         {
                         {
-                            WARN_VARG("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
+                            GP_WARN("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
                         }
                         }
                     }
                     }
 
 
@@ -501,7 +502,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                 }
                 }
                 else
                 else
                 {
                 {
-                    WARN_VARG("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
+                    GP_WARN("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
                 }
                 }
             }
             }
 
 
@@ -522,7 +523,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         {
         {
             if (strlen(ns->getId()) == 0)
             if (strlen(ns->getId()) == 0)
             {
             {
-                WARN("Nodes must have an ID; skipping the current node.");
+                GP_WARN("Nodes must have an ID; skipping the current node.");
                 continue;
                 continue;
             }
             }
 
 
@@ -595,7 +596,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 }
                 else
                 else
                 {
                 {
-                    WARN_VARG("Unsupported node property: %s = %s", name, ns->getString());
+                    GP_WARN("Unsupported node property: %s = %s", name, ns->getString());
                 }
                 }
             }
             }
         }
         }
@@ -610,20 +611,20 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                     const char* animationID = animation->getId();
                     const char* animationID = animation->getId();
                     if (strlen(animationID) == 0)
                     if (strlen(animationID) == 0)
                     {
                     {
-                        WARN("Animations must have an ID; skipping the current animation.");
+                        GP_WARN("Animations must have an ID; skipping the current animation.");
                         continue;
                         continue;
                     }
                     }
 
 
                     const char* url = animation->getString("url");
                     const char* url = animation->getString("url");
                     if (!url)
                     if (!url)
                     {
                     {
-                        WARN_VARG("Animations must have a URL; skipping animation '%s'.", animationID);
+                        GP_WARN("Animations must have a URL; skipping animation '%s'.", animationID);
                         continue;
                         continue;
                     }
                     }
                     const char* targetID = animation->getString("target");
                     const char* targetID = animation->getString("target");
                     if (!targetID)
                     if (!targetID)
                     {
                     {
-                        WARN_VARG("Animations must have a target; skipping animation '%s'.", animationID);
+                        GP_WARN("Animations must have a target; skipping animation '%s'.", animationID);
                         continue;
                         continue;
                     }
                     }
 
 
@@ -631,7 +632,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 }
                 }
                 else
                 else
                 {
                 {
-                    WARN_VARG("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
+                    GP_WARN("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
                 }
                 }
             }
             }
         }
         }
@@ -643,7 +644,7 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
         else
         else
         {
         {
             // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
             // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
-            WARN_VARG("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
+            GP_WARN("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
         }
         }
     }
     }
 }
 }
@@ -658,7 +659,7 @@ void SceneLoader::createAnimations(const Scene* scene)
         Node* node = scene->findNode(_animations[i]._targetID);
         Node* node = scene->findNode(_animations[i]._targetID);
         if (!node)
         if (!node)
         {
         {
-            WARN_VARG("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
+            GP_WARN("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
             continue;
             continue;
         }
         }
 
 
@@ -666,7 +667,7 @@ void SceneLoader::createAnimations(const Scene* scene)
         Properties* p = _propertiesFromFile[_animations[i]._file];
         Properties* p = _propertiesFromFile[_animations[i]._file];
         if (!p)
         if (!p)
         {
         {
-            WARN_VARG("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
+            GP_WARN("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
             continue;
             continue;
         }
         }
         if (_animations[i]._id.size() > 0)
         if (_animations[i]._id.size() > 0)
@@ -674,7 +675,7 @@ void SceneLoader::createAnimations(const Scene* scene)
             p = p->getNamespace(_animations[i]._id.c_str());
             p = p->getNamespace(_animations[i]._id.c_str());
             if (!p)
             if (!p)
             {
             {
-                WARN_VARG("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
+                GP_WARN("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
                 continue;
                 continue;
             }
             }
         }
         }
@@ -769,7 +770,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Bundle* bundle = Bundle::create(_path.c_str());
     Bundle* bundle = Bundle::create(_path.c_str());
     if (!bundle)
     if (!bundle)
     {
     {
-        WARN_VARG("Failed to load scene GPB file '%s'.", _path.c_str());
+        GP_WARN("Failed to load scene GPB file '%s'.", _path.c_str());
         return NULL;
         return NULL;
     }
     }
 
 
@@ -777,7 +778,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Scene* scene = bundle->loadScene(NULL);
     Scene* scene = bundle->loadScene(NULL);
     if (!scene)
     if (!scene)
     {
     {
-        WARN_VARG("Failed to load scene from '%s'.", _path.c_str());
+        GP_WARN("Failed to load scene from '%s'.", _path.c_str());
         SAFE_RELEASE(bundle);
         SAFE_RELEASE(bundle);
         return NULL;
         return NULL;
     }
     }
@@ -816,18 +817,18 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
             name = constraint->getString("rigidBodyA");
             name = constraint->getString("rigidBodyA");
             if (!name)
             if (!name)
             {
             {
-                WARN_VARG("Missing property 'rigidBodyA' for constraint %s", constraint->getId());
+                GP_WARN("Missing property 'rigidBodyA' for constraint %s", constraint->getId());
                 continue;
                 continue;
             }
             }
             Node* rbANode = scene->findNode(name);
             Node* rbANode = scene->findNode(name);
             if (!rbANode)
             if (!rbANode)
             {
             {
-                WARN_VARG("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
+                GP_WARN("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
                 continue;
                 continue;
             }
             }
             if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             {
             {
-                WARN_VARG("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
+                GP_WARN("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
                 continue;
                 continue;
             }
             }
             PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
             PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
@@ -843,12 +844,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 Node* rbBNode = scene->findNode(name);
                 Node* rbBNode = scene->findNode(name);
                 if (!rbBNode)
                 if (!rbBNode)
                 {
                 {
-                    WARN_VARG("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
+                    GP_WARN("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
                     continue;
                     continue;
                 }
                 }
                 if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 {
                 {
-                    WARN_VARG("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
+                    GP_WARN("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
                     continue;
                     continue;
                 }
                 }
                 rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
                 rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
@@ -888,7 +889,7 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
         }
         }
         else
         else
         {
         {
-            WARN_VARG("Unsupported child namespace (of 'physics'): %s", physics->getNamespace());
+            GP_WARN("Unsupported child namespace (of 'physics'): %s", physics->getNamespace());
         }
         }
     }
     }
 }
 }
@@ -900,9 +901,9 @@ void SceneLoader::loadReferencedFiles()
     for (; iter != _propertiesFromFile.end(); iter++)
     for (; iter != _propertiesFromFile.end(); iter++)
     {
     {
         Properties* p = Properties::create(iter->first.c_str());
         Properties* p = Properties::create(iter->first.c_str());
-        assert(p);
+        GP_ASSERT(p);
         if (p == NULL)
         if (p == NULL)
-            WARN_VARG("Failed to load referenced file: %s", iter->first.c_str());
+            GP_WARN("Failed to load referenced file: %s", iter->first.c_str());
 
 
         iter->second = p;
         iter->second = p;
     }
     }
@@ -940,7 +941,7 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
 {
 {
     if (!rbB)
     if (!rbB)
     {
     {
-        WARN("Spring constraints require two rigid bodies.");
+        GP_WARN("Spring constraints require two rigid bodies.");
         return NULL;
         return NULL;
     }
     }
 
 

+ 1 - 1
gameplay/src/SceneLoader.h

@@ -83,7 +83,7 @@ private:
 
 
     static void applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags);
     static void applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags);
 
 
-    static void applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp);
+    static void applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp, const Scene* scene);
 
 
     static void applyNodeUrls(Scene* scene);
     static void applyNodeUrls(Scene* scene);
 
 

+ 4 - 4
gameplay/src/ScreenDisplayer.h

@@ -14,10 +14,10 @@ class ScreenDisplayer
 {
 {
 public:
 public:
 
 
-	/**
-	 * Constructor.
-	 */
-	ScreenDisplayer();
+    /**
+     * Constructor.
+     */
+    ScreenDisplayer();
 
 
     /**
     /**
      * Displays a screen using the {@link Game#renderOnce} mechanism for at least the given amount of time.
      * Displays a screen using the {@link Game#renderOnce} mechanism for at least the given amount of time.

+ 140 - 0
gameplay/src/ScrollLayout.cpp

@@ -0,0 +1,140 @@
+#include "Base.h"
+#include "Control.h"
+#include "ScrollLayout.h"
+#include "Container.h"
+
+namespace gameplay
+{
+
+ScrollLayout::ScrollLayout() 
+    : _scrollPosition(Vector2::zero()), _lastX(0), _lastY(0), _scrolling(false),
+      _positionVertically(false), _positionHorizontally(false)
+{
+}
+
+ScrollLayout::ScrollLayout(const ScrollLayout& copy)
+{
+}
+
+ScrollLayout::~ScrollLayout()
+{
+}
+
+ScrollLayout* ScrollLayout::create()
+{
+    return new ScrollLayout();
+}
+
+Layout::Type ScrollLayout::getType()
+{
+    return Layout::LAYOUT_SCROLL;
+}
+
+void ScrollLayout::update(const Container* container)
+{
+    // Position controls if automatic positioning is enabled.
+    if (_positionVertically && _positionHorizontally)
+    {
+        // Treat as scrollable flow layout.
+    }
+    else if (_positionVertically)
+    {
+        // Scrollable vertical layout.
+    }
+    else if (_positionHorizontally)
+    {
+        // Scrollable horizontal layout.
+    }
+
+    // Calculate total width and height.
+    float totalWidth = 0;
+    float totalHeight = 0;
+    std::vector<Control*> controls = container->getControls();
+    unsigned int controlsCount = controls.size();
+    for (unsigned int i = 0; i < controlsCount; i++)
+    {
+        Control* control = controls.at(i);
+
+        const Rectangle& bounds = control->getBounds();
+        const Theme::Margin& margin = control->getMargin();
+
+        float newWidth = bounds.x + bounds.width + margin.left + margin.right;
+        if (newWidth > totalWidth)
+        {
+            totalWidth = newWidth;
+        }
+
+        float newHeight = bounds.y + bounds.height + margin.top + margin.bottom;
+        if (newHeight > totalHeight)
+        {
+            totalHeight = newHeight;
+        }
+    }
+
+    const Rectangle& containerBounds = container->getBounds();
+    const Theme::Border& containerBorder = container->getBorder(container->getState());
+    const Theme::Padding& containerPadding = container->getPadding();
+
+    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right;
+    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
+
+    // Stop scrolling when the far edge is reached.
+    if (-_scrollPosition.x > totalWidth - clipWidth)
+    {
+        _scrollPosition.x = -(totalWidth - clipWidth);
+    }
+    
+    if (-_scrollPosition.y > totalHeight - clipHeight)
+    {
+        _scrollPosition.y = -(totalHeight - clipHeight);
+    }
+
+    if (_scrollPosition.x > 0)
+    {
+        _scrollPosition.x = 0;
+    }
+
+    if (_scrollPosition.y > 0)
+    {
+        _scrollPosition.y = 0;
+    }
+
+    // Position controls within scroll area.
+    for (unsigned int i = 0; i < controlsCount; i++)
+    {
+        Control* control = controls.at(i);
+        control->update(container->getClip(), _scrollPosition);
+    }
+}
+
+bool ScrollLayout::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    switch(evt)
+    {
+    case Touch::TOUCH_PRESS:
+        _lastX = x;
+        _lastY = y;
+        _scrolling = true;
+        break;
+
+    case Touch::TOUCH_MOVE:
+        if (_scrolling)
+        {
+            // Calculate the latest movement delta for the next update to use.
+            _scrollPosition.x += x - _lastX;
+            _scrollPosition.y += y - _lastY;
+            _lastX = x;
+            _lastY = y;
+            return true;
+        }
+        break;
+
+    case Touch::TOUCH_RELEASE:
+        _scrolling = false;
+        break;
+    }
+
+    return false;
+}
+
+}

+ 72 - 0
gameplay/src/ScrollLayout.h

@@ -0,0 +1,72 @@
+#ifndef SCROLLLAYOUT_H_
+#define SCROLLLAYOUT_H_
+
+#include "Layout.h"
+
+namespace gameplay
+{
+
+class ScrollLayout : public Layout
+{
+    friend class Form;
+    friend class Container;
+
+public:
+
+    /**
+     * Get the type of this Layout.
+     *
+     * @return Layout::LAYOUT_SCROLL
+     */
+    Layout::Type getType();
+
+protected:
+
+    /**
+     * Create a ScrollLayout.
+     *
+     * @return A ScrollLayout object.
+     */
+    static ScrollLayout* create();
+
+    /**
+     * Update the controls contained by the specified container.
+     *
+     * @param container The container to update.
+     */
+    void update(const Container* container);
+
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+private:
+
+    /**
+     * Constructor.
+     */
+    ScrollLayout();
+
+    /**
+     * Constructor.
+     */
+    ScrollLayout(const ScrollLayout& copy);
+
+    /**
+     * Destructor.
+     */
+    virtual ~ScrollLayout();
+
+    Vector2 _scrollPosition;
+
+    // Previous touch point.
+    int _lastX;
+    int _lastY;
+
+    bool _scrolling;
+
+    bool _positionVertically;
+    bool _positionHorizontally;
+};
+
+}
+
+#endif

+ 26 - 20
gameplay/src/Slider.cpp

@@ -3,7 +3,7 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-Slider::Slider()
+Slider::Slider() : _minImage(NULL), _maxImage(NULL), _trackImage(NULL), _markerImage(NULL)
 {
 {
 }
 }
 
 
@@ -13,6 +13,8 @@ Slider::~Slider()
 
 
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 {
 {
+    GP_ASSERT(properties);
+
     Slider* slider = new Slider();
     Slider* slider = new Slider();
     slider->initialize(style, properties);
     slider->initialize(style, properties);
 
 
@@ -68,8 +70,7 @@ void Slider::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
     {
     {
-        assert("TEXT_CHANGED event is not applicable to Slider.");
-        eventFlags &= ~Listener::TEXT_CHANGED;
+        GP_ERROR("TEXT_CHANGED event is not applicable to Slider.");
     }
     }
 
 
     Control::addListener(listener, eventFlags);
     Control::addListener(listener, eventFlags);
@@ -87,21 +88,20 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     case Touch::TOUCH_PRESS:
     case Touch::TOUCH_PRESS:
         _state = Control::ACTIVE;
         _state = Control::ACTIVE;
         // Fall through to calculate new value.
         // Fall through to calculate new value.
-
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_RELEASE:
     case Touch::TOUCH_RELEASE:
         if (_state == ACTIVE &&
         if (_state == ACTIVE &&
-            x > 0 && x <= _clipBounds.width &&
-            y > 0 && y <= _clipBounds.height)
+            x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+            y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
         {
             // Horizontal case.
             // Horizontal case.
             const Theme::Border& border = getBorder(_state);
             const Theme::Border& border = getBorder(_state);
             const Theme::Padding& padding = getPadding();
             const Theme::Padding& padding = getPadding();
-            const Rectangle& minCapRegion = getImageRegion("minCap", _state);
-            const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
+            const Rectangle& minCapRegion = _minImage->getRegion();
+            const Rectangle& maxCapRegion = _maxImage->getRegion();
 
 
             float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
             float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
-                (_clipBounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
+                (_bounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
             
             
             if (markerPosition > 1.0f)
             if (markerPosition > 1.0f)
             {
             {
@@ -141,9 +141,9 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void Slider::update(const Rectangle& clip)
+void Slider::update(const Rectangle& clip, const Vector2& offset)
 {
 {
-    Label::update(clip);
+    Label::update(clip, offset);
 
 
     _minImage = getImage("minCap", _state);
     _minImage = getImage("minCap", _state);
     _maxImage = getImage("maxCap", _state);
     _maxImage = getImage("maxCap", _state);
@@ -153,6 +153,12 @@ void Slider::update(const Rectangle& clip)
 
 
 void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
 {
+    GP_ASSERT(spriteBatch);
+    GP_ASSERT(_minImage);
+    GP_ASSERT(_maxImage);
+    GP_ASSERT(_markerImage);
+    GP_ASSERT(_trackImage);
+
     // TODO: Vertical slider.
     // TODO: Vertical slider.
 
 
     // The slider is drawn in the center of the control (perpendicular to orientation).
     // The slider is drawn in the center of the control (perpendicular to orientation).
@@ -182,23 +188,23 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     trackColor.w *= _opacity;
     trackColor.w *= _opacity;
 
 
     // Draw order: track, caps, marker.
     // Draw order: track, caps, marker.
-    float midY = clip.y + _clipBounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f;
-    Vector2 pos(clip.x + _clipBounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
-    spriteBatch->draw(pos.x, pos.y, _clipBounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor, _clip);
+    float midY = _viewportBounds.y + (_viewportBounds.height) * 0.5f;
+    Vector2 pos(_viewportBounds.x, midY - trackRegion.height * 0.5f);
+    spriteBatch->draw(pos.x, pos.y, _viewportBounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor, _viewportClipBounds);
 
 
     pos.y = midY - minCapRegion.height * 0.5f;
     pos.y = midY - minCapRegion.height * 0.5f;
     pos.x -= minCapRegion.width * 0.5f;
     pos.x -= minCapRegion.width * 0.5f;
-    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor, _clip);
+    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor, _viewportClipBounds);
         
         
-    pos.x = clip.x + _clipBounds.x + _clipBounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
-    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor, _clip);
+    pos.x = _viewportBounds.x + _viewportBounds.width - maxCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor, _viewportClipBounds);
 
 
     // Percent across.
     // Percent across.
     float markerPosition = (_value - _min) / (_max - _min);
     float markerPosition = (_value - _min) / (_max - _min);
-    markerPosition *= _clipBounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
-    pos.x = clip.x + _clipBounds.x + border.left + padding.left + minCapRegion.width * 0.5f + markerPosition;
+    markerPosition *= _viewportBounds.width - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
+    pos.x = _viewportBounds.x + minCapRegion.width * 0.5f + markerPosition;
     pos.y = midY - markerRegion.height / 2.0f;
     pos.y = midY - markerRegion.height / 2.0f;
-    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor, _clip);
+    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor, _viewportClipBounds);
 }
 }
 
 
 }
 }

+ 3 - 2
gameplay/src/Slider.h

@@ -154,9 +154,10 @@ protected:
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
      * @param clip The clipping rectangle of this slider's parent container.
      * @param clip The clipping rectangle of this slider's parent container.
+     * @param offset The scroll offset of this slider's parent container.
      */
      */
-    void update(const Rectangle& clip); 
-
+    void update(const Rectangle& clip, const Vector2& offset);
+
     /**
     /**
      * The minimum value for the Slider.
      * The minimum value for the Slider.
      */
      */

+ 157 - 113
gameplay/src/SpriteBatch.cpp

@@ -45,57 +45,6 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
-/**
- * Sprite vertex structure used for batching.
- */
-struct SpriteVertex
-{
-    /**
-     * The x coordinate of the vertex.
-     */
-    float x;
-    
-    /**
-     * The y coordinate of the vertex.
-     */
-    float y;
-    
-    /**
-     * The z coordinate of the vertex.
-     */
-    float z;
-
-    /**
-     * The u component of the (u, v) texture coordinates for the vertex.
-     */
-    float u;
-    
-    /**
-     * The v component of the (u, v) texture coordinates for the vertex.
-     */
-    float v;
-
-    /**
-     * The red color component of the vertex.
-     */
-    float r;
-    
-    /**
-     * The green color component of the vertex.
-     */
-    float g;
-    
-    /**
-     * The blue color component of the vertex.
-     */
-    float b;
-    
-    /**
-     * The alpha component of the vertex.
-     */
-    float a;
-};
-
 // Shared sprite effects
 // Shared sprite effects
 static Effect* __spriteEffect = NULL;
 static Effect* __spriteEffect = NULL;
 
 
@@ -134,7 +83,7 @@ SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsign
 
 
 SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
 SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
 {
 {
-    assert(texture != NULL);
+    GP_ASSERT(texture != NULL);
 
 
     bool customEffect = (effect != NULL);
     bool customEffect = (effect != NULL);
     if (!customEffect)
     if (!customEffect)
@@ -145,7 +94,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             if (__spriteEffect == NULL)
             if (__spriteEffect == NULL)
             {
             {
-                LOG_ERROR("Unable to load sprite effect.");
+                GP_ERROR("Unable to load sprite effect.");
                 return NULL;
                 return NULL;
             }
             }
 
 
@@ -171,7 +120,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     }
     }
     if (!samplerUniform)
     if (!samplerUniform)
     {
     {
-        LOG_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
+        GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
         SAFE_RELEASE(effect);
         SAFE_RELEASE(effect);
         return NULL;
         return NULL;
     }
     }
@@ -303,31 +252,87 @@ void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vect
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
 {
     // Calculate the vertex positions.
     // Calculate the vertex positions.
-    Vector3 p[4];
+    //static Vector3 p[4];
+
+    // Pre-optimized:
+    /*
     p[0] = position - 0.5f * width * right - 0.5f * height * forward;
     p[0] = position - 0.5f * width * right - 0.5f * height * forward;
     p[1] = position + 0.5f * width * right - 0.5f * height * forward;
     p[1] = position + 0.5f * width * right - 0.5f * height * forward;
     p[2] = p[0] + height * forward;
     p[2] = p[0] + height * forward;
     p[3] = p[1] + height * forward;
     p[3] = p[1] + height * forward;
+    */
+    
+    // Optimized:
+    Vector3 tRight(right);
+    tRight *= width * 0.5f;
+    Vector3 tForward(forward);
+    tForward *= height * 0.5f;
+    
+    Vector3 p0 = position;
+    p0 -= tRight;
+    p0 -= tForward;
+
+    Vector3 p1 = position;
+    p1 += tRight;
+    p1 -= tForward;
+
+    tForward = forward;
+    tForward *= height;
+    Vector3 p2 = p0;
+    p2 += tForward;
+    Vector3 p3 = p1;
+    p3 += tForward;
 
 
     // Calculate the rotation point.
     // Calculate the rotation point.
-    Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+    
+    // Pre-optimized:
+    //Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+
+    // Optimized:
+    Vector3 rp = p0;
+    tRight = right;
+    tRight *= width * rotationPoint.x;
+    tForward *= rotationPoint.y;
+    rp += tRight;
+    rp += tForward;
 
 
     // Rotate all points the specified amount about the given point (about the up vector).
     // Rotate all points the specified amount about the given point (about the up vector).
-    Vector3 u;
+    static Vector3 u;
     Vector3::cross(right, forward, &u);
     Vector3::cross(right, forward, &u);
-    Matrix rotation;
+    static Matrix rotation;
     Matrix::createRotation(u, rotationAngle, &rotation);
     Matrix::createRotation(u, rotationAngle, &rotation);
+
+    // Pre-optimized:
+    /*
     p[0] = (rotation * (p[0] - rp)) + rp;
     p[0] = (rotation * (p[0] - rp)) + rp;
     p[1] = (rotation * (p[1] - rp)) + rp;
     p[1] = (rotation * (p[1] - rp)) + rp;
     p[2] = (rotation * (p[2] - rp)) + rp;
     p[2] = (rotation * (p[2] - rp)) + rp;
     p[3] = (rotation * (p[3] - rp)) + rp;
     p[3] = (rotation * (p[3] - rp)) + rp;
+    */
+
+    // Optimized:
+    p0 -= rp;
+    p0 *= rotation;
+    p0 += rp;
+
+    p1 -= rp;
+    p1 *= rotation;
+    p1 += rp;
+
+    p2 -= rp;
+    p2 *= rotation;
+    p2 += rp;
+
+    p3 -= rp;
+    p3 *= rotation;
+    p3 += rp;
 
 
     // Add the sprite vertex data to the batch.
     // Add the sprite vertex data to the batch.
     static SpriteVertex v[4];
     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);
+    ADD_SPRITE_VERTEX(v[0], p0.x, p0.y, p0.z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], p1.x, p1.y, p1.z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], p2.x, p2.y, p2.z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], p3.x, p3.y, p3.z, u2, v2, color.x, color.y, color.z, color.w);
     
     
     static const unsigned short indices[4] = { 0, 1, 2, 3 };
     static const unsigned short indices[4] = { 0, 1, 2, 3 };
     _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
     _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
@@ -340,64 +345,45 @@ void SpriteBatch::draw(float x, float y, float width, float height, float u1, fl
 
 
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
 {
 {
-    // Need to clip the rectangle given by { x, y, width, height } into clip by potentially:
-    //  - Moving x to the right.
-    //  - Moving y down.
-    //  - Moving width to the left.
-    //  - Moving height up.
-    //  - A combination of the above.
-    //  - Not drawing at all.
-    //
-    // We need to scale the uvs accordingly as we do this.
-
-    // First check to see if we need to draw at all.
-    if (x + width < clip.x || x > clip.x + clip.width ||
-        y + height < clip.y || y > clip.y + clip.height)
-    {
-        return;
-    }
+    // Only draw if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
+        draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+}
 
 
-    const float uvWidth = u2 - u1;
-    const float uvHeight = v2 - v1;
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
 
-    // Moving x to the right.
-    if (x < clip.x)
-    {
-        const float percent = (clip.x - x) / width;
-        x = clip.x;
-        u1 += uvWidth * percent;
-    }
+    const float x2 = x + width;
+    const float y2 = y + height;
+    ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
+}
 
 
-    // Moving y down.
-    if (y < clip.y)
-    {
-        const float percent = (clip.y - y) / height;
-        y = clip.y;
-        v1 += uvHeight * percent;
-    }
+void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices)
+{
+    GP_ASSERT(vertices);
 
 
-    // Moving width to the left.
-    const float clipX2 = clip.x + clip.width;
-    float x2 = x + width;
-    if (x2 > clipX2)
+    // Only add a sprite if at least part of the sprite is within the clip region.
+    if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
     {
     {
-        const float percent = (x2 - clipX2) / width;
-        width = clipX2 - x;
-        u2 -= uvWidth * percent;
+        const float x2 = x + width;
+        const float y2 = y + height;
+        ADD_SPRITE_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
+        ADD_SPRITE_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
     }
     }
+}
 
 
-    // Moving height up.
-    const float clipY2 = clip.y + clip.height;
-    float y2 = y + height;
-    if (y2 > clipY2)
-    {
-        const float percent = (y2 - clipY2) / height;
-        height = clipY2 - y;
-        v2 -= uvHeight * percent;
-    }
+void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
+{
+    GP_ASSERT(vertices);
+    GP_ASSERT(indices);
 
 
-    // Now we can perform a normal draw call.
-    draw(x, y, 0, width, height, u1, v1, u2, v2, color);
+    _batch->add(vertices, vertexCount, indices, indexCount);
 }
 }
 
 
 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)
 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)
@@ -435,7 +421,7 @@ RenderState::StateBlock* SpriteBatch::getStateBlock() const
     return _batch->getMaterial()->getStateBlock();
     return _batch->getMaterial()->getStateBlock();
 }
 }
 
 
-Material* SpriteBatch::getMaterial()
+Material* SpriteBatch::getMaterial() const
 {
 {
     return _batch->getMaterial();
     return _batch->getMaterial();
 }
 }
@@ -454,4 +440,62 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
     return _projectionMatrix;
     return _projectionMatrix;
 }
 }
 
 
+bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
+{
+    // Clip the rectangle given by { x, y, width, height } into clip.
+    // We need to scale the uvs accordingly as we do this.
+
+    // First check to see if we need to draw at all.
+    if (x + width < clip.x || x > clip.x + clip.width ||
+        y + height < clip.y || y > clip.y + clip.height)
+    {
+        return false;
+    }
+
+    const float uvWidth = u2 - u1;
+    const float uvHeight = v2 - v1;
+
+    // Moving x to the right.
+    if (x < clip.x)
+    {
+        const float percent = (clip.x - x) / width;
+        const float dx = clip.x - x;
+        x = clip.x;
+        width -= dx;
+        u1 += uvWidth * percent;
+    }
+
+    // Moving y down.
+    if (y < clip.y)
+    {
+        const float percent = (clip.y - y) / height;
+        const float dy = clip.y - y;
+        y = clip.y;
+        height -= dy;
+        v1 += uvHeight * percent;
+    }
+
+    // Moving width to the left.
+    const float clipX2 = clip.x + clip.width;
+    float x2 = x + width;
+    if (x2 > clipX2)
+    {
+        const float percent = (x2 - clipX2) / width;
+        width = clipX2 - x;
+        u2 -= uvWidth * percent;
+    }
+
+    // Moving height up.
+    const float clipY2 = clip.y + clip.height;
+    float y2 = y + height;
+    if (y2 > clipY2)
+    {
+        const float percent = (y2 - clipY2) / height;
+        height = clipY2 - y;
+        v2 -= uvHeight * percent;
+    }
+
+    return true;
+}
+
 }
 }

+ 68 - 2
gameplay/src/SpriteBatch.h

@@ -25,6 +25,7 @@ namespace gameplay
 class SpriteBatch
 class SpriteBatch
 {
 {
     friend class Bundle;
     friend class Bundle;
+    friend class Font;
 
 
 public:
 public:
 
 
@@ -163,7 +164,7 @@ public:
      * @param rotationAngle The rotation angle.
      * @param rotationAngle The rotation angle.
      */
      */
     void draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height, 
     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);
+              float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle);
 
 
     /**
     /**
      * Draws a single sprite.
      * Draws a single sprite.
@@ -237,7 +238,7 @@ public:
      * 
      * 
      * @return The material.
      * @return The material.
      */
      */
-    Material* getMaterial();
+    Material* getMaterial() const;
 
 
     /**
     /**
      * Sets a custom projection matrix to use with the sprite batch.
      * Sets a custom projection matrix to use with the sprite batch.
@@ -253,6 +254,22 @@ public:
 
 
 private:
 private:
 
 
+    /**
+     * Sprite vertex structure used for batching.
+     */
+    struct SpriteVertex
+    {
+        float x;        
+        float y;
+        float z;
+        float u;
+        float v;
+        float r;
+        float g;
+        float b;
+        float a;
+    };
+
     /**
     /**
      * Constructor.
      * Constructor.
      */
      */
@@ -267,6 +284,55 @@ private:
 
 
     const Matrix& getOrthoMatrix() const;
     const Matrix& getOrthoMatrix() const;
 
 
+    /**
+     * Adds a single sprite to a SpriteVertex array.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @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 clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Adds a single sprite to a SpriteVertex array, clipped within a rectangle.
+     * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The sprite width.
+     * @param height The sprite height
+     * @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 clip The clip rectangle.
+     */
+    void addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices);
+
+    /**
+     * Draws an array of vertices.
+     *
+     * @param vertices The vertices to draw.
+     * @param vertexCount The number of vertices within the vertex array.
+     * @param indices The vertex indices.
+     * @param indexCount The number of indices within the index array.
+     */
+    void draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount);
+
+    /**
+     * Clip position and size to fit within clip region.
+     *
+     * @return true if any part of sprite intersects with the clip region and therefore needs drawing, false otherwise.
+     */
+    bool clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2);
+
     MeshBatch* _batch;
     MeshBatch* _batch;
     bool _customEffect;
     bool _customEffect;
     float _textureWidthRatio;
     float _textureWidthRatio;

+ 2 - 2
gameplay/src/Technique.cpp

@@ -9,7 +9,7 @@ namespace gameplay
 Technique::Technique(const char* id, Material* material)
 Technique::Technique(const char* id, Material* material)
     : _id(id ? id : ""), _material(material)
     : _id(id ? id : ""), _material(material)
 {
 {
-    assert(material);
+    GP_ASSERT(material);
 
 
     RenderState::_parent = material;
     RenderState::_parent = material;
 }
 }
@@ -35,7 +35,7 @@ unsigned int Technique::getPassCount() const
 
 
 Pass* Technique::getPass(unsigned int index) const
 Pass* Technique::getPass(unsigned int index) const
 {
 {
-    assert(index < _passes.size());
+    GP_ASSERT(index < _passes.size());
 
 
     return _passes[index];
     return _passes[index];
 }
 }

+ 67 - 46
gameplay/src/TextBox.cpp

@@ -33,8 +33,7 @@ void TextBox::addListener(Control::Listener* listener, int eventFlags)
 {
 {
     if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
     if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
     {
     {
-        assert("VALUE_CHANGED event is not applicable to TextBox.");
-        eventFlags &= ~Listener::VALUE_CHANGED;
+        GP_ERROR("VALUE_CHANGED event is not applicable to TextBox.");
     }
     }
 
 
     Control::addListener(listener, eventFlags);
     Control::addListener(listener, eventFlags);
@@ -57,8 +56,14 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _dirty = true;
             _dirty = true;
             return _consumeTouchEvents;
             return _consumeTouchEvents;
         }
         }
-        else if (!(x > 0 && x <= _clipBounds.width &&
-                    y > 0 && y <= _clipBounds.height))
+        else if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+                 y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
+        {
+            setCaretLocation(x, y);
+            _dirty = true;
+            return _consumeTouchEvents;
+        }
+        else
         {
         {
             _state = NORMAL;
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             Game::getInstance()->displayKeyboard(false);
@@ -68,8 +73,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         break;
         break;
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_MOVE:
         if (_state == FOCUS &&
         if (_state == FOCUS &&
-            x > 0 && x <= _clipBounds.width &&
-            y > 0 && y <= _clipBounds.height)
+            x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+            y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
         {
             setCaretLocation(x, y);
             setCaretLocation(x, y);
             _dirty = true;
             _dirty = true;
@@ -77,14 +82,21 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         }
         }
         break;
         break;
     case Touch::TOUCH_RELEASE:
     case Touch::TOUCH_RELEASE:
-        if (x > 0 && x <= _clipBounds.width &&
-            y > 0 && y <= _clipBounds.height)
+        if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
+            y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
         {
             setCaretLocation(x, y);
             setCaretLocation(x, y);
             _state = FOCUS;
             _state = FOCUS;
             _dirty = true;
             _dirty = true;
             return _consumeTouchEvents;
             return _consumeTouchEvents;
         }
         }
+        else
+        {
+            _state = NORMAL;
+            Game::getInstance()->displayKeyboard(false);
+            _dirty = true;
+            return _consumeTouchEvents;
+        }
         break;
         break;
     }
     }
 
 
@@ -106,7 +118,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         // TODO: Move cursor to beginning of line.
                         // TODO: Move cursor to beginning of line.
                         // This only works for left alignment...
                         // This only works for left alignment...
                         
                         
-                        //_caretLocation.x = _clip.x;
+                        //_caretLocation.x = _viewportClipBounds.x;
                         //_dirty = true;
                         //_dirty = true;
                         break;
                         break;
                     }
                     }
@@ -118,14 +130,16 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_DELETE:
                     case Keyboard::KEY_DELETE:
                     {
                     {
                         Font* font = getFont(_state);
                         Font* font = getFont(_state);
+                        GP_ASSERT(font);
                         unsigned int fontSize = getFontSize(_state);
                         unsigned int fontSize = getFontSize(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
 
 
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                        int textIndex = font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
+                        
                         _text.erase(textIndex, 1);
                         _text.erase(textIndex, 1);
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                        font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
                         notifyListeners(Listener::TEXT_CHANGED);
                         notifyListeners(Listener::TEXT_CHANGED);
@@ -134,13 +148,14 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_LEFT_ARROW:
                     case Keyboard::KEY_LEFT_ARROW:
                     {
                     {
                         Font* font = getFont(_state);
                         Font* font = getFont(_state);
+                        GP_ASSERT(font);
                         unsigned int fontSize = getFontSize(_state);
                         unsigned int fontSize = getFontSize(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
 
 
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex - 1,
+                        font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex - 1,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
                         break;
                         break;
@@ -148,13 +163,14 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_RIGHT_ARROW:
                     case Keyboard::KEY_RIGHT_ARROW:
                     {
                     {
                         Font* font = getFont(_state);
                         Font* font = getFont(_state);
+                        GP_ASSERT(font);
                         unsigned int fontSize = getFontSize(_state);
                         unsigned int fontSize = getFontSize(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
 
 
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                        int textIndex = font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex + 1,
+                        font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
                         break;
                         break;
@@ -162,12 +178,13 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_UP_ARROW:
                     case Keyboard::KEY_UP_ARROW:
                     {
                     {
                         Font* font = getFont(_state);
                         Font* font = getFont(_state);
+                        GP_ASSERT(font);
                         unsigned int fontSize = getFontSize(_state);
                         unsigned int fontSize = getFontSize(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
 
 
                         _caretLocation.y -= fontSize;
                         _caretLocation.y -= fontSize;
-                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                        font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
                         break;
                         break;
@@ -175,12 +192,13 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     case Keyboard::KEY_DOWN_ARROW:
                     case Keyboard::KEY_DOWN_ARROW:
                     {
                     {
                         Font* font = getFont(_state);
                         Font* font = getFont(_state);
+                        GP_ASSERT(font);
                         unsigned int fontSize = getFontSize(_state);
                         unsigned int fontSize = getFontSize(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         Font::Justify textAlignment = getTextAlignment(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
                         bool rightToLeft = getTextRightToLeft(_state);
 
 
                         _caretLocation.y += fontSize;
                         _caretLocation.y += fontSize;
-                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                        font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
                         _dirty = true;
                         _dirty = true;
                         break;
                         break;
@@ -192,11 +210,12 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
             case Keyboard::KEY_CHAR:
             case Keyboard::KEY_CHAR:
             {
             {
                 Font* font = getFont(_state);
                 Font* font = getFont(_state);
+                GP_ASSERT(font);
                 unsigned int fontSize = getFontSize(_state);
                 unsigned int fontSize = getFontSize(_state);
                 Font::Justify textAlignment = getTextAlignment(_state);
                 Font::Justify textAlignment = getTextAlignment(_state);
                 bool rightToLeft = getTextRightToLeft(_state);
                 bool rightToLeft = getTextRightToLeft(_state);
 
 
-                unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                int textIndex = font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                     textAlignment, true, rightToLeft);
                     textAlignment, true, rightToLeft);
 
 
                 switch (key)
                 switch (key)
@@ -207,7 +226,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         {
                         {
                             --textIndex;
                             --textIndex;
                             _text.erase(textIndex, 1);
                             _text.erase(textIndex, 1);
-                            font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                            font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                                 textAlignment, true, rightToLeft);
                                 textAlignment, true, rightToLeft);
 
 
                             _dirty = true;
                             _dirty = true;
@@ -223,18 +242,18 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         _text.insert(textIndex, 1, (char)key);
                         _text.insert(textIndex, 1, (char)key);
 
 
                         // Get new location of caret.
                         // Get new location of caret.
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex + 1,
+                        font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
                             textAlignment, true, rightToLeft);
                             textAlignment, true, rightToLeft);
 
 
                         if (key == ' ')
                         if (key == ' ')
                         {
                         {
                             // If a space was entered, check that caret is still within bounds.
                             // If a space was entered, check that caret is still within bounds.
-                            if (_caretLocation.x >= _clip.x + _clip.width ||
-                                _caretLocation.y >= _clip.y + _clip.height)
+                            if (_caretLocation.x >= _textBounds.x + _textBounds.width ||
+                                _caretLocation.y >= _textBounds.y + _textBounds.height)
                             {
                             {
                                 // If not, undo the character insertion.
                                 // If not, undo the character insertion.
                                 _text.erase(textIndex, 1);
                                 _text.erase(textIndex, 1);
-                                font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                                font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                                     textAlignment, true, rightToLeft);
                                     textAlignment, true, rightToLeft);
 
 
                                 // No need to check again.
                                 // No need to check again.
@@ -244,13 +263,13 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
 
                         // Always check that the text still fits within the clip region.
                         // Always check that the text still fits within the clip region.
                         Rectangle textBounds;
                         Rectangle textBounds;
-                        font->measureText(_text.c_str(), _clip, fontSize, &textBounds, textAlignment, true, true);
-                        if (textBounds.x <= _clip.x || textBounds.y <= _clip.y ||
-                            textBounds.width >= _clip.width || textBounds.height >= _clip.height)
+                        font->measureText(_text.c_str(), _textBounds, fontSize, &textBounds, textAlignment, true, true);
+                        if (textBounds.x <= _textBounds.x || textBounds.y <= _textBounds.y ||
+                            textBounds.width >= _textBounds.width || textBounds.height >= _textBounds.height)
                         {
                         {
                             // If not, undo the character insertion.
                             // If not, undo the character insertion.
                             _text.erase(textIndex, 1);
                             _text.erase(textIndex, 1);
-                            font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                            font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                                 textAlignment, true, rightToLeft);
                                 textAlignment, true, rightToLeft);
 
 
                             // TextBox is not dirty.
                             // TextBox is not dirty.
@@ -272,21 +291,9 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
     _lastKeypress = key;
     _lastKeypress = key;
 }
 }
 
 
-void TextBox::update(const Rectangle& clip)
+void TextBox::update(const Rectangle& clip, const Vector2& offset)
 {
 {
-    Label::update(clip);
-
-    // Get index into string and cursor location from the last recorded touch location.
-    if (_state == FOCUS)
-    {
-        Font* font = getFont(_state);
-        unsigned int fontSize = getFontSize(_state);
-        Font::Justify textAlignment = getTextAlignment(_state);
-        bool rightToLeft = getTextRightToLeft(_state);
-
-        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
-            textAlignment, true, rightToLeft);
-    }
+    Label::update(clip, offset);
 
 
     _fontSize = getFontSize(_state);
     _fontSize = getFontSize(_state);
     _caretImage = getImage("textCaret", _state);
     _caretImage = getImage("textCaret", _state);
@@ -297,14 +304,16 @@ void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     if (_state == FOCUS)
     if (_state == FOCUS)
     {
     {
         // Draw the cursor at its current location.
         // Draw the cursor at its current location.
+        GP_ASSERT(_caretImage);
         const Rectangle& region = _caretImage->getRegion();
         const Rectangle& region = _caretImage->getRegion();
         if (!region.isEmpty())
         if (!region.isEmpty())
         {
         {
+            GP_ASSERT(spriteBatch);
             const Theme::UVs uvs = _caretImage->getUVs();
             const Theme::UVs uvs = _caretImage->getUVs();
             Vector4 color = _caretImage->getColor();
             Vector4 color = _caretImage->getColor();
             color.w *= _opacity;
             color.w *= _opacity;
 
 
-            spriteBatch->draw(_caretLocation.x - (region.width / 2.0f), _caretLocation.y, region.width, _fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+            spriteBatch->draw(_caretLocation.x - (region.width / 2.0f), _caretLocation.y, region.width, _fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
         }
         }
     }
     }
 
 
@@ -313,11 +322,23 @@ void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 
 
 void TextBox::setCaretLocation(int x, int y)
 void TextBox::setCaretLocation(int x, int y)
 {
 {
-    const Theme::Border& border = getBorder(_state);
-    Theme::Padding padding = getPadding();
+    // Get index into string and cursor location from the latest touch location.
+    _prevCaretLocation.set(_caretLocation);
+    _caretLocation.set(x + _absoluteBounds.x,
+                       y + _absoluteBounds.y);
 
 
-    _caretLocation.set(x - border.left - padding.left + _clip.x,
-                       y - border.top - padding.top + _clip.y);
+    Font* font = getFont(_state);
+    unsigned int fontSize = getFontSize(_state);
+    Font::Justify textAlignment = getTextAlignment(_state);
+    bool rightToLeft = getTextRightToLeft(_state);
+
+    int index = font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
+            textAlignment, true, rightToLeft);
+
+    if (index == -1)
+    {
+        _caretLocation.set(_prevCaretLocation);
+    }
 }
 }
 
 
 }
 }

+ 2 - 1
gameplay/src/TextBox.h

@@ -112,7 +112,7 @@ protected:
      *
      *
      * @param clip The clipping rectangle of this control's parent container.
      * @param clip The clipping rectangle of this control's parent container.
      */
      */
-    void update(const Rectangle& clip);
+    void update(const Rectangle& clip, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
@@ -126,6 +126,7 @@ protected:
      * The current position of the TextBox's caret.
      * The current position of the TextBox's caret.
      */
      */
     Vector2 _caretLocation;
     Vector2 _caretLocation;
+    Vector2 _prevCaretLocation;
 
 
     /**
     /**
      * The index into the TextBox's string that the caret is.
      * The index into the TextBox's string that the caret is.

+ 9 - 9
gameplay/src/Texture.cpp

@@ -97,7 +97,7 @@ Texture* Texture::create(const char* path, bool generateMipmaps)
         return texture;
         return texture;
     }
     }
 
 
-    LOG_ERROR_VARG("Failed to load texture: %s", path);
+    GP_ERROR("Failed to load texture: %s", path);
     return NULL;
     return NULL;
 }
 }
 
 
@@ -178,7 +178,7 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     FILE* file = FileSystem::openFile(path, "rb");
     FILE* file = FileSystem::openFile(path, "rb");
     if (file == NULL)
     if (file == NULL)
     {
     {
-        LOG_ERROR_VARG("Failed to load file: %s", path);
+        GP_ERROR("Failed to load file: %s", path);
         return NULL;
         return NULL;
     }
     }
 
 
@@ -186,10 +186,10 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     unsigned int size = sizeof(pvrtc_file_header);
     unsigned int size = sizeof(pvrtc_file_header);
     pvrtc_file_header header;
     pvrtc_file_header header;
     unsigned int read = (int)fread(&header, 1, size, file);
     unsigned int read = (int)fread(&header, 1, size, file);
-    assert(read == size);
+    GP_ASSERT(read == size);
     if (read != size)
     if (read != size)
     {
     {
-        LOG_ERROR_VARG("Read file header error for pvrtc file: %s (%d < %d)", path, (int)read, (int)size);
+        GP_ERROR("Read file header error for pvrtc file: %s (%d < %d)", path, (int)read, (int)size);
         fclose(file);
         fclose(file);
         return NULL;
         return NULL;
     }
     }
@@ -200,7 +200,7 @@ Texture* Texture::createCompressedPVRTC(const char* path)
         PVRTCIdentifier[2] != (char)((header.pvrtcTag >> 16) & 0xff) ||
         PVRTCIdentifier[2] != (char)((header.pvrtcTag >> 16) & 0xff) ||
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 0xff))
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 0xff))
      {
      {
-        LOG_ERROR_VARG("Invalid PVRTC compressed texture file: %s", path);
+        GP_ERROR("Invalid PVRTC compressed texture file: %s", path);
         fclose(file);
         fclose(file);
         return NULL;
         return NULL;
     }
     }
@@ -218,17 +218,17 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     }
     }
     else
     else
     {
     {
-        LOG_ERROR_VARG("Invalid PVRTC compressed texture format flags for file: %s", path);
+        GP_ERROR("Invalid PVRTC compressed texture format flags for file: %s", path);
         fclose(file);
         fclose(file);
         return NULL;
         return NULL;
     }
     }
 
 
     unsigned char* data = new unsigned char[header.dataSize];
     unsigned char* data = new unsigned char[header.dataSize];
     read = (int)fread(data, 1, header.dataSize, file);
     read = (int)fread(data, 1, header.dataSize, file);
-    assert(read == header.dataSize);
+    GP_ASSERT(read == header.dataSize);
     if (read != header.dataSize)
     if (read != header.dataSize)
     {
     {
-        LOG_ERROR_VARG("Read file data error for pvrtc file: %s (%d < %d)", path, (int)read, (int)header.dataSize);
+        GP_ERROR("Read file data error for pvrtc file: %s (%d < %d)", path, (int)read, (int)header.dataSize);
         SAFE_DELETE_ARRAY(data);
         SAFE_DELETE_ARRAY(data);
         fclose(file);
         fclose(file);
         return NULL;
         return NULL;
@@ -348,7 +348,7 @@ Texture::Sampler::~Sampler()
 
 
 Texture::Sampler* Texture::Sampler::create(Texture* texture)
 Texture::Sampler* Texture::Sampler::create(Texture* texture)
 {
 {
-    assert(texture != NULL);
+    GP_ASSERT(texture != NULL);
 
 
     texture->addRef();
     texture->addRef();
     return new Sampler(texture);
     return new Sampler(texture);

+ 655 - 610
gameplay/src/Theme.cpp

@@ -4,765 +4,810 @@
 
 
 namespace gameplay
 namespace gameplay
 {
 {
-    static std::vector<Theme*> __themeCache;
 
 
-    Theme::Theme()
+static std::vector<Theme*> __themeCache;
+
+Theme::Theme()
+{
+}
+
+Theme::Theme(const Theme& theme)
+{
+}
+
+Theme::~Theme()
+{
+    // Destroy all the cursors, styles and , fonts.
+    for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
     {
     {
+        Style* style = _styles[i];
+        SAFE_DELETE(style);
     }
     }
 
 
-    Theme::Theme(const Theme& theme)
+    for (unsigned int i = 0, count = _images.size(); i < count; ++i)
     {
     {
+        ThemeImage* image = _images[i];
+        SAFE_RELEASE(image);
     }
     }
 
 
-    Theme::~Theme()
+    for (unsigned int i = 0, count = _imageLists.size(); i < count; ++i)
     {
     {
-        // Destroy all the cursors, styles and , fonts.
-        for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
-        {
-            Style* style = _styles[i];
-            SAFE_DELETE(style);
-        }
+        ImageList* imageList = _imageLists[i];
+        SAFE_RELEASE(imageList);
+    }
 
 
-        for (unsigned int i = 0, count = _images.size(); i < count; ++i)
-        {
-            ThemeImage* image = _images[i];
-            SAFE_RELEASE(image);
-        }
+    for (unsigned int i = 0, count = _skins.size(); i < count; ++i)
+    {
+        Skin* skin = _skins[i];
+        SAFE_RELEASE(skin);
+    }
 
 
-        for (unsigned int i = 0, count = _imageLists.size(); i < count; ++i)
-        {
-            ImageList* imageList = _imageLists[i];
-            SAFE_RELEASE(imageList);
-        }
+    SAFE_DELETE(_spriteBatch);
+    SAFE_RELEASE(_texture);
 
 
-        for (unsigned int i = 0, count = _skins.size(); i < count; ++i)
-        {
-            Skin* skin = _skins[i];
-            SAFE_RELEASE(skin);
-        }
+    // Remove ourself from the theme cache.
+    std::vector<Theme*>::iterator itr = std::find(__themeCache.begin(), __themeCache.end(), this);
+    if (itr != __themeCache.end())
+    {
+        __themeCache.erase(itr);
+    }
+}
 
 
-        SAFE_DELETE(_spriteBatch);
-        SAFE_RELEASE(_texture);
+Theme* Theme::create(const char* url)
+{
+    GP_ASSERT(url);
 
 
-        // Remove ourself from the theme cache.
-        std::vector<Theme*>::iterator itr = std::find(__themeCache.begin(), __themeCache.end(), this);
-        if (itr != __themeCache.end())
+    // Search theme cache first.
+    for (unsigned int i = 0, count = __themeCache.size(); i < count; ++i)
+    {
+        Theme* t = __themeCache[i];
+        if (t->_url == url)
         {
         {
-            __themeCache.erase(itr);
+            // Found a match.
+            t->addRef();
+
+            return t;
         }
         }
     }
     }
 
 
-    Theme* Theme::create(const char* url)
+    // Load theme properties from file path.
+    Properties* properties = Properties::create(url);
+    GP_ASSERT(properties);
+    if (properties == NULL)
     {
     {
-        assert(url);
+        return NULL;
+    }
 
 
-        // Search theme cache first.
-        for (unsigned int i = 0, count = __themeCache.size(); i < count; ++i)
-        {
-            Theme* t = __themeCache[i];
-            if (t->_url == url)
-            {
-                // Found a match.
-                t->addRef();
+    // Check if the Properties is valid and has a valid namespace.
+    Properties* themeProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
+    GP_ASSERT(themeProperties);
+    if (!themeProperties || !(strcmp(themeProperties->getNamespace(), "theme") == 0))
+    {
+        SAFE_DELETE(properties);
+        return NULL;
+    }
 
 
-                return t;
-            }
-        }
+    // Create a new theme.
+    Theme* theme = new Theme();
+    theme->_url = url;
+        
+    // Parse the Properties object and set up the theme.
+    const char* textureFile = themeProperties->getString("texture");
+    theme->_texture = Texture::create(textureFile, false);
+    GP_ASSERT(theme->_texture);
+    theme->_spriteBatch = SpriteBatch::create(theme->_texture);
+    GP_ASSERT(theme->_spriteBatch);
+
+    float tw = 1.0f / theme->_texture->getWidth();
+    float th = 1.0f / theme->_texture->getHeight();
 
 
-        // Load theme properties from file path.
-        Properties* properties = Properties::create(url);
-        assert(properties);
-        if (properties == NULL)
+    Properties* space = themeProperties->getNextNamespace();
+    while (space != NULL)
+    {
+        // First load all cursors, checkboxes etc. that can be referred to by styles.
+        const char* spacename = space->getNamespace();
+            
+        if (strcmp(spacename, "image") == 0)
         {
         {
-            return NULL;
+            theme->_images.push_back(ThemeImage::create(tw, th, space, Vector4::one()));
         }
         }
-
-        // Check if the Properties is valid and has a valid namespace.
-        Properties* themeProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
-        assert(themeProperties);
-        if (!themeProperties || !(strcmp(themeProperties->getNamespace(), "theme") == 0))
+        else if (strcmp(spacename, "imageList") == 0)
         {
         {
-            SAFE_DELETE(properties);
-            return NULL;
+            theme->_imageLists.push_back(ImageList::create(tw, th, space));
         }
         }
-
-        // Create a new theme.
-        Theme* theme = new Theme();
-        theme->_url = url;
-        
-        // Parse the Properties object and set up the theme.
-        const char* textureFile = themeProperties->getString("texture");
-        theme->_texture = Texture::create(textureFile, false);
-        theme->_spriteBatch = SpriteBatch::create(theme->_texture);
-
-        float tw = 1.0f / theme->_texture->getWidth();
-        float th = 1.0f / theme->_texture->getHeight();
-
-        Properties* space = themeProperties->getNextNamespace();
-        while (space != NULL)
+        else if (strcmp(spacename, "skin") == 0)
         {
         {
-            // First load all cursors, checkboxes etc. that can be referred to by styles.
-            const char* spacename = space->getNamespace();
-            
-            if (strcmp(spacename, "image") == 0)
+            Theme::Border border;
+            Properties* innerSpace = space->getNextNamespace();
+            if (innerSpace)
             {
             {
-                theme->_images.push_back(ThemeImage::create(tw, th, space, Vector4::one()));
+                const char* innerSpacename = innerSpace->getNamespace();
+                if (strcmp(innerSpacename, "border") == 0)
+                {
+                    border.top = innerSpace->getFloat("top");
+                    border.bottom = innerSpace->getFloat("bottom");
+                    border.left = innerSpace->getFloat("left");
+                    border.right = innerSpace->getFloat("right");
+                }
             }
             }
-            else if (strcmp(spacename, "imageList") == 0)
+
+            Vector4 regionVector;
+            space->getVector4("region", &regionVector);
+            const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
+
+            Vector4 color(1, 1, 1, 1);
+            if (space->exists("color"))
             {
             {
-                theme->_imageLists.push_back(ImageList::create(tw, th, space));
+                space->getColor("color", &color);
             }
             }
-            else if (strcmp(spacename, "skin") == 0)
+
+            Skin* skin = Skin::create(space->getId(), tw, th, region, border, color);
+            GP_ASSERT(skin);
+            theme->_skins.push_back(skin);
+        }
+
+        space = themeProperties->getNextNamespace();
+    }
+
+    themeProperties->rewind();
+    space = themeProperties->getNextNamespace();
+    while (space != NULL)
+    {
+        const char* spacename = space->getNamespace();
+        if (strcmp(spacename, "style") == 0)
+        {
+            // Each style contains up to MAX_OVERLAYS overlays,
+            // as well as Border and Padding namespaces.
+            Theme::Margin margin;
+            Theme::Padding padding;
+            Theme::Style::Overlay* normal = NULL;
+            Theme::Style::Overlay* focus = NULL;
+            Theme::Style::Overlay* active = NULL;
+            Theme::Style::Overlay* disabled = NULL;
+
+            // Need to load OVERLAY_NORMAL first so that the other overlays can inherit from it.
+            Properties* innerSpace = space->getNextNamespace();
+            while (innerSpace != NULL)
             {
             {
-                Theme::Border border;
-                Properties* innerSpace = space->getNextNamespace();
-                if (innerSpace)
+                const char* innerSpacename = innerSpace->getNamespace();
+                if (strcmp(innerSpacename, "stateNormal") == 0)
                 {
                 {
-                    const char* innerSpacename = innerSpace->getNamespace();
-                    if (strcmp(innerSpacename, "border") == 0)
+                    Vector4 textColor(0, 0, 0, 1);
+                    if (innerSpace->exists("textColor"))
                     {
                     {
-                        border.top = innerSpace->getFloat("top");
-                        border.bottom = innerSpace->getFloat("bottom");
-                        border.left = innerSpace->getFloat("left");
-                        border.right = innerSpace->getFloat("right");
+                        innerSpace->getColor("textColor", &textColor);
                     }
                     }
-                }
 
 
-                Vector4 regionVector;
-                space->getVector4("region", &regionVector);
-                const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
+                    const char* fontPath = innerSpace->getString("font");
+                    Font* font = NULL;
+                    if (fontPath)
+                    {
+                        font = Font::create(fontPath);
+                    }
+                    unsigned int fontSize = innerSpace->getInt("fontSize");
+                    const char* textAlignmentString = innerSpace->getString("textAlignment");
+                    Font::Justify textAlignment = Font::ALIGN_TOP_LEFT;
+                    if (textAlignmentString)
+                    {
+                        textAlignment = Font::getJustify(textAlignmentString);
+                    }
+                    bool rightToLeft = innerSpace->getBool("rightToLeft");
 
 
-                Vector4 color(1, 1, 1, 1);
-                if (space->exists("color"))
-                {
-                    space->getColor("color", &color);
+                    float opacity = 1.0f;
+                    if (innerSpace->exists("opacity"))
+                    {
+                        opacity = innerSpace->getFloat("opacity");
+                    }
+
+                    ImageList* imageList = NULL;
+                    ThemeImage* cursor = NULL;
+                    Skin* skin = NULL;
+                    theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
+
+                    normal = Theme::Style::Overlay::create();
+                    GP_ASSERT(normal);
+                    normal->setSkin(skin);
+                    normal->setCursor(cursor);
+                    normal->setImageList(imageList);
+                    normal->setTextColor(textColor);
+                    normal->setFont(font);
+                    normal->setFontSize(fontSize);
+                    normal->setTextAlignment(textAlignment);
+                    normal->setTextRightToLeft(rightToLeft);
+                    normal->setOpacity(opacity);
+
+                    if (font)
+                    {
+                        theme->_fonts.insert(font);
+                        font->release();
+                    }
+
+                    // Done with this pass.
+                    break;
                 }
                 }
 
 
-                Skin* skin = Skin::create(space->getId(), tw, th, region, border, color);
-                theme->_skins.push_back(skin);
+                innerSpace = space->getNextNamespace();
             }
             }
 
 
-            space = themeProperties->getNextNamespace();
-        }
+            // At least the OVERLAY_NORMAL is required.
+            if (!normal)
+                GP_ERROR("All themes require the normal state overlay to be defined.");
 
 
-        themeProperties->rewind();
-        space = themeProperties->getNextNamespace();
-        while (space != NULL)
-        {
-            const char* spacename = space->getNamespace();
-            if (strcmp(spacename, "style") == 0)
+            space->rewind();
+            innerSpace = space->getNextNamespace();
+            while (innerSpace != NULL)
             {
             {
-                // Each style contains up to MAX_OVERLAYS overlays,
-                // as well as Border and Padding namespaces.
-                Theme::Margin margin;
-                Theme::Padding padding;
-                Theme::Style::Overlay* normal = NULL;
-                Theme::Style::Overlay* focus = NULL;
-                Theme::Style::Overlay* active = NULL;
-                Theme::Style::Overlay* disabled = NULL;
-
-                // Need to load OVERLAY_NORMAL first so that the other overlays can inherit from it.
-                Properties* innerSpace = space->getNextNamespace();
-                while (innerSpace != NULL)
+                const char* innerSpacename = innerSpace->getNamespace();
+                if (strcmp(innerSpacename, "margin") == 0)
+                {
+                    margin.top = innerSpace->getFloat("top");
+                    margin.bottom = innerSpace->getFloat("bottom");
+                    margin.left = innerSpace->getFloat("left");
+                    margin.right = innerSpace->getFloat("right");
+                }
+                else if (strcmp(innerSpacename, "padding") == 0)
+                {
+                    padding.top = innerSpace->getFloat("top");
+                    padding.bottom = innerSpace->getFloat("bottom");
+                    padding.left = innerSpace->getFloat("left");
+                    padding.right = innerSpace->getFloat("right");
+                }
+                else if (strcmp(innerSpacename, "stateNormal") != 0)
                 {
                 {
-                    const char* innerSpacename = innerSpace->getNamespace();
-                    if (strcmp(innerSpacename, "stateNormal") == 0)
+                    // Either OVERLAY_FOCUS or OVERLAY_ACTIVE.
+                    // If a property isn't specified, it inherits from OVERLAY_NORMAL.
+                    Vector4 textColor;
+                    if (!innerSpace->getColor("textColor", &textColor))
                     {
                     {
-                        Vector4 textColor(0, 0, 0, 1);
-                        if (innerSpace->exists("textColor"))
-                        {
-                            innerSpace->getColor("textColor", &textColor);
-                        }
-
-                        const char* fontPath = innerSpace->getString("font");
-                        Font* font = NULL;
-                        if (fontPath)
-                        {
-                            font = Font::create(fontPath);
-                        }
-                        unsigned int fontSize = innerSpace->getInt("fontSize");
-                        const char* textAlignmentString = innerSpace->getString("textAlignment");
-                        Font::Justify textAlignment = Font::ALIGN_TOP_LEFT;
-                        if (textAlignmentString)
-                        {
-                            textAlignment = Font::getJustify(textAlignmentString);
-                        }
-                        bool rightToLeft = innerSpace->getBool("rightToLeft");
-
-                        float opacity = 1.0f;
-                        if (innerSpace->exists("opacity"))
-                        {
-                            opacity = innerSpace->getFloat("opacity");
-                        }
-
-                        ImageList* imageList = NULL;
-                        ThemeImage* cursor = NULL;
-                        Skin* skin = NULL;
-                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
-
-                        normal = Theme::Style::Overlay::create();
-                        normal->setSkin(skin);
-                        normal->setCursor(cursor);
-                        normal->setImageList(imageList);
-                        normal->setTextColor(textColor);
-                        normal->setFont(font);
-                        normal->setFontSize(fontSize);
-                        normal->setTextAlignment(textAlignment);
-                        normal->setTextRightToLeft(rightToLeft);
-                        normal->setOpacity(opacity);
-
-                        theme->_fonts.insert(font);
+                        textColor.set(normal->getTextColor());
+                    }
 
 
-                        if (font) font->release();
+                    const char* fontPath = innerSpace->getString("font");
+                    Font* font = NULL;
+                    if (fontPath)
+                    {
+                        font = Font::create(fontPath);
+                    }
+                    if (!font)
+                    {
+                        font = normal->getFont();
+                    }
 
 
-                        // Done with this pass.
-                        break;
+                    unsigned int fontSize;
+                    if (innerSpace->exists("fontSize"))
+                    {
+                        fontSize = innerSpace->getInt("fontSize");
+                    }
+                    else
+                    {
+                        fontSize = normal->getFontSize();
                     }
                     }
 
 
-                    innerSpace = space->getNextNamespace();
-                }
+                    const char* textAlignmentString = innerSpace->getString("textAlignment");
+                    Font::Justify textAlignment;
+                    if (textAlignmentString)
+                    {
+                        textAlignment = Font::getJustify(textAlignmentString);
+                    }
+                    else
+                    {
+                        textAlignment = normal->getTextAlignment();
+                    }
 
 
-                // At least the OVERLAY_NORMAL is required.
-                assert(normal);
+                    bool rightToLeft;
+                    if (innerSpace->exists("rightToLeft"))
+                    {
+                        rightToLeft = innerSpace->getBool("rightToLeft");
+                    }
+                    else
+                    {
+                        rightToLeft = normal->getTextRightToLeft();
+                    }
 
 
-                space->rewind();
-                innerSpace = space->getNextNamespace();
-                while (innerSpace != NULL)
-                {
-                    const char* innerSpacename = innerSpace->getNamespace();
-                    if (strcmp(innerSpacename, "margin") == 0)
+                    float opacity;
+                    if (innerSpace->exists("opacity"))
+                    {
+                        opacity = innerSpace->getFloat("opacity");
+                    }
+                    else
                     {
                     {
-                        margin.top = innerSpace->getFloat("top");
-                        margin.bottom = innerSpace->getFloat("bottom");
-                        margin.left = innerSpace->getFloat("left");
-                        margin.right = innerSpace->getFloat("right");
+                        opacity = normal->getOpacity();
                     }
                     }
-                    else if (strcmp(innerSpacename, "padding") == 0)
+
+                    ImageList* imageList = NULL;
+                    ThemeImage* cursor = NULL;
+                    Skin* skin = NULL;
+                    theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
+
+                    if (!imageList)
                     {
                     {
-                        padding.top = innerSpace->getFloat("top");
-                        padding.bottom = innerSpace->getFloat("bottom");
-                        padding.left = innerSpace->getFloat("left");
-                        padding.right = innerSpace->getFloat("right");
+                        imageList = normal->getImageList();
                     }
                     }
-                    else if (strcmp(innerSpacename, "stateNormal") != 0)
+
+                    if (!cursor)
                     {
                     {
-                        // Either OVERLAY_FOCUS or OVERLAY_ACTIVE.
-                        // If a property isn't specified, it inherits from OVERLAY_NORMAL.
-                        Vector4 textColor;
-                        if (!innerSpace->getColor("textColor", &textColor))
-                        {
-                            textColor.set(normal->getTextColor());
-                        }
-
-                        const char* fontPath = innerSpace->getString("font");
-                        Font* font = NULL;
-                        if (fontPath)
-                        {
-                            font = Font::create(fontPath);
-                        }
-                        if (!font)
-                        {
-                            font = normal->getFont();
-                        }
-
-                        unsigned int fontSize;
-                        if (innerSpace->exists("fontSize"))
-                        {
-                            fontSize = innerSpace->getInt("fontSize");
-                        }
-                        else
-                        {
-                            fontSize = normal->getFontSize();
-                        }
-
-                        const char* textAlignmentString = innerSpace->getString("textAlignment");
-                        Font::Justify textAlignment;
-                        if (textAlignmentString)
-                        {
-                            textAlignment = Font::getJustify(textAlignmentString);
-                        }
-                        else
-                        {
-                            textAlignment = normal->getTextAlignment();
-                        }
-
-                        bool rightToLeft;
-                        if (innerSpace->exists("rightToLeft"))
-                        {
-                            rightToLeft = innerSpace->getBool("rightToLeft");
-                        }
-                        else
-                        {
-                            rightToLeft = normal->getTextRightToLeft();
-                        }
-
-                        float opacity;
-                        if (innerSpace->exists("opacity"))
-                        {
-                            opacity = innerSpace->getFloat("opacity");
-                        }
-                        else
-                        {
-                            opacity = normal->getOpacity();
-                        }
-
-                        ImageList* imageList = NULL;
-                        ThemeImage* cursor = NULL;
-                        Skin* skin = NULL;
-                        theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
-
-                        if (!imageList)
-                        {
-                            imageList = normal->getImageList();
-                        }
-
-                        if (!cursor)
-                        {
-                            cursor = normal->getCursor();
-                        }
+                        cursor = normal->getCursor();
+                    }
                         
                         
-                        if (!skin)
-                        {
-                            skin = normal->getSkin();
-                        }
-
-                        if (strcmp(innerSpacename, "stateFocus") == 0)
-                        {
-                            focus = Theme::Style::Overlay::create();
-                            focus->setSkin(skin);
-                            focus->setCursor(cursor);
-                            focus->setImageList(imageList);
-                            focus->setTextColor(textColor);
-                            focus->setFont(font);
-                            focus->setFontSize(fontSize);
-                            focus->setTextAlignment(textAlignment);
-                            focus->setTextRightToLeft(rightToLeft);
-                            focus->setOpacity(opacity);
+                    if (!skin)
+                    {
+                        skin = normal->getSkin();
+                    }
 
 
+                    if (strcmp(innerSpacename, "stateFocus") == 0)
+                    {
+                        focus = Theme::Style::Overlay::create();
+                        GP_ASSERT(focus);
+                        focus->setSkin(skin);
+                        focus->setCursor(cursor);
+                        focus->setImageList(imageList);
+                        focus->setTextColor(textColor);
+                        focus->setFont(font);
+                        focus->setFontSize(fontSize);
+                        focus->setTextAlignment(textAlignment);
+                        focus->setTextRightToLeft(rightToLeft);
+                        focus->setOpacity(opacity);
+
+                        if (font)
                             theme->_fonts.insert(font);
                             theme->_fonts.insert(font);
-                        }
-                        else if (strcmp(innerSpacename, "stateActive") == 0)
-                        {
-                            active = Theme::Style::Overlay::create();
-                            active->setSkin(skin);
-                            active->setCursor(cursor);
-                            active->setImageList(imageList);
-                            active->setTextColor(textColor);
-                            active->setFont(font);
-                            active->setFontSize(fontSize);
-                            active->setTextAlignment(textAlignment);
-                            active->setTextRightToLeft(rightToLeft);
-                            active->setOpacity(opacity);
-
+                    }
+                    else if (strcmp(innerSpacename, "stateActive") == 0)
+                    {
+                        active = Theme::Style::Overlay::create();
+                        GP_ASSERT(active);
+                        active->setSkin(skin);
+                        active->setCursor(cursor);
+                        active->setImageList(imageList);
+                        active->setTextColor(textColor);
+                        active->setFont(font);
+                        active->setFontSize(fontSize);
+                        active->setTextAlignment(textAlignment);
+                        active->setTextRightToLeft(rightToLeft);
+                        active->setOpacity(opacity);
+
+                        if (font)
                             theme->_fonts.insert(font);
                             theme->_fonts.insert(font);
-                        }
-                        else if (strcmp(innerSpacename, "stateDisabled") == 0)
-                        {
-                            disabled = Theme::Style::Overlay::create();
-                            disabled->setSkin(skin);
-                            disabled->setCursor(cursor);
-                            disabled->setImageList(imageList);
-                            disabled->setTextColor(textColor);
-                            disabled->setFont(font);
-                            disabled->setFontSize(fontSize);
-                            disabled->setTextAlignment(textAlignment);
-                            disabled->setTextRightToLeft(rightToLeft);
-                            disabled->setOpacity(opacity);
-
+                    }
+                    else if (strcmp(innerSpacename, "stateDisabled") == 0)
+                    {
+                        disabled = Theme::Style::Overlay::create();
+                        GP_ASSERT(disabled);
+                        disabled->setSkin(skin);
+                        disabled->setCursor(cursor);
+                        disabled->setImageList(imageList);
+                        disabled->setTextColor(textColor);
+                        disabled->setFont(font);
+                        disabled->setFontSize(fontSize);
+                        disabled->setTextAlignment(textAlignment);
+                        disabled->setTextRightToLeft(rightToLeft);
+                        disabled->setOpacity(opacity);
+
+                        if (font)
                             theme->_fonts.insert(font);
                             theme->_fonts.insert(font);
-                        }
                     }
                     }
-
-                    innerSpace = space->getNextNamespace();
-                }
-                
-                if (!focus)
-                {
-                    focus = normal;
-                    focus->addRef();
                 }
                 }
 
 
-                if (!active)
-                {
-                    active = normal;
-                    active->addRef();
-                }
+                innerSpace = space->getNextNamespace();
+            }
+                
+            if (!focus)
+            {
+                focus = normal;
+                focus->addRef();
+            }
 
 
-                if (!disabled)
-                {
-                    disabled = normal;
-                    disabled->addRef();
-                }
+            if (!active)
+            {
+                active = normal;
+                active->addRef();
+            }
 
 
-                Theme::Style* s = new Theme::Style(theme, space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
-                theme->_styles.push_back(s);
+            if (!disabled)
+            {
+                disabled = normal;
+                disabled->addRef();
             }
             }
 
 
-            space = themeProperties->getNextNamespace();
+            Theme::Style* s = new Theme::Style(theme, space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
+            GP_ASSERT(s);
+            theme->_styles.push_back(s);
         }
         }
 
 
-        // Add this theme to the cache.
-        __themeCache.push_back(theme);
+        space = themeProperties->getNextNamespace();
+    }
 
 
-        SAFE_DELETE(properties);
+    // Add this theme to the cache.
+    __themeCache.push_back(theme);
 
 
-        return theme;
-    }
+    SAFE_DELETE(properties);
 
 
-    Theme::Style* Theme::getStyle(const char* name) const
-    {
-        for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
-        {
-            if (strcmp(name, _styles[i]->getId()) == 0)
-            {
-                return _styles[i];
-            }
-        }
+    return theme;
+}
 
 
-        return NULL;
-    }
+Theme::Style* Theme::getStyle(const char* name) const
+{
+    GP_ASSERT(name);
 
 
-    void Theme::setProjectionMatrix(const Matrix& matrix)
+    for (unsigned int i = 0, count = _styles.size(); i < count; ++i)
     {
     {
-        _spriteBatch->setProjectionMatrix(matrix);
-
-        // Set the matrix on each Font used by the style.
-        std::set<Font*>::const_iterator it;
-        for (it = _fonts.begin(); it != _fonts.end(); ++it)
+        GP_ASSERT(_styles[i]);
+        if (strcmp(name, _styles[i]->getId()) == 0)
         {
         {
-            (*it)->getSpriteBatch()->setProjectionMatrix(matrix);
+            return _styles[i];
         }
         }
     }
     }
 
 
-    SpriteBatch* Theme::getSpriteBatch() const
-    {
-        return _spriteBatch;
-    }
+    return NULL;
+}
 
 
-    /**************
-     * Theme::UVs *
-     **************/
-    Theme::UVs::UVs()
-        : u1(0), v1(0), u2(0), v2(0)
-    {
-    }
+void Theme::setProjectionMatrix(const Matrix& matrix)
+{
+    GP_ASSERT(_spriteBatch);
+    _spriteBatch->setProjectionMatrix(matrix);
 
 
-    Theme::UVs::UVs(float u1, float v1, float u2, float v2)
-        : u1(u1), v1(v1), u2(u2), v2(v2)
+    // Set the matrix on each Font used by the style.
+    std::set<Font*>::const_iterator it;
+    for (it = _fonts.begin(); it != _fonts.end(); ++it)
     {
     {
+        Font* font = *it;
+        GP_ASSERT(font);
+        GP_ASSERT(font->getSpriteBatch());
+        font->getSpriteBatch()->setProjectionMatrix(matrix);
     }
     }
+}
 
 
-    const Theme::UVs& Theme::UVs::empty()
-    {
-        static UVs empty(0, 0, 0, 0);
-        return empty;
-    }
+SpriteBatch* Theme::getSpriteBatch() const
+{
+    return _spriteBatch;
+}
 
 
-    /**********************
-     * Theme::SideRegions *
-     **********************/
-    const Theme::SideRegions& Theme::SideRegions::empty()
-    {
-        static SideRegions empty;
-        return empty;
-    }
+/**************
+ * Theme::UVs *
+ **************/
+Theme::UVs::UVs()
+    : u1(0), v1(0), u2(0), v2(0)
+{
+}
 
 
-    /****************
-     * Theme::ThemeImage *
-     ****************/
-    Theme::ThemeImage::ThemeImage(float tw, float th, const Rectangle& region, const Vector4& color)
-        : _region(region), _color(color)
-    {
-        generateUVs(tw, th, region.x, region.y, region.width, region.height, &_uvs);
-    }
+Theme::UVs::UVs(float u1, float v1, float u2, float v2)
+    : u1(u1), v1(v1), u2(u2), v2(v2)
+{
+}
 
 
-    Theme::ThemeImage::~ThemeImage()
-    {
-    }
+const Theme::UVs& Theme::UVs::empty()
+{
+    static UVs empty(0, 0, 0, 0);
+    return empty;
+}
 
 
-    Theme::ThemeImage* Theme::ThemeImage::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
-    {
-        Vector4 regionVector;                
-        properties->getVector4("region", &regionVector);
-        const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
+/**********************
+ * Theme::SideRegions *
+ **********************/
+const Theme::SideRegions& Theme::SideRegions::empty()
+{
+    static SideRegions empty;
+    return empty;
+}
 
 
-        Vector4 color;
-        if (properties->exists("color"))
-        {
-            properties->getColor("color", &color);
-        }
-        else
-        {
-            color.set(defaultColor);
-        }
+/*********************
+ * Theme::ThemeImage *
+ *********************/
+Theme::ThemeImage::ThemeImage(float tw, float th, const Rectangle& region, const Vector4& color)
+    : _region(region), _color(color)
+{
+    generateUVs(tw, th, region.x, region.y, region.width, region.height, &_uvs);
+}
 
 
-        ThemeImage* image = new ThemeImage(tw, th, region, color);
-        const char* id = properties->getId();
-        if (id)
-        {
-            image->_id = id;
-        }
+Theme::ThemeImage::~ThemeImage()
+{
+}
 
 
-        return image;
-    }
+Theme::ThemeImage* Theme::ThemeImage::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
+{
+    GP_ASSERT(properties);
 
 
-    const char* Theme::ThemeImage::getId() const
-    {
-        return _id.c_str();
-    }
+    Vector4 regionVector;                
+    properties->getVector4("region", &regionVector);
+    const Rectangle region(regionVector.x, regionVector.y, regionVector.z, regionVector.w);
 
 
-    const Theme::UVs& Theme::ThemeImage::getUVs() const
+    Vector4 color;
+    if (properties->exists("color"))
     {
     {
-        return _uvs;
+        properties->getColor("color", &color);
     }
     }
-
-    const Rectangle& Theme::ThemeImage::getRegion() const
+    else
     {
     {
-        return _region;
+        color.set(defaultColor);
     }
     }
 
 
-    const Vector4& Theme::ThemeImage::getColor() const
+    ThemeImage* image = new ThemeImage(tw, th, region, color);
+    const char* id = properties->getId();
+    if (id)
     {
     {
-        return _color;
+        image->_id = id;
     }
     }
 
 
-    /********************
-     * Theme::ImageList *
-     ********************/
-    Theme::ImageList::ImageList(const Vector4& color) : _color(color)
-    {
-    }
+    return image;
+}
 
 
-    Theme::ImageList::ImageList(const ImageList& copy)
-    {
-        _id = copy._id;
-        _color = copy._color;
+const char* Theme::ThemeImage::getId() const
+{
+    return _id.c_str();
+}
 
 
-        std::vector<ThemeImage*>::const_iterator it;
-        for (it = copy._images.begin(); it != copy._images.end(); it++)
-        {
-            ThemeImage* image = *it;
-            _images.push_back(new ThemeImage(*image));
-        }
-    }
+const Theme::UVs& Theme::ThemeImage::getUVs() const
+{
+    return _uvs;
+}
+
+const Rectangle& Theme::ThemeImage::getRegion() const
+{
+    return _region;
+}
 
 
-    Theme::ImageList::~ImageList()
+const Vector4& Theme::ThemeImage::getColor() const
+{
+    return _color;
+}
+
+/********************
+ * Theme::ImageList *
+ ********************/
+Theme::ImageList::ImageList(const Vector4& color) : _color(color)
+{
+}
+
+Theme::ImageList::ImageList(const ImageList& copy)
+{
+    _id = copy._id;
+    _color = copy._color;
+
+    std::vector<ThemeImage*>::const_iterator it;
+    for (it = copy._images.begin(); it != copy._images.end(); it++)
     {
     {
-        std::vector<ThemeImage*>::const_iterator it;
-        for (it = _images.begin(); it != _images.end(); it++)
-        {
-            ThemeImage* image = *it;
-            SAFE_RELEASE(image);
-        }
+        ThemeImage* image = *it;
+        GP_ASSERT(image);
+        _images.push_back(new ThemeImage(*image));
     }
     }
+}
 
 
-    Theme::ImageList* Theme::ImageList::create(float tw, float th, Properties* properties)
+Theme::ImageList::~ImageList()
+{
+    std::vector<ThemeImage*>::const_iterator it;
+    for (it = _images.begin(); it != _images.end(); it++)
     {
     {
-        Vector4 color(1, 1, 1, 1);
-        if (properties->exists("color"))
-        {
-            properties->getColor("color", &color);
-        }
+        ThemeImage* image = *it;
+        SAFE_RELEASE(image);
+    }
+}
 
 
-        ImageList* imageList = new ImageList(color);
+Theme::ImageList* Theme::ImageList::create(float tw, float th, Properties* properties)
+{
+    GP_ASSERT(properties);
 
 
-        const char* id = properties->getId();
-        if (id)
-        {
-            imageList->_id = id;
-        }
+    Vector4 color(1, 1, 1, 1);
+    if (properties->exists("color"))
+    {
+        properties->getColor("color", &color);
+    }
 
 
-        Properties* space = properties->getNextNamespace();
-        while (space != NULL)
-        {
-            ThemeImage* image = ThemeImage::create(tw, th, space, color);
-            imageList->_images.push_back(image);
-            space = properties->getNextNamespace();
-        }
+    ImageList* imageList = new ImageList(color);
 
 
-        return imageList;
+    const char* id = properties->getId();
+    if (id)
+    {
+        imageList->_id = id;
     }
     }
 
 
-    const char* Theme::ImageList::getId() const
+    Properties* space = properties->getNextNamespace();
+    while (space != NULL)
     {
     {
-        return _id.c_str();
+        ThemeImage* image = ThemeImage::create(tw, th, space, color);
+        GP_ASSERT(image);
+        imageList->_images.push_back(image);
+        space = properties->getNextNamespace();
     }
     }
 
 
-    Theme::ThemeImage* Theme::ImageList::getImage(const char* imageId) const
-    {
-        std::vector<ThemeImage*>::const_iterator it;
-        for (it = _images.begin(); it != _images.end(); it++)
-        {
-            ThemeImage* image = *it;
-            if (strcmp(image->getId(), imageId) == 0)
-            {
-                return image;
-            }
-        }
+    return imageList;
+}
 
 
-        return NULL;
-    }
+const char* Theme::ImageList::getId() const
+{
+    return _id.c_str();
+}
 
 
-    /***************
-     * Theme::Skin *
-     ***************/
-    Theme::Skin* Theme::Skin::create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
-    {
-        Skin* skin = new Skin(tw, th, region, border, color);
+Theme::ThemeImage* Theme::ImageList::getImage(const char* imageId) const
+{
+    GP_ASSERT(imageId);
 
 
-        if (id)
+    std::vector<ThemeImage*>::const_iterator it;
+    for (it = _images.begin(); it != _images.end(); it++)
+    {
+        ThemeImage* image = *it;
+        GP_ASSERT(image);
+        GP_ASSERT(image->getId());
+        if (strcmp(image->getId(), imageId) == 0)
         {
         {
-            skin->_id = id;
+            return image;
         }
         }
-
-        return skin;
     }
     }
 
 
-    Theme::Skin::Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
-        : _border(border), _color(color), _region(region)
-    {
-        setRegion(region, tw, th);
-    }
+    return NULL;
+}
 
 
-    Theme::Skin::~Skin()
-    {
-    }
+/***************
+ * Theme::Skin *
+ ***************/
+Theme::Skin* Theme::Skin::create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+{
+    Skin* skin = new Skin(tw, th, region, border, color);
 
 
-    const char* Theme::Skin::getId() const
+    if (id)
     {
     {
-        return _id.c_str();
+        skin->_id = id;
     }
     }
 
 
-    const Theme::Border& Theme::Skin::getBorder() const
-    {
-        return _border;
-    }
+    return skin;
+}
 
 
-    const Rectangle& Theme::Skin::getRegion() const
-    {
-        return _region;
-    }
+Theme::Skin::Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color)
+    : _border(border), _color(color), _region(region)
+{
+    setRegion(region, tw, th);
+}
 
 
-    void Theme::Skin::setRegion(const Rectangle& region, float tw, float th)
-    {
-        // Can calculate all measurements in advance.
-        float leftEdge = region.x * tw;
-        float rightEdge = (region.x + region.width) * tw;
-        float leftBorder = (region.x + _border.left) * tw;
-        float rightBorder = (region.x + region.width - _border.right) * tw;
-
-        float topEdge = 1.0f - (region.y * th);
-        float bottomEdge = 1.0f - ((region.y + region.height) * th);
-        float topBorder = 1.0f - ((region.y + _border.top) * th);
-        float bottomBorder = 1.0f - ((region.y + region.height - _border.bottom) * th);
-
-        // There are 9 sets of UVs to set.
-        _uvs[TOP_LEFT].u1 = leftEdge;
-        _uvs[TOP_LEFT].v1 = topEdge;
-        _uvs[TOP_LEFT].u2 = leftBorder;
-        _uvs[TOP_LEFT].v2 = topBorder;
-
-        _uvs[TOP].u1 = leftBorder;
-        _uvs[TOP].v1 = topEdge;
-        _uvs[TOP].u2 = rightBorder;
-        _uvs[TOP].v2 = topBorder;
-
-        _uvs[TOP_RIGHT].u1 = rightBorder;
-        _uvs[TOP_RIGHT].v1 = topEdge;
-        _uvs[TOP_RIGHT].u2 = rightEdge;
-        _uvs[TOP_RIGHT].v2 = topBorder;
-
-        _uvs[LEFT].u1 = leftEdge;
-        _uvs[LEFT].v1 = topBorder;
-        _uvs[LEFT].u2 = leftBorder;
-        _uvs[LEFT].v2 = bottomBorder;
-
-        _uvs[CENTER].u1 = leftBorder;
-        _uvs[CENTER].v1 = topBorder;
-        _uvs[CENTER].u2 = rightBorder;
-        _uvs[CENTER].v2 = bottomBorder;
-
-        _uvs[RIGHT].u1 = rightBorder;
-        _uvs[RIGHT].v1 = topBorder;
-        _uvs[RIGHT].u2 = rightEdge;
-        _uvs[RIGHT].v2 = bottomBorder;
-
-        _uvs[BOTTOM_LEFT].u1 = leftEdge;
-        _uvs[BOTTOM_LEFT].v1 = bottomBorder;
-        _uvs[BOTTOM_LEFT].u2 = leftBorder;
-        _uvs[BOTTOM_LEFT].v2 = bottomEdge;
-
-        _uvs[BOTTOM].u1 = leftBorder;
-        _uvs[BOTTOM].v1 = bottomBorder;
-        _uvs[BOTTOM].u2 = rightBorder;
-        _uvs[BOTTOM].v2 = bottomEdge;
-
-        _uvs[BOTTOM_RIGHT].u1 = rightBorder;
-        _uvs[BOTTOM_RIGHT].v1 = bottomBorder;
-        _uvs[BOTTOM_RIGHT].u2 = rightEdge;
-        _uvs[BOTTOM_RIGHT].v2 = bottomEdge;
-    }
+Theme::Skin::~Skin()
+{
+}
 
 
-    const Theme::UVs& Theme::Skin::getUVs(SkinArea area) const
-    {
-        return _uvs[area];
-    }
+const char* Theme::Skin::getId() const
+{
+    return _id.c_str();
+}
 
 
-    const Vector4& Theme::Skin::getColor() const
-    {
-        return _color;
-    }
+const Theme::Border& Theme::Skin::getBorder() const
+{
+    return _border;
+}
+
+const Rectangle& Theme::Skin::getRegion() const
+{
+    return _region;
+}
+
+void Theme::Skin::setRegion(const Rectangle& region, float tw, float th)
+{
+    // Can calculate all measurements in advance.
+    float leftEdge = region.x * tw;
+    float rightEdge = (region.x + region.width) * tw;
+    float leftBorder = (region.x + _border.left) * tw;
+    float rightBorder = (region.x + region.width - _border.right) * tw;
+
+    float topEdge = 1.0f - (region.y * th);
+    float bottomEdge = 1.0f - ((region.y + region.height) * th);
+    float topBorder = 1.0f - ((region.y + _border.top) * th);
+    float bottomBorder = 1.0f - ((region.y + region.height - _border.bottom) * th);
+
+    // There are 9 sets of UVs to set.
+    _uvs[TOP_LEFT].u1 = leftEdge;
+    _uvs[TOP_LEFT].v1 = topEdge;
+    _uvs[TOP_LEFT].u2 = leftBorder;
+    _uvs[TOP_LEFT].v2 = topBorder;
+
+    _uvs[TOP].u1 = leftBorder;
+    _uvs[TOP].v1 = topEdge;
+    _uvs[TOP].u2 = rightBorder;
+    _uvs[TOP].v2 = topBorder;
+
+    _uvs[TOP_RIGHT].u1 = rightBorder;
+    _uvs[TOP_RIGHT].v1 = topEdge;
+    _uvs[TOP_RIGHT].u2 = rightEdge;
+    _uvs[TOP_RIGHT].v2 = topBorder;
+
+    _uvs[LEFT].u1 = leftEdge;
+    _uvs[LEFT].v1 = topBorder;
+    _uvs[LEFT].u2 = leftBorder;
+    _uvs[LEFT].v2 = bottomBorder;
+
+    _uvs[CENTER].u1 = leftBorder;
+    _uvs[CENTER].v1 = topBorder;
+    _uvs[CENTER].u2 = rightBorder;
+    _uvs[CENTER].v2 = bottomBorder;
+
+    _uvs[RIGHT].u1 = rightBorder;
+    _uvs[RIGHT].v1 = topBorder;
+    _uvs[RIGHT].u2 = rightEdge;
+    _uvs[RIGHT].v2 = bottomBorder;
+
+    _uvs[BOTTOM_LEFT].u1 = leftEdge;
+    _uvs[BOTTOM_LEFT].v1 = bottomBorder;
+    _uvs[BOTTOM_LEFT].u2 = leftBorder;
+    _uvs[BOTTOM_LEFT].v2 = bottomEdge;
+
+    _uvs[BOTTOM].u1 = leftBorder;
+    _uvs[BOTTOM].v1 = bottomBorder;
+    _uvs[BOTTOM].u2 = rightBorder;
+    _uvs[BOTTOM].v2 = bottomEdge;
+
+    _uvs[BOTTOM_RIGHT].u1 = rightBorder;
+    _uvs[BOTTOM_RIGHT].v1 = bottomBorder;
+    _uvs[BOTTOM_RIGHT].u2 = rightEdge;
+    _uvs[BOTTOM_RIGHT].v2 = bottomEdge;
+}
+
+const Theme::UVs& Theme::Skin::getUVs(SkinArea area) const
+{
+    return _uvs[area];
+}
+
+const Vector4& Theme::Skin::getColor() const
+{
+    return _color;
+}
     
     
-    /**
-     * Theme utility methods.
-     */
-    void Theme::generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs)
-    {
-        uvs->u1 = x * tw;
-        uvs->u2 = (x + width) * tw;
-        uvs->v1 = 1.0f - (y * th);
-        uvs->v2 = 1.0f - ((y + height) * th);
-    }
+/**
+ * Theme utility methods.
+ */
+void Theme::generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs)
+{
+    GP_ASSERT(uvs);
+    uvs->u1 = x * tw;
+    uvs->u2 = (x + width) * tw;
+    uvs->v1 = 1.0f - (y * th);
+    uvs->v2 = 1.0f - ((y + height) * th);
+}
+
+void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, ThemeImage** cursor, Skin** skin)
+{
+    GP_ASSERT(overlaySpace);
 
 
-    void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, ThemeImage** cursor, Skin** Skin)
+    const char* imageListString = overlaySpace->getString("imageList");
+    if (imageListString)
     {
     {
-        const char* imageListString = overlaySpace->getString("imageList");
-        if (imageListString)
+        for (unsigned int i = 0; i < _imageLists.size(); ++i)
         {
         {
-            for (unsigned int i = 0; i < _imageLists.size(); ++i)
+            GP_ASSERT(_imageLists[i]);
+            GP_ASSERT(_imageLists[i]->getId());
+            if (strcmp(_imageLists[i]->getId(), imageListString) == 0)
             {
             {
-                if (strcmp(_imageLists[i]->getId(), imageListString) == 0)
-                {
-                    *imageList = _imageLists[i];
-                    break;
-                }
+                GP_ASSERT(imageList);
+                *imageList = _imageLists[i];
+                break;
             }
             }
         }
         }
+    }
 
 
-        const char* cursorString = overlaySpace->getString("cursor");
-        if (cursorString)
+    const char* cursorString = overlaySpace->getString("cursor");
+    if (cursorString)
+    {
+        for (unsigned int i = 0; i < _images.size(); ++i)
         {
         {
-            for (unsigned int i = 0; i < _images.size(); ++i)
+            GP_ASSERT(_images[i]);
+            GP_ASSERT(_images[i]->getId());
+            if (strcmp(_images[i]->getId(), cursorString) == 0)
             {
             {
-                if (strcmp(_images[i]->getId(), cursorString) == 0)
-                {
-                    *cursor = _images[i];
-                    break;
-                }
+                GP_ASSERT(cursor);
+                *cursor = _images[i];
+                break;
             }
             }
         }
         }
+    }
 
 
-        const char* skinString = overlaySpace->getString("skin");
-        if (skinString)
+    const char* skinString = overlaySpace->getString("skin");
+    if (skinString)
+    {
+        for (unsigned int i = 0; i < _skins.size(); ++i)
         {
         {
-            for (unsigned int i = 0; i < _skins.size(); ++i)
+            GP_ASSERT(_skins[i]);
+            GP_ASSERT(_skins[i]->getId());
+            if (strcmp(_skins[i]->getId(), skinString) == 0)
             {
             {
-                if (strcmp(_skins[i]->getId(), skinString) == 0)
-                {
-                    *Skin = _skins[i];
-                    break;
-                }
+                GP_ASSERT(skin);
+                *skin = _skins[i];
+                break;
             }
             }
         }
         }
     }
     }
 }
 }
+
+}

+ 17 - 6
gameplay/src/ThemeStyle.cpp

@@ -27,6 +27,7 @@ Theme::Style::Style(const Style& copy)
 
 
     for (int i = 0; i < OVERLAY_MAX; i++)
     for (int i = 0; i < OVERLAY_MAX; i++)
     {
     {
+        GP_ASSERT(copy._overlays[i]);
         _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
         _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
     }
     }
 }
 }
@@ -41,7 +42,7 @@ Theme::Style::~Style()
     
     
 const char* Theme::Style::getId() const
 const char* Theme::Style::getId() const
 {
 {
-    return _id.data();
+    return _id.c_str();
 }
 }
 
 
 Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
 Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
@@ -177,7 +178,7 @@ const Vector4& Theme::Style::Overlay::getSkinColor() const
 
 
 void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
 void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
 {
 {
-    assert(_skin);
+    GP_ASSERT(_skin);
     _skin->setRegion(region, tw, th);
     _skin->setRegion(region, tw, th);
 }
 }
 
 
@@ -263,6 +264,8 @@ void Theme::Style::Overlay::setTextColor(const Vector4& color)
 
 
 const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
 const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
 {
 {
+    GP_ASSERT(_imageList);
+
     ThemeImage* image = _imageList->getImage(id);
     ThemeImage* image = _imageList->getImage(id);
     if (image)
     if (image)
     {
     {
@@ -276,14 +279,16 @@ const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
     
     
 void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
 void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
 {
 {
+    GP_ASSERT(_imageList);
     ThemeImage* image = _imageList->getImage(id);
     ThemeImage* image = _imageList->getImage(id);
-    assert(image);
+    GP_ASSERT(image);
     image->_region.set(region);
     image->_region.set(region);
     generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
     generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
 }
 }
 
 
 const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
 const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
 {
 {
+    GP_ASSERT(_imageList);
     ThemeImage* image = _imageList->getImage(id);
     ThemeImage* image = _imageList->getImage(id);
     if (image)
     if (image)
     {
     {
@@ -297,13 +302,15 @@ const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
 
 
 void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
 void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
 {
 {
+    GP_ASSERT(_imageList);
     ThemeImage* image = _imageList->getImage(id);
     ThemeImage* image = _imageList->getImage(id);
-    assert(image);
+    GP_ASSERT(image);
     image->_color.set(color);
     image->_color.set(color);
 }
 }
 
 
 const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
 const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
 {
 {
+    GP_ASSERT(_imageList);
     ThemeImage* image = _imageList->getImage(id);
     ThemeImage* image = _imageList->getImage(id);
     if (image)
     if (image)
     {
     {
@@ -329,7 +336,7 @@ const Rectangle& Theme::Style::Overlay::getCursorRegion() const
     
     
 void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
 void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
 {
 {
-    assert(_cursor);
+    GP_ASSERT(_cursor);
     _cursor->_region.set(region);
     _cursor->_region.set(region);
     generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
     generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
 }
 }
@@ -348,7 +355,7 @@ const Vector4& Theme::Style::Overlay::getCursorColor() const
 
 
 void Theme::Style::Overlay::setCursorColor(const Vector4& color)
 void Theme::Style::Overlay::setCursorColor(const Vector4& color)
 {
 {
-    assert(_cursor);
+    GP_ASSERT(_cursor);
     _cursor->_color.set(color);
     _cursor->_color.set(color);
 }
 }
 
 
@@ -435,6 +442,8 @@ unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int prope
 
 
 void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
 {
+    GP_ASSERT(value);
+
     switch(propertyId)
     switch(propertyId)
     {
     {
     case ANIMATE_OPACITY:
     case ANIMATE_OPACITY:
@@ -447,6 +456,8 @@ void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationV
 
 
 void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
 {
+    GP_ASSERT(value);
+
     switch(propertyId)
     switch(propertyId)
     {
     {
         case ANIMATE_OPACITY:
         case ANIMATE_OPACITY:

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно