First version of the SOurce SDK 2013
This commit is contained in:
285
mp/src/utils/vbsp/boundbox.cpp
Normal file
285
mp/src/utils/vbsp/boundbox.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "BoundBox.h"
|
||||
//#include "hammer_mathlib.h"
|
||||
//#include "MapDefs.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
|
||||
float rint(float f)
|
||||
{
|
||||
if (f > 0.0f) {
|
||||
return (float) floor(f + 0.5f);
|
||||
} else if (f < 0.0f) {
|
||||
return (float) ceil(f - 0.5f);
|
||||
} else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
BoundBox::BoundBox(void)
|
||||
{
|
||||
ResetBounds();
|
||||
}
|
||||
|
||||
BoundBox::BoundBox(const Vector &mins, const Vector &maxs)
|
||||
{
|
||||
bmins = mins;
|
||||
bmaxs = maxs;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the box to an uninitialized state, so that calls to UpdateBounds
|
||||
// will properly set the mins and maxs.
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::ResetBounds(void)
|
||||
{
|
||||
bmins[0] = bmins[1] = bmins[2] = COORD_NOTINIT;
|
||||
bmaxs[0] = bmaxs[1] = bmaxs[2] = -COORD_NOTINIT;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pt -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::UpdateBounds(const Vector& pt)
|
||||
{
|
||||
if(pt[0] < bmins[0])
|
||||
bmins[0] = pt[0];
|
||||
if(pt[1] < bmins[1])
|
||||
bmins[1] = pt[1];
|
||||
if(pt[2] < bmins[2])
|
||||
bmins[2] = pt[2];
|
||||
|
||||
if(pt[0] > bmaxs[0])
|
||||
bmaxs[0] = pt[0];
|
||||
if(pt[1] > bmaxs[1])
|
||||
bmaxs[1] = pt[1];
|
||||
if(pt[2] > bmaxs[2])
|
||||
bmaxs[2] = pt[2];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : bmins -
|
||||
// bmaxs -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::UpdateBounds(const Vector& mins, const Vector& maxs)
|
||||
{
|
||||
if(mins[0] < bmins[0])
|
||||
bmins[0] = mins[0];
|
||||
if(mins[1] < bmins[1])
|
||||
bmins[1] = mins[1];
|
||||
if(mins[2] < bmins[2])
|
||||
bmins[2] = mins[2];
|
||||
|
||||
if(maxs[0] > bmaxs[0])
|
||||
bmaxs[0] = maxs[0];
|
||||
if(maxs[1] > bmaxs[1])
|
||||
bmaxs[1] = maxs[1];
|
||||
if(maxs[2] > bmaxs[2])
|
||||
bmaxs[2] = maxs[2];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pBox -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::UpdateBounds(const BoundBox *pBox)
|
||||
{
|
||||
UpdateBounds(pBox->bmins, pBox->bmaxs);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : ptdest -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::GetBoundsCenter(Vector& ptdest)
|
||||
{
|
||||
ptdest = (bmins + bmaxs)/2.0f;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pt -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BoundBox::ContainsPoint(const Vector& pt) const
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (pt[i] < bmins[i] || pt[i] > bmaxs[i])
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pfMins -
|
||||
// pfMaxs -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BoundBox::IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const
|
||||
{
|
||||
if ((bmins[0] >= pfMaxs[0]) || (bmaxs[0] <= pfMins[0]))
|
||||
{
|
||||
return(false);
|
||||
|
||||
}
|
||||
if ((bmins[1] >= pfMaxs[1]) || (bmaxs[1] <= pfMins[1]))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
if ((bmins[2] >= pfMaxs[2]) || (bmaxs[2] <= pfMins[2]))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : pfMins -
|
||||
// pfMaxs -
|
||||
// Output : Returns true on success, false on failure.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BoundBox::IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const
|
||||
{
|
||||
if ((bmins[0] < pfMins[0]) || (bmaxs[0] > pfMaxs[0]))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
if ((bmins[1] < pfMins[1]) || (bmaxs[1] > pfMaxs[1]))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
if ((bmins[2] < pfMins[2]) || (bmaxs[2] > pfMaxs[2]))
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns whether this bounding box is valid, ie maxs >= mins.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BoundBox::IsValidBox(void) const
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (bmins[i] > bmaxs[i])
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : size -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::GetBoundsSize(Vector& size)
|
||||
{
|
||||
size[0] = bmaxs[0] - bmins[0];
|
||||
size[1] = bmaxs[1] - bmins[1];
|
||||
size[2] = bmaxs[2] - bmins[2];
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : iValue -
|
||||
// iGridSize -
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
static int Snap(/*int*/ float iValue, int iGridSize)
|
||||
{
|
||||
return (int)(rint(iValue/iGridSize) * iGridSize);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : iGridSize -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::SnapToGrid(int iGridSize)
|
||||
{
|
||||
// does not alter the size of the box .. snaps its minimal coordinates
|
||||
// to the grid size specified in iGridSize
|
||||
Vector size;
|
||||
GetBoundsSize(size);
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
bmins[i] = (float)Snap(/* YWB (int)*/bmins[i], iGridSize);
|
||||
bmaxs[i] = bmins[i] + size[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : axis -
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::Rotate90(int axis)
|
||||
{
|
||||
int e1 = AXIS_X, e2 = AXIS_Y;
|
||||
|
||||
// get bounds center first
|
||||
Vector center;
|
||||
GetBoundsCenter(center);
|
||||
|
||||
switch(axis)
|
||||
{
|
||||
case AXIS_Z:
|
||||
e1 = AXIS_X;
|
||||
e2 = AXIS_Y;
|
||||
break;
|
||||
case AXIS_X:
|
||||
e1 = AXIS_Y;
|
||||
e2 = AXIS_Z;
|
||||
break;
|
||||
case AXIS_Y:
|
||||
e1 = AXIS_X;
|
||||
e2 = AXIS_Z;
|
||||
break;
|
||||
}
|
||||
|
||||
float tmp1, tmp2;
|
||||
tmp1 = bmins[e1] - center[e1] + center[e2];
|
||||
tmp2 = bmaxs[e1] - center[e1] + center[e2];
|
||||
bmins[e1] = bmins[e2] - center[e2] + center[e1];
|
||||
bmaxs[e1] = bmaxs[e2] - center[e2] + center[e1];
|
||||
bmins[e2] = tmp1;
|
||||
bmaxs[e2] = tmp2;
|
||||
}
|
||||
|
||||
79
mp/src/utils/vbsp/boundbox.h
Normal file
79
mp/src/utils/vbsp/boundbox.h
Normal file
@@ -0,0 +1,79 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: An axis aligned bounding box class.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef BOUNDBOX_H
|
||||
#define BOUNDBOX_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "mathlib/vector.h"
|
||||
|
||||
#define COORD_NOTINIT ((float)(99999.0))
|
||||
|
||||
enum
|
||||
{
|
||||
AXIS_X = 0,
|
||||
AXIS_Y,
|
||||
AXIS_Z
|
||||
};
|
||||
|
||||
class BoundBox
|
||||
{
|
||||
public:
|
||||
|
||||
BoundBox(void);
|
||||
BoundBox(const Vector &mins, const Vector &maxs);
|
||||
|
||||
void ResetBounds(void);
|
||||
inline void SetBounds(const Vector &mins, const Vector &maxs);
|
||||
|
||||
void UpdateBounds(const Vector& bmins, const Vector& bmaxs);
|
||||
void UpdateBounds(const Vector& pt);
|
||||
void UpdateBounds(const BoundBox *pBox);
|
||||
void GetBoundsCenter(Vector& ptdest);
|
||||
inline void GetBounds(Vector& Mins, Vector& Maxs);
|
||||
|
||||
virtual bool IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const;
|
||||
bool IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const;
|
||||
bool ContainsPoint(const Vector& pt) const;
|
||||
bool IsValidBox(void) const;
|
||||
void GetBoundsSize(Vector& size);
|
||||
void SnapToGrid(int iGridSize);
|
||||
void Rotate90(int axis);
|
||||
|
||||
Vector bmins;
|
||||
Vector bmaxs;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Gets the bounding box as two vectors, a min and a max.
|
||||
// Input : Mins - Receives the box's minima.
|
||||
// Maxs - Receives the box's maxima.
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::GetBounds(Vector &Mins, Vector &Maxs)
|
||||
{
|
||||
Mins = bmins;
|
||||
Maxs = bmaxs;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets the box outright, equivalent to ResetBounds + UpdateBounds.
|
||||
// Input : mins - Minima to set.
|
||||
// maxs - Maxima to set.
|
||||
//-----------------------------------------------------------------------------
|
||||
void BoundBox::SetBounds(const Vector &mins, const Vector &maxs)
|
||||
{
|
||||
bmins = mins;
|
||||
bmaxs = maxs;
|
||||
}
|
||||
|
||||
|
||||
#endif // BOUNDBOX_H
|
||||
1469
mp/src/utils/vbsp/brushbsp.cpp
Normal file
1469
mp/src/utils/vbsp/brushbsp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
784
mp/src/utils/vbsp/csg.cpp
Normal file
784
mp/src/utils/vbsp/csg.cpp
Normal file
@@ -0,0 +1,784 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
|
||||
/*
|
||||
|
||||
tag all brushes with original contents
|
||||
brushes may contain multiple contents
|
||||
there will be no brush overlap after csg phase
|
||||
|
||||
|
||||
|
||||
|
||||
each side has a count of the other sides it splits
|
||||
|
||||
the best split will be the one that minimizes the total split counts
|
||||
of all remaining sides
|
||||
|
||||
precalc side on plane table
|
||||
|
||||
evaluate split side
|
||||
{
|
||||
cost = 0
|
||||
for all sides
|
||||
for all sides
|
||||
get
|
||||
if side splits side and splitside is on same child
|
||||
cost++;
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
void SplitBrush2( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
|
||||
{
|
||||
SplitBrush( brush, planenum, front, back );
|
||||
#if 0
|
||||
if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
|
||||
(*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
|
||||
if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
|
||||
(*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SubtractBrush
|
||||
|
||||
Returns a list of brushes that remain after B is subtracted from A.
|
||||
May by empty if A is contained inside B.
|
||||
|
||||
The originals are undisturbed.
|
||||
===============
|
||||
*/
|
||||
bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
|
||||
{ // a - b = out (list)
|
||||
int i;
|
||||
bspbrush_t *front, *back;
|
||||
bspbrush_t *out, *in;
|
||||
|
||||
in = a;
|
||||
out = NULL;
|
||||
for (i=0 ; i<b->numsides && in ; i++)
|
||||
{
|
||||
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
|
||||
if (in != a)
|
||||
FreeBrush (in);
|
||||
if (front)
|
||||
{ // add to list
|
||||
front->next = out;
|
||||
out = front;
|
||||
}
|
||||
in = back;
|
||||
}
|
||||
if (in)
|
||||
FreeBrush (in);
|
||||
else
|
||||
{ // didn't really intersect
|
||||
FreeBrushList (out);
|
||||
return a;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
IntersectBrush
|
||||
|
||||
Returns a single brush made up by the intersection of the
|
||||
two provided brushes, or NULL if they are disjoint.
|
||||
|
||||
The originals are undisturbed.
|
||||
===============
|
||||
*/
|
||||
bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
|
||||
{
|
||||
int i;
|
||||
bspbrush_t *front, *back;
|
||||
bspbrush_t *in;
|
||||
|
||||
in = a;
|
||||
for (i=0 ; i<b->numsides && in ; i++)
|
||||
{
|
||||
SplitBrush2 (in, b->sides[i].planenum, &front, &back);
|
||||
if (in != a)
|
||||
FreeBrush (in);
|
||||
if (front)
|
||||
FreeBrush (front);
|
||||
in = back;
|
||||
}
|
||||
|
||||
if (in == a || !in)
|
||||
return NULL;
|
||||
|
||||
in->next = NULL;
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
BrushesDisjoint
|
||||
|
||||
Returns true if the two brushes definately do not intersect.
|
||||
There will be false negatives for some non-axial combinations.
|
||||
===============
|
||||
*/
|
||||
qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
// check bounding boxes
|
||||
for (i=0 ; i<3 ; i++)
|
||||
if (a->mins[i] >= b->maxs[i]
|
||||
|| a->maxs[i] <= b->mins[i])
|
||||
return true; // bounding boxes don't overlap
|
||||
|
||||
// check for opposing planes
|
||||
for (i=0 ; i<a->numsides ; i++)
|
||||
{
|
||||
for (j=0 ; j<b->numsides ; j++)
|
||||
{
|
||||
if (a->sides[i].planenum ==
|
||||
(b->sides[j].planenum^1) )
|
||||
return true; // opposite planes, so not touching
|
||||
}
|
||||
}
|
||||
|
||||
return false; // might intersect
|
||||
}
|
||||
|
||||
|
||||
int minplanenums[3];
|
||||
int maxplanenums[3];
|
||||
|
||||
/*
|
||||
===============
|
||||
ClipBrushToBox
|
||||
|
||||
Any planes shared with the box edge will be set to no texinfo
|
||||
===============
|
||||
*/
|
||||
bspbrush_t *ClipBrushToBox (bspbrush_t *brush, const Vector& clipmins, const Vector& clipmaxs)
|
||||
{
|
||||
int i, j;
|
||||
bspbrush_t *front, *back;
|
||||
int p;
|
||||
|
||||
for (j=0 ; j<2 ; j++)
|
||||
{
|
||||
if (brush->maxs[j] > clipmaxs[j])
|
||||
{
|
||||
SplitBrush (brush, maxplanenums[j], &front, &back);
|
||||
if (front)
|
||||
FreeBrush (front);
|
||||
brush = back;
|
||||
if (!brush)
|
||||
return NULL;
|
||||
}
|
||||
if (brush->mins[j] < clipmins[j])
|
||||
{
|
||||
SplitBrush (brush, minplanenums[j], &front, &back);
|
||||
if (back)
|
||||
FreeBrush (back);
|
||||
brush = front;
|
||||
if (!brush)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// remove any colinear faces
|
||||
|
||||
for (i=0 ; i<brush->numsides ; i++)
|
||||
{
|
||||
p = brush->sides[i].planenum & ~1;
|
||||
if (p == maxplanenums[0] || p == maxplanenums[1]
|
||||
|| p == minplanenums[0] || p == minplanenums[1])
|
||||
{
|
||||
brush->sides[i].texinfo = TEXINFO_NODE;
|
||||
brush->sides[i].visible = false;
|
||||
}
|
||||
}
|
||||
return brush;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a clipped brush from a map brush
|
||||
//-----------------------------------------------------------------------------
|
||||
static bspbrush_t *CreateClippedBrush( mapbrush_t *mb, const Vector& clipmins, const Vector& clipmaxs )
|
||||
{
|
||||
int nNumSides = mb->numsides;
|
||||
if (!nNumSides)
|
||||
return NULL;
|
||||
|
||||
// if the brush is outside the clip area, skip it
|
||||
for (int j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j])
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// make a copy of the brush
|
||||
bspbrush_t *newbrush = AllocBrush( nNumSides );
|
||||
newbrush->original = mb;
|
||||
newbrush->numsides = nNumSides;
|
||||
memcpy (newbrush->sides, mb->original_sides, nNumSides*sizeof(side_t));
|
||||
|
||||
for (int j=0 ; j<nNumSides; j++)
|
||||
{
|
||||
if (newbrush->sides[j].winding)
|
||||
{
|
||||
newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
|
||||
}
|
||||
|
||||
if (newbrush->sides[j].surf & SURF_HINT)
|
||||
{
|
||||
newbrush->sides[j].visible = true; // hints are always visible
|
||||
}
|
||||
|
||||
// keep a pointer to the original map brush side -- use to create the original face later!!
|
||||
//newbrush->sides[j].original = &mb->original_sides[j];
|
||||
}
|
||||
|
||||
VectorCopy (mb->mins, newbrush->mins);
|
||||
VectorCopy (mb->maxs, newbrush->maxs);
|
||||
|
||||
// carve off anything outside the clip box
|
||||
newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
|
||||
return newbrush;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Creates a clipped brush from a map brush
|
||||
//-----------------------------------------------------------------------------
|
||||
static void ComputeBoundingPlanes( const Vector& clipmins, const Vector& clipmaxs )
|
||||
{
|
||||
Vector normal;
|
||||
float dist;
|
||||
for (int i=0 ; i<2 ; i++)
|
||||
{
|
||||
VectorClear (normal);
|
||||
normal[i] = 1;
|
||||
dist = clipmaxs[i];
|
||||
maxplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
|
||||
dist = clipmins[i];
|
||||
minplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This forces copies of texinfo data for matching sides of a brush
|
||||
//-----------------------------------------------------------------------------
|
||||
void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_t *pSource )
|
||||
{
|
||||
for ( int i = 0; i < numDestSides; i++ )
|
||||
{
|
||||
side_t *pSide = &pDestSides[i];
|
||||
plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
|
||||
|
||||
// We have to use the *original sides* because MapBSPBrushList could have generated
|
||||
// splits when cutting the original brush to the block being processed. This
|
||||
// will generate faces that use TEXINFO_NODE, which is definitely *not* what we want.
|
||||
// If we end up with faces using TEXINFO_NODE here, the area portal will flood into
|
||||
// the entire water volume intersecting the areaportal.
|
||||
|
||||
mapbrush_t *pSourceBrush = pSource->original;
|
||||
Assert( pSourceBrush );
|
||||
|
||||
const side_t *pSourceSide = pSourceBrush->original_sides;
|
||||
const side_t *pBestSide = NULL;
|
||||
float flBestDot = -1.0f;
|
||||
for ( int j = 0; j < pSourceBrush->numsides; ++j, ++pSourceSide )
|
||||
{
|
||||
if ( pSourceSide->texinfo == TEXINFO_NODE )
|
||||
continue;
|
||||
|
||||
plane_t *pSourcePlane = &g_MainMap->mapplanes[pSourceSide->planenum];
|
||||
float flDot = DotProduct( pPlane->normal, pSourcePlane->normal );
|
||||
if ( flDot == 1.0f || pSide->planenum == pSourceSide->planenum )
|
||||
{
|
||||
pBestSide = pSourceSide;
|
||||
break;
|
||||
}
|
||||
else if ( flDot > flBestDot )
|
||||
{
|
||||
pBestSide = pSourceSide;
|
||||
flBestDot = flDot;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pBestSide )
|
||||
{
|
||||
pSide->texinfo = pBestSide->texinfo;
|
||||
if ( pSide->original )
|
||||
{
|
||||
pSide->original->texinfo = pSide->texinfo;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
texinfo_t *pTexInfo = &texinfo[pSide->texinfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
Msg("Found no matching plane for %s\n", TexDataStringTable_GetString( pTexData->nameStringTableID ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a hack to allow areaportals to work in water
|
||||
// It was done this way for ease of implementation.
|
||||
// This searches a brush list to find intersecting areaportals and water
|
||||
// If an areaportal is found inside water, then the water contents and
|
||||
// texture information is copied over to the areaportal so that the
|
||||
// resulting space has the same properties as the water (normal areaportals assume "empty" surroundings)
|
||||
void FixupAreaportalWaterBrushes( bspbrush_t *pList )
|
||||
{
|
||||
for ( bspbrush_t *pAreaportal = pList; pAreaportal; pAreaportal = pAreaportal->next )
|
||||
{
|
||||
if ( !(pAreaportal->original->contents & CONTENTS_AREAPORTAL) )
|
||||
continue;
|
||||
|
||||
for ( bspbrush_t *pWater = pList; pWater; pWater = pWater->next )
|
||||
{
|
||||
// avoid using areaportal/water combo brushes that have already been fixed up
|
||||
if ( pWater->original->contents & CONTENTS_AREAPORTAL )
|
||||
continue;
|
||||
|
||||
if ( !(pWater->original->contents & MASK_SPLITAREAPORTAL) )
|
||||
continue;
|
||||
|
||||
if ( BrushesDisjoint( pAreaportal, pWater ) )
|
||||
continue;
|
||||
|
||||
bspbrush_t *pIntersect = IntersectBrush( pAreaportal, pWater );
|
||||
if ( !pIntersect )
|
||||
continue;
|
||||
FreeBrush( pIntersect );
|
||||
pAreaportal->original->contents |= pWater->original->contents;
|
||||
|
||||
// HACKHACK: Ideally, this should have been done before the bspbrush_t was
|
||||
// created from the map brush. But since it hasn't been, retexture the original map
|
||||
// brush's sides
|
||||
CopyMatchingTexinfos( pAreaportal->sides, pAreaportal->numsides, pWater );
|
||||
CopyMatchingTexinfos( pAreaportal->original->original_sides, pAreaportal->original->numsides, pWater );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MakeBspBrushList
|
||||
//-----------------------------------------------------------------------------
|
||||
// UNDONE: Put detail brushes in a separate brush array and pass that instead of "onlyDetail" ?
|
||||
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmins, const Vector& clipmaxs, int detailScreen)
|
||||
{
|
||||
ComputeBoundingPlanes( clipmins, clipmaxs );
|
||||
|
||||
bspbrush_t *pBrushList = NULL;
|
||||
|
||||
int i;
|
||||
for (i=startbrush ; i<endbrush ; i++)
|
||||
{
|
||||
mapbrush_t *mb = &g_MainMap->mapbrushes[i];
|
||||
if ( detailScreen != FULL_DETAIL )
|
||||
{
|
||||
bool onlyDetail = (detailScreen == ONLY_DETAIL);
|
||||
bool detail = (mb->contents & CONTENTS_DETAIL) != 0;
|
||||
if ( onlyDetail ^ detail )
|
||||
{
|
||||
// both of these must have the same value or we're not interested in this brush
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bspbrush_t *pNewBrush = CreateClippedBrush( mb, clipmins, clipmaxs );
|
||||
if ( pNewBrush )
|
||||
{
|
||||
pNewBrush->next = pBrushList;
|
||||
pBrushList = pNewBrush;
|
||||
}
|
||||
}
|
||||
|
||||
return pBrushList;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A version which uses a passed-in list of brushes
|
||||
//-----------------------------------------------------------------------------
|
||||
bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs)
|
||||
{
|
||||
ComputeBoundingPlanes( clipmins, clipmaxs );
|
||||
|
||||
bspbrush_t *pBrushList = NULL;
|
||||
for ( int i=0; i < nBrushCount; ++i )
|
||||
{
|
||||
bspbrush_t *pNewBrush = CreateClippedBrush( pBrushes[i], clipmins, clipmaxs );
|
||||
if ( pNewBrush )
|
||||
{
|
||||
pNewBrush->next = pBrushList;
|
||||
pBrushList = pNewBrush;
|
||||
}
|
||||
}
|
||||
|
||||
return pBrushList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
AddBspBrushListToTail
|
||||
===============
|
||||
*/
|
||||
bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
|
||||
{
|
||||
bspbrush_t *walk, *next;
|
||||
|
||||
for (walk=list ; walk ; walk=next)
|
||||
{ // add to end of list
|
||||
next = walk->next;
|
||||
walk->next = NULL;
|
||||
tail->next = walk;
|
||||
tail = walk;
|
||||
}
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
CullList
|
||||
|
||||
Builds a new list that doesn't hold the given brush
|
||||
===========
|
||||
*/
|
||||
bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
|
||||
{
|
||||
bspbrush_t *newlist;
|
||||
bspbrush_t *next;
|
||||
|
||||
newlist = NULL;
|
||||
|
||||
for ( ; list ; list = next)
|
||||
{
|
||||
next = list->next;
|
||||
if (list == skip1)
|
||||
{
|
||||
FreeBrush (list);
|
||||
continue;
|
||||
}
|
||||
list->next = newlist;
|
||||
newlist = list;
|
||||
}
|
||||
return newlist;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteBrushMap
|
||||
==================
|
||||
*/
|
||||
void WriteBrushMap (char *name, bspbrush_t *list)
|
||||
{
|
||||
FILE *f;
|
||||
side_t *s;
|
||||
int i;
|
||||
winding_t *w;
|
||||
|
||||
Msg("writing %s\n", name);
|
||||
f = fopen (name, "w");
|
||||
if (!f)
|
||||
Error ("Can't write %s\b", name);
|
||||
|
||||
fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
|
||||
|
||||
for ( ; list ; list=list->next )
|
||||
{
|
||||
fprintf (f, "{\n");
|
||||
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
|
||||
{
|
||||
w = BaseWindingForPlane (g_MainMap->mapplanes[s->planenum].normal, g_MainMap->mapplanes[s->planenum].dist);
|
||||
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
|
||||
fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
|
||||
|
||||
fprintf (f, "%s 0 0 0 1 1\n", TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
|
||||
FreeWinding (w);
|
||||
}
|
||||
fprintf (f, "}\n");
|
||||
}
|
||||
fprintf (f, "}\n");
|
||||
|
||||
fclose (f);
|
||||
|
||||
}
|
||||
|
||||
// UNDONE: This isn't quite working yet
|
||||
#if 0
|
||||
void WriteBrushVMF(char *name, bspbrush_t *list)
|
||||
{
|
||||
FILE *f;
|
||||
side_t *s;
|
||||
int i;
|
||||
winding_t *w;
|
||||
Vector u, v;
|
||||
|
||||
Msg("writing %s\n", name);
|
||||
f = fopen (name, "w");
|
||||
if (!f)
|
||||
Error ("Can't write %s\b", name);
|
||||
|
||||
fprintf (f, "world\n{\n\"classname\" \"worldspawn\"\n");
|
||||
|
||||
for ( ; list ; list=list->next )
|
||||
{
|
||||
fprintf (f, "\tsolid\n\t{\n");
|
||||
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
|
||||
{
|
||||
fprintf( f, "\t\tside\n\t\t{\n" );
|
||||
fprintf( f, "\t\t\t\"plane\" \"" );
|
||||
w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
|
||||
|
||||
fprintf (f,"(%i %i %i) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
|
||||
fprintf (f,"(%i %i %i) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
|
||||
fprintf (f,"(%i %i %i)", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
|
||||
fprintf( f, "\"\n" );
|
||||
fprintf( f, "\t\t\t\"material\" \"%s\"\n", GetTexData( texinfo[s->texinfo].texdata )->name );
|
||||
// UNDONE: recreate correct texture axes
|
||||
BasisForPlane( mapplanes[s->planenum].normal, u, v );
|
||||
fprintf( f, "\t\t\t\"uaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", u[0], u[1], u[2] );
|
||||
fprintf( f, "\t\t\t\"vaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", v[0], v[1], v[2] );
|
||||
|
||||
fprintf( f, "\t\t\t\"rotation\" \"0.0\"\n" );
|
||||
fprintf( f, "\t\t\t\"lightmapscale\" \"16.0\"\n" );
|
||||
|
||||
FreeWinding (w);
|
||||
fprintf (f, "\t\t}\n");
|
||||
}
|
||||
fprintf (f, "\t}\n");
|
||||
}
|
||||
fprintf (f, "}\n");
|
||||
|
||||
fclose (f);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars )
|
||||
{
|
||||
#define ADD_CONTENTS( flag ) \
|
||||
if ( contents & flag ) \
|
||||
Q_strncat( pOut, #flag " ", nMaxChars, COPY_ALL_CHARACTERS );
|
||||
|
||||
pOut[0] = 0;
|
||||
|
||||
ADD_CONTENTS(CONTENTS_SOLID)
|
||||
ADD_CONTENTS(CONTENTS_WINDOW)
|
||||
ADD_CONTENTS(CONTENTS_AUX)
|
||||
ADD_CONTENTS(CONTENTS_GRATE)
|
||||
ADD_CONTENTS(CONTENTS_SLIME)
|
||||
ADD_CONTENTS(CONTENTS_WATER)
|
||||
ADD_CONTENTS(CONTENTS_BLOCKLOS)
|
||||
ADD_CONTENTS(CONTENTS_OPAQUE)
|
||||
ADD_CONTENTS(CONTENTS_TESTFOGVOLUME)
|
||||
ADD_CONTENTS(CONTENTS_MOVEABLE)
|
||||
ADD_CONTENTS(CONTENTS_AREAPORTAL)
|
||||
ADD_CONTENTS(CONTENTS_PLAYERCLIP)
|
||||
ADD_CONTENTS(CONTENTS_MONSTERCLIP)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_0)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_90)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_180)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_270)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_UP)
|
||||
ADD_CONTENTS(CONTENTS_CURRENT_DOWN)
|
||||
ADD_CONTENTS(CONTENTS_ORIGIN)
|
||||
ADD_CONTENTS(CONTENTS_MONSTER)
|
||||
ADD_CONTENTS(CONTENTS_DEBRIS)
|
||||
ADD_CONTENTS(CONTENTS_DETAIL)
|
||||
ADD_CONTENTS(CONTENTS_TRANSLUCENT)
|
||||
ADD_CONTENTS(CONTENTS_LADDER)
|
||||
ADD_CONTENTS(CONTENTS_HITBOX)
|
||||
}
|
||||
|
||||
void PrintBrushContents( int contents )
|
||||
{
|
||||
char str[1024];
|
||||
PrintBrushContentsToString( contents, str, sizeof( str ) );
|
||||
Msg( "%s", str );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
BrushGE
|
||||
|
||||
Returns true if b1 is allowed to bite b2
|
||||
==================
|
||||
*/
|
||||
qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
|
||||
{
|
||||
// Areaportals are allowed to bite water + slime
|
||||
// NOTE: This brush combo should have been fixed up
|
||||
// in a first pass (FixupAreaportalWaterBrushes)
|
||||
if( (b2->original->contents & MASK_SPLITAREAPORTAL) &&
|
||||
(b1->original->contents & CONTENTS_AREAPORTAL) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// detail brushes never bite structural brushes
|
||||
if ( (b1->original->contents & CONTENTS_DETAIL)
|
||||
&& !(b2->original->contents & CONTENTS_DETAIL) )
|
||||
return false;
|
||||
if (b1->original->contents & CONTENTS_SOLID)
|
||||
return true;
|
||||
// Transparent brushes are not marked as detail anymore, so let them cut each other.
|
||||
if ( (b1->original->contents & TRANSPARENT_CONTENTS) && (b2->original->contents & TRANSPARENT_CONTENTS) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ChopBrushes
|
||||
|
||||
Carves any intersecting solid brushes into the minimum number
|
||||
of non-intersecting brushes.
|
||||
=================
|
||||
*/
|
||||
bspbrush_t *ChopBrushes (bspbrush_t *head)
|
||||
{
|
||||
bspbrush_t *b1, *b2, *next;
|
||||
bspbrush_t *tail;
|
||||
bspbrush_t *keep;
|
||||
bspbrush_t *sub, *sub2;
|
||||
int c1, c2;
|
||||
|
||||
qprintf ("---- ChopBrushes ----\n");
|
||||
qprintf ("original brushes: %i\n", CountBrushList (head));
|
||||
|
||||
#if DEBUG_BRUSHMODEL
|
||||
if (entity_num == DEBUG_BRUSHMODEL)
|
||||
WriteBrushList ("before.gl", head, false);
|
||||
#endif
|
||||
keep = NULL;
|
||||
|
||||
newlist:
|
||||
// find tail
|
||||
if (!head)
|
||||
return NULL;
|
||||
for (tail=head ; tail->next ; tail=tail->next)
|
||||
;
|
||||
|
||||
for (b1=head ; b1 ; b1=next)
|
||||
{
|
||||
next = b1->next;
|
||||
for (b2=b1->next ; b2 ; b2 = b2->next)
|
||||
{
|
||||
if (BrushesDisjoint (b1, b2))
|
||||
continue;
|
||||
|
||||
sub = NULL;
|
||||
sub2 = NULL;
|
||||
c1 = 999999;
|
||||
c2 = 999999;
|
||||
|
||||
if ( BrushGE (b2, b1) )
|
||||
{
|
||||
// printf( "b2 bites b1\n" );
|
||||
sub = SubtractBrush (b1, b2);
|
||||
if (sub == b1)
|
||||
continue; // didn't really intersect
|
||||
if (!sub)
|
||||
{ // b1 is swallowed by b2
|
||||
head = CullList (b1, b1);
|
||||
goto newlist;
|
||||
}
|
||||
c1 = CountBrushList (sub);
|
||||
}
|
||||
|
||||
if ( BrushGE (b1, b2) )
|
||||
{
|
||||
// printf( "b1 bites b2\n" );
|
||||
sub2 = SubtractBrush (b2, b1);
|
||||
if (sub2 == b2)
|
||||
continue; // didn't really intersect
|
||||
if (!sub2)
|
||||
{ // b2 is swallowed by b1
|
||||
FreeBrushList (sub);
|
||||
head = CullList (b1, b2);
|
||||
goto newlist;
|
||||
}
|
||||
c2 = CountBrushList (sub2);
|
||||
}
|
||||
|
||||
if (!sub && !sub2)
|
||||
continue; // neither one can bite
|
||||
|
||||
// only accept if it didn't fragment
|
||||
// (commening this out allows full fragmentation)
|
||||
if (c1 > 1 && c2 > 1)
|
||||
{
|
||||
const int contents1 = b1->original->contents;
|
||||
const int contents2 = b2->original->contents;
|
||||
// if both detail, allow fragmentation
|
||||
if ( !((contents1&contents2) & CONTENTS_DETAIL) && !((contents1|contents2) & CONTENTS_AREAPORTAL) )
|
||||
{
|
||||
if (sub2)
|
||||
FreeBrushList (sub2);
|
||||
if (sub)
|
||||
FreeBrushList (sub);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (c1 < c2)
|
||||
{
|
||||
if (sub2)
|
||||
FreeBrushList (sub2);
|
||||
tail = AddBrushListToTail (sub, tail);
|
||||
head = CullList (b1, b1);
|
||||
goto newlist;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sub)
|
||||
FreeBrushList (sub);
|
||||
tail = AddBrushListToTail (sub2, tail);
|
||||
head = CullList (b1, b2);
|
||||
goto newlist;
|
||||
}
|
||||
}
|
||||
|
||||
if (!b2)
|
||||
{ // b1 is no longer intersecting anything, so keep it
|
||||
b1->next = keep;
|
||||
keep = b1;
|
||||
}
|
||||
}
|
||||
|
||||
qprintf ("output brushes: %i\n", CountBrushList (keep));
|
||||
#if DEBUG_BRUSHMODEL
|
||||
if ( entity_num == DEBUG_BRUSHMODEL )
|
||||
{
|
||||
WriteBrushList ("after.gl", keep, false);
|
||||
WriteBrushMap ("after.map", keep);
|
||||
}
|
||||
#endif
|
||||
return keep;
|
||||
}
|
||||
|
||||
|
||||
32
mp/src/utils/vbsp/csg.h
Normal file
32
mp/src/utils/vbsp/csg.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef CSG_H
|
||||
#define CSG_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Print a CONTENTS_ mask to a string.
|
||||
void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars );
|
||||
|
||||
// Print a CONTENTS_ mask with Msg().
|
||||
void PrintBrushContents( int contents );
|
||||
|
||||
void FixupAreaportalWaterBrushes( bspbrush_t *pList );
|
||||
|
||||
bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
|
||||
const Vector& clipmins, const Vector& clipmaxs, int detailScreen);
|
||||
bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs);
|
||||
|
||||
void WriteBrushMap (char *name, bspbrush_t *list);
|
||||
|
||||
bspbrush_t *ChopBrushes (bspbrush_t *head);
|
||||
bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b);
|
||||
qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b);
|
||||
|
||||
#endif // CSG_H
|
||||
995
mp/src/utils/vbsp/cubemap.cpp
Normal file
995
mp/src/utils/vbsp/cubemap.cpp
Normal file
@@ -0,0 +1,995 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "bsplib.h"
|
||||
#include "tier1/UtlBuffer.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "bitmap/imageformat.h"
|
||||
#include <KeyValues.h>
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/utlsymbol.h"
|
||||
#include "vtf/vtf.h"
|
||||
#include "materialpatch.h"
|
||||
#include "materialsystem/imaterialsystem.h"
|
||||
#include "materialsystem/imaterial.h"
|
||||
#include "materialsystem/imaterialvar.h"
|
||||
|
||||
|
||||
/*
|
||||
Meager documentation for how the cubemaps are assigned.
|
||||
|
||||
|
||||
While loading the map, it calls:
|
||||
*** Cubemap_SaveBrushSides
|
||||
Builds a list of what cubemaps manually were assigned to what faces
|
||||
in s_EnvCubemapToBrushSides.
|
||||
|
||||
Immediately after loading the map, it calls:
|
||||
*** Cubemap_FixupBrushSidesMaterials
|
||||
Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
|
||||
side referenced by an env_cubemap manually.
|
||||
|
||||
Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
|
||||
*** Cubemap_InitCubemapSideData:
|
||||
Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
|
||||
bHasEnvMapInMaterial is set if the side's material has $envmap.
|
||||
bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
|
||||
|
||||
Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
|
||||
referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
|
||||
*/
|
||||
|
||||
struct PatchInfo_t
|
||||
{
|
||||
char *m_pMapName;
|
||||
int m_pOrigin[3];
|
||||
};
|
||||
|
||||
struct CubemapInfo_t
|
||||
{
|
||||
int m_nTableId;
|
||||
bool m_bSpecular;
|
||||
};
|
||||
|
||||
static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
|
||||
{
|
||||
return ( lhs.m_nTableId < rhs.m_nTableId );
|
||||
}
|
||||
|
||||
|
||||
typedef CUtlVector<int> IntVector_t;
|
||||
static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
|
||||
|
||||
static CUtlVector<char *> s_DefaultCubemapNames;
|
||||
static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
|
||||
|
||||
|
||||
struct CubemapSideData_t
|
||||
{
|
||||
bool bHasEnvMapInMaterial;
|
||||
bool bManuallyPickedByAnEnvCubemap;
|
||||
};
|
||||
|
||||
static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
|
||||
|
||||
|
||||
|
||||
inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
|
||||
{
|
||||
return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
|
||||
}
|
||||
|
||||
|
||||
void Cubemap_InsertSample( const Vector& origin, int size )
|
||||
{
|
||||
dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
|
||||
pSample->origin[0] = ( int )origin[0];
|
||||
pSample->origin[1] = ( int )origin[1];
|
||||
pSample->origin[2] = ( int )origin[2];
|
||||
pSample->size = size;
|
||||
g_nCubemapSamples++;
|
||||
}
|
||||
|
||||
static const char *FindSkyboxMaterialName( void )
|
||||
{
|
||||
for( int i = 0; i < g_MainMap->num_entities; i++ )
|
||||
{
|
||||
char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
|
||||
if (!strcmp(pEntity, "worldspawn"))
|
||||
{
|
||||
return ValueForKey( &g_MainMap->entities[i], "skyname" );
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void BackSlashToForwardSlash( char *pname )
|
||||
{
|
||||
while ( *pname ) {
|
||||
if ( *pname == '\\' )
|
||||
*pname = '/';
|
||||
pname++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ForwardSlashToBackSlash( char *pname )
|
||||
{
|
||||
while ( *pname ) {
|
||||
if ( *pname == '/' )
|
||||
*pname = '\\';
|
||||
pname++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds materials that are used by a particular material
|
||||
//-----------------------------------------------------------------------------
|
||||
#define MAX_MATERIAL_NAME 512
|
||||
|
||||
// This is the list of materialvars which are used in our codebase to look up dependent materials
|
||||
static const char *s_pDependentMaterialVar[] =
|
||||
{
|
||||
"$bottommaterial", // Used by water materials
|
||||
"$crackmaterial", // Used by shattered glass materials
|
||||
"$fallbackmaterial", // Used by all materials
|
||||
|
||||
"", // Always must be last
|
||||
};
|
||||
|
||||
static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
|
||||
{
|
||||
// FIXME: This is a terrible way of doing this! It creates a dependency
|
||||
// between vbsp and *all* code which reads dependent materials from materialvars
|
||||
// At the time of writing this function, that means the engine + studiorender.
|
||||
// We need a better way of figuring out how to do this, but for now I'm trying to do
|
||||
// the fastest solution possible since it's close to ship
|
||||
|
||||
static char pDependentMaterialName[MAX_MATERIAL_NAME];
|
||||
for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
|
||||
{
|
||||
if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
|
||||
continue;
|
||||
|
||||
if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
|
||||
{
|
||||
Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return the material var that caused the dependency
|
||||
if ( ppMaterialVar )
|
||||
{
|
||||
*ppMaterialVar = s_pDependentMaterialVar[i];
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// FIXME: Note that this code breaks if a material has more than 1 dependent material
|
||||
++i;
|
||||
static char pDependentMaterialName2[MAX_MATERIAL_NAME];
|
||||
while( s_pDependentMaterialVar[i][0] )
|
||||
{
|
||||
Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
|
||||
++i;
|
||||
}
|
||||
#endif
|
||||
|
||||
return pDependentMaterialName;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads VTF files
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
|
||||
int *pUnionTextureFlags, bool bHDR )
|
||||
{
|
||||
const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
|
||||
int i;
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
char srcMaterialName[1024];
|
||||
sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
|
||||
|
||||
IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
|
||||
//IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
|
||||
IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
|
||||
const char *vtfName = pSkyTextureVar->GetStringValue();
|
||||
char srcVTFFileName[MAX_PATH];
|
||||
Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
|
||||
|
||||
CUtlBuffer buf;
|
||||
if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
|
||||
{
|
||||
// Try looking for a compressed HDR texture
|
||||
if ( bHDR )
|
||||
{
|
||||
/* // FIXME: We need a way to uncompress this format!
|
||||
bool bHDRCompressed = true;
|
||||
|
||||
pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
|
||||
vtfName = pSkyTextureVar->GetStringValue();
|
||||
Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
|
||||
|
||||
if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
|
||||
*/
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pSrcVTFTextures[i] = CreateVTFTexture();
|
||||
if (!pSrcVTFTextures[i]->Unserialize(buf))
|
||||
{
|
||||
Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
|
||||
return false;
|
||||
}
|
||||
|
||||
*pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
|
||||
int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
|
||||
int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
|
||||
|
||||
// NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
|
||||
if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
|
||||
( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
|
||||
( flagsNoAlpha != flagsFirstNoAlpha ) )
|
||||
{
|
||||
Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( bHDR )
|
||||
{
|
||||
pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
|
||||
pSrcVTFTextures[i]->GenerateMipmaps();
|
||||
pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
|
||||
{
|
||||
Q_strncpy( pDest, pSrcName, maxLen );
|
||||
if( !bHDR )
|
||||
{
|
||||
return;
|
||||
}
|
||||
char *pDot = Q_stristr( pDest, ".vtf" );
|
||||
if( !pDot )
|
||||
{
|
||||
return;
|
||||
}
|
||||
Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
|
||||
}
|
||||
|
||||
#define DEFAULT_CUBEMAP_SIZE 32
|
||||
|
||||
void CreateDefaultCubemaps( bool bHDR )
|
||||
{
|
||||
memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
|
||||
|
||||
// NOTE: This implementation depends on the fact that all VTF files contain
|
||||
// all mipmap levels
|
||||
const char *pSkyboxBaseName = FindSkyboxMaterialName();
|
||||
char skyboxMaterialName[MAX_PATH];
|
||||
Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
|
||||
|
||||
IVTFTexture *pSrcVTFTextures[6];
|
||||
|
||||
if( !skyboxMaterialName )
|
||||
{
|
||||
if( s_DefaultCubemapNames.Count() )
|
||||
{
|
||||
Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int unionTextureFlags = 0;
|
||||
if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
|
||||
{
|
||||
Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
|
||||
return;
|
||||
}
|
||||
Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
|
||||
" ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
|
||||
|
||||
// Figure out the mip differences between the two textures
|
||||
int iMipLevelOffset = 0;
|
||||
int tmp = pSrcVTFTextures[0]->Width();
|
||||
while( tmp > DEFAULT_CUBEMAP_SIZE )
|
||||
{
|
||||
iMipLevelOffset++;
|
||||
tmp >>= 1;
|
||||
}
|
||||
|
||||
// Create the destination cubemap
|
||||
IVTFTexture *pDstCubemap = CreateVTFTexture();
|
||||
pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
|
||||
pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
|
||||
pSrcVTFTextures[0]->FrameCount() );
|
||||
|
||||
// First iterate over all frames
|
||||
for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
|
||||
{
|
||||
// Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
|
||||
for (int iFace = 0; iFace < 6; ++iFace )
|
||||
{
|
||||
// Finally, iterate over all mip levels in the *destination*
|
||||
for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
|
||||
{
|
||||
// Copy the bits from the source images into the cube faces
|
||||
unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
|
||||
unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
|
||||
int iSize = pDstCubemap->ComputeMipSize( iMip );
|
||||
int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
|
||||
|
||||
// !!! FIXME: Set this to black until HDR cubemaps are built properly!
|
||||
memset( pDstBits, 0, iSize );
|
||||
continue;
|
||||
|
||||
if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
|
||||
{
|
||||
// Force mip level 2 to get the 1x1 face
|
||||
unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
|
||||
int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
|
||||
|
||||
// Replicate 1x1 mip level across entire face
|
||||
//memset( pDstBits, 0, iSize );
|
||||
for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
|
||||
{
|
||||
memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
|
||||
}
|
||||
}
|
||||
else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
|
||||
{
|
||||
if ( iSrcMipSize != iSize )
|
||||
{
|
||||
Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
|
||||
memset( pDstBits, 0, iSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just copy the mip level
|
||||
memcpy( pDstBits, pSrcBits, iSize );
|
||||
}
|
||||
}
|
||||
else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
|
||||
{
|
||||
int iMipWidth, iMipHeight, iMipDepth;
|
||||
pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
|
||||
if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
|
||||
{
|
||||
Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
|
||||
memset( pDstBits, 0, iSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy row at a time and repeat last row
|
||||
memcpy( pDstBits, pSrcBits, iSize/2 );
|
||||
//memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
|
||||
int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
|
||||
int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
|
||||
if ( nSrcRowSize != nDstRowSize )
|
||||
{
|
||||
Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
|
||||
memset( pDstBits, 0, iSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
|
||||
{
|
||||
memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ERROR! This code only supports square and rectangluar 2x wide
|
||||
Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
|
||||
memset( pDstBits, 0, iSize );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImageFormat originalFormat = pDstCubemap->Format();
|
||||
if( !bHDR )
|
||||
{
|
||||
// Convert the cube to format that we can apply tools to it...
|
||||
pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
|
||||
}
|
||||
|
||||
// Fixup the cubemap facing
|
||||
pDstCubemap->FixCubemapFaceOrientation();
|
||||
|
||||
// Now that the bits are in place, compute the spheremaps...
|
||||
pDstCubemap->GenerateSpheremap();
|
||||
|
||||
if( !bHDR )
|
||||
{
|
||||
// Convert the cubemap to the final format
|
||||
pDstCubemap->ConvertImageFormat( originalFormat, false );
|
||||
}
|
||||
|
||||
// Write the puppy out!
|
||||
char dstVTFFileName[1024];
|
||||
if( bHDR )
|
||||
{
|
||||
sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
|
||||
}
|
||||
|
||||
CUtlBuffer outputBuf;
|
||||
if (!pDstCubemap->Serialize( outputBuf ))
|
||||
{
|
||||
Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
|
||||
return;
|
||||
}
|
||||
|
||||
IZip *pak = GetPakFile();
|
||||
|
||||
// spit out the default one.
|
||||
AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
|
||||
|
||||
// spit out all of the ones that are attached to world geometry.
|
||||
int i;
|
||||
for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
|
||||
{
|
||||
char vtfName[MAX_PATH];
|
||||
VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
|
||||
if( FileExistsInPak( pak, vtfName ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
|
||||
}
|
||||
|
||||
// Clean up the textures
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
DestroyVTFTexture( pSrcVTFTextures[i] );
|
||||
}
|
||||
DestroyVTFTexture( pDstCubemap );
|
||||
}
|
||||
|
||||
void Cubemap_CreateDefaultCubemaps( void )
|
||||
{
|
||||
CreateDefaultCubemaps( false );
|
||||
CreateDefaultCubemaps( true );
|
||||
}
|
||||
|
||||
// Builds a list of what cubemaps manually were assigned to what faces
|
||||
// in s_EnvCubemapToBrushSides.
|
||||
void Cubemap_SaveBrushSides( const char *pSideListStr )
|
||||
{
|
||||
IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
|
||||
char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
|
||||
strcpy( pTmp, pSideListStr );
|
||||
const char *pScan = strtok( pTmp, " " );
|
||||
if( !pScan )
|
||||
{
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
int brushSideID;
|
||||
if( sscanf( pScan, "%d", &brushSideID ) == 1 )
|
||||
{
|
||||
brushSidesVector.AddToTail( brushSideID );
|
||||
}
|
||||
} while( ( pScan = strtok( NULL, " " ) ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generate patched material name
|
||||
//-----------------------------------------------------------------------------
|
||||
static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
|
||||
{
|
||||
const char *pSeparator = bMaterialName ? "_" : "";
|
||||
int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
|
||||
pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
|
||||
|
||||
if ( bMaterialName )
|
||||
{
|
||||
Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
|
||||
if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
|
||||
{
|
||||
Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
|
||||
}
|
||||
}
|
||||
|
||||
BackSlashToForwardSlash( pBuffer );
|
||||
Q_strlower( pBuffer );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Patches the $envmap for a material and all its dependents, returns true if any patching happened
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
|
||||
{
|
||||
// Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
|
||||
|
||||
// FIXME: It's theoretically ok to patch the material if $envmap is not specified,
|
||||
// because we're using the 'replace' block, which will only add the env_cubemap if
|
||||
// $envmap is specified in the source material. But it will fail if someone adds
|
||||
// a specific non-env_cubemap $envmap to the source material at a later point. Bleah
|
||||
|
||||
// See if we have an $envmap to patch
|
||||
bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
|
||||
|
||||
// See if we have a dependent material to patch
|
||||
bool bDependentMaterialPatched = false;
|
||||
const char *pDependentMaterialVar = NULL;
|
||||
const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
|
||||
if ( pDependentMaterial )
|
||||
{
|
||||
bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
|
||||
}
|
||||
|
||||
// If we have neither to patch, we're done
|
||||
if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
|
||||
return false;
|
||||
|
||||
// Otherwise we have to make a patched version of ourselves
|
||||
char pPatchedMaterialName[1024];
|
||||
GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
|
||||
|
||||
MaterialPatchInfo_t pPatchInfo[2];
|
||||
int nPatchCount = 0;
|
||||
if ( bShouldPatchEnvCubemap )
|
||||
{
|
||||
pPatchInfo[nPatchCount].m_pKey = "$envmap";
|
||||
pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
|
||||
pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
|
||||
++nPatchCount;
|
||||
}
|
||||
|
||||
char pDependentPatchedMaterialName[1024];
|
||||
if ( bDependentMaterialPatched )
|
||||
{
|
||||
// FIXME: Annoying! I either have to pass back the patched dependent material name
|
||||
// or reconstruct it. Both are sucky.
|
||||
GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
|
||||
pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
|
||||
pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
|
||||
++nPatchCount;
|
||||
}
|
||||
|
||||
CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds a texinfo that has a particular
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
|
||||
// Returns the index of the new (or preexisting) texinfo referencing that VMT.
|
||||
//
|
||||
// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
|
||||
// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
|
||||
// runtime before they run buildcubemaps.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
|
||||
{
|
||||
// Don't make cubemap tex infos for nodes
|
||||
if ( originalTexInfo == TEXINFO_NODE )
|
||||
return originalTexInfo;
|
||||
|
||||
texinfo_t *pTexInfo = &texinfo[originalTexInfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
if ( g_IsCubemapTexData[pTexInfo->texdata] )
|
||||
{
|
||||
Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
|
||||
return originalTexInfo;
|
||||
}
|
||||
|
||||
// Get out of here if the originalTexInfo is already a generated material for this position.
|
||||
char pStringToSearchFor[512];
|
||||
Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
|
||||
if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
|
||||
return originalTexInfo;
|
||||
|
||||
// Package up information needed to generate patch names
|
||||
PatchInfo_t info;
|
||||
info.m_pMapName = mapbase;
|
||||
info.m_pOrigin[0] = origin[0];
|
||||
info.m_pOrigin[1] = origin[1];
|
||||
info.m_pOrigin[2] = origin[2];
|
||||
|
||||
// Generate the name of the patched material
|
||||
char pGeneratedTexDataName[1024];
|
||||
GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
|
||||
|
||||
// Make sure the texdata doesn't already exist.
|
||||
int nTexDataID = FindTexData( pGeneratedTexDataName );
|
||||
bool bHasTexData = (nTexDataID != -1);
|
||||
if( !bHasTexData )
|
||||
{
|
||||
// Generate the new "$envmap" texture name.
|
||||
char pTextureName[1024];
|
||||
GeneratePatchedName( "c", info, false, pTextureName, 1024 );
|
||||
|
||||
// Hook the texture into the material and all dependent materials
|
||||
// but if no hooking was necessary, exit out
|
||||
if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
|
||||
return originalTexInfo;
|
||||
|
||||
// Store off the name of the cubemap that we need to create since we successfully patched
|
||||
char pFileName[1024];
|
||||
int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
|
||||
int id = s_DefaultCubemapNames.AddToTail();
|
||||
s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
|
||||
strcpy( s_DefaultCubemapNames[id], pFileName );
|
||||
|
||||
// Make a new texdata
|
||||
nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
|
||||
g_IsCubemapTexData[nTexDataID] = true;
|
||||
}
|
||||
|
||||
Assert( nTexDataID != -1 );
|
||||
|
||||
texinfo_t newTexInfo;
|
||||
newTexInfo = *pTexInfo;
|
||||
newTexInfo.texdata = nTexDataID;
|
||||
|
||||
int nTexInfoID = -1;
|
||||
|
||||
// See if we need to make a new texinfo
|
||||
bool bHasTexInfo = false;
|
||||
if( bHasTexData )
|
||||
{
|
||||
nTexInfoID = FindTexInfo( newTexInfo );
|
||||
bHasTexInfo = (nTexInfoID != -1);
|
||||
}
|
||||
|
||||
// Make a new texinfo if we need to.
|
||||
if( !bHasTexInfo )
|
||||
{
|
||||
nTexInfoID = texinfo.AddToTail( newTexInfo );
|
||||
}
|
||||
|
||||
Assert( nTexInfoID != -1 );
|
||||
return nTexInfoID;
|
||||
}
|
||||
|
||||
static int SideIDToIndex( int brushSideID )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
|
||||
{
|
||||
if( g_MainMap->brushsides[i].id == brushSideID )
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
|
||||
// side referenced by an env_cubemap manually.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Cubemap_FixupBrushSidesMaterials( void )
|
||||
{
|
||||
Msg( "fixing up env_cubemap materials on brush sides...\n" );
|
||||
Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
|
||||
|
||||
int cubemapID;
|
||||
for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
|
||||
{
|
||||
IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
|
||||
int i;
|
||||
for( i = 0; i < brushSidesVector.Count(); i++ )
|
||||
{
|
||||
int brushSideID = brushSidesVector[i];
|
||||
int sideIndex = SideIDToIndex( brushSideID );
|
||||
if( sideIndex < 0 )
|
||||
{
|
||||
Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
|
||||
g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
side_t *pSide = &g_MainMap->brushsides[sideIndex];
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( pSide->pMapDisp )
|
||||
{
|
||||
Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
|
||||
}
|
||||
#endif
|
||||
|
||||
pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
|
||||
if ( pSide->pMapDisp )
|
||||
{
|
||||
pSide->pMapDisp->face.texinfo = pSide->texinfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void Cubemap_ResetCubemapSideData( void )
|
||||
{
|
||||
for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
|
||||
{
|
||||
s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
|
||||
s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Returns true if the material or any of its dependents use an $envmap
|
||||
//-----------------------------------------------------------------------------
|
||||
bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
|
||||
{
|
||||
const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
|
||||
if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
|
||||
return true;
|
||||
|
||||
const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
|
||||
if ( !pDependentMaterial )
|
||||
return false;
|
||||
|
||||
return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Builds a list of all texdatas which need fixing up
|
||||
//-----------------------------------------------------------------------------
|
||||
void Cubemap_InitCubemapSideData( void )
|
||||
{
|
||||
// This tree is used to prevent re-parsing material vars multiple times
|
||||
CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
|
||||
|
||||
// Fill in specular data.
|
||||
for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
|
||||
{
|
||||
side_t *pSide = &g_MainMap->brushsides[iSide];
|
||||
if ( !pSide )
|
||||
continue;
|
||||
|
||||
if ( pSide->texinfo == TEXINFO_NODE )
|
||||
continue;
|
||||
|
||||
texinfo_t *pTex = &texinfo[pSide->texinfo];
|
||||
if ( !pTex )
|
||||
continue;
|
||||
|
||||
dtexdata_t *pTexData = GetTexData( pTex->texdata );
|
||||
if ( !pTexData )
|
||||
continue;
|
||||
|
||||
CubemapInfo_t info;
|
||||
info.m_nTableId = pTexData->nameStringTableID;
|
||||
|
||||
// Have we encountered this materal? If so, then copy the data we cached off before
|
||||
int i = lookup.Find( info );
|
||||
if ( i != lookup.InvalidIndex() )
|
||||
{
|
||||
s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
|
||||
continue;
|
||||
}
|
||||
|
||||
// First time we've seen this material. Figure out if it uses env_cubemap
|
||||
const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
|
||||
s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
|
||||
lookup.Insert( info );
|
||||
}
|
||||
|
||||
// Fill in cube map data.
|
||||
for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
|
||||
{
|
||||
IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
|
||||
int nSideCount = sideList.Count();
|
||||
for ( int iSide = 0; iSide < nSideCount; ++iSide )
|
||||
{
|
||||
int nSideID = sideList[iSide];
|
||||
int nIndex = SideIDToIndex( nSideID );
|
||||
if ( nIndex < 0 )
|
||||
continue;
|
||||
|
||||
s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
|
||||
{
|
||||
if ( !pSide )
|
||||
return -1;
|
||||
|
||||
// Return a valid (if random) cubemap if there's no winding
|
||||
if ( !pSide->winding )
|
||||
return 0;
|
||||
|
||||
// Calculate the center point.
|
||||
Vector vecCenter;
|
||||
vecCenter.Init();
|
||||
|
||||
for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
|
||||
{
|
||||
VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
|
||||
}
|
||||
VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
|
||||
vecCenter += entityOrigin;
|
||||
plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
|
||||
|
||||
// Find the closest cubemap.
|
||||
int iMinCubemap = -1;
|
||||
float flMinDist = FLT_MAX;
|
||||
|
||||
// Look for cubemaps in front of the surface first.
|
||||
for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
|
||||
{
|
||||
dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
|
||||
Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
|
||||
static_cast<float>( pSample->origin[1] ),
|
||||
static_cast<float>( pSample->origin[2] ) );
|
||||
Vector vecDelta;
|
||||
VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
|
||||
float flDist = vecDelta.NormalizeInPlace();
|
||||
float flDot = DotProduct( vecDelta, pPlane->normal );
|
||||
if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
|
||||
{
|
||||
flMinDist = flDist;
|
||||
iMinCubemap = iCubemap;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find anything in front search for closest.
|
||||
if( iMinCubemap == -1 )
|
||||
{
|
||||
for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
|
||||
{
|
||||
dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
|
||||
Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
|
||||
static_cast<float>( pSample->origin[1] ),
|
||||
static_cast<float>( pSample->origin[2] ) );
|
||||
Vector vecDelta;
|
||||
VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
|
||||
float flDist = vecDelta.Length();
|
||||
if ( flDist < flMinDist )
|
||||
{
|
||||
flMinDist = flDist;
|
||||
iMinCubemap = iCubemap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iMinCubemap;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
|
||||
//-----------------------------------------------------------------------------
|
||||
void Cubemap_AttachDefaultCubemapToSpecularSides( void )
|
||||
{
|
||||
Cubemap_ResetCubemapSideData();
|
||||
Cubemap_InitCubemapSideData();
|
||||
|
||||
// build a mapping from side to entity id so that we can get the entity origin
|
||||
CUtlVector<int> sideToEntityIndex;
|
||||
sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
|
||||
int i;
|
||||
for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
|
||||
{
|
||||
sideToEntityIndex[i] = -1;
|
||||
}
|
||||
|
||||
for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
|
||||
{
|
||||
int entityIndex = g_MainMap->mapbrushes[i].entitynum;
|
||||
for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
|
||||
{
|
||||
side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
|
||||
int sideIndex = side - g_MainMap->brushsides;
|
||||
sideToEntityIndex[sideIndex] = entityIndex;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
|
||||
{
|
||||
side_t *pSide = &g_MainMap->brushsides[iSide];
|
||||
if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
|
||||
continue;
|
||||
|
||||
|
||||
int currentEntity = sideToEntityIndex[iSide];
|
||||
|
||||
int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
|
||||
if ( iCubemap == -1 )
|
||||
continue;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( pSide->pMapDisp )
|
||||
{
|
||||
Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
|
||||
}
|
||||
#endif
|
||||
pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
|
||||
if ( pSide->pMapDisp )
|
||||
{
|
||||
pSide->pMapDisp->face.texinfo = pSide->texinfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate with cubemaps that were skipped
|
||||
void Cubemap_AddUnreferencedCubemaps()
|
||||
{
|
||||
char pTextureName[1024];
|
||||
char pFileName[1024];
|
||||
PatchInfo_t info;
|
||||
dcubemapsample_t *pSample;
|
||||
int i,j;
|
||||
|
||||
for ( i=0; i<g_nCubemapSamples; ++i )
|
||||
{
|
||||
pSample = &g_CubemapSamples[i];
|
||||
|
||||
// generate the formatted texture name based on cubemap origin
|
||||
info.m_pMapName = mapbase;
|
||||
info.m_pOrigin[0] = pSample->origin[0];
|
||||
info.m_pOrigin[1] = pSample->origin[1];
|
||||
info.m_pOrigin[2] = pSample->origin[2];
|
||||
GeneratePatchedName( "c", info, false, pTextureName, 1024 );
|
||||
|
||||
// find or add
|
||||
for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
|
||||
{
|
||||
if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
|
||||
{
|
||||
// already added
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( j == s_DefaultCubemapNames.Count() )
|
||||
{
|
||||
int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
|
||||
|
||||
int id = s_DefaultCubemapNames.AddToTail();
|
||||
s_DefaultCubemapNames[id] = new char[nLen + 1];
|
||||
strcpy( s_DefaultCubemapNames[id], pFileName );
|
||||
}
|
||||
}
|
||||
}
|
||||
693
mp/src/utils/vbsp/detail.cpp
Normal file
693
mp/src/utils/vbsp/detail.cpp
Normal file
@@ -0,0 +1,693 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Builds/merges the BSP tree of detail brushes
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "detail.h"
|
||||
#include "utlvector.h"
|
||||
#include <assert.h>
|
||||
|
||||
face_t *NewFaceFromFace (face_t *f);
|
||||
face_t *ComputeVisibleBrushSides( bspbrush_t *list );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Copies a face and its winding
|
||||
// Input : *pFace -
|
||||
// Output : face_t
|
||||
//-----------------------------------------------------------------------------
|
||||
face_t *CopyFace( face_t *pFace )
|
||||
{
|
||||
face_t *f = NewFaceFromFace( pFace );
|
||||
f->w = CopyWinding( pFace->w );
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Link this brush into the list for this leaf
|
||||
// Input : *node -
|
||||
// *brush -
|
||||
//-----------------------------------------------------------------------------
|
||||
void AddBrushToLeaf( node_t *node, bspbrush_t *brush )
|
||||
{
|
||||
brush->next = node->brushlist;
|
||||
node->brushlist = brush;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Recursively filter a brush through the tree
|
||||
// Input : *node -
|
||||
// *brush -
|
||||
//-----------------------------------------------------------------------------
|
||||
void MergeBrush_r( node_t *node, bspbrush_t *brush )
|
||||
{
|
||||
if ( node->planenum == PLANENUM_LEAF )
|
||||
{
|
||||
if ( node->contents & CONTENTS_SOLID )
|
||||
{
|
||||
FreeBrush( brush );
|
||||
}
|
||||
else
|
||||
{
|
||||
AddBrushToLeaf( node, brush );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bspbrush_t *front, *back;
|
||||
SplitBrush( brush, node->planenum, &front, &back );
|
||||
FreeBrush( brush );
|
||||
|
||||
if ( front )
|
||||
{
|
||||
MergeBrush_r( node->children[0], front );
|
||||
}
|
||||
if ( back )
|
||||
{
|
||||
MergeBrush_r( node->children[1], back );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Recursively filter a face into the tree leaving references to the
|
||||
// original face in any visible leaves that a clipped fragment falls
|
||||
// into.
|
||||
// Input : *node - current head of tree
|
||||
// *face - clipped face fragment
|
||||
// *original - unclipped original face
|
||||
// Output : Returns true if any references were left
|
||||
//-----------------------------------------------------------------------------
|
||||
bool MergeFace_r( node_t *node, face_t *face, face_t *original )
|
||||
{
|
||||
bool referenced = false;
|
||||
|
||||
if ( node->planenum == PLANENUM_LEAF )
|
||||
{
|
||||
if ( node->contents & CONTENTS_SOLID )
|
||||
{
|
||||
FreeFace( face );
|
||||
return false;
|
||||
}
|
||||
|
||||
leafface_t *plist = new leafface_t;
|
||||
plist->pFace = original;
|
||||
plist->pNext = node->leaffacelist;
|
||||
node->leaffacelist = plist;
|
||||
|
||||
referenced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// UNDONE: Don't copy the faces each time unless it's necessary!?!?!
|
||||
plane_t *plane = &g_MainMap->mapplanes[node->planenum];
|
||||
winding_t *frontwinding, *backwinding, *onwinding;
|
||||
|
||||
Vector offset;
|
||||
WindingCenter( face->w, offset );
|
||||
|
||||
// UNDONE: Export epsilon from original face clipping code
|
||||
ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset);
|
||||
|
||||
if ( onwinding )
|
||||
{
|
||||
// face is in the split plane, go down the appropriate side according to the facing direction
|
||||
assert( frontwinding == NULL );
|
||||
assert( backwinding == NULL );
|
||||
|
||||
if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 )
|
||||
{
|
||||
frontwinding = onwinding;
|
||||
}
|
||||
else
|
||||
{
|
||||
backwinding = onwinding;
|
||||
}
|
||||
}
|
||||
|
||||
if ( frontwinding )
|
||||
{
|
||||
face_t *tmp = NewFaceFromFace( face );
|
||||
tmp->w = frontwinding;
|
||||
referenced = MergeFace_r( node->children[0], tmp, original );
|
||||
}
|
||||
if ( backwinding )
|
||||
{
|
||||
face_t *tmp = NewFaceFromFace( face );
|
||||
tmp->w = backwinding;
|
||||
bool test = MergeFace_r( node->children[1], tmp, original );
|
||||
referenced = referenced || test;
|
||||
}
|
||||
}
|
||||
FreeFace( face );
|
||||
|
||||
return referenced;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loop through each face and filter it into the tree
|
||||
// Input : *out -
|
||||
// *pFaces -
|
||||
//-----------------------------------------------------------------------------
|
||||
face_t *FilterFacesIntoTree( tree_t *out, face_t *pFaces )
|
||||
{
|
||||
face_t *pLeafFaceList = NULL;
|
||||
for ( face_t *f = pFaces; f; f = f->next )
|
||||
{
|
||||
if( f->merged || f->split[0] || f->split[1] )
|
||||
continue;
|
||||
|
||||
face_t *tmp = CopyFace( f );
|
||||
face_t *original = CopyFace( f );
|
||||
|
||||
if ( MergeFace_r( out->headnode, tmp, original ) )
|
||||
{
|
||||
// clear out portal (comes from a different tree)
|
||||
original->portal = NULL;
|
||||
original->next = pLeafFaceList;
|
||||
pLeafFaceList = original;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeFace( original );
|
||||
}
|
||||
}
|
||||
|
||||
return pLeafFaceList;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Splits the face list into faces from the same plane and tries to merge
|
||||
// them if possible
|
||||
// Input : **pFaceList -
|
||||
//-----------------------------------------------------------------------------
|
||||
void TryMergeFaceList( face_t **pFaceList )
|
||||
{
|
||||
face_t **pPlaneList = NULL;
|
||||
|
||||
// divide the list into buckets by plane number
|
||||
pPlaneList = new face_t *[g_MainMap->nummapplanes];
|
||||
memset( pPlaneList, 0, sizeof(face_t *) * g_MainMap->nummapplanes );
|
||||
|
||||
face_t *pFaces = *pFaceList;
|
||||
face_t *pOutput = NULL;
|
||||
|
||||
while ( pFaces )
|
||||
{
|
||||
face_t *next = pFaces->next;
|
||||
|
||||
// go ahead and delete the old split/merged faces
|
||||
if ( pFaces->merged || pFaces->split[0] || pFaces->split[1] )
|
||||
{
|
||||
Error("Split face in merge list!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// add to the list for this plane
|
||||
pFaces->next = pPlaneList[pFaces->planenum];
|
||||
pPlaneList[pFaces->planenum] = pFaces;
|
||||
}
|
||||
|
||||
pFaces = next;
|
||||
}
|
||||
|
||||
// now merge each plane's list of faces
|
||||
int merged = 0;
|
||||
for ( int i = 0; i < g_MainMap->nummapplanes; i++ )
|
||||
{
|
||||
if ( pPlaneList[i] )
|
||||
{
|
||||
MergeFaceList( &pPlaneList[i] );
|
||||
}
|
||||
|
||||
// move these over to the output face list
|
||||
face_t *list = pPlaneList[i];
|
||||
while ( list )
|
||||
{
|
||||
face_t *next = list->next;
|
||||
|
||||
if ( list->merged )
|
||||
merged++;
|
||||
|
||||
list->next = pOutput;
|
||||
pOutput = list;
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
if ( merged )
|
||||
{
|
||||
Msg("\nMerged %d detail faces...", merged );
|
||||
}
|
||||
delete[] pPlaneList;
|
||||
|
||||
*pFaceList = pOutput;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: filter each brush in the list into the tree
|
||||
// Input : *out -
|
||||
// *brushes -
|
||||
//-----------------------------------------------------------------------------
|
||||
void FilterBrushesIntoTree( tree_t *out, bspbrush_t *brushes )
|
||||
{
|
||||
// Merge all of the brushes into the world tree
|
||||
for ( bspbrush_t *plist = brushes; plist; plist = plist->next )
|
||||
{
|
||||
MergeBrush_r( out->headnode, CopyBrush(plist) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Build faces for the detail brushes and merge them into the BSP
|
||||
// Input : *worldtree -
|
||||
// brush_start -
|
||||
// brush_end -
|
||||
//-----------------------------------------------------------------------------
|
||||
face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end )
|
||||
{
|
||||
int start;
|
||||
bspbrush_t *detailbrushes = NULL;
|
||||
face_t *pFaces = NULL;
|
||||
face_t *pLeafFaceList = NULL;
|
||||
|
||||
// Grab the list of detail brushes
|
||||
detailbrushes = MakeBspBrushList (brush_start, brush_end, g_MainMap->map_mins, g_MainMap->map_maxs, ONLY_DETAIL );
|
||||
if (detailbrushes)
|
||||
{
|
||||
start = Plat_FloatTime();
|
||||
Msg("Chop Details...");
|
||||
// if there are detail brushes, chop them against each other
|
||||
if (!nocsg)
|
||||
detailbrushes = ChopBrushes (detailbrushes);
|
||||
|
||||
Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
|
||||
// Now mark the visible sides so we can eliminate all detail brush sides
|
||||
// that are covered by other detail brush sides
|
||||
// NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation)
|
||||
Msg("Find Visible Detail Sides...");
|
||||
pFaces = ComputeVisibleBrushSides( detailbrushes );
|
||||
TryMergeFaceList( &pFaces );
|
||||
SubdivideFaceList( &pFaces );
|
||||
Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
|
||||
|
||||
start = Plat_FloatTime();
|
||||
Msg("Merging details...");
|
||||
// Merge the detail solids and faces into the world tree
|
||||
// Merge all of the faces into the world tree
|
||||
pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces );
|
||||
FilterBrushesIntoTree( worldtree, detailbrushes );
|
||||
|
||||
FreeFaceList( pFaces );
|
||||
FreeBrushList(detailbrushes);
|
||||
|
||||
Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
|
||||
}
|
||||
|
||||
return pLeafFaceList;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Quick overlap test for brushes
|
||||
// Input : *p1 -
|
||||
// *p2 -
|
||||
// Output : Returns false if the brushes cannot intersect
|
||||
//-----------------------------------------------------------------------------
|
||||
bool BrushBoxOverlap( bspbrush_t *p1, bspbrush_t *p2 )
|
||||
{
|
||||
if ( p1 == p2 )
|
||||
return false;
|
||||
|
||||
for ( int i = 0; i < 3; i++ )
|
||||
{
|
||||
if ( p1->mins[i] > p2->maxs[i] || p1->maxs[i] < p2->mins[i] )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *pFace - input face to test
|
||||
// *pbrush - brush to clip face against
|
||||
// **pOutputList - list of faces clipped from pFace
|
||||
// Output : Returns true if the brush completely clips the face
|
||||
//-----------------------------------------------------------------------------
|
||||
// NOTE: This assumes the brushes have already been chopped so that no solid space
|
||||
// is enclosed by more than one brush!!
|
||||
bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList )
|
||||
{
|
||||
int planenum = pFace->planenum & (~1);
|
||||
int foundSide = -1;
|
||||
|
||||
CUtlVector<int> sortedSides;
|
||||
|
||||
int i;
|
||||
for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ )
|
||||
{
|
||||
int bplane = pbrush->sides[i].planenum & (~1);
|
||||
if ( bplane == planenum )
|
||||
foundSide = i;
|
||||
}
|
||||
|
||||
Vector offset = -0.5f * (pbrush->maxs + pbrush->mins);
|
||||
face_t *currentface = CopyFace( pFace );
|
||||
|
||||
if ( foundSide >= 0 )
|
||||
{
|
||||
sortedSides.RemoveAll();
|
||||
for ( i = 0; i < pbrush->numsides; i++ )
|
||||
{
|
||||
// don't clip to bevels
|
||||
if ( pbrush->sides[i].bevel )
|
||||
continue;
|
||||
|
||||
if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z )
|
||||
{
|
||||
sortedSides.AddToHead( i );
|
||||
}
|
||||
else
|
||||
{
|
||||
sortedSides.AddToTail( i );
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < sortedSides.Size(); i++ )
|
||||
{
|
||||
int index = sortedSides[i];
|
||||
if ( index == foundSide )
|
||||
continue;
|
||||
|
||||
plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum];
|
||||
winding_t *frontwinding, *backwinding;
|
||||
ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset);
|
||||
|
||||
// only clip if some part of this face is on the back side of all brush sides
|
||||
if ( !backwinding || WindingIsTiny(backwinding))
|
||||
{
|
||||
FreeFaceList( *pOutputList );
|
||||
*pOutputList = NULL;
|
||||
break;
|
||||
}
|
||||
if ( frontwinding && !WindingIsTiny(frontwinding) )
|
||||
{
|
||||
// add this fragment to the return list
|
||||
// make a face for the fragment
|
||||
face_t *f = NewFaceFromFace( pFace );
|
||||
f->w = frontwinding;
|
||||
|
||||
// link the fragment in
|
||||
f->next = *pOutputList;
|
||||
*pOutputList = f;
|
||||
}
|
||||
|
||||
// update the current winding to be the part behind each plane
|
||||
FreeWinding( currentface->w );
|
||||
currentface->w = backwinding;
|
||||
}
|
||||
|
||||
// free the bit that is left in solid or not clipped (if we broke out early)
|
||||
FreeFace( currentface );
|
||||
|
||||
// if we made it all the way through and didn't produce any fragments then the whole face was clipped away
|
||||
if ( !*pOutputList && i == sortedSides.Size() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Given an original side and chopped winding, make a face_t
|
||||
// Input : *side - side of the original brush
|
||||
// *winding - winding for this face (portion of the side)
|
||||
// Output : face_t
|
||||
//-----------------------------------------------------------------------------
|
||||
face_t *MakeBrushFace( side_t *originalSide, winding_t *winding )
|
||||
{
|
||||
face_t *f = AllocFace();
|
||||
f->merged = NULL;
|
||||
f->split[0] = f->split[1] = NULL;
|
||||
f->w = CopyWinding( winding );
|
||||
f->originalface = originalSide;
|
||||
//
|
||||
// save material info
|
||||
//
|
||||
f->texinfo = originalSide->texinfo;
|
||||
f->dispinfo = -1;
|
||||
|
||||
// save plane info
|
||||
f->planenum = originalSide->planenum;
|
||||
f->contents = originalSide->contents;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Chop away sides that are inside other brushes.
|
||||
// Brushes have already been chopped up so that they do not overlap,
|
||||
// they merely touch.
|
||||
// Input : *list - list of brushes
|
||||
// Output : face_t * - list of visible faces (some marked bad/split)
|
||||
//-----------------------------------------------------------------------------
|
||||
// assumes brushes were chopped!
|
||||
|
||||
|
||||
side_t *FindOriginalSide( mapbrush_t *mb, side_t *pBspSide )
|
||||
{
|
||||
side_t *bestside = NULL;
|
||||
float bestdot = 0;
|
||||
|
||||
plane_t *p1 = g_MainMap->mapplanes + pBspSide->planenum;
|
||||
|
||||
for (int i=0 ; i<mb->numsides ; i++)
|
||||
{
|
||||
side_t *side = &mb->original_sides[i];
|
||||
if (side->bevel)
|
||||
continue;
|
||||
if (side->texinfo == TEXINFO_NODE)
|
||||
continue; // non-visible
|
||||
if ((side->planenum&~1) == (pBspSide->planenum&~1))
|
||||
{ // exact match
|
||||
return mb->original_sides + i;
|
||||
}
|
||||
// see how close the match is
|
||||
plane_t *p2 = &g_MainMap->mapplanes[side->planenum&~1];
|
||||
float dot = DotProduct (p1->normal, p2->normal);
|
||||
if (dot > bestdot)
|
||||
{
|
||||
bestdot = dot;
|
||||
bestside = side;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bestside )
|
||||
{
|
||||
Error( "Bad detail brush side\n" );
|
||||
}
|
||||
return bestside;
|
||||
}
|
||||
|
||||
// Get a list of brushes from pBrushList that could cut faces on the source brush
|
||||
int GetListOfCutBrushes( CUtlVector<bspbrush_t *> &out, bspbrush_t *pSourceBrush, bspbrush_t *pBrushList )
|
||||
{
|
||||
mapbrush_t *mb = pSourceBrush->original;
|
||||
for ( bspbrush_t *walk = pBrushList; walk; walk = walk->next )
|
||||
{
|
||||
if ( walk == pSourceBrush )
|
||||
continue;
|
||||
|
||||
// only clip to transparent brushes if the original brush is transparent
|
||||
if ( walk->original->contents & TRANSPARENT_CONTENTS )
|
||||
{
|
||||
if ( !(mb->contents & TRANSPARENT_CONTENTS) )
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't clip to clip brushes, etc.
|
||||
if ( !(walk->original->contents & ALL_VISIBLE_CONTENTS) )
|
||||
continue;
|
||||
|
||||
// brushes overlap, test faces
|
||||
if ( !BrushBoxOverlap( pSourceBrush, walk ) )
|
||||
continue;
|
||||
|
||||
out.AddToTail( walk );
|
||||
}
|
||||
return out.Count();
|
||||
}
|
||||
|
||||
// Count the number of real (unsplit) faces in the list
|
||||
static int CountFaceList( face_t *f )
|
||||
{
|
||||
int count = 0;
|
||||
for ( ; f; f = f->next )
|
||||
{
|
||||
if ( f->split[0] )
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Clips f to a list of potential cutting brushes
|
||||
// If f clips into new faces, returns the list of new faces in pOutputList
|
||||
static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList )
|
||||
{
|
||||
*pOutputList = NULL;
|
||||
|
||||
if ( f->split[0] )
|
||||
return;
|
||||
|
||||
face_t *pClipList = CopyFace( f );
|
||||
pClipList->next = NULL;
|
||||
bool clipped = false;
|
||||
for ( int i = 0; i < cutBrushes.Count(); i++ )
|
||||
{
|
||||
bspbrush_t *cut = cutBrushes[i];
|
||||
for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next )
|
||||
{
|
||||
face_t *pClip = NULL;
|
||||
// already split, no need to clip
|
||||
if ( pCutFace->split[0] )
|
||||
continue;
|
||||
|
||||
if ( ClipFaceToBrush( pCutFace, cut, &pClip ) )
|
||||
{
|
||||
clipped = true;
|
||||
// mark face bad, the brush clipped it away
|
||||
pCutFace->split[0] = pCutFace;
|
||||
}
|
||||
else if ( pClip )
|
||||
{
|
||||
clipped = true;
|
||||
// mark this face as split
|
||||
pCutFace->split[0] = pCutFace;
|
||||
|
||||
// insert face fragments at head of list (UNDONE: reverses order, do we care?)
|
||||
while ( pClip )
|
||||
{
|
||||
face_t *next = pClip->next;
|
||||
pClip->next = pClipList;
|
||||
pClipList = pClip;
|
||||
pClip = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( clipped )
|
||||
{
|
||||
*pOutputList = pClipList;
|
||||
}
|
||||
else
|
||||
{
|
||||
// didn't do any clipping, go ahead and free the copy of the face here.
|
||||
FreeFaceList( pClipList );
|
||||
}
|
||||
}
|
||||
|
||||
// Compute a list of faces that are visible on the detail brush sides
|
||||
face_t *ComputeVisibleBrushSides( bspbrush_t *list )
|
||||
{
|
||||
face_t *pTotalFaces = NULL;
|
||||
CUtlVector<bspbrush_t *> cutBrushes;
|
||||
|
||||
// Go through the whole brush list
|
||||
for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next )
|
||||
{
|
||||
face_t *pFaces = NULL;
|
||||
mapbrush_t *mb = pbrush->original;
|
||||
|
||||
if ( !(mb->contents & ALL_VISIBLE_CONTENTS) )
|
||||
continue;
|
||||
|
||||
// Make a face for each brush side, then clip it by the other
|
||||
// details to see if any fragments are visible
|
||||
for ( int i = 0; i < pbrush->numsides; i++ )
|
||||
{
|
||||
winding_t *winding = pbrush->sides[i].winding;
|
||||
if ( !winding )
|
||||
continue;
|
||||
|
||||
if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) )
|
||||
continue;
|
||||
|
||||
side_t *side = FindOriginalSide( mb, pbrush->sides + i );
|
||||
face_t *f = MakeBrushFace( side, winding );
|
||||
|
||||
// link to head of face list
|
||||
f->next = pFaces;
|
||||
pFaces = f;
|
||||
}
|
||||
|
||||
// Make a list of brushes that can cut the face list for this brush
|
||||
cutBrushes.RemoveAll();
|
||||
if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) )
|
||||
{
|
||||
// now cut each face to find visible fragments
|
||||
for ( face_t *f = pFaces; f; f = f->next )
|
||||
{
|
||||
// this will be a new list of faces that this face cuts into
|
||||
face_t *pClip = NULL;
|
||||
ClipFaceToBrushList( f, cutBrushes, &pClip );
|
||||
if ( pClip )
|
||||
{
|
||||
int outCount = CountFaceList(pClip);
|
||||
// it cut into more faces (or it was completely cut away)
|
||||
if ( outCount <= 1 )
|
||||
{
|
||||
// was removed or cut down, mark as split
|
||||
f->split[0] = f;
|
||||
// insert face fragments at head of list (UNDONE: reverses order, do we care?)
|
||||
while ( pClip )
|
||||
{
|
||||
face_t *next = pClip->next;
|
||||
pClip->next = pFaces;
|
||||
pFaces = pClip;
|
||||
pClip = next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// it cut into more than one visible fragment
|
||||
// Don't fragment details
|
||||
// UNDONE: Build 2d convex hull of this list and swap face winding
|
||||
// with that polygon? That would fix the remaining issues.
|
||||
FreeFaceList( pClip );
|
||||
pClip = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move visible fragments to global face list
|
||||
while ( pFaces )
|
||||
{
|
||||
face_t *next = pFaces->next;
|
||||
if ( pFaces->split[0] )
|
||||
{
|
||||
FreeFace( pFaces );
|
||||
}
|
||||
else
|
||||
{
|
||||
pFaces->next = pTotalFaces;
|
||||
pTotalFaces = pFaces;
|
||||
}
|
||||
pFaces = next;
|
||||
}
|
||||
}
|
||||
|
||||
return pTotalFaces;
|
||||
}
|
||||
18
mp/src/utils/vbsp/detail.h
Normal file
18
mp/src/utils/vbsp/detail.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef DETAIL_H
|
||||
#define DETAIL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end );
|
||||
|
||||
|
||||
#endif // DETAIL_H
|
||||
966
mp/src/utils/vbsp/detailobjects.cpp
Normal file
966
mp/src/utils/vbsp/detailobjects.cpp
Normal file
@@ -0,0 +1,966 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Places "detail" objects which are client-only renderable things
|
||||
//
|
||||
// $Revision: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include <windows.h>
|
||||
#include "vbsp.h"
|
||||
#include "bsplib.h"
|
||||
#include "KeyValues.h"
|
||||
#include "utlsymbol.h"
|
||||
#include "utlvector.h"
|
||||
#include <io.h>
|
||||
#include "bspfile.h"
|
||||
#include "utilmatlib.h"
|
||||
#include "gamebspfile.h"
|
||||
#include "mathlib/VMatrix.h"
|
||||
#include "materialpatch.h"
|
||||
#include "pacifier.h"
|
||||
#include "vstdlib/random.h"
|
||||
#include "builddisp.h"
|
||||
#include "disp_vbsp.h"
|
||||
#include "UtlBuffer.h"
|
||||
#include "CollisionUtils.h"
|
||||
#include <float.h>
|
||||
#include "UtlLinkedList.h"
|
||||
#include "byteswap.h"
|
||||
#include "writebsp.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Information about particular detail object types
|
||||
//-----------------------------------------------------------------------------
|
||||
enum
|
||||
{
|
||||
MODELFLAG_UPRIGHT = 0x1,
|
||||
};
|
||||
|
||||
struct DetailModel_t
|
||||
{
|
||||
CUtlSymbol m_ModelName;
|
||||
float m_Amount;
|
||||
float m_MinCosAngle;
|
||||
float m_MaxCosAngle;
|
||||
int m_Flags;
|
||||
int m_Orientation;
|
||||
int m_Type;
|
||||
Vector2D m_Pos[2];
|
||||
Vector2D m_Tex[2];
|
||||
float m_flRandomScaleStdDev;
|
||||
unsigned char m_ShapeSize;
|
||||
unsigned char m_ShapeAngle;
|
||||
unsigned char m_SwayAmount;
|
||||
};
|
||||
|
||||
struct DetailObjectGroup_t
|
||||
{
|
||||
float m_Alpha;
|
||||
CUtlVector< DetailModel_t > m_Models;
|
||||
};
|
||||
|
||||
struct DetailObject_t
|
||||
{
|
||||
CUtlSymbol m_Name;
|
||||
float m_Density;
|
||||
CUtlVector< DetailObjectGroup_t > m_Groups;
|
||||
|
||||
bool operator==(const DetailObject_t& src ) const
|
||||
{
|
||||
return src.m_Name == m_Name;
|
||||
}
|
||||
};
|
||||
|
||||
static CUtlVector<DetailObject_t> s_DetailObjectDict;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Error checking.. make sure the model is valid + is a static prop
|
||||
//-----------------------------------------------------------------------------
|
||||
struct StaticPropLookup_t
|
||||
{
|
||||
CUtlSymbol m_ModelName;
|
||||
bool m_IsValid;
|
||||
};
|
||||
|
||||
static bool StaticLess( StaticPropLookup_t const& src1, StaticPropLookup_t const& src2 )
|
||||
{
|
||||
return src1.m_ModelName < src2.m_ModelName;
|
||||
}
|
||||
|
||||
static CUtlRBTree< StaticPropLookup_t, unsigned short > s_StaticPropLookup( 0, 32, StaticLess );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// These puppies are used to construct the game lumps
|
||||
//-----------------------------------------------------------------------------
|
||||
static CUtlVector<DetailObjectDictLump_t> s_DetailObjectDictLump;
|
||||
static CUtlVector<DetailObjectLump_t> s_DetailObjectLump;
|
||||
static CUtlVector<DetailSpriteDictLump_t> s_DetailSpriteDictLump;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parses the key-value pairs in the detail.rad file
|
||||
//-----------------------------------------------------------------------------
|
||||
static void ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
|
||||
{
|
||||
// Sort the group by alpha
|
||||
float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
|
||||
|
||||
int i = s_DetailObjectDict[detailId].m_Groups.Count();
|
||||
while ( --i >= 0 )
|
||||
{
|
||||
if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
|
||||
break;
|
||||
}
|
||||
|
||||
// Insert after the first guy who's more transparent that we are!
|
||||
i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
|
||||
DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
|
||||
|
||||
group.m_Alpha = alpha;
|
||||
|
||||
// Add in all the model groups
|
||||
KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
|
||||
float totalAmount = 0.0f;
|
||||
while( pIter )
|
||||
{
|
||||
if (pIter->GetFirstSubKey())
|
||||
{
|
||||
int i = group.m_Models.AddToTail();
|
||||
|
||||
DetailModel_t &model = group.m_Models[i];
|
||||
|
||||
model.m_ModelName = pIter->GetString( "model", 0 );
|
||||
if (model.m_ModelName != UTL_INVAL_SYMBOL)
|
||||
{
|
||||
model.m_Type = DETAIL_PROP_TYPE_MODEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *pSpriteData = pIter->GetString( "sprite", 0 );
|
||||
if (pSpriteData)
|
||||
{
|
||||
const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
|
||||
|
||||
if ( pProcModelType )
|
||||
{
|
||||
if ( !Q_stricmp( pProcModelType, "cross" ) )
|
||||
{
|
||||
model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
|
||||
}
|
||||
else if ( !Q_stricmp( pProcModelType, "tri" ) )
|
||||
{
|
||||
model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
|
||||
}
|
||||
else
|
||||
model.m_Type = DETAIL_PROP_TYPE_SPRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// card sprite
|
||||
model.m_Type = DETAIL_PROP_TYPE_SPRITE;
|
||||
}
|
||||
|
||||
model.m_Tex[0].Init();
|
||||
model.m_Tex[1].Init();
|
||||
|
||||
float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
|
||||
int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
|
||||
if ( (nValid != 5) || (flTextureSize == 0) )
|
||||
{
|
||||
Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName.String() );
|
||||
}
|
||||
|
||||
model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
|
||||
model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
|
||||
model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
|
||||
model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
|
||||
|
||||
model.m_Pos[0].Init( -10, 20 );
|
||||
model.m_Pos[1].Init( 10, 0 );
|
||||
|
||||
pSpriteData = pIter->GetString( "spritesize", 0 );
|
||||
if (pSpriteData)
|
||||
{
|
||||
sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
|
||||
|
||||
float ox = flWidth * x;
|
||||
float oy = flHeight * y;
|
||||
|
||||
model.m_Pos[0].x = -ox;
|
||||
model.m_Pos[0].y = flHeight - oy;
|
||||
model.m_Pos[1].x = flWidth - ox;
|
||||
model.m_Pos[1].y = -oy;
|
||||
}
|
||||
|
||||
model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
|
||||
|
||||
// sway is a percent of max sway, cl_detail_max_sway
|
||||
float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
|
||||
model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
|
||||
|
||||
// shape angle
|
||||
// for the tri shape, this is the angle each side is fanned out
|
||||
model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
|
||||
|
||||
// shape size
|
||||
// for the tri shape, this is the distance from the origin to the center of a side
|
||||
float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
|
||||
model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
|
||||
}
|
||||
}
|
||||
|
||||
model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
|
||||
totalAmount = model.m_Amount;
|
||||
|
||||
model.m_Flags = 0;
|
||||
if (pIter->GetInt( "upright", 0 ))
|
||||
{
|
||||
model.m_Flags |= MODELFLAG_UPRIGHT;
|
||||
}
|
||||
|
||||
// These are used to prevent emission on steep surfaces
|
||||
float minAngle = pIter->GetFloat( "minAngle", 180 );
|
||||
float maxAngle = pIter->GetFloat( "maxAngle", 180 );
|
||||
model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
|
||||
model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
|
||||
model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
|
||||
|
||||
// Make sure minAngle < maxAngle
|
||||
if ( model.m_MinCosAngle < model.m_MaxCosAngle)
|
||||
{
|
||||
model.m_MinCosAngle = model.m_MaxCosAngle;
|
||||
}
|
||||
}
|
||||
pIter = pIter->GetNextKey();
|
||||
}
|
||||
|
||||
// renormalize the amount if the total > 1
|
||||
if (totalAmount > 1.0f)
|
||||
{
|
||||
for (i = 0; i < group.m_Models.Count(); ++i)
|
||||
{
|
||||
group.m_Models[i].m_Amount /= totalAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parses the key-value pairs in the detail.vbsp file
|
||||
//-----------------------------------------------------------------------------
|
||||
static void ParseDetailObjectFile( KeyValues& keyValues )
|
||||
{
|
||||
// Iterate over all detail object groups...
|
||||
KeyValues* pIter;
|
||||
for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
|
||||
{
|
||||
if (!pIter->GetFirstSubKey())
|
||||
continue;
|
||||
|
||||
int i = s_DetailObjectDict.AddToTail( );
|
||||
s_DetailObjectDict[i].m_Name = pIter->GetName() ;
|
||||
s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
|
||||
|
||||
// Iterate over all detail object groups...
|
||||
KeyValues* pIterGroups = pIter->GetFirstSubKey();
|
||||
while( pIterGroups )
|
||||
{
|
||||
if (pIterGroups->GetFirstSubKey())
|
||||
{
|
||||
ParseDetailGroup( i, pIterGroups );
|
||||
}
|
||||
pIterGroups = pIterGroups->GetNextKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds the name of the detail.vbsp file to use
|
||||
//-----------------------------------------------------------------------------
|
||||
static const char *FindDetailVBSPName( void )
|
||||
{
|
||||
for( int i = 0; i < num_entities; i++ )
|
||||
{
|
||||
char* pEntity = ValueForKey( &entities[i], "classname" );
|
||||
if ( !strcmp( pEntity, "worldspawn" ) )
|
||||
{
|
||||
const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
|
||||
if ( !pDetailVBSP || !pDetailVBSP[0] )
|
||||
{
|
||||
pDetailVBSP = "detail.vbsp";
|
||||
}
|
||||
return pDetailVBSP;
|
||||
}
|
||||
}
|
||||
return "detail.vbsp";
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Loads up the detail object dictionary
|
||||
//-----------------------------------------------------------------------------
|
||||
void LoadEmitDetailObjectDictionary( const char* pGameDir )
|
||||
{
|
||||
// Set the required global lights filename and try looking in qproject
|
||||
const char *pDetailVBSP = FindDetailVBSPName();
|
||||
KeyValues * values = new KeyValues( pDetailVBSP );
|
||||
if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
|
||||
{
|
||||
ParseDetailObjectFile( *values );
|
||||
}
|
||||
values->deleteThis();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Selects a detail group
|
||||
//-----------------------------------------------------------------------------
|
||||
static int SelectGroup( const DetailObject_t& detail, float alpha )
|
||||
{
|
||||
// Find the two groups whose alpha we're between...
|
||||
int start, end;
|
||||
for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
|
||||
{
|
||||
if (alpha < detail.m_Groups[start+1].m_Alpha)
|
||||
break;
|
||||
}
|
||||
|
||||
end = start + 1;
|
||||
if (end >= detail.m_Groups.Count())
|
||||
--end;
|
||||
|
||||
if (start == end)
|
||||
return start;
|
||||
|
||||
// Figure out how far we are between start and end...
|
||||
float dist = 0.0f;
|
||||
float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
|
||||
if (dAlpha != 0.0f)
|
||||
{
|
||||
dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
|
||||
}
|
||||
|
||||
// Pick a number, any number...
|
||||
float r = rand() / (float)VALVE_RAND_MAX;
|
||||
|
||||
// When dist == 0, we *always* want start.
|
||||
// When dist == 1, we *always* want end
|
||||
// That's why this logic looks a little reversed
|
||||
return (r > dist) ? start : end;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Selects a detail object
|
||||
//-----------------------------------------------------------------------------
|
||||
static int SelectDetail( DetailObjectGroup_t const& group )
|
||||
{
|
||||
// Pick a number, any number...
|
||||
float r = rand() / (float)VALVE_RAND_MAX;
|
||||
|
||||
// Look through the list of models + pick the one associated with this number
|
||||
for ( int i = 0; i < group.m_Models.Count(); ++i )
|
||||
{
|
||||
if (r <= group.m_Models[i].m_Amount)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Adds a detail dictionary element (expected to oftentimes be shared)
|
||||
//-----------------------------------------------------------------------------
|
||||
static int AddDetailDictLump( const char* pModelName )
|
||||
{
|
||||
DetailObjectDictLump_t dictLump;
|
||||
strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
|
||||
|
||||
for (int i = s_DetailObjectDictLump.Count(); --i >= 0; )
|
||||
{
|
||||
if (!memcmp(&s_DetailObjectDictLump[i], &dictLump, sizeof(dictLump) ))
|
||||
return i;
|
||||
}
|
||||
|
||||
return s_DetailObjectDictLump.AddToTail( dictLump );
|
||||
}
|
||||
|
||||
static int AddDetailSpriteDictLump( const Vector2D *pPos, const Vector2D *pTex )
|
||||
{
|
||||
DetailSpriteDictLump_t dictLump;
|
||||
dictLump.m_UL = pPos[0];
|
||||
dictLump.m_LR = pPos[1];
|
||||
dictLump.m_TexUL = pTex[0];
|
||||
dictLump.m_TexLR = pTex[1];
|
||||
|
||||
for (int i = s_DetailSpriteDictLump.Count(); --i >= 0; )
|
||||
{
|
||||
if (!memcmp(&s_DetailSpriteDictLump[i], &dictLump, sizeof(dictLump) ))
|
||||
return i;
|
||||
}
|
||||
|
||||
return s_DetailSpriteDictLump.AddToTail( dictLump );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Computes the leaf that the detail lies in
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ComputeDetailLeaf( const Vector& pt )
|
||||
{
|
||||
int node = 0;
|
||||
while( node >= 0 )
|
||||
{
|
||||
dnode_t* pNode = &dnodes[node];
|
||||
dplane_t* pPlane = &dplanes[pNode->planenum];
|
||||
|
||||
if (DotProduct(pt, pPlane->normal) < pPlane->dist)
|
||||
node = pNode->children[1];
|
||||
else
|
||||
node = pNode->children[0];
|
||||
}
|
||||
|
||||
return - node - 1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Make sure the details are compiled with static prop
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool IsModelValid( const char* pModelName )
|
||||
{
|
||||
StaticPropLookup_t lookup;
|
||||
lookup.m_ModelName = pModelName;
|
||||
|
||||
int i = s_StaticPropLookup.Find( lookup );
|
||||
if (i != s_StaticPropLookup.InvalidIndex() )
|
||||
return s_StaticPropLookup[i].m_IsValid;
|
||||
|
||||
CUtlBuffer buf;
|
||||
lookup.m_IsValid = LoadStudioModel( pModelName, "detail_prop", buf );
|
||||
if (!lookup.m_IsValid)
|
||||
{
|
||||
Warning("Error loading studio model \"%s\"!\n", pModelName );
|
||||
}
|
||||
|
||||
s_StaticPropLookup.Insert( lookup );
|
||||
return lookup.m_IsValid;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add a detail to the lump.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int s_nDetailOverflow = 0;
|
||||
static void AddDetailToLump( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
|
||||
{
|
||||
Assert( pt.IsValid() && angles.IsValid() );
|
||||
|
||||
// Make sure the model is valid...
|
||||
if (!IsModelValid(pModelName))
|
||||
return;
|
||||
|
||||
if (s_DetailObjectLump.Count() == 65535)
|
||||
{
|
||||
++s_nDetailOverflow;
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert an element into the object dictionary if it aint there...
|
||||
int i = s_DetailObjectLump.AddToTail( );
|
||||
|
||||
DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
|
||||
objectLump.m_DetailModel = AddDetailDictLump( pModelName );
|
||||
VectorCopy( angles, objectLump.m_Angles );
|
||||
VectorCopy( pt, objectLump.m_Origin );
|
||||
objectLump.m_Leaf = ComputeDetailLeaf(pt);
|
||||
objectLump.m_Lighting.r = 255;
|
||||
objectLump.m_Lighting.g = 255;
|
||||
objectLump.m_Lighting.b = 255;
|
||||
objectLump.m_Lighting.exponent = 0;
|
||||
objectLump.m_LightStyles = 0;
|
||||
objectLump.m_LightStyleCount = 0;
|
||||
objectLump.m_Orientation = nOrientation;
|
||||
objectLump.m_Type = DETAIL_PROP_TYPE_MODEL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add a detail sprite to the lump.
|
||||
//-----------------------------------------------------------------------------
|
||||
static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
|
||||
const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
|
||||
int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
|
||||
{
|
||||
// Insert an element into the object dictionary if it aint there...
|
||||
int i = s_DetailObjectLump.AddToTail( );
|
||||
|
||||
if (i >= 65535)
|
||||
{
|
||||
Error( "Error! Too many detail props emitted on this map! (64K max!)n" );
|
||||
}
|
||||
|
||||
DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
|
||||
objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
|
||||
VectorCopy( vecAngles, objectLump.m_Angles );
|
||||
VectorCopy( vecOrigin, objectLump.m_Origin );
|
||||
objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
|
||||
objectLump.m_Lighting.r = 255;
|
||||
objectLump.m_Lighting.g = 255;
|
||||
objectLump.m_Lighting.b = 255;
|
||||
objectLump.m_Lighting.exponent = 0;
|
||||
objectLump.m_LightStyles = 0;
|
||||
objectLump.m_LightStyleCount = 0;
|
||||
objectLump.m_Orientation = nOrientation;
|
||||
objectLump.m_Type = iType;
|
||||
objectLump.m_flScale = flScale;
|
||||
objectLump.m_ShapeAngle = iShapeAngle;
|
||||
objectLump.m_ShapeSize = iShapeSize;
|
||||
objectLump.m_SwayAmount = iSwayAmount;
|
||||
}
|
||||
|
||||
static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
|
||||
{
|
||||
AddDetailSpriteToLump( vecOrigin,
|
||||
vecAngles,
|
||||
model.m_Orientation,
|
||||
model.m_Pos,
|
||||
model.m_Tex,
|
||||
flScale,
|
||||
model.m_Type,
|
||||
model.m_ShapeAngle,
|
||||
model.m_ShapeSize,
|
||||
model.m_SwayAmount );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Got a detail! Place it on the surface...
|
||||
//-----------------------------------------------------------------------------
|
||||
// BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
|
||||
// (only when not in the debugger?)
|
||||
// Printing the values of normal at the bottom of the function fixes it as does
|
||||
// disabling global optimizations.
|
||||
static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
|
||||
{
|
||||
// But only place it on the surface if it meets the angle constraints...
|
||||
float cosAngle = normal.z;
|
||||
|
||||
// Never emit if the angle's too steep
|
||||
if (cosAngle < model.m_MaxCosAngle)
|
||||
return;
|
||||
|
||||
// If it's between min + max, flip a coin...
|
||||
if (cosAngle < model.m_MinCosAngle)
|
||||
{
|
||||
float probability = (cosAngle - model.m_MaxCosAngle) /
|
||||
(model.m_MinCosAngle - model.m_MaxCosAngle);
|
||||
|
||||
float t = rand() / (float)VALVE_RAND_MAX;
|
||||
if (t > probability)
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the orientation of the detail
|
||||
QAngle angles;
|
||||
if (model.m_Flags & MODELFLAG_UPRIGHT)
|
||||
{
|
||||
// If it's upright, we just select a random yaw
|
||||
angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's not upright, so it must conform to the ground. Choose
|
||||
// a random orientation based on the surface normal
|
||||
|
||||
Vector zaxis;
|
||||
VectorCopy( normal, zaxis );
|
||||
VectorNormalize( zaxis );
|
||||
|
||||
// Choose any two arbitrary axes which are perpendicular to the normal
|
||||
Vector xaxis( 1, 0, 0 );
|
||||
if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
|
||||
xaxis.Init( 0, 1, 0 );
|
||||
Vector yaxis;
|
||||
CrossProduct( zaxis, xaxis, yaxis );
|
||||
VectorNormalize( yaxis );
|
||||
CrossProduct( yaxis, zaxis, xaxis );
|
||||
VectorNormalize( xaxis );
|
||||
VMatrix matrix;
|
||||
matrix.SetBasisVectors( xaxis, yaxis, zaxis );
|
||||
matrix.SetTranslation( vec3_origin );
|
||||
|
||||
float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
|
||||
VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
|
||||
matrix = matrix * rot;
|
||||
|
||||
MatrixToAngles( matrix, angles );
|
||||
}
|
||||
|
||||
// FIXME: We may also want a purely random rotation too
|
||||
|
||||
// Insert an element into the object dictionary if it aint there...
|
||||
switch ( model.m_Type )
|
||||
{
|
||||
case DETAIL_PROP_TYPE_MODEL:
|
||||
AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
|
||||
break;
|
||||
|
||||
// Sprites and procedural models made from sprites
|
||||
case DETAIL_PROP_TYPE_SPRITE:
|
||||
default:
|
||||
{
|
||||
float flScale = 1.0f;
|
||||
if ( model.m_flRandomScaleStdDev != 0.0f )
|
||||
{
|
||||
flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
|
||||
}
|
||||
|
||||
AddDetailSpriteToLump( pt, angles, model, flScale );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects on a face
|
||||
//-----------------------------------------------------------------------------
|
||||
static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
|
||||
{
|
||||
if (pFace->numedges < 3)
|
||||
return;
|
||||
|
||||
// We're going to pick a bunch of random points, and then probabilistically
|
||||
// decide whether or not to plant a detail object there.
|
||||
|
||||
// Turn the face into a bunch of polygons, and compute the area of each
|
||||
int* pSurfEdges = &dsurfedges[pFace->firstedge];
|
||||
int vertexIdx = (pSurfEdges[0] < 0);
|
||||
int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
|
||||
dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
|
||||
for (int i = 1; i < pFace->numedges - 1; ++i )
|
||||
{
|
||||
int vertexIdx = (pSurfEdges[i] < 0);
|
||||
dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
|
||||
|
||||
// Compute two triangle edges
|
||||
Vector e1, e2;
|
||||
VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
|
||||
VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
|
||||
|
||||
// Compute the triangle area
|
||||
Vector areaVec;
|
||||
CrossProduct( e1, e2, areaVec );
|
||||
float normalLength = areaVec.Length();
|
||||
float area = 0.5f * normalLength;
|
||||
|
||||
// Compute the number of samples to take
|
||||
int numSamples = area * detail.m_Density * 0.000001;
|
||||
|
||||
// Now take a sample, and randomly place an object there
|
||||
for (int i = 0; i < numSamples; ++i )
|
||||
{
|
||||
// Create a random sample...
|
||||
float u = rand() / (float)VALVE_RAND_MAX;
|
||||
float v = rand() / (float)VALVE_RAND_MAX;
|
||||
if (v > 1.0f - u)
|
||||
{
|
||||
u = 1.0f - u;
|
||||
v = 1.0f - v;
|
||||
assert( u + v <= 1.0f );
|
||||
}
|
||||
|
||||
// Compute alpha
|
||||
float alpha = 1.0f;
|
||||
|
||||
// Select a group based on the alpha value
|
||||
int group = SelectGroup( detail, alpha );
|
||||
|
||||
// Now that we've got a group, choose a detail
|
||||
int model = SelectDetail( detail.m_Groups[group] );
|
||||
if (model < 0)
|
||||
continue;
|
||||
|
||||
// Got a detail! Place it on the surface...
|
||||
Vector pt, normal;
|
||||
VectorMA( pFirstVertex->point, u, e1, pt );
|
||||
VectorMA( pt, v, e2, pt );
|
||||
VectorDivide( areaVec, -normalLength, normal );
|
||||
|
||||
PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects on a face
|
||||
//-----------------------------------------------------------------------------
|
||||
static float ComputeDisplacementFaceArea( dface_t* pFace )
|
||||
{
|
||||
float area = 0.0f;
|
||||
|
||||
// Compute the area of the base face
|
||||
int* pSurfEdges = &dsurfedges[pFace->firstedge];
|
||||
int vertexIdx = (pSurfEdges[0] < 0);
|
||||
int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
|
||||
dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
|
||||
for (int i = 1; i <= 2; ++i )
|
||||
{
|
||||
int vertexIdx = (pSurfEdges[i] < 0);
|
||||
dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
|
||||
|
||||
// Compute two triangle edges
|
||||
Vector e1, e2;
|
||||
VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
|
||||
VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
|
||||
|
||||
// Compute the triangle area
|
||||
Vector areaVec;
|
||||
CrossProduct( e1, e2, areaVec );
|
||||
float normalLength = areaVec.Length();
|
||||
area += 0.5f * normalLength;
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects on a face
|
||||
//-----------------------------------------------------------------------------
|
||||
static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
|
||||
DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
|
||||
{
|
||||
assert(pFace->numedges == 4);
|
||||
|
||||
// We're going to pick a bunch of random points, and then probabilistically
|
||||
// decide whether or not to plant a detail object there.
|
||||
|
||||
// Compute the area of the base face
|
||||
float area = ComputeDisplacementFaceArea( pFace );
|
||||
|
||||
// Compute the number of samples to take
|
||||
int numSamples = area * detail.m_Density * 0.000001;
|
||||
|
||||
// Now take a sample, and randomly place an object there
|
||||
for (int i = 0; i < numSamples; ++i )
|
||||
{
|
||||
// Create a random sample...
|
||||
float u = rand() / (float)VALVE_RAND_MAX;
|
||||
float v = rand() / (float)VALVE_RAND_MAX;
|
||||
|
||||
// Compute alpha
|
||||
float alpha;
|
||||
Vector pt, normal;
|
||||
coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
|
||||
alpha /= 255.0f;
|
||||
|
||||
// Select a group based on the alpha value
|
||||
int group = SelectGroup( detail, alpha );
|
||||
|
||||
// Now that we've got a group, choose a detail
|
||||
int model = SelectDetail( detail.m_Groups[group] );
|
||||
if (model < 0)
|
||||
continue;
|
||||
|
||||
// Got a detail! Place it on the surface...
|
||||
PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Sort detail objects by leaf
|
||||
//-----------------------------------------------------------------------------
|
||||
static int SortFunc( const void *arg1, const void *arg2 )
|
||||
{
|
||||
int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
|
||||
if ( nDelta < 0 )
|
||||
return -1;
|
||||
if ( nDelta > 0 )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects in the lump
|
||||
//-----------------------------------------------------------------------------
|
||||
static void SetLumpData( )
|
||||
{
|
||||
// Sort detail props by leaf
|
||||
qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
|
||||
|
||||
GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
|
||||
if (handle != g_GameLumps.InvalidGameLump())
|
||||
{
|
||||
g_GameLumps.DestroyGameLump(handle);
|
||||
}
|
||||
int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
|
||||
int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
|
||||
int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
|
||||
int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
|
||||
|
||||
handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
|
||||
|
||||
// Serialize the data
|
||||
CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
|
||||
buf.PutInt( s_DetailObjectDictLump.Count() );
|
||||
if (nDictSize)
|
||||
{
|
||||
buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
|
||||
}
|
||||
buf.PutInt( s_DetailSpriteDictLump.Count() );
|
||||
if (nSpriteDictSize)
|
||||
{
|
||||
buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
|
||||
}
|
||||
buf.PutInt( s_DetailObjectLump.Count() );
|
||||
if (nObjSize)
|
||||
{
|
||||
buf.Put( s_DetailObjectLump.Base(), nObjSize );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects in the level
|
||||
//-----------------------------------------------------------------------------
|
||||
void EmitDetailModels()
|
||||
{
|
||||
StartPacifier("Placing detail props : ");
|
||||
|
||||
// Place stuff on each face
|
||||
dface_t* pFace = dfaces;
|
||||
for (int j = 0; j < numfaces; ++j)
|
||||
{
|
||||
UpdatePacifier( (float)j / (float)numfaces );
|
||||
|
||||
// Get at the material associated with this face
|
||||
texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
|
||||
dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
|
||||
|
||||
// Try to get at the material
|
||||
bool found;
|
||||
MaterialSystemMaterial_t handle =
|
||||
FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
|
||||
&found, false );
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
// See if its got any detail objects on it
|
||||
const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
|
||||
if (!pDetailType)
|
||||
continue;
|
||||
|
||||
// Get the detail type...
|
||||
DetailObject_t search;
|
||||
search.m_Name = pDetailType;
|
||||
int objectType = s_DetailObjectDict.Find(search);
|
||||
if (objectType < 0)
|
||||
{
|
||||
Warning("Material %s uses unknown detail object type %s!\n",
|
||||
TexDataStringTable_GetString( pTexData->nameStringTableID ),
|
||||
pDetailType);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Emit objects on a particular face
|
||||
DetailObject_t& detail = s_DetailObjectDict[objectType];
|
||||
|
||||
// Initialize the Random Number generators for detail prop placement based on the hammer Face num.
|
||||
int detailpropseed = dfaceids[j].hammerfaceid;
|
||||
#ifdef WARNSEEDNUMBER
|
||||
Warning( "[%d]\n",detailpropseed );
|
||||
#endif
|
||||
srand( detailpropseed );
|
||||
RandomSeed( detailpropseed );
|
||||
|
||||
if (pFace[j].dispinfo < 0)
|
||||
{
|
||||
EmitDetailObjectsOnFace( &pFace[j], detail );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
|
||||
CCoreDispInfo coreDispInfo;
|
||||
DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
|
||||
|
||||
EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
|
||||
}
|
||||
}
|
||||
|
||||
// Emit specifically specified detail props
|
||||
Vector origin;
|
||||
QAngle angles;
|
||||
Vector2D pos[2];
|
||||
Vector2D tex[2];
|
||||
for (int i = 0; i < num_entities; ++i)
|
||||
{
|
||||
char* pEntity = ValueForKey(&entities[i], "classname");
|
||||
if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
|
||||
{
|
||||
GetVectorForKey( &entities[i], "origin", origin );
|
||||
GetAnglesForKey( &entities[i], "angles", angles );
|
||||
char* pModelName = ValueForKey( &entities[i], "model" );
|
||||
int nOrientation = IntForKey( &entities[i], "detailOrientation" );
|
||||
|
||||
AddDetailToLump( pModelName, origin, angles, nOrientation );
|
||||
|
||||
// strip this ent from the .bsp file
|
||||
entities[i].epairs = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(pEntity, "prop_detail_sprite"))
|
||||
{
|
||||
GetVectorForKey( &entities[i], "origin", origin );
|
||||
GetAnglesForKey( &entities[i], "angles", angles );
|
||||
int nOrientation = IntForKey( &entities[i], "detailOrientation" );
|
||||
GetVector2DForKey( &entities[i], "position_ul", pos[0] );
|
||||
GetVector2DForKey( &entities[i], "position_lr", pos[1] );
|
||||
GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
|
||||
GetVector2DForKey( &entities[i], "tex_size", tex[1] );
|
||||
float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
|
||||
|
||||
tex[1].x += tex[0].x - 0.5f;
|
||||
tex[1].y += tex[0].y - 0.5f;
|
||||
tex[0].x += 0.5f;
|
||||
tex[0].y += 0.5f;
|
||||
tex[0] /= flTextureSize;
|
||||
tex[1] /= flTextureSize;
|
||||
|
||||
AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
|
||||
|
||||
// strip this ent from the .bsp file
|
||||
entities[i].epairs = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
EndPacifier( true );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Detail Objects in the level
|
||||
//-----------------------------------------------------------------------------
|
||||
void EmitDetailObjects()
|
||||
{
|
||||
EmitDetailModels();
|
||||
|
||||
// Done! Now lets add the lumps (destroy previous ones)
|
||||
SetLumpData( );
|
||||
|
||||
if ( s_nDetailOverflow != 0 )
|
||||
{
|
||||
Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
|
||||
}
|
||||
}
|
||||
359
mp/src/utils/vbsp/disp_ivp.cpp
Normal file
359
mp/src/utils/vbsp/disp_ivp.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "vbsp.h"
|
||||
#include "disp_vbsp.h"
|
||||
#include "builddisp.h"
|
||||
#include "disp_common.h"
|
||||
#include "ivp.h"
|
||||
#include "disp_ivp.h"
|
||||
#include "vphysics_interface.h"
|
||||
#include "vphysics/virtualmesh.h"
|
||||
#include "utlrbtree.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "materialpatch.h"
|
||||
|
||||
struct disp_grid_t
|
||||
{
|
||||
int gridIndex;
|
||||
CUtlVector<int> dispList;
|
||||
};
|
||||
|
||||
static CUtlVector<disp_grid_t> gDispGridList;
|
||||
|
||||
|
||||
|
||||
disp_grid_t &FindOrInsertGrid( int gridIndex )
|
||||
{
|
||||
// linear search is slow, but only a few grids will be present
|
||||
for ( int i = gDispGridList.Count()-1; i >= 0; i-- )
|
||||
{
|
||||
if ( gDispGridList[i].gridIndex == gridIndex )
|
||||
{
|
||||
return gDispGridList[i];
|
||||
}
|
||||
}
|
||||
int index = gDispGridList.AddToTail();
|
||||
gDispGridList[index].gridIndex = gridIndex;
|
||||
|
||||
// must be empty
|
||||
Assert( gDispGridList[index].dispList.Count() == 0 );
|
||||
|
||||
return gDispGridList[index];
|
||||
}
|
||||
|
||||
// UNDONE: Tune these or adapt them to map size or triangle count?
|
||||
#define DISP_GRID_SIZEX 4096
|
||||
#define DISP_GRID_SIZEY 4096
|
||||
#define DISP_GRID_SIZEZ 8192
|
||||
|
||||
int Disp_GridIndex( CCoreDispInfo *pDispInfo )
|
||||
{
|
||||
// quick hash the center into the grid and put the whole terrain in that grid
|
||||
Vector mins, maxs;
|
||||
pDispInfo->GetNode(0)->GetBoundingBox( mins, maxs );
|
||||
Vector center;
|
||||
center = 0.5 * (mins + maxs);
|
||||
// make sure it's positive
|
||||
center += Vector(MAX_COORD_INTEGER,MAX_COORD_INTEGER,MAX_COORD_INTEGER);
|
||||
int gridX = center.x / DISP_GRID_SIZEX;
|
||||
int gridY = center.y / DISP_GRID_SIZEY;
|
||||
int gridZ = center.z / DISP_GRID_SIZEZ;
|
||||
|
||||
gridX &= 0xFF;
|
||||
gridY &= 0xFF;
|
||||
gridZ &= 0xFF;
|
||||
return MAKEID( gridX, gridY, gridZ, 0 );
|
||||
}
|
||||
|
||||
void AddToGrid( int gridIndex, int dispIndex )
|
||||
{
|
||||
disp_grid_t &grid = FindOrInsertGrid( gridIndex );
|
||||
grid.dispList.AddToTail( dispIndex );
|
||||
}
|
||||
|
||||
MaterialSystemMaterial_t GetMatIDFromDisp( mapdispinfo_t *pMapDisp )
|
||||
{
|
||||
texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
MaterialSystemMaterial_t matID = FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
|
||||
return matID;
|
||||
}
|
||||
|
||||
// check this and disable virtual mesh if in use
|
||||
bool Disp_HasPower4Displacements()
|
||||
{
|
||||
for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
|
||||
{
|
||||
if ( g_CoreDispInfos[i]->GetPower() > 3 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// adds all displacement faces as a series of convex objects
|
||||
// UNDONE: Only add the displacements for this model?
|
||||
void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask)
|
||||
{
|
||||
int dispIndex;
|
||||
|
||||
// Add each displacement to the grid hash
|
||||
for ( dispIndex = 0; dispIndex < g_CoreDispInfos.Count(); dispIndex++ )
|
||||
{
|
||||
CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
|
||||
|
||||
// not solid for this pass
|
||||
if ( !(pMapDisp->contents & contentsMask) )
|
||||
continue;
|
||||
|
||||
int gridIndex = Disp_GridIndex( pDispInfo );
|
||||
AddToGrid( gridIndex, dispIndex );
|
||||
}
|
||||
|
||||
// now make a polysoup for the terrain in each grid
|
||||
for ( int grid = 0; grid < gDispGridList.Count(); grid++ )
|
||||
{
|
||||
int triCount = 0;
|
||||
CPhysPolysoup *pTerrainPhysics = physcollision->PolysoupCreate();
|
||||
|
||||
// iterate the displacements in this grid
|
||||
for ( int listIndex = 0; listIndex < gDispGridList[grid].dispList.Count(); listIndex++ )
|
||||
{
|
||||
dispIndex = gDispGridList[grid].dispList[listIndex];
|
||||
CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
|
||||
|
||||
// Get the material id.
|
||||
MaterialSystemMaterial_t matID = GetMatIDFromDisp( pMapDisp );
|
||||
|
||||
// Build a triangle list. This shares the tesselation code with the engine.
|
||||
CUtlVector<unsigned short> indices;
|
||||
CVBSPTesselateHelper helper;
|
||||
helper.m_pIndices = &indices;
|
||||
helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
|
||||
helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
|
||||
|
||||
::TesselateDisplacement( &helper );
|
||||
|
||||
Assert( indices.Count() > 0 );
|
||||
Assert( indices.Count() % 3 == 0 ); // Make sure indices are a multiple of 3.
|
||||
int nTriCount = indices.Count() / 3;
|
||||
triCount += nTriCount;
|
||||
if ( triCount >= 65536 )
|
||||
{
|
||||
// don't put more than 64K tris in any single collision model
|
||||
CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
|
||||
if ( pCollide )
|
||||
{
|
||||
collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
|
||||
}
|
||||
// Throw this polysoup away and start over for the remaining triangles
|
||||
physcollision->PolysoupDestroy( pTerrainPhysics );
|
||||
pTerrainPhysics = physcollision->PolysoupCreate();
|
||||
triCount = nTriCount;
|
||||
}
|
||||
Vector tmpVerts[3];
|
||||
for ( int iTri = 0; iTri < nTriCount; ++iTri )
|
||||
{
|
||||
float flAlphaTotal = 0.0f;
|
||||
for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
|
||||
{
|
||||
pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
|
||||
flAlphaTotal += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
|
||||
}
|
||||
|
||||
int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
|
||||
if ( flAlphaTotal > DISP_ALPHA_PROP_DELTA )
|
||||
{
|
||||
int nProp2 = GetSurfaceProperties2( matID, "surfaceprop2" );
|
||||
if ( nProp2 != -1 )
|
||||
{
|
||||
nProp = nProp2;
|
||||
}
|
||||
}
|
||||
int nMaterialIndex = RemapWorldMaterial( nProp );
|
||||
physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
|
||||
}
|
||||
}
|
||||
|
||||
// convert the whole grid's polysoup to a collide and store in the collision list
|
||||
CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
|
||||
if ( pCollide )
|
||||
{
|
||||
collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
|
||||
}
|
||||
// now that we have the collide, we're done with the soup
|
||||
physcollision->PolysoupDestroy( pTerrainPhysics );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CDispMeshEvent : public IVirtualMeshEvent
|
||||
{
|
||||
public:
|
||||
CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
|
||||
virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
|
||||
virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
|
||||
virtual void GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList );
|
||||
|
||||
CUtlVector<Vector> m_verts;
|
||||
unsigned short *m_pIndices;
|
||||
int m_indexCount;
|
||||
};
|
||||
|
||||
CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
|
||||
{
|
||||
m_pIndices = pIndices;
|
||||
m_indexCount = indexCount;
|
||||
int maxIndex = 0;
|
||||
for ( int i = 0; i < indexCount; i++ )
|
||||
{
|
||||
if ( pIndices[i] > maxIndex )
|
||||
{
|
||||
maxIndex = pIndices[i];
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < indexCount/2; i++ )
|
||||
{
|
||||
V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
|
||||
}
|
||||
int count = maxIndex + 1;
|
||||
m_verts.SetCount( count );
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
m_verts[i] = pDispInfo->GetVert(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
pList->pVerts = m_verts.Base();
|
||||
pList->indexCount = m_indexCount;
|
||||
pList->triangleCount = m_indexCount/3;
|
||||
pList->vertexCount = m_verts.Count();
|
||||
pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
|
||||
pList->pHull = NULL;
|
||||
int indexMax = ARRAYSIZE(pList->indices);
|
||||
int indexCount = min(m_indexCount, indexMax);
|
||||
Assert(m_indexCount < indexMax);
|
||||
Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
ClearBounds( *pMins, *pMaxs );
|
||||
for ( int i = 0; i < m_verts.Count(); i++ )
|
||||
{
|
||||
AddPointToBounds( m_verts[i], *pMins, *pMaxs );
|
||||
}
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
pList->triangleCount = m_indexCount/3;
|
||||
int indexMax = ARRAYSIZE(pList->triangleIndices);
|
||||
int indexCount = min(m_indexCount, indexMax);
|
||||
Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
|
||||
Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
|
||||
}
|
||||
|
||||
void Disp_BuildVirtualMesh( int contentsMask )
|
||||
{
|
||||
CUtlVector<CPhysCollide *> virtualMeshes;
|
||||
virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
|
||||
for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
|
||||
{
|
||||
CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
|
||||
|
||||
virtualMeshes[i] = NULL;
|
||||
// not solid for this pass
|
||||
if ( !(pMapDisp->contents & contentsMask) )
|
||||
continue;
|
||||
|
||||
// Build a triangle list. This shares the tesselation code with the engine.
|
||||
CUtlVector<unsigned short> indices;
|
||||
CVBSPTesselateHelper helper;
|
||||
helper.m_pIndices = &indices;
|
||||
helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
|
||||
helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
|
||||
|
||||
::TesselateDisplacement( &helper );
|
||||
|
||||
// validate the collision data
|
||||
if ( 1 )
|
||||
{
|
||||
int triCount = indices.Count() / 3;
|
||||
for ( int j = 0; j < triCount; j++ )
|
||||
{
|
||||
int index = j * 3;
|
||||
Vector v0 = pDispInfo->GetVert( indices[index+0] );
|
||||
Vector v1 = pDispInfo->GetVert( indices[index+1] );
|
||||
Vector v2 = pDispInfo->GetVert( indices[index+2] );
|
||||
if ( v0 == v1 || v1 == v2 || v2 == v0 )
|
||||
{
|
||||
Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
|
||||
texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
|
||||
Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
|
||||
virtualmeshparams_t params;
|
||||
params.buildOuterHull = true;
|
||||
params.pMeshEventHandler = &meshHandler;
|
||||
params.userData = &meshHandler;
|
||||
virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
|
||||
}
|
||||
unsigned int totalSize = 0;
|
||||
CUtlBuffer buf;
|
||||
dphysdisp_t header;
|
||||
header.numDisplacements = g_CoreDispInfos.Count();
|
||||
buf.PutObjects( &header );
|
||||
|
||||
CUtlVector<char> dispBuf;
|
||||
for ( int i = 0; i < header.numDisplacements; i++ )
|
||||
{
|
||||
if ( virtualMeshes[i] )
|
||||
{
|
||||
unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
|
||||
totalSize += testSize;
|
||||
buf.PutShort( testSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.PutShort( -1 );
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < header.numDisplacements; i++ )
|
||||
{
|
||||
if ( virtualMeshes[i] )
|
||||
{
|
||||
unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
|
||||
dispBuf.RemoveAll();
|
||||
dispBuf.EnsureCount(testSize);
|
||||
|
||||
unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
|
||||
Assert( outSize == testSize );
|
||||
buf.Put( dispBuf.Base(), outSize );
|
||||
}
|
||||
}
|
||||
g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
|
||||
Assert( buf.TellMaxPut() == g_PhysDispSize );
|
||||
g_PhysDispSize = buf.TellMaxPut();
|
||||
g_pPhysDisp = new byte[g_PhysDispSize];
|
||||
Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
|
||||
}
|
||||
|
||||
49
mp/src/utils/vbsp/disp_ivp.h
Normal file
49
mp/src/utils/vbsp/disp_ivp.h
Normal file
@@ -0,0 +1,49 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef DISP_IVP_H
|
||||
#define DISP_IVP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "utlvector.h"
|
||||
#include "../../public/disp_tesselate.h"
|
||||
|
||||
|
||||
class CPhysCollisionEntry;
|
||||
struct dmodel_t;
|
||||
|
||||
|
||||
// This provides the template functions that the engine's tesselation code needs
|
||||
// so we can share the code in VBSP.
|
||||
class CVBSPTesselateHelper : public CBaseTesselateHelper
|
||||
{
|
||||
public:
|
||||
void EndTriangle()
|
||||
{
|
||||
m_pIndices->AddToTail( m_TempIndices[0] );
|
||||
m_pIndices->AddToTail( m_TempIndices[1] );
|
||||
m_pIndices->AddToTail( m_TempIndices[2] );
|
||||
}
|
||||
|
||||
DispNodeInfo_t& GetNodeInfo( int iNodeBit )
|
||||
{
|
||||
// VBSP doesn't care about these. Give it back something to play with.
|
||||
static DispNodeInfo_t dummy;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
public:
|
||||
CUtlVector<unsigned short> *m_pIndices;
|
||||
};
|
||||
|
||||
|
||||
extern void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask );
|
||||
extern void Disp_BuildVirtualMesh( int contentsMask );
|
||||
extern bool Disp_HasPower4Displacements();
|
||||
|
||||
#endif // DISP_IVP_H
|
||||
675
mp/src/utils/vbsp/disp_vbsp.cpp
Normal file
675
mp/src/utils/vbsp/disp_vbsp.cpp
Normal file
@@ -0,0 +1,675 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $Workfile: $
|
||||
// $Date: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "disp_vbsp.h"
|
||||
#include "tier0/dbg.h"
|
||||
#include "vbsp.h"
|
||||
#include "mstristrip.h"
|
||||
#include "writebsp.h"
|
||||
#include "pacifier.h"
|
||||
#include "disp_ivp.h"
|
||||
#include "builddisp.h"
|
||||
#include "mathlib/vector.h"
|
||||
|
||||
// map displacement info -- runs parallel to the dispinfos struct
|
||||
int nummapdispinfo = 0;
|
||||
mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
|
||||
|
||||
CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Computes the bounds for a disp info
|
||||
//-----------------------------------------------------------------------------
|
||||
void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs )
|
||||
{
|
||||
CDispBox box;
|
||||
|
||||
// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[dispinfo];
|
||||
|
||||
CCoreDispInfo coreDispInfo;
|
||||
DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
|
||||
|
||||
GetDispBox( &coreDispInfo, box );
|
||||
mins = box.m_Min;
|
||||
maxs = box.m_Max;
|
||||
}
|
||||
|
||||
// Gets the barycentric coordinates of the position on the triangle where the lightmap
|
||||
// coordinates are equal to lmCoords. This always generates the coordinates but it
|
||||
// returns false if the point containing them does not lie inside the triangle.
|
||||
bool GetBarycentricCoordsFromLightmapCoords( Vector2D tri[3], Vector2D const &lmCoords, float bcCoords[3] )
|
||||
{
|
||||
GetBarycentricCoords2D( tri[0], tri[1], tri[2], lmCoords, bcCoords );
|
||||
|
||||
return
|
||||
(bcCoords[0] >= 0.0f && bcCoords[0] <= 1.0f) &&
|
||||
(bcCoords[1] >= 0.0f && bcCoords[1] <= 1.0f) &&
|
||||
(bcCoords[2] >= 0.0f && bcCoords[2] <= 1.0f);
|
||||
}
|
||||
|
||||
|
||||
bool FindTriIndexMapByUV( CCoreDispInfo *pCoreDisp, Vector2D const &lmCoords,
|
||||
int &iTriangle, float flBarycentric[3] )
|
||||
{
|
||||
const CPowerInfo *pPowerInfo = GetPowerInfo( pCoreDisp->GetPower() );
|
||||
|
||||
// Search all the triangles..
|
||||
int nTriCount= pCoreDisp->GetTriCount();
|
||||
for ( int iTri = 0; iTri < nTriCount; ++iTri )
|
||||
{
|
||||
unsigned short iVerts[3];
|
||||
// pCoreDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] );
|
||||
CTriInfo *pTri = &pPowerInfo->m_pTriInfos[iTri];
|
||||
iVerts[0] = pTri->m_Indices[0];
|
||||
iVerts[1] = pTri->m_Indices[1];
|
||||
iVerts[2] = pTri->m_Indices[2];
|
||||
|
||||
// Get this triangle's UVs.
|
||||
Vector2D vecUV[3];
|
||||
for ( int iCoord = 0; iCoord < 3; ++iCoord )
|
||||
{
|
||||
pCoreDisp->GetLuxelCoord( 0, iVerts[iCoord], vecUV[iCoord] );
|
||||
}
|
||||
|
||||
// See if the passed-in UVs are in this triangle's UVs.
|
||||
if( GetBarycentricCoordsFromLightmapCoords( vecUV, lmCoords, flBarycentric ) )
|
||||
{
|
||||
iTriangle = iTri;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CalculateLightmapSamplePositions( CCoreDispInfo *pCoreDispInfo, const dface_t *pFace, CUtlVector<unsigned char> &out )
|
||||
{
|
||||
int width = pFace->m_LightmapTextureSizeInLuxels[0] + 1;
|
||||
int height = pFace->m_LightmapTextureSizeInLuxels[1] + 1;
|
||||
|
||||
// For each lightmap sample, find the triangle it sits in.
|
||||
Vector2D lmCoords;
|
||||
for( int y=0; y < height; y++ )
|
||||
{
|
||||
lmCoords.y = y + 0.5f;
|
||||
|
||||
for( int x=0; x < width; x++ )
|
||||
{
|
||||
lmCoords.x = x + 0.5f;
|
||||
|
||||
float flBarycentric[3];
|
||||
int iTri;
|
||||
|
||||
if( FindTriIndexMapByUV( pCoreDispInfo, lmCoords, iTri, flBarycentric ) )
|
||||
{
|
||||
if( iTri < 255 )
|
||||
{
|
||||
out.AddToTail( iTri );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.AddToTail( 255 );
|
||||
out.AddToTail( iTri - 255 );
|
||||
}
|
||||
|
||||
out.AddToTail( (unsigned char)( flBarycentric[0] * 255.9f ) );
|
||||
out.AddToTail( (unsigned char)( flBarycentric[1] * 255.9f ) );
|
||||
out.AddToTail( (unsigned char)( flBarycentric[2] * 255.9f ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.AddToTail( 0 );
|
||||
out.AddToTail( 0 );
|
||||
out.AddToTail( 0 );
|
||||
out.AddToTail( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
int GetDispInfoEntityNum( mapdispinfo_t *pDisp )
|
||||
{
|
||||
return pDisp->entitynum;
|
||||
}
|
||||
|
||||
// Setup a CCoreDispInfo given a mapdispinfo_t.
|
||||
// If pFace is non-NULL, then lightmap texture coordinates will be generated.
|
||||
void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp, CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos )
|
||||
{
|
||||
winding_t *pWinding = pMapDisp->face.originalface->winding;
|
||||
|
||||
Assert( pWinding->numpoints == 4 );
|
||||
|
||||
//
|
||||
// set initial surface data
|
||||
//
|
||||
CCoreDispSurface *pSurf = pCoreDispInfo->GetSurface();
|
||||
|
||||
texinfo_t *pTexInfo = &texinfo[ pMapDisp->face.texinfo ];
|
||||
Assert( pTexInfo != NULL );
|
||||
|
||||
// init material contents
|
||||
pMapDisp->contents = pMapDisp->face.contents;
|
||||
if (!(pMapDisp->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
|
||||
{
|
||||
pMapDisp->contents |= CONTENTS_SOLID;
|
||||
}
|
||||
|
||||
pSurf->SetContents( pMapDisp->contents );
|
||||
|
||||
// Calculate the lightmap coordinates.
|
||||
Vector2D tCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)};
|
||||
if( pFace )
|
||||
{
|
||||
Assert( pFace->numedges == 4 );
|
||||
|
||||
Vector pt[4];
|
||||
for( int i=0; i < 4; i++ )
|
||||
pt[i] = pWinding->p[i];
|
||||
|
||||
int zeroOffset[2] = {0,0};
|
||||
CalcTextureCoordsAtPoints(
|
||||
pTexInfo->textureVecsTexelsPerWorldUnits,
|
||||
zeroOffset,
|
||||
pt,
|
||||
4,
|
||||
tCoords );
|
||||
}
|
||||
|
||||
//
|
||||
// set face point data ...
|
||||
//
|
||||
pSurf->SetPointCount( 4 );
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
// position
|
||||
pSurf->SetPoint( i, pWinding->p[i] );
|
||||
pSurf->SetTexCoord( i, tCoords[i] );
|
||||
}
|
||||
|
||||
// reset surface given start info
|
||||
pSurf->SetPointStart( pMapDisp->startPosition );
|
||||
pSurf->FindSurfPointStartIndex();
|
||||
pSurf->AdjustSurfPointData();
|
||||
|
||||
// Set the luxel coordinates on the base displacement surface.
|
||||
Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
|
||||
int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
|
||||
Vector vecU( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
|
||||
Vector vecV( pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1],
|
||||
pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
|
||||
bool bSwap = pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
|
||||
|
||||
// Set the face m_LightmapExtents
|
||||
if ( pFace )
|
||||
{
|
||||
pFace->m_LightmapTextureSizeInLuxels[0] = pSurf->GetLuxelU();
|
||||
pFace->m_LightmapTextureSizeInLuxels[1] = pSurf->GetLuxelV();
|
||||
if ( bSwap )
|
||||
{
|
||||
if ( pSwappedTexInfos[ pMapDisp->face.texinfo ] < 0 )
|
||||
{
|
||||
// Create a new texinfo to hold the swapped data.
|
||||
// We must do this because other surfaces may want the non-swapped data
|
||||
// This fixes a lighting bug in d2_prison_08 where many non-displacement surfaces
|
||||
// were pitch black, in addition to bugs in other maps I bet.
|
||||
|
||||
// NOTE: Copy here because adding a texinfo could realloc.
|
||||
texinfo_t temp = *pTexInfo;
|
||||
memcpy( temp.lightmapVecsLuxelsPerWorldUnits[0], pTexInfo->lightmapVecsLuxelsPerWorldUnits[1], 4 * sizeof(float) );
|
||||
memcpy( temp.lightmapVecsLuxelsPerWorldUnits[1], pTexInfo->lightmapVecsLuxelsPerWorldUnits[0], 4 * sizeof(float) );
|
||||
temp.lightmapVecsLuxelsPerWorldUnits[1][0] *= -1.0f;
|
||||
temp.lightmapVecsLuxelsPerWorldUnits[1][1] *= -1.0f;
|
||||
temp.lightmapVecsLuxelsPerWorldUnits[1][2] *= -1.0f;
|
||||
temp.lightmapVecsLuxelsPerWorldUnits[1][3] *= -1.0f;
|
||||
pSwappedTexInfos[ pMapDisp->face.texinfo ] = texinfo.AddToTail( temp );
|
||||
}
|
||||
pMapDisp->face.texinfo = pSwappedTexInfos[ pMapDisp->face.texinfo ];
|
||||
}
|
||||
|
||||
// NOTE: This is here to help future-proof code, since there are codepaths where
|
||||
// pTexInfo can be made invalid (texinfo.AddToTail above).
|
||||
pTexInfo = NULL;
|
||||
}
|
||||
|
||||
// Setup the displacement vectors and offsets.
|
||||
int size = ( ( ( 1 << pMapDisp->power ) + 1 ) * ( ( 1 << pMapDisp->power ) + 1 ) );
|
||||
|
||||
Vector vectorDisps[2048];
|
||||
float dispDists[2048];
|
||||
Assert( size < sizeof(vectorDisps)/sizeof(vectorDisps[0]) );
|
||||
|
||||
for( int j = 0; j < size; j++ )
|
||||
{
|
||||
Vector v;
|
||||
float dist;
|
||||
|
||||
VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
|
||||
VectorAdd( v, pMapDisp->vectorOffsets[j], v );
|
||||
|
||||
dist = VectorLength( v );
|
||||
VectorNormalize( v );
|
||||
|
||||
vectorDisps[j] = v;
|
||||
dispDists[j] = dist;
|
||||
}
|
||||
|
||||
|
||||
// Use CCoreDispInfo to setup the actual vertex positions.
|
||||
pCoreDispInfo->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle,
|
||||
pMapDisp->alphaValues, vectorDisps, dispDists );
|
||||
pCoreDispInfo->Create();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void EmitInitialDispInfos( void )
|
||||
{
|
||||
int i;
|
||||
mapdispinfo_t *pMapDisp;
|
||||
ddispinfo_t *pDisp;
|
||||
Vector v;
|
||||
|
||||
// Calculate the total number of verts.
|
||||
int nTotalVerts = 0;
|
||||
int nTotalTris = 0;
|
||||
for ( i=0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
|
||||
nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power );
|
||||
}
|
||||
|
||||
// Clear the output arrays..
|
||||
g_dispinfo.Purge();
|
||||
g_dispinfo.SetSize( nummapdispinfo );
|
||||
g_DispVerts.SetSize( nTotalVerts );
|
||||
g_DispTris.SetSize( nTotalTris );
|
||||
|
||||
int iCurVert = 0;
|
||||
int iCurTri = 0;
|
||||
for( i = 0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
pDisp = &g_dispinfo[i];
|
||||
pMapDisp = &mapdispinfo[i];
|
||||
|
||||
CDispVert *pOutVerts = &g_DispVerts[iCurVert];
|
||||
CDispTri *pOutTris = &g_DispTris[iCurTri];
|
||||
|
||||
// Setup the vert pointers.
|
||||
pDisp->m_iDispVertStart = iCurVert;
|
||||
pDisp->m_iDispTriStart = iCurTri;
|
||||
iCurVert += NUM_DISP_POWER_VERTS( pMapDisp->power );
|
||||
iCurTri += NUM_DISP_POWER_TRIS( pMapDisp->power );
|
||||
|
||||
//
|
||||
// save power, minimum tesselation, and smoothing angle
|
||||
//
|
||||
pDisp->power = pMapDisp->power;
|
||||
|
||||
// If the high bit is set - this is FLAGS!
|
||||
pDisp->minTess = pMapDisp->flags;
|
||||
pDisp->minTess |= 0x80000000;
|
||||
// pDisp->minTess = pMapDisp->minTess;
|
||||
pDisp->smoothingAngle = pMapDisp->smoothingAngle;
|
||||
pDisp->m_iMapFace = (unsigned short)-2;
|
||||
|
||||
// get surface contents
|
||||
pDisp->contents = pMapDisp->face.contents;
|
||||
|
||||
pDisp->startPosition = pMapDisp->startPosition;
|
||||
//
|
||||
// add up the vectorOffsets and displacements, save alphas (per vertex)
|
||||
//
|
||||
int size = ( ( ( 1 << pDisp->power ) + 1 ) * ( ( 1 << pDisp->power ) + 1 ) );
|
||||
for( int j = 0; j < size; j++ )
|
||||
{
|
||||
VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
|
||||
VectorAdd( v, pMapDisp->vectorOffsets[j], v );
|
||||
|
||||
float dist = VectorLength( v );
|
||||
VectorNormalize( v );
|
||||
|
||||
VectorCopy( v, pOutVerts[j].m_vVector );
|
||||
pOutVerts[j].m_flDist = dist;
|
||||
|
||||
pOutVerts[j].m_flAlpha = pMapDisp->alphaValues[j];
|
||||
}
|
||||
|
||||
int nTriCount = ( (1 << (pDisp->power)) * (1 << (pDisp->power)) * 2 );
|
||||
for ( int iTri = 0; iTri< nTriCount; ++iTri )
|
||||
{
|
||||
pOutTris[iTri].m_uiTags = pMapDisp->triTags[iTri];
|
||||
}
|
||||
//===================================================================
|
||||
//===================================================================
|
||||
|
||||
// save the index for face data reference
|
||||
pMapDisp->face.dispinfo = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExportCoreDispNeighborData( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
|
||||
{
|
||||
for ( int i=0; i < 4; i++ )
|
||||
{
|
||||
pOut->m_EdgeNeighbors[i] = *pIn->GetEdgeNeighbor( i );
|
||||
pOut->m_CornerNeighbors[i] = *pIn->GetCornerNeighbors( i );
|
||||
}
|
||||
}
|
||||
|
||||
void ExportNeighborData( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
|
||||
{
|
||||
FindNeighboringDispSurfs( ppListBase, listSize );
|
||||
|
||||
// Export the neighbor data.
|
||||
for ( int i=0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
ExportCoreDispNeighborData( g_CoreDispInfos[i], &pBSPDispInfos[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExportCoreDispAllowedVertList( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
|
||||
{
|
||||
ErrorIfNot(
|
||||
pIn->GetAllowedVerts().GetNumDWords() == sizeof( pOut->m_AllowedVerts ) / 4,
|
||||
("ExportCoreDispAllowedVertList: size mismatch")
|
||||
);
|
||||
for ( int i=0; i < pIn->GetAllowedVerts().GetNumDWords(); i++ )
|
||||
pOut->m_AllowedVerts[i] = pIn->GetAllowedVerts().GetDWord( i );
|
||||
}
|
||||
|
||||
|
||||
void ExportAllowedVertLists( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
|
||||
{
|
||||
SetupAllowedVerts( ppListBase, listSize );
|
||||
|
||||
for ( int i=0; i < listSize; i++ )
|
||||
{
|
||||
ExportCoreDispAllowedVertList( ppListBase[i], &pBSPDispInfos[i] );
|
||||
}
|
||||
}
|
||||
|
||||
bool FindEnclosingTri(
|
||||
const Vector2D &vert,
|
||||
CUtlVector<Vector2D> &vertCoords,
|
||||
CUtlVector<unsigned short> &indices,
|
||||
int *pStartVert,
|
||||
float bcCoords[3] )
|
||||
{
|
||||
for ( int i=0; i < indices.Count(); i += 3 )
|
||||
{
|
||||
GetBarycentricCoords2D(
|
||||
vertCoords[indices[i+0]],
|
||||
vertCoords[indices[i+1]],
|
||||
vertCoords[indices[i+2]],
|
||||
vert,
|
||||
bcCoords );
|
||||
|
||||
if ( bcCoords[0] >= 0 && bcCoords[0] <= 1 &&
|
||||
bcCoords[1] >= 0 && bcCoords[1] <= 1 &&
|
||||
bcCoords[2] >= 0 && bcCoords[2] <= 1 )
|
||||
{
|
||||
*pStartVert = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SnapRemainingVertsToSurface( CCoreDispInfo *pCoreDisp, ddispinfo_t *pDispInfo )
|
||||
{
|
||||
// First, tesselate the displacement.
|
||||
CUtlVector<unsigned short> indices;
|
||||
CVBSPTesselateHelper helper;
|
||||
helper.m_pIndices = &indices;
|
||||
helper.m_pActiveVerts = pCoreDisp->GetAllowedVerts().Base();
|
||||
helper.m_pPowerInfo = pCoreDisp->GetPowerInfo();
|
||||
::TesselateDisplacement( &helper );
|
||||
|
||||
// Figure out which verts are actually referenced in the tesselation.
|
||||
CUtlVector<bool> vertsTouched;
|
||||
vertsTouched.SetSize( pCoreDisp->GetSize() );
|
||||
memset( vertsTouched.Base(), 0, sizeof( bool ) * vertsTouched.Count() );
|
||||
|
||||
for ( int i=0; i < indices.Count(); i++ )
|
||||
vertsTouched[ indices[i] ] = true;
|
||||
|
||||
// Generate 2D floating point coordinates for each vertex. We use these to generate
|
||||
// barycentric coordinates, and the scale doesn't matter.
|
||||
CUtlVector<Vector2D> vertCoords;
|
||||
vertCoords.SetSize( pCoreDisp->GetSize() );
|
||||
for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
|
||||
{
|
||||
for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
|
||||
vertCoords[y*pCoreDisp->GetWidth()+x].Init( x, y );
|
||||
}
|
||||
|
||||
// Now, for each vert not touched, snap its position to the main surface.
|
||||
for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
|
||||
{
|
||||
for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
|
||||
{
|
||||
int index = y * pCoreDisp->GetWidth() + x;
|
||||
if ( !( vertsTouched[index] ) )
|
||||
{
|
||||
float bcCoords[3];
|
||||
int iStartVert = -1;
|
||||
if ( FindEnclosingTri( vertCoords[index], vertCoords, indices, &iStartVert, bcCoords ) )
|
||||
{
|
||||
const Vector &A = pCoreDisp->GetVert( indices[iStartVert+0] );
|
||||
const Vector &B = pCoreDisp->GetVert( indices[iStartVert+1] );
|
||||
const Vector &C = pCoreDisp->GetVert( indices[iStartVert+2] );
|
||||
Vector vNewPos = A*bcCoords[0] + B*bcCoords[1] + C*bcCoords[2];
|
||||
|
||||
// This is kind of cheesy, but it gets the job done. Since the CDispVerts store the
|
||||
// verts relative to some other offset, we'll just offset their position instead
|
||||
// of setting it directly.
|
||||
Vector vOffset = vNewPos - pCoreDisp->GetVert( index );
|
||||
|
||||
// Modify the mapfile vert.
|
||||
CDispVert *pVert = &g_DispVerts[pDispInfo->m_iDispVertStart + index];
|
||||
pVert->m_vVector = (pVert->m_vVector * pVert->m_flDist) + vOffset;
|
||||
pVert->m_flDist = 1;
|
||||
|
||||
// Modify the CCoreDispInfo vert (although it probably won't be used later).
|
||||
pCoreDisp->SetVert( index, vNewPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
// This shouldn't happen because it would mean that the triangulation that
|
||||
// disp_tesselation.h produced was missing a chunk of the space that the
|
||||
// displacement covers.
|
||||
// It also could indicate a floating-point epsilon error.. check to see if
|
||||
// FindEnclosingTri finds a triangle that -almost- encloses the vert.
|
||||
Assert( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapRemainingVertsToSurface( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
|
||||
{
|
||||
//g_pPad = ScratchPad3D_Create();
|
||||
for ( int i=0; i < listSize; i++ )
|
||||
{
|
||||
SnapRemainingVertsToSurface( ppListBase[i], &pBSPDispInfos[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void EmitDispLMAlphaAndNeighbors()
|
||||
{
|
||||
int i;
|
||||
|
||||
Msg( "Finding displacement neighbors...\n" );
|
||||
|
||||
// Build the CCoreDispInfos.
|
||||
CUtlVector<dface_t*> faces;
|
||||
|
||||
// Create the core dispinfos and init them for use as CDispUtilsHelpers.
|
||||
for ( int iDisp = 0; iDisp < nummapdispinfo; ++iDisp )
|
||||
{
|
||||
CCoreDispInfo *pDisp = new CCoreDispInfo;
|
||||
if ( !pDisp )
|
||||
{
|
||||
g_CoreDispInfos.Purge();
|
||||
return;
|
||||
}
|
||||
|
||||
int nIndex = g_CoreDispInfos.AddToTail();
|
||||
pDisp->SetListIndex( nIndex );
|
||||
g_CoreDispInfos[nIndex] = pDisp;
|
||||
}
|
||||
|
||||
for ( i=0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
g_CoreDispInfos[i]->SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
|
||||
}
|
||||
|
||||
faces.SetSize( nummapdispinfo );
|
||||
|
||||
int nMemSize = texinfo.Count() * sizeof(int);
|
||||
int *pSwappedTexInfos = (int*)stackalloc( nMemSize );
|
||||
memset( pSwappedTexInfos, 0xFF, nMemSize );
|
||||
for( i = 0; i < numfaces; i++ )
|
||||
{
|
||||
dface_t *pFace = &dfaces[i];
|
||||
|
||||
if( pFace->dispinfo == -1 )
|
||||
continue;
|
||||
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
|
||||
|
||||
// Set the displacement's face index.
|
||||
ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
|
||||
pDisp->m_iMapFace = i;
|
||||
|
||||
// Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
|
||||
CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[pFace->dispinfo];
|
||||
DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace, pSwappedTexInfos );
|
||||
|
||||
faces[pFace->dispinfo] = pFace;
|
||||
}
|
||||
stackfree( pSwappedTexInfos );
|
||||
|
||||
// Generate and export neighbor data.
|
||||
ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
|
||||
|
||||
// Generate and export the active vert lists.
|
||||
ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
|
||||
|
||||
|
||||
// Now that we know which vertices are actually going to be around, snap the ones that won't
|
||||
// be around onto the slightly-reduced mesh. This is so the engine's ray test code and
|
||||
// overlay code works right.
|
||||
SnapRemainingVertsToSurface( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
|
||||
|
||||
Msg( "Finding lightmap sample positions...\n" );
|
||||
for ( i=0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
dface_t *pFace = faces[i];
|
||||
ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
|
||||
CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];
|
||||
|
||||
pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();
|
||||
|
||||
CalculateLightmapSamplePositions( pCoreDispInfo, pFace, g_DispLightmapSamplePositions );
|
||||
}
|
||||
|
||||
StartPacifier( "Displacement Alpha : ");
|
||||
|
||||
// Build lightmap alphas.
|
||||
int dispCount = 0; // How many we've processed.
|
||||
for( i = 0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
dface_t *pFace = faces[i];
|
||||
|
||||
Assert( pFace->dispinfo == i );
|
||||
ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
|
||||
|
||||
// Allocate space for the alpha values.
|
||||
pDisp->m_iLightmapAlphaStart = 0; // not used anymore
|
||||
|
||||
++dispCount;
|
||||
}
|
||||
|
||||
EndPacifier();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void DispGetFaceInfo( mapbrush_t *pBrush )
|
||||
{
|
||||
int i;
|
||||
side_t *pSide;
|
||||
|
||||
// we don't support displacement on entities at the moment!!
|
||||
if( pBrush->entitynum != 0 )
|
||||
{
|
||||
char* pszEntityName = ValueForKey( &g_LoadingMap->entities[pBrush->entitynum], "classname" );
|
||||
Error( "Error: displacement found on a(n) %s entity - not supported (entity %d, brush %d)\n", pszEntityName, pBrush->entitynum, pBrush->brushnum );
|
||||
}
|
||||
|
||||
for( i = 0; i < pBrush->numsides; i++ )
|
||||
{
|
||||
pSide = &pBrush->original_sides[i];
|
||||
if( pSide->pMapDisp )
|
||||
{
|
||||
// error checking!!
|
||||
if( pSide->winding->numpoints != 4 )
|
||||
Error( "Trying to create a non-quad displacement! (entity %d, brush %d)\n", pBrush->entitynum, pBrush->brushnum );
|
||||
pSide->pMapDisp->face.originalface = pSide;
|
||||
pSide->pMapDisp->face.texinfo = pSide->texinfo;
|
||||
pSide->pMapDisp->face.dispinfo = -1;
|
||||
pSide->pMapDisp->face.planenum = pSide->planenum;
|
||||
pSide->pMapDisp->face.numpoints = pSide->winding->numpoints;
|
||||
pSide->pMapDisp->face.w = CopyWinding( pSide->winding );
|
||||
pSide->pMapDisp->face.contents = pBrush->contents;
|
||||
|
||||
pSide->pMapDisp->face.merged = FALSE;
|
||||
pSide->pMapDisp->face.split[0] = FALSE;
|
||||
pSide->pMapDisp->face.split[1] = FALSE;
|
||||
|
||||
pSide->pMapDisp->entitynum = pBrush->entitynum;
|
||||
pSide->pMapDisp->brushSideID = pSide->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
bool HasDispInfo( mapbrush_t *pBrush )
|
||||
{
|
||||
int i;
|
||||
side_t *pSide;
|
||||
|
||||
for( i = 0; i < pBrush->numsides; i++ )
|
||||
{
|
||||
pSide = &pBrush->original_sides[i];
|
||||
if( pSide->pMapDisp )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
46
mp/src/utils/vbsp/disp_vbsp.h
Normal file
46
mp/src/utils/vbsp/disp_vbsp.h
Normal file
@@ -0,0 +1,46 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef VBSP_DISPINFO_H
|
||||
#define VBSP_DISPINFO_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
#include "vbsp.h"
|
||||
|
||||
|
||||
|
||||
class CCoreDispInfo;
|
||||
|
||||
|
||||
extern CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
|
||||
|
||||
|
||||
// Setup initial entries in g_dispinfo with some of the vertex data from the mapdisps.
|
||||
void EmitInitialDispInfos();
|
||||
|
||||
// Resample vertex alpha into lightmap alpha for displacement surfaces so LOD popping artifacts are
|
||||
// less noticeable on the mid-to-high end.
|
||||
//
|
||||
// Also builds neighbor data.
|
||||
void EmitDispLMAlphaAndNeighbors();
|
||||
|
||||
// Setup a CCoreDispInfo given a mapdispinfo_t.
|
||||
// If pFace is non-NULL, then lightmap texture coordinates will be generated.
|
||||
void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp,
|
||||
CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos );
|
||||
|
||||
|
||||
void DispGetFaceInfo( mapbrush_t *pBrush );
|
||||
bool HasDispInfo( mapbrush_t *pBrush );
|
||||
|
||||
// Computes the bounds for a disp info
|
||||
void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs );
|
||||
|
||||
#endif // VBSP_DISPINFO_H
|
||||
1810
mp/src/utils/vbsp/faces.cpp
Normal file
1810
mp/src/utils/vbsp/faces.cpp
Normal file
File diff suppressed because it is too large
Load Diff
20
mp/src/utils/vbsp/faces.h
Normal file
20
mp/src/utils/vbsp/faces.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef FACES_H
|
||||
#define FACES_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
void GetEdge2_InitOptimizedList(); // Call this before calling GetEdge2() on a bunch of edges.
|
||||
int AddEdge( int v1, int v2, face_t *f );
|
||||
int GetEdge2(int v1, int v2, face_t *f);
|
||||
|
||||
|
||||
#endif // FACES_H
|
||||
219
mp/src/utils/vbsp/glfile.cpp
Normal file
219
mp/src/utils/vbsp/glfile.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
|
||||
int c_glfaces;
|
||||
|
||||
int PortalVisibleSides (portal_t *p)
|
||||
{
|
||||
int fcon, bcon;
|
||||
|
||||
if (!p->onnode)
|
||||
return 0; // outside
|
||||
|
||||
fcon = p->nodes[0]->contents;
|
||||
bcon = p->nodes[1]->contents;
|
||||
|
||||
// same contents never create a face
|
||||
if (fcon == bcon)
|
||||
return 0;
|
||||
|
||||
// FIXME: is this correct now?
|
||||
if (!fcon)
|
||||
return 1;
|
||||
if (!bcon)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OutputWinding (winding_t *w, FileHandle_t glview)
|
||||
{
|
||||
static int level = 128;
|
||||
vec_t light;
|
||||
int i;
|
||||
|
||||
CmdLib_FPrintf( glview, "%i\n", w->numpoints);
|
||||
level+=28;
|
||||
light = (level&255)/255.0;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
|
||||
w->p[i][0],
|
||||
w->p[i][1],
|
||||
w->p[i][2],
|
||||
light,
|
||||
light,
|
||||
light);
|
||||
}
|
||||
//CmdLib_FPrintf(glview, "\n");
|
||||
}
|
||||
|
||||
void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b)
|
||||
{
|
||||
int i;
|
||||
|
||||
CmdLib_FPrintf( glview, "%i\n", w->numpoints);
|
||||
float lr = r * (1.0f/255.0f);
|
||||
float lg = g * (1.0f/255.0f);
|
||||
float lb = b * (1.0f/255.0f);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
|
||||
w->p[i][0],
|
||||
w->p[i][1],
|
||||
w->p[i][2],
|
||||
lr,
|
||||
lg,
|
||||
lb);
|
||||
}
|
||||
//CmdLib_FPrintf(glview, "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
OutputPortal
|
||||
=============
|
||||
*/
|
||||
void OutputPortal (portal_t *p, FileHandle_t glview)
|
||||
{
|
||||
winding_t *w;
|
||||
int sides;
|
||||
|
||||
sides = PortalVisibleSides (p);
|
||||
if (!sides)
|
||||
return;
|
||||
|
||||
c_glfaces++;
|
||||
|
||||
w = p->winding;
|
||||
|
||||
if (sides == 2) // back side
|
||||
w = ReverseWinding (w);
|
||||
|
||||
OutputWinding (w, glview);
|
||||
|
||||
if (sides == 2)
|
||||
FreeWinding(w);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView_r
|
||||
=============
|
||||
*/
|
||||
void WriteGLView_r (node_t *node, FileHandle_t glview)
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
WriteGLView_r (node->children[0], glview);
|
||||
WriteGLView_r (node->children[1], glview);
|
||||
return;
|
||||
}
|
||||
|
||||
// write all the portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
if (p->nodes[0] == node)
|
||||
{
|
||||
OutputPortal (p, glview);
|
||||
nextp = p->next[0];
|
||||
}
|
||||
else
|
||||
nextp = p->next[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WriteGLViewFaces_r( node_t *node, FileHandle_t glview )
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
WriteGLViewFaces_r (node->children[0], glview);
|
||||
WriteGLViewFaces_r (node->children[1], glview);
|
||||
return;
|
||||
}
|
||||
|
||||
// write all the portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
int s = (p->nodes[1] == node);
|
||||
|
||||
if ( p->face[s] )
|
||||
{
|
||||
OutputWinding( p->face[s]->w, glview );
|
||||
}
|
||||
nextp = p->next[s];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
WriteGLView
|
||||
=============
|
||||
*/
|
||||
void WriteGLView (tree_t *tree, char *source)
|
||||
{
|
||||
char name[1024];
|
||||
FileHandle_t glview;
|
||||
|
||||
c_glfaces = 0;
|
||||
sprintf (name, "%s%s.gl",outbase, source);
|
||||
Msg("Writing %s\n", name);
|
||||
|
||||
glview = g_pFileSystem->Open( name, "w" );
|
||||
if (!glview)
|
||||
Error ("Couldn't open %s", name);
|
||||
WriteGLView_r (tree->headnode, glview);
|
||||
g_pFileSystem->Close( glview );
|
||||
|
||||
Msg("%5i c_glfaces\n", c_glfaces);
|
||||
}
|
||||
|
||||
|
||||
void WriteGLViewFaces( tree_t *tree, const char *pName )
|
||||
{
|
||||
char name[1024];
|
||||
FileHandle_t glview;
|
||||
|
||||
c_glfaces = 0;
|
||||
sprintf (name, "%s%s.gl", outbase, pName);
|
||||
Msg("Writing %s\n", name);
|
||||
|
||||
glview = g_pFileSystem->Open( name, "w" );
|
||||
if (!glview)
|
||||
Error ("Couldn't open %s", name);
|
||||
WriteGLViewFaces_r (tree->headnode, glview);
|
||||
g_pFileSystem->Close( glview );
|
||||
|
||||
Msg("%5i c_glfaces\n", c_glfaces);
|
||||
}
|
||||
|
||||
|
||||
void WriteGLViewBrushList( bspbrush_t *pList, const char *pName )
|
||||
{
|
||||
char name[1024];
|
||||
FileHandle_t glview;
|
||||
|
||||
sprintf (name, "%s%s.gl", outbase, pName );
|
||||
Msg("Writing %s\n", name);
|
||||
|
||||
glview = g_pFileSystem->Open( name, "w" );
|
||||
if (!glview)
|
||||
Error ("Couldn't open %s", name);
|
||||
for ( bspbrush_t *pBrush = pList; pBrush; pBrush = pBrush->next )
|
||||
{
|
||||
for (int i = 0; i < pBrush->numsides; i++ )
|
||||
OutputWinding( pBrush->sides[i].winding, glview );
|
||||
}
|
||||
g_pFileSystem->Close( glview );
|
||||
}
|
||||
1656
mp/src/utils/vbsp/ivp.cpp
Normal file
1656
mp/src/utils/vbsp/ivp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
75
mp/src/utils/vbsp/ivp.h
Normal file
75
mp/src/utils/vbsp/ivp.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef IVP_H
|
||||
#define IVP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "utlvector.h"
|
||||
|
||||
class CPhysCollide;
|
||||
class CTextBuffer;
|
||||
class IPhysicsCollision;
|
||||
extern IPhysicsCollision *physcollision;
|
||||
|
||||
// a list of all of the materials in the world model
|
||||
extern int RemapWorldMaterial( int materialIndexIn );
|
||||
|
||||
class CPhysCollisionEntry
|
||||
{
|
||||
public:
|
||||
CPhysCollisionEntry( CPhysCollide *pCollide );
|
||||
|
||||
virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
|
||||
virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
|
||||
|
||||
unsigned int GetCollisionBinarySize();
|
||||
unsigned int WriteCollisionBinary( char *pDest );
|
||||
|
||||
protected:
|
||||
void DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer );
|
||||
|
||||
protected:
|
||||
CPhysCollide *m_pCollide;
|
||||
};
|
||||
|
||||
class CPhysCollisionEntryStaticMesh : public CPhysCollisionEntry
|
||||
{
|
||||
public:
|
||||
CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName );
|
||||
|
||||
virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
|
||||
virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
|
||||
|
||||
private:
|
||||
const char *m_pMaterial;
|
||||
};
|
||||
|
||||
|
||||
class CTextBuffer
|
||||
{
|
||||
public:
|
||||
CTextBuffer( void );
|
||||
~CTextBuffer( void );
|
||||
inline int GetSize( void ) { return m_buffer.Count(); }
|
||||
inline char *GetData( void ) { return m_buffer.Base(); }
|
||||
|
||||
void WriteText( const char *pText );
|
||||
void WriteIntKey( const char *pKeyName, int outputData );
|
||||
void WriteStringKey( const char *pKeyName, const char *outputData );
|
||||
void WriteFloatKey( const char *pKeyName, float outputData );
|
||||
void WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count );
|
||||
|
||||
void CopyStringQuotes( const char *pString );
|
||||
void Terminate( void );
|
||||
private:
|
||||
void CopyData( const char *pData, int len );
|
||||
CUtlVector<char> m_buffer;
|
||||
};
|
||||
|
||||
#endif // IVP_H
|
||||
168
mp/src/utils/vbsp/leakfile.cpp
Normal file
168
mp/src/utils/vbsp/leakfile.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "color.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
LEAF FILE GENERATION
|
||||
|
||||
Save out name.line for qe3 to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
LeakFile
|
||||
|
||||
Finds the shortest possible chain of portals
|
||||
that leads from the outside leaf to a specifically
|
||||
occupied leaf
|
||||
=============
|
||||
*/
|
||||
void LeakFile (tree_t *tree)
|
||||
{
|
||||
Vector mid;
|
||||
FILE *linefile;
|
||||
char filename[1024];
|
||||
node_t *node;
|
||||
int count;
|
||||
|
||||
if (!tree->outside_node.occupied)
|
||||
return;
|
||||
|
||||
tree->leaked = true;
|
||||
qprintf ("--- LeakFile ---\n");
|
||||
|
||||
//
|
||||
// write the points to the file
|
||||
//
|
||||
sprintf (filename, "%s.lin", source);
|
||||
linefile = fopen (filename, "w");
|
||||
if (!linefile)
|
||||
Error ("Couldn't open %s\n", filename);
|
||||
|
||||
count = 0;
|
||||
node = &tree->outside_node;
|
||||
while (node->occupied > 1)
|
||||
{
|
||||
portal_t *nextportal = NULL;
|
||||
node_t *nextnode = NULL;
|
||||
int s = 0;
|
||||
|
||||
// find the best portal exit
|
||||
int next = node->occupied;
|
||||
for (portal_t *p=node->portals ; p ; p = p->next[!s])
|
||||
{
|
||||
s = (p->nodes[0] == node);
|
||||
if (p->nodes[s]->occupied
|
||||
&& p->nodes[s]->occupied < next)
|
||||
{
|
||||
nextportal = p;
|
||||
nextnode = p->nodes[s];
|
||||
next = nextnode->occupied;
|
||||
}
|
||||
}
|
||||
node = nextnode;
|
||||
WindingCenter (nextportal->winding, mid);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
count++;
|
||||
}
|
||||
|
||||
// Add the occupant's origin to the leakfile.
|
||||
Vector origin;
|
||||
GetVectorForKey (node->occupant, "origin", origin);
|
||||
|
||||
fprintf (linefile, "%f %f %f\n", origin[0], origin[1], origin[2]);
|
||||
qprintf ("%5i point linefile\n", count+1);
|
||||
|
||||
fclose (linefile);
|
||||
|
||||
// Emit a leak warning.
|
||||
const char *cl = ValueForKey (node->occupant, "classname");
|
||||
Color red(255,0,0,255);
|
||||
ColorSpewMessage( SPEW_MESSAGE, &red, "Entity %s (%.2f %.2f %.2f) leaked!\n", cl, origin[0], origin[1], origin[2] );
|
||||
}
|
||||
|
||||
void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart )
|
||||
{
|
||||
Vector mid;
|
||||
FILE *linefile;
|
||||
char filename[1024];
|
||||
node_t *node;
|
||||
int count;
|
||||
|
||||
// wrote a leak line file already, don't overwrite it with the areaportal leak file
|
||||
if ( tree->leaked )
|
||||
return;
|
||||
|
||||
tree->leaked = true;
|
||||
qprintf ("--- LeakFile ---\n");
|
||||
|
||||
//
|
||||
// write the points to the file
|
||||
//
|
||||
sprintf (filename, "%s.lin", source);
|
||||
linefile = fopen (filename, "w");
|
||||
if (!linefile)
|
||||
Error ("Couldn't open %s\n", filename);
|
||||
|
||||
count = 2;
|
||||
WindingCenter (pEndPortal->winding, mid);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
mid = 0.5 * (pStart->mins + pStart->maxs);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
|
||||
node = pStart;
|
||||
while (node->occupied >= 1)
|
||||
{
|
||||
portal_t *nextportal = NULL;
|
||||
node_t *nextnode = NULL;
|
||||
int s = 0;
|
||||
|
||||
// find the best portal exit
|
||||
int next = node->occupied;
|
||||
for (portal_t *p=node->portals ; p ; p = p->next[!s])
|
||||
{
|
||||
s = (p->nodes[0] == node);
|
||||
if (p->nodes[s]->occupied
|
||||
&& p->nodes[s]->occupied < next)
|
||||
{
|
||||
nextportal = p;
|
||||
nextnode = p->nodes[s];
|
||||
next = nextnode->occupied;
|
||||
}
|
||||
}
|
||||
if ( !nextnode )
|
||||
break;
|
||||
node = nextnode;
|
||||
WindingCenter (nextportal->winding, mid);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
count++;
|
||||
}
|
||||
// add the occupant center
|
||||
if ( node )
|
||||
{
|
||||
mid = 0.5 * (node->mins + node->maxs);
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
count++;
|
||||
}
|
||||
WindingCenter (pStartPortal->winding, mid);
|
||||
count++;
|
||||
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
|
||||
|
||||
qprintf ("%5i point linefile\n", count);
|
||||
|
||||
fclose (linefile);
|
||||
Warning( "Wrote %s\n", filename );
|
||||
Color red(255,0,0,255);
|
||||
ColorSpewMessage( SPEW_MESSAGE, &red, "Areaportal leak ! File: %s ", filename );
|
||||
}
|
||||
568
mp/src/utils/vbsp/manifest.cpp
Normal file
568
mp/src/utils/vbsp/manifest.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
#include "vbsp.h"
|
||||
#include "map_shared.h"
|
||||
#include "fgdlib/fgdlib.h"
|
||||
#include "manifest.h"
|
||||
#include "windows.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: default constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CManifestMap::CManifestMap( void )
|
||||
{
|
||||
m_RelativeMapFileName[ 0 ] = 0;
|
||||
m_bTopLevelMap = false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: default constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CManifest::CManifest( void )
|
||||
{
|
||||
m_InstancePath[ 0 ] = 0;
|
||||
m_bIsCordoning = false;
|
||||
m_CordoningMapEnt = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function will parse through the known keys for the manifest map entry
|
||||
// Input : szKey - the key name
|
||||
// szValue - the value
|
||||
// pManifestMap - the manifest map this belongs to
|
||||
// Output : ChunkFileResult_t - result of the parsing
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap )
|
||||
{
|
||||
if ( !stricmp( szKey, "Name" ) )
|
||||
{
|
||||
// pManifestMap->m_FriendlyName = szValue;
|
||||
}
|
||||
else if ( !stricmp( szKey, "File" ) )
|
||||
{
|
||||
strcpy( pManifestMap->m_RelativeMapFileName, szValue );
|
||||
}
|
||||
else if ( !stricmp( szKey, "IsPrimary" ) )
|
||||
{
|
||||
// pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 );
|
||||
}
|
||||
else if ( !stricmp( szKey, "IsProtected" ) )
|
||||
{
|
||||
// pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 );
|
||||
}
|
||||
else if ( !stricmp( szKey, "TopLevel" ) )
|
||||
{
|
||||
pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 );
|
||||
}
|
||||
|
||||
return ChunkFile_Ok;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function is responsible for setting up the manifest map about to be read in
|
||||
// Input : pFile - the chunk file being read
|
||||
// pDoc - the owning manifest document
|
||||
// Output : ChunkFileResult_t - result of the parsing
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest )
|
||||
{
|
||||
CManifestMap *pManifestMap = new CManifestMap();
|
||||
|
||||
pManifest->m_Maps.AddToTail( pManifestMap );
|
||||
|
||||
ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap );
|
||||
|
||||
return( eResult );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function will load the VMF chunk
|
||||
// Input : pFile - the chunk file being read
|
||||
// pDoc - the owning manifest document
|
||||
// Output : ChunkFileResult_t - result of the parsing
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest )
|
||||
{
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest );
|
||||
pFile->PushHandlers(&Handlers);
|
||||
|
||||
ChunkFileResult_t eResult = ChunkFile_Ok;
|
||||
|
||||
eResult = pFile->ReadChunk();
|
||||
|
||||
pFile->PopHandlers();
|
||||
|
||||
return( eResult );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon )
|
||||
{
|
||||
// Add a box to this cordon.
|
||||
pCordon->m_Boxes.AddToTail();
|
||||
BoundBox &box = pCordon->m_Boxes.Tail();
|
||||
|
||||
// Fill it in with the data from the VMF.
|
||||
return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox )
|
||||
{
|
||||
if (!stricmp(szKey, "mins"))
|
||||
{
|
||||
CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins);
|
||||
}
|
||||
else if (!stricmp(szKey, "maxs"))
|
||||
{
|
||||
CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs);
|
||||
}
|
||||
|
||||
return ChunkFile_Ok;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon )
|
||||
{
|
||||
if (!stricmp(szKey, "name"))
|
||||
{
|
||||
pCordon->m_szName.Set( szValue );
|
||||
}
|
||||
// Whether this particular cordon volume is active.
|
||||
else if (!stricmp(szKey, "active"))
|
||||
{
|
||||
CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive);
|
||||
}
|
||||
|
||||
return ChunkFile_Ok;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest )
|
||||
{
|
||||
// Add a new cordon which will be filled in by the key callback
|
||||
pManifest->m_Cordons.AddToTail();
|
||||
Cordon_t &cordon = pManifest->m_Cordons.Tail();
|
||||
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon );
|
||||
|
||||
pFile->PushHandlers(&Handlers);
|
||||
ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon );
|
||||
pFile->PopHandlers();
|
||||
|
||||
return(eResult);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------
|
||||
// Parses keys that are applicable to all cordons in the map.
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest )
|
||||
{
|
||||
// Whether the cordoning system is enabled or disabled.
|
||||
if ( !stricmp( szKey, "active" ) )
|
||||
{
|
||||
CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning );
|
||||
}
|
||||
|
||||
return ChunkFile_Ok;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parses the VMF chunk that pertains to all the cordons in the map:
|
||||
//
|
||||
// cordons
|
||||
// {
|
||||
// "active" "true"
|
||||
// cordon
|
||||
// {
|
||||
// "active" "true"
|
||||
// "box"
|
||||
// {
|
||||
// "mins" "-1024, -1024, -1024"
|
||||
// "maxs" "1024, 1024, 1024"
|
||||
// }
|
||||
// ...may be more boxes...
|
||||
// }
|
||||
// ...may be more cordons...
|
||||
// }
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest )
|
||||
{
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest );
|
||||
|
||||
pFile->PushHandlers(&Handlers);
|
||||
ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest );
|
||||
pFile->PopHandlers();
|
||||
|
||||
return(eResult);
|
||||
}
|
||||
|
||||
extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
|
||||
|
||||
ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc )
|
||||
{
|
||||
pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities];
|
||||
g_MainMap->num_entities++;
|
||||
memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) );
|
||||
pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes;
|
||||
pDoc->m_CordoningMapEnt->numbrushes = 0;
|
||||
|
||||
LoadEntity_t LoadEntity;
|
||||
LoadEntity.pEntity = pDoc->m_CordoningMapEnt;
|
||||
|
||||
// No default flags/contents
|
||||
LoadEntity.nBaseFlags = 0;
|
||||
LoadEntity.nBaseContents = 0;
|
||||
|
||||
//
|
||||
// Set up handlers for the subchunks that we are interested in.
|
||||
//
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc );
|
||||
Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
|
||||
pFile->PushHandlers(&Handlers);
|
||||
|
||||
ChunkFileResult_t eResult = ChunkFile_Ok;
|
||||
|
||||
eResult = pFile->ReadChunk();
|
||||
|
||||
pFile->PopHandlers();
|
||||
|
||||
return( eResult );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function will create a new entity pair
|
||||
// Input : pKey - the key of the pair
|
||||
// pValue - the value of the pair
|
||||
// Output : returns a newly created epair structure
|
||||
//-----------------------------------------------------------------------------
|
||||
epair_t *CManifest::CreateEPair( char *pKey, char *pValue )
|
||||
{
|
||||
epair_t *pEPair = new epair_t;
|
||||
|
||||
pEPair->key = new char[ strlen( pKey ) + 1 ];
|
||||
pEPair->value = new char[ strlen( pValue ) + 1 ];
|
||||
|
||||
strcpy( pEPair->key, pKey );
|
||||
strcpy( pEPair->value, pValue );
|
||||
|
||||
return pEPair;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function will load in all of the submaps belonging to this manifest,
|
||||
// except for the top level map, which is loaded separately.
|
||||
// Input : pMapFile - the top level map that was previously loaded
|
||||
// pszFileName - the absolute file name of the top level map file
|
||||
// Output : returns true if all submaps were loaded
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName )
|
||||
{
|
||||
entity_t *InstanceEntity;
|
||||
epair_t *pEPair;
|
||||
|
||||
InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
|
||||
pMapFile->num_entities++;
|
||||
memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
|
||||
|
||||
InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
|
||||
pEPair = CreateEPair( "classname", "worldspawn" );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
|
||||
for( int i = 0; i < m_Maps.Count(); i++ )
|
||||
{
|
||||
// if ( m_Maps[ i ]->m_bTopLevelMap == false )
|
||||
{
|
||||
char FileName[ MAX_PATH ];
|
||||
|
||||
sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName );
|
||||
|
||||
InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
|
||||
pMapFile->num_entities++;
|
||||
|
||||
memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
|
||||
InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
|
||||
|
||||
pEPair = CreateEPair( "angles", "0 0 0" );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
|
||||
char temp[ 128 ];
|
||||
sprintf( temp, "%d", GameData::NAME_FIXUP_NONE );
|
||||
|
||||
pEPair = CreateEPair( "fixup_style", temp );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
|
||||
pEPair = CreateEPair( "classname", "func_instance" );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
|
||||
pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
|
||||
if ( m_Maps[ i ]->m_bTopLevelMap == true )
|
||||
{
|
||||
pEPair = CreateEPair( "toplevel", "1" );
|
||||
pEPair->next = InstanceEntity->epairs;
|
||||
InstanceEntity->epairs = pEPair;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName )
|
||||
{
|
||||
char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ];
|
||||
DWORD UserNameSize;
|
||||
|
||||
UserNameSize = sizeof( UserName );
|
||||
if ( GetUserName( UserName, &UserNameSize ) == 0 )
|
||||
{
|
||||
strcpy( UserPrefsFileName, "default" );
|
||||
}
|
||||
|
||||
sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName );
|
||||
V_StripExtension( pszFileName, FileName, sizeof( FileName ) );
|
||||
strcat( FileName, UserPrefsFileName );
|
||||
|
||||
FILE *fp = fopen( FileName, "rb" );
|
||||
if ( !fp )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CChunkFile File;
|
||||
ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read );
|
||||
|
||||
if ( eResult == ChunkFile_Ok )
|
||||
{
|
||||
//
|
||||
// Set up handlers for the subchunks that we are interested in.
|
||||
//
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this );
|
||||
|
||||
// Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this);
|
||||
|
||||
File.PushHandlers(&Handlers);
|
||||
|
||||
while( eResult == ChunkFile_Ok )
|
||||
{
|
||||
eResult = File.ReadChunk();
|
||||
}
|
||||
|
||||
if ( eResult == ChunkFile_EOF )
|
||||
{
|
||||
eResult = ChunkFile_Ok;
|
||||
}
|
||||
|
||||
File.PopHandlers();
|
||||
}
|
||||
|
||||
if ( eResult == ChunkFile_Ok )
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// no pref message for now
|
||||
// GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads a .VMM file.
|
||||
// Input : pszFileName - Full path of the map file to load.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CManifest::LoadVMFManifest( const char *pszFileName )
|
||||
{
|
||||
V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) );
|
||||
strcat( m_InstancePath, "\\" );
|
||||
|
||||
CChunkFile File;
|
||||
ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read );
|
||||
if ( eResult != ChunkFile_Ok )
|
||||
{
|
||||
g_MapError.ReportError( File.GetErrorText( eResult ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
CChunkHandlerMap Handlers;
|
||||
Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this );
|
||||
|
||||
File.PushHandlers(&Handlers);
|
||||
|
||||
while (eResult == ChunkFile_Ok)
|
||||
{
|
||||
eResult = File.ReadChunk();
|
||||
}
|
||||
|
||||
if (eResult == ChunkFile_EOF)
|
||||
{
|
||||
eResult = ChunkFile_Ok;
|
||||
}
|
||||
|
||||
File.PopHandlers();
|
||||
|
||||
if ( eResult == ChunkFile_Ok )
|
||||
{
|
||||
int index = g_Maps.AddToTail( new CMapFile() );
|
||||
g_LoadingMap = g_Maps[ index ];
|
||||
if ( g_MainMap == NULL )
|
||||
{
|
||||
g_MainMap = g_LoadingMap;
|
||||
}
|
||||
|
||||
LoadSubMaps( g_LoadingMap, pszFileName );
|
||||
|
||||
LoadVMFManifestUserPrefs( pszFileName );
|
||||
}
|
||||
|
||||
return ( eResult == ChunkFile_Ok );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input :
|
||||
// Output :
|
||||
//-----------------------------------------------------------------------------
|
||||
void CManifest::CordonWorld( )
|
||||
{
|
||||
if ( m_bIsCordoning == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < g_MainMap->num_entities; i++ )
|
||||
{
|
||||
if ( i == 0 )
|
||||
{ // for world spawn, we look at brushes
|
||||
for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ )
|
||||
{
|
||||
int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum;
|
||||
|
||||
bool bRemove = true;
|
||||
|
||||
for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
|
||||
{
|
||||
if ( m_Cordons[ nCordon ].m_bActive == false )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
|
||||
{
|
||||
if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true )
|
||||
{
|
||||
bRemove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRemove == false )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRemove )
|
||||
{
|
||||
int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] );
|
||||
memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize );
|
||||
g_MainMap->entities[ i ].numbrushes--;
|
||||
nBrushNum--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt )
|
||||
{ // for all other entities, even if they include brushes, we look at origin
|
||||
if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bRemove = true;
|
||||
|
||||
for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
|
||||
{
|
||||
if ( m_Cordons[ nCordon ].m_bActive == false )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
|
||||
{
|
||||
if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true )
|
||||
{
|
||||
bRemove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRemove == false )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bRemove )
|
||||
{
|
||||
g_MainMap->entities[ i ].numbrushes = 0;
|
||||
g_MainMap->entities[ i ].epairs = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_CordoningMapEnt )
|
||||
{
|
||||
g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt );
|
||||
m_CordoningMapEnt->numbrushes = 0;
|
||||
m_CordoningMapEnt->epairs = NULL;
|
||||
}
|
||||
}
|
||||
73
mp/src/utils/vbsp/manifest.h
Normal file
73
mp/src/utils/vbsp/manifest.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=====================================================================================//
|
||||
|
||||
#ifndef __MANIFEST_H
|
||||
#define __MANIFEST_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "boundbox.h"
|
||||
|
||||
//
|
||||
// Each cordon is a named collection of bounding boxes.
|
||||
//
|
||||
struct Cordon_t
|
||||
{
|
||||
inline Cordon_t()
|
||||
{
|
||||
m_bActive = false;
|
||||
}
|
||||
|
||||
CUtlString m_szName;
|
||||
bool m_bActive; // True means cull using this cordon when cordoning is enabled.
|
||||
CUtlVector<BoundBox> m_Boxes;
|
||||
};
|
||||
|
||||
class CManifestMap
|
||||
{
|
||||
public:
|
||||
CManifestMap( void );
|
||||
char m_RelativeMapFileName[ MAX_PATH ];
|
||||
bool m_bTopLevelMap;
|
||||
};
|
||||
|
||||
class CManifest
|
||||
{
|
||||
public:
|
||||
CManifest( void );
|
||||
|
||||
static ChunkFileResult_t LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap );
|
||||
static ChunkFileResult_t LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest );
|
||||
static ChunkFileResult_t LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest );
|
||||
static ChunkFileResult_t LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon );
|
||||
static ChunkFileResult_t LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox );
|
||||
static ChunkFileResult_t LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon );
|
||||
static ChunkFileResult_t LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest );
|
||||
static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest );
|
||||
static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest );
|
||||
static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest );
|
||||
|
||||
bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName );
|
||||
epair_t *CreateEPair( char *pKey, char *pValue );
|
||||
bool LoadVMFManifest( const char *pszFileName );
|
||||
const char *GetInstancePath( ) { return m_InstancePath; }
|
||||
|
||||
void CordonWorld( );
|
||||
|
||||
private:
|
||||
bool LoadVMFManifestUserPrefs( const char *pszFileName );
|
||||
|
||||
|
||||
CUtlVector< CManifestMap * > m_Maps;
|
||||
char m_InstancePath[ MAX_PATH ];
|
||||
bool m_bIsCordoning;
|
||||
CUtlVector< Cordon_t > m_Cordons;
|
||||
entity_t *m_CordoningMapEnt;
|
||||
};
|
||||
|
||||
#endif // #ifndef __MANIFEST_H
|
||||
3304
mp/src/utils/vbsp/map.cpp
Normal file
3304
mp/src/utils/vbsp/map.cpp
Normal file
File diff suppressed because it is too large
Load Diff
18
mp/src/utils/vbsp/map.h
Normal file
18
mp/src/utils/vbsp/map.h
Normal file
@@ -0,0 +1,18 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MAP_H
|
||||
#define MAP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// All the brush sides referenced by info_no_dynamic_shadow entities.
|
||||
extern CUtlVector<int> g_NoDynamicShadowSides;
|
||||
|
||||
|
||||
#endif // MAP_H
|
||||
440
mp/src/utils/vbsp/materialpatch.cpp
Normal file
440
mp/src/utils/vbsp/materialpatch.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "vbsp.h"
|
||||
#include "UtlBuffer.h"
|
||||
#include "utlsymbol.h"
|
||||
#include "utlrbtree.h"
|
||||
#include "KeyValues.h"
|
||||
#include "bsplib.h"
|
||||
#include "materialpatch.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
// case insensitive
|
||||
static CUtlSymbolTable s_SymbolTable( 0, 32, true );
|
||||
|
||||
struct NameTranslationLookup_t
|
||||
{
|
||||
CUtlSymbol m_OriginalFileName;
|
||||
CUtlSymbol m_PatchFileName;
|
||||
};
|
||||
|
||||
static bool NameTranslationLessFunc( NameTranslationLookup_t const& src1,
|
||||
NameTranslationLookup_t const& src2 )
|
||||
{
|
||||
return src1.m_PatchFileName < src2.m_PatchFileName;
|
||||
}
|
||||
|
||||
CUtlRBTree<NameTranslationLookup_t, int> s_MapPatchedMatToOriginalMat( 0, 256, NameTranslationLessFunc );
|
||||
|
||||
void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName )
|
||||
{
|
||||
NameTranslationLookup_t newEntry;
|
||||
|
||||
newEntry.m_OriginalFileName = s_SymbolTable.AddString( pOriginalMaterialName );
|
||||
newEntry.m_PatchFileName = s_SymbolTable.AddString( pNewMaterialName );
|
||||
|
||||
s_MapPatchedMatToOriginalMat.Insert( newEntry );
|
||||
}
|
||||
|
||||
const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName )
|
||||
{
|
||||
const char *pRetName = NULL;
|
||||
int id;
|
||||
NameTranslationLookup_t lookup;
|
||||
lookup.m_PatchFileName = s_SymbolTable.AddString( pPatchMaterialName );
|
||||
do
|
||||
{
|
||||
id = s_MapPatchedMatToOriginalMat.Find( lookup );
|
||||
if( id >= 0 )
|
||||
{
|
||||
NameTranslationLookup_t &found = s_MapPatchedMatToOriginalMat[id];
|
||||
lookup.m_PatchFileName = found.m_OriginalFileName;
|
||||
pRetName = s_SymbolTable.String( found.m_OriginalFileName );
|
||||
}
|
||||
} while( id >= 0 );
|
||||
if( !pRetName )
|
||||
{
|
||||
// This isn't a patched material, so just return the original name.
|
||||
return pPatchMaterialName;
|
||||
}
|
||||
return pRetName;
|
||||
}
|
||||
|
||||
|
||||
void CreateMaterialPatchRecursive( KeyValues *pOriginalKeyValues, KeyValues *pPatchKeyValues, int nKeys, const MaterialPatchInfo_t *pInfo )
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < nKeys; i++ )
|
||||
{
|
||||
const char *pVal = pOriginalKeyValues->GetString( pInfo[i].m_pKey, NULL );
|
||||
if( !pVal )
|
||||
continue;
|
||||
if( pInfo[i].m_pRequiredOriginalValue && Q_stricmp( pVal, pInfo[i].m_pRequiredOriginalValue ) != 0 )
|
||||
continue;
|
||||
pPatchKeyValues->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
|
||||
}
|
||||
KeyValues *pScan;
|
||||
for( pScan = pOriginalKeyValues->GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
|
||||
{
|
||||
CreateMaterialPatchRecursive( pScan, pPatchKeyValues->FindKey( pScan->GetName(), true ), nKeys, pInfo );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A version which allows you to patch multiple key values
|
||||
//-----------------------------------------------------------------------------
|
||||
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
|
||||
int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType )
|
||||
{
|
||||
char pOldVMTFile[ 512 ];
|
||||
char pNewVMTFile[ 512 ];
|
||||
|
||||
AddNewTranslation( pOriginalMaterialName, pNewMaterialName );
|
||||
|
||||
Q_snprintf( pOldVMTFile, 512, "materials/%s.vmt", pOriginalMaterialName );
|
||||
Q_snprintf( pNewVMTFile, 512, "materials/%s.vmt", pNewMaterialName );
|
||||
|
||||
// printf( "Creating material patch file %s which points at %s\n", newVMTFile, oldVMTFile );
|
||||
|
||||
KeyValues *kv = new KeyValues( "patch" );
|
||||
if ( !kv )
|
||||
{
|
||||
Error( "Couldn't allocate KeyValues for %s!!!", pNewMaterialName );
|
||||
}
|
||||
|
||||
kv->SetString( "include", pOldVMTFile );
|
||||
|
||||
const char *pSectionName = (nPatchType == PATCH_INSERT) ? "insert" : "replace";
|
||||
KeyValues *section = kv->FindKey( pSectionName, true );
|
||||
|
||||
if( nPatchType == PATCH_REPLACE )
|
||||
{
|
||||
char name[512];
|
||||
Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pOriginalMaterialName ) );
|
||||
KeyValues *origkv = new KeyValues( "blah" );
|
||||
|
||||
if ( !origkv->LoadFromFile( g_pFileSystem, name ) )
|
||||
{
|
||||
origkv->deleteThis();
|
||||
Assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
CreateMaterialPatchRecursive( origkv, section, nKeys, pInfo );
|
||||
origkv->deleteThis();
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < nKeys; ++i )
|
||||
{
|
||||
section->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
|
||||
}
|
||||
}
|
||||
|
||||
// Write patched .vmt into a memory buffer
|
||||
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
||||
kv->RecursiveSaveToFile( buf, 0 );
|
||||
|
||||
// Add to pak file for this .bsp
|
||||
AddBufferToPak( GetPakFile(), pNewVMTFile, (void*)buf.Base(), buf.TellPut(), true );
|
||||
|
||||
// Cleanup
|
||||
kv->deleteThis();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Patches a single keyvalue in a material
|
||||
//-----------------------------------------------------------------------------
|
||||
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
|
||||
const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType )
|
||||
{
|
||||
MaterialPatchInfo_t info;
|
||||
info.m_pKey = pNewKey;
|
||||
info.m_pValue = pNewValue;
|
||||
CreateMaterialPatch( pOriginalMaterialName, pNewMaterialName, 1, &info, nPatchType );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scan material + all subsections for key
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName )
|
||||
{
|
||||
const char *pVal;
|
||||
pVal = pKeyValues->GetString( pKeyName, NULL );
|
||||
if ( pVal != NULL )
|
||||
return true;
|
||||
|
||||
for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
|
||||
{
|
||||
if ( DoesMaterialHaveKey( pSubKey, pKeyName) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scan material + all subsections for key/value pair
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool DoesMaterialHaveKeyValuePair( KeyValues *pKeyValues, const char *pKeyName, const char *pSearchValue )
|
||||
{
|
||||
const char *pVal;
|
||||
pVal = pKeyValues->GetString( pKeyName, NULL );
|
||||
if ( pVal != NULL && ( Q_stricmp( pSearchValue, pVal ) == 0 ) )
|
||||
return true;
|
||||
|
||||
for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
|
||||
{
|
||||
if ( DoesMaterialHaveKeyValuePair( pSubKey, pKeyName, pSearchValue ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scan material + all subsections for key
|
||||
//-----------------------------------------------------------------------------
|
||||
bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName )
|
||||
{
|
||||
char name[512];
|
||||
Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
|
||||
KeyValues *kv = new KeyValues( "blah" );
|
||||
|
||||
if ( !kv->LoadFromFile( g_pFileSystem, name ) )
|
||||
{
|
||||
kv->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool retVal = DoesMaterialHaveKey( kv, pKeyName );
|
||||
|
||||
kv->deleteThis();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Scan material + all subsections for key/value pair
|
||||
//-----------------------------------------------------------------------------
|
||||
bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue )
|
||||
{
|
||||
char name[512];
|
||||
Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
|
||||
KeyValues *kv = new KeyValues( "blah" );
|
||||
|
||||
if ( !kv->LoadFromFile( g_pFileSystem, name ) )
|
||||
{
|
||||
kv->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool retVal = DoesMaterialHaveKeyValuePair( kv, pKeyName, pSearchValue );
|
||||
|
||||
kv->deleteThis();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets a material value from a material. Ignores all patches
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
|
||||
{
|
||||
char name[512];
|
||||
Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
|
||||
KeyValues *kv = new KeyValues( "blah" );
|
||||
|
||||
if ( !kv->LoadFromFile( g_pFileSystem, name ) )
|
||||
{
|
||||
// Assert( 0 );
|
||||
kv->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *pTmpValue = kv->GetString( pKey, NULL );
|
||||
if( pTmpValue )
|
||||
{
|
||||
Q_strncpy( pValue, pTmpValue, len );
|
||||
}
|
||||
|
||||
kv->deleteThis();
|
||||
return ( pTmpValue != NULL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds the original material associated with a patched material
|
||||
//-----------------------------------------------------------------------------
|
||||
MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain )
|
||||
{
|
||||
MaterialSystemMaterial_t matID;
|
||||
matID = FindMaterial( GetOriginalMaterialNameForPatchedMaterial( materialName ), pFound, bComplain );
|
||||
return matID;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Load keyvalues from the local pack file, or from a file
|
||||
//-----------------------------------------------------------------------------
|
||||
bool LoadKeyValuesFromPackOrFile( const char *pFileName, KeyValues *pKeyValues )
|
||||
{
|
||||
CUtlBuffer buf;
|
||||
if ( ReadFileFromPak( GetPakFile(), pFileName, true, buf ) )
|
||||
{
|
||||
return pKeyValues->LoadFromBuffer( pFileName, buf );
|
||||
}
|
||||
|
||||
return pKeyValues->LoadFromFile( g_pFileSystem, pFileName );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// VMT parser
|
||||
//-----------------------------------------------------------------------------
|
||||
static void InsertKeyValues( KeyValues &dst, KeyValues& src, bool bCheckForExistence )
|
||||
{
|
||||
KeyValues *pSrcVar = src.GetFirstSubKey();
|
||||
while( pSrcVar )
|
||||
{
|
||||
if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
|
||||
{
|
||||
switch( pSrcVar->GetDataType() )
|
||||
{
|
||||
case KeyValues::TYPE_STRING:
|
||||
dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
|
||||
break;
|
||||
case KeyValues::TYPE_INT:
|
||||
dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
|
||||
break;
|
||||
case KeyValues::TYPE_FLOAT:
|
||||
dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
|
||||
break;
|
||||
case KeyValues::TYPE_PTR:
|
||||
dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
pSrcVar = pSrcVar->GetNextKey();
|
||||
}
|
||||
}
|
||||
|
||||
static void ExpandPatchFile( KeyValues &keyValues )
|
||||
{
|
||||
int nCount = 0;
|
||||
while( nCount < 10 && stricmp( keyValues.GetName(), "patch" ) == 0 )
|
||||
{
|
||||
// WriteKeyValuesToFile( "patch.txt", keyValues );
|
||||
const char *pIncludeFileName = keyValues.GetString( "include" );
|
||||
if( !pIncludeFileName )
|
||||
return;
|
||||
|
||||
KeyValues * includeKeyValues = new KeyValues( "vmt" );
|
||||
int nBufLen = Q_strlen( pIncludeFileName ) + Q_strlen( "materials/.vmt" ) + 1;
|
||||
char *pFileName = ( char * )stackalloc( nBufLen );
|
||||
Q_strncpy( pFileName, pIncludeFileName, nBufLen );
|
||||
bool bSuccess = LoadKeyValuesFromPackOrFile( pFileName, includeKeyValues );
|
||||
if ( !bSuccess )
|
||||
{
|
||||
includeKeyValues->deleteThis();
|
||||
return;
|
||||
}
|
||||
|
||||
KeyValues *pInsertSection = keyValues.FindKey( "insert" );
|
||||
if( pInsertSection )
|
||||
{
|
||||
InsertKeyValues( *includeKeyValues, *pInsertSection, false );
|
||||
keyValues = *includeKeyValues;
|
||||
}
|
||||
|
||||
KeyValues *pReplaceSection = keyValues.FindKey( "replace" );
|
||||
if( pReplaceSection )
|
||||
{
|
||||
InsertKeyValues( *includeKeyValues, *pReplaceSection, true );
|
||||
keyValues = *includeKeyValues;
|
||||
}
|
||||
|
||||
// Could add other commands here, like "delete", "rename", etc.
|
||||
|
||||
includeKeyValues->deleteThis();
|
||||
nCount++;
|
||||
}
|
||||
|
||||
if( nCount >= 10 )
|
||||
{
|
||||
Warning( "Infinite recursion in patch file?\n" );
|
||||
}
|
||||
}
|
||||
|
||||
KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags )
|
||||
{
|
||||
// Load the underlying file
|
||||
KeyValues *kv = new KeyValues( "blah" );
|
||||
|
||||
char pFullMaterialName[512];
|
||||
Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
|
||||
if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
|
||||
{
|
||||
// Assert( 0 );
|
||||
kv->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( nFlags & LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH )
|
||||
{
|
||||
ExpandPatchFile( *kv );
|
||||
}
|
||||
|
||||
return kv;
|
||||
}
|
||||
|
||||
void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv )
|
||||
{
|
||||
char pFullMaterialName[512];
|
||||
Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
|
||||
|
||||
// Write patched .vmt into a memory buffer
|
||||
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
||||
kv->RecursiveSaveToFile( buf, 0 );
|
||||
|
||||
// Add to pak file for this .bsp
|
||||
AddBufferToPak( GetPakFile(), pFullMaterialName, (void*)buf.Base(), buf.TellPut(), true );
|
||||
|
||||
// Cleanup
|
||||
kv->deleteThis();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets a keyvalue from a *patched* material
|
||||
//-----------------------------------------------------------------------------
|
||||
bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
|
||||
{
|
||||
// Load the underlying file so that we can check if env_cubemap is in there.
|
||||
KeyValues *kv = new KeyValues( "blah" );
|
||||
|
||||
char pFullMaterialName[512];
|
||||
Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
|
||||
if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
|
||||
{
|
||||
// Assert( 0 );
|
||||
kv->deleteThis();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExpandPatchFile( *kv );
|
||||
|
||||
const char *pTmpValue = kv->GetString( pKey, NULL );
|
||||
if( pTmpValue )
|
||||
{
|
||||
Q_strncpy( pValue, pTmpValue, len );
|
||||
}
|
||||
|
||||
kv->deleteThis();
|
||||
return ( pTmpValue != NULL );
|
||||
}
|
||||
60
mp/src/utils/vbsp/materialpatch.h
Normal file
60
mp/src/utils/vbsp/materialpatch.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MATERIALPATCH_H
|
||||
#define MATERIALPATCH_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "utilmatlib.h"
|
||||
|
||||
struct MaterialPatchInfo_t
|
||||
{
|
||||
const char *m_pKey;
|
||||
const char *m_pRequiredOriginalValue; // NULL if you don't require one.
|
||||
const char *m_pValue;
|
||||
MaterialPatchInfo_t()
|
||||
{
|
||||
memset( this, 0, sizeof( *this ) );
|
||||
}
|
||||
};
|
||||
|
||||
enum MaterialPatchType_t
|
||||
{
|
||||
PATCH_INSERT = 0, // Add the key no matter what
|
||||
PATCH_REPLACE, // Add the key only if it exists
|
||||
};
|
||||
|
||||
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
|
||||
const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType );
|
||||
|
||||
// A version which allows you to use multiple key values
|
||||
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
|
||||
int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType );
|
||||
|
||||
// This gets a keyvalue from the *unpatched* version of the passed-in material
|
||||
bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
|
||||
|
||||
// Gets a keyvalue from a *patched* material
|
||||
bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
|
||||
|
||||
const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName );
|
||||
|
||||
MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain = true );
|
||||
|
||||
bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue );
|
||||
bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName );
|
||||
|
||||
enum LoadMaterialKeyValuesFlags_t
|
||||
{
|
||||
LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH = 1,
|
||||
};
|
||||
|
||||
KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags );
|
||||
void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv );
|
||||
|
||||
#endif // MATERIALPATCH_H
|
||||
90
mp/src/utils/vbsp/materialsub.cpp
Normal file
90
mp/src/utils/vbsp/materialsub.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: This file loads a KeyValues file containing material name mappings.
|
||||
// When the bsp is compiled, all materials listed in the file will
|
||||
// be replaced by the second material in the pair.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "materialsub.h"
|
||||
#include "KeyValues.h"
|
||||
#include "tier1/strtools.h"
|
||||
|
||||
bool g_ReplaceMaterials = false;
|
||||
|
||||
static KeyValues *kv = 0;
|
||||
static KeyValues *allMapKeys = 0;
|
||||
static KeyValues *curMapKeys = 0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads the KeyValues file for materials replacements
|
||||
//-----------------------------------------------------------------------------
|
||||
void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname )
|
||||
{
|
||||
// Careful with static variables
|
||||
if( kv )
|
||||
{
|
||||
kv->deleteThis();
|
||||
kv = 0;
|
||||
}
|
||||
if( allMapKeys )
|
||||
allMapKeys = 0;
|
||||
if( curMapKeys )
|
||||
curMapKeys = 0;
|
||||
|
||||
Msg( "Loading Replacement Keys\n" );
|
||||
|
||||
// Attach the path to the keyValues file
|
||||
char path[1024];
|
||||
Q_snprintf( path, sizeof( path ), "%scfg\\materialsub.cfg", gamedir );
|
||||
|
||||
// Load the keyvalues file
|
||||
kv = new KeyValues( "MaterialReplacements" );
|
||||
|
||||
Msg( "File path: %s", path );
|
||||
if( !kv->LoadFromFile( g_pFileSystem, path ) )
|
||||
{
|
||||
Msg( "Failed to load KeyValues file!\n" );
|
||||
g_ReplaceMaterials = false;
|
||||
kv->deleteThis();
|
||||
kv = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Load global replace keys
|
||||
allMapKeys = kv->FindKey( "AllMaps", true );
|
||||
|
||||
// Load keys for the current map
|
||||
curMapKeys = kv->FindKey( mapname );
|
||||
|
||||
allMapKeys->ChainKeyValue( curMapKeys );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Deletes all keys
|
||||
//-----------------------------------------------------------------------------
|
||||
void DeleteMaterialReplacementKeys( void )
|
||||
{
|
||||
if( kv )
|
||||
{
|
||||
kv->deleteThis();
|
||||
kv = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Replace the passed-in material name with a replacement name, if one exists
|
||||
//-----------------------------------------------------------------------------
|
||||
const char* ReplaceMaterialName( const char *name )
|
||||
{
|
||||
// Look for the material name in the global and map KeyValues
|
||||
// If it's not there, just return the original name
|
||||
|
||||
// HACK: This stinks - KeyValues won't take a string with '/' in it.
|
||||
// If they did, this could be a simple pointer swap.
|
||||
char newName[1024];
|
||||
Q_strncpy( newName, name, sizeof( newName ) );
|
||||
Q_FixSlashes( newName );
|
||||
return allMapKeys->GetString( newName, name );
|
||||
}
|
||||
25
mp/src/utils/vbsp/materialsub.h
Normal file
25
mp/src/utils/vbsp/materialsub.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: This file loads a KeyValues file containing material name mappings.
|
||||
// When the bsp is compiled, all materials listed in the file will
|
||||
// be replaced by the second material in the pair.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef MATERIALSUB_H
|
||||
#define MATERIALSUB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
extern bool g_ReplaceMaterials;
|
||||
|
||||
// Setup / Cleanup
|
||||
void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname );
|
||||
void DeleteMaterialReplacementKeys( void );
|
||||
|
||||
// Takes a material name and returns it's replacement, if there is one.
|
||||
// If there isn't a replacement, it returns the original.
|
||||
const char* ReplaceMaterialName( const char *name );
|
||||
|
||||
#endif // MATERIALSUB_H
|
||||
32
mp/src/utils/vbsp/nodraw.cpp
Normal file
32
mp/src/utils/vbsp/nodraw.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
|
||||
Vector draw_mins, draw_maxs;
|
||||
|
||||
void Draw_ClearWindow (void)
|
||||
{
|
||||
}
|
||||
|
||||
//============================================================
|
||||
|
||||
#define GLSERV_PORT 25001
|
||||
|
||||
|
||||
void GLS_BeginScene (void)
|
||||
{
|
||||
}
|
||||
|
||||
void GLS_Winding (winding_t *w, int code)
|
||||
{
|
||||
}
|
||||
|
||||
void GLS_EndScene (void)
|
||||
{
|
||||
}
|
||||
50
mp/src/utils/vbsp/normals.cpp
Normal file
50
mp/src/utils/vbsp/normals.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "bsplib.h"
|
||||
#include "vbsp.h"
|
||||
|
||||
|
||||
void SaveVertexNormals( void )
|
||||
{
|
||||
int i, j;
|
||||
dface_t *f;
|
||||
texinfo_t *tex;
|
||||
|
||||
|
||||
g_numvertnormalindices = 0;
|
||||
g_numvertnormals = 0;
|
||||
|
||||
for( i = 0 ;i<numfaces ; i++ )
|
||||
{
|
||||
f = &dfaces[i];
|
||||
tex = &texinfo[f->texinfo];
|
||||
|
||||
for( j = 0; j < f->numedges; j++ )
|
||||
{
|
||||
if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
|
||||
{
|
||||
Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES (%d)", MAX_MAP_VERTNORMALINDICES );
|
||||
}
|
||||
|
||||
g_vertnormalindices[g_numvertnormalindices] = g_numvertnormals;
|
||||
g_numvertnormalindices++;
|
||||
}
|
||||
|
||||
// Add this face plane's normal.
|
||||
// Note: this doesn't do an exhaustive vertex normal match because the vrad does it.
|
||||
// The result is that a little extra memory is wasted coming out of vbsp, but it
|
||||
// goes away after vrad.
|
||||
if( g_numvertnormals == MAX_MAP_VERTNORMALS )
|
||||
{
|
||||
Error( "g_numvertnormals == MAX_MAP_VERTNORMALS (%d)", MAX_MAP_VERTNORMALS );
|
||||
}
|
||||
|
||||
g_vertnormals[g_numvertnormals] = dplanes[f->planenum].normal;
|
||||
g_numvertnormals++;
|
||||
}
|
||||
}
|
||||
66
mp/src/utils/vbsp/notes.txt
Normal file
66
mp/src/utils/vbsp/notes.txt
Normal file
@@ -0,0 +1,66 @@
|
||||
// -----------------------------------------------------------------
|
||||
// JAY:
|
||||
//
|
||||
DONE: Fix tools for HL2/TF2 coordinate space
|
||||
DONE: Load textures from Half-Life .WADs
|
||||
DONE: Write out Q2 texture format (not miptex)
|
||||
DONE: Write test map viewer
|
||||
DONE: Test detail brushes
|
||||
DONE: view portals to test
|
||||
NOT DOING:Write out HL style collision trees
|
||||
DONE: new engine loader
|
||||
DONE: new vis in HL2 engine - looks simple now
|
||||
DONE: Do QRAD backwards? i.e. use Valve QRAD, and merge in the Q2 file formats? probably
|
||||
DONE: Integrate Ken's qrad code into qrad3
|
||||
DONE: add area portal visibility to HL2 engine
|
||||
DONE: write area portal entities for HL2/TF2
|
||||
DONE: test area portal code
|
||||
Split clusters for outdoor vis
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
QBSP3 Chop is based on recursive subdivision.
|
||||
- Force natural alignment of some sort to eliminate slivers where brushes meet ?
|
||||
|
||||
|
||||
Use Q2 style ladder indicator? yes
|
||||
Use Q2 style friction indicator? probably or not if physics based
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// CHARLIE:
|
||||
//
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
NOTE: set DISP_PROTO to compile with displacement map info -- not on by default until
|
||||
the prototype is done, the checked in .exe does not have displacement map functionality
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
DONE: put DISP_PROTO defines around all of the displacement code until prototyping is done
|
||||
DONE: add displacement map structure
|
||||
DONE: add displacement face structure
|
||||
DONE: change face/side structures to accept displacement texinfo and displacement face indices
|
||||
DONE: change .map loader to parse displacment map info
|
||||
DONE: don't allow merge or subdivision of displacement faces
|
||||
DONE: when splitting brushes, the new generated side get initialized to -1
|
||||
DONE: add find displacement face functionality, then create it if not found
|
||||
DONE: add find displacement map functionality, then create it if not found
|
||||
DONE: initialize the displacement data before loading the .map file
|
||||
initialize the face data with dispface and dispmap = -1, is this necessary????
|
||||
DONE: copy from bsp tool face to bsp file face -- the displacement info
|
||||
DONE: add/copy lumps
|
||||
DONE: swap data for writing to bsp -- not really necessary, but to keep in sync with the rest
|
||||
DONE: write .bsp data out
|
||||
DONE: add displacement data to .bsp statistics -- print file
|
||||
|
||||
Test maps:
|
||||
DONE: map where disp face gets split by block node
|
||||
DONE: map where disp face attempts merge/split
|
||||
DONE: map with texture disp face
|
||||
DONE: map with lots of disp faces
|
||||
DONE: map with multiple disp faces referencing one map
|
||||
DONE: map with multiple disp faces on one brush
|
||||
DONE: map with multiple disp faces on one brush referencing one map
|
||||
DONE: map with funky texture split encased on one portal
|
||||
|
||||
//------------------------------------------------------------------
|
||||
487
mp/src/utils/vbsp/overlay.cpp
Normal file
487
mp/src/utils/vbsp/overlay.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "disp_vbsp.h"
|
||||
#include "builddisp.h"
|
||||
#include "mathlib/vmatrix.h"
|
||||
|
||||
void Overlay_BuildBasisOrigin( doverlay_t *pOverlay );
|
||||
|
||||
// Overlay list.
|
||||
CUtlVector<mapoverlay_t> g_aMapOverlays;
|
||||
CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
int Overlay_GetFromEntity( entity_t *pMapEnt )
|
||||
{
|
||||
int iAccessorID = -1;
|
||||
|
||||
// Allocate the new overlay.
|
||||
int iOverlay = g_aMapOverlays.AddToTail();
|
||||
mapoverlay_t *pMapOverlay = &g_aMapOverlays[iOverlay];
|
||||
|
||||
// Get the overlay data.
|
||||
pMapOverlay->nId = g_aMapOverlays.Count() - 1;
|
||||
|
||||
if ( ValueForKey( pMapEnt, "targetname" )[ 0 ] != '\0' )
|
||||
{
|
||||
// Overlay has a name, remember it's ID for accessing
|
||||
iAccessorID = pMapOverlay->nId;
|
||||
}
|
||||
|
||||
pMapOverlay->flU[0] = FloatForKey( pMapEnt, "StartU" );
|
||||
pMapOverlay->flU[1] = FloatForKey( pMapEnt, "EndU" );
|
||||
pMapOverlay->flV[0] = FloatForKey( pMapEnt, "StartV" );
|
||||
pMapOverlay->flV[1] = FloatForKey( pMapEnt, "EndV" );
|
||||
|
||||
pMapOverlay->flFadeDistMinSq = FloatForKey( pMapEnt, "fademindist" );
|
||||
if ( pMapOverlay->flFadeDistMinSq > 0 )
|
||||
{
|
||||
pMapOverlay->flFadeDistMinSq *= pMapOverlay->flFadeDistMinSq;
|
||||
}
|
||||
|
||||
pMapOverlay->flFadeDistMaxSq = FloatForKey( pMapEnt, "fademaxdist" );
|
||||
if ( pMapOverlay->flFadeDistMaxSq > 0 )
|
||||
{
|
||||
pMapOverlay->flFadeDistMaxSq *= pMapOverlay->flFadeDistMaxSq;
|
||||
}
|
||||
|
||||
GetVectorForKey( pMapEnt, "BasisOrigin", pMapOverlay->vecOrigin );
|
||||
|
||||
pMapOverlay->m_nRenderOrder = IntForKey( pMapEnt, "RenderOrder" );
|
||||
if ( pMapOverlay->m_nRenderOrder < 0 || pMapOverlay->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
|
||||
Error( "Overlay (%s) at %f %f %f has invalid render order (%d).\n", ValueForKey( pMapEnt, "material" ),
|
||||
pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z,
|
||||
pMapOverlay->m_nRenderOrder );
|
||||
|
||||
GetVectorForKey( pMapEnt, "uv0", pMapOverlay->vecUVPoints[0] );
|
||||
GetVectorForKey( pMapEnt, "uv1", pMapOverlay->vecUVPoints[1] );
|
||||
GetVectorForKey( pMapEnt, "uv2", pMapOverlay->vecUVPoints[2] );
|
||||
GetVectorForKey( pMapEnt, "uv3", pMapOverlay->vecUVPoints[3] );
|
||||
|
||||
GetVectorForKey( pMapEnt, "BasisU", pMapOverlay->vecBasis[0] );
|
||||
GetVectorForKey( pMapEnt, "BasisV", pMapOverlay->vecBasis[1] );
|
||||
GetVectorForKey( pMapEnt, "BasisNormal", pMapOverlay->vecBasis[2] );
|
||||
|
||||
const char *pMaterialName = ValueForKey( pMapEnt, "material" );
|
||||
Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
|
||||
if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
|
||||
{
|
||||
Error( "Overlay Material Name (%s) too long! > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
|
||||
return -1;
|
||||
}
|
||||
strcpy( pMapOverlay->szMaterialName, pMaterialName );
|
||||
|
||||
// Convert the sidelist to side id(s).
|
||||
const char *pSideList = ValueForKey( pMapEnt, "sides" );
|
||||
char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
|
||||
strcpy( pTmpList, pSideList );
|
||||
const char *pScan = strtok( pTmpList, " " );
|
||||
if ( !pScan )
|
||||
return iAccessorID;
|
||||
|
||||
pMapOverlay->aSideList.Purge();
|
||||
pMapOverlay->aFaceList.Purge();
|
||||
|
||||
do
|
||||
{
|
||||
int nSideId;
|
||||
if ( sscanf( pScan, "%d", &nSideId ) == 1 )
|
||||
{
|
||||
pMapOverlay->aSideList.AddToTail( nSideId );
|
||||
}
|
||||
} while ( ( pScan = strtok( NULL, " " ) ) );
|
||||
|
||||
return iAccessorID;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
side_t *GetSide( int nSideId )
|
||||
{
|
||||
for( int iSide = 0; iSide < g_LoadingMap->nummapbrushsides; ++iSide )
|
||||
{
|
||||
if ( g_LoadingMap->brushsides[iSide].id == nSideId )
|
||||
return &g_LoadingMap->brushsides[iSide];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void Overlay_UpdateSideLists( int StartIndex )
|
||||
{
|
||||
int nMapOverlayCount = g_aMapOverlays.Count();
|
||||
for( int iMapOverlay = StartIndex; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
|
||||
{
|
||||
mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( iMapOverlay );
|
||||
if ( pMapOverlay )
|
||||
{
|
||||
int nSideCount = pMapOverlay->aSideList.Count();
|
||||
for( int iSide = 0; iSide < nSideCount; ++iSide )
|
||||
{
|
||||
side_t *pSide = GetSide( pMapOverlay->aSideList[iSide] );
|
||||
if ( pSide )
|
||||
{
|
||||
if ( pSide->aOverlayIds.Find( pMapOverlay->nId ) == -1 )
|
||||
{
|
||||
pSide->aOverlayIds.AddToTail( pMapOverlay->nId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void OverlayTransition_UpdateSideLists( int StartIndex )
|
||||
{
|
||||
int nOverlayCount = g_aMapWaterOverlays.Count();
|
||||
for( int iOverlay = StartIndex; iOverlay < nOverlayCount; ++iOverlay )
|
||||
{
|
||||
mapoverlay_t *pOverlay = &g_aMapWaterOverlays.Element( iOverlay );
|
||||
if ( pOverlay )
|
||||
{
|
||||
int nSideCount = pOverlay->aSideList.Count();
|
||||
for( int iSide = 0; iSide < nSideCount; ++iSide )
|
||||
{
|
||||
side_t *pSide = GetSide( pOverlay->aSideList[iSide] );
|
||||
if ( pSide )
|
||||
{
|
||||
if ( pSide->aWaterOverlayIds.Find( pOverlay->nId ) == -1 )
|
||||
{
|
||||
pSide->aWaterOverlayIds.AddToTail( pOverlay->nId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void Overlay_AddFaceToLists( int iFace, side_t *pSide )
|
||||
{
|
||||
int nOverlayIdCount = pSide->aOverlayIds.Count();
|
||||
for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
|
||||
{
|
||||
mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( pSide->aOverlayIds[iOverlayId] );
|
||||
if ( pMapOverlay )
|
||||
{
|
||||
if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
|
||||
{
|
||||
pMapOverlay->aFaceList.AddToTail( iFace );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide )
|
||||
{
|
||||
int nOverlayIdCount = pSide->aWaterOverlayIds.Count();
|
||||
for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
|
||||
{
|
||||
mapoverlay_t *pMapOverlay = &g_aMapWaterOverlays.Element( pSide->aWaterOverlayIds[iOverlayId] - ( MAX_MAP_OVERLAYS + 1 ) );
|
||||
if ( pMapOverlay )
|
||||
{
|
||||
if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
|
||||
{
|
||||
pMapOverlay->aFaceList.AddToTail( iFace );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void Overlay_EmitOverlayFace( mapoverlay_t *pMapOverlay )
|
||||
{
|
||||
Assert( g_nOverlayCount < MAX_MAP_OVERLAYS );
|
||||
if ( g_nOverlayCount >= MAX_MAP_OVERLAYS )
|
||||
{
|
||||
Error ( "Too Many Overlays!\nMAX_MAP_OVERLAYS = %d", MAX_MAP_OVERLAYS );
|
||||
return;
|
||||
}
|
||||
|
||||
doverlay_t *pOverlay = &g_Overlays[g_nOverlayCount];
|
||||
doverlayfade_t *pOverlayFade = &g_OverlayFades[g_nOverlayCount];
|
||||
|
||||
g_nOverlayCount++;
|
||||
|
||||
// Conver the map overlay into a .bsp overlay (doverlay_t).
|
||||
if ( pOverlay )
|
||||
{
|
||||
pOverlay->nId = pMapOverlay->nId;
|
||||
|
||||
pOverlay->flU[0] = pMapOverlay->flU[0];
|
||||
pOverlay->flU[1] = pMapOverlay->flU[1];
|
||||
pOverlay->flV[0] = pMapOverlay->flV[0];
|
||||
pOverlay->flV[1] = pMapOverlay->flV[1];
|
||||
|
||||
VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
|
||||
|
||||
VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
|
||||
|
||||
VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
|
||||
|
||||
pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
|
||||
|
||||
// Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
|
||||
pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
|
||||
pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
|
||||
pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
|
||||
|
||||
// Encode whether or not the v axis should be flipped.
|
||||
Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
|
||||
if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
|
||||
{
|
||||
pOverlay->vecUVPoints[3].z = 1.0f;
|
||||
}
|
||||
|
||||
// Texinfo.
|
||||
texinfo_t texInfo;
|
||||
texInfo.flags = 0;
|
||||
texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
|
||||
for( int iVec = 0; iVec < 2; ++iVec )
|
||||
{
|
||||
for( int iAxis = 0; iAxis < 3; ++iAxis )
|
||||
{
|
||||
texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
|
||||
texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
|
||||
}
|
||||
|
||||
texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
|
||||
texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
|
||||
}
|
||||
pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
|
||||
|
||||
// Face List
|
||||
int nFaceCount = pMapOverlay->aFaceList.Count();
|
||||
Assert( nFaceCount < OVERLAY_BSP_FACE_COUNT );
|
||||
if ( nFaceCount >= OVERLAY_BSP_FACE_COUNT )
|
||||
{
|
||||
Error( "Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
|
||||
return;
|
||||
}
|
||||
|
||||
pOverlay->SetFaceCount( nFaceCount );
|
||||
for( int iFace = 0; iFace < nFaceCount; ++iFace )
|
||||
{
|
||||
pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the map overlay fade data into a .bsp overlay fade (doverlayfade_t).
|
||||
if ( pOverlayFade )
|
||||
{
|
||||
pOverlayFade->flFadeDistMinSq = pMapOverlay->flFadeDistMinSq;
|
||||
pOverlayFade->flFadeDistMaxSq = pMapOverlay->flFadeDistMaxSq;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
void OverlayTransition_EmitOverlayFace( mapoverlay_t *pMapOverlay )
|
||||
{
|
||||
Assert( g_nWaterOverlayCount < MAX_MAP_WATEROVERLAYS );
|
||||
if ( g_nWaterOverlayCount >= MAX_MAP_WATEROVERLAYS )
|
||||
{
|
||||
Error ( "Too many water overlays!\nMAX_MAP_WATEROVERLAYS = %d", MAX_MAP_WATEROVERLAYS );
|
||||
return;
|
||||
}
|
||||
|
||||
dwateroverlay_t *pOverlay = &g_WaterOverlays[g_nWaterOverlayCount];
|
||||
g_nWaterOverlayCount++;
|
||||
|
||||
// Conver the map overlay into a .bsp overlay (doverlay_t).
|
||||
if ( pOverlay )
|
||||
{
|
||||
pOverlay->nId = pMapOverlay->nId;
|
||||
|
||||
pOverlay->flU[0] = pMapOverlay->flU[0];
|
||||
pOverlay->flU[1] = pMapOverlay->flU[1];
|
||||
pOverlay->flV[0] = pMapOverlay->flV[0];
|
||||
pOverlay->flV[1] = pMapOverlay->flV[1];
|
||||
|
||||
VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
|
||||
VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
|
||||
|
||||
VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
|
||||
|
||||
VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
|
||||
|
||||
pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
|
||||
|
||||
// Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
|
||||
pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
|
||||
pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
|
||||
pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
|
||||
|
||||
// Encode whether or not the v axis should be flipped.
|
||||
Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
|
||||
if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
|
||||
{
|
||||
pOverlay->vecUVPoints[3].z = 1.0f;
|
||||
}
|
||||
|
||||
// Texinfo.
|
||||
texinfo_t texInfo;
|
||||
texInfo.flags = 0;
|
||||
texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
|
||||
for( int iVec = 0; iVec < 2; ++iVec )
|
||||
{
|
||||
for( int iAxis = 0; iAxis < 3; ++iAxis )
|
||||
{
|
||||
texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
|
||||
texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
|
||||
}
|
||||
|
||||
texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
|
||||
texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
|
||||
}
|
||||
pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
|
||||
|
||||
// Face List
|
||||
int nFaceCount = pMapOverlay->aFaceList.Count();
|
||||
Assert( nFaceCount < WATEROVERLAY_BSP_FACE_COUNT );
|
||||
if ( nFaceCount >= WATEROVERLAY_BSP_FACE_COUNT )
|
||||
{
|
||||
Error( "Water Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
|
||||
return;
|
||||
}
|
||||
|
||||
pOverlay->SetFaceCount( nFaceCount );
|
||||
for( int iFace = 0; iFace < nFaceCount; ++iFace )
|
||||
{
|
||||
pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void Overlay_EmitOverlayFaces( void )
|
||||
{
|
||||
int nMapOverlayCount = g_aMapOverlays.Count();
|
||||
for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
|
||||
{
|
||||
Overlay_EmitOverlayFace( &g_aMapOverlays.Element( iMapOverlay ) );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//-----------------------------------------------------------------------------
|
||||
void OverlayTransition_EmitOverlayFaces( void )
|
||||
{
|
||||
int nMapOverlayCount = g_aMapWaterOverlays.Count();
|
||||
for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
|
||||
{
|
||||
OverlayTransition_EmitOverlayFace( &g_aMapWaterOverlays.Element( iMapOverlay ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// These routines were mostly stolen from MapOverlay.cpp in Hammer
|
||||
//-----------------------------------------------------------------------------
|
||||
#define OVERLAY_BASIS_U 0
|
||||
#define OVERLAY_BASIS_V 1
|
||||
#define OVERLAY_BASIS_NORMAL 2
|
||||
#define OVERLAY_HANDLES_COUNT 4
|
||||
|
||||
|
||||
inline void TransformPoint( const VMatrix& matrix, Vector &point )
|
||||
{
|
||||
Vector orgVector = point;
|
||||
matrix.V3Mul( orgVector, point );
|
||||
}
|
||||
|
||||
|
||||
inline bool fequal( float value, float target, float delta) { return ( (value<(target+delta))&&(value>(target-delta)) ); }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: this function translate / rotate an overlay.
|
||||
// Input : pOverlay - the overlay to be translated
|
||||
// OriginOffset - the translation
|
||||
// AngleOffset - the rotation
|
||||
// Matrix - the translation / rotation matrix
|
||||
// Output : none
|
||||
//-----------------------------------------------------------------------------
|
||||
void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix )
|
||||
{
|
||||
VMatrix tmpMatrix( Matrix );
|
||||
|
||||
Vector temp = pOverlay->vecOrigin;
|
||||
VectorTransform( temp, Matrix, pOverlay->vecOrigin );
|
||||
|
||||
// erase move component
|
||||
tmpMatrix.SetTranslation( vec3_origin );
|
||||
|
||||
// check if matrix would still change something
|
||||
if ( !tmpMatrix.IsIdentity() )
|
||||
{
|
||||
// make sure axes are normalized (they should be anyways)
|
||||
pOverlay->vecBasis[OVERLAY_BASIS_U].NormalizeInPlace();
|
||||
pOverlay->vecBasis[OVERLAY_BASIS_V].NormalizeInPlace();
|
||||
|
||||
Vector vecU = pOverlay->vecBasis[OVERLAY_BASIS_U];
|
||||
Vector vecV = pOverlay->vecBasis[OVERLAY_BASIS_V];
|
||||
Vector vecNormal = pOverlay->vecBasis[OVERLAY_BASIS_NORMAL];
|
||||
|
||||
TransformPoint( tmpMatrix, vecU );
|
||||
TransformPoint( tmpMatrix, vecV );
|
||||
TransformPoint( tmpMatrix, vecNormal );
|
||||
|
||||
float fScaleU = vecU.Length();
|
||||
float fScaleV = vecV.Length();
|
||||
float flScaleNormal = vecNormal.Length();
|
||||
|
||||
bool bIsUnit = ( fequal( fScaleU, 1.0f, 0.0001 ) && fequal( fScaleV, 1.0f, 0.0001 ) && fequal( flScaleNormal, 1.0f, 0.0001 ) );
|
||||
bool bIsPerp = ( fequal( DotProduct( vecU, vecV ), 0.0f, 0.0025 ) && fequal( DotProduct( vecU, vecNormal ), 0.0f, 0.0025 ) && fequal( DotProduct( vecV, vecNormal ), 0.0f, 0.0025 ) );
|
||||
|
||||
// if ( fequal(fScaleU,1,0.0001) && fequal(fScaleV,1,0.0001) && fequal(DotProduct( vecU, vecV ),0,0.0025) )
|
||||
if ( bIsUnit && bIsPerp )
|
||||
{
|
||||
// transformation doesnt scale or shear anything, so just update base axes
|
||||
pOverlay->vecBasis[OVERLAY_BASIS_U] = vecU;
|
||||
pOverlay->vecBasis[OVERLAY_BASIS_V] = vecV;
|
||||
pOverlay->vecBasis[OVERLAY_BASIS_NORMAL] = vecNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
// more complex transformation, move UV coordinates, but leave base axes
|
||||
for ( int iHandle=0; iHandle<OVERLAY_HANDLES_COUNT;iHandle++)
|
||||
{
|
||||
Vector vecUV = pOverlay->vecUVPoints[iHandle];
|
||||
Vector vecPos = ( vecUV.x * pOverlay->vecBasis[OVERLAY_BASIS_U] + vecUV.y * pOverlay->vecBasis[OVERLAY_BASIS_V] );
|
||||
|
||||
// to transform in world space
|
||||
TransformPoint( tmpMatrix, vecPos );
|
||||
|
||||
vecUV.x = pOverlay->vecBasis[OVERLAY_BASIS_U].Dot( vecPos );
|
||||
vecUV.y = pOverlay->vecBasis[OVERLAY_BASIS_V].Dot( vecPos );
|
||||
|
||||
pOverlay->vecUVPoints[iHandle] = vecUV;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
1684
mp/src/utils/vbsp/portals.cpp
Normal file
1684
mp/src/utils/vbsp/portals.cpp
Normal file
File diff suppressed because it is too large
Load Diff
19
mp/src/utils/vbsp/portals.h
Normal file
19
mp/src/utils/vbsp/portals.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PORTALS_H
|
||||
#define PORTALS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
// Sets up the g_ClipPortalIndices array.
|
||||
void TranslateClipPortalIndices();
|
||||
|
||||
|
||||
#endif // PORTALS_H
|
||||
374
mp/src/utils/vbsp/prtfile.cpp
Normal file
374
mp/src/utils/vbsp/prtfile.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "collisionutils.h"
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PORTAL FILE GENERATION
|
||||
|
||||
Save out name.prt for qvis to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
#define PORTALFILE "PRT1"
|
||||
|
||||
struct cluster_portals_t
|
||||
{
|
||||
CUtlVector<portal_t *> portals;
|
||||
};
|
||||
|
||||
int num_visclusters; // clusters the player can be in
|
||||
int num_visportals;
|
||||
|
||||
int g_SkyCluster = -1;
|
||||
|
||||
void WriteFloat (FILE *f, vec_t v)
|
||||
{
|
||||
if ( fabs(v - RoundInt(v)) < 0.001 )
|
||||
fprintf (f,"%i ",(int)RoundInt(v));
|
||||
else
|
||||
fprintf (f,"%f ",v);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
WritePortalFile_r
|
||||
=================
|
||||
*/
|
||||
void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list)
|
||||
{
|
||||
portal_t *p;
|
||||
winding_t *w;
|
||||
Vector normal;
|
||||
vec_t dist;
|
||||
|
||||
for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ )
|
||||
{
|
||||
for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ )
|
||||
{
|
||||
p = list[clusterIndex].portals[j];
|
||||
w = p->winding;
|
||||
// write out to the file
|
||||
|
||||
// sometimes planes get turned around when they are very near
|
||||
// the changeover point between different axis. interpret the
|
||||
// plane the same way vis will, and flip the side orders if needed
|
||||
// FIXME: is this still relevent?
|
||||
WindingPlane (w, normal, &dist);
|
||||
if ( DotProduct (p->plane.normal, normal) < 0.99 )
|
||||
{ // backwards...
|
||||
fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
|
||||
}
|
||||
|
||||
for (int i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
fprintf (pFile,"(");
|
||||
WriteFloat (pFile, w->p[i][0]);
|
||||
WriteFloat (pFile, w->p[i][1]);
|
||||
WriteFloat (pFile, w->p[i][2]);
|
||||
fprintf (pFile,") ");
|
||||
}
|
||||
fprintf (pFile,"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct viscluster_t
|
||||
{
|
||||
bspbrush_t *pBrushes;
|
||||
int clusterIndex;
|
||||
int leafCount;
|
||||
int leafStart;
|
||||
};
|
||||
|
||||
static CUtlVector<viscluster_t> g_VisClusters;
|
||||
|
||||
// add to the list of brushes the merge leaves into single vis clusters
|
||||
void AddVisCluster( entity_t *pFuncVisCluster )
|
||||
{
|
||||
viscluster_t tmp;
|
||||
Vector clipMins, clipMaxs;
|
||||
clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER;
|
||||
clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER;
|
||||
|
||||
// build the map brushes out into the minimum non-overlapping set of brushes
|
||||
bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes,
|
||||
clipMins, clipMaxs, NO_DETAIL);
|
||||
tmp.pBrushes = ChopBrushes( pBSPBrush );
|
||||
|
||||
// store the entry in the list
|
||||
tmp.clusterIndex = -1;
|
||||
tmp.leafCount = 0;
|
||||
tmp.leafStart = 0;
|
||||
|
||||
#if DEBUG_VISUALIZE_CLUSTERS
|
||||
int debug = atoi(ValueForKey(pFuncVisCluster,"debug"));
|
||||
if ( debug )
|
||||
{
|
||||
Msg("Debug vis cluster %d\n", g_VisClusters.Count() );
|
||||
}
|
||||
#endif
|
||||
|
||||
g_VisClusters.AddToTail( tmp );
|
||||
|
||||
// clear out this entity so it won't get written to the bsp
|
||||
pFuncVisCluster->epairs = NULL;
|
||||
pFuncVisCluster->numbrushes = 0;
|
||||
}
|
||||
|
||||
// returns the total overlapping volume of intersection between the node and the brush list
|
||||
float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode )
|
||||
{
|
||||
float volume = 0.0f;
|
||||
for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next )
|
||||
{
|
||||
if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) )
|
||||
{
|
||||
bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush );
|
||||
if ( pIntersect )
|
||||
{
|
||||
volume += BrushVolume( pIntersect );
|
||||
FreeBrush( pIntersect );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
// Search for a forced cluster that this node is within
|
||||
// NOTE: Returns the first one found, these won't merge themselves together
|
||||
int GetVisCluster( node_t *pNode )
|
||||
{
|
||||
float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap
|
||||
int maxIndex = -1;
|
||||
// UNDONE: This could get slow
|
||||
for ( int i = g_VisClusters.Count(); --i >= 0; )
|
||||
{
|
||||
float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode );
|
||||
if ( volume > maxVolume )
|
||||
{
|
||||
volume = maxVolume;
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
return maxIndex;
|
||||
}
|
||||
/*
|
||||
================
|
||||
NumberLeafs_r
|
||||
================
|
||||
*/
|
||||
void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{ // decision node
|
||||
node->cluster = -99;
|
||||
BuildVisLeafList_r (node->children[0], leaves);
|
||||
BuildVisLeafList_r (node->children[1], leaves);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->contents & CONTENTS_SOLID )
|
||||
{ // solid block, viewpoint never inside
|
||||
node->cluster = -1;
|
||||
return;
|
||||
}
|
||||
leaves.AddToTail(node);
|
||||
}
|
||||
|
||||
// Give each leaf in the list of empty leaves a vis cluster number
|
||||
// some are within func_viscluster volumes and will be merged together
|
||||
// every other leaf gets its own unique number
|
||||
void NumberLeafs( const CUtlVector<node_t *> &leaves )
|
||||
{
|
||||
for ( int i = 0; i < leaves.Count(); i++ )
|
||||
{
|
||||
node_t *node = leaves[i];
|
||||
int visCluster = GetVisCluster( node );
|
||||
if ( visCluster >= 0 )
|
||||
{
|
||||
if ( g_VisClusters[visCluster].clusterIndex < 0 )
|
||||
{
|
||||
g_VisClusters[visCluster].clusterIndex = num_visclusters;
|
||||
num_visclusters++;
|
||||
}
|
||||
g_VisClusters[visCluster].leafCount++;
|
||||
node->cluster = g_VisClusters[visCluster].clusterIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) )
|
||||
{
|
||||
if ( g_SkyCluster < 0 )
|
||||
{
|
||||
// allocate a cluster for the sky
|
||||
g_SkyCluster = num_visclusters;
|
||||
num_visclusters++;
|
||||
}
|
||||
node->cluster = g_SkyCluster;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->cluster = num_visclusters;
|
||||
num_visclusters++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_VISUALIZE_CLUSTERS
|
||||
for ( int i = 0; i < g_VisClusters.Count(); i++ )
|
||||
{
|
||||
char name[256];
|
||||
sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i );
|
||||
FileHandle_t fp = g_pFileSystem->Open( name, "w" );
|
||||
Msg("Writing %s\n", name );
|
||||
for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next )
|
||||
{
|
||||
for (int i = 0; i < pBrush->numsides; i++ )
|
||||
OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 );
|
||||
}
|
||||
for ( int j = 0; j < leaves.Count(); j++ )
|
||||
{
|
||||
if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex )
|
||||
{
|
||||
bspbrush_t *pBrush = leaves[j]->volume;
|
||||
for (int k = 0; k < pBrush->numsides; k++ )
|
||||
OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) );
|
||||
}
|
||||
}
|
||||
g_pFileSystem->Close(fp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// build a list of all vis portals that connect clusters
|
||||
int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves )
|
||||
{
|
||||
int portalCount = 0;
|
||||
for ( int i = 0; i < leaves.Count(); i++ )
|
||||
{
|
||||
node_t *node = leaves[i];
|
||||
// count the portals
|
||||
for (portal_t *p = node->portals ; p ; )
|
||||
{
|
||||
if (p->nodes[0] == node) // only write out from first leaf
|
||||
{
|
||||
if ( p->nodes[0]->cluster != p->nodes[1]->cluster )
|
||||
{
|
||||
if (Portal_VisFlood (p))
|
||||
{
|
||||
portalCount++;
|
||||
portalList[node->cluster].portals.AddToTail(p);
|
||||
}
|
||||
}
|
||||
p = p->next[0];
|
||||
}
|
||||
else
|
||||
p = p->next[1];
|
||||
}
|
||||
}
|
||||
return portalCount;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CreateVisPortals_r
|
||||
================
|
||||
*/
|
||||
void CreateVisPortals_r (node_t *node)
|
||||
{
|
||||
// stop as soon as we get to a leaf
|
||||
if (node->planenum == PLANENUM_LEAF )
|
||||
return;
|
||||
|
||||
MakeNodePortal (node);
|
||||
SplitNodePortals (node);
|
||||
|
||||
CreateVisPortals_r (node->children[0]);
|
||||
CreateVisPortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
int clusterleaf;
|
||||
void SaveClusters_r (node_t *node)
|
||||
{
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
dleafs[clusterleaf++].cluster = node->cluster;
|
||||
return;
|
||||
}
|
||||
SaveClusters_r (node->children[0]);
|
||||
SaveClusters_r (node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
WritePortalFile
|
||||
================
|
||||
*/
|
||||
void WritePortalFile (tree_t *tree)
|
||||
{
|
||||
char filename[1024];
|
||||
node_t *headnode;
|
||||
int start = Plat_FloatTime();
|
||||
|
||||
qprintf ("--- WritePortalFile ---\n");
|
||||
|
||||
sprintf (filename, "%s.prt", source);
|
||||
Msg ("writing %s...", filename);
|
||||
|
||||
headnode = tree->headnode;
|
||||
|
||||
FreeTreePortals_r (headnode);
|
||||
MakeHeadnodePortals (tree);
|
||||
|
||||
CreateVisPortals_r (headnode);
|
||||
|
||||
// set the cluster field in every leaf and count the total number of portals
|
||||
num_visclusters = 0;
|
||||
Msg("Building visibility clusters...\n");
|
||||
CUtlVector<node_t *> leaves;
|
||||
BuildVisLeafList_r( headnode, leaves );
|
||||
|
||||
NumberLeafs (leaves);
|
||||
CUtlVector<cluster_portals_t> portalList;
|
||||
portalList.SetCount( num_visclusters );
|
||||
num_visportals = BuildPortalList( portalList, leaves );
|
||||
// write the file
|
||||
FILE *pf = fopen (filename, "w");
|
||||
if (!pf)
|
||||
Error ("Error opening %s", filename);
|
||||
|
||||
fprintf (pf, "%s\n", PORTALFILE);
|
||||
fprintf (pf, "%i\n", num_visclusters);
|
||||
fprintf (pf, "%i\n", num_visportals);
|
||||
|
||||
qprintf ("%5i visclusters\n", num_visclusters);
|
||||
qprintf ("%5i visportals\n", num_visportals);
|
||||
|
||||
WritePortalFile(pf, portalList);
|
||||
|
||||
fclose (pf);
|
||||
|
||||
// we need to store the clusters out now because ordering
|
||||
// issues made us do this after writebsp...
|
||||
clusterleaf = 1;
|
||||
SaveClusters_r (headnode);
|
||||
|
||||
Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
|
||||
}
|
||||
|
||||
741
mp/src/utils/vbsp/staticprop.cpp
Normal file
741
mp/src/utils/vbsp/staticprop.cpp
Normal file
@@ -0,0 +1,741 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Places "detail" objects which are client-only renderable things
|
||||
//
|
||||
// $Revision: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "bsplib.h"
|
||||
#include "utlvector.h"
|
||||
#include "bspfile.h"
|
||||
#include "gamebspfile.h"
|
||||
#include "VPhysics_Interface.h"
|
||||
#include "Studio.h"
|
||||
#include "byteswap.h"
|
||||
#include "UtlBuffer.h"
|
||||
#include "CollisionUtils.h"
|
||||
#include <float.h>
|
||||
#include "CModel.h"
|
||||
#include "PhysDll.h"
|
||||
#include "utlsymbol.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "KeyValues.h"
|
||||
|
||||
static void SetCurrentModel( studiohdr_t *pStudioHdr );
|
||||
static void FreeCurrentModelVertexes();
|
||||
|
||||
IPhysicsCollision *s_pPhysCollision = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// These puppies are used to construct the game lumps
|
||||
//-----------------------------------------------------------------------------
|
||||
static CUtlVector<StaticPropDictLump_t> s_StaticPropDictLump;
|
||||
static CUtlVector<StaticPropLump_t> s_StaticPropLump;
|
||||
static CUtlVector<StaticPropLeafLump_t> s_StaticPropLeafLump;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Used to build the static prop
|
||||
//-----------------------------------------------------------------------------
|
||||
struct StaticPropBuild_t
|
||||
{
|
||||
char const* m_pModelName;
|
||||
char const* m_pLightingOrigin;
|
||||
Vector m_Origin;
|
||||
QAngle m_Angles;
|
||||
int m_Solid;
|
||||
int m_Skin;
|
||||
int m_Flags;
|
||||
float m_FadeMinDist;
|
||||
float m_FadeMaxDist;
|
||||
bool m_FadesOut;
|
||||
float m_flForcedFadeScale;
|
||||
unsigned short m_nMinDXLevel;
|
||||
unsigned short m_nMaxDXLevel;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Used to cache collision model generation
|
||||
//-----------------------------------------------------------------------------
|
||||
struct ModelCollisionLookup_t
|
||||
{
|
||||
CUtlSymbol m_Name;
|
||||
CPhysCollide* m_pCollide;
|
||||
};
|
||||
|
||||
static bool ModelLess( ModelCollisionLookup_t const& src1, ModelCollisionLookup_t const& src2 )
|
||||
{
|
||||
return src1.m_Name < src2.m_Name;
|
||||
}
|
||||
|
||||
static CUtlRBTree<ModelCollisionLookup_t, unsigned short> s_ModelCollisionCache( 0, 32, ModelLess );
|
||||
static CUtlVector<int> s_LightingInfo;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Gets the keyvalues from a studiohdr
|
||||
//-----------------------------------------------------------------------------
|
||||
bool StudioKeyValues( studiohdr_t* pStudioHdr, KeyValues *pValue )
|
||||
{
|
||||
if ( !pStudioHdr )
|
||||
return false;
|
||||
|
||||
return pValue->LoadFromBuffer( pStudioHdr->pszName(), pStudioHdr->KeyValueText() );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Makes sure the studio model is a static prop
|
||||
//-----------------------------------------------------------------------------
|
||||
enum isstaticprop_ret
|
||||
{
|
||||
RET_VALID,
|
||||
RET_FAIL_NOT_MARKED_STATIC_PROP,
|
||||
RET_FAIL_DYNAMIC,
|
||||
};
|
||||
|
||||
isstaticprop_ret IsStaticProp( studiohdr_t* pHdr )
|
||||
{
|
||||
if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
|
||||
return RET_FAIL_NOT_MARKED_STATIC_PROP;
|
||||
|
||||
// If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static
|
||||
KeyValues *modelKeyValues = new KeyValues(pHdr->pszName());
|
||||
if ( StudioKeyValues( pHdr, modelKeyValues ) )
|
||||
{
|
||||
KeyValues *sub = modelKeyValues->FindKey("prop_data");
|
||||
if ( sub )
|
||||
{
|
||||
if ( !(sub->GetInt( "allowstatic", 0 )) )
|
||||
{
|
||||
modelKeyValues->deleteThis();
|
||||
return RET_FAIL_DYNAMIC;
|
||||
}
|
||||
}
|
||||
}
|
||||
modelKeyValues->deleteThis();
|
||||
|
||||
return RET_VALID;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add static prop model to the list of models
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static int AddStaticPropDictLump( char const* pModelName )
|
||||
{
|
||||
StaticPropDictLump_t dictLump;
|
||||
strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
|
||||
|
||||
for (int i = s_StaticPropDictLump.Size(); --i >= 0; )
|
||||
{
|
||||
if (!memcmp(&s_StaticPropDictLump[i], &dictLump, sizeof(dictLump) ))
|
||||
return i;
|
||||
}
|
||||
|
||||
return s_StaticPropDictLump.AddToTail( dictLump );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Load studio model vertex data from a file...
|
||||
//-----------------------------------------------------------------------------
|
||||
bool LoadStudioModel( char const* pModelName, char const* pEntityType, CUtlBuffer& buf )
|
||||
{
|
||||
if ( !g_pFullFileSystem->ReadFile( pModelName, NULL, buf ) )
|
||||
return false;
|
||||
|
||||
// Check that it's valid
|
||||
if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
|
||||
strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
|
||||
|
||||
Studio_ConvertStudioHdrToNewVersion( pHdr );
|
||||
|
||||
if (pHdr->version != STUDIO_VERSION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
isstaticprop_ret isStaticProp = IsStaticProp(pHdr);
|
||||
if ( isStaticProp != RET_VALID )
|
||||
{
|
||||
if ( isStaticProp == RET_FAIL_NOT_MARKED_STATIC_PROP )
|
||||
{
|
||||
Warning("Error! To use model \"%s\"\n"
|
||||
" with %s, it must be compiled with $staticprop!\n", pModelName, pEntityType );
|
||||
}
|
||||
else if ( isStaticProp == RET_FAIL_DYNAMIC )
|
||||
{
|
||||
Warning("Error! %s using model \"%s\", which must be used on a dynamic entity (i.e. prop_physics). Deleted.\n", pEntityType, pModelName );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure reset
|
||||
pHdr->pVertexBase = NULL;
|
||||
pHdr->pIndexBase = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Computes a convex hull from a studio mesh
|
||||
//-----------------------------------------------------------------------------
|
||||
static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh )
|
||||
{
|
||||
// Generate a list of all verts in the mesh
|
||||
Vector** ppVerts = (Vector**)stackalloc(pMesh->numvertices * sizeof(Vector*) );
|
||||
const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData();
|
||||
Assert( vertData ); // This can only return NULL on X360 for now
|
||||
for (int i = 0; i < pMesh->numvertices; ++i)
|
||||
{
|
||||
ppVerts[i] = vertData->Position(i);
|
||||
}
|
||||
|
||||
// Generate a convex hull from the verts
|
||||
return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Computes a convex hull from the studio model
|
||||
//-----------------------------------------------------------------------------
|
||||
CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
|
||||
{
|
||||
CUtlVector<CPhysConvex*> convexHulls;
|
||||
|
||||
for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
|
||||
{
|
||||
mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
|
||||
for( int model = 0; model < pBodyPart->nummodels; ++model )
|
||||
{
|
||||
mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
|
||||
for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
|
||||
{
|
||||
// Make a convex hull for each mesh
|
||||
// NOTE: This won't work unless the model has been compiled
|
||||
// with $staticprop
|
||||
mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
|
||||
convexHulls.AddToTail( ComputeConvexHull( pStudioMesh ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an array of convex elements to a compiled collision model
|
||||
// (this deletes the convex elements)
|
||||
return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add, find collision model in cache
|
||||
//-----------------------------------------------------------------------------
|
||||
static CPhysCollide* GetCollisionModel( char const* pModelName )
|
||||
{
|
||||
// Convert to a common string
|
||||
char* pTemp = (char*)_alloca(strlen(pModelName) + 1);
|
||||
strcpy( pTemp, pModelName );
|
||||
_strlwr( pTemp );
|
||||
|
||||
char* pSlash = strchr( pTemp, '\\' );
|
||||
while( pSlash )
|
||||
{
|
||||
*pSlash = '/';
|
||||
pSlash = strchr( pTemp, '\\' );
|
||||
}
|
||||
|
||||
// Find it in the cache
|
||||
ModelCollisionLookup_t lookup;
|
||||
lookup.m_Name = pTemp;
|
||||
int i = s_ModelCollisionCache.Find( lookup );
|
||||
if (i != s_ModelCollisionCache.InvalidIndex())
|
||||
return s_ModelCollisionCache[i].m_pCollide;
|
||||
|
||||
// Load the studio model file
|
||||
CUtlBuffer buf;
|
||||
if (!LoadStudioModel(pModelName, "prop_static", buf))
|
||||
{
|
||||
Warning("Error loading studio model \"%s\"!\n", pModelName );
|
||||
|
||||
// This way we don't try to load it multiple times
|
||||
lookup.m_pCollide = 0;
|
||||
s_ModelCollisionCache.Insert( lookup );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute the convex hull of the model...
|
||||
studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet();
|
||||
|
||||
// necessary for vertex access
|
||||
SetCurrentModel( pStudioHdr );
|
||||
|
||||
lookup.m_pCollide = ComputeConvexHull( pStudioHdr );
|
||||
s_ModelCollisionCache.Insert( lookup );
|
||||
|
||||
if ( !lookup.m_pCollide )
|
||||
{
|
||||
Warning("Bad geometry on \"%s\"!\n", pModelName );
|
||||
}
|
||||
|
||||
// Debugging
|
||||
if (g_DumpStaticProps)
|
||||
{
|
||||
static int propNum = 0;
|
||||
char tmp[128];
|
||||
sprintf( tmp, "staticprop%03d.txt", propNum );
|
||||
DumpCollideToGlView( lookup.m_pCollide, tmp );
|
||||
++propNum;
|
||||
}
|
||||
|
||||
FreeCurrentModelVertexes();
|
||||
|
||||
// Insert into cache...
|
||||
return lookup.m_pCollide;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Tests a single leaf against the static prop
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool TestLeafAgainstCollide( int depth, int* pNodeList,
|
||||
Vector const& origin, QAngle const& angles, CPhysCollide* pCollide )
|
||||
{
|
||||
// Copy the planes in the node list into a list of planes
|
||||
float* pPlanes = (float*)_alloca(depth * 4 * sizeof(float) );
|
||||
int idx = 0;
|
||||
for (int i = depth; --i >= 0; ++idx )
|
||||
{
|
||||
int sign = (pNodeList[i] < 0) ? -1 : 1;
|
||||
int node = (sign < 0) ? - pNodeList[i] - 1 : pNodeList[i];
|
||||
dnode_t* pNode = &dnodes[node];
|
||||
dplane_t* pPlane = &dplanes[pNode->planenum];
|
||||
|
||||
pPlanes[idx*4] = sign * pPlane->normal[0];
|
||||
pPlanes[idx*4+1] = sign * pPlane->normal[1];
|
||||
pPlanes[idx*4+2] = sign * pPlane->normal[2];
|
||||
pPlanes[idx*4+3] = sign * pPlane->dist;
|
||||
}
|
||||
|
||||
// Make a convex solid out of the planes
|
||||
CPhysConvex* pPhysConvex = s_pPhysCollision->ConvexFromPlanes( pPlanes, depth, 0.0f );
|
||||
|
||||
// This should never happen, but if it does, return no collision
|
||||
Assert( pPhysConvex );
|
||||
if (!pPhysConvex)
|
||||
return false;
|
||||
|
||||
CPhysCollide* pLeafCollide = s_pPhysCollision->ConvertConvexToCollide( &pPhysConvex, 1 );
|
||||
|
||||
// Collide the leaf solid with the static prop solid
|
||||
trace_t tr;
|
||||
s_pPhysCollision->TraceCollide( vec3_origin, vec3_origin, pLeafCollide, vec3_angle,
|
||||
pCollide, origin, angles, &tr );
|
||||
|
||||
s_pPhysCollision->DestroyCollide( pLeafCollide );
|
||||
|
||||
return (tr.startsolid != 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Find all leaves that intersect with this bbox + test against the static prop..
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList,
|
||||
Vector const& mins, Vector const& maxs,
|
||||
Vector const& origin, QAngle const& angles, CPhysCollide* pCollide,
|
||||
CUtlVector<unsigned short>& leafList )
|
||||
{
|
||||
Assert( pNodeList && pCollide );
|
||||
Vector cornermin, cornermax;
|
||||
|
||||
while( node >= 0 )
|
||||
{
|
||||
dnode_t* pNode = &dnodes[node];
|
||||
dplane_t* pPlane = &dplanes[pNode->planenum];
|
||||
|
||||
// Arbitrary split plane here
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (pPlane->normal[i] >= 0)
|
||||
{
|
||||
cornermin[i] = mins[i];
|
||||
cornermax[i] = maxs[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
cornermin[i] = maxs[i];
|
||||
cornermax[i] = mins[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (DotProduct( pPlane->normal, cornermax ) <= pPlane->dist)
|
||||
{
|
||||
// Add the node to the list of nodes
|
||||
pNodeList[depth] = node;
|
||||
++depth;
|
||||
|
||||
node = pNode->children[1];
|
||||
}
|
||||
else if (DotProduct( pPlane->normal, cornermin ) >= pPlane->dist)
|
||||
{
|
||||
// In this case, we are going in front of the plane. That means that
|
||||
// this plane must have an outward normal facing in the oppisite direction
|
||||
// We indicate this be storing a negative node index in the node list
|
||||
pNodeList[depth] = - node - 1;
|
||||
++depth;
|
||||
|
||||
node = pNode->children[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here the box is split by the node. First, we'll add the plane as if its
|
||||
// outward facing normal is in the direction of the node plane, then
|
||||
// we'll have to reverse it for the other child...
|
||||
pNodeList[depth] = node;
|
||||
++depth;
|
||||
|
||||
ComputeConvexHullLeaves_R( pNode->children[1],
|
||||
depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
|
||||
|
||||
pNodeList[depth - 1] = - node - 1;
|
||||
ComputeConvexHullLeaves_R( pNode->children[0],
|
||||
depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Assert( pNodeList && pCollide );
|
||||
|
||||
// Never add static props to solid leaves
|
||||
if ( (dleafs[-node-1].contents & CONTENTS_SOLID) == 0 )
|
||||
{
|
||||
if (TestLeafAgainstCollide( depth, pNodeList, origin, angles, pCollide ))
|
||||
{
|
||||
leafList.AddToTail( -node - 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Static Props in the level
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin,
|
||||
QAngle const& angles, CUtlVector<unsigned short>& leafList )
|
||||
{
|
||||
// Compute an axis-aligned bounding box for the collide
|
||||
Vector mins, maxs;
|
||||
s_pPhysCollision->CollideGetAABB( &mins, &maxs, pCollide, origin, angles );
|
||||
|
||||
// Find all leaves that intersect with the bounds
|
||||
int tempNodeList[1024];
|
||||
ComputeConvexHullLeaves_R( 0, 0, tempNodeList, mins, maxs,
|
||||
origin, angles, pCollide, leafList );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Computes the lighting origin
|
||||
//-----------------------------------------------------------------------------
|
||||
static bool ComputeLightingOrigin( StaticPropBuild_t const& build, Vector& lightingOrigin )
|
||||
{
|
||||
for (int i = s_LightingInfo.Count(); --i >= 0; )
|
||||
{
|
||||
int entIndex = s_LightingInfo[i];
|
||||
|
||||
// Check against all lighting info entities
|
||||
char const* pTargetName = ValueForKey( &entities[entIndex], "targetname" );
|
||||
if (!Q_strcmp(pTargetName, build.m_pLightingOrigin))
|
||||
{
|
||||
GetVectorForKey( &entities[entIndex], "origin", lightingOrigin );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Static Props in the level
|
||||
//-----------------------------------------------------------------------------
|
||||
static void AddStaticPropToLump( StaticPropBuild_t const& build )
|
||||
{
|
||||
// Get the collision model
|
||||
CPhysCollide* pConvexHull = GetCollisionModel( build.m_pModelName );
|
||||
if (!pConvexHull)
|
||||
return;
|
||||
|
||||
// Compute the leaves the static prop's convex hull hits
|
||||
CUtlVector< unsigned short > leafList;
|
||||
ComputeStaticPropLeaves( pConvexHull, build.m_Origin, build.m_Angles, leafList );
|
||||
|
||||
if ( !leafList.Count() )
|
||||
{
|
||||
Warning( "Static prop %s outside the map (%.2f, %.2f, %.2f)\n", build.m_pModelName, build.m_Origin.x, build.m_Origin.y, build.m_Origin.z );
|
||||
return;
|
||||
}
|
||||
// Insert an element into the lump data...
|
||||
int i = s_StaticPropLump.AddToTail( );
|
||||
StaticPropLump_t& propLump = s_StaticPropLump[i];
|
||||
propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName );
|
||||
VectorCopy( build.m_Origin, propLump.m_Origin );
|
||||
VectorCopy( build.m_Angles, propLump.m_Angles );
|
||||
propLump.m_FirstLeaf = s_StaticPropLeafLump.Count();
|
||||
propLump.m_LeafCount = leafList.Count();
|
||||
propLump.m_Solid = build.m_Solid;
|
||||
propLump.m_Skin = build.m_Skin;
|
||||
propLump.m_Flags = build.m_Flags;
|
||||
if (build.m_FadesOut)
|
||||
{
|
||||
propLump.m_Flags |= STATIC_PROP_FLAG_FADES;
|
||||
}
|
||||
propLump.m_FadeMinDist = build.m_FadeMinDist;
|
||||
propLump.m_FadeMaxDist = build.m_FadeMaxDist;
|
||||
propLump.m_flForcedFadeScale = build.m_flForcedFadeScale;
|
||||
propLump.m_nMinDXLevel = build.m_nMinDXLevel;
|
||||
propLump.m_nMaxDXLevel = build.m_nMaxDXLevel;
|
||||
|
||||
if (build.m_pLightingOrigin && *build.m_pLightingOrigin)
|
||||
{
|
||||
if (ComputeLightingOrigin( build, propLump.m_LightingOrigin ))
|
||||
{
|
||||
propLump.m_Flags |= STATIC_PROP_USE_LIGHTING_ORIGIN;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the leaves to the leaf lump
|
||||
for (int j = 0; j < leafList.Size(); ++j)
|
||||
{
|
||||
StaticPropLeafLump_t insert;
|
||||
insert.m_Leaf = leafList[j];
|
||||
s_StaticPropLeafLump.AddToTail( insert );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places static props in the lump
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void SetLumpData( )
|
||||
{
|
||||
GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_STATIC_PROPS);
|
||||
if (handle != g_GameLumps.InvalidGameLump())
|
||||
g_GameLumps.DestroyGameLump(handle);
|
||||
|
||||
int dictsize = s_StaticPropDictLump.Size() * sizeof(StaticPropDictLump_t);
|
||||
int objsize = s_StaticPropLump.Size() * sizeof(StaticPropLump_t);
|
||||
int leafsize = s_StaticPropLeafLump.Size() * sizeof(StaticPropLeafLump_t);
|
||||
int size = dictsize + objsize + leafsize + 3 * sizeof(int);
|
||||
|
||||
handle = g_GameLumps.CreateGameLump( GAMELUMP_STATIC_PROPS, size, 0, GAMELUMP_STATIC_PROPS_VERSION );
|
||||
|
||||
// Serialize the data
|
||||
CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size );
|
||||
buf.PutInt( s_StaticPropDictLump.Size() );
|
||||
if (dictsize)
|
||||
buf.Put( s_StaticPropDictLump.Base(), dictsize );
|
||||
buf.PutInt( s_StaticPropLeafLump.Size() );
|
||||
if (leafsize)
|
||||
buf.Put( s_StaticPropLeafLump.Base(), leafsize );
|
||||
buf.PutInt( s_StaticPropLump.Size() );
|
||||
if (objsize)
|
||||
buf.Put( s_StaticPropLump.Base(), objsize );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Places Static Props in the level
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void EmitStaticProps()
|
||||
{
|
||||
CreateInterfaceFn physicsFactory = GetPhysicsFactory();
|
||||
if ( physicsFactory )
|
||||
{
|
||||
s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
|
||||
if( !s_pPhysCollision )
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate a list of lighting origins, and strip them out
|
||||
int i;
|
||||
for ( i = 0; i < num_entities; ++i)
|
||||
{
|
||||
char* pEntity = ValueForKey(&entities[i], "classname");
|
||||
if (!Q_strcmp(pEntity, "info_lighting"))
|
||||
{
|
||||
s_LightingInfo.AddToTail(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit specifically specified static props
|
||||
for ( i = 0; i < num_entities; ++i)
|
||||
{
|
||||
char* pEntity = ValueForKey(&entities[i], "classname");
|
||||
if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static"))
|
||||
{
|
||||
StaticPropBuild_t build;
|
||||
|
||||
GetVectorForKey( &entities[i], "origin", build.m_Origin );
|
||||
GetAnglesForKey( &entities[i], "angles", build.m_Angles );
|
||||
build.m_pModelName = ValueForKey( &entities[i], "model" );
|
||||
build.m_Solid = IntForKey( &entities[i], "solid" );
|
||||
build.m_Skin = IntForKey( &entities[i], "skin" );
|
||||
build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" );
|
||||
build.m_Flags = 0;//IntForKey( &entities[i], "spawnflags" ) & STATIC_PROP_WC_MASK;
|
||||
if (IntForKey( &entities[i], "ignorenormals" ) == 1)
|
||||
{
|
||||
build.m_Flags |= STATIC_PROP_IGNORE_NORMALS;
|
||||
}
|
||||
if (IntForKey( &entities[i], "disableshadows" ) == 1)
|
||||
{
|
||||
build.m_Flags |= STATIC_PROP_NO_SHADOW;
|
||||
}
|
||||
if (IntForKey( &entities[i], "disablevertexlighting" ) == 1)
|
||||
{
|
||||
build.m_Flags |= STATIC_PROP_NO_PER_VERTEX_LIGHTING;
|
||||
}
|
||||
if (IntForKey( &entities[i], "disableselfshadowing" ) == 1)
|
||||
{
|
||||
build.m_Flags |= STATIC_PROP_NO_SELF_SHADOWING;
|
||||
}
|
||||
|
||||
if (IntForKey( &entities[i], "screenspacefade" ) == 1)
|
||||
{
|
||||
build.m_Flags |= STATIC_PROP_SCREEN_SPACE_FADE;
|
||||
}
|
||||
|
||||
const char *pKey = ValueForKey( &entities[i], "fadescale" );
|
||||
if ( pKey && pKey[0] )
|
||||
{
|
||||
build.m_flForcedFadeScale = FloatForKey( &entities[i], "fadescale" );
|
||||
}
|
||||
else
|
||||
{
|
||||
build.m_flForcedFadeScale = 1;
|
||||
}
|
||||
build.m_FadesOut = (build.m_FadeMaxDist > 0);
|
||||
build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" );
|
||||
if (build.m_FadesOut)
|
||||
{
|
||||
build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" );
|
||||
if (build.m_FadeMinDist < 0)
|
||||
{
|
||||
build.m_FadeMinDist = build.m_FadeMaxDist;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
build.m_FadeMinDist = 0;
|
||||
}
|
||||
build.m_nMinDXLevel = (unsigned short)IntForKey( &entities[i], "mindxlevel" );
|
||||
build.m_nMaxDXLevel = (unsigned short)IntForKey( &entities[i], "maxdxlevel" );
|
||||
AddStaticPropToLump( build );
|
||||
|
||||
// strip this ent from the .bsp file
|
||||
entities[i].epairs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Strip out lighting origins; has to be done here because they are used when
|
||||
// static props are made
|
||||
for ( i = s_LightingInfo.Count(); --i >= 0; )
|
||||
{
|
||||
// strip this ent from the .bsp file
|
||||
entities[s_LightingInfo[i]].epairs = 0;
|
||||
}
|
||||
|
||||
|
||||
SetLumpData( );
|
||||
}
|
||||
|
||||
static studiohdr_t *g_pActiveStudioHdr;
|
||||
static void SetCurrentModel( studiohdr_t *pStudioHdr )
|
||||
{
|
||||
// track the correct model
|
||||
g_pActiveStudioHdr = pStudioHdr;
|
||||
}
|
||||
|
||||
static void FreeCurrentModelVertexes()
|
||||
{
|
||||
Assert( g_pActiveStudioHdr );
|
||||
|
||||
if ( g_pActiveStudioHdr->pVertexBase )
|
||||
{
|
||||
free( g_pActiveStudioHdr->pVertexBase );
|
||||
g_pActiveStudioHdr->pVertexBase = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
|
||||
{
|
||||
char fileName[260];
|
||||
FileHandle_t fileHandle;
|
||||
vertexFileHeader_t *pVvdHdr;
|
||||
|
||||
Assert( pModelData == NULL );
|
||||
Assert( g_pActiveStudioHdr );
|
||||
|
||||
if ( g_pActiveStudioHdr->pVertexBase )
|
||||
{
|
||||
return (vertexFileHeader_t *)g_pActiveStudioHdr->pVertexBase;
|
||||
}
|
||||
|
||||
// mandatory callback to make requested data resident
|
||||
// load and persist the vertex file
|
||||
strcpy( fileName, "models/" );
|
||||
strcat( fileName, g_pActiveStudioHdr->pszName() );
|
||||
Q_StripExtension( fileName, fileName, sizeof( fileName ) );
|
||||
strcat( fileName, ".vvd" );
|
||||
|
||||
// load the model
|
||||
fileHandle = g_pFileSystem->Open( fileName, "rb" );
|
||||
if ( !fileHandle )
|
||||
{
|
||||
Error( "Unable to load vertex data \"%s\"\n", fileName );
|
||||
}
|
||||
|
||||
// Get the file size
|
||||
int size = g_pFileSystem->Size( fileHandle );
|
||||
if (size == 0)
|
||||
{
|
||||
g_pFileSystem->Close( fileHandle );
|
||||
Error( "Bad size for vertex data \"%s\"\n", fileName );
|
||||
}
|
||||
|
||||
pVvdHdr = (vertexFileHeader_t *)malloc(size);
|
||||
g_pFileSystem->Read( pVvdHdr, size, fileHandle );
|
||||
g_pFileSystem->Close( fileHandle );
|
||||
|
||||
// check header
|
||||
if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
|
||||
{
|
||||
Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
|
||||
}
|
||||
if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
|
||||
{
|
||||
Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
|
||||
}
|
||||
if (pVvdHdr->checksum != g_pActiveStudioHdr->checksum)
|
||||
{
|
||||
Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, g_pActiveStudioHdr->checksum);
|
||||
}
|
||||
|
||||
g_pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
|
||||
return pVvdHdr;
|
||||
}
|
||||
|
||||
737
mp/src/utils/vbsp/textures.cpp
Normal file
737
mp/src/utils/vbsp/textures.cpp
Normal file
@@ -0,0 +1,737 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "utilmatlib.h"
|
||||
#include "physdll.h"
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include "tier1/strtools.h"
|
||||
#include "materialpatch.h"
|
||||
#include "KeyValues.h"
|
||||
|
||||
void LoadSurfaceProperties( void );
|
||||
|
||||
IPhysicsSurfaceProps *physprops = NULL;
|
||||
|
||||
int nummiptex;
|
||||
textureref_t textureref[MAX_MAP_TEXTURES];
|
||||
|
||||
bool g_bHasWater = false;
|
||||
|
||||
extern qboolean onlyents;
|
||||
|
||||
dtexdata_t *GetTexData( int index )
|
||||
{
|
||||
if ( index < 0 )
|
||||
return NULL;
|
||||
Assert( !onlyents );
|
||||
return &dtexdata[ index ];
|
||||
}
|
||||
|
||||
static qboolean StringIsTrue( const char *str )
|
||||
{
|
||||
if( Q_strcasecmp( str, "true" ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( Q_strcasecmp( str, "1" ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FindMiptex (const char *name)
|
||||
{
|
||||
int i;
|
||||
MaterialSystemMaterial_t matID;
|
||||
const char *propVal, *propVal2;
|
||||
int opacity;
|
||||
bool found;
|
||||
|
||||
for (i=0 ; i<nummiptex ; i++)
|
||||
{
|
||||
if (!strcmp (name, textureref[i].name))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (nummiptex == MAX_MAP_TEXTURES)
|
||||
Error ("Too many unique textures, max %d", MAX_MAP_TEXTURES);
|
||||
strcpy (textureref[i].name, name);
|
||||
|
||||
textureref[i].lightmapWorldUnitsPerLuxel = 0.0f;
|
||||
textureref[i].flags = 0;
|
||||
textureref[i].contents = 0;
|
||||
|
||||
matID = FindOriginalMaterial( name, &found );
|
||||
if( matID == MATERIAL_NOT_FOUND )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Warning("Material not found!: %s\n", name );
|
||||
|
||||
// HANDLE ALL OF THE STUFF THAT ISN'T RENDERED WITH THE MATERIAL THAT IS ONE IT.
|
||||
|
||||
// handle sky
|
||||
if( ( propVal = GetMaterialVar( matID, "%compileSky" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_SKY | SURF_NOLIGHT;
|
||||
}
|
||||
else if( ( propVal = GetMaterialVar( matID, "%compile2DSky" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_SKY | SURF_SKY2D | SURF_NOLIGHT;
|
||||
}
|
||||
// handle hint brushes
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileHint" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_HINT;
|
||||
}
|
||||
// handle skip faces
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileSkip" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP;
|
||||
}
|
||||
// handle origin brushes
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileOrigin" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_ORIGIN | CONTENTS_DETAIL;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
// handle clip brushes
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileClip" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%playerClip" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_PLAYERCLIP;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
// handle npc clip brushes
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileNpcClip" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_MONSTERCLIP;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
// handle surface lights which are meant to
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileNoChop" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOCHOP;
|
||||
}
|
||||
// handle triggers
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compileTrigger" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= ( SURF_NOLIGHT | SURF_TRIGGER );
|
||||
if ( g_NodrawTriggers )
|
||||
{
|
||||
textureref[i].flags |= SURF_NODRAW;
|
||||
}
|
||||
}
|
||||
// handle nolight surfs (except water)
|
||||
else if ( (( propVal = GetMaterialVar( matID, "%compileNoLight" ) ) && StringIsTrue( propVal )) &&
|
||||
!(( propVal2 = GetMaterialVar( matID, "%compileWater" ) ) && StringIsTrue( propVal2 ) ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOLIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
// HANDLE ALL OF THE STUFF THAT IS RENDERED WITH THE MATERIAL THAT IS ON IT.
|
||||
|
||||
// Handle ladders.
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileLadder" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_LADDER;
|
||||
}
|
||||
|
||||
// handle wet materials
|
||||
if ( ( propVal = GetMaterialVar( matID, "%noPortal" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOPORTAL;
|
||||
}
|
||||
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compilePassBullets" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
// change contents to grate, so bullets pass through
|
||||
// NOTE: This has effects on visibility too!
|
||||
textureref[i].contents &= ~CONTENTS_SOLID;
|
||||
textureref[i].contents |= CONTENTS_GRATE;
|
||||
}
|
||||
|
||||
if( g_BumpAll || GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_BUMPLIGHT;
|
||||
}
|
||||
|
||||
if( GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_LIGHTMAP ) )
|
||||
{
|
||||
textureref[i].flags &= ~SURF_NOLIGHT;
|
||||
}
|
||||
else if( !g_bLightIfMissing )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOLIGHT;
|
||||
}
|
||||
// handle nodraw faces/brushes
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileNoDraw" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
// textureref[i].contents |= CONTENTS_DETAIL;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
|
||||
// Just a combination of nodraw + pass bullets, makes things easier
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileInvisible" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
// change contents to grate, so bullets pass through
|
||||
// NOTE: This has effects on visibility too!
|
||||
textureref[i].contents &= ~CONTENTS_SOLID;
|
||||
textureref[i].contents |= CONTENTS_GRATE;
|
||||
textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
|
||||
}
|
||||
|
||||
bool checkWindow = true;
|
||||
// handle non solid
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileNonsolid" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents = CONTENTS_OPAQUE;
|
||||
// Non-Solid can't be a window either!
|
||||
checkWindow = false;
|
||||
}
|
||||
// handle block LOS
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileBlockLOS" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents = CONTENTS_BLOCKLOS;
|
||||
|
||||
// BlockLOS can't be a window either!
|
||||
checkWindow = false;
|
||||
}
|
||||
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileDetail" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_DETAIL;
|
||||
}
|
||||
|
||||
bool bKeepLighting = ( ( propVal = GetMaterialVar( matID, "%compileKeepLight" ) ) &&
|
||||
StringIsTrue( propVal ) );
|
||||
|
||||
// handle materials that want to be treated as water.
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileWater" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
|
||||
textureref[i].contents |= CONTENTS_WATER;
|
||||
textureref[i].flags |= SURF_WARP | SURF_NOSHADOWS | SURF_NODECALS;
|
||||
|
||||
if ( g_DisableWaterLighting && !bKeepLighting )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOLIGHT;
|
||||
}
|
||||
|
||||
// Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
|
||||
g_bHasWater = true;
|
||||
}
|
||||
const char *pShaderName = GetMaterialShaderName(matID);
|
||||
if ( !bKeepLighting && !Q_strncasecmp( pShaderName, "water", 5 ) || !Q_strncasecmp( pShaderName, "UnlitGeneric", 12 ) )
|
||||
{
|
||||
//if ( !(textureref[i].flags & SURF_NOLIGHT) )
|
||||
// Warning("Forcing lit materal %s to nolight\n", name );
|
||||
textureref[i].flags |= SURF_NOLIGHT;
|
||||
}
|
||||
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileSlime" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
|
||||
textureref[i].contents |= CONTENTS_SLIME;
|
||||
textureref[i].flags |= SURF_NODECALS;
|
||||
// Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
|
||||
g_bHasWater = true;
|
||||
}
|
||||
|
||||
opacity = GetMaterialShaderPropertyInt( matID, UTILMATLIB_OPACITY );
|
||||
|
||||
if ( checkWindow && opacity != UTILMATLIB_OPAQUE )
|
||||
{
|
||||
// transparent *and solid* brushes that aren't grates or water must be windows
|
||||
if ( !(textureref[i].contents & (CONTENTS_GRATE|CONTENTS_WATER)) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_WINDOW;
|
||||
}
|
||||
|
||||
textureref[i].contents &= ~CONTENTS_SOLID;
|
||||
|
||||
// this affects engine primitive sorting, SURF_TRANS means sort as a translucent primitive
|
||||
if ( opacity == UTILMATLIB_TRANSLUCENT )
|
||||
{
|
||||
textureref[i].flags |= SURF_TRANS;
|
||||
}
|
||||
|
||||
}
|
||||
if ( textureref[i].flags & SURF_NOLIGHT )
|
||||
{
|
||||
textureref[i].flags &= ~SURF_BUMPLIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
nummiptex++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
textureAxisFromPlane
|
||||
==================
|
||||
*/
|
||||
Vector baseaxis[18] =
|
||||
{
|
||||
Vector(0,0,1), Vector(1,0,0), Vector(0,-1,0), // floor
|
||||
Vector(0,0,-1), Vector(1,0,0), Vector(0,-1,0), // ceiling
|
||||
Vector(1,0,0), Vector(0,1,0), Vector(0,0,-1), // west wall
|
||||
Vector(-1,0,0), Vector(0,1,0), Vector(0,0,-1), // east wall
|
||||
Vector(0,1,0), Vector(1,0,0), Vector(0,0,-1), // south wall
|
||||
Vector(0,-1,0), Vector(1,0,0), Vector(0,0,-1) // north wall
|
||||
};
|
||||
|
||||
void TextureAxisFromPlane(plane_t *pln, Vector& xv, Vector& yv)
|
||||
{
|
||||
int bestaxis;
|
||||
vec_t dot,best;
|
||||
int i;
|
||||
|
||||
best = 0;
|
||||
bestaxis = 0;
|
||||
|
||||
for (i=0 ; i<6 ; i++)
|
||||
{
|
||||
dot = DotProduct (pln->normal, baseaxis[i*3]);
|
||||
if (dot > best)
|
||||
{
|
||||
best = dot;
|
||||
bestaxis = i;
|
||||
}
|
||||
}
|
||||
|
||||
VectorCopy (baseaxis[bestaxis*3+1], xv);
|
||||
VectorCopy (baseaxis[bestaxis*3+2], yv);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int g_SurfaceProperties[MAX_MAP_TEXDATA];
|
||||
|
||||
|
||||
int GetSurfaceProperties( MaterialSystemMaterial_t matID, const char *pMatName )
|
||||
{
|
||||
const char *pPropString = NULL;
|
||||
int surfaceIndex = -1;
|
||||
|
||||
if ( physprops )
|
||||
{
|
||||
pPropString = GetMaterialVar( matID, "$surfaceprop" );
|
||||
if ( pPropString )
|
||||
{
|
||||
surfaceIndex = physprops->GetSurfaceIndex( pPropString );
|
||||
if ( surfaceIndex < 0 )
|
||||
{
|
||||
Msg("Can't find surfaceprop %s for material %s, using default\n", pPropString, pMatName );
|
||||
surfaceIndex = physprops->GetSurfaceIndex( pPropString );
|
||||
surfaceIndex = physprops->GetSurfaceIndex( "default" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return surfaceIndex;
|
||||
}
|
||||
|
||||
int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName )
|
||||
{
|
||||
const char *pPropString = NULL;
|
||||
int surfaceIndex = -1;
|
||||
|
||||
if ( physprops )
|
||||
{
|
||||
pPropString = GetMaterialVar( matID, "$surfaceprop2" );
|
||||
if ( pPropString )
|
||||
{
|
||||
surfaceIndex = physprops->GetSurfaceIndex( pPropString );
|
||||
if ( surfaceIndex < 0 )
|
||||
{
|
||||
Msg("Can't find surfacepropblend %s for material %s, using default\n", pPropString, pMatName );
|
||||
surfaceIndex = physprops->GetSurfaceIndex( "default" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No surface property 2.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return surfaceIndex;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Finds or adds a texdata for the specified name ( same as below except
|
||||
// instead of finding the named texture, copies the settings from the passed
|
||||
// in sourceTexture. )
|
||||
// Used for creation of one off .vmt files for water surface textures
|
||||
// Input : *pName - texture name
|
||||
// Output : int index into dtexdata array
|
||||
//-----------------------------------------------------------------------------
|
||||
int FindAliasedTexData( const char *pName_, dtexdata_t *sourceTexture )
|
||||
{
|
||||
char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
|
||||
strcpy( pName, pName_ );
|
||||
strlwr( pName );
|
||||
int i, output;
|
||||
bool found;
|
||||
dtexdata_t *pTexData;
|
||||
MaterialSystemMaterial_t matID;
|
||||
|
||||
for ( i = 0; i < numtexdata; i++ )
|
||||
{
|
||||
if ( !strcmp( pName, TexDataStringTable_GetString( GetTexData( i )->nameStringTableID ) ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
output = numtexdata;
|
||||
if ( numtexdata >= MAX_MAP_TEXDATA )
|
||||
{
|
||||
Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
|
||||
}
|
||||
pTexData = GetTexData( output );
|
||||
numtexdata++;
|
||||
|
||||
// Save the name of the material.
|
||||
pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
|
||||
|
||||
// Get the width, height, view_width, view_height, and reflectivity from the material system.
|
||||
matID = FindOriginalMaterial( TexDataStringTable_GetString( sourceTexture->nameStringTableID ), &found, false );
|
||||
if( matID == MATERIAL_NOT_FOUND || (!found) )
|
||||
{
|
||||
qprintf( "WARNING: material not found: \"%s\"\n", pName );
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
|
||||
pTexData->view_width = pTexData->width; // undone: what is this?
|
||||
pTexData->view_height = pTexData->height; // undone: what is this?
|
||||
|
||||
GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
|
||||
g_SurfaceProperties[output] = GetSurfaceProperties( matID, pName );
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds a texdata for the specified name, returns -1 if not found
|
||||
//-----------------------------------------------------------------------------
|
||||
int FindTexData( const char *pName )
|
||||
{
|
||||
// Make sure the texdata doesn't already exist.
|
||||
for( int i = 0; i < numtexdata; i++ )
|
||||
{
|
||||
char const *pTexDataName = TexDataStringTable_GetString( GetTexData( i )->nameStringTableID );
|
||||
if ( !Q_stricmp( pTexDataName, pName ) )
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Finds or adds a texdata for the specified name
|
||||
// Input : *pName - texture name
|
||||
// Output : int index into dtexdata array
|
||||
//-----------------------------------------------------------------------------
|
||||
int FindOrCreateTexData( const char *pName_ )
|
||||
{
|
||||
char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
|
||||
strcpy( pName, pName_ );
|
||||
|
||||
int nOutput = FindTexData( pName );
|
||||
if ( nOutput >= 0 )
|
||||
return nOutput;
|
||||
|
||||
// Didn't find it, add a new one
|
||||
nOutput = numtexdata;
|
||||
if ( numtexdata >= MAX_MAP_TEXDATA )
|
||||
{
|
||||
Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
|
||||
}
|
||||
dtexdata_t *pTexData = GetTexData( nOutput );
|
||||
numtexdata++;
|
||||
|
||||
// Save the name of the material.
|
||||
pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
|
||||
|
||||
// Get the width, height, view_width, view_height, and reflectivity from the material system.
|
||||
bool bFound;
|
||||
MaterialSystemMaterial_t matID = FindOriginalMaterial( pName, &bFound );
|
||||
if ( matID == MATERIAL_NOT_FOUND || (!bFound) )
|
||||
{
|
||||
qprintf( "WARNING: material not found: \"%s\"\n", pName );
|
||||
return nOutput;
|
||||
}
|
||||
|
||||
GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
|
||||
pTexData->view_width = pTexData->width; // undone: what is this?
|
||||
pTexData->view_height = pTexData->height; // undone: what is this?
|
||||
|
||||
GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
|
||||
g_SurfaceProperties[nOutput] = GetSurfaceProperties( matID, pName );
|
||||
|
||||
#if 0
|
||||
Msg( "reflectivity: %f %f %f\n",
|
||||
pTexData->reflectivity[0],
|
||||
pTexData->reflectivity[1],
|
||||
pTexData->reflectivity[2] );
|
||||
#endif
|
||||
|
||||
return nOutput;
|
||||
}
|
||||
|
||||
int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName )
|
||||
{
|
||||
int existingIndex = pExistingTexData - GetTexData( 0 );
|
||||
dtexdata_t *pNewTexData = GetTexData( numtexdata );
|
||||
int newIndex = numtexdata;
|
||||
numtexdata++;
|
||||
|
||||
*pNewTexData = *pExistingTexData;
|
||||
pNewTexData->nameStringTableID = TexDataStringTable_AddOrFindString( cloneTexDataName );
|
||||
g_SurfaceProperties[newIndex] = g_SurfaceProperties[existingIndex];
|
||||
|
||||
return newIndex;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds a texinfo that exactly matches the passed in texinfo
|
||||
//-----------------------------------------------------------------------------
|
||||
int FindTexInfo( const texinfo_t &searchTexInfo )
|
||||
{
|
||||
for( int i = 0; i < texinfo.Count(); i++ )
|
||||
{
|
||||
// Just an early-out for performance
|
||||
if ( texinfo[i].texdata != searchTexInfo.texdata )
|
||||
continue;
|
||||
|
||||
if ( !memcmp( &texinfo[i], &searchTexInfo, sizeof( texinfo_t ) ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Finds or creates a texinfo that exactly matches the passed in texinfo
|
||||
//-----------------------------------------------------------------------------
|
||||
int FindOrCreateTexInfo( const texinfo_t &searchTexInfo )
|
||||
{
|
||||
int i = FindTexInfo( searchTexInfo );
|
||||
if ( i >= 0 )
|
||||
return i;
|
||||
|
||||
i = texinfo.AddToTail( searchTexInfo );
|
||||
|
||||
if ( onlyents )
|
||||
{
|
||||
Error( "FindOrCreateTexInfo: Tried to create new texinfo during -onlyents compile!\nMust compile without -onlyents" );
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin)
|
||||
{
|
||||
Vector vecs[2];
|
||||
int sv, tv;
|
||||
vec_t ang, sinv, cosv;
|
||||
vec_t ns, nt;
|
||||
texinfo_t tx;
|
||||
int i, j;
|
||||
|
||||
if (!bt->name[0])
|
||||
return 0;
|
||||
|
||||
memset (&tx, 0, sizeof(tx));
|
||||
|
||||
// HLTOOLS - add support for texture vectors stored in the map file
|
||||
if (g_nMapFileVersion < 220)
|
||||
{
|
||||
TextureAxisFromPlane(plane, vecs[0], vecs[1]);
|
||||
}
|
||||
|
||||
if (!bt->textureWorldUnitsPerTexel[0])
|
||||
bt->textureWorldUnitsPerTexel[0] = 1;
|
||||
if (!bt->textureWorldUnitsPerTexel[1])
|
||||
bt->textureWorldUnitsPerTexel[1] = 1;
|
||||
|
||||
|
||||
float shiftScaleU = 1.0f / 16.0f;
|
||||
float shiftScaleV = 1.0f / 16.0f;
|
||||
|
||||
if (g_nMapFileVersion < 220)
|
||||
{
|
||||
// rotate axis
|
||||
if (bt->rotate == 0)
|
||||
{ sinv = 0 ; cosv = 1; }
|
||||
else if (bt->rotate == 90)
|
||||
{ sinv = 1 ; cosv = 0; }
|
||||
else if (bt->rotate == 180)
|
||||
{ sinv = 0 ; cosv = -1; }
|
||||
else if (bt->rotate == 270)
|
||||
{ sinv = -1 ; cosv = 0; }
|
||||
else
|
||||
{
|
||||
ang = bt->rotate / 180 * M_PI;
|
||||
sinv = sin(ang);
|
||||
cosv = cos(ang);
|
||||
}
|
||||
|
||||
if (vecs[0][0])
|
||||
sv = 0;
|
||||
else if (vecs[0][1])
|
||||
sv = 1;
|
||||
else
|
||||
sv = 2;
|
||||
|
||||
if (vecs[1][0])
|
||||
tv = 0;
|
||||
else if (vecs[1][1])
|
||||
tv = 1;
|
||||
else
|
||||
tv = 2;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
|
||||
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
|
||||
vecs[i][sv] = ns;
|
||||
vecs[i][tv] = nt;
|
||||
}
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
tx.textureVecsTexelsPerWorldUnits[i][j] = vecs[i][j] / bt->textureWorldUnitsPerTexel[i];
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[i][j] = tx.textureVecsTexelsPerWorldUnits[i][j] / 16.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tx.textureVecsTexelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->textureWorldUnitsPerTexel[0];
|
||||
tx.textureVecsTexelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->textureWorldUnitsPerTexel[0];
|
||||
tx.textureVecsTexelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->textureWorldUnitsPerTexel[0];
|
||||
|
||||
tx.textureVecsTexelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->textureWorldUnitsPerTexel[1];
|
||||
tx.textureVecsTexelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->textureWorldUnitsPerTexel[1];
|
||||
tx.textureVecsTexelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->textureWorldUnitsPerTexel[1];
|
||||
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->lightmapWorldUnitsPerLuxel;
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->lightmapWorldUnitsPerLuxel;
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->lightmapWorldUnitsPerLuxel;
|
||||
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->lightmapWorldUnitsPerLuxel;
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->lightmapWorldUnitsPerLuxel;
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->lightmapWorldUnitsPerLuxel;
|
||||
|
||||
shiftScaleU = bt->textureWorldUnitsPerTexel[0] / bt->lightmapWorldUnitsPerLuxel;
|
||||
shiftScaleV = bt->textureWorldUnitsPerTexel[1] / bt->lightmapWorldUnitsPerLuxel;
|
||||
}
|
||||
|
||||
tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] +
|
||||
DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[0] );
|
||||
tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] +
|
||||
DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[1] );
|
||||
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[0][3] = shiftScaleU * bt->shift[0] +
|
||||
DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[0] );
|
||||
tx.lightmapVecsLuxelsPerWorldUnits[1][3] = shiftScaleV * bt->shift[1] +
|
||||
DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[1] );
|
||||
|
||||
tx.flags = bt->flags;
|
||||
tx.texdata = FindOrCreateTexData( bt->name );
|
||||
|
||||
// find the texinfo
|
||||
return FindOrCreateTexInfo( tx );
|
||||
}
|
||||
|
||||
|
||||
void LoadSurfacePropFile( const char *pMaterialFilename )
|
||||
{
|
||||
FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
|
||||
|
||||
if ( fp == FILESYSTEM_INVALID_HANDLE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int len = g_pFileSystem->Size( fp );
|
||||
|
||||
char *pText = new char[len];
|
||||
g_pFileSystem->Read( pText, len, fp );
|
||||
g_pFileSystem->Close( fp );
|
||||
|
||||
physprops->ParseSurfaceData( pMaterialFilename, pText );
|
||||
|
||||
delete[] pText;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads the surface properties database into the physics DLL
|
||||
//-----------------------------------------------------------------------------
|
||||
void LoadSurfaceProperties( void )
|
||||
{
|
||||
CreateInterfaceFn physicsFactory = GetPhysicsFactory();
|
||||
if ( !physicsFactory )
|
||||
return;
|
||||
|
||||
physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
|
||||
|
||||
const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
|
||||
KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
|
||||
if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
|
||||
{
|
||||
for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
|
||||
{
|
||||
if ( !Q_stricmp( sub->GetName(), "file" ) )
|
||||
{
|
||||
// Add
|
||||
LoadSurfacePropFile( sub->GetString() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifest->deleteThis();
|
||||
}
|
||||
|
||||
|
||||
207
mp/src/utils/vbsp/tree.cpp
Normal file
207
mp/src/utils/vbsp/tree.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
#include "vbsp.h"
|
||||
|
||||
extern int c_nodes;
|
||||
|
||||
void RemovePortalFromNode (portal_t *portal, node_t *l);
|
||||
|
||||
node_t *NodeForPoint (node_t *node, Vector& origin)
|
||||
{
|
||||
plane_t *plane;
|
||||
vec_t d;
|
||||
|
||||
while (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
plane = &g_MainMap->mapplanes[node->planenum];
|
||||
d = DotProduct (origin, plane->normal) - plane->dist;
|
||||
if (d >= 0)
|
||||
node = node->children[0];
|
||||
else
|
||||
node = node->children[1];
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTreePortals_r
|
||||
=============
|
||||
*/
|
||||
void FreeTreePortals_r (node_t *node)
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
int s;
|
||||
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTreePortals_r (node->children[0]);
|
||||
FreeTreePortals_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free portals
|
||||
for (p=node->portals ; p ; p=nextp)
|
||||
{
|
||||
s = (p->nodes[1] == node);
|
||||
nextp = p->next[s];
|
||||
|
||||
RemovePortalFromNode (p, p->nodes[!s]);
|
||||
FreePortal (p);
|
||||
}
|
||||
node->portals = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree_r
|
||||
=============
|
||||
*/
|
||||
void FreeTree_r (node_t *node)
|
||||
{
|
||||
face_t *f, *nextf;
|
||||
|
||||
// free children
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
FreeTree_r (node->children[0]);
|
||||
FreeTree_r (node->children[1]);
|
||||
}
|
||||
|
||||
// free bspbrushes
|
||||
FreeBrushList (node->brushlist);
|
||||
|
||||
// free faces
|
||||
for (f=node->faces ; f ; f=nextf)
|
||||
{
|
||||
nextf = f->next;
|
||||
FreeFace (f);
|
||||
}
|
||||
|
||||
// free the node
|
||||
if (node->volume)
|
||||
FreeBrush (node->volume);
|
||||
|
||||
if (numthreads == 1)
|
||||
c_nodes--;
|
||||
free (node);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
FreeTree
|
||||
=============
|
||||
*/
|
||||
void FreeTree (tree_t *tree)
|
||||
{
|
||||
if ( !tree )
|
||||
return;
|
||||
|
||||
FreeTreePortals_r (tree->headnode);
|
||||
FreeTree_r (tree->headnode);
|
||||
free (tree);
|
||||
}
|
||||
|
||||
//===============================================================
|
||||
|
||||
void PrintTree_r (node_t *node, int depth)
|
||||
{
|
||||
int i;
|
||||
plane_t *plane;
|
||||
bspbrush_t *bb;
|
||||
|
||||
for (i=0 ; i<depth ; i++)
|
||||
Msg (" ");
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
{
|
||||
if (!node->brushlist)
|
||||
Msg ("NULL\n");
|
||||
else
|
||||
{
|
||||
for (bb=node->brushlist ; bb ; bb=bb->next)
|
||||
Msg ("%i ", bb->original->brushnum);
|
||||
Msg ("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
plane = &g_MainMap->mapplanes[node->planenum];
|
||||
Msg ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
|
||||
plane->normal[0], plane->normal[1], plane->normal[2],
|
||||
plane->dist);
|
||||
PrintTree_r (node->children[0], depth+1);
|
||||
PrintTree_r (node->children[1], depth+1);
|
||||
}
|
||||
|
||||
/*
|
||||
=========================================================
|
||||
|
||||
NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
|
||||
|
||||
=========================================================
|
||||
*/
|
||||
|
||||
int c_pruned;
|
||||
|
||||
/*
|
||||
============
|
||||
PruneNodes_r
|
||||
============
|
||||
*/
|
||||
void PruneNodes_r (node_t *node)
|
||||
{
|
||||
bspbrush_t *b, *next;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
PruneNodes_r (node->children[0]);
|
||||
PruneNodes_r (node->children[1]);
|
||||
|
||||
if ( (node->children[0]->contents & CONTENTS_SOLID)
|
||||
&& (node->children[1]->contents & CONTENTS_SOLID) )
|
||||
{
|
||||
if (node->faces)
|
||||
Error ("node->faces seperating CONTENTS_SOLID");
|
||||
if (node->children[0]->faces || node->children[1]->faces)
|
||||
Error ("!node->faces with children");
|
||||
|
||||
// FIXME: free stuff
|
||||
node->planenum = PLANENUM_LEAF;
|
||||
node->contents = CONTENTS_SOLID;
|
||||
|
||||
if (node->brushlist)
|
||||
Error ("PruneNodes: node->brushlist");
|
||||
|
||||
// combine brush lists
|
||||
node->brushlist = node->children[1]->brushlist;
|
||||
|
||||
for (b=node->children[0]->brushlist ; b ; b=next)
|
||||
{
|
||||
next = b->next;
|
||||
b->next = node->brushlist;
|
||||
node->brushlist = b;
|
||||
}
|
||||
|
||||
c_pruned++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PruneNodes (node_t *node)
|
||||
{
|
||||
qprintf ("--- PruneNodes ---\n");
|
||||
c_pruned = 0;
|
||||
PruneNodes_r (node);
|
||||
qprintf ("%5i pruned nodes\n", c_pruned);
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
371
mp/src/utils/vbsp/vbsp-2010.vcxproj
Normal file
371
mp/src/utils/vbsp/vbsp-2010.vcxproj
Normal file
@@ -0,0 +1,371 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>Vbsp</ProjectName>
|
||||
<ProjectGuid>{E4F39B89-9731-571D-B69D-C1B8FE56C056}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<TargetName>vbsp</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<TargetName>vbsp</TargetName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
|
||||
<ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
|
||||
<PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
|
||||
<PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
|
||||
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
|
||||
<PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
|
||||
<ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
|
||||
<PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
|
||||
<PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
|
||||
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
|
||||
<GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
|
||||
<PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<PreBuildEvent>
|
||||
<Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj
if ERRORLEVEL 1 exit 1</Command>
|
||||
</PreBuildEvent>
|
||||
<ClCompile>
|
||||
<AdditionalOptions> /MP</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vbsp;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<ExpandAttributedSource>false</ExpandAttributedSource>
|
||||
<AssemblerOutput>NoListing</AssemblerOutput>
|
||||
<AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
|
||||
<ObjectFileName>$(IntDir)/</ObjectFileName>
|
||||
<ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
|
||||
<ErrorReporting>Prompt</ErrorReporting>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<Culture>1033</Culture>
|
||||
</ResourceCompile>
|
||||
<PreLinkEvent>
|
||||
</PreLinkEvent>
|
||||
<Link>
|
||||
<AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
|
||||
<ShowProgress>NotSet</ShowProgress>
|
||||
<OutputFile>$(OutDir)\vbsp.exe</OutputFile>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<BaseAddress> </BaseAddress>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<LinkErrorReporting>PromptImmediately</LinkErrorReporting>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Manifest>
|
||||
<Xdcmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Xdcmake>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<OutputFile>$(OutDir)/vbsp.bsc</OutputFile>
|
||||
</Bscmake>
|
||||
<PostBuildEvent>
|
||||
<Message>Publishing to ..\..\..\game\bin</Message>
|
||||
<Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
|
||||
</PostBuildEvent>
|
||||
<CustomBuildStep>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<PreBuildEvent>
|
||||
<Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj
if ERRORLEVEL 1 exit 1</Command>
|
||||
</PreBuildEvent>
|
||||
<ClCompile>
|
||||
<AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vbsp;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<OpenMPSupport>false</OpenMPSupport>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<ExpandAttributedSource>false</ExpandAttributedSource>
|
||||
<AssemblerOutput>NoListing</AssemblerOutput>
|
||||
<AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
|
||||
<ObjectFileName>$(IntDir)/</ObjectFileName>
|
||||
<ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
<BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
|
||||
<ErrorReporting>Prompt</ErrorReporting>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
|
||||
<Culture>1033</Culture>
|
||||
</ResourceCompile>
|
||||
<PreLinkEvent>
|
||||
</PreLinkEvent>
|
||||
<Link>
|
||||
<AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
|
||||
<ShowProgress>NotSet</ShowProgress>
|
||||
<OutputFile>$(OutDir)\vbsp.exe</OutputFile>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<BaseAddress> </BaseAddress>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<LinkErrorReporting>PromptImmediately</LinkErrorReporting>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Manifest>
|
||||
<Xdcmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
</Xdcmake>
|
||||
<Bscmake>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<OutputFile>$(OutDir)/vbsp.bsc</OutputFile>
|
||||
</Bscmake>
|
||||
<PostBuildEvent>
|
||||
<Message>Publishing to ..\..\..\game\bin</Message>
|
||||
<Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
|
||||
</PostBuildEvent>
|
||||
<CustomBuildStep>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="..\..\lib\public\bitmap.lib" />
|
||||
<Library Include="..\..\lib\public\fgdlib.lib" />
|
||||
<Library Include="..\..\lib\public\mathlib.lib" />
|
||||
<Library Include="..\..\lib\public\tier0.lib" />
|
||||
<Library Include="..\..\lib\public\tier1.lib" />
|
||||
<Library Include="..\..\lib\public\tier2.lib" />
|
||||
<Library Include="..\..\lib\public\vstdlib.lib" />
|
||||
<Library Include="..\..\lib\public\vtf.lib" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="boundbox.h" />
|
||||
<ClInclude Include="csg.h" />
|
||||
<ClInclude Include="detail.h" />
|
||||
<ClInclude Include="..\..\public\disp_powerinfo.h" />
|
||||
<ClInclude Include="disp_vbsp.h" />
|
||||
<ClInclude Include="..\..\public\disp_vertindex.h" />
|
||||
<ClInclude Include="faces.h" />
|
||||
<ClInclude Include="manifest.h" />
|
||||
<ClInclude Include="map.h" />
|
||||
<ClInclude Include="materialpatch.h" />
|
||||
<ClInclude Include="materialsub.h" />
|
||||
<ClInclude Include="..\common\scratchpad_helpers.h" />
|
||||
<ClInclude Include="vbsp.h" />
|
||||
<ClInclude Include="worldvertextransitionfixup.h" />
|
||||
<ClInclude Include="writebsp.h" />
|
||||
<ClInclude Include="..\common\bsplib.h" />
|
||||
<ClInclude Include="..\..\public\builddisp.h" />
|
||||
<ClInclude Include="..\..\public\ChunkFile.h" />
|
||||
<ClInclude Include="..\common\cmdlib.h" />
|
||||
<ClInclude Include="disp_ivp.h" />
|
||||
<ClInclude Include="..\..\public\filesystem.h" />
|
||||
<ClInclude Include="..\..\public\filesystem_helpers.h" />
|
||||
<ClInclude Include="..\common\FileSystem_Tools.h" />
|
||||
<ClInclude Include="..\..\public\GameBSPFile.h" />
|
||||
<ClInclude Include="..\..\public\tier1\interface.h" />
|
||||
<ClInclude Include="ivp.h" />
|
||||
<ClInclude Include="..\common\map_shared.h" />
|
||||
<ClInclude Include="..\common\pacifier.h" />
|
||||
<ClInclude Include="..\common\polylib.h" />
|
||||
<ClInclude Include="..\..\public\tier1\tokenreader.h" />
|
||||
<ClInclude Include="..\common\utilmatlib.h" />
|
||||
<ClInclude Include="..\vmpi\vmpi.h" />
|
||||
<ClInclude Include="..\..\public\zip_uncompressed.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\amd3dx.h" />
|
||||
<ClInclude Include="..\..\public\arraystack.h" />
|
||||
<ClInclude Include="..\..\public\tier0\basetypes.h" />
|
||||
<ClInclude Include="..\..\public\BSPFILE.H" />
|
||||
<ClInclude Include="..\..\public\bspflags.h" />
|
||||
<ClInclude Include="..\..\public\BSPTreeData.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\bumpvects.h" />
|
||||
<ClInclude Include="..\..\public\tier1\byteswap.h" />
|
||||
<ClInclude Include="..\..\public\cmodel.h" />
|
||||
<ClInclude Include="..\..\public\CollisionUtils.h" />
|
||||
<ClInclude Include="..\..\public\tier0\commonmacros.h" />
|
||||
<ClInclude Include="..\..\public\tier0\dbg.h" />
|
||||
<ClInclude Include="..\..\public\disp_common.h" />
|
||||
<ClInclude Include="..\..\public\IScratchPad3D.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\mathlib.h" />
|
||||
<ClInclude Include="..\common\mstristrip.h" />
|
||||
<ClInclude Include="..\..\public\nmatrix.h" />
|
||||
<ClInclude Include="..\..\public\NTree.h" />
|
||||
<ClInclude Include="..\..\public\nvector.h" />
|
||||
<ClInclude Include="..\..\public\phyfile.h" />
|
||||
<ClInclude Include="..\common\physdll.h" />
|
||||
<ClInclude Include="..\common\qfiles.h" />
|
||||
<ClInclude Include="..\..\public\ScratchPad3D.h" />
|
||||
<ClInclude Include="..\common\scriplib.h" />
|
||||
<ClInclude Include="..\..\public\studio.h" />
|
||||
<ClInclude Include="..\common\threads.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utlbuffer.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utlmemory.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utlrbtree.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utlsymbol.h" />
|
||||
<ClInclude Include="..\..\public\tier1\utlvector.h" />
|
||||
<ClInclude Include="..\..\public\vcollide.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\vector.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\vector2d.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\vector4d.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\vmatrix.h" />
|
||||
<ClInclude Include="..\..\public\vphysics_interface.h" />
|
||||
<ClInclude Include="..\..\public\mathlib\vplane.h" />
|
||||
<ClInclude Include="..\..\public\wadtypes.h" />
|
||||
<ClInclude Include="..\..\public\worldsize.h" />
|
||||
<ClInclude Include="..\common\tools_minidump.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="boundbox.cpp" />
|
||||
<ClCompile Include="brushbsp.cpp" />
|
||||
<ClCompile Include="..\..\public\CollisionUtils.cpp" />
|
||||
<ClCompile Include="csg.cpp" />
|
||||
<ClCompile Include="cubemap.cpp" />
|
||||
<ClCompile Include="detail.cpp" />
|
||||
<ClCompile Include="detailObjects.cpp" />
|
||||
<ClCompile Include="..\..\public\disp_common.cpp" />
|
||||
<ClCompile Include="disp_ivp.cpp" />
|
||||
<ClCompile Include="..\..\public\disp_powerinfo.cpp" />
|
||||
<ClCompile Include="disp_vbsp.cpp" />
|
||||
<ClCompile Include="faces.cpp" />
|
||||
<ClCompile Include="glfile.cpp" />
|
||||
<ClCompile Include="ivp.cpp" />
|
||||
<ClCompile Include="leakfile.cpp" />
|
||||
<ClCompile Include="..\..\public\loadcmdline.cpp" />
|
||||
<ClCompile Include="..\..\public\lumpfiles.cpp" />
|
||||
<ClCompile Include="manifest.cpp" />
|
||||
<ClCompile Include="map.cpp" />
|
||||
<ClCompile Include="materialpatch.cpp" />
|
||||
<ClCompile Include="materialsub.cpp" />
|
||||
<ClCompile Include="..\..\public\tier0\memoverride.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\mstristrip.cpp" />
|
||||
<ClCompile Include="nodraw.cpp" />
|
||||
<ClCompile Include="normals.cpp" />
|
||||
<ClCompile Include="overlay.cpp" />
|
||||
<ClCompile Include="..\common\physdll.cpp" />
|
||||
<ClCompile Include="portals.cpp" />
|
||||
<ClCompile Include="prtfile.cpp" />
|
||||
<ClCompile Include="..\..\public\ScratchPad3D.cpp" />
|
||||
<ClCompile Include="..\common\scratchpad_helpers.cpp" />
|
||||
<ClCompile Include="StaticProp.cpp" />
|
||||
<ClCompile Include="textures.cpp" />
|
||||
<ClCompile Include="tree.cpp" />
|
||||
<ClCompile Include="..\common\utilmatlib.cpp" />
|
||||
<ClCompile Include="vbsp.cpp" />
|
||||
<ClCompile Include="worldvertextransitionfixup.cpp" />
|
||||
<ClCompile Include="writebsp.cpp" />
|
||||
<ClCompile Include="..\..\public\zip_utils.cpp" />
|
||||
<ClCompile Include="..\common\bsplib.cpp" />
|
||||
<ClCompile Include="..\..\public\builddisp.cpp" />
|
||||
<ClCompile Include="..\..\public\ChunkFile.cpp" />
|
||||
<ClCompile Include="..\common\cmdlib.cpp" />
|
||||
<ClCompile Include="..\..\public\filesystem_helpers.cpp" />
|
||||
<ClCompile Include="..\..\public\filesystem_init.cpp" />
|
||||
<ClCompile Include="..\common\filesystem_tools.cpp" />
|
||||
<ClCompile Include="..\common\map_shared.cpp" />
|
||||
<ClCompile Include="..\common\pacifier.cpp" />
|
||||
<ClCompile Include="..\common\polylib.cpp" />
|
||||
<ClCompile Include="..\common\scriplib.cpp" />
|
||||
<ClCompile Include="..\common\threads.cpp" />
|
||||
<ClCompile Include="..\common\tools_minidump.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="notes.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
446
mp/src/utils/vbsp/vbsp-2010.vcxproj.filters
Normal file
446
mp/src/utils/vbsp/vbsp-2010.vcxproj.filters
Normal file
@@ -0,0 +1,446 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Common header files">
|
||||
<UniqueIdentifier>{34125DCB-B916-13A4-10E6-A29CCCB0DD70}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Link Libraries">
|
||||
<UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Public Headers">
|
||||
<UniqueIdentifier>{70104EB0-EB8F-8C16-99FB-ED7579D3A29D}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Common Files">
|
||||
<UniqueIdentifier>{A7DC6913-C602-1488-0EDF-DE69D12F2421}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Library Include="..\..\lib\public\bitmap.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\fgdlib.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\mathlib.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\tier0.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\tier1.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\tier2.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\vstdlib.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
<Library Include="..\..\lib\public\vtf.lib">
|
||||
<Filter>Link Libraries</Filter>
|
||||
</Library>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="boundbox.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="csg.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="detail.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\disp_powerinfo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="disp_vbsp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\disp_vertindex.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="faces.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="manifest.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="map.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="materialpatch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="materialsub.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\scratchpad_helpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vbsp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="worldvertextransitionfixup.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="writebsp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\bsplib.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\builddisp.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\ChunkFile.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\cmdlib.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="disp_ivp.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\filesystem.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\filesystem_helpers.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\FileSystem_Tools.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\GameBSPFile.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\interface.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ivp.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\map_shared.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\pacifier.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\polylib.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\tokenreader.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\utilmatlib.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\vmpi\vmpi.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\zip_uncompressed.h">
|
||||
<Filter>Header Files\Common header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\amd3dx.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\arraystack.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier0\basetypes.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\BSPFILE.H">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\bspflags.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\BSPTreeData.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\bumpvects.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\byteswap.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\cmodel.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\CollisionUtils.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier0\commonmacros.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier0\dbg.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\disp_common.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\IScratchPad3D.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\mathlib.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\mstristrip.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\nmatrix.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\NTree.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\nvector.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\phyfile.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\physdll.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\qfiles.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\ScratchPad3D.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\scriplib.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\studio.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\threads.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utlbuffer.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utllinkedlist.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utlmemory.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utlrbtree.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utlsymbol.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\tier1\utlvector.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\vcollide.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\vector.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\vector2d.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\vector4d.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\vmatrix.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\vphysics_interface.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\mathlib\vplane.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\wadtypes.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\public\worldsize.h">
|
||||
<Filter>Public Headers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\common\tools_minidump.h">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="boundbox.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="brushbsp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\CollisionUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="csg.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubemap.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="detail.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="detailObjects.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\disp_common.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="disp_ivp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\disp_powerinfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="disp_vbsp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="faces.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="glfile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ivp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="leakfile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\loadcmdline.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\lumpfiles.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="manifest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="map.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="materialpatch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="materialsub.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\tier0\memoverride.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\mstristrip.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="nodraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="normals.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="overlay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\physdll.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="portals.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="prtfile.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\ScratchPad3D.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\scratchpad_helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StaticProp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="textures.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tree.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\utilmatlib.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vbsp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="worldvertextransitionfixup.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="writebsp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\zip_utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\bsplib.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\builddisp.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\ChunkFile.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\cmdlib.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\filesystem_helpers.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\public\filesystem_init.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\filesystem_tools.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\map_shared.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\pacifier.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\polylib.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\scriplib.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\threads.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\common\tools_minidump.cpp">
|
||||
<Filter>Source Files\Common Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="notes.txt">
|
||||
<Filter></Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
1404
mp/src/utils/vbsp/vbsp.cpp
Normal file
1404
mp/src/utils/vbsp/vbsp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
657
mp/src/utils/vbsp/vbsp.h
Normal file
657
mp/src/utils/vbsp/vbsp.h
Normal file
@@ -0,0 +1,657 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#if !defined( VBSP_H )
|
||||
#define VBSP_H
|
||||
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib/vector.h"
|
||||
#include "scriplib.h"
|
||||
#include "polylib.h"
|
||||
#include "threads.h"
|
||||
#include "bsplib.h"
|
||||
#include "qfiles.h"
|
||||
#include "utilmatlib.h"
|
||||
#include "ChunkFile.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning( disable: 4706 )
|
||||
#endif
|
||||
|
||||
class CUtlBuffer;
|
||||
|
||||
#define MAX_BRUSH_SIDES 128
|
||||
#define CLIP_EPSILON 0.1
|
||||
|
||||
#define TEXINFO_NODE -1 // side is allready on a node
|
||||
|
||||
// this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc.
|
||||
#define DEBUG_BRUSHMODEL 0
|
||||
|
||||
struct portal_t;
|
||||
struct node_t;
|
||||
|
||||
struct plane_t : public dplane_t
|
||||
{
|
||||
plane_t *hash_chain;
|
||||
|
||||
plane_t() { normal.Init(); }
|
||||
};
|
||||
|
||||
|
||||
struct brush_texture_t
|
||||
{
|
||||
Vector UAxis;
|
||||
Vector VAxis;
|
||||
vec_t shift[2];
|
||||
vec_t rotate;
|
||||
vec_t textureWorldUnitsPerTexel[2];
|
||||
vec_t lightmapWorldUnitsPerLuxel;
|
||||
char name[TEXTURE_NAME_LENGTH];
|
||||
int flags;
|
||||
|
||||
brush_texture_t() : UAxis(0,0,0), VAxis(0,0,0) {}
|
||||
};
|
||||
|
||||
struct mapdispinfo_t;
|
||||
|
||||
struct side_t
|
||||
{
|
||||
int planenum;
|
||||
int texinfo;
|
||||
mapdispinfo_t *pMapDisp;
|
||||
|
||||
winding_t *winding;
|
||||
side_t *original; // bspbrush_t sides will reference the mapbrush_t sides
|
||||
int contents; // from miptex
|
||||
int surf; // from miptex
|
||||
qboolean visible; // choose visble planes first
|
||||
qboolean tested; // this plane allready checked as a split
|
||||
qboolean bevel; // don't ever use for bsp splitting
|
||||
|
||||
side_t *next;
|
||||
int origIndex;
|
||||
int id; // This is the unique id generated by worldcraft for this side.
|
||||
unsigned int smoothingGroups;
|
||||
CUtlVector<int> aOverlayIds; // List of overlays that reside on this side.
|
||||
CUtlVector<int> aWaterOverlayIds; // List of water overlays that reside on this side.
|
||||
bool m_bDynamicShadowsEnabled; // Goes into dface_t::SetDynamicShadowsEnabled().
|
||||
};
|
||||
|
||||
struct mapbrush_t
|
||||
{
|
||||
int entitynum;
|
||||
int brushnum;
|
||||
int id; // The unique ID of this brush in the editor, used for reporting errors.
|
||||
int contents;
|
||||
Vector mins, maxs;
|
||||
int numsides;
|
||||
side_t *original_sides;
|
||||
};
|
||||
|
||||
#define PLANENUM_LEAF -1
|
||||
|
||||
#define MAXEDGES 32
|
||||
|
||||
struct face_t
|
||||
{
|
||||
int id;
|
||||
|
||||
face_t *next; // on node
|
||||
|
||||
// the chain of faces off of a node can be merged or split,
|
||||
// but each face_t along the way will remain in the chain
|
||||
// until the entire tree is freed
|
||||
face_t *merged; // if set, this face isn't valid anymore
|
||||
face_t *split[2]; // if set, this face isn't valid anymore
|
||||
|
||||
portal_t *portal;
|
||||
int texinfo;
|
||||
int dispinfo;
|
||||
// This is only for surfaces that are the boundaries of fog volumes
|
||||
// (ie. water surfaces)
|
||||
// All of the rest of the surfaces can look at their leaf to find out
|
||||
// what fog volume they are in.
|
||||
node_t *fogVolumeLeaf;
|
||||
|
||||
int planenum;
|
||||
int contents; // faces in different contents can't merge
|
||||
int outputnumber;
|
||||
winding_t *w;
|
||||
int numpoints;
|
||||
qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
|
||||
int vertexnums[MAXEDGES];
|
||||
side_t *originalface; // save the "side" this face came from
|
||||
int firstPrimID;
|
||||
int numPrims;
|
||||
unsigned int smoothingGroups;
|
||||
};
|
||||
|
||||
void EmitFace( face_t *f, qboolean onNode );
|
||||
|
||||
struct mapdispinfo_t
|
||||
{
|
||||
face_t face;
|
||||
int entitynum;
|
||||
int power;
|
||||
int minTess;
|
||||
float smoothingAngle;
|
||||
Vector uAxis;
|
||||
Vector vAxis;
|
||||
Vector startPosition;
|
||||
float alphaValues[MAX_DISPVERTS];
|
||||
float maxDispDist;
|
||||
float dispDists[MAX_DISPVERTS];
|
||||
Vector vectorDisps[MAX_DISPVERTS];
|
||||
Vector vectorOffsets[MAX_DISPVERTS];
|
||||
int contents;
|
||||
int brushSideID;
|
||||
unsigned short triTags[MAX_DISPTRIS];
|
||||
int flags;
|
||||
|
||||
#ifdef VSVMFIO
|
||||
float m_elevation; // "elevation"
|
||||
Vector m_offsetNormals[ MAX_DISPTRIS ]; // "offset_normals"
|
||||
#endif // VSVMFIO
|
||||
|
||||
};
|
||||
|
||||
extern int nummapdispinfo;
|
||||
extern mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
|
||||
|
||||
extern float g_defaultLuxelSize;
|
||||
extern float g_luxelScale;
|
||||
extern float g_minLuxelScale;
|
||||
extern bool g_BumpAll;
|
||||
extern int g_nDXLevel;
|
||||
|
||||
int GetDispInfoEntityNum( mapdispinfo_t *pDisp );
|
||||
void ComputeBoundsNoSkybox( );
|
||||
|
||||
struct bspbrush_t
|
||||
{
|
||||
int id;
|
||||
bspbrush_t *next;
|
||||
Vector mins, maxs;
|
||||
int side, testside; // side of node during construction
|
||||
mapbrush_t *original;
|
||||
int numsides;
|
||||
side_t sides[6]; // variably sized
|
||||
};
|
||||
|
||||
|
||||
#define MAX_NODE_BRUSHES 8
|
||||
|
||||
struct leafface_t
|
||||
{
|
||||
face_t *pFace;
|
||||
leafface_t *pNext;
|
||||
};
|
||||
|
||||
struct node_t
|
||||
{
|
||||
int id;
|
||||
|
||||
// both leafs and nodes
|
||||
int planenum; // -1 = leaf node
|
||||
node_t *parent;
|
||||
Vector mins, maxs; // valid after portalization
|
||||
bspbrush_t *volume; // one for each leaf/node
|
||||
|
||||
// nodes only
|
||||
side_t *side; // the side that created the node
|
||||
node_t *children[2];
|
||||
face_t *faces; // these are the cutup ones that live in the plane of "side".
|
||||
|
||||
// leafs only
|
||||
bspbrush_t *brushlist; // fragments of all brushes in this leaf
|
||||
leafface_t *leaffacelist;
|
||||
int contents; // OR of all brush contents
|
||||
int occupied; // 1 or greater can reach entity
|
||||
entity_t *occupant; // for leak file testing
|
||||
int cluster; // for portalfile writing
|
||||
int area; // for areaportals
|
||||
portal_t *portals; // also on nodes during construction
|
||||
int diskId; // dnodes or dleafs index after this has been emitted
|
||||
};
|
||||
|
||||
|
||||
struct portal_t
|
||||
{
|
||||
int id;
|
||||
plane_t plane;
|
||||
node_t *onnode; // NULL = outside box
|
||||
node_t *nodes[2]; // [0] = front side of plane
|
||||
portal_t *next[2];
|
||||
winding_t *winding;
|
||||
qboolean sidefound; // false if ->side hasn't been checked
|
||||
side_t *side; // NULL = non-visible
|
||||
face_t *face[2]; // output face in bsp file
|
||||
};
|
||||
|
||||
|
||||
struct tree_t
|
||||
{
|
||||
node_t *headnode;
|
||||
node_t outside_node;
|
||||
Vector mins, maxs;
|
||||
bool leaked;
|
||||
};
|
||||
|
||||
|
||||
extern int entity_num;
|
||||
|
||||
struct LoadSide_t;
|
||||
struct LoadEntity_t;
|
||||
class CManifest;
|
||||
|
||||
class CMapFile
|
||||
{
|
||||
public:
|
||||
CMapFile( void ) { Init(); }
|
||||
|
||||
void Init( void );
|
||||
|
||||
void AddPlaneToHash (plane_t *p);
|
||||
int CreateNewFloatPlane (Vector& normal, vec_t dist);
|
||||
int FindFloatPlane (Vector& normal, vec_t dist);
|
||||
int PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2);
|
||||
void AddBrushBevels (mapbrush_t *b);
|
||||
qboolean MakeBrushWindings (mapbrush_t *ob);
|
||||
void MoveBrushesToWorld( entity_t *mapent );
|
||||
void MoveBrushesToWorldGeneral( entity_t *mapent );
|
||||
void RemoveContentsDetailFromEntity( entity_t *mapent );
|
||||
int SideIDToIndex( int brushSideID );
|
||||
void AddLadderKeys( entity_t *mapent );
|
||||
ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
|
||||
void ForceFuncAreaPortalWindowContents();
|
||||
ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
|
||||
ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
|
||||
ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
|
||||
void TestExpandBrushes(void);
|
||||
|
||||
static char m_InstancePath[ MAX_PATH ];
|
||||
static void SetInstancePath( const char *pszInstancePath );
|
||||
static const char *GetInstancePath( void ) { return m_InstancePath; }
|
||||
static bool DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName );
|
||||
|
||||
void CheckForInstances( const char *pszFileName );
|
||||
void MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance );
|
||||
void MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
|
||||
void MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
|
||||
void MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
|
||||
void ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity );
|
||||
void MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
|
||||
void MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
|
||||
|
||||
static int m_InstanceCount;
|
||||
static int c_areaportals;
|
||||
|
||||
plane_t mapplanes[MAX_MAP_PLANES];
|
||||
int nummapplanes;
|
||||
|
||||
#define PLANE_HASHES 1024
|
||||
plane_t *planehash[PLANE_HASHES];
|
||||
|
||||
int nummapbrushes;
|
||||
mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
|
||||
|
||||
Vector map_mins, map_maxs;
|
||||
|
||||
int nummapbrushsides;
|
||||
side_t brushsides[MAX_MAP_BRUSHSIDES];
|
||||
|
||||
brush_texture_t side_brushtextures[MAX_MAP_BRUSHSIDES];
|
||||
|
||||
int num_entities;
|
||||
entity_t entities[MAX_MAP_ENTITIES];
|
||||
|
||||
int c_boxbevels;
|
||||
int c_edgebevels;
|
||||
int c_clipbrushes;
|
||||
int g_ClipTexinfo;
|
||||
|
||||
class CConnectionPairs
|
||||
{
|
||||
public:
|
||||
CConnectionPairs( epair_t *pair, CConnectionPairs *next )
|
||||
{
|
||||
m_Pair = pair;
|
||||
m_Next = next;
|
||||
}
|
||||
|
||||
epair_t *m_Pair;
|
||||
CConnectionPairs *m_Next;
|
||||
};
|
||||
|
||||
CConnectionPairs *m_ConnectionPairs;
|
||||
|
||||
int m_StartMapOverlays;
|
||||
int m_StartMapWaterOverlays;
|
||||
};
|
||||
|
||||
extern CMapFile *g_MainMap;
|
||||
extern CMapFile *g_LoadingMap;
|
||||
|
||||
extern CUtlVector< CMapFile * > g_Maps;
|
||||
|
||||
extern int g_nMapFileVersion;
|
||||
|
||||
extern qboolean noprune;
|
||||
extern qboolean nodetail;
|
||||
extern qboolean fulldetail;
|
||||
extern qboolean nomerge;
|
||||
extern qboolean nomergewater;
|
||||
extern qboolean nosubdiv;
|
||||
extern qboolean nowater;
|
||||
extern qboolean noweld;
|
||||
extern qboolean noshare;
|
||||
extern qboolean notjunc;
|
||||
extern qboolean nocsg;
|
||||
extern qboolean noopt;
|
||||
extern qboolean dumpcollide;
|
||||
extern qboolean nodetailcuts;
|
||||
extern qboolean g_DumpStaticProps;
|
||||
extern qboolean g_bSkyVis;
|
||||
extern vec_t microvolume;
|
||||
extern bool g_snapAxialPlanes;
|
||||
extern bool g_NodrawTriggers;
|
||||
extern bool g_DisableWaterLighting;
|
||||
extern bool g_bAllowDetailCracks;
|
||||
extern bool g_bNoVirtualMesh;
|
||||
extern char outbase[32];
|
||||
|
||||
extern char source[1024];
|
||||
extern char mapbase[ 64 ];
|
||||
extern CUtlVector<int> g_SkyAreas;
|
||||
|
||||
bool LoadMapFile( const char *pszFileName );
|
||||
int GetVertexnum( Vector& v );
|
||||
bool Is3DSkyboxArea( int area );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// textures.c
|
||||
|
||||
struct textureref_t
|
||||
{
|
||||
char name[TEXTURE_NAME_LENGTH];
|
||||
int flags;
|
||||
float lightmapWorldUnitsPerLuxel;
|
||||
int contents;
|
||||
};
|
||||
|
||||
extern textureref_t textureref[MAX_MAP_TEXTURES];
|
||||
|
||||
int FindMiptex (const char *name);
|
||||
|
||||
int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin);
|
||||
int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName );
|
||||
|
||||
extern int g_SurfaceProperties[MAX_MAP_TEXDATA];
|
||||
void LoadSurfaceProperties( void );
|
||||
|
||||
int PointLeafnum ( dmodel_t* pModel, const Vector& p );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void FindGCD (int *v);
|
||||
|
||||
mapbrush_t *Brush_LoadEntity (entity_t *ent);
|
||||
int PlaneTypeForNormal (Vector& normal);
|
||||
qboolean MakeBrushPlanes (mapbrush_t *b);
|
||||
int FindIntPlane (int *inormal, int *iorigin);
|
||||
void CreateBrush (int brushnum);
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// detail objects
|
||||
//=============================================================================
|
||||
|
||||
void LoadEmitDetailObjectDictionary( char const* pGameDir );
|
||||
void EmitDetailObjects();
|
||||
|
||||
//=============================================================================
|
||||
// static props
|
||||
//=============================================================================
|
||||
|
||||
void EmitStaticProps();
|
||||
bool LoadStudioModel( char const* pFileName, char const* pEntityType, CUtlBuffer& buf );
|
||||
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// procedurally created .vmt files
|
||||
//=============================================================================
|
||||
|
||||
void EmitStaticProps();
|
||||
|
||||
// draw.c
|
||||
|
||||
extern Vector draw_mins, draw_maxs;
|
||||
extern bool g_bLightIfMissing;
|
||||
|
||||
void Draw_ClearWindow (void);
|
||||
void DrawWinding (winding_t *w);
|
||||
|
||||
void GLS_BeginScene (void);
|
||||
void GLS_Winding (winding_t *w, int code);
|
||||
void GLS_EndScene (void);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// csg
|
||||
|
||||
enum detailscreen_e
|
||||
{
|
||||
FULL_DETAIL = 0,
|
||||
ONLY_DETAIL = 1,
|
||||
NO_DETAIL = 2,
|
||||
};
|
||||
|
||||
#define TRANSPARENT_CONTENTS (CONTENTS_GRATE|CONTENTS_WINDOW)
|
||||
|
||||
#include "csg.h"
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// brushbsp
|
||||
|
||||
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
|
||||
|
||||
bspbrush_t *CopyBrush (bspbrush_t *brush);
|
||||
|
||||
void SplitBrush (bspbrush_t *brush, int planenum,
|
||||
bspbrush_t **front, bspbrush_t **back);
|
||||
|
||||
tree_t *AllocTree (void);
|
||||
node_t *AllocNode (void);
|
||||
bspbrush_t *AllocBrush (int numsides);
|
||||
int CountBrushList (bspbrush_t *brushes);
|
||||
void FreeBrush (bspbrush_t *brushes);
|
||||
vec_t BrushVolume (bspbrush_t *brush);
|
||||
node_t *NodeForPoint (node_t *node, Vector& origin);
|
||||
|
||||
void BoundBrush (bspbrush_t *brush);
|
||||
void FreeBrushList (bspbrush_t *brushes);
|
||||
node_t *PointInLeaf (node_t *node, Vector& point);
|
||||
|
||||
tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs);
|
||||
|
||||
#define PSIDE_FRONT 1
|
||||
#define PSIDE_BACK 2
|
||||
#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
|
||||
#define PSIDE_FACING 4
|
||||
int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane);
|
||||
extern qboolean WindingIsTiny (winding_t *w);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// portals.c
|
||||
|
||||
int VisibleContents (int contents);
|
||||
|
||||
void MakeHeadnodePortals (tree_t *tree);
|
||||
void MakeNodePortal (node_t *node);
|
||||
void SplitNodePortals (node_t *node);
|
||||
|
||||
qboolean Portal_VisFlood (portal_t *p);
|
||||
|
||||
qboolean FloodEntities (tree_t *tree);
|
||||
void FillOutside (node_t *headnode);
|
||||
void FloodAreas (tree_t *tree);
|
||||
void MarkVisibleSides (tree_t *tree, int start, int end, int detailScreen);
|
||||
void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount );
|
||||
void FreePortal (portal_t *p);
|
||||
void EmitAreaPortals (node_t *headnode);
|
||||
|
||||
void MakeTreePortals (tree_t *tree);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// glfile.c
|
||||
|
||||
void OutputWinding (winding_t *w, FileHandle_t glview);
|
||||
void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b);
|
||||
void WriteGLView (tree_t *tree, char *source);
|
||||
void WriteGLViewFaces (tree_t *tree, const char *source);
|
||||
void WriteGLViewBrushList( bspbrush_t *pList, const char *pName );
|
||||
//=============================================================================
|
||||
|
||||
// leakfile.c
|
||||
|
||||
void LeakFile (tree_t *tree);
|
||||
void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// prtfile.c
|
||||
|
||||
void AddVisCluster( entity_t *pFuncVisCluster );
|
||||
void WritePortalFile (tree_t *tree);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// writebsp.c
|
||||
|
||||
void SetModelNumbers (void);
|
||||
void SetLightStyles (void);
|
||||
|
||||
void BeginBSPFile (void);
|
||||
void WriteBSP (node_t *headnode, face_t *pLeafFaceList);
|
||||
void EndBSPFile (void);
|
||||
void BeginModel (void);
|
||||
void EndModel (void);
|
||||
|
||||
extern int firstmodeledge;
|
||||
extern int firstmodelface;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// faces.c
|
||||
|
||||
void MakeFaces (node_t *headnode);
|
||||
void MakeDetailFaces (node_t *headnode);
|
||||
face_t *FixTjuncs( node_t *headnode, face_t *pLeafFaceList );
|
||||
|
||||
face_t *AllocFace (void);
|
||||
void FreeFace (face_t *f);
|
||||
void FreeFaceList( face_t *pFaces );
|
||||
|
||||
void MergeFaceList(face_t **pFaceList);
|
||||
void SubdivideFaceList(face_t **pFaceList);
|
||||
|
||||
extern face_t *edgefaces[MAX_MAP_EDGES][2];
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tree.c
|
||||
|
||||
void FreeTree (tree_t *tree);
|
||||
void FreeTree_r (node_t *node);
|
||||
void PrintTree_r (node_t *node, int depth);
|
||||
void FreeTreePortals_r (node_t *node);
|
||||
void PruneNodes_r (node_t *node);
|
||||
void PruneNodes (node_t *node);
|
||||
|
||||
// Returns true if the entity is a func_occluder
|
||||
bool IsFuncOccluder( int entity_num );
|
||||
|
||||
|
||||
//=============================================================================
|
||||
// ivp.cpp
|
||||
class CPhysCollide;
|
||||
void EmitPhysCollision();
|
||||
void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename );
|
||||
void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *headnode );
|
||||
|
||||
//=============================================================================
|
||||
// find + find or create the texdata
|
||||
int FindTexData( const char *pName );
|
||||
int FindOrCreateTexData( const char *pName );
|
||||
// Add a clone of an existing texdata with a new name
|
||||
int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName );
|
||||
int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
|
||||
int FindAliasedTexData( const char *pName, dtexdata_t *sourceTexture );
|
||||
int FindTexInfo( const texinfo_t &searchTexInfo );
|
||||
|
||||
//=============================================================================
|
||||
// normals.c
|
||||
void SaveVertexNormals( void );
|
||||
|
||||
//=============================================================================
|
||||
// cubemap.cpp
|
||||
void Cubemap_InsertSample( const Vector& origin, int size );
|
||||
void Cubemap_CreateDefaultCubemaps( void );
|
||||
void Cubemap_SaveBrushSides( const char *pSideListStr );
|
||||
void Cubemap_FixupBrushSidesMaterials( void );
|
||||
void Cubemap_AttachDefaultCubemapToSpecularSides( void );
|
||||
// Add skipped cubemaps that are referenced by the engine
|
||||
void Cubemap_AddUnreferencedCubemaps( void );
|
||||
|
||||
//=============================================================================
|
||||
// overlay.cpp
|
||||
#define OVERLAY_MAP_STRLEN 256
|
||||
|
||||
struct mapoverlay_t
|
||||
{
|
||||
int nId;
|
||||
unsigned short m_nRenderOrder;
|
||||
char szMaterialName[OVERLAY_MAP_STRLEN];
|
||||
float flU[2];
|
||||
float flV[2];
|
||||
float flFadeDistMinSq;
|
||||
float flFadeDistMaxSq;
|
||||
Vector vecUVPoints[4];
|
||||
Vector vecOrigin;
|
||||
Vector vecBasis[3];
|
||||
CUtlVector<int> aSideList;
|
||||
CUtlVector<int> aFaceList;
|
||||
};
|
||||
|
||||
extern CUtlVector<mapoverlay_t> g_aMapOverlays;
|
||||
extern CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
|
||||
|
||||
int Overlay_GetFromEntity( entity_t *pMapEnt );
|
||||
void Overlay_UpdateSideLists( int StartIndex );
|
||||
void Overlay_AddFaceToLists( int iFace, side_t *pSide );
|
||||
void Overlay_EmitOverlayFaces( void );
|
||||
void OverlayTransition_UpdateSideLists( int StartIndex );
|
||||
void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide );
|
||||
void OverlayTransition_EmitOverlayFaces( void );
|
||||
void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
void RemoveAreaPortalBrushes_R( node_t *node );
|
||||
|
||||
dtexdata_t *GetTexData( int index );
|
||||
|
||||
#endif
|
||||
|
||||
212
mp/src/utils/vbsp/worldvertextransitionfixup.cpp
Normal file
212
mp/src/utils/vbsp/worldvertextransitionfixup.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "bsplib.h"
|
||||
#include "vbsp.h"
|
||||
#include "tier1/UtlBuffer.h"
|
||||
#include "tier1/utlvector.h"
|
||||
#include "KeyValues.h"
|
||||
#include "materialpatch.h"
|
||||
|
||||
struct entitySideList_t
|
||||
{
|
||||
int firstBrushSide;
|
||||
int brushSideCount;
|
||||
};
|
||||
|
||||
static bool SideIsNotDispAndHasDispMaterial( int iSide )
|
||||
{
|
||||
side_t *pSide = &g_MainMap->brushsides[iSide];
|
||||
|
||||
// If it's a displacement, then it's fine to have a displacement-only material.
|
||||
if ( pSide->pMapDisp )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pSide->texinfo;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void BackSlashToForwardSlash( char *pname )
|
||||
{
|
||||
while ( *pname ) {
|
||||
if ( *pname == '\\' )
|
||||
*pname = '/';
|
||||
pname++;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Generate patched material name
|
||||
//-----------------------------------------------------------------------------
|
||||
static void GeneratePatchedMaterialName( const char *pMaterialName, char *pBuffer, int nMaxLen )
|
||||
{
|
||||
int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s_wvt_patch", mapbase, pMaterialName );
|
||||
|
||||
Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
|
||||
if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
|
||||
{
|
||||
Error( "Generated worldvertextransition patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
|
||||
}
|
||||
|
||||
BackSlashToForwardSlash( pBuffer );
|
||||
Q_strlower( pBuffer );
|
||||
}
|
||||
|
||||
static void RemoveKey( KeyValues *kv, const char *pSubKeyName )
|
||||
{
|
||||
KeyValues *pSubKey = kv->FindKey( pSubKeyName );
|
||||
if( pSubKey )
|
||||
{
|
||||
kv->RemoveSubKey( pSubKey );
|
||||
pSubKey->deleteThis();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateWorldVertexTransitionPatchedMaterial( const char *pOriginalMaterialName, const char *pPatchedMaterialName )
|
||||
{
|
||||
KeyValues *kv = LoadMaterialKeyValues( pOriginalMaterialName, 0 );
|
||||
if( kv )
|
||||
{
|
||||
// change shader to Lightmappedgeneric (from worldvertextransition*)
|
||||
kv->SetName( "LightmappedGeneric" );
|
||||
// don't need no stinking $basetexture2 or any other second texture vars
|
||||
RemoveKey( kv, "$basetexture2" );
|
||||
RemoveKey( kv, "$bumpmap2" );
|
||||
RemoveKey( kv, "$bumpframe2" );
|
||||
RemoveKey( kv, "$basetexture2noenvmap" );
|
||||
RemoveKey( kv, "$blendmodulatetexture" );
|
||||
RemoveKey( kv, "$maskedblending" );
|
||||
RemoveKey( kv, "$surfaceprop2" );
|
||||
// If we didn't want a basetexture on the first texture in the blend, we don't want an envmap at all.
|
||||
KeyValues *basetexturenoenvmap = kv->FindKey( "$BASETEXTURENOENVMAP" );
|
||||
if( basetexturenoenvmap->GetInt() )
|
||||
{
|
||||
RemoveKey( kv, "$envmap" );
|
||||
}
|
||||
|
||||
Warning( "Patching WVT material: %s\n", pPatchedMaterialName );
|
||||
WriteMaterialKeyValuesToPak( pPatchedMaterialName, kv );
|
||||
}
|
||||
}
|
||||
|
||||
int CreateBrushVersionOfWorldVertexTransitionMaterial( int originalTexInfo )
|
||||
{
|
||||
// Don't make cubemap tex infos for nodes
|
||||
if ( originalTexInfo == TEXINFO_NODE )
|
||||
return originalTexInfo;
|
||||
|
||||
texinfo_t *pTexInfo = &texinfo[originalTexInfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
const char *pOriginalMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
|
||||
// Get out of here if the originalTexInfo is already a patched wvt material
|
||||
if ( Q_stristr( pOriginalMaterialName, "_wvt_patch" ) )
|
||||
return originalTexInfo;
|
||||
|
||||
char patchedMaterialName[1024];
|
||||
GeneratePatchedMaterialName( pOriginalMaterialName, patchedMaterialName, 1024 );
|
||||
// Warning( "GeneratePatchedMaterialName: %s %s\n", pMaterialName, patchedMaterialName );
|
||||
|
||||
// Make sure the texdata doesn't already exist.
|
||||
int nTexDataID = FindTexData( patchedMaterialName );
|
||||
bool bHasTexData = (nTexDataID != -1);
|
||||
if( !bHasTexData )
|
||||
{
|
||||
// Create the new vmt material file
|
||||
CreateWorldVertexTransitionPatchedMaterial( pOriginalMaterialName, patchedMaterialName );
|
||||
|
||||
// Make a new texdata
|
||||
nTexDataID = AddCloneTexData( pTexData, patchedMaterialName );
|
||||
}
|
||||
|
||||
Assert( nTexDataID != -1 );
|
||||
|
||||
texinfo_t newTexInfo;
|
||||
newTexInfo = *pTexInfo;
|
||||
newTexInfo.texdata = nTexDataID;
|
||||
|
||||
int nTexInfoID = -1;
|
||||
|
||||
// See if we need to make a new texinfo
|
||||
bool bHasTexInfo = false;
|
||||
if( bHasTexData )
|
||||
{
|
||||
nTexInfoID = FindTexInfo( newTexInfo );
|
||||
bHasTexInfo = (nTexInfoID != -1);
|
||||
}
|
||||
|
||||
// Make a new texinfo if we need to.
|
||||
if( !bHasTexInfo )
|
||||
{
|
||||
nTexInfoID = texinfo.AddToTail( newTexInfo );
|
||||
}
|
||||
|
||||
Assert( nTexInfoID != -1 );
|
||||
return nTexInfoID;
|
||||
}
|
||||
|
||||
const char *GetShaderNameForTexInfo( int iTexInfo )
|
||||
{
|
||||
texinfo_t *pTexInfo = &texinfo[iTexInfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
|
||||
const char *pShaderName = GetMaterialShaderName( hMaterial );
|
||||
return pShaderName;
|
||||
}
|
||||
|
||||
void WorldVertexTransitionFixup( void )
|
||||
{
|
||||
CUtlVector<entitySideList_t> sideList;
|
||||
sideList.SetCount( g_MainMap->num_entities );
|
||||
int i;
|
||||
for ( i = 0; i < g_MainMap->num_entities; i++ )
|
||||
{
|
||||
sideList[i].firstBrushSide = 0;
|
||||
sideList[i].brushSideCount = 0;
|
||||
}
|
||||
|
||||
for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
|
||||
{
|
||||
sideList[g_MainMap->mapbrushes[i].entitynum].brushSideCount += g_MainMap->mapbrushes[i].numsides;
|
||||
}
|
||||
int curSide = 0;
|
||||
for ( i = 0; i < g_MainMap->num_entities; i++ )
|
||||
{
|
||||
sideList[i].firstBrushSide = curSide;
|
||||
curSide += sideList[i].brushSideCount;
|
||||
}
|
||||
|
||||
int currentEntity = 0;
|
||||
for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
|
||||
{
|
||||
side_t *pSide = &g_MainMap->brushsides[iSide];
|
||||
|
||||
// skip displacments
|
||||
if ( pSide->pMapDisp )
|
||||
continue;
|
||||
|
||||
if( pSide->texinfo < 0 )
|
||||
continue;
|
||||
|
||||
const char *pShaderName = GetShaderNameForTexInfo( pSide->texinfo );
|
||||
if ( !pShaderName || !Q_stristr( pShaderName, "worldvertextransition" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
while ( currentEntity < g_MainMap->num_entities-1 &&
|
||||
iSide > sideList[currentEntity].firstBrushSide + sideList[currentEntity].brushSideCount )
|
||||
{
|
||||
currentEntity++;
|
||||
}
|
||||
|
||||
pSide->texinfo = CreateBrushVersionOfWorldVertexTransitionMaterial( pSide->texinfo );
|
||||
}
|
||||
}
|
||||
15
mp/src/utils/vbsp/worldvertextransitionfixup.h
Normal file
15
mp/src/utils/vbsp/worldvertextransitionfixup.h
Normal file
@@ -0,0 +1,15 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef WORLDVERTEXTRANSITIONFIXUP_H
|
||||
#define WORLDVERTEXTRANSITIONFIXUP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
void WorldVertexTransitionFixup( void );
|
||||
|
||||
#endif // WORLDVERTEXTRANSITIONFIXUP_H
|
||||
1552
mp/src/utils/vbsp/writebsp.cpp
Normal file
1552
mp/src/utils/vbsp/writebsp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
34
mp/src/utils/vbsp/writebsp.h
Normal file
34
mp/src/utils/vbsp/writebsp.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef WRITEBSP_H
|
||||
#define WRITEBSP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "bspfile.h"
|
||||
#include "utlmap.h"
|
||||
|
||||
struct node_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Emits occluder faces
|
||||
//-----------------------------------------------------------------------------
|
||||
void EmitOccluderFaces (node_t *node);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Free the list of faces stored at the leaves
|
||||
//-----------------------------------------------------------------------------
|
||||
void FreeLeafFaces( face_t *pLeafFaceList );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Make sure that we have a water lod control entity if we have water in the map.
|
||||
//-----------------------------------------------------------------------------
|
||||
void EnsurePresenceOfWaterLODControlEntity( void );
|
||||
|
||||
#endif // WRITEBSP_H
|
||||
Reference in New Issue
Block a user