82ffa6a9288387a7ca1c395d68a2f9a71464fe56
[platform/core/uifw/dali-demo.git] / examples / rendering-basic-pbr / obj-loader.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "obj-loader.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <sstream>
24 #include <string.h>
25
26 namespace PbrDemo
27 {
28
29 namespace
30 {
31 const int MAX_POINT_INDICES = 4;
32 }
33
34 ObjLoader::ObjLoader()
35 : mSceneLoaded( false ),
36   mMaterialLoaded( false ),
37   mHasTextureUv( false ),
38   mHasDiffuseMap( false ),
39   mHasNormalMap( false ),
40   mHasSpecularMap( false )
41 {
42   mSceneAABB.Init();
43 }
44
45 ObjLoader::~ObjLoader()
46 {
47   ClearArrays();
48 }
49
50 bool ObjLoader::IsSceneLoaded()
51 {
52   return mSceneLoaded;
53 }
54
55 bool ObjLoader::IsMaterialLoaded()
56 {
57   return mMaterialLoaded;
58 }
59
60 void ObjLoader::CalculateHardFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
61                                           Dali::Vector<Vector3>& normals )
62 {
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.
65
66   normals.Clear();
67   normals.Resize( numFaceVertices );
68
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++ )
71   {
72     //Triangle vertices.
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]];
76
77     //Triangle edges.
78     Vector3 edge1 = v1 - v0;
79     Vector3 edge2 = v2 - v0;
80
81     //Using edges as vectors on the plane, cross product to get the normal.
82     Vector3 normalVector = edge1.Cross(edge2);
83     normalVector.Normalize();
84
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++ )
87     {
88       triangles[i].normalIndex[j] = normalIndex;
89       normals[normalIndex] = normalVector;
90     }
91   }
92 }
93
94 void ObjLoader::CalculateSoftFaceNormals( const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles,
95                                           Dali::Vector<Vector3>& normals )
96 {
97   int normalIndex = 0;  //Tracks progress through the array of normals.
98
99   normals.Clear();
100   normals.Resize( points.Size() );  //One (averaged) normal per point.
101
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++ )
105   {
106     //Triangle points.
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]];
110
111     //Triangle edges.
112     Vector3 edge1 = v1 - v0;
113     Vector3 edge2 = v2 - v0;
114
115     //Using edges as vectors on the plane, cross to get the normal.
116     Vector3 normalVector = edge1.Cross(edge2);
117
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++)
120     {
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;
124     }
125   }
126
127   //Normalise the normals.
128   for( unsigned long i = 0; i < normals.Size(); i++ )
129   {
130     normals[i].Normalize();
131   }
132 }
133
134 void ObjLoader::CalculateTangentFrame()
135 {
136   //Reset tangent vector to hold new values.
137   mTangents.Clear();
138   mTangents.Resize( mNormals.Size() );
139
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++ )
142   {
143     Vector3 tangentVector;
144
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]];
148
149     Vector3 edge1 = v1 - v0;
150     Vector3 edge2 = v2 - v0;
151
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]];
155
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;
160
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);
164
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 );
168
169     mTangents[mTriangles[a].normalIndex[0]] += tangentVector;
170     mTangents[mTriangles[a].normalIndex[1]] += tangentVector;
171     mTangents[mTriangles[a].normalIndex[2]] += tangentVector;
172   }
173
174   //Orthogonalize tangents.
175   for ( unsigned long a = 0; a < mTangents.Size(); a++ )
176   {
177     const Vector3& n = mNormals[a];
178     const Vector3& t = mTangents[a];
179
180     // Gram-Schmidt orthogonalize
181     mTangents[a] = t - n * n.Dot(t);
182     mTangents[a].Normalize();
183   }
184 }
185
186 void ObjLoader::CenterAndScale( bool center, Dali::Vector<Vector3>& points )
187 {
188   BoundingVolume newAABB;
189
190   Vector3 sceneSize = GetSize();
191
192   float biggestDimension = sceneSize.x;
193   if( sceneSize.y > biggestDimension )
194   {
195     biggestDimension = sceneSize.y;
196   }
197   if( sceneSize.z > biggestDimension )
198   {
199     biggestDimension = sceneSize.z;
200   }
201
202   newAABB.Init();
203   for( unsigned int ui = 0; ui < points.Size(); ++ui )
204   {
205     points[ui] = points[ui] - GetCenter();
206     points[ui] = points[ui] / biggestDimension;
207     newAABB.ConsiderNewPointInVolume(points[ui]);
208   }
209
210   mSceneAABB = newAABB;
211 }
212
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 )
219 {
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() );
222
223   //However, we don't need to do this if the object doesn't use textures to begin with.
224   mustCalculateTangents &= mHasTextureUv;
225
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 )
231   {
232     if( useSoftNormals )
233     {
234       CalculateSoftFaceNormals( mPoints, mTriangles, mNormals );
235     }
236     else
237     {
238       CalculateHardFaceNormals( mPoints, mTriangles, mNormals );
239     }
240   }
241
242   if( mHasTextureUv && mustCalculateTangents )
243   {
244     CalculateTangentFrame();
245   }
246
247   bool mapsCorrespond; //True if the sizes of the arrays necessary to draw the object match.
248
249   if ( mHasTextureUv )
250   {
251     mapsCorrespond = ( mPoints.Size() == mTextureUv.Size() ) && ( mTextureUv.Size() == mNormals.Size() );
252   }
253   else
254   {
255     mapsCorrespond = ( mPoints.Size() == mNormals.Size() );
256   }
257
258   //Check the number of points textures and normals
259   if ( mapsCorrespond )
260   {
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 );
268
269     //We create the vertices array. For now we just copy points info
270     positions = mPoints;
271
272     int indiceIndex = 0;
273
274     //We copy the indices
275     for ( unsigned int ui = 0 ; ui < mTriangles.Size() ; ++ui )
276     {
277       for ( int j = 0 ; j < 3 ; ++j )
278       {
279         indices[indiceIndex] = mTriangles[ui].pointIndex[j];
280         indiceIndex++;
281
282         normals[mTriangles[ui].pointIndex[j]] = mNormals[mTriangles[ui].normalIndex[j]];
283
284         if ( mHasTextureUv )
285         {
286           textures[mTriangles[ui].pointIndex[j]] = mTextureUv[mTriangles[ui].textureIndex[j]];
287           tangents[mTriangles[ui].pointIndex[j]] = mTangents[mTriangles[ui].normalIndex[j]];
288         }
289       }
290     }
291   }
292   else
293   {
294     int numVertices = 3 * mTriangles.Size();
295     positions.Resize( numVertices );
296     normals.Resize( numVertices );
297     textures.Resize( numVertices );
298     tangents.Resize( numVertices );
299
300     int index = 0;
301
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 )
304     {
305       for ( int j = 0 ; j < 3 ; ++j )
306       {
307         positions[index] = mPoints[mTriangles[ui].pointIndex[j]];
308         normals[index] = mNormals[mTriangles[ui].normalIndex[j]];
309
310         if( mHasTextureUv )
311         {
312           textures[index] = mTextureUv[mTriangles[ui].textureIndex[j]];
313           tangents[index] = mTangents[mTriangles[ui].normalIndex[j]];
314         }
315
316         ++index;
317       }
318     }
319   }
320 }
321
322 bool ObjLoader::LoadObject( char* objBuffer, std::streampos fileSize )
323 {
324   Vector3 point;
325   Vector2 texture;
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;
332   bool iniObj = false;
333   bool hasTexture = false;
334   int face = 0;
335
336   //Init AABB for the file
337   mSceneAABB.Init();
338
339   std::string strMatActual;
340
341   std::string input( objBuffer, fileSize );
342   std::istringstream ss(input);
343   ss.imbue( std::locale( "C" ) );
344
345
346   std::string line;
347   std::getline( ss, line );
348
349   while ( std::getline( ss, line ) )
350   {
351     std::istringstream isline( line, std::istringstream::in );
352     std::string tag;
353
354     isline >> tag;
355
356     if ( tag == "v" )
357     {
358       //Two different objects in the same file
359       isline >> point.x;
360       isline >> point.y;
361       isline >> point.z;
362       mPoints.PushBack( point );
363
364       mSceneAABB.ConsiderNewPointInVolume( point );
365     }
366     else if ( tag == "vn" )
367     {
368       isline >> point.x;
369       isline >> point.y;
370       isline >> point.z;
371
372       mNormals.PushBack( point );
373     }
374     else if ( tag == "#_#tangent" )
375     {
376       isline >> point.x;
377       isline >> point.y;
378       isline >> point.z;
379
380       mTangents.PushBack( point );
381     }
382     else if ( tag == "#_#binormal" )
383     {
384       isline >> point.x;
385       isline >> point.y;
386       isline >> point.z;
387
388       mBiTangents.PushBack( point );
389     }
390     else if ( tag == "vt" )
391     {
392       isline >> texture.x;
393       isline >> texture.y;
394       texture.y = 1.0-texture.y;
395       mTextureUv.PushBack( texture );
396     }
397     else if ( tag == "#_#vt1" )
398     {
399       isline >> texture.x;
400       isline >> texture.y;
401
402       texture.y = 1.0-texture.y;
403       mTextureUv2.PushBack( texture );
404     }
405     else if ( tag == "s" )
406     {
407     }
408     else if ( tag == "f" )
409     {
410       if ( !iniObj )
411       {
412         //name assign
413
414         iniObj = true;
415       }
416
417       int numIndices = 0;
418       while( ( numIndices < MAX_POINT_INDICES ) && ( isline >> vet[numIndices] ) )
419       {
420         numIndices++;
421       }
422
423       //Hold slashes that separate attributes of the same point.
424       char separator;
425       char separator2;
426
427       const char * subString; //A pointer to the position in the string as we move through it.
428
429       subString = strstr( vet[0].c_str(),"/" ); //Search for the first '/'
430
431       if( subString )
432       {
433         if( subString[1] == '/' ) // Of the form A//C, so has points and normals but no texture coordinates.
434         {
435           for( int i = 0 ; i < numIndices; i++)
436           {
437             std::istringstream isindex( vet[i] );
438             isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
439             texIdx[i] = 0;
440           }
441         }
442         else if( strstr( subString, "/" ) ) // Of the form A/B/C, so has points, textures and normals.
443         {
444           for( int i = 0 ; i < numIndices; i++ )
445           {
446             std::istringstream isindex( vet[i] );
447             isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
448           }
449
450           hasTexture = true;
451         }
452         else // Of the form A/B, so has points and textures but no normals.
453         {
454           for( int i = 0 ; i < numIndices; i++ )
455           {
456             std::istringstream isindex( vet[i] );
457             isindex >> ptIdx[i] >> separator >> texIdx[i];
458             nrmIdx[i] = 0;
459           }
460
461           hasTexture = true;
462         }
463       }
464       else // Simply of the form A, as in, point indices only.
465       {
466         for( int i = 0 ; i < numIndices; i++ )
467         {
468           std::istringstream isindex( vet[i] );
469           isindex >> ptIdx[i];
470           texIdx[i] = 0;
471           nrmIdx[i] = 0;
472         }
473       }
474
475       //If it is a triangle
476       if( numIndices == 3 )
477       {
478         for( int i = 0 ; i < 3; i++ )
479         {
480           triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
481           triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
482           triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
483         }
484         mTriangles.PushBack( triangle );
485         face++;
486       }
487       //If on the other hand it is a quad, we will create two triangles
488       else if( numIndices == 4 )
489       {
490         for( int i = 0 ; i < 3; i++ )
491         {
492           triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
493           triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
494           triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
495         }
496         mTriangles.PushBack( triangle );
497         face++;
498
499         for( int i = 0 ; i < 3; i++ )
500         {
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;
505         }
506         mTriangles.PushBack( triangle2 );
507         face++;
508       }
509     }
510     else if ( tag == "usemtl" )
511     {
512       isline >> strMatActual;
513     }
514     else if ( tag == "mtllib" )
515     {
516       isline >> strMatActual;
517     }
518     else if ( tag == "g" )
519     {
520       isline >> name;
521     }
522   }
523
524   if ( iniObj )
525   {
526     CenterAndScale( true, mPoints );
527     mSceneLoaded = true;
528     mHasTextureUv = hasTexture;
529     return true;
530   }
531
532   return false;
533 }
534
535 void ObjLoader::LoadMaterial( char* objBuffer,
536                               std::streampos fileSize,
537                               std::string& diffuseTextureUrl,
538                               std::string& normalTextureUrl,
539                               std::string& glossTextureUrl )
540 {
541   float fR,fG,fB;
542
543   std::string info;
544
545   std::string input = objBuffer;
546   std::istringstream ss(input);
547   ss.imbue(std::locale("C"));
548
549   std::string line;
550   std::getline( ss, line );
551
552   while ( std::getline( ss, line ) )
553   {
554     std::istringstream isline( line, std::istringstream::in );
555     std::string tag;
556
557     isline >> tag;
558
559     if ( tag == "newmtl" )  //name of the material
560     {
561       isline >> info;
562     }
563     else if ( tag == "Ka" ) //ambient color
564     {
565       isline >> fR >> fG >> fB;
566     }
567     else if ( tag == "Kd" ) //diffuse color
568     {
569       isline >> fR >> fG >> fB;
570     }
571     else if ( tag == "Ks" ) //specular color
572     {
573       isline >> fR >> fG >> fB;
574     }
575     else if ( tag == "Tf" ) //color
576     {
577     }
578     else if ( tag == "Ni" )
579     {
580     }
581     else if ( tag == "map_Kd" )
582     {
583       isline >> info;
584       diffuseTextureUrl = info;
585       mHasDiffuseMap = true;
586     }
587     else if ( tag == "bump" )
588     {
589       isline >> info;
590       normalTextureUrl = info;
591       mHasNormalMap = true;
592     }
593     else if ( tag == "map_Ks" )
594     {
595       isline >> info;
596       glossTextureUrl = info;
597       mHasSpecularMap = true;
598     }
599   }
600
601   mMaterialLoaded = true;
602 }
603
604 Geometry ObjLoader::CreateGeometry( int objectProperties, bool useSoftNormals )
605 {
606   Geometry surface = Geometry::New();
607
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;
613
614   CreateGeometryArray( positions, normals, tangents, textures, indices, useSoftNormals );
615
616   //All vertices need at least Position and Normal
617
618   Property::Map positionMap;
619   positionMap["aPosition"] = Property::VECTOR3;
620   VertexBuffer positionBuffer = VertexBuffer::New( positionMap );
621   positionBuffer.SetData( positions.Begin(), positions.Count() );
622
623   Property::Map normalMap;
624   normalMap["aNormal"] = Property::VECTOR3;
625   VertexBuffer normalBuffer = VertexBuffer::New( normalMap );
626   normalBuffer.SetData( normals.Begin(), normals.Count() );
627
628   surface.AddVertexBuffer( positionBuffer );
629   surface.AddVertexBuffer( normalBuffer );
630
631   //Some need tangent
632   if( ( objectProperties & TANGENTS ) && mHasTextureUv )
633   {
634     Property::Map tangentMap;
635     tangentMap["aTangent"] = Property::VECTOR3;
636     VertexBuffer tangentBuffer = VertexBuffer::New( tangentMap );
637     tangentBuffer.SetData( tangents.Begin(), tangents.Count() );
638
639     surface.AddVertexBuffer( tangentBuffer );
640   }
641
642   //Some need texture coordinates
643   if( ( objectProperties & TEXTURE_COORDINATES ) && mHasTextureUv )
644   {
645     Property::Map textCoordMap;
646     textCoordMap["aTexCoord"] = Property::VECTOR2;
647     VertexBuffer texCoordBuffer = VertexBuffer::New( textCoordMap );
648     texCoordBuffer.SetData( textures.Begin(), textures.Count() );
649
650     surface.AddVertexBuffer( texCoordBuffer );
651   }
652
653   //If indices are required, we set them.
654   if ( indices.Size() )
655   {
656     surface.SetIndexBuffer ( &indices[0], indices.Size() );
657   }
658
659   return surface;
660 }
661
662 Vector3 ObjLoader::GetCenter()
663 {
664   Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
665   return center;
666 }
667
668 Vector3 ObjLoader::GetSize()
669 {
670   Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
671   return size;
672 }
673
674 void ObjLoader::ClearArrays()
675 {
676   mPoints.Clear();
677   mTextureUv.Clear();
678   mTextureUv2.Clear();
679   mNormals.Clear();
680   mTangents.Clear();
681   mBiTangents.Clear();
682
683   mTriangles.Clear();
684
685   mSceneLoaded = false;
686 }
687
688 bool ObjLoader::IsTexturePresent()
689 {
690   return mHasTextureUv;
691 }
692
693 bool ObjLoader::IsDiffuseMapPresent()
694 {
695   return mHasDiffuseMap;
696 }
697
698 bool ObjLoader::IsNormalMapPresent()
699 {
700   return mHasNormalMap;
701 }
702
703 bool ObjLoader::IsSpecularMapPresent()
704 {
705   return mHasSpecularMap;
706 }
707
708 } // namespace PbrDemo