2 * Copyright (c) 2020 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>
30 const int MAX_POINT_INDICES = 4;
33 ObjLoader::ObjLoader()
34 : mSceneLoaded(false),
35 mMaterialLoaded(false),
37 mHasDiffuseMap(false),
39 mHasSpecularMap(false)
44 ObjLoader::~ObjLoader()
49 bool ObjLoader::IsSceneLoaded()
54 bool ObjLoader::IsMaterialLoaded()
56 return mMaterialLoaded;
59 void ObjLoader::CalculateHardFaceNormals(const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
61 int numFaceVertices = 3 * triangles.Size(); //Vertices per face, as each vertex has different normals instance for each face.
62 int normalIndex = 0; //Tracks progress through the array of normals.
65 normals.Resize(numFaceVertices);
67 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane.
68 for(unsigned long i = 0; i < triangles.Size(); i++)
71 const Vector3& v0 = points[triangles[i].pointIndex[0]];
72 const Vector3& v1 = points[triangles[i].pointIndex[1]];
73 const Vector3& v2 = points[triangles[i].pointIndex[2]];
76 Vector3 edge1 = v1 - v0;
77 Vector3 edge2 = v2 - v0;
79 //Using edges as vectors on the plane, cross product to get the normal.
80 Vector3 normalVector = edge1.Cross(edge2);
81 normalVector.Normalize();
83 //Assign normal index to triangle vertex and set the normal vector to the list of normals.
84 for(unsigned long j = 0; j < 3; j++, normalIndex++)
86 triangles[i].normalIndex[j] = normalIndex;
87 normals[normalIndex] = normalVector;
92 void ObjLoader::CalculateSoftFaceNormals(const Dali::Vector<Vector3>& points, Dali::Vector<TriIndex>& triangles, Dali::Vector<Vector3>& normals)
94 int normalIndex = 0; //Tracks progress through the array of normals.
97 normals.Resize(points.Size()); //One (averaged) normal per point.
99 //For each triangle, calculate the normal by crossing two vectors on the triangle's plane
100 //We then add the triangle's normal to the cumulative normals at each point of it
101 for(unsigned long i = 0; i < triangles.Size(); i++)
104 const Vector3& v0 = points[triangles[i].pointIndex[0]];
105 const Vector3& v1 = points[triangles[i].pointIndex[1]];
106 const Vector3& v2 = points[triangles[i].pointIndex[2]];
109 Vector3 edge1 = v1 - v0;
110 Vector3 edge2 = v2 - v0;
112 //Using edges as vectors on the plane, cross to get the normal.
113 Vector3 normalVector = edge1.Cross(edge2);
115 //Add this triangle's normal to the cumulative normal of each constituent triangle point and set the index of the normal accordingly.
116 for(unsigned long j = 0; j < 3; j++)
118 normalIndex = triangles[i].pointIndex[j];
119 triangles[i].normalIndex[j] = normalIndex; //Normal index matches up to vertex index, as one normal per vertex.
120 normals[normalIndex] += normalVector;
124 //Normalise the normals.
125 for(unsigned long i = 0; i < normals.Size(); i++)
127 normals[i].Normalize();
131 void ObjLoader::CalculateTangentFrame()
133 //Reset tangent vector to hold new values.
135 mTangents.Resize(mNormals.Size());
137 //For each triangle, calculate the tangent vector and then add it to the total tangent vector of each normal.
138 for(unsigned long a = 0; a < mTriangles.Size(); a++)
140 Vector3 tangentVector;
142 const Vector3& v0 = mPoints[mTriangles[a].pointIndex[0]];
143 const Vector3& v1 = mPoints[mTriangles[a].pointIndex[1]];
144 const Vector3& v2 = mPoints[mTriangles[a].pointIndex[2]];
146 Vector3 edge1 = v1 - v0;
147 Vector3 edge2 = v2 - v0;
149 const Vector2& w0 = mTextureUv[mTriangles[a].textureIndex[0]];
150 const Vector2& w1 = mTextureUv[mTriangles[a].textureIndex[1]];
151 const Vector2& w2 = mTextureUv[mTriangles[a].textureIndex[2]];
153 float deltaU1 = w1.x - w0.x;
154 float deltaV1 = w1.y - w0.y;
155 float deltaU2 = w2.x - w0.x;
156 float deltaV2 = w2.y - w0.y;
158 // 1.0/f could cause division by zero in some cases, this factor will act
159 // as a weight of the tangent vector and it is fixed when it is normalised.
160 float f = (deltaU1 * deltaV2 - deltaU2 * deltaV1);
162 tangentVector.x = f * (deltaV2 * edge1.x - deltaV1 * edge2.x);
163 tangentVector.y = f * (deltaV2 * edge1.y - deltaV1 * edge2.y);
164 tangentVector.z = f * (deltaV2 * edge1.z - deltaV1 * edge2.z);
166 mTangents[mTriangles[a].normalIndex[0]] += tangentVector;
167 mTangents[mTriangles[a].normalIndex[1]] += tangentVector;
168 mTangents[mTriangles[a].normalIndex[2]] += tangentVector;
171 //Orthogonalize tangents.
172 for(unsigned long a = 0; a < mTangents.Size(); a++)
174 const Vector3& n = mNormals[a];
175 const Vector3& t = mTangents[a];
177 // Gram-Schmidt orthogonalize
178 mTangents[a] = t - n * n.Dot(t);
179 mTangents[a].Normalize();
183 void ObjLoader::CenterAndScale(bool center, Dali::Vector<Vector3>& points)
185 BoundingVolume newAABB;
187 Vector3 sceneSize = GetSize();
189 float biggestDimension = sceneSize.x;
190 if(sceneSize.y > biggestDimension)
192 biggestDimension = sceneSize.y;
194 if(sceneSize.z > biggestDimension)
196 biggestDimension = sceneSize.z;
200 for(unsigned int ui = 0; ui < points.Size(); ++ui)
202 points[ui] = points[ui] - GetCenter();
203 points[ui] = points[ui] / biggestDimension;
204 newAABB.ConsiderNewPointInVolume(points[ui]);
207 mSceneAABB = newAABB;
210 void ObjLoader::CreateGeometryArray(Dali::Vector<Vector3>& positions,
211 Dali::Vector<Vector3>& normals,
212 Dali::Vector<Vector3>& tangents,
213 Dali::Vector<Vector2>& textures,
214 Dali::Vector<unsigned short>& indices,
217 //We must calculate the tangents if they weren't supplied, or if they don't match up.
218 bool mustCalculateTangents = (mTangents.Size() == 0) || (mTangents.Size() != mNormals.Size());
220 //However, we don't need to do this if the object doesn't use textures to begin with.
221 mustCalculateTangents &= mHasTextureUv;
223 // We calculate the normals if hard normals(flat normals) is set.
224 // Use the normals provided by the file to make the tangent calculation per normal,
225 // the correct results depends of normal generated by file, otherwise we need to recalculate
226 // the normal programmatically.
227 if(((mNormals.Size() == 0) && mustCalculateTangents) || !useSoftNormals)
231 CalculateSoftFaceNormals(mPoints, mTriangles, mNormals);
235 CalculateHardFaceNormals(mPoints, mTriangles, mNormals);
239 if(mHasTextureUv && mustCalculateTangents)
241 CalculateTangentFrame();
244 bool mapsCorrespond; //True if the sizes of the arrays necessary to draw the object match.
248 mapsCorrespond = (mPoints.Size() == mTextureUv.Size()) && (mTextureUv.Size() == mNormals.Size());
252 mapsCorrespond = (mPoints.Size() == mNormals.Size());
255 //Check the number of points textures and normals
258 int numPoints = mPoints.Size();
259 int numIndices = 3 * mTriangles.Size();
260 positions.Resize(numPoints);
261 normals.Resize(numPoints);
262 tangents.Resize(numPoints);
263 textures.Resize(numPoints);
264 indices.Resize(numIndices);
266 //We create the vertices array. For now we just copy points info
271 //We copy the indices
272 for(unsigned int ui = 0; ui < mTriangles.Size(); ++ui)
274 for(int j = 0; j < 3; ++j)
276 indices[indiceIndex] = mTriangles[ui].pointIndex[j];
279 normals[mTriangles[ui].pointIndex[j]] = mNormals[mTriangles[ui].normalIndex[j]];
283 textures[mTriangles[ui].pointIndex[j]] = mTextureUv[mTriangles[ui].textureIndex[j]];
284 tangents[mTriangles[ui].pointIndex[j]] = mTangents[mTriangles[ui].normalIndex[j]];
291 int numVertices = 3 * mTriangles.Size();
292 positions.Resize(numVertices);
293 normals.Resize(numVertices);
294 textures.Resize(numVertices);
295 tangents.Resize(numVertices);
299 //We have to normalize the arrays so we can draw we just one index array
300 for(unsigned int ui = 0; ui < mTriangles.Size(); ++ui)
302 for(int j = 0; j < 3; ++j)
304 positions[index] = mPoints[mTriangles[ui].pointIndex[j]];
305 normals[index] = mNormals[mTriangles[ui].normalIndex[j]];
309 textures[index] = mTextureUv[mTriangles[ui].textureIndex[j]];
310 tangents[index] = mTangents[mTriangles[ui].normalIndex[j]];
319 bool ObjLoader::LoadObject(char* objBuffer, std::streampos fileSize)
323 std::string vet[MAX_POINT_INDICES], name;
324 int ptIdx[MAX_POINT_INDICES];
325 int nrmIdx[MAX_POINT_INDICES];
326 int texIdx[MAX_POINT_INDICES];
327 TriIndex triangle, triangle2;
328 int pntAcum = 0, texAcum = 0, nrmAcum = 0;
330 bool hasTexture = false;
333 //Init AABB for the file
336 std::string strMatActual;
338 std::string input(objBuffer, fileSize);
339 std::istringstream ss(input);
340 ss.imbue(std::locale("C"));
343 std::getline(ss, line);
345 while(std::getline(ss, line))
347 std::istringstream isline(line, std::istringstream::in);
354 //Two different objects in the same file
358 mPoints.PushBack(point);
360 mSceneAABB.ConsiderNewPointInVolume(point);
368 mNormals.PushBack(point);
370 else if(tag == "#_#tangent")
376 mTangents.PushBack(point);
378 else if(tag == "#_#binormal")
384 mBiTangents.PushBack(point);
390 texture.y = 1.0 - texture.y;
391 mTextureUv.PushBack(texture);
393 else if(tag == "#_#vt1")
398 texture.y = 1.0 - texture.y;
399 mTextureUv2.PushBack(texture);
414 while((numIndices < MAX_POINT_INDICES) && (isline >> vet[numIndices]))
419 //Hold slashes that separate attributes of the same point.
423 const char* subString; //A pointer to the position in the string as we move through it.
425 subString = strstr(vet[0].c_str(), "/"); //Search for the first '/'
429 if(subString[1] == '/') // Of the form A//C, so has points and normals but no texture coordinates.
431 for(int i = 0; i < numIndices; i++)
433 std::istringstream isindex(vet[i]);
434 isindex >> ptIdx[i] >> separator >> separator2 >> nrmIdx[i];
438 else if(strstr(subString, "/")) // Of the form A/B/C, so has points, textures and normals.
440 for(int i = 0; i < numIndices; i++)
442 std::istringstream isindex(vet[i]);
443 isindex >> ptIdx[i] >> separator >> texIdx[i] >> separator2 >> nrmIdx[i];
448 else // Of the form A/B, so has points and textures but no normals.
450 for(int i = 0; i < numIndices; i++)
452 std::istringstream isindex(vet[i]);
453 isindex >> ptIdx[i] >> separator >> texIdx[i];
460 else // Simply of the form A, as in, point indices only.
462 for(int i = 0; i < numIndices; i++)
464 std::istringstream isindex(vet[i]);
471 //If it is a triangle
474 for(int i = 0; i < 3; i++)
476 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
477 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
478 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
480 mTriangles.PushBack(triangle);
483 //If on the other hand it is a quad, we will create two triangles
484 else if(numIndices == 4)
486 for(int i = 0; i < 3; i++)
488 triangle.pointIndex[i] = ptIdx[i] - 1 - pntAcum;
489 triangle.normalIndex[i] = nrmIdx[i] - 1 - nrmAcum;
490 triangle.textureIndex[i] = texIdx[i] - 1 - texAcum;
492 mTriangles.PushBack(triangle);
495 for(int i = 0; i < 3; i++)
497 int idx = (i + 2) % numIndices;
498 triangle2.pointIndex[i] = ptIdx[idx] - 1 - pntAcum;
499 triangle2.normalIndex[i] = nrmIdx[idx] - 1 - nrmAcum;
500 triangle2.textureIndex[i] = texIdx[idx] - 1 - texAcum;
502 mTriangles.PushBack(triangle2);
506 else if(tag == "usemtl")
508 isline >> strMatActual;
510 else if(tag == "mtllib")
512 isline >> strMatActual;
522 CenterAndScale(true, mPoints);
524 mHasTextureUv = hasTexture;
531 void ObjLoader::LoadMaterial(char* objBuffer,
532 std::streampos fileSize,
533 std::string& diffuseTextureUrl,
534 std::string& normalTextureUrl,
535 std::string& glossTextureUrl)
541 std::string input = objBuffer;
542 std::istringstream ss(input);
543 ss.imbue(std::locale("C"));
546 std::getline(ss, line);
548 while(std::getline(ss, line))
550 std::istringstream isline(line, std::istringstream::in);
555 if(tag == "newmtl") //name of the material
559 else if(tag == "Ka") //ambient color
561 isline >> fR >> fG >> fB;
563 else if(tag == "Kd") //diffuse color
565 isline >> fR >> fG >> fB;
567 else if(tag == "Ks") //specular color
569 isline >> fR >> fG >> fB;
571 else if(tag == "Tf") //color
577 else if(tag == "map_Kd")
580 diffuseTextureUrl = info;
581 mHasDiffuseMap = true;
583 else if(tag == "bump")
586 normalTextureUrl = info;
587 mHasNormalMap = true;
589 else if(tag == "map_Ks")
592 glossTextureUrl = info;
593 mHasSpecularMap = true;
597 mMaterialLoaded = true;
600 Geometry ObjLoader::CreateGeometry(int objectProperties, bool useSoftNormals)
602 Geometry surface = Geometry::New();
604 Dali::Vector<Vector3> positions;
605 Dali::Vector<Vector3> normals;
606 Dali::Vector<Vector3> tangents;
607 Dali::Vector<Vector2> textures;
608 Dali::Vector<unsigned short> indices;
610 CreateGeometryArray(positions, normals, tangents, textures, indices, useSoftNormals);
612 //All vertices need at least Position and Normal
614 Property::Map positionMap;
615 positionMap["aPosition"] = Property::VECTOR3;
616 VertexBuffer positionBuffer = VertexBuffer::New(positionMap);
617 positionBuffer.SetData(positions.Begin(), positions.Count());
619 Property::Map normalMap;
620 normalMap["aNormal"] = Property::VECTOR3;
621 VertexBuffer normalBuffer = VertexBuffer::New(normalMap);
622 normalBuffer.SetData(normals.Begin(), normals.Count());
624 surface.AddVertexBuffer(positionBuffer);
625 surface.AddVertexBuffer(normalBuffer);
628 if((objectProperties & TANGENTS) && mHasTextureUv)
630 Property::Map tangentMap;
631 tangentMap["aTangent"] = Property::VECTOR3;
632 VertexBuffer tangentBuffer = VertexBuffer::New(tangentMap);
633 tangentBuffer.SetData(tangents.Begin(), tangents.Count());
635 surface.AddVertexBuffer(tangentBuffer);
638 //Some need texture coordinates
639 if((objectProperties & TEXTURE_COORDINATES) && mHasTextureUv)
641 Property::Map textCoordMap;
642 textCoordMap["aTexCoord"] = Property::VECTOR2;
643 VertexBuffer texCoordBuffer = VertexBuffer::New(textCoordMap);
644 texCoordBuffer.SetData(textures.Begin(), textures.Count());
646 surface.AddVertexBuffer(texCoordBuffer);
649 //If indices are required, we set them.
652 surface.SetIndexBuffer(&indices[0], indices.Size());
658 Vector3 ObjLoader::GetCenter()
660 Vector3 center = GetSize() * 0.5 + mSceneAABB.pointMin;
664 Vector3 ObjLoader::GetSize()
666 Vector3 size = mSceneAABB.pointMax - mSceneAABB.pointMin;
670 void ObjLoader::ClearArrays()
681 mSceneLoaded = false;
684 bool ObjLoader::IsTexturePresent()
686 return mHasTextureUv;
689 bool ObjLoader::IsDiffuseMapPresent()
691 return mHasDiffuseMap;
694 bool ObjLoader::IsNormalMapPresent()
696 return mHasNormalMap;
699 bool ObjLoader::IsSpecularMapPresent()
701 return mHasSpecularMap;
704 } // namespace PbrDemo