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