Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

3dsmesh.cpp

Go to the documentation of this file.
00001 // 3dsmesh.cpp
00002 // 
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <algorithm>
00011 #include <iostream>
00012 #include "gl.h"
00013 #ifndef MACOSX
00014 #include "glext.h"
00015 #endif /* MACOSX */
00016 #include "vertexprog.h"
00017 #include "texmanager.h"
00018 #include "3dsmesh.h"
00019 
00020 using namespace std;
00021 
00022 
00023 static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
00024                                        const M3DScene& scene,
00025                                        const string& texturePath);
00026 
00027 // Function to sort vertex lists so that transparent ones are rendered
00028 // after the opaque ones, and vertex lists with the same material properties
00029 // are grouped together.
00030 static int compareVertexLists(VertexList* vl0, VertexList* vl1)
00031 {
00032     float a0 = vl0->getDiffuseColor().alpha();
00033     float a1 = vl1->getDiffuseColor().alpha();
00034 
00035 #if _MSC_VER <= 1200
00036     // In some cases, sorting with this comparison function hangs on Celestia
00037     // executables built with MSVC.  For some reason, adding the following crud
00038     // fixes the problem, but I haven't looked at the generated assembly
00039     // instructions to figure out what's going on.  In any case, the output
00040     // should never be printed because alpha is always >= 0.  Blah.
00041     if (a0 == -50.0f)
00042         cout << "Stupid MSVC compiler bug workaround!  (This line will never be printed)\n";
00043 #endif
00044 
00045     if (a0 == a1)
00046     {
00047         return vl0->getTexture() < vl1->getTexture();
00048     }
00049     else
00050     {
00051         return (a0 > a1);
00052     }
00053 }
00054 
00055 Mesh3DS::Mesh3DS(const M3DScene& scene, const string& texturePath)
00056 {
00057     for (unsigned int i = 0; i < scene.getModelCount(); i++)
00058     {
00059         M3DModel* model = scene.getModel(i);
00060         if (model != NULL)
00061         {
00062             for (unsigned int j = 0; j < model->getTriMeshCount(); j++)
00063             {
00064                 M3DTriangleMesh* mesh = model->getTriMesh(j);
00065                 if (mesh != NULL)
00066                 {
00067                     vertexLists.insert(vertexLists.end(),
00068                                        convertToVertexList(*mesh, scene, texturePath));
00069                 }
00070             }
00071         }
00072     }
00073 
00074     // Sort the vertex lists to make sure that the transparent ones are
00075     // rendered after the opaque ones and material state changes are minimized.
00076     sort(vertexLists.begin(), vertexLists.end(), compareVertexLists);
00077 }
00078 
00079 
00080 Mesh3DS::~Mesh3DS()
00081 {
00082     for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
00083         if (*i != NULL)
00084             delete *i;
00085 }
00086 
00087 
00088 void Mesh3DS::render(float lod)
00089 {
00090     render(Normals | Colors, lod);
00091 }
00092 
00093 
00094 void Mesh3DS::render(unsigned int attributes, float)
00095 {
00096     TextureManager* textureManager = GetTextureManager();
00097     ResourceHandle currentTexture = InvalidResource;
00098     bool specularOn = false;
00099     bool blendOn = false;
00100     Color black(0.0f, 0.0f, 0.0f);
00101 
00102     int count = 0;
00103     for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
00104     {
00105         // Don't mess with the material, texture, blend function, etc. if the
00106         // multipass attribute is set--when the multipass flag is on, all this
00107         // state will have been set up by the caller in order to produce some
00108         // effect (e.g. shadows).
00109         if ((attributes & Multipass) == 0)
00110         {
00111             // Ugly hack to set the diffuse color parameters when vertex
00112             // programs are enabled.
00113             if (attributes & VertexProgParams)
00114                 vp::parameter(20, (*i)->getDiffuseColor());
00115 
00116             // All the vertex lists should have been sorted so that the
00117             // transparent ones are after the opaque ones.  Thus we can assume
00118             // that once we find a transparent vertext list, it's ok to leave
00119             // blending on.
00120             if (!blendOn && (*i)->getDiffuseColor().alpha() <= 254.0f / 255.0f)
00121             {
00122                 glEnable(GL_BLEND);
00123                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00124             }
00125 
00126             Color specular = (*i)->getSpecularColor();
00127             float shininess = (*i)->getShininess();
00128             ResourceHandle texture = (*i)->getTexture();
00129             bool useSpecular = (specular != black);
00130 
00131             if (specularOn && !useSpecular)
00132             {
00133                 float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
00134                 float zero = 0.0f;
00135                 glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
00136                 glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
00137             }
00138             if (useSpecular)
00139             {
00140                 float matSpecular[4] = { specular.red(), specular.green(),
00141                                              specular.blue(), 1.0f };
00142                 glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
00143                 glMaterialfv(GL_FRONT, GL_SHININESS, &shininess);
00144             }
00145             specularOn = useSpecular;
00146 
00147             if (currentTexture != texture)
00148             {
00149                 if (texture == InvalidResource)
00150                 {
00151                     glDisable(GL_TEXTURE_2D);
00152                 }
00153                 else
00154                 {
00155                     if (currentTexture == InvalidResource)
00156                         glEnable(GL_TEXTURE_2D);
00157                     Texture* t = textureManager->find(texture);
00158                     if (t != NULL)
00159                         t->bind();
00160                 }
00161                 currentTexture = texture;
00162             }
00163         }
00164         
00165         (*i)->render();
00166     }
00167 
00168     if (specularOn)
00169     {
00170         float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
00171         float zero = 0.0f;
00172         glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
00173         glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
00174     }
00175 
00176     if (blendOn)
00177     {
00178         glDisable(GL_BLEND);
00179         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
00180     }
00181 }
00182 
00183 void Mesh3DS::render(unsigned int attributes, const Frustum&, float lod)
00184 {
00185     render(attributes, lod);
00186 }
00187 
00188 
00189 bool Mesh3DS::pick(const Ray3d& r, double& distance)
00190 {
00191     double maxDistance = 1.0e30;
00192     double closest = maxDistance;
00193 
00194     for (VertexListVec::const_iterator iter = vertexLists.begin();
00195          iter != vertexLists.end(); iter++)
00196     {
00197         double d = maxDistance;
00198         if ((*iter)->pick(r, d) && d < closest)
00199             closest = d;
00200     }    
00201 
00202     if (closest != maxDistance)
00203     {
00204         distance = closest;
00205         return true;
00206     }
00207     else
00208     {
00209         return false;
00210     }
00211 }
00212 
00213 
00214 // Transform and scale the model so that it fits into an axis aligned bounding
00215 // box with corners at (1, 1, 1) and (-1, -1, -1)
00216 void Mesh3DS::normalize(const Vec3f& centerOffset)
00217 {
00218     AxisAlignedBox bbox;
00219 
00220     VertexListVec::iterator i;
00221     for (i = vertexLists.begin(); i != vertexLists.end(); i++)
00222         bbox.include((*i)->getBoundingBox());
00223 
00224     Point3f center = bbox.getCenter() + centerOffset;
00225     Vec3f extents = bbox.getExtents();
00226     float maxExtent = extents.x;
00227     if (extents.y > maxExtent)
00228         maxExtent = extents.y;
00229     if (extents.z > maxExtent)
00230         maxExtent = extents.z;
00231 
00232     for (i = vertexLists.begin(); i != vertexLists.end(); i++)
00233         (*i)->transform(Point3f(0, 0, 0) - center, 2.0f / maxExtent);
00234 }
00235 
00236 
00237 static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
00238                                        const M3DScene& scene,
00239                                        const string& texturePath)
00240 {
00241     int nFaces = mesh.getFaceCount();
00242     int nVertices = mesh.getVertexCount();
00243     int nTexCoords = mesh.getTexCoordCount();
00244     bool smooth = (mesh.getSmoothingGroupCount() == nFaces);
00245     int i;
00246 
00247     uint32 parts = VertexList::VertexNormal;
00248     if (nTexCoords == nVertices)
00249         parts |= VertexList::TexCoord0;
00250     VertexList* vl = new VertexList(parts);
00251     
00252     Vec3f* faceNormals = new Vec3f[nFaces];
00253     Vec3f* vertexNormals = new Vec3f[nFaces * 3];
00254     int* faceCounts = new int[nVertices];
00255     int** vertexFaces = new int*[nVertices];
00256 
00257     for (i = 0; i < nVertices; i++)
00258     {
00259         faceCounts[i] = 0;
00260         vertexFaces[i] = NULL;
00261     }
00262 
00263     // generate face normals
00264     for (i = 0; i < nFaces; i++)
00265     {
00266         uint16 v0, v1, v2;
00267         mesh.getFace(i, v0, v1, v2);
00268 
00269         faceCounts[v0]++;
00270         faceCounts[v1]++;
00271         faceCounts[v2]++;
00272 
00273         Point3f p0 = mesh.getVertex(v0);
00274         Point3f p1 = mesh.getVertex(v1);
00275         Point3f p2 = mesh.getVertex(v2);
00276         faceNormals[i] = cross(p1 - p0, p2 - p1);
00277         faceNormals[i].normalize();
00278     }
00279 
00280     if (!smooth && 0)
00281     {
00282         for (i = 0; i < nFaces; i++)
00283         {
00284             vertexNormals[i * 3] = faceNormals[i];
00285             vertexNormals[i * 3 + 1] = faceNormals[i];
00286             vertexNormals[i * 3 + 2] = faceNormals[i];
00287         }
00288     }
00289     else
00290     {
00291         // allocate space for vertex face indices
00292         for (i = 0; i < nVertices; i++)
00293         {
00294             vertexFaces[i] = new int[faceCounts[i] + 1];
00295             vertexFaces[i][0] = faceCounts[i];
00296         }
00297 
00298         for (i = 0; i < nFaces; i++)
00299         {
00300             uint16 v0, v1, v2;
00301             mesh.getFace(i, v0, v1, v2);
00302             vertexFaces[v0][faceCounts[v0]--] = i;
00303             vertexFaces[v1][faceCounts[v1]--] = i;
00304             vertexFaces[v2][faceCounts[v2]--] = i;
00305         }
00306 
00307         // average face normals to compute the vertex normals
00308         for (i = 0; i < nFaces; i++)
00309         {
00310             uint16 v0, v1, v2;
00311             mesh.getFace(i, v0, v1, v2);
00312             // uint32 smoothingGroups = mesh.getSmoothingGroups(i);
00313 
00314             int j;
00315             Vec3f v = Vec3f(0, 0, 0);
00316             for (j = 1; j <= vertexFaces[v0][0]; j++)
00317             {
00318                 int k = vertexFaces[v0][j];
00319                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
00320                 if (faceNormals[i] * faceNormals[k] > 0.5f)
00321                     v += faceNormals[k];
00322             }
00323             v.normalize();
00324             vertexNormals[i * 3] = v;
00325 
00326             v = Vec3f(0, 0, 0);
00327             for (j = 1; j <= vertexFaces[v1][0]; j++)
00328             {
00329                 int k = vertexFaces[v1][j];
00330                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
00331                 if (faceNormals[i] * faceNormals[k] > 0.5f)
00332                     v += faceNormals[k];
00333             }
00334             v.normalize();
00335             vertexNormals[i * 3 + 1] = v;
00336 
00337             v = Vec3f(0, 0, 0);
00338             for (j = 1; j <= vertexFaces[v2][0]; j++)
00339             {
00340                 int k = vertexFaces[v2][j];
00341                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
00342                 if (faceNormals[i] * faceNormals[k] > 0.5f)
00343                     v += faceNormals[k];
00344             }
00345             v.normalize();
00346             vertexNormals[i * 3 + 2] = v;
00347         }
00348     }
00349 
00350     // build the triangle list
00351     for (i = 0; i < nFaces; i++)
00352     {
00353         uint16 triVert[3];
00354         mesh.getFace(i, triVert[0], triVert[1], triVert[2]);
00355 
00356         for (int j = 0; j < 3; j++)
00357         {
00358             VertexList::Vertex v;
00359             v.point = mesh.getVertex(triVert[j]);
00360             v.normal = vertexNormals[i * 3 + j];
00361             if ((parts & VertexList::TexCoord0) != 0)
00362                 v.texCoords[0] = mesh.getTexCoord(triVert[j]);
00363             vl->addVertex(v);
00364         }
00365     }
00366 
00367     // Set the material properties
00368     {
00369         string materialName = mesh.getMaterialName();
00370         if (materialName.length() > 0)
00371         {
00372             int nMaterials = scene.getMaterialCount();
00373             for (i = 0; i < nMaterials; i++)
00374             {
00375                 M3DMaterial* material = scene.getMaterial(i);
00376                 if (materialName == material->getName())
00377                 {
00378                     M3DColor diffuse = material->getDiffuseColor();
00379                     vl->setDiffuseColor(Color(diffuse.red, diffuse.green, diffuse.blue, material->getOpacity()));
00380                     M3DColor specular = material->getSpecularColor();
00381                     vl->setSpecularColor(Color(specular.red, specular.green, specular.blue));
00382                     float shininess = material->getShininess();
00383                     
00384                     // Map the shininess from the 3DS file into the 0-128
00385                     // range that OpenGL uses for the specular exponent.
00386                     shininess = (float) pow(2, 10.0 * shininess);
00387                     if (shininess > 128.0f)
00388                         shininess = 128.0f;
00389                     vl->setShininess(128.0f);
00390 
00391                     if (material->getTextureMap() != "")
00392                     {
00393                         ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap(), texturePath, TextureInfo::WrapTexture));
00394                         vl->setTexture(tex);
00395                     }
00396 
00397                     break;
00398                 }
00399             }
00400         }
00401     }
00402 
00403     // clean up
00404     if (faceNormals != NULL)
00405         delete[] faceNormals;
00406     if (vertexNormals != NULL)
00407         delete[] vertexNormals;
00408     if (faceCounts != NULL)
00409         delete[] faceCounts;
00410     if (vertexFaces != NULL)
00411     {
00412         for (i = 0; i < nVertices; i++)
00413         {
00414             if (vertexFaces[i] != NULL)
00415                 delete[] vertexFaces[i];
00416         }
00417         delete[] vertexFaces;
00418     }
00419 
00420     return vl;
00421 }

Generated on Sat Jan 14 22:30:26 2006 for Celestia by  doxygen 1.4.1