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