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++];
|
|
}
|
|
|
|
|