mirror of
https://github.com/Garux/netradiant-custom.git
synced 2026-09-29 00:00:03 +02:00
1039 lines
35 KiB
C++
1039 lines
35 KiB
C++
/*
|
|
BobToolz plugin for GtkRadiant
|
|
Copyright (C) 2001 Gordon Biggans
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#include "shapes.h"
|
|
|
|
#include "DPoint.h"
|
|
#include "DPlane.h"
|
|
|
|
#include "misc.h"
|
|
#include "funchandlers.h"
|
|
|
|
#include "iundo.h"
|
|
#include "ishaders.h"
|
|
#include "ientity.h"
|
|
#include "ieclass.h"
|
|
#include "ipatch.h"
|
|
#include "qerplugin.h"
|
|
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
|
|
#include "scenelib.h"
|
|
#include "texturelib.h"
|
|
|
|
/************************
|
|
Cube Diagram
|
|
************************/
|
|
|
|
/*
|
|
|
|
7 ----- 5
|
|
/| /|
|
|
/ | / |
|
|
/ | / |
|
|
4 ----- 6 |
|
|
| 2|_|___|8
|
|
| / | /
|
|
| / | / ----> WEST, definitely
|
|
||/ | /
|
|
1|_____|/3
|
|
|
|
*/
|
|
|
|
/************************
|
|
Global Variables
|
|
************************/
|
|
|
|
vec3_t g_Origin = { 0.0f, 0.0f, 0.0f };
|
|
|
|
extern bool bFacesAll[];
|
|
|
|
/************************
|
|
Helper Functions
|
|
************************/
|
|
|
|
float Deg2Rad( float angle ){
|
|
return angle * Q_PI / 180;
|
|
}
|
|
// points in CCW order
|
|
void AddFaceWithTexture( scene::Node& brush, const vec3_accu_t va, const vec3_accu_t vb, const vec3_accu_t vc, const char* texture, bool detail ){
|
|
_QERFaceData faceData;
|
|
FillDefaultTexture( &faceData, va, vb, vc, texture );
|
|
if ( detail ) {
|
|
faceData.contents |= FACE_DETAIL;
|
|
}
|
|
GlobalBrushCreator().Brush_addFace( brush, faceData );
|
|
}
|
|
void AddFaceWithTexture( scene::Node& brush, const vec3_t va, const vec3_t vb, const vec3_t vc, const char* texture, bool detail ){
|
|
AddFaceWithTexture( brush, vec3_accu_t{ va[0], va[1], va[2] }, vec3_accu_t{ vb[0], vb[1], vb[2] }, vec3_accu_t{ vc[0], vc[1], vc[2] }, texture, detail );
|
|
}
|
|
|
|
void AddFaceWithTextureScaled( scene::Node& brush, vec3_t va, vec3_t vb, vec3_t vc,
|
|
const char* texture, bool bVertScale, bool bHorScale,
|
|
float minX, float minY, float maxX, float maxY ){
|
|
qtexture_t* pqtTexInfo;
|
|
|
|
// TTimo: there used to be a call to pfnHasShader here
|
|
// this was not necessary. In Radiant everything is shader.
|
|
// If a texture doesn't have a shader script, a default shader object is used.
|
|
// The IShader object was leaking also
|
|
// collect texture info: sizes, etc
|
|
IShader* i = GlobalShaderSystem().getShaderForName( texture );
|
|
pqtTexInfo = i->getTexture(); // shader width/height doesn't come out properly
|
|
|
|
if ( pqtTexInfo ) {
|
|
float scale[2] = {0.5f, 0.5f};
|
|
float shift[2] = {0, 0};
|
|
|
|
if ( bHorScale ) {
|
|
float width = maxX - minX;
|
|
|
|
scale[0] = width / pqtTexInfo->width;
|
|
shift[0] = -( (int)maxX % (int)width ) / scale[0];
|
|
}
|
|
|
|
if ( bVertScale ) {
|
|
float height = maxY - minY;
|
|
|
|
scale[1] = height / pqtTexInfo->height;
|
|
shift[1] = ( (int)minY % (int)height ) / scale[1];
|
|
}
|
|
|
|
_QERFaceData addFace;
|
|
FillDefaultTexture( &addFace, va, vb, vc, texture );
|
|
addFace.m_texdef.scale[0] = scale[0];
|
|
addFace.m_texdef.scale[1] = scale[1];
|
|
addFace.m_texdef.shift[0] = shift[0];
|
|
addFace.m_texdef.shift[1] = shift[1];
|
|
|
|
GlobalBrushCreator().Brush_addFace( brush, addFace );
|
|
}
|
|
else
|
|
{
|
|
// shouldn't even get here, as default missing texture should be returned if
|
|
// texture doesn't exist, but just in case
|
|
AddFaceWithTexture( brush, va, vb, vc, texture, false );
|
|
globalErrorStream() << "BobToolz::Invalid Texture Name-> " << texture;
|
|
}
|
|
// the IShader is not kept referenced, DecRef it
|
|
i->DecRef();
|
|
}
|
|
|
|
/************************
|
|
--Main Functions--
|
|
************************/
|
|
|
|
void Build_Wedge( int dir, vec3_t min, vec3_t max, bool bUp ){
|
|
NodeSmartReference newBrush( GlobalBrushCreator().createBrush() );
|
|
|
|
vec3_t v1, v2, v3, v5, v6, v7, v8;
|
|
VectorCopy( min, v1 );
|
|
VectorCopy( min, v2 );
|
|
VectorCopy( min, v3 );
|
|
VectorCopy( max, v5 );
|
|
VectorCopy( max, v6 );
|
|
VectorCopy( max, v7 );
|
|
VectorCopy( max, v8 );
|
|
|
|
v2[0] = max[0];
|
|
v3[1] = max[1];
|
|
|
|
v6[0] = min[0];
|
|
v7[1] = min[1];
|
|
v8[2] = min[2];
|
|
|
|
if ( bUp ) {
|
|
|
|
if ( dir != MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v7, v5, v8, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v3, v8, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
AddFaceWithTexture( newBrush, v1, v2, v3, "textures/common/caulk", false );
|
|
|
|
if ( dir == MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v5, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v2, v6, v8, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v6, v5, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v7, v3, v8, "textures/common/caulk", false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( dir != MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v7, v5, v8, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v3, v8, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir != MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, "textures/common/caulk", false );
|
|
}
|
|
|
|
|
|
AddFaceWithTexture( newBrush, v6, v5, v7, "textures/common/caulk", false );
|
|
|
|
if ( dir == MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v1, v5, v3, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v2, v8, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v5, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( dir == MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v7, v8, v3, "textures/common/caulk", false );
|
|
}
|
|
}
|
|
|
|
Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( newBrush );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
void Build_StairStep_Wedge( int dir, vec3_t min, vec3_t max, const char* mainTexture, const char* riserTexture, bool detail ){
|
|
NodeSmartReference newBrush( GlobalBrushCreator().createBrush() );
|
|
|
|
//----- Build Outer Bounds ---------
|
|
|
|
vec3_t v1, v2, v3, v5, v6, v7, v8;
|
|
VectorCopy( min, v1 );
|
|
VectorCopy( min, v2 );
|
|
VectorCopy( min, v3 );
|
|
VectorCopy( max, v5 );
|
|
VectorCopy( max, v6 );
|
|
VectorCopy( max, v7 );
|
|
VectorCopy( max, v8 );
|
|
|
|
v2[0] = max[0];
|
|
v3[1] = max[1];
|
|
|
|
v6[0] = min[0];
|
|
v7[1] = min[1];
|
|
|
|
v8[2] = min[2];
|
|
//v8 needed this time, becoz of sloping faces (2-4-6-8)
|
|
|
|
//----------------------------------
|
|
|
|
AddFaceWithTexture( newBrush, v6, v5, v7, mainTexture, detail );
|
|
|
|
if ( dir != MOVE_EAST ) {
|
|
if ( dir == MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v5, v2, v7, riserTexture, detail );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v5, v2, v7, "textures/common/caulk", detail );
|
|
}
|
|
}
|
|
|
|
if ( dir != MOVE_WEST ) {
|
|
if ( dir == MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, riserTexture, detail );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, "textures/common/caulk", detail );
|
|
}
|
|
}
|
|
|
|
if ( dir != MOVE_NORTH ) {
|
|
if ( dir == MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v3, v5, v6, riserTexture, detail );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v3, v5, v6, "textures/common/caulk", detail );
|
|
}
|
|
}
|
|
|
|
if ( dir != MOVE_SOUTH ) {
|
|
if ( dir == MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, riserTexture, detail );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, "textures/common/caulk", detail );
|
|
}
|
|
}
|
|
|
|
|
|
if ( dir == MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v5, v3, "textures/common/caulk", detail );
|
|
}
|
|
|
|
if ( dir == MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v2, v8, v6, "textures/common/caulk", detail );
|
|
}
|
|
|
|
if ( dir == MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v5, v6, "textures/common/caulk", detail );
|
|
}
|
|
|
|
if ( dir == MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v7, v8, v3, "textures/common/caulk", detail );
|
|
}
|
|
|
|
Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( newBrush );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// internal use only, to get a box without finishing construction
|
|
scene::Node& Build_Get_BoundingCube_Selective( vec3_t min, vec3_t max, char* texture, bool* useFaces ){
|
|
NodeSmartReference newBrush( GlobalBrushCreator().createBrush() );
|
|
|
|
//----- Build Outer Bounds ---------
|
|
|
|
vec3_t v1, v2, v3, v5, v6, v7;
|
|
VectorCopy( min, v1 );
|
|
VectorCopy( min, v2 );
|
|
VectorCopy( min, v3 );
|
|
VectorCopy( max, v5 );
|
|
VectorCopy( max, v6 );
|
|
VectorCopy( max, v7 );
|
|
|
|
v2[0] = max[0];
|
|
v3[1] = max[1];
|
|
|
|
v6[0] = min[0];
|
|
v7[1] = min[1];
|
|
|
|
//----------------------------------
|
|
|
|
//----- Add Six Cube Faces ---------
|
|
|
|
if ( useFaces[0] ) {
|
|
AddFaceWithTexture( newBrush, v1, v2, v3, texture, false );
|
|
}
|
|
if ( useFaces[1] ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, texture, false );
|
|
}
|
|
if ( useFaces[2] ) {
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, texture, false );
|
|
}
|
|
|
|
if ( useFaces[3] ) {
|
|
AddFaceWithTexture( newBrush, v5, v6, v3, texture, false );
|
|
}
|
|
if ( useFaces[4] ) {
|
|
AddFaceWithTexture( newBrush, v5, v2, v7, texture, false );
|
|
}
|
|
if ( useFaces[5] ) {
|
|
AddFaceWithTexture( newBrush, v5, v7, v6, texture, false );
|
|
}
|
|
|
|
//----------------------------------
|
|
|
|
return newBrush;
|
|
}
|
|
|
|
scene::Node& Build_Get_BoundingCube( vec3_t min, vec3_t max, char* texture ){
|
|
return Build_Get_BoundingCube_Selective( min, max, texture, bFacesAll );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
void Build_StairStep( vec3_t min, vec3_t max, const char* mainTexture, const char* riserTexture, int direction ){
|
|
NodeSmartReference newBrush( GlobalBrushCreator().createBrush() );
|
|
|
|
//----- Build Outer Bounds ---------
|
|
|
|
vec3_t v1, v2, v3, v5, v6, v7;
|
|
VectorCopy( min, v1 );
|
|
VectorCopy( min, v2 );
|
|
VectorCopy( min, v3 );
|
|
VectorCopy( max, v5 );
|
|
VectorCopy( max, v6 );
|
|
VectorCopy( max, v7 );
|
|
|
|
v2[0] = max[0];
|
|
v3[1] = max[1];
|
|
|
|
v6[0] = min[0];
|
|
v7[1] = min[1];
|
|
|
|
//----------------------------------
|
|
|
|
AddFaceWithTexture( newBrush, v6, v5, v7, mainTexture, false );
|
|
// top gets current texture
|
|
|
|
|
|
if ( direction == MOVE_EAST ) {
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, riserTexture, false );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v1, v3, v6, "textures/common/caulk", false );
|
|
}
|
|
// west facing side, etc...
|
|
|
|
|
|
if ( direction == MOVE_NORTH ) {
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, riserTexture, false );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v1, v7, v2, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( direction == MOVE_SOUTH ) {
|
|
AddFaceWithTexture( newBrush, v3, v5, v6, riserTexture, false );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v3, v5, v6, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( direction == MOVE_WEST ) {
|
|
AddFaceWithTexture( newBrush, v7, v5, v2, riserTexture, false );
|
|
}
|
|
else{
|
|
AddFaceWithTexture( newBrush, v7, v5, v2, "textures/common/caulk", false );
|
|
}
|
|
|
|
|
|
AddFaceWithTexture( newBrush, v1, v2, v3, "textures/common/caulk", false );
|
|
// base is caulked
|
|
|
|
Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( newBrush );
|
|
// finish brush
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
void BuildDoorsX2( vec3_t min, vec3_t max,
|
|
bool bSclMainHor, bool bSclMainVert,
|
|
bool bSclTrimHor, bool bSclTrimVert,
|
|
const char* mainTexture, const char* trimTexture,
|
|
int direction ){
|
|
int xy;
|
|
if ( direction == 0 ) {
|
|
xy = 0;
|
|
}
|
|
else{
|
|
xy = 1;
|
|
}
|
|
|
|
//----- Build Outer Bounds ---------
|
|
|
|
vec3_t v1, v2, v3, v5, v6, v7, ve_1, ve_2, ve_3;
|
|
VectorCopy( min, v1 );
|
|
VectorCopy( min, v2 );
|
|
VectorCopy( min, v3 );
|
|
VectorCopy( max, v5 );
|
|
VectorCopy( max, v6 );
|
|
VectorCopy( max, v7 );
|
|
|
|
v2[0] = max[0];
|
|
v3[1] = max[1];
|
|
|
|
v6[0] = min[0];
|
|
v7[1] = min[1];
|
|
|
|
float width = ( max[xy] - min[xy] ) / 2;
|
|
|
|
if ( direction == 0 ) {
|
|
VectorCopy( v1, ve_1 );
|
|
VectorCopy( v3, ve_2 );
|
|
VectorCopy( v6, ve_3 );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( v7, ve_1 );
|
|
VectorCopy( v1, ve_2 );
|
|
VectorCopy( v2, ve_3 );
|
|
}
|
|
|
|
ve_1[xy] += width;
|
|
ve_2[xy] += width;
|
|
ve_3[xy] += width;
|
|
|
|
//----------------------------------
|
|
|
|
NodeSmartReference newBrush1( GlobalBrushCreator().createBrush() );
|
|
NodeSmartReference newBrush2( GlobalBrushCreator().createBrush() );
|
|
|
|
AddFaceWithTexture( newBrush1, v1, v2, v3, "textures/common/caulk", false );
|
|
AddFaceWithTexture( newBrush1, v5, v7, v6, "textures/common/caulk", false );
|
|
|
|
AddFaceWithTexture( newBrush2, v1, v2, v3, "textures/common/caulk", false );
|
|
AddFaceWithTexture( newBrush2, v5, v7, v6, "textures/common/caulk", false );
|
|
|
|
if ( direction == 0 ) {
|
|
AddFaceWithTexture( newBrush1, v1, v3, v6, "textures/common/caulk", false );
|
|
AddFaceWithTexture( newBrush2, v5, v2, v7, "textures/common/caulk", false );
|
|
}
|
|
else
|
|
{
|
|
AddFaceWithTexture( newBrush1, v1, v7, v2, "textures/common/caulk", false );
|
|
AddFaceWithTexture( newBrush2, v5, v6, v3, "textures/common/caulk", false );
|
|
}
|
|
|
|
if ( direction == 0 ) {
|
|
AddFaceWithTextureScaled( newBrush1, v1, v7, v2, mainTexture, bSclMainVert, bSclMainHor,
|
|
min[0], min[2], max[0], max[2] );
|
|
AddFaceWithTextureScaled( newBrush1, v5, v6, v3, mainTexture, bSclMainVert, bSclMainHor,
|
|
max[0], min[2], min[0], max[2] );
|
|
|
|
|
|
AddFaceWithTextureScaled( newBrush2, v1, v7, v2, mainTexture, bSclMainVert, bSclMainHor,
|
|
min[0], min[2], max[0], max[2] );
|
|
AddFaceWithTextureScaled( newBrush2, v5, v6, v3, mainTexture, bSclMainVert, bSclMainHor,
|
|
max[0], min[2], min[0], max[2] ); // flip max/min to reverse tex dir
|
|
|
|
|
|
|
|
AddFaceWithTextureScaled( newBrush1, ve_3, ve_2, ve_1, trimTexture, bSclTrimVert, bSclTrimHor,
|
|
min[1], min[2], max[1], max[2] );
|
|
|
|
AddFaceWithTextureScaled( newBrush2, ve_1, ve_2, ve_3, trimTexture, bSclTrimVert, bSclTrimHor,
|
|
max[1], min[2], min[1], max[2] );
|
|
}
|
|
else
|
|
{
|
|
AddFaceWithTextureScaled( newBrush1, v1, v3, v6, mainTexture, bSclMainVert, bSclMainHor,
|
|
min[1], min[2], max[1], max[2] );
|
|
AddFaceWithTextureScaled( newBrush1, v5, v2, v7, mainTexture, bSclMainVert, bSclMainHor,
|
|
max[1], min[2], min[1], max[2] );
|
|
|
|
|
|
AddFaceWithTextureScaled( newBrush2, v1, v3, v6, mainTexture, bSclMainVert, bSclMainHor,
|
|
min[1], min[2], max[1], max[2] );
|
|
AddFaceWithTextureScaled( newBrush2, v5, v2, v7, mainTexture, bSclMainVert, bSclMainHor,
|
|
max[1], min[2], min[1], max[2] ); // flip max/min to reverse tex dir
|
|
|
|
|
|
AddFaceWithTextureScaled( newBrush1, ve_1, ve_2, ve_3, trimTexture, bSclTrimVert, bSclTrimHor,
|
|
min[0], min[2], max[0], max[2] );
|
|
|
|
AddFaceWithTextureScaled( newBrush2, ve_3, ve_2, ve_1, trimTexture, bSclTrimVert, bSclTrimHor,
|
|
max[0], min[2], min[0], max[2] );
|
|
}
|
|
|
|
//----------------------------------
|
|
|
|
|
|
EntityClass* doorClass = GlobalEntityClassManager().findOrInsert( "func_door", true );
|
|
NodeSmartReference pEDoor1( GlobalEntityCreator().createEntity( doorClass ) );
|
|
NodeSmartReference pEDoor2( GlobalEntityCreator().createEntity( doorClass ) );
|
|
|
|
if ( direction == 0 ) {
|
|
Node_getEntity( pEDoor1 )->setKeyValue( "angle", "180" );
|
|
Node_getEntity( pEDoor2 )->setKeyValue( "angle", "360" );
|
|
}
|
|
else
|
|
{
|
|
Node_getEntity( pEDoor1 )->setKeyValue( "angle", "270" );
|
|
Node_getEntity( pEDoor2 )->setKeyValue( "angle", "90" );
|
|
}
|
|
|
|
srand( (unsigned)time( nullptr ) );
|
|
|
|
char teamname[256];
|
|
sprintf( teamname, "t%i", rand() );
|
|
Node_getEntity( pEDoor1 )->setKeyValue( "team", teamname );
|
|
Node_getEntity( pEDoor2 )->setKeyValue( "team", teamname );
|
|
|
|
Node_getTraversable( pEDoor1 )->insert( newBrush1 );
|
|
Node_getTraversable( pEDoor2 )->insert( newBrush2 );
|
|
|
|
Node_getTraversable( GlobalSceneGraph().root() )->insert( pEDoor1 );
|
|
Node_getTraversable( GlobalSceneGraph().root() )->insert( pEDoor2 );
|
|
|
|
// ResetCurrentTexture();
|
|
}
|
|
|
|
#include "DBrush.h"
|
|
#include "dialogs/dialogs-gtk.h"
|
|
|
|
/*
|
|
?snap tmp verts for aabb to .125 for lip accuracy (helps) (not relevant when finding out via radiant means)
|
|
?option to actually snap verts //up to user now in from-winding gen method, except center
|
|
arbitrary winding
|
|
add sound cull brushes
|
|
move dist control
|
|
time setting
|
|
?oval shape when generating
|
|
prefer +face when similar area
|
|
?explicit ccw setting or better
|
|
inner layer filling orientation depending on move angle
|
|
?round expansion in non round shape
|
|
?way to adjust lip, speed of existing door
|
|
?reset options on dialog 'cancel'
|
|
*/
|
|
|
|
void BuildApertureDoors( scene::Instance& brushinstance, const class ApertureDoorRS& rs ){
|
|
auto float2string = [string = std::array<char, 64>()]( float value ) mutable -> const char* {
|
|
sprintf( string.data(), "%g", value );
|
|
return string.data();
|
|
};
|
|
|
|
DBrush dbrush;
|
|
dbrush.LoadFromBrush( brushinstance, false );
|
|
|
|
const DPlane *bestplane = nullptr;
|
|
DWinding bestwinding;
|
|
{
|
|
vec_t bestarea = 0;
|
|
for( const DPlane *plane : dbrush.faceList )
|
|
{
|
|
DWinding w = plane->BaseWindingForPlane();
|
|
for( const DPlane *cplane : dbrush.faceList )
|
|
{
|
|
DPlane negcplane( *cplane ); //negate plane to keep proper side
|
|
VectorNegate( negcplane.normal, negcplane.normal );
|
|
negcplane._d = -negcplane._d;
|
|
if( plane != cplane )
|
|
w.ChopWindingInPlace( &negcplane, .1f );
|
|
if( w.numpoints < 3 )
|
|
break;
|
|
}
|
|
if( const vec_t area = w.WindingArea(); area > bestarea + 1 //definitely better
|
|
// in doubts prefer positive direction for selection consistency
|
|
|| ( area > bestarea - 1 && VectorMax( plane->normal ) > VectorMax( bestplane->normal ) ) ){
|
|
bestarea = area;
|
|
bestplane = plane;
|
|
bestwinding = std::move( w );
|
|
}
|
|
}
|
|
}
|
|
if( bestplane != nullptr ){
|
|
UndoableCommand undo( "bobToolz.buildApertureDoor" );
|
|
|
|
vec3_t center;
|
|
bestwinding.WindingCentroid( center );
|
|
|
|
double thickness = 32;
|
|
{
|
|
DPlane *negplane = nullptr;
|
|
vec_t bestdot = 1;
|
|
for( DPlane *plane : dbrush.faceList )
|
|
{
|
|
if( vec_t dot = DotProduct( bestplane->normal, plane->normal ); dot < bestdot ){
|
|
bestdot = dot;
|
|
negplane = plane;
|
|
}
|
|
}
|
|
if( negplane != nullptr )
|
|
thickness = -negplane->DistanceToPoint( center );
|
|
}
|
|
vec_t radius = std::numeric_limits<vec_t>::max();
|
|
for( int i = bestwinding.numpoints - 1, j = 0; j < bestwinding.numpoints; i = j++ )
|
|
{
|
|
vec3_t r;
|
|
VectorMid( bestwinding.p[i], bestwinding.p[j], r );
|
|
VectorSubtract( r, center, r );
|
|
radius = std::min( radius, VectorLength( r ) );
|
|
}
|
|
|
|
{
|
|
const DoubleVector3 normal( vector3_from_array( bestplane->normal ) );
|
|
const DoubleVector3 rot = vector3_normalised( plane3_project_point( Plane3( normal, 0 ), // 0 distance to project vector
|
|
g_vector3_axes[ vector3_min_abs_component_index( normal ) ],
|
|
g_vector3_axes[ vector3_max_abs_component_index( normal ) ] ) ) * radius + vector3_from_array( center );
|
|
|
|
srand( (unsigned)time( nullptr ) );
|
|
char teamname[256];
|
|
sprintf( teamname, "t%i", rand() );
|
|
EntityClass* doorClass = GlobalEntityClassManager().findOrInsert( "func_door", true );
|
|
|
|
const DoubleVector3 cent = vector3_from_array( center );
|
|
std::vector<DoubleVector3> winding;
|
|
if( rs.fromWinding ){
|
|
for( const auto& p : std::span( bestwinding.p, bestwinding.numpoints ) )
|
|
winding.push_back( vector3_from_array( p ) );
|
|
}
|
|
else{
|
|
for( int i = 0; i < rs.segments; ++i )
|
|
{
|
|
Matrix4 mat( g_matrix4_identity );
|
|
// clockwise rotation; clockwise points order
|
|
matrix4_pivoted_rotate_by_axisangle( mat, normal, -c_2pi / rs.segments * ( i + ( rs.offsetStartAngle? 0.5 : 0 ) ), cent );
|
|
winding.push_back( matrix4_transformed_point( mat, rot ) );
|
|
}
|
|
}
|
|
|
|
const double edgespeed0 = rs.speedUse ? rs.speed : ( rs.distanceSet? rs.distance : radius ) / rs.time;
|
|
double edgespeed = edgespeed0;
|
|
|
|
for( size_t i = 0; i < winding.size(); ++i )
|
|
{
|
|
const DoubleVector3 rot1 = winding[ i ];
|
|
const DoubleVector3 rot2 = winding[( i + 1 ) % winding.size()];
|
|
const DoubleVector3 cent_ = cent - normal * thickness;
|
|
const DoubleVector3 rot1_ = rot1 - normal * thickness;
|
|
const DoubleVector3 rot2_ = rot2 - normal * thickness;
|
|
|
|
|
|
NodeSmartReference door( GlobalEntityCreator().createEntity( doorClass ) );
|
|
|
|
NodeSmartReference brush( GlobalBrushCreator().createBrush() );
|
|
AABB bounds;
|
|
|
|
if( !rs.slopedSegments ){
|
|
AddFaceWithTexture( brush, cent.data(), rot1.data(), rot2.data(), rs.textureMain.get(), false );
|
|
AddFaceWithTexture( brush, cent_.data(), rot2_.data(), rot1_.data(), rs.textureMain.get(), false );
|
|
AddFaceWithTexture( brush, cent.data(), cent_.data(), rot1_.data(), rs.textureTrim.get(), false );
|
|
AddFaceWithTexture( brush, cent_.data(), cent.data(), rot2.data(), rs.textureTrim.get(), false );
|
|
AddFaceWithTexture( brush, rot2.data(), rot1.data(), rot1_.data(), rs.textureTrim.get(), false );
|
|
|
|
for( const auto& p : { cent, cent_, rot1, rot1_, rot2, rot2_ } )
|
|
aabb_extend_by_point_safe( bounds, p );
|
|
|
|
if( rs.innerType == ApertureDoorRS::Inner::segmented ){
|
|
const double depth1 = std::clamp( rs.innerDepth1, 0.0, thickness / 2 - 1 );
|
|
const double depth2 = std::clamp( rs.innerDepth2, 0.0, thickness / 2 - 1 );
|
|
|
|
size_t jend = 1;
|
|
for( ; jend < winding.size(); ++jend ) //find out jend beforehand for depth step calc
|
|
{
|
|
const DoubleVector3 r1 = winding[( i + jend + 0 ) % winding.size()];
|
|
if( plane3_distance_to_point( plane3_for_points( cent, cent_, rot1_ ), r1 ) > -1 )
|
|
break;
|
|
}
|
|
|
|
for( size_t j = 1; j < jend; ++j )
|
|
{
|
|
const DoubleVector3 extradepth = normal * ( depth1 + ( depth2 - depth1 ) / std::max( size_t( 1 ), jend - 2 ) * ( j - 1 ) );
|
|
const DoubleVector3 c = cent - extradepth;
|
|
const DoubleVector3 r1 = winding[( i + j + 0 ) % winding.size()] - extradepth;
|
|
const DoubleVector3 r2 = winding[( i + j + 1 ) % winding.size()] - extradepth;
|
|
|
|
const DoubleVector3 c_ = cent_ + extradepth;
|
|
const DoubleVector3 r1_ = r1 - normal * thickness + extradepth * 2;
|
|
const DoubleVector3 r2_ = r2 - normal * thickness + extradepth * 2;
|
|
|
|
for( const auto& p : { c, c_, r1, r1_, r2, r2_ } )
|
|
aabb_extend_by_point_safe( bounds, p );
|
|
|
|
NodeSmartReference b( GlobalBrushCreator().createBrush() );
|
|
|
|
AddFaceWithTexture( b, c.data(), r1.data(), r2.data(), rs.innerTextureMain.get(), false );
|
|
AddFaceWithTexture( b, c_.data(), r2_.data(), r1_.data(), rs.innerTextureMain.get(), false );
|
|
AddFaceWithTexture( b, c.data(), c_.data(), r1_.data(), rs.innerTextureTrim.get(), false );
|
|
AddFaceWithTexture( b, c_.data(), c.data(), r2.data(), rs.innerTextureTrim.get(), false );
|
|
AddFaceWithTexture( b, r2.data(), r1.data(), r1_.data(), rs.innerTextureTrim.get(), false );
|
|
|
|
Node_getTraversable( door )->insert( b );
|
|
}
|
|
}
|
|
else if( rs.innerType == ApertureDoorRS::Inner::sloped ){
|
|
NodeSmartReference b( GlobalBrushCreator().createBrush() );
|
|
AddFaceWithTexture( b, cent.data(), cent_.data(), rot2.data(), rs.innerTextureTrim.get(), false );
|
|
AddFaceWithTexture( b, cent.data(), cent_.data(), rot1_.data(), rs.innerTextureTrim.get(), false );
|
|
double mindot = 2;
|
|
DoubleVector3 r( 0 ); // suppress -Wmaybe-uninitialized
|
|
for( size_t j = 1; j < winding.size(); ++j )
|
|
{
|
|
const DoubleVector3 r1 = winding[( i + j + 0 ) % winding.size()];
|
|
const DoubleVector3 r2 = winding[( i + j + 1 ) % winding.size()];
|
|
const DoubleVector3 r2_ = r2 + normal * 16;
|
|
|
|
if( plane3_distance_to_point( plane3_for_points( cent, cent_, rot1_ ), r1 ) > -1 )
|
|
break;
|
|
|
|
AddFaceWithTexture( b, r1.data(), r2.data(), r2_.data(), rs.innerTextureTrim.get(), false );
|
|
|
|
if( double dot = std::fabs( vector3_dot( vector3_normalised( rot2 - cent ), vector3_normalised( r2 - cent ) ) ); dot < mindot ){
|
|
mindot = dot;
|
|
r = r2;
|
|
}
|
|
}
|
|
const double depth1 = std::clamp( rs.innerDepth1, 0.0, thickness / 2 - 1 );
|
|
const double depth2 = std::clamp( rs.innerDepth2, 0.0, thickness / 2 - 1 );
|
|
AddFaceWithTexture( b, ( cent - normal * depth1 ).data(),
|
|
( rot2 - normal * depth1 ).data(),
|
|
( r - normal * depth2 ).data(), rs.innerTextureMain.get(), false );
|
|
AddFaceWithTexture( b, ( rot2_ + normal * depth1 ).data(),
|
|
( cent_ + normal * depth1 ).data(),
|
|
( r - normal * ( thickness - depth2 ) ).data(), rs.innerTextureMain.get(), false );
|
|
Node_getTraversable( door )->insert( b );
|
|
bounds = AABB(); //invalidate to find out via radiant means
|
|
}
|
|
}
|
|
else if( rs.slopedSegments ){ // bounds via radiant means
|
|
const DoubleVector3 r1 = rot1 + normal * rs.slopedDepth1;
|
|
const DoubleVector3 r2 = rot1 + vector3_cross( normal, cent - rot1 ) + normal * rs.slopedDepth2;
|
|
const DoubleVector3 r4 = cent + ( cent - rot1 );
|
|
const DoubleVector3 r3 = r4 + vector3_cross( normal, cent - rot1 );
|
|
const DoubleVector3 r1_ = rot1_ - normal * rs.slopedDepth1;
|
|
const DoubleVector3 r2_ = rot1_ + vector3_cross( normal, cent - rot1 ) - normal * rs.slopedDepth2;
|
|
const DoubleVector3 r4_ = cent_ + ( cent - rot1 );
|
|
const DoubleVector3 r3_ = r4_ + vector3_cross( normal, cent - rot1 );
|
|
|
|
AddFaceWithTexture( brush, r1.data(), r2.data(), r3.data(), rs.textureMain.get(), false );
|
|
AddFaceWithTexture( brush, r2_.data(), r1_.data(), r3_.data(), rs.textureMain.get(), false );
|
|
AddFaceWithTexture( brush, r1.data(), r4.data(), r4_.data(), rs.textureTrim.get(), false );
|
|
|
|
if( !rs.slopedSegmentsRoundize ){
|
|
AddFaceWithTexture( brush, r2.data(), r1.data(), r1_.data(), rs.textureTrim.get(), false );
|
|
AddFaceWithTexture( brush, r3.data(), r2.data(), r2_.data(), rs.textureTrim.get(), false );
|
|
AddFaceWithTexture( brush, r4.data(), r3.data(), r3_.data(), rs.textureTrim.get(), false );
|
|
}
|
|
else{ // optional round outline
|
|
for( size_t j = 0; j < winding.size(); ++j )
|
|
{
|
|
const DoubleVector3 r1 = winding[( i + j + 0 ) % winding.size()];
|
|
const DoubleVector3 r2 = winding[( i + j + 1 ) % winding.size()];
|
|
const DoubleVector3 r2_ = r2 + normal * 16;
|
|
|
|
if( j != 0 && plane3_distance_to_point( plane3_for_points( cent, cent_, rot1_ ), r1 ) > -1 )
|
|
break;
|
|
|
|
AddFaceWithTexture( brush, r1.data(), r2.data(), r2_.data(), rs.textureTrim.get(), false );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( rs.silenceType == ApertureDoorRS::Silence::brush ){
|
|
const DoubleVector3 a = cent + rs.silenceBrushesOffset;
|
|
const DoubleVector3 b = a + DoubleVector3( 0, 64, 0 );
|
|
const DoubleVector3 c = a + DoubleVector3( 64, 64, 0 );
|
|
const DoubleVector3 d = a + DoubleVector3( 64, 0, 0 );
|
|
const DoubleVector3 a_ = a - DoubleVector3( 0, 0, 64 );
|
|
const DoubleVector3 b_ = b - DoubleVector3( 0, 0, 64 );
|
|
const DoubleVector3 c_ = c - DoubleVector3( 0, 0, 64 );
|
|
const DoubleVector3 d_ = d - DoubleVector3( 0, 0, 64 );
|
|
|
|
NodeSmartReference brush( GlobalBrushCreator().createBrush() );
|
|
AddFaceWithTexture( brush, a.data(), b.data(), c.data(), "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, c_.data(), b_.data(), a_.data(), "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, b.data(), a.data(), a_.data(), "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, c.data(), b.data(), b_.data(), "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, d.data(), c.data(), c_.data(), "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, a.data(), d.data(), d_.data(), "textures/common/caulk", false );
|
|
|
|
Node_getTraversable( door )->insert( brush );
|
|
|
|
if( aabb_valid( bounds ) ) // add to bounds, if we construct bounds here
|
|
for( const auto& p : { a, a_, b, b_, c, c_, d, d_ } )
|
|
aabb_extend_by_point_safe( bounds, p );
|
|
}
|
|
|
|
DoubleVector3 dir;
|
|
{
|
|
Matrix4 mat( g_matrix4_identity );
|
|
matrix4_rotate_by_axisangle( mat, normal, degrees_to_radians( rs.openAngle ) );
|
|
// "equal orthogonal speed of edges"
|
|
// dir = vector3_normalised( matrix4_transformed_direction( mat, vector3_normalised( rot2 - cent ) - vector3_normalised( rot1 - cent ) ) );
|
|
// winding shape dependent speeds
|
|
dir = vector3_normalised( matrix4_transformed_direction( mat, rot2 - rot1 ) );
|
|
|
|
DoubleVector3 angles( -radians_to_degrees( asin( dir[2] ) ), //PITCH
|
|
radians_to_degrees( atan2( dir[1], dir[0] ) ), //YAW
|
|
0 );
|
|
vector3_snap_to_zero( angles, 0.0001 ); // snap to avoid scientific notation
|
|
|
|
char value[64];
|
|
sprintf( value, "%g %g %g", angles[0], angles[1], angles[2] );
|
|
Node_getEntity( door )->setKeyValue( "angles", value );
|
|
}
|
|
Node_getEntity( door )->setKeyValue( "team", teamname );
|
|
if( rs.health )
|
|
Node_getEntity( door )->setKeyValue( "health", "1" );
|
|
Node_getEntity( door )->setKeyValue( "wait", float2string( rs.wait ) );
|
|
|
|
Node_getTraversable( door )->insert( brush );
|
|
Node_getTraversable( GlobalSceneGraph().root() )->insert( door );
|
|
|
|
if( !aabb_valid( bounds ) ){
|
|
scene::Path path( makeReference( GlobalSceneGraph().root() ) );
|
|
path.push( makeReference( door.get() ) );
|
|
bounds = GlobalSceneGraph().find( path )->worldAABB();
|
|
}
|
|
{
|
|
// calculate speeds to work with arbitrary windings as input, shape dependent expansion
|
|
// is it possible to calculate directly w/o global 'edgespeed' value?
|
|
// speed.len = edgespeed.len / speed.dot(edgespeed)
|
|
// edgespeed2.len = speed.len * speed.dot(edgespeed2)
|
|
const double
|
|
speed = edgespeed / vector3_dot( vector3_normalised( rot2 - rot1 ), vector3_cross( vector3_normalised( rot1 - cent ), normal ) );
|
|
edgespeed = speed * vector3_dot( vector3_normalised( rot2 - rot1 ), vector3_cross( vector3_normalised( rot2 - cent ), normal ) );
|
|
// two versions for "equal orthogonal speed of edges" (also arbitrary windings)
|
|
// const double speed = rs.speed / ( vector3_length( vector3_normalised( rot2 - cent ) + vector3_normalised( rot1 - cent ) ) / 2 );
|
|
// const double speed = rs.speed / sin( ( c_pi - acos( vector3_dot( vector3_normalised( rot2 - cent ), vector3_normalised( rot1 - cent ) ) ) ) / 2 );
|
|
Node_getEntity( door )->setKeyValue( "speed", float2string( speed ) );
|
|
|
|
const DoubleVector3 abs_dir( std::fabs( dir[0] ), std::fabs( dir[1] ), std::fabs( dir[2] ) );
|
|
const auto fullDistance = vector3_dot( abs_dir, DoubleVector3( 2 ) + bounds.extents * 2 ); //there is +2 to bounds in engine somewhere 🧩
|
|
// distance = DotProduct( abs_movedir, size ) - lip; // game code
|
|
// VectorMA( ent->pos1, distance, ent->movedir, ent->pos2 );
|
|
// note: radius is too much for 3/4/5 segments, might want to limit value
|
|
const float lip = fullDistance - ( rs.distanceSet? rs.distance : radius ) * ( edgespeed0 != 0 ? speed / edgespeed0 : 0 ); // 0 speed and distance inputs are allowed, handle
|
|
Node_getEntity( door )->setKeyValue( "lip", float2string( lip ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
void MakeBevel( vec3_t vMin, vec3_t vMax ){
|
|
NodeSmartReference patch( GlobalPatchCreator().createPatch() );
|
|
GlobalPatchCreator().Patch_resize( patch, 3, 3 );
|
|
GlobalPatchCreator().Patch_setShader( patch, "textures/common/caulk" );
|
|
PatchControlMatrix matrix = GlobalPatchCreator().Patch_getControlPoints( patch );
|
|
vec3_t x_3, y_3, z_3;
|
|
x_3[0] = vMin[0]; x_3[1] = vMin[0]; x_3[2] = vMax[0];
|
|
y_3[0] = vMin[1]; y_3[1] = vMax[1]; y_3[2] = vMax[1];
|
|
z_3[0] = vMin[2]; z_3[1] = ( vMax[2] + vMin[2] ) / 2; z_3[2] = vMax[2];
|
|
/*
|
|
x_3[0] = 0; x_3[1] = 0; x_3[2] = 64;
|
|
y_3[0] = 0; y_3[1] = 64; y_3[2] = 64;
|
|
z_3[0] = 0; z_3[1] = 32; z_3[2] = 64;*/
|
|
for ( int i = 0; i < 3; ++i )
|
|
{
|
|
for ( int j = 0; j < 3; ++j )
|
|
{
|
|
PatchControl& p = matrix( i, j );
|
|
p.m_vertex[0] = x_3[i];
|
|
p.m_vertex[1] = y_3[i];
|
|
p.m_vertex[2] = z_3[j];
|
|
}
|
|
}
|
|
//does invert the matrix, else the patch face is on wrong side.
|
|
for ( int i = 0; i < 3; ++i )
|
|
{
|
|
for ( int j = 0; j < 1; ++j )
|
|
{
|
|
PatchControl& p = matrix( i, 2 - j );
|
|
PatchControl& q = matrix( i, j );
|
|
std::swap( p.m_vertex, q.m_vertex );
|
|
//std::swap( p.m_texcoord, q.m_texcoord );
|
|
}
|
|
}
|
|
GlobalPatchCreator().Patch_controlPointsChanged( patch );
|
|
//TODO - the patch has textures weird, patchmanip.h has all function it needs.. lots of duplicate code..
|
|
//NaturalTexture( patch );
|
|
Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( patch );
|
|
}
|
|
|
|
void BuildCornerStairs( vec3_t vMin, vec3_t vMax, int nSteps, const char* mainTexture, const char* riserTex ){
|
|
auto *topPoints = new vec3_t[nSteps + 1];
|
|
auto *botPoints = new vec3_t[nSteps + 1];
|
|
|
|
//bool bFacesUse[6] = {true, true, false, true, false, false};
|
|
|
|
vec3_t centre;
|
|
VectorCopy( vMin, centre );
|
|
centre[0] = vMax[0];
|
|
|
|
int height = (int)( vMax[2] - vMin[2] ) / nSteps;
|
|
|
|
vec3_t vTop, vBot;
|
|
VectorCopy( vMax, vTop );
|
|
VectorCopy( vMin, vBot );
|
|
vTop[2] = vMin[2] + height;
|
|
|
|
int i;
|
|
for ( i = 0; i <= nSteps; ++i )
|
|
{
|
|
VectorCopy( centre, topPoints[i] );
|
|
VectorCopy( centre, botPoints[i] );
|
|
|
|
topPoints[i][2] = vMax[2];
|
|
botPoints[i][2] = vMin[2];
|
|
|
|
topPoints[i][0] -= 10 * sinf( Q_PI * i / ( 2 * nSteps ) );
|
|
topPoints[i][1] += 10 * cosf( Q_PI * i / ( 2 * nSteps ) );
|
|
|
|
botPoints[i][0] = topPoints[i][0];
|
|
botPoints[i][1] = topPoints[i][1];
|
|
}
|
|
|
|
vec3_t tp[3];
|
|
for ( int j = 0; j < 3; ++j )
|
|
VectorCopy( topPoints[j], tp[j] );
|
|
|
|
for ( i = 0; i < nSteps; ++i )
|
|
{
|
|
NodeSmartReference brush( GlobalBrushCreator().createBrush() );
|
|
vec3_t v1, v2, v3, v5, v6, v7;
|
|
VectorCopy( vBot, v1 );
|
|
VectorCopy( vBot, v2 );
|
|
VectorCopy( vBot, v3 );
|
|
VectorCopy( vTop, v5 );
|
|
VectorCopy( vTop, v6 );
|
|
VectorCopy( vTop, v7 );
|
|
|
|
v2[0] = vTop[0];
|
|
v3[1] = vTop[1];
|
|
|
|
v6[0] = vBot[0];
|
|
v7[1] = vBot[1];
|
|
|
|
AddFaceWithTexture( brush, v1, v2, v3, "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, v1, v3, v6, "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, v5, v6, v3, "textures/common/caulk", false );
|
|
|
|
for ( int j = 0; j < 3; ++j )
|
|
tp[j][2] = vTop[2];
|
|
|
|
AddFaceWithTexture( brush, tp[2], tp[1], tp[0], mainTexture, false );
|
|
|
|
AddFaceWithTexture( brush, centre, botPoints[i + 1], topPoints[i + 1], "textures/common/caulk", false );
|
|
AddFaceWithTexture( brush, centre, topPoints[i], botPoints[i], riserTex, false );
|
|
|
|
Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( brush );
|
|
|
|
vTop[2] += height;
|
|
vBot[2] += height;
|
|
}
|
|
|
|
delete[] topPoints;
|
|
delete[] botPoints;
|
|
|
|
vMin[2] += height;
|
|
vMax[2] += height;
|
|
MakeBevel( vMin, vMax );
|
|
}
|