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