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