Add "Apply transformations" feature (no connectors or frames done yet)
[wmit:wmit.git] / src / formats / WZM.cpp
1 /*
2         Copyright 2010 Warzone 2100 Project
3
4         This file is part of WMIT.
5
6         WMIT is free software: you can redistribute it and/or modify
7         it under the terms of the GNU General Public License as published by
8         the Free Software Foundation, either version 3 of the License, or
9         (at your option) any later version.
10
11         WMIT is distributed in the hope that it will be useful,
12         but WITHOUT ANY WARRANTY; without even the implied warranty of
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14         GNU General Public License for more details.
15
16         You should have received a copy of the GNU General Public License
17         along with WMIT.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "WZM.hpp"
21
22 #include <algorithm>
23 #include <iterator>
24 #include <map>
25 #include <set>
26 #include <list>
27
28 #include <cmath>
29
30 #include <sstream>
31
32 #ifdef LIB3DS_VERSION_1
33 #include <lib3ds/file.h>
34 #include <lib3ds/mesh.h>
35 #include <lib3ds/material.h>
36 #else
37 #include <lib3ds.h>
38 #endif
39
40 #include "Generic.hpp"
41 #include "Util.hpp"
42 #include "Pie.hpp"
43 #include "Vector.hpp"
44
45 #include "OBJ.hpp"
46
47 WZM::WZM()
48 {
49 }
50
51 WZM::WZM(const Pie3Model &p3)
52 {
53         std::vector<Pie3Level>::const_iterator it;
54         std::stringstream ss;
55         m_texName = p3.m_texture;
56         for (it = p3.m_levels.begin(); it != p3.m_levels.end(); ++it)
57         {
58                 m_meshes.push_back(*it);
59                 ss << m_meshes.size();
60                 m_meshes.back().setName(ss.str());
61                 ss.str(std::string());
62         }
63 }
64
65 WZM::operator Pie3Model() const
66 {
67         Pie3Model p3;
68         p3.m_type = 0x10200;
69         p3.m_texture = m_texName;
70         std::transform(m_meshes.begin(), m_meshes.end(),
71                                    back_inserter(p3.m_levels), Mesh::backConvert);
72         return p3;
73 }
74
75 bool WZM::read(std::istream& in)
76 {
77         std::string str;
78         int i,meshes;
79
80         clear();
81         in >> str;
82         if (in.fail() || str.compare("WZM") != 0)
83         {
84                 std::cerr << "WZM::read - Missing WZM header";
85                 return false;
86         }
87
88         in >> i;
89         if (in.fail())
90         {
91                 std::cerr << "WZM::read - Error reading WZM version";
92                 return false;
93         }
94         else if(i!=2)
95         {
96                 std::cerr << "WZM::read - Unsupported WZM version " << i;
97                 return false;
98         }
99
100         // TEXTURE %s
101         in >> str;
102         if (str.compare("TEXTURE") != 0)
103         {
104                 std::cerr << "WZM::read - Expected TEXTURE directive but got" << str;
105                 return false;
106         }
107         in >> m_texName;
108         if (in.fail())
109         {
110                 std::cerr << "WZM::read - Error reading WZM version";
111                 m_texName = std::string();
112                 return false;
113         }
114
115         // MESHES %u
116         in >> str >> meshes;
117         if (in.fail() || str.compare("MESHES") != 0)
118         {
119                 std::cerr << "WZM::read - Expected MESHES directive but got " << str;
120                 clear();
121                 return false;
122         }
123
124         m_meshes.reserve(meshes);
125         for(; meshes>0; --meshes)
126         {
127                 Mesh mesh;
128                 if (!mesh.read(in))
129                 {
130                         clear();
131                         return false;
132                 }
133                 m_meshes.push_back(mesh);
134         }
135         return true;
136 }
137
138 void WZM::write(std::ostream& out) const
139 {
140         std::vector<Mesh>::const_iterator it;
141         out << "WZM\t" << version() << '\n';
142         if (!m_texName.empty())
143         {
144                 out << "TEXTURE " << m_texName << '\n';
145         }
146         else
147         {
148                 out << "TEXTURE emptytex.png\n";
149         }
150         out << "MESHES " << meshes() << '\n';
151         for (it = m_meshes.begin(); it != m_meshes.end(); ++it)
152         {
153                 it->write(out);
154         }
155 }
156
157 /*
158  * This function does the parsing,
159  * we'll let class Mesh do the WZM'izing
160  */
161 bool WZM::importFromOBJ(std::istream& in)
162 {
163         const bool invertV = true;
164         std::vector<OBJVertex> vertArray;
165
166         std::vector<OBJUV> uvArray;
167
168         std::vector<OBJTri> groupedFaces;
169
170         std::string name("Default"); //Default name of default obj group is default
171         std::vector<std::string> vertices, indices;
172         std::vector<std::string>::iterator itVStr;
173         std::string str;
174         std::stringstream ss;
175
176         // Only give warnings once
177         bool warnLine = false, warnPoint = false;
178
179         // More temporaries
180         OBJTri tri;
181         OBJVertex vert;
182         OBJUV uv;
183         Mesh mesh;
184
185         unsigned i, pos;
186
187         clear();
188
189         /*      Build "global" vertex and
190          *      texture coordinate arrays.
191          *      Also figure out the mesh/group boundaries
192          */
193
194         /* Note: This program tolerates imperfect .obj files
195          * because it accepts any whitespace as a space.
196          */
197
198         while (!(in.eof()|| in.fail()))
199         {
200                 std::getline(in, str);
201                 ss.str(str);
202                 ss.seekg(0);
203                 ss.clear();
204
205                 // We're forgiving of leading whitespaces (though we don't need/shouldn't to be)
206                 skipWhitespace(ss);
207                 if (str.empty() || ss.fail())
208                 {
209                         continue;
210                 }
211
212                 switch(ss.get())
213                 {
214                 case 'v':
215                         switch(ss.get())
216                         {
217                         case 't':
218                                 ss >> uv.u() >> uv.v();
219                                 if (ss.fail())
220                                 {
221                                         return false;
222                                 }
223                                 else if (invertV)
224                                 {
225                                         uv.v() = 1 - uv.v();
226                                 }
227                                 uvArray.push_back(uv);
228                                 break;
229                         case 'n':       // Ignore normals
230                         case 'p':       // and parameter vertices
231                                 break;
232                         default:
233                                 ss >> vert.x() >> vert.y() >> vert.z();
234                                 if (ss.fail())
235                                 {
236                                         return false;
237                                 }
238                                 vertArray.push_back(vert);
239                         }
240                         break;
241                 case 'f':
242                         vertices = split(str);
243                         if (vertices.size() < 2 + 1)
244                         {
245                                  /* Should never happen, faces will have >= 3+1 tokens
246                                   * ( more than 3 vertices + the "f" directive)
247                                   */
248                                 break;
249                         }
250
251                         itVStr = vertices.begin();
252                         ++itVStr; // Advance to first vertex
253
254                         for (i = 0; itVStr != vertices.end(); ++itVStr, ++i)
255                         {
256                                 if (i <= 2)
257                                 {
258                                         pos = i;
259                                 }
260                                 else
261                                 {
262                                         tri.uvs.operator [](1) = tri.uvs.operator [](2);
263                                         tri.tri.operator [](1) = tri.tri.operator [](2);
264                                         pos = 2;
265                                 }
266
267                                 indices = split(*itVStr, '/');
268
269                                 ss.str(indices[0]), ss.clear(), ss.seekg(0);
270
271                                 ss >> tri.tri.operator [](pos);
272
273                                 if (indices.size() >= 2 && !indices[1].empty())
274                                 {
275                                         ss.str(indices[1]), ss.clear(), ss.seekg(0);
276                                         ss >> tri.uvs.operator [](pos);
277                                 }
278                                 else
279                                 {
280                                         tri.uvs.operator [](pos) = -1;
281                                 }
282
283                                 if (indices.size() == 3 && !indices[2].empty())
284                                 {} // ignoring normals
285
286                                 if (i >= 2)
287                                 {
288                                         groupedFaces.push_back(tri);
289                                 }
290                         }
291                         break;
292                 case 'l':
293                         // Ignore lines and give one warning to the user
294                         if (!warnLine)
295                         {
296                                 std::cout << "WZM::importFromOBJ - Warning! Lines are not supported and will be ignored!";
297                                 warnLine = true;
298                         }
299                         break;
300                 case 'p':
301                         // Ignore points and give one warning to the user
302                         if (!warnPoint)
303                         {
304                                 std::cout << "Model::importFromOBJ - Warning! Points are not supported and will be ignored!";
305                                 warnPoint = true;
306                         }
307                         break;
308                 case 'o':
309                         if (!groupedFaces.empty())
310                         {
311                                 mesh.importFromOBJ(groupedFaces,vertArray,uvArray);
312                                 mesh.setTeamColours(false);
313                                 mesh.setName(name);
314                                 m_meshes.push_back(mesh);
315                                 groupedFaces.clear();
316                         }
317                         ss >> name;
318                         if (!isValidWzName(name))
319                         {
320                                 ss.str(std::string()), ss.clear(), ss.seekg(0);
321                                 ss << m_meshes.size();
322                                 ss >> name;
323                         }
324                         break;
325                 }
326         }
327         if (!groupedFaces.empty())
328         {
329                 mesh.importFromOBJ(groupedFaces,vertArray,uvArray);
330                 mesh.setTeamColours(false);
331                 mesh.setName(name);
332                 m_meshes.push_back(mesh);
333         }
334         return true;
335 }
336
337 void WZM::exportToOBJ(std::ostream &out) const
338 {
339         std::list<std::stringstream*> objectBuffers;
340
341         Mesh_exportToOBJ_InOutParams params;
342
343         OBJVertex::less_wEps vertCompare;
344         std::set<OBJVertex, OBJVertex::less_wEps> vertSet(vertCompare);
345         std::vector<OBJVertex> vertices;
346         std::vector<unsigned> vertMapping;
347
348         params.vertices = &vertices;
349         params.vertSet = &vertSet;
350         params.vertMapping = &vertMapping;
351
352         OBJUV::less_wEps uvCompare;
353         std::set<OBJUV, OBJUV::less_wEps> uvSet(uvCompare);
354         std::vector<OBJUV> uvs;
355         std::vector<unsigned> uvMapping;
356
357         params.uvs = &uvs;
358         params.uvSet = &uvSet;
359         params.uvMapping = &uvMapping;
360
361         std::vector<Mesh>::const_iterator itM;
362         std::vector<OBJVertex>::iterator itVert;
363         std::vector<OBJUV>::iterator    itUV;
364         std::stringstream* pSSS;
365
366         if (!m_texName.empty())
367         {
368                 out << "# texture: " << m_texName << '\n';
369         }
370
371         for (itM = m_meshes.begin(); itM != m_meshes.end(); ++itM)
372         {
373                 objectBuffers.push_back(itM->exportToOBJ(params));
374         }
375
376         for (itVert = vertices.begin(); itVert != vertices.end(); ++itVert)
377         {
378                 writeOBJVertex(*itVert, out);
379         }
380
381         out << '\n';
382
383         for (itUV = uvs.begin(); itUV != uvs.end(); ++itUV)
384         {
385                 writeOBJUV(*itUV, out);
386         }
387
388         while (!objectBuffers.empty())
389         {
390                 pSSS = objectBuffers.front();
391                 objectBuffers.pop_front();
392
393                 out << "\n\n" << pSSS->str();
394
395                 delete pSSS;
396         }
397 }
398
399 bool WZM::importFrom3DS(std::string fileName)
400 {
401 #ifdef LIB3DS_VERSION_1
402         Lib3dsFile *f = lib3ds_file_load(fileName.c_str());
403         Lib3dsMaterial *material;
404         Lib3dsMesh *ps3dsMesh;
405 #else
406         Lib3dsFile *f = lib3ds_file_open(fileName.c_str());
407         int i;
408 #endif
409
410         if (f == NULL)
411         {
412                 std::cerr << "Loading 3DS file failed.\n";
413                 return false;
414         }
415
416         clear();
417
418 #ifdef LIB3DS_VERSION_1
419         material = f->materials;
420         // Grab texture name
421         if (material != NULL)
422         {
423                 m_texName = material->texture1_map.name;
424                 if (material->next != NULL)
425                 {
426                         std::cout << "WZM::importFrom3ds - Multiple textures not supported!\n";
427                 }
428         }
429         for (ps3dsMesh = f->meshes; ps3dsMesh != NULL; ps3dsMesh = ps3dsMesh->next)
430         {
431                 m_meshes.push_back(*ps3dsMesh);
432         }
433 #else
434         if (f->nmaterials >= 0)
435         {
436                 m_texName = (*f->materials)->texture1_map.name;
437                 if (f->nmaterials > 1)
438                 {
439                         std::cout << "WZM::importFrom3ds - Multiple textures not supported!\n";
440                 }
441         }
442         for (i = 0; i < f->nmeshes; ++i)
443         {
444                 m_meshes.push_back(*f->meshes[i]);
445         }
446 #endif
447
448         lib3ds_file_free(f);
449         return true;
450 }
451
452
453 bool WZM::exportTo3DS(std::string fileName) const
454 {
455         Lib3dsFile* f = lib3ds_file_new();
456 #ifdef LIB3DS_VERSION_1
457         Lib3dsMaterial* material = lib3ds_material_new();
458         Lib3dsBool retVal;
459 #else
460         Lib3dsMaterial* material = lib3ds_material_new("default");
461         int retVal;
462 #endif
463
464         std::vector<Mesh>::const_iterator itM;
465         unsigned i;
466
467         i = m_texName.copy(material->texture1_map.name, 64 - 1);
468         material->texture1_map.name[i] = '\0';
469 #ifdef LIB3DS_VERSION_1
470         lib3ds_file_insert_material(f, material);
471         for (itM = m_meshes.begin(); itM != m_meshes.end(); ++itM)
472         {
473                 /* FIXME 3DS meshes have unique names,
474                  * and overwrite meshes with same names when inserting
475                  */
476                 lib3ds_file_insert_mesh(f, *itM);
477         }
478 #else
479         lib3ds_file_insert_material(f, material, 0);
480         for (itM = m_meshes.begin(), i = 0; itM != m_meshes.end(); ++itM, ++i)
481         {
482                 lib3ds_file_insert_mesh(f, *itM, i);
483         }
484         
485 #endif
486
487         retVal = lib3ds_file_save (f, fileName.c_str());
488         delete f;
489         return retVal;
490 }
491
492 int WZM::version() const
493 {
494         return 2;
495 }
496
497 int WZM::meshes() const
498 {
499         return m_meshes.size();
500 }
501
502 void WZM::setTextureName(std::string name)
503 {
504         m_texName = name;
505 }
506
507 std::string WZM::getTextureName() const
508 {
509         return m_texName;
510 }
511
512 Mesh& WZM::getMesh(int index)
513 {
514         return m_meshes.at(index);
515 }
516
517 void WZM::addMesh(const Mesh& mesh)
518 {
519         m_meshes.push_back(mesh);
520 }
521
522 void WZM::rmMesh (int index)
523 {
524         std::vector<Mesh>::iterator pos;
525         pos=m_meshes.begin()+index;
526         if(pos==m_meshes.end())
527         {
528                 return;
529         }
530         m_meshes.erase(pos);
531 }
532
533 bool WZM::couldHaveTCArrays() const
534 {
535         std::vector<Mesh>::const_iterator it;
536
537         for (it = m_meshes.begin(); it != m_meshes.end(); ++it)
538         {
539                 if (it->textureArrays() == 8)
540                 {
541                         return true;
542                 }
543         }
544         return false;
545 }
546
547 bool WZM::isValid() const
548 {
549         std::vector<Mesh>::const_iterator it;
550
551         if (!isValidWzName(m_texName))
552         {
553                 return false;
554         }
555
556         for (it = m_meshes.begin(); it != m_meshes.end(); ++it)
557         {
558                 if(!it->isValid())
559                 {
560                         return false;
561                 }
562         }
563         return true;
564 }
565
566 void WZM::clear()
567 {
568         m_meshes.clear();
569         m_texName = std::string();
570 }
571
572 void WZM::scale(GLfloat x, GLfloat y, GLfloat z)
573 {
574         std::vector<Mesh>::iterator it;
575
576         for (it = m_meshes.begin(); it != m_meshes.end(); ++it)
577         {
578                 it->scale(x, y, z);
579         }
580 }