]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - contrib/bobtoolz/shapes.cpp
reformat code! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / contrib / bobtoolz / shapes.cpp
1 /*
2    BobToolz plugin for GtkRadiant
3    Copyright (C) 2001 Gordon Biggans
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20
21 #include "shapes.h"
22
23 #include <list>
24
25 #include "DPoint.h"
26 #include "DPlane.h"
27
28 #include "str.h"
29 #include "misc.h"
30 #include "funchandlers.h"
31
32 #include "iundo.h"
33 #include "ishaders.h"
34 #include "ientity.h"
35 #include "ieclass.h"
36 #include "ipatch.h"
37 #include "qerplugin.h"
38
39 #include <vector>
40 #include <list>
41 #include <map>
42 #include <algorithm>
43 #include <time.h>
44
45 #include "scenelib.h"
46 #include "texturelib.h"
47
48 //#include "dialogs-gtk.h"
49
50 /************************
51     Cube Diagram
52 ************************/
53
54 /*
55
56         7 ----- 5
57         /|    /|
58        / |   / |
59       /  |  /  |
60     4 ----- 6  |
61      |  2|_|___|8
62      |  /  |   /
63      | /   |  /       ----> WEST, definitely
64  ||/    | /
65     1|_____|/3
66
67  */
68
69 /************************
70     Global Variables
71 ************************/
72
73 vec3_t g_Origin = {0.0f, 0.0f, 0.0f};
74
75 extern bool bFacesAll[];
76
77 /************************
78     Helper Functions
79 ************************/
80
81 float Deg2Rad(float angle)
82 {
83     return (float) (angle * Q_PI / 180);
84 }
85
86 void AddFaceWithTexture(scene::Node &brush, vec3_t va, vec3_t vb, vec3_t vc, const char *texture, bool detail)
87 {
88     _QERFaceData faceData;
89     FillDefaultTexture(&faceData, va, vb, vc, texture);
90     if (detail) {
91         faceData.contents |= FACE_DETAIL;
92     }
93     GlobalBrushCreator().Brush_addFace(brush, faceData);
94 }
95
96 void AddFaceWithTextureScaled(scene::Node &brush, vec3_t va, vec3_t vb, vec3_t vc,
97                               const char *texture, bool bVertScale, bool bHorScale,
98                               float minX, float minY, float maxX, float maxY)
99 {
100     qtexture_t *pqtTexInfo;
101
102     // TTimo: there used to be a call to pfnHasShader here
103     //   this was not necessary. In Radiant everything is shader.
104     //   If a texture doesn't have a shader script, a default shader object is used.
105     // The IShader object was leaking also
106     // collect texture info: sizes, etc
107     IShader *i = GlobalShaderSystem().getShaderForName(texture);
108     pqtTexInfo = i->getTexture(); // shader width/height doesn't come out properly
109
110     if (pqtTexInfo) {
111         float scale[2] = {0.5f, 0.5f};
112         float shift[2] = {0, 0};
113
114         if (bHorScale) {
115             float width = maxX - minX;
116
117             scale[0] = width / pqtTexInfo->width;
118             shift[0] = -(float) ((int) maxX % (int) width) / scale[0];
119         }
120
121         if (bVertScale) {
122             float height = maxY - minY;
123
124             scale[1] = height / pqtTexInfo->height;
125             shift[1] = (float) ((int) minY % (int) height) / scale[1];
126         }
127
128         _QERFaceData addFace;
129         FillDefaultTexture(&addFace, va, vb, vc, texture);
130         addFace.m_texdef.scale[0] = scale[0];
131         addFace.m_texdef.scale[1] = scale[1];
132         addFace.m_texdef.shift[0] = shift[0];
133         addFace.m_texdef.shift[1] = shift[1];
134
135         GlobalBrushCreator().Brush_addFace(brush, addFace);
136     } else {
137         // shouldn't even get here, as default missing texture should be returned if
138         // texture doesn't exist, but just in case
139         AddFaceWithTexture(brush, va, vb, vc, texture, false);
140         globalErrorStream() << "BobToolz::Invalid Texture Name-> " << texture;
141     }
142     // the IShader is not kept referenced, DecRef it
143     i->DecRef();
144 }
145
146 /************************
147     --Main Functions--
148 ************************/
149
150 void Build_Wedge(int dir, vec3_t min, vec3_t max, bool bUp)
151 {
152     NodeSmartReference newBrush(GlobalBrushCreator().createBrush());
153
154     vec3_t v1, v2, v3, v5, v6, v7, v8;
155     VectorCopy(min, v1);
156     VectorCopy(min, v2);
157     VectorCopy(min, v3);
158     VectorCopy(max, v5);
159     VectorCopy(max, v6);
160     VectorCopy(max, v7);
161     VectorCopy(max, v8);
162
163     v2[0] = max[0];
164     v3[1] = max[1];
165
166     v6[0] = min[0];
167     v7[1] = min[1];
168     v8[2] = min[2];
169
170     if (bUp) {
171
172         if (dir != MOVE_EAST) {
173             AddFaceWithTexture(newBrush, v1, v3, v6, "textures/common/caulk", false);
174         }
175
176         if (dir != MOVE_WEST) {
177             AddFaceWithTexture(newBrush, v7, v5, v8, "textures/common/caulk", false);
178         }
179
180         if (dir != MOVE_NORTH) {
181             AddFaceWithTexture(newBrush, v1, v7, v2, "textures/common/caulk", false);
182         }
183
184         if (dir != MOVE_SOUTH) {
185             AddFaceWithTexture(newBrush, v3, v8, v6, "textures/common/caulk", false);
186         }
187
188         AddFaceWithTexture(newBrush, v1, v2, v3, "textures/common/caulk", false);
189
190         if (dir == MOVE_EAST) {
191             AddFaceWithTexture(newBrush, v1, v3, v5, "textures/common/caulk", false);
192         }
193
194         if (dir == MOVE_WEST) {
195             AddFaceWithTexture(newBrush, v2, v6, v8, "textures/common/caulk", false);
196         }
197
198         if (dir == MOVE_NORTH) {
199             AddFaceWithTexture(newBrush, v1, v6, v5, "textures/common/caulk", false);
200         }
201
202         if (dir == MOVE_SOUTH) {
203             AddFaceWithTexture(newBrush, v7, v3, v8, "textures/common/caulk", false);
204         }
205     } else {
206         if (dir != MOVE_WEST) {
207             AddFaceWithTexture(newBrush, v7, v5, v8, "textures/common/caulk", false);
208         }
209
210         if (dir != MOVE_EAST) {
211             AddFaceWithTexture(newBrush, v1, v3, v6, "textures/common/caulk", false);
212         }
213
214         if (dir != MOVE_NORTH) {
215             AddFaceWithTexture(newBrush, v3, v8, v6, "textures/common/caulk", false);
216         }
217
218         if (dir != MOVE_SOUTH) {
219             AddFaceWithTexture(newBrush, v1, v7, v2, "textures/common/caulk", false);
220         }
221
222
223         AddFaceWithTexture(newBrush, v6, v5, v7, "textures/common/caulk", false);
224
225         if (dir == MOVE_WEST) {
226             AddFaceWithTexture(newBrush, v1, v5, v3, "textures/common/caulk", false);
227         }
228
229         if (dir == MOVE_EAST) {
230             AddFaceWithTexture(newBrush, v2, v8, v6, "textures/common/caulk", false);
231         }
232
233         if (dir == MOVE_NORTH) {
234             AddFaceWithTexture(newBrush, v1, v5, v6, "textures/common/caulk", false);
235         }
236
237         if (dir == MOVE_SOUTH) {
238             AddFaceWithTexture(newBrush, v7, v8, v3, "textures/common/caulk", false);
239         }
240     }
241
242     Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(newBrush);
243 }
244
245 //-----------------------------------------------------------------------------------
246 //-----------------------------------------------------------------------------------
247
248 void
249 Build_StairStep_Wedge(int dir, vec3_t min, vec3_t max, const char *mainTexture, const char *riserTexture, bool detail)
250 {
251     NodeSmartReference newBrush(GlobalBrushCreator().createBrush());
252
253     //----- Build Outer Bounds ---------
254
255     vec3_t v1, v2, v3, v5, v6, v7, v8;
256     VectorCopy(min, v1);
257     VectorCopy(min, v2);
258     VectorCopy(min, v3);
259     VectorCopy(max, v5);
260     VectorCopy(max, v6);
261     VectorCopy(max, v7);
262     VectorCopy(max, v8);
263
264     v2[0] = max[0];
265     v3[1] = max[1];
266
267     v6[0] = min[0];
268     v7[1] = min[1];
269
270     v8[2] = min[2];
271     //v8 needed this time, becoz of sloping faces (2-4-6-8)
272
273     //----------------------------------
274
275     AddFaceWithTexture(newBrush, v6, v5, v7, mainTexture, detail);
276
277     if (dir != MOVE_EAST) {
278         if (dir == MOVE_WEST) {
279             AddFaceWithTexture(newBrush, v5, v2, v7, riserTexture, detail);
280         } else {
281             AddFaceWithTexture(newBrush, v5, v2, v7, "textures/common/caulk", detail);
282         }
283     }
284
285     if (dir != MOVE_WEST) {
286         if (dir == MOVE_EAST) {
287             AddFaceWithTexture(newBrush, v1, v3, v6, riserTexture, detail);
288         } else {
289             AddFaceWithTexture(newBrush, v1, v3, v6, "textures/common/caulk", detail);
290         }
291     }
292
293     if (dir != MOVE_NORTH) {
294         if (dir == MOVE_SOUTH) {
295             AddFaceWithTexture(newBrush, v3, v5, v6, riserTexture, detail);
296         } else {
297             AddFaceWithTexture(newBrush, v3, v5, v6, "textures/common/caulk", detail);
298         }
299     }
300
301     if (dir != MOVE_SOUTH) {
302         if (dir == MOVE_NORTH) {
303             AddFaceWithTexture(newBrush, v1, v7, v2, riserTexture, detail);
304         } else {
305             AddFaceWithTexture(newBrush, v1, v7, v2, "textures/common/caulk", detail);
306         }
307     }
308
309
310     if (dir == MOVE_EAST) {
311         AddFaceWithTexture(newBrush, v1, v5, v3, "textures/common/caulk", detail);
312     }
313
314     if (dir == MOVE_WEST) {
315         AddFaceWithTexture(newBrush, v2, v8, v6, "textures/common/caulk", detail);
316     }
317
318     if (dir == MOVE_NORTH) {
319         AddFaceWithTexture(newBrush, v1, v5, v6, "textures/common/caulk", detail);
320     }
321
322     if (dir == MOVE_SOUTH) {
323         AddFaceWithTexture(newBrush, v7, v8, v3, "textures/common/caulk", detail);
324     }
325
326     Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(newBrush);
327 }
328
329 //-----------------------------------------------------------------------------------
330 //-----------------------------------------------------------------------------------
331
332 // internal use only, to get a box without finishing construction
333 scene::Node &Build_Get_BoundingCube_Selective(vec3_t min, vec3_t max, char *texture, bool *useFaces)
334 {
335     NodeSmartReference newBrush(GlobalBrushCreator().createBrush());
336
337     //----- Build Outer Bounds ---------
338
339     vec3_t v1, v2, v3, v5, v6, v7;
340     VectorCopy(min, v1);
341     VectorCopy(min, v2);
342     VectorCopy(min, v3);
343     VectorCopy(max, v5);
344     VectorCopy(max, v6);
345     VectorCopy(max, v7);
346
347     v2[0] = max[0];
348     v3[1] = max[1];
349
350     v6[0] = min[0];
351     v7[1] = min[1];
352
353     //----------------------------------
354
355     //----- Add Six Cube Faces ---------
356
357     if (useFaces[0]) {
358         AddFaceWithTexture(newBrush, v1, v2, v3, texture, false);
359     }
360     if (useFaces[1]) {
361         AddFaceWithTexture(newBrush, v1, v3, v6, texture, false);
362     }
363     if (useFaces[2]) {
364         AddFaceWithTexture(newBrush, v1, v7, v2, texture, false);
365     }
366
367     if (useFaces[3]) {
368         AddFaceWithTexture(newBrush, v5, v6, v3, texture, false);
369     }
370     if (useFaces[4]) {
371         AddFaceWithTexture(newBrush, v5, v2, v7, texture, false);
372     }
373     if (useFaces[5]) {
374         AddFaceWithTexture(newBrush, v5, v7, v6, texture, false);
375     }
376
377     //----------------------------------
378
379     return newBrush;
380 }
381
382 scene::Node &Build_Get_BoundingCube(vec3_t min, vec3_t max, char *texture)
383 {
384     return Build_Get_BoundingCube_Selective(min, max, texture, bFacesAll);
385 }
386
387 //-----------------------------------------------------------------------------------
388 //-----------------------------------------------------------------------------------
389
390 void Build_StairStep(vec3_t min, vec3_t max, const char *mainTexture, const char *riserTexture, int direction)
391 {
392     NodeSmartReference newBrush(GlobalBrushCreator().createBrush());
393
394     //----- Build Outer Bounds ---------
395
396     vec3_t v1, v2, v3, v5, v6, v7;
397     VectorCopy(min, v1);
398     VectorCopy(min, v2);
399     VectorCopy(min, v3);
400     VectorCopy(max, v5);
401     VectorCopy(max, v6);
402     VectorCopy(max, v7);
403
404     v2[0] = max[0];
405     v3[1] = max[1];
406
407     v6[0] = min[0];
408     v7[1] = min[1];
409
410     //----------------------------------
411
412     AddFaceWithTexture(newBrush, v6, v5, v7, mainTexture, false);
413     // top gets current texture
414
415
416     if (direction == MOVE_EAST) {
417         AddFaceWithTexture(newBrush, v1, v3, v6, riserTexture, false);
418     } else {
419         AddFaceWithTexture(newBrush, v1, v3, v6, "textures/common/caulk", false);
420     }
421     // west facing side, etc...
422
423
424     if (direction == MOVE_NORTH) {
425         AddFaceWithTexture(newBrush, v1, v7, v2, riserTexture, false);
426     } else {
427         AddFaceWithTexture(newBrush, v1, v7, v2, "textures/common/caulk", false);
428     }
429
430     if (direction == MOVE_SOUTH) {
431         AddFaceWithTexture(newBrush, v3, v5, v6, riserTexture, false);
432     } else {
433         AddFaceWithTexture(newBrush, v3, v5, v6, "textures/common/caulk", false);
434     }
435
436     if (direction == MOVE_WEST) {
437         AddFaceWithTexture(newBrush, v7, v5, v2, riserTexture, false);
438     } else {
439         AddFaceWithTexture(newBrush, v7, v5, v2, "textures/common/caulk", false);
440     }
441
442
443     AddFaceWithTexture(newBrush, v1, v2, v3, "textures/common/caulk", false);
444     // base is caulked
445
446     Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(newBrush);
447     // finish brush
448 }
449
450 //-----------------------------------------------------------------------------------
451 //-----------------------------------------------------------------------------------
452
453 void BuildDoorsX2(vec3_t min, vec3_t max,
454                   bool bSclMainHor, bool bSclMainVert,
455                   bool bSclTrimHor, bool bSclTrimVert,
456                   const char *mainTexture, const char *trimTexture,
457                   int direction)
458 {
459     int xy;
460     if (direction == 0) {
461         xy = 0;
462     } else {
463         xy = 1;
464     }
465
466     //----- Build Outer Bounds ---------
467
468     vec3_t v1, v2, v3, v5, v6, v7, ve_1, ve_2, ve_3;
469     VectorCopy(min, v1);
470     VectorCopy(min, v2);
471     VectorCopy(min, v3);
472     VectorCopy(max, v5);
473     VectorCopy(max, v6);
474     VectorCopy(max, v7);
475
476     v2[0] = max[0];
477     v3[1] = max[1];
478
479     v6[0] = min[0];
480     v7[1] = min[1];
481
482     float width = (max[xy] - min[xy]) / 2;
483
484     if (direction == 0) {
485         VectorCopy(v1, ve_1);
486         VectorCopy(v3, ve_2);
487         VectorCopy(v6, ve_3);
488     } else {
489         VectorCopy(v7, ve_1);
490         VectorCopy(v1, ve_2);
491         VectorCopy(v2, ve_3);
492     }
493
494     ve_1[xy] += width;
495     ve_2[xy] += width;
496     ve_3[xy] += width;
497
498     //----------------------------------
499
500     NodeSmartReference newBrush1(GlobalBrushCreator().createBrush());
501     NodeSmartReference newBrush2(GlobalBrushCreator().createBrush());
502
503     AddFaceWithTexture(newBrush1, v1, v2, v3, "textures/common/caulk", false);
504     AddFaceWithTexture(newBrush1, v5, v7, v6, "textures/common/caulk", false);
505
506     AddFaceWithTexture(newBrush2, v1, v2, v3, "textures/common/caulk", false);
507     AddFaceWithTexture(newBrush2, v5, v7, v6, "textures/common/caulk", false);
508
509     if (direction == 0) {
510         AddFaceWithTexture(newBrush1, v1, v3, v6, "textures/common/caulk", false);
511         AddFaceWithTexture(newBrush2, v5, v2, v7, "textures/common/caulk", false);
512     } else {
513         AddFaceWithTexture(newBrush1, v1, v7, v2, "textures/common/caulk", false);
514         AddFaceWithTexture(newBrush2, v5, v6, v3, "textures/common/caulk", false);
515     }
516
517     if (direction == 0) {
518         AddFaceWithTextureScaled(newBrush1, v1, v7, v2, mainTexture, bSclMainVert, bSclMainHor,
519                                  min[0], min[2], max[0], max[2]);
520         AddFaceWithTextureScaled(newBrush1, v5, v6, v3, mainTexture, bSclMainVert, bSclMainHor,
521                                  max[0], min[2], min[0], max[2]);
522
523
524         AddFaceWithTextureScaled(newBrush2, v1, v7, v2, mainTexture, bSclMainVert, bSclMainHor,
525                                  min[0], min[2], max[0], max[2]);
526         AddFaceWithTextureScaled(newBrush2, v5, v6, v3, mainTexture, bSclMainVert, bSclMainHor,
527                                  max[0], min[2], min[0], max[2]); // flip max/min to reverse tex dir
528
529
530
531         AddFaceWithTextureScaled(newBrush1, ve_3, ve_2, ve_1, trimTexture, bSclTrimVert, bSclTrimHor,
532                                  min[1], min[2], max[1], max[2]);
533
534         AddFaceWithTextureScaled(newBrush2, ve_1, ve_2, ve_3, trimTexture, bSclTrimVert, bSclTrimHor,
535                                  max[1], min[2], min[1], max[2]);
536     } else {
537         AddFaceWithTextureScaled(newBrush1, v1, v3, v6, mainTexture, bSclMainVert, bSclMainHor,
538                                  min[1], min[2], max[1], max[2]);
539         AddFaceWithTextureScaled(newBrush1, v5, v2, v7, mainTexture, bSclMainVert, bSclMainHor,
540                                  max[1], min[2], min[1], max[2]);
541
542
543         AddFaceWithTextureScaled(newBrush2, v1, v3, v6, mainTexture, bSclMainVert, bSclMainHor,
544                                  min[1], min[2], max[1], max[2]);
545         AddFaceWithTextureScaled(newBrush2, v5, v2, v7, mainTexture, bSclMainVert, bSclMainHor,
546                                  max[1], min[2], min[1], max[2]); // flip max/min to reverse tex dir
547
548
549         AddFaceWithTextureScaled(newBrush1, ve_1, ve_2, ve_3, trimTexture, bSclTrimVert, bSclTrimHor,
550                                  min[0], min[2], max[0], max[2]);
551
552         AddFaceWithTextureScaled(newBrush2, ve_3, ve_2, ve_1, trimTexture, bSclTrimVert, bSclTrimHor,
553                                  max[0], min[2], min[0], max[2]);
554     }
555
556     //----------------------------------
557
558
559     EntityClass *doorClass = GlobalEntityClassManager().findOrInsert("func_door", true);
560     NodeSmartReference pEDoor1(GlobalEntityCreator().createEntity(doorClass));
561     NodeSmartReference pEDoor2(GlobalEntityCreator().createEntity(doorClass));
562
563     if (direction == 0) {
564         Node_getEntity(pEDoor1)->setKeyValue("angle", "180");
565         Node_getEntity(pEDoor2)->setKeyValue("angle", "360");
566     } else {
567         Node_getEntity(pEDoor1)->setKeyValue("angle", "270");
568         Node_getEntity(pEDoor2)->setKeyValue("angle", "90");
569     }
570
571     srand((unsigned) time(NULL));
572
573     char teamname[256];
574     sprintf(teamname, "t%i", rand());
575     Node_getEntity(pEDoor1)->setKeyValue("team", teamname);
576     Node_getEntity(pEDoor2)->setKeyValue("team", teamname);
577
578     Node_getTraversable(pEDoor1)->insert(newBrush1);
579     Node_getTraversable(pEDoor2)->insert(newBrush2);
580
581     Node_getTraversable(GlobalSceneGraph().root())->insert(pEDoor1);
582     Node_getTraversable(GlobalSceneGraph().root())->insert(pEDoor2);
583
584 //      ResetCurrentTexture();
585 }
586
587 //-----------------------------------------------------------------------------------
588 //-----------------------------------------------------------------------------------
589
590 void MakeBevel(vec3_t vMin, vec3_t vMax)
591 {
592     NodeSmartReference patch(GlobalPatchCreator().createPatch());
593     GlobalPatchCreator().Patch_resize(patch, 3, 3);
594     GlobalPatchCreator().Patch_setShader(patch, "textures/common/caulk");
595     PatchControlMatrix matrix = GlobalPatchCreator().Patch_getControlPoints(patch);
596     vec3_t x_3, y_3, z_3;
597     x_3[0] = vMin[0];
598     x_3[1] = vMin[0];
599     x_3[2] = vMax[0];
600     y_3[0] = vMin[1];
601     y_3[1] = vMax[1];
602     y_3[2] = vMax[1];
603     z_3[0] = vMin[2];
604     z_3[1] = (vMax[2] + vMin[2]) / 2;
605     z_3[2] = vMax[2];
606     /*
607        x_3[0] = 0;              x_3[1] = 0;             x_3[2] = 64;
608        y_3[0] = 0;              y_3[1] = 64;    y_3[2] = 64;
609        z_3[0] = 0;              z_3[1] = 32;    z_3[2] = 64;*/
610     for (int i = 0; i < 3; i++) {
611         for (int j = 0; j < 3; j++) {
612             PatchControl &p = matrix(i, j);
613             p.m_vertex[0] = x_3[i];
614             p.m_vertex[1] = y_3[i];
615             p.m_vertex[2] = z_3[j];
616         }
617     }
618     //does invert the matrix, else the patch face is on wrong side.
619     for (int i = 0; i < 3; i++) {
620         for (int j = 0; j < 1; j++) {
621             PatchControl &p = matrix(i, 2 - j);
622             PatchControl &q = matrix(i, j);
623             std::swap(p.m_vertex, q.m_vertex);
624             //std::swap(p.m_texcoord, q.m_texcoord);
625         }
626     }
627     GlobalPatchCreator().Patch_controlPointsChanged(patch);
628     //TODO - the patch has textures weird, patchmanip.h has all function it needs.. lots of duplicate code..
629     //NaturalTexture(patch);
630     Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(patch);
631 }
632
633 void BuildCornerStairs(vec3_t vMin, vec3_t vMax, int nSteps, const char *mainTexture, const char *riserTex)
634 {
635     vec3_t *topPoints = new vec3_t[nSteps + 1];
636     vec3_t *botPoints = new vec3_t[nSteps + 1];
637
638     //bool bFacesUse[6] = {true, true, false, true, false, false};
639
640     vec3_t centre;
641     VectorCopy(vMin, centre);
642     centre[0] = vMax[0];
643
644     int height = (int) (vMax[2] - vMin[2]) / nSteps;
645
646     vec3_t vTop, vBot;
647     VectorCopy(vMax, vTop);
648     VectorCopy(vMin, vBot);
649     vTop[2] = vMin[2] + height;
650
651     int i;
652     for (i = 0; i <= nSteps; i++) {
653         VectorCopy(centre, topPoints[i]);
654         VectorCopy(centre, botPoints[i]);
655
656         topPoints[i][2] = vMax[2];
657         botPoints[i][2] = vMin[2];
658
659         topPoints[i][0] -= 10 * sinf(Q_PI * i / (2 * nSteps));
660         topPoints[i][1] += 10 * cosf(Q_PI * i / (2 * nSteps));
661
662         botPoints[i][0] = topPoints[i][0];
663         botPoints[i][1] = topPoints[i][1];
664     }
665
666     vec3_t tp[3];
667     for (int j = 0; j < 3; j++)
668         VectorCopy(topPoints[j], tp[j]);
669
670     for (i = 0; i < nSteps; i++) {
671         NodeSmartReference brush(GlobalBrushCreator().createBrush());
672         vec3_t v1, v2, v3, v5, v6, v7;
673         VectorCopy(vBot, v1);
674         VectorCopy(vBot, v2);
675         VectorCopy(vBot, v3);
676         VectorCopy(vTop, v5);
677         VectorCopy(vTop, v6);
678         VectorCopy(vTop, v7);
679
680         v2[0] = vTop[0];
681         v3[1] = vTop[1];
682
683         v6[0] = vBot[0];
684         v7[1] = vBot[1];
685
686         AddFaceWithTexture(brush, v1, v2, v3, "textures/common/caulk", false);
687         AddFaceWithTexture(brush, v1, v3, v6, "textures/common/caulk", false);
688         AddFaceWithTexture(brush, v5, v6, v3, "textures/common/caulk", false);
689
690         for (int j = 0; j < 3; j++) {
691             tp[j][2] = vTop[2];
692         }
693
694         AddFaceWithTexture(brush, tp[2], tp[1], tp[0], mainTexture, false);
695
696         AddFaceWithTexture(brush, centre, botPoints[i + 1], topPoints[i + 1], "textures/common/caulk", false);
697         AddFaceWithTexture(brush, centre, topPoints[i], botPoints[i], riserTex, false);
698
699         Node_getTraversable(GlobalRadiant().getMapWorldEntity())->insert(brush);
700
701         vTop[2] += height;
702         vBot[2] += height;
703     }
704
705     delete[] topPoints;
706     delete[] botPoints;
707
708     vMin[2] += height;
709     vMax[2] += height;
710     MakeBevel(vMin, vMax);
711 }