2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "obj-loader.h"
22 #include <dali/integration-api/debug.h>
38 const int MAX_POINT_INDICES = 4;
42 ObjLoader::ObjLoader()
45 mMaterialLoaded = false;
46 mHasTexturePoints = false;
47 mHasDiffuseMap = false;
48 mHasNormalMap = false;
49 mHasSpecularMap = false;
53 ObjLoader::~ObjLoader()
58 bool ObjLoader::IsSceneLoaded()
63 bool ObjLoader::IsMaterialLoaded()
65 return mMaterialLoaded;
68 void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
69 Dali::Vector<Vector3>& normals )
71 int numFaceVertices = 3 * triangles.Size(); //Vertex per face, as each point has different normals for each face.
72 int normalIndex = 0; //Tracks progress through the array of normals.
75 normals.Resize( numFaceVertices );
77 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
78 for( unsigned long i = 0; i < triangles.Size(); i++ )
81 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
82 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
83 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
86 Vector3 edge1 = v1 - v0;
87 Vector3 edge2 = v2 - v0;
89 //Using edges as vectors on the plane, cross to get the normal.
90 Vector3 normalVector = edge1.Cross(edge2);
91 normalVector.Normalize();
93 //Assign normals to points.
94 for( unsigned long j = 0; j < 3; j++, normalIndex++ )
96 triangles[i].normalIndex[j] = normalIndex;
97 normals[normalIndex] = normalVector;
102 void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
103 Dali::Vector<Vector3>& normals )
105 int normalIndex = 0; //Tracks progress through the array of normals.
108 normals.Resize( vertices.Size() ); //One (averaged) normal per point.
110 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
111 //We then add the triangle's normal to the cumulative normals at each point of it
112 for( unsigned long i = 0; i < triangles.Size(); i++ )
115 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
116 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
117 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
120 Vector3 edge1 = v1 - v0;
121 Vector3 edge2 = v2 - v0;
123 //Using edges as vectors on the plane, cross to get the normal.
124 Vector3 normalVector = edge1.Cross(edge2);
126 //Add this triangle's normal to the cumulative normal of each constituent point and set the index of the normal accordingly.
127 for( unsigned long j = 0; j < 3; j++, normalIndex++ )
129 triangles[i].normalIndex[j] = triangles[i].pointIndex[j]; //Normal index matches up to vertex index, as one normal per vertex.
130 normals[triangles[i].normalIndex[j]] += normalVector;
134 //Normalise the normals.
135 for( unsigned long i = 0; i < normals.Size(); i++ )
137 normals[i].Normalize();
141 //TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
142 void ObjLoader::CalculateTangentFrame()
144 //Reset tangent and bitangent vectors to hold new values.
147 mTangents.Resize( mPoints.Size() );
148 mBiTangents.Resize( mPoints.Size() );
150 //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each point.
151 for ( unsigned long a = 0; a < mTriangles.Size(); a++ )
153 Vector3 tangentVector;
155 const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
156 const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
157 const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
159 Vector3 edge1 = v1 - v0;
160 Vector3 edge2 = v2 - v0;
162 const Vector2& w0 = mTextures[mTriangles[a].textureIndex[0]];
163 const Vector2& w1 = mTextures[mTriangles[a].textureIndex[1]];
164 const Vector2& w2 = mTextures[mTriangles[a].textureIndex[2]];
166 float deltaU1 = w1.x - w0.x;
167 float deltaV1 = w1.y - w0.y;
168 float deltaU2 = w2.x - w0.x;
169 float deltaV2 = w2.y - w0.y;
171 float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
173 tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
174 tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
175 tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
177 mTangents[mTriangles[a].pointIndex[0]] += tangentVector;
178 mTangents[mTriangles[a].pointIndex[1]] += tangentVector;
179 mTangents[mTriangles[a].pointIndex[2]] += tangentVector;
182 //Orthogonalize tangents and set binormals.
183 for ( unsigned long a = 0; a < mTangents.Size(); a++ )
185 const Vector3& n = mNormals[a];
186 const Vector3& t = mTangents[a];
188 // Gram-Schmidt orthogonalize
189 mTangents[a] = t - n * n.Dot(t);
190 mTangents[a].Normalize();
192 mBiTangents[a] = mNormals[a].Cross( mTangents[a] );
196 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
198 BoundingVolume newAABB;
200 Vector3 sceneSize = GetSize();
202 float biggestDimension = sceneSize.x;
203 if( sceneSize.y > biggestDimension )
205 biggestDimension = sceneSize.y;
207 if( sceneSize.z > biggestDimension )
209 biggestDimension = sceneSize.z;
213 for( unsigned int ui = 0; ui < points.Size(); ++ui )
215 points[ui] = points[ui] - GetCenter();
216 points[ui] = points[ui] / biggestDimension;
217 newAABB.ConsiderNewPointInVolume(points[ui]);
220 mSceneAABB = newAABB;
223 void ObjLoader::CreateGeometryArray( Dali::Vector<Vertex> & vertices,
224 Dali::Vector<Vector2> & textures,
225 Dali::Vector<VertexExt> & verticesExt,
226 Dali::Vector<unsigned short> & indices,
227 bool useSoftNormals )
229 //We must calculate the tangents and bitangents if they weren't supplied, or if they don't match up.
230 bool mustCalculateTangents = mTangents.Size() == 0 || mBiTangents.Size() == 0 ||
231 mTangents.Size() != mBiTangents.Size() || mTangents.Size() != mNormals.Size() ||
232 mBiTangents.Size() != mNormals.Size();
234 //However, we don't need to do this if the object doesn't use textures to begin with.
235 mustCalculateTangents &= mHasTexturePoints;
237 //We also have to recalculate the normals if we need to calculate tangents,
238 // as we need just one normal, tangent and bitangent per vertex, rather than the supplied per-face vertices.
239 //Alternatively, we need to calculate the normals if there weren't any to begin with.
240 if( mNormals.Size() == 0 || mustCalculateTangents )
242 if( useSoftNormals || mustCalculateTangents )
244 CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
248 CalculateHardFaceNormals( mPoints, mTriangles, mNormals );
252 //TODO: Use a better function to calculate tangents
253 if( mHasTexturePoints && mustCalculateTangents )
255 CalculateTangentFrame();
258 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
260 if ( mHasTexturePoints )
262 mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
266 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
269 //Check the number of points textures and normals
270 if ( mapsCorrespond )
272 int numPoints = mPoints.Size();
273 int numIndices = 3 * mTriangles.Size();
274 vertices.Resize( numPoints );
275 textures.Resize( numPoints );
276 verticesExt.Resize( numPoints );
277 indices.Resize( numIndices );
279 //We create the vertices array. For now we just copy points info
280 for (unsigned int ui = 0 ; ui < mPoints.Size() ; ++ui )
283 vertex.position = mPoints[ui];
284 vertices[ui] = vertex;
286 if ( mHasTexturePoints )
288 textures[ui] = Vector2();
289 verticesExt[ui] = VertexExt();
295 //We copy the indices
296 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
298 for ( int j = 0 ; j < 3 ; ++j )
300 indices[indiceIndex] = mTriangles[ui].pointIndex[j];
303 vertices[mTriangles[ui].pointIndex[j]].normal = mNormals[mTriangles[ui].normalIndex[j]];
305 if ( mHasTexturePoints )
307 textures[mTriangles[ui].pointIndex[j]] = mTextures[mTriangles[ui].textureIndex[j]];
308 verticesExt[mTriangles[ui].pointIndex[j]].tangent = mTangents[mTriangles[ui].normalIndex[j]];
309 verticesExt[mTriangles[ui].pointIndex[j]].bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
316 int numVertices = 3 * mTriangles.Size();
317 vertices.Resize( numVertices );
318 textures.Resize( numVertices );
319 verticesExt.Resize( numVertices );
323 //We have to normalize the arrays so we can draw we just one index array
324 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
326 for ( int j = 0 ; j < 3 ; ++j )
329 vertex.position = mPoints[mTriangles[ui].pointIndex[j]];
330 vertex.normal = mNormals[mTriangles[ui].normalIndex[j]];
331 vertices[index] = vertex;
333 if ( mHasTexturePoints )
335 textures[index] = mTextures[mTriangles[ui].textureIndex[j]];
337 vertexExt.tangent = mTangents[mTriangles[ui].normalIndex[j]];
338 vertexExt.bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
339 verticesExt[index] = vertexExt;
348 bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
352 std::string vet[MAX_POINT_INDICES], name;
353 int ptIdx[MAX_POINT_INDICES];
354 int nrmIdx[MAX_POINT_INDICES];
355 int texIdx[MAX_POINT_INDICES];
356 TriIndex triangle,triangle2;
357 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
359 bool hasTexture = false;
362 //Init AABB for the file
365 std::string strMatActual;
367 std::string input = objBuffer;
368 std::istringstream ss(input);
369 ss.imbue( std::locale( "C" ) );
373 std::getline( ss, line );
375 while ( std::getline( ss, line ) )
377 std::istringstream isline( line, std::istringstream::in );
384 //Two different objects in the same file
388 mPoints.PushBack( point );
390 mSceneAABB.ConsiderNewPointInVolume( point );
392 else if ( tag == "vn" )
398 mNormals.PushBack( point );
400 else if ( tag == "#_#tangent" )
406 mTangents.PushBack( point );
408 else if ( tag == "#_#binormal" )
414 mBiTangents.PushBack( point );
416 else if ( tag == "vt" )
421 texture.y = 1.0-texture.y;
422 mTextures.PushBack( texture );
424 else if ( tag == "#_#vt1" )
429 texture.y = 1.0-texture.y;
430 mTextures2.PushBack( texture );
432 else if ( tag == "s" )
435 else if ( tag == "f" )
445 while( isline >> vet[numIndices] && numIndices < MAX_POINT_INDICES )
450 //Hold slashes that separate attributes of the same point.
454 const char * subString; //A pointer to the position in the string as we move through it.
456 subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
460 if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
462 for( int i = 0 ; i < numIndices; i++)
464 std::istringstream isindex( vet[i] );
465 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
469 else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
471 for( int i = 0 ; i < numIndices; i++ )
473 std::istringstream isindex( vet[i] );
474 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
479 else // Of the form A/B, so has points and textures but no normals.
481 for( int i = 0 ; i < numIndices; i++ )
483 std::istringstream isindex( vet[i] );
484 isindex >> ptIdx[i] >> separator >> texIdx[i];
491 else // Simply of the form A, as in, point indices only.
493 for( int i = 0 ; i < numIndices; i++ )
495 std::istringstream isindex( vet[i] );
502 //If it is a triangle
503 if( numIndices == 3 )
505 for( int i = 0 ; i < 3; i++ )
507 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
508 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
509 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
511 mTriangles.PushBack( triangle );
514 //If on the other hand it is a quad, we will create two triangles
515 else if( numIndices == 4 )
517 for( int i = 0 ; i < 3; i++ )
519 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
520 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
521 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
523 mTriangles.PushBack( triangle );
526 for( int i = 0 ; i < 3; i++ )
528 int idx = ( i + 2 ) % numIndices;
529 triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
530 triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
531 triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
533 mTriangles.PushBack( triangle2 );
537 else if ( tag == "usemtl" )
539 isline >> strMatActual;
541 else if ( tag == "mtllib" )
543 isline >> strMatActual;
545 else if ( tag == "g" )
553 CenterAndScale( true, mPoints );
555 mHasTexturePoints = hasTexture;
562 void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& diffuseTextureUrl,
563 std::string& normalTextureUrl, std::string& glossTextureUrl )
569 std::string input = objBuffer;
570 std::istringstream ss(input);
571 ss.imbue(std::locale("C"));
574 std::getline( ss, line );
576 while ( std::getline( ss, line ) )
578 std::istringstream isline( line, std::istringstream::in );
583 if ( tag == "newmtl" ) //name of the material
587 else if ( tag == "Ka" ) //ambient color
589 isline >> fR >> fG >> fB;
591 else if ( tag == "Kd" ) //diffuse color
593 isline >> fR >> fG >> fB;
595 else if ( tag == "Ks" ) //specular color
597 isline >> fR >> fG >> fB;
599 else if ( tag == "Tf" ) //color
602 else if ( tag == "Ni" )
605 else if ( tag == "map_Kd" )
608 diffuseTextureUrl = info;
609 mHasDiffuseMap = true;
611 else if ( tag == "bump" )
614 normalTextureUrl = info;
615 mHasNormalMap = true;
617 else if ( tag == "map_Ks" )
620 glossTextureUrl = info;
621 mHasSpecularMap = true;
625 mMaterialLoaded = true;
628 Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
630 Geometry surface = Geometry::New();
632 Dali::Vector<Vertex> vertices;
633 Dali::Vector<Vector2> textures;
634 Dali::Vector<VertexExt> verticesExt;
635 Dali::Vector<unsigned short> indices;
637 CreateGeometryArray( vertices, textures, verticesExt, indices, useSoftNormals );
639 //All vertices need at least Position and Normal
640 Property::Map vertexFormat;
641 vertexFormat["aPosition"] = Property::VECTOR3;
642 vertexFormat["aNormal"] = Property::VECTOR3;
643 PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat );
644 surfaceVertices.SetData( &vertices[0], vertices.Size() );
645 surface.AddVertexBuffer( surfaceVertices );
647 //Some need texture coordinates
648 if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTexturePoints && mHasDiffuseMap )
650 Property::Map textureFormat;
651 textureFormat["aTexCoord"] = Property::VECTOR2;
652 PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat );
653 extraVertices.SetData( &textures[0], textures.Size() );
655 surface.AddVertexBuffer( extraVertices );
658 //Some need tangent and bitangent
659 if( ( objectProperties & TANGENTS ) && ( objectProperties & BINORMALS ) && mHasTexturePoints )
661 Property::Map vertexExtFormat;
662 vertexExtFormat["aTangent"] = Property::VECTOR3;
663 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
664 PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat );
665 extraVertices.SetData( &verticesExt[0], verticesExt.Size() );
667 surface.AddVertexBuffer( extraVertices );
670 //If indices are required, we set them.
671 if ( indices.Size() )
673 surface.SetIndexBuffer ( &indices[0], indices.Size() );
679 Vector3 ObjLoader::GetCenter()
681 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
685 Vector3 ObjLoader::GetSize()
687 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
691 void ObjLoader::ClearArrays()
701 mSceneLoaded = false;
704 bool ObjLoader::IsTexturePresent()
706 return mHasTexturePoints;
709 bool ObjLoader::IsDiffuseMapPresent()
711 return mHasDiffuseMap;
714 bool ObjLoader::IsNormalMapPresent()
716 return mHasNormalMap;
719 bool ObjLoader::IsSpecularMapPresent()
721 return mHasSpecularMap;
724 } // namespace Internal
725 } // namespace Toolkit