1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 #include "common.h"
14
15 // implementatino header
16 #include "MeshObstacle.h"
17
18 // system headers
19 #include <math.h>
20 #include <stdlib.h>
21
22 // common headers
23 #include "global.h"
24 #include "Pack.h"
25 #include "MeshFace.h"
26 #include "CollisionManager.h"
27 #include "Intersect.h"
28 #include "MeshDrawInfo.h"
29 #include "MeshTransform.h"
30 #include "StateDatabase.h"
31
32 // local headers
33 #include "Triangulate.h"
34
35
36 const char* MeshObstacle::typeName = "MeshObstacle";
37
38
MeshObstacle()39 MeshObstacle::MeshObstacle()
40 {
41 faceCount = faceSize = 0;
42 faces = NULL;
43 checkCount = 0;
44 checkTypes = NULL;
45 checkPoints = NULL;
46 vertexCount = normalCount = 0;
47 vertices = normals = NULL;
48 texcoordCount = 0;
49 texcoords = NULL;
50 noclusters = false;
51 smoothBounce = false;
52 driveThrough = false;
53 ricochet = false;
54 shootThrough = false;
55 inverted = false;
56 drawInfo = NULL;
57 return;
58 }
59
60
cfvec3ListToArray(const std::vector<cfvec3> & list,int & count,afvec3 * & array)61 static void cfvec3ListToArray(const std::vector<cfvec3>& list,
62 int& count, afvec3* &array)
63 {
64 count = list.size();
65 array = new afvec3[count];
66 for (int i = 0; i < count; i++)
67 memcpy (array[i], list[i].data, sizeof(afvec3));
68 return;
69 }
70
arrayToCafvec3List(const afvec3 * array,int count,std::vector<cfvec3> & list)71 static void arrayToCafvec3List(const afvec3* array, int count,
72 std::vector<cfvec3>& list)
73 {
74 list.clear();
75 for (int i = 0; i < count; i++)
76 list.push_back(array[i]);
77 return;
78 }
79
80
MeshObstacle(const MeshTransform & transform,const std::vector<char> & checkTypesL,const std::vector<cfvec3> & checkList,const std::vector<cfvec3> & verticeList,const std::vector<cfvec3> & normalList,const std::vector<cfvec2> & texcoordList,int _faceCount,bool _noclusters,bool bounce,bool drive,bool shoot,bool rico)81 MeshObstacle::MeshObstacle(const MeshTransform& transform,
82 const std::vector<char>& checkTypesL,
83 const std::vector<cfvec3>& checkList,
84 const std::vector<cfvec3>& verticeList,
85 const std::vector<cfvec3>& normalList,
86 const std::vector<cfvec2>& texcoordList,
87 int _faceCount, bool _noclusters,
88 bool bounce, bool drive, bool shoot, bool rico)
89 {
90 unsigned int i;
91
92 // get the transform tool
93 MeshTransform::Tool xformtool(transform);
94 inverted = xformtool.isInverted();
95
96 // copy the info
97 checkTypes = new char[checkTypesL.size()];
98 for (i = 0; i < checkTypesL.size(); i++)
99 checkTypes[i] = checkTypesL[i];
100 cfvec3ListToArray (checkList, checkCount, checkPoints);
101 cfvec3ListToArray (verticeList, vertexCount, vertices);
102 cfvec3ListToArray (normalList, normalCount, normals);
103
104 // modify according to the transform
105 int j;
106 for (j = 0; j < checkCount; j++)
107 xformtool.modifyVertex(checkPoints[j]);
108 for (j = 0; j < vertexCount; j++)
109 xformtool.modifyVertex(vertices[j]);
110 for (j = 0; j < normalCount; j++)
111 xformtool.modifyNormal(normals[j]);
112
113 texcoordCount = texcoordList.size();
114 texcoords = new afvec2[texcoordCount];
115 for (i = 0; i < (unsigned int)texcoordCount; i++)
116 memcpy (texcoords[i], texcoordList[i].data, sizeof(afvec2));
117
118 faceSize = _faceCount;
119 faceCount = 0;
120 faces = new MeshFace*[faceSize];
121
122 noclusters = _noclusters;
123 smoothBounce = bounce;
124 driveThrough = drive;
125 shootThrough = shoot;
126 ricochet = rico;
127
128 drawInfo = NULL;
129
130 return;
131 }
132
133
addFace(const std::vector<int> & _vertices,const std::vector<int> & _normals,const std::vector<int> & _texcoords,const BzMaterial * _material,int phydrv,bool _noclusters,bool bounce,bool drive,bool shoot,bool rico,bool triangulate)134 bool MeshObstacle::addFace(const std::vector<int>& _vertices,
135 const std::vector<int>& _normals,
136 const std::vector<int>& _texcoords,
137 const BzMaterial* _material, int phydrv,
138 bool _noclusters,
139 bool bounce, bool drive, bool shoot, bool rico,
140 bool triangulate)
141 {
142 // protect the face list from overrun
143 if (faceCount >= faceSize)
144 return false;
145
146 // make sure the list lengths are the same
147 unsigned int i;
148 unsigned int count = _vertices.size();
149 if ((count < 3) ||
150 ((_normals.size() > 0) && (_normals.size() != count)) ||
151 ((_texcoords.size() > 0) && (_texcoords.size() != count)))
152 return false;
153
154 // validate the indices
155 for (i = 0; i < _vertices.size(); i++)
156 {
157 if (_vertices[i] >= vertexCount)
158 return false;
159 }
160 for (i = 0; i < _normals.size(); i++)
161 {
162 if (_normals[i] >= normalCount)
163 return false;
164 }
165 for (i = 0; i < _texcoords.size(); i++)
166 {
167 if (_texcoords[i] >= texcoordCount)
168 return false;
169 }
170
171 // use the indices to makes lists of pointers
172 float **v, **n, **t;
173 makeFacePointers(_vertices, _normals, _texcoords, v, n, t);
174
175 // override the flags if they are set for the whole mesh
176 _noclusters = _noclusters || noclusters;
177 bounce = bounce || smoothBounce;
178 drive = drive || driveThrough;
179 shoot = shoot || shootThrough;
180 rico = rico || ricochet;
181
182 // override the triangulation setting depending on count
183 triangulate = triangulate && (count > 3);
184
185 // make the face
186 MeshFace* face;
187 if (triangulate)
188 {
189 // avoid warnings that may not apply
190 int tmpDebugLevel = debugLevel;
191 debugLevel = 0;
192 face = new MeshFace(this, count, v, n, t, _material, phydrv,
193 _noclusters, bounce, drive, shoot, rico);
194 debugLevel = tmpDebugLevel;
195 }
196 else
197 {
198 face = new MeshFace(this, count, v, n, t, _material, phydrv,
199 _noclusters, bounce, drive, shoot, rico);
200 }
201
202 // check its validity
203 if (face->isValid())
204 {
205 faces[faceCount] = face;
206 faceCount++;
207 }
208 else if (triangulate)
209 {
210 // triangulate
211 std::vector<TriIndices> triIndices;
212 triangulateFace(count, v, triIndices);
213 delete face; // delete the old face
214 const unsigned int triSize = triIndices.size();
215 if (triSize == 0)
216 return false;
217 else
218 {
219 // prepare array for extra faces
220 const int extra = (int)(triIndices.size() - 1);
221 MeshFace** tmp = new MeshFace*[faceSize + extra];
222 memcpy(tmp, faces, faceCount * sizeof(MeshFace*));
223 delete[] faces;
224 faces = tmp;
225 faceSize += extra;
226 // add the triangles
227 for (i = 0; i < triSize; i++)
228 {
229 std::vector<int> triV, triN, triT;
230 for (int j = 0; j < 3; j++)
231 {
232 const int index = triIndices[i].indices[j];
233 triV.push_back(_vertices[index]);
234 if (_normals.size() > 0)
235 triN.push_back(_normals[index]);
236 if (_texcoords.size() > 0)
237 triT.push_back(_texcoords[index]);
238 }
239 makeFacePointers(triV, triN, triT, v, n, t);
240 face = new MeshFace(this, 3, v, n, t, _material, phydrv,
241 _noclusters, bounce, drive, shoot, rico);
242 if (face->isValid())
243 {
244 faces[faceCount] = face;
245 faceCount++;
246 }
247 else
248 delete face;
249 }
250 }
251 }
252 else
253 {
254 // just nuke it
255 delete face;
256 return false;
257 }
258
259 return true;
260 }
261
262
makeFacePointers(const std::vector<int> & _vertices,const std::vector<int> & _normals,const std::vector<int> & _texcoords,float ** & v,float ** & n,float ** & t)263 void MeshObstacle::makeFacePointers(const std::vector<int>& _vertices,
264 const std::vector<int>& _normals,
265 const std::vector<int>& _texcoords,
266 float**& v, float**& n, float**& t)
267 {
268 const int count = _vertices.size();
269
270 // use the indices to makes lists of pointers
271 v = new float*[count];
272 n = NULL;
273 t = NULL;
274
275 if (_normals.size() > 0)
276 n = new float*[count];
277 if (_texcoords.size() > 0)
278 t = new float*[count];
279
280 for (int i = 0; i < count; i++)
281 {
282 // invert the vertices if required
283 int index = (inverted ? ((count - 1) - i) : i);
284 v[index] = (float*)vertices[_vertices[i]];
285 if (n != NULL)
286 n[index] = (float*)normals[_normals[i]];
287 if (t != NULL)
288 t[index] = (float*)texcoords[_texcoords[i]];
289 }
290 return;
291 }
292
293
~MeshObstacle()294 MeshObstacle::~MeshObstacle()
295 {
296 delete[] checkTypes;
297 delete[] checkPoints;
298 delete[] vertices;
299 delete[] normals;
300 delete[] texcoords;
301 for (int i = 0; i < faceCount; i++)
302 delete faces[i];
303 delete[] faces;
304 delete drawInfo;
305 return;
306 }
307
308
copyWithTransform(const MeshTransform & xform) const309 Obstacle* MeshObstacle::copyWithTransform(const MeshTransform& xform) const
310 {
311 MeshObstacle* copy;
312 std::vector<char> ctlist;
313 std::vector<cfvec3> clist;
314 std::vector<cfvec3> vlist;
315 std::vector<cfvec3> nlist;
316 std::vector<cfvec2> tlist;
317
318 // empty arrays for copies of pure visual meshes
319 if ((drawInfo != NULL) &&
320 ((faceCount <= 0) || (driveThrough && shootThrough)))
321 {
322 // load blanks for pure visual meshes
323 copy = new MeshObstacle(xform, ctlist, clist,
324 vlist, nlist, tlist, 0, noclusters,
325 smoothBounce, driveThrough, shootThrough, ricochet);
326 }
327 else
328 {
329 int i;
330 for (i = 0; i < checkCount; i++)
331 ctlist.push_back(checkTypes[i]);
332 arrayToCafvec3List(checkPoints, checkCount, clist);
333 arrayToCafvec3List(vertices, vertexCount, vlist);
334 arrayToCafvec3List(normals, normalCount, nlist);
335 for (i = 0; i < texcoordCount; i++)
336 tlist.push_back(texcoords[i]);
337
338 copy = new MeshObstacle(xform, ctlist, clist,
339 vlist, nlist, tlist, faceCount, noclusters,
340 smoothBounce, driveThrough, shootThrough, ricochet);
341
342 for (i = 0; i < faceCount; i++)
343 copyFace(i, copy);
344 }
345
346 copy->finalize();
347
348 return copy;
349 }
350
351
copyFace(int f,MeshObstacle * mesh) const352 void MeshObstacle::copyFace(int f, MeshObstacle* mesh) const
353 {
354 MeshFace* face = faces[f];
355
356 std::vector<int> vlist;
357 std::vector<int> nlist;
358 std::vector<int> tlist;
359 const int vcount = face->getVertexCount();
360 for (int i = 0; i < vcount; i++)
361 {
362 int index;
363 index = ((const afvec3*) face->getVertex(i)) - vertices;
364 vlist.push_back(index);
365
366 if (face->useNormals())
367 {
368 index = ((const afvec3*) face->getNormal(i)) - normals;
369 nlist.push_back(index);
370 }
371 if (face->useTexcoords())
372 {
373 index = ((const afvec2*) face->getTexcoord(i)) - texcoords;
374 tlist.push_back(index);
375 }
376 }
377
378 mesh->addFace(vlist, nlist, tlist, face->getMaterial(),
379 face->getPhysicsDriver(), face->noClusters(),
380 face->isSmoothBounce(),
381 face->isDriveThrough(), face->isShootThrough(),
382 face->canRicochet(), false);
383 return;
384 }
385
386
finalize()387 void MeshObstacle::finalize()
388 {
389 int f;
390
391 // cross-link the face edges - FIXME
392 for (f = 0; f < faceCount; f++)
393 faces[f]->edges = NULL;
394
395 // set the extents
396 for (f = 0; f < faceCount; f++)
397 {
398 const Extents& exts = faces[f]->getExtents();
399 extents.expandToBox(exts);
400 }
401
402 // setup fake obstacle parameters
403 pos[0] = (extents.maxs[0] + extents.mins[0]) / 2.0f;
404 pos[1] = (extents.maxs[1] + extents.mins[1]) / 2.0f;
405 pos[2] = extents.mins[2];
406 size[0] = (extents.maxs[0] - extents.mins[0]) / 2.0f;
407 size[1] = (extents.maxs[1] - extents.mins[1]) / 2.0f;
408 size[2] = (extents.maxs[2] - extents.mins[2]);
409 angle = 0.0f;
410 ZFlip = false;
411
412 return;
413 }
414
415
getType() const416 const char* MeshObstacle::getType() const
417 {
418 return typeName;
419 }
420
421
getClassName()422 const char* MeshObstacle::getClassName() // const
423 {
424 return typeName;
425 }
426
427
setDrawInfo(MeshDrawInfo * di)428 void MeshObstacle::setDrawInfo(MeshDrawInfo* di)
429 {
430 if (drawInfo != NULL)
431 {
432 printf("MeshObstacle::setMeshDrawInfo() already exists\n");
433 exit(1);
434 }
435 else
436 drawInfo = di;
437 return;
438 }
439
440
isValid() const441 bool MeshObstacle::isValid() const
442 {
443 // check the planes
444 /* FIXME - kill the whole thing for one bad face?
445 for (int f = 0; f < faceCount; f++) {
446 if (!faces[f]->isValid()) {
447 return false;
448 }
449 }
450 */
451
452 // now check the vertices
453 for (int v = 0; v < vertexCount; v++)
454 {
455 for (int a = 0; a < 3; a++)
456 {
457 if (fabsf(vertices[v][a]) > maxExtent)
458 return false;
459 }
460 }
461
462 return true;
463 }
464
465
containsPoint(const float point[3]) const466 bool MeshObstacle::containsPoint(const float point[3]) const
467 {
468 // this should use the CollisionManager's rayTest function
469 // const ObsList* olist = COLLISIONMGR.rayTest (&ray, t);
470 return containsPointNoOctree(point);
471 }
472
473
containsPointNoOctree(const float point[3]) const474 bool MeshObstacle::containsPointNoOctree(const float point[3]) const
475 {
476 if (checkCount <= 0)
477 return false;
478
479 int c, f;
480 float dir[3];
481 bool hasOutsides = false;
482
483 for (c = 0; c < checkCount; c++)
484 {
485 if (checkTypes[c] == CheckInside)
486 {
487 vec3sub (dir, checkPoints[c], point);
488 Ray ray(point, dir);
489 bool hitFace = false;
490 for (f = 0; f < faceCount; f++)
491 {
492 const MeshFace* face = faces[f];
493 const float hittime = face->intersect(ray);
494 if ((hittime > 0.0f) && (hittime <= 1.0f))
495 {
496 hitFace = true;
497 break;
498 }
499 }
500 if (!hitFace)
501 return true;
502 }
503 else if (checkTypes[c] == CheckOutside)
504 {
505 hasOutsides = true;
506 vec3sub (dir, point, checkPoints[c]);
507 Ray ray(checkPoints[c], dir);
508 bool hitFace = false;
509 for (f = 0; f < faceCount; f++)
510 {
511 const MeshFace* face = faces[f];
512 const float hittime = face->intersect(ray);
513 if ((hittime > 0.0f) && (hittime <= 1.0f))
514 {
515 hitFace = true;
516 break;
517 }
518 }
519 if (!hitFace)
520 return false;
521 }
522 else
523 {
524 printf ("checkType (%i) is not supported yet\n", checkTypes[c]);
525 exit (1);
526 }
527 }
528
529 return hasOutsides;
530 }
531
532
intersect(const Ray & UNUSED (ray)) const533 float MeshObstacle::intersect(const Ray& UNUSED(ray)) const
534 {
535 return -1.0f; // rays only intersect with mesh faces
536 }
537
538
get3DNormal(const float * UNUSED (p),float * UNUSED (n)) const539 void MeshObstacle::get3DNormal(const float* UNUSED(p), float* UNUSED(n)) const
540 {
541 return; // this should never be called if intersect() is always < 0.0f
542 }
543
544
getNormal(const float * p,float * n) const545 void MeshObstacle::getNormal(const float* p, float* n) const
546 {
547 const afvec3 center = { pos[0], pos[1], pos[2] + (0.5f * size[2]) };
548 afvec3 out;
549 vec3sub (out, p, center);
550 if (out[2] < 0.0f)
551 out[2] = 0.0f;
552 float len = vec3dot(out, out);
553 if (len > 0.0f)
554 {
555 len = 1 / sqrtf(len);
556 n[0] = out[0] * len;
557 n[1] = out[1] * len;
558 n[2] = out[2] * len;
559 }
560 else
561 {
562 n[0] = 0.0f;
563 n[1] = 0.0f;
564 n[2] = 1.0f;
565 }
566
567 return;
568 }
569
570
getHitNormal(const float * UNUSED (oldPos),float UNUSED (oldAngle),const float * p,float UNUSED (_angle),float,float,float UNUSED (height),float * n) const571 bool MeshObstacle::getHitNormal(const float* UNUSED(oldPos), float UNUSED(oldAngle),
572 const float* p, float UNUSED(_angle),
573 float, float, float UNUSED(height),
574 float* n) const
575 {
576 if (n != NULL)
577 getNormal(p, n);
578 return true;
579 }
580
581
inCylinder(const float * p,float UNUSED (radius),float height) const582 bool MeshObstacle::inCylinder(const float* p,
583 float UNUSED(radius), float height) const
584 {
585 const float mid[3] = { p[0], p[1], p[2] + (0.5f * height) };
586 return containsPoint(mid);
587 }
588
589
inBox(const float * p,float UNUSED (_angle),float UNUSED (dx),float UNUSED (dy),float height) const590 bool MeshObstacle::inBox(const float* p, float UNUSED(_angle),
591 float UNUSED(dx), float UNUSED(dy), float height) const
592 {
593 const float mid[3] = { p[0], p[1], p[2] + (0.5f * height) };
594 return containsPoint(mid);
595 }
596
597
inMovingBox(const float *,float,const float * p,float UNUSED (_angle),float UNUSED (dx),float UNUSED (dy),float height) const598 bool MeshObstacle::inMovingBox(const float*, float,
599 const float* p, float UNUSED(_angle),
600 float UNUSED(dx), float UNUSED(dy), float height) const
601 {
602 const float mid[3] = { p[0], p[1], p[2] + (0.5f * height) };
603 return containsPoint(mid);
604 }
605
606
isCrossing(const float * UNUSED (p),float UNUSED (_angle),float UNUSED (dx),float UNUSED (dy),float UNUSED (height),float * UNUSED (plane)) const607 bool MeshObstacle::isCrossing(const float* UNUSED(p), float UNUSED(_angle),
608 float UNUSED(dx), float UNUSED(dy), float UNUSED(height),
609 float* UNUSED(plane)) const
610 {
611 return false; // the MeshFaces should handle this case
612 }
613
614
packSize() const615 int MeshObstacle::packSize() const
616 {
617 int fullSize = 5 * sizeof(int32_t);
618 fullSize += sizeof(char) * checkCount;
619 fullSize += sizeof(afvec3) * checkCount;
620 fullSize += sizeof(afvec3) * vertexCount;
621 fullSize += sizeof(afvec3) * normalCount;
622 fullSize += sizeof(afvec2) * texcoordCount;
623 if ((drawInfo != NULL) && !drawInfo->isCopy())
624 {
625 const int drawInfoPackSize = drawInfo->packSize();
626 // align the packing
627 const int align = sizeof(afvec2);
628 fullSize += align * ((drawInfoPackSize + (align - 1)) / align);
629 // drawInfo fake txcd count
630 fullSize += sizeof(afvec2);
631
632 if (debugLevel >= 4)
633 {
634 logDebugMessage(0,"DrawInfo packSize = %i, align = %i, full = %i\n",
635 drawInfoPackSize,
636 align * ((drawInfoPackSize + (align - 1)) / align),
637 align * ((drawInfoPackSize + (align - 1)) / align) + (int)sizeof(afvec2));
638 }
639 }
640 for (int f = 0; f < faceCount; f++)
641 fullSize += faces[f]->packSize();
642 fullSize += sizeof(unsigned char); // state byte
643 return fullSize;
644 }
645
646
pack(void * buf) const647 void *MeshObstacle::pack(void *buf) const
648 {
649 int i;
650
651 buf = nboPackInt(buf, checkCount);
652 for (i = 0; i < checkCount; i++)
653 {
654 buf = nboPackUByte(buf, checkTypes[i]);
655 buf = nboPackVector(buf, checkPoints[i]);
656 }
657
658 buf = nboPackInt(buf, vertexCount);
659 for (i = 0; i < vertexCount; i++)
660 buf = nboPackVector(buf, vertices[i]);
661
662 buf = nboPackInt(buf, normalCount);
663 for (i = 0; i < normalCount; i++)
664 buf = nboPackVector(buf, normals[i]);
665
666 void* txcdStart = buf;
667 buf = nboPackInt(buf, texcoordCount);
668 for (i = 0; i < texcoordCount; i++)
669 {
670 buf = nboPackFloat(buf, texcoords[i][0]);
671 buf = nboPackFloat(buf, texcoords[i][1]);
672 }
673
674 // pack hidden drawInfo data as extra texture coordinates
675 const bool drawInfoOwner = ((drawInfo != NULL) && !drawInfo->isCopy());
676 if (drawInfoOwner)
677 {
678 void* drawInfoStart = buf;
679 buf = drawInfo->pack(buf);
680 // align the packing
681 const int align = sizeof(afvec2);
682 const int length = (char*)buf - (char*)drawInfoStart;
683 const int missing = (align - (length % align)) % align;
684 for (i = 0; i < missing; i++)
685 buf = nboPackUByte(buf, 0);
686 // bump up the texture coordinate count
687 const int fullLength = ((char*)buf - (char*)drawInfoStart);
688 const int extraTxcds = fullLength / sizeof(afvec2);
689 const int fakeTxcdCount = texcoordCount + extraTxcds + 1;
690 nboPackInt(txcdStart, fakeTxcdCount); // NOTE: 'buf' is not being set
691 // add the drawInfo length at the end
692 buf = nboPackInt(buf, fullLength + sizeof(afvec2));
693 buf = nboPackInt(buf, 0); // for alignment to afvec2
694
695 logDebugMessage(4,"DrawInfo packing: length = %i, missing = %i\n", length, missing);
696 logDebugMessage(4," texcoordCount = %i, fakeTxcdCount = %i, rewindLen = %i\n",
697 texcoordCount, fakeTxcdCount, fullLength + (int)sizeof(afvec2));
698 }
699
700 buf = nboPackInt(buf, faceCount);
701 for (i = 0; i < faceCount; i++)
702 buf = faces[i]->pack(buf);
703
704 // pack the state byte
705 unsigned char stateByte = 0;
706 stateByte |= isDriveThrough() ? (1 << 0) : 0;
707 stateByte |= isShootThrough() ? (1 << 1) : 0;
708 stateByte |= smoothBounce ? (1 << 2) : 0;
709 stateByte |= noclusters ? (1 << 3) : 0;
710 stateByte |= drawInfoOwner ? (1 << 4) : 0;
711 stateByte |= canRicochet() ? (1 << 5) : 0;
712 buf = nboPackUByte(buf, stateByte);
713
714 return buf;
715 }
716
717
unpack(const void * buf)718 const void *MeshObstacle::unpack(const void *buf)
719 {
720 int i;
721 int32_t inTmp;
722
723 buf = nboUnpackInt(buf, inTmp);
724 checkCount = int(inTmp);
725 checkTypes = new char[checkCount];
726 checkPoints = new afvec3[checkCount];
727 for (i = 0; i < checkCount; i++)
728 {
729 unsigned char tmp;
730 buf = nboUnpackUByte(buf, tmp);
731 checkTypes[i] = tmp;
732 buf = nboUnpackVector(buf, checkPoints[i]);
733 }
734
735 buf = nboUnpackInt(buf, inTmp);
736 vertexCount = int(inTmp);
737 vertices = new afvec3[vertexCount];
738 for (i = 0; i < vertexCount; i++)
739 buf = nboUnpackVector(buf, vertices[i]);
740
741 buf = nboUnpackInt(buf, inTmp);
742 normalCount = int(inTmp);
743 normals = new afvec3[normalCount];
744 for (i = 0; i < normalCount; i++)
745 buf = nboUnpackVector(buf, normals[i]);
746
747 buf = nboUnpackInt(buf, inTmp);
748 texcoordCount = int(inTmp);
749 texcoords = new afvec2[texcoordCount];
750 // note where the texture coordinates begin; skip past them
751 const void* texCoordPtr = buf;
752 buf = (const char*)buf + sizeof(float) * 2 * texcoordCount;
753 const char* texcoordEnd = (const char*)buf; // for locating hidden drawInfo data
754
755 buf = nboUnpackInt(buf, inTmp);
756 faceSize = int(inTmp);
757 faces = new MeshFace*[faceSize];
758 faceCount = 0;
759 for (i = 0; i < faceSize; i++)
760 {
761 faces[faceCount] = new MeshFace(this);
762 buf = faces[faceCount]->unpack(buf);
763 if (!faces[faceCount]->isValid())
764 delete faces[faceCount];
765 else
766 faceCount++;
767 }
768
769 // unpack the state byte
770 bool drawInfoOwner;
771 unsigned char stateByte;
772 buf = nboUnpackUByte(buf, stateByte);
773 driveThrough = (stateByte & (1 << 0)) != 0;
774 shootThrough = (stateByte & (1 << 1)) != 0;
775 smoothBounce = (stateByte & (1 << 2)) != 0;
776 noclusters = (stateByte & (1 << 3)) != 0;
777 drawInfoOwner = (stateByte & (1 << 4)) != 0;
778 ricochet = (stateByte & (1 << 5)) != 0;
779
780 if (drawInfoOwner && (vertexCount >= 1))
781 {
782 // remove the extraneous vertex
783 vertexCount--;
784 }
785
786 // unpack hidden drawInfo data as from extra texture coordinates
787 if (drawInfoOwner && (texcoordCount >= 2))
788 {
789 nboUseErrorChecking(false);
790 {
791 const void* drawInfoSize = texcoordEnd - sizeof(afvec2);
792 int32_t rewindLen;
793 nboUnpackInt(drawInfoSize, rewindLen);
794
795 const bool useDrawInfo = BZDB.isTrue("useDrawInfo");
796
797 if (rewindLen <= (int)(texcoordCount * sizeof(afvec2)))
798 {
799 // unpack the drawInfo
800 if (useDrawInfo)
801 {
802 const void* drawInfoData = texcoordEnd - rewindLen;
803 drawInfo = new MeshDrawInfo();
804 drawInfo->unpack(drawInfoData);
805 name = drawInfo->getName(); // get the proxied name
806 }
807
808 // free the bogus texcoords
809 // FIXME - don't allocate storage we won't use
810 const int fakeTxcds = rewindLen / sizeof(afvec2);
811 texcoordCount = texcoordCount - fakeTxcds;
812 afvec2* tmpTxcds = new afvec2[texcoordCount];
813 delete[] texcoords;
814 int ptrDiff = (char*)tmpTxcds - (char*)texcoords;
815 texcoords = tmpTxcds;
816 // unpack actual texcoords
817 for (i = 0; i < texcoordCount; i++)
818 {
819 texCoordPtr = nboUnpackFloat(texCoordPtr, texcoords[i][0]);
820 texCoordPtr = nboUnpackFloat(texCoordPtr, texcoords[i][1]);
821 }
822
823 // setup the drawInfo arrays
824 if (useDrawInfo)
825 {
826 drawInfo->clientSetup(this);
827 if (!drawInfo->isValid())
828 {
829 delete drawInfo;
830 drawInfo = NULL;
831 }
832 }
833
834 // remap the texcoord pointers in the faces
835 for (i = 0; i < faceCount; i++)
836 {
837 MeshFace* face = faces[i];
838 if (face->useTexcoords())
839 {
840 for (int v = 0; v < face->getVertexCount(); v++)
841 {
842 face->texcoords[v] =
843 (float*)((char*)face->texcoords[v] + ptrDiff);
844 }
845 }
846 }
847
848 logDebugMessage(4,"DrawInfo unpacking: fakeTxcds = %i, realTxcds = %i\n",
849 fakeTxcds, texcoordCount);
850 }
851 }
852 nboUseErrorChecking(true);
853 }
854 else
855 {
856 // no "hidden" drawInfo data; just unpack the texcoords
857 for (i = 0; i < texcoordCount; i++)
858 {
859 texCoordPtr = nboUnpackFloat(texCoordPtr, texcoords[i][0]);
860 texCoordPtr = nboUnpackFloat(texCoordPtr, texcoords[i][1]);
861 }
862 }
863
864 finalize();
865
866 return buf;
867 }
868
869
outputFloat(std::ostream & out,float value)870 static void outputFloat(std::ostream& out, float value)
871 {
872 char buffer[32];
873 snprintf(buffer, 30, " %.8f", value);
874 out << buffer;
875 return;
876 }
877
878
print(std::ostream & out,const std::string & indent) const879 void MeshObstacle::print(std::ostream& out, const std::string& indent) const
880 {
881 out << indent << "mesh" << std::endl;
882
883 out << indent << "# faces = " << faceCount << std::endl;
884 out << indent << "# checks = " << checkCount << std::endl;
885 out << indent << "# vertices = " << vertexCount << std::endl;
886 out << indent << "# normals = " << normalCount << std::endl;
887 out << indent << "# texcoords = " << texcoordCount << std::endl;
888 out << indent << "# mins = " << extents.mins[0] << " "
889 << extents.mins[1] << " "
890 << extents.mins[2] << std::endl;
891 out << indent << "# maxs = " << extents.maxs[0] << " "
892 << extents.maxs[1] << " "
893 << extents.maxs[2] << std::endl;
894
895 if (name.size() > 0)
896 out << indent << " name " << name << std::endl;
897
898 if (noclusters)
899 out << indent << " noclusters" << std::endl;
900 if (smoothBounce)
901 out << indent << " smoothBounce" << std::endl;
902 if (driveThrough && shootThrough)
903 out << indent << " passable" << std::endl;
904 else
905 {
906 if (driveThrough)
907 out << indent << " driveThrough" << std::endl;
908 if (shootThrough)
909 out << indent << " shootThrough" << std::endl;
910 }
911 if (ricochet)
912 out << indent << " ricochet" << std::endl;
913
914 int i, j;
915 for (i = 0; i < checkCount; i++)
916 {
917 if (checkTypes[i] == CheckInside)
918 out << indent << " inside";
919 else
920 out << indent << " outside";
921 for (j = 0; j < 3; j++)
922 outputFloat(out, checkPoints[i][j]);
923 out << std::endl;
924 }
925 for (i = 0; i < vertexCount; i++)
926 {
927 out << indent << " vertex";
928 for (j = 0; j < 3; j++)
929 outputFloat(out, vertices[i][j]);
930 out << std::endl;
931 }
932 for (i = 0; i < normalCount; i++)
933 {
934 out << indent << " normal";
935 for (j = 0; j < 3; j++)
936 outputFloat(out, normals[i][j]);
937 out << std::endl;
938 }
939 for (i = 0; i < texcoordCount; i++)
940 {
941 out << indent << " texcoord";
942 for (j = 0; j < 2; j++)
943 outputFloat(out, texcoords[i][j]);
944 out << std::endl;
945 }
946
947 for (int f = 0; f < faceCount; f++)
948 faces[f]->print(out, indent);
949
950 // MeshDrawInfo
951 if ((drawInfo != NULL) && !drawInfo->isCopy())
952 {
953 std::string indent2 = indent + " ";
954 drawInfo->print(out, indent2);
955 }
956
957 out << indent << "end" << std::endl;
958
959 return;
960 }
961
962
printOBJ(std::ostream & out,const std::string & UNUSED (indent)) const963 void MeshObstacle::printOBJ(std::ostream& out, const std::string& UNUSED(indent)) const
964 {
965 // save as OBJ
966 int i;
967
968 out << "# OBJ - start" << std::endl;
969 if (name.size() > 0)
970 out << "o " << name << "_" << getObjCounter() << std::endl;
971 else
972 out << "o unnamed_" << getObjCounter() << std::endl;
973
974 out << "# faces = " << faceCount << std::endl;
975 out << "# vertices = " << vertexCount << std::endl;
976 out << "# normals = " << normalCount << std::endl;
977 out << "# texcoords = " << texcoordCount << std::endl;
978
979 const float* tmp;
980 tmp = extents.mins;
981 out << "# mins = " << tmp[0] << " " << tmp[1] << " " << tmp[2] << std::endl;
982 tmp = extents.maxs;
983 out << "# maxs = " << tmp[0] << " " << tmp[1] << " " << tmp[2] << std::endl;
984
985
986 for (i = 0; i < vertexCount; i++)
987 {
988 out << "v";
989 outputFloat(out, vertices[i][0]);
990 outputFloat(out, vertices[i][1]);
991 outputFloat(out, vertices[i][2]);
992 out << std::endl;
993 }
994 for (i = 0; i < normalCount; i++)
995 {
996 out << "vn";
997 outputFloat(out, normals[i][0]);
998 outputFloat(out, normals[i][1]);
999 outputFloat(out, normals[i][2]);
1000 out << std::endl;
1001 }
1002 for (i = 0; i < texcoordCount; i++)
1003 {
1004 out << "vt";
1005 outputFloat(out, texcoords[i][0]);
1006 outputFloat(out, texcoords[i][1]);
1007 out << std::endl;
1008 }
1009 const BzMaterial* bzmat = NULL;
1010 for (int f = 0; f < faceCount; f++)
1011 {
1012 const MeshFace* face = faces[f];
1013 const BzMaterial* nextMat = face->getMaterial();
1014 if (bzmat != nextMat)
1015 {
1016 bzmat = nextMat;
1017 out << "usemtl ";
1018 MATERIALMGR.printReference(out, bzmat);
1019 out << std::endl;
1020 }
1021 const int vCount = face->getVertexCount();
1022 const bool useNormals = face->useNormals();
1023 const bool useTexcoords = face->useTexcoords();
1024 out << "f";
1025 for (i = 0; i < vCount; i++)
1026 {
1027 int vIndex = (const afvec3*)face->getVertex(i) - vertices;
1028 vIndex = vIndex - vertexCount;
1029 out << " " << vIndex;
1030 if (useTexcoords)
1031 {
1032 int tIndex = (const afvec2*)face->getTexcoord(i) - texcoords;
1033 tIndex = tIndex - texcoordCount;
1034 out << "/" << tIndex;
1035 }
1036 if (useNormals)
1037 {
1038 if (!useTexcoords)
1039 out << "/";
1040 int nIndex = (const afvec3*)face->getNormal(i) - normals;
1041 nIndex = nIndex - normalCount;
1042 out << "/" << nIndex;
1043 }
1044 }
1045 out << std::endl;
1046 }
1047
1048 out << "# OBJ - end" << std::endl << std::endl;
1049
1050 incObjCounter();
1051
1052 return;
1053 }
1054
1055
1056 // Local Variables: ***
1057 // mode: C++ ***
1058 // tab-width: 4 ***
1059 // c-basic-offset: 4 ***
1060 // indent-tabs-mode: nil ***
1061 // End: ***
1062 // ex: shiftwidth=4 tabstop=4
1063