Explorar o código

Added new source and resources files for terrain feature to all platforms except Mac.

Implemented debug bounds drawing for terrain.

Added support for loading uncompressed DDS textures for the following formats: R8G8B8, A8R8G8B8, A8B8G8R8, X8R8G8B8, X8B8G8R8. Using DDS uncompressed textured rather than PNG files results in both increased texture quality (better mipmap generation, especially on current mobile gpus) and faster load times when using mipmaps. XBGR DDS formats are preferred as they require no color conversion and result in the fastest load times.

Added glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST) to prefer higher quality mipmap generation.

Optimizations to terrain shaders.

Changed default texture minification mode from GL_LINEAR_MIPMAP_LINEAR to GL_NEAREST_MIPMAP_LINEAR for newly created textures with mipmaps, since this is the expected OpenGL default value.
Steve Grenier %!s(int64=13) %!d(string=hai) anos
pai
achega
251329ebd7

+ 16 - 0
gameplay/CMakeLists.txt

@@ -78,6 +78,8 @@ set(GAMEPLAY_SRC
     src/gameplay-main-linux.cpp
     src/gameplay-main-windows.cpp
     src/Gesture.h
+    src/HeightField.cpp
+    src/HeightField.h
     src/Image.cpp
     src/Image.h
     src/Image.inl
@@ -193,6 +195,10 @@ set(GAMEPLAY_SRC
     src/SpriteBatch.h
     src/Technique.cpp
     src/Technique.h
+    src/Terrain.cpp
+    src/Terrain.h
+    src/TerrainPatch.cpp
+    src/TerrainPatch.h
     src/TextBox.cpp
     src/TextBox.h
     src/Texture.cpp
@@ -339,6 +345,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_GestureGestureEvent.h
     src/lua/lua_Global.cpp
     src/lua/lua_Global.h
+    src/lua/lua_HeightField.cpp
+    src/lua/lua_HeightField.h
     src/lua/lua_Image.cpp
     src/lua/lua_Image.h
     src/lua/lua_ImageFormat.cpp
@@ -479,6 +487,8 @@ set(GAMEPLAY_LUA
     src/lua/lua_RenderStateAutoBinding.h
     src/lua/lua_RenderStateBlend.cpp
     src/lua/lua_RenderStateBlend.h
+    src/lua/lua_RenderStateDepthFunction.cpp
+    src/lua/lua_RenderStateDepthFunction.h
     src/lua/lua_RenderStateStateBlock.cpp
     src/lua/lua_RenderStateStateBlock.h
     src/lua/lua_RenderTarget.cpp
@@ -499,6 +509,10 @@ set(GAMEPLAY_LUA
     src/lua/lua_SpriteBatch.h
     src/lua/lua_Technique.cpp
     src/lua/lua_Technique.h
+    src/lua/lua_Terrain.cpp
+    src/lua/lua_Terrain.h
+    src/lua/lua_TerrainFlags.cpp
+    src/lua/lua_TerrainFlags.h
     src/lua/lua_TextBox.cpp
     src/lua/lua_TextBox.h
     src/lua/lua_Texture.cpp
@@ -567,6 +581,8 @@ set(GAMEPLAY_RES_SHADERS
     res/shaders/textured-unlit.vert
     res/shaders/textured.frag
     res/shaders/textured.vert
+    res/shaders/terrain.frag
+    res/shaders/terrain.vert
 )
 
 set(GAMEPLAY_RES_SHADERS_LIB

+ 7 - 0
gameplay/android/jni/Android.mk

@@ -52,6 +52,7 @@ LOCAL_SRC_FILES := \
     Frustum.cpp \
     Game.cpp \
     Gamepad.cpp \
+    HeightField.cpp \
     Image.cpp \
     Joint.cpp \
     Joystick.cpp \
@@ -103,6 +104,8 @@ LOCAL_SRC_FILES := \
     Slider.cpp \
     SpriteBatch.cpp \
     Technique.cpp \
+    Terrain.cpp \
+    TerrainPatch.cpp \
     TextBox.cpp \
     Texture.cpp \
     Theme.cpp \
@@ -173,6 +176,7 @@ LOCAL_SRC_FILES := \
     lua/lua_Gesture.cpp \
     lua/lua_GestureGestureEvent.cpp \
     lua/lua_Global.cpp \
+    lua/lua_HeightField.cpp \
     lua/lua_Image.cpp \
     lua/lua_ImageFormat.cpp \
     lua/lua_Joint.cpp \
@@ -243,6 +247,7 @@ LOCAL_SRC_FILES := \
     lua/lua_RenderState.cpp \
     lua/lua_RenderStateAutoBinding.cpp \
     lua/lua_RenderStateBlend.cpp \
+    lua/lua_RenderStateDepthFunction.cpp \
     lua/lua_RenderStateStateBlock.cpp \
     lua/lua_RenderTarget.cpp \
     lua/lua_Scene.cpp \
@@ -253,6 +258,8 @@ LOCAL_SRC_FILES := \
     lua/lua_Slider.cpp \
     lua/lua_SpriteBatch.cpp \
     lua/lua_Technique.cpp \
+    lua/lua_Terrain.cpp \
+    lua/lua_TerrainFlags.cpp \
     lua/lua_TextBox.cpp \
     lua/lua_Texture.cpp \
     lua/lua_TextureFilter.cpp \

+ 15 - 1
gameplay/gameplay.cbp

@@ -121,11 +121,13 @@
 		<Unit filename="src/Gamepad.cpp" />
 		<Unit filename="src/Gamepad.h" />
 		<Unit filename="src/gameplay.h" />
-        <Unit filename="src/gameplay-main-android.cpp" />
+		<Unit filename="src/gameplay-main-android.cpp" />
 		<Unit filename="src/gameplay-main-blackberry.cpp" />
 		<Unit filename="src/gameplay-main-linux.cpp" />
 		<Unit filename="src/gameplay-main-windows.cpp" />
 		<Unit filename="src/Gesture.h" />
+		<Unit filename="src/HeightField.cpp" />
+		<Unit filename="src/HeightField.h" />
 		<Unit filename="src/Image.cpp" />
 		<Unit filename="src/Image.h" />
 		<Unit filename="src/Joint.cpp" />
@@ -233,6 +235,10 @@
 		<Unit filename="src/SpriteBatch.h" />
 		<Unit filename="src/Technique.cpp" />
 		<Unit filename="src/Technique.h" />
+		<Unit filename="src/Terrain.cpp" />
+		<Unit filename="src/Terrain.h" />
+		<Unit filename="src/TerrainPatch.cpp" />
+		<Unit filename="src/TerrainPatch.h" />
 		<Unit filename="src/TextBox.cpp" />
 		<Unit filename="src/TextBox.h" />
 		<Unit filename="src/Texture.cpp" />
@@ -373,6 +379,8 @@
 		<Unit filename="src/lua/lua_GestureGestureEvent.h" />
 		<Unit filename="src/lua/lua_Global.cpp" />
 		<Unit filename="src/lua/lua_Global.h" />
+		<Unit filename="src/lua/lua_HeightField.cpp" />
+		<Unit filename="src/lua/lua_HeightField.h" />
 		<Unit filename="src/lua/lua_Image.cpp" />
 		<Unit filename="src/lua/lua_Image.h" />
 		<Unit filename="src/lua/lua_ImageFormat.cpp" />
@@ -513,6 +521,8 @@
 		<Unit filename="src/lua/lua_RenderStateAutoBinding.h" />
 		<Unit filename="src/lua/lua_RenderStateBlend.cpp" />
 		<Unit filename="src/lua/lua_RenderStateBlend.h" />
+		<Unit filename="src/lua/lua_RenderStateDepthFunction.cpp" />
+		<Unit filename="src/lua/lua_RenderStateDepthFunction.h" />
 		<Unit filename="src/lua/lua_RenderStateStateBlock.cpp" />
 		<Unit filename="src/lua/lua_RenderStateStateBlock.h" />
 		<Unit filename="src/lua/lua_RenderTarget.cpp" />
@@ -533,6 +543,10 @@
 		<Unit filename="src/lua/lua_SpriteBatch.h" />
 		<Unit filename="src/lua/lua_Technique.cpp" />
 		<Unit filename="src/lua/lua_Technique.h" />
+		<Unit filename="src/lua/lua_Terrain.cpp" />
+		<Unit filename="src/lua/lua_Terrain.h" />
+		<Unit filename="src/lua/lua_TerrainFlags.cpp" />
+		<Unit filename="src/lua/lua_TerrainFlags.h" />
 		<Unit filename="src/lua/lua_TextBox.cpp" />
 		<Unit filename="src/lua/lua_TextBox.h" />
 		<Unit filename="src/lua/lua_Texture.cpp" />

+ 14 - 24
gameplay/res/shaders/terrain.frag

@@ -16,40 +16,30 @@ uniform vec3 u_lightColor;                      // Light color
 uniform vec3 u_lightDirection;					// Light direction
 
 #if defined(DEBUG_PATCHES)
-uniform int u_row;
-uniform int u_column;
+uniform float u_row;
+uniform float u_column;
 #endif
 
 #if (LAYER_COUNT > 0)
 uniform sampler2D u_samplers[SAMPLER_COUNT];
-uniform int u_textureIndex[LAYER_COUNT];
-uniform vec2 u_textureRepeat[LAYER_COUNT];
-#if (LAYER_COUNT > 1)
-uniform int u_blendIndex[LAYER_COUNT-1];
-uniform int u_blendChannel[LAYER_COUNT-1];
 #endif
+
 #if defined (NORMAL_MAP)
 uniform sampler2D u_normalMap;
 #endif
-#endif
 
 #include "lib/lighting.frag"
 #include "lib/lighting-directional.frag"
 
 #if (LAYER_COUNT > 1)
-void blendLayer(int index)
+void blendLayer(sampler2D textureMap, vec2 textureRepeat, float alphaBlend)
 {
-    vec2 uv = v_texCoord0 * u_textureRepeat[index];
+    vec2 uv = mod(v_texCoord0 * textureRepeat, vec2(1,1));
 
     // Sample full intensity diffuse color
-    vec3 diffuse = texture2D(u_samplers[u_textureIndex[index]], uv).rgb;
-    
-    // Sample blend map
-    vec4 blend = texture2D(u_samplers[u_blendIndex[index-1]], v_texCoord0).rgba;
-    float channels[4] = float[4](blend.r, blend.g, blend.b, blend.a);
-    float a = channels[u_blendChannel[index-1]];
+    vec3 diffuse = texture2D(textureMap, uv).rgb;
 
-    _baseColor.rgb = _baseColor.rgb * (1.0 - a) + diffuse * a;
+    _baseColor.rgb = _baseColor.rgb * (1.0 - alphaBlend) + diffuse * alphaBlend;
 }
 #endif
 
@@ -57,30 +47,30 @@ void main()
 {
 #if (LAYER_COUNT > 0)
     // Set base diffuse color
-    vec2 uvCoord = v_texCoord0 * u_textureRepeat[0];
-	_baseColor = texture2D(u_samplers[u_textureIndex[0]], uvCoord);
+    vec2 uvCoord = mod(v_texCoord0 * TEXTURE_REPEAT_0, vec2(1,1));
+	_baseColor = texture2D(u_samplers[TEXTURE_INDEX_0], uvCoord);
 #else
     // If no layers are defined, simple use a white color
     _baseColor = vec4(1,1,1,1);
 #endif
 
 #if (LAYER_COUNT > 1)
-    blendLayer(1);
+    blendLayer(u_samplers[TEXTURE_INDEX_1], TEXTURE_REPEAT_1, texture2D(u_samplers[BLEND_INDEX_1], v_texCoord0)[BLEND_CHANNEL_1]);
 #endif
 
 #if (LAYER_COUNT > 2)
-    blendLayer(2);
+    blendLayer(u_samplers[TEXTURE_INDEX_2], TEXTURE_REPEAT_2, texture2D(u_samplers[BLEND_INDEX_2], v_texCoord0)[BLEND_CHANNEL_2]);
 #endif
 
 #if defined(DEBUG_PATCHES)
     // If patch debug drawing is enabled, tint patches alternate colors
-    float tint = mod(u_row + mod(u_column, 2), 2);
-    _baseColor.rgb = _baseColor.rgb * 0.75 + vec3(1-tint, tint, 0) * 0.25;
+    float tint = mod(u_row + mod(u_column, 2.0), 2.0);
+    _baseColor.rgb = _baseColor.rgb * 0.75 + vec3(1.0-tint, tint, 0) * 0.25;
 #endif
 
     // Light the pixel
 #if defined(NORMAL_MAP)
-    v_normalVector = normalize(texture2D(u_normalMap, v_texCoord0).xyz * 2.0f - 1.0f);
+    v_normalVector = normalize(texture2D(u_normalMap, v_texCoord0).xyz * 2.0 - 1.0);
 #endif
 
     gl_FragColor.a = _baseColor.a;

+ 1 - 0
gameplay/src/Base.h

@@ -14,6 +14,7 @@
 #include <cstdarg>
 #include <ctime>
 #include <iostream>
+#include <sstream>
 #include <string>
 #include <cstring>
 #include <vector>

+ 1 - 1
gameplay/src/HeightField.h

@@ -133,8 +133,8 @@ namespace gameplay
         static HeightField* create(const char* path, unsigned int width, unsigned int height, float minHeight, float maxHeight);
 
         float* _array;
-        unsigned int _rows;
         unsigned int _cols;
+        unsigned int _rows;
     };
 
 }

+ 39 - 15
gameplay/src/Scene.cpp

@@ -4,6 +4,7 @@
 #include "SceneLoader.h"
 #include "MeshSkin.h"
 #include "Joint.h"
+#include "Terrain.h"
 
 namespace gameplay
 {
@@ -429,6 +430,9 @@ static void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3
 
 static void drawDebugBox(MeshBatch* batch, const BoundingBox& box, const Matrix& matrix)
 {
+    if (box.isEmpty())
+        return;
+
     // Transform box into world space (since we only store local boxes on mesh)
     BoundingBox worldSpaceBox(box);
     worldSpaceBox.transform(matrix);
@@ -454,6 +458,9 @@ static void drawDebugBox(MeshBatch* batch, const BoundingBox& box, const Matrix&
 
 static void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere)
 {
+    if (sphere.isEmpty())
+        return;
+
     // Draw three rings for the sphere (one for the x, y and z axes)
     Vector3 pos1, pos2;
     float step = MATH_PI * 0.2f;
@@ -499,37 +506,54 @@ static void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere)
     }
 }
 
-static void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
+static void drawDebugNode(Scene* scene, MeshBatch* batch, Node* node, unsigned int debugFlags)
 {
     GP_ASSERT(node);
-    Model* model = node->getModel();
 
-    if ((debugFlags & Scene::DEBUG_BOXES) && model)
+    // If the node isn't visible, don't draw its bounds
+    Camera* camera = scene->getActiveCamera();
+    if (camera)
     {
-        GP_ASSERT(model->getMesh());
+        const BoundingSphere& sphere = node->getBoundingSphere();
+        if (!sphere.isEmpty() && !camera->getFrustum().intersects(sphere))
+            return;
+    }
 
-        MeshSkin* skin = model->getSkin();
-        if (skin && skin->getRootJoint() && skin->getRootJoint()->getParent())
+    if (debugFlags & Scene::DEBUG_BOXES)
+    {
+        if (node->getModel())
         {
-            // For skinned meshes that have a parent node to the skin's root joint,
-            // we need to transform the bounding volume by that parent node's transform
-            // as well to get the full skinned bounding volume.
-            drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix() * skin->getRootJoint()->getParent()->getWorldMatrix());
+            Model* model = node->getModel();
+            GP_ASSERT(model->getMesh());
+
+            MeshSkin* skin = model->getSkin();
+            if (skin && skin->getRootJoint() && skin->getRootJoint()->getParent())
+            {
+                // For skinned meshes that have a parent node to the skin's root joint,
+                // we need to transform the bounding volume by that parent node's transform
+                // as well to get the full skinned bounding volume.
+                drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix() * skin->getRootJoint()->getParent()->getWorldMatrix());
+            }
+            else
+            {
+                drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix());
+            }
         }
-        else
+
+        if (node->getTerrain())
         {
-            drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix());
+            drawDebugBox(batch, node->getTerrain()->getBoundingBox(), node->getWorldMatrix());
         }
     }
 
-    if ((debugFlags & Scene::DEBUG_SPHERES) && model)
+    if (debugFlags & Scene::DEBUG_SPHERES)
     {
         drawDebugSphere(batch, node->getBoundingSphere());
     }
 
     for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
     {
-        drawDebugNode(batch, child, debugFlags);
+        drawDebugNode(scene, batch, child, debugFlags);
     }
 }
 
@@ -554,7 +578,7 @@ void Scene::drawDebug(unsigned int debugFlags)
 
     for (Node* node = _firstNode; node != NULL; node = node->_nextSibling)
     {
-        drawDebugNode(_debugBatch, node, debugFlags);
+        drawDebugNode(this, _debugBatch, node, debugFlags);
     }
 
     _debugBatch->finish();

+ 2 - 2
gameplay/src/Terrain.cpp

@@ -283,7 +283,7 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
         // Parse terrain layers
         Properties* lp;
         int index = -1;
-        while (lp = properties->getNextNamespace())
+        while ((lp = properties->getNextNamespace()) != NULL)
         {
             if (strcmp(lp->getNamespace(), "layer") == 0)
             {
@@ -376,7 +376,7 @@ bool Terrain::setLayer(int index, const char* texturePath, const Vector2& textur
     {
         TerrainPatch* patch = _patches[i];
 
-        if ((row == -1 || patch->_row == row) && (column == -1 || patch->_column == column))
+        if ((row == -1 || (int)patch->_row == row) && (column == -1 || (int)patch->_column == column))
         {
             if (!patch->setLayer(index, texturePath, textureRepeat, blendPath, blendChannel))
                 result = false;

+ 38 - 38
gameplay/src/TerrainPatch.cpp

@@ -352,14 +352,14 @@ int TerrainPatch::addSampler(const char* path)
     if (!texture)
         return -1;
 
-    size_t firstAvailableIndex = -1;
+    int firstAvailableIndex = -1;
     for (size_t i = 0, count = _samplers.size(); i < count; ++i)
     {
         Texture::Sampler* sampler = _samplers[i];
 
         if (sampler == NULL && firstAvailableIndex == -1)
         {
-            firstAvailableIndex = i;
+            firstAvailableIndex = (int)i;
         }
         else if (sampler->getTexture() == texture)
         {
@@ -436,64 +436,64 @@ bool TerrainPatch::updateMaterial()
 
     for (size_t i = 0, count = _levels.size(); i < count; ++i)
     {
-        // Load material/shaders
-        char defines[1024];
-        sprintf(defines, "LAYER_COUNT %d;SAMPLER_COUNT %d%s%s",
-            (int)_layers.size(),
-            (int)_samplers.size(),
-            _terrain->isFlagSet(Terrain::DEBUG_PATCHES) ? ";DEBUG_PATCHES" : "",
-            _terrain->_normalMap ? ";NORMAL_MAP" : "");
-        Material* material = Material::create("res/shaders/terrain.vert", "res/shaders/terrain.frag", defines);
-        if (!material)
-            return false;
-        material->getStateBlock()->setCullFace(true);
-        material->getStateBlock()->setDepthTest(true);
-
-        // Build layer lists
-        _textureIndex.clear();
-        _textureRepeat.clear();
-        _blendIndex.clear();
-        _blendChannel.clear();
-        for (std::set<Layer*, LayerCompare>::iterator itr = _layers.begin(); itr != _layers.end(); ++itr)
+        // Build preprocessor string to pass to shader.
+        // NOTE: I make heavy use of preprocessor definitions, rather than passing in arrays and doing
+        // non-constant array access in the shader. This is due to the fact that non-constant array access
+        // in GLES is very slow on some GLES 2.x hardware.
+        std::ostringstream defines;
+        defines << "LAYER_COUNT " << _layers.size();
+        defines << ";SAMPLER_COUNT " << _samplers.size();
+        if (_terrain->isFlagSet(Terrain::DEBUG_PATCHES))
+            defines << ";DEBUG_PATCHES";
+        if (_terrain->_normalMap)
+            defines << ";NORMAL_MAP";
+
+        // Append texture and blend index constants to preprocessor definition.
+        // We need to do this since older versions of GLSL only allow sampler arrays
+        // to be indexed using constant expressions (otherwise we could simply pass an
+        // array of indices to use for sampler lookup).
+        //
+        // Rebuild layer lists while we're at it.
+        //
+        int layerIndex = 0;
+        for (std::set<Layer*, LayerCompare>::iterator itr = _layers.begin(); itr != _layers.end(); ++itr, ++layerIndex)
         {
             Layer* layer = *itr;
 
-            // Add texture parameters for every layer
-            _textureIndex.push_back(layer->textureIndex);
-            _textureRepeat.push_back(layer->textureRepeat);
+            defines << ";TEXTURE_INDEX_" << layerIndex << " " << layer->textureIndex;
+            defines << ";TEXTURE_REPEAT_" << layerIndex << " vec2(" << layer->textureRepeat.x << "," << layer->textureRepeat.y << ")";
 
-            // Add blend parameters for all but the first layer
-            if (itr != _layers.begin())
+            if (layerIndex > 0)
             {
-                _blendIndex.push_back(layer->blendIndex);
-                _blendChannel.push_back(layer->blendChannel);
+                defines << ";BLEND_INDEX_" << layerIndex << " " << layer->blendIndex;
+                defines << ";BLEND_CHANNEL_" << layerIndex << " " << layer->blendChannel;
             }
         }
 
+        Material* material = Material::create("res/shaders/terrain.vert", "res/shaders/terrain.frag", defines.str().c_str());
+        if (!material)
+            return false;
+        material->getStateBlock()->setCullFace(true);
+        material->getStateBlock()->setDepthTest(true);
+
         // Set material parameter bindings
         material->getParameter("u_worldViewProjectionMatrix")->bindValue(_terrain, &Terrain::getWorldViewProjectionMatrix);
-        material->getParameter("u_normalMatrix")->bindValue(_terrain, &Terrain::getNormalMatrix);
+        if (!_terrain->_normalMap)
+            material->getParameter("u_normalMatrix")->bindValue(_terrain, &Terrain::getNormalMatrix);
         material->getParameter("u_ambientColor")->bindValue(this, &TerrainPatch::getAmbientColor);
         material->getParameter("u_lightColor")->bindValue(this, &TerrainPatch::getLightColor);
         material->getParameter("u_lightDirection")->bindValue(this, &TerrainPatch::getLightDirection);
         if (_layers.size() > 0)
         {
             material->getParameter("u_samplers")->setValue((const Texture::Sampler**)&_samplers[0], (unsigned int)_samplers.size());
-            material->getParameter("u_textureIndex")->setValue(&_textureIndex[0], (unsigned int)_textureIndex.size());
-            material->getParameter("u_textureRepeat")->setValue(&_textureRepeat[0], (unsigned int)_textureRepeat.size());
             if (_terrain->_normalMap)
                 material->getParameter("u_normalMap")->setValue(_terrain->_normalMap);
-            if (_layers.size() > 1)
-            {
-                material->getParameter("u_blendIndex")->setValue(&_blendIndex[0], (unsigned int)_blendIndex.size());
-                material->getParameter("u_blendChannel")->setValue(&_blendChannel[0], (unsigned int)_blendChannel.size());
-            }
         }
 
         if (_terrain->isFlagSet(Terrain::DEBUG_PATCHES))
         {
-            material->getParameter("u_row")->setValue((int)_row);
-            material->getParameter("u_column")->setValue((int)_column);
+            material->getParameter("u_row")->setValue((float)_row);
+            material->getParameter("u_column")->setValue((float)_column);
         }
 
         // Set material on this lod level

+ 0 - 4
gameplay/src/TerrainPatch.h

@@ -153,10 +153,6 @@ private:
     unsigned int _column;
     std::set<Layer*, LayerCompare> _layers;
     std::vector<Texture::Sampler*> _samplers;
-    std::vector<int> _textureIndex;
-    std::vector<Vector2> _textureRepeat;
-    std::vector<int> _blendIndex;
-    std::vector<int> _blendChannel;
     bool _materialDirty;
     BoundingBox _boundingBox;
 

+ 144 - 29
gameplay/src/Texture.cpp

@@ -273,7 +273,7 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     GLuint textureId;
     GL_ASSERT( glGenTextures(1, &textureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
-    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) );
+    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMapCount > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR) );
 
     Texture* texture = new Texture();
     texture->_handle = textureId;
@@ -477,6 +477,23 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, Stream* stream, GL
     return data;
 }
 
+int getMaskByteIndex(unsigned int mask)
+{
+    switch (mask)
+    {
+    case 0xff000000:
+        return 3;
+    case 0x00ff0000:
+        return 2;
+    case 0x0000ff00:
+        return 1;
+    case 0x000000ff:
+        return 0;
+    default:
+        return -1; // no or invalid mask
+    }
+}
+
 Texture* Texture::createCompressedDDS(const char* path)
 {
     GP_ASSERT(path);
@@ -556,15 +573,17 @@ Texture* Texture::createCompressedDDS(const char* path)
     dds_mip_level* mipLevels = new dds_mip_level[header.dwMipMapCount];
     memset(mipLevels, 0, sizeof(dds_mip_level) * header.dwMipMapCount);
 
-    GLenum format, internalFormat;
+    GLenum format = 0;
+    GLenum internalFormat = 0;
     bool compressed = false;
     GLsizei width = header.dwWidth;
     GLsizei height = header.dwHeight;
-    int bytesPerBlock;
+    Texture::Format textureFormat = Texture::UNKNOWN;
 
     if (header.ddspf.dwFlags & 0x4/*DDPF_FOURCC*/)
     {
         compressed = true;
+        int bytesPerBlock;
 
         // Compressed.
         switch (header.ddspf.dwFourCC)
@@ -601,18 +620,20 @@ Texture* Texture::createCompressedDDS(const char* path)
 
         for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
         {
-            mipLevels[i].width = width;
-            mipLevels[i].height = height;
-            mipLevels[i].size =  std::max(1, (width+3) >> 2) * std::max(1, (height+3) >> 2) * bytesPerBlock;
-            mipLevels[i].data = new GLubyte[mipLevels[i].size];
+            dds_mip_level& level = mipLevels[i];
+
+            level.width = width;
+            level.height = height;
+            level.size =  std::max(1, (width+3) >> 2) * std::max(1, (height+3) >> 2) * bytesPerBlock;
+            level.data = new GLubyte[level.size];
 
-            if (stream->read(mipLevels[i].data, 1, mipLevels[i].size) != (unsigned int)mipLevels[i].size)
+            if (stream->read(level.data, 1, level.size) != (unsigned int)level.size)
             {
                 GP_ERROR("Failed to load dds compressed texture bytes for texture: %s", path);
                 
                 // Cleanup mip data.
                 for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
-                    SAFE_DELETE_ARRAY(mipLevels[i].data);
+                    SAFE_DELETE_ARRAY(level.data);
                 SAFE_DELETE_ARRAY(mipLevels);
                 return texture;
             }
@@ -621,21 +642,114 @@ Texture* Texture::createCompressedDDS(const char* path)
             height = std::max(1, height >> 1);
         }
     }
-    else if (header.ddspf.dwFlags == 0x40/*DDPF_RGB*/)
-    {
-        // RGB (uncompressed)
-        // Note: Use GL_BGR as internal format to flip bytes.
-        GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGB format is not supported.", path);
-        SAFE_DELETE_ARRAY(mipLevels);
-        return NULL;
-    }
-    else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
+    else if (header.ddspf.dwFlags & 0x40/*DDPF_RGB*/)
     {
-        // RGBA (uncompressed)
-        // Note: Use GL_BGRA as internal format to flip bytes.
-        GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGBA format is not supported.", path);
-        SAFE_DELETE_ARRAY(mipLevels);
-        return NULL;
+        // RGB/RGBA (uncompressed)
+        bool colorConvert = false;
+        unsigned int rmask = header.ddspf.dwRBitMask;
+        unsigned int gmask = header.ddspf.dwGBitMask;
+        unsigned int bmask = header.ddspf.dwBBitMask;
+        unsigned int amask = header.ddspf.dwABitMask;
+        int ridx = getMaskByteIndex(rmask);
+        int gidx = getMaskByteIndex(gmask);
+        int bidx = getMaskByteIndex(bmask);
+        int aidx = getMaskByteIndex(amask);
+
+        if (header.ddspf.dwRGBBitCount == 24)
+        {
+            format = internalFormat = GL_RGB;
+            textureFormat = Texture::RGB;
+            colorConvert = (ridx != 0) || (gidx != 1) || (bidx != 2);
+        }
+        else if (header.ddspf.dwRGBBitCount == 32)
+        {
+            format = internalFormat = GL_RGBA;
+            textureFormat = Texture::RGBA;
+            if (ridx == 0 && gidx == 1 && bidx == 2)
+            {
+                aidx = 3; // XBGR or ABGR
+                colorConvert = false;
+            }
+            else if (ridx == 2 && gidx == 1 && bidx == 0)
+            {
+                aidx = 3; // XRGB or ARGB
+                colorConvert = true;
+            }
+            else
+            {
+                format = 0; // invalid format
+            }
+        }
+
+        if (format == 0)
+        {
+            GP_ERROR("Failed to create texture from uncompressed DDS file '%s': Unsupported color format (must be one of R8G8B8, A8R8G8B8, A8B8G8R8, X8R8G8B8, X8B8G8R8.", path);
+            SAFE_DELETE_ARRAY(mipLevels);
+            return NULL;
+        }
+
+        // Read data.
+        for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+        {
+            dds_mip_level& level = mipLevels[i];
+
+            level.width = width;
+            level.height = height;
+            level.size =  width * height * (header.ddspf.dwRGBBitCount >> 3);
+            level.data = new GLubyte[level.size];
+
+            if (stream->read(level.data, 1, level.size) != (unsigned int)level.size)
+            {
+                GP_ERROR("Failed to load bytes for RGB dds texture: %s", path);
+
+                // Cleanup mip data.
+                for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+                    SAFE_DELETE_ARRAY(level.data);
+                SAFE_DELETE_ARRAY(mipLevels);
+                return texture;
+            }
+
+            width  = std::max(1, width >> 1);
+            height = std::max(1, height >> 1);
+        }
+
+        // Perform color conversion.
+        if (colorConvert)
+        {
+            // Note: While it's possible to use BGRA_EXT texture formats here and avoid CPU color conversion below,
+            // there seems to be different flavors of the BGRA extension, with some vendors requiring an internal
+            // format of RGBA and others requiring an internal format of BGRA.
+            // We could be smarter here later and skip color conversion in favor of GL_BGRA_EXT (for format
+            // and/or internal format) based on which GL extensions are available.
+            // Tip: Using A8B8G8R8 and X8B8G8R8 DDS format maps directly to GL RGBA and requires on no color conversion.
+            GLubyte *pixel, r, g, b, a;
+            if (format == GL_RGB)
+            {
+                for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+                {
+                    dds_mip_level& level = mipLevels[i];
+                    for (int j = 0; j < level.size; j += 3)
+                    {
+                        pixel = &level.data[j];
+                        r = pixel[ridx]; g = pixel[gidx]; b = pixel[bidx];
+                        pixel[0] = r; pixel[1] = g; pixel[2] = b;
+                    }
+                }
+            }
+            else if (format == GL_RGBA)
+            {
+                for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
+                {
+                    dds_mip_level& level = mipLevels[i];
+                    for (int j = 0; j < level.size; j += 4)
+                    {
+                        pixel = &level.data[j];
+                        r = pixel[ridx]; g = pixel[gidx]; b = pixel[bidx]; a = pixel[aidx];
+                        pixel[0] = r; pixel[1] = g; pixel[2] = b; pixel[3] = a;
+                    }
+                }
+            }
+        }
     }
     else
     {
@@ -644,7 +758,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
-    
+
     // Close file.
     stream->close();
 
@@ -652,7 +766,7 @@ Texture* Texture::createCompressedDDS(const char* path)
     GLuint textureId;
     GL_ASSERT( glGenTextures(1, &textureId) );
     GL_ASSERT( glBindTexture(GL_TEXTURE_2D, textureId) );
-    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, header.dwMipMapCount > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR ) );
+    GL_ASSERT( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, header.dwMipMapCount > 1 ? GL_NEAREST_MIPMAP_LINEAR : GL_LINEAR ) );
 
     // Create gameplay texture.
     texture = new Texture();
@@ -665,18 +779,18 @@ Texture* Texture::createCompressedDDS(const char* path)
     // Load texture data.
     for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
     {
+        dds_mip_level& level = mipLevels[i];
         if (compressed)
         {
-            GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, i, format, mipLevels[i].width, mipLevels[i].height, 0, mipLevels[i].size, mipLevels[i].data) );
+            GL_ASSERT( glCompressedTexImage2D(GL_TEXTURE_2D, i, format, level.width, level.height, 0, level.size, level.data) );
         }
         else
         {
-            // TODO: For uncompressed formats, set GL_UNPACK_ALIGNMENT based on stride
-            GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
+            GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, level.width, level.height, 0, format, GL_UNSIGNED_BYTE, level.data) );
         }
 
         // Clean up the texture data.
-        SAFE_DELETE_ARRAY(mipLevels[i].data);
+        SAFE_DELETE_ARRAY(level.data);
     }
 
     // Clean up mip levels structure.
@@ -729,6 +843,7 @@ void Texture::generateMipmaps()
     if (!_mipmapped)
     {
         GL_ASSERT( glBindTexture(GL_TEXTURE_2D, _handle) );
+        GL_ASSERT( glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST) );
         GL_ASSERT( glGenerateMipmap(GL_TEXTURE_2D) );
 
         _mipmapped = true;