initial
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
// ----------------------------------------- //
|
||||
// File generated by VPC //
|
||||
// ----------------------------------------- //
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\boundbox.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\boundbox.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\boundbox.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\brushbsp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\brushbsp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\brushbsp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\collisionutils.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\collisionutils.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\collisionutils.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\csg.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\csg.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\csg.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\cubemap.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\cubemap.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\cubemap.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\detail.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\detail.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\detail.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\detailobjects.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\detailobjects.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\detailobjects.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\disp_common.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\disp_common.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\disp_common.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_ivp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_ivp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_ivp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\disp_powerinfo.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\disp_powerinfo.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\disp_powerinfo.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_vbsp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_vbsp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\disp_vbsp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\faces.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\faces.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\faces.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\glfile.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\glfile.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\glfile.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\ivp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\ivp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\ivp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\leakfile.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\leakfile.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\leakfile.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\loadcmdline.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\loadcmdline.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\loadcmdline.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\lumpfiles.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\lumpfiles.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\lumpfiles.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\manifest.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\manifest.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\manifest.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\map.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\map.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\map.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\materialpatch.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\materialpatch.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\materialpatch.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\materialsub.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\materialsub.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\materialsub.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\tier0\memoverride.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\mstristrip.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\mstristrip.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\mstristrip.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\nodraw.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\nodraw.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\nodraw.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\normals.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\normals.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\normals.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\overlay.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\overlay.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\overlay.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\physdll.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\physdll.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\physdll.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\portals.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\portals.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\portals.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\prtfile.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\prtfile.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\prtfile.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\scratchpad3d.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\scratchpad3d.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\scratchpad3d.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\scratchpad_helpers.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\scratchpad_helpers.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\scratchpad_helpers.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\staticprop.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\staticprop.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\staticprop.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\textures.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\textures.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\textures.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\tree.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\tree.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\tree.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\utilmatlib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\utilmatlib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\utilmatlib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\vbsp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\vbsp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\vbsp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\worldvertextransitionfixup.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\worldvertextransitionfixup.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\worldvertextransitionfixup.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\vbsp\writebsp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\vbsp\writebsp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\vbsp\writebsp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\zip_utils.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\zip_utils.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\zip_utils.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\bsplib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\bsplib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\bsplib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\builddisp.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\builddisp.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\builddisp.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\chunkfile.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\chunkfile.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\chunkfile.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\cmdlib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\cmdlib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\cmdlib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\filesystem_helpers.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\public\filesystem_init.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\public\filesystem_init.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\public\filesystem_init.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\filesystem_tools.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\filesystem_tools.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\filesystem_tools.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\map_shared.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\map_shared.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\map_shared.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\pacifier.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\pacifier.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\pacifier.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\polylib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\polylib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\polylib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\scriplib.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\scriplib.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\scriplib.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\threads.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\threads.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\threads.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\utils\common\tools_minidump.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\utils\common\tools_minidump.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\utils\common\tools_minidump.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\vmfentitysupport.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\vmfentitysupport.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\vmfentitysupport.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
Source file: F:\csgo_64\cstrike15_src\common\vmfmeshdatasupport.cpp
|
||||
Debug output file: F:\csgo_64\cstrike15_src\common\vmfmeshdatasupport.cpp
|
||||
Release output file: F:\csgo_64\cstrike15_src\common\vmfmeshdatasupport.cpp
|
||||
Containing unity file:
|
||||
PCH file:
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
//========= Copyright © 1996-2005, 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"
|
||||
|
||||
|
||||
#if !defined(_MSC_VER) || _MSC_VER < 1800
|
||||
// This C99 function exists in VS 2013's math.h but are not currently available elsewhere.
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
//========= Copyright © 1996-2005, 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
@@ -0,0 +1,781 @@
|
||||
//========= Copyright © 1996-2005, 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_BLOCKLIGHT)
|
||||
ADD_CONTENTS(CONTENTS_TEAM1)
|
||||
ADD_CONTENTS(CONTENTS_TEAM2)
|
||||
ADD_CONTENTS(CONTENTS_MOVEABLE)
|
||||
ADD_CONTENTS(CONTENTS_AREAPORTAL)
|
||||
ADD_CONTENTS(CONTENTS_PLAYERCLIP)
|
||||
ADD_CONTENTS(CONTENTS_MONSTERCLIP)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,795 @@
|
||||
//========= Copyright © 1996-2005, 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;
|
||||
}
|
||||
|
||||
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) );
|
||||
|
||||
// Create the destination cubemap
|
||||
IVTFTexture *pDstCubemap = CreateVTFTexture();
|
||||
pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
|
||||
IMAGE_FORMAT_DEFAULT, TEXTUREFLAGS_ENVMAP, 1 );
|
||||
|
||||
// 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 )
|
||||
{
|
||||
unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
|
||||
int iSize = pDstCubemap->ComputeMipSize( iMip );
|
||||
// Make the default cubemap black
|
||||
memset( pDstBits, 0, iSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the bits are in place, compute the spheremaps...
|
||||
pDstCubemap->GenerateSpheremap();
|
||||
|
||||
// Convert the cubemap to the final format
|
||||
pDstCubemap->ConvertImageFormat( bHDR ? IMAGE_FORMAT_RGBA16161616F : IMAGE_FORMAT_DXT5, 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 );
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
//========= Copyright © 1996-2005, 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.Count(); 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.Count() )
|
||||
{
|
||||
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;
|
||||
f->smoothingGroups = originalSide->smoothingGroups;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static bool IsAllNodraw( mapbrush_t *pMapBrush )
|
||||
{
|
||||
for ( int i = 0; i < pMapBrush->numsides; i++ )
|
||||
{
|
||||
if ( !(pMapBrush->original_sides[i].surf & SURF_NODRAW) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,999 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Places "detail" objects which are client-only renderable things
|
||||
//
|
||||
// $Revision: $
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#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;
|
||||
static CUtlVector<entity_t *> g_BlockerList;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 );
|
||||
}
|
||||
|
||||
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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define MAX_DETAIL_SPRITES 65535 * 32
|
||||
|
||||
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 >= MAX_DETAIL_SPRITES)
|
||||
{
|
||||
Error( "Error! Too many detail props emitted on this map!\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
|
||||
|
||||
// TERROR:
|
||||
for( int i = 0; i < g_BlockerList.Count(); ++i )
|
||||
{
|
||||
entity_t *ent = g_BlockerList[i];
|
||||
{
|
||||
for ( int j = 0; j < ent->numbrushes; ++j )
|
||||
{
|
||||
int brushnum = ent->firstbrush + j;
|
||||
mapbrush_t *brush = &g_MainMap->mapbrushes[ brushnum ];
|
||||
if ( IsPointInBox( pt, brush->mins, brush->maxs ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 : ");
|
||||
|
||||
// build detail blocker list
|
||||
g_BlockerList.RemoveAll();
|
||||
for( int i = 0; i < num_entities; ++i )
|
||||
{
|
||||
entity_t *ent = &entities[i];
|
||||
char* classname = ValueForKey( ent, "classname" );
|
||||
if ( !strcmp( classname, "func_detail_blocker" ) )
|
||||
{
|
||||
g_BlockerList.AddToTail(ent);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
//========= Copyright © 1996-2005, 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;
|
||||
}
|
||||
|
||||
const char* GetMatNameFromDisp( mapdispinfo_t *pMapDisp )
|
||||
{
|
||||
texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
return TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
}
|
||||
|
||||
// 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 flTotalAlpha[3] = { 0,0,0 };
|
||||
for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
|
||||
{
|
||||
pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
|
||||
if ( ( pDispInfo->GetFlags() & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
|
||||
{
|
||||
Vector4D vBlend;
|
||||
pDispInfo->GetMultiBlend( indices[iTri*3+iTriVert], vBlend );
|
||||
flTotalAlpha[0] += vBlend.y;
|
||||
flTotalAlpha[1] += vBlend.z;
|
||||
flTotalAlpha[2] += vBlend.w;
|
||||
}
|
||||
else
|
||||
{
|
||||
flTotalAlpha[0] += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
|
||||
}
|
||||
}
|
||||
|
||||
int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
|
||||
|
||||
if ( ( pDispInfo->GetFlags() & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
|
||||
{
|
||||
if ( ( flTotalAlpha[2] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[2] >= flTotalAlpha[1] ) && ( flTotalAlpha[2] >= flTotalAlpha[0] ) )
|
||||
{
|
||||
int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop4" );
|
||||
if ( nPropA != -1 )
|
||||
{
|
||||
nProp = nPropA;
|
||||
}
|
||||
Msg("Picked prop4\n");
|
||||
}
|
||||
else if ( ( flTotalAlpha[1] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[1] >= flTotalAlpha[0] ) )
|
||||
{
|
||||
int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop3" );
|
||||
if ( nPropA != -1 )
|
||||
{
|
||||
nProp = nPropA;
|
||||
}
|
||||
Msg("Picked prop3\n");
|
||||
}
|
||||
else if ( flTotalAlpha[0] > DISP_MULTIBLEND_PROP_THRESHOLD )
|
||||
{
|
||||
int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop2" );
|
||||
if ( nPropA != -1 )
|
||||
{
|
||||
nProp = nPropA;
|
||||
}
|
||||
Msg("Picked prop2\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( flTotalAlpha[0] > DISP_ALPHA_PROP_DELTA )
|
||||
{
|
||||
int nProp2 = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop2" );
|
||||
if ( nProp2 != -1 )
|
||||
{
|
||||
nProp = nProp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
int nMaterialIndex = RemapWorldMaterial( nProp );
|
||||
physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
|
||||
}
|
||||
}
|
||||
|
||||
// convert the whole grid's polysoup to a collide and store in the collision list
|
||||
CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
|
||||
if ( pCollide )
|
||||
{
|
||||
collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
|
||||
}
|
||||
// now that we have the collide, we're done with the soup
|
||||
physcollision->PolysoupDestroy( pTerrainPhysics );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CDispMeshEvent : public IVirtualMeshEvent
|
||||
{
|
||||
public:
|
||||
CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
|
||||
virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
|
||||
virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
|
||||
virtual void GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList );
|
||||
|
||||
CUtlVector<Vector> m_verts;
|
||||
unsigned short *m_pIndices;
|
||||
int m_indexCount;
|
||||
};
|
||||
|
||||
CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
|
||||
{
|
||||
m_pIndices = pIndices;
|
||||
m_indexCount = indexCount;
|
||||
int maxIndex = 0;
|
||||
for ( int i = 0; i < indexCount; i++ )
|
||||
{
|
||||
if ( pIndices[i] > maxIndex )
|
||||
{
|
||||
maxIndex = pIndices[i];
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < indexCount/2; i++ )
|
||||
{
|
||||
V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
|
||||
}
|
||||
int count = maxIndex + 1;
|
||||
m_verts.SetCount( count );
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
m_verts[i] = pDispInfo->GetVert(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
pList->pVerts = m_verts.Base();
|
||||
pList->indexCount = m_indexCount;
|
||||
pList->triangleCount = m_indexCount/3;
|
||||
pList->vertexCount = m_verts.Count();
|
||||
pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
|
||||
pList->pHull = NULL;
|
||||
int indexMax = ARRAYSIZE(pList->indices);
|
||||
int indexCount = MIN(m_indexCount, indexMax);
|
||||
Assert(m_indexCount < indexMax);
|
||||
Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
ClearBounds( *pMins, *pMaxs );
|
||||
for ( int i = 0; i < m_verts.Count(); i++ )
|
||||
{
|
||||
AddPointToBounds( m_verts[i], *pMins, *pMaxs );
|
||||
}
|
||||
}
|
||||
|
||||
void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList )
|
||||
{
|
||||
Assert(userData==((void *)this));
|
||||
pList->triangleCount = m_indexCount/3;
|
||||
int indexMax = ARRAYSIZE(pList->triangleIndices);
|
||||
int indexCount = MIN(m_indexCount, indexMax);
|
||||
Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
|
||||
Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
|
||||
}
|
||||
|
||||
void Disp_BuildVirtualMesh( int contentsMask )
|
||||
{
|
||||
CUtlVector<CPhysCollide *> virtualMeshes;
|
||||
virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
|
||||
for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
|
||||
{
|
||||
CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
|
||||
mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
|
||||
|
||||
virtualMeshes[i] = NULL;
|
||||
// not solid for this pass
|
||||
if ( !(pMapDisp->contents & contentsMask) )
|
||||
continue;
|
||||
|
||||
// Build a triangle list. This shares the tesselation code with the engine.
|
||||
CUtlVector<unsigned short> indices;
|
||||
CVBSPTesselateHelper helper;
|
||||
helper.m_pIndices = &indices;
|
||||
helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
|
||||
helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
|
||||
|
||||
::TesselateDisplacement( &helper );
|
||||
|
||||
// validate the collision data
|
||||
if ( 1 )
|
||||
{
|
||||
int triCount = indices.Count() / 3;
|
||||
for ( int j = 0; j < triCount; j++ )
|
||||
{
|
||||
int index = j * 3;
|
||||
Vector v0 = pDispInfo->GetVert( indices[index+0] );
|
||||
Vector v1 = pDispInfo->GetVert( indices[index+1] );
|
||||
Vector v2 = pDispInfo->GetVert( indices[index+2] );
|
||||
if ( v0 == v1 || v1 == v2 || v2 == v0 )
|
||||
{
|
||||
Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
|
||||
texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
|
||||
dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
|
||||
const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
|
||||
|
||||
Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
|
||||
virtualmeshparams_t params;
|
||||
params.buildOuterHull = true;
|
||||
params.pMeshEventHandler = &meshHandler;
|
||||
params.userData = &meshHandler;
|
||||
virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
|
||||
}
|
||||
unsigned int totalSize = 0;
|
||||
CUtlBuffer buf;
|
||||
dphysdisp_t header;
|
||||
header.numDisplacements = g_CoreDispInfos.Count();
|
||||
buf.PutObjects( &header );
|
||||
|
||||
CUtlVector<char> dispBuf;
|
||||
for ( int i = 0; i < header.numDisplacements; i++ )
|
||||
{
|
||||
if ( virtualMeshes[i] )
|
||||
{
|
||||
unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
|
||||
totalSize += testSize;
|
||||
buf.PutShort( testSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.PutShort( -1 );
|
||||
}
|
||||
}
|
||||
for ( int i = 0; i < header.numDisplacements; i++ )
|
||||
{
|
||||
if ( virtualMeshes[i] )
|
||||
{
|
||||
unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
|
||||
dispBuf.RemoveAll();
|
||||
dispBuf.EnsureCount(testSize);
|
||||
|
||||
unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
|
||||
Assert( outSize == testSize );
|
||||
buf.Put( dispBuf.Base(), outSize );
|
||||
}
|
||||
}
|
||||
g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
|
||||
Assert( buf.TellMaxPut() == g_PhysDispSize );
|
||||
g_PhysDispSize = buf.TellMaxPut();
|
||||
g_pPhysDisp = new byte[g_PhysDispSize];
|
||||
Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
|
||||
#endif // DISP_IVP_H
|
||||
@@ -0,0 +1,688 @@
|
||||
//========= Copyright © 1996-2005, 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;
|
||||
CUtlBlockVector<mapdispinfo_t> mapdispinfo;
|
||||
|
||||
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, pMapDisp->flags, pMapDisp->m_vMultiBlends );
|
||||
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;
|
||||
int nTotalMultiBlend = 0;
|
||||
for ( i=0; i < nummapdispinfo; i++ )
|
||||
{
|
||||
nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
|
||||
nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power );
|
||||
if ( ( mapdispinfo[ i ].flags & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
|
||||
{
|
||||
nTotalMultiBlend += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the output arrays..
|
||||
g_dispinfo.Purge();
|
||||
g_dispinfo.SetSize( nummapdispinfo );
|
||||
g_DispVerts.SetSize( nTotalVerts );
|
||||
g_DispTris.SetSize( nTotalTris );
|
||||
g_DispMultiBlend.SetSize( nTotalMultiBlend );
|
||||
|
||||
int iCurVert = 0;
|
||||
int iCurTri = 0;
|
||||
int iCurMultiBlend = 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 | DISP_INFO_FLAG_MAGIC;
|
||||
// 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];
|
||||
|
||||
if ( ( pMapDisp->flags & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
|
||||
{
|
||||
g_DispMultiBlend[ iCurMultiBlend ] = pMapDisp->m_vMultiBlends[ j ];
|
||||
iCurMultiBlend++;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,219 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
}
|
||||
+1652
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,167 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "vbsp.h"
|
||||
#include "color.h"
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
LEAF FILE GENERATION
|
||||
|
||||
Save out name.line for qe3 to read
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_GENERAL, "General" );
|
||||
|
||||
/*
|
||||
=============
|
||||
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");
|
||||
Log_Msg( LOG_GENERAL, Color( 255, 0, 0, 255 ), "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 );
|
||||
Log_Msg( LOG_GENERAL, Color( 255, 0, 0, 255 ), "Areaportal leak ! File: %s ", filename );
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
//========= Copyright © 1996-2009, 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
|
||||
+3932
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
//========= Copyright © 1996-2005, 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;
|
||||
|
||||
|
||||
class IMapDataFilesMgr
|
||||
{
|
||||
public:
|
||||
virtual void RegisterFile( char const *szFileName, CUtlBuffer &bufData ) = 0;
|
||||
virtual bool ReadRegisteredFile( char const *szFileName, CUtlBuffer &bufRead ) = 0;
|
||||
virtual void AddAllRegisteredFilesToPak() = 0;
|
||||
};
|
||||
|
||||
IMapDataFilesMgr *GetMapDataFilesMgr();
|
||||
|
||||
|
||||
#endif // MAP_H
|
||||
@@ -0,0 +1,440 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//========= Copyright © 1996-2005, 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 );
|
||||
|
||||
// This makes it so that when looking for whether a material has a cubemap, etc., anything that
|
||||
// has the pNewMaterialName will look at it's original material name.
|
||||
extern void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName );
|
||||
|
||||
#endif // MATERIALPATCH_H
|
||||
@@ -0,0 +1,90 @@
|
||||
//====== Copyright © 1996-2004, 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 );
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//====== Copyright © 1996-2004, 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
|
||||
@@ -0,0 +1,32 @@
|
||||
//========= Copyright © 1996-2005, 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)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//========= Copyright © 1996-2005, 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++;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
//------------------------------------------------------------------
|
||||
@@ -0,0 +1,499 @@
|
||||
//========= Copyright © 1996-2005, 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;
|
||||
}
|
||||
|
||||
pMapOverlay->nMinCPULevel = IntForKey( pMapEnt, "mincpulevel" );
|
||||
pMapOverlay->nMaxCPULevel = IntForKey( pMapEnt, "maxcpulevel" );
|
||||
pMapOverlay->nMinGPULevel = IntForKey( pMapEnt, "mingpulevel" );
|
||||
pMapOverlay->nMaxGPULevel = IntForKey( pMapEnt, "maxgpulevel" );
|
||||
|
||||
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 );
|
||||
|
||||
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];
|
||||
doverlaysystemlevel_t *pOverlaySystemLevel = &g_OverlaySystemLevels[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;
|
||||
}
|
||||
|
||||
if ( pOverlaySystemLevel )
|
||||
{
|
||||
pOverlaySystemLevel->nMinCPULevel = pMapOverlay->nMinCPULevel;
|
||||
pOverlaySystemLevel->nMaxCPULevel = pMapOverlay->nMaxCPULevel;
|
||||
pOverlaySystemLevel->nMinGPULevel = pMapOverlay->nMinGPULevel;
|
||||
pOverlaySystemLevel->nMaxGPULevel = pMapOverlay->nMaxGPULevel;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#include "physlevelhavok.h"
|
||||
#include "alignedarray.h"
|
||||
#include "bsplib.h"
|
||||
#include "physdll.h"
|
||||
#include "vbsp.h"
|
||||
|
||||
CPhysLevelHavokEmitter::CPhysLevelHavokEmitter()
|
||||
{
|
||||
m_numModels = nummodels;
|
||||
m_pModels = dmodels;
|
||||
m_pNodes = dnodes;
|
||||
m_numBrushes = numbrushes;
|
||||
m_pLeafs = dleafs;
|
||||
m_pLeafBrushes = dleafbrushes;
|
||||
m_pBrushes = dbrushes;
|
||||
m_pBrushSides = dbrushsides;
|
||||
m_pPlanes = dplanes;
|
||||
|
||||
m_bConvertBrushesToMopp = !g_bPhysNoMopp;
|
||||
m_buildInertia = false;
|
||||
m_exportObjMopp = !!g_bPhysExportMoppObj;
|
||||
|
||||
m_physics = NULL;
|
||||
|
||||
CreateInterfaceFn physicsFactory = GetPhysicsFactory();
|
||||
if ( physicsFactory )
|
||||
{
|
||||
m_physics = (IPhysics2*)physicsFactory( VPHYSICS2_INTERFACE_VERSION, NULL );
|
||||
}
|
||||
|
||||
if ( !m_physics )
|
||||
Warning("!!! WARNING: Can't build collision2 data!\n" );
|
||||
|
||||
m_cook = m_physics->GetCook();
|
||||
}
|
||||
|
||||
CPhysLevelHavokEmitter::~CPhysLevelHavokEmitter()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// TODO: move this to Cook interface
|
||||
void CPhysLevelHavokEmitter::Emit()
|
||||
{
|
||||
CVarBitVec useBrush(m_numBrushes);
|
||||
for ( int i = 0; i < m_numModels; ++i )
|
||||
{
|
||||
dmodel_t *pModel = &m_pModels[i];
|
||||
AddBrushes(pModel->headnode, useBrush);
|
||||
}
|
||||
|
||||
CUtlVector<IPhysics2CookedPolytope *> arrPolytopes;
|
||||
ConvertBrushesToPolytopes(useBrush, arrPolytopes);
|
||||
|
||||
dphyslevelV0_t *pRoot = Write<dphyslevelV0_t>();
|
||||
pRoot->toolVersion = 2;
|
||||
pRoot->dataVersion = m_physics->GetSerializeVersion();
|
||||
pRoot->sizeofDiskPhysics2Polytope = sizeof(DiskPhysics2Polytope_t);
|
||||
pRoot->buildTime = (int)time(NULL);
|
||||
|
||||
bool outputIndividualPolytopes = true;
|
||||
// having the number of polytopes, we need to generate MOPP
|
||||
if(m_bConvertBrushesToMopp)
|
||||
{
|
||||
int numPolytopes = arrPolytopes.Count();
|
||||
CUtlVector<IPhysics2CookedMeshBase*>arrMeshes(0,numPolytopes);
|
||||
for(int i = 0; i < numPolytopes; ++i)
|
||||
arrMeshes[i] = arrPolytopes[i]; // conver the pointers
|
||||
if(m_exportObjMopp)m_cook->ExportObj("d:\\mopp.obj", arrMeshes.Base(), numPolytopes);
|
||||
|
||||
IPhysics2CookedMopp *pMopp = m_cook->CookMopp(arrMeshes.Base(), numPolytopes);
|
||||
if(pMopp)
|
||||
{
|
||||
Msg("Physics2Mopp cooked, mem size: %.1f\n", pMopp->GetSizeOf()/1024.);
|
||||
|
||||
void *pSerializedMopp = m_cook->Serialize(pMopp->GetMopp(), this);
|
||||
IStream::Link(&pRoot->mopp, pSerializedMopp);
|
||||
outputIndividualPolytopes = false; // we don't need to output individual polytopes, because MOPP will contain them separately
|
||||
m_cook->Destroy(pMopp);
|
||||
}
|
||||
else
|
||||
Warning("Could not build polysoup from brushes\n");
|
||||
}
|
||||
|
||||
if(false)
|
||||
{
|
||||
int numPolytopes = arrPolytopes.Count();
|
||||
CUtlVector<IPhysics2CookedMeshBase*>arrMeshes(0,numPolytopes);
|
||||
for(int i = 0; i < numPolytopes; ++i)
|
||||
arrMeshes[i] = arrPolytopes[i]; // conver the pointers
|
||||
IPhysics2CookedPolysoup *polysoup = m_cook->CookPolysoupFromMeshes(arrMeshes.Base(), numPolytopes);
|
||||
if(polysoup)
|
||||
{
|
||||
//polysoup->ExportObj("d:\\test.obj");
|
||||
m_cook->Destroy(polysoup);
|
||||
}
|
||||
}
|
||||
|
||||
int numPolytopes = arrPolytopes.Count();
|
||||
DiskPhysics2Polytope_t *pDiskPolytopes = NULL;
|
||||
if(outputIndividualPolytopes)
|
||||
pDiskPolytopes = WriteAndLinkArray(&pRoot->polytopes,numPolytopes);
|
||||
|
||||
for(int nPolytope = 0; nPolytope < numPolytopes; ++nPolytope)
|
||||
{
|
||||
IPhysics2CookedPolytope *pPolytope = arrPolytopes[nPolytope];
|
||||
if(outputIndividualPolytopes)
|
||||
{
|
||||
void *pSerializedPolytope = m_cook->Serialize(pPolytope->GetPolytope(), this);
|
||||
Link(&pDiskPolytopes[nPolytope].offsetPolytope, pSerializedPolytope);
|
||||
|
||||
if(m_buildInertia)
|
||||
{
|
||||
IPhysics2CookedInertia *pInertia = m_cook->CookInertia(pPolytope->GetPolytope());
|
||||
void* pSerializedInertia = m_cook->Serialize(pInertia->GetInertia(), this);
|
||||
Link(&pDiskPolytopes[nPolytope].offsetInertia, pSerializedInertia);
|
||||
m_cook->Destroy(pInertia);
|
||||
}
|
||||
}
|
||||
m_cook->Destroy(pPolytope);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CPhysLevelHavokEmitter::ConvertBrushesToPolytopes(CVarBitVec &useBrushIn, CUtlVector<IPhysics2CookedPolytope*> &arrPolytopesOut)
|
||||
{
|
||||
uint numCouldntCreate = 0;
|
||||
for( int nBrush = 0; nBrush < m_numBrushes; ++nBrush )
|
||||
{
|
||||
if(useBrushIn[nBrush])
|
||||
{
|
||||
int numBrushSides = m_pBrushes[nBrush].numsides;
|
||||
CUtlVector_Vector4DAligned arrSides(numBrushSides);
|
||||
|
||||
dbrushside_t *pThisBrushSides = m_pBrushSides + m_pBrushes[nBrush].firstside;
|
||||
for(int nSide = 0; nSide < numBrushSides; ++nSide)
|
||||
{
|
||||
dbrushside_t *pSide = pThisBrushSides + nSide;
|
||||
if(!pSide->bevel)
|
||||
{
|
||||
dplane_t &plane = m_pPlanes[pSide->planenum];
|
||||
Vector4DAligned side;
|
||||
side.Init(plane.normal.x,plane.normal.y,plane.normal.z, -plane.dist);
|
||||
arrSides.AddToTail(side);
|
||||
}
|
||||
}
|
||||
IPhysics2CookedPolytope *pPolytope = m_cook->CookPolytopeFromPlanes((Vector4DAligned*)arrSides.Base(), arrSides.Size());
|
||||
if(pPolytope)
|
||||
{
|
||||
arrPolytopesOut.AddToTail(pPolytope);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Warning("Couldn't create a convex out of brush #%d\n", nBrush);
|
||||
++numCouldntCreate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(numCouldntCreate)
|
||||
Warning("Couldn't create %u/%u brushes\n", numCouldntCreate, m_numBrushes);
|
||||
else
|
||||
Msg("Created %u brushes\n", m_numBrushes);
|
||||
}
|
||||
|
||||
|
||||
void CPhysLevelHavokEmitter::AddBrushes(int nNode, CVarBitVec &isBrushAdded)
|
||||
{
|
||||
if(nNode < 0)
|
||||
{
|
||||
int leafIndex = -1 - nNode;
|
||||
for ( int i = 0; i < m_pLeafs[leafIndex].numleafbrushes; ++i )
|
||||
{
|
||||
int brushIndex = m_pLeafBrushes[m_pLeafs[leafIndex].firstleafbrush + i];
|
||||
isBrushAdded.Set(brushIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dnode_t *pNode = m_pNodes + nNode;
|
||||
|
||||
AddBrushes( pNode->children[0] , isBrushAdded );
|
||||
AddBrushes( pNode->children[1] , isBrushAdded );
|
||||
}
|
||||
}
|
||||
|
||||
// This is the only public entry to this file.
|
||||
// The global data touched in the file is:
|
||||
// from bsplib.h:
|
||||
// g_pPhysLevel : This is an output from this file.
|
||||
// g_PhysLevelSize : This is set in this file.
|
||||
// g_dispinfo : This is an input to this file.
|
||||
// int nummodels;
|
||||
// dmodel_t dmodels[MAX_MAP_MODELS];
|
||||
// numnodewaterdata : This is an input to this file from EmitPhysCollision()
|
||||
// dleafwaterdata : This is an input to this file from EmitPhysCollision()
|
||||
// from vbsp.h:
|
||||
// g_SurfaceProperties : This is an input to this file.
|
||||
void EmitPhysLevel()
|
||||
{
|
||||
if(g_pPhysLevel)
|
||||
MemAlloc_FreeAligned(g_pPhysLevel);
|
||||
CPhysLevelHavokEmitter emitter;
|
||||
emitter.Emit();
|
||||
|
||||
g_PhysLevelSize = emitter.GetTotalSize();
|
||||
g_pPhysLevel = (byte*)MemAlloc_AllocAligned(g_PhysLevelSize, 16);
|
||||
if(!emitter.Compile(g_pPhysLevel))
|
||||
{
|
||||
free(g_pPhysLevel);
|
||||
g_pPhysLevel = NULL;
|
||||
g_PhysLevelSize = 0;
|
||||
}
|
||||
else
|
||||
{// Msg("Compiled PhysLevel: %.1fk\n", g_PhysLevelSize / 1024.0);
|
||||
emitter.PrintStats();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef PHYS_LEVEL_HAVOK_H
|
||||
#define PHYS_LEVEL_HAVOK_H
|
||||
|
||||
#include "platform.h"
|
||||
#include "basetypes.h"
|
||||
#include "datalinker.h"
|
||||
#include "bspfile.h"
|
||||
#include "bitvec.h"
|
||||
|
||||
#include "vphysics2_interface.h"
|
||||
|
||||
extern byte *g_pPhysLevel;
|
||||
extern int g_PhysLevelSize;
|
||||
|
||||
class CPhysLevelHavokEmitter: public DataLinker::Stream
|
||||
{
|
||||
public:
|
||||
CPhysLevelHavokEmitter();
|
||||
~CPhysLevelHavokEmitter();
|
||||
|
||||
void Emit();
|
||||
|
||||
protected:
|
||||
void AddBrushes(int headnode, CVarBitVec &isBrushAdded);
|
||||
void ConvertBrushesToPolytopes(CVarBitVec &useBrushIn, CUtlVector<IPhysics2CookedPolytope*> &arrPolytopesOut);
|
||||
protected:
|
||||
public:
|
||||
bool m_bConvertBrushesToMopp, m_buildInertia, m_exportObjMopp;
|
||||
|
||||
int m_numModels;
|
||||
dmodel_t *m_pModels;
|
||||
dnode_t *m_pNodes;
|
||||
dleaf_t *m_pLeafs;
|
||||
dbrush_t *m_pBrushes;
|
||||
dbrushside_t *m_pBrushSides;
|
||||
int m_numBrushes;
|
||||
dplane_t *m_pPlanes;
|
||||
unsigned short *m_pLeafBrushes;
|
||||
|
||||
IPhysics2 *m_physics;
|
||||
IPhysics2Cook *m_cook;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
//========= Copyright © 1996-2005, 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
|
||||
@@ -0,0 +1,374 @@
|
||||
//========= Copyright © 1996-2005, 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) );
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,779 @@
|
||||
//========= Copyright © 1996-2005, 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.
|
||||
|
||||
// can't have portals
|
||||
if ( ( propVal = GetMaterialVar( matID, "%noPortal" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOPORTAL;
|
||||
}
|
||||
|
||||
// can't have paint
|
||||
if ( ( propVal = GetMaterialVar( matID, "%noPaint" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOPAINT;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Handle grenade clip.
|
||||
else if ( ( propVal = GetMaterialVar( matID, "%compilegrenadeclip" ) ) && StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_DETAIL;
|
||||
textureref[i].contents |= CONTENTS_GRENADECLIP;
|
||||
textureref[i].flags |= SURF_NODRAW | 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 team-specific textures.
|
||||
if ( propVal = GetMaterialVar( matID, "%compileTeam" ) )
|
||||
{
|
||||
int val = atoi( propVal );
|
||||
if ( val == 1 )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_TEAM1;
|
||||
}
|
||||
else if ( val == 2 )
|
||||
{
|
||||
textureref[i].contents |= CONTENTS_TEAM2;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle hot surfaces (ice won't stick to them). We're piggybacking on SURF_NOPORTAL for now.
|
||||
if ( ( propVal = GetMaterialVar( matID, "%hotSurface" ) ) && 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;
|
||||
}
|
||||
|
||||
// handle materials that want to be treated as water.
|
||||
if ( ( propVal = GetMaterialVar( matID, "%compileNoShadows" ) ) &&
|
||||
StringIsTrue( propVal ) )
|
||||
{
|
||||
textureref[i].flags |= SURF_NOSHADOWS;
|
||||
}
|
||||
|
||||
const char *pShaderName = GetMaterialShaderName(matID);
|
||||
if ( !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 *pVarName )
|
||||
{
|
||||
const char *pPropString = NULL;
|
||||
int surfaceIndex = -1;
|
||||
|
||||
if ( physprops )
|
||||
{
|
||||
pPropString = GetMaterialVar( matID, pVarName );
|
||||
if ( pPropString )
|
||||
{
|
||||
surfaceIndex = physprops->GetSurfaceIndex( pPropString );
|
||||
if ( surfaceIndex < 0 )
|
||||
{
|
||||
Msg("Can't find %s %s for material %s, using default\n", pVarName, 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
//========= Copyright © 1996-2005, 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);
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
+1757
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,682 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#if !defined( VBSP_H )
|
||||
#define VBSP_H
|
||||
|
||||
|
||||
#include "cmdlib.h"
|
||||
#include "mathlib/vector.h"
|
||||
#include "mathlib/vector4d.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
|
||||
bool visible; // choose visble planes first
|
||||
bool tested; // this plane allready checked as a split
|
||||
bool bevel; // don't ever use for bsp splitting
|
||||
bool thin; // surface is thin
|
||||
|
||||
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;
|
||||
CUtlVector<side_t *> *pMergedList; // FOR PORTAL2 paint face->brush map, keep track of a list of all sides that contributed faces to this face (via merges)
|
||||
};
|
||||
|
||||
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];
|
||||
CDispMultiBlend m_vMultiBlends[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 CUtlBlockVector<mapdispinfo_t> mapdispinfo;
|
||||
|
||||
extern float g_defaultLuxelSize;
|
||||
extern float g_luxelScale;
|
||||
extern float g_minLuxelScale;
|
||||
extern float g_maxLuxelScale;
|
||||
extern bool g_BumpAll;
|
||||
|
||||
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 GameData;
|
||||
|
||||
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; }
|
||||
|
||||
void CheckForInstances( const char *pszFileName );
|
||||
void MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance );
|
||||
void PreLoadInstances( GameData *pGD );
|
||||
void PostLoadInstances( );
|
||||
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, entity_t *pParmsEntity );
|
||||
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 );
|
||||
void MergeIOProxy( 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 qboolean staticpropcombine;
|
||||
extern qboolean staticpropcombine_delsources;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_IGNORE_NORMALS;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_SHADOW;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_FLASHLIGHT;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_MARKED_FOR_FAST_REFLECTION;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_PER_VERTEX_LIGHTING;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_SELF_SHADOWING;
|
||||
extern qboolean staticpropcombine_doflagcompare_STATIC_PROP_FLAGS_EX_DISABLE_SHADOW_DEPTH;
|
||||
extern qboolean staticpropcombine_considervis;
|
||||
extern qboolean staticpropcombine_autocombine;
|
||||
extern qboolean staticpropcombine_suggestcombinerules;
|
||||
extern int g_nAutoCombineMinInstances;
|
||||
extern vec_t microvolume;
|
||||
extern bool g_bConvertStructureToDetail;
|
||||
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, const char *pVarName );
|
||||
|
||||
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;
|
||||
unsigned char nMinCPULevel;
|
||||
unsigned char nMaxCPULevel;
|
||||
unsigned char nMinGPULevel;
|
||||
unsigned char nMaxGPULevel;
|
||||
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
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// VBSP.VPC
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Macro SRCDIR "..\.."
|
||||
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
|
||||
|
||||
$Include "$SRCDIR\vpc_scripts\source_exe_con_win32_base.vpc"
|
||||
|
||||
$Configuration
|
||||
{
|
||||
$Compiler
|
||||
{
|
||||
$AdditionalIncludeDirectories "$BASE,..\common,..\vmpi"
|
||||
$PreprocessorDefinitions "$BASE;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE"
|
||||
}
|
||||
|
||||
$Linker
|
||||
{
|
||||
$AdditionalDependencies "$BASE ws2_32.lib odbc32.lib odbccp32.lib winmm.lib"
|
||||
}
|
||||
}
|
||||
|
||||
$Project "Vbsp"
|
||||
{
|
||||
$Folder "Source Files"
|
||||
{
|
||||
$File "boundbox.cpp"
|
||||
$File "brushbsp.cpp"
|
||||
$File "$SRCDIR\public\collisionutils.cpp"
|
||||
$File "csg.cpp"
|
||||
$File "cubemap.cpp"
|
||||
$File "detail.cpp"
|
||||
$File "detailobjects.cpp"
|
||||
$File "$SRCDIR\public\disp_common.cpp"
|
||||
$File "disp_ivp.cpp"
|
||||
$File "$SRCDIR\public\disp_powerinfo.cpp"
|
||||
$File "disp_vbsp.cpp"
|
||||
$File "faces.cpp"
|
||||
$File "glfile.cpp"
|
||||
$File "ivp.cpp"
|
||||
$File "leakfile.cpp"
|
||||
$File "$SRCDIR\public\loadcmdline.cpp"
|
||||
$File "$SRCDIR\public\lumpfiles.cpp"
|
||||
$File "map.cpp"
|
||||
$File "manifest.cpp"
|
||||
$File "materialpatch.cpp"
|
||||
$File "materialsub.cpp"
|
||||
$File "..\common\mstristrip.cpp"
|
||||
$File "nodraw.cpp"
|
||||
$File "normals.cpp"
|
||||
$File "overlay.cpp"
|
||||
$File "..\common\physdll.cpp"
|
||||
$File "portals.cpp"
|
||||
$File "prtfile.cpp"
|
||||
$File "$SRCDIR\public\scratchpad3d.cpp"
|
||||
$File "..\common\scratchpad_helpers.cpp"
|
||||
$File "staticprop.cpp"
|
||||
$File "textures.cpp"
|
||||
$File "tree.cpp"
|
||||
$File "..\common\utilmatlib.cpp"
|
||||
$File "vbsp.cpp"
|
||||
$File "worldvertextransitionfixup.cpp"
|
||||
$File "writebsp.cpp"
|
||||
$File "$SRCDIR\public\zip_utils.cpp"
|
||||
|
||||
$Folder "Common Files"
|
||||
{
|
||||
$File "..\common\bsplib.cpp"
|
||||
$File "$SRCDIR\public\builddisp.cpp"
|
||||
$File "$SRCDIR\public\chunkfile.cpp"
|
||||
$File "..\common\cmdlib.cpp"
|
||||
$File "$SRCDIR\public\filesystem_helpers.cpp"
|
||||
$File "$SRCDIR\public\filesystem_init.cpp"
|
||||
$File "..\common\filesystem_tools.cpp"
|
||||
$File "..\common\map_shared.cpp"
|
||||
$File "..\common\pacifier.cpp"
|
||||
$File "..\common\polylib.cpp"
|
||||
$File "..\common\scriplib.cpp"
|
||||
$File "..\common\threads.cpp"
|
||||
$File "..\common\tools_minidump.cpp"
|
||||
$File "..\common\tools_minidump.h"
|
||||
$File "$SRCDIR\common\vmfentitysupport.cpp"
|
||||
$File "$SRCDIR\common\vmfmeshdatasupport.cpp"
|
||||
}
|
||||
}
|
||||
|
||||
$Folder "Header Files"
|
||||
{
|
||||
$File "boundbox.h"
|
||||
$File "csg.h"
|
||||
$File "detail.h"
|
||||
$File "$SRCDIR\public\disp_powerinfo.h"
|
||||
$File "disp_vbsp.h"
|
||||
$File "$SRCDIR\public\disp_vertindex.h"
|
||||
$File "faces.h"
|
||||
$File "map.h"
|
||||
$File "manifest.h"
|
||||
$File "materialpatch.h"
|
||||
$File "materialsub.h"
|
||||
$File "..\common\scratchpad_helpers.h"
|
||||
$File "vbsp.h"
|
||||
$File "worldvertextransitionfixup.h"
|
||||
$File "writebsp.h"
|
||||
|
||||
$Folder "Common header files"
|
||||
{
|
||||
$File "..\common\bsplib.h"
|
||||
$File "$SRCDIR\public\builddisp.h"
|
||||
$File "$SRCDIR\public\chunkfile.h"
|
||||
$File "..\common\cmdlib.h"
|
||||
$File "disp_ivp.h"
|
||||
$File "$SRCDIR\public\filesystem.h"
|
||||
$File "$SRCDIR\public\filesystem_helpers.h"
|
||||
$File "..\common\filesystem_tools.h"
|
||||
$File "$SRCDIR\public\gamebspfile.h"
|
||||
$File "..\common\instancing_helper.h"
|
||||
$File "$SRCDIR\public\tier1\interface.h"
|
||||
$File "ivp.h"
|
||||
$File "..\common\map_shared.h"
|
||||
$File "..\common\pacifier.h"
|
||||
$File "..\common\polylib.h"
|
||||
$File "$SRCDIR\public\tier2\tokenreader.h"
|
||||
$File "..\common\utilmatlib.h"
|
||||
$File "..\vmpi\vmpi.h"
|
||||
}
|
||||
}
|
||||
|
||||
$Folder "Public Headers"
|
||||
{
|
||||
$File "$SRCDIR\public\arraystack.h"
|
||||
$File "$SRCDIR\public\tier0\basetypes.h"
|
||||
$File "$SRCDIR\public\bspfile.h"
|
||||
$File "$SRCDIR\public\bspflags.h"
|
||||
$File "$SRCDIR\public\bsptreedata.h"
|
||||
$File "$SRCDIR\public\mathlib\bumpvects.h"
|
||||
$File "$SRCDIR\public\tier1\byteswap.h"
|
||||
$File "$SRCDIR\public\cmodel.h"
|
||||
$File "$SRCDIR\public\collisionutils.h"
|
||||
$File "$SRCDIR\public\tier0\dbg.h"
|
||||
$File "$SRCDIR\public\disp_common.h"
|
||||
$File "$SRCDIR\public\iscratchpad3d.h"
|
||||
$File "$SRCDIR\common\lzma\lzma.h"
|
||||
$File "$SRCDIR\public\mathlib\mathlib.h"
|
||||
$File "..\common\mstristrip.h"
|
||||
$File "$SRCDIR\public\nmatrix.h"
|
||||
$File "$SRCDIR\public\ntree.h"
|
||||
$File "$SRCDIR\public\nvector.h"
|
||||
$File "$SRCDIR\public\phyfile.h"
|
||||
$File "..\common\physdll.h"
|
||||
$File "..\common\qfiles.h"
|
||||
$File "$SRCDIR\public\scratchpad3d.h"
|
||||
$File "..\common\scriplib.h"
|
||||
$File "$SRCDIR\public\studio.h"
|
||||
$File "..\common\threads.h"
|
||||
$File "$SRCDIR\public\tier1\utlbuffer.h"
|
||||
$File "$SRCDIR\public\tier1\utllinkedlist.h"
|
||||
$File "$SRCDIR\public\tier1\utlmemory.h"
|
||||
$File "$SRCDIR\public\tier1\utlrbtree.h"
|
||||
$File "$SRCDIR\public\tier1\utlsymbol.h"
|
||||
$File "$SRCDIR\public\tier1\utlvector.h"
|
||||
$File "$SRCDIR\public\vcollide.h"
|
||||
$File "$SRCDIR\public\mathlib\vector.h"
|
||||
$File "$SRCDIR\public\mathlib\vector2d.h"
|
||||
$File "$SRCDIR\public\mathlib\vector4d.h"
|
||||
$File "$SRCDIR\public\mathlib\vmatrix.h"
|
||||
$File "$SRCDIR\public\vphysics_interface.h"
|
||||
$File "$SRCDIR\public\mathlib\vplane.h"
|
||||
$File "$SRCDIR\public\wadtypes.h"
|
||||
$File "$SRCDIR\public\worldsize.h"
|
||||
}
|
||||
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
$File "$SRCDIR\lib\public\bitmap.lib"
|
||||
$File "$SRCDIR\lib\public\fgdlib.lib"
|
||||
$File "$SRCDIR\lib\public\mathlib.lib"
|
||||
$File "$SRCDIR\lib\public\tier2.lib"
|
||||
$File "$SRCDIR\lib\public\vtf.lib"
|
||||
$File "$SRCDIR\lib\common\vc8\lzma.lib"
|
||||
$File "$SRCDIR\lib\common\vc8\libmad.lib"
|
||||
}
|
||||
|
||||
$File "notes.txt"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "vbsp.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "vbsp.vcxproj"
|
||||
"1" "vbsp.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
//====== Copyright © 1996-2007, 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 && basetexturenoenvmap->GetInt() )
|
||||
{
|
||||
RemoveKey( kv, "$envmap" );
|
||||
}
|
||||
|
||||
Warning( "Patching WVT material: %s\n", pPatchedMaterialName );
|
||||
WriteMaterialKeyValuesToPak( pPatchedMaterialName, kv );
|
||||
|
||||
AddNewTranslation( pOriginalMaterialName, pPatchedMaterialName );
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//====== Copyright © 1996-2007, 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
@@ -0,0 +1,34 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef WRITEBSP_H
|
||||
#define WRITEBSP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "bspfile.h"
|
||||
#include "utlmap.h"
|
||||
|
||||
struct node_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Emits occluder faces
|
||||
//-----------------------------------------------------------------------------
|
||||
void EmitOccluderFaces (node_t *node);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Free the list of faces stored at the leaves
|
||||
//-----------------------------------------------------------------------------
|
||||
void FreeLeafFaces( face_t *pLeafFaceList );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Make sure that we have a water lod control entity if we have water in the map.
|
||||
//-----------------------------------------------------------------------------
|
||||
void EnsurePresenceOfWaterLODControlEntity( void );
|
||||
|
||||
#endif // WRITEBSP_H
|
||||
Reference in New Issue
Block a user