X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fmodel3d-view%2Fobj-loader.cpp;h=273c8cac548a41fc86efb5d05bc9218b3781d65a;hp=bc4e1748b5cc04e5f6860369b967f77b502602f3;hb=b1e8521ad77e7b4e62b59613b2edef64429130e9;hpb=0f09829877c9b8523b00445e953b61e417ec3b5a diff --git a/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp b/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp index bc4e174..273c8ca 100644 --- a/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp +++ b/dali-toolkit/internal/controls/model3d-view/obj-loader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Copyright (c) 2017 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include "obj-loader.h" // EXTERNAL INCLUDES +#include #include #include #include @@ -32,6 +33,10 @@ namespace Toolkit namespace Internal { +namespace +{ + const int MAX_POINT_INDICES = 4; +} using namespace Dali; ObjLoader::ObjLoader() @@ -39,8 +44,8 @@ ObjLoader::ObjLoader() mSceneLoaded = false; mMaterialLoaded = false; mHasTexturePoints = false; - mHasNormalMap = false; mHasDiffuseMap = false; + mHasNormalMap = false; mHasSpecularMap = false; mSceneAABB.Init(); } @@ -60,37 +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 ) { - normal.Clear(); - normal.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. - Dali::Vector tangents; - tangents.Resize( vertex.Size() ); + normals.Clear(); + normals.Resize( numFaceVertices ); - // Resize of a vector of Vector3 will initialise with the default constructor, setting to all zeros. - - 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; + } + } +} + +void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector& vertices, Dali::Vector& triangles, + Dali::Vector& normals ) +{ + int normalIndex = 0; //Tracks progress through the array of normals. - const Vector3& v0 = vertex[triangle[a].pntIndex[0]]; - const Vector3& v1 = vertex[triangle[a].pntIndex[1]]; - const Vector3& v2 = vertex[triangle[a].pntIndex[2]]; + 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 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 = texcoord[triangle[a].texIndex[0]]; - const Vector2& w1 = texcoord[triangle[a].texIndex[1]]; - const Vector2& w2 = texcoord[triangle[a].texIndex[2]]; + 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; @@ -103,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; @@ -151,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 ) { @@ -163,25 +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 ) { - mTangents.Resize( mNormals.Size() ); - mBiTangents.Resize( mNormals.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. @@ -226,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]]; } } } @@ -255,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++; @@ -275,14 +345,14 @@ void ObjLoader::CreateGeometryArray(Dali::Vector & vertices, } } -bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& materialFile ) +bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize ) { Vector3 point; Vector2 texture; - std::string vet[4], name; - int ptIdx[4]; - int nrmIdx[4]; - int texIdx[4]; + std::string vet[MAX_POINT_INDICES], name; + int ptIdx[MAX_POINT_INDICES]; + int nrmIdx[MAX_POINT_INDICES]; + int texIdx[MAX_POINT_INDICES]; TriIndex triangle,triangle2; int pntAcum = 0, texAcum = 0, nrmAcum = 0; bool iniObj = false; @@ -294,7 +364,7 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat std::string strMatActual; - std::string input = objBuffer; + std::string input( objBuffer, fileSize ); std::istringstream ss(input); ss.imbue( std::locale( "C" ) ); @@ -372,7 +442,7 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat } int numIndices = 0; - while( isline >> vet[numIndices] ) + while( ( numIndices < MAX_POINT_INDICES ) && ( isline >> vet[numIndices] ) ) { numIndices++; } @@ -434,9 +504,9 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat { 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++; @@ -446,9 +516,9 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat { 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++; @@ -456,9 +526,9 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat 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++; @@ -476,9 +546,6 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat { isline >> name; } - else - { - } } if ( iniObj ) @@ -490,17 +557,16 @@ bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& mat } return false; - } -void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& texture0Url, - std::string& texture1Url, std::string& texture2Url ) +void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& diffuseTextureUrl, + std::string& normalTextureUrl, std::string& glossTextureUrl ) { float fR,fG,fB; std::string info; - std::string input = objBuffer; + std::string input( objBuffer, fileSize ); std::istringstream ss(input); ss.imbue(std::locale("C")); @@ -518,11 +584,15 @@ void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::str { isline >> info; } + else if ( tag == "Ka" ) //ambient color + { + isline >> fR >> fG >> fB; + } else if ( tag == "Kd" ) //diffuse color { isline >> fR >> fG >> fB; } - else if ( tag == "Kd" ) //Ambient color + else if ( tag == "Ks" ) //specular color { isline >> fR >> fG >> fB; } @@ -535,19 +605,19 @@ void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::str else if ( tag == "map_Kd" ) { isline >> info; - texture0Url = info; + diffuseTextureUrl = info; mHasDiffuseMap = true; } else if ( tag == "bump" ) { isline >> info; - texture1Url = info; + normalTextureUrl = info; mHasNormalMap = true; } else if ( tag == "map_Ks" ) { isline >> info; - texture2Url = info; + glossTextureUrl = info; mHasSpecularMap = true; } } @@ -555,7 +625,7 @@ void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::str mMaterialLoaded = true; } -Geometry ObjLoader::CreateGeometry( Toolkit::Model3dView::IlluminationType illuminationType ) +Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals ) { Geometry surface = Geometry::New(); @@ -564,49 +634,45 @@ Geometry ObjLoader::CreateGeometry( Toolkit::Model3dView::IlluminationType illum 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; vertexFormat["aPosition"] = Property::VECTOR3; vertexFormat["aNormal"] = Property::VECTOR3; - PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat ); + VertexBuffer surfaceVertices = VertexBuffer::New( vertexFormat ); surfaceVertices.SetData( &vertices[0], vertices.Size() ); surface.AddVertexBuffer( surfaceVertices ); //Some need texture coordinates - if( ( (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP ) || - (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ) ) && mHasTexturePoints && mHasDiffuseMap ) + if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTexturePoints && mHasDiffuseMap ) { Property::Map textureFormat; textureFormat["aTexCoord"] = Property::VECTOR2; - PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat ); + VertexBuffer extraVertices = VertexBuffer::New( textureFormat ); extraVertices.SetData( &textures[0], textures.Size() ); surface.AddVertexBuffer( extraVertices ); } //Some need tangent and bitangent - if( illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP && mHasTexturePoints ) + if( ( objectProperties & TANGENTS ) && ( objectProperties & BINORMALS ) && mHasTexturePoints ) { Property::Map vertexExtFormat; vertexExtFormat["aTangent"] = Property::VECTOR3; vertexExtFormat["aBiNormal"] = Property::VECTOR3; - PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat ); + VertexBuffer extraVertices = VertexBuffer::New( vertexExtFormat ); extraVertices.SetData( &verticesExt[0], verticesExt.Size() ); surface.AddVertexBuffer( extraVertices ); } + //If indices are required, we set them. if ( indices.Size() ) { surface.SetIndexBuffer ( &indices[0], indices.Size() ); } - vertices.Clear(); - verticesExt.Clear(); - indices.Clear(); - return surface; }