celestia/src/celengine/3dsmesh.cpp

334 lines
10 KiB
C++

// 3dsmesh.cpp
//
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#include <iostream.h>
#include "gl.h"
#include "glext.h"
#include "vertexprog.h"
#include "texmanager.h"
#include "3dsmesh.h"
using namespace std;
static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
const M3DScene& scene);
Mesh3DS::Mesh3DS(const M3DScene& scene)
{
for (unsigned int i = 0; i < scene.getModelCount(); i++)
{
M3DModel* model = scene.getModel(i);
if (model != NULL)
{
for (unsigned int j = 0; j < model->getTriMeshCount(); j++)
{
M3DTriangleMesh* mesh = model->getTriMesh(j);
if (mesh != NULL)
{
vertexLists.insert(vertexLists.end(),
convertToVertexList(*mesh, scene));
}
}
}
}
}
Mesh3DS::~Mesh3DS()
{
for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
if (*i != NULL)
delete *i;
}
void Mesh3DS::render(float lod)
{
render(Normals | Colors, lod);
}
void Mesh3DS::render(unsigned int attributes, float)
{
TextureManager* textureManager = GetTextureManager();
ResourceHandle currentTexture = InvalidResource;
bool specularOn = false;
Color black(0.0f, 0.0f, 0.0f);
for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
{
// Ugly hack to set the diffuse color parameters when vertex programs
// are enabled.
if (attributes & VertexProgParams)
vp::parameter(20, (*i)->getDiffuseColor());
Color specular = (*i)->getSpecularColor();
float shininess = (*i)->getShininess();
ResourceHandle texture = (*i)->getTexture();
bool useSpecular = (specular != black);
if (specularOn && !useSpecular)
{
float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
float zero = 0.0f;
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
}
if (useSpecular)
{
float matSpecular[4] = { specular.red(), specular.green(),
specular.blue(), 1.0f };
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
glMaterialfv(GL_FRONT, GL_SHININESS, &shininess);
}
specularOn = useSpecular;
if (currentTexture != texture)
{
if (texture == InvalidResource)
{
glDisable(GL_TEXTURE_2D);
}
else
{
if (currentTexture == InvalidResource)
glEnable(GL_TEXTURE_2D);
Texture* t = textureManager->find(texture);
if (t != NULL)
t->bind();
}
currentTexture = texture;
}
(*i)->render();
}
if (specularOn)
{
float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
float zero = 0.0f;
glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
}
}
void Mesh3DS::render(unsigned int attributes, const Frustum&, float lod)
{
render(attributes, lod);
}
// Transform and scale the model so that it fits into an axis aligned bounding
// box with corners at (1, 1, 1) and (-1, -1, -1)
void Mesh3DS::normalize()
{
AxisAlignedBox bbox;
VertexListVec::iterator i;
for (i = vertexLists.begin(); i != vertexLists.end(); i++)
bbox.include((*i)->getBoundingBox());
Point3f center = bbox.getCenter();
Vec3f extents = bbox.getExtents();
float maxExtent = extents.x;
if (extents.y > maxExtent)
maxExtent = extents.y;
if (extents.z > maxExtent)
maxExtent = extents.z;
for (i = vertexLists.begin(); i != vertexLists.end(); i++)
(*i)->transform(Point3f(0, 0, 0) - center, 2.0f / maxExtent);
}
static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
const M3DScene& scene)
{
int nFaces = mesh.getFaceCount();
int nVertices = mesh.getVertexCount();
int nTexCoords = mesh.getTexCoordCount();
bool smooth = (mesh.getSmoothingGroupCount() == nFaces);
int i;
uint32 parts = VertexList::VertexNormal;
if (nTexCoords == nVertices)
parts |= VertexList::TexCoord0;
VertexList* vl = new VertexList(parts);
Vec3f* faceNormals = new Vec3f[nFaces];
Vec3f* vertexNormals = new Vec3f[nFaces * 3];
int* faceCounts = new int[nVertices];
int** vertexFaces = new int*[nVertices];
for (i = 0; i < nVertices; i++)
{
faceCounts[i] = 0;
vertexFaces[i] = NULL;
}
// generate face normals
for (i = 0; i < nFaces; i++)
{
uint16 v0, v1, v2;
mesh.getFace(i, v0, v1, v2);
faceCounts[v0]++;
faceCounts[v1]++;
faceCounts[v2]++;
Point3f p0 = mesh.getVertex(v0);
Point3f p1 = mesh.getVertex(v1);
Point3f p2 = mesh.getVertex(v2);
faceNormals[i] = cross(p1 - p0, p2 - p1);
faceNormals[i].normalize();
}
if (!smooth && 0)
{
for (i = 0; i < nFaces; i++)
{
vertexNormals[i * 3] = faceNormals[i];
vertexNormals[i * 3 + 1] = faceNormals[i];
vertexNormals[i * 3 + 2] = faceNormals[i];
}
}
else
{
// allocate space for vertex face indices
for (i = 0; i < nVertices; i++)
{
vertexFaces[i] = new int[faceCounts[i] + 1];
vertexFaces[i][0] = faceCounts[i];
}
for (i = 0; i < nFaces; i++)
{
uint16 v0, v1, v2;
mesh.getFace(i, v0, v1, v2);
vertexFaces[v0][faceCounts[v0]--] = i;
vertexFaces[v1][faceCounts[v1]--] = i;
vertexFaces[v2][faceCounts[v2]--] = i;
}
// average face normals to compute the vertex normals
for (i = 0; i < nFaces; i++)
{
uint16 v0, v1, v2;
mesh.getFace(i, v0, v1, v2);
// uint32 smoothingGroups = mesh.getSmoothingGroups(i);
int j;
Vec3f v = Vec3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v0][0]; j++)
{
int k = vertexFaces[v0][j];
// if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
if (faceNormals[i] * faceNormals[k] > 0.5f)
v += faceNormals[k];
}
v.normalize();
vertexNormals[i * 3] = v;
v = Vec3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v1][0]; j++)
{
int k = vertexFaces[v1][j];
// if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
if (faceNormals[i] * faceNormals[k] > 0.5f)
v += faceNormals[k];
}
v.normalize();
vertexNormals[i * 3 + 1] = v;
v = Vec3f(0, 0, 0);
for (j = 1; j <= vertexFaces[v2][0]; j++)
{
int k = vertexFaces[v2][j];
// if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
if (faceNormals[i] * faceNormals[k] > 0.5f)
v += faceNormals[k];
}
v.normalize();
vertexNormals[i * 3 + 2] = v;
}
}
// build the triangle list
for (i = 0; i < nFaces; i++)
{
uint16 triVert[3];
mesh.getFace(i, triVert[0], triVert[1], triVert[2]);
for (int j = 0; j < 3; j++)
{
VertexList::Vertex v;
v.point = mesh.getVertex(triVert[j]);
v.normal = vertexNormals[i * 3 + j];
if ((parts & VertexList::TexCoord0) != 0)
v.texCoords[0] = mesh.getTexCoord(triVert[j]);
vl->addVertex(v);
}
}
// Set the material properties
{
string materialName = mesh.getMaterialName();
if (materialName.length() > 0)
{
int nMaterials = scene.getMaterialCount();
for (i = 0; i < nMaterials; i++)
{
M3DMaterial* material = scene.getMaterial(i);
if (materialName == material->getName())
{
M3DColor diffuse = material->getDiffuseColor();
vl->setDiffuseColor(Color(diffuse.red, diffuse.green, diffuse.blue));
M3DColor specular = material->getSpecularColor();
vl->setSpecularColor(Color(specular.red, specular.green, specular.blue));
float shininess = material->getShininess();
// Map the shininess from the 3DS file into the 0-128
// range that OpenGL uses for the specular exponent.
shininess = (float) pow(2, 10.0 * shininess);
if (shininess > 128.0f)
shininess = 128.0f;
vl->setShininess(128.0f);
if (material->getTextureMap() != "")
{
ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap()));
vl->setTexture(tex);
}
break;
}
}
}
}
// clean up
if (faceNormals != NULL)
delete[] faceNormals;
if (vertexNormals != NULL)
delete[] vertexNormals;
if (faceCounts != NULL)
delete[] faceCounts;
if (vertexFaces != NULL)
{
for (i = 0; i < nVertices; i++)
{
if (vertexFaces[i] != NULL)
delete[] vertexFaces[i];
}
delete[] vertexFaces;
}
return vl;
}