Kaynağa Gözat

Fixing a bug in Frustum where planes were being calculated incorrectly.
Frustum culling in ParticleEmitter.
First steps towards optimizing forms.

Note to self: There are unfinished changes in this commit. Don't push this yet!

Adam Blake 13 yıl önce
ebeveyn
işleme
37374d81cd

+ 1 - 1
gameplay/.cproject

@@ -17,7 +17,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.debug.1686166742" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.545743487" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.158841187" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="org.eclipse.cdt.build.core.internal.builder.159190287" superClass="org.eclipse.cdt.build.core.internal.builder"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="org.eclipse.cdt.build.core.internal.builder.159190287" keepEnvironmentInBuildfile="false" name="CDT Internal Builder" superClass="org.eclipse.cdt.build.core.internal.builder"/>
 							<tool id="com.qnx.qcc.tool.compiler.96907942" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1765481355" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.security.311918799" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>

+ 1 - 2
gameplay/src/AbsoluteLayout.cpp

@@ -47,10 +47,9 @@ namespace gameplay
         {
             Control* control = controls[i];
 
-            align(control, container);
-
             if (control->isDirty() || control->isContainer())
             {
+                align(control, container);
                 control->update(container->getClip(), Vector2::zero());
             }
         }

+ 35 - 8
gameplay/src/Container.cpp

@@ -11,6 +11,7 @@
 #include "RadioButton.h"
 #include "Slider.h"
 #include "TextBox.h"
+#include "Game.h"
 
 namespace gameplay
 {
@@ -240,21 +241,21 @@ namespace gameplay
 
         _layout->update(this);
     }
-
+    /*
     void Container::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset)
     {
         // First draw our own border.
-        Control::drawBorder(spriteBatch, clip, offset);
+        Control::drawBorder(spriteBatch, clip);
 
         // Now call drawBorder on all controls within this container.
         std::vector<Control*>::const_iterator it;
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            if (_layout->getType() == Layout::LAYOUT_SCROLL)
-                control->drawBorder(spriteBatch, _clip, ((ScrollLayout*)_layout)->_scrollPosition);
-            else
-                control->drawBorder(spriteBatch, _clip, Vector2::zero());
+            if (control->isDirty())
+            {
+                control->drawBorder(spriteBatch, _clip);
+            }
         }
     }
 
@@ -264,7 +265,8 @@ namespace gameplay
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawImages(spriteBatch, _clip);
+            if (control->isDirty())
+                control->drawImages(spriteBatch, _clip);
         }
 
         _dirty = false;
@@ -276,7 +278,32 @@ namespace gameplay
         for (it = _controls.begin(); it < _controls.end(); it++)
         {
             Control* control = *it;
-            control->drawText(_clip);
+            if (control->isDirty())
+                control->drawText(_clip);
+        }
+
+        _dirty = false;
+    }*/
+
+    void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
+    {
+        GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
+        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        GL_ASSERT( glScissor(_absoluteBounds.x, Game::getInstance()->getHeight() - _absoluteBounds.y - _absoluteBounds.height,
+            _absoluteBounds.width, _absoluteBounds.height) );
+        GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
+        GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
+
+        Control::drawBorder(spriteBatch, clip);
+
+        std::vector<Control*>::const_iterator it;
+        for (it = _controls.begin(); it < _controls.end(); it++)
+        {
+            Control* control = *it;
+            //if (control->isDirty())
+            {
+                control->draw(spriteBatch, _clip);
+            }
         }
 
         _dirty = false;

+ 5 - 3
gameplay/src/Container.h

@@ -166,7 +166,7 @@ protected:
      * @param spriteBatch The sprite batch containing this container's border images.
      * @param clip The clipping rectangle of this container's parent container.
      */
-    void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
+    //void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
 
     /**
      * Draws the icons of all controls within this container.
@@ -174,14 +174,14 @@ protected:
      * @param spriteBatch The sprite batch containing this control's icons.
      * @param clip The clipping rectangle of this container's parent container.
      */
-    virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+    //virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     /**
      * Draws the text of all controls within this container.
      *
      * @param clip The clipping rectangle of this container's parent container.
      */
-    virtual void drawText(const Rectangle& clip);
+    //virtual void drawText(const Rectangle& clip);
 
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
@@ -244,6 +244,8 @@ protected:
 private:
 
     Container(const Container& copy);
+
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip);
 };
 
 }

+ 9 - 1
gameplay/src/Control.cpp

@@ -735,7 +735,7 @@ namespace gameplay
         _opacity = getOpacity(_state);
     }
 
-    void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset)
+    void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
         if (!_skin || _bounds.width <= 0 || _bounds.height <= 0)
             return;
@@ -802,6 +802,14 @@ namespace gameplay
     {
     }
 
+    void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
+    {
+        drawBorder(spriteBatch, clip);
+        drawImages(spriteBatch, clip);
+        drawText(clip);
+        _dirty = false;
+    }
+
     bool Control::isDirty()
     {
         return _dirty;

+ 3 - 2
gameplay/src/Control.h

@@ -888,9 +888,10 @@ private:
      *
      * @param spriteBatch The sprite batch containing this control's border images.
      * @param clip The clipping rectangle of this control's parent container.
-     * @param offset Layout-computed positioning offset to add to the control's position.
      */
-    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
+    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip);
     
     bool _styleOverridden;
     Theme::Skin* _skin;

+ 186 - 2
gameplay/src/Font.cpp

@@ -173,7 +173,13 @@ void Font::begin()
 {
     _batch->begin();
 }
+/*
+Font::TextBatch* Font::getTextBatch(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify,
+    bool wrap, bool rightToLeft, const Rectangle* clip)
+{
 
+}
+*/
 void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 {
     if (size == 0)
@@ -311,8 +317,6 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         hAlign = ALIGN_LEFT;
     }
 
-    token = text;
-
     // For alignments other than top-left, need to calculate the y position to begin drawing from
     // and the starting x position of each line.  For right-to-left text, need to determine
     // the number of characters on each line.
@@ -1010,6 +1014,186 @@ void Font::measureText(const char* text, const Rectangle& clip, unsigned int siz
     }
 }
 
+void Font::getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
+        std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths)
+{
+    float scale = (float)size / _size;
+
+    Justify vAlign = static_cast<Justify>(justify & 0xF0);
+    if (vAlign == 0)
+    {
+        vAlign = ALIGN_TOP;
+    }
+
+    Justify hAlign = static_cast<Justify>(justify & 0x0F);
+    if (hAlign == 0)
+    {
+        hAlign = ALIGN_LEFT;
+    }
+
+    const char* token = text;
+    const float areaHeight = area.height - size;
+
+    // For alignments other than top-left, need to calculate the y position to begin drawing from
+    // and the starting x position of each line.  For right-to-left text, need to determine
+    // the number of characters on each line.
+    if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
+    {
+        int lineWidth = 0;
+        int delimWidth = 0;
+
+        if (wrap)
+        {
+            // Go a word at a time.
+            bool reachedEOF = false;
+            unsigned int lineLength = 0;
+            while (token[0] != 0)
+            {
+                unsigned int tokenWidth = 0;
+
+                // Handle delimiters until next token.
+                char delimiter = token[0];
+                while (delimiter == ' ' ||
+                       delimiter == '\t' ||
+                       delimiter == '\r' ||
+                       delimiter == '\n' ||
+                       delimiter == 0)
+                {
+                    switch (delimiter)
+                    {
+                        case ' ':
+                            delimWidth += size >> 1;
+                            lineLength++;
+                            break;
+                        case '\r':
+                        case '\n':
+                            *yPosition += size;
+
+                            if (lineWidth > 0)
+                            {
+                                addLineInfo(area, lineWidth, lineLength, hAlign, xPositions, lineLengths, rightToLeft);
+                            }
+
+                            lineWidth = 0;
+                            lineLength = 0;
+                            delimWidth = 0;
+                            break;
+                        case '\t':
+                            delimWidth += (size >> 1)*4;
+                            lineLength++;
+                            break;
+                        case 0:
+                            reachedEOF = true;
+                            break;
+                    }
+
+                    if (reachedEOF)
+                    {
+                        break;
+                    }
+
+                    token++;
+                    delimiter = token[0];
+                }
+
+                if (reachedEOF || token == NULL)
+                {
+                    break;
+                }
+
+                unsigned int tokenLength = strcspn(token, " \r\n\t");
+                tokenWidth += getTokenWidth(token, tokenLength, size, scale);
+
+                // Wrap if necessary.
+                if (lineWidth + tokenWidth + delimWidth > area.width)
+                {
+                    *yPosition += size;
+
+                    // Push position of current line.
+                    if (lineLength)
+                    {
+                        addLineInfo(area, lineWidth, lineLength-1, hAlign, xPositions, lineLengths, rightToLeft);
+                    }
+                    else
+                    {
+                        addLineInfo(area, lineWidth, tokenLength, hAlign, xPositions, lineLengths, rightToLeft);
+                    }
+
+                    // Move token to the next line.
+                    lineWidth = 0;
+                    lineLength = 0;
+                    delimWidth = 0;
+                }
+                else
+                {
+                    lineWidth += delimWidth;
+                    delimWidth = 0;
+                }
+
+                lineWidth += tokenWidth;
+                lineLength += tokenLength;
+                token += tokenLength;
+            }
+
+            // Final calculation of vertical position.
+            int textHeight = *yPosition - area.y;
+            int vWhiteSpace = areaHeight - textHeight;
+            if (vAlign == ALIGN_VCENTER)
+            {
+                *yPosition = area.y + vWhiteSpace / 2;
+            }
+            else if (vAlign == ALIGN_BOTTOM)
+            {
+                *yPosition = area.y + vWhiteSpace;
+            }
+
+            // Calculation of final horizontal position.
+            addLineInfo(area, lineWidth, lineLength, hAlign, xPositions, lineLengths, rightToLeft);
+        }
+        else
+        {
+            // Go a line at a time.
+            while (token[0] != 0)
+            {
+                char delimiter = token[0];
+                while (delimiter == '\n')
+                {
+                    *yPosition += size;
+                    ++token;
+                    delimiter = token[0];
+                }
+
+                unsigned int tokenLength = strcspn(token, "\n");
+                if (tokenLength == 0)
+                {
+                    tokenLength = strlen(token);
+                }
+
+                int lineWidth = getTokenWidth(token, tokenLength, size, scale);
+                addLineInfo(area, lineWidth, tokenLength, hAlign, xPositions, lineLengths, rightToLeft);
+
+                token += tokenLength;
+            }
+
+            int textHeight = *yPosition - area.y;
+            int vWhiteSpace = areaHeight - textHeight;
+            if (vAlign == ALIGN_VCENTER)
+            {
+                *yPosition = area.y + vWhiteSpace / 2;
+            }
+            else if (vAlign == ALIGN_BOTTOM)
+            {
+                *yPosition = area.y + vWhiteSpace;
+            }
+        }
+
+        if (vAlign == ALIGN_TOP)
+        {
+            *yPosition = area.y;
+        }
+    }
+}
+
 int Font::getIndexAtLocation(const char* text, const Rectangle& area, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
                                       Justify justify, bool wrap, bool rightToLeft)
 {

+ 21 - 0
gameplay/src/Font.h

@@ -76,6 +76,13 @@ public:
         float uvs[4];
     };
 
+    class TextBatch
+    {
+    public:
+        SpriteBatch::SpriteVertex* _vertices;
+        unsigned int _vertexCount;
+    };
+
     /**
      * Creates a font from the given bundle.
      *
@@ -125,6 +132,17 @@ public:
      */
     void end();
 
+    /**
+     * Compute vertex coordinates and UVs for a given string.
+     */
+    TextBatch* getTextBatch(const char* text, const Rectangle& area, const Vector4& color, unsigned int size = 0,
+        Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false, const Rectangle* clip = NULL);
+
+    /**
+     * Draw a string from a precomputed StringBatch.
+     */
+    void drawTextBatch(TextBatch* textBatch);
+
     /**
      * Draws the specified text in a solid color, with a scaling factor.
      *
@@ -225,6 +243,9 @@ private:
      */
     ~Font();
 
+    void getMeasurementInfo(const char* text, const Rectangle& area, unsigned int size, Justify justify, bool wrap, bool rightToLeft,
+        std::vector<int>* xPositions, int* yPosition, std::vector<unsigned int>* lineLengths);
+
     int getIndexOrLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
                                     const int destIndex = -1, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 

+ 47 - 49
gameplay/src/Form.cpp

@@ -13,7 +13,7 @@ namespace gameplay
 {
     static std::vector<Form*> __forms;
 
-    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL)
+    Form::Form() : _theme(NULL), _quad(NULL), _node(NULL), _frameBuffer(NULL), _spriteBatch(NULL)
     {
     }
 
@@ -97,6 +97,15 @@ namespace gameplay
         // Add all the controls to the form.
         form->addControls(theme, formProperties);
 
+        // Create the frame buffer.
+        form->_frameBuffer = FrameBuffer::create(form->_id.c_str());
+        RenderTarget* rt = RenderTarget::create(form->_id.c_str(), form->_bounds.width, form->_bounds.height);
+        form->_frameBuffer->setRenderTarget(rt);
+        SAFE_RELEASE(rt);
+
+        Matrix::createOrthographicOffCenter(0, form->_bounds.width, form->_bounds.height, 0, 0, 1, &form->_projectionMatrix);
+        form->_theme->setProjectionMatrix(form->_projectionMatrix);
+
         SAFE_DELETE(properties);
 
         __forms.push_back(form);
@@ -136,15 +145,11 @@ namespace gameplay
     void Form::setNode(Node* node)
     {
         _node = node;
-
-        if (_node && !_quad)
+        
+        if (_node)
         {
-            // Set this Form up to be 3D by initializing a quad, projection matrix and viewport.
+            // Set this Form up to be 3D by initializing a quad.
             setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
-
-            Matrix::createOrthographicOffCenter(0, _bounds.width, _bounds.height, 0, 0, 1, &_projectionMatrix);
-            _theme->setProjectionMatrix(_projectionMatrix);
-            
             _node->setModel(_quad);
         }
     }
@@ -167,33 +172,43 @@ namespace gameplay
         // On the other hand, if this form has not been set on a node it will render
         // directly to the display.
 
-        if (_node)
+        //
+
+        // Check whether this form has changed since the last call to draw()
+        // and if so, render into the framebuffer.
+        if (isDirty())
         {
-            // Check whether this form has changed since the last call to draw()
-            // and if so, render into the framebuffer.
-            if (isDirty())
-            {
-                _frameBuffer->bind();
+            _frameBuffer->bind();
 
-                Game* game = Game::getInstance();
-                Rectangle prevViewport = game->getViewport();
-                
-                game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
+            Game* game = Game::getInstance();
+            Rectangle prevViewport = game->getViewport();
+            game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
+            //game->clear(Game::CLEAR_COLOR_DEPTH, Vector4(0, 0, 0, 0), 1, 0);
 
-                draw(_theme->getSpriteBatch(), _clip);
+            draw(_theme->getSpriteBatch(), _clip);
 
-                // Rebind the default framebuffer and game viewport.
-                FrameBuffer::bindDefault();
+            // Rebind the default framebuffer and game viewport.
+            FrameBuffer::bindDefault();
 
-                // restore the previous game viewport
-                game->setViewport(prevViewport);
-            }
+            // restore the previous game viewport
+            game->setViewport(prevViewport);
+        }
 
+        if (_node)
+        {
             _quad->draw();
         }
         else
         {
-            draw(_theme->getSpriteBatch(), _clip);
+            if (!_spriteBatch)
+            {
+                _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
+                _spriteBatch->setProjectionMatrix(_projectionMatrix);
+            }
+
+            _spriteBatch->begin();
+            _spriteBatch->draw(_bounds.x, _bounds.y, 0, _bounds.width, _bounds.height, 0, 1, 1, 0, Vector4::one());
+            _spriteBatch->end();
         }
     }
 
@@ -201,9 +216,6 @@ namespace gameplay
     {
         std::vector<Control*>::const_iterator it;
 
-        // Batch for all themed border and background sprites.
-        spriteBatch->begin();
-
         // Batch each font individually.
         std::set<Font*>::const_iterator fontIter;
         for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
@@ -215,6 +227,9 @@ namespace gameplay
             }
         }
 
+        // Batch for all themed border and background sprites.
+        spriteBatch->begin();
+
         // Draw the form's border and background.
         // We don't pass the form's position to itself or it will be applied twice!
         Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
@@ -223,13 +238,10 @@ namespace gameplay
         {
             Control* control = *it;
 
-            // Draw this control's border and background.
-            control->drawBorder(spriteBatch, clip);
-
-            // Add all themed foreground sprites (checkboxes etc.) to the same batch.
-            control->drawImages(spriteBatch, clip);
-
-            control->drawText(clip);
+            //if (control->isDirty())
+            {
+                control->draw(spriteBatch, clip);
+            }
         }
 
         // Done all batching.
@@ -266,20 +278,6 @@ namespace gameplay
         // Bind the WorldViewProjection matrix
         material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
 
-        // Create a FrameBuffer if necessary.
-        if (!_frameBuffer)
-        {
-            _frameBuffer = FrameBuffer::create(_id.c_str());
-        }
-
-        // Use the FrameBuffer to texture the quad.
-        if (!_frameBuffer->getRenderTarget())
-        {
-            RenderTarget* rt = RenderTarget::create(_id.c_str(), _bounds.width, _bounds.height);
-            _frameBuffer->setRenderTarget(rt);
-            SAFE_RELEASE(rt);
-        }
-
         Texture::Sampler* sampler = Texture::Sampler::create(_frameBuffer->getRenderTarget()->getTexture());
         sampler->setWrapMode(Texture::CLAMP, Texture::CLAMP);
         material->getParameter("u_texture")->setValue(sampler);

+ 1 - 0
gameplay/src/Form.h

@@ -160,6 +160,7 @@ private:
     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.
+    SpriteBatch* _spriteBatch;
 };
 
 }

+ 27 - 21
gameplay/src/Frustum.cpp

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

+ 9 - 0
gameplay/src/Frustum.h

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

+ 4 - 4
gameplay/src/Label.cpp

@@ -56,10 +56,10 @@ namespace gameplay
     
     void Label::setText(const char* text)
     {
-        if (text)
-        {
-            _text = text;
-        }
+        assert(text);
+
+        _text = text;
+        _dirty = true;
     }
 
     const char* Label::getText()

+ 25 - 9
gameplay/src/ParticleEmitter.cpp

@@ -48,7 +48,7 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
     assert(textureFile);
 
     Texture* texture = NULL;
-    texture = Texture::create(textureFile, true);    
+    texture = Texture::create(textureFile, false);
 
     if (!texture)
     {
@@ -277,6 +277,7 @@ void ParticleEmitter::emit(unsigned int particleCount)
     for (unsigned int i = 0; i < particleCount; i++)
     {
         Particle* p = &_particles[_particleCount];
+        p->_visible = true;
 
         generateColor(_colorStart, _colorStartVar, &p->_colorStart);
         generateColor(_colorEnd, _colorEndVar, &p->_colorEnd);
@@ -779,14 +780,19 @@ void ParticleEmitter::update(long elapsedTime)
         // How many particles should we emit this frame?
         unsigned int emitCount = _timeRunning / _timePerEmission;
             
-        if ((int)_timePerEmission > 0)
+        if (emitCount)
         {
-            _timeRunning %= (int)_timePerEmission;
-        }
+            if ((int)_timePerEmission > 0)
+            {
+                _timeRunning %= (int)_timePerEmission;
+            }
 
-        emit(emitCount);
+            emit(emitCount);
+        }
     }
 
+    const Frustum& frustum = _node->getScene()->getActiveCamera()->getFrustum();
+
     // Now update all currently living particles.
     for (unsigned int particlesIndex = 0; particlesIndex < _particleCount; ++particlesIndex)
     {
@@ -812,6 +818,12 @@ void ParticleEmitter::update(long elapsedTime)
             p->_position.y += p->_velocity.y * elapsedSecs;
             p->_position.z += p->_velocity.z * elapsedSecs;
 
+            if (!frustum.intersects(p->_position))
+            {
+                p->_visible = false;
+                continue;
+            }
+
             p->_angle += p->_rotationPerParticleSpeed * elapsedSecs;
 
             // Simple linear interpolation of color and size.
@@ -891,10 +903,11 @@ void ParticleEmitter::draw()
         _spriteBatch->begin();
 
         // 2D Rotation.
-        Vector2 pivot(0.5f, 0.5f);
+        static const Vector2 pivot(0.5f, 0.5f);
 
         // 3D Rotation so that particles always face the camera.
         const Matrix& cameraWorldMatrix = _node->getScene()->getActiveCamera()->getNode()->getWorldMatrix();
+
         Vector3 right;
         cameraWorldMatrix.getRightVector(&right);
         Vector3 up;
@@ -904,9 +917,12 @@ void ParticleEmitter::draw()
         {
             Particle* p = &_particles[i];
 
-            _spriteBatch->draw(p->_position, right, up, p->_size, p->_size,
-                               _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3],
-                               p->_color, pivot, p->_angle);
+            if (p->_visible)
+            {
+                _spriteBatch->draw(p->_position, right, up, p->_size, p->_size,
+                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3],
+                                   p->_color, pivot, p->_angle);
+            }
         }
 
         // Render.

+ 1 - 0
gameplay/src/ParticleEmitter.h

@@ -685,6 +685,7 @@ private:
         float _size;
         unsigned int _frame;
         float _timeOnCurrentFrame;
+        bool _visible;
     };
 
     unsigned int _particleCountMax;

+ 2 - 2
gameplay/src/Plane.cpp

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

+ 64 - 59
gameplay/src/SpriteBatch.cpp

@@ -45,57 +45,6 @@
 namespace gameplay
 {
 
-/**
- * Sprite vertex structure used for batching.
- */
-struct SpriteVertex
-{
-    /**
-     * The x coordinate of the vertex.
-     */
-    float x;
-    
-    /**
-     * The y coordinate of the vertex.
-     */
-    float y;
-    
-    /**
-     * The z coordinate of the vertex.
-     */
-    float z;
-
-    /**
-     * The u component of the (u, v) texture coordinates for the vertex.
-     */
-    float u;
-    
-    /**
-     * The v component of the (u, v) texture coordinates for the vertex.
-     */
-    float v;
-
-    /**
-     * The red color component of the vertex.
-     */
-    float r;
-    
-    /**
-     * The green color component of the vertex.
-     */
-    float g;
-    
-    /**
-     * The blue color component of the vertex.
-     */
-    float b;
-    
-    /**
-     * The alpha component of the vertex.
-     */
-    float a;
-};
-
 // Shared sprite effects
 static Effect* __spriteEffect = NULL;
 
@@ -303,31 +252,87 @@ void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vect
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
     // Calculate the vertex positions.
-    Vector3 p[4];
+    //static Vector3 p[4];
+
+    // Pre-optimized:
+    /*
     p[0] = position - 0.5f * width * right - 0.5f * height * forward;
     p[1] = position + 0.5f * width * right - 0.5f * height * forward;
     p[2] = p[0] + height * forward;
     p[3] = p[1] + height * forward;
+    */
+    
+    // Optimized:
+    Vector3 tRight(right);
+    tRight *= width * 0.5f;
+    Vector3 tForward(forward);
+    tForward *= height * 0.5f;
+    
+    Vector3 p0 = position;
+    p0 -= tRight;
+    p0 -= tForward;
+
+    Vector3 p1 = position;
+    p1 += tRight;
+    p1 -= tForward;
+
+    tForward = forward;
+    tForward *= height;
+    Vector3 p2 = p0;
+    p2 += tForward;
+    Vector3 p3 = p1;
+    p3 += tForward;
 
     // Calculate the rotation point.
-    Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+    
+    // Pre-optimized:
+    //Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+
+    // Optimized:
+    Vector3 rp = p0;
+    tRight = right;
+    tRight *= width * rotationPoint.x;
+    tForward *= rotationPoint.y;
+    rp += tRight;
+    rp += tForward;
 
     // Rotate all points the specified amount about the given point (about the up vector).
-    Vector3 u;
+    static Vector3 u;
     Vector3::cross(right, forward, &u);
-    Matrix rotation;
+    static Matrix rotation;
     Matrix::createRotation(u, rotationAngle, &rotation);
+
+    // Pre-optimized:
+    /*
     p[0] = (rotation * (p[0] - rp)) + rp;
     p[1] = (rotation * (p[1] - rp)) + rp;
     p[2] = (rotation * (p[2] - rp)) + rp;
     p[3] = (rotation * (p[3] - rp)) + rp;
+    */
+
+    // Optimized:
+    p0 -= rp;
+    p0 *= rotation;
+    p0 += rp;
+
+    p1 -= rp;
+    p1 *= rotation;
+    p1 += rp;
+
+    p2 -= rp;
+    p2 *= rotation;
+    p2 += rp;
+
+    p3 -= rp;
+    p3 *= rotation;
+    p3 += rp;
 
     // Add the sprite vertex data to the batch.
     static SpriteVertex v[4];
-    ADD_SPRITE_VERTEX(v[0], p[0].x, p[0].y, p[0].z, u1, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[1], p[1].x, p[1].y, p[1].z, u2, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[2], p[2].x, p[2].y, p[2].z, u1, v2, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[3], p[3].x, p[3].y, p[3].z, u2, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[0], p0.x, p0.y, p0.z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], p1.x, p1.y, p1.z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], p2.x, p2.y, p2.z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], p3.x, p3.y, p3.z, u2, v2, color.x, color.y, color.z, color.w);
     
     static const unsigned short indices[4] = { 0, 1, 2, 3 };
     _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);

+ 51 - 0
gameplay/src/SpriteBatch.h

@@ -28,6 +28,57 @@ class SpriteBatch
 
 public:
 
+    /**
+     * Sprite vertex structure used for batching.
+     */
+    struct SpriteVertex
+    {
+        /**
+         * The x coordinate of the vertex.
+         */
+        float x;
+    
+        /**
+         * The y coordinate of the vertex.
+         */
+        float y;
+    
+        /**
+         * The z coordinate of the vertex.
+         */
+        float z;
+
+        /**
+         * The u component of the (u, v) texture coordinates for the vertex.
+         */
+        float u;
+    
+        /**
+         * The v component of the (u, v) texture coordinates for the vertex.
+         */
+        float v;
+
+        /**
+         * The red color component of the vertex.
+         */
+        float r;
+    
+        /**
+         * The green color component of the vertex.
+         */
+        float g;
+    
+        /**
+         * The blue color component of the vertex.
+         */
+        float b;
+    
+        /**
+         * The alpha component of the vertex.
+         */
+        float a;
+    };
+
     /**
      * Creates a new SpriteBatch for drawing sprites with the given texture.
      *

+ 3 - 1
gameplay/src/Theme.cpp

@@ -430,7 +430,9 @@ namespace gameplay
         std::set<Font*>::const_iterator it;
         for (it = _fonts.begin(); it != _fonts.end(); ++it)
         {
-            (*it)->getSpriteBatch()->setProjectionMatrix(matrix);
+            Font* font = *it;
+            if (font)
+                font->getSpriteBatch()->setProjectionMatrix(matrix);
         }
     }
 

+ 2 - 2
gameplay/src/VerticalLayout.cpp

@@ -68,7 +68,7 @@ namespace gameplay
         while (i != end)
         {
             Control* control = controls.at(i);
-
+            
             align(control, container);
 
             const Rectangle& bounds = control->getBounds();
@@ -76,9 +76,9 @@ namespace gameplay
 
             yPosition += margin.top;
 
-            control->setPosition(0, yPosition);
             if (control->isDirty() || control->isContainer())
             {
+                control->setPosition(0, yPosition);
                 control->update(container->getClip(), Vector2::zero());
             }