Normal-less objects now have their normals calculated so that they can be displayed... 25/77425/14
authorAndrew Poor <andrew.poor@samsung.com>
Wed, 29 Jun 2016 14:01:06 +0000 (15:01 +0100)
committerAndrew Poor <andrew.poor@samsung.com>
Fri, 22 Jul 2016 13:28:48 +0000 (14:28 +0100)
Change-Id: Ia34116fd1d2c94aa20747bd5a273d8c807a75be6

12 files changed:
automated-tests/resources/Cube-Points-Only.obj [new file with mode: 0644]
automated-tests/src/dali-toolkit/utc-Dali-ControlRenderer.cpp
automated-tests/src/dali-toolkit/utc-Dali-RendererFactory.cpp
dali-toolkit/internal/controls/model3d-view/model3d-view-impl.cpp
dali-toolkit/internal/controls/model3d-view/obj-loader.cpp
dali-toolkit/internal/controls/model3d-view/obj-loader.h
dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.cpp
dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.h
dali-toolkit/internal/controls/renderers/primitive/primitive-renderer.h
dali-toolkit/internal/controls/renderers/renderer-string-constants.cpp
dali-toolkit/internal/controls/renderers/renderer-string-constants.h
docs/content/shared-javascript-and-cpp-documentation/control-renderers.md

diff --git a/automated-tests/resources/Cube-Points-Only.obj b/automated-tests/resources/Cube-Points-Only.obj
new file mode 100644 (file)
index 0000000..b5fd755
--- /dev/null
@@ -0,0 +1,23 @@
+g cube
+
+v  0.0  0.0  0.0
+v  0.0  0.0  1.0
+v  0.0  1.0  0.0
+v  0.0  1.0  1.0
+v  1.0  0.0  0.0
+v  1.0  0.0  1.0
+v  1.0  1.0  0.0
+v  1.0  1.0  1.0
+
+f  1  7  5
+f  1  3  7
+f  1  4  3
+f  1  2  4
+f  3  8  7
+f  3  4  8
+f  5  7  8
+f  5  8  6
+f  1  5  6
+f  1  6  2
+f  2  6  8
+f  2  8  4
index 8b3989fa8e579c368f8b796ed9c93ec829c2c829..e37b0408bbc8e125e16312fbff7cd2e8c6166ad1 100644 (file)
@@ -699,7 +699,7 @@ int UtcDaliControlRendererGetPropertyMap9(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "CUBE" );
-  propertyMap.Insert( "color", color );
+  propertyMap.Insert( "shapeColor", color );
   propertyMap.Insert( "slices", 10 );
   propertyMap.Insert( "stacks", 20 );
   propertyMap.Insert( "scaleTopRadius", 30.0f );
@@ -724,7 +724,7 @@ int UtcDaliControlRendererGetPropertyMap9(void)
   DALI_TEST_CHECK( value );
   DALI_TEST_EQUALS( value->Get<std::string>(), "CUBE", TEST_LOCATION );
 
-  value = resultMap.Find( "color", Property::VECTOR4 );
+  value = resultMap.Find( "shapeColor", Property::VECTOR4 );
   DALI_TEST_CHECK( value );
   DALI_TEST_CHECK( value->Get<Vector4>() == color );
   DALI_TEST_EQUALS( value->Get<Vector4>(), color, Math::MACHINE_EPSILON_100, TEST_LOCATION );
index 19df59dfd0e5fc717f384b56a1b29bd104332654..3ec4f95e24f3473a4ec3e65ae8775c9dfa45c9c5 100644 (file)
@@ -36,6 +36,7 @@ const char* TEST_NPATCH_FILE_NAME =  "gallery_image_01.9.png";
 const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
 const char* TEST_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube.obj";
 const char* TEST_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal.mtl";
+const char* TEST_SIMPLE_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube-Points-Only.obj";
 const char* TEST_SIMPLE_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal-Simple.mtl";
 
 Integration::Bitmap* CreateBitmap( unsigned int imageWidth, unsigned int imageHeight, unsigned int initialColor, Pixel::Format pixelFormat )
@@ -1169,6 +1170,63 @@ int UtcDaliRendererFactoryGetMeshRenderer4(void)
   END_TEST;
 }
 
+//Test if mesh renderer loads correctly when supplied an object file without face normals or texture points.
+//Note that this notably tests object loader functionality.
+int UtcDaliRendererFactoryGetMeshRenderer5(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliRendererFactoryGetMeshRenderer5:  Request mesh renderer with normal-less object file." );
+
+  RendererFactory factory = RendererFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  //Set up renderer properties.
+  Property::Map propertyMap;
+  propertyMap.Insert( "rendererType", "MESH" );
+  propertyMap.Insert( "objectUrl", TEST_SIMPLE_OBJ_FILE_NAME );
+  propertyMap.Insert( "materialUrl", TEST_MTL_FILE_NAME );
+  propertyMap.Insert( "texturesPath", TEST_RESOURCE_DIR "/" );
+
+  ControlRenderer controlRenderer = factory.CreateControlRenderer( propertyMap );
+  DALI_TEST_CHECK( controlRenderer );
+
+  //Add renderer to an actor on stage.
+  Actor actor = Actor::New();
+  actor.SetSize( 200.f, 200.f );
+  Stage::GetCurrent().Add( actor );
+  controlRenderer.SetSize( Vector2( 200.f, 200.f ) );
+  controlRenderer.SetOnStage( actor );
+
+  DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+  //Attempt to render to queue resource load requests.
+  application.SendNotification();
+  application.Render( 0 );
+
+  //Tell the platform abstraction that the required resources have been loaded.
+  TestPlatformAbstraction& platform = application.GetPlatform();
+  platform.SetAllResourceRequestsAsLoaded();
+
+  //Render again to upload the now-loaded textures.
+  application.SendNotification();
+  application.Render( 0 );
+
+  TestGlAbstraction& gl = application.GetGlAbstraction();
+
+  Matrix testScaleMatrix;
+  testScaleMatrix.SetIdentityAndScale( Vector3( 1.0, -1.0, 1.0 ) );
+  Matrix actualScaleMatrix;
+
+  //Test to see if the object has been successfully loaded.
+  DALI_TEST_CHECK( gl.GetUniformValue<Matrix>( "uObjectMatrix", actualScaleMatrix ) );
+  DALI_TEST_EQUALS( actualScaleMatrix, testScaleMatrix, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+  controlRenderer.SetOffStage( actor );
+  DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+  END_TEST;
+}
+
 //Test if mesh renderer handles the case of lacking an object file.
 int UtcDaliRendererFactoryGetMeshRendererN1(void)
 {
@@ -1392,7 +1450,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer2(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "CUBE" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "slices", 10 );
   propertyMap.Insert( "stacks", 20 );
   propertyMap.Insert( "scaleTopRadius", 30.0f );
@@ -1419,7 +1477,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer3(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "SPHERE" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "slices", 10 );
   propertyMap.Insert( "stacks", 20 );
 
@@ -1441,7 +1499,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer4(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "CONICAL_FRUSTRUM" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "slices", 10 );
   propertyMap.Insert( "scaleTopRadius", 30.0f );
   propertyMap.Insert( "scaleBottomRadius", 40.0f );
@@ -1465,7 +1523,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer5(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "BEVELLED_CUBE" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "bevelPercentage", 0.7f );
 
   //Test to see if shape loads correctly.
@@ -1486,7 +1544,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer6(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "OCTAHEDRON" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
 
   //Test to see if shape loads correctly.
   TestPrimitiveRendererWithProperties( propertyMap, application );
@@ -1506,7 +1564,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer7(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "CONE" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "slices", 10 );
   propertyMap.Insert( "scaleTopRadius", 30.0f );
   propertyMap.Insert( "scaleHeight", 50.0f );
@@ -1529,7 +1587,7 @@ int UtcDaliRendererFactoryGetPrimitiveRenderer8(void)
   Property::Map propertyMap;
   propertyMap.Insert( "rendererType", "PRIMITIVE" );
   propertyMap.Insert( "shape", "SPHERE" );
-  propertyMap.Insert( "color", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
+  propertyMap.Insert( "shapeColor", Vector4( 0.5, 0.5, 0.5, 1.0 ) );
   propertyMap.Insert( "uLightPosition", Vector3( 0.0, 1.0, 2.0 ) );
 
   //Test to see if shape loads correctly.
index 2d75ede89a23ab49222421b1b563a3fa5e7a0de7..e1e4b6f26f4c2e7cef4b1313096b4ec64e4d9a12 100644 (file)
@@ -434,7 +434,7 @@ void Model3dView::OnStageConnection( int depth )
 
   if( mObjLoader.IsSceneLoaded() )
   {
-    mMesh = mObjLoader.CreateGeometry( GetShaderProperties( mIlluminationType ) );
+    mMesh = mObjLoader.CreateGeometry( GetShaderProperties( mIlluminationType ), true );
 
     CreateMaterial();
     LoadTextures();
@@ -529,7 +529,7 @@ void Model3dView::CreateGeometry()
 {
   if( mObjLoader.IsSceneLoaded() )
   {
-    mMesh = mObjLoader.CreateGeometry( GetShaderProperties( mIlluminationType ) );
+    mMesh = mObjLoader.CreateGeometry( GetShaderProperties( mIlluminationType ), true );
 
     if( mRenderer )
     {
index 2a34f59d83479c0a8b881445eef968f17eeb1624..6717bab412bf6e72496287b3291d4293f495ebfb 100644 (file)
@@ -65,34 +65,103 @@ bool ObjLoader::IsMaterialLoaded()
   return mMaterialLoaded;
 }
 
-//TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
-void ObjLoader::CalculateTangentArray(const Dali::Vector<Vector3>& vertex,
-                                      const Dali::Vector<Vector2>& texcoord,
-                                      Dali::Vector<TriIndex>& triangle,
-                                      Dali::Vector<Vector3>& normal,
-                                      Dali::Vector<Vector3>& tangent)
+void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
+                                          Dali::Vector<Vector3>& normals )
 {
-  Dali::Vector<Vector3> tangents;
-  tangents.Resize( vertex.Size() );
+  int numFaceVertices = 3 * triangles.Size();  //Vertex per face, as each point has different normals for each face.
+  int normalIndex = 0;  //Tracks progress through the array of normals.
 
-  // Resize of a vector of Vector3 will initialise with the default constructor, setting to all zeros.
+  normals.Clear();
+  normals.Resize( numFaceVertices );
 
-  for ( unsigned long a = 0; a < triangle.Size(); a++ )
+  //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
+  for( unsigned long i = 0; i < triangles.Size(); i++ )
   {
-    Vector3 tangentVector, normalVector;
+    //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 edges.
+    Vector3 edge1 = v1 - v0;
+    Vector3 edge2 = v2 - v0;
+
+    //Using edges as vectors on the plane, cross to get the normal.
+    Vector3 normalVector = edge1.Cross(edge2);
+    normalVector.Normalize();
+
+    //Assign normals to points.
+    for( unsigned long j = 0; j < 3; j++, normalIndex++ )
+    {
+      triangles[i].normalIndex[j] = normalIndex;
+      normals[normalIndex] = normalVector;
+    }
+  }
+}
 
-    const Vector3& v0 = vertex[triangle[a].pntIndex[0]];
-    const Vector3& v1 = vertex[triangle[a].pntIndex[1]];
-    const Vector3& v2 = vertex[triangle[a].pntIndex[2]];
+void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices, 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.
+
+  //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 edges.
     Vector3 edge1 = v1 - v0;
     Vector3 edge2 = v2 - v0;
 
-    normalVector = edge1.Cross(edge2);
+    //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++ )
+    {
+      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;
+    }
+  }
+
+  //Normalise the normals.
+  for( unsigned long i = 0; i < normals.Size(); i++ )
+  {
+    normals[i].Normalize();
+  }
+}
+
+//TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
+void ObjLoader::CalculateTangentFrame()
+{
+  //Reset tangent and bitangent vectors to hold new values.
+  mTangents.Clear();
+  mBiTangents.Clear();
+  mTangents.Resize( mPoints.Size() );
+  mBiTangents.Resize( mPoints.Size() );
+
+  //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each point.
+  for ( unsigned long a = 0; a < mTriangles.Size(); a++ )
+  {
+    Vector3 tangentVector;
 
-    const Vector2& w0 = texcoord[triangle[a].texIndex[0]];
-    const Vector2& w1 = texcoord[triangle[a].texIndex[1]];
-    const Vector2& w2 = texcoord[triangle[a].texIndex[2]];
+    const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
+    const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
+    const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
+
+    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]];
 
     float deltaU1 = w1.x - w0.x;
     float deltaV1 = w1.y - w0.y;
@@ -105,38 +174,25 @@ void ObjLoader::CalculateTangentArray(const Dali::Vector<Vector3>& vertex,
     tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
     tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
 
-    tangents[triangle[a].pntIndex[0]] += tangentVector;
-    tangents[triangle[a].pntIndex[1]] += tangentVector;
-    tangents[triangle[a].pntIndex[2]] += tangentVector;
-
-    normal[triangle[a].pntIndex[0]] += normalVector;
-    normal[triangle[a].pntIndex[1]] += normalVector;
-    normal[triangle[a].pntIndex[2]] += normalVector;
+    mTangents[mTriangles[a].pointIndex[0]] += tangentVector;
+    mTangents[mTriangles[a].pointIndex[1]] += tangentVector;
+    mTangents[mTriangles[a].pointIndex[2]] += tangentVector;
   }
 
-  for ( unsigned long a = 0; a < triangle.Size(); a++ )
+  //Orthogonalize tangents and set binormals.
+  for ( unsigned long a = 0; a < mTangents.Size(); a++ )
   {
-    for ( unsigned long j = 0; j < 3; j++ )
-    {
-      triangle[a].nrmIndex[j] = triangle[a].pntIndex[j];
-    }
-  }
-
-  for ( unsigned long a = 0; a < normal.Size(); a++ )
-  {
-    normal[a].Normalize();
-
-    const Vector3& n = normal[a];
-    const Vector3& t = tangents[a];
+    const Vector3& n = mNormals[a];
+    const Vector3& t = mTangents[a];
 
     // Gram-Schmidt orthogonalize
-    Vector3 calc = t - n * n.Dot(t);
-    calc.Normalize();
-    tangent[a] = Vector3( calc.x,calc.y,calc.z );
+    mTangents[a] = t - n * n.Dot(t);
+    mTangents[a].Normalize();
+
+    mBiTangents[a] = mNormals[a].Cross( mTangents[a] );
   }
 }
 
-
 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
 {
   BoundingVolume newAABB;
@@ -153,7 +209,6 @@ void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
     biggestDimension = sceneSize.z;
   }
 
-
   newAABB.Init();
   for( unsigned int ui = 0; ui < points.Size(); ++ui )
   {
@@ -165,29 +220,39 @@ void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
   mSceneAABB = newAABB;
 }
 
-void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
-                                    Dali::Vector<Vector2> & textures,
-                                    Dali::Vector<VertexExt> & verticesExt,
-                                    Dali::Vector<unsigned short> & indices)
+void ObjLoader::CreateGeometryArray( Dali::Vector<Vertex> & vertices,
+                                     Dali::Vector<Vector2> & textures,
+                                     Dali::Vector<VertexExt> & verticesExt,
+                                     Dali::Vector<unsigned short> & indices,
+                                     bool useSoftNormals )
 {
-  //If we don't have tangents, calculate them
-  //we need to recalculate the normals too, because we need just one normal,tangent, bitangent per vertex
-  //In the case of a textureless object, we don't need tangents for our shader and so we skip this step
-  //TODO: Use a better function to calculate tangents
-  if( mTangents.Size() == 0 && mHasTexturePoints )
+  //We must calculate the tangents and bitangents if they weren't supplied, or if they don't match up.
+  bool mustCalculateTangents = mTangents.Size() == 0 || mBiTangents.Size() == 0 ||
+                               mTangents.Size() != mBiTangents.Size() || mTangents.Size() != mNormals.Size() ||
+                               mBiTangents.Size() != mNormals.Size();
+
+  //However, we don't need to do this if the object doesn't use textures to begin with.
+  mustCalculateTangents &= mHasTexturePoints;
+
+  //We also have to recalculate the normals if we need to calculate tangents,
+  // as we need just one normal, tangent and bitangent 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 )
   {
-    mNormals.Clear();
-
-    mNormals.Resize( mPoints.Size() );
-    mTangents.Resize( mPoints.Size() );
-    mBiTangents.Resize( mPoints.Size() );
-
-    CalculateTangentArray( mPoints, mTextures, mTriangles, mNormals, mTangents );
-
-    for ( unsigned int ui = 0 ; ui < mNormals.Size() ; ++ui )
+    if( useSoftNormals || mustCalculateTangents )
     {
-      mBiTangents[ui] = mNormals[ui].Cross(mTangents[ui]);
+      CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
     }
+    else
+    {
+      CalculateHardFaceNormals( mPoints, mTriangles, mNormals );
+    }
+  }
+
+  //TODO: Use a better function to calculate tangents
+  if( mHasTexturePoints && mustCalculateTangents )
+  {
+    CalculateTangentFrame();
   }
 
   bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
@@ -232,16 +297,16 @@ void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
     {
       for ( int j = 0 ; j < 3 ; ++j )
       {
-        indices[indiceIndex] = mTriangles[ui].pntIndex[j];
+        indices[indiceIndex] = mTriangles[ui].pointIndex[j];
         indiceIndex++;
 
-        vertices[mTriangles[ui].pntIndex[j]].normal = mNormals[mTriangles[ui].nrmIndex[j]];
+        vertices[mTriangles[ui].pointIndex[j]].normal = mNormals[mTriangles[ui].normalIndex[j]];
 
         if ( mHasTexturePoints )
         {
-          textures[mTriangles[ui].pntIndex[j]] = mTextures[mTriangles[ui].texIndex[j]];
-          verticesExt[mTriangles[ui].pntIndex[j]].tangent = mTangents[mTriangles[ui].nrmIndex[j]];
-          verticesExt[mTriangles[ui].pntIndex[j]].bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
+          textures[mTriangles[ui].pointIndex[j]] = mTextures[mTriangles[ui].textureIndex[j]];
+          verticesExt[mTriangles[ui].pointIndex[j]].tangent = mTangents[mTriangles[ui].normalIndex[j]];
+          verticesExt[mTriangles[ui].pointIndex[j]].bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
         }
       }
     }
@@ -261,18 +326,17 @@ void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
       for ( int j = 0 ; j < 3 ; ++j )
       {
         Vertex vertex;
-        vertex.position = mPoints[mTriangles[ui].pntIndex[j]];
-        vertex.normal = mNormals[mTriangles[ui].nrmIndex[j]];
+        vertex.position = mPoints[mTriangles[ui].pointIndex[j]];
+        vertex.normal = mNormals[mTriangles[ui].normalIndex[j]];
         vertices[index] = vertex;
 
         if ( mHasTexturePoints )
         {
-          textures[index] = mTextures[mTriangles[ui].texIndex[j]];
+          textures[index] = mTextures[mTriangles[ui].textureIndex[j]];
           VertexExt vertexExt;
-          vertexExt.tangent = mTangents[mTriangles[ui].nrmIndex[j]];
-          vertexExt.bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
+          vertexExt.tangent = mTangents[mTriangles[ui].normalIndex[j]];
+          vertexExt.bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
           verticesExt[index] = vertexExt;
-
         }
 
         index++;
@@ -440,9 +504,9 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
       {
         for( int i = 0 ; i < 3; i++ )
         {
-          triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
-          triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
-          triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
+          triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
+          triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
+          triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
         }
         mTriangles.PushBack( triangle );
         face++;
@@ -452,9 +516,9 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
       {
         for( int i = 0 ; i < 3; i++ )
         {
-          triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
-          triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
-          triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
+          triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
+          triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
+          triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
         }
         mTriangles.PushBack( triangle );
         face++;
@@ -462,9 +526,9 @@ bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
         for( int i = 0 ; i < 3; i++ )
         {
           int idx = ( i + 2 ) % numIndices;
-          triangle2.pntIndex[i] = ptIdx[idx] - 1 - pntAcum;
-          triangle2.nrmIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
-          triangle2.texIndex[i] = texIdx[idx] - 1 - texAcum;
+          triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
+          triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
+          triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
         }
         mTriangles.PushBack( triangle2 );
         face++;
@@ -561,7 +625,7 @@ void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::str
   mMaterialLoaded = true;
 }
 
-Geometry ObjLoader::CreateGeometry( int objectProperties )
+Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
 {
   Geometry surface = Geometry::New();
 
@@ -570,7 +634,7 @@ Geometry ObjLoader::CreateGeometry( int objectProperties )
   Dali::Vector<VertexExt> verticesExt;
   Dali::Vector<unsigned short> indices;
 
-  CreateGeometryArray( vertices, textures, verticesExt, indices );
+  CreateGeometryArray( vertices, textures, verticesExt, indices, useSoftNormals );
 
   //All vertices need at least Position and Normal
   Property::Map vertexFormat;
index 5f4662d7400497bddda791eff7575f2223044e97..78de23396950f2535a832d5be8a75c8558d1743b 100644 (file)
@@ -38,9 +38,9 @@ public:
 
   struct TriIndex
   {
-    int pntIndex[3];
-    int nrmIndex[3];
-    int texIndex[3];
+    int pointIndex[3];
+    int normalIndex[3];
+    int textureIndex[3];
   };
 
   struct Vertex
@@ -111,7 +111,7 @@ public:
   void      LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& diffuseTextureUrl,
                           std::string& normalTextureUrl, std::string& glossTextureUrl );
 
-  Geometry  CreateGeometry( int objectProperties );
+  Geometry  CreateGeometry( int objectProperties, bool useSoftNormals );
 
   Vector3   GetCenter();
   Vector3   GetSize();
@@ -136,27 +136,63 @@ private:
   bool mHasNormalMap;
   bool mHasSpecularMap;
 
-  Dali::Vector<Vector3> mPoints;
-  Dali::Vector<Vector2> mTextures;
-  Dali::Vector<Vector2> mTextures2;
-  Dali::Vector<Vector3> mNormals;
-  Dali::Vector<Vector3> mTangents;
-  Dali::Vector<Vector3> mBiTangents;
+  Dali::Vector<Vector3>  mPoints;
+  Dali::Vector<Vector2>  mTextures;
+  Dali::Vector<Vector2>  mTextures2;
+  Dali::Vector<Vector3>  mNormals;
+  Dali::Vector<Vector3>  mTangents;
+  Dali::Vector<Vector3>  mBiTangents;
   Dali::Vector<TriIndex> mTriangles;
 
-  void CalculateTangentArray( const Dali::Vector<Vector3>& vertex,
-                              const Dali::Vector<Vector2>& texcoord,
-                              Dali::Vector<TriIndex>& triangle,
-                              Dali::Vector<Vector3>& normal,
-                              Dali::Vector<Vector3>& tangent );
+  /**
+   * @brief Calculates normals for each point on a per-face basis.
+   *
+   * There are multiple normals per point, each corresponding to the normal of a face connecting to the point.
+   *
+   * @param[in] vertices The vertices of the object.
+   * @param[in, out] triangles The triangles that form the faces. The normals of each triangle will be updated.
+   * @param[in, out] normals The normals to be calculated.
+   */
+  void CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices,
+                                 Dali::Vector<TriIndex>& triangles,
+                                 Dali::Vector<Vector3>& normals );
+
+  /**
+   * @brief Calculates smoothed normals for each point.
+   *
+   * There is one normal per point, an average of the connecting faces.
+   *
+   * @param[in] vertices The vertices of the object.
+   * @param[in, out] triangles The triangles that form the faces. The normals of each triangle will be updated.
+   * @param[in, out] normals The normals to be calculated.
+   */
+  void CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices,
+                                 Dali::Vector<TriIndex>& triangles,
+                                 Dali::Vector<Vector3>& normals );
+
+  /**
+   * @brief Calculates tangents and bitangents for each point of the object.
+   *
+   * These are calculated using the object's points, texture coordinates and normals, so these must be initialised first.
+   */
+  void CalculateTangentFrame();
 
   void CenterAndScale( bool center, Dali::Vector<Vector3>& points );
 
-
+  /**
+   * @brief Using the data loaded from the file, create arrays of data to be used in creating the geometry.
+   *
+   * @param[in] vertices The vertices of the object.
+   * @param[in] textures The texture coordinates of the object.
+   * @param[in] verticesExt Extension to vertices, storing tangents and bitangents.
+   * @param[in] indices Indices of corresponding values to match triangles to their respective data.
+   * @param[in] useSoftNormals Indicates whether we should average the normals at each point to smooth the surface or not.
+   */
   void CreateGeometryArray( Dali::Vector<Vertex> & vertices,
                             Dali::Vector<Vector2> & textures,
                             Dali::Vector<VertexExt> & verticesExt,
-                            Dali::Vector<unsigned short> & indices );
+                            Dali::Vector<unsigned short> & indices,
+                            bool useSoftNormals );
 
 };
 
index acc248c34d909411afbd34e84577048b8601a41f..350b8a18d55390e28efd4e22e740793af395ab95 100644 (file)
@@ -276,7 +276,8 @@ MeshRenderer::MeshRenderer( RendererFactoryCache& factoryCache )
 : ControlRenderer( factoryCache ),
   mShaderType( ALL_TEXTURES ),
   mUseTexture( true ),
-  mUseMipmapping( true )
+  mUseMipmapping( true ),
+  mUseSoftNormals( true )
 {
 }
 
@@ -337,6 +338,12 @@ void MeshRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap
       }
     }
   }
+
+  Property::Value* useSoftNormals = propertyMap.Find( USE_SOFT_NORMALS );
+  if( useSoftNormals )
+  {
+    useSoftNormals->Get( mUseSoftNormals );
+  }
 }
 
 void MeshRenderer::SetSize( const Vector2& size )
@@ -393,6 +400,9 @@ void MeshRenderer::DoCreatePropertyMap( Property::Map& map ) const
     }
   }
   map.Insert( SHADER_TYPE, shaderTypeString );
+
+  map.Insert( USE_MIPMAPPING, mUseMipmapping );
+  map.Insert( USE_SOFT_NORMALS, mUseSoftNormals );
 }
 
 void MeshRenderer::InitializeRenderer()
@@ -500,7 +510,7 @@ bool MeshRenderer::CreateGeometry()
   }
 
   //Create geometry with attributes required by shader.
-  mGeometry = mObjLoader.CreateGeometry( objectProperties );
+  mGeometry = mObjLoader.CreateGeometry( objectProperties, mUseSoftNormals );
 
   if( mGeometry )
   {
index 30d032b73ef77056c6ab493334756db83b72181b..ea324fce49800b3637b3f748bf9158bb4a219653 100644 (file)
@@ -40,12 +40,14 @@ namespace Internal
  *
  * The following Property::Map keys are required to create a MeshRender
  *
- * | %Property Name  | Type        | Representing                            |
- * |-----------------|-------------|-----------------------------------------|
- * | objectUrl       | STRING      | A URL to the .obj file                  |
- * | materialUrl     | STRING      | A URL to the .mtl file                  |
- * | texturesPath    | STRING      | A URL of the path to the texture images |
- * | shaderType      | STRING      | An enum of shader types                 |
+ * | %Property Name  | Type        | Representing                                                          |
+ * |-----------------|-------------|-----------------------------------------------------------------------|
+ * | objectUrl       | STRING      | A URL to the .obj file                                                |
+ * | materialUrl     | STRING      | A URL to the .mtl file                                                |
+ * | texturesPath    | STRING      | A URL of the path to the texture images                               |
+ * | shaderType      | STRING      | An enum of shader types                                               |
+ * | useMipmapping   | BOOLEAN     | If true, use mipmaps for textures. Default true.                      |
+ * | useSoftNormals  | BOOLEAN     | If true, average normals at points for smooth textures. Default true. |
  */
 class MeshRenderer: public ControlRenderer
 {
@@ -195,6 +197,7 @@ private:
 
   bool mUseTexture;
   bool mUseMipmapping;
+  bool mUseSoftNormals;
 };
 
 } // namespace Internal
index 273bb363668acd3f081f658a2b37e078095ee560..9e9294564c494f75c561edb2bb7606ba658d98b7 100644 (file)
@@ -70,7 +70,7 @@ namespace Internal
  *
  * | %Property Name    | Type        | Shapes Affected                          |
  * |-------------------|-------------|------------------------------------------|
- * | color             | VECTOR4     | all                                      |
+ * | shapeColor        | VECTOR4     | all                                      |
  * | slices            | INTEGER     | sphere, cone, conical frustrum, cylinder |
  * | stacks            | INTEGER     | sphere                                   |
  * | scaleTopRadius    | FLOAT       | conical frustrum                         |
index baca92010e714555f9206069fa36179505bbceec..319b5961af4742f2c9885ecac07e4e55be465316 100644 (file)
@@ -46,10 +46,11 @@ const char * const MATERIAL_URL( "materialUrl" );
 const char * const TEXTURES_PATH( "texturesPath" );
 const char * const SHADER_TYPE( "shaderType" );
 const char * const USE_MIPMAPPING( "useMipmapping" );
+const char * const USE_SOFT_NORMALS( "useSoftNormals" );
 
 //Primitive shape properties
 const char * const PRIMITIVE_SHAPE( "shape" );
-const char * const SHAPE_COLOR( "color" );
+const char * const SHAPE_COLOR( "shapeColor" );
 const char * const SLICES( "slices" );
 const char * const STACKS( "stacks" );
 const char * const SCALE_TOP_RADIUS( "scaleTopRadius" );
index ec3e161f111bd9b49384d4155f82fa640c19a5df..e07ee978de823a9e60757338e6936ba0ff194dae 100644 (file)
@@ -46,6 +46,7 @@ extern const char * const MATERIAL_URL;
 extern const char * const TEXTURES_PATH;
 extern const char * const SHADER_TYPE;
 extern const char * const USE_MIPMAPPING;
+extern const char * const USE_SOFT_NORMALS;
 
 //Primitive shape properties
 extern const char * const PRIMITIVE_SHAPE;
index 2e1f0637258745c8a6133e87f26489fcb5c75966..1e796563ffc16cf25c4c680adcb01ed4c62c523a 100644 (file)
@@ -438,12 +438,14 @@ Renders a mesh using a .obj file, optionally with textures provided by a mtl fil
 
 **RendererType** "MESH"
 
-| Property Name                                | Type    | Required           | Description                                                                    |
-|----------------------------------------------|:-------:|:------------------:|--------------------------------------------------------------------------------|
-| objectUrl                                    | STRING  | Yes                | The location of the ".obj" file.                                               |
-| materialUrl                                  | STRING  | No                 | The location of the ".mtl" file. Leave blank for a textureless object.         |
-| texturesPath                                 | STRING  | If using material  | Path to the directory the textures (including gloss and normal) are stored in. |
-| [shaderType](@ref mesh-renderer-shader-type) | STRING  | No                 | Sets the type of shader to be used with the mesh.                              |
+| Property Name                                | Type    | Required           | Description                                                                                |
+|----------------------------------------------|:-------:|:------------------:|--------------------------------------------------------------------------------------------|
+| objectUrl                                    | STRING  | Yes                | The location of the ".obj" file.                                                           |
+| materialUrl                                  | STRING  | No                 | The location of the ".mtl" file. Leave blank for a textureless object.                     |
+| texturesPath                                 | STRING  | If using material  | Path to the directory the textures (including gloss and normal) are stored in.             |
+| [shaderType](@ref mesh-renderer-shader-type) | STRING  | No                 | Sets the type of shader to be used with the mesh.                                          |
+| useMipmapping                                | BOOLEAN | No                 | Flag for whether to use mipmaps for textures or not. Default true.                         |
+| useSoftNormals                               | BOOLEAN | No                 | Flag for whether to average normals at each point to smooth textures or not. Default true. |
 
 ### Shader Type {#mesh-renderer-shader-type}
 
@@ -489,7 +491,7 @@ Renders a simple 3D shape, such as a cube or sphere. Scaled to fit the control.
 | Property Name                         | Type    | Description                                                                     | Default Value        | Range                          |
 |---------------------------------------|:-------:|---------------------------------------------------------------------------------|:--------------------:|:------------------------------:|
 | [shape](@ref shape-details)           | STRING  | The specific shape to render.                                                   | "SPHERE"             | [See list](@ref shape-details) |
-| color                                 | VECTOR4 | The color of the shape.                                                         | (0.5, 0.5, 0.5, 1.0) | 0.0 - 1.0 for each             |
+| shapeColor                            | VECTOR4 | The color of the shape.                                                         | (0.5, 0.5, 0.5, 1.0) | 0.0 - 1.0 for each             |
 | [slices](@ref slices-details)         | INT     | The number of slices as you go around the shape.                                | 128                  | 1 - 255                        |
 | [stacks](@ref stacks-details)         | INT     | The number of stacks as you go down the shape.                                  | 128                  | 1 - 255                        |
 | scaleTopRadius                        | FLOAT   | The scale of the radius of the top circle of a conical frustrum.                | 1.0                  | ≥ 0.0                          |
@@ -562,7 +564,7 @@ Dali::Property::Map map;
 
 map[ "rendererType" ] = "PRIMITIVE";
 map[ "shape"        ] = "SPHERE";
-map[ "color"        ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
+map[ "shapeColor"   ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
 
 control.SetProperty( Dali::Toolkit::Control::Property::BACKGROUND, map );
 ~~~
@@ -577,7 +579,7 @@ Dali::Property::Map map;
 
 map[ "rendererType"      ] = "PRIMITIVE";
 map[ "shape"             ] = "CONICAL_FRUSTRUM";
-map[ "color"             ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
+map[ "shapeColor"        ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
 map[ "scaleTopRadius"    ] = 1.0f;
 map[ "scaleBottomRadius" ] = 1.5f;
 map[ "scaleHeight"       ] = 3.0f;
@@ -595,7 +597,7 @@ Dali::Property::Map map;
 
 map[ "rendererType"    ] = "PRIMITIVE";
 map[ "shape"           ] = "BEVELLED_CUBE";
-map[ "color"           ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
+map[ "shapeColor"      ] = Vector4( 1.0, 0.5, 0.0, 1.0 );
 map[ "bevelPercentage" ] = 0.4f;
 
 control.SetProperty( Dali::Toolkit::Control::Property::BACKGROUND, map );