1 // 3dsmesh.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 
10 #include <algorithm>
11 #include <iostream>
12 #include "gl.h"
13 #ifndef MACOSX
14 #include "glext.h"
15 #endif /* MACOSX */
16 #include "vertexprog.h"
17 #include "texmanager.h"
18 #include "3dsmesh.h"
19 
20 using namespace std;
21 
22 
23 static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
24                                        const M3DScene& scene,
25                                        const string& texturePath);
26 
27 // Function to sort vertex lists so that transparent ones are rendered
28 // after the opaque ones, and vertex lists with the same material properties
29 // are grouped together.
compareVertexLists(VertexList * vl0,VertexList * vl1)30 static int compareVertexLists(VertexList* vl0, VertexList* vl1)
31 {
32     float a0 = vl0->getDiffuseColor().alpha();
33     float a1 = vl1->getDiffuseColor().alpha();
34 
35 #if _MSC_VER <= 1200
36     // In some cases, sorting with this comparison function hangs on Celestia
37     // executables built with MSVC.  For some reason, adding the following crud
38     // fixes the problem, but I haven't looked at the generated assembly
39     // instructions to figure out what's going on.  In any case, the output
40     // should never be printed because alpha is always >= 0.  Blah.
41     if (a0 == -50.0f)
42         cout << "Stupid MSVC compiler bug workaround!  (This line will never be printed)\n";
43 #endif
44 
45     if (a0 == a1)
46     {
47         return vl0->getTexture() < vl1->getTexture();
48     }
49     else
50     {
51         return (a0 > a1);
52     }
53 }
54 
Mesh3DS(const M3DScene & scene,const string & texturePath)55 Mesh3DS::Mesh3DS(const M3DScene& scene, const string& texturePath)
56 {
57     for (unsigned int i = 0; i < scene.getModelCount(); i++)
58     {
59         M3DModel* model = scene.getModel(i);
60         if (model != NULL)
61         {
62             for (unsigned int j = 0; j < model->getTriMeshCount(); j++)
63             {
64                 M3DTriangleMesh* mesh = model->getTriMesh(j);
65                 if (mesh != NULL)
66                 {
67                     vertexLists.insert(vertexLists.end(),
68                                        convertToVertexList(*mesh, scene, texturePath));
69                 }
70             }
71         }
72     }
73 
74     // Sort the vertex lists to make sure that the transparent ones are
75     // rendered after the opaque ones and material state changes are minimized.
76     sort(vertexLists.begin(), vertexLists.end(), compareVertexLists);
77 }
78 
79 
~Mesh3DS()80 Mesh3DS::~Mesh3DS()
81 {
82     for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
83         if (*i != NULL)
84             delete *i;
85 }
86 
87 
render(float lod)88 void Mesh3DS::render(float lod)
89 {
90     render(Normals | Colors, lod);
91 }
92 
93 
render(unsigned int attributes,float)94 void Mesh3DS::render(unsigned int attributes, float)
95 {
96     TextureManager* textureManager = GetTextureManager();
97     ResourceHandle currentTexture = InvalidResource;
98     bool specularOn = false;
99     bool blendOn = false;
100     Color black(0.0f, 0.0f, 0.0f);
101 
102     int count = 0;
103     for (VertexListVec::iterator i = vertexLists.begin(); i != vertexLists.end(); i++)
104     {
105         // Don't mess with the material, texture, blend function, etc. if the
106         // multipass attribute is set--when the multipass flag is on, all this
107         // state will have been set up by the caller in order to produce some
108         // effect (e.g. shadows).
109         if ((attributes & Multipass) == 0)
110         {
111             // Ugly hack to set the diffuse color parameters when vertex
112             // programs are enabled.
113             if (attributes & VertexProgParams)
114                 vp::parameter(20, (*i)->getDiffuseColor());
115 
116             // All the vertex lists should have been sorted so that the
117             // transparent ones are after the opaque ones.  Thus we can assume
118             // that once we find a transparent vertext list, it's ok to leave
119             // blending on.
120             if (!blendOn && (*i)->getDiffuseColor().alpha() <= 254.0f / 255.0f)
121             {
122                 glEnable(GL_BLEND);
123                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
124             }
125 
126             Color specular = (*i)->getSpecularColor();
127             float shininess = (*i)->getShininess();
128             ResourceHandle texture = (*i)->getTexture();
129             bool useSpecular = (specular != black);
130 
131             if (specularOn && !useSpecular)
132             {
133                 float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
134                 float zero = 0.0f;
135                 glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
136                 glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
137             }
138             if (useSpecular)
139             {
140                 float matSpecular[4] = { specular.red(), specular.green(),
141                                              specular.blue(), 1.0f };
142                 glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
143                 glMaterialfv(GL_FRONT, GL_SHININESS, &shininess);
144             }
145             specularOn = useSpecular;
146 
147             if (currentTexture != texture)
148             {
149                 if (texture == InvalidResource)
150                 {
151                     glDisable(GL_TEXTURE_2D);
152                 }
153                 else
154                 {
155                     if (currentTexture == InvalidResource)
156                         glEnable(GL_TEXTURE_2D);
157                     Texture* t = textureManager->find(texture);
158                     if (t != NULL)
159                         t->bind();
160                 }
161                 currentTexture = texture;
162             }
163         }
164 
165         (*i)->render();
166     }
167 
168     if (specularOn)
169     {
170         float matSpecular[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
171         float zero = 0.0f;
172         glMaterialfv(GL_FRONT, GL_SPECULAR, matSpecular);
173         glMaterialfv(GL_FRONT, GL_SHININESS, &zero);
174     }
175 
176     if (blendOn)
177     {
178         glDisable(GL_BLEND);
179         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
180     }
181 }
182 
render(unsigned int attributes,const Frustum &,float lod)183 void Mesh3DS::render(unsigned int attributes, const Frustum&, float lod)
184 {
185     render(attributes, lod);
186 }
187 
188 
pick(const Ray3d & r,double & distance)189 bool Mesh3DS::pick(const Ray3d& r, double& distance)
190 {
191     double maxDistance = 1.0e30;
192     double closest = maxDistance;
193 
194     for (VertexListVec::const_iterator iter = vertexLists.begin();
195          iter != vertexLists.end(); iter++)
196     {
197         double d = maxDistance;
198         if ((*iter)->pick(r, d) && d < closest)
199             closest = d;
200     }
201 
202     if (closest != maxDistance)
203     {
204         distance = closest;
205         return true;
206     }
207     else
208     {
209         return false;
210     }
211 }
212 
213 
214 // Transform and scale the model so that it fits into an axis aligned bounding
215 // box with corners at (1, 1, 1) and (-1, -1, -1)
normalize(const Vec3f & centerOffset)216 void Mesh3DS::normalize(const Vec3f& centerOffset)
217 {
218     AxisAlignedBox bbox;
219 
220     VertexListVec::iterator i;
221     for (i = vertexLists.begin(); i != vertexLists.end(); i++)
222         bbox.include((*i)->getBoundingBox());
223 
224     Point3f center = bbox.getCenter() + centerOffset;
225     Vec3f extents = bbox.getExtents();
226     float maxExtent = extents.x;
227     if (extents.y > maxExtent)
228         maxExtent = extents.y;
229     if (extents.z > maxExtent)
230         maxExtent = extents.z;
231 
232     for (i = vertexLists.begin(); i != vertexLists.end(); i++)
233         (*i)->transform(Point3f(0, 0, 0) - center, 2.0f / maxExtent);
234 }
235 
236 
convertToVertexList(M3DTriangleMesh & mesh,const M3DScene & scene,const string & texturePath)237 static VertexList* convertToVertexList(M3DTriangleMesh& mesh,
238                                        const M3DScene& scene,
239                                        const string& texturePath)
240 {
241     int nFaces = mesh.getFaceCount();
242     int nVertices = mesh.getVertexCount();
243     int nTexCoords = mesh.getTexCoordCount();
244     bool smooth = (mesh.getSmoothingGroupCount() == nFaces);
245     int i;
246 
247     uint32 parts = VertexList::VertexNormal;
248     if (nTexCoords == nVertices)
249         parts |= VertexList::TexCoord0;
250     VertexList* vl = new VertexList(parts);
251 
252     Vec3f* faceNormals = new Vec3f[nFaces];
253     Vec3f* vertexNormals = new Vec3f[nFaces * 3];
254     int* faceCounts = new int[nVertices];
255     int** vertexFaces = new int*[nVertices];
256 
257     for (i = 0; i < nVertices; i++)
258     {
259         faceCounts[i] = 0;
260         vertexFaces[i] = NULL;
261     }
262 
263     // generate face normals
264     for (i = 0; i < nFaces; i++)
265     {
266         uint16 v0, v1, v2;
267         mesh.getFace(i, v0, v1, v2);
268 
269         faceCounts[v0]++;
270         faceCounts[v1]++;
271         faceCounts[v2]++;
272 
273         Point3f p0 = mesh.getVertex(v0);
274         Point3f p1 = mesh.getVertex(v1);
275         Point3f p2 = mesh.getVertex(v2);
276         faceNormals[i] = cross(p1 - p0, p2 - p1);
277         faceNormals[i].normalize();
278     }
279 
280     if (!smooth && 0)
281     {
282         for (i = 0; i < nFaces; i++)
283         {
284             vertexNormals[i * 3] = faceNormals[i];
285             vertexNormals[i * 3 + 1] = faceNormals[i];
286             vertexNormals[i * 3 + 2] = faceNormals[i];
287         }
288     }
289     else
290     {
291         // allocate space for vertex face indices
292         for (i = 0; i < nVertices; i++)
293         {
294             vertexFaces[i] = new int[faceCounts[i] + 1];
295             vertexFaces[i][0] = faceCounts[i];
296         }
297 
298         for (i = 0; i < nFaces; i++)
299         {
300             uint16 v0, v1, v2;
301             mesh.getFace(i, v0, v1, v2);
302             vertexFaces[v0][faceCounts[v0]--] = i;
303             vertexFaces[v1][faceCounts[v1]--] = i;
304             vertexFaces[v2][faceCounts[v2]--] = i;
305         }
306 
307         // average face normals to compute the vertex normals
308         for (i = 0; i < nFaces; i++)
309         {
310             uint16 v0, v1, v2;
311             mesh.getFace(i, v0, v1, v2);
312             // uint32 smoothingGroups = mesh.getSmoothingGroups(i);
313 
314             int j;
315             Vec3f v = Vec3f(0, 0, 0);
316             for (j = 1; j <= vertexFaces[v0][0]; j++)
317             {
318                 int k = vertexFaces[v0][j];
319                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
320                 if (faceNormals[i] * faceNormals[k] > 0.5f)
321                     v += faceNormals[k];
322             }
323             v.normalize();
324             vertexNormals[i * 3] = v;
325 
326             v = Vec3f(0, 0, 0);
327             for (j = 1; j <= vertexFaces[v1][0]; j++)
328             {
329                 int k = vertexFaces[v1][j];
330                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
331                 if (faceNormals[i] * faceNormals[k] > 0.5f)
332                     v += faceNormals[k];
333             }
334             v.normalize();
335             vertexNormals[i * 3 + 1] = v;
336 
337             v = Vec3f(0, 0, 0);
338             for (j = 1; j <= vertexFaces[v2][0]; j++)
339             {
340                 int k = vertexFaces[v2][j];
341                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
342                 if (faceNormals[i] * faceNormals[k] > 0.5f)
343                     v += faceNormals[k];
344             }
345             v.normalize();
346             vertexNormals[i * 3 + 2] = v;
347         }
348     }
349 
350     // build the triangle list
351     for (i = 0; i < nFaces; i++)
352     {
353         uint16 triVert[3];
354         mesh.getFace(i, triVert[0], triVert[1], triVert[2]);
355 
356         for (int j = 0; j < 3; j++)
357         {
358             VertexList::Vertex v;
359             v.point = mesh.getVertex(triVert[j]);
360             v.normal = vertexNormals[i * 3 + j];
361             if ((parts & VertexList::TexCoord0) != 0)
362                 v.texCoords[0] = mesh.getTexCoord(triVert[j]);
363             vl->addVertex(v);
364         }
365     }
366 
367     // Set the material properties
368     {
369         string materialName = mesh.getMaterialName();
370         if (materialName.length() > 0)
371         {
372             int nMaterials = scene.getMaterialCount();
373             for (i = 0; i < nMaterials; i++)
374             {
375                 M3DMaterial* material = scene.getMaterial(i);
376                 if (materialName == material->getName())
377                 {
378                     M3DColor diffuse = material->getDiffuseColor();
379                     vl->setDiffuseColor(Color(diffuse.red, diffuse.green, diffuse.blue, material->getOpacity()));
380                     M3DColor specular = material->getSpecularColor();
381                     vl->setSpecularColor(Color(specular.red, specular.green, specular.blue));
382                     float shininess = material->getShininess();
383 
384                     // Map the shininess from the 3DS file into the 0-128
385                     // range that OpenGL uses for the specular exponent.
386                     shininess = (float) pow(2, 10.0 * shininess);
387                     if (shininess > 128.0f)
388                         shininess = 128.0f;
389                     vl->setShininess(128.0f);
390 
391                     if (material->getTextureMap() != "")
392                     {
393                         ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap(), texturePath, TextureInfo::WrapTexture));
394                         vl->setTexture(tex);
395                     }
396 
397                     break;
398                 }
399             }
400         }
401     }
402 
403     // clean up
404     if (faceNormals != NULL)
405         delete[] faceNormals;
406     if (vertexNormals != NULL)
407         delete[] vertexNormals;
408     if (faceCounts != NULL)
409         delete[] faceCounts;
410     if (vertexFaces != NULL)
411     {
412         for (i = 0; i < nVertices; i++)
413         {
414             if (vertexFaces[i] != NULL)
415                 delete[] vertexFaces[i];
416         }
417         delete[] vertexFaces;
418     }
419 
420     return vl;
421 }
422