Quellcode durchsuchen

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

Steve Grenier vor 13 Jahren
Ursprung
Commit
936a4efd3a

+ 8 - 2
CHANGES.md

@@ -2,8 +2,14 @@
 
 - Pre-built versions gameplay-encoder added to bin folder with TTF, DAE and FBX support built-in.
 - Improved modular shader library with support for #include in shaders.
+- Adds Gamepad class for virtual gamepad support.
+- Adds the ability for cloning and wireframe the boy in sample03-character.
+- Fixes to gameplay-encoder to prompt user for font size if not specified.
+- Fixes to add "-g" as short form argument for grouping animations.
+- Fixes node cloning.
+- Fixes to gameplay-encoder for output file path when encoding fonts.
 - Fixes to FrameBuffer, RenderTarget and DepthStencilTarget.
-- TODO
+
 
 ## v1.3.0
 
@@ -12,7 +18,7 @@
 - User Interface support for scrolling with scrollbars on Container.
 - PVRTC, ATC and DXT texture compression support.
 - Performance improvements in user interface forms and text.
-- Performance improvements in animations on transforms.
+- Performance improvements in animations on transforms.
 - Performance improvements using NEON math for BlackBerry and iOS.
 - Fixes for improvements in error handling throughout all systems.
 - Fixes supporting built-in Maya COLLADA exporter via DAE_FBX export.

+ 2 - 2
gameplay/gameplay.vcxproj

@@ -206,8 +206,6 @@
     <None Include="res\logo_powered_black.png" />
     <None Include="res\logo_powered_white.png" />
     <None Include="res\logo_white.png" />
-    <None Include="res\shaders\bumped.frag" />
-    <None Include="res\shaders\bumped.vert" />
     <None Include="res\shaders\colored-unlit.frag" />
     <None Include="res\shaders\colored-unlit.vert" />
     <None Include="res\shaders\colored.frag" />
@@ -221,6 +219,8 @@
     <None Include="res\shaders\lib\lighting-spot.frag" />
     <None Include="res\shaders\lib\lighting-spot.vert" />
     <None Include="res\shaders\lib\lighting.frag" />
+    <None Include="res\shaders\textured-bumped.frag" />
+    <None Include="res\shaders\textured-bumped.vert" />
     <None Include="res\shaders\textured-unlit.frag" />
     <None Include="res\shaders\textured-unlit.vert" />
     <None Include="res\shaders\textured.frag" />

+ 6 - 6
gameplay/gameplay.vcxproj.filters

@@ -637,12 +637,6 @@
     <None Include="res\shaders\colored-unlit.frag">
       <Filter>res\shaders</Filter>
     </None>
-    <None Include="res\shaders\bumped.vert">
-      <Filter>res\shaders</Filter>
-    </None>
-    <None Include="res\shaders\bumped.frag">
-      <Filter>res\shaders</Filter>
-    </None>
     <None Include="res\shaders\lib\lighting.frag">
       <Filter>res\shaders\lib</Filter>
     </None>
@@ -670,6 +664,12 @@
     <None Include="res\shaders\lib\attributes-skinning.vert">
       <Filter>res\shaders\lib</Filter>
     </None>
+    <None Include="res\shaders\textured-bumped.frag">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\textured-bumped.vert">
+      <Filter>res\shaders</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

+ 8 - 3
gameplay/res/shaders/colored-unlit.frag

@@ -7,9 +7,11 @@ precision highp float;
 varying vec3 v_color;						// Input Vertex color ( r g b )
 #endif
 
-uniform vec4 u_diffuseColor;               	// Diffuse color
-
 // Uniforms
+uniform vec4 u_diffuseColor;               	// Diffuse color
+#if defined(TEXTURE_LIGHTMAP)
+uniform sampler2D u_lightmapTexture;     	// Lightmap texture
+#endif
 #if defined(MODULATE_COLOR)
 uniform vec4 u_modulateColor;               // Modulation color
 #endif
@@ -26,7 +28,10 @@ void main()
 	#else
 	gl_FragColor = u_diffuseColor;
     #endif
-	
+	#if defined(TEXTURE_LIGHTMAP)
+	vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord);
+	gl_FragColor.a *= lightColor.a;
+	#endif
 	// Global color modulation
 	#if defined(MODULATE_COLOR)
 	gl_FragColor *= u_modulateColor;

+ 0 - 0
gameplay/res/shaders/bumped.frag → gameplay/res/shaders/textured-bumped.frag


+ 0 - 0
gameplay/res/shaders/bumped.vert → gameplay/res/shaders/textured-bumped.vert


+ 3 - 3
gameplay/res/shaders/textured-unlit.frag

@@ -4,7 +4,7 @@ precision highp float;
 
 // Uniforms
 uniform sampler2D u_diffuseTexture;     	// Diffuse texture
-#if defined(TEXTURE_LIGHT)
+#if defined(TEXTURE_LIGHTMAP)
 uniform sampler2D u_lightmapTexture;     	// Lightmap texture
 #endif
 #if defined(MODULATE_COLOR)
@@ -22,8 +22,8 @@ void main()
 {
     // Sample the texture for the color
     gl_FragColor = texture2D(u_diffuseTexture, v_texCoord);
-	#if defined(TEXTURE_LIGHT)
-	vec4 lightColor = texture2D(u_lightTexture, v_texCoord);
+	#if defined(TEXTURE_LIGHTMAP)
+	vec4 lightColor = texture2D(u_lightmapTexture, v_texCoord);
 	gl_FragColor.a *= lightColor.a;
 	#endif
 	// Global color modulation

+ 10 - 0
gameplay/src/Animation.cpp

@@ -450,6 +450,16 @@ Animation* Animation::clone(Channel* channel, AnimationTarget* target)
     // Release the animation because a newly created animation has a ref count of 1 and the channels hold the ref to animation.
     animation->release();
     GP_ASSERT(animation->getRefCount() == 1);
+
+    // Clone the clips
+    if (_clips)
+    {
+        for (std::vector<AnimationClip*>::iterator it = _clips->begin(); it != _clips->end(); ++it)
+        {
+            AnimationClip* newClip = (*it)->clone(animation);
+            animation->addClip(newClip);
+        }
+    }
     return animation;
 }
 

+ 26 - 1
gameplay/src/AnimationClip.cpp

@@ -64,7 +64,7 @@ AnimationClip::ListenerEvent::~ListenerEvent()
 {
 }
 
-const char* AnimationClip::getID() const
+const char* AnimationClip::getId() const
 {
     return _id.c_str();
 }
@@ -550,4 +550,29 @@ void AnimationClip::resetClipStateBit(unsigned char bit)
     _stateBits &= ~bit;
 }
 
+AnimationClip* AnimationClip::clone(Animation* animation) const
+{
+    // Don't clone the elapsed time, listeners or crossfade information.
+    AnimationClip* newClip = new AnimationClip(getId(), animation, getStartTime(), getEndTime());
+    newClip->setRepeatCount(getRepeatCount());
+    newClip->setSpeed(getSpeed());
+    newClip->setRepeatCount(getRepeatCount());
+    newClip->setBlendWeight(getBlendWeight());
+    
+    size_t size = _values.size();
+    newClip->_values.resize(size, NULL);
+    for (size_t i = 0; i < size; ++i)
+    {
+        if (newClip->_values[i] == NULL)
+        {
+            newClip->_values[i] = new AnimationValue(*_values[i]);
+        }
+        else
+        {
+            *newClip->_values[i] = *_values[i];
+        }
+    }
+    return newClip;
 }
+
+}

+ 10 - 1
gameplay/src/AnimationClip.h

@@ -75,7 +75,7 @@ public:
      *
      * @return The AnimationClip's ID.
      */
-    const char* getID() const;
+    const char* getId() const;
 
     /**
      * Gets the Animation that this AnimationClip was created from.
@@ -310,6 +310,15 @@ private:
      */
     void resetClipStateBit(unsigned char bit);
 
+    /**
+     * Clones the animation clip.
+     * 
+     * @param animation The animation that the new clip belongs to.
+     * 
+     * @return The newly created animation clip.
+     */
+    AnimationClip* clone(Animation* animation) const;
+
     std::string _id;                                    // AnimationClip ID.
     Animation* _animation;                              // The Animation this clip is created from.
     unsigned long _startTime;                           // Start time of the clip.

+ 24 - 0
gameplay/src/AnimationValue.cpp

@@ -11,11 +11,35 @@ AnimationValue::AnimationValue(unsigned int componentCount)
     _value = new float[_componentCount];
 }
 
+AnimationValue::AnimationValue(const AnimationValue& copy)
+{
+    _value = new float[copy._componentCount];
+    _componentSize = copy._componentSize;
+    _componentCount = copy._componentCount;
+    memcpy(_value, copy._value, _componentSize);
+}
+
 AnimationValue::~AnimationValue()
 {
     SAFE_DELETE_ARRAY(_value);
 }
 
+AnimationValue& AnimationValue::operator=(const AnimationValue& v)
+{
+    if (this != &v)
+    {
+        if (_value == NULL || _componentSize != v._componentSize || _componentCount != v._componentCount)
+        {
+            _componentSize = v._componentSize;
+            _componentCount = v._componentCount;
+            SAFE_DELETE_ARRAY(_value);
+            _value = new float[v._componentCount];
+        }
+        memcpy(_value, v._value, _componentSize);
+    }
+    return *this;
+}
+
 float AnimationValue::getFloat(unsigned int index) const
 {
     GP_ASSERT(index < _componentCount);

+ 5 - 0
gameplay/src/AnimationValue.h

@@ -72,6 +72,11 @@ private:
      */
     ~AnimationValue();
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    AnimationValue& operator=(const AnimationValue& v);
+
     unsigned int _componentCount;   // The number of float values for the property.
     unsigned int _componentSize;    // The number of bytes of memory the property is.
     float* _value;                  // The current value of the property.

+ 8 - 8
gameplay/src/Bundle.cpp

@@ -1129,37 +1129,37 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                     // No parent currently set for this joint.
                     // Lookup its parentID in case it references a node that was not yet loaded as part
                     // of the mesh skin's joint list.
-                    std::string nodeID = node->getId();
+                    std::string nodeId = node->getId();
 
                     while (true)
                     {
                         // Get the node's type.
-                        Reference* ref = find(nodeID.c_str());
+                        Reference* ref = find(nodeId.c_str());
                         if (ref == NULL)
                         {
-                            GP_ERROR("No object with name '%s' in bundle '%s'.", nodeID.c_str(), _path.c_str());
+                            GP_ERROR("No object with name '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
                             return;
                         }
 
                         // Seek to the current node in the file so we can get it's parent ID.
-                        seekTo(nodeID.c_str(), ref->type);
+                        seekTo(nodeId.c_str(), ref->type);
 
                         // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
                         if (fseek(_file, sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) != 0)
                         {
-                            GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeID.c_str(), _path.c_str());
+                            GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
                             return;
                         }
                         std::string parentID = readString(_file);
                         
                         if (!parentID.empty())
-                            nodeID = parentID;
+                            nodeId = parentID;
                         else
                             break;
                     }
 
-                    if (nodeID != rootJoint->getId())
-                        loadedNodes.push_back(loadNode(nodeID.c_str(), sceneContext, nodeContext));
+                    if (nodeId != rootJoint->getId())
+                        loadedNodes.push_back(loadNode(nodeId.c_str(), sceneContext, nodeContext));
 
                     break;
                 }

+ 0 - 3
gameplay/src/Button.cpp

@@ -5,14 +5,11 @@ namespace gameplay
 {
 
 Button::Button()
-    : _gamepadButtonIndex(NULL)
 {
 }
 
 Button::~Button()
 {
-    if (_gamepadButtonIndex)
-        SAFE_DELETE(_gamepadButtonIndex);
 }
 
 Button* Button::create(const char* id, Theme::Style* style)

+ 0 - 1
gameplay/src/Button.h

@@ -94,7 +94,6 @@ private:
      */
     Button(const Button& copy);
 
-    int* _gamepadButtonIndex;
 };
 
 }

+ 2 - 2
gameplay/src/Container.cpp

@@ -222,7 +222,7 @@ void Container::removeControl(const char* id)
     for (it = _controls.begin(); it < _controls.end(); it++)
     {
         Control* c = *it;
-        if (strcmp(id, c->getID()) == 0)
+        if (strcmp(id, c->getId()) == 0)
         {
             _controls.erase(it);
             return;
@@ -258,7 +258,7 @@ Control* Container::getControl(const char* id) const
     {
         Control* c = *it;
         GP_ASSERT(c);
-        if (strcmp(id, c->getID()) == 0)
+        if (strcmp(id, c->getId()) == 0)
         {
             return c;
         }

+ 6 - 8
gameplay/src/Container.h

@@ -177,6 +177,11 @@ public:
      */
     Animation* getAnimation(const char* id = NULL) const;
 
+    /**
+     * @see Control::isContainer
+     */
+    bool isContainer() const;
+
     /**
      * @see Control::getType
      */
@@ -286,14 +291,7 @@ protected:
      * @param layoutString The layout string to parse
      */
     static Layout::Type getLayoutType(const char* layoutString);
-
-    /**
-     * Returns whether this control is a container.
-     *
-     * @return true if this is a container, false if not.
-     */
-    bool isContainer() const;
-
+    
     /**
      * Returns whether this container or any of its controls have been modified and require an update.
      * 

+ 5 - 5
gameplay/src/Control.cpp

@@ -6,9 +6,9 @@ namespace gameplay
 {
 
 Control::Control()
-    : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
+    : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
     _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _listeners(NULL), _contactIndex(INVALID_CONTACT_INDEX),
-    _styleOverridden(false), _skin(NULL)
+    _styleOverridden(false), _skin(NULL)
 {
 }
 
@@ -134,7 +134,7 @@ void initialize(const char* id, Theme::Style* style, const Vector2& position, co
 
 }
 
-const char* Control::getID() const
+const char* Control::getId() const
 {
     return _id.c_str();
 }
@@ -744,7 +744,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
             // Leave this control in the FOCUS state.
-            notifyListeners(Listener::CLICK);
+            notifyListeners(Listener::CLICK);
         }
 
         return _consumeInputEvents;
@@ -1131,7 +1131,7 @@ void Control::setAnimationPropertyValue(int propertyId, AnimationValue* value, f
         _dirty = true;
         break;
     case ANIMATE_OPACITY:
-        _dirty = true;
+        _dirty = true;
         break;
     }
 }

+ 15 - 26
gameplay/src/Control.h

@@ -25,7 +25,6 @@ class Control : public Ref, public AnimationTarget
     friend class AbsoluteLayout;
     friend class VerticalLayout;
     friend class FlowLayout;
-    friend class Gamepad;
 
 public:
 
@@ -188,7 +187,7 @@ public:
      *
      * @return This control's ID.
      */
-    const char* getID() const;
+    const char* getId() const;
 
     /**
      * Set the position of this control relative to its parent container.
@@ -698,6 +697,20 @@ public:
      */
     void setFocusIndex(int focusIndex);
 
+    /**
+     * Returns whether this Control object is a Container or not.
+     *
+     * @return true if this object is of class Container, false otherwise.
+     */
+    virtual bool isContainer() const;
+
+    /**
+     * Gets the type of the Control and returns it as a string.
+     *
+     * @return The string of the Control type, all in lower-case.
+     */
+    virtual const char* getType() const;
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -710,13 +723,6 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
-    /**
-     * Gets the type of the Control and returns it as a string.
-     *
-     * @return The string of the Control type, all in lower-case.
-     */
-    virtual const char* getType() const;
-
     /**
      * @see AnimationTarget#getAnimationPropertyComponentCount
      */
@@ -841,23 +847,6 @@ protected:
      */
     virtual void initialize(Theme::Style* style, Properties* properties);
 
-    /**
-     * Initialize properties common to all Controls.
-     *
-     * @param id This control's ID.
-     * @param style The style to apply to this control.
-     * @param position This control's position.
-     * @param size This control's size.
-     */
-    //virtual void initialize(const char* id, Theme::Style* style, const Vector2& position, const Vector2& size);
-
-    /**
-     * Container and classes that extend it should implement this and return true.
-     *
-     * @return true if this object is of class Container, false otherwise.
-     */
-    virtual bool isContainer() const;
-
     /**
      * Returns whether this control has been modified and requires an update.
      *

+ 2 - 2
gameplay/src/DepthStencilTarget.cpp

@@ -46,7 +46,7 @@ DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
     {
         DepthStencilTarget* dst = *it;
         GP_ASSERT(dst);
-        if (strcmp(id, dst->getID()) == 0)
+        if (strcmp(id, dst->getId()) == 0)
         {
             return dst;
         }
@@ -54,7 +54,7 @@ DepthStencilTarget* DepthStencilTarget::getDepthStencilTarget(const char* id)
     return NULL;
 }
 
-const char* DepthStencilTarget::getID() const
+const char* DepthStencilTarget::getId() const
 {
     return _id.c_str();
 }

+ 1 - 1
gameplay/src/DepthStencilTarget.h

@@ -62,7 +62,7 @@ public:
      *
      * @return The ID of this DepthStencilTarget.
      */
-    const char* getID() const;
+    const char* getId() const;
 
     /**
      * Returns the format of the DepthStencilTarget.

+ 5 - 2
gameplay/src/Effect.cpp

@@ -253,7 +253,9 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
         }
 
         // Write out the expanded shader file.
-        writeShaderToErrorFile(vshPath, shaderSource[2]);
+        if (vshPath)
+            writeShaderToErrorFile(vshPath, shaderSource[2]);
+
         GP_ERROR("Compile failed for vertex shader '%s' with error '%s'.", vshPath == NULL ? "NULL" : vshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);
 
@@ -290,7 +292,8 @@ Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, con
         }
         
         // Write out the expanded shader file.
-        writeShaderToErrorFile(fshPath, shaderSource[2]);
+        if (fshPath)
+            writeShaderToErrorFile(fshPath, shaderSource[2]);
 
         GP_ERROR("Compile failed for fragment shader (%s): %s", fshPath == NULL ? "NULL" : fshPath, infoLog == NULL ? "" : infoLog);
         SAFE_DELETE_ARRAY(infoLog);

+ 125 - 112
gameplay/src/Form.cpp

@@ -10,12 +10,35 @@
 #include "CheckBox.h"
 #include "Scene.h"
 
+#define FORM_VSH \
+    "uniform mat4 u_worldViewProjectionMatrix;\n" \
+    "attribute vec3 a_position;\n" \
+    "attribute vec2 a_texCoord;\n" \
+    "varying vec2 v_texCoord;\n" \
+    "void main()\n" \
+    "{\n" \
+        "gl_Position = u_worldViewProjectionMatrix * vec4(a_position, 1);\n" \
+        "v_texCoord = a_texCoord;\n" \
+    "}\n"
+
+#define FORM_FSH \
+    "#ifdef OPENGL_ES\n" \
+    "precision highp float;\n" \
+    "#endif\n" \
+    "varying vec2 v_texCoord;\n" \
+    "uniform sampler2D u_texture;\n" \
+    "void main()\n" \
+    "{\n" \
+        "gl_FragColor = texture2D(u_texture, v_texCoord);\n" \
+    "}\n"
+
 namespace gameplay
 {
 
+static Effect* __formEffect = NULL;
 static std::vector<Form*> __forms;
 
-Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _spriteBatch(NULL)
+Form::Form() : _theme(NULL), _frameBuffer(NULL), _spriteBatch(NULL), _node(NULL), _nodeQuad(NULL), _nodeMaterial(NULL) , _u2(0), _v1(0)
 {
 }
 
@@ -25,11 +48,19 @@ Form::Form(const Form& copy)
 
 Form::~Form()
 {
-    SAFE_RELEASE(_quad);
     SAFE_RELEASE(_node);
+    SAFE_DELETE(_spriteBatch);
     SAFE_RELEASE(_frameBuffer);
     SAFE_RELEASE(_theme);
-    SAFE_DELETE(_spriteBatch);
+
+    if (__formEffect)
+    {
+        if (__formEffect->getRefCount() == 1)
+        {
+            __formEffect->release();
+            __formEffect = NULL;
+        }
+    }
 
     // Remove this Form from the global list.
     std::vector<Form*>::iterator it = std::find(__forms.begin(), __forms.end(), this);
@@ -169,7 +200,7 @@ Form* Form::create(const char* url)
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
-    form->update();
+    form->update(0.0f);
 
     SAFE_DELETE(properties);
 
@@ -185,12 +216,11 @@ Form* Form::getForm(const char* id)
     {
         Form* f = *it;
         GP_ASSERT(f);
-        if (strcmp(id, f->getID()) == 0)
+        if (strcmp(id, f->getId()) == 0)
         {
             return f;
         }
     }
-        
     return NULL;
 }
 
@@ -234,9 +264,9 @@ void Form::setSize(float width, float height)
         _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
         GP_ASSERT(_spriteBatch);
 
-        // Clear FBO.
-        _frameBuffer->bind();
+        // Clear the framebuffer black
         Game* game = Game::getInstance();
+        _frameBuffer->bind();
         Rectangle prevViewport = game->getViewport();
         game->setViewport(Rectangle(0, 0, width, height));
         _theme->setProjectionMatrix(_projectionMatrix);
@@ -285,54 +315,86 @@ void Form::setAutoHeight(bool autoHeight)
     }
 }
 
-void Form::setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4)
+Effect* createEffect()
 {
-    Mesh* mesh = Mesh::createQuad(p1, p2, p3, p4);
-    initializeQuad(mesh);
-    SAFE_RELEASE(mesh);
-}
-
-void Form::setQuad(float x, float y, float width, float height)
-{
-    float x2 = x + width;
-    float y2 = y + height;
-
-    float vertices[] =
+    Effect* effect = NULL;
+    if (__formEffect == NULL)
     {
-        x, y2, 0,   0, _v1,
-        x, y, 0,    0, 0,
-        x2, y2, 0,  _u2, _v1,
-        x2, y, 0,   _u2, 0
-    };
-
-    VertexFormat::Element elements[] =
+        __formEffect = Effect::createFromSource(FORM_VSH, FORM_FSH);
+        if (__formEffect == NULL)
+        {
+            GP_ERROR("Unable to load form effect.");
+            return NULL;
+        }
+        effect = __formEffect;
+    }
+    else
     {
-        VertexFormat::Element(VertexFormat::POSITION, 3),
-        VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
-    };
-    Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
-    assert(mesh);
-
-    mesh->setPrimitiveType(Mesh::TRIANGLE_STRIP);
-    mesh->setVertexData(vertices, 0, 4);
-
-    initializeQuad(mesh);
-    SAFE_RELEASE(mesh);
+        effect = __formEffect;
+    }
+    return effect;
 }
 
 void Form::setNode(Node* node)
 {
-    _node = node;
-        
-    if (_node)
+    // If the user wants a custom node then we need to create a 3D quad
+    if (node && node != _node)
     {
         // Set this Form up to be 3D by initializing a quad.
-        setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
-        _node->setModel(_quad);
+        float x2 = _bounds.width;
+        float y2 = _bounds.height;
+        float vertices[] =
+        {
+            0, y2, 0,   0, _v1,
+            0, 0, 0,    0, 0,
+            x2, y2, 0,  _u2, _v1,
+            x2, 0, 0,   _u2, 0
+        };
+        VertexFormat::Element elements[] =
+        {
+            VertexFormat::Element(VertexFormat::POSITION, 3),
+            VertexFormat::Element(VertexFormat::TEXCOORD0, 2)
+        };
+        Mesh* mesh = Mesh::createMesh(VertexFormat(elements, 2), 4, false);
+        GP_ASSERT(mesh);
+        mesh->setPrimitiveType(Mesh::TRIANGLE_STRIP);
+        mesh->setVertexData(vertices, 0, 4);
+
+        _nodeQuad = Model::create(mesh);
+        SAFE_RELEASE(mesh);
+        GP_ASSERT(_nodeQuad);
+
+        // Create the effect and material
+        Effect* effect = createEffect();
+        GP_ASSERT(effect);
+        _nodeMaterial = Material::create(effect);
+
+        GP_ASSERT(_nodeMaterial);
+        _nodeQuad->setMaterial(_nodeMaterial);
+        _nodeMaterial->release();
+        node->setModel(_nodeQuad);
+        _nodeQuad->release();
+
+        // Bind the WorldViewProjection matrix.
+        _nodeMaterial->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
+
+        // Bind the texture from the framebuffer and set the texture to clamp
+        Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
+        GP_ASSERT(sampler);
+        sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
+        _nodeMaterial->getParameter("u_texture")->setValue(sampler);
+        sampler->release();
+
+        RenderState::StateBlock* rsBlock = _nodeMaterial->getStateBlock();
+        rsBlock->setDepthWrite(true);
+        rsBlock->setBlend(true);
+        rsBlock->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
+        rsBlock->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
     }
+    _node = node;
 }
 
-void Form::update()
+void Form::update(float elapsedTime)
 {
     if (isDirty())
     {
@@ -383,8 +445,7 @@ void Form::update()
         y = 0;
         _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
-        // Calculate the absolute viewport bounds.
-        // Absolute bounds minus border and padding.
+        // Calculate the absolute viewport bounds. Absolute bounds minus border and padding.
         const Theme::Border& border = getBorder(_state);
         const Theme::Padding& padding = getPadding();
 
@@ -395,9 +456,7 @@ void Form::update()
 
         _viewportBounds.set(x, y, width, height);
 
-        // Calculate the clip area.
-        // Absolute bounds, minus border and padding,
-        // clipped to the parent container's clip area.
+        // Calculate the clip area. Absolute bounds, minus border and padding, clipped to the parent container's clip area.
         clipX2 = clip.x + clip.width;
         x2 = x + width;
         if (x2 > clipX2)
@@ -465,20 +524,15 @@ void Form::update()
 
 void Form::draw()
 {
-    /*
-    The first time a form is drawn, its contents are rendered into a framebuffer.
-    The framebuffer will only be drawn into again when the contents of the form change.
-
-    If this form has a node then it's a 3D form and the framebuffer will be used
-    to texture a quad.  The quad will be given the same dimensions as the form and
-    must be transformed appropriately by the user, unless they call setQuad() themselves.
-
-    On the other hand, if this form has not been set on a node, SpriteBatch will be used
-    to render the contents of the frambuffer directly to the display.
-    */
-
-    // Check whether this form has changed since the last call to draw()
-    // and if so, render into the framebuffer.
+    // The first time a form is drawn, its contents are rendered into a framebuffer.
+    // The framebuffer will only be drawn into again when the contents of the form change.
+    // If this form has a node then it's a 3D form and the framebuffer will be used
+    // to texture a quad.  The quad will be given the same dimensions as the form and
+    // must be transformed appropriately by the user, unless they call setQuad() themselves.
+    // On the other hand, if this form has not been set on a node, SpriteBatch will be used
+    // to render the contents of the frambuffer directly to the display.
+
+    // Check whether this form has changed since the last call to draw() and if so, render into the framebuffer.
     if (isDirty())
     {
         GP_ASSERT(_frameBuffer);
@@ -500,19 +554,20 @@ void Form::draw()
         game->setViewport(prevViewport);
     }
 
+    // Draw either with a 3D quad or sprite batch
     if (_node)
     {
-        GP_ASSERT(_quad);
-        _quad->draw();
+         // If we have the node set, then draw a 3D quad model
+        _nodeQuad->draw();
     }
     else
     {
+        // Otherwise we draw the framebuffer in ortho space with a spritebatch.
         if (!_spriteBatch)
         {
             _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
             GP_ASSERT(_spriteBatch);
         }
-
         _spriteBatch->begin();
         _spriteBatch->draw(_bounds.x, _bounds.y, 0, _bounds.width, _bounds.height, 0, _v1, _u2, 0, Vector4::one());
         _spriteBatch->end();
@@ -524,39 +579,6 @@ const char* Form::getType() const
     return "form";
 }
 
-void Form::initializeQuad(Mesh* mesh)
-{
-    // Release current model.
-    SAFE_RELEASE(_quad);
-
-    // Create the model.
-    _quad = Model::create(mesh);
-
-    // Create the material.
-    Material* material = _quad->setMaterial("res/shaders/textured-unlit.vert", "res/shaders/textured-unlit.frag");
-    GP_ASSERT(material);
-
-    // Set the common render state block for the material.
-    GP_ASSERT(_theme);
-    GP_ASSERT(_theme->getSpriteBatch());
-    RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
-    GP_ASSERT(stateBlock);
-    stateBlock->setDepthWrite(true);
-    stateBlock->setDepthTest(false);
-    material->setStateBlock(stateBlock);
-
-    // Bind the WorldViewProjection matrix.
-    material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
-
-    // Bind the texture.
-    Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
-    GP_ASSERT(sampler);
-    sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
-    material->getParameter("u_diffuseTexture")->setValue(sampler);
-
-    SAFE_RELEASE(sampler);
-}
-
 bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     // Check for a collision with each Form in __forms.
@@ -604,7 +626,6 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
             }
         }
     }
-
     return eventConsumed;
 }
 
@@ -621,7 +642,6 @@ bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
                 return true;
         }
     }
-
     return false;
 }
 
@@ -677,7 +697,6 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
             }
         }
     }
-
     return eventConsumed;
 }
 
@@ -698,14 +717,11 @@ bool Form::projectPoint(int x, int y, Vector3* point)
         Ray ray;
         camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
 
-        // Find the quad's plane.
-        // We know its normal is the quad's forward vector.
+        // Find the quad's plane.  We know its normal is the quad's forward vector.
         Vector3 normal = _node->getForwardVectorWorld();
 
-        // To get the plane's distance from the origin,
-        // we'll find the distance from the plane defined
-        // by the quad's forward vector and one of its points
-        // to the plane defined by the same vector and the origin.
+        // To get the plane's distance from the origin, we'll find the distance from the plane defined
+        // by the quad's forward vector and one of its points to the plane defined by the same vector and the origin.
         const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
         const float d = -(a*min.x) - (b*min.y) - (c*min.z);
         const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
@@ -715,8 +731,7 @@ bool Form::projectPoint(int x, int y, Vector3* point)
         float collisionDistance = ray.intersects(plane);
         if (collisionDistance != Ray::INTERSECTS_NONE)
         {
-            // Multiply the ray's direction vector by collision distance
-            // and add that to the ray's origin.
+            // Multiply the ray's direction vector by collision distance and add that to the ray's origin.
             point->set(ray.getOrigin() + collisionDistance*ray.getDirection());
 
             // Project this point into the plane.
@@ -726,13 +741,11 @@ bool Form::projectPoint(int x, int y, Vector3* point)
             return true;
         }
     }
-
     return false;
 }
 
 unsigned int Form::nextPowerOfTwo(unsigned int v)
 {
-
     if (!((v & (v - 1)) == 0))
     {
         v--;

+ 13 - 32
gameplay/src/Form.h

@@ -78,7 +78,12 @@ public:
      * @return A form with the given ID, or null if one was not found.
      */
     static Form* getForm(const char* id);
-
+    
+    /**
+     * Gets the theme for the form.
+     *
+     * @return The theme for the form.
+     */
     Theme* getTheme() const;
 
     /**
@@ -110,30 +115,6 @@ public:
      */
     virtual void setAutoHeight(bool autoHeight);
 
-    /**
-     * Create a 3D quad to texture with this Form.
-     *
-     * The specified points should describe a triangle strip with the first 3 points
-     * forming a triangle wound in counter-clockwise direction, with the second triangle
-     * formed from the last three points in clockwise direction.
-     *
-     * @param p1 The first point.
-     * @param p2 The second point.
-     * @param p3 The third point.
-     * @param p4 The fourth point.
-     */
-    void setQuad(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4);
-
-    /**
-     * Create a 2D quad to texture with this Form.
-     *
-     * @param x The x coordinate.
-     * @param y The y coordinate.
-     * @param width The width of the quad.
-     * @param height The height of the quad.
-     */
-    void setQuad(float x, float y, float width, float height);
-
     /**
      * Attach this form to a node.
      *
@@ -149,7 +130,7 @@ public:
     /**
      * Updates each control within this form, and positions them according to its layout.
      */
-    void update();
+    void update(float elapsedTime);
 
     /**
      * Draws this form.
@@ -229,15 +210,15 @@ private:
     bool projectPoint(int x, int y, Vector3* point);
 
     Theme* _theme;                      // The Theme applied to this Form.
-    Model* _quad;                       // Quad for rendering this Form in world-space.
-    Node* _node;                        // Node for transforming this Form in world-space.
-    FrameBuffer* _frameBuffer;          // FBO the Form is rendered into for texturing the quad.
-    Matrix _projectionMatrix;           // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
-    Matrix _defaultProjectionMatrix;
+    FrameBuffer* _frameBuffer;          // FBO the Form is rendered into for texturing the quad. 
     SpriteBatch* _spriteBatch;
-
+    Node* _node;                        // Node for transforming this Form in world-space.
+    Model* _nodeQuad;                   // Quad for rendering this Form in 3d space.
+    Material* _nodeMaterial;            // Material for rendering this Form in 3d space.
     float _u2;
     float _v1;
+    Matrix _projectionMatrix;           // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
+    Matrix _defaultProjectionMatrix;   
 };
 
 }

+ 6 - 3
gameplay/src/FrameBuffer.cpp

@@ -24,7 +24,10 @@ FrameBuffer::~FrameBuffer()
     {
         for (unsigned int i = 0; i < __maxRenderTargets; ++i)
         {
-            SAFE_RELEASE(_renderTargets[i]);
+            if (_renderTargets[i])
+            {
+                SAFE_RELEASE(_renderTargets[i]);
+            }
         }
         SAFE_DELETE_ARRAY(_renderTargets);
     }
@@ -103,7 +106,7 @@ FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
     {
         FrameBuffer* fb = *it;
         GP_ASSERT(fb);
-        if (strcmp(id, fb->getID()) == 0)
+        if (strcmp(id, fb->getId()) == 0)
         {
             return fb;
         }
@@ -111,7 +114,7 @@ FrameBuffer* FrameBuffer::getFrameBuffer(const char* id)
     return NULL;
 }
 
-const char* FrameBuffer::getID() const
+const char* FrameBuffer::getId() const
 {
     return _id.c_str();
 }

+ 1 - 1
gameplay/src/FrameBuffer.h

@@ -50,7 +50,7 @@ public:
      *
      * @return The ID of this FrameBuffer.
      */
-    const char* getID() const;
+    const char* getId() const;
 
     /**
      * Gets the width of the frame buffer.

+ 13 - 45
gameplay/src/Game.cpp

@@ -20,8 +20,7 @@ Game::Game()
     : _initialized(false), _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
       _clearDepth(1.0f), _clearStencil(0), _properties(NULL),
-      _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL), 
-      _gamepadCount(0), _gamepads(NULL)
+      _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
 {
     GP_ASSERT(__gameInstance == NULL);
     __gameInstance = this;
@@ -149,16 +148,13 @@ void Game::shutdown()
 
         Platform::signalShutdown();
         finalize();
-
-        if (_gamepads)
+        
+        for (std::vector<Gamepad*>::iterator itr = _gamepads.begin(); itr != _gamepads.end(); itr++)
         {
-            for (unsigned int i = 0; i < _gamepadCount; i++)
-            {
-                SAFE_DELETE(_gamepads[i]);
-            }
-            SAFE_DELETE_ARRAY(_gamepads);
+            SAFE_DELETE((*itr));
         }
-
+        _gamepads.clear();
+        
         _animationController->finalize();
         SAFE_DELETE(_animationController);
 
@@ -243,13 +239,7 @@ void Game::frame()
     
         // Update the physics.
         _physicsController->update(elapsedTime);
-
-        if (_gamepads)
-        {
-            for (unsigned int i = 0; i < _gamepadCount; i++)
-                _gamepads[i]->update();
-        }
-
+        
         // Application Update.
         update(elapsedTime);
 
@@ -259,12 +249,6 @@ void Game::frame()
         // Graphics Rendering.
         render(elapsedTime);
         
-        if (_gamepads)
-        {
-            for (unsigned int i = 0; i < _gamepadCount; i++)
-                _gamepads[i]->render();
-        }
-
         // Update FPS.
         ++_frameCount;
         if ((Game::getGameTime() - _frameLastFPS) >= 1000)
@@ -354,7 +338,7 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 {
 }
 
-void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int index)
+void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }
 
@@ -439,30 +423,14 @@ bool Game::TimeEvent::operator<(const TimeEvent& v) const
     return time > v.time;
 }
 
-Gamepad* Game::createGamepad(const char* gamepadFormPath)
+Gamepad* Game::createGamepad(const char* gamepadId, const char* gamepadFormPath)
 {
     GP_ASSERT(gamepadFormPath);
 
-    Gamepad* gamepad = new Gamepad(gamepadFormPath);
+    Gamepad* gamepad = new Gamepad(gamepadId, gamepadFormPath);
     GP_ASSERT(gamepad);
 
-    if (!_gamepads)
-    {
-        _gamepadCount++;
-        _gamepads = new Gamepad*[_gamepadCount];
-        _gamepads[0] = gamepad;
-    }
-    else
-    {
-        int oldSize = _gamepadCount;
-        _gamepadCount++;
-        Gamepad** tempGamepads = new Gamepad*[_gamepadCount];
-        memcpy(tempGamepads, _gamepads, sizeof(Gamepad*) * oldSize);
-        tempGamepads[oldSize] = gamepad;
-        
-        SAFE_DELETE_ARRAY(_gamepads);
-        _gamepads = tempGamepads;
-    }
+    _gamepads.push_back(gamepad);
 
     return gamepad;
 }
@@ -473,13 +441,13 @@ void Game::loadGamepad()
     {
         // Check if there is a virtual keyboard included in the .config file.
         // If there is, try to create it and assign it to "player one".
-        Properties* gamepadProperties = _properties->getNamespace("gamepad", true);
+        Properties* gamepadProperties = _properties->getNamespace("gamepads", true);
         if (gamepadProperties && gamepadProperties->exists("form"))
         {
             const char* gamepadFormPath = gamepadProperties->getString("form");
             GP_ASSERT(gamepadFormPath);
 
-            Gamepad* gamepad = createGamepad(gamepadFormPath);
+            Gamepad* gamepad = createGamepad(gamepadProperties->getId(), gamepadFormPath);
             GP_ASSERT(gamepad);
         }
     }

+ 22 - 17
gameplay/src/Game.h

@@ -25,7 +25,7 @@ class Game
 {
 
 public:
-
+    
     /**
      * The game states.
      */
@@ -326,11 +326,10 @@ public:
     /**
      * Gamepad callback on gamepad events.
      *
-     * @param evt The gamepad event that occured.
-     * @param gamepad the gamepad the event occured on
-     * @param index The joystick or button index that triggered the event.
+     * @param evt The gamepad event that occurred.
+     * @param gamepad the gamepad the event occurred on
      */
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int index);
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
 
     /**
      * Sets muli-touch is to be enabled/disabled. Default is disabled.
@@ -363,21 +362,20 @@ public:
      * @param cookie The cookie data that the time event will contain.
      */
     void schedule(float timeOffset, TimeListener* timeListener, void* cookie = 0);
-
-    /** 
-     * Creates a Gamepad object from a .form file.
-     *
-     * @param playerIndex
-     * @param formPath
+    
+    /**
+     * Gets the number of gamepad's connected to the game.
+     * 
+     * @return The number of gamepad's connected to the game.
      */
-    Gamepad* createGamepad(const char* gamepadFormPath);
+    inline unsigned int getGamepadCount() const;
 
     /**
-     * Gets the gamepad for the specified player index.
+     * Gets the gamepad at the specified index.
      *
-     * @param playerIndex The player index to get the gamepad for (0 <= playerIndex <= 3) 
+     * @param index The index to get the gamepad for (0 <= index <= Game::getGamepadCount) 
      */
-    inline Gamepad* getGamepad(unsigned int playerIndex = 0);
+    inline Gamepad* getGamepad(unsigned int index = 0) const;
 
 protected:
 
@@ -485,6 +483,14 @@ private:
      */
     void loadConfig();
 
+    /** 
+     * Creates a Gamepad object from a .form file.
+     *
+     * @param playerIndex
+     * @param formPath
+     */
+    Gamepad* createGamepad(const char* gamepadId, const char* gamepadFormPath);
+
     /**
      * Loads a gamepad from the configuration file.
      */
@@ -508,8 +514,7 @@ private:
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
     AudioListener* _audioListener;              // The audio listener in 3D space.
-    unsigned int _gamepadCount;
-    Gamepad** _gamepads;
+    std::vector<Gamepad*> _gamepads;
     std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> >* _timeEvents; // Contains the scheduled time events.
 
     // Note: Do not add STL object member variables on the stack; this will cause false memory leaks to be reported.

+ 9 - 5
gameplay/src/Game.inl

@@ -77,14 +77,18 @@ inline void Game::displayKeyboard(bool display)
     Platform::displayKeyboard(display);
 }
 
-inline Gamepad* Game::getGamepad(unsigned int playerIndex)
+inline unsigned int Game::getGamepadCount() const
 {
-    GP_ASSERT(playerIndex < _gamepadCount);
+    return _gamepads.size();
+}
+
+inline Gamepad* Game::getGamepad(unsigned int index) const
+{
+    GP_ASSERT(index < _gamepads.size());
 
-    if (_gamepads)
-        return _gamepads[playerIndex];
+    if (!_gamepads.empty())
+        return _gamepads[index];
     else
         return NULL;
 }
-
 }

+ 46 - 96
gameplay/src/Gamepad.cpp

@@ -1,49 +1,31 @@
 #include "Base.h"
 #include "Gamepad.h"
-#include "Joystick.h"
 #include "Game.h"
 
 namespace gameplay
 {
 
-Gamepad::Gamepad()
-    : _playerIndex(-1), _joystickValues(NULL), _joystickCount(0), _buttonStates(NULL), _buttonCount(0), _gamepadForm(NULL)
+Gamepad::Gamepad(const char* id)
+    : _id(id), _gamepadForm(NULL)
 {
 }
 
-Gamepad::Gamepad(const char* formPath)
-    : _playerIndex(-1), _joystickValues(NULL), _joystickCount(0), _buttonStates(NULL), _buttonCount(0), _gamepadForm(NULL)
+Gamepad::Gamepad(const char* id, const char* formPath)
+    : _id(id), _gamepadForm(NULL)
 {
     GP_ASSERT(formPath);
 
     _gamepadForm = Form::create(formPath);
     GP_ASSERT(_gamepadForm);
-
+    
     _gamepadForm->setConsumeInputEvents(false);
 
-    getGamepadControls(_gamepadForm);
-
-    if (_joystickCount > 0)
-    {
-        _joystickValues = new Vector2*[_joystickCount];
-        for (unsigned int i = 0; i < _joystickCount; i++)
-        {
-            _joystickValues[i] = new Vector2();
-        }
-    }
-    if (_buttonCount > 0)
-    {
-        _buttonStates = new ButtonState*[_buttonCount];
-        for (unsigned int i = 0; i < _buttonCount; i++)
-        {
-            _buttonStates[i] = new ButtonState();
-        }
-    }
+    bindGamepadControls(_gamepadForm);
 }
 
-void Gamepad::getGamepadControls(Form* form)
+void Gamepad::bindGamepadControls(Container* container)
 {
-    std::vector<Control*> controls = form->getControls();
+    std::vector<Control*> controls = container->getControls();
     std::vector<Control*>::iterator itr = controls.begin();
 
     for (; itr != controls.end(); itr++)
@@ -51,64 +33,53 @@ void Gamepad::getGamepadControls(Form* form)
         Control* control = *itr;
         GP_ASSERT(control);
 
-        if (std::strcmp("container", control->getType()) == 0)
+        if (control->isContainer())
         {
-            getGamepadControls((Form*) control);
+            bindGamepadControls((Container*) control);
         }
         else if (std::strcmp("joystick", control->getType()) == 0)
         {
-            control->addListener(this, Control::Listener::PRESS | Control::Listener::VALUE_CHANGED | Control::Listener::RELEASE);
-            Joystick* joystick = (Joystick*) control;
-            
-            if (!joystick->_gamepadJoystickIndex)
-                joystick->_gamepadJoystickIndex = new int[1];
-            
-            *joystick->_gamepadJoystickIndex = _joystickCount;
-            _joystickCount++;
+            control->addRef();
+            _joysticks.push_back((Joystick*) control);
         }
         else if (std::strcmp("button", control->getType()) == 0)
         {
-            control->addListener(this, Control::Listener::PRESS | Control::Listener::RELEASE);
-            Button* button = (Button*) control;
-            
-            if (!button->_gamepadButtonIndex)
-                button->_gamepadButtonIndex = new int[1];
-
-            *button->_gamepadButtonIndex = _buttonCount;
-            _buttonCount++;
-        }
+            control->addRef();
+            _buttons.push_back((Button*) control);
+        }   
     }
 }
 
 Gamepad::~Gamepad()
 {
-    if (_joystickValues)
+    for (std::vector<Joystick*>::iterator itr = _joysticks.begin(); itr != _joysticks.end(); itr++)
     {
-        for (unsigned int i = 0; i < _joystickCount; i++)
-            SAFE_DELETE(_joystickValues[i]);
-        SAFE_DELETE_ARRAY(_joystickValues);
+        SAFE_RELEASE((*itr));
     }
 
-    if (_buttonStates)
+    for (std::vector<Button*>::iterator itr = _buttons.begin(); itr!= _buttons.end(); itr++)
     {
-        for (unsigned int i = 0; i < _buttonCount; i++)
-           SAFE_DELETE(_buttonStates[i]);
-        SAFE_DELETE_ARRAY(_buttonStates);
+        SAFE_RELEASE((*itr));
     }
 
     if (_gamepadForm)
         SAFE_RELEASE(_gamepadForm);
 }
 
-void Gamepad::update()
+const char* Gamepad::getId() const
+{
+    return _id.c_str();
+}
+
+void Gamepad::update(float elapsedTime)
 {
     if (_gamepadForm && _gamepadForm->isEnabled())
     {
-        _gamepadForm->update();
+        _gamepadForm->update(elapsedTime);
     }
 }
 
-void Gamepad::render()
+void Gamepad::draw()
 {
     if (_gamepadForm && _gamepadForm->isEnabled())
     {
@@ -116,66 +87,45 @@ void Gamepad::render()
     }
 }
 
-void Gamepad::controlEvent(Control* control, Control::Listener::EventType evt)
+unsigned int Gamepad::getButtonCount() const
 {
-    if (_gamepadForm && _gamepadForm->isEnabled())
-    {
-        if (std::strcmp("joystick", control->getType()) == 0)
-        {
-            int joystickIndex = *(((Joystick*) control)->_gamepadJoystickIndex);
-            switch(evt)
-            {
-                case Control::Listener::PRESS:
-                case Control::Listener::VALUE_CHANGED:
-                    _joystickValues[joystickIndex]->set(((Joystick*)control)->getValue());
-                    break;
-                case Control::Listener::RELEASE:
-                    _joystickValues[joystickIndex]->set(0.0f, 0.0f);
-                    break;
-            }
-            Game::getInstance()->gamepadEvent(JOYSTICK_EVENT, this, joystickIndex);
-        }
-        else if (std::strcmp("button", control->getType()) == 0)
-        {
-            int buttonIndex = *(((Button*) control)->_gamepadButtonIndex);
-            switch(evt)
-            {
-                case Control::Listener::PRESS:
-                    *_buttonStates[buttonIndex] = BUTTON_PRESSED;
-                    break;
-                case Control::Listener::RELEASE:
-                    *_buttonStates[buttonIndex] = BUTTON_RELEASED;
-                    break;
-            }
-            Game::getInstance()->gamepadEvent(BUTTON_EVENT, this, buttonIndex);
-        }
-    }
+    return _buttons.size();
 }
 
 Gamepad::ButtonState Gamepad::getButtonState(unsigned int buttonId) const
 {
-    GP_ASSERT(buttonId < _buttonCount);
+    GP_ASSERT(buttonId < _buttons.size());
+
+    return _buttons[buttonId]->getState() == Control::ACTIVE ? BUTTON_PRESSED : BUTTON_RELEASED;
+}
 
-    return *_buttonStates[buttonId];
+unsigned int Gamepad::getJoystickCount() const
+{
+    return _joysticks.size();
 }
 
 bool Gamepad::isJoystickActive(unsigned int joystickId) const
 {
-    GP_ASSERT(joystickId < _joystickCount);
+    GP_ASSERT(joystickId < _joysticks.size());
 
-    return !(_joystickValues[joystickId]->isZero());
+    return !_joysticks[joystickId]->getValue().isZero();
 }
 
 const Vector2& Gamepad::getJoystickValue(unsigned int joystickId) const
 {
-    GP_ASSERT(joystickId < _joystickCount);
+    GP_ASSERT(joystickId < _joysticks.size());
 
-    return *_joystickValues[joystickId];
+    return _joysticks[joystickId]->getValue();
 }
 
 bool Gamepad::isVirtual() const
 {
-    return (_gamepadForm && _gamepadForm->isEnabled());
+    return true;
+}
+
+Form* Gamepad::getForm() const
+{
+    return _gamepadForm;
 }
 
 }

+ 68 - 32
gameplay/src/Gamepad.h

@@ -3,40 +3,72 @@
 
 #include "Form.h"
 #include "Button.h"
+#include "Joystick.h"
 
 namespace gameplay
 {
 
 /**
- * Defines an interface for handling gamepad support.
+ * Defines an interface for handling gamepad input.
  */
-class Gamepad : public Control::Listener
+class Gamepad
 {
     friend class Game;
+    friend class Control;
 
 public:
 
+    /**
+     *  Gamepad events.
+     */
     enum GamepadEvent
     {
-        JOYSTICK_EVENT,
-        BUTTON_EVENT
+        ATTACHED_EVENT,
+        DETACHED_EVENT,
     };
 
+    /**
+     * Gamepad button states.
+     */
     enum ButtonState
     {
         BUTTON_PRESSED = gameplay::Button::Listener::PRESS, 
         BUTTON_RELEASED = gameplay::Button::Listener::RELEASE
     };
-    
+
+    /**
+     * Gets the Gamepad's ID.
+     *
+     * @return the Gamepad's ID.
+     */
+    const char* getId() const;
+
+    /**
+     * Gets the number of button on the gamepad.
+     *
+     * @return the number of buttons on the gamepad.
+     */
+    unsigned int getButtonCount() const;
+
     /** 
      * Gets the current state of the specified button.
+     *
+     * @param buttonId The index of the button on the gamepad to get the state for.
+     * @return whether the button is currently pressed or not.
      */
     ButtonState getButtonState(unsigned int buttonId) const;
+    
+    /**
+     * Gets the number of joysticks on the gamepad.
+     *
+     * @return the number of joysticks on the gamepad.
+     */
+    unsigned int getJoystickCount() const;
 
     /**
      * Gets whether the specified joystick's state is active or not.
      * 
-     * @param joystickId The unique integer ID of the joystick to set.
+     * @param joystickId The index of the joystick on the gamepad to get state for.
      * @return Whether the given joystick is active or not.
      */
     bool isJoystickActive(unsigned int joystickId) const;
@@ -49,22 +81,39 @@ public:
      */
     const Vector2& getJoystickValue(unsigned int joystickId) const;
 
-    /** 
-     * Listener for Control events on the gamepads Joystick or Buttons.
+    /**
+     * Returns whether the gamepad is currently represented with a UI form or not.
+     *
+     * @return true if the gamepad is currently represented by a UI form; false if the gamepad is
+     *      not represented by a UI form
      */
-    void controlEvent(Control* control, Control::Listener::EventType evt);
+    bool isVirtual() const;
 
     /**
-     * Returns whether the gamepad is represented with a UI form or not.
+     * Gets the Form used to represent this gamepad.
+     *
+     * Note: What if the user decides to add gamepad controls (joysticks, buttons) to the gamepad form? How do we handle new/deleted controls?
+     *
+     * @return the Form used to represent this gamepad. NULL if the gamepad is not reprented with a Form.
      */
-    bool isVirtual() const;
+    Form* getForm() const;
+
+    /**
+     * Updates the gamepad.
+     */
+    void update(float elapsedTime);
+
+    /**
+     * Draws the gamepad if it is based on a form and if the form is enabled.
+     */
+    void draw();
 
 private:
 
     /**
      * Constructor.
      */
-    Gamepad();
+    Gamepad(const char* id);
     
     /**
      * Constructor.
@@ -72,7 +121,7 @@ private:
      *
      * @param formPath
      */ 
-    Gamepad(const char* formPath);
+    Gamepad(const char* id, const char* formPath);
 
     /**
      * Copy constructor.
@@ -83,28 +132,15 @@ private:
      * Destructor.
      */
     virtual ~Gamepad();
-    
-    /** 
-     * Gets the Joystick and Button Control object's from the specified form.
-     */
-    void getGamepadControls(Form* form);
 
-    /**
-     * Updates the gamepad.
-     */
-    void update();
-
-    /**
-     * Renders the gamepad if it is based on a form and if the form is enabled.
+    /** 
+     * Binds the Joystick and Button Control object's from the specified container.
      */
-    void render();
-
-    int _playerIndex;
-    Vector2** _joystickValues;
-    unsigned int _joystickCount;
-    ButtonState** _buttonStates;
-    unsigned int _buttonCount;
+    void bindGamepadControls(Container* container);
 
+    std::string _id;
+    std::vector<Joystick*> _joysticks;
+    std::vector<Button*> _buttons;
     Form* _gamepadForm;
 };
 

+ 1 - 3
gameplay/src/Joystick.cpp

@@ -4,7 +4,7 @@
 namespace gameplay
 {
 
-Joystick::Joystick() : _absolute(true), _gamepadJoystickIndex(NULL)
+Joystick::Joystick() : _absolute(true)
 {
 }
 
@@ -14,8 +14,6 @@ Joystick::Joystick(const Joystick& copy)
 
 Joystick::~Joystick()
 {
-    if (_gamepadJoystickIndex)
-        SAFE_DELETE(_gamepadJoystickIndex);
 }
 
 Joystick* Joystick::create(const char* id, Theme::Style* style)

+ 0 - 1
gameplay/src/Joystick.h

@@ -155,7 +155,6 @@ private:
     Vector2 _displacement;
     Vector2 _value;
     Rectangle _region;
-    int* _gamepadJoystickIndex;
 };
 
 }

+ 9 - 1
gameplay/src/MeshSkin.cpp

@@ -79,7 +79,15 @@ MeshSkin* MeshSkin::clone(NodeCloneContext &context) const
             skin->_rootNode = _rootNode->cloneRecursive(context);
         }
         
-        Node* node = skin->_rootNode->findNode(_rootJoint->getId());
+        Node* node = NULL;
+        if (strcmp(skin->_rootNode->getId(), _rootJoint->getId()) == 0)
+        {
+            node = skin->_rootNode;
+        }
+        else
+        {
+            node = skin->_rootNode->findNode(_rootJoint->getId());
+        }
         GP_ASSERT(node);
         skin->_rootJoint = static_cast<Joint*>(node);
         for (unsigned int i = 0; i < jointCount; ++i)

+ 2 - 2
gameplay/src/RenderTarget.cpp

@@ -51,7 +51,7 @@ RenderTarget* RenderTarget::getRenderTarget(const char* id)
     {
         RenderTarget* dst = *it;
         GP_ASSERT(dst);
-        if (strcmp(id, dst->getID()) == 0)
+        if (strcmp(id, dst->getId()) == 0)
         {
             return dst;
         }
@@ -60,7 +60,7 @@ RenderTarget* RenderTarget::getRenderTarget(const char* id)
     return NULL;
 }
 
-const char* RenderTarget::getID() const
+const char* RenderTarget::getId() const
 {
     return _id.c_str();
 }

+ 1 - 1
gameplay/src/RenderTarget.h

@@ -42,7 +42,7 @@ public:
      *
      * @return The ID of this RenderTarget.
      */
-    const char* getID() const;
+    const char* getId() const;
 
     /**
      * Get the backing texture of this RenderTarget.

+ 1 - 0
gameplay/src/Scene.cpp

@@ -32,6 +32,7 @@ Scene::~Scene()
 
     // Remove all nodes from the scene
     removeAllNodes();
+    SAFE_DELETE(_debugBatch);
 }
 
 Scene* Scene::createScene()

+ 1 - 1
gameplay/src/SpriteBatch.cpp

@@ -62,7 +62,7 @@ SpriteBatch::~SpriteBatch()
     SAFE_DELETE(_batch);
     if (!_customEffect)
     {
-        if (__spriteEffect->getRefCount() == 1)
+        if (__spriteEffect && __spriteEffect->getRefCount() == 1)
         {
             __spriteEffect->release();
             __spriteEffect = NULL;

+ 1 - 0
gameplay/src/Transform.cpp

@@ -853,6 +853,7 @@ void Transform::cloneInto(Transform* transform, NodeCloneContext &context) const
     transform->_scale.set(_scale);
     transform->_rotation.set(_rotation);
     transform->_translation.set(_translation);
+    transform->dirty(DIRTY_TRANSLATION | DIRTY_ROTATION | DIRTY_SCALE);
 }
 
 void Transform::applyAnimationValueRotation(AnimationValue* value, unsigned int index, float blendWeight)