First version of the SOurce SDK 2013

This commit is contained in:
Joe Ludwig
2013-06-26 15:22:04 -07:00
commit 39ed87570b
7469 changed files with 3312656 additions and 0 deletions

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

784
mp/src/utils/vbsp/csg.cpp Normal file
View 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
View 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

View 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 );
}
}
}

View 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;
}

View 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

View 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 );
}
}

View 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 &center, 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 &center, 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 );
}

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

20
mp/src/utils/vbsp/faces.h Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

75
mp/src/utils/vbsp/ivp.h Normal file
View 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

View 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 );
}

View 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;
}
}

View 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

File diff suppressed because it is too large Load Diff

18
mp/src/utils/vbsp/map.h Normal file
View 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

View 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 );
}

View 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

View 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 );
}

View 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

View 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)
{
}

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

View 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
//------------------------------------------------------------------

View 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;
}
}
}
}

File diff suppressed because it is too large Load Diff

View 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

View 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) );
}

View 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;
}

View 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
View 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);
}
//===========================================================

View 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 &quot;delims=&quot; %%A in (&apos;attrib &quot;..\..\..\game\bin\$(TargetFileName)&quot;&apos;) do set valveTmpIsReadOnly=&quot;%%A&quot;&#x0D;&#x0A;set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%&#x0D;&#x0A;if &quot;%valveTmpIsReadOnlyLetter%&quot;==&quot;R&quot; del /q &quot;$(TargetDir)&quot;$(TargetFileName)&#x0D;&#x0A;if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj&#x0D;&#x0A;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 &quot;..\..\..\game\bin&quot; mkdir &quot;..\..\..\game\bin&quot;&#x0D;&#x0A;copy &quot;$(TargetDir)&quot;$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)&#x0D;&#x0A;if ERRORLEVEL 1 goto BuildEventFailed&#x0D;&#x0A;if exist &quot;$(TargetDir)&quot;$(TargetName).map copy &quot;$(TargetDir)&quot;$(TargetName).map ..\..\..\game\bin\$(TargetName).map&#x0D;&#x0A;copy &quot;$(TargetDir)&quot;$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb&#x0D;&#x0A;if ERRORLEVEL 1 goto BuildEventFailed&#x0D;&#x0A;goto BuildEventOK&#x0D;&#x0A;:BuildEventFailed&#x0D;&#x0A;echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***&#x0D;&#x0A;del /q &quot;$(TargetDir)&quot;$(TargetFileName)&#x0D;&#x0A;exit 1&#x0D;&#x0A;:BuildEventOK&#x0D;&#x0A;</Command>
</PostBuildEvent>
<CustomBuildStep>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PreBuildEvent>
<Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f &quot;delims=&quot; %%A in (&apos;attrib &quot;..\..\..\game\bin\$(TargetFileName)&quot;&apos;) do set valveTmpIsReadOnly=&quot;%%A&quot;&#x0D;&#x0A;set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%&#x0D;&#x0A;if &quot;%valveTmpIsReadOnlyLetter%&quot;==&quot;R&quot; del /q &quot;$(TargetDir)&quot;$(TargetFileName)&#x0D;&#x0A;if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj&#x0D;&#x0A;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 &quot;..\..\..\game\bin&quot; mkdir &quot;..\..\..\game\bin&quot;&#x0D;&#x0A;copy &quot;$(TargetDir)&quot;$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)&#x0D;&#x0A;if ERRORLEVEL 1 goto BuildEventFailed&#x0D;&#x0A;if exist &quot;$(TargetDir)&quot;$(TargetName).map copy &quot;$(TargetDir)&quot;$(TargetName).map ..\..\..\game\bin\$(TargetName).map&#x0D;&#x0A;copy &quot;$(TargetDir)&quot;$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb&#x0D;&#x0A;if ERRORLEVEL 1 goto BuildEventFailed&#x0D;&#x0A;goto BuildEventOK&#x0D;&#x0A;:BuildEventFailed&#x0D;&#x0A;echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***&#x0D;&#x0A;del /q &quot;$(TargetDir)&quot;$(TargetFileName)&#x0D;&#x0A;exit 1&#x0D;&#x0A;:BuildEventOK&#x0D;&#x0A;</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'">&quot;$(VCInstallDir)bin\ml.exe&quot; /c /Cp /Zi /Fo&quot;$(IntDir)\%(Filename).obj&quot; &quot;%(FullPath)&quot;</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'">&quot;$(VCInstallDir)bin\ml.exe&quot; /c /Cp /Zi /Fo&quot;$(IntDir)\%(Filename).obj&quot; &quot;%(FullPath)&quot;</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>

View 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

File diff suppressed because it is too large Load Diff

657
mp/src/utils/vbsp/vbsp.h Normal file
View 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

View 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 );
}
}

View 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

File diff suppressed because it is too large Load Diff

View 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