Kaynağa Gözat

Fixed normal vector generation and transformation for terrain.
Completed Terrain::getHeight() and fixed PhysicsRigidBody::getHeight().

Steve Grenier 13 yıl önce
ebeveyn
işleme
abf2df5316

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -621,6 +621,8 @@
     <None Include="res\shaders\lib\lighting-spot.frag" />
     <None Include="res\shaders\lib\lighting-spot.vert" />
     <None Include="res\shaders\lib\lighting.frag" />
+    <None Include="res\shaders\terrain.frag" />
+    <None Include="res\shaders\terrain.vert" />
     <None Include="res\shaders\textured-bumped.frag" />
     <None Include="res\shaders\textured-bumped.vert" />
     <None Include="res\shaders\textured-unlit.frag" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -1759,6 +1759,12 @@
     <None Include="res\shaders\lib\lighting-spot.vert">
       <Filter>res\shaders\lib</Filter>
     </None>
+    <None Include="res\shaders\terrain.vert">
+      <Filter>res\shaders</Filter>
+    </None>
+    <None Include="res\shaders\terrain.frag">
+      <Filter>res\shaders</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

+ 5 - 1
gameplay/res/shaders/terrain.frag

@@ -53,7 +53,6 @@ void blendLayer(int index)
 }
 #endif
 
-// Fragment program
 void main()
 {
 #if (LAYER_COUNT > 0)
@@ -69,6 +68,10 @@ void main()
     blendLayer(1);
 #endif
 
+#if (LAYER_COUNT > 2)
+    blendLayer(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);
@@ -79,6 +82,7 @@ void main()
 #if defined(NORMAL_MAP)
     v_normalVector = normalize(texture2D(u_normalMap, v_texCoord0).xyz * 2.0f - 1.0f);
 #endif
+
     gl_FragColor.a = _baseColor.a;
     gl_FragColor.rgb = getLitPixel();
 }

+ 5 - 3
gameplay/res/shaders/terrain.vert

@@ -16,6 +16,9 @@ attribute vec2 a_texCoord3;
 
 // Uniforms
 uniform mat4 u_worldViewProjectionMatrix;					// World view projection matrix
+#ifndef NORMAL_MAP
+uniform mat4 u_normalMatrix;					            // Matrix used for normal vector transformation
+#endif
 uniform vec3 u_lightDirection;								// Direction of light
 
 // Outputs
@@ -33,7 +36,6 @@ varying vec2 v_texCoord2;
 varying vec2 v_texCoord3;
 #endif
 
-// Vertex program
 void main()
 {
     // Transform position to clip space.
@@ -41,7 +43,7 @@ void main()
 
 #ifndef NORMAL_MAP
     // Pass normal to fragment shader
-    v_normalVector = a_normal;
+    v_normalVector = (u_normalMatrix * vec4(a_normal.x, a_normal.y, a_normal.z, 0)).xyz;
 #endif
 
     v_texCoord0 = a_texCoord0;
@@ -54,5 +56,5 @@ void main()
 #endif
 #if LAYER_COUNT > 3
     v_texCoord3 = a_texCoord3;
-#endif*/
+#endif
 }

+ 11 - 2
gameplay/src/Node.cpp

@@ -858,11 +858,20 @@ const BoundingSphere& Node::getBoundingSphere() const
         bool empty = true;
         if (_terrain)
         {
+            _bounds.set(_terrain->getBoundingBox());
+            empty = false;
         }
         if (_model && _model->getMesh())
         {
-            _bounds.set(_model->getMesh()->getBoundingSphere());
-            empty = false;
+            if (empty)
+            {
+                _bounds.set(_model->getMesh()->getBoundingSphere());
+                empty = false;
+            }
+            else
+            {
+                _bounds.merge(_model->getMesh()->getBoundingSphere());
+            }
         }
         else
         {

+ 10 - 14
gameplay/src/PhysicsRigidBody.cpp

@@ -284,6 +284,11 @@ float PhysicsRigidBody::getHeight(float x, float z) const
     GP_ASSERT(_collisionShape);
     GP_ASSERT(_node);
 
+    // If our node has a terrain, call getHeight() on it since we need to factor in local
+    // scaling on the terrain into the height calculation.
+    if (_node->getTerrain())
+        return _node->getTerrain()->getHeight(x, z);
+
     // This function is only supported for heightfield rigid bodies.
     if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
@@ -296,16 +301,9 @@ float PhysicsRigidBody::getHeight(float x, float z) const
     // Ensure inverse matrix is updated so we can transform from world back into local heightfield coordinates for indexing
     if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
-        Matrix& inverse = _collisionShape->_shapeData.heightfieldData->inverse;
-        inverse.set(_node->getWorldMatrix());
-
-        // Apply heightfield local scaling
-        const btVector3& localScale = _collisionShape->getShape()->getLocalScaling();
-        if (localScale.x() != 1.0f || localScale.y() != 1.0f || localScale.z() != 1.0f)
-            inverse.scale(localScale.x(), localScale.y(), localScale.z());
-
-        inverse.invert();
         _collisionShape->_shapeData.heightfieldData->inverseIsDirty = false;
+
+        _node->getWorldMatrix().invert(&_collisionShape->_shapeData.heightfieldData->inverse);
     }
 
     // Calculate the correct x, z position relative to the heightfield data.
@@ -316,18 +314,16 @@ float PhysicsRigidBody::getHeight(float x, float z) const
     GP_ASSERT(rows > 0);
 
     Vector3 v = _collisionShape->_shapeData.heightfieldData->inverse * Vector3(x, 0.0f, z);
-    //x = (v.x + (0.5f * (cols - 1))) * cols / (cols - 1);
-    //z = (v.z + (0.5f * (rows - 1))) * rows / (rows - 1);
     x = v.x + (cols - 1) * 0.5f;
     z = v.z + (rows - 1) * 0.5f;
 
-    // Get the un-scaled height value from the HeightField
+    // Get the unscaled height value from the HeightField
     float height = _collisionShape->_shapeData.heightfieldData->heightfield->getHeight(x, z);
 
-    // Apply scaling (both world and local)
+    // Apply scale back to height
     Vector3 worldScale;
     _node->getWorldMatrix().getScale(&worldScale);
-    height *= worldScale.y * _collisionShape->getShape()->getLocalScaling().y();
+    height *= worldScale.y;
 
     return height;
 }

+ 58 - 9
gameplay/src/Terrain.cpp

@@ -19,13 +19,19 @@ namespace gameplay
 //
 #define 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
+
 /**
  * @script{ignore}
  */
 float getDefaultHeight(unsigned int width, unsigned int height);
 
 Terrain::Terrain() :
-    _heightfield(NULL), _node(NULL), _normalMap(NULL), _flags(ENABLE_FRUSTUM_CULLING | ENABLE_LEVEL_OF_DETAIL), _worldMatrixDirty(true)
+    _heightfield(NULL), _node(NULL), _normalMap(NULL), _flags(ENABLE_FRUSTUM_CULLING | ENABLE_LEVEL_OF_DETAIL),
+    _dirtyFlags(TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX)
 {
 }
 
@@ -239,6 +245,7 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
     terrain->_heightfield = heightfield;
     terrain->_localScale = scale;
 
+    // Store reference to bounding box (it is calculated and updated from TerrainPatch)
     BoundingBox& bounds = terrain->_boundingBox;
 
     if (normalMapPath)
@@ -354,7 +361,7 @@ void Terrain::setNode(Node* node)
         if (_node)
             _node->addListener(this);
 
-        _worldMatrixDirty = true;
+        _dirtyFlags |= TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX;
     }
 }
 
@@ -461,11 +468,29 @@ const BoundingBox& Terrain::getBoundingBox() const
 
 float Terrain::getHeight(float x, float z) const
 {
-    // The specified point is in world-space, so we need to transform it
-    // back into local space to index into our HeightField object.
-    // TODO
+    // Calculate the correct x, z position relative to the heightfield data.
+    float cols = _heightfield->getColumnCount();
+    float rows = _heightfield->getRowCount();
+
+    GP_ASSERT(cols > 0);
+    GP_ASSERT(rows > 0);
+
+    // Since the specified coordinates are in world space, we need to use the 
+    // inverse of our world matrix to transform the world x,z coords back into
+    // local heightfield coordinates for indexing into the height array.
+    Vector3 v = getInverseWorldMatrix() * Vector3(x, 0.0f, z);
+    x = v.x + (cols - 1) * 0.5f;
+    z = v.z + (rows - 1) * 0.5f;
+
+    // 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;
 
-    return _heightfield->getHeight(x, z);
+    return height;
 }
 
 void Terrain::draw(bool wireframe)
@@ -478,14 +503,14 @@ void Terrain::draw(bool wireframe)
 
 void Terrain::transformChanged(Transform* transform, long cookie)
 {
-    _worldMatrixDirty = true;
+    _dirtyFlags |= TERRAIN_DIRTY_WORLD_MATRIX | TERRAIN_DIRTY_INV_WORLD_MATRIX | TERRAIN_DIRTY_NORMAL_MATRIX;
 }
 
 const Matrix& Terrain::getWorldMatrix() const
 {
-    if (_worldMatrixDirty)
+    if (_dirtyFlags & TERRAIN_DIRTY_WORLD_MATRIX)
     {
-        _worldMatrixDirty = false;
+        _dirtyFlags &= ~TERRAIN_DIRTY_WORLD_MATRIX;
 
         // Apply our attached node's world matrix
         if (_node)
@@ -504,6 +529,30 @@ const Matrix& Terrain::getWorldMatrix() const
     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;
+
+        getInverseWorldMatrix().transpose(&_normalMatrix);
+    }
+
+    return _normalMatrix;
+}
+
 const Matrix& Terrain::getWorldViewProjectionMatrix() const
 {
     static Matrix worldViewProj;

+ 16 - 1
gameplay/src/Terrain.h

@@ -120,6 +120,7 @@ public:
      * @param path Path to a properties file describing the terrain.
      *
      * @return A new Terrain.
+     * @script{create}
      */
     static Terrain* create(const char* path);
 
@@ -131,6 +132,7 @@ public:
      * @return A new Terrain.
      *
      * @see create(const char*)
+     * @script{create}
      */
     static Terrain* create(Properties* properties);
 
@@ -156,6 +158,7 @@ public:
      * @param normalMapPath Path to an object-space normal map to use for terrain lighting, instead of vertex normals.
      *
      * @return A new Terrain.
+     * @script{create}
      */
     static Terrain* create(HeightField* heightfield,
         const Vector3& scale = Vector3::one(), unsigned int patchSize = 32,
@@ -338,6 +341,16 @@ private:
      */
     const Matrix& getWorldMatrix() const;
 
+    /**
+     * Returns the terrain's inverse world matrix.
+     */
+    const Matrix& getInverseWorldMatrix() const;
+
+    /**
+     * Returns a matrix to be used for transforming normal vectors for the terrain.
+     */
+    const Matrix& getNormalMatrix() const;
+
     /**
      * Returns the world view projection matrix for the terrain, factoring in terrain local scaling.
      */
@@ -350,7 +363,9 @@ private:
     Texture::Sampler* _normalMap;
     unsigned int _flags;
     mutable Matrix _worldMatrix;
-    mutable bool _worldMatrixDirty;
+    mutable Matrix _inverseWorldMatrix;
+    mutable Matrix _normalMatrix;
+    mutable unsigned int _dirtyFlags;
     BoundingBox _boundingBox;
 
 };

+ 6 - 6
gameplay/src/TerrainPatch.cpp

@@ -146,12 +146,11 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
             // Compute normal
             if (!_terrain->_normalMap)
             {
-                float maxHeight = 100;
-                Vector3 p(x, calculateHeight(heights, width, height, x, z)*maxHeight, z);
-                Vector3 w(Vector3(x>=step ? x-step : x, calculateHeight(heights, width, height, x>=step ? x-step : x, z)*maxHeight, z), p);
-                Vector3 e(Vector3(x<width-step ? x+step : x, calculateHeight(heights, width, height, x<width-step ? x+step : x, z)*maxHeight, z), p);
-                Vector3 s(Vector3(x, calculateHeight(heights, width, height, x, z>=step ? z-step : z)*maxHeight, z>=step ? z-step : z), p);
-                Vector3 n(Vector3(x, calculateHeight(heights, width, height, x, z<height-step ? z+step : z)*maxHeight, z<height-step ? z+step : z), p);
+                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 normals[4];
                 Vector3::cross(n, w, &normals[0]);
                 Vector3::cross(w, s, &normals[1]);
@@ -473,6 +472,7 @@ bool TerrainPatch::updateMaterial()
 
         // Set material parameter bindings
         material->getParameter("u_worldViewProjectionMatrix")->bindValue(_terrain, &Terrain::getWorldViewProjectionMatrix);
+        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);