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