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