Просмотр исходного кода

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

Adam Blake 13 лет назад
Родитель
Сommit
219943febf

+ 3 - 0
.gitignore

@@ -9,7 +9,9 @@ Icon?
 Thumbs.db
 /.metadata
 /169.254.0.1
+/usb
 /ipch
+/build
 /cmake
 /Debug
 /Release
@@ -45,6 +47,7 @@ Thumbs.db
 /gameplay/Device-Release
 /gameplay.xcworkspace/xcuserdata
 /gameplay/gameplay.xcodeproj/xcuserdata
+/gameplay/windows
 /gameplay/android/NUL
 /gameplay/android/proguard.cfg
 /gameplay/android/proguard-project.txt

+ 0 - 1
.kateconfig

@@ -1 +0,0 @@
-kate: space-indent on; tab-width 4; indent-width 4; replace-tabs on; show-tabs: on; replace-tabs-save: off; replace-trailing-space-save: off;

+ 16 - 3
CHANGES.md

@@ -1,13 +1,26 @@
 ## v1.6.0
-- Adds Stream interface for reading/writing files instead of using fread/fwrite. 
-- Android no longer needs to copy files to the SD card before reading them. None of the Android samples require an SD card.
+- Adds file Stream interface for reading/writing files instead of using fread/fwrite. 
+- Adds additional gameplay-tests for billboards and forms.
+- Adds support for launching the browser via launchURL(const char*).
+- Adds physics support for setLinearFactor and setAngularFactor  on rigid bodies.
+- Adds option for fullscreen without width/height config to use native desktop resolution.
+- Adds Linux support for OpenAL PulseAudio back-end.
+- Adds support for latest Bullet Physics 2.81 with NEON optimizations for mobile targets.
+- Adds Windows 7 64-bit support.
+- Fixes to external-deps to reduce the size of the libraries on Windows.
+- Fixes for Android to no longer need to copy files to the SD card before reading them. None of the Android samples require an SD card.
+- Fixes for animation of opacity on UI and fonts.
+- Fixes in UI for removing controls and also setVisible(bool)
+- Fixes for UI controls missing on MacOSX.
+- Fixes for setting UI alignment programmatically.
+- Fixes for directional and spotlight shaders.
 
 ## v1.5.0
 
 - Linux support. (tested on Ubuntu 12)
 - CMake support for makefile generation for Linux.
 - CodeBlocks 10 IDE support for Linux.
-- Gamepad controllers support for desktops.
+- Gamepad API support for desktops.
 - Touch gesture support for tap, swipe and pinch.
 - Vehicle physics support via new PhysicsVehicle and PhysicsVehicleWheel classes.
 - Adds new racer sample (sample06-racer).

+ 23 - 15
README.md

@@ -1,29 +1,37 @@
-## gameplay v1.5.0
-An open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
+## gameplay v1.6.0
+
+GamePlay3D is an open-source, cross-platform 3D native C++ game framework making it easy to learn and write mobile and desktop games. 
+
+<img align="right" src="https://raw.github.com/wiki/blackberry/GamePlay/img/logo.png" alt="gameplay" />
+
+- [Website](http://www.gameplay3d.org/)
+- [Forums](http://www.gameplay3d.org/forums/)
+- [Wiki](https://github.com/blackberry/GamePlay/wiki)
+- [API Reference](http://www.gameplay3d.org/api.php)
+- [Documentation](http://www.gameplay3d.org/docs.php)
 
 ## Supported Mobile Platforms
-- BlackBerry 10 and PlayBook 2.0 (using BlackBerry Native SDK)
-- Apple iOS 5.1 (using Apple XCode 4.3.2)
-- Google Android 2.3+ (using Google Android NDK, SDK API level 9+)
+- BlackBerry 10 and PlayBook (using [BlackBerry Native SDK](http://developer.blackberry.com/native/))
+- Apple iOS 5 (using Apple XCode 4)
+- Google Android 2.3+ (using Google Android NDK)
 
 ## Supported Desktop Platforms
 - Microsoft Windows 7 (using Microsoft Visual Studio 2010)
-- Apple MacOS X (using Apple XCode 4.3.2)
+- Apple MacOS X (using Apple XCode 4)
 - Linux (using CMake or CodeBlocks 10)
 
 ## Roadmap for 'next' branch
-- AI Pathfinding
-- Terrain and Water
-- Asset Pipeline improvements
+- [Version 1.7.0 Milestone](https://github.com/blackberry/GamePlay/issues?milestone=4)
 
 ## License
-The project is open sourced under the Apache 2.0 license.
+The project is open sourced under the [Apache 2.0 license](http://www.tldrlegal.com/license/apache-license-2.0-%28apache-2.0%29).
+
+## Bug Reporting
+Please log bugs under [Issues](https://github.com/blackberry/GamePlay/issues) on github.
+If you are unsure if your problem is a bug then post on the [Help Forum](http://www.gameplay3d.org/forums/viewforum.php?f=3).
 
-## Bug Reporting and Feature Requests
-If you find a bug in a Sample, or have an enhancement request, simply file an 
-[Issue](https://github.com/blackberry/GamePlay/issues) and send a message (via github messages) 
-to the Committers to the project to let them know that you have filed 
-an [Issue](https://github.com/blackberry/GamePlay/issues).
+## Feature Requests
+Please post feature requests on the [Feature Request Forum](http://www.gameplay3d.org/forums/viewforum.php?f=4). Approved feature requests will be added to the github issues list. 
 
 ## Disclaimer
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 

+ 0 - 8
cmake/README.md

@@ -1,8 +0,0 @@
-## Building with CMake
-Run the following command lines from this directory:
-* cmake ..
-* make
-
-
-
-

+ 1 - 1
gameplay.doxyfile

@@ -32,7 +32,7 @@ PROJECT_NAME           = gameplay
 # This could be handy for archiving the generated documentation or 
 # if some version control system is used.
 
-PROJECT_NUMBER         = 1.5.0
+PROJECT_NUMBER         = 1.6.0
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description 
 # for a project that appears at the top of each page and should give viewer 

+ 3 - 2
gameplay/src/Control.cpp

@@ -7,8 +7,8 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _autoWidth(false), _autoHeight(false), _listeners(NULL),
-    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL), _visible(true)
+    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
+    _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(0), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
 }
@@ -208,6 +208,7 @@ float Control::getHeight() const
 void Control::setAlignment(Alignment alignment)
 {
     _alignment = alignment;
+    _isAlignmentSet = true;
     _dirty = true;
 }
 

+ 5 - 0
gameplay/src/Control.h

@@ -985,6 +985,11 @@ protected:
      * The Control's Alignment
      */
     Alignment _alignment;
+
+    /**
+     * Whether the Control's alignment has been set programmatically.
+     */
+    bool _isAlignmentSet;
     
     /**
      * Whether the Control's width is auto-sized.

+ 3 - 3
gameplay/src/Font.cpp

@@ -272,7 +272,7 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
         }
 
         bool draw = true;
-        if (yPos < area.y)
+        if (yPos < static_cast<int>(area.y))
         {
             // Skip drawing until line break or wrap.
             draw = false;
@@ -490,7 +490,7 @@ void Font::drawText(const char* text, int x, int y, const Vector4& color, unsign
 
         GP_ASSERT(_glyphs);
         GP_ASSERT(_batch);
-        for (size_t i = startIndex; i < length && i >= 0; i += (size_t)iteration)
+        for (size_t i = startIndex; i < length; i += (size_t)iteration)
         {
             char c = 0;
             if (rightToLeft)
@@ -629,7 +629,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         }
 
         bool draw = true;
-        if (yPos < area.y - size)
+        if (yPos < static_cast<int>(area.y - size))
         {
             // Skip drawing until line break or wrap.
             draw = false;

+ 11 - 1
gameplay/src/Form.cpp

@@ -99,6 +99,8 @@ Form* Form::create(const char* id, Theme::Style* style, Layout::Type layoutType)
     Game* game = Game::getInstance();
     Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
 
+    form->updateBounds();
+
     __forms.push_back(form);
 
     return form;
@@ -200,6 +202,8 @@ Form* Form::create(const char* url)
     form->addControls(theme, formProperties);
 
     SAFE_DELETE(properties);
+    
+    form->updateBounds();
 
     __forms.push_back(form);
 
@@ -403,6 +407,11 @@ void Form::setNode(Node* node)
 }
 
 void Form::update(float elapsedTime)
+{
+    updateBounds();
+}
+
+void Form::updateBounds()
 {
     if (isDirty())
     {
@@ -552,7 +561,8 @@ void Form::draw()
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
-        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin != NULL, false, _bounds.height);
+        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height),
+                        true/*WAS _skin!=NULL*/, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
         // Rebind the default framebuffer and game viewport.

+ 5 - 0
gameplay/src/Form.h

@@ -183,6 +183,11 @@ private:
      */
     void initializeQuad(Mesh* mesh);
 
+    /**
+     * Update this form's bounds.
+     */
+    void updateBounds();
+
     /**
      * Propagate touch events to enabled forms.
      *

+ 1 - 0
gameplay/src/Layout.cpp

@@ -12,6 +12,7 @@ void Layout::align(Control* control, const Container* container)
     GP_ASSERT(container);
 
     if (control->_alignment != Control::ALIGN_TOP_LEFT ||
+        control->_isAlignmentSet ||
         control->_autoWidth || control->_autoHeight)
     {
         Rectangle controlBounds = control->getBounds();

+ 72 - 3
gameplay/src/Matrix.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Matrix.h"
+#include "Plane.h"
 #include "Quaternion.h"
 #include "MathUtil.h"
 
@@ -19,7 +20,8 @@ Matrix::Matrix()
     *this = Matrix::identity();
 }
 
-Matrix::Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
+Matrix::Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+               float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
 {
     set(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
 }
@@ -60,7 +62,8 @@ const Matrix& Matrix::zero()
 
 void Matrix::createLookAt(const Vector3& eyePosition, const Vector3& targetPosition, const Vector3& up, Matrix* dst)
 {
-    createLookAt(eyePosition.x, eyePosition.y, eyePosition.z, targetPosition.x, targetPosition.y, targetPosition.z, up.x, up.y, up.z, dst);
+    createLookAt(eyePosition.x, eyePosition.y, eyePosition.z, targetPosition.x, targetPosition.y, targetPosition.z,
+                 up.x, up.y, up.z, dst);
 }
 
 void Matrix::createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ,
@@ -162,6 +165,71 @@ void Matrix::createOrthographicOffCenter(float left, float right, float bottom,
     dst->m[14] = (-(zFarPlane + zNearPlane)) * f_n;
     dst->m[15] = 1.0f;
 }
+    
+void Matrix::createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                             const Vector3& cameraUpVector, Matrix* dst)
+{
+    createBillboardHelper(objectPosition, cameraPosition, cameraUpVector, NULL, dst);
+}
+
+void Matrix::createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                             const Vector3& cameraUpVector, const Vector3& cameraForwardVector,
+                             Matrix* dst)
+{
+    createBillboardHelper(objectPosition, cameraPosition, cameraUpVector, &cameraForwardVector, dst);
+}
+
+void Matrix::createBillboardHelper(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                   const Vector3& cameraUpVector, const Vector3* cameraForwardVector,
+                                   Matrix* dst)
+{
+    Vector3 delta(objectPosition, cameraPosition);
+    bool isSufficientDelta = delta.lengthSquared() > MATH_EPSILON;
+
+    dst->setIdentity();
+    dst->m[3] = objectPosition.x;
+    dst->m[7] = objectPosition.y;
+    dst->m[11] = objectPosition.z;
+
+    // As per the contracts for the 2 variants of createBillboard, we need
+    // either a safe default or a sufficient distance between object and camera.
+    if (cameraForwardVector || isSufficientDelta)
+    {
+        Vector3 target = isSufficientDelta ? cameraPosition : (objectPosition - *cameraForwardVector);
+
+        // A billboard is the inverse of a lookAt rotation
+        Matrix lookAt;
+        createLookAt(objectPosition, target, cameraUpVector, &lookAt);
+        dst->m[0] = lookAt.m[0];
+        dst->m[1] = lookAt.m[4];
+        dst->m[2] = lookAt.m[8];
+        dst->m[4] = lookAt.m[1];
+        dst->m[5] = lookAt.m[5];
+        dst->m[6] = lookAt.m[9];
+        dst->m[8] = lookAt.m[2];
+        dst->m[9] = lookAt.m[6];
+        dst->m[10] = lookAt.m[10];
+    }
+}
+    
+void Matrix::createReflection(const Plane& plane, Matrix* dst)
+{
+    Vector3 normal(plane.getNormal());
+    float k = -2.0f * plane.getDistance();
+
+    dst->setIdentity();
+
+    dst->m[0] -= 2.0f * normal.x * normal.x;
+    dst->m[5] -= 2.0f * normal.y * normal.y;
+    dst->m[10] -= 2.0f * normal.z * normal.z;
+    dst->m[1] = dst->m[4] = -2.0f * normal.x * normal.y;
+    dst->m[2] = dst->m[8] = -2.0f * normal.x * normal.z;
+    dst->m[6] = dst->m[9] = -2.0f * normal.y * normal.z;
+    
+    dst->m[3] = k * normal.x;
+    dst->m[7] = k * normal.y;
+    dst->m[11] = k * normal.z;
+}
 
 void Matrix::createScale(const Vector3& scale, Matrix* dst)
 {
@@ -763,7 +831,8 @@ void Matrix::scale(const Vector3& s, Matrix* dst) const
     scale(s.x, s.y, s.z, dst);
 }
 
-void Matrix::set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
+void Matrix::set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+                 float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44)
 {
     m[0]  = m11;
     m[1]  = m21;

+ 63 - 31
gameplay/src/Matrix.h

@@ -7,6 +7,8 @@
 namespace gameplay
 {
 
+class Plane;
+
 /**
  * Defines a 4 x 4 floating point matrix representing a 3D transformation.
  *
@@ -77,8 +79,8 @@ public:
      * @param m43 The third element of the fourth row.
      * @param m44 The fourth element of the fourth row.
      */
-    Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31,
-           float m32, float m33, float m34, float m41, float m42, float m43, float m44);
+    Matrix(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+           float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
 
     /**
      * Creates a matrix initialized to the specified column-major array.
@@ -149,8 +151,9 @@ public:
      * @param upZ The up vector z-coordinate value.
      * @param dst A matrix to store the result in.
      */
-    static void createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ, float targetCenterX,
-                             float targetCenterY, float targetCenterZ, float upX, float upY, float upZ, Matrix* dst);
+    static void createLookAt(float eyePositionX, float eyePositionY, float eyePositionZ,
+                             float targetCenterX, float targetCenterY, float targetCenterZ,
+                             float upX, float upY, float upZ, Matrix* dst);
 
     /**
      * Builds a perspective projection matrix based on a field of view and returns by value.
@@ -207,31 +210,54 @@ public:
      * @param zFarPlane The maximum z-value of the view volume.
      * @param dst A matrix to store the result in.
      */
-    static void createOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane,
-                                            float zFarPlane, Matrix* dst);
-
-//    /*
-//     * Creates a spherical billboard that rotates around a specified object position.
-//     *
-//     * This method computes the facing direction of the billboard from the object position
-//     * and camera position. When the object and camera positions are too close, the matrix
-//     * will not be accurate. To avoid this problem, the method uses the optional camera
-//     * forward vector if the positions are too close.
-//     *
-//     * @param objectPosition The position of the object the billboard will rotate around.
-//     * @param cameraPosition The position of the camera.
-//     * @param cameraUpVector The up vector of the camera.
-//     * @param dst A matrix to store the result in.
-//     */
-//    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition, const Vector3& cameraUpVector, Matrix* dst);
-//
-//    /*
-//     * Fills in an existing Matrix so that it reflects the coordinate system about a specified Plane.
-//     *
-//     * @param plane The Plane about which to create a reflection.
-//     * @param dst A matrix to store the result in.
-//     */
-//    static void createReflection(const Plane& plane, Matrix* dst);
+    static void createOrthographicOffCenter(float left, float right, float bottom, float top,
+                                            float zNearPlane, float zFarPlane, Matrix* dst);
+
+    /*
+     * Creates a spherical billboard that rotates around a specified object position.
+     *
+     * This method computes the facing direction of the billboard from the object position
+     * and camera position. When the object and camera positions are too close, the matrix
+     * will not be accurate. To avoid this problem, this method defaults to the identity
+     * rotation if the positions are too close. (See the other overload of createBillboard
+     * for an alternative approach).
+     *
+     * @param objectPosition The position of the object the billboard will rotate around.
+     * @param cameraPosition The position of the camera.
+     * @param cameraUpVector The up vector of the camera.
+     * @param dst A matrix to store the result in.
+     */
+    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                const Vector3& cameraUpVector, Matrix* dst);
+
+    /*
+     * Creates a spherical billboard that rotates around a specified object position with
+     * provision for a safe default orientation.
+     *
+     * This method computes the facing direction of the billboard from the object position
+     * and camera position. When the object and camera positions are too close, the matrix
+     * will not be accurate. To avoid this problem, this method uses the specified camera
+     * forward vector if the positions are too close. (See the other overload of createBillboard
+     * for an alternative approach).
+     *
+     * @param objectPosition The position of the object the billboard will rotate around.
+     * @param cameraPosition The position of the camera.
+     * @param cameraUpVector The up vector of the camera.
+     * @param cameraForwardVector The forward vector of the camera, used if the positions
+     *                            are too close.
+     * @param dst A matrix to store the result in.
+     */
+    static void createBillboard(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                const Vector3& cameraUpVector, const Vector3& cameraForwardVector,
+                                Matrix* dst);
+
+    /*
+     * Fills in an existing Matrix so that it reflects the coordinate system about a specified Plane.
+     *
+     * @param plane The Plane about which to create a reflection.
+     * @param dst A matrix to store the result in.
+     */
+    static void createReflection(const Plane& plane, Matrix* dst);
 
     /**
      * Creates a scale matrix.
@@ -664,8 +690,8 @@ public:
      * @param m43 The third element of the fourth row.
      * @param m44 The fourth element of the fourth row.
      */
-    void set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31,
-             float m32, float m33, float m34, float m41, float m42, float m43, float m44);
+    void set(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24,
+             float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
 
     /**
      * Sets the values of this matrix to those in the specified column-major array.
@@ -885,6 +911,12 @@ public:
      * @return This matrix, after the multiplication occurs.
      */
     inline Matrix& operator*=(const Matrix& m);
+    
+private:
+
+    static void createBillboardHelper(const Vector3& objectPosition, const Vector3& cameraPosition,
+                                      const Vector3& cameraUpVector, const Vector3* cameraForwardVector,
+                                      Matrix* dst);
 };
 
 /**

+ 12 - 0
gameplay/src/Node.cpp

@@ -337,6 +337,18 @@ unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool rec
     
     unsigned int count = 0;
 
+    // If the node has a model with a mesh skin, search the skin's hierarchy as well.
+    Node* rootNode = NULL;
+    if (_model != NULL && _model->getSkin() != NULL && (rootNode = _model->getSkin()->_rootNode) != NULL)
+    {
+        if ((exactMatch && rootNode->_id == id) || (!exactMatch && rootNode->_id.find(id) == 0))
+        {
+            nodes.push_back(rootNode);
+            ++count;
+        }
+        count += rootNode->findNodes(id, nodes, true, exactMatch);
+    }
+
     // Search immediate children first.
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {

+ 7 - 3
gameplay/src/PhysicsCharacter.cpp

@@ -625,7 +625,9 @@ bool PhysicsCharacter::fixCollision(btCollisionWorld* world)
     }
 
     // Set the new world transformation to apply to fix the collision.
-    _node->translate(Vector3(currentPosition.x(), currentPosition.y(), currentPosition.z()) - startPosition);
+    Vector3 newPosition = Vector3(currentPosition.x(), currentPosition.y(), currentPosition.z()) - startPosition;
+    if (newPosition != Vector3::zero())
+        _node->translate(newPosition);
 
     return collision;
 }
@@ -687,8 +689,10 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
         stepDown(collisionWorld, deltaTimeStep);
 
     // Set new position.
-    btVector3 translation = _currentPosition - startPosition;
-    _node->translate(translation.x(), translation.y(), translation.z());
+    btVector3 newPosition = _currentPosition - startPosition;
+    Vector3 translation = Vector3(newPosition.x(), newPosition.y(), newPosition.z());
+    if (translation !=  Vector3::zero())
+        _node->translate(translation);
 }
 
 

+ 126 - 75
gameplay/src/PlatformLinux.cpp

@@ -24,6 +24,9 @@ static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static float __pitch;
 static float __roll;
+static bool __mouseCaptured = false;
+static float __mouseCapturePointX = 0;
+static float __mouseCapturePointY = 0;
 static bool __cursorVisible = true;
 static Display* __display;
 static Window   __window;
@@ -499,10 +502,16 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     __attachToWindow = (Window)attachToWindow;
     FileSystem::setResourcePath("./");
     Platform* platform = new Platform(game);
-
-    // Get window configuration
-
-    // Default values
+    
+    // Get the display and initialize
+    __display = XOpenDisplay(NULL);
+    if (__display == NULL)
+    {
+        perror("XOpenDisplay");
+        return NULL;
+    }
+     
+    // Get the window configuration values
     const char *title = NULL;
     int __x = 0, __y = 0, __width = 1280, __height = 800;
     bool fullscreen = false;
@@ -516,35 +525,29 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
             // Read window rect.
             int x = config->getInt("x");
-            if (x != 0) __x = x;
-
             int y = config->getInt("y");
-            if (y != 0) __y = y;
-            
             int width = config->getInt("width");
-            if (width != 0) __width = width;
-
             int height = config->getInt("height");
-            if (height != 0) __height = height;
-
             fullscreen = config->getBool("fullscreen");
 
-
+            if (fullscreen && width == 0 && height == 0)
+            {
+                // Use the screen resolution if fullscreen is true but width and height were not set in the config
+                int screen_num = DefaultScreen(__display);
+                width = DisplayWidth(__display, screen_num);
+                height = DisplayHeight(__display, screen_num);
+            }
+            if (x != 0) __x = x;
+            if (y != 0) __y = y;
+            if (width != 0) __width = width;
+            if (height != 0) __height = height;
         }
     }
 
-    // Get the display and initialize.
-    __display = XOpenDisplay(NULL);
-    if (__display == NULL)
-    {
-        perror("XOpenDisplay");
-        return NULL;
-    }
-
     // GLX version
     GLint majorGLX, minorGLX = 0;
     glXQueryVersion(__display, &majorGLX, &minorGLX);
-    if(majorGLX == 1 && minorGLX < 2)
+    if (majorGLX == 1 && minorGLX < 2)
     {
         perror("GLX 1.2 or greater is required.");
         XCloseDisplay(__display);
@@ -563,25 +566,25 @@ Platform* Platform::create(Game* game, void* attachToWindow)
 
     // Get the configs
     int configAttribs[] = 
-    { 
+    {
         GLX_RENDER_TYPE,    GLX_RGBA_BIT,
         GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
         GLX_X_RENDERABLE,   True,
-        GLX_DEPTH_SIZE,     24, 
+        GLX_DEPTH_SIZE,     24,
         GLX_STENCIL_SIZE,   8,
         GLX_RED_SIZE,       8,
         GLX_GREEN_SIZE,     8,
         GLX_BLUE_SIZE,      8,
         GLX_DOUBLEBUFFER,   True,
-        0 
+        0
     };
     GLXFBConfig* configs;
     int configCount = 0;
     configs = glXChooseFBConfig(__display, DefaultScreen(__display), configAttribs, &configCount);
-    if( configCount == 0 || configs == 0 )    
-    {    
+    if ( configCount == 0 || configs == 0 )
+    {
         perror( "glXChooseFBConfig" );
-        return NULL;    
+        return NULL;
     }
 
     // Create the windows
@@ -591,8 +594,8 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     XSetWindowAttributes winAttribs;
     long eventMask;
     eventMask = ExposureMask | VisibilityChangeMask | StructureNotifyMask |
-                KeyPressMask | KeyReleaseMask | PointerMotionMask | 
-                ButtonPressMask | ButtonReleaseMask | 
+                KeyPressMask | KeyReleaseMask | PointerMotionMask |
+                ButtonPressMask | ButtonReleaseMask |
                 EnterWindowMask | LeaveWindowMask;
     winAttribs.event_mask = eventMask;
     winAttribs.border_pixel = 0;
@@ -602,9 +605,9 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     GLint winMask;
     winMask = CWBorderPixel | CWBitGravity | CWEventMask| CWColormap;
    
-    __window = XCreateWindow(__display, DefaultRootWindow(__display), __x, __y, __width, __height, 0, 
+    __window = XCreateWindow(__display, DefaultRootWindow(__display), __x, __y, __width, __height, 0,
                             visualInfo->depth, InputOutput, visualInfo->visual, winMask,
-                            &winAttribs); 
+                            &winAttribs);
 
     // Tell the window manager that it should send the delete window notification through ClientMessage
     __atomWmDeleteWindow = XInternAtom(__display, "WM_DELETE_WINDOW", False);
@@ -635,7 +638,7 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     XStoreName(__display, __window, title ? title : "");
 
     __context = glXCreateContext(__display, visualInfo, NULL, True);
-    if(!__context)
+    if (!__context)
     {
         perror("glXCreateContext");
         return NULL;
@@ -645,7 +648,7 @@ Platform* Platform::create(Game* game, void* attachToWindow)
     // Use OpenGL 2.x with GLEW
     glewExperimental = GL_TRUE;
     GLenum glewStatus = glewInit();
-    if(glewStatus != GLEW_OK)
+    if (glewStatus != GLEW_OK)
     {
         perror("glewInit");
         return NULL;
@@ -685,14 +688,14 @@ double timespec2millis(struct timespec *a)
     return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
 }
 
-void updateWindowSize()    
-{    
-    GP_ASSERT(__display);    
+void updateWindowSize()
+{
+    GP_ASSERT(__display);
     GP_ASSERT(__window);
-    XWindowAttributes windowAttrs;    
-    XGetWindowAttributes(__display, __window, &windowAttrs);      
-    __windowSize[0] = windowAttrs.width;    
-    __windowSize[1] = windowAttrs.height;    
+    XWindowAttributes windowAttrs;
+    XGetWindowAttributes(__display, __window, &windowAttrs);
+    __windowSize[0] = windowAttrs.width;
+    __windowSize[1] = windowAttrs.height;
 }
 
 int Platform::enterMessagePump()
@@ -717,7 +720,7 @@ int Platform::enterMessagePump()
     // Run the game.
     _game->run();
 
-    // Setup select for message handling (to allow non-blocking)    
+    // Setup select for message handling (to allow non-blocking)
     int x11_fd = ConnectionNumber(__display);
     
     pollfd xpolls[1];
@@ -728,12 +731,12 @@ int Platform::enterMessagePump()
     while (true)
     {
         poll( xpolls, 1, 16 );
-        // handle all pending events in one block 
-        while (XPending(__display))    
+        // handle all pending events in one block
+        while (XPending(__display))
         {
            XNextEvent(__display, &evt);
         
-            switch (evt.type) 
+            switch (evt.type)
             {
             case ClientMessage:
                 {
@@ -751,7 +754,7 @@ int Platform::enterMessagePump()
                 }
                 break;
 
-            case Expose: 
+            case Expose:
                 {
                     updateWindowSize();
                 }
@@ -764,18 +767,18 @@ int Platform::enterMessagePump()
 
                     //TempSym needed because XConvertCase operates on two keysyms: One lower and the other upper, we are only interested in the upper case
                     KeySym tempSym;
-                    if(capsOn && !shiftDown)
+                    if (capsOn && !shiftDown)
                         XConvertCase(sym,  &tempSym, &sym);
 
                     Keyboard::Key key = getKey(sym);
                     gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_PRESS, key);
 
-                    if(key == Keyboard::KEY_CAPS_LOCK)
+                    if (key == Keyboard::KEY_CAPS_LOCK)
                         capsOn = !capsOn;
-                    if(key == Keyboard::KEY_SHIFT)
+                    if (key == Keyboard::KEY_SHIFT)
                         shiftDown = true;
 
-                    if(int character = getUnicode(key))
+                    if (int character = getUnicode(key))
                         gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_CHAR, character);
 
                 }
@@ -785,10 +788,10 @@ int Platform::enterMessagePump()
                 {
                     //detect and drop repeating keystrokes (no other way to do this using the event interface)
                     XEvent next;
-                    if( XPending(__display) )
+                    if ( XPending(__display) )
                     {
                         XPeekEvent(__display,&next);
-                        if( next.type == KeyPress 
+                        if ( next.type == KeyPress
                             && next.xkey.time == evt.xkey.time
                             && next.xkey.keycode == evt.xkey.keycode )
                         {
@@ -801,7 +804,7 @@ int Platform::enterMessagePump()
                     Keyboard::Key key = getKey(sym);
                     gameplay::Platform::keyEventInternal(gameplay::Keyboard::KEY_RELEASE, key);
 
-                    if(key == Keyboard::KEY_SHIFT)
+                    if (key == Keyboard::KEY_SHIFT)
                         shiftDown = false;
                 }
                 break;
@@ -809,7 +812,7 @@ int Platform::enterMessagePump()
             case ButtonPress:
                 {
                     gameplay::Mouse::MouseEvent mouseEvt;
-                    switch(evt.xbutton.button)
+                    switch (evt.xbutton.button)
                     {
                         case 1:
                             mouseEvt = gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON;
@@ -822,8 +825,8 @@ int Platform::enterMessagePump()
                             break;
                         case 4:
                         case 5:
-                            gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, 
-                                                                   evt.xbutton.x, evt.xbutton.y, 
+                            gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL,
+                                                                   evt.xbutton.x, evt.xbutton.y,
                                                                    evt.xbutton.button == Button4 ? 1 : -1);
                             break;
                         default:
@@ -839,7 +842,7 @@ int Platform::enterMessagePump()
             case ButtonRelease:
                 {
                     gameplay::Mouse::MouseEvent mouseEvt;
-                    switch(evt.xbutton.button)
+                    switch (evt.xbutton.button)
                     {
                         case 1:
                             mouseEvt = gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON;
@@ -862,25 +865,45 @@ int Platform::enterMessagePump()
         
             case MotionNotify:
                 {
-                    if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, evt.xmotion.x, evt.xmotion.y, 0))
+                    int x = evt.xmotion.x;
+                    int y = evt.xmotion.y;
+
+                    if (__mouseCaptured)
+                    {
+                        if (x == __mouseCapturePointX && y == __mouseCapturePointY)
+                        {
+                            // Discard the first MotionNotify following capture
+                            // since it contains bogus x,y data.
+                            break;
+                        }
+
+                        // Convert to deltas
+                        x -= __mouseCapturePointX;
+                        y -= __mouseCapturePointY;
+
+                        // Warp mouse back to center of screen.
+                        XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+                    }
+
+                    if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
                     {
                         if (evt.xmotion.state & Button1Mask)
                         {
-                            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, evt.xmotion.x, evt.xmotion.y, 0);
+                            gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_MOVE, x, y, 0);
                         }
                         else if (evt.xmotion.state & Button3Mask)
                         {
                             // Update the pitch and roll by adding the scaled deltas.
-                            __roll += (float)(evt.xbutton.x - lx) * ACCELEROMETER_X_FACTOR;
-                            __pitch += -(float)(evt.xbutton.y - ly) * ACCELEROMETER_Y_FACTOR;
+                            __roll += (float)(x - lx) * ACCELEROMETER_X_FACTOR;
+                            __pitch += -(float)(y - ly) * ACCELEROMETER_Y_FACTOR;
 
                             // Clamp the values to the valid range.
                             __roll = max(min(__roll, 90.0f), -90.0f);
                             __pitch = max(min(__pitch, 90.0f), -90.0f);
 
                             // Update the last X/Y values.
-                            lx = evt.xbutton.x;
-                            ly = evt.xbutton.y;
+                            lx = x;
+                            ly = y;
                         }
                     }
                 }
@@ -895,7 +918,7 @@ int Platform::enterMessagePump()
         {
             // Game state will be uninitialized if game was closed through Game::exit()
             if (_game->getState() == Game::UNINITIALIZED)
-                break;            
+                break;
             
             _game->frame();
         }
@@ -908,15 +931,15 @@ int Platform::enterMessagePump()
     return 0;
 }
     
-void Platform::signalShutdown() 
-{ 
+void Platform::signalShutdown()
+{
     // nothing to do  
 }
-
-bool Platform::canExit()
-{
-    return true;
-}
+
+bool Platform::canExit()
+{
+    return true;
+}
     
 unsigned int Platform::getDisplayWidth()
 {
@@ -992,13 +1015,31 @@ bool Platform::hasMouse()
 
 void Platform::setMouseCaptured(bool captured)
 {
-    // TODO
+    if (captured != __mouseCaptured)
+    {
+        if (captured)
+        {
+            // Hide the cursor and warp it to the center of the screen
+            __mouseCapturePointX = getDisplayWidth() / 2;
+            __mouseCapturePointY = getDisplayHeight() / 2;
+
+            setCursorVisible(false);
+            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+        }
+        else
+        {
+            // Restore cursor
+            XWarpPointer(__display, None, __window, 0, 0, 0, 0, __mouseCapturePointX, __mouseCapturePointY);
+            setCursorVisible(true);
+        }
+
+        __mouseCaptured = captured;
+    }
 }
 
 bool Platform::isMouseCaptured()
 {
-    // TODO
-    return false;
+    return __mouseCaptured;
 }
 
 void Platform::setCursorVisible(bool visible)
@@ -1007,7 +1048,17 @@ void Platform::setCursorVisible(bool visible)
     {
         if (visible)
         {
-            XDefineCursor(__display, __window, None);
+            Cursor invisibleCursor;
+            Pixmap bitmapNoData;
+            XColor black;
+            static char noData[] = {0, 0, 0, 0, 0, 0, 0, 0};
+            black.red = black.green = black.blue = 0;
+            bitmapNoData = XCreateBitmapFromData(__display, __window, noData, 8, 8);
+            invisibleCursor = XCreatePixmapCursor(__display, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
+
+            XDefineCursor(__display, __window, invisibleCursor);
+            XFreeCursor(__display, invisibleCursor);
+            XFreePixmap(__display, bitmapNoData);
         }
         else
         {

+ 34 - 12
gameplay/src/PlatformMacOSX.mm

@@ -48,6 +48,7 @@ static char* __title = NULL;
 static bool __fullscreen = false;
 static void* __attachToWindow = NULL;
 static bool __mouseCaptured = false;
+static bool __mouseCapturedFirstPass = false;
 static CGPoint __mouseCapturePoint;
 static bool __cursorVisible = true;
 static View* __view = NULL;
@@ -582,7 +583,7 @@ double getMachTimeInMilliseconds()
 - (void)hidValueAvailable:(IOHIDValueRef)value
 {
     IOHIDElementRef element = IOHIDValueGetElement(value);
-	IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
+    IOHIDElementCookie cookie = IOHIDElementGetCookie(element);
     
     if(IOHIDValueGetLength(value) > 4) return; // saftey precaution for PS3 cotroller
     CFIndex integerValue = IOHIDValueGetIntegerValue(value);
@@ -848,8 +849,17 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     
+    float y;
     if (__mouseCaptured)
     {
+        if (__mouseCapturedFirstPass)
+        {
+            // Discard the first mouseMoved event following transition into capture
+            // since it contains bogus x,y data.
+            __mouseCapturedFirstPass = false;
+            return;
+        }
+
         point.x = [event deltaX];
         point.y = [event deltaY];
 
@@ -859,9 +869,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
         centerPoint.x = rect.origin.x + (rect.size.width / 2);
         centerPoint.y = rect.origin.y + (rect.size.height / 2);
         CGDisplayMoveCursorToPoint(CGDisplayPrimaryDisplay(NULL), centerPoint);
+        y = point.y;
+    }
+    else
+    {
+        y = __height - point.y;
     }
     
-    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, y, 0);
 }
 
 - (void) mouseDragged: (NSEvent*) event
@@ -1461,6 +1476,12 @@ int Platform::enterMessagePump()
 
             // Read fullscreen state.
             __fullscreen = config->getBool("fullscreen");
+            if (__fullscreen && width == 0 && height == 0)
+            {
+                CGRect mainMonitor = CGDisplayBounds(CGMainDisplayID());
+                __width = CGRectGetWidth(mainMonitor);
+                __height = CGRectGetHeight(mainMonitor);
+            }
         }
     }
 
@@ -1603,6 +1624,7 @@ void Platform::setMouseCaptured(bool captured)
         if (captured)
         {
             [NSCursor hide];
+            __mouseCapturedFirstPass = true;
         }
         else
         {   
@@ -1811,25 +1833,25 @@ CFMutableDictionaryRef IOHIDCreateDeviceMatchingDictionary(UInt32 inUsagePage, U
 
 CFStringRef IOHIDDeviceGetStringProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
 {
-	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
     {
-		return NULL;
-	}
+        return NULL;
+    }
     return (CFStringRef)typeRef;
 }
 
 int IOHIDDeviceGetIntProperty(IOHIDDeviceRef deviceRef, CFStringRef key) 
 {
-	CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
-	if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
+    CFTypeRef typeRef = IOHIDDeviceGetProperty(deviceRef, key);
+    if (typeRef == NULL || CFGetTypeID(typeRef) != CFNumberGetTypeID()) 
     {
-		return 0;
-	}
+        return 0;
+    }
     
     int value;
-	CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
-	return value;
+    CFNumberGetValue((CFNumberRef) typeRef, kCFNumberSInt32Type, &value);
+    return value;
 }
 
 static void hidDeviceDiscoveredCallback(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inIOHIDDeviceRef) 

+ 29 - 12
gameplay/src/PlatformWindows.cpp

@@ -292,7 +292,7 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
     }
 }
 
-void UpdateCapture(LPARAM lParam)
+static void UpdateCapture(LPARAM lParam)
 {
     if ((lParam & MK_LBUTTON) || (lParam & MK_MBUTTON) || (lParam & MK_RBUTTON))
         SetCapture(__hwnd);
@@ -300,13 +300,27 @@ void UpdateCapture(LPARAM lParam)
         ReleaseCapture();
 }
 
-void WarpMouse(int clientX, int clientY)
+static void WarpMouse(int clientX, int clientY)
 {
     POINT p = { clientX, clientY };
     ClientToScreen(__hwnd, &p);
     SetCursorPos(p.x, p.y);
 }
 
+
+/**
+ * Gets the width and height of the screen in pixels.
+ */
+static void getDesktopResolution(int& width, int& height)
+{
+   RECT desktop;
+   const HWND hDesktop = GetDesktopWindow();
+   // Get the size of screen to the variable desktop
+   GetWindowRect(hDesktop, &desktop);
+   width = desktop.right;
+   height = desktop.bottom;
+}
+
 LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     static gameplay::Game* game = gameplay::Game::getInstance();
@@ -756,6 +770,11 @@ Platform* Platform::create(Game* game, void* attachToWindow)
                 SAFE_DELETE_ARRAY(wtitle);
             }
 
+            // Read fullscreen state.
+            params.fullscreen = config->getBool("fullscreen");
+            // Read multisampling state.
+            params.samples = config->getInt("samples");
+
             // Read window rect.
             int x = config->getInt("x");
             if (x != 0)
@@ -764,17 +783,15 @@ Platform* Platform::create(Game* game, void* attachToWindow)
             if (y != 0)
                 params.rect.top = y;
             int width = config->getInt("width");
+            int height = config->getInt("height");
+
+            if (width == 0 && height == 0 && params.fullscreen)
+                getDesktopResolution(width, height);
+
             if (width != 0)
                 params.rect.right = params.rect.left + width;
-            int height = config->getInt("height");
             if (height != 0)
                 params.rect.bottom = params.rect.top + height;
-
-            // Read fullscreen state.
-            params.fullscreen = config->getBool("fullscreen");
-
-            // Read multisampling state.
-            params.samples = config->getInt("samples");
         }
     }
 
@@ -845,9 +862,9 @@ Platform* Platform::create(Game* game, void* attachToWindow)
             DEVMODE dm;
             memset(&dm, 0, sizeof(dm));
             dm.dmSize= sizeof(dm);
-            dm.dmPelsWidth	= width;
-            dm.dmPelsHeight	= height;
-            dm.dmBitsPerPel	= DEFAULT_COLOR_BUFFER_SIZE;
+            dm.dmPelsWidth  = width;
+            dm.dmPelsHeight = height;
+            dm.dmBitsPerPel = DEFAULT_COLOR_BUFFER_SIZE;
             dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
             // Try to set selected mode and get results. NOTE: CDS_FULLSCREEN gets rid of start bar.

+ 8 - 7
gameplay/src/SpriteBatch.cpp

@@ -73,12 +73,12 @@ SpriteBatch::~SpriteBatch()
 SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
 {
     Texture* texture = Texture::create(texturePath);
-    SpriteBatch* batch = SpriteBatch::create(texture);
+    SpriteBatch* batch = SpriteBatch::create(texture, effect, initialCapacity);
     SAFE_RELEASE(texture);
     return batch;
 }
 
-SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
+SpriteBatch* SpriteBatch::create(Texture* texture,  Effect* effect, unsigned int initialCapacity)
 {
     GP_ASSERT(texture != NULL);
 
@@ -154,10 +154,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureWidthRatio = 1.0f / (float)texture->getWidth();
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
-    // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
-    Game* game = Game::getInstance();
-    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
-    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
+	// Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
+	Game* game = Game::getInstance();
+	Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
+	material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
+	
     return batch;
 }
 
@@ -185,7 +186,7 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
     float u2 = u1 + _textureWidthRatio * src.width;
     float v2 = v1 - _textureHeightRatio * src.height;
 
-    draw(dst.x, dst.y, dst.z, scale.x, scale.y, u2, v2, u1, v1, color);
+    draw(dst.x, dst.y, dst.z, scale.x, scale.y, u1, v1, u2, v2, color);
 }
 
 void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color,

+ 1 - 1
gameplay/src/Vector2.cpp

@@ -171,7 +171,7 @@ Vector2& Vector2::normalize()
     return *this;
 }
 
-void Vector2::normalize(Vector2* dst)
+void Vector2::normalize(Vector2* dst) const
 {
     GP_ASSERT(dst);
 

+ 1 - 1
gameplay/src/Vector2.h

@@ -245,7 +245,7 @@ public:
      *
      * @param dst The destination vector.
      */
-    void normalize(Vector2* dst);
+    void normalize(Vector2* dst) const;
 
     /**
      * Scales all elements of this vector by the specified value.

+ 1 - 1
gameplay/src/Vector4.cpp

@@ -239,7 +239,7 @@ Vector4& Vector4::normalize()
     return *this;
 }
 
-void Vector4::normalize(Vector4* dst)
+void Vector4::normalize(Vector4* dst) const
 {
     GP_ASSERT(dst);
 

+ 1 - 1
gameplay/src/Vector4.h

@@ -283,7 +283,7 @@ public:
      *
      * @param dst The destination vector.
      */
-    void normalize(Vector4* dst);
+    void normalize(Vector4* dst) const;
 
     /**
      * Scales all elements of this vector by the specified value.