1295 lines
36 KiB
C++
1295 lines
36 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2007 dhpoware. All Rights Reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// The methods normalize() and scale() are based on source code from
|
|
// http://www.mvps.org/directx/articles/scalemesh9.htm.
|
|
//
|
|
// The addVertex() method is based on source code from the Direct3D MeshFromOBJ
|
|
// sample found in the DirectX SDK.
|
|
//
|
|
// The generateTangents() method is based on public source code from
|
|
// http://www.terathon.com/code/tangent.php.
|
|
//
|
|
// The importGeometryFirstPass(), importGeometrySecondPass(), and
|
|
// importMaterials() methods are based on source code from Nate Robins' OpenGL
|
|
// Tutors programs (http://www.xmission.com/~nate/tutors.html).
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <cstdio>
|
|
#include "Engine.h"
|
|
#include "OBJModel.h"
|
|
|
|
namespace Engine {
|
|
|
|
namespace
|
|
{
|
|
bool MeshCompFunc(const OBJModel::Mesh &lhs, const OBJModel::Mesh &rhs)
|
|
{
|
|
return lhs.pMaterial->alpha > rhs.pMaterial->alpha;
|
|
}
|
|
}
|
|
|
|
OBJModel::OBJModel()
|
|
{
|
|
m_hasPositions = false;
|
|
m_hasNormals = false;
|
|
m_hasTextureCoords = false;
|
|
m_hasTangents = false;
|
|
|
|
m_numberOfVertexCoords = 0;
|
|
m_numberOfTextureCoords = 0;
|
|
m_numberOfNormals = 0;
|
|
m_numberOfTriangles = 0;
|
|
m_numberOfMaterials = 0;
|
|
m_numberOfMeshes = 0;
|
|
|
|
m_center[0] = m_center[1] = m_center[2] = 0.0f;
|
|
m_width = m_height = m_length = m_radius = 0.0f;
|
|
}
|
|
|
|
OBJModel::~OBJModel()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
void OBJModel::bounds(float center[3], float &width, float &height,
|
|
float &length, float &radius) const
|
|
{
|
|
float xMax = std::numeric_limits<float>::min();
|
|
float yMax = std::numeric_limits<float>::min();
|
|
float zMax = std::numeric_limits<float>::min();
|
|
|
|
float xMin = std::numeric_limits<float>::max();
|
|
float yMin = std::numeric_limits<float>::max();
|
|
float zMin = std::numeric_limits<float>::max();
|
|
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
float z = 0.0f;
|
|
|
|
int numVerts = static_cast<int>(m_vertexBuffer.size());
|
|
|
|
for (int i = 0; i < numVerts; ++i)
|
|
{
|
|
x = m_vertexBuffer[i].position[0];
|
|
y = m_vertexBuffer[i].position[1];
|
|
z = m_vertexBuffer[i].position[2];
|
|
|
|
if (x < xMin)
|
|
xMin = x;
|
|
|
|
if (x > xMax)
|
|
xMax = x;
|
|
|
|
if (y < yMin)
|
|
yMin = y;
|
|
|
|
if (y > yMax)
|
|
yMax = y;
|
|
|
|
if (z < zMin)
|
|
zMin = z;
|
|
|
|
if (z > zMax)
|
|
zMax = z;
|
|
}
|
|
|
|
center[0] = (xMin + xMax) / 2.0f;
|
|
center[1] = (yMin + yMax) / 2.0f;
|
|
center[2] = (zMin + zMax) / 2.0f;
|
|
|
|
width = xMax - xMin;
|
|
height = yMax - yMin;
|
|
length = zMax - zMin;
|
|
|
|
radius = std::max(std::max(width, height), length);
|
|
}
|
|
|
|
void OBJModel::destroy()
|
|
{
|
|
m_hasPositions = false;
|
|
m_hasTextureCoords = false;
|
|
m_hasNormals = false;
|
|
m_hasTangents = false;
|
|
|
|
m_numberOfVertexCoords = 0;
|
|
m_numberOfTextureCoords = 0;
|
|
m_numberOfNormals = 0;
|
|
m_numberOfTriangles = 0;
|
|
m_numberOfMaterials = 0;
|
|
m_numberOfMeshes = 0;
|
|
|
|
m_center[0] = m_center[1] = m_center[2] = 0.0f;
|
|
m_width = m_height = m_length = m_radius = 0.0f;
|
|
|
|
m_directoryPath.clear();
|
|
|
|
m_meshes.clear();
|
|
m_materials.clear();
|
|
m_vertexBuffer.clear();
|
|
m_indexBuffer.clear();
|
|
m_attributeBuffer.clear();
|
|
|
|
m_vertexCoords.clear();
|
|
m_textureCoords.clear();
|
|
m_normals.clear();
|
|
|
|
m_materialCache.clear();
|
|
m_vertexCache.clear();
|
|
}
|
|
|
|
bool OBJModel::import(const char *pszFilename, bool rebuildNormals)
|
|
{
|
|
FILE *pFile = fopen(pszFilename, "r");
|
|
|
|
if (!pFile)
|
|
return false;
|
|
|
|
// Extract the directory the OBJ file is in from the file name.
|
|
// This directory path will be used to load the OBJ's associated MTL file.
|
|
|
|
m_directoryPath.clear();
|
|
|
|
boost::filesystem::path full_path (pszFilename);
|
|
m_directoryPath = full_path.parent_path().string();
|
|
|
|
LogDebug ("parent path for %s is %s", pszFilename, m_directoryPath.c_str());
|
|
|
|
// Import the OBJ file.
|
|
|
|
importGeometryFirstPass(pFile);
|
|
rewind(pFile);
|
|
importGeometrySecondPass(pFile);
|
|
fclose(pFile);
|
|
|
|
// Perform post import tasks.
|
|
|
|
buildMeshes();
|
|
bounds(m_center, m_width, m_height, m_length, m_radius);
|
|
|
|
// Build vertex normals if required.
|
|
|
|
if (rebuildNormals)
|
|
{
|
|
generateNormals();
|
|
}
|
|
else
|
|
{
|
|
if (!hasNormals())
|
|
generateNormals();
|
|
}
|
|
|
|
// Build tangents is required.
|
|
|
|
for (int i = 0; i < m_numberOfMaterials; ++i)
|
|
{
|
|
if (!m_materials[i].bumpMapFilename.empty())
|
|
{
|
|
generateTangents();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void OBJModel::normalize(float scaleTo, bool center)
|
|
{
|
|
float width = 0.0f;
|
|
float height = 0.0f;
|
|
float length = 0.0f;
|
|
float radius = 0.0f;
|
|
float centerPos[3] = {0.0f};
|
|
|
|
bounds(centerPos, width, height, length, radius);
|
|
|
|
float scalingFactor = scaleTo / radius;
|
|
float offset[3] = {0.0f};
|
|
|
|
if (center)
|
|
{
|
|
offset[0] = -centerPos[0];
|
|
offset[1] = -centerPos[1];
|
|
offset[2] = -centerPos[2];
|
|
}
|
|
else
|
|
{
|
|
offset[0] = 0.0f;
|
|
offset[1] = 0.0f;
|
|
offset[2] = 0.0f;
|
|
}
|
|
|
|
scale(scalingFactor, offset);
|
|
bounds(m_center, m_width, m_height, m_length, m_radius);
|
|
}
|
|
|
|
void OBJModel::reverseWinding()
|
|
{
|
|
int swap = 0;
|
|
|
|
// Reverse face winding.
|
|
for (int i = 0; i < static_cast<int>(m_indexBuffer.size()); i += 3)
|
|
{
|
|
swap = m_indexBuffer[i + 1];
|
|
m_indexBuffer[i + 1] = m_indexBuffer[i + 2];
|
|
m_indexBuffer[i + 2] = swap;
|
|
}
|
|
|
|
float *pNormal = 0;
|
|
float *pTangent = 0;
|
|
|
|
// Invert normals and tangents.
|
|
for (int i = 0; i < static_cast<int>(m_vertexBuffer.size()); ++i)
|
|
{
|
|
pNormal = m_vertexBuffer[i].normal;
|
|
pNormal[0] = -pNormal[0];
|
|
pNormal[1] = -pNormal[1];
|
|
pNormal[2] = -pNormal[2];
|
|
|
|
pTangent = m_vertexBuffer[i].tangent;
|
|
pTangent[0] = -pTangent[0];
|
|
pTangent[1] = -pTangent[1];
|
|
pTangent[2] = -pTangent[2];
|
|
}
|
|
}
|
|
|
|
void OBJModel::scale(float scaleFactor, float offset[3])
|
|
{
|
|
float *pPosition = 0;
|
|
|
|
for (int i = 0; i < static_cast<int>(m_vertexBuffer.size()); ++i)
|
|
{
|
|
pPosition = m_vertexBuffer[i].position;
|
|
|
|
pPosition[0] += offset[0];
|
|
pPosition[1] += offset[1];
|
|
pPosition[2] += offset[2];
|
|
|
|
pPosition[0] *= scaleFactor;
|
|
pPosition[1] *= scaleFactor;
|
|
pPosition[2] *= scaleFactor;
|
|
}
|
|
}
|
|
|
|
void OBJModel::addTrianglePos(int index, int material, int v0, int v1, int v2)
|
|
{
|
|
Vertex vertex =
|
|
{
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f}
|
|
};
|
|
|
|
m_attributeBuffer[index] = material;
|
|
|
|
vertex.position[0] = m_vertexCoords[v0 * 3];
|
|
vertex.position[1] = m_vertexCoords[v0 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v0 * 3 + 2];
|
|
m_indexBuffer[index * 3] = addVertex(v0, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v1 * 3];
|
|
vertex.position[1] = m_vertexCoords[v1 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v1 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v2 * 3];
|
|
vertex.position[1] = m_vertexCoords[v2 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v2 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex);
|
|
}
|
|
|
|
void OBJModel::addTrianglePosNormal(int index, int material, int v0, int v1,
|
|
int v2, int vn0, int vn1, int vn2)
|
|
{
|
|
Vertex vertex =
|
|
{
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f}
|
|
};
|
|
|
|
m_attributeBuffer[index] = material;
|
|
|
|
vertex.position[0] = m_vertexCoords[v0 * 3];
|
|
vertex.position[1] = m_vertexCoords[v0 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v0 * 3 + 2];
|
|
vertex.normal[0] = m_normals[vn0 * 3];
|
|
vertex.normal[1] = m_normals[vn0 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn0 * 3 + 2];
|
|
m_indexBuffer[index * 3] = addVertex(v0, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v1 * 3];
|
|
vertex.position[1] = m_vertexCoords[v1 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v1 * 3 + 2];
|
|
vertex.normal[0] = m_normals[vn1 * 3];
|
|
vertex.normal[1] = m_normals[vn1 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn1 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v2 * 3];
|
|
vertex.position[1] = m_vertexCoords[v2 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v2 * 3 + 2];
|
|
vertex.normal[0] = m_normals[vn2 * 3];
|
|
vertex.normal[1] = m_normals[vn2 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn2 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex);
|
|
}
|
|
|
|
void OBJModel::addTrianglePosTexCoord(int index, int material, int v0, int v1,
|
|
int v2, int vt0, int vt1, int vt2)
|
|
{
|
|
Vertex vertex =
|
|
{
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f}
|
|
};
|
|
|
|
m_attributeBuffer[index] = material;
|
|
|
|
vertex.position[0] = m_vertexCoords[v0 * 3];
|
|
vertex.position[1] = m_vertexCoords[v0 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v0 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt0 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt0 * 2 + 1];
|
|
m_indexBuffer[index * 3] = addVertex(v0, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v1 * 3];
|
|
vertex.position[1] = m_vertexCoords[v1 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v1 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt1 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt1 * 2 + 1];
|
|
m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v2 * 3];
|
|
vertex.position[1] = m_vertexCoords[v2 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v2 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt2 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt2 * 2 + 1];
|
|
m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex);
|
|
}
|
|
|
|
void OBJModel::addTrianglePosTexCoordNormal(int index, int material, int v0,
|
|
int v1, int v2, int vt0, int vt1,
|
|
int vt2, int vn0, int vn1, int vn2)
|
|
{
|
|
Vertex vertex =
|
|
{
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f},
|
|
{0.0f, 0.0f, 0.0f}
|
|
};
|
|
|
|
m_attributeBuffer[index] = material;
|
|
|
|
vertex.position[0] = m_vertexCoords[v0 * 3];
|
|
vertex.position[1] = m_vertexCoords[v0 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v0 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt0 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt0 * 2 + 1];
|
|
vertex.normal[0] = m_normals[vn0 * 3];
|
|
vertex.normal[1] = m_normals[vn0 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn0 * 3 + 2];
|
|
m_indexBuffer[index * 3] = addVertex(v0, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v1 * 3];
|
|
vertex.position[1] = m_vertexCoords[v1 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v1 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt1 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt1 * 2 + 1];
|
|
vertex.normal[0] = m_normals[vn1 * 3];
|
|
vertex.normal[1] = m_normals[vn1 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn1 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 1] = addVertex(v1, &vertex);
|
|
|
|
vertex.position[0] = m_vertexCoords[v2 * 3];
|
|
vertex.position[1] = m_vertexCoords[v2 * 3 + 1];
|
|
vertex.position[2] = m_vertexCoords[v2 * 3 + 2];
|
|
vertex.texCoord[0] = m_textureCoords[vt2 * 2];
|
|
vertex.texCoord[1] = m_textureCoords[vt2 * 2 + 1];
|
|
vertex.normal[0] = m_normals[vn2 * 3];
|
|
vertex.normal[1] = m_normals[vn2 * 3 + 1];
|
|
vertex.normal[2] = m_normals[vn2 * 3 + 2];
|
|
m_indexBuffer[index * 3 + 2] = addVertex(v2, &vertex);
|
|
}
|
|
|
|
int OBJModel::addVertex(int hash, const Vertex *pVertex)
|
|
{
|
|
int index = -1;
|
|
std::map<int, std::vector<int> >::const_iterator iter = m_vertexCache.find(hash);
|
|
|
|
if (iter == m_vertexCache.end())
|
|
{
|
|
// Vertex hash doesn't exist in the cache.
|
|
|
|
index = static_cast<int>(m_vertexBuffer.size());
|
|
m_vertexBuffer.push_back(*pVertex);
|
|
m_vertexCache.insert(std::make_pair(hash, std::vector<int>(1, index)));
|
|
}
|
|
else
|
|
{
|
|
// One or more vertices have been hashed to this entry in the cache.
|
|
|
|
const std::vector<int> &vertices = iter->second;
|
|
const Vertex *pCachedVertex = 0;
|
|
bool found = false;
|
|
|
|
for (std::vector<int>::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
|
|
{
|
|
index = *i;
|
|
pCachedVertex = &m_vertexBuffer[index];
|
|
|
|
if (memcmp(pCachedVertex, pVertex, sizeof(Vertex)) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
index = static_cast<int>(m_vertexBuffer.size());
|
|
m_vertexBuffer.push_back(*pVertex);
|
|
m_vertexCache[hash].push_back(index);
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void OBJModel::buildMeshes()
|
|
{
|
|
// Group the model's triangles based on material type.
|
|
|
|
Mesh *pMesh = 0;
|
|
int materialId = -1;
|
|
int numMeshes = 0;
|
|
|
|
// Count the number of meshes.
|
|
for (int i = 0; i < static_cast<int>(m_attributeBuffer.size()); ++i)
|
|
{
|
|
if (m_attributeBuffer[i] != materialId)
|
|
{
|
|
materialId = m_attributeBuffer[i];
|
|
++numMeshes;
|
|
}
|
|
}
|
|
|
|
// Allocate memory for the meshes and reset counters.
|
|
m_numberOfMeshes = numMeshes;
|
|
m_meshes.resize(m_numberOfMeshes);
|
|
numMeshes = 0;
|
|
materialId = -1;
|
|
|
|
// Build the meshes. One mesh for each unique material.
|
|
for (int i = 0; i < static_cast<int>(m_attributeBuffer.size()); ++i)
|
|
{
|
|
if (m_attributeBuffer[i] != materialId)
|
|
{
|
|
materialId = m_attributeBuffer[i];
|
|
pMesh = &m_meshes[numMeshes++];
|
|
pMesh->pMaterial = &m_materials[materialId];
|
|
pMesh->startIndex = i * 3;
|
|
++pMesh->triangleCount;
|
|
}
|
|
else
|
|
{
|
|
++pMesh->triangleCount;
|
|
}
|
|
}
|
|
|
|
// Sort the meshes based on its material alpha. Fully opaque meshes
|
|
// towards the front and fully transparent towards the back.
|
|
std::sort(m_meshes.begin(), m_meshes.end(), MeshCompFunc);
|
|
}
|
|
|
|
void OBJModel::generateNormals()
|
|
{
|
|
const int *pTriangle = 0;
|
|
Vertex *pVertex0 = 0;
|
|
Vertex *pVertex1 = 0;
|
|
Vertex *pVertex2 = 0;
|
|
float edge1[3] = {0.0f, 0.0f, 0.0f};
|
|
float edge2[3] = {0.0f, 0.0f, 0.0f};
|
|
float normal[3] = {0.0f, 0.0f, 0.0f};
|
|
float length = 0.0f;
|
|
int totalVertices = getNumberOfVertices();
|
|
int totalTriangles = getNumberOfTriangles();
|
|
|
|
// Initialize all the vertex normals.
|
|
for (int i = 0; i < totalVertices; ++i)
|
|
{
|
|
pVertex0 = &m_vertexBuffer[i];
|
|
pVertex0->normal[0] = 0.0f;
|
|
pVertex0->normal[1] = 0.0f;
|
|
pVertex0->normal[2] = 0.0f;
|
|
}
|
|
|
|
// Calculate the vertex normals.
|
|
for (int i = 0; i < totalTriangles; ++i)
|
|
{
|
|
pTriangle = &m_indexBuffer[i * 3];
|
|
|
|
pVertex0 = &m_vertexBuffer[pTriangle[0]];
|
|
pVertex1 = &m_vertexBuffer[pTriangle[1]];
|
|
pVertex2 = &m_vertexBuffer[pTriangle[2]];
|
|
|
|
// Calculate triangle face normal.
|
|
|
|
edge1[0] = pVertex1->position[0] - pVertex0->position[0];
|
|
edge1[1] = pVertex1->position[1] - pVertex0->position[1];
|
|
edge1[2] = pVertex1->position[2] - pVertex0->position[2];
|
|
|
|
edge2[0] = pVertex2->position[0] - pVertex0->position[0];
|
|
edge2[1] = pVertex2->position[1] - pVertex0->position[1];
|
|
edge2[2] = pVertex2->position[2] - pVertex0->position[2];
|
|
|
|
normal[0] = (edge1[1] * edge2[2]) - (edge1[2] * edge2[1]);
|
|
normal[1] = (edge1[2] * edge2[0]) - (edge1[0] * edge2[2]);
|
|
normal[2] = (edge1[0] * edge2[1]) - (edge1[1] * edge2[0]);
|
|
|
|
// Accumulate the normals.
|
|
|
|
pVertex0->normal[0] += normal[0];
|
|
pVertex0->normal[1] += normal[1];
|
|
pVertex0->normal[2] += normal[2];
|
|
|
|
pVertex1->normal[0] += normal[0];
|
|
pVertex1->normal[1] += normal[1];
|
|
pVertex1->normal[2] += normal[2];
|
|
|
|
pVertex2->normal[0] += normal[0];
|
|
pVertex2->normal[1] += normal[1];
|
|
pVertex2->normal[2] += normal[2];
|
|
}
|
|
|
|
// Normalize the vertex normals.
|
|
for (int i = 0; i < totalVertices; ++i)
|
|
{
|
|
pVertex0 = &m_vertexBuffer[i];
|
|
|
|
length = 1.0f / sqrtf(pVertex0->normal[0] * pVertex0->normal[0] +
|
|
pVertex0->normal[1] * pVertex0->normal[1] +
|
|
pVertex0->normal[2] * pVertex0->normal[2]);
|
|
|
|
pVertex0->normal[0] *= length;
|
|
pVertex0->normal[1] *= length;
|
|
pVertex0->normal[2] *= length;
|
|
}
|
|
|
|
m_hasNormals = true;
|
|
}
|
|
|
|
void OBJModel::generateTangents()
|
|
{
|
|
const int *pTriangle = 0;
|
|
Vertex *pVertex0 = 0;
|
|
Vertex *pVertex1 = 0;
|
|
Vertex *pVertex2 = 0;
|
|
float edge1[3] = {0.0f, 0.0f, 0.0f};
|
|
float edge2[3] = {0.0f, 0.0f, 0.0f};
|
|
float texEdge1[2] = {0.0f, 0.0f};
|
|
float texEdge2[2] = {0.0f, 0.0f};
|
|
float tangent[3] = {0.0f, 0.0f, 0.0f};
|
|
float bitangent[3] = {0.0f, 0.0f, 0.0f};
|
|
float det = 0.0f;
|
|
float nDotT = 0.0f;
|
|
float bDotB = 0.0f;
|
|
float length = 0.0f;
|
|
int totalVertices = getNumberOfVertices();
|
|
int totalTriangles = getNumberOfTriangles();
|
|
|
|
// Initialize all the vertex tangents and bitangents.
|
|
for (int i = 0; i < totalVertices; ++i)
|
|
{
|
|
pVertex0 = &m_vertexBuffer[i];
|
|
|
|
pVertex0->tangent[0] = 0.0f;
|
|
pVertex0->tangent[1] = 0.0f;
|
|
pVertex0->tangent[2] = 0.0f;
|
|
pVertex0->tangent[3] = 0.0f;
|
|
|
|
pVertex0->bitangent[0] = 0.0f;
|
|
pVertex0->bitangent[1] = 0.0f;
|
|
pVertex0->bitangent[2] = 0.0f;
|
|
}
|
|
|
|
// Calculate the vertex tangents and bitangents.
|
|
for (int i = 0; i < totalTriangles; ++i)
|
|
{
|
|
pTriangle = &m_indexBuffer[i * 3];
|
|
|
|
pVertex0 = &m_vertexBuffer[pTriangle[0]];
|
|
pVertex1 = &m_vertexBuffer[pTriangle[1]];
|
|
pVertex2 = &m_vertexBuffer[pTriangle[2]];
|
|
|
|
// Calculate the triangle face tangent and bitangent.
|
|
|
|
edge1[0] = pVertex1->position[0] - pVertex0->position[0];
|
|
edge1[1] = pVertex1->position[1] - pVertex0->position[1];
|
|
edge1[2] = pVertex1->position[2] - pVertex0->position[2];
|
|
|
|
edge2[0] = pVertex2->position[0] - pVertex0->position[0];
|
|
edge2[1] = pVertex2->position[1] - pVertex0->position[1];
|
|
edge2[2] = pVertex2->position[2] - pVertex0->position[2];
|
|
|
|
texEdge1[0] = pVertex1->texCoord[0] - pVertex0->texCoord[0];
|
|
texEdge1[1] = pVertex1->texCoord[1] - pVertex0->texCoord[1];
|
|
|
|
texEdge2[0] = pVertex2->texCoord[0] - pVertex0->texCoord[0];
|
|
texEdge2[1] = pVertex2->texCoord[1] - pVertex0->texCoord[1];
|
|
|
|
det = texEdge1[0] * texEdge2[1] - texEdge2[0] * texEdge1[1];
|
|
|
|
if (fabs(det) < 1e-6f)
|
|
{
|
|
tangent[0] = 1.0f;
|
|
tangent[1] = 0.0f;
|
|
tangent[2] = 0.0f;
|
|
|
|
bitangent[0] = 0.0f;
|
|
bitangent[1] = 1.0f;
|
|
bitangent[2] = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
det = 1.0f / det;
|
|
|
|
tangent[0] = (texEdge2[1] * edge1[0] - texEdge1[1] * edge2[0]) * det;
|
|
tangent[1] = (texEdge2[1] * edge1[1] - texEdge1[1] * edge2[1]) * det;
|
|
tangent[2] = (texEdge2[1] * edge1[2] - texEdge1[1] * edge2[2]) * det;
|
|
|
|
bitangent[0] = (-texEdge2[0] * edge1[0] + texEdge1[0] * edge2[0]) * det;
|
|
bitangent[1] = (-texEdge2[0] * edge1[1] + texEdge1[0] * edge2[1]) * det;
|
|
bitangent[2] = (-texEdge2[0] * edge1[2] + texEdge1[0] * edge2[2]) * det;
|
|
}
|
|
|
|
// Accumulate the tangents and bitangents.
|
|
|
|
pVertex0->tangent[0] += tangent[0];
|
|
pVertex0->tangent[1] += tangent[1];
|
|
pVertex0->tangent[2] += tangent[2];
|
|
pVertex0->bitangent[0] += bitangent[0];
|
|
pVertex0->bitangent[1] += bitangent[1];
|
|
pVertex0->bitangent[2] += bitangent[2];
|
|
|
|
pVertex1->tangent[0] += tangent[0];
|
|
pVertex1->tangent[1] += tangent[1];
|
|
pVertex1->tangent[2] += tangent[2];
|
|
pVertex1->bitangent[0] += bitangent[0];
|
|
pVertex1->bitangent[1] += bitangent[1];
|
|
pVertex1->bitangent[2] += bitangent[2];
|
|
|
|
pVertex2->tangent[0] += tangent[0];
|
|
pVertex2->tangent[1] += tangent[1];
|
|
pVertex2->tangent[2] += tangent[2];
|
|
pVertex2->bitangent[0] += bitangent[0];
|
|
pVertex2->bitangent[1] += bitangent[1];
|
|
pVertex2->bitangent[2] += bitangent[2];
|
|
}
|
|
|
|
// Orthogonalize and normalize the vertex tangents.
|
|
for (int i = 0; i < totalVertices; ++i)
|
|
{
|
|
pVertex0 = &m_vertexBuffer[i];
|
|
|
|
// Gram-Schmidt orthogonalize tangent with normal.
|
|
|
|
nDotT = pVertex0->normal[0] * pVertex0->tangent[0] +
|
|
pVertex0->normal[1] * pVertex0->tangent[1] +
|
|
pVertex0->normal[2] * pVertex0->tangent[2];
|
|
|
|
pVertex0->tangent[0] -= pVertex0->normal[0] * nDotT;
|
|
pVertex0->tangent[1] -= pVertex0->normal[1] * nDotT;
|
|
pVertex0->tangent[2] -= pVertex0->normal[2] * nDotT;
|
|
|
|
// Normalize the tangent.
|
|
|
|
length = 1.0f / sqrtf(pVertex0->tangent[0] * pVertex0->tangent[0] +
|
|
pVertex0->tangent[1] * pVertex0->tangent[1] +
|
|
pVertex0->tangent[2] * pVertex0->tangent[2]);
|
|
|
|
pVertex0->tangent[0] *= length;
|
|
pVertex0->tangent[1] *= length;
|
|
pVertex0->tangent[2] *= length;
|
|
|
|
// Calculate the handedness of the local tangent space.
|
|
// The bitangent vector is the cross product between the triangle face
|
|
// normal vector and the calculated tangent vector. The resulting
|
|
// bitangent vector should be the same as the bitangent vector
|
|
// calculated from the set of linear equations above. If they point in
|
|
// different directions then we need to invert the cross product
|
|
// calculated bitangent vector. We store this scalar multiplier in the
|
|
// tangent vector's 'w' component so that the correct bitangent vector
|
|
// can be generated in the normal mapping shader's vertex shader.
|
|
//
|
|
// Normal maps have a left handed coordinate system with the origin
|
|
// located at the top left of the normal map texture. The x coordinates
|
|
// run horizontally from left to right. The y coordinates run
|
|
// vertically from top to bottom. The z coordinates run out of the
|
|
// normal map texture towards the viewer. Our handedness calculations
|
|
// must take this fact into account as well so that the normal mapping
|
|
// shader's vertex shader will generate the correct bitangent vectors.
|
|
// Some normal map authoring tools such as Crazybump
|
|
// (http://www.crazybump.com/) includes options to allow you to control
|
|
// the orientation of the normal map normal's y-axis.
|
|
|
|
bitangent[0] = (pVertex0->normal[1] * pVertex0->tangent[2]) -
|
|
(pVertex0->normal[2] * pVertex0->tangent[1]);
|
|
bitangent[1] = (pVertex0->normal[2] * pVertex0->tangent[0]) -
|
|
(pVertex0->normal[0] * pVertex0->tangent[2]);
|
|
bitangent[2] = (pVertex0->normal[0] * pVertex0->tangent[1]) -
|
|
(pVertex0->normal[1] * pVertex0->tangent[0]);
|
|
|
|
bDotB = bitangent[0] * pVertex0->bitangent[0] +
|
|
bitangent[1] * pVertex0->bitangent[1] +
|
|
bitangent[2] * pVertex0->bitangent[2];
|
|
|
|
pVertex0->tangent[3] = (bDotB < 0.0f) ? 1.0f : -1.0f;
|
|
|
|
pVertex0->bitangent[0] = bitangent[0];
|
|
pVertex0->bitangent[1] = bitangent[1];
|
|
pVertex0->bitangent[2] = bitangent[2];
|
|
}
|
|
|
|
m_hasTangents = true;
|
|
}
|
|
|
|
void OBJModel::importGeometryFirstPass(FILE *pFile)
|
|
{
|
|
m_hasTextureCoords = false;
|
|
m_hasNormals = false;
|
|
|
|
m_numberOfVertexCoords = 0;
|
|
m_numberOfTextureCoords = 0;
|
|
m_numberOfNormals = 0;
|
|
m_numberOfTriangles = 0;
|
|
|
|
int v = 0;
|
|
int vt = 0;
|
|
int vn = 0;
|
|
char buffer[256] = {0};
|
|
std::string name;
|
|
|
|
boost::filesystem::path file_path;
|
|
|
|
while (fscanf(pFile, "%s", buffer) != EOF)
|
|
{
|
|
switch (buffer[0])
|
|
{
|
|
case 'f': // v, v//vn, v/vt, v/vt/vn.
|
|
fscanf(pFile, "%s", buffer);
|
|
|
|
if (strstr(buffer, "//")) // v//vn
|
|
{
|
|
sscanf(buffer, "%d//%d", &v, &vn);
|
|
fscanf(pFile, "%d//%d", &v, &vn);
|
|
fscanf(pFile, "%d//%d", &v, &vn);
|
|
++m_numberOfTriangles;
|
|
|
|
while (fscanf(pFile, "%d//%d", &v, &vn) > 0)
|
|
++m_numberOfTriangles;
|
|
}
|
|
else if (sscanf(buffer, "%d/%d/%d", &v, &vt, &vn) == 3) // v/vt/vn
|
|
{
|
|
fscanf(pFile, "%d/%d/%d", &v, &vt, &vn);
|
|
fscanf(pFile, "%d/%d/%d", &v, &vt, &vn);
|
|
++m_numberOfTriangles;
|
|
|
|
while (fscanf(pFile, "%d/%d/%d", &v, &vt, &vn) > 0)
|
|
++m_numberOfTriangles;
|
|
}
|
|
else if (sscanf(buffer, "%d/%d", &v, &vt) == 2) // v/vt
|
|
{
|
|
fscanf(pFile, "%d/%d", &v, &vt);
|
|
fscanf(pFile, "%d/%d", &v, &vt);
|
|
++m_numberOfTriangles;
|
|
|
|
while (fscanf(pFile, "%d/%d", &v, &vt) > 0)
|
|
++m_numberOfTriangles;
|
|
}
|
|
else // v
|
|
{
|
|
fscanf(pFile, "%d", &v);
|
|
fscanf(pFile, "%d", &v);
|
|
++m_numberOfTriangles;
|
|
|
|
while (fscanf(pFile, "%d", &v) > 0)
|
|
++m_numberOfTriangles;
|
|
}
|
|
break;
|
|
|
|
case 'm': // mtllib
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
file_path = m_directoryPath;
|
|
file_path /= buffer;
|
|
// LogMessage ("full mtl path = %s", file_path.string().c_str());
|
|
name = file_path.string();
|
|
importMaterials(name.c_str());
|
|
break;
|
|
|
|
case 'v': // v, vt, or vn
|
|
switch (buffer[1])
|
|
{
|
|
case '\0':
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
++m_numberOfVertexCoords;
|
|
break;
|
|
|
|
case 'n':
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
++m_numberOfNormals;
|
|
break;
|
|
|
|
case 't':
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
++m_numberOfTextureCoords;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_hasPositions = m_numberOfVertexCoords > 0;
|
|
m_hasNormals = m_numberOfNormals > 0;
|
|
m_hasTextureCoords = m_numberOfTextureCoords > 0;
|
|
|
|
// Allocate memory for the OBJ model data.
|
|
m_vertexCoords.resize(m_numberOfVertexCoords * 3);
|
|
m_textureCoords.resize(m_numberOfTextureCoords * 2);
|
|
m_normals.resize(m_numberOfNormals * 3);
|
|
m_indexBuffer.resize(m_numberOfTriangles * 3);
|
|
m_attributeBuffer.resize(m_numberOfTriangles);
|
|
|
|
// Define a default material if no materials were loaded.
|
|
if (m_numberOfMaterials == 0)
|
|
{
|
|
Material defaultMaterial =
|
|
{
|
|
{0.2f, 0.2f, 0.2f, 1.0f},
|
|
{0.8f, 0.8f, 0.8f, 1.0f},
|
|
{0.0f, 0.0f, 0.0f, 1.0f},
|
|
0.0f,
|
|
1.0f,
|
|
std::string("default"),
|
|
std::string(),
|
|
std::string()
|
|
};
|
|
|
|
m_materials.push_back(defaultMaterial);
|
|
m_materialCache[defaultMaterial.name] = 0;
|
|
}
|
|
}
|
|
|
|
void OBJModel::importGeometrySecondPass(FILE *pFile)
|
|
{
|
|
int v[3] = {0};
|
|
int vt[3] = {0};
|
|
int vn[3] = {0};
|
|
int numVertices = 0;
|
|
int numTexCoords = 0;
|
|
int numNormals = 0;
|
|
int numTriangles = 0;
|
|
int activeMaterial = 0;
|
|
char buffer[256] = {0};
|
|
std::string name;
|
|
std::map<std::string, int>::const_iterator iter;
|
|
|
|
while (fscanf(pFile, "%s", buffer) != EOF)
|
|
{
|
|
switch (buffer[0])
|
|
{
|
|
case 'f': // v, v//vn, v/vt, or v/vt/vn.
|
|
v[0] = v[1] = v[2] = 0;
|
|
vt[0] = vt[1] = vt[2] = 0;
|
|
vn[0] = vn[1] = vn[2] = 0;
|
|
|
|
fscanf(pFile, "%s", buffer);
|
|
|
|
if (strstr(buffer, "//")) // v//vn
|
|
{
|
|
sscanf(buffer, "%d//%d", &v[0], &vn[0]);
|
|
fscanf(pFile, "%d//%d", &v[1], &vn[1]);
|
|
fscanf(pFile, "%d//%d", &v[2], &vn[2]);
|
|
|
|
v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
|
|
v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
|
|
vn[0] = (vn[0] < 0) ? vn[0] + numNormals - 1 : vn[0] - 1;
|
|
vn[1] = (vn[1] < 0) ? vn[1] + numNormals - 1 : vn[1] - 1;
|
|
vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1;
|
|
|
|
addTrianglePosNormal(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vn[0], vn[1], vn[2]);
|
|
|
|
v[1] = v[2];
|
|
vn[1] = vn[2];
|
|
|
|
while (fscanf(pFile, "%d//%d", &v[2], &vn[2]) > 0)
|
|
{
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1;
|
|
|
|
addTrianglePosNormal(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vn[0], vn[1], vn[2]);
|
|
|
|
v[1] = v[2];
|
|
vn[1] = vn[2];
|
|
}
|
|
}
|
|
else if (sscanf(buffer, "%d/%d/%d", &v[0], &vt[0], &vn[0]) == 3) // v/vt/vn
|
|
{
|
|
fscanf(pFile, "%d/%d/%d", &v[1], &vt[1], &vn[1]);
|
|
fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]);
|
|
|
|
v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
|
|
v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
|
|
vt[0] = (vt[0] < 0) ? vt[0] + numTexCoords - 1 : vt[0] - 1;
|
|
vt[1] = (vt[1] < 0) ? vt[1] + numTexCoords - 1 : vt[1] - 1;
|
|
vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1;
|
|
|
|
vn[0] = (vn[0] < 0) ? vn[0] + numNormals - 1 : vn[0] - 1;
|
|
vn[1] = (vn[1] < 0) ? vn[1] + numNormals - 1 : vn[1] - 1;
|
|
vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1;
|
|
|
|
addTrianglePosTexCoordNormal(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vt[0], vt[1], vt[2], vn[0], vn[1], vn[2]);
|
|
|
|
v[1] = v[2];
|
|
vt[1] = vt[2];
|
|
vn[1] = vn[2];
|
|
|
|
while (fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]) > 0)
|
|
{
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1;
|
|
vn[2] = (vn[2] < 0) ? vn[2] + numNormals - 1 : vn[2] - 1;
|
|
|
|
addTrianglePosTexCoordNormal(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vt[0], vt[1], vt[2], vn[0], vn[1], vn[2]);
|
|
|
|
v[1] = v[2];
|
|
vt[1] = vt[2];
|
|
vn[1] = vn[2];
|
|
}
|
|
}
|
|
else if (sscanf(buffer, "%d/%d", &v[0], &vt[0]) == 2) // v/vt
|
|
{
|
|
fscanf(pFile, "%d/%d", &v[1], &vt[1]);
|
|
fscanf(pFile, "%d/%d", &v[2], &vt[2]);
|
|
|
|
v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
|
|
v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
|
|
vt[0] = (vt[0] < 0) ? vt[0] + numTexCoords - 1 : vt[0] - 1;
|
|
vt[1] = (vt[1] < 0) ? vt[1] + numTexCoords - 1 : vt[1] - 1;
|
|
vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1;
|
|
|
|
addTrianglePosTexCoord(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vt[0], vt[1], vt[2]);
|
|
|
|
v[1] = v[2];
|
|
vt[1] = vt[2];
|
|
|
|
while (fscanf(pFile, "%d/%d", &v[2], &vt[2]) > 0)
|
|
{
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
vt[2] = (vt[2] < 0) ? vt[2] + numTexCoords - 1 : vt[2] - 1;
|
|
|
|
addTrianglePosTexCoord(numTriangles++, activeMaterial,
|
|
v[0], v[1], v[2], vt[0], vt[1], vt[2]);
|
|
|
|
v[1] = v[2];
|
|
vt[1] = vt[2];
|
|
}
|
|
}
|
|
else // v
|
|
{
|
|
sscanf(buffer, "%d", &v[0]);
|
|
fscanf(pFile, "%d", &v[1]);
|
|
fscanf(pFile, "%d", &v[2]);
|
|
|
|
v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
|
|
v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
|
|
addTrianglePos(numTriangles++, activeMaterial, v[0], v[1], v[2]);
|
|
|
|
v[1] = v[2];
|
|
|
|
while (fscanf(pFile, "%d", &v[2]) > 0)
|
|
{
|
|
v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;
|
|
|
|
addTrianglePos(numTriangles++, activeMaterial, v[0], v[1], v[2]);
|
|
|
|
v[1] = v[2];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'u': // usemtl
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
name = buffer;
|
|
iter = m_materialCache.find(buffer);
|
|
activeMaterial = (iter == m_materialCache.end()) ? 0 : iter->second;
|
|
break;
|
|
|
|
case 'v': // v, vn, or vt.
|
|
switch (buffer[1])
|
|
{
|
|
case '\0': // v
|
|
fscanf(pFile, "%f %f %f",
|
|
&m_vertexCoords[3 * numVertices],
|
|
&m_vertexCoords[3 * numVertices + 1],
|
|
&m_vertexCoords[3 * numVertices + 2]);
|
|
++numVertices;
|
|
break;
|
|
|
|
case 'n': // vn
|
|
fscanf(pFile, "%f %f %f",
|
|
&m_normals[3 * numNormals],
|
|
&m_normals[3 * numNormals + 1],
|
|
&m_normals[3 * numNormals + 2]);
|
|
++numNormals;
|
|
break;
|
|
|
|
case 't': // vt
|
|
fscanf(pFile, "%f %f",
|
|
&m_textureCoords[2 * numTexCoords],
|
|
&m_textureCoords[2 * numTexCoords + 1]);
|
|
++numTexCoords;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool OBJModel::importMaterials(const char *pszFilename)
|
|
{
|
|
LogMessage ("Loading material %s", pszFilename);
|
|
FILE *pFile = fopen(pszFilename, "r");
|
|
|
|
if (!pFile)
|
|
return false;
|
|
|
|
Material *pMaterial = 0;
|
|
int illum = 0;
|
|
int numMaterials = 0;
|
|
char buffer[256] = {0};
|
|
|
|
// Count the number of materials in the MTL file.
|
|
while (fscanf(pFile, "%s", buffer) != EOF)
|
|
{
|
|
switch (buffer[0])
|
|
{
|
|
case 'n': // newmtl
|
|
++numMaterials;
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
rewind(pFile);
|
|
|
|
m_numberOfMaterials = numMaterials;
|
|
|
|
LogMessage ("file contains %d materials", m_numberOfMaterials);
|
|
|
|
numMaterials = 0;
|
|
m_materials.resize(m_numberOfMaterials);
|
|
boost::filesystem::path file_path;
|
|
|
|
// Load the materials in the MTL file.
|
|
while (fscanf(pFile, "%s", buffer) != EOF)
|
|
{
|
|
switch (buffer[0])
|
|
{
|
|
case 'N': // Ns
|
|
fscanf(pFile, "%f", &pMaterial->shininess);
|
|
|
|
// Wavefront .MTL file shininess is from [0,1000].
|
|
// Scale back to a generic [0,1] range.
|
|
pMaterial->shininess /= 1000.0f;
|
|
break;
|
|
|
|
case 'K': // Ka, Kd, or Ks
|
|
switch (buffer[1])
|
|
{
|
|
case 'a': // Ka
|
|
fscanf(pFile, "%f %f %f",
|
|
&pMaterial->ambient[0],
|
|
&pMaterial->ambient[1],
|
|
&pMaterial->ambient[2]);
|
|
pMaterial->ambient[3] = 1.0f;
|
|
break;
|
|
|
|
case 'd': // Kd
|
|
fscanf(pFile, "%f %f %f",
|
|
&pMaterial->diffuse[0],
|
|
&pMaterial->diffuse[1],
|
|
&pMaterial->diffuse[2]);
|
|
pMaterial->diffuse[3] = 1.0f;
|
|
break;
|
|
|
|
case 's': // Ks
|
|
fscanf(pFile, "%f %f %f",
|
|
&pMaterial->specular[0],
|
|
&pMaterial->specular[1],
|
|
&pMaterial->specular[2]);
|
|
pMaterial->specular[3] = 1.0f;
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'T': // Tr
|
|
switch (buffer[1])
|
|
{
|
|
case 'r': // Tr
|
|
fscanf(pFile, "%f", &pMaterial->alpha);
|
|
pMaterial->alpha = 1.0f - pMaterial->alpha;
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
fscanf(pFile, "%f", &pMaterial->alpha);
|
|
break;
|
|
|
|
case 'i': // illum
|
|
fscanf(pFile, "%d", &illum);
|
|
|
|
if (illum == 1)
|
|
{
|
|
pMaterial->specular[0] = 0.0f;
|
|
pMaterial->specular[1] = 0.0f;
|
|
pMaterial->specular[2] = 0.0f;
|
|
pMaterial->specular[3] = 1.0f;
|
|
}
|
|
break;
|
|
|
|
case 'm': // map_Kd, map_bump
|
|
if (strstr(buffer, "map_Kd") != 0)
|
|
{
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
file_path = m_directoryPath;
|
|
file_path /= buffer;
|
|
pMaterial->colorMapFilename = file_path.string();
|
|
LogMessage ("colorMapFilename = %s", pMaterial->colorMapFilename.c_str());
|
|
}
|
|
else if (strstr(buffer, "map_bump") != 0)
|
|
{
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
file_path = m_directoryPath;
|
|
file_path /= buffer;
|
|
pMaterial->bumpMapFilename = file_path.string();
|
|
LogMessage ("bumpMapFilename = %s", pMaterial->bumpMapFilename.c_str());
|
|
}
|
|
else
|
|
{
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
}
|
|
break;
|
|
|
|
case 'n': // newmtl
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
sscanf(buffer, "%s %s", buffer, buffer);
|
|
|
|
pMaterial = &m_materials[numMaterials];
|
|
pMaterial->ambient[0] = 0.2f;
|
|
pMaterial->ambient[1] = 0.2f;
|
|
pMaterial->ambient[2] = 0.2f;
|
|
pMaterial->ambient[3] = 1.0f;
|
|
pMaterial->diffuse[0] = 0.8f;
|
|
pMaterial->diffuse[1] = 0.8f;
|
|
pMaterial->diffuse[2] = 0.8f;
|
|
pMaterial->diffuse[3] = 1.0f;
|
|
pMaterial->specular[0] = 0.0f;
|
|
pMaterial->specular[1] = 0.0f;
|
|
pMaterial->specular[2] = 0.0f;
|
|
pMaterial->specular[3] = 1.0f;
|
|
pMaterial->shininess = 0.0f;
|
|
pMaterial->alpha = 1.0f;
|
|
pMaterial->name = buffer;
|
|
pMaterial->colorMapFilename.clear();
|
|
pMaterial->bumpMapFilename.clear();
|
|
|
|
m_materialCache[pMaterial->name] = numMaterials;
|
|
++numMaterials;
|
|
break;
|
|
|
|
default:
|
|
fgets(buffer, sizeof(buffer), pFile);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(pFile);
|
|
return true;
|
|
}
|
|
|
|
}
|