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;
44 ObjLoader::~ObjLoader()
49 bool ObjLoader::IsSceneLoaded()
54 bool ObjLoader::IsMaterialLoaded()
56 return mMaterialLoaded;
59 //TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
60 void ObjLoader::CalculateTangentArray(const Dali::Vector<Vector3>& vertex,
61 const Dali::Vector<Vector2>& texcoord,
62 Dali::Vector<TriIndex>& triangle,
63 Dali::Vector<Vector3>& normal,
64 Dali::Vector<Vector3>& tangent)
67 normal.Resize(vertex.Size());
69 Vector3 *tan1 = new Vector3[vertex.Size() * 2];
71 memset( tan1, 0, normal.Size() * sizeof(Vector3) * 2 );
72 memset( &normal[0], 0, normal.Size() * sizeof(Vector3) * 2 );
74 for ( unsigned long a = 0; a < triangle.Size(); a++ )
76 Vector3 Tangent, Bitangent, Normal;
78 const Vector3& v0 = vertex[triangle[a].pntIndex[0]];
79 const Vector3& v1 = vertex[triangle[a].pntIndex[1]];
80 const Vector3& v2 = vertex[triangle[a].pntIndex[2]];
82 Vector3 Edge1 = v1 - v0;
83 Vector3 Edge2 = v2 - v0;
85 Normal = Edge1.Cross(Edge2);
87 const Vector2& w0 = texcoord[triangle[a].texIndex[0]];
88 const Vector2& w1 = texcoord[triangle[a].texIndex[1]];
89 const Vector2& w2 = texcoord[triangle[a].texIndex[2]];
91 float DeltaU1 = w1.x - w0.x;
92 float DeltaV1 = w1.y - w0.y;
93 float DeltaU2 = w2.x - w0.x;
94 float DeltaV2 = w2.y - w0.y;
96 float f = 1.0f / (DeltaU1 * DeltaV2 - DeltaU2 * DeltaV1);
98 Tangent.x = f * ( DeltaV2 * Edge1.x - DeltaV1 * Edge2.x );
99 Tangent.y = f * ( DeltaV2 * Edge1.y - DeltaV1 * Edge2.y );
100 Tangent.z = f * ( DeltaV2 * Edge1.z - DeltaV1 * Edge2.z );
102 tan1[triangle[a].pntIndex[0]] += Tangent;
103 tan1[triangle[a].pntIndex[1]] += Tangent;
104 tan1[triangle[a].pntIndex[2]] += Tangent;
106 normal[triangle[a].pntIndex[0]] += Normal;
107 normal[triangle[a].pntIndex[1]] += Normal;
108 normal[triangle[a].pntIndex[2]] += Normal;
111 for ( unsigned long a = 0; a < triangle.Size(); a++ )
113 for ( unsigned long j = 0; j < 3; j++ )
115 triangle[a].nrmIndex[j] = triangle[a].pntIndex[j];
119 for ( unsigned long a = 0; a < normal.Size(); a++ )
121 normal[a].Normalize();
123 const Vector3& n = normal[a];
124 const Vector3& t = tan1[a];
126 // Gram-Schmidt orthogonalize
127 Vector3 calc = t - n * n.Dot(t);
129 tangent[a] = Vector3( calc.x,calc.y,calc.z );
136 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
138 BoundingVolume newAABB;
140 Vector3 sceneSize = GetSize();
142 float biggestDimension = sceneSize.x;
143 if( sceneSize.y > biggestDimension )
145 biggestDimension = sceneSize.y;
147 if( sceneSize.z > biggestDimension )
149 biggestDimension = sceneSize.z;
154 for( unsigned int ui = 0; ui < points.Size(); ++ui )
156 points[ui] = points[ui] - GetCenter();
157 points[ui] = points[ui] / biggestDimension;
158 newAABB.ConsiderNewPointInVolume(points[ui]);
161 mSceneAABB = newAABB;
164 void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex> & vertices,
165 Dali::Vector<Vector2> & textures,
166 Dali::Vector<VertexExt> & verticesExt,
167 Dali::Vector<unsigned short> & indices)
169 //If we don't have tangents, calculate them
170 //we need to recalculate the normals too, because we need just one normal,tangent, bitangent per vertex
171 //In the case of a textureless object, we don't need tangents and so we skip this step
172 //TODO: Use a better function to calculate tangents
173 if( mTangents.Size() == 0 && mHasTexture && mHasNormalMap )
175 mTangents.Resize( mNormals.Size() );
176 mBiTangents.Resize( mNormals.Size() );
177 CalculateTangentArray( mPoints, mTextures, mTriangles, mNormals, mTangents );
178 for ( unsigned int ui = 0 ; ui < mNormals.Size() ; ++ui )
180 mBiTangents[ui] = mNormals[ui].Cross(mTangents[ui]);
184 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
188 mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
192 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
195 //Check the number of points textures and normals
196 if ( mapsCorrespond )
198 int numPoints = mPoints.Size();
199 int numIndices = 3 * mTriangles.Size();
200 vertices.Resize( numPoints );
201 textures.Resize( numPoints );
202 verticesExt.Resize( numPoints );
203 indices.Resize( numIndices );
205 //We create the vertices array. For now we just copy points info
206 for (unsigned int ui = 0 ; ui < mPoints.Size() ; ++ui )
209 vertex.position = mPoints[ui];
210 vertices[ui] = vertex;
214 textures[ui] = Vector2();
215 verticesExt[ui] = VertexExt();
221 //We copy the indices
222 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
224 for ( int j = 0 ; j < 3 ; ++j )
226 indices[indiceIndex] = mTriangles[ui].pntIndex[j];
229 vertices[mTriangles[ui].pntIndex[j]].normal = mNormals[mTriangles[ui].nrmIndex[j]];
233 textures[mTriangles[ui].pntIndex[j]] = mTextures[mTriangles[ui].texIndex[j]];
236 if ( mHasNormalMap && mHasTexture )
238 verticesExt[mTriangles[ui].pntIndex[j]].tangent = mTangents[mTriangles[ui].nrmIndex[j]];
239 verticesExt[mTriangles[ui].pntIndex[j]].bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
246 int numVertices = 3 * mTriangles.Size();
247 vertices.Resize( numVertices );
248 textures.Resize( numVertices );
249 verticesExt.Resize( numVertices );
253 //We have to normalize the arrays so we can draw we just one index array
254 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
256 for ( int j = 0 ; j < 3 ; ++j )
259 vertex.position = mPoints[mTriangles[ui].pntIndex[j]];
260 vertex.normal = mNormals[mTriangles[ui].nrmIndex[j]];
261 vertices[index] = vertex;
265 textures[index] = mTextures[mTriangles[ui].texIndex[j]];
268 if ( mHasNormalMap && mHasTexture )
271 vertexExt.tangent = mTangents[mTriangles[ui].nrmIndex[j]];
272 vertexExt.bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
273 verticesExt[index] = vertexExt;
282 bool ObjLoader::Load( char* objBuffer, std::streampos fileSize, std::string& materialFile )
286 std::string vet[4], name;
290 TriIndex triangle,triangle2;
291 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
293 bool hasTexture = false;
294 bool hasNormalMap = false;
297 //Init AABB for the file
300 std::string strMatActual;
302 std::string input = objBuffer;
303 std::istringstream ss(input);
304 ss.imbue( std::locale( "C" ) );
308 std::getline( ss, line );
310 while ( std::getline( ss, line ) )
312 std::istringstream isline( line, std::istringstream::in );
319 //Two different objects in the same file
323 mPoints.PushBack( point );
325 mSceneAABB.ConsiderNewPointInVolume( point );
327 else if ( tag == "vn" )
333 mNormals.PushBack( point );
335 else if ( tag == "#_#tangent" )
341 mTangents.PushBack( point );
344 else if ( tag == "#_#binormal" )
350 mBiTangents.PushBack( point );
353 else if ( tag == "vt" )
358 texture.y = 1.0-texture.y;
359 mTextures.PushBack( texture );
361 else if ( tag == "#_#vt1" )
366 texture.y = 1.0-texture.y;
367 mTextures2.PushBack( texture );
369 else if ( tag == "s" )
372 else if ( tag == "f" )
382 while( isline >> vet[numIndices] )
390 if ( strstr( vet[0].c_str(),"//" ) ) //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( vet[0].c_str(),"/" ) ) //Has texture coordinates, and possibly also 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];
408 else //Has just points.
410 for( int i = 0 ; i < numIndices; i++ )
412 std::istringstream isindex( vet[i] );
419 //If it is a triangle
420 if( numIndices == 3 )
422 for( int i = 0 ; i < 3; i++ )
424 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
425 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
426 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
428 mTriangles.PushBack( triangle );
431 //If on the other hand it is a quad, we will create two triangles
432 else if( numIndices == 4 )
434 for( int i = 0 ; i < 3; i++ )
436 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
437 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
438 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
440 mTriangles.PushBack( triangle );
443 for( int i = 0 ; i < 3; i++ )
445 int idx = ( i + 2 ) % numIndices;
446 triangle2.pntIndex[i] = ptIdx[idx] - 1 - pntAcum;
447 triangle2.nrmIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
448 triangle2.texIndex[i] = texIdx[idx] - 1 - texAcum;
450 mTriangles.PushBack( triangle2 );
454 else if ( tag == "usemtl" )
456 isline >> strMatActual;
458 else if ( tag == "mtllib" )
460 isline >> strMatActual;
462 else if ( tag == "g" )
473 CenterAndScale( true, mPoints );
475 mHasTexture = hasTexture;
476 mHasNormalMap = hasNormalMap;
484 void ObjLoader::LoadMaterial( char* objBuffer, std::streampos fileSize, std::string& texture0Url,
485 std::string& texture1Url, std::string& texture2Url )
491 std::string input = objBuffer;
492 std::istringstream ss(input);
493 ss.imbue(std::locale("C"));
496 std::getline( ss, line );
498 while ( std::getline( ss, line ) )
500 std::istringstream isline( line, std::istringstream::in );
505 if ( tag == "newmtl" ) //name of the material
509 else if ( tag == "Kd" ) //diffuse color
511 isline >> fR >> fG >> fB;
513 else if ( tag == "Kd" ) //Ambient color
515 isline >> fR >> fG >> fB;
517 else if ( tag == "Tf" ) //color
520 else if ( tag == "Ni" )
523 else if ( tag == "map_Kd" )
528 else if ( tag == "bump" )
532 mHasNormalMap = true;
534 else if ( tag == "map_Ks" )
541 mMaterialLoaded = true;
544 Geometry ObjLoader::CreateGeometry( Toolkit::Model3dView::IlluminationType illuminationType )
546 Dali::Vector<Vertex> vertices;
547 Dali::Vector<Vector2> textures;
548 Dali::Vector<VertexExt> verticesExt;
549 Dali::Vector<unsigned short> indices;
551 CreateGeometryArray( vertices, textures, verticesExt, indices );
553 //All vertices need at least Position and Normal
554 Property::Map vertexFormat;
555 vertexFormat["aPosition"] = Property::VECTOR3;
556 vertexFormat["aNormal"] = Property::VECTOR3;
557 PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat );
558 surfaceVertices.SetData( &vertices[0], vertices.Size() );
560 Geometry surface = Geometry::New();
561 surface.AddVertexBuffer( surfaceVertices );
563 //Some need texture coordinates
564 if( ( (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP ) ||
565 (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ) ) && mHasTexture )
567 Property::Map textureFormat;
568 textureFormat["aTexCoord"] = Property::VECTOR2;
569 PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat );
570 extraVertices.SetData( &textures[0], textures.Size() );
572 surface.AddVertexBuffer( extraVertices );
575 //Some need tangent and bitangent
576 if( illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP && mHasNormalMap && mHasTexture )
578 Property::Map vertexExtFormat;
579 vertexExtFormat["aTangent"] = Property::VECTOR3;
580 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
581 PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat );
582 extraVertices.SetData( &verticesExt[0], verticesExt.Size() );
584 surface.AddVertexBuffer( extraVertices );
587 if ( indices.Size() )
589 surface.SetIndexBuffer ( &indices[0], indices.Size() );
599 Vector3 ObjLoader::GetCenter()
601 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
605 Vector3 ObjLoader::GetSize()
607 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
611 void ObjLoader::ClearArrays()
621 mSceneLoaded = false;
624 bool ObjLoader::IsTexturePresent()
629 bool ObjLoader::IsNormalMapPresent()
631 return mHasNormalMap;
634 } // namespace Internal
635 } // namespace Toolkit