source-engine/hammer/brushops.cpp

354 lines
7.0 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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++];
}