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