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 "Teleporter.h"
18 #include "Intersect.h"
19 #include "MeshTransform.h"
20
21
22 const char* Teleporter::typeName = "Teleporter";
23
24
Teleporter()25 Teleporter::Teleporter()
26 {
27 backLink = NULL;
28 frontLink = NULL;
29 return;
30 }
31
32
Teleporter(const float * p,float a,float w,float b,float h,float _border,bool _horizontal,bool drive,bool shoot,bool rico)33 Teleporter::Teleporter(const float* p, float a, float w,
34 float b, float h, float _border, bool _horizontal,
35 bool drive, bool shoot, bool rico) :
36 Obstacle(p, a, w, b, h, drive, shoot, rico),
37 border(_border), horizontal(_horizontal)
38 {
39 finalize();
40 return;
41 }
42
43
~Teleporter()44 Teleporter::~Teleporter()
45 {
46 delete backLink;
47 delete frontLink;
48 return;
49 }
50
51
copyWithTransform(const MeshTransform & xform) const52 Obstacle* Teleporter::copyWithTransform(const MeshTransform& xform) const
53 {
54 float newPos[3], newSize[3], newAngle;
55 memcpy(newPos, pos, sizeof(float[3]));
56 memcpy(newSize, origSize, sizeof(float[3]));
57 newAngle = angle;
58
59 MeshTransform::Tool tool(xform);
60 bool flipped;
61 tool.modifyOldStyle(newPos, newSize, newAngle, flipped);
62
63 Teleporter* copy =
64 new Teleporter(newPos, newAngle, newSize[0], newSize[1], newSize[2],
65 border, horizontal, driveThrough, shootThrough, ricochet);
66
67 copy->setName(name);
68
69 return copy;
70 }
71
finalize()72 void Teleporter::finalize()
73 {
74 origSize[0] = size[0];
75 origSize[1] = size[1];
76 origSize[2] = size[2];
77
78 if (!horizontal)
79 {
80 size[1] = origSize[1] + (border * 2.0f);
81 size[2] = origSize[2] + border;
82 }
83
84 // the same as the default Obstacle::getExtents(), except
85 // that we use the larger of the border half-width and size[0].
86 float sizeX = border * 0.5f;
87 if (size[0] > sizeX)
88 sizeX = size[0];
89 float xspan = (fabsf(cosf(angle)) * sizeX) + (fabsf(sinf(angle)) * size[1]);
90 float yspan = (fabsf(cosf(angle)) * size[1]) + (fabsf(sinf(angle)) * sizeX);
91 extents.mins[0] = pos[0] - xspan;
92 extents.maxs[0] = pos[0] + xspan;
93 extents.mins[1] = pos[1] - yspan;
94 extents.maxs[1] = pos[1] + yspan;
95 extents.mins[2] = pos[2];
96 extents.maxs[2] = pos[2] + size[2];
97
98 makeLinks();
99
100 return;
101 }
102
103
makeLinks()104 void Teleporter::makeLinks()
105 {
106 int i;
107
108 // make the new pointers to floats,
109 // the MeshFace will delete[] them
110 float **fvrts = new float*[4];
111 float **bvrts = new float*[4];
112 float **ftxcds = new float*[4];
113 float **btxcds = new float*[4];
114 for (i = 0; i < 4; i++)
115 {
116 fvrts[i] = fvertices[i];
117 bvrts[i] = bvertices[i];
118 ftxcds[i] = texcoords[i];
119 btxcds[i] = texcoords[i];
120 }
121
122 // get the basics
123 const float* p = getPosition();
124 const float a = getRotation();
125 const float w = getWidth();
126 const float b = getBreadth();
127 const float br = getBorder();
128 const float h = getHeight();
129
130 // setup the texcoord coordinates
131 const float xtxcd = 1.0f;
132 float ytxcd;
133 if ((b - br) > 0.0f)
134 ytxcd = h / (2.0f * (b - br));
135 else
136 ytxcd = 1.0f;
137 texcoords[0][0] = 0.0f;
138 texcoords[0][1] = 0.0f;
139 texcoords[1][0] = xtxcd;
140 texcoords[1][1] = 0.0f;
141 texcoords[2][0] = xtxcd;
142 texcoords[2][1] = ytxcd;
143 texcoords[3][0] = 0.0f;
144 texcoords[3][1] = ytxcd;
145
146 const float cos_val = cosf(a);
147 const float sin_val = sinf(a);
148
149 if (!horizontal)
150 {
151 const float params[4][2] =
152 {{-1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {-1.0f, 1.0f}};
153 float wlen[2] = { (cos_val * w), (sin_val * w) };
154 float blen[2] = { (-sin_val * (b - br)), (cos_val * (b - br)) };
155
156 for (i = 0; i < 4 ; i++)
157 {
158 bvrts[i][0] = p[0] + (wlen[0] + (blen[0] * params[i][0]));
159 bvrts[i][1] = p[1] + (wlen[1] + (blen[1] * params[i][0]));
160 bvrts[i][2] = p[2] + ((h - br) * params[i][1]);
161 }
162 backLink = new MeshFace(NULL, 4, bvrts, NULL, btxcds,
163 NULL, -1, false, false, true, true, false);
164
165 for (i = 0; i < 4 ; i++)
166 {
167 fvrts[i][0] = p[0] - (wlen[0] + (blen[0] * params[i][0]));
168 fvrts[i][1] = p[1] - (wlen[1] + (blen[1] * params[i][0]));
169 fvrts[i][2] = p[2] + ((h - br) * params[i][1]);
170 }
171 frontLink = new MeshFace(NULL, 4, fvrts, NULL, ftxcds,
172 NULL, -1, false, false, true, true, false);
173 }
174 else
175 {
176 float xlen = w - br;
177 float ylen = b - br;
178 bvrts[0][0] = p[0] + ((cos_val * xlen) - (sin_val * ylen));
179 bvrts[0][1] = p[1] + ((cos_val * ylen) + (sin_val * xlen));
180 bvrts[0][2] = p[2] + h - br;
181 bvrts[1][0] = p[0] + ((cos_val * xlen) - (sin_val * -ylen));
182 bvrts[1][1] = p[1] + ((cos_val * -ylen) + (sin_val * xlen));
183 bvrts[1][2] = p[2] + h - br;
184 bvrts[2][0] = p[0] + ((cos_val * -xlen) - (sin_val * -ylen));
185 bvrts[2][1] = p[1] + ((cos_val * -ylen) + (sin_val * -xlen));
186 bvrts[2][2] = p[2] + h - br;
187 bvrts[3][0] = p[0] + ((cos_val * -xlen) - (sin_val * ylen));
188 bvrts[3][1] = p[1] + ((cos_val * ylen) + (sin_val * -xlen));
189 bvrts[3][2] = p[2] + h - br;
190 backLink = new MeshFace(NULL, 4, bvrts, NULL, btxcds,
191 NULL, -1, false, false, true, true, false);
192
193 for (i = 0; i < 4; i++)
194 {
195 memcpy(fvrts[i], bvrts[3 - i], sizeof(float[3])); // reverse order
196 fvrts[i][2] = p[2] + h; // change the height
197 }
198 frontLink = new MeshFace(NULL, 4, fvrts, NULL, ftxcds,
199 NULL, -1, false, false, true, true, false);
200 }
201
202 return;
203 }
204
205
getType() const206 const char* Teleporter::getType() const
207 {
208 return typeName;
209 }
210
211
getClassName()212 const char* Teleporter::getClassName() // const
213 {
214 return typeName;
215 }
216
217
isValid() const218 bool Teleporter::isValid() const
219 {
220 if (!backLink->isValid() || !frontLink->isValid())
221 return false;
222 return Obstacle::isValid();
223 }
224
225
intersect(const Ray & r) const226 float Teleporter::intersect(const Ray& r) const
227 {
228 // expand to include border
229 return timeRayHitsBlock(r, getPosition(), getRotation(),
230 getWidth(), getBreadth(), getHeight());
231 }
232
233
getNormal(const float * p1,float * n) const234 void Teleporter::getNormal(const float* p1, float* n) const
235 {
236 // get normal to closest border column (assume column is circular)
237 const float* p2 = getPosition();
238 const float c = cosf(-getRotation());
239 const float s = sinf(-getRotation());
240 const float b = 0.5f * getBorder();
241 const float d = getBreadth() - b;
242 const float j = (c * (p1[1] - p2[1]) + s * (p1[0] - p2[0]) > 0.0f) ? d : -d;
243 float cc[2];
244 cc[0] = p2[0] + (s * j);
245 cc[1] = p2[1] + (c * j);
246 getNormalRect(p1, cc, getRotation(), b, b, n);
247 }
248
249
inCylinder(const float * p,float radius,float height) const250 bool Teleporter::inCylinder(const float* p, float radius, float height) const
251 {
252 return (p[2]+height) >= getPosition()[2] &&
253 p[2] <= getPosition()[2] + getHeight() &&
254 testRectCircle(getPosition(), getRotation(),
255 getWidth(), getBreadth(), p, radius);
256 }
257
258
inBox(const float * p,float a,float dx,float dy,float dz) const259 bool Teleporter::inBox(const float* p, float a,
260 float dx, float dy, float dz) const
261 {
262 const float tankTop = p[2] + dz;
263 const float teleTop = getExtents().maxs[2];
264 const float crossbarBottom = teleTop - getBorder();
265
266 if ((p[2] < crossbarBottom) && (tankTop >= getPosition()[2]))
267 {
268 // test individual border columns
269 const float c = cosf(getRotation());
270 const float s = sinf(getRotation());
271 const float d = getBreadth() - (0.5f * getBorder());
272 const float r = 0.5f * getBorder();
273 float o[2];
274 o[0] = getPosition()[0] - (s * d);
275 o[1] = getPosition()[1] + (c * d);
276 if (testRectRect(p, a, dx, dy, o, getRotation(), r, r))
277 return true;
278 o[0] = getPosition()[0] + (s * d);
279 o[1] = getPosition()[1] - (c * d);
280 if (testRectRect(p, a, dx, dy, o, getRotation(), r, r))
281 return true;
282 }
283
284 if ((p[2] < teleTop) && (tankTop >= crossbarBottom))
285 {
286 // test crossbar
287 if (testRectRect(p, a, dx, dy, getPosition(),
288 getRotation(), getWidth(), getBreadth()))
289 return true;
290 }
291
292 return false;
293 }
294
295
inMovingBox(const float * oldP,float UNUSED (oldAngle),const float * p,float a,float dx,float dy,float dz) const296 bool Teleporter::inMovingBox(const float* oldP, float UNUSED(oldAngle),
297 const float* p, float a,
298 float dx, float dy, float dz) const
299 {
300 float minPos[3];
301 minPos[0] = p[0];
302 minPos[1] = p[1];
303 if (oldP[2] < p[2])
304 minPos[2] = oldP[2];
305 else
306 minPos[2] = p[2];
307 dz += fabsf(oldP[2] - p[2]);
308 return inBox(minPos, a, dx, dy, dz);
309 }
310
311
isCrossing(const float * p,float a,float dx,float dy,float,float * plane) const312 bool Teleporter::isCrossing(const float* p, float a,
313 float dx, float dy, float /* dz */, float* plane) const
314 {
315 // if not inside or contained then not crossing
316 const float* p2 = getPosition();
317 if (!testRectRect(p, a, dx, dy,
318 p2, getRotation(), getWidth(), getBreadth() - getBorder()) ||
319 p[2] < p2[2] || p[2] > p2[2] + getHeight() - getBorder())
320 return false;
321 if (!plane) return true;
322
323 // it's crossing -- choose which wall is being crossed (this
324 // is a guestimate, should really do a careful test). just
325 // see which wall the point is closest to.
326 const float a2 = getRotation();
327 const float c = cosf(-a2);
328 const float s = sinf(-a2);
329 const float x = c * (p[0] - p2[0]) - s * (p[1] - p2[1]);
330 float pw[2];
331 plane[0] = ((x < 0.0f) ? -cosf(a2) : cosf(a2));
332 plane[1] = ((x < 0.0f) ? -sinf(a2) : sinf(a2));
333 pw[0] = p2[0] + getWidth() * plane[0];
334 pw[1] = p2[1] + getWidth() * plane[1];
335
336 // now finish off plane equation
337 plane[2] = 0.0f;
338 plane[3] = -(plane[0] * pw[0] + plane[1] * pw[1]);
339 return true;
340 }
341
342
343 // return true if ray goes through teleporting part
isTeleported(const Ray & r,int & face) const344 float Teleporter::isTeleported(const Ray& r, int& face) const
345 {
346 // get t's for teleporter with and without border
347 const float tb = intersect(r);
348 const float t = timeRayHitsBlock(r, getPosition(),
349 getRotation(), getWidth(),
350 getBreadth() - getBorder(), getHeight() - getBorder());
351
352 // if intersection with border is before one without then doesn't teleport
353 // (cos it hit the border first). also no teleport if no intersection.
354 if ((tb >= 0.0f && t - tb > 1e-6) || t < 0.0f)
355 return -1.0f;
356
357 // get teleport position. if above or below teleporter then no teleportation.
358 float p[3];
359 r.getPoint(t, p);
360 p[2] -= getPosition()[2];
361 if (p[2] < 0.0f || p[2] > getHeight() - getBorder())
362 return -1.0f;
363
364 // figure out which face: rotate intersection into teleporter space,
365 // if to east of teleporter then face 0 else face 1.
366 const float x = cosf(-getRotation()) * (p[0] - getPosition()[0]) -
367 sinf(-getRotation()) * (p[1] - getPosition()[1]);
368 face = (x > 0.0f) ? 0 : 1;
369 return t;
370 }
371
372
getProximity(const float * p,float radius) const373 float Teleporter::getProximity(const float* p, float radius) const
374 {
375 // make sure tank is sufficiently close
376 if (!testRectCircle(getPosition(), getRotation(),
377 getWidth(), getBreadth() - getBorder(),
378 p, 1.2f * radius))
379 return 0.0f;
380
381 // transform point to teleporter space
382 // translate origin
383 float pa[3];
384 pa[0] = p[0] - getPosition()[0];
385 pa[1] = p[1] - getPosition()[1];
386 pa[2] = p[2] - getPosition()[2];
387
388 // make sure not too far above or below teleporter
389 if (pa[2] < -1.2f * radius ||
390 pa[2] > getHeight() - getBorder() + 1.2f * radius)
391 return 0.0f;
392
393 // rotate and reflect into first quadrant
394 const float c = cosf(-getRotation()), s = sinf(-getRotation());
395 const float x = fabsf(c * pa[0] - s * pa[1]);
396 const float y = fabsf(c * pa[1] + s * pa[0]);
397
398 // get proximity to face
399 float t = 1.2f - x / radius;
400
401 // if along side then trail off as point moves away from faces
402 if (y > getBreadth() - getBorder())
403 {
404 float f = (float)(2.0 / M_PI) * atan2f(x, y - getBreadth() + getBorder());
405 t *= f * f;
406 }
407 else if (pa[2] < 0.0f)
408 {
409 float f = 1.0f + pa[2] / (1.2f * radius);
410 if (f >= 0.0f && f <= 1.0f) t *= f * f;
411 }
412 else if (pa[2] > getHeight() - getBorder())
413 {
414 float f = 1.0f - (pa[2] - getHeight() + getBorder()) / (1.2f * radius);
415 if (f >= 0.0f && f <= 1.0f) t *= f * f;
416 }
417
418 return t > 0.0f ? (t > 1.0f ? 1.0f : t) : 0.0f;
419 }
420
421
hasCrossed(const float * p1,const float * p2,int & f) const422 bool Teleporter::hasCrossed(const float* p1, const float* p2, int& f) const
423 {
424 // check above/below teleporter
425 const float* p = getPosition();
426 if ((p1[2] < p[2] && p2[2] < p[2]) ||
427 (p1[2] > p[2] + getHeight() - getBorder() &&
428 p2[2] > p[2] + getHeight() - getBorder()))
429 return false;
430
431 const float c = cosf(-getRotation()), s = sinf(-getRotation());
432 const float x1 = c * (p1[0] - p[0]) - s * (p1[1] - p[1]);
433 const float x2 = c * (p2[0] - p[0]) - s * (p2[1] - p[1]);
434 const float y2 = c * (p2[1] - p[1]) + s * (p2[0] - p[0]);
435 if (x1 * x2 < 0.0f && fabsf(y2) <= getBreadth() - getBorder())
436 {
437 f = (x1 > 0.0f) ? 0 : 1;
438 return true;
439 }
440 return false;
441 }
442
443
getPointWRT(const Teleporter & t2,int face1,int face2,const float * pIn,const float * dIn,float aIn,float * pOut,float * dOut,float * aOut) const444 void Teleporter::getPointWRT(const Teleporter& t2, int face1, int face2,
445 const float* pIn, const float* dIn, float aIn,
446 float* pOut, float* dOut, float* aOut) const
447 {
448 // transforming pIn to pOut, going from tele1(this) to tele2
449 const Teleporter& tele1 = *this;
450 const Teleporter& tele2 = t2;
451
452 const float* p1 = tele1.getPosition();
453 const float* p2 = tele2.getPosition();
454 const float* s1 = tele1.getSize();
455 const float* s2 = tele2.getSize();
456
457 const fvec3 pos1 (p1[0], p1[1], p1[2]);
458 const fvec3 size1 (s1[0], s1[1], s1[2]);
459 const fvec3 pos2 (p2[0], p2[1], p2[2]);
460 const fvec3 size2 (s2[0], s2[1], s2[2]);
461
462 const float bord1 = tele1.getBorder();
463 const float bord2 = tele2.getBorder();
464
465 // y & z axis scaling factors (relative active areas)
466 const fvec2 dims1(size1.y - bord1, size1.z - bord1);
467 const fvec2 dims2(size2.y - bord2, size2.z - bord2);
468 const fvec2 dimsScale = (dims2 / dims1);
469
470 // adjust the angles for the faces
471 // NOTE: if (face1 == face2), there's an extra 180 degrees spin
472 const float pi = float(M_PI);
473 const float radians1 = tele1.getRotation() + ((face1 == 0) ? 0.0f : pi);
474 const float radians2 = tele2.getRotation() + ((face2 == 1) ? 0.0f : pi);
475
476 // intermediate output position
477 fvec3 p(pIn[0], pIn[1], pIn[2]);
478
479 // translate to origin, and revert rotation
480 p -= pos1;
481 p = p.rotateZ(-radians1);
482
483 // fixed x offset, and scale y & z coordinates
484 p.x = -size2.x;
485 p.y *= dimsScale.x;
486 p.z *= dimsScale.y;
487
488 // apply rotation, translate to new position
489 p = p.rotateZ(+radians2);
490 p += pos2;
491
492 // final output position
493 pOut[0] = p.x;
494 pOut[1] = p.y;
495 pOut[2] = p.z;
496
497 // fill in output angle and direction variables, if requested
498 const float a = radians2 - radians1;
499 if (aOut)
500 *aOut = aIn + a;
501 if (dOut && dIn)
502 {
503 const float c = cosf(a);
504 const float s = sinf(a);
505 const float dx = dIn[0];
506 const float dy = dIn[1];
507 dOut[0] = (c * dx) - (s * dy);
508 dOut[1] = (c * dy) + (s * dx);
509 dOut[2] = dIn[2];
510 }
511 }
512
513
getHitNormal(const float * pos1,float azimuth1,const float * pos2,float azimuth2,float width,float breadth,float,float * normal) const514 bool Teleporter::getHitNormal(const float* pos1, float azimuth1,
515 const float* pos2, float azimuth2,
516 float width, float breadth, float,
517 float* normal) const
518 {
519 return Obstacle::getHitNormal(pos1, azimuth1,
520 pos2, azimuth2, width, breadth,
521 getPosition(), getRotation(), getWidth(), getBreadth(),
522 getHeight(), normal) >= 0.0f;
523 }
524
525
526
pack(void * buf) const527 void* Teleporter::pack(void* buf) const
528 {
529 buf = nboPackStdString(buf, name);
530
531 buf = nboPackVector(buf, pos);
532 buf = nboPackFloat(buf, angle);
533 buf = nboPackVector(buf, origSize);
534 buf = nboPackFloat(buf, border);
535
536 unsigned char horizontalByte = horizontal ? 1 : 0;
537 buf = nboPackUByte(buf, horizontalByte);
538
539 unsigned char stateByte = 0;
540 stateByte |= isDriveThrough() ? _DRIVE_THRU : 0;
541 stateByte |= isShootThrough() ? _SHOOT_THRU : 0;
542 stateByte |= canRicochet() ? _RICOCHET : 0;
543 buf = nboPackUByte(buf, stateByte);
544
545 return buf;
546 }
547
548
unpack(const void * buf)549 const void* Teleporter::unpack(const void* buf)
550 {
551 buf = nboUnpackStdString(buf, name);
552
553 buf = nboUnpackVector(buf, pos);
554 buf = nboUnpackFloat(buf, angle);
555 buf = nboUnpackVector(buf, size);
556 buf = nboUnpackFloat(buf, border);
557
558 unsigned char horizontalByte;
559 buf = nboUnpackUByte(buf, horizontalByte);
560 horizontal = (horizontalByte == 0) ? false : true;
561
562 unsigned char stateByte;
563 buf = nboUnpackUByte(buf, stateByte);
564 driveThrough = (stateByte & _DRIVE_THRU) != 0;
565 shootThrough = (stateByte & _SHOOT_THRU) != 0;
566 ricochet = (stateByte & _RICOCHET) != 0;
567
568 finalize();
569
570 return buf;
571 }
572
573
packSize() const574 int Teleporter::packSize() const
575 {
576 int fullSize = 0;
577 fullSize += nboStdStringPackSize(name);
578 fullSize += sizeof(float[3]); // pos
579 fullSize += sizeof(float); // rotation
580 fullSize += sizeof(float[3]); // size
581 fullSize += sizeof(float); // border
582 fullSize += sizeof(uint8_t); // horizontal
583 fullSize += sizeof(uint8_t); // state bits
584 return fullSize;
585 }
586
587
print(std::ostream & out,const std::string & indent) const588 void Teleporter::print(std::ostream& out, const std::string& indent) const
589 {
590 out << indent << "teleporter";
591 if (name.size() > 0)
592 out << " " << name;
593 out << std::endl;
594 const float *_pos = getPosition();
595 out << indent << " position " << _pos[0] << " " << _pos[1] << " "
596 << _pos[2] << std::endl;
597 out << indent << " size " << origSize[0] << " " << origSize[1] << " "
598 << origSize[2] << std::endl;
599 out << indent << " rotation " << ((getRotation() * 180.0) / M_PI)
600 << std::endl;
601 out << indent << " border " << getBorder() << std::endl;
602 if (horizontal)
603 out << indent << " horizontal" << std::endl;
604 if (ricochet)
605 out << indent << " ricochet" << std::endl;
606 out << indent << "end" << std::endl;
607 return;
608 }
609
610
outputFloat(std::ostream & out,float value)611 static void outputFloat(std::ostream& out, float value)
612 {
613 char buffer[32];
614 snprintf(buffer, 30, " %.8f", value);
615 out << buffer;
616 return;
617 }
618
printOBJ(std::ostream & out,const std::string & UNUSED (indent)) const619 void Teleporter::printOBJ(std::ostream& out, const std::string& UNUSED(indent)) const
620 {
621 int i;
622 float verts[8][3] =
623 {
624 {-1.0f, -1.0f, 0.0f},
625 {+1.0f, -1.0f, 0.0f},
626 {+1.0f, +1.0f, 0.0f},
627 {-1.0f, +1.0f, 0.0f},
628 {-1.0f, -1.0f, 1.0f},
629 {+1.0f, -1.0f, 1.0f},
630 {+1.0f, +1.0f, 1.0f},
631 {-1.0f, +1.0f, 1.0f}
632 };
633 float norms[6][3] =
634 {
635 {0.0f, -1.0f, 0.0f}, {+1.0f, 0.0f, 0.0f},
636 {0.0f, +1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f},
637 {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, +1.0f}
638 };
639 float txcds[4][2] =
640 {
641 {0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}
642 };
643 MeshTransform xform;
644 const float degrees = getRotation() * (float)(180.0 / M_PI);
645 const float zAxis[3] = {0.0f, 0.0f, +1.0f};
646 xform.addScale(getSize());
647 xform.addSpin(degrees, zAxis);
648 xform.addShift(getPosition());
649 xform.finalize();
650 MeshTransform::Tool xtool(xform);
651 for (i = 0; i < 8; i++)
652 xtool.modifyVertex(verts[i]);
653 for (i = 0; i < 6; i++)
654 xtool.modifyNormal(norms[i]);
655
656 out << "# OBJ - start tele" << std::endl;
657 out << "o bztele_" << name << std::endl;
658
659 for (i = 0; i < 8; i++)
660 {
661 out << "v";
662 outputFloat(out, verts[i][0]);
663 outputFloat(out, verts[i][1]);
664 outputFloat(out, verts[i][2]);
665 out << std::endl;
666 }
667 for (i = 0; i < 4; i++)
668 {
669 out << "vt";
670 outputFloat(out, txcds[i][0]);
671 outputFloat(out, txcds[i][1]);
672 out << std::endl;
673 }
674 for (i = 0; i < 6; i++)
675 {
676 out << "vn";
677 outputFloat(out, norms[i][0]);
678 outputFloat(out, norms[i][1]);
679 outputFloat(out, norms[i][2]);
680 out << std::endl;
681 }
682 out << "usemtl telefront" << std::endl;
683 out << "f -7/-4/-5 -6/-3/-5 -2/-2/-5 -3/-1/-5" << std::endl;
684 out << "usemtl teleback" << std::endl;
685 out << "f -5/-4/-3 -8/-3/-3 -4/-2/-3 -1/-1/-3" << std::endl;
686 out << "usemtl telerim" << std::endl;
687 out << "f -5/-4/-2 -6/-3/-2 -7/-2/-2 -8/-1/-2" << std::endl;
688 out << "f -4/-4/-1 -3/-3/-1 -2/-2/-1 -1/-1/-1" << std::endl;
689 out << "f -8/-4/-6 -7/-3/-6 -3/-2/-6 -4/-1/-6" << std::endl;
690 out << "f -6/-4/-4 -5/-3/-4 -1/-2/-4 -2/-1/-4" << std::endl;
691
692 out << std::endl;
693
694 return;
695 }
696
697
698 // Local Variables: ***
699 // mode: C++ ***
700 // tab-width: 4 ***
701 // c-basic-offset: 4 ***
702 // indent-tabs-mode: nil ***
703 // End: ***
704 // ex: shiftwidth=4 tabstop=4
705