1 #include <QtGlobal>
2
3 #include <lib3ds/file.h>
4 #include <lib3ds/mesh.h>
5 #include <lib3ds/vector.h>
6 #include <lib3ds/material.h>
7
8 #include <sstream>
9 #include <fstream>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <string>
13 #include <set>
14 #include <list>
15 #include <queue>
16 #include <cmath>
17
18 #include "qwzm.h"
19 #include "conversion.h"
20
loadPIE(QString inputFile)21 MODEL *QWzmViewer::loadPIE(QString inputFile)
22 {
23 const bool swapYZ = false;
24 const bool reverseWinding = false;
25 const bool invertUV = false;
26 const bool assumeAnimation = false;
27 int num, x, y, z, levels, level, pieVersion;
28 char s[200];
29 QByteArray inFile = inputFile.toAscii();
30 const char *input = inFile.constData();
31 MODEL *psModel = NULL;
32 FILE *fp = fopen(input, "r");
33
34 if (!fp)
35 {
36 qWarning("Cannot open \"%s\" for reading: %s", input, strerror(errno));
37 return NULL;
38 }
39
40 num = fscanf(fp, "PIE %d\n", &pieVersion);
41 if (num != 1)
42 {
43 qWarning("Bad PIE file %s\n", input);
44 fclose(fp);
45 freeModel(psModel);
46 return NULL;
47 }
48
49 num = fscanf(fp, "TYPE %x\n", &x); // ignore
50 if (num != 1)
51 {
52 qWarning("Bad TYPE directive in %s\n", input);
53 fclose(fp);
54 freeModel(psModel);
55 return NULL;
56 }
57
58 num = fscanf(fp, "TEXTURE %d %s %d %d\n", &z, s, &x, &y);
59 if (num != 4)
60 {
61 qWarning("Bad TEXTURE directive in %s\n", input);
62 fclose(fp);
63 freeModel(psModel);
64 return NULL;
65 }
66
67 num = fscanf(fp, "LEVELS %d\n", &levels);
68 if (num != 1)
69 {
70 qWarning("Bad LEVELS directive in %s\n", input);
71 fclose(fp);
72 freeModel(psModel);
73 return NULL;
74 }
75
76 psModel = createModel(levels, 0);
77 if (!psModel)
78 {
79 qFatal("Out of memory");
80 fclose(fp);
81 exit(EXIT_FAILURE);
82 }
83 strcpy(psModel->texPath, s);
84
85 for (level = 0; level < levels; level++)
86 {
87 MESH *psMesh = &psModel->mesh[level];
88 int j, points, faces, facesWZM, faceCount, pointsWZM, pointCount, textureArrays = 1;
89 WZ_FACE *faceList;
90 WZ_POSITION *posList;
91
92 num = fscanf(fp, "\nLEVEL %d\n", &x);
93 if (num != 1 || level + 1 != x)
94 {
95 qWarning("Bad LEVEL directive in %s, was %d should be %d.\n", input, x, level + 1);
96 fclose(fp);
97 freeModel(psModel);
98 return NULL;
99 }
100
101 num = fscanf(fp, "POINTS %d\n", &points);
102 if (num != 1)
103 {
104 qWarning("Bad POINTS directive in %s, level %d.\n", input, level);
105 fclose(fp);
106 freeModel(psModel);
107 return NULL;
108 }
109 posList = (WZ_POSITION*)malloc(sizeof(WZ_POSITION) * points);
110 for (j = 0; j < points; j++)
111 {
112 if (swapYZ)
113 {
114 num = fscanf(fp, "%f %f %f\n", &posList[j].x, &posList[j].z, &posList[j].y);
115 }
116 else
117 {
118 num = fscanf(fp, "%f %f %f\n", &posList[j].x, &posList[j].y, &posList[j].z);
119 }
120 if (num != 3)
121 {
122 qWarning("Bad POINTS entry level %d, number %d\n", level, j);
123 fclose(fp);
124 freeModel(psModel);
125 return NULL;
126 }
127 }
128
129 num = fscanf(fp, "POLYGONS %d", &faces);
130 if (num != 1)
131 {
132 qWarning("Bad POLYGONS directive in %s, level %d.\n", input, level);
133 fclose(fp);
134 freeModel(psModel);
135 return NULL;
136 }
137 facesWZM = faces; // for starters
138 faceList = (WZ_FACE*)malloc(sizeof(WZ_FACE) * faces);
139 pointsWZM = 0;
140 for (j = 0; j < faces; ++j)
141 {
142 int k;
143 unsigned int flags;
144
145 num = fscanf(fp, "\n%x", &flags);
146 if (num != 1)
147 {
148 qWarning("Bad POLYGONS texture flag entry level %d, number %d\n", level, j);
149 fclose(fp);
150 freeModel(psModel);
151 return NULL;
152 }
153 if (!(flags & iV_IMD_TEX))
154 {
155 qWarning("Bad texture flag entry level %d, number %d - no texture flag!\n", level, j);
156 fclose(fp);
157 freeModel(psModel);
158 return NULL;
159 }
160
161 if (flags & iV_IMD_XNOCUL)
162 {
163 faceList[j].cull = true;
164 facesWZM++; // must add additional face that is faced in the opposite direction
165 }
166 else
167 {
168 faceList[j].cull = false;
169 }
170
171 num = fscanf(fp, "%u", &faceList[j].vertices);
172 if (num != 1 || faceList[j].vertices < 0)
173 {
174 qWarning("Bad POLYGONS vertices entry level %d, number %d\n", level, j);
175 fclose(fp);
176 freeModel(psModel);
177 return NULL;
178 }
179 assert(faceList[j].vertices <= MAX_POLYGON_SIZE); // larger polygons not supported
180 assert(faceList[j].vertices >= VERTICES_PER_TRIANGLE); // points and lines not supported
181 if (faceList[j].vertices > VERTICES_PER_TRIANGLE)
182 {
183 // since they are triangle fans already, we get to do easy tessellation
184 facesWZM += (faceList[j].vertices - VERTICES_PER_TRIANGLE);
185 if (faceList[j].cull)
186 {
187 facesWZM += (faceList[j].vertices - VERTICES_PER_TRIANGLE); // double the number of extra faces needed
188 }
189 }
190 pointsWZM += faceList[j].vertices;
191
192 // Read in vertex indices and texture coordinates
193 for (k = 0; k < faceList[j].vertices; k++)
194 {
195 num = fscanf(fp, "%d", &faceList[j].index[k]);
196 if (num != 1)
197 {
198 qWarning("Bad vertex position entry level %d, number %d\n", level, j);
199 fclose(fp);
200 freeModel(psModel);
201 return NULL;
202 }
203 }
204 if (flags & iV_IMD_TEXANIM)
205 {
206 num = fscanf(fp, "%d %d %f %f", &faceList[j].frames, &faceList[j].rate, &faceList[j].width, &faceList[j].height);
207 if (num != 4)
208 {
209 qWarning("Bad texture animation entry level %d, number %d.\n", level, j);
210 fclose(fp);
211 freeModel(psModel);
212 return NULL;
213 }
214 if (faceList[j].frames <= 1)
215 {
216 qWarning("Level %d, polygon %d has a single animation frame. That makes no sense.\n", level, j);
217 }
218 if (textureArrays < faceList[j].frames)
219 {
220 textureArrays = faceList[j].frames;
221 }
222 }
223 else
224 {
225 faceList[j].frames = 0;
226 faceList[j].rate = 0;
227 faceList[j].width = 0;
228 faceList[j].height = 0;
229 }
230 for (k = 0; k < faceList[j].vertices; k++)
231 {
232 num = fscanf(fp, "%f %f", &faceList[j].texCoord[k][0], &faceList[j].texCoord[k][1]);
233 if (num != 2)
234 {
235 qWarning("Bad texture coordinate entry level %d, number %d\n", level, j);
236 fclose(fp);
237 freeModel(psModel);
238 return NULL;
239 }
240 }
241 }
242 if (textureArrays == 8 && !assumeAnimation) // guesswork
243 {
244 psMesh->teamColours = true;
245 }
246 else
247 {
248 psMesh->teamColours = false;
249 }
250
251 // Calculate position list. Since positions hold texture coordinates in WZM, unlike in Warzone,
252 // we need to use some black magic to transfer them over here.
253 pointCount = 0;
254 psMesh->vertices = pointsWZM;
255 psMesh->faces = facesWZM;
256 psMesh->vertexArray = (GLfloat*)malloc(sizeof(GLfloat) * psMesh->vertices * VERTICES_PER_TRIANGLE);
257 psMesh->indexArray = (GLuint*)malloc(sizeof(GLuint) * psMesh->vertices * VERTICES_PER_TRIANGLE);
258 psMesh->textureArrays = textureArrays;
259 for (j = 0; j < textureArrays; j++)
260 {
261 psMesh->textureArray[j] = (GLfloat*)malloc(sizeof(GLfloat) * psMesh->vertices * 2);
262 }
263
264 for (z = 0, j = 0; j < faces; j++)
265 {
266 for (int k = 0; k < faceList[j].vertices; k++, pointCount++)
267 {
268 int pos = faceList[j].index[k];
269
270 // Generate new position
271 psMesh->vertexArray[z++] = posList[pos].x;
272 psMesh->vertexArray[z++] = posList[pos].y;
273 psMesh->vertexArray[z++] = posList[pos].z;
274
275 // Use the new position
276 faceList[j].index[k] = pointCount;
277 }
278 }
279
280 // Handle texture animation or team colours. In either case, add multiple texture arrays.
281 for (z = 0; z < textureArrays; z++)
282 {
283 for (x = 0, j = 0; j < faces; j++)
284 {
285 for (int k = 0; k < faceList[j].vertices; k++)
286 {
287 double u;
288 double v;
289 // Fix for division by zero in pie 2
290 int framesPerLine = OLD_TEXTURE_SIZE_FIX;
291
292 if (pieVersion == 3)
293 {
294 if(faceList[j].width != 0)
295 {
296 framesPerLine = 1/ faceList[j].width;
297 }
298 else
299 {
300 // Fix for division by zero in pie 3
301 framesPerLine = 1;
302 }
303 }
304 else if (faceList[j].width != 0) // else pieVersion ==2
305 {
306 framesPerLine = OLD_TEXTURE_SIZE_FIX / faceList[j].width;
307 }
308
309 int frameH = z % framesPerLine;
310
311 /*
312 * This works because wrap around is only permitted if you start the animation at the
313 * left border of the texture. What a horrible hack this was.
314 * Note: It is done the same way in the Warzone source.
315 */
316 int frameV = z / framesPerLine;
317 double width = faceList[j].texCoord[k][0] + faceList[j].width * frameH;
318 double height = faceList[j].texCoord[k][1] + faceList[j].height * frameV;
319
320 if (pieVersion == 2)
321 {
322 u = width / OLD_TEXTURE_SIZE_FIX;
323 v = height / OLD_TEXTURE_SIZE_FIX;
324 }
325 else if (pieVersion == 3)
326 {
327 u = width;
328 v = height;
329 }
330
331 if (invertUV)
332 {
333 v = 1.0f - v;
334 }
335 psMesh->textureArray[z][x++] = u;
336 psMesh->textureArray[z][x++] = v;
337 }
338 }
339 }
340
341 faceCount = 0;
342
343 for (z = 0, j = 0; j < faces; j++)
344 {
345 int k, key, previous;
346
347 key = faceList[j].index[0];
348 previous = faceList[j].index[2];
349 faceCount++;
350 if (reverseWinding || faceList[j].cull)
351 {
352 GLuint *v = &psMesh->indexArray[z];
353
354 z += 3;
355 v[0] = key;
356 v[1] = previous;
357 v[2] = faceList[j].index[1];
358 }
359 if (!reverseWinding || faceList[j].cull)
360 {
361 GLuint *v = &psMesh->indexArray[z];
362
363 z += 3;
364 v[0] = key;
365 v[2] = previous;
366 v[1] = faceList[j].index[1];
367 }
368
369 // Generate triangles from the Warzone triangle fans (PIEs, get it?)
370 for (k = VERTICES_PER_TRIANGLE; k < faceList[j].vertices; k++)
371 {
372 if (reverseWinding || faceList[j].cull)
373 {
374 GLuint *v = &psMesh->indexArray[z];
375
376 z += 3;
377 v[0] = key;
378 v[2] = previous;
379 v[1] = faceList[j].index[k];
380 }
381 if (!reverseWinding || faceList[j].cull)
382 {
383 GLuint *v = &psMesh->indexArray[z];
384
385 z += 3;
386 v[0] = key;
387 v[1] = previous;
388 v[2] = faceList[j].index[k];
389 }
390 previous = faceList[j].index[k];
391 }
392 }
393
394 // We only handle texture animation here, so leave bone heap animation out of it for now.
395 if (textureArrays == 1 || (textureArrays == 8 && !assumeAnimation))
396 {
397 psMesh->frames = 0;
398 }
399 else
400 {
401 psMesh->frames = textureArrays;
402 psMesh->frameArray = (FRAME*)malloc(sizeof(FRAME) * psMesh->frames);
403 for (j = 0; j < textureArrays; j++)
404 {
405 FRAME *psFrame = &psMesh->frameArray[j];
406
407 psFrame->timeSlice = (float)faceList[j].rate;
408 psFrame->textureArray = j;
409 psFrame->translation.x = 0;
410 psFrame->translation.y = 0;
411 psFrame->translation.z = 0;
412 psFrame->rotation.x = 0;
413 psFrame->rotation.y = 0;
414 psFrame->rotation.z = 0;
415 }
416 }
417
418 num = fscanf(fp, "\nCONNECTORS %d", &psMesh->connectors);
419 if (num == 1 && psMesh->connectors > 0)
420 {
421 psMesh->connectorArray = (CONNECTOR *)malloc(sizeof(CONNECTOR) * psMesh->connectors);
422
423 for (j = 0; j < psMesh->connectors; j++)
424 {
425 CONNECTOR *conn = &psMesh->connectorArray[j];
426 GLfloat a, b, c;
427
428 num = fscanf(fp, "\n%f %f %f", &a, &b, &c);
429 if (num != 3)
430 {
431 qWarning("Bad CONNECTORS directive in %s entry level %d, number %d\n", input, level, j);
432 fclose(fp);
433 freeModel(psModel);
434 return NULL;
435 }
436 conn->pos.x = a;
437 conn->pos.y = b;
438 conn->pos.z = c;
439 conn->type = 0; // generic type of connector, only type supported in PIE
440 }
441 }
442
443 free(faceList);
444 free(posList);
445 }
446 fclose(fp);
447 return psModel;
448 }
449
load3DS(QString input)450 MODEL *QWzmViewer::load3DS(QString input)
451 {
452 const bool swapYZ = true;
453 const bool reverseWinding = true;
454 const bool invertUV = true;
455 const float scaleFactor = 1.0f;
456 Lib3dsFile *f = lib3ds_file_load(input.toAscii().constData());
457 Lib3dsMaterial *material = f->materials;
458 int meshes = 0;
459 Lib3dsMesh *m;
460 int meshIdx;
461 MODEL *psModel;
462
463 if (!f)
464 {
465 qWarning("Loading 3DS file %s failed", input.toAscii().constData());
466 return NULL;
467 }
468
469 for (meshes = 0, m = f->meshes; m; m = m->next, meshes++) ; // count the meshes
470
471 psModel = createModel(meshes, 0);
472 if (!psModel)
473 {
474 qFatal("Out of memory");
475 }
476
477 // Grab texture name
478 for (int j = 0; material; material = material->next, j++)
479 {
480 Lib3dsTextureMap *texture = &material->texture1_map;
481 QString texName(texture->name);
482
483 if (j > 0)
484 {
485 qWarning("Texture %d %s: More than one texture currently not supported!", j, texture->name);
486 continue;
487 }
488 strcpy(psModel->texPath, texName.toLower().toAscii().constData());
489 }
490
491 // Grab each mesh
492 for (meshIdx = 0, m = f->meshes; m; m = m->next, meshIdx++)
493 {
494 MESH *psMesh = &psModel->mesh[meshIdx];
495
496 psMesh->vertices = m->points;
497 psMesh->faces = m->faces;
498 psMesh->vertexArray = (GLfloat*)malloc(sizeof(GLfloat) * psMesh->vertices * 3);
499 psMesh->indexArray = (GLuint*)malloc(sizeof(GLuint) * psMesh->faces * 3);
500 psMesh->textureArrays = 1; // only one supported from 3DS
501 psMesh->textureArray[0] = (GLfloat*)malloc(sizeof(GLfloat) * psMesh->vertices * 2);
502
503 for (unsigned int i = 0; i < m->points; i++)
504 {
505 Lib3dsVector pos;
506
507 lib3ds_vector_copy(pos, m->pointL[i].pos);
508
509 if (swapYZ)
510 {
511 psMesh->vertexArray[i * 3 + 0] = pos[0] * scaleFactor;
512 psMesh->vertexArray[i * 3 + 1] = pos[2] * scaleFactor;
513 psMesh->vertexArray[i * 3 + 2] = pos[1] * scaleFactor;
514 }
515 else
516 {
517 psMesh->vertexArray[i * 3 + 0] = pos[0] * scaleFactor;
518 psMesh->vertexArray[i * 3 + 1] = pos[1] * scaleFactor;
519 psMesh->vertexArray[i * 3 + 2] = pos[2] * scaleFactor;
520 }
521 }
522
523 for (unsigned int i = 0; i < m->points; i++)
524 {
525 GLfloat *v = &psMesh->textureArray[0][i * 2];
526
527 v[0] = m->texelL[i][0];
528 if (invertUV)
529 {
530 v[1] = 1.0f - m->texelL[i][1];
531 }
532 else
533 {
534 v[1] = m->texelL[i][1];
535 }
536 }
537
538 for (unsigned int i = 0; i < m->faces; ++i)
539 {
540 Lib3dsFace *face = &m->faceL[i];
541 GLuint *v = &psMesh->indexArray[i * 3];
542
543 if (reverseWinding)
544 {
545 v[0] = face->points[2];
546 v[1] = face->points[1];
547 v[2] = face->points[0];
548 }
549 else
550 {
551 v[0] = face->points[0];
552 v[1] = face->points[1];
553 v[2] = face->points[2];
554 }
555 }
556 }
557
558 lib3ds_file_free(f);
559 return psModel;
560 }
561
savePIE(const char * filename,const MODEL * psModel,int pieVersion,int type)562 int QWzmViewer::savePIE(const char *filename, const MODEL *psModel, int pieVersion, int type)
563 {
564 std::fstream file;
565 int i, j, k;
566
567 /* variable ptOffset: Temporary storage of calculated index
568 * for a give face OR vertex in the WZM vertex array.
569 */
570 /* variable ptSetIndex: Index of a vertex in the set
571 * of PIE points for a given PIE level.
572 */
573 // variable polyFlag: Use to compose the flag for a PIE polygon
574 unsigned int ptOffset, ptSetIndex, polyFlag;
575
576 /* Temporary stringstream used to accommodate
577 * the order PIE polygon data is written
578 */
579 std::stringstream animTmpSs;
580
581 MESH *mesh;
582
583 /* variable unitW: Width of the bounding box for
584 * a single tex. anim. "frame" of a polygon.
585 * variable unitH: Same as unitW except represents
586 * the height.
587 */
588 /* variable t: represents the top of the bounding box
589 * when estimating the tex. anim.'s
590 * height.
591 * variable b: Same as t except represents the
592 * bottom.
593 */
594 double unitW, unitH, t, b;
595
596 /* variable ptSet: for creating a unique set
597 * of PIE points for a given PIE level.
598 */
599 std::set< pie_Point> ptSet;
600
601 /* variable textures: Temporary storage of
602 * texture coordinates to avoid redundant
603 * ptOffset calculations.
604 */
605 std::queue< textCoords, std::list<textCoords> > textures;
606
607 file.open(filename, std::fstream::out);
608
609 if (!file.is_open())
610 {
611 qWarning("QWzmViewer::savePIE - Failed to open file.");
612 return -1;
613 }
614
615 file << "PIE " << pieVersion << '\n';
616 file << "TYPE " << std::hex << type << std::dec << "\n";
617
618 // Texture directive:
619 file << "TEXTURE 0 " << psModel->texPath;
620 switch (pieVersion)
621 {
622 case 2:
623 if (psModel->pixmap != NULL)
624 {
625 file << ' ' << psModel->pixmap->h << ' ' << psModel->pixmap->w << '\n';
626 }
627 else
628 {
629 file << " 256 256\n";
630 }
631 break;
632 case 3:
633 file << " 0 0\n";
634 break;
635 default:
636 file.close();
637 qWarning("QWzmViewer::savePIE - Unsupported pie version");
638 return -1;
639 }
640
641 // LEVELS directive
642 file << "LEVELS " << psModel->meshes << '\n';
643
644 // For each WZM mesh a PIE level.
645 for (i = 0; i < psModel->meshes; i++)
646 {
647 ptSet.clear();
648 mesh = &(psModel->mesh[i]);
649
650 // LEVEL directive
651 file << "LEVEL " << i + 1 << "\n";
652
653 // Create the set of unique points.
654 for (j = 0; j < mesh->vertices; j++)
655 {
656 ptSet.insert(pie_Point(mesh->vertexArray[j*VERTICES_PER_TRIANGLE],
657 mesh->vertexArray[j*VERTICES_PER_TRIANGLE+1],
658 mesh->vertexArray[j*VERTICES_PER_TRIANGLE+2]));
659 }
660
661 if (ptSet.size() > pie_MAX_POLYGONS)
662 {
663 file.close();
664 qWarning("QWzmViewer::savePIE - Model has too many vertices to save as PIE.");
665 return -1;
666 }
667
668 // POINTS directive
669 file << "POINTS " << ptSet.size() << '\n';
670
671 // print out all the points in the set
672 std::set< pie_Point>::iterator it;
673 for (it = ptSet.begin(); it != ptSet.end(); it++)
674 {
675 GLfloat x, y, z;
676 it->getXYZ(x, y, z);
677 switch (pieVersion)
678 {
679 case 2:
680 file << '\t' << (int)rintf(x) << ' ' << (int)rintf(y) << ' ' << (int)rintf(z) << '\n';
681 break;
682 case 3:
683 file << '\t' << x << ' ' << y << ' ' << z << '\n';
684 break;
685 }
686 }
687
688 // POLYGONS directive
689 file << "POLYGONS " << mesh->faces << '\n';
690
691 for (j = 0; j < mesh->faces; j++)
692 {
693 polyFlag = iV_IMD_TEX; // Default flag
694 animTmpSs.str(std::string());
695
696 if (mesh->textureArrays > 1)
697 {
698 ptOffset = mesh->indexArray[j*VERTICES_PER_TRIANGLE];
699
700 /*
701 * TODO:
702 * This _if_ statement isn't a robust way of checking
703 * for animations or team colours.
704 */
705 if ((mesh->textureArray[0][ptOffset*2] < mesh->textureArray[1][ptOffset*2])
706 || (mesh->textureArray[0][ptOffset*2+1] < mesh->textureArray[mesh->textureArrays-1][ptOffset*2+1]))
707 {
708 /* Find height and width for team colours
709 * and animations.
710 * TODO: Check that the animations are legal
711 * for the PIE format.
712 */
713 unitH = 0;
714
715 // Try to find width the easy way
716 unitW = mesh->textureArray[1][ptOffset*2] - mesh->textureArray[0][ptOffset*2];
717
718 // Try to find height the easy way
719 for (k = 0; k < mesh->textureArrays - 1; k++)
720 {
721 // Look for wrap around
722 if (mesh->textureArray[k+1][ptOffset*2+1] > mesh->textureArray[k][ptOffset*2+1])
723 {
724 unitH = mesh->textureArray[k+1][ptOffset*2+1] - mesh->textureArray[k][ptOffset*2+1];
725 break;
726 }
727 }
728
729 // If the easy way failed
730 if ( unitH <= 0)
731 {
732 // Find top and bottom, add pixels to the difference for good luck.
733 b = 0; // Search for the bottom starting from the top
734 t = 1; // Search for the top starting from the bottom
735
736 // Try every V to find the min and max
737 for (k = 0; k < VERTICES_PER_TRIANGLE; k++)
738 {
739 ptOffset = mesh->indexArray[j*VERTICES_PER_TRIANGLE+k];
740 if (mesh->textureArray[0][ptOffset*2+1] < t)
741 {
742 t = mesh->textureArray[0][ptOffset*2+1];
743 }
744 if (mesh->textureArray[0][ptOffset*2+1] > b)
745 {
746 b = mesh->textureArray[0][ptOffset*2+1];
747 }
748 }
749
750 unitH = fabs(b - t);
751
752 if (psModel->pixmap != NULL && psModel->pixmap->h != 0)
753 {
754 unitH += 2 / psModel->pixmap->h;
755 }
756 else if (pieVersion == 2)
757 {
758 unitH += 2 / OLD_TEXTURE_SIZE_FIX;
759 }
760 }
761
762 /* If the animations wraps around for each frame
763 * then the easy way of finding width would fail.
764 */
765 if (unitW <= 0 && unitH ==0)
766 {
767 qWarning("QWzmViewer::savePIE - Texture animation appears to be illegal for PIE format.");
768 file.close();
769 return -1;
770 }
771 else if ( unitW <=0)
772 {
773 unitW = 1;
774 }
775
776 switch (pieVersion)
777 {
778 // TODO: Get playback rate and replace the literal " 1 " with it.
779 case 2:
780 animTmpSs << mesh->textureArrays << " 1 " << (int)rintf(OLD_TEXTURE_SIZE_FIX*unitW)
781 << ' ' << (int)rintf(OLD_TEXTURE_SIZE_FIX*unitH);
782 break;
783 case 3:
784 animTmpSs << mesh->textureArrays << " 1 " << (GLfloat)unitW << ' ' << (GLfloat)unitH;
785 break;
786 }
787 polyFlag |= iV_IMD_TEXANIM;
788 }
789 }
790
791 // Write out the flag followed by the number of vertices
792 file << "\t" << std::hex << polyFlag << std::dec << ' ' << VERTICES_PER_TRIANGLE; // Triangles only
793
794 // Write the three polygon indices
795 for (k = 0; k < VERTICES_PER_TRIANGLE; k++)
796 {
797 ptOffset = mesh->indexArray[j*VERTICES_PER_TRIANGLE+k];
798
799 pie_Point thisPoint(mesh->vertexArray[ptOffset*VERTICES_PER_TRIANGLE],
800 mesh->vertexArray[ptOffset*VERTICES_PER_TRIANGLE+1],
801 mesh->vertexArray[ptOffset*VERTICES_PER_TRIANGLE+2]);
802
803 ptSetIndex = distance(ptSet.begin(), ptSet.find(thisPoint));
804
805 if (ptSetIndex == ptSet.size())
806 {
807 file.close();
808 qWarning("QWzmViewer::savePIE - Internal error: Failed to find vertex in set.");
809 return -1;
810 }
811 else
812 {
813 file << ' ' << ptSetIndex;
814
815 // Store the texture coordinates for later
816 textures.push(textCoords(mesh->textureArray[0][ptOffset*2],
817 mesh->textureArray[0][ptOffset*2+1]));
818 }
819 }
820
821 // If the polygon has team colours or animations, write that data now
822 if (polyFlag & iV_IMD_TEXANIM)
823 {
824 file << ' ' << animTmpSs.str();
825 }
826
827 // Write out all the texture coordinates
828 while (!textures.empty())
829 {
830 switch (pieVersion)
831 {
832 case 2:
833 file << ' ' << (int)rintf(OLD_TEXTURE_SIZE_FIX*textures.front().u);
834 file << ' ' << (int)rintf(OLD_TEXTURE_SIZE_FIX*textures.front().v);
835 break;
836 case 3:
837 file << ' ' << textures.front().u << ' ' << textures.front().v;
838 break;
839 }
840 textures.pop();
841 }
842 file << '\n';
843 }
844
845 // In PIE the CONNECTORS directive is optional
846 if (mesh->connectors > 0)
847 {
848 // CONNECTORS directive
849 file << "CONNECTORS " << mesh->connectors << "\n";
850
851 for (j = 0; j < mesh->connectors; j++)
852 {
853 switch (pieVersion)
854 {
855 case 2:
856 file << "\t" << (int)rintf(mesh->connectorArray[j].pos.x)
857 << ' ' << (int)rintf(mesh->connectorArray[j].pos.y)
858 << ' ' << (int)rintf(mesh->connectorArray[j].pos.z) << '\n';
859 break;
860 case 3:
861 file << "\t" << mesh->connectorArray[j].pos.x
862 << ' ' << mesh->connectorArray[j].pos.y
863 << ' ' << mesh->connectorArray[j].pos.z << '\n';
864 break;
865 }
866 }
867 }
868 }
869
870 if(file.bad())
871 {
872 file.close();
873 return -1;
874 }
875 file.close();
876 return 0;
877 }
878
QPieExportDialog(QWidget * parent)879 QPieExportDialog::QPieExportDialog(QWidget *parent)
880 : QDialog(parent), Ui_PieExportDialog()
881 {
882 setupUi(this);
883 }
884
~QPieExportDialog()885 QPieExportDialog::~QPieExportDialog()
886 {
887 }
888
getPieVersion()889 int QPieExportDialog::getPieVersion()
890 {
891 return cbb_pieversion->currentIndex() + 2;
892 }
893
getFlags()894 int QPieExportDialog::getFlags()
895 {
896 int retVal = 0;
897 if (cb_TCMask->checkState())
898 {
899 retVal |= iV_IMD_TCMASK;
900 }
901 if (cb_Tex->checkState())
902 {
903 retVal |= iV_IMD_TEX;
904 }
905 if (cb_Other->checkState())
906 {
907 retVal |= sb_OtherFlags->value();
908 }
909 return retVal;
910 }
911