//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include #include #include "hammer.h" #include "MapEntity.h" #include "MapDefs.h" #include "MapFace.h" #include "hammer_mathlib.h" #include "history.h" #include "Error3d.h" #include "BrushOps.h" #include "GlobalFunctions.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #pragma warning( disable : 4244 ) // Disable warning messages #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 #define BOGUS_RANGE ( MAX_COORD_INTEGER * 4 ) float lightaxis[3] = {1, 0.6f, 0.75f}; const int MAX_POINTS_ON_WINDING = 128; void Error(char* fmt, ...) { char str[300]; sprintf(str, fmt, (&fmt)+1); Msg(mwError, str); } /* ============================================================================= TURN PLANES INTO GROUPS OF FACES ============================================================================= */ /* ================== NewWinding ================== */ winding_t *NewWinding (int points) { winding_t *w; if (points > MAX_POINTS_ON_WINDING) Error ("NewWinding: %i points", points); w = (winding_t *)malloc(sizeof(*w)); w->numpoints = 0; // None are occupied yet even though allocated. w->p = (Vector *)calloc( points, sizeof(Vector) ); return w; } void FreeWinding (winding_t *w) { if (*(unsigned *)w == 0xdeaddead) Error ("FreeWinding: freed a freed winding"); *(unsigned *)w = 0xdeaddead; if (w->p) { free (w->p); w->p = NULL; } free (w); } size_t WindingSize(int points) { return (size_t)(&((winding_t *)0)->p[points]); } //----------------------------------------------------------------------------- // Purpose: Removes points that are withing a given distance from each other // from the winding. // Input : pWinding - The winding to remove duplicates from. // fMinDist - The minimum distance two points must be from one another // to be considered different. If this is zero, the points must be // identical to be considered duplicates. //----------------------------------------------------------------------------- void RemoveDuplicateWindingPoints(winding_t *pWinding, float fMinDist) { for (int i = 0; i < pWinding->numpoints; i++) { for (int j = i + 1; j < pWinding->numpoints; j++) { Vector edge; VectorSubtract(pWinding->p[i], pWinding->p[j], edge); if (VectorLength(edge) < fMinDist) { if (j + 1 < pWinding->numpoints) { memmove(&pWinding->p[j], &pWinding->p[j + 1], (pWinding->numpoints - (j + 1)) * sizeof(pWinding->p[0])); } pWinding->numpoints--; } } } } /* ================== CopyWinding ================== */ winding_t *CopyWinding (winding_t *w) { int size; winding_t *c; c = NewWinding (w->numpoints); c->numpoints = w->numpoints; size = w->numpoints*sizeof(w->p[0]); memcpy (c->p, w->p, size); return c; } /* ================== ClipWinding Clips the winding to the plane, returning the new winding on the positive side Frees the input winding. ================== */ // YWB ADDED SPLIT EPS to match qcsg splitting #define SPLIT_EPSILON 0.01 winding_t *ClipWinding (winding_t *in, PLANE *split) { float dists[MAX_POINTS_ON_WINDING]; int sides[MAX_POINTS_ON_WINDING]; int counts[3]; float dot; int i, j; Vector *p1, *p2, *mid; winding_t *neww; int maxpts; counts[0] = counts[1] = counts[2] = 0; // determine sides for each point for (i=0 ; inumpoints ; i++) { dot = DotProduct (in->p[i], split->normal); dot -= split->dist; dists[i] = dot; if (dot > SPLIT_EPSILON) sides[i] = SIDE_FRONT; else if (dot < -SPLIT_EPSILON) sides[i] = SIDE_BACK; else { sides[i] = SIDE_ON; } counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0]; if (!counts[0] && !counts[1]) return in; if (!counts[0]) { free (in); return NULL; } if (!counts[1]) return in; maxpts = in->numpoints+4; // can't use counts[0]+2 because // of fp grouping errors neww = NewWinding (maxpts); for (i=0 ; inumpoints ; i++) { p1 = &in->p[i]; mid = &neww->p[neww->numpoints]; if (sides[i] == SIDE_FRONT || sides[i] == SIDE_ON) { *mid = *p1; neww->numpoints++; if (sides[i] == SIDE_ON) continue; mid = &neww->p[neww->numpoints]; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; // generate a split point if (i == in->numpoints - 1) p2 = &in->p[0]; else p2 = p1 + 1; neww->numpoints++; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { // avoid round off error when possible if (split->normal[j] == 1) mid[0][j] = split->dist; else if (split->normal[j] == -1) mid[0][j] = -split->dist; mid[0][j] = p1[0][j] + dot*(p2[0][j]-p1[0][j]); } // mid[3] = p1[3] + dot*(p2[3]-p1[3]); // mid[4] = p1[4] + dot*(p2[4]-p1[4]); } if (neww->numpoints > maxpts) Error ("ClipWinding: points exceeded estimate"); // free the original winding FreeWinding (in); return neww; } //----------------------------------------------------------------------------- // Purpose: Creates a huge quadrilateral winding given a plane. // Input : pPlane - Plane normal and distance to use when creating the winding. // Output : Returns a winding with 4 points. //----------------------------------------------------------------------------- // dvs: read through this and clean it up winding_t *CreateWindingFromPlane(PLANE *pPlane) { int i, x; float max, v; Vector org, vright, vup; winding_t *w; // find the major axis max = -BOGUS_RANGE; x = -1; for (i=0 ; i<3; i++) { v = fabs(pPlane->normal[i]); if (v > max) { x = i; max = v; } } if (x==-1) Error ("BasePolyForPlane: no axis found"); vup = vec3_origin; switch (x) { case 0: case 1: vup[2] = 1; break; case 2: vup[0] = 1; break; } v = DotProduct (vup, pPlane->normal); VectorMA (vup, -v, pPlane->normal, vup); VectorNormalize (vup); org = pPlane->normal * pPlane->dist; CrossProduct (vup, pPlane->normal, vright); vup = vup * MAX_TRACE_LENGTH; vright = vright * MAX_TRACE_LENGTH; // project a really big axis aligned box onto the plane w = NewWinding (4); w->numpoints = 4; VectorSubtract (org, vright, w->p[0]); VectorAdd (w->p[0], vup, w->p[0]); VectorAdd (org, vright, w->p[1]); VectorAdd (w->p[1], vup, w->p[1]); VectorAdd (org, vright, w->p[2]); VectorSubtract (w->p[2], vup, w->p[2]); VectorSubtract (org, vright, w->p[3]); VectorSubtract (w->p[3], vup, w->p[3]); return w; } static CArray Errors; static int nErrors; void Add3dError(DWORD dwObjectID, LPCTSTR pszReason, PVOID pInfo) { error3d err; err.dwObjectID = dwObjectID; err.pszReason = pszReason; err.pInfo = pInfo; Errors.Add(err); ++nErrors; } int Get3dErrorCount() { return nErrors; } error3d * Enum3dErrors(BOOL bStart) { static int iCurrent = 0; if(bStart) iCurrent = 0; if(iCurrent == nErrors) return NULL; return & Errors.GetData()[iCurrent++]; }