Kaynağa Gözat

Changed terrain to bake local scaling into the patch vertices to simplify transformations.
Changed terrain to use view-space lighting to be more consistent with other gameplay shaders.
Fixed issue with terrain normal map vectors not being properly transformed in fragment shader for terrains with world transformations.

sgrenier 12 yıl önce
ebeveyn
işleme
fd57c6d4d0

+ 6 - 11
gameplay/res/materials/terrain.material

@@ -1,28 +1,23 @@
 //
-// Terrain material file.
+// Default Terrain material file.
 // 
-// Supported terrain-specific auto-bindings:
+// In addition to the built-in Node material auto-bindings, the following terrain-specific
+// auto-bindings are also supported:
 //
-// TERRAIN_WORLD_MATRIX                 : terrain world matrix
-// TERRAIN_WORLD_VIEW_MATRIX            : terrain world*view matrix
-// TERRAIN_WORLD_VIEW_PROJECTION_MATRIX : terrain world*view*projection matrix for vertex transformations
-// TERRAIN_INVERSE_WORLD_MATRIX         : terrain inverse world matrix
-// TERRAIN_NORMAL_MATRIX                : matrix for normal vector transformations (if NOT using a normal map)
 // TERRAIN_NORMAL_MAP                   : normal map sampler (if using a normal map)
 // TERRAIN_LAYER_MAPS                   : array of texture samplers for each terrain layer
 // TERRAIN_ROW                          : row index of the current terrain patch
 // TERRAIN_COLUMN                       : column index of the current terrain patch
 //
-// To add lighting (other than ambient) to a terrain, you can add additional pass defines and 
+// To add lighting (other than ambient) to a terrain, you can add additional pass defines and
 // uniform bindings and handle them in your specific game or renderer. See the gameplay
 // terrain sample for an example.
 //
 material terrain
 {
-    u_worldViewProjectionMatrix = TERRAIN_WORLD_VIEW_PROJECTION_MATRIX
+    u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX
 
-    // For terrain lighting, use either u_normalMatrix
-    u_normalMatrix = TERRAIN_NORMAL_MATRIX
+    u_normalMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
     //u_normalMap = TERRAIN_NORMAL_MAP
 
     u_surfaceLayerMaps = TERRAIN_LAYER_MAPS

+ 8 - 2
gameplay/res/shaders/terrain.frag

@@ -41,7 +41,8 @@ uniform float u_spotLightOuterAngleCos[SPOT_LIGHT_COUNT];
 #endif
 
 #if defined (NORMAL_MAP)
-uniform sampler2D u_normalMap; 
+uniform sampler2D u_normalMap;
+uniform mat4 u_normalMatrix;
 #endif
 
 #endif
@@ -62,8 +63,12 @@ vec4 _baseColor;
 ///////////////////////////////////////////////////////////
 // Varyings
 #if defined(LIGHTING)
+#if !defined(NORMAL_MAP)
+varying vec3 v_normalVector;
+#else
 vec3 v_normalVector;
 #endif
+#endif
 
 varying vec2 v_texCoord0;
 
@@ -115,7 +120,8 @@ void main()
     #if defined(LIGHTING)
 
     #if defined(NORMAL_MAP)
-    v_normalVector = normalize(texture2D(u_normalMap, v_texCoord0).xyz * 2.0 - 1.0);
+    v_normalVector = texture2D(u_normalMap, v_texCoord0).xyz * 2.0 - 1.0;
+    v_normalVector = (u_normalMatrix * vec4(v_normalVector.x, v_normalVector.y, v_normalVector.z, 0)).xyz;
     #endif
 
     gl_FragColor.a = _baseColor.a;

+ 2 - 2
gameplay/res/shaders/terrain.vert

@@ -89,9 +89,9 @@ void main()
     gl_Position = u_worldViewProjectionMatrix * a_position;
 
     #if defined(LIGHTING)
-    
+
     #if !defined(NORMAL_MAP) 
-    v_normalVector = (u_normalMatrix * vec4(a_normal.x, a_normal.y, a_normal.z, 0)).xyz;
+    v_normalVector = normalize((u_normalMatrix * vec4(a_normal.x, a_normal.y, a_normal.z, 0)).xyz);
     #endif
 
     applyLight(a_position);

+ 1 - 1
gameplay/src/PhysicsController.cpp

@@ -1033,7 +1033,7 @@ PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, HeightFi
     // If the node has a terrain, apply the terrain's local scale to the world scale
     if (node->getTerrain())
     {
-        Vector3& tScale = node->getTerrain()->_localScale;
+        const Vector3& tScale = node->getTerrain()->_localScale;
         scale.set(scale.x * tScale.x, scale.y * tScale.y, scale.z * tScale.z);
     }
 

+ 1 - 1
gameplay/src/PhysicsRigidBody.cpp

@@ -376,7 +376,7 @@ void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
         // If the node has a terrain attached, factor in the terrain local scaling as well for the collision shape
         if (_node->getTerrain())
         {
-            Vector3& tScale = _node->getTerrain()->_localScale;
+            const Vector3& tScale = _node->getTerrain()->_localScale;
             scale.set(scale.x * tScale.x, scale.y * tScale.y, scale.z * tScale.z);
         }
 

+ 151 - 191
gameplay/src/Terrain.cpp

@@ -12,7 +12,7 @@ namespace gameplay
 
 // The default square size of terrain patches for a terrain that
 // does not have an explicitly specified patch size.
-#define DEFAULT_TERRAIN_PATCH_SIZE 32
+static const unsigned int DEFAULT_TERRAIN_PATCH_SIZE = 32;
 
 // The default height of a terrain that does not have an explicitly
 // specified terrain size, expressed as a ratio of the average
@@ -20,21 +20,15 @@ namespace gameplay
 //
 //   heightMax = (image.width + image.height) / 2 * DEFAULT_TERRAIN_HEIGHT_RATIO
 //
-#define DEFAULT_TERRAIN_HEIGHT_RATIO 0.3f
+static const float DEFAULT_TERRAIN_HEIGHT_RATIO = 0.3f;
 
-// Terrain dirty flag bits
-#define TERRAIN_DIRTY_WORLD_MATRIX 1
-#define TERRAIN_DIRTY_INV_WORLD_MATRIX 2
-#define TERRAIN_DIRTY_NORMAL_MATRIX 4
+// Terrain dirty flags
+static const unsigned int DIRTY_FLAG_INVERSE_WORLD = 1;
 
-/**
- * @script{ignore}
- */
-float getDefaultHeight(unsigned int width, unsigned int height);
+static float getDefaultHeight(unsigned int width, unsigned int height);
 
 Terrain::Terrain() :
-    _heightfield(NULL), _node(NULL), _normalMap(NULL), _flags(FRUSTUM_CULLING | LEVEL_OF_DETAIL),
-    _dirtyFlags(TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX)
+    _heightfield(NULL), _node(NULL), _normalMap(NULL), _flags(FRUSTUM_CULLING | LEVEL_OF_DETAIL), _dirtyFlags(DIRTY_FLAG_INVERSE_WORLD)
 {
 }
 
@@ -77,129 +71,132 @@ Terrain* Terrain::create(const char* path, Properties* properties)
         p = Properties::create(path);
     }
 
-    if (p)
+    if (!p)
     {
-        pTerrain = strlen(p->getNamespace()) > 0 ? p : p->getNextNamespace();
-        if (pTerrain == NULL)
+        GP_WARN("Failed to properties for terrain: %s", path ? path : "");
+        return NULL;
+    }
+
+    pTerrain = strlen(p->getNamespace()) > 0 ? p : p->getNextNamespace();
+    if (pTerrain == NULL)
+    {
+        GP_WARN("Invalid terrain definition.");
+        if (!externalProperties)
+            SAFE_DELETE(p);
+        return NULL;
+    }
+
+    // Read heightmap info
+    Properties* pHeightmap = pTerrain->getNamespace("heightmap", true);
+    if (pHeightmap)
+    {
+        // Read heightmap path
+        std::string heightmap;
+        if (!pHeightmap->getPath("path", &heightmap))
         {
-            GP_WARN("Invalid terrain definition.");
+            GP_WARN("No 'path' property supplied in heightmap section of terrain definition: %s", path);
             if (!externalProperties)
                 SAFE_DELETE(p);
             return NULL;
         }
 
-        // Read heightmap info
-        Properties* pHeightmap = pTerrain->getNamespace("heightmap", true);
-        if (pHeightmap)
+        std::string ext = FileSystem::getExtension(heightmap.c_str());
+        if (ext == ".PNG")
         {
-            // Read heightmap path
-            std::string heightmap;
-            if (!pHeightmap->getPath("path", &heightmap))
+            // Read normalized height values from heightmap image
+            heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
+        }
+        else if (ext == ".RAW" || ext == ".R16")
+        {
+            // Require additional properties to be specified for RAW files
+            Vector2 imageSize;
+            if (!pHeightmap->getVector2("size", &imageSize))
             {
-                GP_WARN("No 'path' property supplied in heightmap section of terrain definition: %s", path);
+                GP_WARN("Invalid or missing 'size' attribute in heightmap defintion of terrain definition: %s", path);
                 if (!externalProperties)
                     SAFE_DELETE(p);
                 return NULL;
             }
 
-            std::string ext = FileSystem::getExtension(heightmap.c_str());
-            if (ext == ".PNG")
-            {
-                // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
-            }
-            else if (ext == ".RAW" || ext == ".R16")
-            {
-                // Require additional properties to be specified for RAW files
-                Vector2 imageSize;
-                if (!pHeightmap->getVector2("size", &imageSize))
-                {
-                    GP_WARN("Invalid or missing 'size' attribute in heightmap defintion of terrain definition: %s", path);
-                    if (!externalProperties)
-                        SAFE_DELETE(p);
-                    return NULL;
-                }
-
-                // Read normalized height values from RAW file
-                heightfield = HeightField::createFromRAW(heightmap.c_str(), (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
-            }
-            else
-            {
-                // Unsupported heightmap format
-                GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap.c_str(), path);
-                if (!externalProperties)
-                    SAFE_DELETE(p);
-                return NULL;
-            }
+            // Read normalized height values from RAW file
+            heightfield = HeightField::createFromRAW(heightmap.c_str(), (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
         }
         else
         {
-            // Try to read 'heightmap' as a simple string property
-            std::string heightmap;
-            if (!pTerrain->getPath("heightmap", &heightmap))
-            {
-                GP_WARN("No 'heightmap' property supplied in terrain definition: %s", path);
-                if (!externalProperties)
-                    SAFE_DELETE(p);
-                return NULL;
-            }
-
-            std::string ext = FileSystem::getExtension(heightmap.c_str());
-            if (ext == ".PNG")
-            {
-                // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
-            }
-            else if (ext == ".RAW" || ext == ".R16")
-            {
-                GP_WARN("RAW heightmaps must be specified inside a heightmap block with width and height properties.");
-                if (!externalProperties)
-                    SAFE_DELETE(p);
-                return NULL;
-            }
-            else
-            {
-                GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap.c_str(), path);
-                if (!externalProperties)
-                    SAFE_DELETE(p);
-                return NULL;
-            }
+            // Unsupported heightmap format
+            GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap.c_str(), path);
+            if (!externalProperties)
+                SAFE_DELETE(p);
+            return NULL;
         }
-
-        // Read terrain 'size'
-        if (pTerrain->exists("size"))
+    }
+    else
+    {
+        // Try to read 'heightmap' as a simple string property
+        std::string heightmap;
+        if (!pTerrain->getPath("heightmap", &heightmap))
         {
-            if (!pTerrain->getVector3("size", &terrainSize))
-            {
-                GP_WARN("Invalid 'size' value ('%s') in terrain definition: %s", pTerrain->getString("size"), path);
-            }
+            GP_WARN("No 'heightmap' property supplied in terrain definition: %s", path);
+            if (!externalProperties)
+                SAFE_DELETE(p);
+            return NULL;
         }
 
-        // Read terrain 'patch size'
-        if (pTerrain->exists("patchSize"))
+        std::string ext = FileSystem::getExtension(heightmap.c_str());
+        if (ext == ".PNG")
         {
-            patchSize = pTerrain->getInt("patchSize");
+            // Read normalized height values from heightmap image
+            heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
         }
-
-        // Read terrain 'detailLevels'
-        if (pTerrain->exists("detailLevels"))
+        else if (ext == ".RAW" || ext == ".R16")
         {
-            detailLevels = pTerrain->getInt("detailLevels");
+            GP_WARN("RAW heightmaps must be specified inside a heightmap block with width and height properties.");
+            if (!externalProperties)
+                SAFE_DELETE(p);
+            return NULL;
         }
+        else
+        {
+            GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap.c_str(), path);
+            if (!externalProperties)
+                SAFE_DELETE(p);
+            return NULL;
+        }
+    }
 
-        // Read 'skirtScale'
-        if (pTerrain->exists("skirtScale"))
+    // Read terrain 'size'
+    if (pTerrain->exists("size"))
+    {
+        if (!pTerrain->getVector3("size", &terrainSize))
         {
-            skirtScale = pTerrain->getFloat("skirtScale");
+            GP_WARN("Invalid 'size' value ('%s') in terrain definition: %s", pTerrain->getString("size"), path);
         }
+    }
 
-        // Read 'normalMap'
-        normalMap = pTerrain->getString("normalMap");
+    // Read terrain 'patch size'
+    if (pTerrain->exists("patchSize"))
+    {
+        patchSize = pTerrain->getInt("patchSize");
+    }
 
-        // Read 'material'
-        materialPath = pTerrain->getString("material", "");
+    // Read terrain 'detailLevels'
+    if (pTerrain->exists("detailLevels"))
+    {
+        detailLevels = pTerrain->getInt("detailLevels");
     }
 
+    // Read 'skirtScale'
+    if (pTerrain->exists("skirtScale"))
+    {
+        skirtScale = pTerrain->getFloat("skirtScale");
+    }
+
+    // Read 'normalMap'
+    normalMap = pTerrain->getString("normalMap");
+
+    // Read 'material'
+    materialPath = pTerrain->getString("material", "");
+
     if (heightfield == NULL)
     {
         GP_WARN("Failed to read heightfield heights for terrain definition: %s", path);
@@ -215,7 +212,7 @@ Terrain* Terrain::create(const char* path, Properties* properties)
 
     if (patchSize <= 0 || patchSize > (int)heightfield->getColumnCount() || patchSize > (int)heightfield->getRowCount())
     {
-        patchSize = std::min(heightfield->getRowCount(), std::min(heightfield->getColumnCount(), (unsigned int)DEFAULT_TERRAIN_PATCH_SIZE));
+        patchSize = std::min(heightfield->getRowCount(), std::min(heightfield->getColumnCount(), DEFAULT_TERRAIN_PATCH_SIZE));
     }
 
     if (detailLevels <= 0)
@@ -253,9 +250,11 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale,
     // Create the terrain object
     Terrain* terrain = new Terrain();
     terrain->_heightfield = heightfield;
-    terrain->_localScale = scale;
     terrain->_materialPath = (materialPath == NULL || strlen(materialPath) == 0) ? TERRAIN_MATERIAL : materialPath;
 
+    // Store terrain local scaling so it can be applied to the heightfield
+    terrain->_localScale.set(scale);
+
     // Store reference to bounding box (it is calculated and updated from TerrainPatch)
     BoundingBox& bounds = terrain->_boundingBox;
 
@@ -264,6 +263,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale,
 
     float halfWidth = (width - 1) * 0.5f;
     float halfHeight = (height - 1) * 0.5f;
+
+    // Compute the maximum step size, which is a function of our lowest level of detail.
+    // This determines how many vertices will be skipped per triange/quad on the lowest
+    // level detail terrain patch.
     unsigned int maxStep = (unsigned int)std::pow(2.0, (double)(detailLevels-1));
 
     // Create terrain patches
@@ -373,21 +376,49 @@ void Terrain::setNode(Node* node)
     if (_node != node)
     {
         if (_node)
+        {
             _node->removeListener(this);
+        }
 
         _node = node;
 
-        // Update node bindings for all materails
+        if (_node)
+        {
+            _node->addListener(this);
+        }
+
+        // Update patch node bindings
         for (size_t i = 0, count = _patches.size(); i < count; ++i)
         {
-            _patches[i]->updateNodeBinding(_node);
+            _patches[i]->updateNodeBindings();
         }
 
+        _dirtyFlags |= DIRTY_FLAG_INVERSE_WORLD;
+    }
+}
+
+void Terrain::transformChanged(Transform* transform, long cookie)
+{
+    _dirtyFlags |= DIRTY_FLAG_INVERSE_WORLD;
+}
+
+const Matrix& Terrain::getInverseWorldMatrix() const
+{
+    if (_dirtyFlags & DIRTY_FLAG_INVERSE_WORLD)
+    {
+        _dirtyFlags &= ~DIRTY_FLAG_INVERSE_WORLD;
+
         if (_node)
-            _node->addListener(this);
+            _inverseWorldMatrix.set(_node->getWorldMatrix());
+        else
+            _inverseWorldMatrix = Matrix::identity();
 
-        _dirtyFlags |= TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX;
+        // Apply local scale and invert
+        _inverseWorldMatrix.scale(_localScale);
+        _inverseWorldMatrix.invert();
+        
     }
+    return _inverseWorldMatrix;
 }
 
 bool Terrain::setLayer(int index, const char* texturePath, const Vector2& textureRepeat, const char* blendPath, int blendChannel, int row, int column)
@@ -484,10 +515,16 @@ float Terrain::getHeight(float x, float z) const
     // Get the unscaled height value from the HeightField
     float height = _heightfield->getHeight(x, z);
 
-    // Now apply world scale (this includes local terrain scale) to the heightfield value
-    Vector3 worldScale;
-    getWorldMatrix().getScale(&worldScale);
-    height *= worldScale.y;
+    // Apply world scale to the height value
+    if (_node)
+    {
+        Vector3 worldScale;
+        _node->getWorldMatrix().getScale(&worldScale);
+        height *= worldScale.y;
+    }
+
+    // Apply local scale
+    height *= _localScale.y;
 
     return height;
 }
@@ -502,84 +539,7 @@ unsigned int Terrain::draw(bool wireframe)
     return visibleCount;
 }
 
-void Terrain::transformChanged(Transform* transform, long cookie)
-{
-    _dirtyFlags |= TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX;
-}
-
-const Matrix& Terrain::getWorldMatrix() const
-{
-    if (_dirtyFlags & TERRAIN_DIRTY_WORLD_MATRIX)
-    {
-        _dirtyFlags &= ~TERRAIN_DIRTY_WORLD_MATRIX;
-
-        // Apply our attached node's world matrix
-        if (_node)
-        {
-            _worldMatrix = _node->getWorldMatrix();
-        }
-        else
-        {
-            _worldMatrix.setIdentity();
-        }
-
-        // Factor in our local scaling
-        _worldMatrix.scale(_localScale);
-    }
-
-    return _worldMatrix;
-}
-
-const Matrix& Terrain::getInverseWorldMatrix() const
-{
-    if (_dirtyFlags & TERRAIN_DIRTY_INV_WORLD_MATRIX)
-    {
-        _dirtyFlags &= ~TERRAIN_DIRTY_INV_WORLD_MATRIX;
-
-        getWorldMatrix().invert(&_inverseWorldMatrix);
-    }
-
-    return _inverseWorldMatrix;
-}
-
-const Matrix& Terrain::getNormalMatrix() const
-{
-    if (_dirtyFlags & TERRAIN_DIRTY_NORMAL_MATRIX)
-    {
-        _dirtyFlags &= ~TERRAIN_DIRTY_NORMAL_MATRIX;
-
-        // Note: Terrain lighting is done in world space to simplify use of object-space height normal maps.
-        getInverseWorldMatrix().transpose(&_normalMatrix);
-    }
-
-    return _normalMatrix;
-}
-
-const Matrix& Terrain::getWorldViewMatrix() const
-{
-    static Matrix worldView;
-
-    if (_node)
-        Matrix::multiply(_node->getViewMatrix(), getWorldMatrix(), &worldView);
-    else
-        worldView = getWorldMatrix(); // no node, so nothing to get view from
-
-    return worldView;
-}
-
-const Matrix& Terrain::getWorldViewProjectionMatrix() const
-{
-    static Matrix worldViewProj;
-
-    if (_node)
-        Matrix::multiply(_node->getViewProjectionMatrix(), getWorldMatrix(), &worldViewProj);
-    else
-        worldViewProj = getWorldMatrix(); // no node, so nothing to get viewProjection from
-
-    return worldViewProj;
-}
-
-float getDefaultHeight(unsigned int width, unsigned int height)
+static float getDefaultHeight(unsigned int width, unsigned int height)
 {
     // When terrain height is not specified, we'll use a default height of ~ 0.3 of the image dimensions
     return ((width + height) * 0.5f) * DEFAULT_TERRAIN_HEIGHT_RATIO;

+ 4 - 39
gameplay/src/Terrain.h

@@ -80,7 +80,7 @@ class TerrainAutoBindingResolver;
  *
  * @see http://blackberry.github.io/GamePlay/docs/file-formats.html#wiki-Terrain
  */
-class Terrain : public Ref, public Transform::Listener
+class Terrain : public Ref, private Transform::Listener
 {
     friend class Node;
     friend class PhysicsController;
@@ -318,48 +318,15 @@ private:
 
     /**
      * @see Transform::Listener::transformChanged.
-     *
-     * Internal use only.
-     *
-     * @script{ignore}
      */
     void transformChanged(Transform* transform, long cookie);
 
     /**
-     * Returns the world matrix of the terrain, factoring in terrain local scaling.
-     *
-     * @return The world matrix for the terrain.
-     */
-    const Matrix& getWorldMatrix() const;
-
-    /**
-     * Returns the world view matrix for the terrain, factoring in terrain local scaling.
-     *
-     * @return The world-view matrix for the terrain.
-     */
-    const Matrix& getWorldViewMatrix() const;
-
-    /**
-     * Returns the world view projection matrix for the terrain, factoring in terrain local scaling.
-     *
-     * @return The world-view-projection matrix for the terrain.
-     */
-    const Matrix& getWorldViewProjectionMatrix() const;
-
-    /**
-     * Returns the terrain's inverse world matrix.
-     *
-     * @return The inverse world matrix for the terrain.
+     * Returns the terrain's inverse world matrix, used for transforming world-space positions
+     * to local positions for height lookups.
      */
     const Matrix& getInverseWorldMatrix() const;
 
-    /**
-     * Returns a matrix to be used for transforming normal vectors for the terrain.
-     *
-     * @return The matrix used for normal vector transformation for the terrain.
-     */
-    const Matrix& getNormalMatrix() const;
-
     /**
      * Returns the local bounding box for this patch, at the base LOD level.
      */
@@ -368,13 +335,11 @@ private:
     std::string _materialPath;
     HeightField* _heightfield;
     Node* _node;
-    std::vector<TerrainPatch*> _patches;
     Vector3 _localScale;
+    std::vector<TerrainPatch*> _patches;
     Texture::Sampler* _normalMap;
     unsigned int _flags;
-    mutable Matrix _worldMatrix;
     mutable Matrix _inverseWorldMatrix;
-    mutable Matrix _normalMatrix;
     mutable unsigned int _dirtyFlags;
     BoundingBox _boundingBox;
 };

+ 55 - 120
gameplay/src/TerrainPatch.cpp

@@ -8,11 +8,6 @@
 namespace gameplay
 {
 
-/**
- * @script{ignore}
- */
-float calculateHeight(float* heights, unsigned int width, unsigned int height, unsigned int x, unsigned int z);
-
 /**
  * @script{ignore}
  */
@@ -79,13 +74,6 @@ TerrainPatch* TerrainPatch::create(Terrain* terrain, unsigned int index,
     BoundingBox& bounds = patch->_boundingBox;
     bounds.set(patch->_levels[0]->model->getMesh()->getBoundingBox());
 
-    // Apply the terrain's local scale to our bounds
-    const Vector3& localScale = terrain->_localScale;
-    if (!localScale.isOne())
-    {
-        bounds.min.set(bounds.min.x * localScale.x, bounds.min.y * localScale.y, bounds.min.z * localScale.z);
-        bounds.max.set(bounds.max.x * localScale.x, bounds.max.y * localScale.y, bounds.max.z * localScale.z);
-    }
     return patch;
 }
 
@@ -147,6 +135,8 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
     unsigned int index = 0;
     Vector3 min(FLT_MAX, FLT_MAX, FLT_MAX);
     Vector3 max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+    float stepXScaled = step * _terrain->_localScale.x;
+    float stepZScaled = step * _terrain->_localScale.z;
     bool zskirt = verticalSkirtSize > 0 ? true : false;
     for (unsigned int z = z1; ; )
     {
@@ -158,12 +148,12 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
             float* v = vertices + (index * vertexElements);
             index++;
 
-            // Compute position
-            v[0] = x + xOffset;
-            v[1] = calculateHeight(heights, width, height, x, z);
+            // Compute position - apply the local scale of the terrain into the vertex data
+            v[0] = (x + xOffset) * _terrain->_localScale.x;
+            v[1] = computeHeight(heights, width, x, z);
             if (xskirt || zskirt)
-                v[1] -= verticalSkirtSize;
-            v[2] = z + zOffset;
+                v[1] -= verticalSkirtSize * _terrain->_localScale.y;
+            v[2] = (z + zOffset) * _terrain->_localScale.z;
 
             // Update bounding box min/max (don't include vertical skirt vertices in bounding box)
             if (!(xskirt || zskirt))
@@ -181,16 +171,15 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
                 if (v[2] > max.z)
                     max.z = v[2];
             }
-            v += 3;
 
             // Compute normal
             if (!_terrain->_normalMap)
             {
-                Vector3 p(x, calculateHeight(heights, width, height, x, z), z);
-                Vector3 w(Vector3(x>=step ? x-step : x, calculateHeight(heights, width, height, x>=step ? x-step : x, z), z), p);
-                Vector3 e(Vector3(x<width-step ? x+step : x, calculateHeight(heights, width, height, x<width-step ? x+step : x, z), z), p);
-                Vector3 s(Vector3(x, calculateHeight(heights, width, height, x, z>=step ? z-step : z), z>=step ? z-step : z), p);
-                Vector3 n(Vector3(x, calculateHeight(heights, width, height, x, z<height-step ? z+step : z), z<height-step ? z+step : z), p);
+                Vector3 p(v[0], computeHeight(heights, width, x, z), v[2]);
+                Vector3 w(Vector3(x>=step ? v[0]-stepXScaled : v[0], computeHeight(heights, width, x>=step ? x-step : x, z), v[2]), p);
+                Vector3 e(Vector3(x<width-step ? v[0]+stepXScaled : v[0], computeHeight(heights, width, x<width-step ? x+step : x, z), v[2]), p);
+                Vector3 s(Vector3(v[0], computeHeight(heights, width, x, z>=step ? z-step : z), z>=step ? v[2]-stepZScaled : v[2]), p);
+                Vector3 n(Vector3(v[0], computeHeight(heights, width, x, z<height-step ? z+step : z), z<height-step ? v[2]+stepZScaled : v[2]), p);
                 Vector3 normals[4];
                 Vector3::cross(n, w, &normals[0]);
                 Vector3::cross(w, s, &normals[1]);
@@ -198,12 +187,14 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
                 Vector3::cross(s, e, &normals[3]);
                 Vector3 normal = -(normals[0] + normals[1] + normals[2] + normals[3]);
                 normal.normalize();
-                v[0] = normal.x;
-                v[1] = normal.y;
-                v[2] = normal.z;
+                v[3] = normal.x;
+                v[4] = normal.y;
+                v[5] = normal.z;
                 v += 3;
             }
 
+            v += 3;
+
             // Compute texture coord
             v[0] = (float)x / width;
             v[1] = 1.0f - (float)z / height;
@@ -540,8 +531,7 @@ bool TerrainPatch::updateMaterial()
             return false;
         }
 
-        if (_terrain->_node)
-            material->setNodeBinding(_terrain->_node);
+        material->setNodeBinding(_terrain->_node);
 
         // Set material on this lod level
         _levels[i]->model->setMaterial(material);
@@ -554,6 +544,16 @@ bool TerrainPatch::updateMaterial()
     return true;
 }
 
+void TerrainPatch::updateNodeBindings()
+{
+    __currentPatchIndex = _index;
+    for (size_t i = 0, count = _levels.size(); i < count; ++i)
+    {
+        _levels[i]->model->getMaterial()->setNodeBinding(_terrain->_node);
+    }
+    __currentPatchIndex = -1;
+}
+
 unsigned int TerrainPatch::draw(bool wireframe)
 {
     Scene* scene = _terrain->_node ? _terrain->_node->getScene() : NULL;
@@ -592,9 +592,6 @@ const BoundingBox& TerrainPatch::getBoundingBox(bool worldSpace) const
     _boundingBoxWorld.set(_boundingBox);
 
     // Transform the bounding box by the terrain node's world transform.
-    // We don't use Terrain::getWorldMatrix because that returns a matrix
-    // that has terrain->_localScale factored in - and our patche's bounding
-    // box already has local scale factored in.
     if (_terrain->_node)
         _boundingBoxWorld.transform(_terrain->_node->getWorldMatrix());
 
@@ -676,9 +673,9 @@ void TerrainPatch::setMaterialDirty()
     _bits |= TERRAINPATCH_DIRTY_MATERIAL;
 }
 
-float calculateHeight(float* heights, unsigned int width, unsigned int height, unsigned int x, unsigned int z)
+float TerrainPatch::computeHeight(float* heights, unsigned int width, unsigned int x, unsigned int z)
 {
-    return heights[z * width + x];
+    return heights[z * width + x] * _terrain->_localScale.y;
 }
 
 TerrainPatch::Layer::Layer() :
@@ -699,112 +696,50 @@ bool TerrainPatch::LayerCompare::operator() (const Layer* lhs, const Layer* rhs)
     return (lhs->index < rhs->index);
 }
 
-void TerrainPatch::updateNodeBinding(Node* node)
-{
-    __currentPatchIndex = _index;
-
-    for (size_t i = 0, count = _levels.size(); i < count; ++i)
-    {
-        _levels[i]->model->getMaterial()->setNodeBinding(node);
-    }
-
-    __currentPatchIndex = -1;
-}
-
 bool TerrainAutoBindingResolver::resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter)
 {
-    if (strcmp(autoBinding, "TERRAIN_WORLD_MATRIX") == 0)
+    // Local helper functions
+    struct HelperFunctions
     {
-        Terrain* terrain = node->getTerrain();
-        if (terrain)
+        static TerrainPatch* getPatch(Node* node)
         {
-            parameter->bindValue(terrain, &Terrain::getWorldMatrix);
-            return true;
-        }
-    }
-    else if (strcmp(autoBinding, "TERRAIN_WORLD_VIEW_MATRIX") == 0)
-    {
-        Terrain* terrain = node->getTerrain();
-        if (terrain)
-        {
-            parameter->bindValue(terrain, &Terrain::getWorldViewMatrix);
-            return true;
-        }
-    }
-    else if (strcmp(autoBinding, "TERRAIN_WORLD_VIEW_PROJECTION_MATRIX") == 0)
-    {
-        Terrain* terrain = node->getTerrain();
-        if (terrain)
-        {
-            parameter->bindValue(terrain, &Terrain::getWorldViewProjectionMatrix);
-            return true;
-        }
-    }
-    else if (strcmp(autoBinding, "TERRAIN_INVERSE_WORLD_MATRIX") == 0)
-    {
-        Terrain* terrain = node->getTerrain();
-        if (terrain)
-        {
-            parameter->bindValue(terrain, &Terrain::getInverseWorldMatrix);
-            return true;
-        }
-    }
-    else if (strcmp(autoBinding, "TERRAIN_NORMAL_MATRIX") == 0)
-    {
-        Terrain* terrain = node->getTerrain();
-        if (terrain)
-        {
-            parameter->bindValue(terrain, &Terrain::getNormalMatrix);
-            return true;
-        }
-    }
-    else if (strcmp(autoBinding, "TERRAIN_LAYER_MAPS") == 0)
-    {
-        Terrain* terrain = node->getTerrain();
-        if (terrain && __currentPatchIndex >= 0 && __currentPatchIndex < (int)terrain->_patches.size())
-        {
-            TerrainPatch* patch = terrain->_patches[__currentPatchIndex];
-            if (patch && patch->_layers.size() > 0)
+            Terrain* terrain = node->getTerrain();
+            if (terrain)
             {
-                parameter->setValue((const Texture::Sampler**)&patch->_samplers[0], (unsigned int)patch->_samplers.size());
+                if (__currentPatchIndex >= 0 && __currentPatchIndex < (int)terrain->_patches.size())
+                    return terrain->_patches[__currentPatchIndex];
             }
-            return true;
+            return NULL;
         }
+    };
+
+    if (strcmp(autoBinding, "TERRAIN_LAYER_MAPS") == 0)
+    {
+        TerrainPatch* patch = HelperFunctions::getPatch(node);
+        if (patch && patch->_layers.size() > 0)
+            parameter->setValue((const Texture::Sampler**)&patch->_samplers[0], (unsigned int)patch->_samplers.size());
+        return true;
     }
     else if (strcmp(autoBinding, "TERRAIN_NORMAL_MAP") == 0)
     {
         Terrain* terrain = node->getTerrain();
         if (terrain && terrain->_normalMap)
-        {
             parameter->setValue(terrain->_normalMap);
-            return true;
-        }
+        return true;
     }
     else if (strcmp(autoBinding, "TERRAIN_ROW") == 0)
     {
-        Terrain* terrain = node->getTerrain();
-        if (terrain && __currentPatchIndex >= 0 && __currentPatchIndex < (int)terrain->_patches.size())
-        {
-            TerrainPatch* patch = terrain->_patches[__currentPatchIndex];
-            if (patch)
-            {
-                parameter->setValue((float)patch->_row);
-                return true;
-            }
-        }
+        TerrainPatch* patch = HelperFunctions::getPatch(node);
+        if (patch)
+            parameter->setValue((float)patch->_row);
+        return true;
     }
     else if (strcmp(autoBinding, "TERRAIN_COLUMN") == 0)
     {
-        Terrain* terrain = node->getTerrain();
-        if (terrain && __currentPatchIndex >= 0 && __currentPatchIndex < (int)terrain->_patches.size())
-        {
-            TerrainPatch* patch = terrain->_patches[__currentPatchIndex];
-            if (patch)
-            {
-                parameter->setValue((float)patch->_column);
-                return true;
-            }
-        }
+        TerrainPatch* patch = HelperFunctions::getPatch(node);
+        if (patch)
+            parameter->setValue((float)patch->_column);
+        return true;
     }
 
     return false;

+ 3 - 1
gameplay/src/TerrainPatch.h

@@ -132,7 +132,9 @@ private:
 
     void setMaterialDirty();
 
-    void updateNodeBinding(Node* node);
+    float computeHeight(float* heights, unsigned int width, unsigned int x, unsigned int z);
+
+    void updateNodeBindings();
 
     std::string passCreated(Pass* pass);
 

+ 1 - 1
samples/browser/res/common/terrain/shapes.material

@@ -4,7 +4,7 @@ material textured
     u_inverseTransposeWorldViewMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
     
     u_ambientColor = SCENE_AMBIENT_COLOR
-    u_directionalLightDirection[0] = LIGHT0_DIRECTION_VIEW
+    u_directionalLightDirection[0] = LIGHT0_DIRECTION
     u_directionalLightColor[0] = LIGHT0_COLOR
 
     sampler u_diffuseTexture

+ 3 - 4
samples/browser/res/common/terrain/terrain.material

@@ -1,15 +1,14 @@
 
 material terrain
 {
-    u_worldViewProjectionMatrix = TERRAIN_WORLD_VIEW_PROJECTION_MATRIX
+    u_worldViewProjectionMatrix = WORLD_VIEW_PROJECTION_MATRIX
 
-    //u_normalMatrix = TERRAIN_NORMAL_MATRIX
+    u_normalMatrix = INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX
     u_normalMap = TERRAIN_NORMAL_MAP
-
     u_surfaceLayerMaps = TERRAIN_LAYER_MAPS
 
     u_ambientColor = SCENE_AMBIENT_COLOR
-    u_directionalLightDirection[0] = LIGHT0_DIRECTION_WORLD
+    u_directionalLightDirection[0] = LIGHT0_DIRECTION
     u_directionalLightColor[0] = LIGHT0_COLOR
 
     renderState

+ 3 - 13
samples/browser/src/TerrainSample.cpp

@@ -369,12 +369,7 @@ void TerrainSample::setMessage(const char* message)
     _form->getControl("messageBox")->setVisible(message ? true : false);
 }
 
-Vector3 TerrainSample::getLight0DirectionWorld() const
-{
-    return _directionalLight->getNode()->getForwardVectorWorld();
-}
-
-Vector3 TerrainSample::getLight0DirectionView() const
+Vector3 TerrainSample::getLight0Direction() const
 {
     return _directionalLight->getNode()->getForwardVectorView();
 }
@@ -386,14 +381,9 @@ Vector3 TerrainSample::getLight0Color() const
 
 bool TerrainSample::resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter)
 {
-    if (strcmp(autoBinding, "LIGHT0_DIRECTION_WORLD") == 0)
-    {
-        parameter->bindValue(this, &TerrainSample::getLight0DirectionWorld);
-        return true;
-    }
-    if (strcmp(autoBinding, "LIGHT0_DIRECTION_VIEW") == 0)
+    if (strcmp(autoBinding, "LIGHT0_DIRECTION") == 0)
     {
-        parameter->bindValue(this, &TerrainSample::getLight0DirectionView);
+        parameter->bindValue(this, &TerrainSample::getLight0Direction);
         return true;
     }
     else if (strcmp(autoBinding, "LIGHT0_COLOR") == 0)

+ 1 - 2
samples/browser/src/TerrainSample.h

@@ -47,8 +47,7 @@ private:
         MODE_DROP_BOX
     };
 
-    Vector3 getLight0DirectionWorld() const;
-    Vector3 getLight0DirectionView() const;
+    Vector3 getLight0Direction() const;
     Vector3 getLight0Color() const;
 
     bool resolveAutoBinding(const char* autoBinding, Node* node, MaterialParameter* parameter);