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"
37 ObjLoader::ObjLoader()
40 mMaterialLoaded = false;
41 mHasTexturePoints = false;
42 mHasNormalMap = false;
43 mHasDiffuseMap = false;
44 mHasSpecularMap = false;
48 ObjLoader::~ObjLoader()
53 bool ObjLoader::IsSceneLoaded()
58 bool ObjLoader::IsMaterialLoaded()
60 return mMaterialLoaded;
63 //TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
64 void ObjLoader::CalculateTangentArray(const Dali::Vector<Vector3>& vertex,
65 const Dali::Vector<Vector2>& texcoord,
66 Dali::Vector<TriIndex>& triangle,
67 Dali::Vector<Vector3>& normal,
68 Dali::Vector<Vector3>& tangent)
71 normal.Resize(vertex.Size());
73 Dali::Vector<Vector3> tangents;
74 tangents.Resize( vertex.Size() );
76 // Resize of a vector of Vector3 will initialise with the default constructor, setting to all zeros.
78 for ( unsigned long a = 0; a < triangle.Size(); a++ )
80 Vector3 tangentVector, normalVector;
82 const Vector3& v0 = vertex[triangle[a].pntIndex[0]];
83 const Vector3& v1 = vertex[triangle[a].pntIndex[1]];
84 const Vector3& v2 = vertex[triangle[a].pntIndex[2]];
86 Vector3 edge1 = v1 - v0;
87 Vector3 edge2 = v2 - v0;
89 normalVector = edge1.Cross(edge2);
91 const Vector2& w0 = texcoord[triangle[a].texIndex[0]];
92 const Vector2& w1 = texcoord[triangle[a].texIndex[1]];
93 const Vector2& w2 = texcoord[triangle[a].texIndex[2]];
95 float deltaU1 = w1.x - w0.x;
96 float deltaV1 = w1.y - w0.y;
97 float deltaU2 = w2.x - w0.x;
98 float deltaV2 = w2.y - w0.y;
100 float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
102 tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
103 tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
104 tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
106 tangents[triangle[a].pntIndex[0]] += tangentVector;
107 tangents[triangle[a].pntIndex[1]] += tangentVector;
108 tangents[triangle[a].pntIndex[2]] += tangentVector;
110 normal[triangle[a].pntIndex[0]] += normalVector;
111 normal[triangle[a].pntIndex[1]] += normalVector;
112 normal[triangle[a].pntIndex[2]] += normalVector;
115 for ( unsigned long a = 0; a < triangle.Size(); a++ )
117 for ( unsigned long j = 0; j < 3; j++ )
119 triangle[a].nrmIndex[j] = triangle[a].pntIndex[j];
123 for ( unsigned long a = 0; a < normal.Size(); a++ )
125 normal[a].Normalize();
127 const Vector3& n = normal[a];
128 const Vector3& t = tangents[a];
130 // Gram-Schmidt orthogonalize
131 Vector3 calc = t - n * n.Dot(t);
133 tangent[a] = Vector3( calc.x,calc.y,calc.z );
138 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
140 BoundingVolume newAABB;
142 Vector3 sceneSize = GetSize();
144 float biggestDimension = sceneSize.x;
145 if( sceneSize.y > biggestDimension )
147 biggestDimension = sceneSize.y;
149 if( sceneSize.z > biggestDimension )
151 biggestDimension = sceneSize.z;
156 for( unsigned int ui = 0; ui < points.Size(); ++ui )
158 points[ui] = points[ui] - GetCenter();
159 points[ui] = points[ui] / biggestDimension;
160 newAABB.ConsiderNewPointInVolume(points[ui]);
163 mSceneAABB = newAABB;
166 void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
167 Dali::Vector<Vector2> & textures,
168 Dali::Vector<VertexExt> & verticesExt,
169 Dali::Vector<unsigned short> & indices)
171 //If we don't have tangents, calculate them
172 //we need to recalculate the normals too, because we need just one normal,tangent, bitangent per vertex
173 //In the case of a textureless object, we don't need tangents for our shader and so we skip this step
174 //TODO: Use a better function to calculate tangents
176 if( mTangents.Size() == 0 && mHasTexturePoints )
178 mTangents.Resize( mNormals.Size() );
179 mBiTangents.Resize( mNormals.Size() );
180 CalculateTangentArray( mPoints, mTextures, mTriangles, mNormals, mTangents );
181 for ( unsigned int ui = 0 ; ui < mNormals.Size() ; ++ui )
183 mBiTangents[ui] = mNormals[ui].Cross(mTangents[ui]);
187 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
189 if ( mHasTexturePoints )
191 mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
195 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
198 //Check the number of points textures and normals
199 if ( mapsCorrespond )
201 int numPoints = mPoints.Size();
202 int numIndices = 3 * mTriangles.Size();
203 vertices.Resize( numPoints );
204 textures.Resize( numPoints );
205 verticesExt.Resize( numPoints );
206 indices.Resize( numIndices );
208 //We create the vertices array. For now we just copy points info
209 for (unsigned int ui = 0 ; ui < mPoints.Size() ; ++ui )
212 vertex.position = mPoints[ui];
213 vertices[ui] = vertex;
215 if ( mHasTexturePoints )
217 textures[ui] = Vector2();
218 verticesExt[ui] = VertexExt();
224 //We copy the indices
225 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
227 for ( int j = 0 ; j < 3 ; ++j )
229 indices[indiceIndex] = mTriangles[ui].pntIndex[j];
232 vertices[mTriangles[ui].pntIndex[j]].normal = mNormals[mTriangles[ui].nrmIndex[j]];
234 if ( mHasTexturePoints )
236 textures[mTriangles[ui].pntIndex[j]] = mTextures[mTriangles[ui].texIndex[j]];
237 verticesExt[mTriangles[ui].pntIndex[j]].tangent = mTangents[mTriangles[ui].nrmIndex[j]];
238 verticesExt[mTriangles[ui].pntIndex[j]].bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
245 int numVertices = 3 * mTriangles.Size();
246 vertices.Resize( numVertices );
247 textures.Resize( numVertices );
248 verticesExt.Resize( numVertices );
252 //We have to normalize the arrays so we can draw we just one index array
253 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
255 for ( int j = 0 ; j < 3 ; ++j )
258 vertex.position = mPoints[mTriangles[ui].pntIndex[j]];
259 vertex.normal = mNormals[mTriangles[ui].nrmIndex[j]];
260 vertices[index] = vertex;
262 if ( mHasTexturePoints )
264 textures[index] = mTextures[mTriangles[ui].texIndex[j]];
266 vertexExt.tangent = mTangents[mTriangles[ui].nrmIndex[j]];
267 vertexExt.bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
268 verticesExt[index] = vertexExt;
278 bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& materialFile )
282 std::string vet[4], name;
286 TriIndex triangle,triangle2;
287 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
289 bool hasTexture = false;
292 //Init AABB for the file
295 std::string strMatActual;
297 std::string input = objBuffer;
298 std::istringstream ss(input);
299 ss.imbue( std::locale( "C" ) );
303 std::getline( ss, line );
305 while ( std::getline( ss, line ) )
307 std::istringstream isline( line, std::istringstream::in );
314 //Two different objects in the same file
318 mPoints.PushBack( point );
320 mSceneAABB.ConsiderNewPointInVolume( point );
322 else if ( tag == "vn" )
328 mNormals.PushBack( point );
330 else if ( tag == "#_#tangent" )
336 mTangents.PushBack( point );
338 else if ( tag == "#_#binormal" )
344 mBiTangents.PushBack( point );
346 else if ( tag == "vt" )
351 texture.y = 1.0-texture.y;
352 mTextures.PushBack( texture );
354 else if ( tag == "#_#vt1" )
359 texture.y = 1.0-texture.y;
360 mTextures2.PushBack( texture );
362 else if ( tag == "s" )
365 else if ( tag == "f" )
375 while( isline >> vet[numIndices] )
380 //Hold slashes that separate attributes of the same point.
384 const char * subString; //A pointer to the position in the string as we move through it.
386 subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
390 if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
392 for( int i = 0 ; i < numIndices; i++)
394 std::istringstream isindex( vet[i] );
395 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
399 else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
401 for( int i = 0 ; i < numIndices; i++ )
403 std::istringstream isindex( vet[i] );
404 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
409 else // Of the form A/B, so has points and textures but no normals.
411 for( int i = 0 ; i < numIndices; i++ )
413 std::istringstream isindex( vet[i] );
414 isindex >> ptIdx[i] >> separator >> texIdx[i];
421 else // Simply of the form A, as in, point indices only.
423 for( int i = 0 ; i < numIndices; i++ )
425 std::istringstream isindex( vet[i] );
432 //If it is a triangle
433 if( numIndices == 3 )
435 for( int i = 0 ; i < 3; i++ )
437 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
438 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
439 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
441 mTriangles.PushBack( triangle );
444 //If on the other hand it is a quad, we will create two triangles
445 else if( numIndices == 4 )
447 for( int i = 0 ; i < 3; i++ )
449 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
450 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
451 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
453 mTriangles.PushBack( triangle );
456 for( int i = 0 ; i < 3; i++ )
458 int idx = ( i + 2 ) % numIndices;
459 triangle2.pntIndex[i] = ptIdx[idx] - 1 - pntAcum;
460 triangle2.nrmIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
461 triangle2.texIndex[i] = texIdx[idx] - 1 - texAcum;
463 mTriangles.PushBack( triangle2 );
467 else if ( tag == "usemtl" )
469 isline >> strMatActual;
471 else if ( tag == "mtllib" )
473 isline >> strMatActual;
475 else if ( tag == "g" )
486 CenterAndScale( true, mPoints );
488 mHasTexturePoints = hasTexture;
496 void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& texture0Url,
497 std::string& texture1Url, std::string& texture2Url )
503 std::string input = objBuffer;
504 std::istringstream ss(input);
505 ss.imbue(std::locale("C"));
508 std::getline( ss, line );
510 while ( std::getline( ss, line ) )
512 std::istringstream isline( line, std::istringstream::in );
517 if ( tag == "newmtl" ) //name of the material
521 else if ( tag == "Kd" ) //diffuse color
523 isline >> fR >> fG >> fB;
525 else if ( tag == "Kd" ) //Ambient color
527 isline >> fR >> fG >> fB;
529 else if ( tag == "Tf" ) //color
532 else if ( tag == "Ni" )
535 else if ( tag == "map_Kd" )
539 mHasDiffuseMap = true;
541 else if ( tag == "bump" )
545 mHasNormalMap = true;
547 else if ( tag == "map_Ks" )
551 mHasSpecularMap = true;
555 mMaterialLoaded = true;
558 Geometry ObjLoader::CreateGeometry( Toolkit::Model3dView::IlluminationType illuminationType )
560 Geometry surface = Geometry::New();
562 Dali::Vector<Vertex> vertices;
563 Dali::Vector<Vector2> textures;
564 Dali::Vector<VertexExt> verticesExt;
565 Dali::Vector<unsigned short> indices;
567 CreateGeometryArray( vertices, textures, verticesExt, indices );
569 //All vertices need at least Position and Normal
570 Property::Map vertexFormat;
571 vertexFormat["aPosition"] = Property::VECTOR3;
572 vertexFormat["aNormal"] = Property::VECTOR3;
573 PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat );
574 surfaceVertices.SetData( &vertices[0], vertices.Size() );
575 surface.AddVertexBuffer( surfaceVertices );
577 //Some need texture coordinates
578 if( ( (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP ) ||
579 (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ) ) && mHasTexturePoints && mHasDiffuseMap )
581 Property::Map textureFormat;
582 textureFormat["aTexCoord"] = Property::VECTOR2;
583 PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat );
584 extraVertices.SetData( &textures[0], textures.Size() );
586 surface.AddVertexBuffer( extraVertices );
589 //Some need tangent and bitangent
590 if( illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP && mHasTexturePoints )
592 Property::Map vertexExtFormat;
593 vertexExtFormat["aTangent"] = Property::VECTOR3;
594 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
595 PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat );
596 extraVertices.SetData( &verticesExt[0], verticesExt.Size() );
598 surface.AddVertexBuffer( extraVertices );
601 if ( indices.Size() )
603 surface.SetIndexBuffer ( &indices[0], indices.Size() );
613 Vector3 ObjLoader::GetCenter()
615 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
619 Vector3 ObjLoader::GetSize()
621 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
625 void ObjLoader::ClearArrays()
635 mSceneLoaded = false;
638 bool ObjLoader::IsTexturePresent()
640 return mHasTexturePoints;
643 bool ObjLoader::IsDiffuseMapPresent()
645 return mHasDiffuseMap;
648 bool ObjLoader::IsNormalMapPresent()
650 return mHasNormalMap;
653 bool ObjLoader::IsSpecularMapPresent()
655 return mHasSpecularMap;
658 } // namespace Internal
659 } // namespace Toolkit