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