Przeglądaj źródła

Merge pull request #77 from blackberry-gaming/next

Next
Sean Paul Taylor 13 lat temu
rodzic
commit
3be1816f0c
41 zmienionych plików z 877 dodań i 844 usunięć
  1. 1 1
      gameplay/android/jni/Android.mk
  2. 1 1
      gameplay/src/AbsoluteLayout.h
  3. 13 0
      gameplay/src/Animation.cpp
  4. 12 0
      gameplay/src/Animation.h
  5. 10 10
      gameplay/src/Bundle.cpp
  6. 15 13
      gameplay/src/Button.h
  7. 24 41
      gameplay/src/CheckBox.cpp
  8. 18 15
      gameplay/src/CheckBox.h
  9. 1 0
      gameplay/src/Container.cpp
  10. 29 27
      gameplay/src/Container.h
  11. 154 13
      gameplay/src/Control.cpp
  12. 17 1
      gameplay/src/Control.h
  13. 6 6
      gameplay/src/Font.cpp
  14. 27 30
      gameplay/src/Form.h
  15. 9 0
      gameplay/src/FrameBuffer.h
  16. 14 6
      gameplay/src/Label.cpp
  17. 19 13
      gameplay/src/Label.h
  18. 6 6
      gameplay/src/Matrix.cpp
  19. 3 0
      gameplay/src/MeshBatch.h
  20. 18 4
      gameplay/src/Node.cpp
  21. 23 0
      gameplay/src/Node.h
  22. 45 165
      gameplay/src/PhysicsCharacter.cpp
  23. 3 111
      gameplay/src/PhysicsCharacter.h
  24. 2 2
      gameplay/src/PhysicsCollisionShape.cpp
  25. 1 1
      gameplay/src/PhysicsGhostObject.cpp
  26. 2 2
      gameplay/src/PhysicsRigidBody.cpp
  27. 67 70
      gameplay/src/Properties.h
  28. 35 52
      gameplay/src/RadioButton.cpp
  29. 18 15
      gameplay/src/RadioButton.h
  30. 3 0
      gameplay/src/RenderState.h
  31. 4 0
      gameplay/src/RenderTarget.h
  32. 33 23
      gameplay/src/SceneLoader.cpp
  33. 3 2
      gameplay/src/SceneLoader.h
  34. 29 20
      gameplay/src/Slider.cpp
  35. 24 16
      gameplay/src/Slider.h
  36. 10 7
      gameplay/src/TextBox.cpp
  37. 17 13
      gameplay/src/TextBox.h
  38. 24 24
      gameplay/src/Theme.cpp
  39. 119 117
      gameplay/src/Theme.h
  40. 12 12
      gameplay/src/ThemeStyle.cpp
  41. 6 5
      gameplay/src/ThemeStyle.h

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
-LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp gameplay-main-qnx.cpp gameplay-main-win32.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp Package.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp PlatformQNX.cpp PlatformWin32.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
+LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 

+ 1 - 1
gameplay/src/AbsoluteLayout.h

@@ -29,7 +29,7 @@ protected:
     /**
      * Create an AbsoluteLayout.
      *
-     * @return an AbsoluteLayout object.
+     * @return An AbsoluteLayout object.
      */
     static AbsoluteLayout* create();
 

+ 13 - 0
gameplay/src/Animation.cpp

@@ -146,6 +146,19 @@ AnimationClip* Animation::getClip(const char* id)
     }
 }
 
+AnimationClip* Animation::getClip(unsigned int index) const
+{
+    if (_clips)
+        return _clips->at(index);
+
+    return NULL;
+}
+
+unsigned int Animation::getClipCount() const
+{
+    return _clips ? _clips->size() : 0;
+}
+
 void Animation::play(const char* clipId)
 {
     // If id is NULL, play the default clip.

+ 12 - 0
gameplay/src/Animation.h

@@ -66,6 +66,18 @@ public:
      * @return The AnimationClip with the specified ID; NULL if an AnimationClip with the given ID is not found.
      */
     AnimationClip* getClip(const char* clipId = NULL);
+
+    /**
+     * Returns the AnimationClip at the given index.
+     *
+     * @param index Index of the clip to return.
+     */
+    AnimationClip* getClip(unsigned int index) const;
+
+    /**
+     * Returns the number of animation clips in this animation.
+     */
+    unsigned int getClipCount() const;
     
     /**
      * Plays the AnimationClip with the specified name. 

+ 10 - 10
gameplay/src/Bundle.cpp

@@ -199,12 +199,12 @@ Bundle* Bundle::create(const char* path)
     }
 
     // Keep file open for faster reading later
-    Bundle* pkg = new Bundle(path);
-    pkg->_referenceCount = refCount;
-    pkg->_references = refs;
-    pkg->_file = fp;
+    Bundle* bundle = new Bundle(path);
+    bundle->_referenceCount = refCount;
+    bundle->_references = refs;
+    bundle->_file = fp;
 
-    return pkg;
+    return bundle;
 }
 
 Bundle::Reference* Bundle::find(const char* id) const
@@ -1163,19 +1163,19 @@ Bundle::MeshData* Bundle::readMeshData(const char* url)
     std::string id = urlstring.substr(pos + 1);
 
     // Load bundle
-    Bundle* pkg = Bundle::create(file.c_str());
-    if (pkg == NULL)
+    Bundle* bundle = Bundle::create(file.c_str());
+    if (bundle == NULL)
         return NULL;
 
     // Seek to mesh with specified ID in bundle
-    Reference* ref = pkg->seekTo(id.c_str(), BUNDLE_TYPE_MESH);
+    Reference* ref = bundle->seekTo(id.c_str(), BUNDLE_TYPE_MESH);
     if (ref == NULL)
         return NULL;
 
     // Read mesh data from current file position
-    MeshData* meshData = pkg->readMeshData();
+    MeshData* meshData = bundle->readMeshData();
 
-    SAFE_RELEASE(pkg);
+    SAFE_RELEASE(bundle);
 
     return meshData;
 }

+ 15 - 13
gameplay/src/Button.h

@@ -13,19 +13,21 @@ namespace gameplay
  * Defines a button UI control. This is essentially a label that can have a callback method set on it.
  *
  * The following properties are available for buttons:
- *
- * button <buttonID>
- * {
- *      style       = <styleID>
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *      text        = <string>
- * }
+
+ @verbatim
+    button <buttonID>
+    {
+         style       = <styleID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+    }
+ @endverbatim
  */
 class Button : public Label
 {

+ 24 - 41
gameplay/src/CheckBox.cpp

@@ -101,7 +101,7 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
 
 void CheckBox::update(const Rectangle& clip)
 {
-    Control::update(clip);
+    Label::update(clip);
 
     Vector2 size;
     if (_imageSize.isZero())
@@ -125,60 +125,43 @@ void CheckBox::update(const Rectangle& clip)
 
     _textBounds.x += iconWidth + 5;
     _textBounds.width -= iconWidth + 5;
+
+    if (_checked)
+    {
+        _image = getImage("checked", _state);
+    }
+    else
+    {
+        _image = getImage("unchecked", _state);
+    }
 }
 
 void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // Left, v-center.
     // TODO: Set an alignment for icons.
-    const Theme::Border border = getBorder(_state);
+    const Theme::Border& border = getBorder(_state);
     const Theme::Padding padding = getPadding();
-    float opacity = getOpacity(_state);
+    
+    const Rectangle& region = _image->getRegion();
+    const Theme::UVs& uvs = _image->getUVs();
+    Vector4 color = _image->getColor();
+    color.w *= _opacity;
 
-    if (_checked)
+    Vector2 size;
+    if (_imageSize.isZero())
     {
-        const Rectangle& selectedRegion = getImageRegion("checked", _state);
-        const Theme::UVs& selected = getImageUVs("checked", _state);
-        Vector4 selectedColor = getImageColor("checked", _state);
-        selectedColor.w *= opacity;
-
-        Vector2 size;
-        if (_imageSize.isZero())
-        {
-            size.set(selectedRegion.width, selectedRegion.height);
-        }
-        else
-        {
-            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);
-
-        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
+        size.set(region.width, region.height);
     }
     else
     {
-        const Rectangle& unselectedRegion = getImageRegion("unchecked", _state);
-        const Theme::UVs& unselected = getImageUVs("unchecked", _state);
-        Vector4 unselectedColor = getImageColor("unchecked", _state);
-        unselectedColor.w *= opacity;
-
-        Vector2 size;
-        if (_imageSize.isZero())
-        {
-            size.set(unselectedRegion.width, unselectedRegion.height);
-        }
-        else
-        {
-            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(clip.x + _bounds.x + border.left + padding.left,
+        clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
-        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
-    }
+    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _clip);
 }
 
 }

+ 18 - 15
gameplay/src/CheckBox.h

@@ -13,21 +13,23 @@ namespace gameplay
  * Defines a checkbox UI control.  This is a button that toggles between two icons when clicked.
  *
  * The following properties are available for checkboxes:
- *
- * checkBox <checkBoxID>
- * {
- *      style       = <styleID>
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *      text        = <string>
- *      checked     = <bool>
- *      iconSize    = <width, height>   // The size to draw the checkbox icon, if different from its size in the texture.
- * }
+
+ @verbatim
+    checkBox <checkBoxID>
+    {
+         style       = <styleID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+         checked     = <bool>
+         iconSize    = <width, height>   // The size to draw the checkbox icon, if different from its size in the texture.
+    }
+ @endverbatim
  */
 class CheckBox : public Button
 {
@@ -125,6 +127,7 @@ protected:
 
     bool _checked;      // Whether this checkbox is currently checked.
     Vector2 _imageSize;  // The size to draw the checkbox icon, if different from its size in the texture.
+    Theme::ThemeImage* _image;
 
 private:
 

+ 1 - 0
gameplay/src/Container.cpp

@@ -76,6 +76,7 @@ namespace gameplay
             {
                  controlStyle = theme->getStyle(controlStyleName);
             }
+            assert(controlStyle);
 
             std::string controlName(controlSpace->getNamespace());
             std::transform(controlName.begin(), controlName.end(), controlName.begin(), (int(*)(int))toupper);

+ 29 - 27
gameplay/src/Container.h

@@ -11,33 +11,35 @@ namespace gameplay
  * A container is a UI control that can contain other controls.
  *
  * The following properties are available for containers:
- *
- * container <containerID>
- * {
- *      // Container properties.
- *      layout   = <Layout::Type>        // A value from the Layout::Type enum.  E.g.: LAYOUT_VERTICAL
- *      style    = <styleID>           // A style from the form's theme.
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>    // Position of the container on-screen, measured in pixels.
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>   // Size of the container, measured in pixels.
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *   
- *      // All the nested controls within this container.
- *      container 
- *      { 
- *          ...
- *      }
- * 
- *      label { }
- *      textBox { }
- *      button { }
- *      checkBox { }
- *      radioButton { }
- *      slider { }
- * }
+
+ @verbatim
+    container <containerID>
+    {
+         // Container properties.
+         layout   = <Layout::Type>        // A value from the Layout::Type enum.  E.g.: LAYOUT_VERTICAL
+         style    = <styleID>           // A style from the form's theme.
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>    // Position of the container on-screen, measured in pixels.
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>   // Size of the container, measured in pixels.
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+  
+         // All the nested controls within this container.
+         container 
+         { 
+             ...
+         }
+
+         label { }
+         textBox { }
+         button { }
+         checkBox { }
+         radioButton { }
+         slider { }
+    }
+ @endverbatim
  */
 class Container : public Control
 {

+ 154 - 13
gameplay/src/Control.cpp

@@ -36,6 +36,7 @@ namespace gameplay
     {
         _style = style;
 
+        // Properties not defined by the style.
         _alignment = getAlignment(properties->getString("alignment"));
         _autoWidth = properties->getBool("autoWidth");
         _autoHeight = properties->getBool("autoHeight");
@@ -68,6 +69,35 @@ namespace gameplay
         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)
+        {
+            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();
+        }
     }
 
     const char* Control::getID() const
@@ -651,32 +681,34 @@ namespace gameplay
             y = clip.y;
 
         _clip.set(x, y, width, height);
+
+        _skin = getSkin(_state);
+        _opacity = getOpacity(_state);
     }
 
     void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
-        if (_bounds.width <= 0 || _bounds.height <= 0)
+        if (!_skin || _bounds.width <= 0 || _bounds.height <= 0)
             return;
 
         Vector2 pos(clip.x + _bounds.x, clip.y + _bounds.y);
 
         // Get the border and background images for this control's current state.
-        //Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
-        const Theme::UVs& topLeft = getSkinUVs(Theme::Skin::TOP_LEFT, _state);
-        const Theme::UVs& top = getSkinUVs(Theme::Skin::TOP, _state);
-        const Theme::UVs& topRight = getSkinUVs(Theme::Skin::TOP_RIGHT, _state);
-        const Theme::UVs& left = getSkinUVs(Theme::Skin::LEFT, _state);
-        const Theme::UVs& center = getSkinUVs(Theme::Skin::CENTER, _state);
-        const Theme::UVs& right = getSkinUVs(Theme::Skin::RIGHT, _state);
-        const Theme::UVs& bottomLeft = getSkinUVs(Theme::Skin::BOTTOM_LEFT, _state);
-        const Theme::UVs& bottom = getSkinUVs(Theme::Skin::BOTTOM, _state);
-        const Theme::UVs& bottomRight = getSkinUVs(Theme::Skin::BOTTOM_RIGHT, _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 = getSkinColor(_state);
-        skinColor.w *= getOpacity(_state);
+        Vector4 skinColor = _skin->getColor();
+        skinColor.w *= _opacity;
 
         float midWidth = _bounds.width - border.left - border.right;
         float midHeight = _bounds.height - border.top - border.bottom;
@@ -760,6 +792,11 @@ namespace gameplay
         return NORMAL;
     }
 
+    Theme::ThemeImage* Control::getImage(const char* id, State state)
+    {
+        return getOverlay(state)->getImageList()->getImage(id);
+    }
+
     // Implementation of AnimationHandler
     unsigned int Control::getAnimationPropertyComponentCount(int propertyId) const
     {
@@ -963,6 +1000,110 @@ namespace gameplay
         _styleOverridden = true;
     }
 
+    void Control::overrideThemedProperties(Properties* properties, unsigned char states)
+    {
+        Theme::ImageList* imageList = NULL;
+        Theme::ThemeImage* cursor = NULL;
+        Theme::Skin* skin = NULL;
+        _style->_theme->lookUpSprites(properties, &imageList, &cursor, &skin);
+
+        if (imageList)
+        {
+            setImageList(imageList, states);
+        }
+
+        if (cursor)
+        {
+            setCursor(cursor, states);
+        }
+
+        if (skin)
+        {
+            setSkin(skin, states);
+        }
+
+        if (properties->exists("font"))
+        {
+            Font* font = Font::create(properties->getString("font"));
+            setFont(font, states);
+            font->release();
+        }
+
+        if (properties->exists("fontSize"))
+        {
+            setFontSize(properties->getInt("fontSize"), states);
+        }
+
+        if (properties->exists("textColor"))
+        {
+            Vector4 textColor(0, 0, 0, 1);
+            properties->getColor("textColor", &textColor);
+            setTextColor(textColor, states);
+        }
+
+        if (properties->exists("textAlignment"))
+        {
+            setTextAlignment(Font::getJustify(properties->getString("textAlignment")), states);
+        }
+
+        if (properties->exists("rightToLeft"))
+        {
+            setTextRightToLeft(properties->getBool("rightToLeft"), states);
+        }
+
+        if (properties->exists("opacity"))
+        {
+            setOpacity(properties->getFloat("opacity"), states);
+        }
+    }
+
+    void Control::setImageList(Theme::ImageList* imageList, 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]->setImageList(imageList);
+        }
+
+        _dirty = true;
+    }
+
+    void Control::setCursor(Theme::ThemeImage* cursor, 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]->setCursor(cursor);
+        }
+
+        _dirty = true;
+    }
+
+    void Control::setSkin(Theme::Skin* skin, 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]->setSkin(skin);
+        }
+
+        _dirty = true;
+    }
+
+    Theme::Skin* Control::getSkin(State state)
+    {
+        return getOverlay(state)->getSkin();
+    }
+
     Control::Alignment Control::getAlignment(const char* alignment)
     {
         if (!alignment)

+ 17 - 1
gameplay/src/Control.h

@@ -23,6 +23,7 @@ class Control : public Ref, public AnimationTarget
     friend class Layout;
     friend class AbsoluteLayout;
     friend class VerticalLayout;
+    friend class FlowLayout;
 
 public:
 
@@ -776,6 +777,8 @@ protected:
      */
     static State getState(const char* state);
 
+    Theme::ThemeImage* getImage(const char* id, State state);
+
     /**
      * Notify this control's listeners of a specific event.
      *
@@ -799,6 +802,8 @@ protected:
     Theme::Style* _style;
     std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
 
+    float _opacity;         // Current opacity.
+
 private:
 
     static const char ANIMATION_POSITION_X_BIT = 0x01;
@@ -827,7 +832,17 @@ private:
     Theme::Style::Overlay* getOverlay(Control::State state) const;
 
     void overrideStyle();
-    
+
+    void overrideThemedProperties(Properties* properties, unsigned char states);
+
+    void setImageList(Theme::ImageList* imageList, unsigned char states = STATE_ALL);
+
+    void setCursor(Theme::ThemeImage* cursor, unsigned char states = STATE_ALL);
+
+    void setSkin(Theme::Skin* skin, unsigned char states = STATE_ALL);
+
+    Theme::Skin* getSkin(State state);
+
     void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
     
     /**
@@ -839,6 +854,7 @@ private:
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
     
     bool _styleOverridden;
+    Theme::Skin* _skin;
 };
 
 }

+ 6 - 6
gameplay/src/Font.cpp

@@ -79,8 +79,8 @@ Font* Font::create(const char* path, const char* id)
     }
 
     // Load the bundle.
-    Bundle* pkg = Bundle::create(path);
-    if (pkg == NULL)
+    Bundle* bundle = Bundle::create(path);
+    if (bundle == NULL)
     {
         return NULL;
     }
@@ -91,18 +91,18 @@ Font* Font::create(const char* path, const char* id)
     {
         // Get the ID of the first/only object in the bundle (assume it's a Font).
         const char* id;
-        if (pkg->getObjectCount() != 1 || (id = pkg->getObjectID(0)) == NULL)
+        if (bundle->getObjectCount() != 1 || (id = bundle->getObjectID(0)) == NULL)
         {
             return NULL;
         }
 
         // Load the font using the ID of the first object in the bundle.
-        font = pkg->loadFont(pkg->getObjectID(0));
+        font = bundle->loadFont(bundle->getObjectID(0));
     }
     else
     {
         // Load the font with the given ID.
-        font = pkg->loadFont(id);
+        font = bundle->loadFont(id);
     }
 
     if (font)
@@ -111,7 +111,7 @@ Font* Font::create(const char* path, const char* id)
         __fontCache.push_back(font);
     }
 
-    SAFE_RELEASE(pkg);
+    SAFE_RELEASE(bundle);
 
     return font;
 }

+ 27 - 30
gameplay/src/Form.h

@@ -15,7 +15,33 @@ namespace gameplay
 class Theme;
 
 /**
- * Top-level container of UI controls.
+ * Top-level container of UI controls.  The following properties are available for forms:
+
+ @verbatim
+    form <formID>
+    {
+        // Form properties.
+        theme       = <Path to .theme File> // See Theme.h.
+        layout      = <Layout::Type>        // A value from the Layout::Type enum.  e.g.: LAYOUT_VERTICAL
+        style       = <styleID>             // A style from the referenced theme.
+        position   = <x, y>                // Position of the form on-screen, measured in pixels.
+        alignment  = <Control::Alignment>  // Note: 'position' will be ignored.
+        autoWidth  = <bool>                // Will result in a form the width of the display.
+        autoHeight = <bool>                // Will result in a form the height of the display.
+        size       = <width, height>       // Size of the form, measured in pixels.
+        width      = <width>               // Can be used in place of 'size', e.g. with 'autoHeight = true'
+        height     = <height>              // Can be used in place of 'size', e.g. with 'autoWidth = true'
+      
+        // All the nested controls within this form.
+        container { }
+        label { }
+        textBox { }
+        button { }
+        checkBox { }
+        radioButton { }
+        slider { }
+    }
+ @endverbatim
  */
 class Form : public Container
 {
@@ -25,35 +51,6 @@ public:
 
     /**
      * Create from properties file.
-     * The top-most namespace in the file must be named 'form'.  The following properties are available for forms:
-     *
-     * form <formID>
-     * {
-     *      // Form properties.
-     *      theme    = <Path to .theme File>    // See Theme.h.
-     *      layout   = <Layout::Type>           // A value from the Layout::Type enum.  e.g.: LAYOUT_VERTICAL
-     *      style    = <styleID>                // A style from the referenced theme.
-     *      position = <x, y>                   // Position of the form on-screen, measured in pixels.
-     *      size     = <width, height>          // Size of the form, measured in pixels.
-     *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
-     *      autoWidth   = <bool>                // Will result in a form the width of the display.
-     *      autoHeight  = <bool>                // Will result in a form the height of the display.
-     *      size        = <width, height>
-     *      width       = <width>               // Can be used in place of 'size', e.g. with 'autoHeight = true'
-     *      height      = <height>              // Can be used in place of 'size', e.g. with 'autoWidth = true'
-     *   
-     *      // All the nested controls within this form.
-     *      container 
-     *      {
-     *          ...
-     *      }
-     *      label { }
-     *      textBox { }
-     *      button { }
-     *      checkBox { }
-     *      radioButton { }
-     *      slider { }
-     * }
      *
      * @param path Path to the properties file to create a new form from.
      */

+ 9 - 0
gameplay/src/FrameBuffer.h

@@ -8,6 +8,15 @@
 namespace gameplay
 {
 
+/**
+ * Defines a video output off all graphics buffer containing a complete frame of data.
+ * This consists of a RenderTarget and DepthStencilTarget holding the color, depth and
+ * stencil data in the rendering frame. 
+ * 
+ * to change the default Game framebuffer call Game::setFrameBuffer(myFrameBuffer);
+ * To restore back to the default call Game::setFrameBuffer(NULL).
+ * This is useful for rendering shadows and other post-processing effects.
+ */
 class FrameBuffer : public Ref
 {
 public:

+ 14 - 6
gameplay/src/Label.cpp

@@ -3,7 +3,7 @@
 
 namespace gameplay
 {
-    Label::Label() : _text("")
+    Label::Label() : _text(""), _font(NULL)
     {
     }
 
@@ -64,17 +64,25 @@ namespace gameplay
         return _text.c_str();
     }
 
+    void Label::update(const Rectangle& clip)
+    {
+        Control::update(clip);
+
+        _font = getFont(_state);
+        _textColor = getTextColor(_state);
+        _textColor.w *= getOpacity(_state);
+    }
+
     void Label::drawText(const Rectangle& clip)
     {
         if (_text.size() <= 0)
             return;
 
-        Font* font = getFont(_state);
-        Vector4 textColor = getTextColor(_state);
-        textColor.w *= getOpacity(_state);
-
         // Draw the text.
-        font->drawText(_text.c_str(), _textBounds, textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_clip);
+        if (_font)
+        {
+            _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_clip);
+        }
 
         _dirty = false;
     }

+ 19 - 13
gameplay/src/Label.h

@@ -11,19 +11,21 @@ namespace gameplay
  * A label is the most basic type of control, capable only of rendering text within its border.
  *
  * The following properties are available for labels:
- *
- * label <labelID>
- * {
- *      style       = <styleID>
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *      text        = <string>
- * }
+
+ @verbatim
+    label <labelID>
+    {
+         style       = <styleID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+    }
+ @endverbatim
  */
 class Label : public Control
 {
@@ -84,6 +86,8 @@ protected:
      */
     virtual void initialize(Theme::Style* style, Properties* properties);
 
+    void update(const Rectangle& clip);
+
     /**
      * Draw this label's text.
      *
@@ -92,6 +96,8 @@ protected:
     void drawText(const Rectangle& clip);
 
     std::string _text;      // The text displayed by this label.
+    Font* _font;
+    Vector4 _textColor;
 
 private:
 

+ 6 - 6
gameplay/src/Matrix.cpp

@@ -554,18 +554,18 @@ void Matrix::getLeftVector(Vector3* dst) const
 {
     assert(dst);
 
-    dst->x = -m[0];
-    dst->y = -m[1];
-    dst->z = -m[2];
+    dst->x = m[0];
+    dst->y = m[1];
+    dst->z = m[2];
 }
 
 void Matrix::getRightVector(Vector3* dst) const
 {
     assert(dst);
 
-    dst->x = m[0];
-    dst->y = m[1];
-    dst->z = m[2];
+    dst->x = -m[0];
+    dst->y = -m[1];
+    dst->z = -m[2];
 }
 
 void Matrix::getForwardVector(Vector3* dst) const

+ 3 - 0
gameplay/src/MeshBatch.h

@@ -7,6 +7,9 @@
 namespace gameplay
 {
 
+/**
+ * Defines a class for rendering multiple mesh into a single draw call on the graphics device.
+ */
 class MeshBatch
 {
 public:

+ 18 - 4
gameplay/src/Node.cpp

@@ -15,6 +15,7 @@
 // Node property flags
 #define NODE_FLAG_VISIBLE 1
 #define NODE_FLAG_TRANSPARENT 2
+#define NODE_FLAG_DYNAMIC 4
 
 namespace gameplay
 {
@@ -235,6 +236,19 @@ void Node::setTransparent(bool transparent)
         _nodeFlags &= ~NODE_FLAG_TRANSPARENT;
 }
 
+bool Node::isDynamic() const
+{
+    return ((_nodeFlags & NODE_FLAG_DYNAMIC) == NODE_FLAG_DYNAMIC);
+}
+
+void Node::setDynamic(bool dynamic)
+{
+    if (dynamic)
+        _nodeFlags |= NODE_FLAG_DYNAMIC;
+    else
+        _nodeFlags &= ~NODE_FLAG_DYNAMIC;
+}
+
 void* Node::getUserPointer() const
 {
     return (_userData ? _userData->pointer : NULL);
@@ -1004,8 +1018,8 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
     // Check if the properties is valid.
     if (!properties || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghost") == 0 || 
-        strcmp(properties->getNamespace(), "rigidbody") == 0))
+        strcmp(properties->getNamespace(), "ghostObject") == 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\', \'ghost\', or \'rigidbody\'.");
         return NULL;
@@ -1015,11 +1029,11 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
     {
         _collisionObject = PhysicsCharacter::create(this, properties);
     }
-    else if (strcmp(properties->getNamespace(), "ghost") == 0)
+    else if (strcmp(properties->getNamespace(), "ghostObject") == 0)
     {
         _collisionObject = PhysicsGhostObject::create(this, properties);
     }
-    else if (strcmp(properties->getNamespace(), "rigidbody") == 0)
+    else if (strcmp(properties->getNamespace(), "rigidBody") == 0)
     {
         _collisionObject = PhysicsRigidBody::create(this, properties);
     }

+ 23 - 0
gameplay/src/Node.h

@@ -148,6 +148,26 @@ public:
      */
     void setTransparent(bool transparent);
 
+    /**
+     * Returns whether this node is dynamic.
+     *
+     * The dynamic propery can be used to flag nodes as being non-static.
+     * This can be useful for modifying behavior or rendering/material
+     * logic at runtime for static vs dynamic (moving) objects. An
+     * example would be determing whether to use static or  dyanmic
+     * lighting materials for node models during loading.
+     *
+     * @return Whether this node is dynamic (false by default).
+     */
+    bool isDynamic() const;
+
+    /**
+     * Sets whether this node is dynamic.
+     *
+     * @param dynamic Whether the node is dynamic.
+     */
+    void setDynamic(bool dynamic);
+
     /**
      * Returns the user pointer for this node.
      *
@@ -613,6 +633,9 @@ private:
 
 protected:
 
+    /**
+     * Defines a pointer and cleanup callback to custom user data that can be store in a Node.
+     */
     struct UserData
     {
         UserData() : pointer(NULL), cleanupCallback(NULL) {}

+ 45 - 165
gameplay/src/PhysicsCharacter.cpp

@@ -59,9 +59,9 @@ protected:
 
 PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Definition& shape, float mass)
     : PhysicsGhostObject(node, shape), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
-    _fallVelocity(0, 0, 0), _currentVelocity(0,0,0), _normalizedVelocity(0,0,0),
-    _colliding(false), _collisionNormal(0,0,0), _currentPosition(0,0,0),
-    _stepHeight(0.1f), _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true), _mass(mass)
+    _verticalVelocity(0, 0, 0), _currentVelocity(0,0,0), _normalizedVelocity(0,0,0),
+    _colliding(false), _collisionNormal(0,0,0), _currentPosition(0,0,0), _stepHeight(0.1f),
+    _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true), _mass(mass)
 {
     setMaxSlopeAngle(45.0f);
 
@@ -99,6 +99,8 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
     // Load the character's parameters.
     properties->rewind();
     float mass = 1.0f;
+    float maxStepHeight = 0.1f;
+    float maxSlopeAngle = 0.0f;
     const char* name = NULL;
     while ((name = properties->getNextProperty()) != NULL)
     {
@@ -106,10 +108,20 @@ PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
         {
             mass = properties->getFloat();
         }
+        else if (strcmp(name, "maxStepHeight") == 0)
+        {
+            maxStepHeight = properties->getFloat();
+        }
+        else if (strcmp(name, "maxSlopeAngle") == 0)
+        {
+            maxSlopeAngle = properties->getFloat();
+        }
     }
 
     // Create the physics character.
     PhysicsCharacter* character = new PhysicsCharacter(node, *shape, mass);
+    character->setMaxStepHeight(maxStepHeight);
+    character->setMaxSlopeAngle(maxSlopeAngle);
     SAFE_DELETE(shape);
 
     return character;
@@ -156,120 +168,6 @@ void PhysicsCharacter::setMaxSlopeAngle(float angle)
     _cosSlopeAngle = std::cos(MATH_DEG_TO_RAD(angle));
 }
 
-void PhysicsCharacter::addAnimation(const char* name, AnimationClip* clip, float moveSpeed)
-{
-    CharacterAnimation a;
-    a.name = name;
-    a.clip = clip;
-    a.moveSpeed = moveSpeed;
-    a.layer = 0;
-    a.playing = false;
-    a.animationFlags = ANIMATION_STOP;
-    a.prev = NULL;
-    _animations[name] = a;
-}
-
-AnimationClip* PhysicsCharacter::getAnimation(const char* name)
-{
-    if (name)
-    {
-        // Lookup the specified animation
-        std::map<const char*, CharacterAnimation>::iterator aitr = _animations.find(name);
-        if (aitr != _animations.end())
-        {
-            return aitr->second.clip;
-        }
-    }
-    return NULL;
-}
-
-void PhysicsCharacter::play(const char* name, AnimationFlags flags, float speed, unsigned int blendDuration, unsigned int layer)
-{
-    CharacterAnimation* animation = NULL;
-    if (name)
-    {
-        // Lookup the specified animation
-        std::map<const char*, CharacterAnimation>::iterator aitr = _animations.find(name);
-        if (aitr == _animations.end())
-            return; // invalid animation name
-
-        animation = &(aitr->second);
-
-        // Set animation flags
-        animation->clip->setRepeatCount(flags & ANIMATION_REPEAT ? AnimationClip::REPEAT_INDEFINITE : 1);
-        animation->clip->setSpeed(speed);
-        animation->animationFlags = flags;
-        animation->layer = layer;
-        animation->blendDuration = blendDuration;
-        animation->prev = NULL;
-
-        // If the animation is already marked playing, do nothing more
-        if (animation->playing)
-            return;
-    }
-
-    play(animation, layer);
-}
-
-void PhysicsCharacter::play(CharacterAnimation* animation, unsigned int layer)
-{
-    // Is there already an animation playing on this layer?
-    std::map<unsigned int, CharacterAnimation*>::iterator litr = _layers.find(layer);
-    CharacterAnimation* prevAnimation = (litr == _layers.end() ? NULL : litr->second);
-    if (prevAnimation && prevAnimation->playing)
-    {
-        // An animation is already playing on this layer
-        if (animation)
-        {
-            if (animation->animationFlags == ANIMATION_RESUME)
-                animation->prev = prevAnimation;
-
-            if (animation->blendDuration > 0L)
-            {
-                // Crossfade from current animation into the new one
-                prevAnimation->clip->crossFade(animation->clip, animation->blendDuration);
-            }
-            else
-            {
-                // Stop the previous animation (no blending)
-                prevAnimation->clip->stop();
-
-                // Play the new animation
-                animation->clip->play();
-            }
-        }
-        else
-        {
-            // No new animaton specified - stop current animation on this layer
-            prevAnimation->clip->stop();
-        }
-
-        prevAnimation->playing = false;
-    }
-    else if (animation)
-    {
-        // No animations currently playing - just play the new one
-        animation->clip->play();
-    }
-
-    // Update animaton and layers
-    if (animation)
-    {
-        animation->playing = true;
-
-        // Update layer to point to the new animation
-        if (litr != _layers.end())
-            litr->second = animation;
-        else
-            _layers[layer] = animation;
-    }
-    else if (litr != _layers.end())
-    {
-        // Remove layer sine we stopped the animation previously on it
-        _layers.erase(litr);
-    }
-}
-
 void PhysicsCharacter::setVelocity(const Vector3& velocity)
 {
     _moveVelocity.setValue(velocity.x, velocity.y, velocity.z);
@@ -307,7 +205,20 @@ void PhysicsCharacter::setRightVelocity(float velocity)
 
 void PhysicsCharacter::jump(float height)
 {
-    // TODO
+    // TODO: Add support for different jump modes (i.e. double jump, changing direction in air, holding down jump button for extra height, etc)
+    if (!_verticalVelocity.isZero())
+        return;
+
+    // v = sqrt(v0^2 + 2 a s)
+    //  v0 == initial velocity (zero for jumping)
+    //  a == acceleration (inverse gravity)
+    //  s == linear displacement (height)
+    Vector3 jumpVelocity = -Game::getInstance()->getPhysicsController()->getGravity() * height * 2.0f;
+    jumpVelocity.set(
+        jumpVelocity.x == 0 ? 0 : std::sqrt(jumpVelocity.x),
+        jumpVelocity.y == 0 ? 0 : std::sqrt(jumpVelocity.y),
+        jumpVelocity.z == 0 ? 0 : std::sqrt(jumpVelocity.z));
+    _verticalVelocity += BV(jumpVelocity);
 }
 
 void PhysicsCharacter::updateCurrentVelocity()
@@ -403,58 +314,26 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
 
 void PhysicsCharacter::stepUp(btCollisionWorld* collisionWorld, btScalar time)
 {
-    // Note: btKinematicCharacterController implements this by always just setting
-    // target position to currentPosition.y + stepHeight, and then checking for collisions.
-    // Don't let the character move up if it hits the ceiling (or something above).
-    // Do this WITHOUT using time in the calculation - this way you are always guarnateed
-    // to step over a step that is stepHeight high.
-    // 
-    // Note that stepDown() will be called right after this, so the character will move back
-    // down to collide with the ground so that he smoothly steps up stairs.
-    _currentPosition += btVector3(0, _stepHeight, 0);
-}
+    btVector3 targetPosition(_currentPosition);
 
-void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, float time)
-{
-    // Process currently playing movements+animations and determine final move location
-    float animationMoveSpeed = 0.0f;
-    unsigned int animationCount = 0;
-    for (std::map<unsigned int, CharacterAnimation*>::iterator itr = _layers.begin(); itr != _layers.end(); ++itr)
+    if (_verticalVelocity.isZero())
     {
-        CharacterAnimation* animation = itr->second;
-
-        // If the animation is not playing, ignore it
-        if (!animation->playing)
-            continue;
-
-        AnimationClip* clip = animation->clip;
-
-        // Did the clip finish playing (but we still have it marked playing)?
-        if (!clip->isPlaying())
-        {
-            // If the animaton was flaged the ANIMATION_RESUME bit, start the previously playing animation
-            if ((animation->animationFlags == ANIMATION_RESUME) && animation->prev)
-            {
-                play(animation->prev, animation->prev->layer);
-            }
-
-            animation->playing = false;
+        // Simply increase our poisiton by step height to enable us
+        // to smoothly move over steps.
+        targetPosition += btVector3(0, _stepHeight, 0);
+    }
 
-            continue;
-        }
+    // TODO: Convex sweep test to ensure we didn't hit anything during the step up.
 
-        animationMoveSpeed += animation->moveSpeed;
-        ++animationCount;
-    }
+    _currentPosition = targetPosition;
+}
 
+void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, float time)
+{
     updateCurrentVelocity();
 
     // Calculate final velocity
     btVector3 velocity(_currentVelocity);
-    if (animationCount > 0)
-    {
-        velocity *= animationMoveSpeed;
-    }
     velocity *= time; // since velocity is in meters per second
 
     if (velocity.isZero())
@@ -542,11 +421,12 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 
 void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
 {
-    // Contribute basic gravity to fall velocity.
+    // Contribute gravity to vertical velocity.
     btVector3 gravity = Game::getInstance()->getPhysicsController()->_world->getGravity();
-    _fallVelocity += (gravity * time);
+    _verticalVelocity += (gravity * time);
 
-    btVector3 targetPosition = _currentPosition + (_fallVelocity * time);
+    // Compute new position from vertical velocity.
+    btVector3 targetPosition = _currentPosition + (_verticalVelocity * time);
     targetPosition -= btVector3(0, _stepHeight, 0);
 
     // Perform a convex sweep test between current and target position
@@ -569,7 +449,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
         _currentPosition.setInterpolate3(_currentPosition, targetPosition, callback.m_closestHitFraction);
 
         // Zero out fall velocity when we hit an object
-        _fallVelocity.setZero();
+        _verticalVelocity.setZero();
     }
     else
     {

+ 3 - 111
gameplay/src/PhysicsCharacter.h

@@ -18,10 +18,6 @@ namespace gameplay
  * PhysicsCharacter class. This results in a more responsive and typical game
  * character than would be possible if trying to move a character by applying
  * physical simulation with forces.
- *
- * This class can also be used to control animations for a character. Animation
- * clips can be setup for typical character animations, such as walk, run, jump,
- * etc; and the controller will handle blending between these animations as needed.
  */
 class PhysicsCharacter : public PhysicsGhostObject, public btActionInterface
 {
@@ -29,27 +25,6 @@ class PhysicsCharacter : public PhysicsGhostObject, public btActionInterface
 
 public:
 
-    /**
-     * Flags for controlling how a character animation is played back.
-     */
-    enum AnimationFlags
-    {
-        /**
-         * Plays an animation once and then stops.
-         */
-        ANIMATION_STOP,
-
-        /**
-         * Play an animation once and then resumes the previous playing animation.
-         */
-        ANIMATION_RESUME,
-
-        /**
-         * Plays an animation and repeats it indefinitely.
-         */
-         ANIMATION_REPEAT
-    };
-
     /**
      * @see PhysicsCollisionObject#getType
      */
@@ -110,74 +85,13 @@ public:
      */
     void setMaxSlopeAngle(float angle);
 
-    /**
-     * Configures a new animation for this character.
-     *
-     * This method registers an animation for the character, with an associated movement speed.
-     * The moveSpeed specifies how fast the character moves while the animation is playing.
-     * The final velocity of the character is the product of the current move velocity and
-     * the currently playing animation(s) moveSpeed.
-     *
-     * @param name Name of the animation.
-     * @param animationClip Animation clip associated with the new character animation.
-     * @param moveSpeed Base movement speed (meters per second) associated with the animation.
-     */
-    void addAnimation(const char* name, AnimationClip* animationClip, float moveSpeed);
-
-    /**
-     * Returns the animation with the specified name.
-     *
-     * @return The specified animation clip.
-     */
-    AnimationClip* getAnimation(const char* name);
-
-    /**
-     * Plays the specified animation.
-     *
-     * There are some limiations and considerations that should be ponited out when
-     * playing animations:
-     * <li>You should avoid playing multiple animations concurrently that have the same target.
-     * For example, two animations targetting the character's joints should not be played 
-     * concurrently, but it is fine to play one animation that targets the joints and another
-     * that targets the character's Node.
-     * <li>When playing an animation that targets the transform of the character's Node
-     * (such as a motion path animation), the character's velocity vector should be set to
-     * Vector3::zero() so that the PhysicsCharacter stops applying motion directly
-     * and instead relies on the motion animation to control the character.
-     *
-     * The optional animation layer can be used to group animations on separate layers.
-     * Each animation layer can have at most one active animation. Playing multiple
-     * animations concurrently can be achieved by putting the different animations
-     * on separate layers. For example, a motion path animation that targets the
-     * character's Node can be put on one layer, while a running animation that targets
-     * a character's Joints can be put on a separate layer. This allows a character's
-     * movement to be animated at the same time as the run animation is playing.
-     *
-     * @param name Animation name, or NULL to stop all character animations on the given layer.
-     * @param flags Animation flags from the AnimationFlags enumeration.
-     * @param animationSpeed Optional animation speed (default is 1.0).
-     * @param blendDuration Optional number of milliseconds to crossfade between the
-     *      currently playing animation on the given layer and the new animation.
-     * @param layer Optional animation layer.
-     */
-    void play(const char* name, AnimationFlags flags, float animationSpeed = 1.0f, unsigned int blendDuration = 0, unsigned int layer = 0);
-
     /**
      * Sets the velocity of the character.
      *
      * Calling this function sets the velocity (speed and direction) for the character.
      * The velocity is maintained until this method is called again. The final velocity
-     * of the character is determined by product of the current velocity vector(s)
-     * and the current character animation's move speed. Therefore, specifying a
-     * normalized (unit-length) velocity vector results in the character speed being
-     * controled entirely by the current animation's velocity; whereas the speed of
-     * the character can be augmented by modifying the magnitude of the velocity vector.
-
-     * Note that a zero velocity vector and/or a zero animation move speed will
-     * result in no character movement (the character will be stationary). A zero
-     * velocity vector should be used when playing an animation that targets the
-     * character's transform directly (such as a motion path animation), since these
-     * animations will overwrite any transformations on the character's node.
+     * of the character is determined by product of the current velocity, right and
+     * forward vectors.
      *
      * @param velocity Movement velocity.
      */
@@ -219,9 +133,6 @@ public:
      * The forward velocity is defined by the character's current orientation
      * (it is the forward vector from the character's current world transform).
      *
-     * The specified velocity acts as a multiplier on the currently playing animation's
-     * velocity (or, if there is no animation playing, it directly impacts velocity).
-     *
      * Note that a negative velocity (i.e. -1.0f) will move the character backwards.
      *
      * @param velocity Optional velocity modifier.
@@ -234,9 +145,6 @@ public:
      * The right velocity is defined by the character's current orientation
      * (it is the right vector from the character's current world transform).
      *
-     * The specified velocity acts as a multiplier on the currently playing animation's
-     * velocity (or, if there is no animation playing, it directly impacts velocity).
-     *
      * Note that a negative velocity (i.e. -1.0f) will move the character left.
      *
      * @param velocity Optional velocity modifier.
@@ -269,18 +177,6 @@ protected:
 
 private:
 
-    struct CharacterAnimation
-    {
-        const char* name;
-        AnimationClip* clip;
-        float moveSpeed;
-        unsigned int layer;
-        bool playing;
-        AnimationFlags animationFlags;
-        unsigned int blendDuration;
-        CharacterAnimation* prev;
-    };
-
     /**
      * Creates a new PhysicsCharacter.
      *
@@ -311,8 +207,6 @@ private:
 
     void updateCurrentVelocity();
 
-    void play(CharacterAnimation* animation, unsigned int layer);
-
     void stepUp(btCollisionWorld* collisionWorld, btScalar time);
 
     void stepDown(btCollisionWorld* collisionWorld, btScalar time);
@@ -326,14 +220,12 @@ private:
     btVector3 _moveVelocity;
     float _forwardVelocity;
     float _rightVelocity;
-    btVector3 _fallVelocity;
+    btVector3 _verticalVelocity;
     btVector3 _currentVelocity;
     btVector3 _normalizedVelocity;
     bool _colliding;
     btVector3 _collisionNormal;
     btVector3 _currentPosition;
-    std::map<const char*, CharacterAnimation> _animations;
-    std::map<unsigned int, CharacterAnimation*> _layers;
     btManifoldArray _manifoldArray;
     float _stepHeight;
     float _slopeAngle;

+ 2 - 2
gameplay/src/PhysicsCollisionShape.cpp

@@ -121,8 +121,8 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     assert(properties);
     if (!properties || 
         !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghost") == 0 || 
-        strcmp(properties->getNamespace(), "rigidbody") == 0))
+        strcmp(properties->getNamespace(), "ghostObject") == 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\', \'ghost\', or \'rigidbody\'.");
         return NULL;

+ 1 - 1
gameplay/src/PhysicsGhostObject.cpp

@@ -43,7 +43,7 @@ PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* propertie
 {
     // Check if the properties is valid and has a valid namespace.
     assert(properties);
-    if (!properties || !(strcmp(properties->getNamespace(), "ghost") == 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\'.");
         return NULL;

+ 2 - 2
gameplay/src/PhysicsRigidBody.cpp

@@ -145,9 +145,9 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
     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\'.");
+        WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to \'rigidBody\'.");
         return NULL;
     }
 

+ 67 - 70
gameplay/src/Properties.h

@@ -22,48 +22,45 @@ namespace gameplay
  * Here's an example of a simple
  * file that uses all the available features of the markup language:
  
-\verbatim
---- File Start: example.properties ---
+ @verbatim
+    // This is a comment.
  
-// This is a comment.
+    // This property is in the default namespace:
+    integerProperty = 5
  
-// This property is in the default namespace:
-integerProperty = 5
- 
-// This line defines a namespace of type "mynamespace" without an ID:
-mynamespace
-{
-    // This namespace can be retrieved by searching for its ID, "spriteTexture":
-    texture spriteTexture 
+    // This line defines a namespace of type "mynamespace" without an ID:
+    mynamespace
     {
-        fileName = sprite.png
-        width = 64
-        height = 64
-    }
+        // This namespace can be retrieved by searching for its ID, "spriteTexture":
+        texture spriteTexture 
+        {
+            fileName = sprite.png
+            width = 64
+            height = 64
+        }
  
-    // This property is in the "space" namespace:
-    booleanProperty = true
+        // This property is in the "space" namespace:
+        booleanProperty = true
  
-    // It's legal to have a name without a value if you leave out the '=' character:
-    foo
+        // It's legal to have a name without a value if you leave out the '=' character:
+        foo
  
-    // In fact, the '=' character is optional if you'd rather write:
-    bar 23
+        // In fact, the '=' character is optional if you'd rather write:
+        bar 23
  
-    // But don't write this or you'll get an error:
-    // illegalProperty =
+        // But don't write this or you'll get an error:
+        // illegalProperty =
  
-    // Or this:
-    // = 15
+        // Or this:
+        // = 15
  
-    // Properties objects let you retrieve values as various types.
-    floatProperty = 3.333
-    stringProperty = This is a string.
-    vector3Property = 1.0, 5.0, 3.55
-    colorProperty = 1.0, 0.4, 0.0, 1.0
-}
---- File End ---
-\endverbatim
+        // Properties objects let you retrieve values as various types.
+        floatProperty = 3.333
+        stringProperty = This is a string.
+        vector3Property = 1.0, 5.0, 3.55
+        colorProperty = 1.0, 0.4, 0.0, 1.0
+    }
+ @endverbatim
  
  * Retrieving information out of a file like this could be done in two ways.  If the
  * available namespaces and name/value pairs are known in advance they can be queried by ID or name.
@@ -72,54 +69,54 @@ mynamespace
  * A namespace is stored and retrieved as a Properties object.
  * Reading the spriteTexture properties out of the file above in this way could be done with the following code:
  
-\verbatim
-// Create the top-level Properties object.
-Properties* properties = Properties::create("example.properties");
-// Retrieve the "spriteTexture" namespace.
-Properties* spriteTexture = properties->getNamespace("spriteTexture");
+ @verbatim
+    // Create the top-level Properties object.
+    Properties* properties = Properties::create("example.properties");
+    // Retrieve the "spriteTexture" namespace.
+    Properties* spriteTexture = properties->getNamespace("spriteTexture");
  
-// Get the values of known texture properties out of the namespace.
-const char* fileName = spriteTexture->getString("fileName");
-int width = spriteTexture->getInt("width");
-int height = spriteTexture->getInt("height");
+    // Get the values of known texture properties out of the namespace.
+    const char* fileName = spriteTexture->getString("fileName");
+    int width = spriteTexture->getInt("width");
+    int height = spriteTexture->getInt("height");
  
-// Deleting the top-level Properties object will clean up all nested namespaces.
-SAFE_DELETE(properties);
-\endverbatim
+    // Deleting the top-level Properties object will clean up all nested namespaces.
+    SAFE_DELETE(properties);
+ @endverbatim
 
  * On the other hand, if the structure of the file is not known in advance its 
  * namespaces and name/value pairs can be retrieved one by one using the getNextNamespace()
  * and getNextProperty() methods.  The following method prints the contents of any properties file
  * to the console:
  
-\verbatim
-void printProperties(Properties* properties)
-{
-    // Print the name and ID of the current namespace.
-    const char* spacename = properties->getNamespace();
-    const char* id = properties->getId();
-    WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
- 
-    // Print all properties in this namespace.
-    const char* name = properties->getNextProperty();
-    const char* value = NULL;
-    while (name != NULL)
+ @verbatim
+    void printProperties(Properties* properties)
     {
-        value = properties->getString(name);
-        WARN_VARG("%s = %s", name, value);
-        name = properties->getNextProperty();
-    }
-    WARN("}\n");
+        // Print the name and ID of the current namespace.
+        const char* spacename = properties->getNamespace();
+        const char* id = properties->getId();
+        WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
  
-    // Print the properties of every namespace within this one.
-    Properties* space = properties->getNextNamespace();
-    while (space != NULL)
-    {
-        printProperties(space);
-        space = properties->getNextNamespace();
+        // Print all properties in this namespace.
+        const char* name = properties->getNextProperty();
+        const char* value = NULL;
+        while (name != NULL)
+        {
+            value = properties->getString(name);
+            WARN_VARG("%s = %s", name, value);
+            name = properties->getNextProperty();
+        }
+        WARN("}\n");
+ 
+        // Print the properties of every namespace within this one.
+        Properties* space = properties->getNextNamespace();
+        while (space != NULL)
+        {
+            printProperties(space);
+            space = properties->getNextNamespace();
+        }
     }
-}
-\endverbatim
+ @endverbatim
 
  * Note that this method does not keep track of the namespace hierarchy, but could be
  * modified to do so.  Also note that nothing in a properties file indicates the type

+ 35 - 52
gameplay/src/RadioButton.cpp

@@ -120,86 +120,69 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
 }
 
-void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
+void RadioButton::update(const Rectangle& clip)
 {
-    // Left, v-center.
-    // TODO: Set an alignment for radio button images.
-    const Theme::Border border = getBorder(_state);
-    const Theme::Padding padding = getPadding();
-    float opacity = getOpacity(_state);
+    Label::update(clip);
 
-    if (_selected)
+    Vector2 size;
+    if (_imageSize.isZero())
     {
-        const Rectangle& selectedRegion = getImageRegion("selected", _state);
-        const Theme::UVs& selected = getImageUVs("selected", _state);
-        Vector4 selectedColor = getImageColor("selected", _state);
-        selectedColor.w *= opacity;
-
-        Vector2 size;
-        if (_imageSize.isZero())
+        if (_selected)
         {
+            const Rectangle& selectedRegion = getImageRegion("selected", _state);
             size.set(selectedRegion.width, selectedRegion.height);
         }
         else
         {
-            size.set(_imageSize);
+            const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
+            size.set(unselectedRegion.width, unselectedRegion.height);
         }
-
-        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);
-
-        spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
     }
     else
     {
-        const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
-        const Theme::UVs& unselected = getImageUVs("unselected", _state);
-        Vector4 unselectedColor = getImageColor("unselected", _state);
-        unselectedColor.w *= opacity;
-
-        Vector2 size;
-        if (_imageSize.isZero())
-        {
-            size.set(unselectedRegion.width, unselectedRegion.height);
-        }
-        else
-        {
-            size.set(_imageSize);
-        }
+        size.set(_imageSize);
+    }
+    float iconWidth = size.x;
 
-        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);
+    _textBounds.x += iconWidth + 5;
+    _textBounds.width -= iconWidth + 5;
 
-        spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
+    if (_selected)
+    {
+        _image = getImage("selected", _state);
+    }
+    else
+    {
+        _image = getImage("unselected", _state);
     }
 }
 
-void RadioButton::update(const Rectangle& clip)
+void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
-    Control::update(clip);
+    // Left, v-center.
+    // TODO: Set an alignment for radio button images.
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding padding = getPadding();
+    
+    const Rectangle& region = _image->getRegion();
+    const Theme::UVs& uvs = _image->getUVs();
+    Vector4 color = _image->getColor();
+    color.w *= _opacity;
 
     Vector2 size;
     if (_imageSize.isZero())
     {
-        if (_selected)
-        {
-            const Rectangle& selectedRegion = getImageRegion("selected", _state);
-            size.set(selectedRegion.width, selectedRegion.height);
-        }
-        else
-        {
-            const Rectangle& unselectedRegion = getImageRegion("unselected", _state);
-            size.set(unselectedRegion.width, unselectedRegion.height);
-        }
+        size.set(region.width, region.height);
     }
     else
     {
         size.set(_imageSize);
     }
-    float iconWidth = size.x;
 
-    _textBounds.x += iconWidth + 5;
-    _textBounds.width -= iconWidth + 5;
+    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);
+
+    spriteBatch->draw(pos.x, pos.y, size.x, size.y, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _clip);
 }
 
 }

+ 18 - 15
gameplay/src/RadioButton.h

@@ -15,21 +15,23 @@ namespace gameplay
  * from a group can be selected at one time.
  *
  * The following properties are available for radio buttons:
- *
- * radioButton <RadioButton ID>
- * {
- *      style       = <Style ID>
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *      text        = <string>
- *      group       = <string>
- *      iconSize    = <width, height>   // The size to draw the radio button icon, if different from its size in the texture.
- * }
+
+ @verbatim
+    radioButton <RadioButton ID>
+    {
+         style       = <Style ID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+         group       = <string>
+         iconSize    = <width, height>   // The size to draw the radio button icon, if different from its size in the texture.
+    }
+ @endverbatim
  */
 class RadioButton : public Button
 {
@@ -132,6 +134,7 @@ protected:
     std::string _groupId;
     bool _selected;
     Vector2 _imageSize;
+    Theme::ThemeImage* _image;
 
 private:
 

+ 3 - 0
gameplay/src/RenderState.h

@@ -11,6 +11,9 @@ class Node;
 class NodeCloneContext;
 class Pass;
 
+/**
+ * Defines the render state of the graphics device.
+ */
 class RenderState : public Ref
 {
     friend class Game;

+ 4 - 0
gameplay/src/RenderTarget.h

@@ -7,6 +7,10 @@
 namespace gameplay
 {
 
+/**
+ * Represents a linear area of display memory and usually reside 
+ * in the display memory of the display card.
+ */
 class RenderTarget : public Ref
 {
 public:

+ 33 - 23
gameplay/src/SceneLoader.cpp

@@ -61,8 +61,9 @@ Scene* SceneLoader::load(const char* filePath)
         SceneNodeProperty::ROTATE |
         SceneNodeProperty::SCALE |
         SceneNodeProperty::TRANSLATE | 
-        SceneNodeProperty::TRANSPARENT);
-    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOST | SceneNodeProperty::RIGIDBODY);
+        SceneNodeProperty::TRANSPARENT |
+        SceneNodeProperty::DYNAMIC);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOSTOBJECT | SceneNodeProperty::RIGIDBODY);
     createAnimations(scene);
 
     // Find the physics properties object.
@@ -195,7 +196,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
         snp._type == SceneNodeProperty::CHARACTER ||
-        snp._type == SceneNodeProperty::GHOST ||
+        snp._type == SceneNodeProperty::GHOSTOBJECT ||
         snp._type == SceneNodeProperty::RIGIDBODY)
     {
         // Check to make sure the referenced properties object was loaded properly.
@@ -250,7 +251,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             break;
         }
         case SceneNodeProperty::CHARACTER:
-        case SceneNodeProperty::GHOST:
+        case SceneNodeProperty::GHOSTOBJECT:
         case SceneNodeProperty::RIGIDBODY:
         {
             // Check to make sure the referenced properties object was loaded properly.
@@ -283,20 +284,20 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             {
                 WARN_VARG("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
             }
-            else if (snp._type == SceneNodeProperty::GHOST && strcmp(p->getNamespace(), "ghost") != 0)
+            else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
             {
-                WARN_VARG("Attempting to set a 'ghost' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                WARN_VARG("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());
+                WARN_VARG("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
             }
             else
             {
                 // If the scene file specifies a rigid body model, use it for creating the collision object.
                 Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
                 const char* name = NULL;
-                if (np && (name = np->getString("rigidbodymodel")))
+                if (np && (name = np->getString("rigidBodyModel")))
                 {
                     Node* modelNode = node->getScene()->findNode(name);
                     if (!modelNode)
@@ -307,14 +308,14 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                             WARN_VARG("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
                         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();
                             assert(model);
                         
                             // Up ref count to prevent node from releasing the model when we swap it.
                             model->addRef(); 
                         
-                            // Create collision object with new rigidbodymodel set.
+                            // Create collision object with new rigidBodyModel set.
                             node->setModel(modelNode->getModel());
                             node->setCollisionObject(p);
 
@@ -370,6 +371,11 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             node->setTransparent(true);
             break;
         }
+        case SceneNodeProperty::DYNAMIC:
+        {
+            node->setDynamic(true);
+            break;
+        }
         default:
             WARN_VARG("Unsupported node property type: %d.", snp._type);
             break;
@@ -439,14 +445,14 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                 // An external file was referenced, so load the node from file and then insert it into the scene with the new ID.
 
                 // TODO: Revisit this to determine if we should cache Bundle objects for the duration of the scene
-                // load to prevent constantly creating/destroying the same externally referenced packages each time
+                // load to prevent constantly creating/destroying the same externally referenced bundles each time
                 // a url with a file is encountered.
-                Bundle* tmpPackage = Bundle::create(snp._file.c_str());
-                if (tmpPackage)
+                Bundle* tmpBundle = Bundle::create(snp._file.c_str());
+                if (tmpBundle)
                 {
                     if (sceneNode._exactMatch)
                     {
-                        Node* node = tmpPackage->loadNode(snp._id.c_str());
+                        Node* node = tmpBundle->loadNode(snp._id.c_str());
                         if (node)
                         {
                             node->setId(sceneNode._nodeID);
@@ -462,16 +468,16 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                     {
                         // Search for nodes in the bundle using a partial match
                         std::string partialMatch = snp._id;
-                        unsigned int objectCount = tmpPackage->getObjectCount();
+                        unsigned int objectCount = tmpBundle->getObjectCount();
                         unsigned int matchCount = 0;
                         for (unsigned int k = 0; k < objectCount; ++k)
                         {
-                            const char* objid = tmpPackage->getObjectID(k);
+                            const char* objid = tmpBundle->getObjectID(k);
                             if (strstr(objid, snp._id.c_str()) == objid)
                             {
                                 // This object ID matches (starts with).
                                 // Try to load this object as a Node.
-                                Node* node = tmpPackage->loadNode(objid);
+                                Node* node = tmpBundle->loadNode(objid);
                                 if (node)
                                 {
                                     // Construct a new node ID using _nodeID plus the remainder of the partial match.
@@ -490,7 +496,7 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                         }
                     }
 
-                    SAFE_RELEASE(tmpPackage);
+                    SAFE_RELEASE(tmpBundle);
                 }
                 else
                 {
@@ -554,15 +560,15 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::CHARACTER, ns->getString());
                 }
-                else if (strcmp(name, "ghost") == 0)
+                else if (strcmp(name, "ghostObject") == 0)
                 {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::GHOST, ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::GHOSTOBJECT, ns->getString());
                 }
-                else if (strcmp(name, "rigidbody") == 0)
+                else if (strcmp(name, "rigidBody") == 0)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::RIGIDBODY, ns->getString());
                 }
-                else if (strcmp(name, "rigidbodymodel") == 0)
+                else if (strcmp(name, "rigidBodyModel") == 0)
                 {
                     // Ignore this for now. We process this when we do rigid body creation.
                 }
@@ -582,6 +588,10 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::TRANSPARENT);
                 }
+                else if (strcmp(name, "dynamic") == 0)
+                {
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::DYNAMIC);
+                }
                 else
                 {
                     WARN_VARG("Unsupported node property: %s = %s", name, ns->getString());

+ 3 - 2
gameplay/src/SceneLoader.h

@@ -46,13 +46,14 @@ private:
             MATERIAL = 2,
             PARTICLE = 4,
             CHARACTER = 8,
-            GHOST = 16,
+            GHOSTOBJECT = 16,
             RIGIDBODY = 32,
             TRANSLATE = 64,
             ROTATE = 128,
             SCALE = 256,
             URL = 512,
-            TRANSPARENT = 1024
+            TRANSPARENT = 1024,
+            DYNAMIC = 2048
         };
 
         SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }

+ 29 - 20
gameplay/src/Slider.cpp

@@ -141,6 +141,16 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 
+void Slider::update(const Rectangle& clip)
+{
+    Label::update(clip);
+
+    _minImage = getImage("minCap", _state);
+    _maxImage = getImage("maxCap", _state);
+    _markerImage = getImage("marker", _state);
+    _trackImage = getImage("track", _state);
+}
+
 void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 {
     // TODO: Vertical slider.
@@ -151,26 +161,25 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     const Theme::Border& border = getBorder(_state);
     const Theme::Padding& padding = getPadding();
 
-    const Rectangle& minCapRegion = getImageRegion("minCap", _state);
-    const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
-    const Rectangle& markerRegion = getImageRegion("marker", _state);
-    const Rectangle& trackRegion = getImageRegion("track", _state);
-
-    const Theme::UVs minCap = getImageUVs("minCap", _state);
-    const Theme::UVs maxCap = getImageUVs("maxCap", _state);
-    const Theme::UVs marker = getImageUVs("marker", _state);
-    const Theme::UVs track = getImageUVs("track", _state);
-
-    Vector4 minCapColor = getImageColor("minCap", _state);
-    Vector4 maxCapColor = getImageColor("maxCap", _state);
-    Vector4 markerColor = getImageColor("marker", _state);
-    Vector4 trackColor = getImageColor("track", _state);
-
-    float opacity = getOpacity(_state);
-    minCapColor.w *= opacity;
-    maxCapColor.w *= opacity;
-    markerColor.w *= opacity;
-    trackColor.w *= opacity;
+    const Rectangle& minCapRegion = _minImage->getRegion();
+    const Rectangle& maxCapRegion = _maxImage->getRegion();
+    const Rectangle& markerRegion = _markerImage->getRegion();
+    const Rectangle& trackRegion = _trackImage->getRegion();
+
+    const Theme::UVs minCap = _minImage->getUVs();
+    const Theme::UVs maxCap = _maxImage->getUVs();
+    const Theme::UVs marker = _markerImage->getUVs();
+    const Theme::UVs track = _trackImage->getUVs();
+
+    Vector4 minCapColor = _minImage->getColor();
+    Vector4 maxCapColor = _maxImage->getColor();
+    Vector4 markerColor = _markerImage->getColor();
+    Vector4 trackColor = _trackImage->getColor();
+
+    minCapColor.w *= _opacity;
+    maxCapColor.w *= _opacity;
+    markerColor.w *= _opacity;
+    trackColor.w *= _opacity;
 
     // Draw order: track, caps, marker.
     float midY = clip.y + _clipBounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f;

+ 24 - 16
gameplay/src/Slider.h

@@ -11,22 +11,23 @@ namespace gameplay
 {
 
 /**
- *  A slider consists of a marker that can slide along a track between two end-caps.
- *  The following properties are available for sliders:
- *
- *  slider
- *  {
- *      style       = <styleID>                 // A Style from the Theme.
- *      position    = <x, y>                    // Position of the Control on-screen, measured in pixels.
- *      size        = <width, height>           // Size of the Control, measured in pixels.
- *      min         = <float>                   // The value of the left- / bottom-most point on the slider.
- *      max         = <float>                   // The value of the right- / top-most point on the slider.
- *      value       = <float>                   // The default position of the marker.
- *      step        = <float>                   // If greater than 0, force the marker to snap to discrete multiples of 'step'.
- *      text        = <string>                  // Text to display above, below or alongside the slider (depending on the style).
- *
-*      // TODO: orientation = <HORIZONTAL or VERTICAL>  // Determines whether a slider is stretched along its width or its height
- *  }
+ * A slider consists of a marker that can slide along a track between two end-caps.
+ * The following properties are available for sliders:
+
+ @verbatim
+    slider
+    {
+        style       = <styleID>                 // A Style from the Theme.
+        position    = <x, y>                    // Position of the Control on-screen, measured in pixels.
+        size        = <width, height>           // Size of the Control, measured in pixels.
+        min         = <float>                   // The value of the left- / bottom-most point on the slider.
+        max         = <float>                   // The value of the right- / top-most point on the slider.
+        value       = <float>                   // The default position of the marker.
+        step        = <float>                   // If greater than 0, force the marker to snap to discrete multiples of 'step'.
+        text        = <string>                  // Text to display above, below or alongside the slider (depending on the style).
+        // TODO: orientation = <HORIZONTAL or VERTICAL>  // Determines whether a slider is stretched along its width or its height
+    }
+ @endverbatim
  */
 class Slider : public Label
 {
@@ -148,11 +149,18 @@ protected:
      */
     void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
+    void update(const Rectangle& clip);
+
     float _min;
     float _max;
     float _step;
     float _value;
 
+    Theme::ThemeImage* _minImage;
+    Theme::ThemeImage* _maxImage;
+    Theme::ThemeImage* _trackImage;
+    Theme::ThemeImage* _markerImage;
+
 private:
 
     /**

+ 10 - 7
gameplay/src/TextBox.cpp

@@ -274,7 +274,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
 void TextBox::update(const Rectangle& clip)
 {
-    Control::update(clip);
+    Label::update(clip);
 
     // Get index into string and cursor location from the last recorded touch location.
     if (_state == FOCUS)
@@ -287,6 +287,9 @@ void TextBox::update(const Rectangle& clip)
         font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
             textAlignment, true, rightToLeft);
     }
+
+    _fontSize = getFontSize(_state);
+    _caretImage = getImage("textCaret", _state);
 }
 
 void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
@@ -294,14 +297,14 @@ void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     if (_state == FOCUS)
     {
         // Draw the cursor at its current location.
-        const Rectangle& region = getImageRegion("textCaret", _state);
+        const Rectangle& region = _caretImage->getRegion();
         if (!region.isEmpty())
         {
-            const Vector4& color = getImageColor("textCaret", _state);
-            const Theme::UVs uvs = getImageUVs("textCaret", _state);
-            unsigned int fontSize = getFontSize(_state);
+            const Theme::UVs uvs = _caretImage->getUVs();
+            Vector4 color = _caretImage->getColor();
+            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);
         }
     }
 
@@ -310,7 +313,7 @@ void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
 
 void TextBox::setCaretLocation(int x, int y)
 {
-    Theme::Border border = getBorder(_state);
+    const Theme::Border& border = getBorder(_state);
     Theme::Padding padding = getPadding();
 
     _caretLocation.set(x - border.left - padding.left + _clip.x,

+ 17 - 13
gameplay/src/TextBox.h

@@ -17,19 +17,21 @@ namespace gameplay
  * for the last keypress it received.
  *
  * The following properties are available for text boxes:
- *
- * label <labelID>
- * {
- *      style       = <styleID>
- *      alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
- *      position    = <x, y>
- *      autoWidth   = <bool>
- *      autoHeight  = <bool>
- *      size        = <width, height>
- *      width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
- *      height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
- *      text        = <string>
- * }
+
+ @verbatim
+    label <labelID>
+    {
+         style       = <styleID>
+         alignment   = <Control::Alignment constant> // Note: 'position' will be ignored.
+         position    = <x, y>
+         autoWidth   = <bool>
+         autoHeight  = <bool>
+         size        = <width, height>
+         width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
+         height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         text        = <string>
+    }
+ @endverbatim
  */
 class TextBox : public Label
 {
@@ -123,6 +125,8 @@ protected:
     Vector2 _caretLocation;
     unsigned int textIndex;
     int _lastKeypress;
+    unsigned int _fontSize;
+    Theme::ThemeImage* _caretImage;
 
 private:
 

+ 24 - 24
gameplay/src/Theme.cpp

@@ -25,7 +25,7 @@ namespace gameplay
 
         for (unsigned int i = 0, count = _images.size(); i < count; ++i)
         {
-            Image* image = _images[i];
+            ThemeImage* image = _images[i];
             SAFE_RELEASE(image);
         }
 
@@ -106,7 +106,7 @@ namespace gameplay
             
             if (strcmp(spacename, "image") == 0)
             {
-                theme->_images.push_back(Image::create(tw, th, space, Vector4::one()));
+                theme->_images.push_back(ThemeImage::create(tw, th, space, Vector4::one()));
             }
             else if (strcmp(spacename, "imageList") == 0)
             {
@@ -196,7 +196,7 @@ namespace gameplay
                         }
 
                         ImageList* imageList = NULL;
-                        Image* cursor = NULL;
+                        ThemeImage* cursor = NULL;
                         Skin* skin = NULL;
                         theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
 
@@ -307,7 +307,7 @@ namespace gameplay
                         }
 
                         ImageList* imageList = NULL;
-                        Image* cursor = NULL;
+                        ThemeImage* cursor = NULL;
                         Skin* skin = NULL;
                         theme->lookUpSprites(innerSpace, &imageList, &cursor, &skin);
 
@@ -394,7 +394,7 @@ namespace gameplay
                     disabled->addRef();
                 }
 
-                Theme::Style* s = new Theme::Style(space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
+                Theme::Style* s = new Theme::Style(theme, space->getId(), tw, th, margin, padding, normal, focus, active, disabled);
                 theme->_styles.push_back(s);
             }
 
@@ -468,19 +468,19 @@ namespace gameplay
     }
 
     /****************
-     * Theme::Image *
+     * Theme::ThemeImage *
      ****************/
-    Theme::Image::Image(float tw, float th, const Rectangle& region, const Vector4& color)
+    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::Image::~Image()
+    Theme::ThemeImage::~ThemeImage()
     {
     }
 
-    Theme::Image* Theme::Image::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
+    Theme::ThemeImage* Theme::ThemeImage::create(float tw, float th, Properties* properties, const Vector4& defaultColor)
     {
         Vector4 regionVector;                
         properties->getVector4("region", &regionVector);
@@ -496,7 +496,7 @@ namespace gameplay
             color.set(defaultColor);
         }
 
-        Image* image = new Image(tw, th, region, color);
+        ThemeImage* image = new ThemeImage(tw, th, region, color);
         const char* id = properties->getId();
         if (id)
         {
@@ -506,22 +506,22 @@ namespace gameplay
         return image;
     }
 
-    const char* Theme::Image::getId() const
+    const char* Theme::ThemeImage::getId() const
     {
         return _id.c_str();
     }
 
-    const Theme::UVs& Theme::Image::getUVs() const
+    const Theme::UVs& Theme::ThemeImage::getUVs() const
     {
         return _uvs;
     }
 
-    const Rectangle& Theme::Image::getRegion() const
+    const Rectangle& Theme::ThemeImage::getRegion() const
     {
         return _region;
     }
 
-    const Vector4& Theme::Image::getColor() const
+    const Vector4& Theme::ThemeImage::getColor() const
     {
         return _color;
     }
@@ -538,20 +538,20 @@ namespace gameplay
         _id = copy._id;
         _color = copy._color;
 
-        std::vector<Image*>::const_iterator it;
+        std::vector<ThemeImage*>::const_iterator it;
         for (it = copy._images.begin(); it != copy._images.end(); it++)
         {
-            Image* image = *it;
-            _images.push_back(new Image(*image));
+            ThemeImage* image = *it;
+            _images.push_back(new ThemeImage(*image));
         }
     }
 
     Theme::ImageList::~ImageList()
     {
-        std::vector<Image*>::const_iterator it;
+        std::vector<ThemeImage*>::const_iterator it;
         for (it = _images.begin(); it != _images.end(); it++)
         {
-            Image* image = *it;
+            ThemeImage* image = *it;
             SAFE_RELEASE(image);
         }
     }
@@ -575,7 +575,7 @@ namespace gameplay
         Properties* space = properties->getNextNamespace();
         while (space != NULL)
         {
-            Image* image = Image::create(tw, th, space, color);
+            ThemeImage* image = ThemeImage::create(tw, th, space, color);
             imageList->_images.push_back(image);
             space = properties->getNextNamespace();
         }
@@ -588,12 +588,12 @@ namespace gameplay
         return _id.c_str();
     }
 
-    Theme::Image* Theme::ImageList::getImage(const char* imageId) const
+    Theme::ThemeImage* Theme::ImageList::getImage(const char* imageId) const
     {
-        std::vector<Image*>::const_iterator it;
+        std::vector<ThemeImage*>::const_iterator it;
         for (it = _images.begin(); it != _images.end(); it++)
         {
-            Image* image = *it;
+            ThemeImage* image = *it;
             if (strcmp(image->getId(), imageId) == 0)
             {
                 return image;
@@ -724,7 +724,7 @@ namespace gameplay
         uvs->v2 = 1.0f - ((y + height) * th);
     }
 
-    void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** cursor, Skin** Skin)
+    void Theme::lookUpSprites(const Properties* overlaySpace, ImageList** imageList, ThemeImage** cursor, Skin** Skin)
     {
         const char* imageListString = overlaySpace->getString("imageList");
         if (imageListString)

+ 119 - 117
gameplay/src/Theme.h

@@ -21,113 +21,115 @@ namespace gameplay
  * are associated with a Control, and Font properties to apply to a Control's text.
  *
  * Below is an explanation of the properties that can be set within themes:
- *
- * theme
- * {
- *     texture = <Path to texture>
- * 
- *     // Describes a single image, to be used as a cursor.
- *     cursor <Cursor ID>
- *     {
- *         region = <x, y, width, height>
- *         color = <#ffffffff>
- *     }
- * 
- *     // Describes all the images used by a control for one or more states.
- *     imageList <ImageList ID>
- *     {
- *         image checked
- *         {
- *             region = <x, y, width, height>
- *         }
- * 
- *         image unchecked
- *         {
- *             region = <x, y, width, height>
- *             color = <#fffffffff>            // Optionally override image-list color.
- *         }
- * 
- *         color = <#fffffffff>    // Default blend color for images that don't specify their own.
- *     }
- *     
- *     // Defines the border and background of a Control.
- *     skin <Skin ID>
- *     {
- *         // The corners and edges of the given region will be used as border sprites.
- *         border
- *         {
- *             top     =   <int>   // Height of top border, top corners.
- *             bottom  =   <int>   // Height of bottom border, bottom corners.
- *             left    =   <int>   // Width of left border, left corners.
- *             right   =   <int>   // Width of right border, right corners.
- *         }
- *         
- *         region  =   <x, y, width, height>   // Total container region including entire border.
- *         color   =   <#ffffffff>             // Tint to apply to skin.
- *     }
- *     
- *     style <Style ID>
- *     {
- *         // Layouts may make use of a style's margin to put space between adjacent controls.
- *         margin
- *         {
- *             top     =   <int>
- *             bottom  =   <int>
- *             left    =   <int>
- *             right   =   <int>        
- *         }
- *         
- *         // Padding is the space between a control's border and its content.
- *         padding
- *         {
- *             top     =   <int>
- *             bottom  =   <int>
- *             left    =   <int>
- *             right   =   <int>        
- *         }
- *         
- *         // Properties used when in control is in the normal state.
- *         stateNormal
- *         {
- *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
- *             imageList = <ImageList ID>
- * 
- *             cursor      =   <Cursor ID>                 // Cursor to use when the mouse is over this control.
- *             font        =   <Path to font>              // Font to use for rendering text.
- *             fontSize    =   <int>                       // Size of text.
- *             textColor   =   <#ffffffff>                 // Color of text.
- *             alignment   =   <Text alignment constant>   // Member of Font::Justify enum.
- *             rightToLeft =   <bool>                      // Whether to draw text from right to left.
- *             opacity     =   <float>                     // Opacity to apply to all text/border/icon colors.
- *         }
- *         
- *         // Properties used when in control is in the focus state
- *         // If not specified, the 'normal' overlay will be used.
- *         stateFocus
- *         {
- *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
- *             ...
- *         }
- *         
- *         // Properties used when in control is in the focus. 
- *         // This is when a touch/mouse is down within the control.
- *         // If not specified, the 'normal' overlay will be used.
- *         stateActive
- *         {
- *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
- *             ...
- *         }
- * 
- *         // Properties used when in control is in the focus. 
- *         // This is when a touch/mouse is down within the control.
- *         // If not specified, the 'normal' overlay will be used.
- *         state-disabled
- *         {
- *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
- *             ...        
- *         }
- *     }
- * }
+
+@verbatim
+    theme
+    {
+        texture = <Path to texture>
+
+        // Describes a single image, to be used as a cursor.
+        cursor <Cursor ID>
+        {
+            region = <x, y, width, height>
+            color = <#ffffffff>
+        }
+
+        // Describes all the images used by a control for one or more states.
+        imageList <ImageList ID>
+        {
+            image checked
+            {
+                region = <x, y, width, height>
+            }
+
+            image unchecked
+            {
+                region = <x, y, width, height>
+                color = <#fffffffff>            // Optionally override image-list color.
+            }
+
+            color = <#fffffffff>    // Default blend color for images that don't specify their own.
+        }
+    
+        // Defines the border and background of a Control.
+        skin <Skin ID>
+        {
+            // The corners and edges of the given region will be used as border sprites.
+            border
+            {
+                top     =   <int>   // Height of top border, top corners.
+                bottom  =   <int>   // Height of bottom border, bottom corners.
+                left    =   <int>   // Width of left border, left corners.
+                right   =   <int>   // Width of right border, right corners.
+            }
+        
+            region  =   <x, y, width, height>   // Total container region including entire border.
+            color   =   <#ffffffff>             // Tint to apply to skin.
+        }
+    
+        style <Style ID>
+        {
+            // Layouts may make use of a style's margin to put space between adjacent controls.
+            margin
+            {
+                top     =   <int>
+                bottom  =   <int>
+                left    =   <int>
+                right   =   <int>        
+            }
+        
+            // Padding is the space between a control's border and its content.
+            padding
+            {
+                top     =   <int>
+                bottom  =   <int>
+                left    =   <int>
+                right   =   <int>        
+            }
+        
+            // Properties used when in control is in the normal state.
+            stateNormal
+            {
+                skin   =   <Skin ID>             // Skin to use for border and background sprites.
+                imageList = <ImageList ID>
+
+                cursor      =   <Cursor ID>                 // Cursor to use when the mouse is over this control.
+                font        =   <Path to font>              // Font to use for rendering text.
+                fontSize    =   <int>                       // Size of text.
+                textColor   =   <#ffffffff>                 // Color of text.
+                alignment   =   <Text alignment constant>   // Member of Font::Justify enum.
+                rightToLeft =   <bool>                      // Whether to draw text from right to left.
+                opacity     =   <float>                     // Opacity to apply to all text/border/icon colors.
+            }
+        
+            // Properties used when in control is in the focus state
+            // If not specified, the 'normal' overlay will be used.
+            stateFocus
+            {
+                skin   =   <Skin ID>             // Skin to use for border and background sprites.
+                ...
+            }
+        
+            // Properties used when in control is in the focus. 
+            // This is when a touch/mouse is down within the control.
+            // If not specified, the 'normal' overlay will be used.
+            stateActive
+            {
+                skin   =   <Skin ID>             // Skin to use for border and background sprites.
+                ...
+            }
+
+            // Properties used when in control is in the focus. 
+            // This is when a touch/mouse is down within the control.
+            // If not specified, the 'normal' overlay will be used.
+            state-disabled
+            {
+                skin   =   <Skin ID>             // Skin to use for border and background sprites.
+                ...        
+            }
+        }
+    }
+@endverbatim
  *
  */
 class Theme: public Ref
@@ -181,14 +183,12 @@ public:
         float right;
     } Margin, Border, Padding;
 
-private:
-
     /**
      * Class representing an image within the theme's texture atlas.
      * An image has a region and a blend color in addition to an ID.
      * UV coordinates are calculated from the region and can be retrieved.
      */
-    class Image : public Ref
+    class ThemeImage : public Ref
     {
         friend class Theme;
         friend class Control;
@@ -205,11 +205,11 @@ private:
 
     private:
 
-        Image(float tw, float th, const Rectangle& region, const Vector4& color);
+        ThemeImage(float tw, float th, const Rectangle& region, const Vector4& color);
 
-        ~Image();
+        ~ThemeImage();
 
-        static Image* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
+        static ThemeImage* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
 
         std::string _id;
         UVs _uvs;
@@ -217,6 +217,8 @@ private:
         Vector4 _color;
     };
 
+private:
+
     /**
      * Class representing a collection of theme images.  An image list
      * can be assigned to each overlay of a style, and controls
@@ -231,7 +233,7 @@ private:
 
         const char* getId() const;
 
-        Image* getImage(const char* imageId) const;
+        ThemeImage* getImage(const char* imageId) const;
 
     private:
 
@@ -244,7 +246,7 @@ private:
         static ImageList* create(float tw, float th, Properties* properties);
 
         std::string _id;
-        std::vector<Image*> _images;
+        std::vector<ThemeImage*> _images;
         Vector4 _color;
     };
 
@@ -344,13 +346,13 @@ private:
 
     static void generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs);
 
-    void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** mouseCursor, Skin** skin);
+    void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, ThemeImage** mouseCursor, Skin** skin);
 
     std::string _path;
     Texture* _texture;
     SpriteBatch* _spriteBatch;
     std::vector<Style*> _styles;
-    std::vector<Image*> _images;
+    std::vector<ThemeImage*> _images;
     std::vector<ImageList*> _imageLists;
     std::vector<Skin*> _skins;
     std::set<Font*> _fonts;

+ 12 - 12
gameplay/src/ThemeStyle.cpp

@@ -6,10 +6,10 @@ namespace gameplay
 /****************
  * Theme::Style *
  ****************/
-Theme::Style::Style(const char* id, float tw, float th,
+Theme::Style::Style(Theme* theme, const char* id, float tw, float th,
         const Theme::Margin& margin, const Theme::Padding& padding,
         Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
-    : _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
+    : _theme(theme), _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
 {
     _overlays[OVERLAY_NORMAL] = normal;
     _overlays[OVERLAY_FOCUS] = focus;
@@ -76,8 +76,8 @@ const Theme::Padding& Theme::Style::getPadding() const
 }
     
 /*************************
-    * Theme::Style::Overlay *
-    *************************/
+ * Theme::Style::Overlay *
+ *************************/
 Theme::Style::Overlay* Theme::Style::Overlay::create()
 {
     Overlay* overlay = new Overlay();
@@ -96,7 +96,7 @@ Theme::Style::Overlay::Overlay(const Overlay& copy) : _skin(NULL), _cursor(NULL)
     }
     if (copy._cursor)
     {
-        _cursor = new Image(*copy._cursor);
+        _cursor = new ThemeImage(*copy._cursor);
     }
     if (copy._imageList)
     {
@@ -263,7 +263,7 @@ void Theme::Style::Overlay::setTextColor(const Vector4& color)
 
 const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
 {
-    Image* image = _imageList->getImage(id);
+    ThemeImage* image = _imageList->getImage(id);
     if (image)
     {
         return image->getRegion();
@@ -276,7 +276,7 @@ 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)
 {
-    Image* image = _imageList->getImage(id);
+    ThemeImage* image = _imageList->getImage(id);
     assert(image);
     image->_region.set(region);
     generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
@@ -284,7 +284,7 @@ void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& regi
 
 const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
 {
-    Image* image = _imageList->getImage(id);
+    ThemeImage* image = _imageList->getImage(id);
     if (image)
     {
         return image->getColor();
@@ -297,14 +297,14 @@ const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
 
 void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
 {
-    Image* image = _imageList->getImage(id);
+    ThemeImage* image = _imageList->getImage(id);
     assert(image);
     image->_color.set(color);
 }
 
 const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
 {
-    Image* image = _imageList->getImage(id);
+    ThemeImage* image = _imageList->getImage(id);
     if (image)
     {
         return image->getUVs();
@@ -383,7 +383,7 @@ Theme::Skin* Theme::Style::Overlay::getSkin() const
     return _skin;
 }
 
-void Theme::Style::Overlay::setCursor(Image* cursor)
+void Theme::Style::Overlay::setCursor(ThemeImage* cursor)
 {
     if (_cursor != cursor)
     {
@@ -397,7 +397,7 @@ void Theme::Style::Overlay::setCursor(Image* cursor)
     }
 }
 
-Theme::Image* Theme::Style::Overlay::getCursor() const
+Theme::ThemeImage* Theme::Style::Overlay::getCursor() const
 {
     return _cursor;
 }

+ 6 - 5
gameplay/src/ThemeStyle.h

@@ -138,9 +138,9 @@ private:
 
         Theme::Skin* getSkin() const;
 
-        void setCursor(Theme::Image* cursor);
+        void setCursor(Theme::ThemeImage* cursor);
             
-        Theme::Image* getCursor() const;
+        Theme::ThemeImage* getCursor() const;
             
         void setImageList(Theme::ImageList* imageList);
             
@@ -149,7 +149,7 @@ private:
         void applyAnimationValueOpacity(float opacity, float blendWeight);
 
         Skin* _skin;
-        Theme::Image* _cursor;
+        Theme::ThemeImage* _cursor;
         Theme::ImageList* _imageList;
         Font* _font;
         unsigned int _fontSize;
@@ -162,7 +162,7 @@ private:
     /**
      * Constructor.
      */
-    Style(const char* id, float tw, float th,
+    Style(Theme* theme, const char* id, float tw, float th,
           const Theme::Margin& margin, const Theme::Padding& padding,
           Overlay* normal, Overlay* focus, Overlay* active, Overlay* disabled);
 
@@ -209,7 +209,8 @@ private:
      * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
      */
     void setMargin(float top, float bottom, float left, float right);
-        
+    
+    Theme* _theme;
     std::string _id;
     float _tw;
     float _th;