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 //TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
69 void ObjLoader::CalculateTangentArray(const Dali::Vector<Vector3>& vertex,
70 const Dali::Vector<Vector2>& texcoord,
71 Dali::Vector<TriIndex>& triangle,
72 Dali::Vector<Vector3>& normal,
73 Dali::Vector<Vector3>& tangent)
75 Dali::Vector<Vector3> tangents;
76 tangents.Resize( vertex.Size() );
78 // Resize of a vector of Vector3 will initialise with the default constructor, setting to all zeros.
80 for ( unsigned long a = 0; a < triangle.Size(); a++ )
82 Vector3 tangentVector, normalVector;
84 const Vector3& v0 = vertex[triangle[a].pntIndex[0]];
85 const Vector3& v1 = vertex[triangle[a].pntIndex[1]];
86 const Vector3& v2 = vertex[triangle[a].pntIndex[2]];
88 Vector3 edge1 = v1 - v0;
89 Vector3 edge2 = v2 - v0;
91 normalVector = edge1.Cross(edge2);
93 const Vector2& w0 = texcoord[triangle[a].texIndex[0]];
94 const Vector2& w1 = texcoord[triangle[a].texIndex[1]];
95 const Vector2& w2 = texcoord[triangle[a].texIndex[2]];
97 float deltaU1 = w1.x - w0.x;
98 float deltaV1 = w1.y - w0.y;
99 float deltaU2 = w2.x - w0.x;
100 float deltaV2 = w2.y - w0.y;
102 float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
104 tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
105 tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
106 tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
108 tangents[triangle[a].pntIndex[0]] += tangentVector;
109 tangents[triangle[a].pntIndex[1]] += tangentVector;
110 tangents[triangle[a].pntIndex[2]] += tangentVector;
112 normal[triangle[a].pntIndex[0]] += normalVector;
113 normal[triangle[a].pntIndex[1]] += normalVector;
114 normal[triangle[a].pntIndex[2]] += normalVector;
117 for ( unsigned long a = 0; a < triangle.Size(); a++ )
119 for ( unsigned long j = 0; j < 3; j++ )
121 triangle[a].nrmIndex[j] = triangle[a].pntIndex[j];
125 for ( unsigned long a = 0; a < normal.Size(); a++ )
127 normal[a].Normalize();
129 const Vector3& n = normal[a];
130 const Vector3& t = tangents[a];
132 // Gram-Schmidt orthogonalize
133 Vector3 calc = t - n * n.Dot(t);
135 tangent[a] = Vector3( calc.x,calc.y,calc.z );
140 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
142 BoundingVolume newAABB;
144 Vector3 sceneSize = GetSize();
146 float biggestDimension = sceneSize.x;
147 if( sceneSize.y > biggestDimension )
149 biggestDimension = sceneSize.y;
151 if( sceneSize.z > biggestDimension )
153 biggestDimension = sceneSize.z;
158 for( unsigned int ui = 0; ui < points.Size(); ++ui )
160 points[ui] = points[ui] - GetCenter();
161 points[ui] = points[ui] / biggestDimension;
162 newAABB.ConsiderNewPointInVolume(points[ui]);
165 mSceneAABB = newAABB;
168 void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
169 Dali::Vector<Vector2> & textures,
170 Dali::Vector<VertexExt> & verticesExt,
171 Dali::Vector<unsigned short> & indices)
173 //If we don't have tangents, calculate them
174 //we need to recalculate the normals too, because we need just one normal,tangent, bitangent per vertex
175 //In the case of a textureless object, we don't need tangents for our shader and so we skip this step
176 //TODO: Use a better function to calculate tangents
177 if( mTangents.Size() == 0 && mHasTexturePoints )
181 mNormals.Resize( mPoints.Size() );
182 mTangents.Resize( mPoints.Size() );
183 mBiTangents.Resize( mPoints.Size() );
185 CalculateTangentArray( mPoints, mTextures, mTriangles, mNormals, mTangents );
187 for ( unsigned int ui = 0 ; ui < mNormals.Size() ; ++ui )
189 mBiTangents[ui] = mNormals[ui].Cross(mTangents[ui]);
193 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
195 if ( mHasTexturePoints )
197 mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
201 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
204 //Check the number of points textures and normals
205 if ( mapsCorrespond )
207 int numPoints = mPoints.Size();
208 int numIndices = 3 * mTriangles.Size();
209 vertices.Resize( numPoints );
210 textures.Resize( numPoints );
211 verticesExt.Resize( numPoints );
212 indices.Resize( numIndices );
214 //We create the vertices array. For now we just copy points info
215 for (unsigned int ui = 0 ; ui < mPoints.Size() ; ++ui )
218 vertex.position = mPoints[ui];
219 vertices[ui] = vertex;
221 if ( mHasTexturePoints )
223 textures[ui] = Vector2();
224 verticesExt[ui] = VertexExt();
230 //We copy the indices
231 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
233 for ( int j = 0 ; j < 3 ; ++j )
235 indices[indiceIndex] = mTriangles[ui].pntIndex[j];
238 vertices[mTriangles[ui].pntIndex[j]].normal = mNormals[mTriangles[ui].nrmIndex[j]];
240 if ( mHasTexturePoints )
242 textures[mTriangles[ui].pntIndex[j]] = mTextures[mTriangles[ui].texIndex[j]];
243 verticesExt[mTriangles[ui].pntIndex[j]].tangent = mTangents[mTriangles[ui].nrmIndex[j]];
244 verticesExt[mTriangles[ui].pntIndex[j]].bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
251 int numVertices = 3 * mTriangles.Size();
252 vertices.Resize( numVertices );
253 textures.Resize( numVertices );
254 verticesExt.Resize( numVertices );
258 //We have to normalize the arrays so we can draw we just one index array
259 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
261 for ( int j = 0 ; j < 3 ; ++j )
264 vertex.position = mPoints[mTriangles[ui].pntIndex[j]];
265 vertex.normal = mNormals[mTriangles[ui].nrmIndex[j]];
266 vertices[index] = vertex;
268 if ( mHasTexturePoints )
270 textures[index] = mTextures[mTriangles[ui].texIndex[j]];
272 vertexExt.tangent = mTangents[mTriangles[ui].nrmIndex[j]];
273 vertexExt.bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
274 verticesExt[index] = vertexExt;
284 bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
288 std::string vet[MAX_POINT_INDICES], name;
289 int ptIdx[MAX_POINT_INDICES];
290 int nrmIdx[MAX_POINT_INDICES];
291 int texIdx[MAX_POINT_INDICES];
292 TriIndex triangle,triangle2;
293 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
295 bool hasTexture = false;
298 //Init AABB for the file
301 std::string strMatActual;
303 std::string input = objBuffer;
304 std::istringstream ss(input);
305 ss.imbue( std::locale( "C" ) );
309 std::getline( ss, line );
311 while ( std::getline( ss, line ) )
313 std::istringstream isline( line, std::istringstream::in );
320 //Two different objects in the same file
324 mPoints.PushBack( point );
326 mSceneAABB.ConsiderNewPointInVolume( point );
328 else if ( tag == "vn" )
334 mNormals.PushBack( point );
336 else if ( tag == "#_#tangent" )
342 mTangents.PushBack( point );
344 else if ( tag == "#_#binormal" )
350 mBiTangents.PushBack( point );
352 else if ( tag == "vt" )
357 texture.y = 1.0-texture.y;
358 mTextures.PushBack( texture );
360 else if ( tag == "#_#vt1" )
365 texture.y = 1.0-texture.y;
366 mTextures2.PushBack( texture );
368 else if ( tag == "s" )
371 else if ( tag == "f" )
381 while( isline >> vet[numIndices] && numIndices < MAX_POINT_INDICES )
386 //Hold slashes that separate attributes of the same point.
390 const char * subString; //A pointer to the position in the string as we move through it.
392 subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
396 if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
398 for( int i = 0 ; i < numIndices; i++)
400 std::istringstream isindex( vet[i] );
401 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
405 else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
407 for( int i = 0 ; i < numIndices; i++ )
409 std::istringstream isindex( vet[i] );
410 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
415 else // Of the form A/B, so has points and textures but no normals.
417 for( int i = 0 ; i < numIndices; i++ )
419 std::istringstream isindex( vet[i] );
420 isindex >> ptIdx[i] >> separator >> texIdx[i];
427 else // Simply of the form A, as in, point indices only.
429 for( int i = 0 ; i < numIndices; i++ )
431 std::istringstream isindex( vet[i] );
438 //If it is a triangle
439 if( numIndices == 3 )
441 for( int i = 0 ; i < 3; i++ )
443 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
444 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
445 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
447 mTriangles.PushBack( triangle );
450 //If on the other hand it is a quad, we will create two triangles
451 else if( numIndices == 4 )
453 for( int i = 0 ; i < 3; i++ )
455 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
456 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
457 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
459 mTriangles.PushBack( triangle );
462 for( int i = 0 ; i < 3; i++ )
464 int idx = ( i + 2 ) % numIndices;
465 triangle2.pntIndex[i] = ptIdx[idx] - 1 - pntAcum;
466 triangle2.nrmIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
467 triangle2.texIndex[i] = texIdx[idx] - 1 - texAcum;
469 mTriangles.PushBack( triangle2 );
473 else if ( tag == "usemtl" )
475 isline >> strMatActual;
477 else if ( tag == "mtllib" )
479 isline >> strMatActual;
481 else if ( tag == "g" )
489 CenterAndScale( true, mPoints );
491 mHasTexturePoints = hasTexture;
498 void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& diffuseTextureUrl,
499 std::string& normalTextureUrl, std::string& glossTextureUrl )
505 std::string input = objBuffer;
506 std::istringstream ss(input);
507 ss.imbue(std::locale("C"));
510 std::getline( ss, line );
512 while ( std::getline( ss, line ) )
514 std::istringstream isline( line, std::istringstream::in );
519 if ( tag == "newmtl" ) //name of the material
523 else if ( tag == "Ka" ) //ambient color
525 isline >> fR >> fG >> fB;
527 else if ( tag == "Kd" ) //diffuse color
529 isline >> fR >> fG >> fB;
531 else if ( tag == "Ks" ) //specular color
533 isline >> fR >> fG >> fB;
535 else if ( tag == "Tf" ) //color
538 else if ( tag == "Ni" )
541 else if ( tag == "map_Kd" )
544 diffuseTextureUrl = info;
545 mHasDiffuseMap = true;
547 else if ( tag == "bump" )
550 normalTextureUrl = info;
551 mHasNormalMap = true;
553 else if ( tag == "map_Ks" )
556 glossTextureUrl = info;
557 mHasSpecularMap = true;
561 mMaterialLoaded = true;
564 Geometry ObjLoader::CreateGeometry( int objectProperties )
566 Geometry surface = Geometry::New();
568 Dali::Vector<Vertex> vertices;
569 Dali::Vector<Vector2> textures;
570 Dali::Vector<VertexExt> verticesExt;
571 Dali::Vector<unsigned short> indices;
573 CreateGeometryArray( vertices, textures, verticesExt, indices );
575 //All vertices need at least Position and Normal
576 Property::Map vertexFormat;
577 vertexFormat["aPosition"] = Property::VECTOR3;
578 vertexFormat["aNormal"] = Property::VECTOR3;
579 PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat );
580 surfaceVertices.SetData( &vertices[0], vertices.Size() );
581 surface.AddVertexBuffer( surfaceVertices );
583 //Some need texture coordinates
584 if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTexturePoints && mHasDiffuseMap )
586 Property::Map textureFormat;
587 textureFormat["aTexCoord"] = Property::VECTOR2;
588 PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat );
589 extraVertices.SetData( &textures[0], textures.Size() );
591 surface.AddVertexBuffer( extraVertices );
594 //Some need tangent and bitangent
595 if( ( objectProperties & TANGENTS ) && ( objectProperties & BINOMIALS ) && mHasTexturePoints )
597 Property::Map vertexExtFormat;
598 vertexExtFormat["aTangent"] = Property::VECTOR3;
599 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
600 PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat );
601 extraVertices.SetData( &verticesExt[0], verticesExt.Size() );
603 surface.AddVertexBuffer( extraVertices );
606 //If indices are required, we set them.
607 if ( indices.Size() )
609 surface.SetIndexBuffer ( &indices[0], indices.Size() );
615 Vector3 ObjLoader::GetCenter()
617 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
621 Vector3 ObjLoader::GetSize()
623 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
627 void ObjLoader::ClearArrays()
637 mSceneLoaded = false;
640 bool ObjLoader::IsTexturePresent()
642 return mHasTexturePoints;
645 bool ObjLoader::IsDiffuseMapPresent()
647 return mHasDiffuseMap;
650 bool ObjLoader::IsNormalMapPresent()
652 return mHasNormalMap;
655 bool ObjLoader::IsSpecularMapPresent()
657 return mHasSpecularMap;
660 } // namespace Internal
661 } // namespace Toolkit