//========= Copyright © 1996-2005, 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 //----------------------------------------------------------------------------- // 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] = rint(xCenter + (float)cos(DEG2RAD(angle)) * xrad); points[point][1] = 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; }