Updated demos to use DALi clang-format
[platform/core/uifw/dali-demo.git] / examples / rendering-basic-pbr / obj-loader.cpp
1 /*
2  * Copyright (c) 2020 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.h>
24 #include <sstream>
25
26 namespace PbrDemo
27 {
28 namespace
29 {
30 const int MAX_POINT_INDICES = 4;
31 }
32
33 ObjLoader::ObjLoader()
34 : mSceneLoaded(false),
35   mMaterialLoaded(false),
36   mHasTextureUv(false),
37   mHasDiffuseMap(false),
38   mHasNormalMap(false),
39   mHasSpecularMap(false)
40 {
41   mSceneAABB.Init();
42 }
43
44 ObjLoader::~ObjLoader()
45 {
46   ClearArrays();
47 }
48
49 bool ObjLoader::IsSceneLoaded()
50 {
51   return mSceneLoaded;
52 }
53
54 bool ObjLoader::IsMaterialLoaded()
55 {
56   return mMaterialLoaded;
57 }
58
59 void ObjLoader::CalculateHardFaceNormals(const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
60 {
61   int numFaceVertices = 3 * triangles.Size(); //Vertices per face, as each vertex has different normals instance for each face.
62   int normalIndex     = 0;                    //Tracks progress through the array of normals.
63
64   normals.Clear();
65   normals.Resize(numFaceVertices);
66
67   //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
68   for(unsigned long i = 0; i < triangles.Size(); i++)
69   {
70     //Triangle vertices.
71     const Vector3& v0 = points[triangles[i].pointIndex[0]];
72     const Vector3& v1 = points[triangles[i].pointIndex[1]];
73     const Vector3& v2 = points[triangles[i].pointIndex[2]];
74
75     //Triangle edges.
76     Vector3 edge1 = v1 - v0;
77     Vector3 edge2 = v2 - v0;
78
79     //Using edges as vectors on the plane, cross product to get the normal.
80     Vector3 normalVector = edge1.Cross(edge2);
81     normalVector.Normalize();
82
83     //Assign normal index to triangle vertex and set the normal vector to the list of normals.
84     for(unsigned long j = 0; j < 3; j++, normalIndex++)
85     {
86       triangles[i].normalIndex[j] = normalIndex;
87       normals[normalIndex]        = normalVector;
88     }
89   }
90 }
91
92 void ObjLoader::CalculateSoftFaceNormals(const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
93 {
94   int normalIndex = 0; //Tracks progress through the array of normals.
95
96   normals.Clear();
97   normals.Resize(points.Size()); //One (averaged) normal per point.
98
99   //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
100   //We then add the triangle's normal to the cumulative normals at each point of it
101   for(unsigned long i = 0; i < triangles.Size(); i++)
102   {
103     //Triangle points.
104     const Vector3& v0 = points[triangles[i].pointIndex[0]];
105     const Vector3& v1 = points[triangles[i].pointIndex[1]];
106     const Vector3& v2 = points[triangles[i].pointIndex[2]];
107
108     //Triangle edges.
109     Vector3 edge1 = v1 - v0;
110     Vector3 edge2 = v2 - v0;
111
112     //Using edges as vectors on the plane, cross to get the normal.
113     Vector3 normalVector = edge1.Cross(edge2);
114
115     //Add this triangle's normal to the cumulative normal of each constituent triangle point and set the index of the normal accordingly.
116     for(unsigned long j = 0; j < 3; j++)
117     {
118       normalIndex                 = triangles[i].pointIndex[j];
119       triangles[i].normalIndex[j] = normalIndex; //Normal index matches up to vertex index, as one normal per vertex.
120       normals[normalIndex] += normalVector;
121     }
122   }
123
124   //Normalise the normals.
125   for(unsigned long i = 0; i < normals.Size(); i++)
126   {
127     normals[i].Normalize();
128   }
129 }
130
131 void ObjLoader::CalculateTangentFrame()
132 {
133   //Reset tangent vector to hold new values.
134   mTangents.Clear();
135   mTangents.Resize(mNormals.Size());
136
137   //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each normal.
138   for(unsigned long a = 0; a < mTriangles.Size(); a++)
139   {
140     Vector3 tangentVector;
141
142     const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
143     const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
144     const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
145
146     Vector3 edge1 = v1 - v0;
147     Vector3 edge2 = v2 - v0;
148
149     const Vector2& w0 = mTextureUv[mTriangles[a].textureIndex[0]];
150     const Vector2& w1 = mTextureUv[mTriangles[a].textureIndex[1]];
151     const Vector2& w2 = mTextureUv[mTriangles[a].textureIndex[2]];
152
153     float deltaU1 = w1.x - w0.x;
154     float deltaV1 = w1.y - w0.y;
155     float deltaU2 = w2.x - w0.x;
156     float deltaV2 = w2.y - w0.y;
157
158     // 1.0/f could cause division by zero in some cases, this factor will act
159     // as a weight of the tangent vector and it is fixed when it is normalised.
160     float f = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
161
162     tangentVector.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x);
163     tangentVector.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y);
164     tangentVector.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z);
165
166     mTangents[mTriangles[a].normalIndex[0]] += tangentVector;
167     mTangents[mTriangles[a].normalIndex[1]] += tangentVector;
168     mTangents[mTriangles[a].normalIndex[2]] += tangentVector;
169   }
170
171   //Orthogonalize tangents.
172   for(unsigned long a = 0; a < mTangents.Size(); a++)
173   {
174     const Vector3& n = mNormals[a];
175     const Vector3& t = mTangents[a];
176
177     // Gram-Schmidt orthogonalize
178     mTangents[a] = t - n * n.Dot(t);
179     mTangents[a].Normalize();
180   }
181 }
182
183 void ObjLoader::CenterAndScale(bool center, Dali::Vector<Vector3>& points)
184 {
185   BoundingVolume newAABB;
186
187   Vector3 sceneSize = GetSize();
188
189   float biggestDimension = sceneSize.x;
190   if(sceneSize.y > biggestDimension)
191   {
192     biggestDimension = sceneSize.y;
193   }
194   if(sceneSize.z > biggestDimension)
195   {
196     biggestDimension = sceneSize.z;
197   }
198
199   newAABB.Init();
200   for(unsigned int ui = 0; ui < points.Size(); ++ui)
201   {
202     points[ui] = points[ui] - GetCenter();
203     points[ui] = points[ui] / biggestDimension;
204     newAABB.ConsiderNewPointInVolume(points[ui]);
205   }
206
207   mSceneAABB = newAABB;
208 }
209
210 void ObjLoader::CreateGeometryArray(Dali::Vector<Vector3>&        positions,
211                                     Dali::Vector<Vector3>&        normals,
212                                     Dali::Vector<Vector3>&        tangents,
213                                     Dali::Vector<Vector2>&        textures,
214                                     Dali::Vector<unsigned short>& indices,
215                                     bool                          useSoftNormals)
216 {
217   //We must calculate the tangents if they weren't supplied, or if they don't match up.
218   bool mustCalculateTangents = (mTangents.Size() == 0) || (mTangents.Size() != mNormals.Size());
219
220   //However, we don't need to do this if the object doesn't use textures to begin with.
221   mustCalculateTangents &= mHasTextureUv;
222
223   // We calculate the normals if hard normals(flat normals) is set.
224   // Use the normals provided by the file to make the tangent calculation per normal,
225   // the correct results depends of normal generated by file, otherwise we need to recalculate
226   // the normal programmatically.
227   if(((mNormals.Size() == 0) && mustCalculateTangents) || !useSoftNormals)
228   {
229     if(useSoftNormals)
230     {
231       CalculateSoftFaceNormals(mPoints, mTriangles, mNormals);
232     }
233     else
234     {
235       CalculateHardFaceNormals(mPoints, mTriangles, mNormals);
236     }
237   }
238
239   if(mHasTextureUv && mustCalculateTangents)
240   {
241     CalculateTangentFrame();
242   }
243
244   bool mapsCorrespond; //True if the sizes of the arrays necessary to draw the object match.
245
246   if(mHasTextureUv)
247   {
248     mapsCorrespond = (mPoints.Size() == mTextureUv.Size()) && (mTextureUv.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(mHasTextureUv)
282         {
283           textures[mTriangles[ui].pointIndex[j]] = mTextureUv[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(mHasTextureUv)
308         {
309           textures[index] = mTextureUv[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, fileSize);
339   std::istringstream ss(input);
340   ss.imbue(std::locale("C"));
341
342   std::string line;
343   std::getline(ss, line);
344
345   while(std::getline(ss, line))
346   {
347     std::istringstream isline(line, std::istringstream::in);
348     std::string        tag;
349
350     isline >> tag;
351
352     if(tag == "v")
353     {
354       //Two different objects in the same file
355       isline >> point.x;
356       isline >> point.y;
357       isline >> point.z;
358       mPoints.PushBack(point);
359
360       mSceneAABB.ConsiderNewPointInVolume(point);
361     }
362     else if(tag == "vn")
363     {
364       isline >> point.x;
365       isline >> point.y;
366       isline >> point.z;
367
368       mNormals.PushBack(point);
369     }
370     else if(tag == "#_#tangent")
371     {
372       isline >> point.x;
373       isline >> point.y;
374       isline >> point.z;
375
376       mTangents.PushBack(point);
377     }
378     else if(tag == "#_#binormal")
379     {
380       isline >> point.x;
381       isline >> point.y;
382       isline >> point.z;
383
384       mBiTangents.PushBack(point);
385     }
386     else if(tag == "vt")
387     {
388       isline >> texture.x;
389       isline >> texture.y;
390       texture.y = 1.0 - texture.y;
391       mTextureUv.PushBack(texture);
392     }
393     else if(tag == "#_#vt1")
394     {
395       isline >> texture.x;
396       isline >> texture.y;
397
398       texture.y = 1.0 - texture.y;
399       mTextureUv2.PushBack(texture);
400     }
401     else if(tag == "s")
402     {
403     }
404     else if(tag == "f")
405     {
406       if(!iniObj)
407       {
408         //name assign
409
410         iniObj = true;
411       }
412
413       int numIndices = 0;
414       while((numIndices < MAX_POINT_INDICES) && (isline >> vet[numIndices]))
415       {
416         numIndices++;
417       }
418
419       //Hold slashes that separate attributes of the same point.
420       char separator;
421       char separator2;
422
423       const char* subString; //A pointer to the position in the string as we move through it.
424
425       subString = strstr(vet[0].c_str(), "/"); //Search for the first '/'
426
427       if(subString)
428       {
429         if(subString[1] == '/') // Of the form A//C, so has points and normals but no texture coordinates.
430         {
431           for(int i = 0; i < numIndices; i++)
432           {
433             std::istringstream isindex(vet[i]);
434             isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
435             texIdx[i] = 0;
436           }
437         }
438         else if(strstr(subString, "/")) // Of the form A/B/C, so has points, textures and normals.
439         {
440           for(int i = 0; i < numIndices; i++)
441           {
442             std::istringstream isindex(vet[i]);
443             isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
444           }
445
446           hasTexture = true;
447         }
448         else // Of the form A/B, so has points and textures but no normals.
449         {
450           for(int i = 0; i < numIndices; i++)
451           {
452             std::istringstream isindex(vet[i]);
453             isindex >> ptIdx[i] >> separator >> texIdx[i];
454             nrmIdx[i] = 0;
455           }
456
457           hasTexture = true;
458         }
459       }
460       else // Simply of the form A, as in, point indices only.
461       {
462         for(int i = 0; i < numIndices; i++)
463         {
464           std::istringstream isindex(vet[i]);
465           isindex >> ptIdx[i];
466           texIdx[i] = 0;
467           nrmIdx[i] = 0;
468         }
469       }
470
471       //If it is a triangle
472       if(numIndices == 3)
473       {
474         for(int i = 0; i < 3; i++)
475         {
476           triangle.pointIndex[i]   = ptIdx[i] - 1 - pntAcum;
477           triangle.normalIndex[i]  = nrmIdx[i] - 1 - nrmAcum;
478           triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
479         }
480         mTriangles.PushBack(triangle);
481         face++;
482       }
483       //If on the other hand it is a quad, we will create two triangles
484       else if(numIndices == 4)
485       {
486         for(int i = 0; i < 3; i++)
487         {
488           triangle.pointIndex[i]   = ptIdx[i] - 1 - pntAcum;
489           triangle.normalIndex[i]  = nrmIdx[i] - 1 - nrmAcum;
490           triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
491         }
492         mTriangles.PushBack(triangle);
493         face++;
494
495         for(int i = 0; i < 3; i++)
496         {
497           int idx                   = (i + 2) % numIndices;
498           triangle2.pointIndex[i]   = ptIdx[idx] - 1 - pntAcum;
499           triangle2.normalIndex[i]  = nrmIdx[idx] - 1 - nrmAcum;
500           triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
501         }
502         mTriangles.PushBack(triangle2);
503         face++;
504       }
505     }
506     else if(tag == "usemtl")
507     {
508       isline >> strMatActual;
509     }
510     else if(tag == "mtllib")
511     {
512       isline >> strMatActual;
513     }
514     else if(tag == "g")
515     {
516       isline >> name;
517     }
518   }
519
520   if(iniObj)
521   {
522     CenterAndScale(true, mPoints);
523     mSceneLoaded  = true;
524     mHasTextureUv = hasTexture;
525     return true;
526   }
527
528   return false;
529 }
530
531 void ObjLoader::LoadMaterial(char*          objBuffer,
532                              std::streampos fileSize,
533                              std::string&   diffuseTextureUrl,
534                              std::string&   normalTextureUrl,
535                              std::string&   glossTextureUrl)
536 {
537   float fR, fG, fB;
538
539   std::string info;
540
541   std::string        input = objBuffer;
542   std::istringstream ss(input);
543   ss.imbue(std::locale("C"));
544
545   std::string line;
546   std::getline(ss, line);
547
548   while(std::getline(ss, line))
549   {
550     std::istringstream isline(line, std::istringstream::in);
551     std::string        tag;
552
553     isline >> tag;
554
555     if(tag == "newmtl") //name of the material
556     {
557       isline >> info;
558     }
559     else if(tag == "Ka") //ambient color
560     {
561       isline >> fR >> fG >> fB;
562     }
563     else if(tag == "Kd") //diffuse color
564     {
565       isline >> fR >> fG >> fB;
566     }
567     else if(tag == "Ks") //specular color
568     {
569       isline >> fR >> fG >> fB;
570     }
571     else if(tag == "Tf") //color
572     {
573     }
574     else if(tag == "Ni")
575     {
576     }
577     else if(tag == "map_Kd")
578     {
579       isline >> info;
580       diffuseTextureUrl = info;
581       mHasDiffuseMap    = true;
582     }
583     else if(tag == "bump")
584     {
585       isline >> info;
586       normalTextureUrl = info;
587       mHasNormalMap    = true;
588     }
589     else if(tag == "map_Ks")
590     {
591       isline >> info;
592       glossTextureUrl = info;
593       mHasSpecularMap = true;
594     }
595   }
596
597   mMaterialLoaded = true;
598 }
599
600 Geometry ObjLoader::CreateGeometry(int objectProperties, bool useSoftNormals)
601 {
602   Geometry surface = Geometry::New();
603
604   Dali::Vector<Vector3>        positions;
605   Dali::Vector<Vector3>        normals;
606   Dali::Vector<Vector3>        tangents;
607   Dali::Vector<Vector2>        textures;
608   Dali::Vector<unsigned short> indices;
609
610   CreateGeometryArray(positions, normals, tangents, textures, indices, useSoftNormals);
611
612   //All vertices need at least Position and Normal
613
614   Property::Map positionMap;
615   positionMap["aPosition"]    = Property::VECTOR3;
616   VertexBuffer positionBuffer = VertexBuffer::New(positionMap);
617   positionBuffer.SetData(positions.Begin(), positions.Count());
618
619   Property::Map normalMap;
620   normalMap["aNormal"]      = Property::VECTOR3;
621   VertexBuffer normalBuffer = VertexBuffer::New(normalMap);
622   normalBuffer.SetData(normals.Begin(), normals.Count());
623
624   surface.AddVertexBuffer(positionBuffer);
625   surface.AddVertexBuffer(normalBuffer);
626
627   //Some need tangent
628   if((objectProperties & TANGENTS) && mHasTextureUv)
629   {
630     Property::Map tangentMap;
631     tangentMap["aTangent"]     = Property::VECTOR3;
632     VertexBuffer tangentBuffer = VertexBuffer::New(tangentMap);
633     tangentBuffer.SetData(tangents.Begin(), tangents.Count());
634
635     surface.AddVertexBuffer(tangentBuffer);
636   }
637
638   //Some need texture coordinates
639   if((objectProperties & TEXTURE_COORDINATES) && mHasTextureUv)
640   {
641     Property::Map textCoordMap;
642     textCoordMap["aTexCoord"]   = Property::VECTOR2;
643     VertexBuffer texCoordBuffer = VertexBuffer::New(textCoordMap);
644     texCoordBuffer.SetData(textures.Begin(), textures.Count());
645
646     surface.AddVertexBuffer(texCoordBuffer);
647   }
648
649   //If indices are required, we set them.
650   if(indices.Size())
651   {
652     surface.SetIndexBuffer(&indices[0], indices.Size());
653   }
654
655   return surface;
656 }
657
658 Vector3 ObjLoader::GetCenter()
659 {
660   Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
661   return center;
662 }
663
664 Vector3 ObjLoader::GetSize()
665 {
666   Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
667   return size;
668 }
669
670 void ObjLoader::ClearArrays()
671 {
672   mPoints.Clear();
673   mTextureUv.Clear();
674   mTextureUv2.Clear();
675   mNormals.Clear();
676   mTangents.Clear();
677   mBiTangents.Clear();
678
679   mTriangles.Clear();
680
681   mSceneLoaded = false;
682 }
683
684 bool ObjLoader::IsTexturePresent()
685 {
686   return mHasTextureUv;
687 }
688
689 bool ObjLoader::IsDiffuseMapPresent()
690 {
691   return mHasDiffuseMap;
692 }
693
694 bool ObjLoader::IsNormalMapPresent()
695 {
696   return mHasNormalMap;
697 }
698
699 bool ObjLoader::IsSpecularMapPresent()
700 {
701   return mHasSpecularMap;
702 }
703
704 } // namespace PbrDemo