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