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 // Interface
14 #include "ArcObstacle.h"
15 
16 // System interfaces
17 #include <math.h>
18 #include <assert.h>
19 
20 //Common interfaces
21 #include "global.h"
22 #include "Pack.h"
23 #include "PhysicsDriver.h"
24 #include "MeshTransform.h"
25 
26 #include "MeshUtils.h"
27 
28 
29 const char* ArcObstacle::typeName = "ArcObstacle";
30 
31 
ArcObstacle()32 ArcObstacle::ArcObstacle()
33 {
34     return;
35 }
36 
37 
ArcObstacle(const MeshTransform & xform,const float * _pos,const float * _size,float _rotation,float _sweepAngle,float _ratio,const float _texsize[4],bool _useNormals,int _divisions,const BzMaterial * mats[MaterialCount],int physics,bool bounce,bool drive,bool shoot,bool rico)38 ArcObstacle::ArcObstacle(const MeshTransform& xform,
39                          const float* _pos, const float* _size,
40                          float _rotation, float _sweepAngle, float _ratio,
41                          const float _texsize[4], bool _useNormals,
42                          int _divisions, const BzMaterial* mats[MaterialCount],
43                          int physics, bool bounce, bool drive, bool shoot, bool rico)
44 {
45     // common obstace parameters
46     memcpy(pos, _pos, sizeof(pos));
47     memcpy(size, _size, sizeof(size));
48     angle = _rotation;
49     ZFlip = false;
50     driveThrough = drive;
51     shootThrough = shoot;
52     ricochet = rico;
53 
54     // arc specific parameters
55     transform = xform;
56     divisions = _divisions;
57     sweepAngle = _sweepAngle;
58     ratio = _ratio;
59     phydrv = physics;
60     smoothBounce = bounce;
61     useNormals = _useNormals;
62     memcpy(texsize, _texsize, sizeof(texsize));
63     memcpy(materials, mats, sizeof(materials));
64 
65     finalize();
66 
67     return;
68 }
69 
70 
~ArcObstacle()71 ArcObstacle::~ArcObstacle()
72 {
73     return;
74 }
75 
76 
copyWithTransform(const MeshTransform & xform) const77 Obstacle* ArcObstacle::copyWithTransform(const MeshTransform& xform) const
78 {
79     MeshTransform tmpXform = transform;
80     tmpXform.append(xform);
81 
82     ArcObstacle* copy =
83         new ArcObstacle(tmpXform, pos, size, angle, sweepAngle, ratio,
84                         texsize, useNormals, divisions,
85                         const_cast<const BzMaterial**>(materials), phydrv,
86                         smoothBounce, driveThrough, shootThrough, ricochet);
87     return copy;
88 }
89 
90 
getType() const91 const char* ArcObstacle::getType() const
92 {
93     return typeName;
94 }
95 
96 
getClassName()97 const char* ArcObstacle::getClassName() // const
98 {
99     return typeName;
100 }
101 
102 
isValid() const103 bool ArcObstacle::isValid() const
104 {
105     return true;
106 }
107 
108 
isFlatTop() const109 bool ArcObstacle::isFlatTop() const
110 {
111     return true;
112 }
113 
114 
finalize()115 void ArcObstacle::finalize()
116 {
117     return;
118 }
119 
120 
makeMesh()121 MeshObstacle* ArcObstacle::makeMesh()
122 {
123     MeshObstacle* mesh;
124 
125     bool isPie = false;    // has no inside edge
126     bool isCircle = false; // angle of 360 degrees
127     const float minSize = 1.0e-6f; // cheezy / lazy
128 
129     // absolute the sizes
130     float sz[3];
131     sz[0] = fabsf(size[0]);
132     sz[1] = fabsf(size[1]);
133     sz[2] = fabsf(size[2]);
134 
135     // validity checking
136     if ((sz[0] < minSize) || (sz[1] < minSize) || (sz[2] < minSize) ||
137             (fabsf(texsize[0]) < minSize) || (fabsf(texsize[1]) < minSize) ||
138             (fabsf(texsize[2]) < minSize) || (fabsf(texsize[3]) < minSize) ||
139             (ratio < 0.0f) || (ratio > 1.0f))
140         return NULL;
141 
142     // adjust the texture sizes   FIXME: finish texsz[2] & texsz[3]
143     float texsz[4];
144     memcpy (texsz, texsize, sizeof(float[4]));
145     if (texsz[0] < 0.0f)
146     {
147         // unless you want to do elliptic integrals, here's
148         // the Ramanujan approximation for the circumference
149         // of an ellipse  (it will be rounded anyways)
150         const float circ =
151             (float)M_PI * ((3.0f * (sz[0] + sz[1])) -
152                            sqrtf ((sz[0] + (3.0f * sz[1])) * (sz[1] + (3.0f * sz[0]))));
153         // make sure it's an integral number so that the edges line up
154         texsz[0] = -floorf(circ / texsz[0]);
155     }
156     if (texsz[1] < 0.0f)
157         texsz[1] = -(sz[2] / texsz[1]);
158 
159     // setup the angles
160     float r = getRotation();
161     float a = sweepAngle;
162     if (a > +360.0f)
163         a = +360.0f;
164     if (a < -360.0f)
165         a = -360.0f;
166     a = a * (float)(M_PI / 180.0); // convert to radians
167     if (a < 0.0f)
168     {
169         r = r + a;
170         a = -a;
171     }
172 
173     // more validity checking
174     if (divisions <= (int) ((a + minSize) / M_PI))
175         return NULL;
176 
177     if (fabsf ((float)M_PI - fmodf (a + (float)M_PI, (float)M_PI * 2.0f)) < minSize)
178         isCircle = true;
179 
180     // setup the radii
181     float inrad = sz[0] * (1.0f - ratio);
182     float outrad = sz[0];
183     if (inrad > outrad)
184     {
185         const float tmp = inrad;
186         inrad = outrad;
187         outrad = tmp;
188     }
189     if ((outrad < minSize) || ((outrad - inrad) < minSize))
190         return NULL;
191     if (inrad < minSize)
192         isPie = true;
193     const float squish = sz[1] / sz[0];
194 
195     if (isPie)
196         mesh = makePie(isCircle, a, r, sz[2], outrad, squish, texsz);
197     else
198         mesh = makeRing(isCircle, a, r, sz[2], inrad, outrad, squish, texsz);
199 
200     // wrap it up
201     mesh->finalize();
202 
203     if (mesh->isValid())
204         return mesh;
205     else
206     {
207         delete mesh;
208         return NULL;
209     }
210 }
211 
212 
makePie(bool isCircle,float a,float r,float h,float radius,float squish,float texsz[4])213 MeshObstacle* ArcObstacle::makePie(bool isCircle, float a, float r,
214                                    float h, float radius, float squish,
215                                    float texsz[4])
216 {
217     MeshObstacle* mesh;
218     int i;
219 
220     // setup the coordinates
221     std::vector<char> checkTypes;
222     std::vector<cfvec3> checkPoints;
223     std::vector<cfvec3> vertices;
224     std::vector<cfvec3> normals;
225     std::vector<cfvec2> texcoords;
226     cfvec3 v, n;
227     cfvec2 t;
228 
229     // add the checkpoint (one is sufficient)
230     if (isCircle)
231     {
232         v[0] = pos[0];
233         v[1] = pos[1];
234     }
235     else
236     {
237         const float dir = r + (0.5f * a);
238         v[0] = pos[0] + (cosf(dir) * radius * 0.5f);
239         v[1] = pos[1] + (sinf(dir) * radius * 0.5f * squish);
240     }
241     v[2] = pos[2] + (0.5f * fabsf(size[2]));
242     checkPoints.push_back(v);
243     checkTypes.push_back(MeshObstacle::CheckInside);
244 
245     // setup the texsize across the disc
246     if (texsz[2] < 0.0f)
247         texsz[2] = -((2.0f * radius) / texsz[2]);
248     if (texsz[3] < 0.0f)
249         texsz[3] = -((2.0f * radius * squish) / texsz[3]);
250 
251     const float astep = a / (float) divisions;
252 
253     for (i = 0; i < (divisions + 1); i++)
254     {
255         float ang = r + (astep * (float)i);
256         float cos_val = cosf(ang);
257         float sin_val = sinf(ang);
258 
259         // vertices and normals
260         if (!isCircle || (i != divisions))
261         {
262             float delta[2];
263             delta[0] = cos_val * radius;
264             delta[1] = (sin_val * radius) * squish;
265             // vertices
266             v[0] = pos[0] + delta[0];
267             v[1] = pos[1] + delta[1];
268             v[2] = pos[2];
269             vertices.push_back(v);
270             v[2] = v[2] + h;
271             vertices.push_back(v);
272             // normal
273             if (useNormals)
274             {
275                 n[2] = 0.0f;
276                 n[0] = cos_val * squish;
277                 n[1] = sin_val;
278                 float len = 1.0f / sqrtf((n[0] * n[0]) + (n[1] * n[1]));
279                 n[0] = n[0] * len;
280                 n[1] = n[1] * len;
281                 normals.push_back(n);
282             }
283         }
284 
285         // texture coordinates (around the edge)
286         t[0] = (float) i / (float) divisions;
287         t[0] = texsz[0] * t[0];
288         t[1] = 0.0f;
289         texcoords.push_back(t);
290         // outside texcoord
291         t[1] = texsz[1] * 1.0f;
292         texcoords.push_back(t);
293     }
294 
295     // texture coordinates (around the disc)
296     for (i = 0; i < (divisions + 1); i++)
297     {
298         float ang = astep * (float)i;
299         float cos_val = cosf(ang);
300         float sin_val = sinf(ang);
301         t[0] = texsz[2] * (0.5f + (0.5f * cos_val));
302         t[1] = texsz[3] * (0.5f + (0.5f * sin_val));
303         texcoords.push_back(t);
304     }
305 
306     // the central coordinates
307     v[0] = pos[0];
308     v[1] = pos[1];
309     v[2] = pos[2];
310     vertices.push_back(v); // bottom
311     v[2] = pos[2] + h;
312     vertices.push_back(v); // top
313     t[0] = texsz[2] * 0.5f;
314     t[1] = texsz[3] * 0.5f;
315     texcoords.push_back(t);
316 
317     // setup the face count
318     int fcount = (divisions * 3);
319     if (!isCircle)
320     {
321         fcount = fcount + 2; // add the start and end faces
322     }
323 
324     mesh = new MeshObstacle(transform, checkTypes, checkPoints,
325                             vertices, normals, texcoords, fcount,
326                             false, smoothBounce, driveThrough, shootThrough, ricochet);
327 
328     // now make the faces
329     int vlen, nlen;
330     if (isCircle)
331     {
332         vlen = divisions * 2;
333         nlen = divisions;
334     }
335     else
336     {
337         vlen = (divisions + 1) * 2;
338         nlen = (divisions + 1);
339     }
340 
341     const int vtop = vlen + 1;
342     const int vbot = vlen;
343     const int tmid = ((divisions + 1) * 3);
344 
345     std::vector<int> vlist;
346     std::vector<int> nlist;
347     std::vector<int> tlist;
348 
349     for (i = 0; i < divisions; i++)
350     {
351 
352 // handy macros
353 #define PV(x) (((x) + (i * 2)) % vlen)
354 #define PN(x) (((x) + i) % nlen)
355 #define PTO(x) ((x) + (i * 2))           // outside edge
356 #define PTC(x) (((divisions + 1) * 2) + (x) + i)   // around the disc
357 #define PTCI(x) (((divisions + 1) * 3) - (x) - i - 1)
358 
359         // outside
360         push4Ints(vlist, PV(0), PV(2), PV(3), PV(1));
361         if (useNormals) push4Ints(nlist, PN(0), PN(1), PN(1), PN(0));
362         push4Ints(tlist, PTO(0), PTO(2), PTO(3), PTO(1));
363         addFace(mesh, vlist, nlist, tlist, materials[Outside_], phydrv);
364 
365         // top
366         push3Ints(vlist, vtop, PV(1), PV(3));
367         push3Ints(tlist, tmid, PTC(0), PTC(1));
368         addFace(mesh, vlist, nlist, tlist, materials[Top], phydrv);
369 
370         // bottom
371         push3Ints(vlist, vbot, PV(2), PV(0));
372         push3Ints(tlist, tmid, PTCI(1), PTCI(0));
373         addFace(mesh, vlist, nlist, tlist, materials[Bottom], phydrv);
374     }
375 
376 
377     if (!isCircle)
378     {
379         int tc = (divisions * 2);
380         // start face
381         push4Ints(vlist, vbot, 0, 1, vtop);
382         push4Ints(tlist, 0, tc + 0, tc + 1, 1);
383         addFace(mesh, vlist, nlist, tlist, materials[StartFace], phydrv);
384 
385         // end face
386         int e = divisions * 2;
387         push4Ints(vlist, e + 0, vbot, vtop, e + 1);
388         push4Ints(tlist, 0, tc + 0, tc + 1, 1);
389         addFace(mesh, vlist, nlist, tlist, materials[EndFace], phydrv);
390     }
391 
392     return mesh;
393 }
394 
395 
makeRing(bool isCircle,float a,float r,float h,float inrad,float outrad,float squish,float texsz[4])396 MeshObstacle* ArcObstacle::makeRing(bool isCircle, float a, float r,
397                                     float h, float inrad, float outrad,
398                                     float squish, float texsz[4])
399 {
400     MeshObstacle* mesh;
401 
402     // setup the coordinates
403     std::vector<char> checkTypes;
404     std::vector<cfvec3> checkPoints;
405     std::vector<cfvec3> vertices;
406     std::vector<cfvec3> normals;
407     std::vector<cfvec2> texcoords;
408     cfvec3 v, n;
409     cfvec2 t;
410 
411     // add the checkpoints (very wasteful)
412     v[0] = pos[0];
413     v[1] = pos[1];
414     const float height = fabsf(size[2]);
415     if (pos[2] > 0.0f)
416     {
417         // down
418         v[2] = pos[2] - (1.0f * height);
419         checkPoints.push_back(v);
420         checkTypes.push_back(MeshObstacle::CheckOutside);
421         // up
422         v[2] = pos[2] + (2.0f * height);
423         checkPoints.push_back(v);
424         checkTypes.push_back(MeshObstacle::CheckOutside);
425     }
426     else
427     {
428         // up
429         v[2] = pos[2] + (2.0f * height);
430         checkPoints.push_back(v);
431         checkTypes.push_back(MeshObstacle::CheckOutside);
432         // down
433         v[2] = pos[2] - (1.0f * height);
434         checkPoints.push_back(v);
435         checkTypes.push_back(MeshObstacle::CheckOutside);
436     }
437     // east
438     v[2] = pos[2] + (0.5f * height);
439     v[0] = pos[0] + (outrad * 2.0f);
440     checkPoints.push_back(v);
441     checkTypes.push_back(MeshObstacle::CheckOutside);
442     // west
443     v[0] = pos[0] - (outrad * 2.0f);
444     checkPoints.push_back(v);
445     checkTypes.push_back(MeshObstacle::CheckOutside);
446     // north
447     v[0] = pos[0];
448     v[1] = pos[1] + (outrad * squish * 2.0f);
449     checkPoints.push_back(v);
450     checkTypes.push_back(MeshObstacle::CheckOutside);
451     // south
452     v[1] = pos[1] - (outrad * squish * 2.0f);
453     checkPoints.push_back(v);
454     checkTypes.push_back(MeshObstacle::CheckOutside);
455 
456     int i;
457     const float astep = a / (float) divisions;
458 
459     for (i = 0; i < (divisions + 1); i++)
460     {
461         float ang = r + (astep * (float)i);
462         float cos_val = cosf(ang);
463         float sin_val = sinf(ang);
464 
465         // vertices and normals
466         if (!isCircle || (i != divisions))
467         {
468             // inside points
469             v[0] = pos[0] + (cos_val * inrad);
470             v[1] = pos[1] + (squish * (sin_val * inrad));
471             v[2] = pos[2];
472             vertices.push_back(v);
473             v[2] = v[2] + h;
474             vertices.push_back(v);
475             // outside points
476             v[0] = pos[0] + (cos_val * outrad);
477             v[1] = pos[1] + (squish * (sin_val * outrad));
478             v[2] = pos[2];
479             vertices.push_back(v);
480             v[2] = v[2] + h;
481             vertices.push_back(v);
482             // inside normal
483             if (useNormals)
484             {
485                 n[2] = 0.0f;
486                 n[0] = -cos_val * squish;
487                 n[1] = -sin_val;
488                 float len = 1.0f / sqrtf((n[0] * n[0]) + (n[1] * n[1]));
489                 n[0] = n[0] * len;
490                 n[1] = n[1] * len;
491                 normals.push_back(n);
492                 // outside normal
493                 n[0] = -n[0];
494                 n[1] = -n[1];
495                 normals.push_back(n);
496             }
497         }
498 
499         // texture coordinates
500         // inside texcoord
501         t[0] = (float) i / (float) divisions;
502         t[0] = texsz[0] * t[0];
503         t[1] = 0.0f;
504         texcoords.push_back(t);
505         // outside texcoord
506         t[1] = texsz[1] * 1.0f;
507         texcoords.push_back(t);
508     }
509 
510     // setup the face count
511     int fcount = (divisions * 4);
512     if (!isCircle)
513     {
514         fcount = fcount + 2; // add the start and end faces
515     }
516 
517     mesh = new MeshObstacle(transform, checkTypes, checkPoints,
518                             vertices, normals, texcoords, fcount,
519                             false, smoothBounce, driveThrough, shootThrough, ricochet);
520 
521     // now make the faces
522     int vlen, nlen;
523     if (isCircle)
524     {
525         vlen = divisions * 4;
526         nlen = divisions * 2;
527     }
528     else
529     {
530         vlen = (divisions + 1) * 4;
531         nlen = (divisions + 1) * 2;
532     }
533 
534     std::vector<int> vlist;
535     std::vector<int> nlist;
536     std::vector<int> tlist;
537 
538     for (i = 0; i < divisions; i++)
539     {
540 
541 // handy macros
542 #define RV(x) (((x) + (i * 4)) % vlen)
543 #define RN(x) (((x) + (i * 2)) % nlen)
544 #define RT(x) ((x) + (i * 2))
545 #define RIT(x) ((divisions + ((x)%2))*2 - ((x) + (i * 2)))
546 
547         // inside
548         push4Ints(vlist, RV(4), RV(0), RV(1), RV(5));
549         if (useNormals) push4Ints(nlist, RN(2), RN(0), RN(0), RN(2));
550         push4Ints(tlist, RIT(2), RIT(0), RIT(1), RIT(3));
551         addFace(mesh, vlist, nlist, tlist, materials[Inside], phydrv);
552 
553         // outside
554         push4Ints(vlist, RV(2), RV(6), RV(7), RV(3));
555         if (useNormals) push4Ints(nlist, RN(1), RN(3), RN(3), RN(1));
556         push4Ints(tlist, RT(0), RT(2), RT(3), RT(1));
557         addFace(mesh, vlist, nlist, tlist, materials[Outside_], phydrv);
558 
559         // top
560         push4Ints(vlist, RV(3), RV(7), RV(5), RV(1));
561         push4Ints(tlist, RT(0), RT(2), RT(3), RT(1));
562         addFace(mesh, vlist, nlist, tlist, materials[Top], phydrv);
563 
564         // bottom
565         push4Ints(vlist, RV(0), RV(4), RV(6), RV(2));
566         push4Ints(tlist, RT(0), RT(2), RT(3), RT(1));
567         addFace(mesh, vlist, nlist, tlist, materials[Bottom], phydrv);
568     }
569 
570     if (!isCircle)
571     {
572         int tc = (divisions * 2);
573         // start face
574         push4Ints(vlist, 0, 2, 3, 1);
575         push4Ints(tlist, 0, tc + 0, tc + 1, 1);
576         addFace(mesh, vlist, nlist, tlist, materials[StartFace], phydrv);
577 
578         // end face
579         int e = divisions * 4;
580         push4Ints(vlist, e + 2, e + 0, e + 1, e + 3);
581         push4Ints(tlist, 0, tc + 0, tc + 1, 1);
582         addFace(mesh, vlist, nlist, tlist, materials[EndFace], phydrv);
583     }
584 
585     return mesh;
586 }
587 
588 
intersect(const Ray &) const589 float ArcObstacle::intersect(const Ray&) const
590 {
591     assert(false);
592     return -1.0f;
593 }
594 
595 
get3DNormal(const float *,float *) const596 void ArcObstacle::get3DNormal(const float*, float*) const
597 {
598     assert(false);
599     return;
600 }
601 
602 
getNormal(const float *,float *) const603 void ArcObstacle::getNormal(const float*, float*) const
604 {
605     assert(false);
606     return;
607 }
608 
609 
getHitNormal(const float *,float,const float *,float,float,float,float,float *) const610 bool ArcObstacle::getHitNormal(const float*, float, const float*, float,
611                                float, float, float, float*) const
612 {
613     assert(false);
614     return false;
615 }
616 
617 
inCylinder(const float *,float,float) const618 bool ArcObstacle::inCylinder(const float*,float, float) const
619 {
620     assert(false);
621     return false;
622 }
623 
624 
inBox(const float *,float,float,float,float) const625 bool ArcObstacle::inBox(const float*, float, float, float, float) const
626 {
627     assert(false);
628     return false;
629 }
630 
631 
inMovingBox(const float *,float,const float *,float,float,float,float) const632 bool ArcObstacle::inMovingBox(const float*, float, const float*, float,
633                               float, float, float) const
634 {
635     assert(false);
636     return false;
637 }
638 
639 
isCrossing(const float * UNUSED (p),float UNUSED (_angle),float UNUSED (dx),float UNUSED (dy),float UNUSED (height),float * UNUSED (_plane)) const640 bool ArcObstacle::isCrossing(const float* UNUSED(p), float UNUSED(_angle),
641                              float UNUSED(dx), float UNUSED(dy), float UNUSED(height),
642                              float* UNUSED(_plane)) const
643 {
644     assert(false);
645     return false;
646 }
647 
648 
pack(void * buf) const649 void* ArcObstacle::pack(void* buf) const
650 {
651     buf = transform.pack(buf);
652     buf = nboPackVector(buf, pos);
653     buf = nboPackVector(buf, size);
654     buf = nboPackFloat(buf, angle);
655     buf = nboPackFloat(buf, sweepAngle);
656     buf = nboPackFloat(buf, ratio);
657     buf = nboPackInt(buf, divisions);
658     buf = nboPackInt(buf, phydrv);
659 
660     int i;
661     for (i = 0; i < 4; i++)
662         buf = nboPackFloat(buf, texsize[i]);
663     for (i = 0; i < MaterialCount; i++)
664     {
665         int matindex = MATERIALMGR.getIndex(materials[i]);
666         buf = nboPackInt(buf, matindex);
667     }
668 
669     // pack the state byte
670     unsigned char stateByte = 0;
671     stateByte |= isDriveThrough() ? (1 << 0) : 0;
672     stateByte |= isShootThrough() ? (1 << 1) : 0;
673     stateByte |= smoothBounce     ? (1 << 2) : 0;
674     stateByte |= useNormals       ? (1 << 3) : 0;
675     stateByte |= canRicochet()    ? (1 << 4) : 0;
676     buf = nboPackUByte(buf, stateByte);
677 
678     return buf;
679 }
680 
681 
unpack(const void * buf)682 const void* ArcObstacle::unpack(const void* buf)
683 {
684     int32_t inTmp;
685     buf = transform.unpack(buf);
686     buf = nboUnpackVector(buf, pos);
687     buf = nboUnpackVector(buf, size);
688     buf = nboUnpackFloat(buf, angle);
689     buf = nboUnpackFloat(buf, sweepAngle);
690     buf = nboUnpackFloat(buf, ratio);
691     buf = nboUnpackInt(buf, inTmp);
692     divisions = int(inTmp);
693     buf = nboUnpackInt(buf, inTmp);
694     phydrv = int(inTmp);
695 
696     int i;
697     for (i = 0; i < 4; i++)
698         buf = nboUnpackFloat(buf, texsize[i]);
699     for (i = 0; i < MaterialCount; i++)
700     {
701         int matindex;
702         buf = nboUnpackInt(buf, inTmp);
703         matindex = int(inTmp);
704         materials[i] = MATERIALMGR.getMaterial(matindex);
705     }
706 
707     // unpack the state byte
708     unsigned char stateByte;
709     buf = nboUnpackUByte(buf, stateByte);
710     driveThrough = (stateByte & (1 << 0)) != 0;
711     shootThrough = (stateByte & (1 << 1)) != 0;
712     smoothBounce = (stateByte & (1 << 2)) != 0;
713     useNormals   = (stateByte & (1 << 3)) != 0;
714     ricochet     = (stateByte & (1 << 4)) != 0;
715 
716     finalize();
717 
718     return buf;
719 }
720 
721 
packSize() const722 int ArcObstacle::packSize() const
723 {
724     int fullSize = transform.packSize();
725     fullSize += sizeof(float[3]);
726     fullSize += sizeof(float[3]);
727     fullSize += sizeof(float);
728     fullSize += sizeof(float);
729     fullSize += sizeof(float);
730     fullSize += sizeof(int32_t);
731     fullSize += sizeof(int32_t);
732     fullSize += sizeof(float[4]);
733     fullSize += sizeof(int32_t[MaterialCount]);
734     fullSize += sizeof(unsigned char);
735     return fullSize;
736 }
737 
738 
print(std::ostream & out,const std::string & indent) const739 void ArcObstacle::print(std::ostream& out, const std::string& indent) const
740 {
741     int i;
742 
743     out << indent << "arc" << std::endl;
744 
745     out << indent << "  position " << pos[0] << " "
746         << pos[1] << " " << pos[2] << std::endl;
747     out << indent << "  size " << size[0] << " "
748         << size[1] << " " << size[2] << std::endl;
749     out << indent << "  rotation " << ((angle * 180.0) / M_PI) << std::endl;
750     out << indent << "  angle " << sweepAngle << std::endl;
751     out << indent << "  ratio " << ratio << std::endl;
752     out << indent << "  divisions " << divisions << std::endl;
753 
754     transform.printTransforms(out, indent);
755 
756     out << indent << "  texsize " << texsize[0] << " " << texsize[1] << " "
757         << texsize[2] << " " << texsize[3]
758         << std::endl;
759 
760     const char* sideNames[MaterialCount] =
761     { "top", "bottom", "inside", "outside", "startside", "endside" };
762     for (i = 0; i < MaterialCount; i++)
763     {
764         out << indent << "  " << sideNames[i] << " matref ";
765         MATERIALMGR.printReference(out, materials[i]);
766         out << std::endl;
767     }
768 
769     const PhysicsDriver* driver = PHYDRVMGR.getDriver(phydrv);
770     if (driver != NULL)
771     {
772         out << indent << "  phydrv ";
773         if (driver->getName().size() > 0)
774             out << driver->getName();
775         else
776             out << phydrv;
777         out << std::endl;
778     }
779 
780     if (smoothBounce)
781         out << indent << "  smoothBounce" << std::endl;
782     if (driveThrough)
783         out << indent << "  driveThrough" << std::endl;
784     if (shootThrough)
785         out << indent << "  shootThrough" << std::endl;
786     if (ricochet)
787         out << indent << "  ricochet" << std::endl;
788     if (!useNormals)
789         out << indent << "  flatshading" << std::endl;
790 
791     out << indent << "end" << std::endl;
792 
793     return;
794 }
795 
796 
797 // Local Variables: ***
798 // mode: C++ ***
799 // tab-width: 4 ***
800 // c-basic-offset: 4 ***
801 // indent-tabs-mode: nil ***
802 // End: ***
803 // ex: shiftwidth=4 tabstop=4
804