obj-loader Tangent fix 34/136934/7
authorgreynaga <g.reynaga@samsung.com>
Mon, 3 Jul 2017 16:16:04 +0000 (17:16 +0100)
committergreynaga <g.reynaga@samsung.com>
Fri, 7 Jul 2017 13:53:05 +0000 (14:53 +0100)
The tangent was generated by vertex, and was changed to per normal, this will work if the normal are defined per vertex.

This patch is creating good results with the current models tested, but depends on the output of the exporter.

In case the tangents are not correct, the 3D model should be re-exported or generate the normals programmatically.

Change-Id: I35cdf2ce581974ab5fbc85709442bcb3a8112b8f

examples/rendering-basic-pbr/obj-loader.cpp
examples/rendering-basic-pbr/obj-loader.h

index ee711ba..bb21c9c 100644 (file)
@@ -20,7 +20,6 @@
 
 // EXTERNAL INCLUDES
 #include <dali/integration-api/debug.h>
-#include <string>
 #include <sstream>
 #include <string.h>
 
@@ -35,7 +34,7 @@ const int MAX_POINT_INDICES = 4;
 ObjLoader::ObjLoader()
 : mSceneLoaded( false ),
   mMaterialLoaded( false ),
-  mHasTexturePoints( false ),
+  mHasTextureUv( false ),
   mHasDiffuseMap( false ),
   mHasNormalMap( false ),
   mHasSpecularMap( false )
@@ -58,10 +57,10 @@ bool ObjLoader::IsMaterialLoaded()
   return mMaterialLoaded;
 }
 
-void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
+void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
                                           Dali::Vector<Vector3>& normals )
 {
-  int numFaceVertices = 3 * triangles.Size();  //Vertex per face, as each point has different normals for each face.
+  int numFaceVertices = 3 * triangles.Size();  //Vertices per face, as each vertex has different normals instance for each face.
   int normalIndex = 0;  //Tracks progress through the array of normals.
 
   normals.Clear();
@@ -71,19 +70,19 @@ void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices,
   for( unsigned long i = 0; i < triangles.Size(); i++ )
   {
     //Triangle vertices.
-    const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
-    const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
-    const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
+    const Vector3& v0 = points[triangles[i].pointIndex[0]];
+    const Vector3& v1 = points[triangles[i].pointIndex[1]];
+    const Vector3& v2 = points[triangles[i].pointIndex[2]];
 
     //Triangle edges.
     Vector3 edge1 = v1 - v0;
     Vector3 edge2 = v2 - v0;
 
-    //Using edges as vectors on the plane, cross to get the normal.
+    //Using edges as vectors on the plane, cross product to get the normal.
     Vector3 normalVector = edge1.Cross(edge2);
     normalVector.Normalize();
 
-    //Assign normals to points.
+    //Assign normal index to triangle vertex and set the normal vector to the list of normals.
     for( unsigned long j = 0; j < 3; j++, normalIndex++ )
     {
       triangles[i].normalIndex[j] = normalIndex;
@@ -92,22 +91,22 @@ void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices,
   }
 }
 
-void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
+void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
                                           Dali::Vector<Vector3>& normals )
 {
   int normalIndex = 0;  //Tracks progress through the array of normals.
 
   normals.Clear();
-  normals.Resize( vertices.Size() );  //One (averaged) normal per point.
+  normals.Resize( points.Size() );  //One (averaged) normal per point.
 
   //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
   //We then add the triangle's normal to the cumulative normals at each point of it
   for( unsigned long i = 0; i < triangles.Size(); i++ )
   {
-    //Triangle vertices.
-    const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
-    const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
-    const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
+    //Triangle points.
+    const Vector3& v0 = points[triangles[i].pointIndex[0]];
+    const Vector3& v1 = points[triangles[i].pointIndex[1]];
+    const Vector3& v2 = points[triangles[i].pointIndex[2]];
 
     //Triangle edges.
     Vector3 edge1 = v1 - v0;
@@ -116,11 +115,12 @@ void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices,
     //Using edges as vectors on the plane, cross to get the normal.
     Vector3 normalVector = edge1.Cross(edge2);
 
-    //Add this triangle's normal to the cumulative normal of each constituent point and set the index of the normal accordingly.
-    for( unsigned long j = 0; j < 3; j++, normalIndex++ )
+    //Add this triangle's normal to the cumulative normal of each constituent triangle point and set the index of the normal accordingly.
+    for( unsigned long j = 0; j < 3; j++)
     {
-      triangles[i].normalIndex[j] = triangles[i].pointIndex[j]; //Normal index matches up to vertex index, as one normal per vertex.
-      normals[triangles[i].normalIndex[j]] += normalVector;
+      normalIndex = triangles[i].pointIndex[j];
+      triangles[i].normalIndex[j] = normalIndex; //Normal index matches up to vertex index, as one normal per vertex.
+      normals[normalIndex] += normalVector;
     }
   }
 
@@ -135,9 +135,9 @@ void ObjLoader::CalculateTangentFrame()
 {
   //Reset tangent vector to hold new values.
   mTangents.Clear();
-  mTangents.Resize( mPoints.Size() );
+  mTangents.Resize( mNormals.Size() );
 
-  //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each point.
+  //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each normal.
   for ( unsigned long a = 0; a < mTriangles.Size(); a++ )
   {
     Vector3 tangentVector;
@@ -149,27 +149,29 @@ void ObjLoader::CalculateTangentFrame()
     Vector3 edge1 = v1 - v0;
     Vector3 edge2 = v2 - v0;
 
-    const Vector2& w0 = mTextures[mTriangles[a].textureIndex[0]];
-    const Vector2& w1 = mTextures[mTriangles[a].textureIndex[1]];
-    const Vector2& w2 = mTextures[mTriangles[a].textureIndex[2]];
+    const Vector2& w0 = mTextureUv[mTriangles[a].textureIndex[0]];
+    const Vector2& w1 = mTextureUv[mTriangles[a].textureIndex[1]];
+    const Vector2& w2 = mTextureUv[mTriangles[a].textureIndex[2]];
 
     float deltaU1 = w1.x - w0.x;
     float deltaV1 = w1.y - w0.y;
     float deltaU2 = w2.x - w0.x;
     float deltaV2 = w2.y - w0.y;
 
-    float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
+    // 1.0/f could cause division by zero in some cases, this factor will act
+    // as a weight of the tangent vector and it is fixed when it is normalised.
+    float f = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
 
     tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
     tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
     tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
 
-    mTangents[mTriangles[a].pointIndex[0]] += tangentVector;
-    mTangents[mTriangles[a].pointIndex[1]] += tangentVector;
-    mTangents[mTriangles[a].pointIndex[2]] += tangentVector;
+    mTangents[mTriangles[a].normalIndex[0]] += tangentVector;
+    mTangents[mTriangles[a].normalIndex[1]] += tangentVector;
+    mTangents[mTriangles[a].normalIndex[2]] += tangentVector;
   }
 
-  //Orthogonalize tangents and set binormals.
+  //Orthogonalize tangents.
   for ( unsigned long a = 0; a < mTangents.Size(); a++ )
   {
     const Vector3& n = mNormals[a];
@@ -219,14 +221,15 @@ void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
   bool mustCalculateTangents = ( mTangents.Size() == 0 ) || ( mTangents.Size() != mNormals.Size() );
 
   //However, we don't need to do this if the object doesn't use textures to begin with.
-  mustCalculateTangents &= mHasTexturePoints;
+  mustCalculateTangents &= mHasTextureUv;
 
-  //We also have to recalculate the normals if we need to calculate tangents,
-  // as we need just one normal, tangent per vertex, rather than the supplied per-face vertices.
-  //Alternatively, we need to calculate the normals if there weren't any to begin with.
-  if( mNormals.Size() == 0 || mustCalculateTangents )
+  // We calculate the normals if hard normals(flat normals) is set.
+  // Use the normals provided by the file to make the tangent calculation per normal,
+  // the correct results depends of normal generated by file, otherwise we need to recalculate
+  // the normal programmatically.
+  if( ( ( mNormals.Size() == 0 ) && mustCalculateTangents ) || !useSoftNormals )
   {
-    if( useSoftNormals || mustCalculateTangents )
+    if( useSoftNormals )
     {
       CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
     }
@@ -236,16 +239,16 @@ void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
     }
   }
 
-  if( mHasTexturePoints && mustCalculateTangents )
+  if( mHasTextureUv && mustCalculateTangents )
   {
     CalculateTangentFrame();
   }
 
-  bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
+  bool mapsCorrespond; //True if the sizes of the arrays necessary to draw the object match.
 
-  if ( mHasTexturePoints )
+  if ( mHasTextureUv )
   {
-    mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
+    mapsCorrespond = ( mPoints.Size() == mTextureUv.Size() ) && ( mTextureUv.Size() == mNormals.Size() );
   }
   else
   {
@@ -278,9 +281,9 @@ void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
 
         normals[mTriangles[ui].pointIndex[j]] = mNormals[mTriangles[ui].normalIndex[j]];
 
-        if ( mHasTexturePoints )
+        if ( mHasTextureUv )
         {
-          textures[mTriangles[ui].pointIndex[j]] = mTextures[mTriangles[ui].textureIndex[j]];
+          textures[mTriangles[ui].pointIndex[j]] = mTextureUv[mTriangles[ui].textureIndex[j]];
           tangents[mTriangles[ui].pointIndex[j]] = mTangents[mTriangles[ui].normalIndex[j]];
         }
       }
@@ -304,9 +307,9 @@ void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
         positions[index] = mPoints[mTriangles[ui].pointIndex[j]];
         normals[index] = mNormals[mTriangles[ui].normalIndex[j]];
 
-        if( mHasTexturePoints )
+        if( mHasTextureUv )
         {
-          textures[index] = mTextures[mTriangles[ui].textureIndex[j]];
+          textures[index] = mTextureUv[mTriangles[ui].textureIndex[j]];
           tangents[index] = mTangents[mTriangles[ui].normalIndex[j]];
         }
 
@@ -335,7 +338,7 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
 
   std::string strMatActual;
 
-  std::string input = objBuffer;
+  std::string input( objBuffer, fileSize );
   std::istringstream ss(input);
   ss.imbue( std::locale( "C" ) );
 
@@ -388,9 +391,8 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
     {
       isline >> texture.x;
       isline >> texture.y;
-
       texture.y = 1.0-texture.y;
-      mTextures.PushBack( texture );
+      mTextureUv.PushBack( texture );
     }
     else if ( tag == "#_#vt1" )
     {
@@ -398,7 +400,7 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
       isline >> texture.y;
 
       texture.y = 1.0-texture.y;
-      mTextures2.PushBack( texture );
+      mTextureUv2.PushBack( texture );
     }
     else if ( tag == "s" )
     {
@@ -413,7 +415,7 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
       }
 
       int numIndices = 0;
-      while( isline >> vet[numIndices] && numIndices < MAX_POINT_INDICES )
+      while( ( numIndices < MAX_POINT_INDICES ) && ( isline >> vet[numIndices] ) )
       {
         numIndices++;
       }
@@ -523,7 +525,7 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
   {
     CenterAndScale( true, mPoints );
     mSceneLoaded = true;
-    mHasTexturePoints = hasTexture;
+    mHasTextureUv = hasTexture;
     return true;
   }
 
@@ -627,7 +629,7 @@ Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
   surface.AddVertexBuffer( normalBuffer );
 
   //Some need tangent
-  if( ( objectProperties & TANGENTS ) && mHasTexturePoints )
+  if( ( objectProperties & TANGENTS ) && mHasTextureUv )
   {
     Property::Map tangentMap;
     tangentMap["aTangent"] = Property::VECTOR3;
@@ -638,7 +640,7 @@ Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
   }
 
   //Some need texture coordinates
-  if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTexturePoints )
+  if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTextureUv )
   {
     Property::Map textCoordMap;
     textCoordMap["aTexCoord"] = Property::VECTOR2;
@@ -672,8 +674,8 @@ Vector3 ObjLoader::GetSize()
 void ObjLoader::ClearArrays()
 {
   mPoints.Clear();
-  mTextures.Clear();
-  mTextures2.Clear();
+  mTextureUv.Clear();
+  mTextureUv2.Clear();
   mNormals.Clear();
   mTangents.Clear();
   mBiTangents.Clear();
@@ -685,7 +687,7 @@ void ObjLoader::ClearArrays()
 
 bool ObjLoader::IsTexturePresent()
 {
-  return mHasTexturePoints;
+  return mHasTextureUv;
 }
 
 bool ObjLoader::IsDiffuseMapPresent()
index cd0bdb5..11d289c 100644 (file)
@@ -95,8 +95,8 @@ public:
 private:
 
   Dali::Vector<Vector3>  mPoints;
-  Dali::Vector<Vector2>  mTextures;
-  Dali::Vector<Vector2>  mTextures2;
+  Dali::Vector<Vector2>  mTextureUv;
+  Dali::Vector<Vector2>  mTextureUv2;
   Dali::Vector<Vector3>  mNormals;
   Dali::Vector<Vector3>  mTangents;
   Dali::Vector<Vector3>  mBiTangents;
@@ -106,7 +106,7 @@ private:
 
   bool mSceneLoaded;
   bool mMaterialLoaded;
-  bool mHasTexturePoints;
+  bool mHasTextureUv;
 
   //Material file properties.
   bool mHasDiffuseMap;