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>
31 const int MAX_POINT_INDICES = 4;
34 ObjLoader::ObjLoader()
35 : mSceneLoaded( false ),
36 mMaterialLoaded( false ),
37 mHasTextureUv( false ),
38 mHasDiffuseMap( false ),
39 mHasNormalMap( false ),
40 mHasSpecularMap( false )
45 ObjLoader::~ObjLoader()
50 bool ObjLoader::IsSceneLoaded()
55 bool ObjLoader::IsMaterialLoaded()
57 return mMaterialLoaded;
60 void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
61 Dali::Vector<Vector3>& normals )
63 int numFaceVertices = 3 * triangles.Size(); //Vertices per face, as each vertex has different normals instance for each face.
64 int normalIndex = 0; //Tracks progress through the array of normals.
67 normals.Resize( numFaceVertices );
69 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
70 for( unsigned long i = 0; i < triangles.Size(); i++ )
73 const Vector3& v0 = points[triangles[i].pointIndex[0]];
74 const Vector3& v1 = points[triangles[i].pointIndex[1]];
75 const Vector3& v2 = points[triangles[i].pointIndex[2]];
78 Vector3 edge1 = v1 - v0;
79 Vector3 edge2 = v2 - v0;
81 //Using edges as vectors on the plane, cross product to get the normal.
82 Vector3 normalVector = edge1.Cross(edge2);
83 normalVector.Normalize();
85 //Assign normal index to triangle vertex and set the normal vector to the list of normals.
86 for( unsigned long j = 0; j < 3; j++, normalIndex++ )
88 triangles[i].normalIndex[j] = normalIndex;
89 normals[normalIndex] = normalVector;
94 void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
95 Dali::Vector<Vector3>& normals )
97 int normalIndex = 0; //Tracks progress through the array of normals.
100 normals.Resize( points.Size() ); //One (averaged) normal per point.
102 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
103 //We then add the triangle's normal to the cumulative normals at each point of it
104 for( unsigned long i = 0; i < triangles.Size(); i++ )
107 const Vector3& v0 = points[triangles[i].pointIndex[0]];
108 const Vector3& v1 = points[triangles[i].pointIndex[1]];
109 const Vector3& v2 = points[triangles[i].pointIndex[2]];
112 Vector3 edge1 = v1 - v0;
113 Vector3 edge2 = v2 - v0;
115 //Using edges as vectors on the plane, cross to get the normal.
116 Vector3 normalVector = edge1.Cross(edge2);
118 //Add this triangle's normal to the cumulative normal of each constituent triangle point and set the index of the normal accordingly.
119 for( unsigned long j = 0; j < 3; j++)
121 normalIndex = triangles[i].pointIndex[j];
122 triangles[i].normalIndex[j] = normalIndex; //Normal index matches up to vertex index, as one normal per vertex.
123 normals[normalIndex] += 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( mNormals.Size() );
140 //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each normal.
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 = mTextureUv[mTriangles[a].textureIndex[0]];
153 const Vector2& w1 = mTextureUv[mTriangles[a].textureIndex[1]];
154 const Vector2& w2 = mTextureUv[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 // 1.0/f could cause division by zero in some cases, this factor will act
162 // as a weight of the tangent vector and it is fixed when it is normalised.
163 float f = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
165 tangentVector.x = f * ( deltaV2 * edge1.x - deltaV1 * edge2.x );
166 tangentVector.y = f * ( deltaV2 * edge1.y - deltaV1 * edge2.y );
167 tangentVector.z = f * ( deltaV2 * edge1.z - deltaV1 * edge2.z );
169 mTangents[mTriangles[a].normalIndex[0]] += tangentVector;
170 mTangents[mTriangles[a].normalIndex[1]] += tangentVector;
171 mTangents[mTriangles[a].normalIndex[2]] += tangentVector;
174 //Orthogonalize tangents.
175 for ( unsigned long a = 0; a < mTangents.Size(); a++ )
177 const Vector3& n = mNormals[a];
178 const Vector3& t = mTangents[a];
180 // Gram-Schmidt orthogonalize
181 mTangents[a] = t - n * n.Dot(t);
182 mTangents[a].Normalize();
186 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
188 BoundingVolume newAABB;
190 Vector3 sceneSize = GetSize();
192 float biggestDimension = sceneSize.x;
193 if( sceneSize.y > biggestDimension )
195 biggestDimension = sceneSize.y;
197 if( sceneSize.z > biggestDimension )
199 biggestDimension = sceneSize.z;
203 for( unsigned int ui = 0; ui < points.Size(); ++ui )
205 points[ui] = points[ui] - GetCenter();
206 points[ui] = points[ui] / biggestDimension;
207 newAABB.ConsiderNewPointInVolume(points[ui]);
210 mSceneAABB = newAABB;
213 void ObjLoader::CreateGeometryArray( Dali::Vector<Vector3>& positions,
214 Dali::Vector<Vector3>& normals,
215 Dali::Vector<Vector3>& tangents,
216 Dali::Vector<Vector2>& textures,
217 Dali::Vector<unsigned short> & indices,
218 bool useSoftNormals )
220 //We must calculate the tangents if they weren't supplied, or if they don't match up.
221 bool mustCalculateTangents = ( mTangents.Size() == 0 ) || ( mTangents.Size() != mNormals.Size() );
223 //However, we don't need to do this if the object doesn't use textures to begin with.
224 mustCalculateTangents &= mHasTextureUv;
226 // We calculate the normals if hard normals(flat normals) is set.
227 // Use the normals provided by the file to make the tangent calculation per normal,
228 // the correct results depends of normal generated by file, otherwise we need to recalculate
229 // the normal programmatically.
230 if( ( ( mNormals.Size() == 0 ) && mustCalculateTangents ) || !useSoftNormals )
234 CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
238 CalculateHardFaceNormals( mPoints, mTriangles, mNormals );
242 if( mHasTextureUv && mustCalculateTangents )
244 CalculateTangentFrame();
247 bool mapsCorrespond; //True if the sizes of the arrays necessary to draw the object match.
251 mapsCorrespond = ( mPoints.Size() == mTextureUv.Size() ) && ( mTextureUv.Size() == mNormals.Size() );
255 mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
258 //Check the number of points textures and normals
259 if ( mapsCorrespond )
261 int numPoints = mPoints.Size();
262 int numIndices = 3 * mTriangles.Size();
263 positions.Resize( numPoints );
264 normals.Resize( numPoints );
265 tangents.Resize( numPoints );
266 textures.Resize( numPoints );
267 indices.Resize( numIndices );
269 //We create the vertices array. For now we just copy points info
274 //We copy the indices
275 for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
277 for ( int j = 0 ; j < 3 ; ++j )
279 indices[indiceIndex] = mTriangles[ui].pointIndex[j];
282 normals[mTriangles[ui].pointIndex[j]] = mNormals[mTriangles[ui].normalIndex[j]];
286 textures[mTriangles[ui].pointIndex[j]] = mTextureUv[mTriangles[ui].textureIndex[j]];
287 tangents[mTriangles[ui].pointIndex[j]] = mTangents[mTriangles[ui].normalIndex[j]];
294 int numVertices = 3 * mTriangles.Size();
295 positions.Resize( numVertices );
296 normals.Resize( numVertices );
297 textures.Resize( numVertices );
298 tangents.Resize( numVertices );
302 //We have to normalize the arrays so we can draw we just one index array
303 for( unsigned int ui = 0; ui < mTriangles.Size(); ++ui )
305 for ( int j = 0 ; j < 3 ; ++j )
307 positions[index] = mPoints[mTriangles[ui].pointIndex[j]];
308 normals[index] = mNormals[mTriangles[ui].normalIndex[j]];
312 textures[index] = mTextureUv[mTriangles[ui].textureIndex[j]];
313 tangents[index] = mTangents[mTriangles[ui].normalIndex[j]];
322 bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
326 std::string vet[MAX_POINT_INDICES], name;
327 int ptIdx[MAX_POINT_INDICES];
328 int nrmIdx[MAX_POINT_INDICES];
329 int texIdx[MAX_POINT_INDICES];
330 TriIndex triangle,triangle2;
331 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
333 bool hasTexture = false;
336 //Init AABB for the file
339 std::string strMatActual;
341 std::string input( objBuffer, fileSize );
342 std::istringstream ss(input);
343 ss.imbue( std::locale( "C" ) );
347 std::getline( ss, line );
349 while ( std::getline( ss, line ) )
351 std::istringstream isline( line, std::istringstream::in );
358 //Two different objects in the same file
362 mPoints.PushBack( point );
364 mSceneAABB.ConsiderNewPointInVolume( point );
366 else if ( tag == "vn" )
372 mNormals.PushBack( point );
374 else if ( tag == "#_#tangent" )
380 mTangents.PushBack( point );
382 else if ( tag == "#_#binormal" )
388 mBiTangents.PushBack( point );
390 else if ( tag == "vt" )
394 texture.y = 1.0-texture.y;
395 mTextureUv.PushBack( texture );
397 else if ( tag == "#_#vt1" )
402 texture.y = 1.0-texture.y;
403 mTextureUv2.PushBack( texture );
405 else if ( tag == "s" )
408 else if ( tag == "f" )
418 while( ( numIndices < MAX_POINT_INDICES ) && ( isline >> vet[numIndices] ) )
423 //Hold slashes that separate attributes of the same point.
427 const char * subString; //A pointer to the position in the string as we move through it.
429 subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
433 if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
435 for( int i = 0 ; i < numIndices; i++)
437 std::istringstream isindex( vet[i] );
438 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
442 else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
444 for( int i = 0 ; i < numIndices; i++ )
446 std::istringstream isindex( vet[i] );
447 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
452 else // Of the form A/B, so has points and textures but no normals.
454 for( int i = 0 ; i < numIndices; i++ )
456 std::istringstream isindex( vet[i] );
457 isindex >> ptIdx[i] >> separator >> texIdx[i];
464 else // Simply of the form A, as in, point indices only.
466 for( int i = 0 ; i < numIndices; i++ )
468 std::istringstream isindex( vet[i] );
475 //If it is a triangle
476 if( numIndices == 3 )
478 for( int i = 0 ; i < 3; i++ )
480 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
481 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
482 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
484 mTriangles.PushBack( triangle );
487 //If on the other hand it is a quad, we will create two triangles
488 else if( numIndices == 4 )
490 for( int i = 0 ; i < 3; i++ )
492 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
493 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
494 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
496 mTriangles.PushBack( triangle );
499 for( int i = 0 ; i < 3; i++ )
501 int idx = ( i + 2 ) % numIndices;
502 triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
503 triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
504 triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
506 mTriangles.PushBack( triangle2 );
510 else if ( tag == "usemtl" )
512 isline >> strMatActual;
514 else if ( tag == "mtllib" )
516 isline >> strMatActual;
518 else if ( tag == "g" )
526 CenterAndScale( true, mPoints );
528 mHasTextureUv = hasTexture;
535 void ObjLoader::LoadMaterial( char* objBuffer,
536 std::streampos fileSize,
537 std::string& diffuseTextureUrl,
538 std::string& normalTextureUrl,
539 std::string& glossTextureUrl )
545 std::string input = objBuffer;
546 std::istringstream ss(input);
547 ss.imbue(std::locale("C"));
550 std::getline( ss, line );
552 while ( std::getline( ss, line ) )
554 std::istringstream isline( line, std::istringstream::in );
559 if ( tag == "newmtl" ) //name of the material
563 else if ( tag == "Ka" ) //ambient color
565 isline >> fR >> fG >> fB;
567 else if ( tag == "Kd" ) //diffuse color
569 isline >> fR >> fG >> fB;
571 else if ( tag == "Ks" ) //specular color
573 isline >> fR >> fG >> fB;
575 else if ( tag == "Tf" ) //color
578 else if ( tag == "Ni" )
581 else if ( tag == "map_Kd" )
584 diffuseTextureUrl = info;
585 mHasDiffuseMap = true;
587 else if ( tag == "bump" )
590 normalTextureUrl = info;
591 mHasNormalMap = true;
593 else if ( tag == "map_Ks" )
596 glossTextureUrl = info;
597 mHasSpecularMap = true;
601 mMaterialLoaded = true;
604 Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
606 Geometry surface = Geometry::New();
608 Dali::Vector<Vector3> positions;
609 Dali::Vector<Vector3> normals;
610 Dali::Vector<Vector3> tangents;
611 Dali::Vector<Vector2> textures;
612 Dali::Vector<unsigned short> indices;
614 CreateGeometryArray( positions, normals, tangents, textures, indices, useSoftNormals );
616 //All vertices need at least Position and Normal
618 Property::Map positionMap;
619 positionMap["aPosition"] = Property::VECTOR3;
620 PropertyBuffer positionBuffer = PropertyBuffer::New( positionMap );
621 positionBuffer.SetData( positions.Begin(), positions.Count() );
623 Property::Map normalMap;
624 normalMap["aNormal"] = Property::VECTOR3;
625 PropertyBuffer normalBuffer = PropertyBuffer::New( normalMap );
626 normalBuffer.SetData( normals.Begin(), normals.Count() );
628 surface.AddVertexBuffer( positionBuffer );
629 surface.AddVertexBuffer( normalBuffer );
632 if( ( objectProperties & TANGENTS ) && mHasTextureUv )
634 Property::Map tangentMap;
635 tangentMap["aTangent"] = Property::VECTOR3;
636 PropertyBuffer tangentBuffer = PropertyBuffer::New( tangentMap );
637 tangentBuffer.SetData( tangents.Begin(), tangents.Count() );
639 surface.AddVertexBuffer( tangentBuffer );
642 //Some need texture coordinates
643 if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTextureUv )
645 Property::Map textCoordMap;
646 textCoordMap["aTexCoord"] = Property::VECTOR2;
647 PropertyBuffer texCoordBuffer = PropertyBuffer::New( textCoordMap );
648 texCoordBuffer.SetData( textures.Begin(), textures.Count() );
650 surface.AddVertexBuffer( texCoordBuffer );
653 //If indices are required, we set them.
654 if ( indices.Size() )
656 surface.SetIndexBuffer ( &indices[0], indices.Size() );
662 Vector3 ObjLoader::GetCenter()
664 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
668 Vector3 ObjLoader::GetSize()
670 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
674 void ObjLoader::ClearArrays()
685 mSceneLoaded = false;
688 bool ObjLoader::IsTexturePresent()
690 return mHasTextureUv;
693 bool ObjLoader::IsDiffuseMapPresent()
695 return mHasDiffuseMap;
698 bool ObjLoader::IsNormalMapPresent()
700 return mHasNormalMap;
703 bool ObjLoader::IsSpecularMapPresent()
705 return mHasSpecularMap;
708 } // namespace PbrDemo