1 // 3dstocmod.cpp
2 //
3 // Copyright (C) 2004, 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 // Convert a 3DS file to a Celestia mesh (.cmod) file
11 
12 #include <celengine/modelfile.h>
13 #include <celengine/tokenizer.h>
14 #include <celengine/texmanager.h>
15 #include <cel3ds/3dsread.h>
16 #include <cstring>
17 #include <cassert>
18 #include <cmath>
19 #include <cstdio>
20 
21 
22 static Model* Convert3DSModel(const M3DScene& scene, const string& texPath);
23 
24 
usage()25 void usage()
26 {
27     cerr << "Usage: 3dstocmod <input 3ds file>\n";
28 }
29 
30 
main(int argc,char * argv[])31 int main(int argc, char* argv[])
32 {
33     if (argc != 2)
34     {
35         usage();
36         return 1;
37     }
38 
39     string inputFileName = argv[1];
40 
41     M3DScene* scene = Read3DSFile(inputFileName);
42     if (scene == NULL)
43     {
44         cerr << "Error reading 3DS file '" << inputFileName << "'\n";
45         return 1;
46     }
47 
48     Model* model = Convert3DSModel(*scene, ".");
49     if (!model)
50     {
51         cerr << "Error converting 3DS file to Celestia model\n";
52         return 1;
53     }
54 
55     for (uint32 i = 0; model->getMesh(i); i++)
56     {
57         const Mesh* mesh = model->getMesh(i);
58         for (uint32 j = 0; mesh->getGroup(j); j++)
59         {
60             const Mesh::PrimitiveGroup* group = mesh->getGroup(j);
61             cerr << "Group: #" << i << ", indices=" << group->nIndices << '\n';
62         }
63     }
64 
65     SaveModelAscii(model, cout);
66 
67     return 0;
68 }
69 
70 
71 void
Convert3DSMesh(Model & model,M3DTriangleMesh & mesh3ds,const M3DScene & scene,const string & meshName)72 Convert3DSMesh(Model& model,
73                M3DTriangleMesh& mesh3ds,
74                const M3DScene& scene,
75                const string& meshName)
76 {
77     int nFaces = mesh3ds.getFaceCount();
78     int nVertices = mesh3ds.getVertexCount();
79     int nTexCoords = mesh3ds.getTexCoordCount();
80     bool smooth = (mesh3ds.getSmoothingGroupCount() == nFaces);
81     bool hasTexCoords = (nTexCoords >= nVertices);
82     int vertexSize = hasTexCoords ? 8 : 6;
83     int i;
84 
85     Vec3f* faceNormals = new Vec3f[nFaces];
86     Vec3f* vertexNormals = new Vec3f[nFaces * 3];
87     int* faceCounts = new int[nVertices];
88     int** vertexFaces = new int*[nVertices];
89 
90     int nOutputVertices = nFaces * 3;
91     float* vertices = new float[nOutputVertices * vertexSize];
92 
93 
94     for (i = 0; i < nVertices; i++)
95     {
96         faceCounts[i] = 0;
97         vertexFaces[i] = NULL;
98     }
99 
100     // generate face normals
101     for (i = 0; i < nFaces; i++)
102     {
103         uint16 v0, v1, v2;
104         mesh3ds.getFace(i, v0, v1, v2);
105 
106         faceCounts[v0]++;
107         faceCounts[v1]++;
108         faceCounts[v2]++;
109 
110         Point3f p0 = mesh3ds.getVertex(v0);
111         Point3f p1 = mesh3ds.getVertex(v1);
112         Point3f p2 = mesh3ds.getVertex(v2);
113         faceNormals[i] = cross(p1 - p0, p2 - p1);
114         faceNormals[i].normalize();
115     }
116 
117     if (!smooth && 0)
118     {
119         for (i = 0; i < nFaces; i++)
120         {
121             vertexNormals[i * 3] = faceNormals[i];
122             vertexNormals[i * 3 + 1] = faceNormals[i];
123             vertexNormals[i * 3 + 2] = faceNormals[i];
124         }
125     }
126     else
127     {
128         // allocate space for vertex face indices
129         for (i = 0; i < nVertices; i++)
130         {
131             vertexFaces[i] = new int[faceCounts[i] + 1];
132             vertexFaces[i][0] = faceCounts[i];
133         }
134 
135         for (i = 0; i < nFaces; i++)
136         {
137             uint16 v0, v1, v2;
138             mesh3ds.getFace(i, v0, v1, v2);
139             vertexFaces[v0][faceCounts[v0]--] = i;
140             vertexFaces[v1][faceCounts[v1]--] = i;
141             vertexFaces[v2][faceCounts[v2]--] = i;
142         }
143 
144         // average face normals to compute the vertex normals
145         for (i = 0; i < nFaces; i++)
146         {
147             uint16 v0, v1, v2;
148             mesh3ds.getFace(i, v0, v1, v2);
149             // uint32 smoothingGroups = mesh3ds.getSmoothingGroups(i);
150 
151             int j;
152             Vec3f v = Vec3f(0, 0, 0);
153             for (j = 1; j <= vertexFaces[v0][0]; j++)
154             {
155                 int k = vertexFaces[v0][j];
156                 // if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
157                 if (faceNormals[i] * faceNormals[k] > 0.5f)
158                     v += faceNormals[k];
159             }
160             if (v * v == 0.0f)
161                 v = Vec3f(1.0f, 0.0f, 0.0f);
162             v.normalize();
163             vertexNormals[i * 3] = v;
164 
165             v = Vec3f(0, 0, 0);
166             for (j = 1; j <= vertexFaces[v1][0]; j++)
167             {
168                 int k = vertexFaces[v1][j];
169                 // if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
170                 if (faceNormals[i] * faceNormals[k] > 0.5f)
171                     v += faceNormals[k];
172             }
173             if (v * v == 0.0f)
174                 v = Vec3f(1.0f, 0.0f, 0.0f);
175             v.normalize();
176             vertexNormals[i * 3 + 1] = v;
177 
178             v = Vec3f(0, 0, 0);
179             for (j = 1; j <= vertexFaces[v2][0]; j++)
180             {
181                 int k = vertexFaces[v2][j];
182                 // if (k == i || (smoothingGroups & mesh3ds.getSmoothingGroups(k)) != 0)
183                 if (faceNormals[i] * faceNormals[k] > 0.5f)
184                     v += faceNormals[k];
185             }
186             if (v * v == 0.0f)
187                 v = Vec3f(1.0f, 0.0f, 0.0f);
188             v.normalize();
189             vertexNormals[i * 3 + 2] = v;
190         }
191     }
192 
193     // build the triangle list
194     for (i = 0; i < nFaces; i++)
195     {
196         uint16 triVert[3];
197         mesh3ds.getFace(i, triVert[0], triVert[1], triVert[2]);
198 
199         for (int j = 0; j < 3; j++)
200         {
201             Point3f pos = mesh3ds.getVertex(triVert[j]);
202             Vec3f norm = vertexNormals[i * 3 + j];
203             int k = (i * 3 + j) * vertexSize;
204 
205             vertices[k + 0] = pos.x;
206             vertices[k + 1] = pos.y;
207             vertices[k + 2] = pos.z;
208             vertices[k + 3] = norm.x;
209             vertices[k + 4] = norm.y;
210             vertices[k + 5] = norm.z;
211             if (hasTexCoords)
212             {
213                 vertices[k + 6] = mesh3ds.getTexCoord(triVert[j]).x;
214                 vertices[k + 7] = mesh3ds.getTexCoord(triVert[j]).y;
215             }
216         }
217     }
218 
219     // clean up
220     if (faceNormals != NULL)
221         delete[] faceNormals;
222     if (vertexNormals != NULL)
223         delete[] vertexNormals;
224     if (faceCounts != NULL)
225         delete[] faceCounts;
226     if (vertexFaces != NULL)
227     {
228         for (i = 0; i < nVertices; i++)
229         {
230             if (vertexFaces[i] != NULL)
231                 delete[] vertexFaces[i];
232         }
233         delete[] vertexFaces;
234     }
235 
236 
237     Mesh::VertexAttribute attributes[8];
238     uint32 nAttributes = 0;
239     uint32 offset = 0;
240 
241     // Position attribute is always present
242     attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Position, Mesh::Float3, 0);
243     nAttributes++;
244     offset += 12;
245 
246     // Normal attribute is always present
247     attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Normal, Mesh::Float3, offset);
248     nAttributes++;
249     offset += 12;
250 
251     if (hasTexCoords)
252     {
253         attributes[nAttributes] = Mesh::VertexAttribute(Mesh::Texture0, Mesh::Float2, offset);
254         nAttributes++;
255         offset += 8;
256     }
257 
258     // Create the Celestia mesh
259     Mesh* mesh = new Mesh();
260     mesh->setVertexDescription(Mesh::VertexDescription(offset, nAttributes, attributes));
261     mesh->setVertices(nOutputVertices, vertices);
262 
263     mesh->setName(meshName);
264 
265     // Vertex lists are not indexed, so the conversion to an indexed format is
266     // trivial (although much space is wasted storing unnecessary indices.)
267     uint32* indices = new uint32[nOutputVertices];
268     for (int i = 0; i < nOutputVertices; i++)
269         indices[i] = i;
270 
271     // Convert the 3DS mesh's material
272     Mesh::Material* material = new Mesh::Material();
273 
274     string material3dsName = mesh3ds.getMaterialName();
275     if (material3dsName.length() > 0)
276     {
277         int nMaterials = scene.getMaterialCount();
278         for (int i = 0; i < nMaterials; i++)
279         {
280             M3DMaterial* material3ds = scene.getMaterial(i);
281             if (material3dsName == material3ds->getName())
282             {
283                 M3DColor diffuse = material3ds->getDiffuseColor();
284                 material->diffuse = Color(diffuse.red, diffuse.green, diffuse.blue);
285                 M3DColor specular = material3ds->getSpecularColor();
286                 material->specular = Color(specular.red, specular.green, specular.blue);
287                 // Map the shininess from the 3DS file into the 0-128
288                 // range that OpenGL uses for the specular exponent.
289                 float specPow = (float) pow(2.0, 1.0 + 0.1 * material3ds->getShininess());
290                 if (specPow > 128.0f)
291                     specPow = 128.0f;
292                 material->specularPower = specPow;
293 
294                 material->opacity = material3ds->getOpacity();
295                 if (material3ds->getTextureMap() != "")
296                 {
297                     material->maps[Mesh::DiffuseMap] = GetTextureManager()->getHandle(TextureInfo(material3ds->getTextureMap(), ".", TextureInfo::WrapTexture));
298                 }
299             }
300         }
301     }
302 
303     uint32 materialIndex = model.addMaterial(material) - 1;
304     mesh->addGroup(Mesh::TriList, materialIndex, nOutputVertices, indices);
305     model.addMesh(mesh);
306 }
307 
308 
309 static Model*
Convert3DSModel(const M3DScene & scene,const string & texPath)310 Convert3DSModel(const M3DScene& scene, const string& texPath)
311 {
312     Model* model = new Model();
313     uint32 materialIndex = 0;
314 
315     for (unsigned int i = 0; i < scene.getModelCount(); i++)
316     {
317         M3DModel* model3ds = scene.getModel(i);
318         if (model3ds != NULL)
319         {
320             for (unsigned int j = 0; j < model3ds->getTriMeshCount(); j++)
321             {
322                 M3DTriangleMesh* mesh = model3ds->getTriMesh(j);
323 
324                 if (mesh != NULL && mesh->getFaceCount() > 0)
325                 {
326                     Convert3DSMesh(*model, *mesh, scene, model3ds->getName());
327                 }
328             }
329         }
330     }
331 
332     return model;
333 #if 0
334     // Sort the vertex lists to make sure that the transparent ones are
335     // rendered after the opaque ones and material state changes are minimized.
336     sort(vertexLists.begin(), vertexLists.end(), compareVertexLists);
337 #endif
338 }
339 
340