1 // meshmanager.cpp
2 //
3 // Copyright (C) 2001-2006 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 // Experimental particle system support
11 #define PARTICLE_SYSTEM 0
12 
13 #include <iostream>
14 #include <fstream>
15 #include <cassert>
16 
17 #include "celestia.h"
18 #include <celutil/debug.h>
19 #include <celutil/filetype.h>
20 #include <celutil/util.h>
21 #include <celmath/mathlib.h>
22 #include <celmath/perlin.h>
23 #include <cel3ds/3dsread.h>
24 
25 #include "modelfile.h"
26 #if PARTICLE_SYSTEM
27 #include "particlesystem.h"
28 #include "particlesystemfile.h"
29 #endif
30 #include "vertexlist.h"
31 #include "parser.h"
32 #include "spheremesh.h"
33 #include "texmanager.h"
34 #include "meshmanager.h"
35 
36 using namespace std;
37 
38 
39 static Model* LoadCelestiaMesh(const string& filename);
40 static Model* Convert3DSModel(const M3DScene& scene, const string& texPath);
41 
42 static GeometryManager* geometryManager = NULL;
43 
44 static const char UniqueSuffixChar = '!';
45 
46 
GetGeometryManager()47 GeometryManager* GetGeometryManager()
48 {
49     if (geometryManager == NULL)
50         geometryManager = new GeometryManager("models");
51     return geometryManager;
52 }
53 
54 
resolve(const string & baseDir)55 string GeometryInfo::resolve(const string& baseDir)
56 {
57     // Ensure that models with different centers get resolved to different objects by
58     // adding a 'uniquifying' suffix to the filename that encodes the center value.
59     // This suffix is stripped before the file is actually loaded.
60     char uniquifyingSuffix[128];
61     sprintf(uniquifyingSuffix, "%c%f,%f,%f,%f,%d", UniqueSuffixChar, center.x, center.y, center.z, scale, (int) isNormalized);
62 
63     if (!path.empty())
64     {
65         string filename = path + "/models/" + source;
66         ifstream in(filename.c_str());
67         if (in.good())
68         {
69             resolvedToPath = true;
70             return filename + uniquifyingSuffix;
71         }
72     }
73 
74     return baseDir + "/" + source + uniquifyingSuffix;
75 }
76 
77 
load(const string & resolvedFilename)78 Geometry* GeometryInfo::load(const string& resolvedFilename)
79 {
80     // Strip off the uniquifying suffix
81     string::size_type uniquifyingSuffixStart = resolvedFilename.rfind(UniqueSuffixChar);
82     string filename(resolvedFilename, 0, uniquifyingSuffixStart);
83 
84     clog << _("Loading model: ") << filename << '\n';
85     Model* model = NULL;
86     ContentType fileType = DetermineFileType(filename);
87 
88     if (fileType == Content_3DStudio)
89     {
90         M3DScene* scene = Read3DSFile(filename);
91         if (scene != NULL)
92         {
93             if (resolvedToPath)
94                 model = Convert3DSModel(*scene, path);
95             else
96                 model = Convert3DSModel(*scene, "");
97 
98             if (isNormalized)
99                 model->normalize(center);
100             else
101                 model->transform(center, scale);
102 
103             delete scene;
104         }
105     }
106     else if (fileType == Content_CelestiaModel)
107     {
108         ifstream in(filename.c_str(), ios::binary);
109         if (in.good())
110         {
111             model = LoadModel(in, path);
112             if (model != NULL)
113             {
114                 if (isNormalized)
115                     model->normalize(center);
116                 else
117                     model->transform(center, scale);
118             }
119         }
120     }
121     else if (fileType == Content_CelestiaMesh)
122     {
123         model = LoadCelestiaMesh(filename);
124         if (model != NULL)
125         {
126             if (isNormalized)
127                 model->normalize(center);
128             else
129                 model->transform(center, scale);
130         }
131     }
132 #if PARTICLE_SYSTEM
133     else if (fileType == Content_CelestiaParticleSystem)
134     {
135         ifstream in(filename.c_str());
136         if (in.good())
137         {
138             return LoadParticleSystem(in, path);
139         }
140     }
141 #endif
142 
143     // Condition the model for optimal rendering
144     if (model != NULL)
145     {
146         // Many models tend to have a lot of duplicate materials; eliminate
147         // them, since unnecessarily setting material parameters can adversely
148         // impact rendering performance. Ideally uniquification of materials
149         // would be performed just once when the model was created, but
150         // that's not the case.
151         uint32 originalMaterialCount = model->getMaterialCount();
152         model->uniquifyMaterials();
153 
154         // Sort the submeshes roughly by opacity.  This will eliminate a
155         // good number of the errors caused when translucent triangles are
156         // rendered before geometry that they cover.
157         model->sortMeshes(Model::OpacityComparator());
158 
159         model->determineOpacity();
160 
161         // Display some statics for the model
162         clog << _("   Model statistics: ")
163              << model->getVertexCount() << _(" vertices, ")
164              << model->getPrimitiveCount() << _(" primitives, ")
165              << originalMaterialCount << _(" materials ")
166              << "(" << model->getMaterialCount() << _(" unique)\n");
167     }
168     else
169     {
170         cerr << _("Error loading model '") << filename << "'\n";
171     }
172 
173     return model;
174 }
175 
176 
177 struct NoiseMeshParameters
178 {
179     Vec3f size;
180     Vec3f offset;
181     float featureHeight;
182     float octaves;
183     float slices;
184     float rings;
185 };
186 
187 
NoiseDisplacementFunc(float u,float v,void * info)188 static float NoiseDisplacementFunc(float u, float v, void* info)
189 {
190     float theta = u * (float) PI * 2;
191     float phi = (v - 0.5f) * (float) PI;
192     float x = (float) (cos(phi) * cos(theta));
193     float y = (float) sin(phi);
194     float z = (float) (cos(phi) * sin(theta));
195 
196     // assert(info != NULL);
197     NoiseMeshParameters* params = (NoiseMeshParameters*) info;
198 
199     return fractalsum(Point3f(x, y, z) + params->offset,
200                       params->octaves) * params->featureHeight;
201 }
202 
203 
204 // TODO: The Celestia mesh format is deprecated
LoadCelestiaMesh(const string & filename)205 Model* LoadCelestiaMesh(const string& filename)
206 {
207     ifstream meshFile(filename.c_str(), ios::in);
208     if (!meshFile.good())
209     {
210         DPRINTF(0, "Error opening mesh file: %s\n", filename.c_str());
211         return NULL;
212     }
213 
214     Tokenizer tokenizer(&meshFile);
215     Parser parser(&tokenizer);
216 
217     if (tokenizer.nextToken() != Tokenizer::TokenName)
218     {
219         DPRINTF(0, "Mesh file %s is invalid.\n", filename.c_str());
220         return NULL;
221     }
222 
223     if (tokenizer.getStringValue() != "SphereDisplacementMesh")
224     {
225         DPRINTF(0, "%s: Unrecognized mesh type %s.\n",
226                 filename.c_str(),
227                 tokenizer.getStringValue().c_str());
228         return NULL;
229     }
230 
231     Value* meshDefValue = parser.readValue();
232     if (meshDefValue == NULL)
233     {
234         DPRINTF(0, "%s: Bad mesh file.\n", filename.c_str());
235         return NULL;
236     }
237 
238     if (meshDefValue->getType() != Value::HashType)
239     {
240         DPRINTF(0, "%s: Bad mesh file.\n", filename.c_str());
241         delete meshDefValue;
242         return NULL;
243     }
244 
245     Hash* meshDef = meshDefValue->getHash();
246 
247     NoiseMeshParameters params;
248 
249     params.size = Vec3f(1, 1, 1);
250     params.offset = Vec3f(10, 10, 10);
251     params.featureHeight = 0.0f;
252     params.octaves = 1;
253     params.slices = 20;
254     params.rings = 20;
255 
256     meshDef->getVector("Size", params.size);
257     meshDef->getVector("NoiseOffset", params.offset);
258     meshDef->getNumber("FeatureHeight", params.featureHeight);
259     meshDef->getNumber("Octaves", params.octaves);
260     meshDef->getNumber("Slices", params.slices);
261     meshDef->getNumber("Rings", params.rings);
262 
263     delete meshDefValue;
264 
265     Model* model = new Model();
266     SphereMesh* sphereMesh = new SphereMesh(params.size,
267                                             (int) params.rings, (int) params.slices,
268                                             NoiseDisplacementFunc,
269                                             (void*) &params);
270     if (sphereMesh != NULL)
271     {
272         Mesh* mesh = sphereMesh->convertToMesh();
273         model->addMesh(mesh);
274         delete sphereMesh;
275     }
276 
277     return model;
278 }
279 
280 
ConvertToVertexList(M3DTriangleMesh & mesh,const M3DScene & scene,const string & texturePath)281 static VertexList* ConvertToVertexList(M3DTriangleMesh& mesh,
282                                        const M3DScene& scene,
283                                        const string& texturePath)
284 {
285     int nFaces = mesh.getFaceCount();
286     int nVertices = mesh.getVertexCount();
287     int nTexCoords = mesh.getTexCoordCount();
288     bool smooth = (mesh.getSmoothingGroupCount() == nFaces);
289     int i;
290 
291     uint32 parts = VertexList::VertexNormal;
292     if (nTexCoords >= nVertices)
293         parts |= VertexList::TexCoord0;
294     VertexList* vl = new VertexList(parts);
295 
296     Vec3f* faceNormals = new Vec3f[nFaces];
297     Vec3f* vertexNormals = new Vec3f[nFaces * 3];
298     int* faceCounts = new int[nVertices];
299     int** vertexFaces = new int*[nVertices];
300 
301     for (i = 0; i < nVertices; i++)
302     {
303         faceCounts[i] = 0;
304         vertexFaces[i] = NULL;
305     }
306 
307     // generate face normals
308     for (i = 0; i < nFaces; i++)
309     {
310         uint16 v0, v1, v2;
311         mesh.getFace(i, v0, v1, v2);
312 
313         faceCounts[v0]++;
314         faceCounts[v1]++;
315         faceCounts[v2]++;
316 
317         Point3f p0 = mesh.getVertex(v0);
318         Point3f p1 = mesh.getVertex(v1);
319         Point3f p2 = mesh.getVertex(v2);
320         faceNormals[i] = cross(p1 - p0, p2 - p1);
321         faceNormals[i].normalize();
322     }
323 
324     if (!smooth && 0)
325     {
326         for (i = 0; i < nFaces; i++)
327         {
328             vertexNormals[i * 3] = faceNormals[i];
329             vertexNormals[i * 3 + 1] = faceNormals[i];
330             vertexNormals[i * 3 + 2] = faceNormals[i];
331         }
332     }
333     else
334     {
335         // allocate space for vertex face indices
336         for (i = 0; i < nVertices; i++)
337         {
338             vertexFaces[i] = new int[faceCounts[i] + 1];
339             vertexFaces[i][0] = faceCounts[i];
340         }
341 
342         for (i = 0; i < nFaces; i++)
343         {
344             uint16 v0, v1, v2;
345             mesh.getFace(i, v0, v1, v2);
346             vertexFaces[v0][faceCounts[v0]--] = i;
347             vertexFaces[v1][faceCounts[v1]--] = i;
348             vertexFaces[v2][faceCounts[v2]--] = i;
349         }
350 
351         // average face normals to compute the vertex normals
352         for (i = 0; i < nFaces; i++)
353         {
354             uint16 v0, v1, v2;
355             mesh.getFace(i, v0, v1, v2);
356             // uint32 smoothingGroups = mesh.getSmoothingGroups(i);
357 
358             int j;
359             Vec3f v = Vec3f(0, 0, 0);
360             for (j = 1; j <= vertexFaces[v0][0]; j++)
361             {
362                 int k = vertexFaces[v0][j];
363                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
364                 if (faceNormals[i] * faceNormals[k] > 0.5f)
365                     v += faceNormals[k];
366             }
367             v.normalize();
368             vertexNormals[i * 3] = v;
369 
370             v = Vec3f(0, 0, 0);
371             for (j = 1; j <= vertexFaces[v1][0]; j++)
372             {
373                 int k = vertexFaces[v1][j];
374                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
375                 if (faceNormals[i] * faceNormals[k] > 0.5f)
376                     v += faceNormals[k];
377             }
378             v.normalize();
379             vertexNormals[i * 3 + 1] = v;
380 
381             v = Vec3f(0, 0, 0);
382             for (j = 1; j <= vertexFaces[v2][0]; j++)
383             {
384                 int k = vertexFaces[v2][j];
385                 // if (k == i || (smoothingGroups & mesh.getSmoothingGroups(k)) != 0)
386                 if (faceNormals[i] * faceNormals[k] > 0.5f)
387                     v += faceNormals[k];
388             }
389             v.normalize();
390             vertexNormals[i * 3 + 2] = v;
391         }
392     }
393 
394     // build the triangle list
395     for (i = 0; i < nFaces; i++)
396     {
397         uint16 triVert[3];
398         mesh.getFace(i, triVert[0], triVert[1], triVert[2]);
399 
400         for (int j = 0; j < 3; j++)
401         {
402             VertexList::Vertex v;
403             v.point = mesh.getVertex(triVert[j]);
404             v.normal = vertexNormals[i * 3 + j];
405             if ((parts & VertexList::TexCoord0) != 0)
406                 v.texCoords[0] = mesh.getTexCoord(triVert[j]);
407             vl->addVertex(v);
408         }
409     }
410 
411     // Set the material properties
412     {
413         string materialName = mesh.getMaterialName();
414         if (materialName.length() > 0)
415         {
416             int nMaterials = scene.getMaterialCount();
417             for (i = 0; i < nMaterials; i++)
418             {
419                 M3DMaterial* material = scene.getMaterial(i);
420                 if (materialName == material->getName())
421                 {
422                     M3DColor diffuse = material->getDiffuseColor();
423                     vl->setDiffuseColor(Color(diffuse.red, diffuse.green, diffuse.blue, material->getOpacity()));
424                     M3DColor specular = material->getSpecularColor();
425                     vl->setSpecularColor(Color(specular.red, specular.green, specular.blue));
426                     float shininess = material->getShininess();
427 
428                     // Map the 3DS file's shininess from percentage (0-100) to
429                     // range that OpenGL uses for the specular exponent. The
430                     // current equation is just a guess at the mapping that
431                     // 3DS actually uses.
432                     shininess = (float) pow(2.0, 1.0 + 0.1 * shininess);
433                     //shininess = 2.0f + shininess;
434                     //clog << materialName << ": shininess=" << shininess << ", color=" << specular.red << "," << specular.green << "," << specular.blue << '\n';
435                     if (shininess > 128.0f)
436                         shininess = 128.0f;
437                     vl->setShininess(shininess);
438 
439                     if (material->getTextureMap() != "")
440                     {
441                         ResourceHandle tex = GetTextureManager()->getHandle(TextureInfo(material->getTextureMap(), texturePath, TextureInfo::WrapTexture));
442                         vl->setTexture(tex);
443                     }
444 
445                     break;
446                 }
447             }
448         }
449     }
450 
451     // clean up
452     if (faceNormals != NULL)
453         delete[] faceNormals;
454     if (vertexNormals != NULL)
455         delete[] vertexNormals;
456     if (faceCounts != NULL)
457         delete[] faceCounts;
458     if (vertexFaces != NULL)
459     {
460         for (i = 0; i < nVertices; i++)
461         {
462             if (vertexFaces[i] != NULL)
463                 delete[] vertexFaces[i];
464         }
465         delete[] vertexFaces;
466     }
467 
468     return vl;
469 }
470 
471 
472 static Mesh*
ConvertVertexListToMesh(VertexList * vlist,const string &,uint32 material)473 ConvertVertexListToMesh(VertexList* vlist,
474                         const string& /*texPath*/,      //TODO: remove parameter??
475                         uint32 material)
476 {
477     Mesh::VertexAttribute attributes[8];
478     uint32 nAttributes = 0;
479     uint32 offset = 0;
480 
481     uint32 parts = vlist->getVertexParts();
482 
483     // Position attribute is always present in a vertex list
484     attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, 0);
485     nAttributes++;
486     offset += 12;
487 
488     if ((parts & VertexList::VertexNormal) != 0)
489     {
490         attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Normal, Mesh::Float3, offset);
491         nAttributes++;
492         offset += 12;
493     }
494 
495     if ((parts & VertexList::VertexColor0) != 0)
496     {
497         attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Color0, Mesh::UByte4, offset);
498         nAttributes++;
499         offset += 4;
500     }
501 
502     if ((parts & VertexList::TexCoord0) != 0)
503     {
504         attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset);
505         nAttributes++;
506         offset += 8;
507     }
508 
509     if ((parts & VertexList::TexCoord1) != 0)
510     {
511         attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture1, Mesh::Float2, offset);
512         nAttributes++;
513         offset += 8;
514     }
515 
516     uint32 nVertices = vlist->getVertexCount();
517 
518     Mesh* mesh = new Mesh();
519     mesh->setVertexDescription(Mesh::VertexDescription(offset, nAttributes, attributes));
520     mesh->setVertices(nVertices, vlist->getVertexData());
521 
522     // Vertex lists are not indexed, so the conversion to an indexed format is
523     // trivial (although much space is wasted storing unnecessary indices.)
524     uint32* indices = new uint32[nVertices];
525     for (uint32 i = 0; i < nVertices; i++)
526         indices[i] = i;
527 
528     mesh->addGroup(Mesh::TriList, material, nVertices, indices);
529 
530     return mesh;
531 }
532 
533 
534 static Model*
Convert3DSModel(const M3DScene & scene,const string & texPath)535 Convert3DSModel(const M3DScene& scene, const string& texPath)
536 {
537     Model* model = new Model();
538     uint32 materialIndex = 0;
539 
540     for (unsigned int i = 0; i < scene.getModelCount(); i++)
541     {
542         M3DModel* model3ds = scene.getModel(i);
543         if (model3ds != NULL)
544         {
545             for (unsigned int j = 0; j < model3ds->getTriMeshCount(); j++)
546             {
547                 M3DTriangleMesh* mesh = model3ds->getTriMesh(j);
548                 if (mesh != NULL)
549                 {
550                     // The vertex list is just an intermediate stage in conversion
551                     // to a Celestia model structure.  Eventually, we should handle
552                     // the conversion in a single step.
553                     VertexList* vlist = ConvertToVertexList(*mesh, scene, texPath);
554                     Mesh* mesh = ConvertVertexListToMesh(vlist, texPath, materialIndex);
555 
556                     // Convert the vertex list material
557                     Mesh::Material* material = new Mesh::Material();
558                     material->diffuse = vlist->getDiffuseColor();
559                     material->specular = vlist->getSpecularColor();
560                     material->specularPower = vlist->getShininess();
561                     material->opacity = vlist->getDiffuseColor().alpha();
562                     material->maps[Mesh::DiffuseMap] = vlist->getTexture();
563                     model->addMaterial(material);
564                     materialIndex++;
565 
566                     model->addMesh(mesh);
567 
568                     delete vlist;
569                 }
570             }
571         }
572     }
573 
574     return model;
575 #if 0
576     // Sort the vertex lists to make sure that the transparent ones are
577     // rendered after the opaque ones and material state changes are minimized.
578     sort(vertexLists.begin(), vertexLists.end(), compareVertexLists);
579 #endif
580 }
581