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