From: Andrew Poor Date: Wed, 29 Jun 2016 14:01:06 +0000 (+0100) Subject: Normal-less objects now have their normals calculated so that they can be displayed... X-Git-Tag: dali_1.1.45~18 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=34f1c102c367acc11a4bf793600acc2815ee201d Normal-less objects now have their normals calculated so that they can be displayed correctly. Change-Id: Ia34116fd1d2c94aa20747bd5a273d8c807a75be6 --- diff --git a/automated-tests/resources/Cube-Points-Only.obj b/automated-tests/resources/Cube-Points-Only.obj new file mode 100644 index 0000000..b5fd755 --- /dev/null +++ b/automated-tests/resources/Cube-Points-Only.obj @@ -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 diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ControlRenderer.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ControlRenderer.cpp index 8b3989f..e37b040 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ControlRenderer.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ControlRenderer.cpp @@ -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(), "CUBE", TEST_LOCATION ); - value = resultMap.Find( "color", Property::VECTOR4 ); + value = resultMap.Find( "shapeColor", Property::VECTOR4 ); DALI_TEST_CHECK( value ); DALI_TEST_CHECK( value->Get() == color ); DALI_TEST_EQUALS( value->Get(), color, Math::MACHINE_EPSILON_100, TEST_LOCATION ); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-RendererFactory.cpp b/automated-tests/src/dali-toolkit/utc-Dali-RendererFactory.cpp index 19df59d..3ec4f95 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-RendererFactory.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-RendererFactory.cpp @@ -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( "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. diff --git a/dali-toolkit/internal/controls/model3d-view/model3d-view-impl.cpp b/dali-toolkit/internal/controls/model3d-view/model3d-view-impl.cpp index 2d75ede..e1e4b6f 100644 --- a/dali-toolkit/internal/controls/model3d-view/model3d-view-impl.cpp +++ b/dali-toolkit/internal/controls/model3d-view/model3d-view-impl.cpp @@ -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 ) { diff --git a/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp b/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp index 2a34f59..6717bab 100644 --- a/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp +++ b/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp @@ -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& vertex, - const Dali::Vector& texcoord, - Dali::Vector& triangle, - Dali::Vector& normal, - Dali::Vector& tangent) +void ObjLoader::CalculateHardFaceNormals( const Dali::Vector& vertices, Dali::Vector& triangles, + Dali::Vector& normals ) { - Dali::Vector 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& vertices, Dali::Vector& triangles, + Dali::Vector& 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& 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& points ) { BoundingVolume newAABB; @@ -153,7 +209,6 @@ void ObjLoader::CenterAndScale( bool center, Dali::Vector& 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& points ) mSceneAABB = newAABB; } -void ObjLoader::CreateGeometryArray(Dali::Vector & vertices, - Dali::Vector & textures, - Dali::Vector & verticesExt, - Dali::Vector & indices) +void ObjLoader::CreateGeometryArray( Dali::Vector & vertices, + Dali::Vector & textures, + Dali::Vector & verticesExt, + Dali::Vector & 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 & 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 & 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 verticesExt; Dali::Vector indices; - CreateGeometryArray( vertices, textures, verticesExt, indices ); + CreateGeometryArray( vertices, textures, verticesExt, indices, useSoftNormals ); //All vertices need at least Position and Normal Property::Map vertexFormat; diff --git a/dali-toolkit/internal/controls/model3d-view/obj-loader.h b/dali-toolkit/internal/controls/model3d-view/obj-loader.h index 5f4662d..78de233 100644 --- a/dali-toolkit/internal/controls/model3d-view/obj-loader.h +++ b/dali-toolkit/internal/controls/model3d-view/obj-loader.h @@ -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 mPoints; - Dali::Vector mTextures; - Dali::Vector mTextures2; - Dali::Vector mNormals; - Dali::Vector mTangents; - Dali::Vector mBiTangents; + Dali::Vector mPoints; + Dali::Vector mTextures; + Dali::Vector mTextures2; + Dali::Vector mNormals; + Dali::Vector mTangents; + Dali::Vector mBiTangents; Dali::Vector mTriangles; - void CalculateTangentArray( const Dali::Vector& vertex, - const Dali::Vector& texcoord, - Dali::Vector& triangle, - Dali::Vector& normal, - Dali::Vector& 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& vertices, + Dali::Vector& triangles, + Dali::Vector& 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& vertices, + Dali::Vector& triangles, + Dali::Vector& 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& 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 & vertices, Dali::Vector & textures, Dali::Vector & verticesExt, - Dali::Vector & indices ); + Dali::Vector & indices, + bool useSoftNormals ); }; diff --git a/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.cpp b/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.cpp index acc248c..350b8a1 100644 --- a/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.cpp +++ b/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.cpp @@ -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 ) { diff --git a/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.h b/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.h index 30d032b..ea324fc 100644 --- a/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.h +++ b/dali-toolkit/internal/controls/renderers/mesh/mesh-renderer.h @@ -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 diff --git a/dali-toolkit/internal/controls/renderers/primitive/primitive-renderer.h b/dali-toolkit/internal/controls/renderers/primitive/primitive-renderer.h index 273bb36..9e92945 100644 --- a/dali-toolkit/internal/controls/renderers/primitive/primitive-renderer.h +++ b/dali-toolkit/internal/controls/renderers/primitive/primitive-renderer.h @@ -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 | diff --git a/dali-toolkit/internal/controls/renderers/renderer-string-constants.cpp b/dali-toolkit/internal/controls/renderers/renderer-string-constants.cpp index baca920..319b596 100644 --- a/dali-toolkit/internal/controls/renderers/renderer-string-constants.cpp +++ b/dali-toolkit/internal/controls/renderers/renderer-string-constants.cpp @@ -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" ); diff --git a/dali-toolkit/internal/controls/renderers/renderer-string-constants.h b/dali-toolkit/internal/controls/renderers/renderer-string-constants.h index ec3e161..e07ee97 100644 --- a/dali-toolkit/internal/controls/renderers/renderer-string-constants.h +++ b/dali-toolkit/internal/controls/renderers/renderer-string-constants.h @@ -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; diff --git a/docs/content/shared-javascript-and-cpp-documentation/control-renderers.md b/docs/content/shared-javascript-and-cpp-documentation/control-renderers.md index 2e1f063..1e79656 100644 --- a/docs/content/shared-javascript-and-cpp-documentation/control-renderers.md +++ b/docs/content/shared-javascript-and-cpp-documentation/control-renderers.md @@ -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 );