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

Merge pull request #1339 from sgrenier/next

Terrain cleanup
Steve Grenier 12 лет назад
Родитель
Сommit
e76049284f

+ 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);