2 * Copyright (c) 2017 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>
32 const int MAX_POINT_INDICES = 4;
35 ObjLoader::ObjLoader()
36 : mSceneLoaded( false ),
37 mMaterialLoaded( false ),
38 mHasTexturePoints( false ),
39 mHasDiffuseMap( false ),
40 mHasNormalMap( false ),
41 mHasSpecularMap( false )
46 ObjLoader::~ObjLoader()
51 bool ObjLoader::IsSceneLoaded()
56 bool ObjLoader::IsMaterialLoaded()
58 return mMaterialLoaded;
61 void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
62 Dali::Vector<Vector3>& normals )
64 int numFaceVertices = 3 * triangles.Size(); //Vertex per face, as each point has different normals for each face.
65 int normalIndex = 0; //Tracks progress through the array of normals.
68 normals.Resize( numFaceVertices );
70 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
71 for( unsigned long i = 0; i < triangles.Size(); i++ )
74 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
75 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
76 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
79 Vector3 edge1 = v1 - v0;
80 Vector3 edge2 = v2 - v0;
82 //Using edges as vectors on the plane, cross to get the normal.
83 Vector3 normalVector = edge1.Cross(edge2);
84 normalVector.Normalize();
86 //Assign normals to points.
87 for( unsigned long j = 0; j < 3; j++, normalIndex++ )
89 triangles[i].normalIndex[j] = normalIndex;
90 normals[normalIndex] = normalVector;
95 void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles,
96 Dali::Vector<Vector3>& normals )
98 int normalIndex = 0; //Tracks progress through the array of normals.
101 normals.Resize( vertices.Size() ); //One (averaged) normal per point.
103 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
104 //We then add the triangle's normal to the cumulative normals at each point of it
105 for( unsigned long i = 0; i < triangles.Size(); i++ )
108 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
109 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
110 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
113 Vector3 edge1 = v1 - v0;
114 Vector3 edge2 = v2 - v0;
116 //Using edges as vectors on the plane, cross to get the normal.
117 Vector3 normalVector = edge1.Cross(edge2);
119 //Add this triangle's normal to the cumulative normal of each constituent point and set the index of the normal accordingly.
120 for( unsigned long j = 0; j < 3; j++, normalIndex++ )
122 triangles[i].normalIndex[j] = triangles[i].pointIndex[j]; //Normal index matches up to vertex index, as one normal per vertex.
123 normals[triangles[i].normalIndex[j]] += normalVector;
127 //Normalise the normals.
128 for( unsigned long i = 0; i < normals.Size(); i++ )
130 normals[i].Normalize();
134 void ObjLoader::CalculateTangentFrame()
136 //Reset tangent vector to hold new values.
138 mTangents.Resize( mPoints.Size() );
140 //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each point.
141 for ( unsigned long a = 0; a < mTriangles.Size(); a++ )
143 Vector3 tangentVector;
145 const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
146 const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
147 const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
149 Vector3 edge1 = v1 - v0;
150 Vector3 edge2 = v2 - v0;
152 const Vector2& w0 = mTextures[mTriangles[a].textureIndex[0]];
153 const Vector2& w1 = mTextures[mTriangles[a].textureIndex[1]];
154 const Vector2& w2 = mTextures[mTriangles[a].textureIndex[2]];
156 float deltaU1 = w1.x - w0.x;
157 float deltaV1 = w1.y - w0.y;
158 float deltaU2 = w2.x - w0.x;
159 float deltaV2 = w2.y - w0.y;
161 float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
163 tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
164 tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
165 tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
167 mTangents[mTriangles[a].pointIndex[0]] += tangentVector;
168 mTangents[mTriangles[a].pointIndex[1]] += tangentVector;
169 mTangents[mTriangles[a].pointIndex[2]] += tangentVector;
172 //Orthogonalize tangents and set binormals.
173 for ( unsigned long a = 0; a < mTangents.Size(); a++ )
175 const Vector3& n = mNormals[a];
176 const Vector3& t = mTangents[a];
178 // Gram-Schmidt orthogonalize
179 mTangents[a] = t - n * n.Dot(t);
180 mTangents[a].Normalize();
184 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
186 BoundingVolume newAABB;
188 Vector3 sceneSize = GetSize();
190 float biggestDimension = sceneSize.x;
191 if( sceneSize.y > biggestDimension )
193 biggestDimension = sceneSize.y;
195 if( sceneSize.z > biggestDimension )
197 biggestDimension = sceneSize.z;
201 for( unsigned int ui = 0; ui < points.Size(); ++ui )
203 points[ui] = points[ui] - GetCenter();
204 points[ui] = points[ui] / biggestDimension;
205 newAABB.ConsiderNewPointInVolume(points[ui]);
208 mSceneAABB = newAABB;
211 void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
212 Dali::Vector<Vector3>& normals,
213 Dali::Vector<Vector3>& tangents,
214 Dali::Vector<Vector2>& textures,
215 Dali::Vector<unsigned short> & indices,
216 bool useSoftNormals )
218 //We must calculate the tangents if they weren't supplied, or if they don't match up.
219 bool mustCalculateTangents = ( mTangents.Size() == 0 ) || ( mTangents.Size() != mNormals.Size() );
221 //However, we don't need to do this if the object doesn't use textures to begin with.
222 mustCalculateTangents &= mHasTexturePoints;
224 //We also have to recalculate the normals if we need to calculate tangents,
225 // as we need just one normal, tangent per vertex, rather than the supplied per-face vertices.
226 //Alternatively, we need to calculate the normals if there weren't any to begin with.
227 if( mNormals.Size() == 0 || mustCalculateTangents )
229 if( useSoftNormals || mustCalculateTangents )
231 CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
235 CalculateHardFaceNormals( mPoints, mTriangles, mNormals );
239 if( mHasTexturePoints && mustCalculateTangents )
241 CalculateTangentFrame();
244 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
246 if ( mHasTexturePoints )
248 mapsCorrespond = ( mPoints.Size() == mTextures.Size() ) && ( mTextures.Size() == mNormals.Size() );
252 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
255 //Check the number of points textures and normals
256 if ( mapsCorrespond )
258 int numPoints = mPoints.Size();
259 int numIndices = 3 * mTriangles.Size();
260 positions.Resize( numPoints );
261 normals.Resize( numPoints );
262 tangents.Resize( numPoints );
263 textures.Resize( numPoints );
264 indices.Resize( numIndices );
266 //We create the vertices array. For now we just copy points info
271 //We copy the indices
272 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
274 for ( int j = 0 ; j < 3 ; ++j )
276 indices[indiceIndex] = mTriangles[ui].pointIndex[j];
279 normals[mTriangles[ui].pointIndex[j]] = mNormals[mTriangles[ui].normalIndex[j]];
281 if ( mHasTexturePoints )
283 textures[mTriangles[ui].pointIndex[j]] = mTextures[mTriangles[ui].textureIndex[j]];
284 tangents[mTriangles[ui].pointIndex[j]] = mTangents[mTriangles[ui].normalIndex[j]];
291 int numVertices = 3 * mTriangles.Size();
292 positions.Resize( numVertices );
293 normals.Resize( numVertices );
294 textures.Resize( numVertices );
295 tangents.Resize( numVertices );
299 //We have to normalize the arrays so we can draw we just one index array
300 for( unsigned int ui = 0; ui < mTriangles.Size(); ++ui )
302 for ( int j = 0 ; j < 3 ; ++j )
304 positions[index] = mPoints[mTriangles[ui].pointIndex[j]];
305 normals[index] = mNormals[mTriangles[ui].normalIndex[j]];
307 if( mHasTexturePoints )
309 textures[index] = mTextures[mTriangles[ui].textureIndex[j]];
310 tangents[index] = mTangents[mTriangles[ui].normalIndex[j]];
319 bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
323 std::string vet[MAX_POINT_INDICES], name;
324 int ptIdx[MAX_POINT_INDICES];
325 int nrmIdx[MAX_POINT_INDICES];
326 int texIdx[MAX_POINT_INDICES];
327 TriIndex triangle,triangle2;
328 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
330 bool hasTexture = false;
333 //Init AABB for the file
336 std::string strMatActual;
338 std::string input = objBuffer;
339 std::istringstream ss(input);
340 ss.imbue( std::locale( "C" ) );
344 std::getline( ss, line );
346 while ( std::getline( ss, line ) )
348 std::istringstream isline( line, std::istringstream::in );
355 //Two different objects in the same file
359 mPoints.PushBack( point );
361 mSceneAABB.ConsiderNewPointInVolume( point );
363 else if ( tag == "vn" )
369 mNormals.PushBack( point );
371 else if ( tag == "#_#tangent" )
377 mTangents.PushBack( point );
379 else if ( tag == "#_#binormal" )
385 mBiTangents.PushBack( point );
387 else if ( tag == "vt" )
392 texture.y = 1.0-texture.y;
393 mTextures.PushBack( texture );
395 else if ( tag == "#_#vt1" )
400 texture.y = 1.0-texture.y;
401 mTextures2.PushBack( texture );
403 else if ( tag == "s" )
406 else if ( tag == "f" )
416 while( isline >> vet[numIndices] && numIndices < MAX_POINT_INDICES )
421 //Hold slashes that separate attributes of the same point.
425 const char * subString; //A pointer to the position in the string as we move through it.
427 subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
431 if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
433 for( int i = 0 ; i < numIndices; i++)
435 std::istringstream isindex( vet[i] );
436 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
440 else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
442 for( int i = 0 ; i < numIndices; i++ )
444 std::istringstream isindex( vet[i] );
445 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
450 else // Of the form A/B, so has points and textures but no normals.
452 for( int i = 0 ; i < numIndices; i++ )
454 std::istringstream isindex( vet[i] );
455 isindex >> ptIdx[i] >> separator >> texIdx[i];
462 else // Simply of the form A, as in, point indices only.
464 for( int i = 0 ; i < numIndices; i++ )
466 std::istringstream isindex( vet[i] );
473 //If it is a triangle
474 if( numIndices == 3 )
476 for( int i = 0 ; i < 3; i++ )
478 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
479 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
480 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
482 mTriangles.PushBack( triangle );
485 //If on the other hand it is a quad, we will create two triangles
486 else if( numIndices == 4 )
488 for( int i = 0 ; i < 3; i++ )
490 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
491 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
492 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
494 mTriangles.PushBack( triangle );
497 for( int i = 0 ; i < 3; i++ )
499 int idx = ( i + 2 ) % numIndices;
500 triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
501 triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
502 triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
504 mTriangles.PushBack( triangle2 );
508 else if ( tag == "usemtl" )
510 isline >> strMatActual;
512 else if ( tag == "mtllib" )
514 isline >> strMatActual;
516 else if ( tag == "g" )
524 CenterAndScale( true, mPoints );
526 mHasTexturePoints = hasTexture;
533 void ObjLoader::LoadMaterial( char* objBuffer,
534 std::streampos fileSize,
535 std::string& diffuseTextureUrl,
536 std::string& normalTextureUrl,
537 std::string& glossTextureUrl )
543 std::string input = objBuffer;
544 std::istringstream ss(input);
545 ss.imbue(std::locale("C"));
548 std::getline( ss, line );
550 while ( std::getline( ss, line ) )
552 std::istringstream isline( line, std::istringstream::in );
557 if ( tag == "newmtl" ) //name of the material
561 else if ( tag == "Ka" ) //ambient color
563 isline >> fR >> fG >> fB;
565 else if ( tag == "Kd" ) //diffuse color
567 isline >> fR >> fG >> fB;
569 else if ( tag == "Ks" ) //specular color
571 isline >> fR >> fG >> fB;
573 else if ( tag == "Tf" ) //color
576 else if ( tag == "Ni" )
579 else if ( tag == "map_Kd" )
582 diffuseTextureUrl = info;
583 mHasDiffuseMap = true;
585 else if ( tag == "bump" )
588 normalTextureUrl = info;
589 mHasNormalMap = true;
591 else if ( tag == "map_Ks" )
594 glossTextureUrl = info;
595 mHasSpecularMap = true;
599 mMaterialLoaded = true;
602 Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
604 Geometry surface = Geometry::New();
606 Dali::Vector<Vector3> positions;
607 Dali::Vector<Vector3> normals;
608 Dali::Vector<Vector3> tangents;
609 Dali::Vector<Vector2> textures;
610 Dali::Vector<unsigned short> indices;
612 CreateGeometryArray( positions, normals, tangents, textures, indices, useSoftNormals );
614 //All vertices need at least Position and Normal
616 Property::Map positionMap;
617 positionMap["aPosition"] = Property::VECTOR3;
618 PropertyBuffer positionBuffer = PropertyBuffer::New( positionMap );
619 positionBuffer.SetData( positions.Begin(), positions.Count() );
621 Property::Map normalMap;
622 normalMap["aNormal"] = Property::VECTOR3;
623 PropertyBuffer normalBuffer = PropertyBuffer::New( normalMap );
624 normalBuffer.SetData( normals.Begin(), normals.Count() );
626 surface.AddVertexBuffer( positionBuffer );
627 surface.AddVertexBuffer( normalBuffer );
630 if( ( objectProperties & TANGENTS ) && mHasTexturePoints )
632 Property::Map tangentMap;
633 tangentMap["aTangent"] = Property::VECTOR3;
634 PropertyBuffer tangentBuffer = PropertyBuffer::New( tangentMap );
635 tangentBuffer.SetData( tangents.Begin(), tangents.Count() );
637 surface.AddVertexBuffer( tangentBuffer );
640 //Some need texture coordinates
641 if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTexturePoints )
643 Property::Map textCoordMap;
644 textCoordMap["aTexCoord"] = Property::VECTOR2;
645 PropertyBuffer texCoordBuffer = PropertyBuffer::New( textCoordMap );
646 texCoordBuffer.SetData( textures.Begin(), textures.Count() );
648 surface.AddVertexBuffer( texCoordBuffer );
651 //If indices are required, we set them.
652 if ( indices.Size() )
654 surface.SetIndexBuffer ( &indices[0], indices.Size() );
660 Vector3 ObjLoader::GetCenter()
662 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
666 Vector3 ObjLoader::GetSize()
668 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
672 void ObjLoader::ClearArrays()
683 mSceneLoaded = false;
686 bool ObjLoader::IsTexturePresent()
688 return mHasTexturePoints;
691 bool ObjLoader::IsDiffuseMapPresent()
693 return mHasDiffuseMap;
696 bool ObjLoader::IsNormalMapPresent()
698 return mHasNormalMap;
701 bool ObjLoader::IsSpecularMapPresent()
703 return mHasSpecularMap;
706 } // namespace PbrDemo