source-engine/hammer/createarch.cpp

658 lines
18 KiB
C++
Raw Permalink Normal View History

2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "stdafx.h"
#include "OPTGeneral.h"
#include "Options.h"
#include "hammer_mathlib.h"
#include "MapFace.h"
#include "MapGroup.h"
#include "MapSolid.h"
#include "hammer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
//-----------------------------------------------------------------------------
// Purpose: Create a segment using two polygons and a start and end position in
// those polygons.
// Input : fZMin -
// fZMax -
// fOuterPoints -
// fInnerPoints -
// iStart -
// iEnd -
// bCreateSouthFace -
// Output :
//-----------------------------------------------------------------------------
static CMapSolid *CreateSegment(float fZMin, float fZMax, float fOuterPoints[][2], float fInnerPoints[][2], int iStart, int iEnd, BOOL bCreateSouthFace)
{
CMapFace Face;
Vector points[4]; // all sides have four vertices
CMapSolid *pSolid = new CMapSolid;
int iNorthSouthPoints = 3 + (bCreateSouthFace ? 1 : 0);
// create top face
points[0][0] = fOuterPoints[iStart][0];
points[0][1] = fOuterPoints[iStart][1];
points[0][2] = fZMin;
points[1][0] = fOuterPoints[iEnd][0];
points[1][1] = fOuterPoints[iEnd][1];
points[1][2] = fZMin;
points[2][0] = fInnerPoints[iEnd][0];
points[2][1] = fInnerPoints[iEnd][1];
points[2][2] = fZMin;
points[3][0] = fInnerPoints[iStart][0];
points[3][1] = fInnerPoints[iStart][1];
points[3][2] = fZMin;
Face.CreateFace(points, iNorthSouthPoints);
pSolid->AddFace(&Face);
// bottom face - set other z value and reverse order
for (int i = 0; i < 4; i++)
{
points[i][2] = fZMax;
}
Face.CreateFace(points, -iNorthSouthPoints);
pSolid->AddFace(&Face);
// left side
points[0][0] = fOuterPoints[iStart][0];
points[0][1] = fOuterPoints[iStart][1];
points[0][2] = fZMax;
points[1][0] = fOuterPoints[iStart][0];
points[1][1] = fOuterPoints[iStart][1];
points[1][2] = fZMin;
points[2][0] = fInnerPoints[iStart][0];
points[2][1] = fInnerPoints[iStart][1];
points[2][2] = fZMin;
points[3][0] = fInnerPoints[iStart][0];
points[3][1] = fInnerPoints[iStart][1];
points[3][2] = fZMax;
Face.CreateFace(points, 4);
pSolid->AddFace(&Face);
// right side
points[0][0] = fOuterPoints[iEnd][0];
points[0][1] = fOuterPoints[iEnd][1];
points[0][2] = fZMin;
points[1][0] = fOuterPoints[iEnd][0];
points[1][1] = fOuterPoints[iEnd][1];
points[1][2] = fZMax;
points[2][0] = fInnerPoints[iEnd][0];
points[2][1] = fInnerPoints[iEnd][1];
points[2][2] = fZMax;
points[3][0] = fInnerPoints[iEnd][0];
points[3][1] = fInnerPoints[iEnd][1];
points[3][2] = fZMin;
Face.CreateFace(points, 4);
pSolid->AddFace(&Face);
// north face
points[0][0] = fOuterPoints[iEnd][0];
points[0][1] = fOuterPoints[iEnd][1];
points[0][2] = fZMin;
points[1][0] = fOuterPoints[iStart][0];
points[1][1] = fOuterPoints[iStart][1];
points[1][2] = fZMin;
points[2][0] = fOuterPoints[iStart][0];
points[2][1] = fOuterPoints[iStart][1];
points[2][2] = fZMax;
points[3][0] = fOuterPoints[iEnd][0];
points[3][1] = fOuterPoints[iEnd][1];
points[3][2] = fZMax;
Face.CreateFace(points, 4);
pSolid->AddFace(&Face);
// south face
if (bCreateSouthFace)
{
points[0][0] = fInnerPoints[iStart][0];
points[0][1] = fInnerPoints[iStart][1];
points[0][2] = fZMin;
points[1][0] = fInnerPoints[iEnd][0];
points[1][1] = fInnerPoints[iEnd][1];
points[1][2] = fZMin;
points[2][0] = fInnerPoints[iEnd][0];
points[2][1] = fInnerPoints[iEnd][1];
points[2][2] = fZMax;
points[3][0] = fInnerPoints[iStart][0];
points[3][1] = fInnerPoints[iStart][1];
points[3][2] = fZMax;
Face.CreateFace(points, 4);
pSolid->AddFace(&Face);
}
pSolid->InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_ALL | INIT_TEXTURE_FORCE);
return(pSolid);
}
//-----------------------------------------------------------------------------
// Purpose: Create a segment using two polygons and a start and end position in
// those polygons.
// Input : fZMin -
// fZMax -
// fOuterPoints -
// fInnerPoints -
// iStart -
// iEnd -
// bCreateSouthFace -
// Output :
//-----------------------------------------------------------------------------
static CMapSolid *CreateSegment(float fStartOuterPoints[][3], float fStartInnerPoints[][3],
float fEndOuterPoints[][3], float fEndInnerPoints[][3],
int iStart, int iEnd, BOOL bCreateSouthFace)
{
CMapFace Face;
Vector points[4]; // all sides have four vertices
CMapSolid *pSolid = new CMapSolid;
// create top face
points[0][0] = fStartOuterPoints[iStart][0];
points[0][1] = fStartOuterPoints[iStart][1];
points[0][2] = fStartOuterPoints[iStart][2];
points[1][0] = fStartOuterPoints[iEnd][0];
points[1][1] = fStartOuterPoints[iEnd][1];
points[1][2] = fStartOuterPoints[iEnd][2];
points[2][0] = fStartInnerPoints[iEnd][0];
points[2][1] = fStartInnerPoints[iEnd][1];
points[2][2] = fStartInnerPoints[iEnd][2];
points[3][0] = fStartInnerPoints[iStart][0];
points[3][1] = fStartInnerPoints[iStart][1];
points[3][2] = fStartInnerPoints[iStart][2];
Face.CreateFace(points, -4);
pSolid->AddFace(&Face);
// bottom face - set other z value and reverse order
points[0][0] = fEndOuterPoints[iStart][0];
points[0][1] = fEndOuterPoints[iStart][1];
points[0][2] = fEndOuterPoints[iStart][2];
points[1][0] = fEndOuterPoints[iEnd][0];
points[1][1] = fEndOuterPoints[iEnd][1];
points[1][2] = fEndOuterPoints[iEnd][2];
points[2][0] = fEndInnerPoints[iEnd][0];
points[2][1] = fEndInnerPoints[iEnd][1];
points[2][2] = fEndInnerPoints[iEnd][2];
points[3][0] = fEndInnerPoints[iStart][0];
points[3][1] = fEndInnerPoints[iStart][1];
points[3][2] = fEndInnerPoints[iStart][2];
Face.CreateFace(points, 4);
pSolid->AddFace(&Face);
// left side
points[0][0] = fEndOuterPoints[iStart][0];
points[0][1] = fEndOuterPoints[iStart][1];
points[0][2] = fEndOuterPoints[iStart][2];
points[1][0] = fStartOuterPoints[iStart][0];
points[1][1] = fStartOuterPoints[iStart][1];
points[1][2] = fStartOuterPoints[iStart][2];
points[2][0] = fStartInnerPoints[iStart][0];
points[2][1] = fStartInnerPoints[iStart][1];
points[2][2] = fStartInnerPoints[iStart][2];
points[3][0] = fEndInnerPoints[iStart][0];
points[3][1] = fEndInnerPoints[iStart][1];
points[3][2] = fEndInnerPoints[iStart][2];
Face.CreateFace(points, -4);
pSolid->AddFace(&Face);
// right side
points[0][0] = fStartOuterPoints[iEnd][0];
points[0][1] = fStartOuterPoints[iEnd][1];
points[0][2] = fStartOuterPoints[iEnd][2];
points[1][0] = fEndOuterPoints[iEnd][0];
points[1][1] = fEndOuterPoints[iEnd][1];
points[1][2] = fEndOuterPoints[iEnd][2];
points[2][0] = fEndInnerPoints[iEnd][0];
points[2][1] = fEndInnerPoints[iEnd][1];
points[2][2] = fEndInnerPoints[iEnd][2];
points[3][0] = fStartInnerPoints[iEnd][0];
points[3][1] = fStartInnerPoints[iEnd][1];
points[3][2] = fStartInnerPoints[iEnd][2];
Face.CreateFace(points, -4);
pSolid->AddFace(&Face);
// north face
points[0][0] = fStartOuterPoints[iEnd][0];
points[0][1] = fStartOuterPoints[iEnd][1];
points[0][2] = fStartOuterPoints[iEnd][2];
points[1][0] = fStartOuterPoints[iStart][0];
points[1][1] = fStartOuterPoints[iStart][1];
points[1][2] = fStartOuterPoints[iStart][2];
points[2][0] = fEndOuterPoints[iStart][0];
points[2][1] = fEndOuterPoints[iStart][1];
points[2][2] = fEndOuterPoints[iStart][2];
points[3][0] = fEndOuterPoints[iEnd][0];
points[3][1] = fEndOuterPoints[iEnd][1];
points[3][2] = fEndOuterPoints[iEnd][2];
Face.CreateFace(points, -4);
pSolid->AddFace(&Face);
// south face
if (bCreateSouthFace)
{
points[0][0] = fStartInnerPoints[iStart][0];
points[0][1] = fStartInnerPoints[iStart][1];
points[0][2] = fStartInnerPoints[iStart][2];
points[1][0] = fStartInnerPoints[iEnd][0];
points[1][1] = fStartInnerPoints[iEnd][1];
points[1][2] = fStartInnerPoints[iEnd][2];
points[2][0] = fEndInnerPoints[iEnd][0];
points[2][1] = fEndInnerPoints[iEnd][1];
points[2][2] = fEndInnerPoints[iEnd][2];
points[3][0] = fEndInnerPoints[iStart][0];
points[3][1] = fEndInnerPoints[iStart][1];
points[3][2] = fEndInnerPoints[iStart][2];
Face.CreateFace(points, -4);
pSolid->AddFace(&Face);
}
pSolid->InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_ALL | INIT_TEXTURE_FORCE);
return(pSolid);
}
//-----------------------------------------------------------------------------
// Make a 2d arc
//-----------------------------------------------------------------------------
void MakeArcCenterRadius(float xCenter, float yCenter, float xrad, float yrad, int npoints, float start_ang, float fArc, float points[][2])
{
int point;
float angle = start_ang;
float angle_delta;
angle_delta = fArc / (float)npoints;
// Add an additional points if we are not doing a full circle
if (fArc != 360.0)
{
++npoints;
}
for( point = 0; point < npoints; point++ )
{
if ( angle > 360 )
{
angle -= 360;
}
points[point][0] = V_rint(xCenter + (float)cos(DEG2RAD(angle)) * xrad);
points[point][1] = V_rint(yCenter + (float)sin(DEG2RAD(angle)) * yrad);
angle += angle_delta;
}
// Full circle, recopy the first point as the closing point.
if (fArc == 360.0)
{
points[point][0] = points[0][0];
points[point][1] = points[0][1];
}
}
void MakeArc(float x1, float y1, float x2, float y2, int npoints, float start_ang, float fArc, float points[][2])
{
float xrad = (x2 - x1) / 2.0f;
float yrad = (y2 - y1) / 2.0f;
// make centerpoint for polygon:
float xCenter = x1 + xrad;
float yCenter = y1 + yrad;
MakeArcCenterRadius( xCenter, yCenter, xrad, yrad, npoints, start_ang, fArc, points );
}
#define ARC_MAX_POINTS 4096
//-----------------------------------------------------------------------------
// Purpose:
// Input : pBox -
// fStartAngle -
// iSides -
// fArc -
// iWallWidth -
// iAddHeight -
// bPreview -
// Output : Returns a group containing the arch solids.
//-----------------------------------------------------------------------------
CMapClass *CreateArch(BoundBox *pBox, float fStartAngle, int iSides, float fArc, int iWallWidth, int iAddHeight, BOOL bPreview)
{
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
//
// create outer points
//
MakeArc(pBox->bmins[AXIS_X], pBox->bmins[AXIS_Y],
pBox->bmaxs[AXIS_X], pBox->bmaxs[AXIS_Y], iSides,
fStartAngle, fArc, fOuterPoints);
//
// create inner points
//
MakeArc(pBox->bmins[AXIS_X] + iWallWidth,
pBox->bmins[AXIS_Y] + iWallWidth,
pBox->bmaxs[AXIS_X] - iWallWidth,
pBox->bmaxs[AXIS_Y] - iWallWidth, iSides,
fStartAngle, fArc, fInnerPoints);
//
// check wall width - if it's half or more of the total,
// set the inner poinst to the center point of the box
// and turn off the CreateSouthFace flag
//
BOOL bCreateSouthFace = TRUE;
Vector Center;
pBox->GetBoundsCenter(Center);
if((iWallWidth*2+8) >= (pBox->bmaxs[AXIS_X] - pBox->bmins[AXIS_X]) ||
(iWallWidth*2+8) >= (pBox->bmaxs[AXIS_Y] - pBox->bmins[AXIS_Y]))
{
for(int i = 0; i < ARC_MAX_POINTS; i++)
{
fInnerPoints[i][AXIS_X] = Center[AXIS_X];
fInnerPoints[i][AXIS_Y] = Center[AXIS_Y];
}
bCreateSouthFace = FALSE;
}
// create group for segments
CMapGroup *pGroup = new CMapGroup;
Vector MoveAccum( 0.f, 0.f, 0.f );
float fMinZ, fMaxZ;
fMinZ = pBox->bmins[2];
fMaxZ = pBox->bmaxs[2];
if ((fMaxZ - fMinZ) < 1.0f)
fMaxZ = fMinZ + 1.0f;
for (int i = 0; i < iSides; i++)
{
int iNextPoint = i+1;
if (iNextPoint >= iSides + 1)
iNextPoint = 0;
CMapSolid *pSolid = CreateSegment(
fMinZ, fMaxZ,
fOuterPoints, fInnerPoints,
i, iNextPoint, bCreateSouthFace);
pGroup->AddChild(pSolid);
if (iAddHeight && i) // don't move first segment
{
MoveAccum[2] += iAddHeight;
pSolid->TransMove(MoveAccum);
}
}
pGroup->CalcBounds(TRUE);
if (Options.general.bStretchArches)
{
// make sure size of group's bounds are size of original bounds -
// if not, scale up. this can happen when we use rotation.
Vector objsize, boundsize;
pBox->GetBoundsSize(boundsize);
pGroup->GetBoundsSize(objsize);
if (boundsize[AXIS_X] > objsize[AXIS_X] ||
boundsize[AXIS_Y] > objsize[AXIS_Y])
{
Vector scale;
scale[AXIS_X] = boundsize[AXIS_X] / objsize[AXIS_X];
scale[AXIS_Y] = boundsize[AXIS_Y] / objsize[AXIS_Y];
scale[AXIS_Z] = 1.0f; // xxxYWB scaling by 0 causes veneers, so I changed to 1.0
Vector center;
pBox->GetBoundsCenter(center);
pGroup->TransScale(center, scale);
}
}
return pGroup;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pBox -
// fStartAngle -
// iSides -
// fArc -
// iWallWidth -
// iAddHeight -
// bPreview -
// Output : Returns a group containing the arch solids.
//-----------------------------------------------------------------------------
typedef float TorusPointList_t[ARC_MAX_POINTS][3];
CMapClass *CreateTorus(BoundBox *pBox, float fStartAngle, int iSides, float fArc, int iWallWidth, float flCrossSectionalRadius,
float fRotationStartAngle, int iRotationSides, float fRotationArc, int iAddHeight, BOOL bPreview)
{
float xCenter = (pBox->bmaxs[AXIS_X] + pBox->bmins[AXIS_X]) * 0.5f;
float yCenter = (pBox->bmaxs[AXIS_Y] + pBox->bmins[AXIS_Y]) * 0.5f;
float xRad = (pBox->bmaxs[AXIS_X] - xCenter);
float yRad = (pBox->bmaxs[AXIS_Y] - yCenter);
if (xRad < 0.0f )
{
xRad = 0.0f;
}
if (yRad < 0.0f )
{
yRad = 0.0f;
}
if ( flCrossSectionalRadius > (xRad * 0.5f) )
{
flCrossSectionalRadius = (xRad * 0.5f);
}
if ( flCrossSectionalRadius > (yRad * 0.5f) )
{
flCrossSectionalRadius = (yRad * 0.5f);
}
if ( iWallWidth < flCrossSectionalRadius )
{
flCrossSectionalRadius -= iWallWidth;
}
else
{
iWallWidth = flCrossSectionalRadius;
flCrossSectionalRadius = 0.0f;
}
float flCrossSectionHalfWidth = flCrossSectionalRadius + iWallWidth;
xRad -= flCrossSectionHalfWidth;
yRad -= flCrossSectionHalfWidth;
float fOuterPoints[ARC_MAX_POINTS][2];
float fInnerPoints[ARC_MAX_POINTS][2];
// create outer points (unrotated)
MakeArcCenterRadius(0.0f, 0.0f,
flCrossSectionalRadius + iWallWidth, flCrossSectionalRadius + iWallWidth,
iSides, fStartAngle, fArc, fOuterPoints);
BOOL bCreateSouthFace = TRUE;
if ( flCrossSectionalRadius != 0.0f )
{
// create inner points (unrotated)
MakeArcCenterRadius(0.0f, 0.0f, flCrossSectionalRadius, flCrossSectionalRadius,
iSides, fStartAngle, fArc, fInnerPoints);
}
else
{
for( int i = 0; i < iSides; i++)
{
fInnerPoints[i][0] = fInnerPoints[i][1] = 0.0f;
}
bCreateSouthFace = FALSE;
}
// create group for segments
CMapGroup *pGroup = new CMapGroup;
TorusPointList_t innerPoints[2];
TorusPointList_t outerPoints[2];
TorusPointList_t *pStartInnerPoints;
TorusPointList_t *pStartOuterPoints;
TorusPointList_t *pEndInnerPoints = &innerPoints[1];
TorusPointList_t *pEndOuterPoints = &outerPoints[1];
int nCurrIndex = 0;
float flCurrentZ = pBox->bmins[AXIS_Z] + iWallWidth + flCrossSectionalRadius;
float flDeltaZ = (float)iAddHeight / (float)(iRotationSides);
float flRotationAngle = fRotationStartAngle;
float flRotationDeltaAngle = fRotationArc / iRotationSides;
bool bIsCircle = ( iAddHeight == 0.0f ) && ( fRotationArc == 360.0f );
++iRotationSides;
for ( int i = 0; i != iRotationSides; ++i )
{
// This eliminates a seam in circular toruses
if ( bIsCircle && (i == iRotationSides - 1) )
{
flRotationAngle = fRotationStartAngle;
}
float xCurrCenter, yCurrCenter;
float flCosAngle = cos( DEG2RAD(flRotationAngle) );
float flSinAngle = sin( DEG2RAD(flRotationAngle) );
xCurrCenter = xCenter + xRad * flCosAngle;
yCurrCenter = yCenter + yRad * flSinAngle;
// Update buffers
pStartInnerPoints = pEndInnerPoints;
pStartOuterPoints = pEndOuterPoints;
pEndInnerPoints = &innerPoints[nCurrIndex];
pEndOuterPoints = &outerPoints[nCurrIndex];
nCurrIndex = 1 - nCurrIndex;
// Transform points into actual space.
int jPrevPoint = -1;
int j = 0;
do
{
// x original is transformed into x/y based on rotation
// y original is transformed into z
(*pEndInnerPoints)[j][0] = xCurrCenter + fInnerPoints[j][0] * flCosAngle;
(*pEndInnerPoints)[j][1] = yCurrCenter + fInnerPoints[j][0] * flSinAngle;
(*pEndInnerPoints)[j][2] = flCurrentZ + fInnerPoints[j][1];
(*pEndOuterPoints)[j][0] = xCurrCenter + fOuterPoints[j][0] * flCosAngle;
(*pEndOuterPoints)[j][1] = yCurrCenter + fOuterPoints[j][0] * flSinAngle;
(*pEndOuterPoints)[j][2] = flCurrentZ + fOuterPoints[j][1];
// We'll use the j == 0 data when iNextPoint = iSides - 1
if (( i != 0 ) && ( jPrevPoint != -1 ))
{
CMapSolid *pSolid = CreateSegment(
*pStartOuterPoints, *pStartInnerPoints,
*pEndOuterPoints, *pEndInnerPoints,
jPrevPoint, j, bCreateSouthFace);
pGroup->AddChild(pSolid);
}
jPrevPoint = j;
++j;
} while( jPrevPoint != iSides );
flRotationAngle += flRotationDeltaAngle;
flCurrentZ += flDeltaZ;
if ( flRotationAngle >= 360.0f )
{
flRotationAngle -= 360.0f;
}
}
pGroup->CalcBounds(TRUE);
if (Options.general.bStretchArches)
{
// make sure size of group's bounds are size of original bounds -
// if not, scale up. this can happen when we use rotation.
Vector objsize, boundsize;
pBox->GetBoundsSize(boundsize);
pGroup->GetBoundsSize(objsize);
if (boundsize[AXIS_X] > objsize[AXIS_X] ||
boundsize[AXIS_Y] > objsize[AXIS_Y])
{
Vector scale;
scale[AXIS_X] = boundsize[AXIS_X] / objsize[AXIS_X];
scale[AXIS_Y] = boundsize[AXIS_Y] / objsize[AXIS_Y];
scale[AXIS_Z] = 1.0f; // xxxYWB scaling by 0 causes veneers, so I changed to 1.0
Vector center;
pBox->GetBoundsCenter(center);
pGroup->TransScale(center, scale);
}
}
return pGroup;
}