You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
7.0 KiB
353 lines
7.0 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include <stdio.h> |
|
#include <math.h> |
|
#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 ; i<in->numpoints ; 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 ; i<in->numpoints ; 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<error3d, error3d&> 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++]; |
|
} |
|
|
|
|
|
|