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