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