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