2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "obj-loader.h"
22 #include <dali/integration-api/debug.h>
35 const int MAX_POINT_INDICES = 4;
39 ObjLoader::ObjLoader()
42 mMaterialLoaded = false;
43 mHasTexturePoints = false;
44 mHasDiffuseMap = false;
45 mHasNormalMap = false;
46 mHasSpecularMap = false;
50 ObjLoader::~ObjLoader()
55 bool ObjLoader::IsSceneLoaded()
60 bool ObjLoader::IsMaterialLoaded()
62 return mMaterialLoaded;
65 void ObjLoader::CalculateHardFaceNormals(const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
67 int numFaceVertices = 3 * triangles.Size(); //Vertex per face, as each point has different normals for each face.
68 int normalIndex = 0; //Tracks progress through the array of normals.
71 normals.Resize(numFaceVertices);
73 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
74 for(unsigned long i = 0; i < triangles.Size(); i++)
77 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
78 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
79 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
82 Vector3 edge1 = v1 - v0;
83 Vector3 edge2 = v2 - v0;
85 //Using edges as vectors on the plane, cross to get the normal.
86 Vector3 normalVector = edge1.Cross(edge2);
87 normalVector.Normalize();
89 //Assign normals to points.
90 for(unsigned long j = 0; j < 3; j++, normalIndex++)
92 triangles[i].normalIndex[j] = normalIndex;
93 normals[normalIndex] = normalVector;
98 void ObjLoader::CalculateSoftFaceNormals(const Dali::Vector<Vector3>& vertices, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
100 int normalIndex = 0; //Tracks progress through the array of normals.
103 normals.Resize(vertices.Size()); //One (averaged) normal per point.
105 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
106 //We then add the triangle's normal to the cumulative normals at each point of it
107 for(unsigned long i = 0; i < triangles.Size(); i++)
110 const Vector3& v0 = vertices[triangles[i].pointIndex[0]];
111 const Vector3& v1 = vertices[triangles[i].pointIndex[1]];
112 const Vector3& v2 = vertices[triangles[i].pointIndex[2]];
115 Vector3 edge1 = v1 - v0;
116 Vector3 edge2 = v2 - v0;
118 //Using edges as vectors on the plane, cross to get the normal.
119 Vector3 normalVector = edge1.Cross(edge2);
121 //Add this triangle's normal to the cumulative normal of each constituent point and set the index of the normal accordingly.
122 for(unsigned long j = 0; j < 3; j++, normalIndex++)
124 triangles[i].normalIndex[j] = triangles[i].pointIndex[j]; //Normal index matches up to vertex index, as one normal per vertex.
125 normals[triangles[i].normalIndex[j]] += normalVector;
129 //Normalise the normals.
130 for(unsigned long i = 0; i < normals.Size(); i++)
132 normals[i].Normalize();
136 //TODO: Use a function that can generate more than one normal/tangent per vertex (using angle)
137 void ObjLoader::CalculateTangentFrame()
139 //Reset tangent and bitangent vectors to hold new values.
142 mTangents.Resize(mPoints.Size());
143 mBiTangents.Resize(mPoints.Size());
145 //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each point.
146 for(unsigned long a = 0; a < mTriangles.Size(); a++)
148 Vector3 tangentVector;
150 const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
151 const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
152 const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
154 Vector3 edge1 = v1 - v0;
155 Vector3 edge2 = v2 - v0;
157 const Vector2& w0 = mTextures[mTriangles[a].textureIndex[0]];
158 const Vector2& w1 = mTextures[mTriangles[a].textureIndex[1]];
159 const Vector2& w2 = mTextures[mTriangles[a].textureIndex[2]];
161 float deltaU1 = w1.x - w0.x;
162 float deltaV1 = w1.y - w0.y;
163 float deltaU2 = w2.x - w0.x;
164 float deltaV2 = w2.y - w0.y;
166 float f = 1.0f / (deltaU1 * deltaV2 - deltaU2 * deltaV1);
168 tangentVector.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x);
169 tangentVector.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y);
170 tangentVector.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z);
172 mTangents[mTriangles[a].pointIndex[0]] += tangentVector;
173 mTangents[mTriangles[a].pointIndex[1]] += tangentVector;
174 mTangents[mTriangles[a].pointIndex[2]] += tangentVector;
177 //Orthogonalize tangents and set binormals.
178 for(unsigned long a = 0; a < mTangents.Size(); a++)
180 const Vector3& n = mNormals[a];
181 const Vector3& t = mTangents[a];
183 // Gram-Schmidt orthogonalize
184 mTangents[a] = t - n * n.Dot(t);
185 mTangents[a].Normalize();
187 mBiTangents[a] = mNormals[a].Cross(mTangents[a]);
191 void ObjLoader::CenterAndScale(bool center, Dali::Vector<Vector3>& points)
193 BoundingVolume newAABB;
195 Vector3 sceneSize = GetSize();
197 float biggestDimension = sceneSize.x;
198 if(sceneSize.y > biggestDimension)
200 biggestDimension = sceneSize.y;
202 if(sceneSize.z > biggestDimension)
204 biggestDimension = sceneSize.z;
208 for(unsigned int ui = 0; ui < points.Size(); ++ui)
210 points[ui] = points[ui] - GetCenter();
211 points[ui] = points[ui] / biggestDimension;
212 newAABB.ConsiderNewPointInVolume(points[ui]);
215 mSceneAABB = newAABB;
218 void ObjLoader::CreateGeometryArray(Dali::Vector<Vertex>& vertices,
219 Dali::Vector<Vector2>& textures,
220 Dali::Vector<VertexExt>& verticesExt,
221 Dali::Vector<unsigned short>& indices,
224 //We must calculate the tangents and bitangents if they weren't supplied, or if they don't match up.
225 bool mustCalculateTangents = mTangents.Size() == 0 || mBiTangents.Size() == 0 ||
226 mTangents.Size() != mBiTangents.Size() || mTangents.Size() != mNormals.Size() ||
227 mBiTangents.Size() != mNormals.Size();
229 //However, we don't need to do this if the object doesn't use textures to begin with.
230 mustCalculateTangents &= mHasTexturePoints;
232 //We also have to recalculate the normals if we need to calculate tangents,
233 // as we need just one normal, tangent and bitangent per vertex, rather than the supplied per-face vertices.
234 //Alternatively, we need to calculate the normals if there weren't any to begin with.
235 if(mNormals.Size() == 0 || mustCalculateTangents)
237 if(useSoftNormals || mustCalculateTangents)
239 CalculateSoftFaceNormals(mPoints, mTriangles, mNormals);
243 CalculateHardFaceNormals(mPoints, mTriangles, mNormals);
247 //TODO: Use a better function to calculate tangents
248 if(mHasTexturePoints && mustCalculateTangents)
250 CalculateTangentFrame();
253 bool mapsCorrespond; //True if the sizes of the arrays necessary for the object agree.
255 if(mHasTexturePoints)
257 mapsCorrespond = (mPoints.Size() == mTextures.Size()) && (mTextures.Size() == mNormals.Size());
261 mapsCorrespond = (mPoints.Size() == mNormals.Size());
264 //Check the number of points textures and normals
267 int numPoints = mPoints.Size();
268 int numIndices = 3 * mTriangles.Size();
269 vertices.Resize(numPoints);
270 textures.Resize(numPoints);
271 verticesExt.Resize(numPoints);
272 indices.Resize(numIndices);
274 //We create the vertices array. For now we just copy points info
275 for(unsigned int ui = 0; ui < mPoints.Size(); ++ui)
278 vertex.position = mPoints[ui];
279 vertices[ui] = vertex;
281 if(mHasTexturePoints)
283 textures[ui] = Vector2();
284 verticesExt[ui] = VertexExt();
290 //We copy the indices
291 for(unsigned int ui = 0; ui < mTriangles.Size(); ++ui)
293 for(int j = 0; j < 3; ++j)
295 indices[indiceIndex] = mTriangles[ui].pointIndex[j];
298 vertices[mTriangles[ui].pointIndex[j]].normal = mNormals[mTriangles[ui].normalIndex[j]];
300 if(mHasTexturePoints)
302 textures[mTriangles[ui].pointIndex[j]] = mTextures[mTriangles[ui].textureIndex[j]];
303 verticesExt[mTriangles[ui].pointIndex[j]].tangent = mTangents[mTriangles[ui].normalIndex[j]];
304 verticesExt[mTriangles[ui].pointIndex[j]].bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
311 int numVertices = 3 * mTriangles.Size();
312 vertices.Resize(numVertices);
313 textures.Resize(numVertices);
314 verticesExt.Resize(numVertices);
318 //We have to normalize the arrays so we can draw we just one index array
319 for(unsigned int ui = 0; ui < mTriangles.Size(); ++ui)
321 for(int j = 0; j < 3; ++j)
324 vertex.position = mPoints[mTriangles[ui].pointIndex[j]];
325 vertex.normal = mNormals[mTriangles[ui].normalIndex[j]];
326 vertices[index] = vertex;
328 if(mHasTexturePoints)
330 textures[index] = mTextures[mTriangles[ui].textureIndex[j]];
332 vertexExt.tangent = mTangents[mTriangles[ui].normalIndex[j]];
333 vertexExt.bitangent = mBiTangents[mTriangles[ui].normalIndex[j]];
334 verticesExt[index] = vertexExt;
343 bool ObjLoader::LoadObject(char* objBuffer, std::streampos fileSize)
347 std::string vet[MAX_POINT_INDICES], name;
348 int ptIdx[MAX_POINT_INDICES];
349 int nrmIdx[MAX_POINT_INDICES];
350 int texIdx[MAX_POINT_INDICES];
351 TriIndex triangle, triangle2;
352 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
354 bool hasTexture = false;
357 //Init AABB for the file
360 std::string strMatActual;
362 std::string input(objBuffer, fileSize);
363 std::istringstream ss(input);
364 ss.imbue(std::locale("C"));
367 std::getline(ss, line);
369 while(std::getline(ss, line))
371 std::istringstream isline(line, std::istringstream::in);
378 //Two different objects in the same file
382 mPoints.PushBack(point);
384 mSceneAABB.ConsiderNewPointInVolume(point);
392 mNormals.PushBack(point);
394 else if(tag == "#_#tangent")
400 mTangents.PushBack(point);
402 else if(tag == "#_#binormal")
408 mBiTangents.PushBack(point);
415 texture.y = 1.0 - texture.y;
416 mTextures.PushBack(texture);
418 else if(tag == "#_#vt1")
423 texture.y = 1.0 - texture.y;
424 mTextures2.PushBack(texture);
439 while((numIndices < MAX_POINT_INDICES) && (isline >> vet[numIndices]))
444 //Hold slashes that separate attributes of the same point.
448 const char* subString; //A pointer to the position in the string as we move through it.
450 subString = strstr(vet[0].c_str(), "/"); //Search for the first '/'
454 if(subString[1] == '/') // Of the form A//C, so has points and normals but no texture coordinates.
456 for(int i = 0; i < numIndices; i++)
458 std::istringstream isindex(vet[i]);
459 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
463 else if(strstr(subString, "/")) // Of the form A/B/C, so has points, textures and normals.
465 for(int i = 0; i < numIndices; i++)
467 std::istringstream isindex(vet[i]);
468 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
473 else // Of the form A/B, so has points and textures but no normals.
475 for(int i = 0; i < numIndices; i++)
477 std::istringstream isindex(vet[i]);
478 isindex >> ptIdx[i] >> separator >> texIdx[i];
485 else // Simply of the form A, as in, point indices only.
487 for(int i = 0; i < numIndices; i++)
489 std::istringstream isindex(vet[i]);
496 //If it is a triangle
499 for(int i = 0; i < 3; i++)
501 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
502 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
503 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
505 mTriangles.PushBack(triangle);
508 //If on the other hand it is a quad, we will create two triangles
509 else if(numIndices == 4)
511 for(int i = 0; i < 3; i++)
513 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
514 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
515 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
517 mTriangles.PushBack(triangle);
520 for(int i = 0; i < 3; i++)
522 int idx = (i + 2) % numIndices;
523 triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
524 triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
525 triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
527 mTriangles.PushBack(triangle2);
531 else if(tag == "usemtl")
533 isline >> strMatActual;
535 else if(tag == "mtllib")
537 isline >> strMatActual;
547 CenterAndScale(true, mPoints);
549 mHasTexturePoints = hasTexture;
556 void ObjLoader::LoadMaterial(char* objBuffer, std::streampos fileSize, std::string& diffuseTextureUrl, std::string& normalTextureUrl, std::string& glossTextureUrl)
562 std::string input(objBuffer, fileSize);
563 std::istringstream ss(input);
564 ss.imbue(std::locale("C"));
567 std::getline(ss, line);
569 while(std::getline(ss, line))
571 std::istringstream isline(line, std::istringstream::in);
576 if(tag == "newmtl") //name of the material
580 else if(tag == "Ka") //ambient color
582 isline >> fR >> fG >> fB;
584 else if(tag == "Kd") //diffuse color
586 isline >> fR >> fG >> fB;
588 else if(tag == "Ks") //specular color
590 isline >> fR >> fG >> fB;
592 else if(tag == "Tf") //color
598 else if(tag == "map_Kd")
601 diffuseTextureUrl = info;
602 mHasDiffuseMap = true;
604 else if(tag == "bump")
607 normalTextureUrl = info;
608 mHasNormalMap = true;
610 else if(tag == "map_Ks")
613 glossTextureUrl = info;
614 mHasSpecularMap = true;
618 mMaterialLoaded = true;
621 Geometry ObjLoader::CreateGeometry(int objectProperties, bool useSoftNormals)
623 Geometry surface = Geometry::New();
625 Dali::Vector<Vertex> vertices;
626 Dali::Vector<Vector2> textures;
627 Dali::Vector<VertexExt> verticesExt;
628 Dali::Vector<unsigned short> indices;
630 CreateGeometryArray(vertices, textures, verticesExt, indices, useSoftNormals);
632 //All vertices need at least Position and Normal
633 Property::Map vertexFormat;
634 vertexFormat["aPosition"] = Property::VECTOR3;
635 vertexFormat["aNormal"] = Property::VECTOR3;
636 VertexBuffer surfaceVertices = VertexBuffer::New(vertexFormat);
637 surfaceVertices.SetData(&vertices[0], vertices.Size());
638 surface.AddVertexBuffer(surfaceVertices);
640 //Some need texture coordinates
641 if((objectProperties & TEXTURE_COORDINATES) && mHasTexturePoints && mHasDiffuseMap)
643 Property::Map textureFormat;
644 textureFormat["aTexCoord"] = Property::VECTOR2;
645 VertexBuffer extraVertices = VertexBuffer::New(textureFormat);
646 extraVertices.SetData(&textures[0], textures.Size());
648 surface.AddVertexBuffer(extraVertices);
651 //Some need tangent and bitangent
652 if((objectProperties & TANGENTS) && (objectProperties & BINORMALS) && mHasTexturePoints)
654 Property::Map vertexExtFormat;
655 vertexExtFormat["aTangent"] = Property::VECTOR3;
656 vertexExtFormat["aBiNormal"] = Property::VECTOR3;
657 VertexBuffer extraVertices = VertexBuffer::New(vertexExtFormat);
658 extraVertices.SetData(&verticesExt[0], verticesExt.Size());
660 surface.AddVertexBuffer(extraVertices);
663 //If indices are required, we set them.
666 surface.SetIndexBuffer(&indices[0], indices.Size());
672 Vector3 ObjLoader::GetCenter()
674 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
678 Vector3 ObjLoader::GetSize()
680 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
684 void ObjLoader::ClearArrays()
694 mSceneLoaded = false;
697 bool ObjLoader::IsTexturePresent()
699 return mHasTexturePoints;
702 bool ObjLoader::IsDiffuseMapPresent()
704 return mHasDiffuseMap;
707 bool ObjLoader::IsNormalMapPresent()
709 return mHasNormalMap;
712 bool ObjLoader::IsSpecularMapPresent()
714 return mHasSpecularMap;
717 } // namespace Internal
718 } // namespace Toolkit