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<int> & 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 //TODO: Use a better function to calculate tangents
172 if( mTangents.Size() == 0 )
174 mTangents.Resize(mNormals.Size());
175 mBiTangents.Resize(mNormals.Size());
176 CalculateTangentArray(mPoints, mTextures, mTriangles, mNormals, mTangents);
177 for (unsigned int ui = 0 ; ui < mNormals.Size() ; ++ui )
179 mBiTangents[ui] = mNormals[ui].Cross(mTangents[ui]);
183 //Check the number of points textures and normals
184 if ((mPoints.Size() == mTextures.Size()) && (mTextures.Size() == mNormals.Size()))
186 //We create the vertices array. For now we just copy points info
187 for (unsigned int ui = 0 ; ui < mPoints.Size() ; ++ui )
190 vertex.position = mPoints[ui];
191 vertices.PushBack(vertex);
193 textures.PushBack(Vector2());
194 verticesExt.PushBack(VertexExt());
197 //We copy the indices
198 for (unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
200 for (int j = 0 ; j < 3 ; ++j)
202 indices.PushBack(mTriangles[ui].pntIndex[j]);
204 vertices[mTriangles[ui].pntIndex[j]].normal = mNormals[mTriangles[ui].nrmIndex[j]];
206 textures[mTriangles[ui].pntIndex[j]] = mTextures[mTriangles[ui].texIndex[j]];
208 verticesExt[mTriangles[ui].pntIndex[j]].tangent = mTangents[mTriangles[ui].nrmIndex[j]];
209 verticesExt[mTriangles[ui].pntIndex[j]].bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
215 //We have to normalize the arrays so we can draw we just one index array
216 for (unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
218 for (int j = 0 ; j < 3 ; ++j)
221 vertex.position = mPoints[mTriangles[ui].pntIndex[j]];
222 vertex.normal = mNormals[mTriangles[ui].nrmIndex[j]];
223 vertices.PushBack(vertex);
225 textures.PushBack(mTextures[mTriangles[ui].texIndex[j]]);
228 vertexExt.tangent = mTangents[mTriangles[ui].nrmIndex[j]];
229 vertexExt.bitangent = mBiTangents[mTriangles[ui].nrmIndex[j]];
230 verticesExt.PushBack(vertexExt);
236 bool ObjLoader::Load(char* objBuffer, std::streampos fileSize, std::string& materialFile)
240 std::string vet[4], name;
244 TriIndex triangle,triangle2;
245 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
249 //Init AABB for the file
252 std::string strMatActual;
254 std::string input = objBuffer;
255 std::istringstream ss(input);
256 ss.imbue(std::locale("C"));
260 std::getline(ss, line);
262 while (std::getline(ss, line))
264 std::istringstream isline(line, std::istringstream::in);
271 //Two different objects in the same file
275 mPoints.PushBack(point);
277 mSceneAABB.ConsiderNewPointInVolume(point);
279 else if (tag == "vn")
285 mNormals.PushBack(point);
287 else if (tag == "#_#tangent")
293 mTangents.PushBack(point);
295 else if (tag == "#_#binormal")
301 mBiTangents.PushBack(point);
303 else if (tag == "vt")
308 texture.y = 1.0-texture.y;
309 mTextures.PushBack(texture);
311 else if (tag == "#_#vt1")
316 texture.y = 1.0-texture.y;
317 mTextures2.PushBack(texture);
332 while( isline >> vet[numIndices] )
339 //File could not have texture Coordinates
340 if (strstr(vet[0].c_str(),"//"))
342 for( int i = 0 ; i < numIndices; i++)
344 std::istringstream isindex(vet[i]);
345 isindex >> ptIdx[i] >> separator >> nrmIdx[i];
349 else if (strstr(vet[0].c_str(),"/"))
351 for( int i = 0 ; i < numIndices; i++)
353 std::istringstream isindex(vet[i]);
354 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
359 for( int i = 0 ; i < numIndices; i++)
361 std::istringstream isindex(vet[i]);
368 //If it is a triangle
369 if( numIndices == 3 )
371 for( int i = 0 ; i < 3; i++)
373 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
374 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
375 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
377 mTriangles.PushBack(triangle);
380 //If on the other hand it is a quad, we will create two triangles
381 else if( numIndices == 4 )
383 for( int i = 0 ; i < 3; i++)
385 triangle.pntIndex[i] = ptIdx[i] - 1 - pntAcum;
386 triangle.nrmIndex[i] = nrmIdx[i] - 1 - nrmAcum;
387 triangle.texIndex[i] = texIdx[i] - 1 - texAcum;
389 mTriangles.PushBack(triangle);
392 for( int i = 0 ; i < 3; i++)
394 int idx = (i+2) % numIndices;
395 triangle2.pntIndex[i] = ptIdx[idx] - 1 - pntAcum;
396 triangle2.nrmIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
397 triangle2.texIndex[i] = texIdx[idx] - 1 - texAcum;
399 mTriangles.PushBack(triangle2);
403 else if (tag == "usemtl")
405 isline >> strMatActual;
407 else if (tag == "mtllib")
409 isline >> strMatActual;
422 pntAcum += (int)mPoints.Size();
423 texAcum += (int)mTextures.Size();
424 nrmAcum += (int)mNormals.Size();
426 CenterAndScale(true, mPoints);
439 void ObjLoader::LoadMaterial(char* objBuffer, std::streampos fileSize, std::string& texture0Url, std::string& texture1Url, std::string& texture2Url)
445 std::string input = objBuffer;
446 std::istringstream ss(input);
447 ss.imbue(std::locale("C"));
450 std::getline(ss, line);
452 while (std::getline(ss, line))
454 std::istringstream isline(line, std::istringstream::in);
459 if (tag == "newmtl") //name of the material
463 else if (tag == "Kd") //diffuse color
465 isline >> fR >> fG >> fB;
467 else if (tag == "Kd") //Ambient color
469 isline >> fR >> fG >> fB;
471 else if (tag == "Tf") //color
474 else if (tag == "Ni")
477 else if (tag == "map_Kd")
482 else if (tag == "bump")
487 else if (tag == "map_Ks")
494 mMaterialLoaded = true;
497 Geometry ObjLoader::CreateGeometry(Toolkit::Model3dView::IlluminationType illuminationType)
499 Dali::Vector<Vertex> vertices;
500 Dali::Vector<Vector2> textures;
501 Dali::Vector<VertexExt> verticesExt;
502 Dali::Vector<int> indices;
504 CreateGeometryArray(vertices, textures, verticesExt, indices);
506 //All vertices need at least Position and Normal
507 Property::Map vertexFormat;
508 vertexFormat["aPosition"] = Property::VECTOR3;
509 vertexFormat["aNormal"] = Property::VECTOR3;
510 PropertyBuffer surfaceVertices = PropertyBuffer::New( vertexFormat, vertices.Size() );
511 surfaceVertices.SetData( &vertices[0] );
513 Geometry surface = Geometry::New();
514 surface.AddVertexBuffer( surfaceVertices );
516 //Some need texture coordinates
517 if( (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP ) || (illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_TEXTURE ) )
519 Property::Map textureFormat;
520 textureFormat["aTexCoord"] = Property::VECTOR2;
521 PropertyBuffer extraVertices = PropertyBuffer::New( textureFormat, textures.Size() );
522 extraVertices.SetData( &textures[0] );
524 surface.AddVertexBuffer( extraVertices );
527 //Some need tangent and bitangent
528 if( illuminationType == Toolkit::Model3dView::DIFFUSE_WITH_NORMAL_MAP )
530 Property::Map vertexExtFormat;
531 vertexExtFormat["aTangent"] = Property::VECTOR3;
532 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
533 PropertyBuffer extraVertices = PropertyBuffer::New( vertexExtFormat, verticesExt.Size() );
534 extraVertices.SetData( &verticesExt[0] );
536 surface.AddVertexBuffer( extraVertices );
542 Property::Map indicesVertexFormat;
543 indicesVertexFormat["aIndices"] = Property::INTEGER;
544 PropertyBuffer indicesToVertices = PropertyBuffer::New( indicesVertexFormat, indices.Size() );
545 indicesToVertices.SetData(&indices[0]);
547 surface.SetIndexBuffer ( indicesToVertices );
550 surface.SetRequiresDepthTesting(true);
551 //surface.SetProperty(Geometry::Property::GEOMETRY_HALF_EXTENTS, GetSize() * 0.5);
560 Vector3 ObjLoader::GetCenter()
562 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
566 Vector3 ObjLoader::GetSize()
568 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
572 void ObjLoader::ClearArrays()
582 mSceneLoaded = false;
585 } // namespace Internal
586 } // namespace Toolkit