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 #include <math.h>
15 #include "global.h"
16 #include "Pack.h"
17 #include "BaseBuilding.h"
18 #include "Intersect.h"
19 #include "MeshTransform.h"
20 
21 
22 const char*     BaseBuilding::typeName = "BaseBuilding";
23 
BaseBuilding()24 BaseBuilding::BaseBuilding()
25 {
26 }
27 
BaseBuilding(const float * p,float rotation,const float * _size,int _team,bool rico)28 BaseBuilding::BaseBuilding(const float *p, float rotation,
29                            const float *_size, int _team, bool rico) :
30     Obstacle(p, rotation, _size[0], _size[1], _size[2], false, false, rico),
31     team(_team)
32 {
33     finalize();
34     return;
35 }
36 
~BaseBuilding()37 BaseBuilding::~BaseBuilding()
38 {
39     // do nothing
40 }
41 
finalize()42 void BaseBuilding::finalize()
43 {
44     Obstacle::setExtents();
45     return;
46 }
47 
copyWithTransform(const MeshTransform & xform) const48 Obstacle* BaseBuilding::copyWithTransform(const MeshTransform& xform) const
49 {
50     float newPos[3], newSize[3], newAngle;
51     memcpy(newPos, pos, sizeof(float[3]));
52     memcpy(newSize, size, sizeof(float[3]));
53     newAngle = angle;
54 
55     MeshTransform::Tool tool(xform);
56     bool flipped;
57     tool.modifyOldStyle(newPos, newSize, newAngle, flipped);
58 
59     BaseBuilding* copy = new BaseBuilding(newPos, newAngle, newSize, team, ricochet);
60 
61     return copy;
62 }
63 
getType() const64 const char*     BaseBuilding::getType() const
65 {
66     return typeName;
67 }
68 
getClassName()69 const char*     BaseBuilding::getClassName()
70 {
71     return typeName;
72 }
73 
intersect(const Ray & r) const74 float           BaseBuilding::intersect(const Ray &r) const
75 {
76     return timeRayHitsBlock(r, getPosition(), getRotation(),
77                             getWidth(), getBreadth(), getHeight());
78 }
79 
getNormal(const float * p,float * n) const80 void            BaseBuilding::getNormal(const float *p, float *n) const
81 {
82     getNormalRect(p, getPosition(), getRotation(), getWidth(), getBreadth(), n);
83 }
84 
get3DNormal(const float * p,float * n) const85 void            BaseBuilding::get3DNormal(const float* p, float* n) const
86 {
87     // This bit of cruft causes bullets to bounce of buildings in the z direction
88     if (fabs(p[2] - getPosition()[2]) < Epsilon)
89     {
90         n[0] = 0.0f;
91         n[1] = 0.0f;
92         n[2] = -1.0f;
93     }
94     else if (fabs(p[2] - (getPosition()[2] + getHeight())) < Epsilon)
95     {
96         n[0] = 0.0f;
97         n[1] = 0.0f;
98         n[2] = 1.0f;
99     } // end cruftiness
100     else
101         getNormal(p, n);
102 }
103 
inCylinder(const float * p,float radius,float height) const104 bool            BaseBuilding::inCylinder(const float *p, float radius, float height) const
105 {
106     return (p[2] < (getPosition()[2] + getHeight()))
107            &&     ((p[2]+height) > getPosition()[2])
108            &&     testRectCircle(getPosition(), getRotation(), getWidth(), getBreadth(), p, radius);
109 }
110 
inBox(const float * p,float _angle,float dx,float dy,float height) const111 bool            BaseBuilding::inBox(const float *p, float _angle,
112                                     float dx, float dy, float height) const
113 {
114     return (p[2] < (getPosition()[2] + getHeight()))
115            &&     ((p[2]+height) >= getPosition()[2])
116            &&     testRectRect(getPosition(), getRotation(), getWidth(), getBreadth(),
117                                p, _angle, dx, dy);
118 }
119 
inMovingBox(const float * oldP,float,const float * p,float _angle,float dx,float dy,float height) const120 bool            BaseBuilding::inMovingBox(const float* oldP, float,
121         const float *p, float _angle,
122         float dx, float dy, float height) const
123 {
124     float topBaseHeight = getPosition()[2] + getHeight();
125     float higherZ;
126     float lowerZ;
127     // if a base is just the ground (z == 0 && height == 0) no collision
128     // ground is already handled
129     if (topBaseHeight <= 0.0)
130         return false;
131     if (oldP[2] > p[2])
132     {
133         higherZ = oldP[2];
134         lowerZ  = p[2];
135     }
136     else
137     {
138         higherZ = p[2];
139         lowerZ  = oldP[2];
140     }
141     if (lowerZ >= topBaseHeight)
142         return false;
143     if ((higherZ + height) < getPosition()[2])
144         return false;
145     return testRectRect(getPosition(), getRotation(), getWidth(), getBreadth(),
146                         p, _angle, dx, dy);
147 }
148 
isCrossing(const float * p,float _angle,float dx,float dy,float height,float * plane) const149 bool            BaseBuilding::isCrossing(const float *p, float _angle,
150         float dx, float dy, float height,
151         float *plane) const
152 {
153     // if not inside or contained, then not crossing
154     if (!inBox(p, _angle, dx, dy, height) ||
155             testRectInRect(getPosition(), getRotation(),
156                            getWidth(), getBreadth(), p, _angle, dx, dy))
157         return false;
158     if (!plane) return true;
159 
160     // it's crossing -- choose which wall is being crossed (this
161     // is a guestimate, should really do a careful test). Just
162     // see which wall the point is closest to
163     const float *p2 = getPosition();
164     const float a2  = getRotation();
165     const float c   = cosf(-a2), s = sinf(-a2);
166     const float x   = c * (p[0] - p2[0]) - s * (p[1] - p2[1]);
167     const float y   = c * (p[1] - p2[1]) - s * (p[0] - p2[0]);
168     float pw[2];
169     if (fabsf(fabsf(x) - getWidth()) < fabsf(fabsf(y) - getBreadth()))
170     {
171         plane[0] = ((x < 0.0) ? -cosf(a2) : cosf(a2));
172         plane[1] = ((x < 0.0) ? -sinf(a2) : sinf(a2));
173         pw[0] = p2[0] + getWidth() * plane[0];
174         pw[1] = p2[1] + getWidth() * plane[1];
175     }
176     else
177     {
178         plane[0] = ((y < 0.0) ? sinf(a2) : -sinf(a2));
179         plane[1] = ((y < 0.0) ? cosf(a2) : -cosf(a2));
180         pw[0] = p2[0] + getBreadth() * plane[0];
181         pw[1] = p2[1] + getBreadth() * plane[1];
182     }
183 
184     // now finish off plane equation
185     plane[2] = 0.0;
186     plane[3] = -(plane[0] * pw[0] + plane[1] * pw[1]);
187     return true;
188 }
189 
getHitNormal(const float * pos1,float azimuth1,const float * pos2,float azimuth2,float halfWidth,float halfBreadth,float,float * normal) const190 bool            BaseBuilding::getHitNormal(const float *pos1, float azimuth1,
191         const float *pos2, float azimuth2,
192         float halfWidth, float halfBreadth, float,
193         float *normal) const
194 {
195     return Obstacle::getHitNormal(pos1, azimuth1, pos2, azimuth2, halfWidth, halfBreadth,
196                                   getPosition(), getRotation(), getWidth(), getBreadth(),
197                                   getHeight(), normal) >= 0.0f;
198 }
199 
getCorner(int index,float * _pos) const200 void            BaseBuilding::getCorner(int index, float *_pos) const
201 {
202     const float *base = getPosition();
203     const float c = cosf(getRotation());
204     const float s = sinf(getRotation());
205     const float w = getWidth();
206     const float b = getBreadth();
207     switch (index & 3)
208     {
209     case 0:
210         _pos[0] = base[0] + c * w - s * b;
211         _pos[1] = base[1] + s * w + c * b;
212         break;
213     case 1:
214         _pos[0] = base[0] - c * w - s * b;
215         _pos[1] = base[1] - s * w + c * b;
216         break;
217     case 2:
218         _pos[0] = base[0] - c * w + s * b;
219         _pos[1] = base[1] - s * w - c * b;
220         break;
221     case 3:
222         _pos[0] = base[0] + c * w + s * b;
223         _pos[1] = base[1] + s * w - c * b;
224         break;
225     }
226     _pos[2] = base[2];
227     if (index >= 4) _pos[2] += getHeight();
228 }
229 
getTeam() const230 int BaseBuilding::getTeam() const
231 {
232     return team;
233 }
234 
isFlatTop() const235 bool            BaseBuilding::isFlatTop() const
236 {
237     return true;
238 }
239 
240 
pack(void * buf) const241 void* BaseBuilding::pack(void* buf) const
242 {
243     buf = nboPackUShort(buf, (uint16_t) team);
244 
245     buf = nboPackVector(buf, pos);
246     buf = nboPackFloat(buf, angle);
247     buf = nboPackVector(buf, size);
248 
249     unsigned char stateByte = 0;
250     stateByte |= isDriveThrough() ? _DRIVE_THRU : 0;
251     stateByte |= isShootThrough() ? _SHOOT_THRU : 0;
252     stateByte |= canRicochet()    ? _RICOCHET   : 0;
253     buf = nboPackUByte(buf, stateByte);
254 
255     return buf;
256 }
257 
258 
unpack(const void * buf)259 const void* BaseBuilding::unpack(const void* buf)
260 {
261     uint16_t shortTeam;
262     buf = nboUnpackUShort(buf, shortTeam);
263     team = (int)shortTeam;
264 
265     buf = nboUnpackVector(buf, pos);
266     buf = nboUnpackFloat(buf, angle);
267     buf = nboUnpackVector(buf, size);
268 
269     unsigned char stateByte;
270     buf = nboUnpackUByte(buf, stateByte);
271     driveThrough = (stateByte & _DRIVE_THRU) != 0;
272     shootThrough = (stateByte & _SHOOT_THRU) != 0;
273     ricochet     = (stateByte & _RICOCHET)   != 0;
274 
275     finalize();
276 
277     return buf;
278 }
279 
280 
packSize() const281 int BaseBuilding::packSize() const
282 {
283     int fullSize = 0;
284     fullSize += sizeof(uint16_t); // team
285     fullSize += sizeof(float[3]); // pos
286     fullSize += sizeof(float);    // rotation
287     fullSize += sizeof(float[3]); // size
288     fullSize += sizeof(uint8_t);  // state bits
289     return fullSize;
290 }
291 
292 
print(std::ostream & out,const std::string & indent) const293 void BaseBuilding::print(std::ostream& out, const std::string& indent) const
294 {
295     out << indent << "base" << std::endl;
296     const float *myPos = getPosition();
297     out << indent << "  position " << myPos[0] << " " << myPos[1] << " "
298         << myPos[2] << std::endl;
299     out << indent << "  size " << getWidth() << " " << getBreadth()
300         << " " << getHeight() << std::endl;
301     out << indent << "  rotation " << ((getRotation() * 180.0) / M_PI)
302         << std::endl;
303     out << indent << "  color " << getTeam() << std::endl;
304     if (isPassable())
305         out << indent << "  passable" << std::endl;
306     else
307     {
308         if (isDriveThrough())
309             out << indent << "  drivethrough" << std::endl;
310         if (isShootThrough())
311             out << indent << "  shootthrough" << std::endl;
312     }
313     if (canRicochet())
314         out << indent << "  ricochet" << std::endl;
315     out << indent << "end" << std::endl;
316     return;
317 }
318 
319 
outputFloat(std::ostream & out,float value)320 static void outputFloat(std::ostream& out, float value)
321 {
322     char buffer[32];
323     snprintf(buffer, 30, " %.8f", value);
324     out << buffer;
325     return;
326 }
327 
printOBJ(std::ostream & out,const std::string & UNUSED (indent)) const328 void BaseBuilding::printOBJ(std::ostream& out, const std::string& UNUSED(indent)) const
329 {
330     int i;
331     float verts[8][3] =
332     {
333         {-1.0f, -1.0f, 0.0f},
334         {+1.0f, -1.0f, 0.0f},
335         {+1.0f, +1.0f, 0.0f},
336         {-1.0f, +1.0f, 0.0f},
337         {-1.0f, -1.0f, 1.0f},
338         {+1.0f, -1.0f, 1.0f},
339         {+1.0f, +1.0f, 1.0f},
340         {-1.0f, +1.0f, 1.0f}
341     };
342     float norms[6][3] =
343     {
344         {0.0f, -1.0f, 0.0f}, {+1.0f, 0.0f, 0.0f},
345         {0.0f, +1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f},
346         {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, +1.0f}
347     };
348     float txcds[4][2] =
349     {
350         {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}
351     };
352     MeshTransform xform;
353     const float degrees = getRotation() * (float)(180.0 / M_PI);
354     const float zAxis[3] = {0.0f, 0.0f, +1.0f};
355     xform.addScale(getSize());
356     xform.addSpin(degrees, zAxis);
357     xform.addShift(getPosition());
358     xform.finalize();
359     MeshTransform::Tool xtool(xform);
360     for (i = 0; i < 8; i++)
361         xtool.modifyVertex(verts[i]);
362     for (i = 0; i < 6; i++)
363         xtool.modifyNormal(norms[i]);
364 
365     out << "# OBJ - start base" << std::endl;
366     out << "o bzbase_team" << team << "_" << getObjCounter() << std::endl;
367 
368     for (i = 0; i < 8; i++)
369     {
370         out << "v";
371         outputFloat(out, verts[i][0]);
372         outputFloat(out, verts[i][1]);
373         outputFloat(out, verts[i][2]);
374         out << std::endl;
375     }
376     for (i = 0; i < 4; i++)
377     {
378         out << "vt";
379         outputFloat(out, txcds[i][0]);
380         outputFloat(out, txcds[i][1]);
381         out << std::endl;
382     }
383     for (i = 0; i < 6; i++)
384     {
385         out << "vn";
386         outputFloat(out, norms[i][0]);
387         outputFloat(out, norms[i][1]);
388         outputFloat(out, norms[i][2]);
389         out << std::endl;
390     }
391     out << "usemtl basetop_team" << team << std::endl;
392     out << "f -5/-4/-2 -6/-3/-2 -7/-2/-2 -8/-1/-2" << std::endl;
393     out << "f -4/-4/-1 -3/-3/-1 -2/-2/-1 -1/-1/-1" << std::endl;
394     out << "usemtl basewall_team" << team << std::endl;
395     out << "f -8/-4/-6 -7/-3/-6 -3/-2/-6 -4/-1/-6" << std::endl;
396     out << "f -7/-4/-5 -6/-3/-5 -2/-2/-5 -3/-1/-5" << std::endl;
397     out << "f -6/-4/-4 -5/-3/-4 -1/-2/-4 -2/-1/-4" << std::endl;
398     out << "f -5/-4/-3 -8/-3/-3 -4/-2/-3 -1/-1/-3" << std::endl;
399 
400     out << std::endl;
401 
402     incObjCounter();
403 
404     return;
405 }
406 
407 
408 // Local Variables: ***
409 // mode: C++ ***
410 // tab-width: 4 ***
411 // c-basic-offset: 4 ***
412 // indent-tabs-mode: nil ***
413 // End: ***
414 // ex: shiftwidth=4 tabstop=4
415