aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vbsp
diff options
context:
space:
mode:
authorNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
committerNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
commit8737f191f3b59f001a77bf6c08091109211c1c9f (patch)
treedbbf05c004d9b026f2c1f23f06600fe0add82c36 /mp/src/utils/vbsp
parentUpdate .gitignore. (diff)
parentMake .xcconfigs text files too. (diff)
downloadsource-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.tar.xz
source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'mp/src/utils/vbsp')
-rw-r--r--mp/src/utils/vbsp/boundbox.cpp570
-rw-r--r--mp/src/utils/vbsp/boundbox.h158
-rw-r--r--mp/src/utils/vbsp/brushbsp.cpp2938
-rw-r--r--mp/src/utils/vbsp/csg.cpp1568
-rw-r--r--mp/src/utils/vbsp/csg.h64
-rw-r--r--mp/src/utils/vbsp/cubemap.cpp1992
-rw-r--r--mp/src/utils/vbsp/detail.cpp1386
-rw-r--r--mp/src/utils/vbsp/detail.h36
-rw-r--r--mp/src/utils/vbsp/detailobjects.cpp1932
-rw-r--r--mp/src/utils/vbsp/disp_ivp.cpp718
-rw-r--r--mp/src/utils/vbsp/disp_ivp.h98
-rw-r--r--mp/src/utils/vbsp/disp_vbsp.cpp1350
-rw-r--r--mp/src/utils/vbsp/disp_vbsp.h92
-rw-r--r--mp/src/utils/vbsp/faces.cpp3618
-rw-r--r--mp/src/utils/vbsp/faces.h40
-rw-r--r--mp/src/utils/vbsp/glfile.cpp438
-rw-r--r--mp/src/utils/vbsp/ivp.cpp3312
-rw-r--r--mp/src/utils/vbsp/ivp.h150
-rw-r--r--mp/src/utils/vbsp/leakfile.cpp334
-rw-r--r--mp/src/utils/vbsp/manifest.cpp1136
-rw-r--r--mp/src/utils/vbsp/manifest.h146
-rw-r--r--mp/src/utils/vbsp/map.cpp6608
-rw-r--r--mp/src/utils/vbsp/map.h36
-rw-r--r--mp/src/utils/vbsp/materialpatch.cpp880
-rw-r--r--mp/src/utils/vbsp/materialpatch.h120
-rw-r--r--mp/src/utils/vbsp/materialsub.cpp178
-rw-r--r--mp/src/utils/vbsp/materialsub.h48
-rw-r--r--mp/src/utils/vbsp/nodraw.cpp64
-rw-r--r--mp/src/utils/vbsp/normals.cpp100
-rw-r--r--mp/src/utils/vbsp/overlay.cpp974
-rw-r--r--mp/src/utils/vbsp/portals.cpp3368
-rw-r--r--mp/src/utils/vbsp/portals.h38
-rw-r--r--mp/src/utils/vbsp/prtfile.cpp748
-rw-r--r--mp/src/utils/vbsp/staticprop.cpp1482
-rw-r--r--mp/src/utils/vbsp/textures.cpp1474
-rw-r--r--mp/src/utils/vbsp/tree.cpp414
-rw-r--r--mp/src/utils/vbsp/vbsp.cpp2808
-rw-r--r--mp/src/utils/vbsp/vbsp.h1314
-rw-r--r--mp/src/utils/vbsp/vbsp.vpc368
-rw-r--r--mp/src/utils/vbsp/worldvertextransitionfixup.cpp422
-rw-r--r--mp/src/utils/vbsp/worldvertextransitionfixup.h30
-rw-r--r--mp/src/utils/vbsp/writebsp.cpp3104
-rw-r--r--mp/src/utils/vbsp/writebsp.h68
43 files changed, 23361 insertions, 23361 deletions
diff --git a/mp/src/utils/vbsp/boundbox.cpp b/mp/src/utils/vbsp/boundbox.cpp
index d0366cfc..ecb8dfa0 100644
--- a/mp/src/utils/vbsp/boundbox.cpp
+++ b/mp/src/utils/vbsp/boundbox.cpp
@@ -1,285 +1,285 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "BoundBox.h"
-//#include "hammer_mathlib.h"
-//#include "MapDefs.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-float rint(float f)
-{
- if (f > 0.0f) {
- return (float) floor(f + 0.5f);
- } else if (f < 0.0f) {
- return (float) ceil(f - 0.5f);
- } else
- return 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-BoundBox::BoundBox(void)
-{
- ResetBounds();
-}
-
-BoundBox::BoundBox(const Vector &mins, const Vector &maxs)
-{
- bmins = mins;
- bmaxs = maxs;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the box to an uninitialized state, so that calls to UpdateBounds
-// will properly set the mins and maxs.
-//-----------------------------------------------------------------------------
-void BoundBox::ResetBounds(void)
-{
- bmins[0] = bmins[1] = bmins[2] = COORD_NOTINIT;
- bmaxs[0] = bmaxs[1] = bmaxs[2] = -COORD_NOTINIT;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pt -
-//-----------------------------------------------------------------------------
-void BoundBox::UpdateBounds(const Vector& pt)
-{
- if(pt[0] < bmins[0])
- bmins[0] = pt[0];
- if(pt[1] < bmins[1])
- bmins[1] = pt[1];
- if(pt[2] < bmins[2])
- bmins[2] = pt[2];
-
- if(pt[0] > bmaxs[0])
- bmaxs[0] = pt[0];
- if(pt[1] > bmaxs[1])
- bmaxs[1] = pt[1];
- if(pt[2] > bmaxs[2])
- bmaxs[2] = pt[2];
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : bmins -
-// bmaxs -
-//-----------------------------------------------------------------------------
-void BoundBox::UpdateBounds(const Vector& mins, const Vector& maxs)
-{
- if(mins[0] < bmins[0])
- bmins[0] = mins[0];
- if(mins[1] < bmins[1])
- bmins[1] = mins[1];
- if(mins[2] < bmins[2])
- bmins[2] = mins[2];
-
- if(maxs[0] > bmaxs[0])
- bmaxs[0] = maxs[0];
- if(maxs[1] > bmaxs[1])
- bmaxs[1] = maxs[1];
- if(maxs[2] > bmaxs[2])
- bmaxs[2] = maxs[2];
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pBox -
-//-----------------------------------------------------------------------------
-void BoundBox::UpdateBounds(const BoundBox *pBox)
-{
- UpdateBounds(pBox->bmins, pBox->bmaxs);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : ptdest -
-//-----------------------------------------------------------------------------
-void BoundBox::GetBoundsCenter(Vector& ptdest)
-{
- ptdest = (bmins + bmaxs)/2.0f;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pt -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool BoundBox::ContainsPoint(const Vector& pt) const
-{
- for (int i = 0; i < 3; i++)
- {
- if (pt[i] < bmins[i] || pt[i] > bmaxs[i])
- {
- return(false);
- }
- }
- return(true);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pfMins -
-// pfMaxs -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool BoundBox::IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const
-{
- if ((bmins[0] >= pfMaxs[0]) || (bmaxs[0] <= pfMins[0]))
- {
- return(false);
-
- }
- if ((bmins[1] >= pfMaxs[1]) || (bmaxs[1] <= pfMins[1]))
- {
- return(false);
- }
-
- if ((bmins[2] >= pfMaxs[2]) || (bmaxs[2] <= pfMins[2]))
- {
- return(false);
- }
-
- return(true);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pfMins -
-// pfMaxs -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool BoundBox::IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const
-{
- if ((bmins[0] < pfMins[0]) || (bmaxs[0] > pfMaxs[0]))
- {
- return(false);
- }
-
- if ((bmins[1] < pfMins[1]) || (bmaxs[1] > pfMaxs[1]))
- {
- return(false);
- }
-
- if ((bmins[2] < pfMins[2]) || (bmaxs[2] > pfMaxs[2]))
- {
- return(false);
- }
-
- return(true);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns whether this bounding box is valid, ie maxs >= mins.
-//-----------------------------------------------------------------------------
-bool BoundBox::IsValidBox(void) const
-{
- for (int i = 0; i < 3; i++)
- {
- if (bmins[i] > bmaxs[i])
- {
- return(false);
- }
- }
-
- return(true);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : size -
-//-----------------------------------------------------------------------------
-void BoundBox::GetBoundsSize(Vector& size)
-{
- size[0] = bmaxs[0] - bmins[0];
- size[1] = bmaxs[1] - bmins[1];
- size[2] = bmaxs[2] - bmins[2];
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iValue -
-// iGridSize -
-// Output :
-//-----------------------------------------------------------------------------
-static int Snap(/*int*/ float iValue, int iGridSize)
-{
- return (int)(rint(iValue/iGridSize) * iGridSize);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iGridSize -
-//-----------------------------------------------------------------------------
-void BoundBox::SnapToGrid(int iGridSize)
-{
- // does not alter the size of the box .. snaps its minimal coordinates
- // to the grid size specified in iGridSize
- Vector size;
- GetBoundsSize(size);
-
- for(int i = 0; i < 3; i++)
- {
- bmins[i] = (float)Snap(/* YWB (int)*/bmins[i], iGridSize);
- bmaxs[i] = bmins[i] + size[i];
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : axis -
-//-----------------------------------------------------------------------------
-void BoundBox::Rotate90(int axis)
-{
- int e1 = AXIS_X, e2 = AXIS_Y;
-
- // get bounds center first
- Vector center;
- GetBoundsCenter(center);
-
- switch(axis)
- {
- case AXIS_Z:
- e1 = AXIS_X;
- e2 = AXIS_Y;
- break;
- case AXIS_X:
- e1 = AXIS_Y;
- e2 = AXIS_Z;
- break;
- case AXIS_Y:
- e1 = AXIS_X;
- e2 = AXIS_Z;
- break;
- }
-
- float tmp1, tmp2;
- tmp1 = bmins[e1] - center[e1] + center[e2];
- tmp2 = bmaxs[e1] - center[e1] + center[e2];
- bmins[e1] = bmins[e2] - center[e2] + center[e1];
- bmaxs[e1] = bmaxs[e2] - center[e2] + center[e1];
- bmins[e2] = tmp1;
- bmaxs[e2] = tmp2;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "BoundBox.h"
+//#include "hammer_mathlib.h"
+//#include "MapDefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+float rint(float f)
+{
+ if (f > 0.0f) {
+ return (float) floor(f + 0.5f);
+ } else if (f < 0.0f) {
+ return (float) ceil(f - 0.5f);
+ } else
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BoundBox::BoundBox(void)
+{
+ ResetBounds();
+}
+
+BoundBox::BoundBox(const Vector &mins, const Vector &maxs)
+{
+ bmins = mins;
+ bmaxs = maxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the box to an uninitialized state, so that calls to UpdateBounds
+// will properly set the mins and maxs.
+//-----------------------------------------------------------------------------
+void BoundBox::ResetBounds(void)
+{
+ bmins[0] = bmins[1] = bmins[2] = COORD_NOTINIT;
+ bmaxs[0] = bmaxs[1] = bmaxs[2] = -COORD_NOTINIT;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pt -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const Vector& pt)
+{
+ if(pt[0] < bmins[0])
+ bmins[0] = pt[0];
+ if(pt[1] < bmins[1])
+ bmins[1] = pt[1];
+ if(pt[2] < bmins[2])
+ bmins[2] = pt[2];
+
+ if(pt[0] > bmaxs[0])
+ bmaxs[0] = pt[0];
+ if(pt[1] > bmaxs[1])
+ bmaxs[1] = pt[1];
+ if(pt[2] > bmaxs[2])
+ bmaxs[2] = pt[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bmins -
+// bmaxs -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const Vector& mins, const Vector& maxs)
+{
+ if(mins[0] < bmins[0])
+ bmins[0] = mins[0];
+ if(mins[1] < bmins[1])
+ bmins[1] = mins[1];
+ if(mins[2] < bmins[2])
+ bmins[2] = mins[2];
+
+ if(maxs[0] > bmaxs[0])
+ bmaxs[0] = maxs[0];
+ if(maxs[1] > bmaxs[1])
+ bmaxs[1] = maxs[1];
+ if(maxs[2] > bmaxs[2])
+ bmaxs[2] = maxs[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pBox -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const BoundBox *pBox)
+{
+ UpdateBounds(pBox->bmins, pBox->bmaxs);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ptdest -
+//-----------------------------------------------------------------------------
+void BoundBox::GetBoundsCenter(Vector& ptdest)
+{
+ ptdest = (bmins + bmaxs)/2.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pt -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::ContainsPoint(const Vector& pt) const
+{
+ for (int i = 0; i < 3; i++)
+ {
+ if (pt[i] < bmins[i] || pt[i] > bmaxs[i])
+ {
+ return(false);
+ }
+ }
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfMins -
+// pfMaxs -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const
+{
+ if ((bmins[0] >= pfMaxs[0]) || (bmaxs[0] <= pfMins[0]))
+ {
+ return(false);
+
+ }
+ if ((bmins[1] >= pfMaxs[1]) || (bmaxs[1] <= pfMins[1]))
+ {
+ return(false);
+ }
+
+ if ((bmins[2] >= pfMaxs[2]) || (bmaxs[2] <= pfMins[2]))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfMins -
+// pfMaxs -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const
+{
+ if ((bmins[0] < pfMins[0]) || (bmaxs[0] > pfMaxs[0]))
+ {
+ return(false);
+ }
+
+ if ((bmins[1] < pfMins[1]) || (bmaxs[1] > pfMaxs[1]))
+ {
+ return(false);
+ }
+
+ if ((bmins[2] < pfMins[2]) || (bmaxs[2] > pfMaxs[2]))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether this bounding box is valid, ie maxs >= mins.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsValidBox(void) const
+{
+ for (int i = 0; i < 3; i++)
+ {
+ if (bmins[i] > bmaxs[i])
+ {
+ return(false);
+ }
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : size -
+//-----------------------------------------------------------------------------
+void BoundBox::GetBoundsSize(Vector& size)
+{
+ size[0] = bmaxs[0] - bmins[0];
+ size[1] = bmaxs[1] - bmins[1];
+ size[2] = bmaxs[2] - bmins[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iValue -
+// iGridSize -
+// Output :
+//-----------------------------------------------------------------------------
+static int Snap(/*int*/ float iValue, int iGridSize)
+{
+ return (int)(rint(iValue/iGridSize) * iGridSize);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iGridSize -
+//-----------------------------------------------------------------------------
+void BoundBox::SnapToGrid(int iGridSize)
+{
+ // does not alter the size of the box .. snaps its minimal coordinates
+ // to the grid size specified in iGridSize
+ Vector size;
+ GetBoundsSize(size);
+
+ for(int i = 0; i < 3; i++)
+ {
+ bmins[i] = (float)Snap(/* YWB (int)*/bmins[i], iGridSize);
+ bmaxs[i] = bmins[i] + size[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : axis -
+//-----------------------------------------------------------------------------
+void BoundBox::Rotate90(int axis)
+{
+ int e1 = AXIS_X, e2 = AXIS_Y;
+
+ // get bounds center first
+ Vector center;
+ GetBoundsCenter(center);
+
+ switch(axis)
+ {
+ case AXIS_Z:
+ e1 = AXIS_X;
+ e2 = AXIS_Y;
+ break;
+ case AXIS_X:
+ e1 = AXIS_Y;
+ e2 = AXIS_Z;
+ break;
+ case AXIS_Y:
+ e1 = AXIS_X;
+ e2 = AXIS_Z;
+ break;
+ }
+
+ float tmp1, tmp2;
+ tmp1 = bmins[e1] - center[e1] + center[e2];
+ tmp2 = bmaxs[e1] - center[e1] + center[e2];
+ bmins[e1] = bmins[e2] - center[e2] + center[e1];
+ bmaxs[e1] = bmaxs[e2] - center[e2] + center[e1];
+ bmins[e2] = tmp1;
+ bmaxs[e2] = tmp2;
+}
+
diff --git a/mp/src/utils/vbsp/boundbox.h b/mp/src/utils/vbsp/boundbox.h
index c9838fe6..4720a40e 100644
--- a/mp/src/utils/vbsp/boundbox.h
+++ b/mp/src/utils/vbsp/boundbox.h
@@ -1,79 +1,79 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: An axis aligned bounding box class.
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef BOUNDBOX_H
-#define BOUNDBOX_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "mathlib/vector.h"
-
-#define COORD_NOTINIT ((float)(99999.0))
-
-enum
-{
- AXIS_X = 0,
- AXIS_Y,
- AXIS_Z
-};
-
-class BoundBox
-{
- public:
-
- BoundBox(void);
- BoundBox(const Vector &mins, const Vector &maxs);
-
- void ResetBounds(void);
- inline void SetBounds(const Vector &mins, const Vector &maxs);
-
- void UpdateBounds(const Vector& bmins, const Vector& bmaxs);
- void UpdateBounds(const Vector& pt);
- void UpdateBounds(const BoundBox *pBox);
- void GetBoundsCenter(Vector& ptdest);
- inline void GetBounds(Vector& Mins, Vector& Maxs);
-
- virtual bool IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const;
- bool IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const;
- bool ContainsPoint(const Vector& pt) const;
- bool IsValidBox(void) const;
- void GetBoundsSize(Vector& size);
- void SnapToGrid(int iGridSize);
- void Rotate90(int axis);
-
- Vector bmins;
- Vector bmaxs;
-};
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the bounding box as two vectors, a min and a max.
-// Input : Mins - Receives the box's minima.
-// Maxs - Receives the box's maxima.
-//-----------------------------------------------------------------------------
-void BoundBox::GetBounds(Vector &Mins, Vector &Maxs)
-{
- Mins = bmins;
- Maxs = bmaxs;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the box outright, equivalent to ResetBounds + UpdateBounds.
-// Input : mins - Minima to set.
-// maxs - Maxima to set.
-//-----------------------------------------------------------------------------
-void BoundBox::SetBounds(const Vector &mins, const Vector &maxs)
-{
- bmins = mins;
- bmaxs = maxs;
-}
-
-
-#endif // BOUNDBOX_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: An axis aligned bounding box class.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BOUNDBOX_H
+#define BOUNDBOX_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+
+#define COORD_NOTINIT ((float)(99999.0))
+
+enum
+{
+ AXIS_X = 0,
+ AXIS_Y,
+ AXIS_Z
+};
+
+class BoundBox
+{
+ public:
+
+ BoundBox(void);
+ BoundBox(const Vector &mins, const Vector &maxs);
+
+ void ResetBounds(void);
+ inline void SetBounds(const Vector &mins, const Vector &maxs);
+
+ void UpdateBounds(const Vector& bmins, const Vector& bmaxs);
+ void UpdateBounds(const Vector& pt);
+ void UpdateBounds(const BoundBox *pBox);
+ void GetBoundsCenter(Vector& ptdest);
+ inline void GetBounds(Vector& Mins, Vector& Maxs);
+
+ virtual bool IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const;
+ bool IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const;
+ bool ContainsPoint(const Vector& pt) const;
+ bool IsValidBox(void) const;
+ void GetBoundsSize(Vector& size);
+ void SnapToGrid(int iGridSize);
+ void Rotate90(int axis);
+
+ Vector bmins;
+ Vector bmaxs;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the bounding box as two vectors, a min and a max.
+// Input : Mins - Receives the box's minima.
+// Maxs - Receives the box's maxima.
+//-----------------------------------------------------------------------------
+void BoundBox::GetBounds(Vector &Mins, Vector &Maxs)
+{
+ Mins = bmins;
+ Maxs = bmaxs;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the box outright, equivalent to ResetBounds + UpdateBounds.
+// Input : mins - Minima to set.
+// maxs - Maxima to set.
+//-----------------------------------------------------------------------------
+void BoundBox::SetBounds(const Vector &mins, const Vector &maxs)
+{
+ bmins = mins;
+ bmaxs = maxs;
+}
+
+
+#endif // BOUNDBOX_H
diff --git a/mp/src/utils/vbsp/brushbsp.cpp b/mp/src/utils/vbsp/brushbsp.cpp
index 17323c58..d455f30b 100644
--- a/mp/src/utils/vbsp/brushbsp.cpp
+++ b/mp/src/utils/vbsp/brushbsp.cpp
@@ -1,1469 +1,1469 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-
-
-int c_nodes;
-int c_nonvis;
-int c_active_brushes;
-
-// if a brush just barely pokes onto the other side,
-// let it slide by without chopping
-#define PLANESIDE_EPSILON 0.001
-//0.1
-
-
-void FindBrushInTree (node_t *node, int brushnum)
-{
- bspbrush_t *b;
-
- if (node->planenum == PLANENUM_LEAF)
- {
- for (b=node->brushlist ; b ; b=b->next)
- if (b->original->brushnum == brushnum)
- Msg("here\n");
- return;
- }
- FindBrushInTree (node->children[0], brushnum);
- FindBrushInTree (node->children[1], brushnum);
-}
-
-//==================================================
-
-/*
-================
-DrawBrushList
-================
-*/
-void DrawBrushList (bspbrush_t *brush, node_t *node)
-{
- int i;
- side_t *s;
-
- GLS_BeginScene ();
- for ( ; brush ; brush=brush->next)
- {
- for (i=0 ; i<brush->numsides ; i++)
- {
- s = &brush->sides[i];
- if (!s->winding)
- continue;
- if (s->texinfo == TEXINFO_NODE)
- GLS_Winding (s->winding, 1);
- else if (!s->visible)
- GLS_Winding (s->winding, 2);
- else
- GLS_Winding (s->winding, 0);
- }
- }
- GLS_EndScene ();
-}
-
-/*
-================
-WriteBrushList
-================
-*/
-void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
-{
- int i;
- side_t *s;
-
- qprintf ("writing %s\n", name);
- FileHandle_t f = g_pFileSystem->Open(name, "w");
-
- for ( ; brush ; brush=brush->next)
- {
- for (i=0 ; i<brush->numsides ; i++)
- {
- s = &brush->sides[i];
- if (!s->winding)
- continue;
- if (onlyvis && !s->visible)
- continue;
- OutputWinding (brush->sides[i].winding, f);
- }
- }
-
- g_pFileSystem->Close (f);
-}
-
-void PrintBrush (bspbrush_t *brush)
-{
- int i;
-
- Msg("brush: %p\n", brush);
- for (i=0;i<brush->numsides ; i++)
- {
- pw(brush->sides[i].winding);
- Msg("\n");
- }
-}
-
-/*
-==================
-BoundBrush
-
-Sets the mins/maxs based on the windings
-==================
-*/
-void BoundBrush (bspbrush_t *brush)
-{
- int i, j;
- winding_t *w;
-
- ClearBounds (brush->mins, brush->maxs);
- for (i=0 ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (!w)
- continue;
- for (j=0 ; j<w->numpoints ; j++)
- AddPointToBounds (w->p[j], brush->mins, brush->maxs);
- }
-}
-
-Vector PointInsideBrush( bspbrush_t *brush )
-{
- Vector insidePoint = vec3_origin;
-
- bool bInside = false;
- for ( int k = 0; k < 4 && !bInside; k++ )
- {
- bInside = true;
- for (int i = 0; i < brush->numsides; i++)
- {
- side_t *side = &brush->sides[i];
- plane_t *plane = &g_MainMap->mapplanes[side->planenum];
- float d = DotProduct( plane->normal, insidePoint ) - plane->dist;
- if ( d < 0 )
- {
- bInside = false;
- insidePoint -= d * plane->normal;
- }
- }
- }
- return insidePoint;
-}
-
-/*
-==================
-CreateBrushWindings
-
-==================
-*/
-void CreateBrushWindings (bspbrush_t *brush)
-{
- int i, j;
- winding_t *w;
- side_t *side;
- plane_t *plane;
-
- // translate the CSG problem to improve precision
- Vector insidePoint = PointInsideBrush( brush );
- Vector offset = -insidePoint;
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- side = &brush->sides[i];
- plane = &g_MainMap->mapplanes[side->planenum];
- w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal, offset));
- for (j=0 ; j<brush->numsides && w; j++)
- {
- if (i == j)
- continue;
- if (brush->sides[j].bevel)
- continue;
- plane = &g_MainMap->mapplanes[brush->sides[j].planenum^1];
- ChopWindingInPlace (&w, plane->normal, plane->dist + DotProduct(plane->normal, offset), 0); //CLIP_EPSILON);
- }
-
- TranslateWinding( w, -offset );
- side->winding = w;
- }
-
- BoundBrush (brush);
-}
-
-/*
-==================
-BrushFromBounds
-
-Creates a new axial brush
-==================
-*/
-bspbrush_t *BrushFromBounds (Vector& mins, Vector& maxs)
-{
- bspbrush_t *b;
- int i;
- Vector normal;
- vec_t dist;
-
- b = AllocBrush (6);
- b->numsides = 6;
- for (i=0 ; i<3 ; i++)
- {
- VectorClear (normal);
- normal[i] = 1;
- dist = maxs[i];
- b->sides[i].planenum = g_MainMap->FindFloatPlane (normal, dist);
-
- normal[i] = -1;
- dist = -mins[i];
- b->sides[3+i].planenum = g_MainMap->FindFloatPlane (normal, dist);
- }
-
- CreateBrushWindings (b);
-
- return b;
-}
-
-/*
-==================
-BrushVolume
-
-==================
-*/
-vec_t BrushVolume (bspbrush_t *brush)
-{
- int i;
- winding_t *w;
- Vector corner;
- vec_t d, area, volume;
- plane_t *plane;
-
- if (!brush)
- return 0;
-
- // grab the first valid point as the corner
-
- w = NULL;
- for (i=0 ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (w)
- break;
- }
- if (!w)
- return 0;
- VectorCopy (w->p[0], corner);
-
- // make tetrahedrons to all other faces
-
- volume = 0;
- for ( ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (!w)
- continue;
- plane = &g_MainMap->mapplanes[brush->sides[i].planenum];
- d = -(DotProduct (corner, plane->normal) - plane->dist);
- area = WindingArea (w);
- volume += d*area;
- }
-
- volume /= 3;
- return volume;
-}
-
-/*
-================
-CountBrushList
-================
-*/
-int CountBrushList (bspbrush_t *brushes)
-{
- int c;
-
- c = 0;
- for ( ; brushes ; brushes = brushes->next)
- c++;
- return c;
-}
-
-/*
-================
-AllocTree
-================
-*/
-tree_t *AllocTree (void)
-{
- tree_t *tree;
-
- tree = (tree_t*)malloc(sizeof(*tree));
- memset (tree, 0, sizeof(*tree));
- ClearBounds (tree->mins, tree->maxs);
-
- return tree;
-}
-
-/*
-================
-AllocNode
-================
-*/
-node_t *AllocNode (void)
-{
- static int s_NodeCount = 0;
-
- node_t *node;
-
- node = (node_t*)malloc(sizeof(*node));
- memset (node, 0, sizeof(*node));
- node->id = s_NodeCount;
- node->diskId = -1;
-
- s_NodeCount++;
-
- return node;
-}
-
-
-/*
-================
-AllocBrush
-================
-*/
-bspbrush_t *AllocBrush (int numsides)
-{
- static int s_BrushId = 0;
-
- bspbrush_t *bb;
- int c;
-
- c = (int)&(((bspbrush_t *)0)->sides[numsides]);
- bb = (bspbrush_t*)malloc(c);
- memset (bb, 0, c);
- bb->id = s_BrushId++;
- if (numthreads == 1)
- c_active_brushes++;
- return bb;
-}
-
-/*
-================
-FreeBrush
-================
-*/
-void FreeBrush (bspbrush_t *brushes)
-{
- int i;
-
- for (i=0 ; i<brushes->numsides ; i++)
- if (brushes->sides[i].winding)
- FreeWinding(brushes->sides[i].winding);
- free (brushes);
- if (numthreads == 1)
- c_active_brushes--;
-}
-
-
-/*
-================
-FreeBrushList
-================
-*/
-void FreeBrushList (bspbrush_t *brushes)
-{
- bspbrush_t *next;
-
- for ( ; brushes ; brushes = next)
- {
- next = brushes->next;
-
- FreeBrush (brushes);
- }
-}
-
-/*
-==================
-CopyBrush
-
-Duplicates the brush, the sides, and the windings
-==================
-*/
-bspbrush_t *CopyBrush (bspbrush_t *brush)
-{
- bspbrush_t *newbrush;
- int size;
- int i;
-
- size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
-
- newbrush = AllocBrush (brush->numsides);
- memcpy (newbrush, brush, size);
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- if (brush->sides[i].winding)
- newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
- }
-
- return newbrush;
-}
-
-
-/*
-==================
-PointInLeaf
-
-==================
-*/
-node_t *PointInLeaf (node_t *node, Vector& point)
-{
- vec_t d;
- plane_t *plane;
-
- while (node->planenum != PLANENUM_LEAF)
- {
- plane = &g_MainMap->mapplanes[node->planenum];
- if (plane->type < 3)
- {
- d = point[plane->type] - plane->dist;
- }
- else
- {
- d = DotProduct (point, plane->normal) - plane->dist;
- }
-
- if (d >= 0)
- node = node->children[0];
- else
- node = node->children[1];
- }
-
- return node;
-}
-
-//========================================================
-
-/*
-==============
-BoxOnPlaneSide
-
-Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
-==============
-*/
-int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane)
-{
- int side;
- int i;
- Vector corners[2];
- vec_t dist1, dist2;
-
- // axial planes are easy
- if (plane->type < 3)
- {
- side = 0;
- if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
- side |= PSIDE_FRONT;
- if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
- side |= PSIDE_BACK;
- return side;
- }
-
- // create the proper leading and trailing verts for the box
-
- for (i=0 ; i<3 ; i++)
- {
- if (plane->normal[i] < 0)
- {
- corners[0][i] = mins[i];
- corners[1][i] = maxs[i];
- }
- else
- {
- corners[1][i] = mins[i];
- corners[0][i] = maxs[i];
- }
- }
-
- dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
- dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
- side = 0;
- if (dist1 >= PLANESIDE_EPSILON)
- side = PSIDE_FRONT;
- if (dist2 < PLANESIDE_EPSILON)
- side |= PSIDE_BACK;
-
- return side;
-}
-
-/*
-============
-QuickTestBrushToPlanenum
-
-============
-*/
-int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
-{
- int i, num;
- plane_t *plane;
- int s;
-
- *numsplits = 0;
-
- // if the brush actually uses the planenum,
- // we can tell the side for sure
- for (i=0 ; i<brush->numsides ; i++)
- {
- num = brush->sides[i].planenum;
- if (num >= 0x10000)
- Error ("bad planenum");
- if (num == planenum)
- return PSIDE_BACK|PSIDE_FACING;
- if (num == (planenum ^ 1) )
- return PSIDE_FRONT|PSIDE_FACING;
- }
-
- // box on plane side
- plane = &g_MainMap->mapplanes[planenum];
- s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
-
- // if both sides, count the visible faces split
- if (s == PSIDE_BOTH)
- {
- *numsplits += 3;
- }
-
- return s;
-}
-
-/*
-============
-TestBrushToPlanenum
-
-============
-*/
-int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
- int *numsplits, qboolean *hintsplit, int *epsilonbrush)
-{
- int i, j, num;
- plane_t *plane;
- int s;
- winding_t *w;
- vec_t d, d_front, d_back;
- int front, back;
-
- *numsplits = 0;
- *hintsplit = false;
-
- // if the brush actually uses the planenum,
- // we can tell the side for sure
- for (i=0 ; i<brush->numsides ; i++)
- {
- num = brush->sides[i].planenum;
- if (num >= 0x10000)
- Error ("bad planenum");
- if (num == planenum)
- return PSIDE_BACK|PSIDE_FACING;
- if (num == (planenum ^ 1) )
- return PSIDE_FRONT|PSIDE_FACING;
- }
-
- // box on plane side
- plane = &g_MainMap->mapplanes[planenum];
- s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
-
- if (s != PSIDE_BOTH)
- return s;
-
-// if both sides, count the visible faces split
- d_front = d_back = 0;
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- if (brush->sides[i].texinfo == TEXINFO_NODE)
- continue; // on node, don't worry about splits
- if (!brush->sides[i].visible)
- continue; // we don't care about non-visible
- w = brush->sides[i].winding;
- if (!w)
- continue;
-
- front = back = 0;
- for (j=0 ; j<w->numpoints; j++)
- {
- d = DotProduct (w->p[j], plane->normal) - plane->dist;
-
- if (d > d_front)
- d_front = d;
- if (d < d_back)
- d_back = d;
-
- if (d > 0.1) // PLANESIDE_EPSILON)
- front = 1;
- if (d < -0.1) // PLANESIDE_EPSILON)
- back = 1;
- }
-
- if (front && back)
- {
- if ( !(brush->sides[i].surf & SURF_SKIP) )
- {
- (*numsplits)++;
- if (brush->sides[i].surf & SURF_HINT)
- *hintsplit = true;
- }
- }
- }
-
- if ( (d_front > 0.0 && d_front < 1.0)
- || (d_back < 0.0 && d_back > -1.0) )
- (*epsilonbrush)++;
-
-#if 0
- if (*numsplits == 0)
- { // didn't really need to be split
- if (front)
- s = PSIDE_FRONT;
- else if (back)
- s = PSIDE_BACK;
- else
- s = 0;
- }
-#endif
-
- return s;
-}
-
-//========================================================
-
-/*
-================
-WindingIsTiny
-
-Returns true if the winding would be crunched out of
-existance by the vertex snapping.
-================
-*/
-#define EDGE_LENGTH 0.2
-qboolean WindingIsTiny (winding_t *w)
-{
- int i, j;
- vec_t len;
- Vector delta;
- int edges;
-
- edges = 0;
- for (i=0 ; i<w->numpoints ; i++)
- {
- j = i == w->numpoints - 1 ? 0 : i+1;
- VectorSubtract (w->p[j], w->p[i], delta);
- len = VectorLength (delta);
- if (len > EDGE_LENGTH)
- {
- if (++edges == 3)
- return false;
- }
- }
- return true;
-}
-
-
-// UNDONE: JAY: This should be a slightly better heuristic - it builds an OBB
-// around the winding and tests planar dimensions. NOTE: This can fail when a
-// winding normal cannot be constructed (or is degenerate), but that is probably
-// desired in this case.
-// UNDONE: Test & use this instead.
-#if 0
-qboolean WindingIsTiny2 (winding_t *w)
-{
- int i, j;
- vec_t len;
- Vector delta;
- int edges;
-
- vec_t maxLen = 0;
- Vector maxEdge = vec3_origin;
-
- edges = 0;
- for (i=0 ; i<w->numpoints ; i++)
- {
- j = i == w->numpoints - 1 ? 0 : i+1;
- VectorSubtract (w->p[j], w->p[i], delta);
- len = VectorLength (delta);
- if (len > maxLen)
- {
- maxEdge = delta;
- maxLen = len;
- }
- }
- Vector normal;
- vec_t dist;
- WindingPlane (w, normal, &dist); // normal can come back vec3_origin in some cases
- VectorNormalize(maxEdge);
- Vector cross = CrossProduct(normal, maxEdge);
- VectorNormalize(cross);
- Vector mins, maxs;
- ClearBounds( mins, maxs );
- for (i=0 ; i<w->numpoints ; i++)
- {
- Vector point;
- point.x = DotProduct( w->p[i], maxEdge );
- point.y = DotProduct( w->p[i], cross );
- point.z = DotProduct( w->p[i], normal );
- AddPointToBounds( point, mins, maxs );
- }
-
- // check to see if the size in the plane is too small in either dimension
- Vector size = maxs - mins;
- for ( i = 0; i < 2; i++ )
- {
- if ( size[i] < EDGE_LENGTH )
- return true;
- }
- return false;
-}
-#endif
-
-
-/*
-================
-WindingIsHuge
-
-Returns true if the winding still has one of the points
-from basewinding for plane
-================
-*/
-qboolean WindingIsHuge (winding_t *w)
-{
- int i, j;
-
- for (i=0 ; i<w->numpoints ; i++)
- {
- for (j=0 ; j<3 ; j++)
- if (w->p[i][j] < MIN_COORD_INTEGER || w->p[i][j] > MAX_COORD_INTEGER)
- return true;
- }
- return false;
-}
-
-//============================================================
-
-/*
-================
-Leafnode
-================
-*/
-void LeafNode (node_t *node, bspbrush_t *brushes)
-{
- bspbrush_t *b;
- int i;
-
- node->planenum = PLANENUM_LEAF;
- node->contents = 0;
-
- for (b=brushes ; b ; b=b->next)
- {
- // if the brush is solid and all of its sides are on nodes,
- // it eats everything
- if (b->original->contents & CONTENTS_SOLID)
- {
- for (i=0 ; i<b->numsides ; i++)
- if (b->sides[i].texinfo != TEXINFO_NODE)
- break;
- if (i == b->numsides)
- {
- node->contents = CONTENTS_SOLID;
- break;
- }
- }
- node->contents |= b->original->contents;
- }
-
- node->brushlist = brushes;
-}
-
-
-void RemoveAreaPortalBrushes_R( node_t *node )
-{
- if( node->planenum == PLANENUM_LEAF )
- {
- // Remove any CONTENTS_AREAPORTAL brushes we added. We don't want them in the engine
- // at runtime but we do want their flags in the leaves.
- bspbrush_t **pPrev = &node->brushlist;
- for( bspbrush_t *b=node->brushlist; b; b=b->next )
- {
- if( b->original->contents == CONTENTS_AREAPORTAL )
- {
- *pPrev = b->next;
- }
- else
- {
- pPrev = &b->next;
- }
- }
- }
- else
- {
- RemoveAreaPortalBrushes_R( node->children[0] );
- RemoveAreaPortalBrushes_R( node->children[1] );
- }
-}
-
-
-//============================================================
-
-void CheckPlaneAgainstParents (int pnum, node_t *node)
-{
- node_t *p;
-
- for (p=node->parent ; p ; p=p->parent)
- {
- if (p->planenum == pnum)
- Error ("Tried parent");
- }
-}
-
-qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
-{
- bspbrush_t *front, *back;
- qboolean good;
-
- SplitBrush (node->volume, pnum, &front, &back);
-
- good = (front && back);
-
- if (front)
- FreeBrush (front);
- if (back)
- FreeBrush (back);
-
- return good;
-}
-
-/*
-================
-SelectSplitSide
-
-Using a hueristic, choses one of the sides out of the brushlist
-to partition the brushes with.
-Returns NULL if there are no valid planes to split with..
-================
-*/
-
-side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
-{
- int value, bestvalue;
- bspbrush_t *brush, *test;
- side_t *side, *bestside;
- int i, j, pass, numpasses;
- int pnum;
- int s;
- int front, back, both, facing, splits;
- int bsplits;
- int bestsplits;
- int epsilonbrush;
- qboolean hintsplit = false;
-
- bestside = NULL;
- bestvalue = -99999;
- bestsplits = 0;
-
- // the search order goes: visible-structural, nonvisible-structural
- // If any valid plane is available in a pass, no further
- // passes will be tried.
- numpasses = 2;
- for (pass = 0 ; pass < numpasses ; pass++)
- {
- for (brush = brushes ; brush ; brush=brush->next)
- {
- for (i=0 ; i<brush->numsides ; i++)
- {
- side = brush->sides + i;
-
- if (side->bevel)
- continue; // never use a bevel as a spliter
- if (!side->winding)
- continue; // nothing visible, so it can't split
- if (side->texinfo == TEXINFO_NODE)
- continue; // allready a node splitter
- if (side->tested)
- continue; // we allready have metrics for this plane
- if (side->surf & SURF_SKIP)
- continue; // skip surfaces are never chosen
- if ( side->visible ^ (pass<1) )
- continue; // only check visible faces on first pass
-
- pnum = side->planenum;
- pnum &= ~1; // allways use positive facing plane
-
- CheckPlaneAgainstParents (pnum, node);
-
- if (!CheckPlaneAgainstVolume (pnum, node))
- continue; // would produce a tiny volume
-
- front = 0;
- back = 0;
- both = 0;
- facing = 0;
- splits = 0;
- epsilonbrush = 0;
-
- for (test = brushes ; test ; test=test->next)
- {
- s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
-
- splits += bsplits;
- if (bsplits && (s&PSIDE_FACING) )
- Error ("PSIDE_FACING with splits");
-
- test->testside = s;
- // if the brush shares this face, don't bother
- // testing that facenum as a splitter again
- if (s & PSIDE_FACING)
- {
- facing++;
- for (j=0 ; j<test->numsides ; j++)
- {
- if ( (test->sides[j].planenum&~1) == pnum)
- test->sides[j].tested = true;
- }
- }
- if (s & PSIDE_FRONT)
- front++;
- if (s & PSIDE_BACK)
- back++;
- if (s == PSIDE_BOTH)
- both++;
- }
-
- // give a value estimate for using this plane
- value = 5*facing - 5*splits - abs(front-back);
-// value = -5*splits;
-// value = 5*facing - 5*splits;
- if (g_MainMap->mapplanes[pnum].type < 3)
- value+=5; // axial is better
- value -= epsilonbrush*1000; // avoid!
-
- // trans should split last
- if ( side->surf & SURF_TRANS )
- {
- value -= 500;
- }
-
- // never split a hint side except with another hint
- if (hintsplit && !(side->surf & SURF_HINT) )
- value = -9999999;
-
- // water should split first
- if (side->contents & (CONTENTS_WATER | CONTENTS_SLIME))
- value = 9999999;
-
- // save off the side test so we don't need
- // to recalculate it when we actually seperate
- // the brushes
- if (value > bestvalue)
- {
- bestvalue = value;
- bestside = side;
- bestsplits = splits;
- for (test = brushes ; test ; test=test->next)
- test->side = test->testside;
- }
- }
- }
-
- // if we found a good plane, don't bother trying any
- // other passes
- if (bestside)
- {
- if (pass > 0)
- {
- if (numthreads == 1)
- c_nonvis++;
- }
- break;
- }
- }
-
- //
- // clear all the tested flags we set
- //
- for (brush = brushes ; brush ; brush=brush->next)
- {
- for (i=0 ; i<brush->numsides ; i++)
- brush->sides[i].tested = false;
- }
-
- return bestside;
-}
-
-
-/*
-==================
-BrushMostlyOnSide
-
-==================
-*/
-int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
-{
- int i, j;
- winding_t *w;
- vec_t d, max;
- int side;
-
- max = 0;
- side = PSIDE_FRONT;
- for (i=0 ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (!w)
- continue;
- for (j=0 ; j<w->numpoints ; j++)
- {
- d = DotProduct (w->p[j], plane->normal) - plane->dist;
- if (d > max)
- {
- max = d;
- side = PSIDE_FRONT;
- }
- if (-d > max)
- {
- max = -d;
- side = PSIDE_BACK;
- }
- }
- }
- return side;
-}
-
-/*
-================
-SplitBrush
-
-Generates two new brushes, leaving the original
-unchanged
-================
-*/
-
-
-void SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
-{
- bspbrush_t *b[2];
- int i, j;
- winding_t *w, *cw[2], *midwinding;
- plane_t *plane, *plane2;
- side_t *s, *cs;
- float d, d_front, d_back;
-
- *front = *back = NULL;
- plane = &g_MainMap->mapplanes[planenum];
-
- // check all points
- d_front = d_back = 0;
- for (i=0 ; i<brush->numsides ; i++)
- {
- w = brush->sides[i].winding;
- if (!w)
- continue;
- for (j=0 ; j<w->numpoints ; j++)
- {
- d = DotProduct (w->p[j], plane->normal) - plane->dist;
- if (d > 0 && d > d_front)
- d_front = d;
- if (d < 0 && d < d_back)
- d_back = d;
- }
- }
-
- if (d_front < 0.1) // PLANESIDE_EPSILON)
- { // only on back
- *back = CopyBrush (brush);
- return;
- }
- if (d_back > -0.1) // PLANESIDE_EPSILON)
- { // only on front
- *front = CopyBrush (brush);
- return;
- }
-
-
- // Move the CSG problem so that offset is at the origin
- // This gives us much better floating point precision in the clipping operations
- Vector offset = -0.5f * (brush->mins + brush->maxs);
- // create a new winding from the split plane
-
- w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal,offset));
- for (i=0 ; i<brush->numsides && w ; i++)
- {
- plane2 = &g_MainMap->mapplanes[brush->sides[i].planenum ^ 1];
- ChopWindingInPlace (&w, plane2->normal, plane2->dist+DotProduct(plane2->normal,offset), 0); // PLANESIDE_EPSILON);
- }
-
- if (!w || WindingIsTiny (w) )
- { // the brush isn't really split
- int side;
-
- side = BrushMostlyOnSide (brush, plane);
- if (side == PSIDE_FRONT)
- *front = CopyBrush (brush);
- if (side == PSIDE_BACK)
- *back = CopyBrush (brush);
- return;
- }
-
- if (WindingIsHuge (w))
- {
- qprintf ("WARNING: huge winding\n");
- }
-
- TranslateWinding( w, -offset );
- midwinding = w;
-
- //
- //
- // split it for real
- //
- //
-
- //
- // allocate two new brushes referencing the original
- //
- for( i = 0; i < 2; i++ )
- {
- b[i] = AllocBrush( brush->numsides + 1 );
- b[i]->original = brush->original;
- }
-
- //
- // split all the current windings
- //
- for( i = 0; i < brush->numsides; i++ )
- {
- // get the current side
- s = &brush->sides[i];
-
- // get the sides winding
- w = s->winding;
- if( !w )
- continue;
-
- // clip the winding
- ClipWindingEpsilon_Offset( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1], offset );
-
- for( j = 0; j < 2; j++ )
- {
- // does winding exist?
- if( !cw[j] )
- continue;
-#if 0
- if (WindingIsTiny (cw[j]))
- {
- FreeWinding (cw[j]);
- continue;
- }
-#endif
-
- //
- // create a clipped "side" with the new winding
- //
- cs = &b[j]->sides[b[j]->numsides];
- b[j]->numsides++;
- *cs = *s;
- cs->winding = cw[j];
- cs->tested = false;
- // save the original side information
- //cs->original = s->original;
- }
- }
-
-
- // see if we have valid polygons on both sides
-
- for (i=0 ; i<2 ; i++)
- {
- BoundBrush (b[i]);
- for (j=0 ; j<3 ; j++)
- {
- if (b[i]->mins[j] < MIN_COORD_INTEGER || b[i]->maxs[j] > MAX_COORD_INTEGER)
- {
- qprintf ("bogus brush after clip\n");
- break;
- }
- }
-
- if (b[i]->numsides < 3 || j < 3)
- {
- FreeBrush (b[i]);
- b[i] = NULL;
- }
- }
-
- if ( !(b[0] && b[1]) )
- {
- if (!b[0] && !b[1])
- qprintf ("split removed brush\n");
- else
- qprintf ("split not on both sides\n");
- if (b[0])
- {
- FreeBrush (b[0]);
- *front = CopyBrush (brush);
- }
- if (b[1])
- {
- FreeBrush (b[1]);
- *back = CopyBrush (brush);
- }
- return;
- }
-
- // add the midwinding to both sides
- for (i=0 ; i<2 ; i++)
- {
- cs = &b[i]->sides[b[i]->numsides];
- b[i]->numsides++;
-
- cs->planenum = planenum^i^1;
- cs->texinfo = TEXINFO_NODE;
-
- // initialize the displacement map index
- cs->pMapDisp = NULL;
-
- cs->visible = false;
- cs->tested = false;
- if (i==0)
- cs->winding = CopyWinding (midwinding);
- else
- cs->winding = midwinding;
- }
-
-{
- vec_t v1;
- int i;
-
- for (i=0 ; i<2 ; i++)
- {
- v1 = BrushVolume (b[i]);
- if (v1 < 1.0)
- {
- FreeBrush (b[i]);
- b[i] = NULL;
-// qprintf ("tiny volume after clip\n");
- }
- }
-}
-
- *front = b[0];
- *back = b[1];
-}
-
-
-/*
-================
-SplitBrushList
-================
-*/
-void SplitBrushList (bspbrush_t *brushes,
- node_t *node, bspbrush_t **front, bspbrush_t **back)
-{
- bspbrush_t *brush, *newbrush, *newbrush2;
- side_t *side;
- int sides;
- int i;
-
- *front = *back = NULL;
-
- for (brush = brushes ; brush ; brush=brush->next)
- {
- sides = brush->side;
-
- if (sides == PSIDE_BOTH)
- { // split into two brushes
- SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
- if (newbrush)
- {
- newbrush->next = *front;
- *front = newbrush;
- }
- if (newbrush2)
- {
- newbrush2->next = *back;
- *back = newbrush2;
- }
- continue;
- }
-
- newbrush = CopyBrush (brush);
-
- // if the planenum is actualy a part of the brush
- // find the plane and flag it as used so it won't be tried
- // as a splitter again
- if (sides & PSIDE_FACING)
- {
- for (i=0 ; i<newbrush->numsides ; i++)
- {
- side = newbrush->sides + i;
- if ( (side->planenum& ~1) == node->planenum)
- side->texinfo = TEXINFO_NODE;
- }
- }
-
-
- if (sides & PSIDE_FRONT)
- {
- newbrush->next = *front;
- *front = newbrush;
- continue;
- }
- if (sides & PSIDE_BACK)
- {
- newbrush->next = *back;
- *back = newbrush;
- continue;
- }
- }
-}
-
-
-/*
-================
-BuildTree_r
-================
-*/
-
-
-node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
-{
- node_t *newnode;
- side_t *bestside;
- int i;
- bspbrush_t *children[2];
-
- if (numthreads == 1)
- c_nodes++;
-
- // find the best plane to use as a splitter
- bestside = SelectSplitSide (brushes, node);
-
- if (!bestside)
- {
- // leaf node
- node->side = NULL;
- node->planenum = -1;
- LeafNode (node, brushes);
- return node;
- }
-
- // this is a splitplane node
- node->side = bestside;
- node->planenum = bestside->planenum & ~1; // always use front facing
-
- SplitBrushList (brushes, node, &children[0], &children[1]);
- FreeBrushList (brushes);
-
- // allocate children before recursing
- for (i=0 ; i<2 ; i++)
- {
- newnode = AllocNode ();
- newnode->parent = node;
- node->children[i] = newnode;
- }
-
- SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
- &node->children[1]->volume);
-
- // recursively process children
- for (i=0 ; i<2 ; i++)
- {
- node->children[i] = BuildTree_r (node->children[i], children[i]);
- }
-
- return node;
-}
-
-
-//===========================================================
-
-/*
-=================
-BrushBSP
-
-The incoming list will be freed before exiting
-=================
-*/
-tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs)
-{
- node_t *node;
- bspbrush_t *b;
- int c_faces, c_nonvisfaces;
- int c_brushes;
- tree_t *tree;
- int i;
- vec_t volume;
-
- qprintf ("--- BrushBSP ---\n");
-
- tree = AllocTree ();
-
- c_faces = 0;
- c_nonvisfaces = 0;
- c_brushes = 0;
- for (b=brushlist ; b ; b=b->next)
- {
- c_brushes++;
-
- volume = BrushVolume (b);
- if (volume < microvolume)
- {
- Warning("Brush %i: WARNING, microbrush\n", b->original->id);
- }
-
- for (i=0 ; i<b->numsides ; i++)
- {
- if (b->sides[i].bevel)
- continue;
- if (!b->sides[i].winding)
- continue;
- if (b->sides[i].texinfo == TEXINFO_NODE)
- continue;
- if (b->sides[i].visible)
- c_faces++;
- else
- c_nonvisfaces++;
- }
-
- AddPointToBounds (b->mins, tree->mins, tree->maxs);
- AddPointToBounds (b->maxs, tree->mins, tree->maxs);
- }
-
- qprintf ("%5i brushes\n", c_brushes);
- qprintf ("%5i visible faces\n", c_faces);
- qprintf ("%5i nonvisible faces\n", c_nonvisfaces);
-
- c_nodes = 0;
- c_nonvis = 0;
- node = AllocNode ();
-
- node->volume = BrushFromBounds (mins, maxs);
-
- tree->headnode = node;
-
- node = BuildTree_r (node, brushlist);
- qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
- qprintf ("%5i nonvis nodes\n", c_nonvis);
- qprintf ("%5i leafs\n", (c_nodes+1)/2);
-#if 0
-{ // debug code
-static node_t *tnode;
-Vector p;
-
-p[0] = -1469;
-p[1] = -118;
-p[2] = 119;
-tnode = PointInLeaf (tree->headnode, p);
-Msg("contents: %i\n", tnode->contents);
-p[0] = 0;
-}
-#endif
- return tree;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+
+int c_nodes;
+int c_nonvis;
+int c_active_brushes;
+
+// if a brush just barely pokes onto the other side,
+// let it slide by without chopping
+#define PLANESIDE_EPSILON 0.001
+//0.1
+
+
+void FindBrushInTree (node_t *node, int brushnum)
+{
+ bspbrush_t *b;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ for (b=node->brushlist ; b ; b=b->next)
+ if (b->original->brushnum == brushnum)
+ Msg("here\n");
+ return;
+ }
+ FindBrushInTree (node->children[0], brushnum);
+ FindBrushInTree (node->children[1], brushnum);
+}
+
+//==================================================
+
+/*
+================
+DrawBrushList
+================
+*/
+void DrawBrushList (bspbrush_t *brush, node_t *node)
+{
+ int i;
+ side_t *s;
+
+ GLS_BeginScene ();
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (s->texinfo == TEXINFO_NODE)
+ GLS_Winding (s->winding, 1);
+ else if (!s->visible)
+ GLS_Winding (s->winding, 2);
+ else
+ GLS_Winding (s->winding, 0);
+ }
+ }
+ GLS_EndScene ();
+}
+
+/*
+================
+WriteBrushList
+================
+*/
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
+{
+ int i;
+ side_t *s;
+
+ qprintf ("writing %s\n", name);
+ FileHandle_t f = g_pFileSystem->Open(name, "w");
+
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (onlyvis && !s->visible)
+ continue;
+ OutputWinding (brush->sides[i].winding, f);
+ }
+ }
+
+ g_pFileSystem->Close (f);
+}
+
+void PrintBrush (bspbrush_t *brush)
+{
+ int i;
+
+ Msg("brush: %p\n", brush);
+ for (i=0;i<brush->numsides ; i++)
+ {
+ pw(brush->sides[i].winding);
+ Msg("\n");
+ }
+}
+
+/*
+==================
+BoundBrush
+
+Sets the mins/maxs based on the windings
+==================
+*/
+void BoundBrush (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+
+ ClearBounds (brush->mins, brush->maxs);
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], brush->mins, brush->maxs);
+ }
+}
+
+Vector PointInsideBrush( bspbrush_t *brush )
+{
+ Vector insidePoint = vec3_origin;
+
+ bool bInside = false;
+ for ( int k = 0; k < 4 && !bInside; k++ )
+ {
+ bInside = true;
+ for (int i = 0; i < brush->numsides; i++)
+ {
+ side_t *side = &brush->sides[i];
+ plane_t *plane = &g_MainMap->mapplanes[side->planenum];
+ float d = DotProduct( plane->normal, insidePoint ) - plane->dist;
+ if ( d < 0 )
+ {
+ bInside = false;
+ insidePoint -= d * plane->normal;
+ }
+ }
+ }
+ return insidePoint;
+}
+
+/*
+==================
+CreateBrushWindings
+
+==================
+*/
+void CreateBrushWindings (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ // translate the CSG problem to improve precision
+ Vector insidePoint = PointInsideBrush( brush );
+ Vector offset = -insidePoint;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->sides[i];
+ plane = &g_MainMap->mapplanes[side->planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal, offset));
+ for (j=0 ; j<brush->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (brush->sides[j].bevel)
+ continue;
+ plane = &g_MainMap->mapplanes[brush->sides[j].planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist + DotProduct(plane->normal, offset), 0); //CLIP_EPSILON);
+ }
+
+ TranslateWinding( w, -offset );
+ side->winding = w;
+ }
+
+ BoundBrush (brush);
+}
+
+/*
+==================
+BrushFromBounds
+
+Creates a new axial brush
+==================
+*/
+bspbrush_t *BrushFromBounds (Vector& mins, Vector& maxs)
+{
+ bspbrush_t *b;
+ int i;
+ Vector normal;
+ vec_t dist;
+
+ b = AllocBrush (6);
+ b->numsides = 6;
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = maxs[i];
+ b->sides[i].planenum = g_MainMap->FindFloatPlane (normal, dist);
+
+ normal[i] = -1;
+ dist = -mins[i];
+ b->sides[3+i].planenum = g_MainMap->FindFloatPlane (normal, dist);
+ }
+
+ CreateBrushWindings (b);
+
+ return b;
+}
+
+/*
+==================
+BrushVolume
+
+==================
+*/
+vec_t BrushVolume (bspbrush_t *brush)
+{
+ int i;
+ winding_t *w;
+ Vector corner;
+ vec_t d, area, volume;
+ plane_t *plane;
+
+ if (!brush)
+ return 0;
+
+ // grab the first valid point as the corner
+
+ w = NULL;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (w)
+ break;
+ }
+ if (!w)
+ return 0;
+ VectorCopy (w->p[0], corner);
+
+ // make tetrahedrons to all other faces
+
+ volume = 0;
+ for ( ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ plane = &g_MainMap->mapplanes[brush->sides[i].planenum];
+ d = -(DotProduct (corner, plane->normal) - plane->dist);
+ area = WindingArea (w);
+ volume += d*area;
+ }
+
+ volume /= 3;
+ return volume;
+}
+
+/*
+================
+CountBrushList
+================
+*/
+int CountBrushList (bspbrush_t *brushes)
+{
+ int c;
+
+ c = 0;
+ for ( ; brushes ; brushes = brushes->next)
+ c++;
+ return c;
+}
+
+/*
+================
+AllocTree
+================
+*/
+tree_t *AllocTree (void)
+{
+ tree_t *tree;
+
+ tree = (tree_t*)malloc(sizeof(*tree));
+ memset (tree, 0, sizeof(*tree));
+ ClearBounds (tree->mins, tree->maxs);
+
+ return tree;
+}
+
+/*
+================
+AllocNode
+================
+*/
+node_t *AllocNode (void)
+{
+ static int s_NodeCount = 0;
+
+ node_t *node;
+
+ node = (node_t*)malloc(sizeof(*node));
+ memset (node, 0, sizeof(*node));
+ node->id = s_NodeCount;
+ node->diskId = -1;
+
+ s_NodeCount++;
+
+ return node;
+}
+
+
+/*
+================
+AllocBrush
+================
+*/
+bspbrush_t *AllocBrush (int numsides)
+{
+ static int s_BrushId = 0;
+
+ bspbrush_t *bb;
+ int c;
+
+ c = (int)&(((bspbrush_t *)0)->sides[numsides]);
+ bb = (bspbrush_t*)malloc(c);
+ memset (bb, 0, c);
+ bb->id = s_BrushId++;
+ if (numthreads == 1)
+ c_active_brushes++;
+ return bb;
+}
+
+/*
+================
+FreeBrush
+================
+*/
+void FreeBrush (bspbrush_t *brushes)
+{
+ int i;
+
+ for (i=0 ; i<brushes->numsides ; i++)
+ if (brushes->sides[i].winding)
+ FreeWinding(brushes->sides[i].winding);
+ free (brushes);
+ if (numthreads == 1)
+ c_active_brushes--;
+}
+
+
+/*
+================
+FreeBrushList
+================
+*/
+void FreeBrushList (bspbrush_t *brushes)
+{
+ bspbrush_t *next;
+
+ for ( ; brushes ; brushes = next)
+ {
+ next = brushes->next;
+
+ FreeBrush (brushes);
+ }
+}
+
+/*
+==================
+CopyBrush
+
+Duplicates the brush, the sides, and the windings
+==================
+*/
+bspbrush_t *CopyBrush (bspbrush_t *brush)
+{
+ bspbrush_t *newbrush;
+ int size;
+ int i;
+
+ size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
+
+ newbrush = AllocBrush (brush->numsides);
+ memcpy (newbrush, brush, size);
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].winding)
+ newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
+ }
+
+ return newbrush;
+}
+
+
+/*
+==================
+PointInLeaf
+
+==================
+*/
+node_t *PointInLeaf (node_t *node, Vector& point)
+{
+ vec_t d;
+ plane_t *plane;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &g_MainMap->mapplanes[node->planenum];
+ if (plane->type < 3)
+ {
+ d = point[plane->type] - plane->dist;
+ }
+ else
+ {
+ d = DotProduct (point, plane->normal) - plane->dist;
+ }
+
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+//========================================================
+
+/*
+==============
+BoxOnPlaneSide
+
+Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
+==============
+*/
+int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane)
+{
+ int side;
+ int i;
+ Vector corners[2];
+ vec_t dist1, dist2;
+
+ // axial planes are easy
+ if (plane->type < 3)
+ {
+ side = 0;
+ if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
+ side |= PSIDE_FRONT;
+ if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+ return side;
+ }
+
+ // create the proper leading and trailing verts for the box
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (plane->normal[i] < 0)
+ {
+ corners[0][i] = mins[i];
+ corners[1][i] = maxs[i];
+ }
+ else
+ {
+ corners[1][i] = mins[i];
+ corners[0][i] = maxs[i];
+ }
+ }
+
+ dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
+ dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+ side = 0;
+ if (dist1 >= PLANESIDE_EPSILON)
+ side = PSIDE_FRONT;
+ if (dist2 < PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+
+ return side;
+}
+
+/*
+============
+QuickTestBrushToPlanenum
+
+============
+*/
+int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
+{
+ int i, num;
+ plane_t *plane;
+ int s;
+
+ *numsplits = 0;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &g_MainMap->mapplanes[planenum];
+ s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ // if both sides, count the visible faces split
+ if (s == PSIDE_BOTH)
+ {
+ *numsplits += 3;
+ }
+
+ return s;
+}
+
+/*
+============
+TestBrushToPlanenum
+
+============
+*/
+int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
+ int *numsplits, qboolean *hintsplit, int *epsilonbrush)
+{
+ int i, j, num;
+ plane_t *plane;
+ int s;
+ winding_t *w;
+ vec_t d, d_front, d_back;
+ int front, back;
+
+ *numsplits = 0;
+ *hintsplit = false;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &g_MainMap->mapplanes[planenum];
+ s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ if (s != PSIDE_BOTH)
+ return s;
+
+// if both sides, count the visible faces split
+ d_front = d_back = 0;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].texinfo == TEXINFO_NODE)
+ continue; // on node, don't worry about splits
+ if (!brush->sides[i].visible)
+ continue; // we don't care about non-visible
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+
+ front = back = 0;
+ for (j=0 ; j<w->numpoints; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+
+ if (d > d_front)
+ d_front = d;
+ if (d < d_back)
+ d_back = d;
+
+ if (d > 0.1) // PLANESIDE_EPSILON)
+ front = 1;
+ if (d < -0.1) // PLANESIDE_EPSILON)
+ back = 1;
+ }
+
+ if (front && back)
+ {
+ if ( !(brush->sides[i].surf & SURF_SKIP) )
+ {
+ (*numsplits)++;
+ if (brush->sides[i].surf & SURF_HINT)
+ *hintsplit = true;
+ }
+ }
+ }
+
+ if ( (d_front > 0.0 && d_front < 1.0)
+ || (d_back < 0.0 && d_back > -1.0) )
+ (*epsilonbrush)++;
+
+#if 0
+ if (*numsplits == 0)
+ { // didn't really need to be split
+ if (front)
+ s = PSIDE_FRONT;
+ else if (back)
+ s = PSIDE_BACK;
+ else
+ s = 0;
+ }
+#endif
+
+ return s;
+}
+
+//========================================================
+
+/*
+================
+WindingIsTiny
+
+Returns true if the winding would be crunched out of
+existance by the vertex snapping.
+================
+*/
+#define EDGE_LENGTH 0.2
+qboolean WindingIsTiny (winding_t *w)
+{
+ int i, j;
+ vec_t len;
+ Vector delta;
+ int edges;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > EDGE_LENGTH)
+ {
+ if (++edges == 3)
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// UNDONE: JAY: This should be a slightly better heuristic - it builds an OBB
+// around the winding and tests planar dimensions. NOTE: This can fail when a
+// winding normal cannot be constructed (or is degenerate), but that is probably
+// desired in this case.
+// UNDONE: Test & use this instead.
+#if 0
+qboolean WindingIsTiny2 (winding_t *w)
+{
+ int i, j;
+ vec_t len;
+ Vector delta;
+ int edges;
+
+ vec_t maxLen = 0;
+ Vector maxEdge = vec3_origin;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > maxLen)
+ {
+ maxEdge = delta;
+ maxLen = len;
+ }
+ }
+ Vector normal;
+ vec_t dist;
+ WindingPlane (w, normal, &dist); // normal can come back vec3_origin in some cases
+ VectorNormalize(maxEdge);
+ Vector cross = CrossProduct(normal, maxEdge);
+ VectorNormalize(cross);
+ Vector mins, maxs;
+ ClearBounds( mins, maxs );
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ Vector point;
+ point.x = DotProduct( w->p[i], maxEdge );
+ point.y = DotProduct( w->p[i], cross );
+ point.z = DotProduct( w->p[i], normal );
+ AddPointToBounds( point, mins, maxs );
+ }
+
+ // check to see if the size in the plane is too small in either dimension
+ Vector size = maxs - mins;
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( size[i] < EDGE_LENGTH )
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+/*
+================
+WindingIsHuge
+
+Returns true if the winding still has one of the points
+from basewinding for plane
+================
+*/
+qboolean WindingIsHuge (winding_t *w)
+{
+ int i, j;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (w->p[i][j] < MIN_COORD_INTEGER || w->p[i][j] > MAX_COORD_INTEGER)
+ return true;
+ }
+ return false;
+}
+
+//============================================================
+
+/*
+================
+Leafnode
+================
+*/
+void LeafNode (node_t *node, bspbrush_t *brushes)
+{
+ bspbrush_t *b;
+ int i;
+
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0;
+
+ for (b=brushes ; b ; b=b->next)
+ {
+ // if the brush is solid and all of its sides are on nodes,
+ // it eats everything
+ if (b->original->contents & CONTENTS_SOLID)
+ {
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->sides[i].texinfo != TEXINFO_NODE)
+ break;
+ if (i == b->numsides)
+ {
+ node->contents = CONTENTS_SOLID;
+ break;
+ }
+ }
+ node->contents |= b->original->contents;
+ }
+
+ node->brushlist = brushes;
+}
+
+
+void RemoveAreaPortalBrushes_R( node_t *node )
+{
+ if( node->planenum == PLANENUM_LEAF )
+ {
+ // Remove any CONTENTS_AREAPORTAL brushes we added. We don't want them in the engine
+ // at runtime but we do want their flags in the leaves.
+ bspbrush_t **pPrev = &node->brushlist;
+ for( bspbrush_t *b=node->brushlist; b; b=b->next )
+ {
+ if( b->original->contents == CONTENTS_AREAPORTAL )
+ {
+ *pPrev = b->next;
+ }
+ else
+ {
+ pPrev = &b->next;
+ }
+ }
+ }
+ else
+ {
+ RemoveAreaPortalBrushes_R( node->children[0] );
+ RemoveAreaPortalBrushes_R( node->children[1] );
+ }
+}
+
+
+//============================================================
+
+void CheckPlaneAgainstParents (int pnum, node_t *node)
+{
+ node_t *p;
+
+ for (p=node->parent ; p ; p=p->parent)
+ {
+ if (p->planenum == pnum)
+ Error ("Tried parent");
+ }
+}
+
+qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
+{
+ bspbrush_t *front, *back;
+ qboolean good;
+
+ SplitBrush (node->volume, pnum, &front, &back);
+
+ good = (front && back);
+
+ if (front)
+ FreeBrush (front);
+ if (back)
+ FreeBrush (back);
+
+ return good;
+}
+
+/*
+================
+SelectSplitSide
+
+Using a hueristic, choses one of the sides out of the brushlist
+to partition the brushes with.
+Returns NULL if there are no valid planes to split with..
+================
+*/
+
+side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
+{
+ int value, bestvalue;
+ bspbrush_t *brush, *test;
+ side_t *side, *bestside;
+ int i, j, pass, numpasses;
+ int pnum;
+ int s;
+ int front, back, both, facing, splits;
+ int bsplits;
+ int bestsplits;
+ int epsilonbrush;
+ qboolean hintsplit = false;
+
+ bestside = NULL;
+ bestvalue = -99999;
+ bestsplits = 0;
+
+ // the search order goes: visible-structural, nonvisible-structural
+ // If any valid plane is available in a pass, no further
+ // passes will be tried.
+ numpasses = 2;
+ for (pass = 0 ; pass < numpasses ; pass++)
+ {
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = brush->sides + i;
+
+ if (side->bevel)
+ continue; // never use a bevel as a spliter
+ if (!side->winding)
+ continue; // nothing visible, so it can't split
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // allready a node splitter
+ if (side->tested)
+ continue; // we allready have metrics for this plane
+ if (side->surf & SURF_SKIP)
+ continue; // skip surfaces are never chosen
+ if ( side->visible ^ (pass<1) )
+ continue; // only check visible faces on first pass
+
+ pnum = side->planenum;
+ pnum &= ~1; // allways use positive facing plane
+
+ CheckPlaneAgainstParents (pnum, node);
+
+ if (!CheckPlaneAgainstVolume (pnum, node))
+ continue; // would produce a tiny volume
+
+ front = 0;
+ back = 0;
+ both = 0;
+ facing = 0;
+ splits = 0;
+ epsilonbrush = 0;
+
+ for (test = brushes ; test ; test=test->next)
+ {
+ s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
+
+ splits += bsplits;
+ if (bsplits && (s&PSIDE_FACING) )
+ Error ("PSIDE_FACING with splits");
+
+ test->testside = s;
+ // if the brush shares this face, don't bother
+ // testing that facenum as a splitter again
+ if (s & PSIDE_FACING)
+ {
+ facing++;
+ for (j=0 ; j<test->numsides ; j++)
+ {
+ if ( (test->sides[j].planenum&~1) == pnum)
+ test->sides[j].tested = true;
+ }
+ }
+ if (s & PSIDE_FRONT)
+ front++;
+ if (s & PSIDE_BACK)
+ back++;
+ if (s == PSIDE_BOTH)
+ both++;
+ }
+
+ // give a value estimate for using this plane
+ value = 5*facing - 5*splits - abs(front-back);
+// value = -5*splits;
+// value = 5*facing - 5*splits;
+ if (g_MainMap->mapplanes[pnum].type < 3)
+ value+=5; // axial is better
+ value -= epsilonbrush*1000; // avoid!
+
+ // trans should split last
+ if ( side->surf & SURF_TRANS )
+ {
+ value -= 500;
+ }
+
+ // never split a hint side except with another hint
+ if (hintsplit && !(side->surf & SURF_HINT) )
+ value = -9999999;
+
+ // water should split first
+ if (side->contents & (CONTENTS_WATER | CONTENTS_SLIME))
+ value = 9999999;
+
+ // save off the side test so we don't need
+ // to recalculate it when we actually seperate
+ // the brushes
+ if (value > bestvalue)
+ {
+ bestvalue = value;
+ bestside = side;
+ bestsplits = splits;
+ for (test = brushes ; test ; test=test->next)
+ test->side = test->testside;
+ }
+ }
+ }
+
+ // if we found a good plane, don't bother trying any
+ // other passes
+ if (bestside)
+ {
+ if (pass > 0)
+ {
+ if (numthreads == 1)
+ c_nonvis++;
+ }
+ break;
+ }
+ }
+
+ //
+ // clear all the tested flags we set
+ //
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ brush->sides[i].tested = false;
+ }
+
+ return bestside;
+}
+
+
+/*
+==================
+BrushMostlyOnSide
+
+==================
+*/
+int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
+{
+ int i, j;
+ winding_t *w;
+ vec_t d, max;
+ int side;
+
+ max = 0;
+ side = PSIDE_FRONT;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > max)
+ {
+ max = d;
+ side = PSIDE_FRONT;
+ }
+ if (-d > max)
+ {
+ max = -d;
+ side = PSIDE_BACK;
+ }
+ }
+ }
+ return side;
+}
+
+/*
+================
+SplitBrush
+
+Generates two new brushes, leaving the original
+unchanged
+================
+*/
+
+
+void SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
+{
+ bspbrush_t *b[2];
+ int i, j;
+ winding_t *w, *cw[2], *midwinding;
+ plane_t *plane, *plane2;
+ side_t *s, *cs;
+ float d, d_front, d_back;
+
+ *front = *back = NULL;
+ plane = &g_MainMap->mapplanes[planenum];
+
+ // check all points
+ d_front = d_back = 0;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > 0 && d > d_front)
+ d_front = d;
+ if (d < 0 && d < d_back)
+ d_back = d;
+ }
+ }
+
+ if (d_front < 0.1) // PLANESIDE_EPSILON)
+ { // only on back
+ *back = CopyBrush (brush);
+ return;
+ }
+ if (d_back > -0.1) // PLANESIDE_EPSILON)
+ { // only on front
+ *front = CopyBrush (brush);
+ return;
+ }
+
+
+ // Move the CSG problem so that offset is at the origin
+ // This gives us much better floating point precision in the clipping operations
+ Vector offset = -0.5f * (brush->mins + brush->maxs);
+ // create a new winding from the split plane
+
+ w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal,offset));
+ for (i=0 ; i<brush->numsides && w ; i++)
+ {
+ plane2 = &g_MainMap->mapplanes[brush->sides[i].planenum ^ 1];
+ ChopWindingInPlace (&w, plane2->normal, plane2->dist+DotProduct(plane2->normal,offset), 0); // PLANESIDE_EPSILON);
+ }
+
+ if (!w || WindingIsTiny (w) )
+ { // the brush isn't really split
+ int side;
+
+ side = BrushMostlyOnSide (brush, plane);
+ if (side == PSIDE_FRONT)
+ *front = CopyBrush (brush);
+ if (side == PSIDE_BACK)
+ *back = CopyBrush (brush);
+ return;
+ }
+
+ if (WindingIsHuge (w))
+ {
+ qprintf ("WARNING: huge winding\n");
+ }
+
+ TranslateWinding( w, -offset );
+ midwinding = w;
+
+ //
+ //
+ // split it for real
+ //
+ //
+
+ //
+ // allocate two new brushes referencing the original
+ //
+ for( i = 0; i < 2; i++ )
+ {
+ b[i] = AllocBrush( brush->numsides + 1 );
+ b[i]->original = brush->original;
+ }
+
+ //
+ // split all the current windings
+ //
+ for( i = 0; i < brush->numsides; i++ )
+ {
+ // get the current side
+ s = &brush->sides[i];
+
+ // get the sides winding
+ w = s->winding;
+ if( !w )
+ continue;
+
+ // clip the winding
+ ClipWindingEpsilon_Offset( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1], offset );
+
+ for( j = 0; j < 2; j++ )
+ {
+ // does winding exist?
+ if( !cw[j] )
+ continue;
+#if 0
+ if (WindingIsTiny (cw[j]))
+ {
+ FreeWinding (cw[j]);
+ continue;
+ }
+#endif
+
+ //
+ // create a clipped "side" with the new winding
+ //
+ cs = &b[j]->sides[b[j]->numsides];
+ b[j]->numsides++;
+ *cs = *s;
+ cs->winding = cw[j];
+ cs->tested = false;
+ // save the original side information
+ //cs->original = s->original;
+ }
+ }
+
+
+ // see if we have valid polygons on both sides
+
+ for (i=0 ; i<2 ; i++)
+ {
+ BoundBrush (b[i]);
+ for (j=0 ; j<3 ; j++)
+ {
+ if (b[i]->mins[j] < MIN_COORD_INTEGER || b[i]->maxs[j] > MAX_COORD_INTEGER)
+ {
+ qprintf ("bogus brush after clip\n");
+ break;
+ }
+ }
+
+ if (b[i]->numsides < 3 || j < 3)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+ }
+ }
+
+ if ( !(b[0] && b[1]) )
+ {
+ if (!b[0] && !b[1])
+ qprintf ("split removed brush\n");
+ else
+ qprintf ("split not on both sides\n");
+ if (b[0])
+ {
+ FreeBrush (b[0]);
+ *front = CopyBrush (brush);
+ }
+ if (b[1])
+ {
+ FreeBrush (b[1]);
+ *back = CopyBrush (brush);
+ }
+ return;
+ }
+
+ // add the midwinding to both sides
+ for (i=0 ; i<2 ; i++)
+ {
+ cs = &b[i]->sides[b[i]->numsides];
+ b[i]->numsides++;
+
+ cs->planenum = planenum^i^1;
+ cs->texinfo = TEXINFO_NODE;
+
+ // initialize the displacement map index
+ cs->pMapDisp = NULL;
+
+ cs->visible = false;
+ cs->tested = false;
+ if (i==0)
+ cs->winding = CopyWinding (midwinding);
+ else
+ cs->winding = midwinding;
+ }
+
+{
+ vec_t v1;
+ int i;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ v1 = BrushVolume (b[i]);
+ if (v1 < 1.0)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+// qprintf ("tiny volume after clip\n");
+ }
+ }
+}
+
+ *front = b[0];
+ *back = b[1];
+}
+
+
+/*
+================
+SplitBrushList
+================
+*/
+void SplitBrushList (bspbrush_t *brushes,
+ node_t *node, bspbrush_t **front, bspbrush_t **back)
+{
+ bspbrush_t *brush, *newbrush, *newbrush2;
+ side_t *side;
+ int sides;
+ int i;
+
+ *front = *back = NULL;
+
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ sides = brush->side;
+
+ if (sides == PSIDE_BOTH)
+ { // split into two brushes
+ SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
+ if (newbrush)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ }
+ if (newbrush2)
+ {
+ newbrush2->next = *back;
+ *back = newbrush2;
+ }
+ continue;
+ }
+
+ newbrush = CopyBrush (brush);
+
+ // if the planenum is actualy a part of the brush
+ // find the plane and flag it as used so it won't be tried
+ // as a splitter again
+ if (sides & PSIDE_FACING)
+ {
+ for (i=0 ; i<newbrush->numsides ; i++)
+ {
+ side = newbrush->sides + i;
+ if ( (side->planenum& ~1) == node->planenum)
+ side->texinfo = TEXINFO_NODE;
+ }
+ }
+
+
+ if (sides & PSIDE_FRONT)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ continue;
+ }
+ if (sides & PSIDE_BACK)
+ {
+ newbrush->next = *back;
+ *back = newbrush;
+ continue;
+ }
+ }
+}
+
+
+/*
+================
+BuildTree_r
+================
+*/
+
+
+node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
+{
+ node_t *newnode;
+ side_t *bestside;
+ int i;
+ bspbrush_t *children[2];
+
+ if (numthreads == 1)
+ c_nodes++;
+
+ // find the best plane to use as a splitter
+ bestside = SelectSplitSide (brushes, node);
+
+ if (!bestside)
+ {
+ // leaf node
+ node->side = NULL;
+ node->planenum = -1;
+ LeafNode (node, brushes);
+ return node;
+ }
+
+ // this is a splitplane node
+ node->side = bestside;
+ node->planenum = bestside->planenum & ~1; // always use front facing
+
+ SplitBrushList (brushes, node, &children[0], &children[1]);
+ FreeBrushList (brushes);
+
+ // allocate children before recursing
+ for (i=0 ; i<2 ; i++)
+ {
+ newnode = AllocNode ();
+ newnode->parent = node;
+ node->children[i] = newnode;
+ }
+
+ SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
+ &node->children[1]->volume);
+
+ // recursively process children
+ for (i=0 ; i<2 ; i++)
+ {
+ node->children[i] = BuildTree_r (node->children[i], children[i]);
+ }
+
+ return node;
+}
+
+
+//===========================================================
+
+/*
+=================
+BrushBSP
+
+The incoming list will be freed before exiting
+=================
+*/
+tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs)
+{
+ node_t *node;
+ bspbrush_t *b;
+ int c_faces, c_nonvisfaces;
+ int c_brushes;
+ tree_t *tree;
+ int i;
+ vec_t volume;
+
+ qprintf ("--- BrushBSP ---\n");
+
+ tree = AllocTree ();
+
+ c_faces = 0;
+ c_nonvisfaces = 0;
+ c_brushes = 0;
+ for (b=brushlist ; b ; b=b->next)
+ {
+ c_brushes++;
+
+ volume = BrushVolume (b);
+ if (volume < microvolume)
+ {
+ Warning("Brush %i: WARNING, microbrush\n", b->original->id);
+ }
+
+ for (i=0 ; i<b->numsides ; i++)
+ {
+ if (b->sides[i].bevel)
+ continue;
+ if (!b->sides[i].winding)
+ continue;
+ if (b->sides[i].texinfo == TEXINFO_NODE)
+ continue;
+ if (b->sides[i].visible)
+ c_faces++;
+ else
+ c_nonvisfaces++;
+ }
+
+ AddPointToBounds (b->mins, tree->mins, tree->maxs);
+ AddPointToBounds (b->maxs, tree->mins, tree->maxs);
+ }
+
+ qprintf ("%5i brushes\n", c_brushes);
+ qprintf ("%5i visible faces\n", c_faces);
+ qprintf ("%5i nonvisible faces\n", c_nonvisfaces);
+
+ c_nodes = 0;
+ c_nonvis = 0;
+ node = AllocNode ();
+
+ node->volume = BrushFromBounds (mins, maxs);
+
+ tree->headnode = node;
+
+ node = BuildTree_r (node, brushlist);
+ qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
+ qprintf ("%5i nonvis nodes\n", c_nonvis);
+ qprintf ("%5i leafs\n", (c_nodes+1)/2);
+#if 0
+{ // debug code
+static node_t *tnode;
+Vector p;
+
+p[0] = -1469;
+p[1] = -118;
+p[2] = 119;
+tnode = PointInLeaf (tree->headnode, p);
+Msg("contents: %i\n", tnode->contents);
+p[0] = 0;
+}
+#endif
+ return tree;
+}
+
diff --git a/mp/src/utils/vbsp/csg.cpp b/mp/src/utils/vbsp/csg.cpp
index c4b89bd9..5de1d680 100644
--- a/mp/src/utils/vbsp/csg.cpp
+++ b/mp/src/utils/vbsp/csg.cpp
@@ -1,784 +1,784 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-
-/*
-
-tag all brushes with original contents
-brushes may contain multiple contents
-there will be no brush overlap after csg phase
-
-
-
-
-each side has a count of the other sides it splits
-
-the best split will be the one that minimizes the total split counts
-of all remaining sides
-
-precalc side on plane table
-
-evaluate split side
-{
-cost = 0
-for all sides
- for all sides
- get
- if side splits side and splitside is on same child
- cost++;
-}
-
-
- */
-
-void SplitBrush2( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
-{
- SplitBrush( brush, planenum, front, back );
-#if 0
- if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
- (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
- if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
- (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
-#endif
-}
-
-/*
-===============
-SubtractBrush
-
-Returns a list of brushes that remain after B is subtracted from A.
-May by empty if A is contained inside B.
-
-The originals are undisturbed.
-===============
-*/
-bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
-{ // a - b = out (list)
- int i;
- bspbrush_t *front, *back;
- bspbrush_t *out, *in;
-
- in = a;
- out = NULL;
- for (i=0 ; i<b->numsides && in ; i++)
- {
- SplitBrush2 (in, b->sides[i].planenum, &front, &back);
- if (in != a)
- FreeBrush (in);
- if (front)
- { // add to list
- front->next = out;
- out = front;
- }
- in = back;
- }
- if (in)
- FreeBrush (in);
- else
- { // didn't really intersect
- FreeBrushList (out);
- return a;
- }
- return out;
-}
-
-/*
-===============
-IntersectBrush
-
-Returns a single brush made up by the intersection of the
-two provided brushes, or NULL if they are disjoint.
-
-The originals are undisturbed.
-===============
-*/
-bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
-{
- int i;
- bspbrush_t *front, *back;
- bspbrush_t *in;
-
- in = a;
- for (i=0 ; i<b->numsides && in ; i++)
- {
- SplitBrush2 (in, b->sides[i].planenum, &front, &back);
- if (in != a)
- FreeBrush (in);
- if (front)
- FreeBrush (front);
- in = back;
- }
-
- if (in == a || !in)
- return NULL;
-
- in->next = NULL;
- return in;
-}
-
-
-/*
-===============
-BrushesDisjoint
-
-Returns true if the two brushes definately do not intersect.
-There will be false negatives for some non-axial combinations.
-===============
-*/
-qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
-{
- int i, j;
-
- // check bounding boxes
- for (i=0 ; i<3 ; i++)
- if (a->mins[i] >= b->maxs[i]
- || a->maxs[i] <= b->mins[i])
- return true; // bounding boxes don't overlap
-
- // check for opposing planes
- for (i=0 ; i<a->numsides ; i++)
- {
- for (j=0 ; j<b->numsides ; j++)
- {
- if (a->sides[i].planenum ==
- (b->sides[j].planenum^1) )
- return true; // opposite planes, so not touching
- }
- }
-
- return false; // might intersect
-}
-
-
-int minplanenums[3];
-int maxplanenums[3];
-
-/*
-===============
-ClipBrushToBox
-
-Any planes shared with the box edge will be set to no texinfo
-===============
-*/
-bspbrush_t *ClipBrushToBox (bspbrush_t *brush, const Vector& clipmins, const Vector& clipmaxs)
-{
- int i, j;
- bspbrush_t *front, *back;
- int p;
-
- for (j=0 ; j<2 ; j++)
- {
- if (brush->maxs[j] > clipmaxs[j])
- {
- SplitBrush (brush, maxplanenums[j], &front, &back);
- if (front)
- FreeBrush (front);
- brush = back;
- if (!brush)
- return NULL;
- }
- if (brush->mins[j] < clipmins[j])
- {
- SplitBrush (brush, minplanenums[j], &front, &back);
- if (back)
- FreeBrush (back);
- brush = front;
- if (!brush)
- return NULL;
- }
- }
-
- // remove any colinear faces
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- p = brush->sides[i].planenum & ~1;
- if (p == maxplanenums[0] || p == maxplanenums[1]
- || p == minplanenums[0] || p == minplanenums[1])
- {
- brush->sides[i].texinfo = TEXINFO_NODE;
- brush->sides[i].visible = false;
- }
- }
- return brush;
-}
-
-
-//-----------------------------------------------------------------------------
-// Creates a clipped brush from a map brush
-//-----------------------------------------------------------------------------
-static bspbrush_t *CreateClippedBrush( mapbrush_t *mb, const Vector& clipmins, const Vector& clipmaxs )
-{
- int nNumSides = mb->numsides;
- if (!nNumSides)
- return NULL;
-
- // if the brush is outside the clip area, skip it
- for (int j=0 ; j<3 ; j++)
- {
- if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j])
- {
- return NULL;
- }
- }
-
- // make a copy of the brush
- bspbrush_t *newbrush = AllocBrush( nNumSides );
- newbrush->original = mb;
- newbrush->numsides = nNumSides;
- memcpy (newbrush->sides, mb->original_sides, nNumSides*sizeof(side_t));
-
- for (int j=0 ; j<nNumSides; j++)
- {
- if (newbrush->sides[j].winding)
- {
- newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
- }
-
- if (newbrush->sides[j].surf & SURF_HINT)
- {
- newbrush->sides[j].visible = true; // hints are always visible
- }
-
- // keep a pointer to the original map brush side -- use to create the original face later!!
- //newbrush->sides[j].original = &mb->original_sides[j];
- }
-
- VectorCopy (mb->mins, newbrush->mins);
- VectorCopy (mb->maxs, newbrush->maxs);
-
- // carve off anything outside the clip box
- newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
- return newbrush;
-}
-
-
-//-----------------------------------------------------------------------------
-// Creates a clipped brush from a map brush
-//-----------------------------------------------------------------------------
-static void ComputeBoundingPlanes( const Vector& clipmins, const Vector& clipmaxs )
-{
- Vector normal;
- float dist;
- for (int i=0 ; i<2 ; i++)
- {
- VectorClear (normal);
- normal[i] = 1;
- dist = clipmaxs[i];
- maxplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
- dist = clipmins[i];
- minplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// This forces copies of texinfo data for matching sides of a brush
-//-----------------------------------------------------------------------------
-void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_t *pSource )
-{
- for ( int i = 0; i < numDestSides; i++ )
- {
- side_t *pSide = &pDestSides[i];
- plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
-
- // We have to use the *original sides* because MapBSPBrushList could have generated
- // splits when cutting the original brush to the block being processed. This
- // will generate faces that use TEXINFO_NODE, which is definitely *not* what we want.
- // If we end up with faces using TEXINFO_NODE here, the area portal will flood into
- // the entire water volume intersecting the areaportal.
-
- mapbrush_t *pSourceBrush = pSource->original;
- Assert( pSourceBrush );
-
- const side_t *pSourceSide = pSourceBrush->original_sides;
- const side_t *pBestSide = NULL;
- float flBestDot = -1.0f;
- for ( int j = 0; j < pSourceBrush->numsides; ++j, ++pSourceSide )
- {
- if ( pSourceSide->texinfo == TEXINFO_NODE )
- continue;
-
- plane_t *pSourcePlane = &g_MainMap->mapplanes[pSourceSide->planenum];
- float flDot = DotProduct( pPlane->normal, pSourcePlane->normal );
- if ( flDot == 1.0f || pSide->planenum == pSourceSide->planenum )
- {
- pBestSide = pSourceSide;
- break;
- }
- else if ( flDot > flBestDot )
- {
- pBestSide = pSourceSide;
- flBestDot = flDot;
- }
- }
-
- if ( pBestSide )
- {
- pSide->texinfo = pBestSide->texinfo;
- if ( pSide->original )
- {
- pSide->original->texinfo = pSide->texinfo;
- }
- }
- else
- {
- texinfo_t *pTexInfo = &texinfo[pSide->texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- Msg("Found no matching plane for %s\n", TexDataStringTable_GetString( pTexData->nameStringTableID ) );
- }
- }
-}
-
-// This is a hack to allow areaportals to work in water
-// It was done this way for ease of implementation.
-// This searches a brush list to find intersecting areaportals and water
-// If an areaportal is found inside water, then the water contents and
-// texture information is copied over to the areaportal so that the
-// resulting space has the same properties as the water (normal areaportals assume "empty" surroundings)
-void FixupAreaportalWaterBrushes( bspbrush_t *pList )
-{
- for ( bspbrush_t *pAreaportal = pList; pAreaportal; pAreaportal = pAreaportal->next )
- {
- if ( !(pAreaportal->original->contents & CONTENTS_AREAPORTAL) )
- continue;
-
- for ( bspbrush_t *pWater = pList; pWater; pWater = pWater->next )
- {
- // avoid using areaportal/water combo brushes that have already been fixed up
- if ( pWater->original->contents & CONTENTS_AREAPORTAL )
- continue;
-
- if ( !(pWater->original->contents & MASK_SPLITAREAPORTAL) )
- continue;
-
- if ( BrushesDisjoint( pAreaportal, pWater ) )
- continue;
-
- bspbrush_t *pIntersect = IntersectBrush( pAreaportal, pWater );
- if ( !pIntersect )
- continue;
- FreeBrush( pIntersect );
- pAreaportal->original->contents |= pWater->original->contents;
-
- // HACKHACK: Ideally, this should have been done before the bspbrush_t was
- // created from the map brush. But since it hasn't been, retexture the original map
- // brush's sides
- CopyMatchingTexinfos( pAreaportal->sides, pAreaportal->numsides, pWater );
- CopyMatchingTexinfos( pAreaportal->original->original_sides, pAreaportal->original->numsides, pWater );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// MakeBspBrushList
-//-----------------------------------------------------------------------------
-// UNDONE: Put detail brushes in a separate brush array and pass that instead of "onlyDetail" ?
-bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmins, const Vector& clipmaxs, int detailScreen)
-{
- ComputeBoundingPlanes( clipmins, clipmaxs );
-
- bspbrush_t *pBrushList = NULL;
-
- int i;
- for (i=startbrush ; i<endbrush ; i++)
- {
- mapbrush_t *mb = &g_MainMap->mapbrushes[i];
- if ( detailScreen != FULL_DETAIL )
- {
- bool onlyDetail = (detailScreen == ONLY_DETAIL);
- bool detail = (mb->contents & CONTENTS_DETAIL) != 0;
- if ( onlyDetail ^ detail )
- {
- // both of these must have the same value or we're not interested in this brush
- continue;
- }
- }
-
- bspbrush_t *pNewBrush = CreateClippedBrush( mb, clipmins, clipmaxs );
- if ( pNewBrush )
- {
- pNewBrush->next = pBrushList;
- pBrushList = pNewBrush;
- }
- }
-
- return pBrushList;
-}
-
-
-//-----------------------------------------------------------------------------
-// A version which uses a passed-in list of brushes
-//-----------------------------------------------------------------------------
-bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs)
-{
- ComputeBoundingPlanes( clipmins, clipmaxs );
-
- bspbrush_t *pBrushList = NULL;
- for ( int i=0; i < nBrushCount; ++i )
- {
- bspbrush_t *pNewBrush = CreateClippedBrush( pBrushes[i], clipmins, clipmaxs );
- if ( pNewBrush )
- {
- pNewBrush->next = pBrushList;
- pBrushList = pNewBrush;
- }
- }
-
- return pBrushList;
-}
-
-
-/*
-===============
-AddBspBrushListToTail
-===============
-*/
-bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
-{
- bspbrush_t *walk, *next;
-
- for (walk=list ; walk ; walk=next)
- { // add to end of list
- next = walk->next;
- walk->next = NULL;
- tail->next = walk;
- tail = walk;
- }
-
- return tail;
-}
-
-/*
-===========
-CullList
-
-Builds a new list that doesn't hold the given brush
-===========
-*/
-bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
-{
- bspbrush_t *newlist;
- bspbrush_t *next;
-
- newlist = NULL;
-
- for ( ; list ; list = next)
- {
- next = list->next;
- if (list == skip1)
- {
- FreeBrush (list);
- continue;
- }
- list->next = newlist;
- newlist = list;
- }
- return newlist;
-}
-
-
-/*
-==================
-WriteBrushMap
-==================
-*/
-void WriteBrushMap (char *name, bspbrush_t *list)
-{
- FILE *f;
- side_t *s;
- int i;
- winding_t *w;
-
- Msg("writing %s\n", name);
- f = fopen (name, "w");
- if (!f)
- Error ("Can't write %s\b", name);
-
- fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
-
- for ( ; list ; list=list->next )
- {
- fprintf (f, "{\n");
- for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
- {
- w = BaseWindingForPlane (g_MainMap->mapplanes[s->planenum].normal, g_MainMap->mapplanes[s->planenum].dist);
-
- fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
- fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
- fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
-
- fprintf (f, "%s 0 0 0 1 1\n", TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
- FreeWinding (w);
- }
- fprintf (f, "}\n");
- }
- fprintf (f, "}\n");
-
- fclose (f);
-
-}
-
-// UNDONE: This isn't quite working yet
-#if 0
-void WriteBrushVMF(char *name, bspbrush_t *list)
-{
- FILE *f;
- side_t *s;
- int i;
- winding_t *w;
- Vector u, v;
-
- Msg("writing %s\n", name);
- f = fopen (name, "w");
- if (!f)
- Error ("Can't write %s\b", name);
-
- fprintf (f, "world\n{\n\"classname\" \"worldspawn\"\n");
-
- for ( ; list ; list=list->next )
- {
- fprintf (f, "\tsolid\n\t{\n");
- for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
- {
- fprintf( f, "\t\tside\n\t\t{\n" );
- fprintf( f, "\t\t\t\"plane\" \"" );
- w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
-
- fprintf (f,"(%i %i %i) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
- fprintf (f,"(%i %i %i) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
- fprintf (f,"(%i %i %i)", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
- fprintf( f, "\"\n" );
- fprintf( f, "\t\t\t\"material\" \"%s\"\n", GetTexData( texinfo[s->texinfo].texdata )->name );
- // UNDONE: recreate correct texture axes
- BasisForPlane( mapplanes[s->planenum].normal, u, v );
- fprintf( f, "\t\t\t\"uaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", u[0], u[1], u[2] );
- fprintf( f, "\t\t\t\"vaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", v[0], v[1], v[2] );
-
- fprintf( f, "\t\t\t\"rotation\" \"0.0\"\n" );
- fprintf( f, "\t\t\t\"lightmapscale\" \"16.0\"\n" );
-
- FreeWinding (w);
- fprintf (f, "\t\t}\n");
- }
- fprintf (f, "\t}\n");
- }
- fprintf (f, "}\n");
-
- fclose (f);
-
-}
-#endif
-
-void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars )
-{
- #define ADD_CONTENTS( flag ) \
- if ( contents & flag ) \
- Q_strncat( pOut, #flag " ", nMaxChars, COPY_ALL_CHARACTERS );
-
- pOut[0] = 0;
-
- ADD_CONTENTS(CONTENTS_SOLID)
- ADD_CONTENTS(CONTENTS_WINDOW)
- ADD_CONTENTS(CONTENTS_AUX)
- ADD_CONTENTS(CONTENTS_GRATE)
- ADD_CONTENTS(CONTENTS_SLIME)
- ADD_CONTENTS(CONTENTS_WATER)
- ADD_CONTENTS(CONTENTS_BLOCKLOS)
- ADD_CONTENTS(CONTENTS_OPAQUE)
- ADD_CONTENTS(CONTENTS_TESTFOGVOLUME)
- ADD_CONTENTS(CONTENTS_MOVEABLE)
- ADD_CONTENTS(CONTENTS_AREAPORTAL)
- ADD_CONTENTS(CONTENTS_PLAYERCLIP)
- ADD_CONTENTS(CONTENTS_MONSTERCLIP)
- ADD_CONTENTS(CONTENTS_CURRENT_0)
- ADD_CONTENTS(CONTENTS_CURRENT_90)
- ADD_CONTENTS(CONTENTS_CURRENT_180)
- ADD_CONTENTS(CONTENTS_CURRENT_270)
- ADD_CONTENTS(CONTENTS_CURRENT_UP)
- ADD_CONTENTS(CONTENTS_CURRENT_DOWN)
- ADD_CONTENTS(CONTENTS_ORIGIN)
- ADD_CONTENTS(CONTENTS_MONSTER)
- ADD_CONTENTS(CONTENTS_DEBRIS)
- ADD_CONTENTS(CONTENTS_DETAIL)
- ADD_CONTENTS(CONTENTS_TRANSLUCENT)
- ADD_CONTENTS(CONTENTS_LADDER)
- ADD_CONTENTS(CONTENTS_HITBOX)
-}
-
-void PrintBrushContents( int contents )
-{
- char str[1024];
- PrintBrushContentsToString( contents, str, sizeof( str ) );
- Msg( "%s", str );
-}
-
-/*
-==================
-BrushGE
-
-Returns true if b1 is allowed to bite b2
-==================
-*/
-qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
-{
- // Areaportals are allowed to bite water + slime
- // NOTE: This brush combo should have been fixed up
- // in a first pass (FixupAreaportalWaterBrushes)
- if( (b2->original->contents & MASK_SPLITAREAPORTAL) &&
- (b1->original->contents & CONTENTS_AREAPORTAL) )
- {
- return true;
- }
-
- // detail brushes never bite structural brushes
- if ( (b1->original->contents & CONTENTS_DETAIL)
- && !(b2->original->contents & CONTENTS_DETAIL) )
- return false;
- if (b1->original->contents & CONTENTS_SOLID)
- return true;
- // Transparent brushes are not marked as detail anymore, so let them cut each other.
- if ( (b1->original->contents & TRANSPARENT_CONTENTS) && (b2->original->contents & TRANSPARENT_CONTENTS) )
- return true;
-
- return false;
-}
-
-/*
-=================
-ChopBrushes
-
-Carves any intersecting solid brushes into the minimum number
-of non-intersecting brushes.
-=================
-*/
-bspbrush_t *ChopBrushes (bspbrush_t *head)
-{
- bspbrush_t *b1, *b2, *next;
- bspbrush_t *tail;
- bspbrush_t *keep;
- bspbrush_t *sub, *sub2;
- int c1, c2;
-
- qprintf ("---- ChopBrushes ----\n");
- qprintf ("original brushes: %i\n", CountBrushList (head));
-
-#if DEBUG_BRUSHMODEL
- if (entity_num == DEBUG_BRUSHMODEL)
- WriteBrushList ("before.gl", head, false);
-#endif
- keep = NULL;
-
-newlist:
- // find tail
- if (!head)
- return NULL;
- for (tail=head ; tail->next ; tail=tail->next)
- ;
-
- for (b1=head ; b1 ; b1=next)
- {
- next = b1->next;
- for (b2=b1->next ; b2 ; b2 = b2->next)
- {
- if (BrushesDisjoint (b1, b2))
- continue;
-
- sub = NULL;
- sub2 = NULL;
- c1 = 999999;
- c2 = 999999;
-
- if ( BrushGE (b2, b1) )
- {
-// printf( "b2 bites b1\n" );
- sub = SubtractBrush (b1, b2);
- if (sub == b1)
- continue; // didn't really intersect
- if (!sub)
- { // b1 is swallowed by b2
- head = CullList (b1, b1);
- goto newlist;
- }
- c1 = CountBrushList (sub);
- }
-
- if ( BrushGE (b1, b2) )
- {
-// printf( "b1 bites b2\n" );
- sub2 = SubtractBrush (b2, b1);
- if (sub2 == b2)
- continue; // didn't really intersect
- if (!sub2)
- { // b2 is swallowed by b1
- FreeBrushList (sub);
- head = CullList (b1, b2);
- goto newlist;
- }
- c2 = CountBrushList (sub2);
- }
-
- if (!sub && !sub2)
- continue; // neither one can bite
-
- // only accept if it didn't fragment
- // (commening this out allows full fragmentation)
- if (c1 > 1 && c2 > 1)
- {
- const int contents1 = b1->original->contents;
- const int contents2 = b2->original->contents;
- // if both detail, allow fragmentation
- if ( !((contents1&contents2) & CONTENTS_DETAIL) && !((contents1|contents2) & CONTENTS_AREAPORTAL) )
- {
- if (sub2)
- FreeBrushList (sub2);
- if (sub)
- FreeBrushList (sub);
- continue;
- }
- }
-
- if (c1 < c2)
- {
- if (sub2)
- FreeBrushList (sub2);
- tail = AddBrushListToTail (sub, tail);
- head = CullList (b1, b1);
- goto newlist;
- }
- else
- {
- if (sub)
- FreeBrushList (sub);
- tail = AddBrushListToTail (sub2, tail);
- head = CullList (b1, b2);
- goto newlist;
- }
- }
-
- if (!b2)
- { // b1 is no longer intersecting anything, so keep it
- b1->next = keep;
- keep = b1;
- }
- }
-
- qprintf ("output brushes: %i\n", CountBrushList (keep));
-#if DEBUG_BRUSHMODEL
- if ( entity_num == DEBUG_BRUSHMODEL )
- {
- WriteBrushList ("after.gl", keep, false);
- WriteBrushMap ("after.map", keep);
- }
-#endif
- return keep;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+/*
+
+tag all brushes with original contents
+brushes may contain multiple contents
+there will be no brush overlap after csg phase
+
+
+
+
+each side has a count of the other sides it splits
+
+the best split will be the one that minimizes the total split counts
+of all remaining sides
+
+precalc side on plane table
+
+evaluate split side
+{
+cost = 0
+for all sides
+ for all sides
+ get
+ if side splits side and splitside is on same child
+ cost++;
+}
+
+
+ */
+
+void SplitBrush2( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
+{
+ SplitBrush( brush, planenum, front, back );
+#if 0
+ if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
+ (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
+ if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
+ (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
+#endif
+}
+
+/*
+===============
+SubtractBrush
+
+Returns a list of brushes that remain after B is subtracted from A.
+May by empty if A is contained inside B.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
+{ // a - b = out (list)
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *out, *in;
+
+ in = a;
+ out = NULL;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ { // add to list
+ front->next = out;
+ out = front;
+ }
+ in = back;
+ }
+ if (in)
+ FreeBrush (in);
+ else
+ { // didn't really intersect
+ FreeBrushList (out);
+ return a;
+ }
+ return out;
+}
+
+/*
+===============
+IntersectBrush
+
+Returns a single brush made up by the intersection of the
+two provided brushes, or NULL if they are disjoint.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
+{
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *in;
+
+ in = a;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ FreeBrush (front);
+ in = back;
+ }
+
+ if (in == a || !in)
+ return NULL;
+
+ in->next = NULL;
+ return in;
+}
+
+
+/*
+===============
+BrushesDisjoint
+
+Returns true if the two brushes definately do not intersect.
+There will be false negatives for some non-axial combinations.
+===============
+*/
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
+{
+ int i, j;
+
+ // check bounding boxes
+ for (i=0 ; i<3 ; i++)
+ if (a->mins[i] >= b->maxs[i]
+ || a->maxs[i] <= b->mins[i])
+ return true; // bounding boxes don't overlap
+
+ // check for opposing planes
+ for (i=0 ; i<a->numsides ; i++)
+ {
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (a->sides[i].planenum ==
+ (b->sides[j].planenum^1) )
+ return true; // opposite planes, so not touching
+ }
+ }
+
+ return false; // might intersect
+}
+
+
+int minplanenums[3];
+int maxplanenums[3];
+
+/*
+===============
+ClipBrushToBox
+
+Any planes shared with the box edge will be set to no texinfo
+===============
+*/
+bspbrush_t *ClipBrushToBox (bspbrush_t *brush, const Vector& clipmins, const Vector& clipmaxs)
+{
+ int i, j;
+ bspbrush_t *front, *back;
+ int p;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ if (brush->maxs[j] > clipmaxs[j])
+ {
+ SplitBrush (brush, maxplanenums[j], &front, &back);
+ if (front)
+ FreeBrush (front);
+ brush = back;
+ if (!brush)
+ return NULL;
+ }
+ if (brush->mins[j] < clipmins[j])
+ {
+ SplitBrush (brush, minplanenums[j], &front, &back);
+ if (back)
+ FreeBrush (back);
+ brush = front;
+ if (!brush)
+ return NULL;
+ }
+ }
+
+ // remove any colinear faces
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ p = brush->sides[i].planenum & ~1;
+ if (p == maxplanenums[0] || p == maxplanenums[1]
+ || p == minplanenums[0] || p == minplanenums[1])
+ {
+ brush->sides[i].texinfo = TEXINFO_NODE;
+ brush->sides[i].visible = false;
+ }
+ }
+ return brush;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a clipped brush from a map brush
+//-----------------------------------------------------------------------------
+static bspbrush_t *CreateClippedBrush( mapbrush_t *mb, const Vector& clipmins, const Vector& clipmaxs )
+{
+ int nNumSides = mb->numsides;
+ if (!nNumSides)
+ return NULL;
+
+ // if the brush is outside the clip area, skip it
+ for (int j=0 ; j<3 ; j++)
+ {
+ if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j])
+ {
+ return NULL;
+ }
+ }
+
+ // make a copy of the brush
+ bspbrush_t *newbrush = AllocBrush( nNumSides );
+ newbrush->original = mb;
+ newbrush->numsides = nNumSides;
+ memcpy (newbrush->sides, mb->original_sides, nNumSides*sizeof(side_t));
+
+ for (int j=0 ; j<nNumSides; j++)
+ {
+ if (newbrush->sides[j].winding)
+ {
+ newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
+ }
+
+ if (newbrush->sides[j].surf & SURF_HINT)
+ {
+ newbrush->sides[j].visible = true; // hints are always visible
+ }
+
+ // keep a pointer to the original map brush side -- use to create the original face later!!
+ //newbrush->sides[j].original = &mb->original_sides[j];
+ }
+
+ VectorCopy (mb->mins, newbrush->mins);
+ VectorCopy (mb->maxs, newbrush->maxs);
+
+ // carve off anything outside the clip box
+ newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
+ return newbrush;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a clipped brush from a map brush
+//-----------------------------------------------------------------------------
+static void ComputeBoundingPlanes( const Vector& clipmins, const Vector& clipmaxs )
+{
+ Vector normal;
+ float dist;
+ for (int i=0 ; i<2 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = clipmaxs[i];
+ maxplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
+ dist = clipmins[i];
+ minplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This forces copies of texinfo data for matching sides of a brush
+//-----------------------------------------------------------------------------
+void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_t *pSource )
+{
+ for ( int i = 0; i < numDestSides; i++ )
+ {
+ side_t *pSide = &pDestSides[i];
+ plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
+
+ // We have to use the *original sides* because MapBSPBrushList could have generated
+ // splits when cutting the original brush to the block being processed. This
+ // will generate faces that use TEXINFO_NODE, which is definitely *not* what we want.
+ // If we end up with faces using TEXINFO_NODE here, the area portal will flood into
+ // the entire water volume intersecting the areaportal.
+
+ mapbrush_t *pSourceBrush = pSource->original;
+ Assert( pSourceBrush );
+
+ const side_t *pSourceSide = pSourceBrush->original_sides;
+ const side_t *pBestSide = NULL;
+ float flBestDot = -1.0f;
+ for ( int j = 0; j < pSourceBrush->numsides; ++j, ++pSourceSide )
+ {
+ if ( pSourceSide->texinfo == TEXINFO_NODE )
+ continue;
+
+ plane_t *pSourcePlane = &g_MainMap->mapplanes[pSourceSide->planenum];
+ float flDot = DotProduct( pPlane->normal, pSourcePlane->normal );
+ if ( flDot == 1.0f || pSide->planenum == pSourceSide->planenum )
+ {
+ pBestSide = pSourceSide;
+ break;
+ }
+ else if ( flDot > flBestDot )
+ {
+ pBestSide = pSourceSide;
+ flBestDot = flDot;
+ }
+ }
+
+ if ( pBestSide )
+ {
+ pSide->texinfo = pBestSide->texinfo;
+ if ( pSide->original )
+ {
+ pSide->original->texinfo = pSide->texinfo;
+ }
+ }
+ else
+ {
+ texinfo_t *pTexInfo = &texinfo[pSide->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ Msg("Found no matching plane for %s\n", TexDataStringTable_GetString( pTexData->nameStringTableID ) );
+ }
+ }
+}
+
+// This is a hack to allow areaportals to work in water
+// It was done this way for ease of implementation.
+// This searches a brush list to find intersecting areaportals and water
+// If an areaportal is found inside water, then the water contents and
+// texture information is copied over to the areaportal so that the
+// resulting space has the same properties as the water (normal areaportals assume "empty" surroundings)
+void FixupAreaportalWaterBrushes( bspbrush_t *pList )
+{
+ for ( bspbrush_t *pAreaportal = pList; pAreaportal; pAreaportal = pAreaportal->next )
+ {
+ if ( !(pAreaportal->original->contents & CONTENTS_AREAPORTAL) )
+ continue;
+
+ for ( bspbrush_t *pWater = pList; pWater; pWater = pWater->next )
+ {
+ // avoid using areaportal/water combo brushes that have already been fixed up
+ if ( pWater->original->contents & CONTENTS_AREAPORTAL )
+ continue;
+
+ if ( !(pWater->original->contents & MASK_SPLITAREAPORTAL) )
+ continue;
+
+ if ( BrushesDisjoint( pAreaportal, pWater ) )
+ continue;
+
+ bspbrush_t *pIntersect = IntersectBrush( pAreaportal, pWater );
+ if ( !pIntersect )
+ continue;
+ FreeBrush( pIntersect );
+ pAreaportal->original->contents |= pWater->original->contents;
+
+ // HACKHACK: Ideally, this should have been done before the bspbrush_t was
+ // created from the map brush. But since it hasn't been, retexture the original map
+ // brush's sides
+ CopyMatchingTexinfos( pAreaportal->sides, pAreaportal->numsides, pWater );
+ CopyMatchingTexinfos( pAreaportal->original->original_sides, pAreaportal->original->numsides, pWater );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// MakeBspBrushList
+//-----------------------------------------------------------------------------
+// UNDONE: Put detail brushes in a separate brush array and pass that instead of "onlyDetail" ?
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmins, const Vector& clipmaxs, int detailScreen)
+{
+ ComputeBoundingPlanes( clipmins, clipmaxs );
+
+ bspbrush_t *pBrushList = NULL;
+
+ int i;
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mapbrush_t *mb = &g_MainMap->mapbrushes[i];
+ if ( detailScreen != FULL_DETAIL )
+ {
+ bool onlyDetail = (detailScreen == ONLY_DETAIL);
+ bool detail = (mb->contents & CONTENTS_DETAIL) != 0;
+ if ( onlyDetail ^ detail )
+ {
+ // both of these must have the same value or we're not interested in this brush
+ continue;
+ }
+ }
+
+ bspbrush_t *pNewBrush = CreateClippedBrush( mb, clipmins, clipmaxs );
+ if ( pNewBrush )
+ {
+ pNewBrush->next = pBrushList;
+ pBrushList = pNewBrush;
+ }
+ }
+
+ return pBrushList;
+}
+
+
+//-----------------------------------------------------------------------------
+// A version which uses a passed-in list of brushes
+//-----------------------------------------------------------------------------
+bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs)
+{
+ ComputeBoundingPlanes( clipmins, clipmaxs );
+
+ bspbrush_t *pBrushList = NULL;
+ for ( int i=0; i < nBrushCount; ++i )
+ {
+ bspbrush_t *pNewBrush = CreateClippedBrush( pBrushes[i], clipmins, clipmaxs );
+ if ( pNewBrush )
+ {
+ pNewBrush->next = pBrushList;
+ pBrushList = pNewBrush;
+ }
+ }
+
+ return pBrushList;
+}
+
+
+/*
+===============
+AddBspBrushListToTail
+===============
+*/
+bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
+{
+ bspbrush_t *walk, *next;
+
+ for (walk=list ; walk ; walk=next)
+ { // add to end of list
+ next = walk->next;
+ walk->next = NULL;
+ tail->next = walk;
+ tail = walk;
+ }
+
+ return tail;
+}
+
+/*
+===========
+CullList
+
+Builds a new list that doesn't hold the given brush
+===========
+*/
+bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
+{
+ bspbrush_t *newlist;
+ bspbrush_t *next;
+
+ newlist = NULL;
+
+ for ( ; list ; list = next)
+ {
+ next = list->next;
+ if (list == skip1)
+ {
+ FreeBrush (list);
+ continue;
+ }
+ list->next = newlist;
+ newlist = list;
+ }
+ return newlist;
+}
+
+
+/*
+==================
+WriteBrushMap
+==================
+*/
+void WriteBrushMap (char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+
+ Msg("writing %s\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ w = BaseWindingForPlane (g_MainMap->mapplanes[s->planenum].normal, g_MainMap->mapplanes[s->planenum].dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "%s 0 0 0 1 1\n", TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+
+// UNDONE: This isn't quite working yet
+#if 0
+void WriteBrushVMF(char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+ Vector u, v;
+
+ Msg("writing %s\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "world\n{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "\tsolid\n\t{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ fprintf( f, "\t\tside\n\t\t{\n" );
+ fprintf( f, "\t\t\t\"plane\" \"" );
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
+
+ fprintf (f,"(%i %i %i) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"(%i %i %i) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"(%i %i %i)", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+ fprintf( f, "\"\n" );
+ fprintf( f, "\t\t\t\"material\" \"%s\"\n", GetTexData( texinfo[s->texinfo].texdata )->name );
+ // UNDONE: recreate correct texture axes
+ BasisForPlane( mapplanes[s->planenum].normal, u, v );
+ fprintf( f, "\t\t\t\"uaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", u[0], u[1], u[2] );
+ fprintf( f, "\t\t\t\"vaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", v[0], v[1], v[2] );
+
+ fprintf( f, "\t\t\t\"rotation\" \"0.0\"\n" );
+ fprintf( f, "\t\t\t\"lightmapscale\" \"16.0\"\n" );
+
+ FreeWinding (w);
+ fprintf (f, "\t\t}\n");
+ }
+ fprintf (f, "\t}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+#endif
+
+void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars )
+{
+ #define ADD_CONTENTS( flag ) \
+ if ( contents & flag ) \
+ Q_strncat( pOut, #flag " ", nMaxChars, COPY_ALL_CHARACTERS );
+
+ pOut[0] = 0;
+
+ ADD_CONTENTS(CONTENTS_SOLID)
+ ADD_CONTENTS(CONTENTS_WINDOW)
+ ADD_CONTENTS(CONTENTS_AUX)
+ ADD_CONTENTS(CONTENTS_GRATE)
+ ADD_CONTENTS(CONTENTS_SLIME)
+ ADD_CONTENTS(CONTENTS_WATER)
+ ADD_CONTENTS(CONTENTS_BLOCKLOS)
+ ADD_CONTENTS(CONTENTS_OPAQUE)
+ ADD_CONTENTS(CONTENTS_TESTFOGVOLUME)
+ ADD_CONTENTS(CONTENTS_MOVEABLE)
+ ADD_CONTENTS(CONTENTS_AREAPORTAL)
+ ADD_CONTENTS(CONTENTS_PLAYERCLIP)
+ ADD_CONTENTS(CONTENTS_MONSTERCLIP)
+ ADD_CONTENTS(CONTENTS_CURRENT_0)
+ ADD_CONTENTS(CONTENTS_CURRENT_90)
+ ADD_CONTENTS(CONTENTS_CURRENT_180)
+ ADD_CONTENTS(CONTENTS_CURRENT_270)
+ ADD_CONTENTS(CONTENTS_CURRENT_UP)
+ ADD_CONTENTS(CONTENTS_CURRENT_DOWN)
+ ADD_CONTENTS(CONTENTS_ORIGIN)
+ ADD_CONTENTS(CONTENTS_MONSTER)
+ ADD_CONTENTS(CONTENTS_DEBRIS)
+ ADD_CONTENTS(CONTENTS_DETAIL)
+ ADD_CONTENTS(CONTENTS_TRANSLUCENT)
+ ADD_CONTENTS(CONTENTS_LADDER)
+ ADD_CONTENTS(CONTENTS_HITBOX)
+}
+
+void PrintBrushContents( int contents )
+{
+ char str[1024];
+ PrintBrushContentsToString( contents, str, sizeof( str ) );
+ Msg( "%s", str );
+}
+
+/*
+==================
+BrushGE
+
+Returns true if b1 is allowed to bite b2
+==================
+*/
+qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
+{
+ // Areaportals are allowed to bite water + slime
+ // NOTE: This brush combo should have been fixed up
+ // in a first pass (FixupAreaportalWaterBrushes)
+ if( (b2->original->contents & MASK_SPLITAREAPORTAL) &&
+ (b1->original->contents & CONTENTS_AREAPORTAL) )
+ {
+ return true;
+ }
+
+ // detail brushes never bite structural brushes
+ if ( (b1->original->contents & CONTENTS_DETAIL)
+ && !(b2->original->contents & CONTENTS_DETAIL) )
+ return false;
+ if (b1->original->contents & CONTENTS_SOLID)
+ return true;
+ // Transparent brushes are not marked as detail anymore, so let them cut each other.
+ if ( (b1->original->contents & TRANSPARENT_CONTENTS) && (b2->original->contents & TRANSPARENT_CONTENTS) )
+ return true;
+
+ return false;
+}
+
+/*
+=================
+ChopBrushes
+
+Carves any intersecting solid brushes into the minimum number
+of non-intersecting brushes.
+=================
+*/
+bspbrush_t *ChopBrushes (bspbrush_t *head)
+{
+ bspbrush_t *b1, *b2, *next;
+ bspbrush_t *tail;
+ bspbrush_t *keep;
+ bspbrush_t *sub, *sub2;
+ int c1, c2;
+
+ qprintf ("---- ChopBrushes ----\n");
+ qprintf ("original brushes: %i\n", CountBrushList (head));
+
+#if DEBUG_BRUSHMODEL
+ if (entity_num == DEBUG_BRUSHMODEL)
+ WriteBrushList ("before.gl", head, false);
+#endif
+ keep = NULL;
+
+newlist:
+ // find tail
+ if (!head)
+ return NULL;
+ for (tail=head ; tail->next ; tail=tail->next)
+ ;
+
+ for (b1=head ; b1 ; b1=next)
+ {
+ next = b1->next;
+ for (b2=b1->next ; b2 ; b2 = b2->next)
+ {
+ if (BrushesDisjoint (b1, b2))
+ continue;
+
+ sub = NULL;
+ sub2 = NULL;
+ c1 = 999999;
+ c2 = 999999;
+
+ if ( BrushGE (b2, b1) )
+ {
+// printf( "b2 bites b1\n" );
+ sub = SubtractBrush (b1, b2);
+ if (sub == b1)
+ continue; // didn't really intersect
+ if (!sub)
+ { // b1 is swallowed by b2
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ c1 = CountBrushList (sub);
+ }
+
+ if ( BrushGE (b1, b2) )
+ {
+// printf( "b1 bites b2\n" );
+ sub2 = SubtractBrush (b2, b1);
+ if (sub2 == b2)
+ continue; // didn't really intersect
+ if (!sub2)
+ { // b2 is swallowed by b1
+ FreeBrushList (sub);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ c2 = CountBrushList (sub2);
+ }
+
+ if (!sub && !sub2)
+ continue; // neither one can bite
+
+ // only accept if it didn't fragment
+ // (commening this out allows full fragmentation)
+ if (c1 > 1 && c2 > 1)
+ {
+ const int contents1 = b1->original->contents;
+ const int contents2 = b2->original->contents;
+ // if both detail, allow fragmentation
+ if ( !((contents1&contents2) & CONTENTS_DETAIL) && !((contents1|contents2) & CONTENTS_AREAPORTAL) )
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ if (sub)
+ FreeBrushList (sub);
+ continue;
+ }
+ }
+
+ if (c1 < c2)
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ tail = AddBrushListToTail (sub, tail);
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ else
+ {
+ if (sub)
+ FreeBrushList (sub);
+ tail = AddBrushListToTail (sub2, tail);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ }
+
+ if (!b2)
+ { // b1 is no longer intersecting anything, so keep it
+ b1->next = keep;
+ keep = b1;
+ }
+ }
+
+ qprintf ("output brushes: %i\n", CountBrushList (keep));
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ {
+ WriteBrushList ("after.gl", keep, false);
+ WriteBrushMap ("after.map", keep);
+ }
+#endif
+ return keep;
+}
+
+
diff --git a/mp/src/utils/vbsp/csg.h b/mp/src/utils/vbsp/csg.h
index 30436dd9..158dcf43 100644
--- a/mp/src/utils/vbsp/csg.h
+++ b/mp/src/utils/vbsp/csg.h
@@ -1,32 +1,32 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef CSG_H
-#define CSG_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-// Print a CONTENTS_ mask to a string.
-void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars );
-
-// Print a CONTENTS_ mask with Msg().
-void PrintBrushContents( int contents );
-
-void FixupAreaportalWaterBrushes( bspbrush_t *pList );
-
-bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
- const Vector& clipmins, const Vector& clipmaxs, int detailScreen);
-bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs);
-
-void WriteBrushMap (char *name, bspbrush_t *list);
-
-bspbrush_t *ChopBrushes (bspbrush_t *head);
-bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b);
-qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b);
-
-#endif // CSG_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CSG_H
+#define CSG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Print a CONTENTS_ mask to a string.
+void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars );
+
+// Print a CONTENTS_ mask with Msg().
+void PrintBrushContents( int contents );
+
+void FixupAreaportalWaterBrushes( bspbrush_t *pList );
+
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+ const Vector& clipmins, const Vector& clipmaxs, int detailScreen);
+bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs);
+
+void WriteBrushMap (char *name, bspbrush_t *list);
+
+bspbrush_t *ChopBrushes (bspbrush_t *head);
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b);
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b);
+
+#endif // CSG_H
diff --git a/mp/src/utils/vbsp/cubemap.cpp b/mp/src/utils/vbsp/cubemap.cpp
index 2415115b..aeff12f1 100644
--- a/mp/src/utils/vbsp/cubemap.cpp
+++ b/mp/src/utils/vbsp/cubemap.cpp
@@ -1,996 +1,996 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "bsplib.h"
-#include "tier1/UtlBuffer.h"
-#include "tier1/utlvector.h"
-#include "bitmap/imageformat.h"
-#include <KeyValues.h>
-#include "tier1/strtools.h"
-#include "tier1/utlsymbol.h"
-#include "vtf/vtf.h"
-#include "materialpatch.h"
-#include "materialsystem/imaterialsystem.h"
-#include "materialsystem/imaterial.h"
-#include "materialsystem/imaterialvar.h"
-
-
-/*
- Meager documentation for how the cubemaps are assigned.
-
-
- While loading the map, it calls:
- *** Cubemap_SaveBrushSides
- Builds a list of what cubemaps manually were assigned to what faces
- in s_EnvCubemapToBrushSides.
-
- Immediately after loading the map, it calls:
- *** Cubemap_FixupBrushSidesMaterials
- Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
- side referenced by an env_cubemap manually.
-
- Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
- *** Cubemap_InitCubemapSideData:
- Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
- bHasEnvMapInMaterial is set if the side's material has $envmap.
- bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
-
- Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
- referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
-*/
-
-struct PatchInfo_t
-{
- char *m_pMapName;
- int m_pOrigin[3];
-};
-
-struct CubemapInfo_t
-{
- int m_nTableId;
- bool m_bSpecular;
-};
-
-static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
-{
- return ( lhs.m_nTableId < rhs.m_nTableId );
-}
-
-
-typedef CUtlVector<int> IntVector_t;
-static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
-
-static CUtlVector<char *> s_DefaultCubemapNames;
-static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
-
-
-struct CubemapSideData_t
-{
- bool bHasEnvMapInMaterial;
- bool bManuallyPickedByAnEnvCubemap;
-};
-
-static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
-
-
-
-inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
-{
- return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
-}
-
-
-void Cubemap_InsertSample( const Vector& origin, int size )
-{
- dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
- pSample->origin[0] = ( int )origin[0];
- pSample->origin[1] = ( int )origin[1];
- pSample->origin[2] = ( int )origin[2];
- pSample->size = size;
- g_nCubemapSamples++;
-}
-
-static const char *FindSkyboxMaterialName( void )
-{
- for( int i = 0; i < g_MainMap->num_entities; i++ )
- {
- char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
- if (!strcmp(pEntity, "worldspawn"))
- {
- return ValueForKey( &g_MainMap->entities[i], "skyname" );
- }
- }
- return NULL;
-}
-
-static void BackSlashToForwardSlash( char *pname )
-{
- while ( *pname ) {
- if ( *pname == '\\' )
- *pname = '/';
- pname++;
- }
-}
-
-static void ForwardSlashToBackSlash( char *pname )
-{
- while ( *pname ) {
- if ( *pname == '/' )
- *pname = '\\';
- pname++;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds materials that are used by a particular material
-//-----------------------------------------------------------------------------
-#define MAX_MATERIAL_NAME 512
-
-// This is the list of materialvars which are used in our codebase to look up dependent materials
-static const char *s_pDependentMaterialVar[] =
-{
- "$bottommaterial", // Used by water materials
- "$crackmaterial", // Used by shattered glass materials
- "$fallbackmaterial", // Used by all materials
-
- "", // Always must be last
-};
-
-static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
-{
- // FIXME: This is a terrible way of doing this! It creates a dependency
- // between vbsp and *all* code which reads dependent materials from materialvars
- // At the time of writing this function, that means the engine + studiorender.
- // We need a better way of figuring out how to do this, but for now I'm trying to do
- // the fastest solution possible since it's close to ship
-
- static char pDependentMaterialName[MAX_MATERIAL_NAME];
- for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
- {
- if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
- continue;
-
- if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
- {
- Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
- continue;
- }
-
- // Return the material var that caused the dependency
- if ( ppMaterialVar )
- {
- *ppMaterialVar = s_pDependentMaterialVar[i];
- }
-
-#ifdef _DEBUG
- // FIXME: Note that this code breaks if a material has more than 1 dependent material
- ++i;
- static char pDependentMaterialName2[MAX_MATERIAL_NAME];
- while( s_pDependentMaterialVar[i][0] )
- {
- Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
- ++i;
- }
-#endif
-
- return pDependentMaterialName;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Loads VTF files
-//-----------------------------------------------------------------------------
-static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
- int *pUnionTextureFlags, bool bHDR )
-{
- const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
- int i;
- for( i = 0; i < 6; i++ )
- {
- char srcMaterialName[1024];
- sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
-
- IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
- //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
- IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
- const char *vtfName = pSkyTextureVar->GetStringValue();
- char srcVTFFileName[MAX_PATH];
- Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
-
- CUtlBuffer buf;
- if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
- {
- // Try looking for a compressed HDR texture
- if ( bHDR )
- {
- /* // FIXME: We need a way to uncompress this format!
- bool bHDRCompressed = true;
-
- pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
- vtfName = pSkyTextureVar->GetStringValue();
- Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
-
- if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
- */
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
-
- pSrcVTFTextures[i] = CreateVTFTexture();
- if (!pSrcVTFTextures[i]->Unserialize(buf))
- {
- Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
- return false;
- }
-
- *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
- int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
- int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
-
- // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
- if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
- ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
- ( flagsNoAlpha != flagsFirstNoAlpha ) )
- {
- Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
- return false;
- }
-
- if ( bHDR )
- {
- pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
- pSrcVTFTextures[i]->GenerateMipmaps();
- pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
- }
- }
-
- return true;
-}
-
-void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
-{
- Q_strncpy( pDest, pSrcName, maxLen );
- if( !bHDR )
- {
- return;
- }
- char *pDot = Q_stristr( pDest, ".vtf" );
- if( !pDot )
- {
- return;
- }
- Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
-}
-
-#define DEFAULT_CUBEMAP_SIZE 32
-
-void CreateDefaultCubemaps( bool bHDR )
-{
- memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
-
- // NOTE: This implementation depends on the fact that all VTF files contain
- // all mipmap levels
- const char *pSkyboxBaseName = FindSkyboxMaterialName();
-
- if( !pSkyboxBaseName )
- {
- if( s_DefaultCubemapNames.Count() )
- {
- Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
- }
- return;
- }
-
- char skyboxMaterialName[MAX_PATH];
- Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
-
- IVTFTexture *pSrcVTFTextures[6];
-
- int unionTextureFlags = 0;
- if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
- {
- Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
- return;
- }
- Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
- " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
-
- // Figure out the mip differences between the two textures
- int iMipLevelOffset = 0;
- int tmp = pSrcVTFTextures[0]->Width();
- while( tmp > DEFAULT_CUBEMAP_SIZE )
- {
- iMipLevelOffset++;
- tmp >>= 1;
- }
-
- // Create the destination cubemap
- IVTFTexture *pDstCubemap = CreateVTFTexture();
- pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
- pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
- pSrcVTFTextures[0]->FrameCount() );
-
- // First iterate over all frames
- for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
- {
- // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
- for (int iFace = 0; iFace < 6; ++iFace )
- {
- // Finally, iterate over all mip levels in the *destination*
- for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
- {
- // Copy the bits from the source images into the cube faces
- unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
- unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
- int iSize = pDstCubemap->ComputeMipSize( iMip );
- int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
-
- // !!! FIXME: Set this to black until HDR cubemaps are built properly!
- memset( pDstBits, 0, iSize );
- continue;
-
- if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
- {
- // Force mip level 2 to get the 1x1 face
- unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
- int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
-
- // Replicate 1x1 mip level across entire face
- //memset( pDstBits, 0, iSize );
- for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
- {
- memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
- }
- }
- else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
- {
- if ( iSrcMipSize != iSize )
- {
- Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- // Just copy the mip level
- memcpy( pDstBits, pSrcBits, iSize );
- }
- }
- else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
- {
- int iMipWidth, iMipHeight, iMipDepth;
- pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
- if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
- {
- Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- // Copy row at a time and repeat last row
- memcpy( pDstBits, pSrcBits, iSize/2 );
- //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
- int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
- int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
- if ( nSrcRowSize != nDstRowSize )
- {
- Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
- memset( pDstBits, 0, iSize );
- }
- else
- {
- for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
- {
- memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
- }
- }
- }
- }
- else
- {
- // ERROR! This code only supports square and rectangluar 2x wide
- Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
- memset( pDstBits, 0, iSize );
- return;
- }
- }
- }
- }
-
- ImageFormat originalFormat = pDstCubemap->Format();
- if( !bHDR )
- {
- // Convert the cube to format that we can apply tools to it...
- pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
- }
-
- // Fixup the cubemap facing
- pDstCubemap->FixCubemapFaceOrientation();
-
- // Now that the bits are in place, compute the spheremaps...
- pDstCubemap->GenerateSpheremap();
-
- if( !bHDR )
- {
- // Convert the cubemap to the final format
- pDstCubemap->ConvertImageFormat( originalFormat, false );
- }
-
- // Write the puppy out!
- char dstVTFFileName[1024];
- if( bHDR )
- {
- sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
- }
- else
- {
- sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
- }
-
- CUtlBuffer outputBuf;
- if (!pDstCubemap->Serialize( outputBuf ))
- {
- Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
- return;
- }
-
- IZip *pak = GetPakFile();
-
- // spit out the default one.
- AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
-
- // spit out all of the ones that are attached to world geometry.
- int i;
- for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
- {
- char vtfName[MAX_PATH];
- VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
- if( FileExistsInPak( pak, vtfName ) )
- {
- continue;
- }
- AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
- }
-
- // Clean up the textures
- for( i = 0; i < 6; i++ )
- {
- DestroyVTFTexture( pSrcVTFTextures[i] );
- }
- DestroyVTFTexture( pDstCubemap );
-}
-
-void Cubemap_CreateDefaultCubemaps( void )
-{
- CreateDefaultCubemaps( false );
- CreateDefaultCubemaps( true );
-}
-
-// Builds a list of what cubemaps manually were assigned to what faces
-// in s_EnvCubemapToBrushSides.
-void Cubemap_SaveBrushSides( const char *pSideListStr )
-{
- IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
- char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
- strcpy( pTmp, pSideListStr );
- const char *pScan = strtok( pTmp, " " );
- if( !pScan )
- {
- return;
- }
- do
- {
- int brushSideID;
- if( sscanf( pScan, "%d", &brushSideID ) == 1 )
- {
- brushSidesVector.AddToTail( brushSideID );
- }
- } while( ( pScan = strtok( NULL, " " ) ) );
-}
-
-
-//-----------------------------------------------------------------------------
-// Generate patched material name
-//-----------------------------------------------------------------------------
-static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
-{
- const char *pSeparator = bMaterialName ? "_" : "";
- int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
- pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
-
- if ( bMaterialName )
- {
- Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
- if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
- {
- Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
- }
- }
-
- BackSlashToForwardSlash( pBuffer );
- Q_strlower( pBuffer );
-}
-
-
-//-----------------------------------------------------------------------------
-// Patches the $envmap for a material and all its dependents, returns true if any patching happened
-//-----------------------------------------------------------------------------
-static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
-{
- // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
-
- // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
- // because we're using the 'replace' block, which will only add the env_cubemap if
- // $envmap is specified in the source material. But it will fail if someone adds
- // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
-
- // See if we have an $envmap to patch
- bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
-
- // See if we have a dependent material to patch
- bool bDependentMaterialPatched = false;
- const char *pDependentMaterialVar = NULL;
- const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
- if ( pDependentMaterial )
- {
- bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
- }
-
- // If we have neither to patch, we're done
- if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
- return false;
-
- // Otherwise we have to make a patched version of ourselves
- char pPatchedMaterialName[1024];
- GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
-
- MaterialPatchInfo_t pPatchInfo[2];
- int nPatchCount = 0;
- if ( bShouldPatchEnvCubemap )
- {
- pPatchInfo[nPatchCount].m_pKey = "$envmap";
- pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
- pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
- ++nPatchCount;
- }
-
- char pDependentPatchedMaterialName[1024];
- if ( bDependentMaterialPatched )
- {
- // FIXME: Annoying! I either have to pass back the patched dependent material name
- // or reconstruct it. Both are sucky.
- GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
- pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
- pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
- ++nPatchCount;
- }
-
- CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds a texinfo that has a particular
-//-----------------------------------------------------------------------------
-
-
-//-----------------------------------------------------------------------------
-// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
-// Returns the index of the new (or preexisting) texinfo referencing that VMT.
-//
-// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
-// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
-// runtime before they run buildcubemaps.
-//-----------------------------------------------------------------------------
-static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
-{
- // Don't make cubemap tex infos for nodes
- if ( originalTexInfo == TEXINFO_NODE )
- return originalTexInfo;
-
- texinfo_t *pTexInfo = &texinfo[originalTexInfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- if ( g_IsCubemapTexData[pTexInfo->texdata] )
- {
- Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
- return originalTexInfo;
- }
-
- // Get out of here if the originalTexInfo is already a generated material for this position.
- char pStringToSearchFor[512];
- Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
- if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
- return originalTexInfo;
-
- // Package up information needed to generate patch names
- PatchInfo_t info;
- info.m_pMapName = mapbase;
- info.m_pOrigin[0] = origin[0];
- info.m_pOrigin[1] = origin[1];
- info.m_pOrigin[2] = origin[2];
-
- // Generate the name of the patched material
- char pGeneratedTexDataName[1024];
- GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
-
- // Make sure the texdata doesn't already exist.
- int nTexDataID = FindTexData( pGeneratedTexDataName );
- bool bHasTexData = (nTexDataID != -1);
- if( !bHasTexData )
- {
- // Generate the new "$envmap" texture name.
- char pTextureName[1024];
- GeneratePatchedName( "c", info, false, pTextureName, 1024 );
-
- // Hook the texture into the material and all dependent materials
- // but if no hooking was necessary, exit out
- if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
- return originalTexInfo;
-
- // Store off the name of the cubemap that we need to create since we successfully patched
- char pFileName[1024];
- int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
- int id = s_DefaultCubemapNames.AddToTail();
- s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
- strcpy( s_DefaultCubemapNames[id], pFileName );
-
- // Make a new texdata
- nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
- g_IsCubemapTexData[nTexDataID] = true;
- }
-
- Assert( nTexDataID != -1 );
-
- texinfo_t newTexInfo;
- newTexInfo = *pTexInfo;
- newTexInfo.texdata = nTexDataID;
-
- int nTexInfoID = -1;
-
- // See if we need to make a new texinfo
- bool bHasTexInfo = false;
- if( bHasTexData )
- {
- nTexInfoID = FindTexInfo( newTexInfo );
- bHasTexInfo = (nTexInfoID != -1);
- }
-
- // Make a new texinfo if we need to.
- if( !bHasTexInfo )
- {
- nTexInfoID = texinfo.AddToTail( newTexInfo );
- }
-
- Assert( nTexInfoID != -1 );
- return nTexInfoID;
-}
-
-static int SideIDToIndex( int brushSideID )
-{
- int i;
- for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
- {
- if( g_MainMap->brushsides[i].id == brushSideID )
- {
- return i;
- }
- }
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
-// side referenced by an env_cubemap manually.
-//-----------------------------------------------------------------------------
-void Cubemap_FixupBrushSidesMaterials( void )
-{
- Msg( "fixing up env_cubemap materials on brush sides...\n" );
- Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
-
- int cubemapID;
- for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
- {
- IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
- int i;
- for( i = 0; i < brushSidesVector.Count(); i++ )
- {
- int brushSideID = brushSidesVector[i];
- int sideIndex = SideIDToIndex( brushSideID );
- if( sideIndex < 0 )
- {
- Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
- g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
-
- continue;
- }
-
- side_t *pSide = &g_MainMap->brushsides[sideIndex];
-
-#ifdef DEBUG
- if ( pSide->pMapDisp )
- {
- Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
- }
-#endif
-
- pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
- if ( pSide->pMapDisp )
- {
- pSide->pMapDisp->face.texinfo = pSide->texinfo;
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void Cubemap_ResetCubemapSideData( void )
-{
- for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
- {
- s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
- s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Returns true if the material or any of its dependents use an $envmap
-//-----------------------------------------------------------------------------
-bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
-{
- const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
- if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
- return true;
-
- const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
- if ( !pDependentMaterial )
- return false;
-
- return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
-}
-
-
-//-----------------------------------------------------------------------------
-// Builds a list of all texdatas which need fixing up
-//-----------------------------------------------------------------------------
-void Cubemap_InitCubemapSideData( void )
-{
- // This tree is used to prevent re-parsing material vars multiple times
- CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
-
- // Fill in specular data.
- for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
- {
- side_t *pSide = &g_MainMap->brushsides[iSide];
- if ( !pSide )
- continue;
-
- if ( pSide->texinfo == TEXINFO_NODE )
- continue;
-
- texinfo_t *pTex = &texinfo[pSide->texinfo];
- if ( !pTex )
- continue;
-
- dtexdata_t *pTexData = GetTexData( pTex->texdata );
- if ( !pTexData )
- continue;
-
- CubemapInfo_t info;
- info.m_nTableId = pTexData->nameStringTableID;
-
- // Have we encountered this materal? If so, then copy the data we cached off before
- int i = lookup.Find( info );
- if ( i != lookup.InvalidIndex() )
- {
- s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
- continue;
- }
-
- // First time we've seen this material. Figure out if it uses env_cubemap
- const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
- s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
- lookup.Insert( info );
- }
-
- // Fill in cube map data.
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
- int nSideCount = sideList.Count();
- for ( int iSide = 0; iSide < nSideCount; ++iSide )
- {
- int nSideID = sideList[iSide];
- int nIndex = SideIDToIndex( nSideID );
- if ( nIndex < 0 )
- continue;
-
- s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
-{
- if ( !pSide )
- return -1;
-
- // Return a valid (if random) cubemap if there's no winding
- if ( !pSide->winding )
- return 0;
-
- // Calculate the center point.
- Vector vecCenter;
- vecCenter.Init();
-
- for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
- {
- VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
- }
- VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
- vecCenter += entityOrigin;
- plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
-
- // Find the closest cubemap.
- int iMinCubemap = -1;
- float flMinDist = FLT_MAX;
-
- // Look for cubemaps in front of the surface first.
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
- Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
- static_cast<float>( pSample->origin[1] ),
- static_cast<float>( pSample->origin[2] ) );
- Vector vecDelta;
- VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
- float flDist = vecDelta.NormalizeInPlace();
- float flDot = DotProduct( vecDelta, pPlane->normal );
- if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
- {
- flMinDist = flDist;
- iMinCubemap = iCubemap;
- }
- }
-
- // Didn't find anything in front search for closest.
- if( iMinCubemap == -1 )
- {
- for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
- {
- dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
- Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
- static_cast<float>( pSample->origin[1] ),
- static_cast<float>( pSample->origin[2] ) );
- Vector vecDelta;
- VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
- float flDist = vecDelta.Length();
- if ( flDist < flMinDist )
- {
- flMinDist = flDist;
- iMinCubemap = iCubemap;
- }
- }
- }
-
- return iMinCubemap;
-}
-
-
-//-----------------------------------------------------------------------------
-// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
-//-----------------------------------------------------------------------------
-void Cubemap_AttachDefaultCubemapToSpecularSides( void )
-{
- Cubemap_ResetCubemapSideData();
- Cubemap_InitCubemapSideData();
-
- // build a mapping from side to entity id so that we can get the entity origin
- CUtlVector<int> sideToEntityIndex;
- sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
- int i;
- for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
- {
- sideToEntityIndex[i] = -1;
- }
-
- for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
- {
- int entityIndex = g_MainMap->mapbrushes[i].entitynum;
- for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
- {
- side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
- int sideIndex = side - g_MainMap->brushsides;
- sideToEntityIndex[sideIndex] = entityIndex;
- }
- }
-
- for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
- {
- side_t *pSide = &g_MainMap->brushsides[iSide];
- if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
- continue;
-
-
- int currentEntity = sideToEntityIndex[iSide];
-
- int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
- if ( iCubemap == -1 )
- continue;
-
-#ifdef DEBUG
- if ( pSide->pMapDisp )
- {
- Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
- }
-#endif
- pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
- if ( pSide->pMapDisp )
- {
- pSide->pMapDisp->face.texinfo = pSide->texinfo;
- }
- }
-}
-
-// Populate with cubemaps that were skipped
-void Cubemap_AddUnreferencedCubemaps()
-{
- char pTextureName[1024];
- char pFileName[1024];
- PatchInfo_t info;
- dcubemapsample_t *pSample;
- int i,j;
-
- for ( i=0; i<g_nCubemapSamples; ++i )
- {
- pSample = &g_CubemapSamples[i];
-
- // generate the formatted texture name based on cubemap origin
- info.m_pMapName = mapbase;
- info.m_pOrigin[0] = pSample->origin[0];
- info.m_pOrigin[1] = pSample->origin[1];
- info.m_pOrigin[2] = pSample->origin[2];
- GeneratePatchedName( "c", info, false, pTextureName, 1024 );
-
- // find or add
- for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
- {
- if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
- {
- // already added
- break;
- }
- }
- if ( j == s_DefaultCubemapNames.Count() )
- {
- int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
-
- int id = s_DefaultCubemapNames.AddToTail();
- s_DefaultCubemapNames[id] = new char[nLen + 1];
- strcpy( s_DefaultCubemapNames[id], pFileName );
- }
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "bsplib.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlvector.h"
+#include "bitmap/imageformat.h"
+#include <KeyValues.h>
+#include "tier1/strtools.h"
+#include "tier1/utlsymbol.h"
+#include "vtf/vtf.h"
+#include "materialpatch.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+
+
+/*
+ Meager documentation for how the cubemaps are assigned.
+
+
+ While loading the map, it calls:
+ *** Cubemap_SaveBrushSides
+ Builds a list of what cubemaps manually were assigned to what faces
+ in s_EnvCubemapToBrushSides.
+
+ Immediately after loading the map, it calls:
+ *** Cubemap_FixupBrushSidesMaterials
+ Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+ side referenced by an env_cubemap manually.
+
+ Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
+ *** Cubemap_InitCubemapSideData:
+ Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
+ bHasEnvMapInMaterial is set if the side's material has $envmap.
+ bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
+
+ Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
+ referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
+*/
+
+struct PatchInfo_t
+{
+ char *m_pMapName;
+ int m_pOrigin[3];
+};
+
+struct CubemapInfo_t
+{
+ int m_nTableId;
+ bool m_bSpecular;
+};
+
+static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
+{
+ return ( lhs.m_nTableId < rhs.m_nTableId );
+}
+
+
+typedef CUtlVector<int> IntVector_t;
+static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
+
+static CUtlVector<char *> s_DefaultCubemapNames;
+static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
+
+
+struct CubemapSideData_t
+{
+ bool bHasEnvMapInMaterial;
+ bool bManuallyPickedByAnEnvCubemap;
+};
+
+static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
+
+
+
+inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
+{
+ return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
+}
+
+
+void Cubemap_InsertSample( const Vector& origin, int size )
+{
+ dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
+ pSample->origin[0] = ( int )origin[0];
+ pSample->origin[1] = ( int )origin[1];
+ pSample->origin[2] = ( int )origin[2];
+ pSample->size = size;
+ g_nCubemapSamples++;
+}
+
+static const char *FindSkyboxMaterialName( void )
+{
+ for( int i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
+ if (!strcmp(pEntity, "worldspawn"))
+ {
+ return ValueForKey( &g_MainMap->entities[i], "skyname" );
+ }
+ }
+ return NULL;
+}
+
+static void BackSlashToForwardSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '\\' )
+ *pname = '/';
+ pname++;
+ }
+}
+
+static void ForwardSlashToBackSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '/' )
+ *pname = '\\';
+ pname++;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds materials that are used by a particular material
+//-----------------------------------------------------------------------------
+#define MAX_MATERIAL_NAME 512
+
+// This is the list of materialvars which are used in our codebase to look up dependent materials
+static const char *s_pDependentMaterialVar[] =
+{
+ "$bottommaterial", // Used by water materials
+ "$crackmaterial", // Used by shattered glass materials
+ "$fallbackmaterial", // Used by all materials
+
+ "", // Always must be last
+};
+
+static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
+{
+ // FIXME: This is a terrible way of doing this! It creates a dependency
+ // between vbsp and *all* code which reads dependent materials from materialvars
+ // At the time of writing this function, that means the engine + studiorender.
+ // We need a better way of figuring out how to do this, but for now I'm trying to do
+ // the fastest solution possible since it's close to ship
+
+ static char pDependentMaterialName[MAX_MATERIAL_NAME];
+ for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
+ {
+ if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
+ continue;
+
+ if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
+ {
+ Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
+ continue;
+ }
+
+ // Return the material var that caused the dependency
+ if ( ppMaterialVar )
+ {
+ *ppMaterialVar = s_pDependentMaterialVar[i];
+ }
+
+#ifdef _DEBUG
+ // FIXME: Note that this code breaks if a material has more than 1 dependent material
+ ++i;
+ static char pDependentMaterialName2[MAX_MATERIAL_NAME];
+ while( s_pDependentMaterialVar[i][0] )
+ {
+ Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
+ ++i;
+ }
+#endif
+
+ return pDependentMaterialName;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads VTF files
+//-----------------------------------------------------------------------------
+static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
+ int *pUnionTextureFlags, bool bHDR )
+{
+ const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
+ int i;
+ for( i = 0; i < 6; i++ )
+ {
+ char srcMaterialName[1024];
+ sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
+
+ IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
+ //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
+ IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
+ const char *vtfName = pSkyTextureVar->GetStringValue();
+ char srcVTFFileName[MAX_PATH];
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ {
+ // Try looking for a compressed HDR texture
+ if ( bHDR )
+ {
+ /* // FIXME: We need a way to uncompress this format!
+ bool bHDRCompressed = true;
+
+ pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
+ vtfName = pSkyTextureVar->GetStringValue();
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ */
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ pSrcVTFTextures[i] = CreateVTFTexture();
+ if (!pSrcVTFTextures[i]->Unserialize(buf))
+ {
+ Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
+ int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+ int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+
+ // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
+ if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
+ ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
+ ( flagsNoAlpha != flagsFirstNoAlpha ) )
+ {
+ Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ if ( bHDR )
+ {
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
+ pSrcVTFTextures[i]->GenerateMipmaps();
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
+ }
+ }
+
+ return true;
+}
+
+void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
+{
+ Q_strncpy( pDest, pSrcName, maxLen );
+ if( !bHDR )
+ {
+ return;
+ }
+ char *pDot = Q_stristr( pDest, ".vtf" );
+ if( !pDot )
+ {
+ return;
+ }
+ Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
+}
+
+#define DEFAULT_CUBEMAP_SIZE 32
+
+void CreateDefaultCubemaps( bool bHDR )
+{
+ memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
+
+ // NOTE: This implementation depends on the fact that all VTF files contain
+ // all mipmap levels
+ const char *pSkyboxBaseName = FindSkyboxMaterialName();
+
+ if( !pSkyboxBaseName )
+ {
+ if( s_DefaultCubemapNames.Count() )
+ {
+ Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
+ }
+ return;
+ }
+
+ char skyboxMaterialName[MAX_PATH];
+ Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
+
+ IVTFTexture *pSrcVTFTextures[6];
+
+ int unionTextureFlags = 0;
+ if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
+ {
+ Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
+ return;
+ }
+ Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
+ " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
+
+ // Figure out the mip differences between the two textures
+ int iMipLevelOffset = 0;
+ int tmp = pSrcVTFTextures[0]->Width();
+ while( tmp > DEFAULT_CUBEMAP_SIZE )
+ {
+ iMipLevelOffset++;
+ tmp >>= 1;
+ }
+
+ // Create the destination cubemap
+ IVTFTexture *pDstCubemap = CreateVTFTexture();
+ pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
+ pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
+ pSrcVTFTextures[0]->FrameCount() );
+
+ // First iterate over all frames
+ for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
+ {
+ // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
+ for (int iFace = 0; iFace < 6; ++iFace )
+ {
+ // Finally, iterate over all mip levels in the *destination*
+ for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
+ {
+ // Copy the bits from the source images into the cube faces
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
+ unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
+ int iSize = pDstCubemap->ComputeMipSize( iMip );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
+
+ // !!! FIXME: Set this to black until HDR cubemaps are built properly!
+ memset( pDstBits, 0, iSize );
+ continue;
+
+ if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
+ {
+ // Force mip level 2 to get the 1x1 face
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
+
+ // Replicate 1x1 mip level across entire face
+ //memset( pDstBits, 0, iSize );
+ for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
+ {
+ memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
+ {
+ if ( iSrcMipSize != iSize )
+ {
+ Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Just copy the mip level
+ memcpy( pDstBits, pSrcBits, iSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
+ {
+ int iMipWidth, iMipHeight, iMipDepth;
+ pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
+ if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
+ {
+ Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Copy row at a time and repeat last row
+ memcpy( pDstBits, pSrcBits, iSize/2 );
+ //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
+ int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
+ int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
+ if ( nSrcRowSize != nDstRowSize )
+ {
+ Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
+ {
+ memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
+ }
+ }
+ }
+ }
+ else
+ {
+ // ERROR! This code only supports square and rectangluar 2x wide
+ Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
+ memset( pDstBits, 0, iSize );
+ return;
+ }
+ }
+ }
+ }
+
+ ImageFormat originalFormat = pDstCubemap->Format();
+ if( !bHDR )
+ {
+ // Convert the cube to format that we can apply tools to it...
+ pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
+ }
+
+ // Fixup the cubemap facing
+ pDstCubemap->FixCubemapFaceOrientation();
+
+ // Now that the bits are in place, compute the spheremaps...
+ pDstCubemap->GenerateSpheremap();
+
+ if( !bHDR )
+ {
+ // Convert the cubemap to the final format
+ pDstCubemap->ConvertImageFormat( originalFormat, false );
+ }
+
+ // Write the puppy out!
+ char dstVTFFileName[1024];
+ if( bHDR )
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
+ }
+ else
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
+ }
+
+ CUtlBuffer outputBuf;
+ if (!pDstCubemap->Serialize( outputBuf ))
+ {
+ Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
+ return;
+ }
+
+ IZip *pak = GetPakFile();
+
+ // spit out the default one.
+ AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
+
+ // spit out all of the ones that are attached to world geometry.
+ int i;
+ for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
+ {
+ char vtfName[MAX_PATH];
+ VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
+ if( FileExistsInPak( pak, vtfName ) )
+ {
+ continue;
+ }
+ AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
+ }
+
+ // Clean up the textures
+ for( i = 0; i < 6; i++ )
+ {
+ DestroyVTFTexture( pSrcVTFTextures[i] );
+ }
+ DestroyVTFTexture( pDstCubemap );
+}
+
+void Cubemap_CreateDefaultCubemaps( void )
+{
+ CreateDefaultCubemaps( false );
+ CreateDefaultCubemaps( true );
+}
+
+// Builds a list of what cubemaps manually were assigned to what faces
+// in s_EnvCubemapToBrushSides.
+void Cubemap_SaveBrushSides( const char *pSideListStr )
+{
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
+ char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
+ strcpy( pTmp, pSideListStr );
+ const char *pScan = strtok( pTmp, " " );
+ if( !pScan )
+ {
+ return;
+ }
+ do
+ {
+ int brushSideID;
+ if( sscanf( pScan, "%d", &brushSideID ) == 1 )
+ {
+ brushSidesVector.AddToTail( brushSideID );
+ }
+ } while( ( pScan = strtok( NULL, " " ) ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate patched material name
+//-----------------------------------------------------------------------------
+static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
+{
+ const char *pSeparator = bMaterialName ? "_" : "";
+ int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
+ pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
+
+ if ( bMaterialName )
+ {
+ Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
+ if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
+ {
+ Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
+ }
+ }
+
+ BackSlashToForwardSlash( pBuffer );
+ Q_strlower( pBuffer );
+}
+
+
+//-----------------------------------------------------------------------------
+// Patches the $envmap for a material and all its dependents, returns true if any patching happened
+//-----------------------------------------------------------------------------
+static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
+{
+ // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
+
+ // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
+ // because we're using the 'replace' block, which will only add the env_cubemap if
+ // $envmap is specified in the source material. But it will fail if someone adds
+ // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
+
+ // See if we have an $envmap to patch
+ bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
+
+ // See if we have a dependent material to patch
+ bool bDependentMaterialPatched = false;
+ const char *pDependentMaterialVar = NULL;
+ const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
+ if ( pDependentMaterial )
+ {
+ bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
+ }
+
+ // If we have neither to patch, we're done
+ if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
+ return false;
+
+ // Otherwise we have to make a patched version of ourselves
+ char pPatchedMaterialName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
+
+ MaterialPatchInfo_t pPatchInfo[2];
+ int nPatchCount = 0;
+ if ( bShouldPatchEnvCubemap )
+ {
+ pPatchInfo[nPatchCount].m_pKey = "$envmap";
+ pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
+ pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
+ ++nPatchCount;
+ }
+
+ char pDependentPatchedMaterialName[1024];
+ if ( bDependentMaterialPatched )
+ {
+ // FIXME: Annoying! I either have to pass back the patched dependent material name
+ // or reconstruct it. Both are sucky.
+ GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
+ pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
+ pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
+ ++nPatchCount;
+ }
+
+ CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texinfo that has a particular
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
+// Returns the index of the new (or preexisting) texinfo referencing that VMT.
+//
+// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
+// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
+// runtime before they run buildcubemaps.
+//-----------------------------------------------------------------------------
+static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
+{
+ // Don't make cubemap tex infos for nodes
+ if ( originalTexInfo == TEXINFO_NODE )
+ return originalTexInfo;
+
+ texinfo_t *pTexInfo = &texinfo[originalTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ if ( g_IsCubemapTexData[pTexInfo->texdata] )
+ {
+ Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
+ return originalTexInfo;
+ }
+
+ // Get out of here if the originalTexInfo is already a generated material for this position.
+ char pStringToSearchFor[512];
+ Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
+ if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
+ return originalTexInfo;
+
+ // Package up information needed to generate patch names
+ PatchInfo_t info;
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = origin[0];
+ info.m_pOrigin[1] = origin[1];
+ info.m_pOrigin[2] = origin[2];
+
+ // Generate the name of the patched material
+ char pGeneratedTexDataName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
+
+ // Make sure the texdata doesn't already exist.
+ int nTexDataID = FindTexData( pGeneratedTexDataName );
+ bool bHasTexData = (nTexDataID != -1);
+ if( !bHasTexData )
+ {
+ // Generate the new "$envmap" texture name.
+ char pTextureName[1024];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // Hook the texture into the material and all dependent materials
+ // but if no hooking was necessary, exit out
+ if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
+ return originalTexInfo;
+
+ // Store off the name of the cubemap that we need to create since we successfully patched
+ char pFileName[1024];
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+
+ // Make a new texdata
+ nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
+ g_IsCubemapTexData[nTexDataID] = true;
+ }
+
+ Assert( nTexDataID != -1 );
+
+ texinfo_t newTexInfo;
+ newTexInfo = *pTexInfo;
+ newTexInfo.texdata = nTexDataID;
+
+ int nTexInfoID = -1;
+
+ // See if we need to make a new texinfo
+ bool bHasTexInfo = false;
+ if( bHasTexData )
+ {
+ nTexInfoID = FindTexInfo( newTexInfo );
+ bHasTexInfo = (nTexInfoID != -1);
+ }
+
+ // Make a new texinfo if we need to.
+ if( !bHasTexInfo )
+ {
+ nTexInfoID = texinfo.AddToTail( newTexInfo );
+ }
+
+ Assert( nTexInfoID != -1 );
+ return nTexInfoID;
+}
+
+static int SideIDToIndex( int brushSideID )
+{
+ int i;
+ for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ if( g_MainMap->brushsides[i].id == brushSideID )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+// side referenced by an env_cubemap manually.
+//-----------------------------------------------------------------------------
+void Cubemap_FixupBrushSidesMaterials( void )
+{
+ Msg( "fixing up env_cubemap materials on brush sides...\n" );
+ Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
+
+ int cubemapID;
+ for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
+ {
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
+ int i;
+ for( i = 0; i < brushSidesVector.Count(); i++ )
+ {
+ int brushSideID = brushSidesVector[i];
+ int sideIndex = SideIDToIndex( brushSideID );
+ if( sideIndex < 0 )
+ {
+ Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
+ g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
+
+ continue;
+ }
+
+ side_t *pSide = &g_MainMap->brushsides[sideIndex];
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Cubemap_ResetCubemapSideData( void )
+{
+ for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
+ s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the material or any of its dependents use an $envmap
+//-----------------------------------------------------------------------------
+bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
+{
+ const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
+ if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
+ return true;
+
+ const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
+ if ( !pDependentMaterial )
+ return false;
+
+ return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all texdatas which need fixing up
+//-----------------------------------------------------------------------------
+void Cubemap_InitCubemapSideData( void )
+{
+ // This tree is used to prevent re-parsing material vars multiple times
+ CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
+
+ // Fill in specular data.
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !pSide )
+ continue;
+
+ if ( pSide->texinfo == TEXINFO_NODE )
+ continue;
+
+ texinfo_t *pTex = &texinfo[pSide->texinfo];
+ if ( !pTex )
+ continue;
+
+ dtexdata_t *pTexData = GetTexData( pTex->texdata );
+ if ( !pTexData )
+ continue;
+
+ CubemapInfo_t info;
+ info.m_nTableId = pTexData->nameStringTableID;
+
+ // Have we encountered this materal? If so, then copy the data we cached off before
+ int i = lookup.Find( info );
+ if ( i != lookup.InvalidIndex() )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
+ continue;
+ }
+
+ // First time we've seen this material. Figure out if it uses env_cubemap
+ const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
+ s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
+ lookup.Insert( info );
+ }
+
+ // Fill in cube map data.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
+ int nSideCount = sideList.Count();
+ for ( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ int nSideID = sideList[iSide];
+ int nIndex = SideIDToIndex( nSideID );
+ if ( nIndex < 0 )
+ continue;
+
+ s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
+{
+ if ( !pSide )
+ return -1;
+
+ // Return a valid (if random) cubemap if there's no winding
+ if ( !pSide->winding )
+ return 0;
+
+ // Calculate the center point.
+ Vector vecCenter;
+ vecCenter.Init();
+
+ for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
+ {
+ VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
+ }
+ VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
+ vecCenter += entityOrigin;
+ plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
+
+ // Find the closest cubemap.
+ int iMinCubemap = -1;
+ float flMinDist = FLT_MAX;
+
+ // Look for cubemaps in front of the surface first.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.NormalizeInPlace();
+ float flDot = DotProduct( vecDelta, pPlane->normal );
+ if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+
+ // Didn't find anything in front search for closest.
+ if( iMinCubemap == -1 )
+ {
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.Length();
+ if ( flDist < flMinDist )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+ }
+
+ return iMinCubemap;
+}
+
+
+//-----------------------------------------------------------------------------
+// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
+//-----------------------------------------------------------------------------
+void Cubemap_AttachDefaultCubemapToSpecularSides( void )
+{
+ Cubemap_ResetCubemapSideData();
+ Cubemap_InitCubemapSideData();
+
+ // build a mapping from side to entity id so that we can get the entity origin
+ CUtlVector<int> sideToEntityIndex;
+ sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
+ int i;
+ for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ sideToEntityIndex[i] = -1;
+ }
+
+ for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
+ {
+ int entityIndex = g_MainMap->mapbrushes[i].entitynum;
+ for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
+ {
+ side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
+ int sideIndex = side - g_MainMap->brushsides;
+ sideToEntityIndex[sideIndex] = entityIndex;
+ }
+ }
+
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
+ continue;
+
+
+ int currentEntity = sideToEntityIndex[iSide];
+
+ int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
+ if ( iCubemap == -1 )
+ continue;
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+}
+
+// Populate with cubemaps that were skipped
+void Cubemap_AddUnreferencedCubemaps()
+{
+ char pTextureName[1024];
+ char pFileName[1024];
+ PatchInfo_t info;
+ dcubemapsample_t *pSample;
+ int i,j;
+
+ for ( i=0; i<g_nCubemapSamples; ++i )
+ {
+ pSample = &g_CubemapSamples[i];
+
+ // generate the formatted texture name based on cubemap origin
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = pSample->origin[0];
+ info.m_pOrigin[1] = pSample->origin[1];
+ info.m_pOrigin[2] = pSample->origin[2];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // find or add
+ for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
+ {
+ if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
+ {
+ // already added
+ break;
+ }
+ }
+ if ( j == s_DefaultCubemapNames.Count() )
+ {
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[nLen + 1];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+ }
+ }
+}
diff --git a/mp/src/utils/vbsp/detail.cpp b/mp/src/utils/vbsp/detail.cpp
index 88071dd3..840068de 100644
--- a/mp/src/utils/vbsp/detail.cpp
+++ b/mp/src/utils/vbsp/detail.cpp
@@ -1,693 +1,693 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Builds/merges the BSP tree of detail brushes
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "detail.h"
-#include "utlvector.h"
-#include <assert.h>
-
-face_t *NewFaceFromFace (face_t *f);
-face_t *ComputeVisibleBrushSides( bspbrush_t *list );
-
-//-----------------------------------------------------------------------------
-// Purpose: Copies a face and its winding
-// Input : *pFace -
-// Output : face_t
-//-----------------------------------------------------------------------------
-face_t *CopyFace( face_t *pFace )
-{
- face_t *f = NewFaceFromFace( pFace );
- f->w = CopyWinding( pFace->w );
-
- return f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Link this brush into the list for this leaf
-// Input : *node -
-// *brush -
-//-----------------------------------------------------------------------------
-void AddBrushToLeaf( node_t *node, bspbrush_t *brush )
-{
- brush->next = node->brushlist;
- node->brushlist = brush;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Recursively filter a brush through the tree
-// Input : *node -
-// *brush -
-//-----------------------------------------------------------------------------
-void MergeBrush_r( node_t *node, bspbrush_t *brush )
-{
- if ( node->planenum == PLANENUM_LEAF )
- {
- if ( node->contents & CONTENTS_SOLID )
- {
- FreeBrush( brush );
- }
- else
- {
- AddBrushToLeaf( node, brush );
- }
- return;
- }
-
- bspbrush_t *front, *back;
- SplitBrush( brush, node->planenum, &front, &back );
- FreeBrush( brush );
-
- if ( front )
- {
- MergeBrush_r( node->children[0], front );
- }
- if ( back )
- {
- MergeBrush_r( node->children[1], back );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Recursively filter a face into the tree leaving references to the
-// original face in any visible leaves that a clipped fragment falls
-// into.
-// Input : *node - current head of tree
-// *face - clipped face fragment
-// *original - unclipped original face
-// Output : Returns true if any references were left
-//-----------------------------------------------------------------------------
-bool MergeFace_r( node_t *node, face_t *face, face_t *original )
-{
- bool referenced = false;
-
- if ( node->planenum == PLANENUM_LEAF )
- {
- if ( node->contents & CONTENTS_SOLID )
- {
- FreeFace( face );
- return false;
- }
-
- leafface_t *plist = new leafface_t;
- plist->pFace = original;
- plist->pNext = node->leaffacelist;
- node->leaffacelist = plist;
-
- referenced = true;
- }
- else
- {
- // UNDONE: Don't copy the faces each time unless it's necessary!?!?!
- plane_t *plane = &g_MainMap->mapplanes[node->planenum];
- winding_t *frontwinding, *backwinding, *onwinding;
-
- Vector offset;
- WindingCenter( face->w, offset );
-
- // UNDONE: Export epsilon from original face clipping code
- ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset);
-
- if ( onwinding )
- {
- // face is in the split plane, go down the appropriate side according to the facing direction
- assert( frontwinding == NULL );
- assert( backwinding == NULL );
-
- if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 )
- {
- frontwinding = onwinding;
- }
- else
- {
- backwinding = onwinding;
- }
- }
-
- if ( frontwinding )
- {
- face_t *tmp = NewFaceFromFace( face );
- tmp->w = frontwinding;
- referenced = MergeFace_r( node->children[0], tmp, original );
- }
- if ( backwinding )
- {
- face_t *tmp = NewFaceFromFace( face );
- tmp->w = backwinding;
- bool test = MergeFace_r( node->children[1], tmp, original );
- referenced = referenced || test;
- }
- }
- FreeFace( face );
-
- return referenced;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Loop through each face and filter it into the tree
-// Input : *out -
-// *pFaces -
-//-----------------------------------------------------------------------------
-face_t *FilterFacesIntoTree( tree_t *out, face_t *pFaces )
-{
- face_t *pLeafFaceList = NULL;
- for ( face_t *f = pFaces; f; f = f->next )
- {
- if( f->merged || f->split[0] || f->split[1] )
- continue;
-
- face_t *tmp = CopyFace( f );
- face_t *original = CopyFace( f );
-
- if ( MergeFace_r( out->headnode, tmp, original ) )
- {
- // clear out portal (comes from a different tree)
- original->portal = NULL;
- original->next = pLeafFaceList;
- pLeafFaceList = original;
- }
- else
- {
- FreeFace( original );
- }
- }
-
- return pLeafFaceList;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Splits the face list into faces from the same plane and tries to merge
-// them if possible
-// Input : **pFaceList -
-//-----------------------------------------------------------------------------
-void TryMergeFaceList( face_t **pFaceList )
-{
- face_t **pPlaneList = NULL;
-
- // divide the list into buckets by plane number
- pPlaneList = new face_t *[g_MainMap->nummapplanes];
- memset( pPlaneList, 0, sizeof(face_t *) * g_MainMap->nummapplanes );
-
- face_t *pFaces = *pFaceList;
- face_t *pOutput = NULL;
-
- while ( pFaces )
- {
- face_t *next = pFaces->next;
-
- // go ahead and delete the old split/merged faces
- if ( pFaces->merged || pFaces->split[0] || pFaces->split[1] )
- {
- Error("Split face in merge list!");
- }
- else
- {
- // add to the list for this plane
- pFaces->next = pPlaneList[pFaces->planenum];
- pPlaneList[pFaces->planenum] = pFaces;
- }
-
- pFaces = next;
- }
-
- // now merge each plane's list of faces
- int merged = 0;
- for ( int i = 0; i < g_MainMap->nummapplanes; i++ )
- {
- if ( pPlaneList[i] )
- {
- MergeFaceList( &pPlaneList[i] );
- }
-
- // move these over to the output face list
- face_t *list = pPlaneList[i];
- while ( list )
- {
- face_t *next = list->next;
-
- if ( list->merged )
- merged++;
-
- list->next = pOutput;
- pOutput = list;
- list = next;
- }
- }
-
- if ( merged )
- {
- Msg("\nMerged %d detail faces...", merged );
- }
- delete[] pPlaneList;
-
- *pFaceList = pOutput;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: filter each brush in the list into the tree
-// Input : *out -
-// *brushes -
-//-----------------------------------------------------------------------------
-void FilterBrushesIntoTree( tree_t *out, bspbrush_t *brushes )
-{
- // Merge all of the brushes into the world tree
- for ( bspbrush_t *plist = brushes; plist; plist = plist->next )
- {
- MergeBrush_r( out->headnode, CopyBrush(plist) );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Build faces for the detail brushes and merge them into the BSP
-// Input : *worldtree -
-// brush_start -
-// brush_end -
-//-----------------------------------------------------------------------------
-face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end )
-{
- int start;
- bspbrush_t *detailbrushes = NULL;
- face_t *pFaces = NULL;
- face_t *pLeafFaceList = NULL;
-
- // Grab the list of detail brushes
- detailbrushes = MakeBspBrushList (brush_start, brush_end, g_MainMap->map_mins, g_MainMap->map_maxs, ONLY_DETAIL );
- if (detailbrushes)
- {
- start = Plat_FloatTime();
- Msg("Chop Details...");
- // if there are detail brushes, chop them against each other
- if (!nocsg)
- detailbrushes = ChopBrushes (detailbrushes);
-
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
- // Now mark the visible sides so we can eliminate all detail brush sides
- // that are covered by other detail brush sides
- // NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation)
- Msg("Find Visible Detail Sides...");
- pFaces = ComputeVisibleBrushSides( detailbrushes );
- TryMergeFaceList( &pFaces );
- SubdivideFaceList( &pFaces );
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
-
- start = Plat_FloatTime();
- Msg("Merging details...");
- // Merge the detail solids and faces into the world tree
- // Merge all of the faces into the world tree
- pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces );
- FilterBrushesIntoTree( worldtree, detailbrushes );
-
- FreeFaceList( pFaces );
- FreeBrushList(detailbrushes);
-
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
- }
-
- return pLeafFaceList;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Quick overlap test for brushes
-// Input : *p1 -
-// *p2 -
-// Output : Returns false if the brushes cannot intersect
-//-----------------------------------------------------------------------------
-bool BrushBoxOverlap( bspbrush_t *p1, bspbrush_t *p2 )
-{
- if ( p1 == p2 )
- return false;
-
- for ( int i = 0; i < 3; i++ )
- {
- if ( p1->mins[i] > p2->maxs[i] || p1->maxs[i] < p2->mins[i] )
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFace - input face to test
-// *pbrush - brush to clip face against
-// **pOutputList - list of faces clipped from pFace
-// Output : Returns true if the brush completely clips the face
-//-----------------------------------------------------------------------------
-// NOTE: This assumes the brushes have already been chopped so that no solid space
-// is enclosed by more than one brush!!
-bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList )
-{
- int planenum = pFace->planenum & (~1);
- int foundSide = -1;
-
- CUtlVector<int> sortedSides;
-
- int i;
- for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ )
- {
- int bplane = pbrush->sides[i].planenum & (~1);
- if ( bplane == planenum )
- foundSide = i;
- }
-
- Vector offset = -0.5f * (pbrush->maxs + pbrush->mins);
- face_t *currentface = CopyFace( pFace );
-
- if ( foundSide >= 0 )
- {
- sortedSides.RemoveAll();
- for ( i = 0; i < pbrush->numsides; i++ )
- {
- // don't clip to bevels
- if ( pbrush->sides[i].bevel )
- continue;
-
- if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z )
- {
- sortedSides.AddToHead( i );
- }
- else
- {
- sortedSides.AddToTail( i );
- }
- }
-
- for ( i = 0; i < sortedSides.Size(); i++ )
- {
- int index = sortedSides[i];
- if ( index == foundSide )
- continue;
-
- plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum];
- winding_t *frontwinding, *backwinding;
- ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset);
-
- // only clip if some part of this face is on the back side of all brush sides
- if ( !backwinding || WindingIsTiny(backwinding))
- {
- FreeFaceList( *pOutputList );
- *pOutputList = NULL;
- break;
- }
- if ( frontwinding && !WindingIsTiny(frontwinding) )
- {
- // add this fragment to the return list
- // make a face for the fragment
- face_t *f = NewFaceFromFace( pFace );
- f->w = frontwinding;
-
- // link the fragment in
- f->next = *pOutputList;
- *pOutputList = f;
- }
-
- // update the current winding to be the part behind each plane
- FreeWinding( currentface->w );
- currentface->w = backwinding;
- }
-
- // free the bit that is left in solid or not clipped (if we broke out early)
- FreeFace( currentface );
-
- // if we made it all the way through and didn't produce any fragments then the whole face was clipped away
- if ( !*pOutputList && i == sortedSides.Size() )
- {
- return true;
- }
- }
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Given an original side and chopped winding, make a face_t
-// Input : *side - side of the original brush
-// *winding - winding for this face (portion of the side)
-// Output : face_t
-//-----------------------------------------------------------------------------
-face_t *MakeBrushFace( side_t *originalSide, winding_t *winding )
-{
- face_t *f = AllocFace();
- f->merged = NULL;
- f->split[0] = f->split[1] = NULL;
- f->w = CopyWinding( winding );
- f->originalface = originalSide;
- //
- // save material info
- //
- f->texinfo = originalSide->texinfo;
- f->dispinfo = -1;
-
- // save plane info
- f->planenum = originalSide->planenum;
- f->contents = originalSide->contents;
-
- return f;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Chop away sides that are inside other brushes.
-// Brushes have already been chopped up so that they do not overlap,
-// they merely touch.
-// Input : *list - list of brushes
-// Output : face_t * - list of visible faces (some marked bad/split)
-//-----------------------------------------------------------------------------
-// assumes brushes were chopped!
-
-
-side_t *FindOriginalSide( mapbrush_t *mb, side_t *pBspSide )
-{
- side_t *bestside = NULL;
- float bestdot = 0;
-
- plane_t *p1 = g_MainMap->mapplanes + pBspSide->planenum;
-
- for (int i=0 ; i<mb->numsides ; i++)
- {
- side_t *side = &mb->original_sides[i];
- if (side->bevel)
- continue;
- if (side->texinfo == TEXINFO_NODE)
- continue; // non-visible
- if ((side->planenum&~1) == (pBspSide->planenum&~1))
- { // exact match
- return mb->original_sides + i;
- }
- // see how close the match is
- plane_t *p2 = &g_MainMap->mapplanes[side->planenum&~1];
- float dot = DotProduct (p1->normal, p2->normal);
- if (dot > bestdot)
- {
- bestdot = dot;
- bestside = side;
- }
- }
-
- if ( !bestside )
- {
- Error( "Bad detail brush side\n" );
- }
- return bestside;
-}
-
-// Get a list of brushes from pBrushList that could cut faces on the source brush
-int GetListOfCutBrushes( CUtlVector<bspbrush_t *> &out, bspbrush_t *pSourceBrush, bspbrush_t *pBrushList )
-{
- mapbrush_t *mb = pSourceBrush->original;
- for ( bspbrush_t *walk = pBrushList; walk; walk = walk->next )
- {
- if ( walk == pSourceBrush )
- continue;
-
- // only clip to transparent brushes if the original brush is transparent
- if ( walk->original->contents & TRANSPARENT_CONTENTS )
- {
- if ( !(mb->contents & TRANSPARENT_CONTENTS) )
- continue;
- }
-
- // don't clip to clip brushes, etc.
- if ( !(walk->original->contents & ALL_VISIBLE_CONTENTS) )
- continue;
-
- // brushes overlap, test faces
- if ( !BrushBoxOverlap( pSourceBrush, walk ) )
- continue;
-
- out.AddToTail( walk );
- }
- return out.Count();
-}
-
-// Count the number of real (unsplit) faces in the list
-static int CountFaceList( face_t *f )
-{
- int count = 0;
- for ( ; f; f = f->next )
- {
- if ( f->split[0] )
- continue;
- count++;
- }
-
- return count;
-}
-
-// Clips f to a list of potential cutting brushes
-// If f clips into new faces, returns the list of new faces in pOutputList
-static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList )
-{
- *pOutputList = NULL;
-
- if ( f->split[0] )
- return;
-
- face_t *pClipList = CopyFace( f );
- pClipList->next = NULL;
- bool clipped = false;
- for ( int i = 0; i < cutBrushes.Count(); i++ )
- {
- bspbrush_t *cut = cutBrushes[i];
- for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next )
- {
- face_t *pClip = NULL;
- // already split, no need to clip
- if ( pCutFace->split[0] )
- continue;
-
- if ( ClipFaceToBrush( pCutFace, cut, &pClip ) )
- {
- clipped = true;
- // mark face bad, the brush clipped it away
- pCutFace->split[0] = pCutFace;
- }
- else if ( pClip )
- {
- clipped = true;
- // mark this face as split
- pCutFace->split[0] = pCutFace;
-
- // insert face fragments at head of list (UNDONE: reverses order, do we care?)
- while ( pClip )
- {
- face_t *next = pClip->next;
- pClip->next = pClipList;
- pClipList = pClip;
- pClip = next;
- }
- }
- }
- }
- if ( clipped )
- {
- *pOutputList = pClipList;
- }
- else
- {
- // didn't do any clipping, go ahead and free the copy of the face here.
- FreeFaceList( pClipList );
- }
-}
-
-// Compute a list of faces that are visible on the detail brush sides
-face_t *ComputeVisibleBrushSides( bspbrush_t *list )
-{
- face_t *pTotalFaces = NULL;
- CUtlVector<bspbrush_t *> cutBrushes;
-
- // Go through the whole brush list
- for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next )
- {
- face_t *pFaces = NULL;
- mapbrush_t *mb = pbrush->original;
-
- if ( !(mb->contents & ALL_VISIBLE_CONTENTS) )
- continue;
-
- // Make a face for each brush side, then clip it by the other
- // details to see if any fragments are visible
- for ( int i = 0; i < pbrush->numsides; i++ )
- {
- winding_t *winding = pbrush->sides[i].winding;
- if ( !winding )
- continue;
-
- if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) )
- continue;
-
- side_t *side = FindOriginalSide( mb, pbrush->sides + i );
- face_t *f = MakeBrushFace( side, winding );
-
- // link to head of face list
- f->next = pFaces;
- pFaces = f;
- }
-
- // Make a list of brushes that can cut the face list for this brush
- cutBrushes.RemoveAll();
- if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) )
- {
- // now cut each face to find visible fragments
- for ( face_t *f = pFaces; f; f = f->next )
- {
- // this will be a new list of faces that this face cuts into
- face_t *pClip = NULL;
- ClipFaceToBrushList( f, cutBrushes, &pClip );
- if ( pClip )
- {
- int outCount = CountFaceList(pClip);
- // it cut into more faces (or it was completely cut away)
- if ( outCount <= 1 )
- {
- // was removed or cut down, mark as split
- f->split[0] = f;
- // insert face fragments at head of list (UNDONE: reverses order, do we care?)
- while ( pClip )
- {
- face_t *next = pClip->next;
- pClip->next = pFaces;
- pFaces = pClip;
- pClip = next;
- }
- }
- else
- {
- // it cut into more than one visible fragment
- // Don't fragment details
- // UNDONE: Build 2d convex hull of this list and swap face winding
- // with that polygon? That would fix the remaining issues.
- FreeFaceList( pClip );
- pClip = NULL;
- }
- }
- }
- }
-
- // move visible fragments to global face list
- while ( pFaces )
- {
- face_t *next = pFaces->next;
- if ( pFaces->split[0] )
- {
- FreeFace( pFaces );
- }
- else
- {
- pFaces->next = pTotalFaces;
- pTotalFaces = pFaces;
- }
- pFaces = next;
- }
- }
-
- return pTotalFaces;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Builds/merges the BSP tree of detail brushes
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "detail.h"
+#include "utlvector.h"
+#include <assert.h>
+
+face_t *NewFaceFromFace (face_t *f);
+face_t *ComputeVisibleBrushSides( bspbrush_t *list );
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies a face and its winding
+// Input : *pFace -
+// Output : face_t
+//-----------------------------------------------------------------------------
+face_t *CopyFace( face_t *pFace )
+{
+ face_t *f = NewFaceFromFace( pFace );
+ f->w = CopyWinding( pFace->w );
+
+ return f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Link this brush into the list for this leaf
+// Input : *node -
+// *brush -
+//-----------------------------------------------------------------------------
+void AddBrushToLeaf( node_t *node, bspbrush_t *brush )
+{
+ brush->next = node->brushlist;
+ node->brushlist = brush;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively filter a brush through the tree
+// Input : *node -
+// *brush -
+//-----------------------------------------------------------------------------
+void MergeBrush_r( node_t *node, bspbrush_t *brush )
+{
+ if ( node->planenum == PLANENUM_LEAF )
+ {
+ if ( node->contents & CONTENTS_SOLID )
+ {
+ FreeBrush( brush );
+ }
+ else
+ {
+ AddBrushToLeaf( node, brush );
+ }
+ return;
+ }
+
+ bspbrush_t *front, *back;
+ SplitBrush( brush, node->planenum, &front, &back );
+ FreeBrush( brush );
+
+ if ( front )
+ {
+ MergeBrush_r( node->children[0], front );
+ }
+ if ( back )
+ {
+ MergeBrush_r( node->children[1], back );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively filter a face into the tree leaving references to the
+// original face in any visible leaves that a clipped fragment falls
+// into.
+// Input : *node - current head of tree
+// *face - clipped face fragment
+// *original - unclipped original face
+// Output : Returns true if any references were left
+//-----------------------------------------------------------------------------
+bool MergeFace_r( node_t *node, face_t *face, face_t *original )
+{
+ bool referenced = false;
+
+ if ( node->planenum == PLANENUM_LEAF )
+ {
+ if ( node->contents & CONTENTS_SOLID )
+ {
+ FreeFace( face );
+ return false;
+ }
+
+ leafface_t *plist = new leafface_t;
+ plist->pFace = original;
+ plist->pNext = node->leaffacelist;
+ node->leaffacelist = plist;
+
+ referenced = true;
+ }
+ else
+ {
+ // UNDONE: Don't copy the faces each time unless it's necessary!?!?!
+ plane_t *plane = &g_MainMap->mapplanes[node->planenum];
+ winding_t *frontwinding, *backwinding, *onwinding;
+
+ Vector offset;
+ WindingCenter( face->w, offset );
+
+ // UNDONE: Export epsilon from original face clipping code
+ ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset);
+
+ if ( onwinding )
+ {
+ // face is in the split plane, go down the appropriate side according to the facing direction
+ assert( frontwinding == NULL );
+ assert( backwinding == NULL );
+
+ if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 )
+ {
+ frontwinding = onwinding;
+ }
+ else
+ {
+ backwinding = onwinding;
+ }
+ }
+
+ if ( frontwinding )
+ {
+ face_t *tmp = NewFaceFromFace( face );
+ tmp->w = frontwinding;
+ referenced = MergeFace_r( node->children[0], tmp, original );
+ }
+ if ( backwinding )
+ {
+ face_t *tmp = NewFaceFromFace( face );
+ tmp->w = backwinding;
+ bool test = MergeFace_r( node->children[1], tmp, original );
+ referenced = referenced || test;
+ }
+ }
+ FreeFace( face );
+
+ return referenced;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loop through each face and filter it into the tree
+// Input : *out -
+// *pFaces -
+//-----------------------------------------------------------------------------
+face_t *FilterFacesIntoTree( tree_t *out, face_t *pFaces )
+{
+ face_t *pLeafFaceList = NULL;
+ for ( face_t *f = pFaces; f; f = f->next )
+ {
+ if( f->merged || f->split[0] || f->split[1] )
+ continue;
+
+ face_t *tmp = CopyFace( f );
+ face_t *original = CopyFace( f );
+
+ if ( MergeFace_r( out->headnode, tmp, original ) )
+ {
+ // clear out portal (comes from a different tree)
+ original->portal = NULL;
+ original->next = pLeafFaceList;
+ pLeafFaceList = original;
+ }
+ else
+ {
+ FreeFace( original );
+ }
+ }
+
+ return pLeafFaceList;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Splits the face list into faces from the same plane and tries to merge
+// them if possible
+// Input : **pFaceList -
+//-----------------------------------------------------------------------------
+void TryMergeFaceList( face_t **pFaceList )
+{
+ face_t **pPlaneList = NULL;
+
+ // divide the list into buckets by plane number
+ pPlaneList = new face_t *[g_MainMap->nummapplanes];
+ memset( pPlaneList, 0, sizeof(face_t *) * g_MainMap->nummapplanes );
+
+ face_t *pFaces = *pFaceList;
+ face_t *pOutput = NULL;
+
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+
+ // go ahead and delete the old split/merged faces
+ if ( pFaces->merged || pFaces->split[0] || pFaces->split[1] )
+ {
+ Error("Split face in merge list!");
+ }
+ else
+ {
+ // add to the list for this plane
+ pFaces->next = pPlaneList[pFaces->planenum];
+ pPlaneList[pFaces->planenum] = pFaces;
+ }
+
+ pFaces = next;
+ }
+
+ // now merge each plane's list of faces
+ int merged = 0;
+ for ( int i = 0; i < g_MainMap->nummapplanes; i++ )
+ {
+ if ( pPlaneList[i] )
+ {
+ MergeFaceList( &pPlaneList[i] );
+ }
+
+ // move these over to the output face list
+ face_t *list = pPlaneList[i];
+ while ( list )
+ {
+ face_t *next = list->next;
+
+ if ( list->merged )
+ merged++;
+
+ list->next = pOutput;
+ pOutput = list;
+ list = next;
+ }
+ }
+
+ if ( merged )
+ {
+ Msg("\nMerged %d detail faces...", merged );
+ }
+ delete[] pPlaneList;
+
+ *pFaceList = pOutput;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: filter each brush in the list into the tree
+// Input : *out -
+// *brushes -
+//-----------------------------------------------------------------------------
+void FilterBrushesIntoTree( tree_t *out, bspbrush_t *brushes )
+{
+ // Merge all of the brushes into the world tree
+ for ( bspbrush_t *plist = brushes; plist; plist = plist->next )
+ {
+ MergeBrush_r( out->headnode, CopyBrush(plist) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Build faces for the detail brushes and merge them into the BSP
+// Input : *worldtree -
+// brush_start -
+// brush_end -
+//-----------------------------------------------------------------------------
+face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end )
+{
+ int start;
+ bspbrush_t *detailbrushes = NULL;
+ face_t *pFaces = NULL;
+ face_t *pLeafFaceList = NULL;
+
+ // Grab the list of detail brushes
+ detailbrushes = MakeBspBrushList (brush_start, brush_end, g_MainMap->map_mins, g_MainMap->map_maxs, ONLY_DETAIL );
+ if (detailbrushes)
+ {
+ start = Plat_FloatTime();
+ Msg("Chop Details...");
+ // if there are detail brushes, chop them against each other
+ if (!nocsg)
+ detailbrushes = ChopBrushes (detailbrushes);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+ // Now mark the visible sides so we can eliminate all detail brush sides
+ // that are covered by other detail brush sides
+ // NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation)
+ Msg("Find Visible Detail Sides...");
+ pFaces = ComputeVisibleBrushSides( detailbrushes );
+ TryMergeFaceList( &pFaces );
+ SubdivideFaceList( &pFaces );
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ start = Plat_FloatTime();
+ Msg("Merging details...");
+ // Merge the detail solids and faces into the world tree
+ // Merge all of the faces into the world tree
+ pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces );
+ FilterBrushesIntoTree( worldtree, detailbrushes );
+
+ FreeFaceList( pFaces );
+ FreeBrushList(detailbrushes);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+ }
+
+ return pLeafFaceList;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Quick overlap test for brushes
+// Input : *p1 -
+// *p2 -
+// Output : Returns false if the brushes cannot intersect
+//-----------------------------------------------------------------------------
+bool BrushBoxOverlap( bspbrush_t *p1, bspbrush_t *p2 )
+{
+ if ( p1 == p2 )
+ return false;
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( p1->mins[i] > p2->maxs[i] || p1->maxs[i] < p2->mins[i] )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFace - input face to test
+// *pbrush - brush to clip face against
+// **pOutputList - list of faces clipped from pFace
+// Output : Returns true if the brush completely clips the face
+//-----------------------------------------------------------------------------
+// NOTE: This assumes the brushes have already been chopped so that no solid space
+// is enclosed by more than one brush!!
+bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList )
+{
+ int planenum = pFace->planenum & (~1);
+ int foundSide = -1;
+
+ CUtlVector<int> sortedSides;
+
+ int i;
+ for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ )
+ {
+ int bplane = pbrush->sides[i].planenum & (~1);
+ if ( bplane == planenum )
+ foundSide = i;
+ }
+
+ Vector offset = -0.5f * (pbrush->maxs + pbrush->mins);
+ face_t *currentface = CopyFace( pFace );
+
+ if ( foundSide >= 0 )
+ {
+ sortedSides.RemoveAll();
+ for ( i = 0; i < pbrush->numsides; i++ )
+ {
+ // don't clip to bevels
+ if ( pbrush->sides[i].bevel )
+ continue;
+
+ if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z )
+ {
+ sortedSides.AddToHead( i );
+ }
+ else
+ {
+ sortedSides.AddToTail( i );
+ }
+ }
+
+ for ( i = 0; i < sortedSides.Size(); i++ )
+ {
+ int index = sortedSides[i];
+ if ( index == foundSide )
+ continue;
+
+ plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum];
+ winding_t *frontwinding, *backwinding;
+ ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset);
+
+ // only clip if some part of this face is on the back side of all brush sides
+ if ( !backwinding || WindingIsTiny(backwinding))
+ {
+ FreeFaceList( *pOutputList );
+ *pOutputList = NULL;
+ break;
+ }
+ if ( frontwinding && !WindingIsTiny(frontwinding) )
+ {
+ // add this fragment to the return list
+ // make a face for the fragment
+ face_t *f = NewFaceFromFace( pFace );
+ f->w = frontwinding;
+
+ // link the fragment in
+ f->next = *pOutputList;
+ *pOutputList = f;
+ }
+
+ // update the current winding to be the part behind each plane
+ FreeWinding( currentface->w );
+ currentface->w = backwinding;
+ }
+
+ // free the bit that is left in solid or not clipped (if we broke out early)
+ FreeFace( currentface );
+
+ // if we made it all the way through and didn't produce any fragments then the whole face was clipped away
+ if ( !*pOutputList && i == sortedSides.Size() )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given an original side and chopped winding, make a face_t
+// Input : *side - side of the original brush
+// *winding - winding for this face (portion of the side)
+// Output : face_t
+//-----------------------------------------------------------------------------
+face_t *MakeBrushFace( side_t *originalSide, winding_t *winding )
+{
+ face_t *f = AllocFace();
+ f->merged = NULL;
+ f->split[0] = f->split[1] = NULL;
+ f->w = CopyWinding( winding );
+ f->originalface = originalSide;
+ //
+ // save material info
+ //
+ f->texinfo = originalSide->texinfo;
+ f->dispinfo = -1;
+
+ // save plane info
+ f->planenum = originalSide->planenum;
+ f->contents = originalSide->contents;
+
+ return f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Chop away sides that are inside other brushes.
+// Brushes have already been chopped up so that they do not overlap,
+// they merely touch.
+// Input : *list - list of brushes
+// Output : face_t * - list of visible faces (some marked bad/split)
+//-----------------------------------------------------------------------------
+// assumes brushes were chopped!
+
+
+side_t *FindOriginalSide( mapbrush_t *mb, side_t *pBspSide )
+{
+ side_t *bestside = NULL;
+ float bestdot = 0;
+
+ plane_t *p1 = g_MainMap->mapplanes + pBspSide->planenum;
+
+ for (int i=0 ; i<mb->numsides ; i++)
+ {
+ side_t *side = &mb->original_sides[i];
+ if (side->bevel)
+ continue;
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // non-visible
+ if ((side->planenum&~1) == (pBspSide->planenum&~1))
+ { // exact match
+ return mb->original_sides + i;
+ }
+ // see how close the match is
+ plane_t *p2 = &g_MainMap->mapplanes[side->planenum&~1];
+ float dot = DotProduct (p1->normal, p2->normal);
+ if (dot > bestdot)
+ {
+ bestdot = dot;
+ bestside = side;
+ }
+ }
+
+ if ( !bestside )
+ {
+ Error( "Bad detail brush side\n" );
+ }
+ return bestside;
+}
+
+// Get a list of brushes from pBrushList that could cut faces on the source brush
+int GetListOfCutBrushes( CUtlVector<bspbrush_t *> &out, bspbrush_t *pSourceBrush, bspbrush_t *pBrushList )
+{
+ mapbrush_t *mb = pSourceBrush->original;
+ for ( bspbrush_t *walk = pBrushList; walk; walk = walk->next )
+ {
+ if ( walk == pSourceBrush )
+ continue;
+
+ // only clip to transparent brushes if the original brush is transparent
+ if ( walk->original->contents & TRANSPARENT_CONTENTS )
+ {
+ if ( !(mb->contents & TRANSPARENT_CONTENTS) )
+ continue;
+ }
+
+ // don't clip to clip brushes, etc.
+ if ( !(walk->original->contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ // brushes overlap, test faces
+ if ( !BrushBoxOverlap( pSourceBrush, walk ) )
+ continue;
+
+ out.AddToTail( walk );
+ }
+ return out.Count();
+}
+
+// Count the number of real (unsplit) faces in the list
+static int CountFaceList( face_t *f )
+{
+ int count = 0;
+ for ( ; f; f = f->next )
+ {
+ if ( f->split[0] )
+ continue;
+ count++;
+ }
+
+ return count;
+}
+
+// Clips f to a list of potential cutting brushes
+// If f clips into new faces, returns the list of new faces in pOutputList
+static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList )
+{
+ *pOutputList = NULL;
+
+ if ( f->split[0] )
+ return;
+
+ face_t *pClipList = CopyFace( f );
+ pClipList->next = NULL;
+ bool clipped = false;
+ for ( int i = 0; i < cutBrushes.Count(); i++ )
+ {
+ bspbrush_t *cut = cutBrushes[i];
+ for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next )
+ {
+ face_t *pClip = NULL;
+ // already split, no need to clip
+ if ( pCutFace->split[0] )
+ continue;
+
+ if ( ClipFaceToBrush( pCutFace, cut, &pClip ) )
+ {
+ clipped = true;
+ // mark face bad, the brush clipped it away
+ pCutFace->split[0] = pCutFace;
+ }
+ else if ( pClip )
+ {
+ clipped = true;
+ // mark this face as split
+ pCutFace->split[0] = pCutFace;
+
+ // insert face fragments at head of list (UNDONE: reverses order, do we care?)
+ while ( pClip )
+ {
+ face_t *next = pClip->next;
+ pClip->next = pClipList;
+ pClipList = pClip;
+ pClip = next;
+ }
+ }
+ }
+ }
+ if ( clipped )
+ {
+ *pOutputList = pClipList;
+ }
+ else
+ {
+ // didn't do any clipping, go ahead and free the copy of the face here.
+ FreeFaceList( pClipList );
+ }
+}
+
+// Compute a list of faces that are visible on the detail brush sides
+face_t *ComputeVisibleBrushSides( bspbrush_t *list )
+{
+ face_t *pTotalFaces = NULL;
+ CUtlVector<bspbrush_t *> cutBrushes;
+
+ // Go through the whole brush list
+ for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next )
+ {
+ face_t *pFaces = NULL;
+ mapbrush_t *mb = pbrush->original;
+
+ if ( !(mb->contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ // Make a face for each brush side, then clip it by the other
+ // details to see if any fragments are visible
+ for ( int i = 0; i < pbrush->numsides; i++ )
+ {
+ winding_t *winding = pbrush->sides[i].winding;
+ if ( !winding )
+ continue;
+
+ if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ side_t *side = FindOriginalSide( mb, pbrush->sides + i );
+ face_t *f = MakeBrushFace( side, winding );
+
+ // link to head of face list
+ f->next = pFaces;
+ pFaces = f;
+ }
+
+ // Make a list of brushes that can cut the face list for this brush
+ cutBrushes.RemoveAll();
+ if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) )
+ {
+ // now cut each face to find visible fragments
+ for ( face_t *f = pFaces; f; f = f->next )
+ {
+ // this will be a new list of faces that this face cuts into
+ face_t *pClip = NULL;
+ ClipFaceToBrushList( f, cutBrushes, &pClip );
+ if ( pClip )
+ {
+ int outCount = CountFaceList(pClip);
+ // it cut into more faces (or it was completely cut away)
+ if ( outCount <= 1 )
+ {
+ // was removed or cut down, mark as split
+ f->split[0] = f;
+ // insert face fragments at head of list (UNDONE: reverses order, do we care?)
+ while ( pClip )
+ {
+ face_t *next = pClip->next;
+ pClip->next = pFaces;
+ pFaces = pClip;
+ pClip = next;
+ }
+ }
+ else
+ {
+ // it cut into more than one visible fragment
+ // Don't fragment details
+ // UNDONE: Build 2d convex hull of this list and swap face winding
+ // with that polygon? That would fix the remaining issues.
+ FreeFaceList( pClip );
+ pClip = NULL;
+ }
+ }
+ }
+ }
+
+ // move visible fragments to global face list
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+ if ( pFaces->split[0] )
+ {
+ FreeFace( pFaces );
+ }
+ else
+ {
+ pFaces->next = pTotalFaces;
+ pTotalFaces = pFaces;
+ }
+ pFaces = next;
+ }
+ }
+
+ return pTotalFaces;
+}
diff --git a/mp/src/utils/vbsp/detail.h b/mp/src/utils/vbsp/detail.h
index aa0147d1..64f02b33 100644
--- a/mp/src/utils/vbsp/detail.h
+++ b/mp/src/utils/vbsp/detail.h
@@ -1,18 +1,18 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef DETAIL_H
-#define DETAIL_H
-
-#ifdef _WIN32
-#pragma once
-#endif
-
-face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end );
-
-
-#endif // DETAIL_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DETAIL_H
+#define DETAIL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end );
+
+
+#endif // DETAIL_H
diff --git a/mp/src/utils/vbsp/detailobjects.cpp b/mp/src/utils/vbsp/detailobjects.cpp
index b110534c..e7475d94 100644
--- a/mp/src/utils/vbsp/detailobjects.cpp
+++ b/mp/src/utils/vbsp/detailobjects.cpp
@@ -1,966 +1,966 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Places "detail" objects which are client-only renderable things
-//
-// $Revision: $
-// $NoKeywords: $
-//=============================================================================//
-
-#include <windows.h>
-#include "vbsp.h"
-#include "bsplib.h"
-#include "KeyValues.h"
-#include "utlsymbol.h"
-#include "utlvector.h"
-#include <io.h>
-#include "bspfile.h"
-#include "utilmatlib.h"
-#include "gamebspfile.h"
-#include "mathlib/VMatrix.h"
-#include "materialpatch.h"
-#include "pacifier.h"
-#include "vstdlib/random.h"
-#include "builddisp.h"
-#include "disp_vbsp.h"
-#include "UtlBuffer.h"
-#include "CollisionUtils.h"
-#include <float.h>
-#include "UtlLinkedList.h"
-#include "byteswap.h"
-#include "writebsp.h"
-
-//-----------------------------------------------------------------------------
-// Information about particular detail object types
-//-----------------------------------------------------------------------------
-enum
-{
- MODELFLAG_UPRIGHT = 0x1,
-};
-
-struct DetailModel_t
-{
- CUtlSymbol m_ModelName;
- float m_Amount;
- float m_MinCosAngle;
- float m_MaxCosAngle;
- int m_Flags;
- int m_Orientation;
- int m_Type;
- Vector2D m_Pos[2];
- Vector2D m_Tex[2];
- float m_flRandomScaleStdDev;
- unsigned char m_ShapeSize;
- unsigned char m_ShapeAngle;
- unsigned char m_SwayAmount;
-};
-
-struct DetailObjectGroup_t
-{
- float m_Alpha;
- CUtlVector< DetailModel_t > m_Models;
-};
-
-struct DetailObject_t
-{
- CUtlSymbol m_Name;
- float m_Density;
- CUtlVector< DetailObjectGroup_t > m_Groups;
-
- bool operator==(const DetailObject_t& src ) const
- {
- return src.m_Name == m_Name;
- }
-};
-
-static CUtlVector<DetailObject_t> s_DetailObjectDict;
-
-
-//-----------------------------------------------------------------------------
-// Error checking.. make sure the model is valid + is a static prop
-//-----------------------------------------------------------------------------
-struct StaticPropLookup_t
-{
- CUtlSymbol m_ModelName;
- bool m_IsValid;
-};
-
-static bool StaticLess( StaticPropLookup_t const& src1, StaticPropLookup_t const& src2 )
-{
- return src1.m_ModelName < src2.m_ModelName;
-}
-
-static CUtlRBTree< StaticPropLookup_t, unsigned short > s_StaticPropLookup( 0, 32, StaticLess );
-
-
-//-----------------------------------------------------------------------------
-// These puppies are used to construct the game lumps
-//-----------------------------------------------------------------------------
-static CUtlVector<DetailObjectDictLump_t> s_DetailObjectDictLump;
-static CUtlVector<DetailObjectLump_t> s_DetailObjectLump;
-static CUtlVector<DetailSpriteDictLump_t> s_DetailSpriteDictLump;
-
-
-//-----------------------------------------------------------------------------
-// Parses the key-value pairs in the detail.rad file
-//-----------------------------------------------------------------------------
-static void ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
-{
- // Sort the group by alpha
- float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
-
- int i = s_DetailObjectDict[detailId].m_Groups.Count();
- while ( --i >= 0 )
- {
- if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
- break;
- }
-
- // Insert after the first guy who's more transparent that we are!
- i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
- DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
-
- group.m_Alpha = alpha;
-
- // Add in all the model groups
- KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
- float totalAmount = 0.0f;
- while( pIter )
- {
- if (pIter->GetFirstSubKey())
- {
- int i = group.m_Models.AddToTail();
-
- DetailModel_t &model = group.m_Models[i];
-
- model.m_ModelName = pIter->GetString( "model", 0 );
- if (model.m_ModelName != UTL_INVAL_SYMBOL)
- {
- model.m_Type = DETAIL_PROP_TYPE_MODEL;
- }
- else
- {
- const char *pSpriteData = pIter->GetString( "sprite", 0 );
- if (pSpriteData)
- {
- const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
-
- if ( pProcModelType )
- {
- if ( !Q_stricmp( pProcModelType, "cross" ) )
- {
- model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
- }
- else if ( !Q_stricmp( pProcModelType, "tri" ) )
- {
- model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
- }
- else
- model.m_Type = DETAIL_PROP_TYPE_SPRITE;
- }
- else
- {
- // card sprite
- model.m_Type = DETAIL_PROP_TYPE_SPRITE;
- }
-
- model.m_Tex[0].Init();
- model.m_Tex[1].Init();
-
- float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
- int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
- if ( (nValid != 5) || (flTextureSize == 0) )
- {
- Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName.String() );
- }
-
- model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
- model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
- model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
- model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
-
- model.m_Pos[0].Init( -10, 20 );
- model.m_Pos[1].Init( 10, 0 );
-
- pSpriteData = pIter->GetString( "spritesize", 0 );
- if (pSpriteData)
- {
- sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
-
- float ox = flWidth * x;
- float oy = flHeight * y;
-
- model.m_Pos[0].x = -ox;
- model.m_Pos[0].y = flHeight - oy;
- model.m_Pos[1].x = flWidth - ox;
- model.m_Pos[1].y = -oy;
- }
-
- model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
-
- // sway is a percent of max sway, cl_detail_max_sway
- float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
- model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
-
- // shape angle
- // for the tri shape, this is the angle each side is fanned out
- model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
-
- // shape size
- // for the tri shape, this is the distance from the origin to the center of a side
- float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
- model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
- }
- }
-
- model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
- totalAmount = model.m_Amount;
-
- model.m_Flags = 0;
- if (pIter->GetInt( "upright", 0 ))
- {
- model.m_Flags |= MODELFLAG_UPRIGHT;
- }
-
- // These are used to prevent emission on steep surfaces
- float minAngle = pIter->GetFloat( "minAngle", 180 );
- float maxAngle = pIter->GetFloat( "maxAngle", 180 );
- model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
- model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
- model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
-
- // Make sure minAngle < maxAngle
- if ( model.m_MinCosAngle < model.m_MaxCosAngle)
- {
- model.m_MinCosAngle = model.m_MaxCosAngle;
- }
- }
- pIter = pIter->GetNextKey();
- }
-
- // renormalize the amount if the total > 1
- if (totalAmount > 1.0f)
- {
- for (i = 0; i < group.m_Models.Count(); ++i)
- {
- group.m_Models[i].m_Amount /= totalAmount;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Parses the key-value pairs in the detail.vbsp file
-//-----------------------------------------------------------------------------
-static void ParseDetailObjectFile( KeyValues& keyValues )
-{
- // Iterate over all detail object groups...
- KeyValues* pIter;
- for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
- {
- if (!pIter->GetFirstSubKey())
- continue;
-
- int i = s_DetailObjectDict.AddToTail( );
- s_DetailObjectDict[i].m_Name = pIter->GetName() ;
- s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
-
- // Iterate over all detail object groups...
- KeyValues* pIterGroups = pIter->GetFirstSubKey();
- while( pIterGroups )
- {
- if (pIterGroups->GetFirstSubKey())
- {
- ParseDetailGroup( i, pIterGroups );
- }
- pIterGroups = pIterGroups->GetNextKey();
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds the name of the detail.vbsp file to use
-//-----------------------------------------------------------------------------
-static const char *FindDetailVBSPName( void )
-{
- for( int i = 0; i < num_entities; i++ )
- {
- char* pEntity = ValueForKey( &entities[i], "classname" );
- if ( !strcmp( pEntity, "worldspawn" ) )
- {
- const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
- if ( !pDetailVBSP || !pDetailVBSP[0] )
- {
- pDetailVBSP = "detail.vbsp";
- }
- return pDetailVBSP;
- }
- }
- return "detail.vbsp";
-}
-
-
-//-----------------------------------------------------------------------------
-// Loads up the detail object dictionary
-//-----------------------------------------------------------------------------
-void LoadEmitDetailObjectDictionary( const char* pGameDir )
-{
- // Set the required global lights filename and try looking in qproject
- const char *pDetailVBSP = FindDetailVBSPName();
- KeyValues * values = new KeyValues( pDetailVBSP );
- if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
- {
- ParseDetailObjectFile( *values );
- }
- values->deleteThis();
-}
-
-
-//-----------------------------------------------------------------------------
-// Selects a detail group
-//-----------------------------------------------------------------------------
-static int SelectGroup( const DetailObject_t& detail, float alpha )
-{
- // Find the two groups whose alpha we're between...
- int start, end;
- for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
- {
- if (alpha < detail.m_Groups[start+1].m_Alpha)
- break;
- }
-
- end = start + 1;
- if (end >= detail.m_Groups.Count())
- --end;
-
- if (start == end)
- return start;
-
- // Figure out how far we are between start and end...
- float dist = 0.0f;
- float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
- if (dAlpha != 0.0f)
- {
- dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
- }
-
- // Pick a number, any number...
- float r = rand() / (float)VALVE_RAND_MAX;
-
- // When dist == 0, we *always* want start.
- // When dist == 1, we *always* want end
- // That's why this logic looks a little reversed
- return (r > dist) ? start : end;
-}
-
-
-//-----------------------------------------------------------------------------
-// Selects a detail object
-//-----------------------------------------------------------------------------
-static int SelectDetail( DetailObjectGroup_t const& group )
-{
- // Pick a number, any number...
- float r = rand() / (float)VALVE_RAND_MAX;
-
- // Look through the list of models + pick the one associated with this number
- for ( int i = 0; i < group.m_Models.Count(); ++i )
- {
- if (r <= group.m_Models[i].m_Amount)
- return i;
- }
-
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Adds a detail dictionary element (expected to oftentimes be shared)
-//-----------------------------------------------------------------------------
-static int AddDetailDictLump( const char* pModelName )
-{
- DetailObjectDictLump_t dictLump;
- Q_strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
-
- for (int i = s_DetailObjectDictLump.Count(); --i >= 0; )
- {
- if (!memcmp(&s_DetailObjectDictLump[i], &dictLump, sizeof(dictLump) ))
- return i;
- }
-
- return s_DetailObjectDictLump.AddToTail( dictLump );
-}
-
-static int AddDetailSpriteDictLump( const Vector2D *pPos, const Vector2D *pTex )
-{
- DetailSpriteDictLump_t dictLump;
- dictLump.m_UL = pPos[0];
- dictLump.m_LR = pPos[1];
- dictLump.m_TexUL = pTex[0];
- dictLump.m_TexLR = pTex[1];
-
- for (int i = s_DetailSpriteDictLump.Count(); --i >= 0; )
- {
- if (!memcmp(&s_DetailSpriteDictLump[i], &dictLump, sizeof(dictLump) ))
- return i;
- }
-
- return s_DetailSpriteDictLump.AddToTail( dictLump );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the leaf that the detail lies in
-//-----------------------------------------------------------------------------
-static int ComputeDetailLeaf( const Vector& pt )
-{
- int node = 0;
- while( node >= 0 )
- {
- dnode_t* pNode = &dnodes[node];
- dplane_t* pPlane = &dplanes[pNode->planenum];
-
- if (DotProduct(pt, pPlane->normal) < pPlane->dist)
- node = pNode->children[1];
- else
- node = pNode->children[0];
- }
-
- return - node - 1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Make sure the details are compiled with static prop
-//-----------------------------------------------------------------------------
-static bool IsModelValid( const char* pModelName )
-{
- StaticPropLookup_t lookup;
- lookup.m_ModelName = pModelName;
-
- int i = s_StaticPropLookup.Find( lookup );
- if (i != s_StaticPropLookup.InvalidIndex() )
- return s_StaticPropLookup[i].m_IsValid;
-
- CUtlBuffer buf;
- lookup.m_IsValid = LoadStudioModel( pModelName, "detail_prop", buf );
- if (!lookup.m_IsValid)
- {
- Warning("Error loading studio model \"%s\"!\n", pModelName );
- }
-
- s_StaticPropLookup.Insert( lookup );
- return lookup.m_IsValid;
-}
-
-
-//-----------------------------------------------------------------------------
-// Add a detail to the lump.
-//-----------------------------------------------------------------------------
-static int s_nDetailOverflow = 0;
-static void AddDetailToLump( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
-{
- Assert( pt.IsValid() && angles.IsValid() );
-
- // Make sure the model is valid...
- if (!IsModelValid(pModelName))
- return;
-
- if (s_DetailObjectLump.Count() == 65535)
- {
- ++s_nDetailOverflow;
- return;
- }
-
- // Insert an element into the object dictionary if it aint there...
- int i = s_DetailObjectLump.AddToTail( );
-
- DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
- objectLump.m_DetailModel = AddDetailDictLump( pModelName );
- VectorCopy( angles, objectLump.m_Angles );
- VectorCopy( pt, objectLump.m_Origin );
- objectLump.m_Leaf = ComputeDetailLeaf(pt);
- objectLump.m_Lighting.r = 255;
- objectLump.m_Lighting.g = 255;
- objectLump.m_Lighting.b = 255;
- objectLump.m_Lighting.exponent = 0;
- objectLump.m_LightStyles = 0;
- objectLump.m_LightStyleCount = 0;
- objectLump.m_Orientation = nOrientation;
- objectLump.m_Type = DETAIL_PROP_TYPE_MODEL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Add a detail sprite to the lump.
-//-----------------------------------------------------------------------------
-static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
- const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
- int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
-{
- // Insert an element into the object dictionary if it aint there...
- int i = s_DetailObjectLump.AddToTail( );
-
- if (i >= 65535)
- {
- Error( "Error! Too many detail props emitted on this map! (64K max!)n" );
- }
-
- DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
- objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
- VectorCopy( vecAngles, objectLump.m_Angles );
- VectorCopy( vecOrigin, objectLump.m_Origin );
- objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
- objectLump.m_Lighting.r = 255;
- objectLump.m_Lighting.g = 255;
- objectLump.m_Lighting.b = 255;
- objectLump.m_Lighting.exponent = 0;
- objectLump.m_LightStyles = 0;
- objectLump.m_LightStyleCount = 0;
- objectLump.m_Orientation = nOrientation;
- objectLump.m_Type = iType;
- objectLump.m_flScale = flScale;
- objectLump.m_ShapeAngle = iShapeAngle;
- objectLump.m_ShapeSize = iShapeSize;
- objectLump.m_SwayAmount = iSwayAmount;
-}
-
-static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
-{
- AddDetailSpriteToLump( vecOrigin,
- vecAngles,
- model.m_Orientation,
- model.m_Pos,
- model.m_Tex,
- flScale,
- model.m_Type,
- model.m_ShapeAngle,
- model.m_ShapeSize,
- model.m_SwayAmount );
-}
-
-//-----------------------------------------------------------------------------
-// Got a detail! Place it on the surface...
-//-----------------------------------------------------------------------------
-// BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
-// (only when not in the debugger?)
-// Printing the values of normal at the bottom of the function fixes it as does
-// disabling global optimizations.
-static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
-{
- // But only place it on the surface if it meets the angle constraints...
- float cosAngle = normal.z;
-
- // Never emit if the angle's too steep
- if (cosAngle < model.m_MaxCosAngle)
- return;
-
- // If it's between min + max, flip a coin...
- if (cosAngle < model.m_MinCosAngle)
- {
- float probability = (cosAngle - model.m_MaxCosAngle) /
- (model.m_MinCosAngle - model.m_MaxCosAngle);
-
- float t = rand() / (float)VALVE_RAND_MAX;
- if (t > probability)
- return;
- }
-
- // Compute the orientation of the detail
- QAngle angles;
- if (model.m_Flags & MODELFLAG_UPRIGHT)
- {
- // If it's upright, we just select a random yaw
- angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
- }
- else
- {
- // It's not upright, so it must conform to the ground. Choose
- // a random orientation based on the surface normal
-
- Vector zaxis;
- VectorCopy( normal, zaxis );
- VectorNormalize( zaxis );
-
- // Choose any two arbitrary axes which are perpendicular to the normal
- Vector xaxis( 1, 0, 0 );
- if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
- xaxis.Init( 0, 1, 0 );
- Vector yaxis;
- CrossProduct( zaxis, xaxis, yaxis );
- VectorNormalize( yaxis );
- CrossProduct( yaxis, zaxis, xaxis );
- VectorNormalize( xaxis );
- VMatrix matrix;
- matrix.SetBasisVectors( xaxis, yaxis, zaxis );
- matrix.SetTranslation( vec3_origin );
-
- float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
- VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
- matrix = matrix * rot;
-
- MatrixToAngles( matrix, angles );
- }
-
- // FIXME: We may also want a purely random rotation too
-
- // Insert an element into the object dictionary if it aint there...
- switch ( model.m_Type )
- {
- case DETAIL_PROP_TYPE_MODEL:
- AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
- break;
-
- // Sprites and procedural models made from sprites
- case DETAIL_PROP_TYPE_SPRITE:
- default:
- {
- float flScale = 1.0f;
- if ( model.m_flRandomScaleStdDev != 0.0f )
- {
- flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
- }
-
- AddDetailSpriteToLump( pt, angles, model, flScale );
- }
- break;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects on a face
-//-----------------------------------------------------------------------------
-static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
-{
- if (pFace->numedges < 3)
- return;
-
- // We're going to pick a bunch of random points, and then probabilistically
- // decide whether or not to plant a detail object there.
-
- // Turn the face into a bunch of polygons, and compute the area of each
- int* pSurfEdges = &dsurfedges[pFace->firstedge];
- int vertexIdx = (pSurfEdges[0] < 0);
- int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
- dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
- for (int i = 1; i < pFace->numedges - 1; ++i )
- {
- int vertexIdx = (pSurfEdges[i] < 0);
- dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
-
- // Compute two triangle edges
- Vector e1, e2;
- VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
- VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
-
- // Compute the triangle area
- Vector areaVec;
- CrossProduct( e1, e2, areaVec );
- float normalLength = areaVec.Length();
- float area = 0.5f * normalLength;
-
- // Compute the number of samples to take
- int numSamples = area * detail.m_Density * 0.000001;
-
- // Now take a sample, and randomly place an object there
- for (int i = 0; i < numSamples; ++i )
- {
- // Create a random sample...
- float u = rand() / (float)VALVE_RAND_MAX;
- float v = rand() / (float)VALVE_RAND_MAX;
- if (v > 1.0f - u)
- {
- u = 1.0f - u;
- v = 1.0f - v;
- assert( u + v <= 1.0f );
- }
-
- // Compute alpha
- float alpha = 1.0f;
-
- // Select a group based on the alpha value
- int group = SelectGroup( detail, alpha );
-
- // Now that we've got a group, choose a detail
- int model = SelectDetail( detail.m_Groups[group] );
- if (model < 0)
- continue;
-
- // Got a detail! Place it on the surface...
- Vector pt, normal;
- VectorMA( pFirstVertex->point, u, e1, pt );
- VectorMA( pt, v, e2, pt );
- VectorDivide( areaVec, -normalLength, normal );
-
- PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects on a face
-//-----------------------------------------------------------------------------
-static float ComputeDisplacementFaceArea( dface_t* pFace )
-{
- float area = 0.0f;
-
- // Compute the area of the base face
- int* pSurfEdges = &dsurfedges[pFace->firstedge];
- int vertexIdx = (pSurfEdges[0] < 0);
- int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
- dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
- for (int i = 1; i <= 2; ++i )
- {
- int vertexIdx = (pSurfEdges[i] < 0);
- dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
-
- // Compute two triangle edges
- Vector e1, e2;
- VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
- VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
-
- // Compute the triangle area
- Vector areaVec;
- CrossProduct( e1, e2, areaVec );
- float normalLength = areaVec.Length();
- area += 0.5f * normalLength;
- }
-
- return area;
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects on a face
-//-----------------------------------------------------------------------------
-static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
- DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
-{
- assert(pFace->numedges == 4);
-
- // We're going to pick a bunch of random points, and then probabilistically
- // decide whether or not to plant a detail object there.
-
- // Compute the area of the base face
- float area = ComputeDisplacementFaceArea( pFace );
-
- // Compute the number of samples to take
- int numSamples = area * detail.m_Density * 0.000001;
-
- // Now take a sample, and randomly place an object there
- for (int i = 0; i < numSamples; ++i )
- {
- // Create a random sample...
- float u = rand() / (float)VALVE_RAND_MAX;
- float v = rand() / (float)VALVE_RAND_MAX;
-
- // Compute alpha
- float alpha;
- Vector pt, normal;
- coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
- alpha /= 255.0f;
-
- // Select a group based on the alpha value
- int group = SelectGroup( detail, alpha );
-
- // Now that we've got a group, choose a detail
- int model = SelectDetail( detail.m_Groups[group] );
- if (model < 0)
- continue;
-
- // Got a detail! Place it on the surface...
- PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Sort detail objects by leaf
-//-----------------------------------------------------------------------------
-static int SortFunc( const void *arg1, const void *arg2 )
-{
- int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
- if ( nDelta < 0 )
- return -1;
- if ( nDelta > 0 )
- return 1;
- return 0;
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects in the lump
-//-----------------------------------------------------------------------------
-static void SetLumpData( )
-{
- // Sort detail props by leaf
- qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
-
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
- if (handle != g_GameLumps.InvalidGameLump())
- {
- g_GameLumps.DestroyGameLump(handle);
- }
- int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
- int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
- int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
- int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
-
- handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
-
- // Serialize the data
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
- buf.PutInt( s_DetailObjectDictLump.Count() );
- if (nDictSize)
- {
- buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
- }
- buf.PutInt( s_DetailSpriteDictLump.Count() );
- if (nSpriteDictSize)
- {
- buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
- }
- buf.PutInt( s_DetailObjectLump.Count() );
- if (nObjSize)
- {
- buf.Put( s_DetailObjectLump.Base(), nObjSize );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects in the level
-//-----------------------------------------------------------------------------
-void EmitDetailModels()
-{
- StartPacifier("Placing detail props : ");
-
- // Place stuff on each face
- dface_t* pFace = dfaces;
- for (int j = 0; j < numfaces; ++j)
- {
- UpdatePacifier( (float)j / (float)numfaces );
-
- // Get at the material associated with this face
- texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
- dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
-
- // Try to get at the material
- bool found;
- MaterialSystemMaterial_t handle =
- FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
- &found, false );
- if (!found)
- continue;
-
- // See if its got any detail objects on it
- const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
- if (!pDetailType)
- continue;
-
- // Get the detail type...
- DetailObject_t search;
- search.m_Name = pDetailType;
- int objectType = s_DetailObjectDict.Find(search);
- if (objectType < 0)
- {
- Warning("Material %s uses unknown detail object type %s!\n",
- TexDataStringTable_GetString( pTexData->nameStringTableID ),
- pDetailType);
- continue;
- }
-
- // Emit objects on a particular face
- DetailObject_t& detail = s_DetailObjectDict[objectType];
-
- // Initialize the Random Number generators for detail prop placement based on the hammer Face num.
- int detailpropseed = dfaceids[j].hammerfaceid;
-#ifdef WARNSEEDNUMBER
- Warning( "[%d]\n",detailpropseed );
-#endif
- srand( detailpropseed );
- RandomSeed( detailpropseed );
-
- if (pFace[j].dispinfo < 0)
- {
- EmitDetailObjectsOnFace( &pFace[j], detail );
- }
- else
- {
- // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
- mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
- CCoreDispInfo coreDispInfo;
- DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
-
- EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
- }
- }
-
- // Emit specifically specified detail props
- Vector origin;
- QAngle angles;
- Vector2D pos[2];
- Vector2D tex[2];
- for (int i = 0; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
- {
- GetVectorForKey( &entities[i], "origin", origin );
- GetAnglesForKey( &entities[i], "angles", angles );
- char* pModelName = ValueForKey( &entities[i], "model" );
- int nOrientation = IntForKey( &entities[i], "detailOrientation" );
-
- AddDetailToLump( pModelName, origin, angles, nOrientation );
-
- // strip this ent from the .bsp file
- entities[i].epairs = 0;
- continue;
- }
-
- if (!strcmp(pEntity, "prop_detail_sprite"))
- {
- GetVectorForKey( &entities[i], "origin", origin );
- GetAnglesForKey( &entities[i], "angles", angles );
- int nOrientation = IntForKey( &entities[i], "detailOrientation" );
- GetVector2DForKey( &entities[i], "position_ul", pos[0] );
- GetVector2DForKey( &entities[i], "position_lr", pos[1] );
- GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
- GetVector2DForKey( &entities[i], "tex_size", tex[1] );
- float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
-
- tex[1].x += tex[0].x - 0.5f;
- tex[1].y += tex[0].y - 0.5f;
- tex[0].x += 0.5f;
- tex[0].y += 0.5f;
- tex[0] /= flTextureSize;
- tex[1] /= flTextureSize;
-
- AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
-
- // strip this ent from the .bsp file
- entities[i].epairs = 0;
- continue;
- }
- }
-
- EndPacifier( true );
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Detail Objects in the level
-//-----------------------------------------------------------------------------
-void EmitDetailObjects()
-{
- EmitDetailModels();
-
- // Done! Now lets add the lumps (destroy previous ones)
- SetLumpData( );
-
- if ( s_nDetailOverflow != 0 )
- {
- Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Places "detail" objects which are client-only renderable things
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include <windows.h>
+#include "vbsp.h"
+#include "bsplib.h"
+#include "KeyValues.h"
+#include "utlsymbol.h"
+#include "utlvector.h"
+#include <io.h>
+#include "bspfile.h"
+#include "utilmatlib.h"
+#include "gamebspfile.h"
+#include "mathlib/VMatrix.h"
+#include "materialpatch.h"
+#include "pacifier.h"
+#include "vstdlib/random.h"
+#include "builddisp.h"
+#include "disp_vbsp.h"
+#include "UtlBuffer.h"
+#include "CollisionUtils.h"
+#include <float.h>
+#include "UtlLinkedList.h"
+#include "byteswap.h"
+#include "writebsp.h"
+
+//-----------------------------------------------------------------------------
+// Information about particular detail object types
+//-----------------------------------------------------------------------------
+enum
+{
+ MODELFLAG_UPRIGHT = 0x1,
+};
+
+struct DetailModel_t
+{
+ CUtlSymbol m_ModelName;
+ float m_Amount;
+ float m_MinCosAngle;
+ float m_MaxCosAngle;
+ int m_Flags;
+ int m_Orientation;
+ int m_Type;
+ Vector2D m_Pos[2];
+ Vector2D m_Tex[2];
+ float m_flRandomScaleStdDev;
+ unsigned char m_ShapeSize;
+ unsigned char m_ShapeAngle;
+ unsigned char m_SwayAmount;
+};
+
+struct DetailObjectGroup_t
+{
+ float m_Alpha;
+ CUtlVector< DetailModel_t > m_Models;
+};
+
+struct DetailObject_t
+{
+ CUtlSymbol m_Name;
+ float m_Density;
+ CUtlVector< DetailObjectGroup_t > m_Groups;
+
+ bool operator==(const DetailObject_t& src ) const
+ {
+ return src.m_Name == m_Name;
+ }
+};
+
+static CUtlVector<DetailObject_t> s_DetailObjectDict;
+
+
+//-----------------------------------------------------------------------------
+// Error checking.. make sure the model is valid + is a static prop
+//-----------------------------------------------------------------------------
+struct StaticPropLookup_t
+{
+ CUtlSymbol m_ModelName;
+ bool m_IsValid;
+};
+
+static bool StaticLess( StaticPropLookup_t const& src1, StaticPropLookup_t const& src2 )
+{
+ return src1.m_ModelName < src2.m_ModelName;
+}
+
+static CUtlRBTree< StaticPropLookup_t, unsigned short > s_StaticPropLookup( 0, 32, StaticLess );
+
+
+//-----------------------------------------------------------------------------
+// These puppies are used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<DetailObjectDictLump_t> s_DetailObjectDictLump;
+static CUtlVector<DetailObjectLump_t> s_DetailObjectLump;
+static CUtlVector<DetailSpriteDictLump_t> s_DetailSpriteDictLump;
+
+
+//-----------------------------------------------------------------------------
+// Parses the key-value pairs in the detail.rad file
+//-----------------------------------------------------------------------------
+static void ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
+{
+ // Sort the group by alpha
+ float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
+
+ int i = s_DetailObjectDict[detailId].m_Groups.Count();
+ while ( --i >= 0 )
+ {
+ if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
+ break;
+ }
+
+ // Insert after the first guy who's more transparent that we are!
+ i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
+ DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
+
+ group.m_Alpha = alpha;
+
+ // Add in all the model groups
+ KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
+ float totalAmount = 0.0f;
+ while( pIter )
+ {
+ if (pIter->GetFirstSubKey())
+ {
+ int i = group.m_Models.AddToTail();
+
+ DetailModel_t &model = group.m_Models[i];
+
+ model.m_ModelName = pIter->GetString( "model", 0 );
+ if (model.m_ModelName != UTL_INVAL_SYMBOL)
+ {
+ model.m_Type = DETAIL_PROP_TYPE_MODEL;
+ }
+ else
+ {
+ const char *pSpriteData = pIter->GetString( "sprite", 0 );
+ if (pSpriteData)
+ {
+ const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
+
+ if ( pProcModelType )
+ {
+ if ( !Q_stricmp( pProcModelType, "cross" ) )
+ {
+ model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
+ }
+ else if ( !Q_stricmp( pProcModelType, "tri" ) )
+ {
+ model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
+ }
+ else
+ model.m_Type = DETAIL_PROP_TYPE_SPRITE;
+ }
+ else
+ {
+ // card sprite
+ model.m_Type = DETAIL_PROP_TYPE_SPRITE;
+ }
+
+ model.m_Tex[0].Init();
+ model.m_Tex[1].Init();
+
+ float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
+ int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
+ if ( (nValid != 5) || (flTextureSize == 0) )
+ {
+ Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName.String() );
+ }
+
+ model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
+ model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
+ model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
+ model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
+
+ model.m_Pos[0].Init( -10, 20 );
+ model.m_Pos[1].Init( 10, 0 );
+
+ pSpriteData = pIter->GetString( "spritesize", 0 );
+ if (pSpriteData)
+ {
+ sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
+
+ float ox = flWidth * x;
+ float oy = flHeight * y;
+
+ model.m_Pos[0].x = -ox;
+ model.m_Pos[0].y = flHeight - oy;
+ model.m_Pos[1].x = flWidth - ox;
+ model.m_Pos[1].y = -oy;
+ }
+
+ model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
+
+ // sway is a percent of max sway, cl_detail_max_sway
+ float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
+ model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
+
+ // shape angle
+ // for the tri shape, this is the angle each side is fanned out
+ model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
+
+ // shape size
+ // for the tri shape, this is the distance from the origin to the center of a side
+ float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
+ model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
+ }
+ }
+
+ model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
+ totalAmount = model.m_Amount;
+
+ model.m_Flags = 0;
+ if (pIter->GetInt( "upright", 0 ))
+ {
+ model.m_Flags |= MODELFLAG_UPRIGHT;
+ }
+
+ // These are used to prevent emission on steep surfaces
+ float minAngle = pIter->GetFloat( "minAngle", 180 );
+ float maxAngle = pIter->GetFloat( "maxAngle", 180 );
+ model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
+ model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
+ model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
+
+ // Make sure minAngle < maxAngle
+ if ( model.m_MinCosAngle < model.m_MaxCosAngle)
+ {
+ model.m_MinCosAngle = model.m_MaxCosAngle;
+ }
+ }
+ pIter = pIter->GetNextKey();
+ }
+
+ // renormalize the amount if the total > 1
+ if (totalAmount > 1.0f)
+ {
+ for (i = 0; i < group.m_Models.Count(); ++i)
+ {
+ group.m_Models[i].m_Amount /= totalAmount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses the key-value pairs in the detail.vbsp file
+//-----------------------------------------------------------------------------
+static void ParseDetailObjectFile( KeyValues& keyValues )
+{
+ // Iterate over all detail object groups...
+ KeyValues* pIter;
+ for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
+ {
+ if (!pIter->GetFirstSubKey())
+ continue;
+
+ int i = s_DetailObjectDict.AddToTail( );
+ s_DetailObjectDict[i].m_Name = pIter->GetName() ;
+ s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
+
+ // Iterate over all detail object groups...
+ KeyValues* pIterGroups = pIter->GetFirstSubKey();
+ while( pIterGroups )
+ {
+ if (pIterGroups->GetFirstSubKey())
+ {
+ ParseDetailGroup( i, pIterGroups );
+ }
+ pIterGroups = pIterGroups->GetNextKey();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the name of the detail.vbsp file to use
+//-----------------------------------------------------------------------------
+static const char *FindDetailVBSPName( void )
+{
+ for( int i = 0; i < num_entities; i++ )
+ {
+ char* pEntity = ValueForKey( &entities[i], "classname" );
+ if ( !strcmp( pEntity, "worldspawn" ) )
+ {
+ const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
+ if ( !pDetailVBSP || !pDetailVBSP[0] )
+ {
+ pDetailVBSP = "detail.vbsp";
+ }
+ return pDetailVBSP;
+ }
+ }
+ return "detail.vbsp";
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads up the detail object dictionary
+//-----------------------------------------------------------------------------
+void LoadEmitDetailObjectDictionary( const char* pGameDir )
+{
+ // Set the required global lights filename and try looking in qproject
+ const char *pDetailVBSP = FindDetailVBSPName();
+ KeyValues * values = new KeyValues( pDetailVBSP );
+ if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
+ {
+ ParseDetailObjectFile( *values );
+ }
+ values->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a detail group
+//-----------------------------------------------------------------------------
+static int SelectGroup( const DetailObject_t& detail, float alpha )
+{
+ // Find the two groups whose alpha we're between...
+ int start, end;
+ for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
+ {
+ if (alpha < detail.m_Groups[start+1].m_Alpha)
+ break;
+ }
+
+ end = start + 1;
+ if (end >= detail.m_Groups.Count())
+ --end;
+
+ if (start == end)
+ return start;
+
+ // Figure out how far we are between start and end...
+ float dist = 0.0f;
+ float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
+ if (dAlpha != 0.0f)
+ {
+ dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
+ }
+
+ // Pick a number, any number...
+ float r = rand() / (float)VALVE_RAND_MAX;
+
+ // When dist == 0, we *always* want start.
+ // When dist == 1, we *always* want end
+ // That's why this logic looks a little reversed
+ return (r > dist) ? start : end;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a detail object
+//-----------------------------------------------------------------------------
+static int SelectDetail( DetailObjectGroup_t const& group )
+{
+ // Pick a number, any number...
+ float r = rand() / (float)VALVE_RAND_MAX;
+
+ // Look through the list of models + pick the one associated with this number
+ for ( int i = 0; i < group.m_Models.Count(); ++i )
+ {
+ if (r <= group.m_Models[i].m_Amount)
+ return i;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a detail dictionary element (expected to oftentimes be shared)
+//-----------------------------------------------------------------------------
+static int AddDetailDictLump( const char* pModelName )
+{
+ DetailObjectDictLump_t dictLump;
+ Q_strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
+
+ for (int i = s_DetailObjectDictLump.Count(); --i >= 0; )
+ {
+ if (!memcmp(&s_DetailObjectDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_DetailObjectDictLump.AddToTail( dictLump );
+}
+
+static int AddDetailSpriteDictLump( const Vector2D *pPos, const Vector2D *pTex )
+{
+ DetailSpriteDictLump_t dictLump;
+ dictLump.m_UL = pPos[0];
+ dictLump.m_LR = pPos[1];
+ dictLump.m_TexUL = pTex[0];
+ dictLump.m_TexLR = pTex[1];
+
+ for (int i = s_DetailSpriteDictLump.Count(); --i >= 0; )
+ {
+ if (!memcmp(&s_DetailSpriteDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_DetailSpriteDictLump.AddToTail( dictLump );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the leaf that the detail lies in
+//-----------------------------------------------------------------------------
+static int ComputeDetailLeaf( const Vector& pt )
+{
+ int node = 0;
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if (DotProduct(pt, pPlane->normal) < pPlane->dist)
+ node = pNode->children[1];
+ else
+ node = pNode->children[0];
+ }
+
+ return - node - 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Make sure the details are compiled with static prop
+//-----------------------------------------------------------------------------
+static bool IsModelValid( const char* pModelName )
+{
+ StaticPropLookup_t lookup;
+ lookup.m_ModelName = pModelName;
+
+ int i = s_StaticPropLookup.Find( lookup );
+ if (i != s_StaticPropLookup.InvalidIndex() )
+ return s_StaticPropLookup[i].m_IsValid;
+
+ CUtlBuffer buf;
+ lookup.m_IsValid = LoadStudioModel( pModelName, "detail_prop", buf );
+ if (!lookup.m_IsValid)
+ {
+ Warning("Error loading studio model \"%s\"!\n", pModelName );
+ }
+
+ s_StaticPropLookup.Insert( lookup );
+ return lookup.m_IsValid;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a detail to the lump.
+//-----------------------------------------------------------------------------
+static int s_nDetailOverflow = 0;
+static void AddDetailToLump( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
+{
+ Assert( pt.IsValid() && angles.IsValid() );
+
+ // Make sure the model is valid...
+ if (!IsModelValid(pModelName))
+ return;
+
+ if (s_DetailObjectLump.Count() == 65535)
+ {
+ ++s_nDetailOverflow;
+ return;
+ }
+
+ // Insert an element into the object dictionary if it aint there...
+ int i = s_DetailObjectLump.AddToTail( );
+
+ DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
+ objectLump.m_DetailModel = AddDetailDictLump( pModelName );
+ VectorCopy( angles, objectLump.m_Angles );
+ VectorCopy( pt, objectLump.m_Origin );
+ objectLump.m_Leaf = ComputeDetailLeaf(pt);
+ objectLump.m_Lighting.r = 255;
+ objectLump.m_Lighting.g = 255;
+ objectLump.m_Lighting.b = 255;
+ objectLump.m_Lighting.exponent = 0;
+ objectLump.m_LightStyles = 0;
+ objectLump.m_LightStyleCount = 0;
+ objectLump.m_Orientation = nOrientation;
+ objectLump.m_Type = DETAIL_PROP_TYPE_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a detail sprite to the lump.
+//-----------------------------------------------------------------------------
+static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
+ const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
+ int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
+{
+ // Insert an element into the object dictionary if it aint there...
+ int i = s_DetailObjectLump.AddToTail( );
+
+ if (i >= 65535)
+ {
+ Error( "Error! Too many detail props emitted on this map! (64K max!)n" );
+ }
+
+ DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
+ objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
+ VectorCopy( vecAngles, objectLump.m_Angles );
+ VectorCopy( vecOrigin, objectLump.m_Origin );
+ objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
+ objectLump.m_Lighting.r = 255;
+ objectLump.m_Lighting.g = 255;
+ objectLump.m_Lighting.b = 255;
+ objectLump.m_Lighting.exponent = 0;
+ objectLump.m_LightStyles = 0;
+ objectLump.m_LightStyleCount = 0;
+ objectLump.m_Orientation = nOrientation;
+ objectLump.m_Type = iType;
+ objectLump.m_flScale = flScale;
+ objectLump.m_ShapeAngle = iShapeAngle;
+ objectLump.m_ShapeSize = iShapeSize;
+ objectLump.m_SwayAmount = iSwayAmount;
+}
+
+static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
+{
+ AddDetailSpriteToLump( vecOrigin,
+ vecAngles,
+ model.m_Orientation,
+ model.m_Pos,
+ model.m_Tex,
+ flScale,
+ model.m_Type,
+ model.m_ShapeAngle,
+ model.m_ShapeSize,
+ model.m_SwayAmount );
+}
+
+//-----------------------------------------------------------------------------
+// Got a detail! Place it on the surface...
+//-----------------------------------------------------------------------------
+// BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
+// (only when not in the debugger?)
+// Printing the values of normal at the bottom of the function fixes it as does
+// disabling global optimizations.
+static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
+{
+ // But only place it on the surface if it meets the angle constraints...
+ float cosAngle = normal.z;
+
+ // Never emit if the angle's too steep
+ if (cosAngle < model.m_MaxCosAngle)
+ return;
+
+ // If it's between min + max, flip a coin...
+ if (cosAngle < model.m_MinCosAngle)
+ {
+ float probability = (cosAngle - model.m_MaxCosAngle) /
+ (model.m_MinCosAngle - model.m_MaxCosAngle);
+
+ float t = rand() / (float)VALVE_RAND_MAX;
+ if (t > probability)
+ return;
+ }
+
+ // Compute the orientation of the detail
+ QAngle angles;
+ if (model.m_Flags & MODELFLAG_UPRIGHT)
+ {
+ // If it's upright, we just select a random yaw
+ angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
+ }
+ else
+ {
+ // It's not upright, so it must conform to the ground. Choose
+ // a random orientation based on the surface normal
+
+ Vector zaxis;
+ VectorCopy( normal, zaxis );
+ VectorNormalize( zaxis );
+
+ // Choose any two arbitrary axes which are perpendicular to the normal
+ Vector xaxis( 1, 0, 0 );
+ if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
+ xaxis.Init( 0, 1, 0 );
+ Vector yaxis;
+ CrossProduct( zaxis, xaxis, yaxis );
+ VectorNormalize( yaxis );
+ CrossProduct( yaxis, zaxis, xaxis );
+ VectorNormalize( xaxis );
+ VMatrix matrix;
+ matrix.SetBasisVectors( xaxis, yaxis, zaxis );
+ matrix.SetTranslation( vec3_origin );
+
+ float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
+ VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
+ matrix = matrix * rot;
+
+ MatrixToAngles( matrix, angles );
+ }
+
+ // FIXME: We may also want a purely random rotation too
+
+ // Insert an element into the object dictionary if it aint there...
+ switch ( model.m_Type )
+ {
+ case DETAIL_PROP_TYPE_MODEL:
+ AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
+ break;
+
+ // Sprites and procedural models made from sprites
+ case DETAIL_PROP_TYPE_SPRITE:
+ default:
+ {
+ float flScale = 1.0f;
+ if ( model.m_flRandomScaleStdDev != 0.0f )
+ {
+ flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
+ }
+
+ AddDetailSpriteToLump( pt, angles, model, flScale );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
+{
+ if (pFace->numedges < 3)
+ return;
+
+ // We're going to pick a bunch of random points, and then probabilistically
+ // decide whether or not to plant a detail object there.
+
+ // Turn the face into a bunch of polygons, and compute the area of each
+ int* pSurfEdges = &dsurfedges[pFace->firstedge];
+ int vertexIdx = (pSurfEdges[0] < 0);
+ int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
+ dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
+ for (int i = 1; i < pFace->numedges - 1; ++i )
+ {
+ int vertexIdx = (pSurfEdges[i] < 0);
+ dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
+
+ // Compute two triangle edges
+ Vector e1, e2;
+ VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
+ VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
+
+ // Compute the triangle area
+ Vector areaVec;
+ CrossProduct( e1, e2, areaVec );
+ float normalLength = areaVec.Length();
+ float area = 0.5f * normalLength;
+
+ // Compute the number of samples to take
+ int numSamples = area * detail.m_Density * 0.000001;
+
+ // Now take a sample, and randomly place an object there
+ for (int i = 0; i < numSamples; ++i )
+ {
+ // Create a random sample...
+ float u = rand() / (float)VALVE_RAND_MAX;
+ float v = rand() / (float)VALVE_RAND_MAX;
+ if (v > 1.0f - u)
+ {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ assert( u + v <= 1.0f );
+ }
+
+ // Compute alpha
+ float alpha = 1.0f;
+
+ // Select a group based on the alpha value
+ int group = SelectGroup( detail, alpha );
+
+ // Now that we've got a group, choose a detail
+ int model = SelectDetail( detail.m_Groups[group] );
+ if (model < 0)
+ continue;
+
+ // Got a detail! Place it on the surface...
+ Vector pt, normal;
+ VectorMA( pFirstVertex->point, u, e1, pt );
+ VectorMA( pt, v, e2, pt );
+ VectorDivide( areaVec, -normalLength, normal );
+
+ PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static float ComputeDisplacementFaceArea( dface_t* pFace )
+{
+ float area = 0.0f;
+
+ // Compute the area of the base face
+ int* pSurfEdges = &dsurfedges[pFace->firstedge];
+ int vertexIdx = (pSurfEdges[0] < 0);
+ int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
+ dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
+ for (int i = 1; i <= 2; ++i )
+ {
+ int vertexIdx = (pSurfEdges[i] < 0);
+ dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
+
+ // Compute two triangle edges
+ Vector e1, e2;
+ VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
+ VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
+
+ // Compute the triangle area
+ Vector areaVec;
+ CrossProduct( e1, e2, areaVec );
+ float normalLength = areaVec.Length();
+ area += 0.5f * normalLength;
+ }
+
+ return area;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
+ DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
+{
+ assert(pFace->numedges == 4);
+
+ // We're going to pick a bunch of random points, and then probabilistically
+ // decide whether or not to plant a detail object there.
+
+ // Compute the area of the base face
+ float area = ComputeDisplacementFaceArea( pFace );
+
+ // Compute the number of samples to take
+ int numSamples = area * detail.m_Density * 0.000001;
+
+ // Now take a sample, and randomly place an object there
+ for (int i = 0; i < numSamples; ++i )
+ {
+ // Create a random sample...
+ float u = rand() / (float)VALVE_RAND_MAX;
+ float v = rand() / (float)VALVE_RAND_MAX;
+
+ // Compute alpha
+ float alpha;
+ Vector pt, normal;
+ coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
+ alpha /= 255.0f;
+
+ // Select a group based on the alpha value
+ int group = SelectGroup( detail, alpha );
+
+ // Now that we've got a group, choose a detail
+ int model = SelectDetail( detail.m_Groups[group] );
+ if (model < 0)
+ continue;
+
+ // Got a detail! Place it on the surface...
+ PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sort detail objects by leaf
+//-----------------------------------------------------------------------------
+static int SortFunc( const void *arg1, const void *arg2 )
+{
+ int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
+ if ( nDelta < 0 )
+ return -1;
+ if ( nDelta > 0 )
+ return 1;
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the lump
+//-----------------------------------------------------------------------------
+static void SetLumpData( )
+{
+ // Sort detail props by leaf
+ qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
+
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
+ if (handle != g_GameLumps.InvalidGameLump())
+ {
+ g_GameLumps.DestroyGameLump(handle);
+ }
+ int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
+ int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
+ int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
+ int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
+
+ handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
+ buf.PutInt( s_DetailObjectDictLump.Count() );
+ if (nDictSize)
+ {
+ buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
+ }
+ buf.PutInt( s_DetailSpriteDictLump.Count() );
+ if (nSpriteDictSize)
+ {
+ buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
+ }
+ buf.PutInt( s_DetailObjectLump.Count() );
+ if (nObjSize)
+ {
+ buf.Put( s_DetailObjectLump.Base(), nObjSize );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the level
+//-----------------------------------------------------------------------------
+void EmitDetailModels()
+{
+ StartPacifier("Placing detail props : ");
+
+ // Place stuff on each face
+ dface_t* pFace = dfaces;
+ for (int j = 0; j < numfaces; ++j)
+ {
+ UpdatePacifier( (float)j / (float)numfaces );
+
+ // Get at the material associated with this face
+ texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
+ dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
+
+ // Try to get at the material
+ bool found;
+ MaterialSystemMaterial_t handle =
+ FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
+ &found, false );
+ if (!found)
+ continue;
+
+ // See if its got any detail objects on it
+ const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
+ if (!pDetailType)
+ continue;
+
+ // Get the detail type...
+ DetailObject_t search;
+ search.m_Name = pDetailType;
+ int objectType = s_DetailObjectDict.Find(search);
+ if (objectType < 0)
+ {
+ Warning("Material %s uses unknown detail object type %s!\n",
+ TexDataStringTable_GetString( pTexData->nameStringTableID ),
+ pDetailType);
+ continue;
+ }
+
+ // Emit objects on a particular face
+ DetailObject_t& detail = s_DetailObjectDict[objectType];
+
+ // Initialize the Random Number generators for detail prop placement based on the hammer Face num.
+ int detailpropseed = dfaceids[j].hammerfaceid;
+#ifdef WARNSEEDNUMBER
+ Warning( "[%d]\n",detailpropseed );
+#endif
+ srand( detailpropseed );
+ RandomSeed( detailpropseed );
+
+ if (pFace[j].dispinfo < 0)
+ {
+ EmitDetailObjectsOnFace( &pFace[j], detail );
+ }
+ else
+ {
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
+ CCoreDispInfo coreDispInfo;
+ DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
+
+ EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
+ }
+ }
+
+ // Emit specifically specified detail props
+ Vector origin;
+ QAngle angles;
+ Vector2D pos[2];
+ Vector2D tex[2];
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
+ {
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ char* pModelName = ValueForKey( &entities[i], "model" );
+ int nOrientation = IntForKey( &entities[i], "detailOrientation" );
+
+ AddDetailToLump( pModelName, origin, angles, nOrientation );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ continue;
+ }
+
+ if (!strcmp(pEntity, "prop_detail_sprite"))
+ {
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ int nOrientation = IntForKey( &entities[i], "detailOrientation" );
+ GetVector2DForKey( &entities[i], "position_ul", pos[0] );
+ GetVector2DForKey( &entities[i], "position_lr", pos[1] );
+ GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
+ GetVector2DForKey( &entities[i], "tex_size", tex[1] );
+ float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
+
+ tex[1].x += tex[0].x - 0.5f;
+ tex[1].y += tex[0].y - 0.5f;
+ tex[0].x += 0.5f;
+ tex[0].y += 0.5f;
+ tex[0] /= flTextureSize;
+ tex[1] /= flTextureSize;
+
+ AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ continue;
+ }
+ }
+
+ EndPacifier( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the level
+//-----------------------------------------------------------------------------
+void EmitDetailObjects()
+{
+ EmitDetailModels();
+
+ // Done! Now lets add the lumps (destroy previous ones)
+ SetLumpData( );
+
+ if ( s_nDetailOverflow != 0 )
+ {
+ Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
+ }
+}
diff --git a/mp/src/utils/vbsp/disp_ivp.cpp b/mp/src/utils/vbsp/disp_ivp.cpp
index 3fe50799..1ed16518 100644
--- a/mp/src/utils/vbsp/disp_ivp.cpp
+++ b/mp/src/utils/vbsp/disp_ivp.cpp
@@ -1,359 +1,359 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-#include "vbsp.h"
-#include "disp_vbsp.h"
-#include "builddisp.h"
-#include "disp_common.h"
-#include "ivp.h"
-#include "disp_ivp.h"
-#include "vphysics_interface.h"
-#include "vphysics/virtualmesh.h"
-#include "utlrbtree.h"
-#include "tier1/utlbuffer.h"
-#include "materialpatch.h"
-
-struct disp_grid_t
-{
- int gridIndex;
- CUtlVector<int> dispList;
-};
-
-static CUtlVector<disp_grid_t> gDispGridList;
-
-
-
-disp_grid_t &FindOrInsertGrid( int gridIndex )
-{
- // linear search is slow, but only a few grids will be present
- for ( int i = gDispGridList.Count()-1; i >= 0; i-- )
- {
- if ( gDispGridList[i].gridIndex == gridIndex )
- {
- return gDispGridList[i];
- }
- }
- int index = gDispGridList.AddToTail();
- gDispGridList[index].gridIndex = gridIndex;
-
- // must be empty
- Assert( gDispGridList[index].dispList.Count() == 0 );
-
- return gDispGridList[index];
-}
-
-// UNDONE: Tune these or adapt them to map size or triangle count?
-#define DISP_GRID_SIZEX 4096
-#define DISP_GRID_SIZEY 4096
-#define DISP_GRID_SIZEZ 8192
-
-int Disp_GridIndex( CCoreDispInfo *pDispInfo )
-{
- // quick hash the center into the grid and put the whole terrain in that grid
- Vector mins, maxs;
- pDispInfo->GetNode(0)->GetBoundingBox( mins, maxs );
- Vector center;
- center = 0.5 * (mins + maxs);
- // make sure it's positive
- center += Vector(MAX_COORD_INTEGER,MAX_COORD_INTEGER,MAX_COORD_INTEGER);
- int gridX = center.x / DISP_GRID_SIZEX;
- int gridY = center.y / DISP_GRID_SIZEY;
- int gridZ = center.z / DISP_GRID_SIZEZ;
-
- gridX &= 0xFF;
- gridY &= 0xFF;
- gridZ &= 0xFF;
- return MAKEID( gridX, gridY, gridZ, 0 );
-}
-
-void AddToGrid( int gridIndex, int dispIndex )
-{
- disp_grid_t &grid = FindOrInsertGrid( gridIndex );
- grid.dispList.AddToTail( dispIndex );
-}
-
-MaterialSystemMaterial_t GetMatIDFromDisp( mapdispinfo_t *pMapDisp )
-{
- texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- MaterialSystemMaterial_t matID = FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
- return matID;
-}
-
-// check this and disable virtual mesh if in use
-bool Disp_HasPower4Displacements()
-{
- for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
- {
- if ( g_CoreDispInfos[i]->GetPower() > 3 )
- {
- return true;
- }
- }
- return false;
-}
-
-// adds all displacement faces as a series of convex objects
-// UNDONE: Only add the displacements for this model?
-void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask)
-{
- int dispIndex;
-
- // Add each displacement to the grid hash
- for ( dispIndex = 0; dispIndex < g_CoreDispInfos.Count(); dispIndex++ )
- {
- CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
- mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
-
- // not solid for this pass
- if ( !(pMapDisp->contents & contentsMask) )
- continue;
-
- int gridIndex = Disp_GridIndex( pDispInfo );
- AddToGrid( gridIndex, dispIndex );
- }
-
- // now make a polysoup for the terrain in each grid
- for ( int grid = 0; grid < gDispGridList.Count(); grid++ )
- {
- int triCount = 0;
- CPhysPolysoup *pTerrainPhysics = physcollision->PolysoupCreate();
-
- // iterate the displacements in this grid
- for ( int listIndex = 0; listIndex < gDispGridList[grid].dispList.Count(); listIndex++ )
- {
- dispIndex = gDispGridList[grid].dispList[listIndex];
- CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
- mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
-
- // Get the material id.
- MaterialSystemMaterial_t matID = GetMatIDFromDisp( pMapDisp );
-
- // Build a triangle list. This shares the tesselation code with the engine.
- CUtlVector<unsigned short> indices;
- CVBSPTesselateHelper helper;
- helper.m_pIndices = &indices;
- helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
- helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
-
- ::TesselateDisplacement( &helper );
-
- Assert( indices.Count() > 0 );
- Assert( indices.Count() % 3 == 0 ); // Make sure indices are a multiple of 3.
- int nTriCount = indices.Count() / 3;
- triCount += nTriCount;
- if ( triCount >= 65536 )
- {
- // don't put more than 64K tris in any single collision model
- CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
- if ( pCollide )
- {
- collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
- }
- // Throw this polysoup away and start over for the remaining triangles
- physcollision->PolysoupDestroy( pTerrainPhysics );
- pTerrainPhysics = physcollision->PolysoupCreate();
- triCount = nTriCount;
- }
- Vector tmpVerts[3];
- for ( int iTri = 0; iTri < nTriCount; ++iTri )
- {
- float flAlphaTotal = 0.0f;
- for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
- {
- pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
- flAlphaTotal += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
- }
-
- int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
- if ( flAlphaTotal > DISP_ALPHA_PROP_DELTA )
- {
- int nProp2 = GetSurfaceProperties2( matID, "surfaceprop2" );
- if ( nProp2 != -1 )
- {
- nProp = nProp2;
- }
- }
- int nMaterialIndex = RemapWorldMaterial( nProp );
- physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
- }
- }
-
- // convert the whole grid's polysoup to a collide and store in the collision list
- CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
- if ( pCollide )
- {
- collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
- }
- // now that we have the collide, we're done with the soup
- physcollision->PolysoupDestroy( pTerrainPhysics );
- }
-}
-
-
-class CDispMeshEvent : public IVirtualMeshEvent
-{
-public:
- CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
- virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
- virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
- virtual void GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList );
-
- CUtlVector<Vector> m_verts;
- unsigned short *m_pIndices;
- int m_indexCount;
-};
-
-CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
-{
- m_pIndices = pIndices;
- m_indexCount = indexCount;
- int maxIndex = 0;
- for ( int i = 0; i < indexCount; i++ )
- {
- if ( pIndices[i] > maxIndex )
- {
- maxIndex = pIndices[i];
- }
- }
- for ( int i = 0; i < indexCount/2; i++ )
- {
- V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
- }
- int count = maxIndex + 1;
- m_verts.SetCount( count );
- for ( int i = 0; i < count; i++ )
- {
- m_verts[i] = pDispInfo->GetVert(i);
- }
-}
-
-void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
-{
- Assert(userData==((void *)this));
- pList->pVerts = m_verts.Base();
- pList->indexCount = m_indexCount;
- pList->triangleCount = m_indexCount/3;
- pList->vertexCount = m_verts.Count();
- pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
- pList->pHull = NULL;
- int indexMax = ARRAYSIZE(pList->indices);
- int indexCount = min(m_indexCount, indexMax);
- Assert(m_indexCount < indexMax);
- Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
-}
-
-void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
-{
- Assert(userData==((void *)this));
- ClearBounds( *pMins, *pMaxs );
- for ( int i = 0; i < m_verts.Count(); i++ )
- {
- AddPointToBounds( m_verts[i], *pMins, *pMaxs );
- }
-}
-
-void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList )
-{
- Assert(userData==((void *)this));
- pList->triangleCount = m_indexCount/3;
- int indexMax = ARRAYSIZE(pList->triangleIndices);
- int indexCount = min(m_indexCount, indexMax);
- Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
- Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
-}
-
-void Disp_BuildVirtualMesh( int contentsMask )
-{
- CUtlVector<CPhysCollide *> virtualMeshes;
- virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
- for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
- {
- CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
- mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
-
- virtualMeshes[i] = NULL;
- // not solid for this pass
- if ( !(pMapDisp->contents & contentsMask) )
- continue;
-
- // Build a triangle list. This shares the tesselation code with the engine.
- CUtlVector<unsigned short> indices;
- CVBSPTesselateHelper helper;
- helper.m_pIndices = &indices;
- helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
- helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
-
- ::TesselateDisplacement( &helper );
-
- // validate the collision data
- if ( 1 )
- {
- int triCount = indices.Count() / 3;
- for ( int j = 0; j < triCount; j++ )
- {
- int index = j * 3;
- Vector v0 = pDispInfo->GetVert( indices[index+0] );
- Vector v1 = pDispInfo->GetVert( indices[index+1] );
- Vector v2 = pDispInfo->GetVert( indices[index+2] );
- if ( v0 == v1 || v1 == v2 || v2 == v0 )
- {
- Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
- texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
-
- Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
- }
- }
-
- }
- CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
- virtualmeshparams_t params;
- params.buildOuterHull = true;
- params.pMeshEventHandler = &meshHandler;
- params.userData = &meshHandler;
- virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
- }
- unsigned int totalSize = 0;
- CUtlBuffer buf;
- dphysdisp_t header;
- header.numDisplacements = g_CoreDispInfos.Count();
- buf.PutObjects( &header );
-
- CUtlVector<char> dispBuf;
- for ( int i = 0; i < header.numDisplacements; i++ )
- {
- if ( virtualMeshes[i] )
- {
- unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
- totalSize += testSize;
- buf.PutShort( testSize );
- }
- else
- {
- buf.PutShort( -1 );
- }
- }
- for ( int i = 0; i < header.numDisplacements; i++ )
- {
- if ( virtualMeshes[i] )
- {
- unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
- dispBuf.RemoveAll();
- dispBuf.EnsureCount(testSize);
-
- unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
- Assert( outSize == testSize );
- buf.Put( dispBuf.Base(), outSize );
- }
- }
- g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
- Assert( buf.TellMaxPut() == g_PhysDispSize );
- g_PhysDispSize = buf.TellMaxPut();
- g_pPhysDisp = new byte[g_PhysDispSize];
- Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "builddisp.h"
+#include "disp_common.h"
+#include "ivp.h"
+#include "disp_ivp.h"
+#include "vphysics_interface.h"
+#include "vphysics/virtualmesh.h"
+#include "utlrbtree.h"
+#include "tier1/utlbuffer.h"
+#include "materialpatch.h"
+
+struct disp_grid_t
+{
+ int gridIndex;
+ CUtlVector<int> dispList;
+};
+
+static CUtlVector<disp_grid_t> gDispGridList;
+
+
+
+disp_grid_t &FindOrInsertGrid( int gridIndex )
+{
+ // linear search is slow, but only a few grids will be present
+ for ( int i = gDispGridList.Count()-1; i >= 0; i-- )
+ {
+ if ( gDispGridList[i].gridIndex == gridIndex )
+ {
+ return gDispGridList[i];
+ }
+ }
+ int index = gDispGridList.AddToTail();
+ gDispGridList[index].gridIndex = gridIndex;
+
+ // must be empty
+ Assert( gDispGridList[index].dispList.Count() == 0 );
+
+ return gDispGridList[index];
+}
+
+// UNDONE: Tune these or adapt them to map size or triangle count?
+#define DISP_GRID_SIZEX 4096
+#define DISP_GRID_SIZEY 4096
+#define DISP_GRID_SIZEZ 8192
+
+int Disp_GridIndex( CCoreDispInfo *pDispInfo )
+{
+ // quick hash the center into the grid and put the whole terrain in that grid
+ Vector mins, maxs;
+ pDispInfo->GetNode(0)->GetBoundingBox( mins, maxs );
+ Vector center;
+ center = 0.5 * (mins + maxs);
+ // make sure it's positive
+ center += Vector(MAX_COORD_INTEGER,MAX_COORD_INTEGER,MAX_COORD_INTEGER);
+ int gridX = center.x / DISP_GRID_SIZEX;
+ int gridY = center.y / DISP_GRID_SIZEY;
+ int gridZ = center.z / DISP_GRID_SIZEZ;
+
+ gridX &= 0xFF;
+ gridY &= 0xFF;
+ gridZ &= 0xFF;
+ return MAKEID( gridX, gridY, gridZ, 0 );
+}
+
+void AddToGrid( int gridIndex, int dispIndex )
+{
+ disp_grid_t &grid = FindOrInsertGrid( gridIndex );
+ grid.dispList.AddToTail( dispIndex );
+}
+
+MaterialSystemMaterial_t GetMatIDFromDisp( mapdispinfo_t *pMapDisp )
+{
+ texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ MaterialSystemMaterial_t matID = FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
+ return matID;
+}
+
+// check this and disable virtual mesh if in use
+bool Disp_HasPower4Displacements()
+{
+ for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
+ {
+ if ( g_CoreDispInfos[i]->GetPower() > 3 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// adds all displacement faces as a series of convex objects
+// UNDONE: Only add the displacements for this model?
+void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask)
+{
+ int dispIndex;
+
+ // Add each displacement to the grid hash
+ for ( dispIndex = 0; dispIndex < g_CoreDispInfos.Count(); dispIndex++ )
+ {
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
+
+ // not solid for this pass
+ if ( !(pMapDisp->contents & contentsMask) )
+ continue;
+
+ int gridIndex = Disp_GridIndex( pDispInfo );
+ AddToGrid( gridIndex, dispIndex );
+ }
+
+ // now make a polysoup for the terrain in each grid
+ for ( int grid = 0; grid < gDispGridList.Count(); grid++ )
+ {
+ int triCount = 0;
+ CPhysPolysoup *pTerrainPhysics = physcollision->PolysoupCreate();
+
+ // iterate the displacements in this grid
+ for ( int listIndex = 0; listIndex < gDispGridList[grid].dispList.Count(); listIndex++ )
+ {
+ dispIndex = gDispGridList[grid].dispList[listIndex];
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
+
+ // Get the material id.
+ MaterialSystemMaterial_t matID = GetMatIDFromDisp( pMapDisp );
+
+ // Build a triangle list. This shares the tesselation code with the engine.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
+
+ ::TesselateDisplacement( &helper );
+
+ Assert( indices.Count() > 0 );
+ Assert( indices.Count() % 3 == 0 ); // Make sure indices are a multiple of 3.
+ int nTriCount = indices.Count() / 3;
+ triCount += nTriCount;
+ if ( triCount >= 65536 )
+ {
+ // don't put more than 64K tris in any single collision model
+ CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
+ if ( pCollide )
+ {
+ collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
+ }
+ // Throw this polysoup away and start over for the remaining triangles
+ physcollision->PolysoupDestroy( pTerrainPhysics );
+ pTerrainPhysics = physcollision->PolysoupCreate();
+ triCount = nTriCount;
+ }
+ Vector tmpVerts[3];
+ for ( int iTri = 0; iTri < nTriCount; ++iTri )
+ {
+ float flAlphaTotal = 0.0f;
+ for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
+ {
+ pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
+ flAlphaTotal += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
+ }
+
+ int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
+ if ( flAlphaTotal > DISP_ALPHA_PROP_DELTA )
+ {
+ int nProp2 = GetSurfaceProperties2( matID, "surfaceprop2" );
+ if ( nProp2 != -1 )
+ {
+ nProp = nProp2;
+ }
+ }
+ int nMaterialIndex = RemapWorldMaterial( nProp );
+ physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
+ }
+ }
+
+ // convert the whole grid's polysoup to a collide and store in the collision list
+ CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
+ if ( pCollide )
+ {
+ collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
+ }
+ // now that we have the collide, we're done with the soup
+ physcollision->PolysoupDestroy( pTerrainPhysics );
+ }
+}
+
+
+class CDispMeshEvent : public IVirtualMeshEvent
+{
+public:
+ CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
+ virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
+ virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
+ virtual void GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList );
+
+ CUtlVector<Vector> m_verts;
+ unsigned short *m_pIndices;
+ int m_indexCount;
+};
+
+CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
+{
+ m_pIndices = pIndices;
+ m_indexCount = indexCount;
+ int maxIndex = 0;
+ for ( int i = 0; i < indexCount; i++ )
+ {
+ if ( pIndices[i] > maxIndex )
+ {
+ maxIndex = pIndices[i];
+ }
+ }
+ for ( int i = 0; i < indexCount/2; i++ )
+ {
+ V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
+ }
+ int count = maxIndex + 1;
+ m_verts.SetCount( count );
+ for ( int i = 0; i < count; i++ )
+ {
+ m_verts[i] = pDispInfo->GetVert(i);
+ }
+}
+
+void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
+{
+ Assert(userData==((void *)this));
+ pList->pVerts = m_verts.Base();
+ pList->indexCount = m_indexCount;
+ pList->triangleCount = m_indexCount/3;
+ pList->vertexCount = m_verts.Count();
+ pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
+ pList->pHull = NULL;
+ int indexMax = ARRAYSIZE(pList->indices);
+ int indexCount = min(m_indexCount, indexMax);
+ Assert(m_indexCount < indexMax);
+ Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
+}
+
+void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
+{
+ Assert(userData==((void *)this));
+ ClearBounds( *pMins, *pMaxs );
+ for ( int i = 0; i < m_verts.Count(); i++ )
+ {
+ AddPointToBounds( m_verts[i], *pMins, *pMaxs );
+ }
+}
+
+void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList )
+{
+ Assert(userData==((void *)this));
+ pList->triangleCount = m_indexCount/3;
+ int indexMax = ARRAYSIZE(pList->triangleIndices);
+ int indexCount = min(m_indexCount, indexMax);
+ Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
+ Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
+}
+
+void Disp_BuildVirtualMesh( int contentsMask )
+{
+ CUtlVector<CPhysCollide *> virtualMeshes;
+ virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
+ for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
+ {
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
+
+ virtualMeshes[i] = NULL;
+ // not solid for this pass
+ if ( !(pMapDisp->contents & contentsMask) )
+ continue;
+
+ // Build a triangle list. This shares the tesselation code with the engine.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
+
+ ::TesselateDisplacement( &helper );
+
+ // validate the collision data
+ if ( 1 )
+ {
+ int triCount = indices.Count() / 3;
+ for ( int j = 0; j < triCount; j++ )
+ {
+ int index = j * 3;
+ Vector v0 = pDispInfo->GetVert( indices[index+0] );
+ Vector v1 = pDispInfo->GetVert( indices[index+1] );
+ Vector v2 = pDispInfo->GetVert( indices[index+2] );
+ if ( v0 == v1 || v1 == v2 || v2 == v0 )
+ {
+ Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
+ texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
+ }
+ }
+
+ }
+ CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
+ virtualmeshparams_t params;
+ params.buildOuterHull = true;
+ params.pMeshEventHandler = &meshHandler;
+ params.userData = &meshHandler;
+ virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
+ }
+ unsigned int totalSize = 0;
+ CUtlBuffer buf;
+ dphysdisp_t header;
+ header.numDisplacements = g_CoreDispInfos.Count();
+ buf.PutObjects( &header );
+
+ CUtlVector<char> dispBuf;
+ for ( int i = 0; i < header.numDisplacements; i++ )
+ {
+ if ( virtualMeshes[i] )
+ {
+ unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
+ totalSize += testSize;
+ buf.PutShort( testSize );
+ }
+ else
+ {
+ buf.PutShort( -1 );
+ }
+ }
+ for ( int i = 0; i < header.numDisplacements; i++ )
+ {
+ if ( virtualMeshes[i] )
+ {
+ unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
+ dispBuf.RemoveAll();
+ dispBuf.EnsureCount(testSize);
+
+ unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
+ Assert( outSize == testSize );
+ buf.Put( dispBuf.Base(), outSize );
+ }
+ }
+ g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
+ Assert( buf.TellMaxPut() == g_PhysDispSize );
+ g_PhysDispSize = buf.TellMaxPut();
+ g_pPhysDisp = new byte[g_PhysDispSize];
+ Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
+}
+
diff --git a/mp/src/utils/vbsp/disp_ivp.h b/mp/src/utils/vbsp/disp_ivp.h
index 2c473f9a..c89daceb 100644
--- a/mp/src/utils/vbsp/disp_ivp.h
+++ b/mp/src/utils/vbsp/disp_ivp.h
@@ -1,49 +1,49 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef DISP_IVP_H
-#define DISP_IVP_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "utlvector.h"
-#include "../../public/disp_tesselate.h"
-
-
-class CPhysCollisionEntry;
-struct dmodel_t;
-
-
-// This provides the template functions that the engine's tesselation code needs
-// so we can share the code in VBSP.
-class CVBSPTesselateHelper : public CBaseTesselateHelper
-{
-public:
- void EndTriangle()
- {
- m_pIndices->AddToTail( m_TempIndices[0] );
- m_pIndices->AddToTail( m_TempIndices[1] );
- m_pIndices->AddToTail( m_TempIndices[2] );
- }
-
- DispNodeInfo_t& GetNodeInfo( int iNodeBit )
- {
- // VBSP doesn't care about these. Give it back something to play with.
- static DispNodeInfo_t dummy;
- return dummy;
- }
-
-public:
- CUtlVector<unsigned short> *m_pIndices;
-};
-
-
-extern void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask );
-extern void Disp_BuildVirtualMesh( int contentsMask );
-extern bool Disp_HasPower4Displacements();
-
-#endif // DISP_IVP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DISP_IVP_H
+#define DISP_IVP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+#include "../../public/disp_tesselate.h"
+
+
+class CPhysCollisionEntry;
+struct dmodel_t;
+
+
+// This provides the template functions that the engine's tesselation code needs
+// so we can share the code in VBSP.
+class CVBSPTesselateHelper : public CBaseTesselateHelper
+{
+public:
+ void EndTriangle()
+ {
+ m_pIndices->AddToTail( m_TempIndices[0] );
+ m_pIndices->AddToTail( m_TempIndices[1] );
+ m_pIndices->AddToTail( m_TempIndices[2] );
+ }
+
+ DispNodeInfo_t& GetNodeInfo( int iNodeBit )
+ {
+ // VBSP doesn't care about these. Give it back something to play with.
+ static DispNodeInfo_t dummy;
+ return dummy;
+ }
+
+public:
+ CUtlVector<unsigned short> *m_pIndices;
+};
+
+
+extern void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask );
+extern void Disp_BuildVirtualMesh( int contentsMask );
+extern bool Disp_HasPower4Displacements();
+
+#endif // DISP_IVP_H
diff --git a/mp/src/utils/vbsp/disp_vbsp.cpp b/mp/src/utils/vbsp/disp_vbsp.cpp
index 82934f67..f3688595 100644
--- a/mp/src/utils/vbsp/disp_vbsp.cpp
+++ b/mp/src/utils/vbsp/disp_vbsp.cpp
@@ -1,675 +1,675 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-// $NoKeywords: $
-//=============================================================================//
-
-#include "disp_vbsp.h"
-#include "tier0/dbg.h"
-#include "vbsp.h"
-#include "mstristrip.h"
-#include "writebsp.h"
-#include "pacifier.h"
-#include "disp_ivp.h"
-#include "builddisp.h"
-#include "mathlib/vector.h"
-
-// map displacement info -- runs parallel to the dispinfos struct
-int nummapdispinfo = 0;
-mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
-
-CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
-
-//-----------------------------------------------------------------------------
-// Computes the bounds for a disp info
-//-----------------------------------------------------------------------------
-void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs )
-{
- CDispBox box;
-
- // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
- mapdispinfo_t *pMapDisp = &mapdispinfo[dispinfo];
-
- CCoreDispInfo coreDispInfo;
- DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
-
- GetDispBox( &coreDispInfo, box );
- mins = box.m_Min;
- maxs = box.m_Max;
-}
-
-// Gets the barycentric coordinates of the position on the triangle where the lightmap
-// coordinates are equal to lmCoords. This always generates the coordinates but it
-// returns false if the point containing them does not lie inside the triangle.
-bool GetBarycentricCoordsFromLightmapCoords( Vector2D tri[3], Vector2D const &lmCoords, float bcCoords[3] )
-{
- GetBarycentricCoords2D( tri[0], tri[1], tri[2], lmCoords, bcCoords );
-
- return
- (bcCoords[0] >= 0.0f && bcCoords[0] <= 1.0f) &&
- (bcCoords[1] >= 0.0f && bcCoords[1] <= 1.0f) &&
- (bcCoords[2] >= 0.0f && bcCoords[2] <= 1.0f);
-}
-
-
-bool FindTriIndexMapByUV( CCoreDispInfo *pCoreDisp, Vector2D const &lmCoords,
- int &iTriangle, float flBarycentric[3] )
-{
- const CPowerInfo *pPowerInfo = GetPowerInfo( pCoreDisp->GetPower() );
-
- // Search all the triangles..
- int nTriCount= pCoreDisp->GetTriCount();
- for ( int iTri = 0; iTri < nTriCount; ++iTri )
- {
- unsigned short iVerts[3];
-// pCoreDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] );
- CTriInfo *pTri = &pPowerInfo->m_pTriInfos[iTri];
- iVerts[0] = pTri->m_Indices[0];
- iVerts[1] = pTri->m_Indices[1];
- iVerts[2] = pTri->m_Indices[2];
-
- // Get this triangle's UVs.
- Vector2D vecUV[3];
- for ( int iCoord = 0; iCoord < 3; ++iCoord )
- {
- pCoreDisp->GetLuxelCoord( 0, iVerts[iCoord], vecUV[iCoord] );
- }
-
- // See if the passed-in UVs are in this triangle's UVs.
- if( GetBarycentricCoordsFromLightmapCoords( vecUV, lmCoords, flBarycentric ) )
- {
- iTriangle = iTri;
- return true;
- }
- }
-
- return false;
-}
-
-
-void CalculateLightmapSamplePositions( CCoreDispInfo *pCoreDispInfo, const dface_t *pFace, CUtlVector<unsigned char> &out )
-{
- int width = pFace->m_LightmapTextureSizeInLuxels[0] + 1;
- int height = pFace->m_LightmapTextureSizeInLuxels[1] + 1;
-
- // For each lightmap sample, find the triangle it sits in.
- Vector2D lmCoords;
- for( int y=0; y < height; y++ )
- {
- lmCoords.y = y + 0.5f;
-
- for( int x=0; x < width; x++ )
- {
- lmCoords.x = x + 0.5f;
-
- float flBarycentric[3];
- int iTri;
-
- if( FindTriIndexMapByUV( pCoreDispInfo, lmCoords, iTri, flBarycentric ) )
- {
- if( iTri < 255 )
- {
- out.AddToTail( iTri );
- }
- else
- {
- out.AddToTail( 255 );
- out.AddToTail( iTri - 255 );
- }
-
- out.AddToTail( (unsigned char)( flBarycentric[0] * 255.9f ) );
- out.AddToTail( (unsigned char)( flBarycentric[1] * 255.9f ) );
- out.AddToTail( (unsigned char)( flBarycentric[2] * 255.9f ) );
- }
- else
- {
- out.AddToTail( 0 );
- out.AddToTail( 0 );
- out.AddToTail( 0 );
- out.AddToTail( 0 );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int GetDispInfoEntityNum( mapdispinfo_t *pDisp )
-{
- return pDisp->entitynum;
-}
-
-// Setup a CCoreDispInfo given a mapdispinfo_t.
-// If pFace is non-NULL, then lightmap texture coordinates will be generated.
-void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp, CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos )
-{
- winding_t *pWinding = pMapDisp->face.originalface->winding;
-
- Assert( pWinding->numpoints == 4 );
-
- //
- // set initial surface data
- //
- CCoreDispSurface *pSurf = pCoreDispInfo->GetSurface();
-
- texinfo_t *pTexInfo = &texinfo[ pMapDisp->face.texinfo ];
- Assert( pTexInfo != NULL );
-
- // init material contents
- pMapDisp->contents = pMapDisp->face.contents;
- if (!(pMapDisp->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
- {
- pMapDisp->contents |= CONTENTS_SOLID;
- }
-
- pSurf->SetContents( pMapDisp->contents );
-
- // Calculate the lightmap coordinates.
- Vector2D tCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)};
- if( pFace )
- {
- Assert( pFace->numedges == 4 );
-
- Vector pt[4];
- for( int i=0; i < 4; i++ )
- pt[i] = pWinding->p[i];
-
- int zeroOffset[2] = {0,0};
- CalcTextureCoordsAtPoints(
- pTexInfo->textureVecsTexelsPerWorldUnits,
- zeroOffset,
- pt,
- 4,
- tCoords );
- }
-
- //
- // set face point data ...
- //
- pSurf->SetPointCount( 4 );
- for( int i = 0; i < 4; i++ )
- {
- // position
- pSurf->SetPoint( i, pWinding->p[i] );
- pSurf->SetTexCoord( i, tCoords[i] );
- }
-
- // reset surface given start info
- pSurf->SetPointStart( pMapDisp->startPosition );
- pSurf->FindSurfPointStartIndex();
- pSurf->AdjustSurfPointData();
-
- // Set the luxel coordinates on the base displacement surface.
- Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
- int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
- Vector vecU( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
- Vector vecV( pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1],
- pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
- bool bSwap = pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
-
- // Set the face m_LightmapExtents
- if ( pFace )
- {
- pFace->m_LightmapTextureSizeInLuxels[0] = pSurf->GetLuxelU();
- pFace->m_LightmapTextureSizeInLuxels[1] = pSurf->GetLuxelV();
- if ( bSwap )
- {
- if ( pSwappedTexInfos[ pMapDisp->face.texinfo ] < 0 )
- {
- // Create a new texinfo to hold the swapped data.
- // We must do this because other surfaces may want the non-swapped data
- // This fixes a lighting bug in d2_prison_08 where many non-displacement surfaces
- // were pitch black, in addition to bugs in other maps I bet.
-
- // NOTE: Copy here because adding a texinfo could realloc.
- texinfo_t temp = *pTexInfo;
- memcpy( temp.lightmapVecsLuxelsPerWorldUnits[0], pTexInfo->lightmapVecsLuxelsPerWorldUnits[1], 4 * sizeof(float) );
- memcpy( temp.lightmapVecsLuxelsPerWorldUnits[1], pTexInfo->lightmapVecsLuxelsPerWorldUnits[0], 4 * sizeof(float) );
- temp.lightmapVecsLuxelsPerWorldUnits[1][0] *= -1.0f;
- temp.lightmapVecsLuxelsPerWorldUnits[1][1] *= -1.0f;
- temp.lightmapVecsLuxelsPerWorldUnits[1][2] *= -1.0f;
- temp.lightmapVecsLuxelsPerWorldUnits[1][3] *= -1.0f;
- pSwappedTexInfos[ pMapDisp->face.texinfo ] = texinfo.AddToTail( temp );
- }
- pMapDisp->face.texinfo = pSwappedTexInfos[ pMapDisp->face.texinfo ];
- }
-
- // NOTE: This is here to help future-proof code, since there are codepaths where
- // pTexInfo can be made invalid (texinfo.AddToTail above).
- pTexInfo = NULL;
- }
-
- // Setup the displacement vectors and offsets.
- int size = ( ( ( 1 << pMapDisp->power ) + 1 ) * ( ( 1 << pMapDisp->power ) + 1 ) );
-
- Vector vectorDisps[2048];
- float dispDists[2048];
- Assert( size < sizeof(vectorDisps)/sizeof(vectorDisps[0]) );
-
- for( int j = 0; j < size; j++ )
- {
- Vector v;
- float dist;
-
- VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
- VectorAdd( v, pMapDisp->vectorOffsets[j], v );
-
- dist = VectorLength( v );
- VectorNormalize( v );
-
- vectorDisps[j] = v;
- dispDists[j] = dist;
- }
-
-
- // Use CCoreDispInfo to setup the actual vertex positions.
- pCoreDispInfo->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle,
- pMapDisp->alphaValues, vectorDisps, dispDists );
- pCoreDispInfo->Create();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void EmitInitialDispInfos( void )
-{
- int i;
- mapdispinfo_t *pMapDisp;
- ddispinfo_t *pDisp;
- Vector v;
-
- // Calculate the total number of verts.
- int nTotalVerts = 0;
- int nTotalTris = 0;
- for ( i=0; i < nummapdispinfo; i++ )
- {
- nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
- nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power );
- }
-
- // Clear the output arrays..
- g_dispinfo.Purge();
- g_dispinfo.SetSize( nummapdispinfo );
- g_DispVerts.SetSize( nTotalVerts );
- g_DispTris.SetSize( nTotalTris );
-
- int iCurVert = 0;
- int iCurTri = 0;
- for( i = 0; i < nummapdispinfo; i++ )
- {
- pDisp = &g_dispinfo[i];
- pMapDisp = &mapdispinfo[i];
-
- CDispVert *pOutVerts = &g_DispVerts[iCurVert];
- CDispTri *pOutTris = &g_DispTris[iCurTri];
-
- // Setup the vert pointers.
- pDisp->m_iDispVertStart = iCurVert;
- pDisp->m_iDispTriStart = iCurTri;
- iCurVert += NUM_DISP_POWER_VERTS( pMapDisp->power );
- iCurTri += NUM_DISP_POWER_TRIS( pMapDisp->power );
-
- //
- // save power, minimum tesselation, and smoothing angle
- //
- pDisp->power = pMapDisp->power;
-
- // If the high bit is set - this is FLAGS!
- pDisp->minTess = pMapDisp->flags;
- pDisp->minTess |= 0x80000000;
-// pDisp->minTess = pMapDisp->minTess;
- pDisp->smoothingAngle = pMapDisp->smoothingAngle;
- pDisp->m_iMapFace = (unsigned short)-2;
-
- // get surface contents
- pDisp->contents = pMapDisp->face.contents;
-
- pDisp->startPosition = pMapDisp->startPosition;
- //
- // add up the vectorOffsets and displacements, save alphas (per vertex)
- //
- int size = ( ( ( 1 << pDisp->power ) + 1 ) * ( ( 1 << pDisp->power ) + 1 ) );
- for( int j = 0; j < size; j++ )
- {
- VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
- VectorAdd( v, pMapDisp->vectorOffsets[j], v );
-
- float dist = VectorLength( v );
- VectorNormalize( v );
-
- VectorCopy( v, pOutVerts[j].m_vVector );
- pOutVerts[j].m_flDist = dist;
-
- pOutVerts[j].m_flAlpha = pMapDisp->alphaValues[j];
- }
-
- int nTriCount = ( (1 << (pDisp->power)) * (1 << (pDisp->power)) * 2 );
- for ( int iTri = 0; iTri< nTriCount; ++iTri )
- {
- pOutTris[iTri].m_uiTags = pMapDisp->triTags[iTri];
- }
- //===================================================================
- //===================================================================
-
- // save the index for face data reference
- pMapDisp->face.dispinfo = i;
- }
-}
-
-
-void ExportCoreDispNeighborData( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
-{
- for ( int i=0; i < 4; i++ )
- {
- pOut->m_EdgeNeighbors[i] = *pIn->GetEdgeNeighbor( i );
- pOut->m_CornerNeighbors[i] = *pIn->GetCornerNeighbors( i );
- }
-}
-
-void ExportNeighborData( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
-{
- FindNeighboringDispSurfs( ppListBase, listSize );
-
- // Export the neighbor data.
- for ( int i=0; i < nummapdispinfo; i++ )
- {
- ExportCoreDispNeighborData( g_CoreDispInfos[i], &pBSPDispInfos[i] );
- }
-}
-
-
-void ExportCoreDispAllowedVertList( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
-{
- ErrorIfNot(
- pIn->GetAllowedVerts().GetNumDWords() == sizeof( pOut->m_AllowedVerts ) / 4,
- ("ExportCoreDispAllowedVertList: size mismatch")
- );
- for ( int i=0; i < pIn->GetAllowedVerts().GetNumDWords(); i++ )
- pOut->m_AllowedVerts[i] = pIn->GetAllowedVerts().GetDWord( i );
-}
-
-
-void ExportAllowedVertLists( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
-{
- SetupAllowedVerts( ppListBase, listSize );
-
- for ( int i=0; i < listSize; i++ )
- {
- ExportCoreDispAllowedVertList( ppListBase[i], &pBSPDispInfos[i] );
- }
-}
-
-bool FindEnclosingTri(
- const Vector2D &vert,
- CUtlVector<Vector2D> &vertCoords,
- CUtlVector<unsigned short> &indices,
- int *pStartVert,
- float bcCoords[3] )
-{
- for ( int i=0; i < indices.Count(); i += 3 )
- {
- GetBarycentricCoords2D(
- vertCoords[indices[i+0]],
- vertCoords[indices[i+1]],
- vertCoords[indices[i+2]],
- vert,
- bcCoords );
-
- if ( bcCoords[0] >= 0 && bcCoords[0] <= 1 &&
- bcCoords[1] >= 0 && bcCoords[1] <= 1 &&
- bcCoords[2] >= 0 && bcCoords[2] <= 1 )
- {
- *pStartVert = i;
- return true;
- }
- }
-
- return false;
-}
-
-void SnapRemainingVertsToSurface( CCoreDispInfo *pCoreDisp, ddispinfo_t *pDispInfo )
-{
- // First, tesselate the displacement.
- CUtlVector<unsigned short> indices;
- CVBSPTesselateHelper helper;
- helper.m_pIndices = &indices;
- helper.m_pActiveVerts = pCoreDisp->GetAllowedVerts().Base();
- helper.m_pPowerInfo = pCoreDisp->GetPowerInfo();
- ::TesselateDisplacement( &helper );
-
- // Figure out which verts are actually referenced in the tesselation.
- CUtlVector<bool> vertsTouched;
- vertsTouched.SetSize( pCoreDisp->GetSize() );
- memset( vertsTouched.Base(), 0, sizeof( bool ) * vertsTouched.Count() );
-
- for ( int i=0; i < indices.Count(); i++ )
- vertsTouched[ indices[i] ] = true;
-
- // Generate 2D floating point coordinates for each vertex. We use these to generate
- // barycentric coordinates, and the scale doesn't matter.
- CUtlVector<Vector2D> vertCoords;
- vertCoords.SetSize( pCoreDisp->GetSize() );
- for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
- {
- for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
- vertCoords[y*pCoreDisp->GetWidth()+x].Init( x, y );
- }
-
- // Now, for each vert not touched, snap its position to the main surface.
- for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
- {
- for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
- {
- int index = y * pCoreDisp->GetWidth() + x;
- if ( !( vertsTouched[index] ) )
- {
- float bcCoords[3];
- int iStartVert = -1;
- if ( FindEnclosingTri( vertCoords[index], vertCoords, indices, &iStartVert, bcCoords ) )
- {
- const Vector &A = pCoreDisp->GetVert( indices[iStartVert+0] );
- const Vector &B = pCoreDisp->GetVert( indices[iStartVert+1] );
- const Vector &C = pCoreDisp->GetVert( indices[iStartVert+2] );
- Vector vNewPos = A*bcCoords[0] + B*bcCoords[1] + C*bcCoords[2];
-
- // This is kind of cheesy, but it gets the job done. Since the CDispVerts store the
- // verts relative to some other offset, we'll just offset their position instead
- // of setting it directly.
- Vector vOffset = vNewPos - pCoreDisp->GetVert( index );
-
- // Modify the mapfile vert.
- CDispVert *pVert = &g_DispVerts[pDispInfo->m_iDispVertStart + index];
- pVert->m_vVector = (pVert->m_vVector * pVert->m_flDist) + vOffset;
- pVert->m_flDist = 1;
-
- // Modify the CCoreDispInfo vert (although it probably won't be used later).
- pCoreDisp->SetVert( index, vNewPos );
- }
- else
- {
- // This shouldn't happen because it would mean that the triangulation that
- // disp_tesselation.h produced was missing a chunk of the space that the
- // displacement covers.
- // It also could indicate a floating-point epsilon error.. check to see if
- // FindEnclosingTri finds a triangle that -almost- encloses the vert.
- Assert( false );
- }
- }
- }
- }
-}
-
-void SnapRemainingVertsToSurface( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
-{
-//g_pPad = ScratchPad3D_Create();
- for ( int i=0; i < listSize; i++ )
- {
- SnapRemainingVertsToSurface( ppListBase[i], &pBSPDispInfos[i] );
- }
-}
-
-void EmitDispLMAlphaAndNeighbors()
-{
- int i;
-
- Msg( "Finding displacement neighbors...\n" );
-
- // Build the CCoreDispInfos.
- CUtlVector<dface_t*> faces;
-
- // Create the core dispinfos and init them for use as CDispUtilsHelpers.
- for ( int iDisp = 0; iDisp < nummapdispinfo; ++iDisp )
- {
- CCoreDispInfo *pDisp = new CCoreDispInfo;
- if ( !pDisp )
- {
- g_CoreDispInfos.Purge();
- return;
- }
-
- int nIndex = g_CoreDispInfos.AddToTail();
- pDisp->SetListIndex( nIndex );
- g_CoreDispInfos[nIndex] = pDisp;
- }
-
- for ( i=0; i < nummapdispinfo; i++ )
- {
- g_CoreDispInfos[i]->SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
- }
-
- faces.SetSize( nummapdispinfo );
-
- int nMemSize = texinfo.Count() * sizeof(int);
- int *pSwappedTexInfos = (int*)stackalloc( nMemSize );
- memset( pSwappedTexInfos, 0xFF, nMemSize );
- for( i = 0; i < numfaces; i++ )
- {
- dface_t *pFace = &dfaces[i];
-
- if( pFace->dispinfo == -1 )
- continue;
-
- mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
-
- // Set the displacement's face index.
- ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
- pDisp->m_iMapFace = i;
-
- // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
- CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[pFace->dispinfo];
- DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace, pSwappedTexInfos );
-
- faces[pFace->dispinfo] = pFace;
- }
- stackfree( pSwappedTexInfos );
-
- // Generate and export neighbor data.
- ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
-
- // Generate and export the active vert lists.
- ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
-
-
- // Now that we know which vertices are actually going to be around, snap the ones that won't
- // be around onto the slightly-reduced mesh. This is so the engine's ray test code and
- // overlay code works right.
- SnapRemainingVertsToSurface( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
-
- Msg( "Finding lightmap sample positions...\n" );
- for ( i=0; i < nummapdispinfo; i++ )
- {
- dface_t *pFace = faces[i];
- ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
- CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];
-
- pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();
-
- CalculateLightmapSamplePositions( pCoreDispInfo, pFace, g_DispLightmapSamplePositions );
- }
-
- StartPacifier( "Displacement Alpha : ");
-
- // Build lightmap alphas.
- int dispCount = 0; // How many we've processed.
- for( i = 0; i < nummapdispinfo; i++ )
- {
- dface_t *pFace = faces[i];
-
- Assert( pFace->dispinfo == i );
- ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
-
- // Allocate space for the alpha values.
- pDisp->m_iLightmapAlphaStart = 0; // not used anymore
-
- ++dispCount;
- }
-
- EndPacifier();
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void DispGetFaceInfo( mapbrush_t *pBrush )
-{
- int i;
- side_t *pSide;
-
- // we don't support displacement on entities at the moment!!
- if( pBrush->entitynum != 0 )
- {
- char* pszEntityName = ValueForKey( &g_LoadingMap->entities[pBrush->entitynum], "classname" );
- Error( "Error: displacement found on a(n) %s entity - not supported (entity %d, brush %d)\n", pszEntityName, pBrush->entitynum, pBrush->brushnum );
- }
-
- for( i = 0; i < pBrush->numsides; i++ )
- {
- pSide = &pBrush->original_sides[i];
- if( pSide->pMapDisp )
- {
- // error checking!!
- if( pSide->winding->numpoints != 4 )
- Error( "Trying to create a non-quad displacement! (entity %d, brush %d)\n", pBrush->entitynum, pBrush->brushnum );
- pSide->pMapDisp->face.originalface = pSide;
- pSide->pMapDisp->face.texinfo = pSide->texinfo;
- pSide->pMapDisp->face.dispinfo = -1;
- pSide->pMapDisp->face.planenum = pSide->planenum;
- pSide->pMapDisp->face.numpoints = pSide->winding->numpoints;
- pSide->pMapDisp->face.w = CopyWinding( pSide->winding );
- pSide->pMapDisp->face.contents = pBrush->contents;
-
- pSide->pMapDisp->face.merged = FALSE;
- pSide->pMapDisp->face.split[0] = FALSE;
- pSide->pMapDisp->face.split[1] = FALSE;
-
- pSide->pMapDisp->entitynum = pBrush->entitynum;
- pSide->pMapDisp->brushSideID = pSide->id;
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool HasDispInfo( mapbrush_t *pBrush )
-{
- int i;
- side_t *pSide;
-
- for( i = 0; i < pBrush->numsides; i++ )
- {
- pSide = &pBrush->original_sides[i];
- if( pSide->pMapDisp )
- return true;
- }
-
- return false;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "disp_vbsp.h"
+#include "tier0/dbg.h"
+#include "vbsp.h"
+#include "mstristrip.h"
+#include "writebsp.h"
+#include "pacifier.h"
+#include "disp_ivp.h"
+#include "builddisp.h"
+#include "mathlib/vector.h"
+
+// map displacement info -- runs parallel to the dispinfos struct
+int nummapdispinfo = 0;
+mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
+
+CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
+
+//-----------------------------------------------------------------------------
+// Computes the bounds for a disp info
+//-----------------------------------------------------------------------------
+void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs )
+{
+ CDispBox box;
+
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ mapdispinfo_t *pMapDisp = &mapdispinfo[dispinfo];
+
+ CCoreDispInfo coreDispInfo;
+ DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
+
+ GetDispBox( &coreDispInfo, box );
+ mins = box.m_Min;
+ maxs = box.m_Max;
+}
+
+// Gets the barycentric coordinates of the position on the triangle where the lightmap
+// coordinates are equal to lmCoords. This always generates the coordinates but it
+// returns false if the point containing them does not lie inside the triangle.
+bool GetBarycentricCoordsFromLightmapCoords( Vector2D tri[3], Vector2D const &lmCoords, float bcCoords[3] )
+{
+ GetBarycentricCoords2D( tri[0], tri[1], tri[2], lmCoords, bcCoords );
+
+ return
+ (bcCoords[0] >= 0.0f && bcCoords[0] <= 1.0f) &&
+ (bcCoords[1] >= 0.0f && bcCoords[1] <= 1.0f) &&
+ (bcCoords[2] >= 0.0f && bcCoords[2] <= 1.0f);
+}
+
+
+bool FindTriIndexMapByUV( CCoreDispInfo *pCoreDisp, Vector2D const &lmCoords,
+ int &iTriangle, float flBarycentric[3] )
+{
+ const CPowerInfo *pPowerInfo = GetPowerInfo( pCoreDisp->GetPower() );
+
+ // Search all the triangles..
+ int nTriCount= pCoreDisp->GetTriCount();
+ for ( int iTri = 0; iTri < nTriCount; ++iTri )
+ {
+ unsigned short iVerts[3];
+// pCoreDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] );
+ CTriInfo *pTri = &pPowerInfo->m_pTriInfos[iTri];
+ iVerts[0] = pTri->m_Indices[0];
+ iVerts[1] = pTri->m_Indices[1];
+ iVerts[2] = pTri->m_Indices[2];
+
+ // Get this triangle's UVs.
+ Vector2D vecUV[3];
+ for ( int iCoord = 0; iCoord < 3; ++iCoord )
+ {
+ pCoreDisp->GetLuxelCoord( 0, iVerts[iCoord], vecUV[iCoord] );
+ }
+
+ // See if the passed-in UVs are in this triangle's UVs.
+ if( GetBarycentricCoordsFromLightmapCoords( vecUV, lmCoords, flBarycentric ) )
+ {
+ iTriangle = iTri;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void CalculateLightmapSamplePositions( CCoreDispInfo *pCoreDispInfo, const dface_t *pFace, CUtlVector<unsigned char> &out )
+{
+ int width = pFace->m_LightmapTextureSizeInLuxels[0] + 1;
+ int height = pFace->m_LightmapTextureSizeInLuxels[1] + 1;
+
+ // For each lightmap sample, find the triangle it sits in.
+ Vector2D lmCoords;
+ for( int y=0; y < height; y++ )
+ {
+ lmCoords.y = y + 0.5f;
+
+ for( int x=0; x < width; x++ )
+ {
+ lmCoords.x = x + 0.5f;
+
+ float flBarycentric[3];
+ int iTri;
+
+ if( FindTriIndexMapByUV( pCoreDispInfo, lmCoords, iTri, flBarycentric ) )
+ {
+ if( iTri < 255 )
+ {
+ out.AddToTail( iTri );
+ }
+ else
+ {
+ out.AddToTail( 255 );
+ out.AddToTail( iTri - 255 );
+ }
+
+ out.AddToTail( (unsigned char)( flBarycentric[0] * 255.9f ) );
+ out.AddToTail( (unsigned char)( flBarycentric[1] * 255.9f ) );
+ out.AddToTail( (unsigned char)( flBarycentric[2] * 255.9f ) );
+ }
+ else
+ {
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int GetDispInfoEntityNum( mapdispinfo_t *pDisp )
+{
+ return pDisp->entitynum;
+}
+
+// Setup a CCoreDispInfo given a mapdispinfo_t.
+// If pFace is non-NULL, then lightmap texture coordinates will be generated.
+void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp, CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos )
+{
+ winding_t *pWinding = pMapDisp->face.originalface->winding;
+
+ Assert( pWinding->numpoints == 4 );
+
+ //
+ // set initial surface data
+ //
+ CCoreDispSurface *pSurf = pCoreDispInfo->GetSurface();
+
+ texinfo_t *pTexInfo = &texinfo[ pMapDisp->face.texinfo ];
+ Assert( pTexInfo != NULL );
+
+ // init material contents
+ pMapDisp->contents = pMapDisp->face.contents;
+ if (!(pMapDisp->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
+ {
+ pMapDisp->contents |= CONTENTS_SOLID;
+ }
+
+ pSurf->SetContents( pMapDisp->contents );
+
+ // Calculate the lightmap coordinates.
+ Vector2D tCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)};
+ if( pFace )
+ {
+ Assert( pFace->numedges == 4 );
+
+ Vector pt[4];
+ for( int i=0; i < 4; i++ )
+ pt[i] = pWinding->p[i];
+
+ int zeroOffset[2] = {0,0};
+ CalcTextureCoordsAtPoints(
+ pTexInfo->textureVecsTexelsPerWorldUnits,
+ zeroOffset,
+ pt,
+ 4,
+ tCoords );
+ }
+
+ //
+ // set face point data ...
+ //
+ pSurf->SetPointCount( 4 );
+ for( int i = 0; i < 4; i++ )
+ {
+ // position
+ pSurf->SetPoint( i, pWinding->p[i] );
+ pSurf->SetTexCoord( i, tCoords[i] );
+ }
+
+ // reset surface given start info
+ pSurf->SetPointStart( pMapDisp->startPosition );
+ pSurf->FindSurfPointStartIndex();
+ pSurf->AdjustSurfPointData();
+
+ // Set the luxel coordinates on the base displacement surface.
+ Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
+ Vector vecU( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecV( pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
+ bool bSwap = pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
+
+ // Set the face m_LightmapExtents
+ if ( pFace )
+ {
+ pFace->m_LightmapTextureSizeInLuxels[0] = pSurf->GetLuxelU();
+ pFace->m_LightmapTextureSizeInLuxels[1] = pSurf->GetLuxelV();
+ if ( bSwap )
+ {
+ if ( pSwappedTexInfos[ pMapDisp->face.texinfo ] < 0 )
+ {
+ // Create a new texinfo to hold the swapped data.
+ // We must do this because other surfaces may want the non-swapped data
+ // This fixes a lighting bug in d2_prison_08 where many non-displacement surfaces
+ // were pitch black, in addition to bugs in other maps I bet.
+
+ // NOTE: Copy here because adding a texinfo could realloc.
+ texinfo_t temp = *pTexInfo;
+ memcpy( temp.lightmapVecsLuxelsPerWorldUnits[0], pTexInfo->lightmapVecsLuxelsPerWorldUnits[1], 4 * sizeof(float) );
+ memcpy( temp.lightmapVecsLuxelsPerWorldUnits[1], pTexInfo->lightmapVecsLuxelsPerWorldUnits[0], 4 * sizeof(float) );
+ temp.lightmapVecsLuxelsPerWorldUnits[1][0] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][1] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][2] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][3] *= -1.0f;
+ pSwappedTexInfos[ pMapDisp->face.texinfo ] = texinfo.AddToTail( temp );
+ }
+ pMapDisp->face.texinfo = pSwappedTexInfos[ pMapDisp->face.texinfo ];
+ }
+
+ // NOTE: This is here to help future-proof code, since there are codepaths where
+ // pTexInfo can be made invalid (texinfo.AddToTail above).
+ pTexInfo = NULL;
+ }
+
+ // Setup the displacement vectors and offsets.
+ int size = ( ( ( 1 << pMapDisp->power ) + 1 ) * ( ( 1 << pMapDisp->power ) + 1 ) );
+
+ Vector vectorDisps[2048];
+ float dispDists[2048];
+ Assert( size < sizeof(vectorDisps)/sizeof(vectorDisps[0]) );
+
+ for( int j = 0; j < size; j++ )
+ {
+ Vector v;
+ float dist;
+
+ VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
+ VectorAdd( v, pMapDisp->vectorOffsets[j], v );
+
+ dist = VectorLength( v );
+ VectorNormalize( v );
+
+ vectorDisps[j] = v;
+ dispDists[j] = dist;
+ }
+
+
+ // Use CCoreDispInfo to setup the actual vertex positions.
+ pCoreDispInfo->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle,
+ pMapDisp->alphaValues, vectorDisps, dispDists );
+ pCoreDispInfo->Create();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void EmitInitialDispInfos( void )
+{
+ int i;
+ mapdispinfo_t *pMapDisp;
+ ddispinfo_t *pDisp;
+ Vector v;
+
+ // Calculate the total number of verts.
+ int nTotalVerts = 0;
+ int nTotalTris = 0;
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
+ nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power );
+ }
+
+ // Clear the output arrays..
+ g_dispinfo.Purge();
+ g_dispinfo.SetSize( nummapdispinfo );
+ g_DispVerts.SetSize( nTotalVerts );
+ g_DispTris.SetSize( nTotalTris );
+
+ int iCurVert = 0;
+ int iCurTri = 0;
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ pDisp = &g_dispinfo[i];
+ pMapDisp = &mapdispinfo[i];
+
+ CDispVert *pOutVerts = &g_DispVerts[iCurVert];
+ CDispTri *pOutTris = &g_DispTris[iCurTri];
+
+ // Setup the vert pointers.
+ pDisp->m_iDispVertStart = iCurVert;
+ pDisp->m_iDispTriStart = iCurTri;
+ iCurVert += NUM_DISP_POWER_VERTS( pMapDisp->power );
+ iCurTri += NUM_DISP_POWER_TRIS( pMapDisp->power );
+
+ //
+ // save power, minimum tesselation, and smoothing angle
+ //
+ pDisp->power = pMapDisp->power;
+
+ // If the high bit is set - this is FLAGS!
+ pDisp->minTess = pMapDisp->flags;
+ pDisp->minTess |= 0x80000000;
+// pDisp->minTess = pMapDisp->minTess;
+ pDisp->smoothingAngle = pMapDisp->smoothingAngle;
+ pDisp->m_iMapFace = (unsigned short)-2;
+
+ // get surface contents
+ pDisp->contents = pMapDisp->face.contents;
+
+ pDisp->startPosition = pMapDisp->startPosition;
+ //
+ // add up the vectorOffsets and displacements, save alphas (per vertex)
+ //
+ int size = ( ( ( 1 << pDisp->power ) + 1 ) * ( ( 1 << pDisp->power ) + 1 ) );
+ for( int j = 0; j < size; j++ )
+ {
+ VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
+ VectorAdd( v, pMapDisp->vectorOffsets[j], v );
+
+ float dist = VectorLength( v );
+ VectorNormalize( v );
+
+ VectorCopy( v, pOutVerts[j].m_vVector );
+ pOutVerts[j].m_flDist = dist;
+
+ pOutVerts[j].m_flAlpha = pMapDisp->alphaValues[j];
+ }
+
+ int nTriCount = ( (1 << (pDisp->power)) * (1 << (pDisp->power)) * 2 );
+ for ( int iTri = 0; iTri< nTriCount; ++iTri )
+ {
+ pOutTris[iTri].m_uiTags = pMapDisp->triTags[iTri];
+ }
+ //===================================================================
+ //===================================================================
+
+ // save the index for face data reference
+ pMapDisp->face.dispinfo = i;
+ }
+}
+
+
+void ExportCoreDispNeighborData( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
+{
+ for ( int i=0; i < 4; i++ )
+ {
+ pOut->m_EdgeNeighbors[i] = *pIn->GetEdgeNeighbor( i );
+ pOut->m_CornerNeighbors[i] = *pIn->GetCornerNeighbors( i );
+ }
+}
+
+void ExportNeighborData( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+ FindNeighboringDispSurfs( ppListBase, listSize );
+
+ // Export the neighbor data.
+ for ( int i=0; i < nummapdispinfo; i++ )
+ {
+ ExportCoreDispNeighborData( g_CoreDispInfos[i], &pBSPDispInfos[i] );
+ }
+}
+
+
+void ExportCoreDispAllowedVertList( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
+{
+ ErrorIfNot(
+ pIn->GetAllowedVerts().GetNumDWords() == sizeof( pOut->m_AllowedVerts ) / 4,
+ ("ExportCoreDispAllowedVertList: size mismatch")
+ );
+ for ( int i=0; i < pIn->GetAllowedVerts().GetNumDWords(); i++ )
+ pOut->m_AllowedVerts[i] = pIn->GetAllowedVerts().GetDWord( i );
+}
+
+
+void ExportAllowedVertLists( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+ SetupAllowedVerts( ppListBase, listSize );
+
+ for ( int i=0; i < listSize; i++ )
+ {
+ ExportCoreDispAllowedVertList( ppListBase[i], &pBSPDispInfos[i] );
+ }
+}
+
+bool FindEnclosingTri(
+ const Vector2D &vert,
+ CUtlVector<Vector2D> &vertCoords,
+ CUtlVector<unsigned short> &indices,
+ int *pStartVert,
+ float bcCoords[3] )
+{
+ for ( int i=0; i < indices.Count(); i += 3 )
+ {
+ GetBarycentricCoords2D(
+ vertCoords[indices[i+0]],
+ vertCoords[indices[i+1]],
+ vertCoords[indices[i+2]],
+ vert,
+ bcCoords );
+
+ if ( bcCoords[0] >= 0 && bcCoords[0] <= 1 &&
+ bcCoords[1] >= 0 && bcCoords[1] <= 1 &&
+ bcCoords[2] >= 0 && bcCoords[2] <= 1 )
+ {
+ *pStartVert = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SnapRemainingVertsToSurface( CCoreDispInfo *pCoreDisp, ddispinfo_t *pDispInfo )
+{
+ // First, tesselate the displacement.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pCoreDisp->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pCoreDisp->GetPowerInfo();
+ ::TesselateDisplacement( &helper );
+
+ // Figure out which verts are actually referenced in the tesselation.
+ CUtlVector<bool> vertsTouched;
+ vertsTouched.SetSize( pCoreDisp->GetSize() );
+ memset( vertsTouched.Base(), 0, sizeof( bool ) * vertsTouched.Count() );
+
+ for ( int i=0; i < indices.Count(); i++ )
+ vertsTouched[ indices[i] ] = true;
+
+ // Generate 2D floating point coordinates for each vertex. We use these to generate
+ // barycentric coordinates, and the scale doesn't matter.
+ CUtlVector<Vector2D> vertCoords;
+ vertCoords.SetSize( pCoreDisp->GetSize() );
+ for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
+ {
+ for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
+ vertCoords[y*pCoreDisp->GetWidth()+x].Init( x, y );
+ }
+
+ // Now, for each vert not touched, snap its position to the main surface.
+ for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
+ {
+ for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
+ {
+ int index = y * pCoreDisp->GetWidth() + x;
+ if ( !( vertsTouched[index] ) )
+ {
+ float bcCoords[3];
+ int iStartVert = -1;
+ if ( FindEnclosingTri( vertCoords[index], vertCoords, indices, &iStartVert, bcCoords ) )
+ {
+ const Vector &A = pCoreDisp->GetVert( indices[iStartVert+0] );
+ const Vector &B = pCoreDisp->GetVert( indices[iStartVert+1] );
+ const Vector &C = pCoreDisp->GetVert( indices[iStartVert+2] );
+ Vector vNewPos = A*bcCoords[0] + B*bcCoords[1] + C*bcCoords[2];
+
+ // This is kind of cheesy, but it gets the job done. Since the CDispVerts store the
+ // verts relative to some other offset, we'll just offset their position instead
+ // of setting it directly.
+ Vector vOffset = vNewPos - pCoreDisp->GetVert( index );
+
+ // Modify the mapfile vert.
+ CDispVert *pVert = &g_DispVerts[pDispInfo->m_iDispVertStart + index];
+ pVert->m_vVector = (pVert->m_vVector * pVert->m_flDist) + vOffset;
+ pVert->m_flDist = 1;
+
+ // Modify the CCoreDispInfo vert (although it probably won't be used later).
+ pCoreDisp->SetVert( index, vNewPos );
+ }
+ else
+ {
+ // This shouldn't happen because it would mean that the triangulation that
+ // disp_tesselation.h produced was missing a chunk of the space that the
+ // displacement covers.
+ // It also could indicate a floating-point epsilon error.. check to see if
+ // FindEnclosingTri finds a triangle that -almost- encloses the vert.
+ Assert( false );
+ }
+ }
+ }
+ }
+}
+
+void SnapRemainingVertsToSurface( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+//g_pPad = ScratchPad3D_Create();
+ for ( int i=0; i < listSize; i++ )
+ {
+ SnapRemainingVertsToSurface( ppListBase[i], &pBSPDispInfos[i] );
+ }
+}
+
+void EmitDispLMAlphaAndNeighbors()
+{
+ int i;
+
+ Msg( "Finding displacement neighbors...\n" );
+
+ // Build the CCoreDispInfos.
+ CUtlVector<dface_t*> faces;
+
+ // Create the core dispinfos and init them for use as CDispUtilsHelpers.
+ for ( int iDisp = 0; iDisp < nummapdispinfo; ++iDisp )
+ {
+ CCoreDispInfo *pDisp = new CCoreDispInfo;
+ if ( !pDisp )
+ {
+ g_CoreDispInfos.Purge();
+ return;
+ }
+
+ int nIndex = g_CoreDispInfos.AddToTail();
+ pDisp->SetListIndex( nIndex );
+ g_CoreDispInfos[nIndex] = pDisp;
+ }
+
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ g_CoreDispInfos[i]->SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
+ }
+
+ faces.SetSize( nummapdispinfo );
+
+ int nMemSize = texinfo.Count() * sizeof(int);
+ int *pSwappedTexInfos = (int*)stackalloc( nMemSize );
+ memset( pSwappedTexInfos, 0xFF, nMemSize );
+ for( i = 0; i < numfaces; i++ )
+ {
+ dface_t *pFace = &dfaces[i];
+
+ if( pFace->dispinfo == -1 )
+ continue;
+
+ mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
+
+ // Set the displacement's face index.
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ pDisp->m_iMapFace = i;
+
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[pFace->dispinfo];
+ DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace, pSwappedTexInfos );
+
+ faces[pFace->dispinfo] = pFace;
+ }
+ stackfree( pSwappedTexInfos );
+
+ // Generate and export neighbor data.
+ ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+ // Generate and export the active vert lists.
+ ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+
+ // Now that we know which vertices are actually going to be around, snap the ones that won't
+ // be around onto the slightly-reduced mesh. This is so the engine's ray test code and
+ // overlay code works right.
+ SnapRemainingVertsToSurface( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+ Msg( "Finding lightmap sample positions...\n" );
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ dface_t *pFace = faces[i];
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];
+
+ pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();
+
+ CalculateLightmapSamplePositions( pCoreDispInfo, pFace, g_DispLightmapSamplePositions );
+ }
+
+ StartPacifier( "Displacement Alpha : ");
+
+ // Build lightmap alphas.
+ int dispCount = 0; // How many we've processed.
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ dface_t *pFace = faces[i];
+
+ Assert( pFace->dispinfo == i );
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+
+ // Allocate space for the alpha values.
+ pDisp->m_iLightmapAlphaStart = 0; // not used anymore
+
+ ++dispCount;
+ }
+
+ EndPacifier();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void DispGetFaceInfo( mapbrush_t *pBrush )
+{
+ int i;
+ side_t *pSide;
+
+ // we don't support displacement on entities at the moment!!
+ if( pBrush->entitynum != 0 )
+ {
+ char* pszEntityName = ValueForKey( &g_LoadingMap->entities[pBrush->entitynum], "classname" );
+ Error( "Error: displacement found on a(n) %s entity - not supported (entity %d, brush %d)\n", pszEntityName, pBrush->entitynum, pBrush->brushnum );
+ }
+
+ for( i = 0; i < pBrush->numsides; i++ )
+ {
+ pSide = &pBrush->original_sides[i];
+ if( pSide->pMapDisp )
+ {
+ // error checking!!
+ if( pSide->winding->numpoints != 4 )
+ Error( "Trying to create a non-quad displacement! (entity %d, brush %d)\n", pBrush->entitynum, pBrush->brushnum );
+ pSide->pMapDisp->face.originalface = pSide;
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ pSide->pMapDisp->face.dispinfo = -1;
+ pSide->pMapDisp->face.planenum = pSide->planenum;
+ pSide->pMapDisp->face.numpoints = pSide->winding->numpoints;
+ pSide->pMapDisp->face.w = CopyWinding( pSide->winding );
+ pSide->pMapDisp->face.contents = pBrush->contents;
+
+ pSide->pMapDisp->face.merged = FALSE;
+ pSide->pMapDisp->face.split[0] = FALSE;
+ pSide->pMapDisp->face.split[1] = FALSE;
+
+ pSide->pMapDisp->entitynum = pBrush->entitynum;
+ pSide->pMapDisp->brushSideID = pSide->id;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool HasDispInfo( mapbrush_t *pBrush )
+{
+ int i;
+ side_t *pSide;
+
+ for( i = 0; i < pBrush->numsides; i++ )
+ {
+ pSide = &pBrush->original_sides[i];
+ if( pSide->pMapDisp )
+ return true;
+ }
+
+ return false;
+}
diff --git a/mp/src/utils/vbsp/disp_vbsp.h b/mp/src/utils/vbsp/disp_vbsp.h
index 1c26775e..5af77601 100644
--- a/mp/src/utils/vbsp/disp_vbsp.h
+++ b/mp/src/utils/vbsp/disp_vbsp.h
@@ -1,46 +1,46 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef VBSP_DISPINFO_H
-#define VBSP_DISPINFO_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "vbsp.h"
-
-
-
-class CCoreDispInfo;
-
-
-extern CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
-
-
-// Setup initial entries in g_dispinfo with some of the vertex data from the mapdisps.
-void EmitInitialDispInfos();
-
-// Resample vertex alpha into lightmap alpha for displacement surfaces so LOD popping artifacts are
-// less noticeable on the mid-to-high end.
-//
-// Also builds neighbor data.
-void EmitDispLMAlphaAndNeighbors();
-
-// Setup a CCoreDispInfo given a mapdispinfo_t.
-// If pFace is non-NULL, then lightmap texture coordinates will be generated.
-void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp,
- CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos );
-
-
-void DispGetFaceInfo( mapbrush_t *pBrush );
-bool HasDispInfo( mapbrush_t *pBrush );
-
-// Computes the bounds for a disp info
-void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs );
-
-#endif // VBSP_DISPINFO_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VBSP_DISPINFO_H
+#define VBSP_DISPINFO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "vbsp.h"
+
+
+
+class CCoreDispInfo;
+
+
+extern CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
+
+
+// Setup initial entries in g_dispinfo with some of the vertex data from the mapdisps.
+void EmitInitialDispInfos();
+
+// Resample vertex alpha into lightmap alpha for displacement surfaces so LOD popping artifacts are
+// less noticeable on the mid-to-high end.
+//
+// Also builds neighbor data.
+void EmitDispLMAlphaAndNeighbors();
+
+// Setup a CCoreDispInfo given a mapdispinfo_t.
+// If pFace is non-NULL, then lightmap texture coordinates will be generated.
+void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp,
+ CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos );
+
+
+void DispGetFaceInfo( mapbrush_t *pBrush );
+bool HasDispInfo( mapbrush_t *pBrush );
+
+// Computes the bounds for a disp info
+void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs );
+
+#endif // VBSP_DISPINFO_H
diff --git a/mp/src/utils/vbsp/faces.cpp b/mp/src/utils/vbsp/faces.cpp
index 13ed9d61..f6ec3ee3 100644
--- a/mp/src/utils/vbsp/faces.cpp
+++ b/mp/src/utils/vbsp/faces.cpp
@@ -1,1810 +1,1810 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-// faces.c
-
-#include "vbsp.h"
-#include "utlvector.h"
-#include "utilmatlib.h"
-#include <float.h>
-#include "mstristrip.h"
-#include "tier1/strtools.h"
-#include "materialpatch.h"
-/*
-
- some faces will be removed before saving, but still form nodes:
-
- the insides of sky volumes
- meeting planes of different water current volumes
-
-*/
-
-// undefine for dumb linear searches
-#define USE_HASHING
-
-#define INTEGRAL_EPSILON 0.01
-#define POINT_EPSILON 0.1
-#define OFF_EPSILON 0.25
-
-int c_merge;
-int c_subdivide;
-
-int c_totalverts;
-int c_uniqueverts;
-int c_degenerate;
-int c_tjunctions;
-int c_faceoverflows;
-int c_facecollapse;
-int c_badstartverts;
-
-#define MAX_SUPERVERTS 512
-int superverts[MAX_SUPERVERTS];
-int numsuperverts;
-
-face_t *edgefaces[MAX_MAP_EDGES][2];
-int firstmodeledge = 1;
-int firstmodelface;
-
-int c_tryedges;
-
-Vector edge_dir;
-Vector edge_start;
-vec_t edge_len;
-
-int num_edge_verts;
-int edge_verts[MAX_MAP_VERTS];
-
-
-float g_maxLightmapDimension = 32;
-
-
-face_t *NewFaceFromFace (face_t *f);
-
-// Used to speed up GetEdge2(). Holds a list of edges connected to each vert.
-CUtlVector<int> g_VertEdgeList[MAX_MAP_VERTS];
-
-
-//===========================================================================
-
-typedef struct hashvert_s
-{
- struct hashvert_s *next;
- int num;
-} hashvert_t;
-
-#define HASH_BITS 7
-#define HASH_SIZE (COORD_EXTENT>>HASH_BITS)
-
-
-int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain
-int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
-
-//face_t *edgefaces[MAX_MAP_EDGES][2];
-
-//============================================================================
-
-
-unsigned HashVec (Vector& vec)
-{
- int x, y;
-
- x = (MAX_COORD_INTEGER + (int)(vec[0]+0.5)) >> HASH_BITS;
- y = (MAX_COORD_INTEGER + (int)(vec[1]+0.5)) >> HASH_BITS;
-
- if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
- Error ("HashVec: point outside valid range");
-
- return y*HASH_SIZE + x;
-}
-
-#ifdef USE_HASHING
-/*
-=============
-GetVertex
-
-Uses hashing
-=============
-*/
-int GetVertexnum (Vector& in)
-{
- int h;
- int i;
- Vector vert;
- int vnum;
-
- c_totalverts++;
-
- for (i=0 ; i<3 ; i++)
- {
- if ( fabs(in[i] - (int)(in[i]+0.5)) < INTEGRAL_EPSILON)
- vert[i] = (int)(in[i]+0.5);
- else
- vert[i] = in[i];
- }
-
- h = HashVec (vert);
-
- for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
- {
- Vector& p = dvertexes[vnum].point;
- if ( fabs(p[0]-vert[0])<POINT_EPSILON
- && fabs(p[1]-vert[1])<POINT_EPSILON
- && fabs(p[2]-vert[2])<POINT_EPSILON )
- return vnum;
- }
-
-// emit a vertex
- if (numvertexes == MAX_MAP_VERTS)
- Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
-
- dvertexes[numvertexes].point[0] = vert[0];
- dvertexes[numvertexes].point[1] = vert[1];
- dvertexes[numvertexes].point[2] = vert[2];
-
- vertexchain[numvertexes] = hashverts[h];
- hashverts[h] = numvertexes;
-
- c_uniqueverts++;
-
- numvertexes++;
-
- return numvertexes-1;
-}
-#else
-/*
-==================
-GetVertexnum
-
-Dumb linear search
-==================
-*/
-int GetVertexnum (Vector& v)
-{
- int i, j;
- dvertex_t *dv;
- vec_t d;
-
- c_totalverts++;
-
- // make really close values exactly integral
- for (i=0 ; i<3 ; i++)
- {
- if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
- v[i] = (int)(v[i]+0.5);
- if (v[i] < MIN_COORD_INTEGER || v[i] > MAX_COORD_INTEGER)
- Error ("GetVertexnum: outside world, vertex %.1f %.1f %.1f", v.x, v.y, v.z);
- }
-
- // search for an existing vertex match
- for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
- {
- for (j=0 ; j<3 ; j++)
- {
- d = v[j] - dv->point[j];
- if ( d > POINT_EPSILON || d < -POINT_EPSILON)
- break;
- }
- if (j == 3)
- return i; // a match
- }
-
- // new point
- if (numvertexes == MAX_MAP_VERTS)
- Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
- VectorCopy (v, dv->point);
- numvertexes++;
- c_uniqueverts++;
-
- return numvertexes-1;
-}
-#endif
-
-
-/*
-==================
-FaceFromSuperverts
-
-The faces vertexes have beeb added to the superverts[] array,
-and there may be more there than can be held in a face (MAXEDGES).
-
-If less, the faces vertexnums[] will be filled in, otherwise
-face will reference a tree of split[] faces until all of the
-vertexnums can be added.
-
-superverts[base] will become face->vertexnums[0], and the others
-will be circularly filled in.
-==================
-*/
-void FaceFromSuperverts (face_t **pListHead, face_t *f, int base)
-{
- face_t *newf;
- int remaining;
- int i;
-
- remaining = numsuperverts;
- while (remaining > MAXEDGES)
- { // must split into two faces, because of vertex overload
- c_faceoverflows++;
-
- newf = NewFaceFromFace (f);
- f->split[0] = newf;
-
- newf->next = *pListHead;
- *pListHead = newf;
-
- newf->numpoints = MAXEDGES;
- for (i=0 ; i<MAXEDGES ; i++)
- newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
-
- f->split[1] = NewFaceFromFace (f);
- f = f->split[1];
-
- f->next = *pListHead;
- *pListHead = f;
-
- remaining -= (MAXEDGES-2);
- base = (base+MAXEDGES-1)%numsuperverts;
- }
-
- // copy the vertexes back to the face
- f->numpoints = remaining;
- for (i=0 ; i<remaining ; i++)
- f->vertexnums[i] = superverts[(i+base)%numsuperverts];
-}
-
-
-/*
-==================
-EmitFaceVertexes
-==================
-*/
-void EmitFaceVertexes (face_t **pListHead, face_t *f)
-{
- winding_t *w;
- int i;
-
- if (f->merged || f->split[0] || f->split[1])
- return;
-
- w = f->w;
- for (i=0 ; i<w->numpoints ; i++)
- {
- if (noweld)
- { // make every point unique
- if (numvertexes == MAX_MAP_VERTS)
- Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
- superverts[i] = numvertexes;
- VectorCopy (w->p[i], dvertexes[numvertexes].point);
- numvertexes++;
- c_uniqueverts++;
- c_totalverts++;
- }
- else
- superverts[i] = GetVertexnum (w->p[i]);
- }
- numsuperverts = w->numpoints;
-
- // this may fragment the face if > MAXEDGES
- FaceFromSuperverts (pListHead, f, 0);
-}
-
-/*
-==================
-EmitNodeFaceVertexes_r
-==================
-*/
-void EmitNodeFaceVertexes_r (node_t *node)
-{
- int i;
- face_t *f;
-
- if (node->planenum == PLANENUM_LEAF)
- {
- // leaf faces are emitted in second pass
- return;
- }
-
- for (f=node->faces ; f ; f=f->next)
- {
- EmitFaceVertexes (&node->faces, f);
- }
-
- for (i=0 ; i<2 ; i++)
- {
- EmitNodeFaceVertexes_r (node->children[i]);
- }
-}
-
-void EmitLeafFaceVertexes( face_t **ppLeafFaceList )
-{
- face_t *f = *ppLeafFaceList;
-
- while ( f )
- {
- EmitFaceVertexes( ppLeafFaceList, f );
- f = f->next;
- }
-}
-
-
-#ifdef USE_HASHING
-/*
-==========
-FindEdgeVerts
-
-Uses the hash tables to cut down to a small number
-==========
-*/
-void FindEdgeVerts (Vector& v1, Vector& v2)
-{
- int x1, x2, y1, y2, t;
- int x, y;
- int vnum;
-
-#if 0
-{
- int i;
- num_edge_verts = numvertexes-1;
- for (i=0 ; i<numvertexes-1 ; i++)
- edge_verts[i] = i+1;
-}
-#endif
-
- x1 = (MAX_COORD_INTEGER + (int)(v1[0]+0.5)) >> HASH_BITS;
- y1 = (MAX_COORD_INTEGER + (int)(v1[1]+0.5)) >> HASH_BITS;
- x2 = (MAX_COORD_INTEGER + (int)(v2[0]+0.5)) >> HASH_BITS;
- y2 = (MAX_COORD_INTEGER + (int)(v2[1]+0.5)) >> HASH_BITS;
-
- if (x1 > x2)
- {
- t = x1;
- x1 = x2;
- x2 = t;
- }
- if (y1 > y2)
- {
- t = y1;
- y1 = y2;
- y2 = t;
- }
-#if 0
- x1--;
- x2++;
- y1--;
- y2++;
- if (x1 < 0)
- x1 = 0;
- if (x2 >= HASH_SIZE)
- x2 = HASH_SIZE;
- if (y1 < 0)
- y1 = 0;
- if (y2 >= HASH_SIZE)
- y2 = HASH_SIZE;
-#endif
- num_edge_verts = 0;
- for (x=x1 ; x <= x2 ; x++)
- {
- for (y=y1 ; y <= y2 ; y++)
- {
- for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
- {
- edge_verts[num_edge_verts++] = vnum;
- }
- }
- }
-}
-
-#else
-/*
-==========
-FindEdgeVerts
-
-Forced a dumb check of everything
-==========
-*/
-void FindEdgeVerts (Vector& v1, Vector& v2)
-{
- int i;
-
- num_edge_verts = numvertexes-1;
- for (i=0 ; i<num_edge_verts ; i++)
- edge_verts[i] = i+1;
-}
-#endif
-
-/*
-==========
-TestEdge
-
-Can be recursively reentered
-==========
-*/
-void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
-{
- int j, k;
- vec_t dist;
- Vector delta;
- Vector exact;
- Vector off;
- vec_t error;
- Vector p;
-
- if (p1 == p2)
- {
- c_degenerate++;
- return; // degenerate edge
- }
-
- for (k=startvert ; k<num_edge_verts ; k++)
- {
- j = edge_verts[k];
- if (j==p1 || j == p2)
- continue;
-
- VectorCopy (dvertexes[j].point, p);
-
- VectorSubtract (p, edge_start, delta);
- dist = DotProduct (delta, edge_dir);
- if (dist <=start || dist >= end)
- continue; // off an end
- VectorMA (edge_start, dist, edge_dir, exact);
- VectorSubtract (p, exact, off);
- error = off.Length();
-
- if (error > OFF_EPSILON)
- continue; // not on the edge
-
- // break the edge
- c_tjunctions++;
- TestEdge (start, dist, p1, j, k+1);
- TestEdge (dist, end, j, p2, k+1);
- return;
- }
-
- // the edge p1 to p2 is now free of tjunctions
- if (numsuperverts >= MAX_SUPERVERTS)
- Error ("Edge with too many vertices due to t-junctions. Max %d verts along an edge!\n", MAX_SUPERVERTS);
- superverts[numsuperverts] = p1;
- numsuperverts++;
-}
-
-
-// stores the edges that each vert is part of
-struct face_vert_table_t
-{
- face_vert_table_t()
- {
- edge0 = -1;
- edge1 = -1;
- }
-
- void AddEdge( int edge )
- {
- if ( edge0 == -1 )
- {
- edge0 = edge;
- }
- else
- {
- // can only have two edges
- Assert(edge1==-1);
- edge1 = edge;
- }
- }
-
- bool HasEdge( int edge ) const
- {
- if ( edge >= 0 )
- {
- if ( edge0 == edge || edge1 == edge )
- return true;
- }
- return false;
- }
-
- int edge0;
- int edge1;
-};
-
-// if these two verts share an edge, they must be collinear
-bool IsDiagonal( const face_vert_table_t &v0, const face_vert_table_t &v1 )
-{
- if ( v1.HasEdge(v0.edge0) || v1.HasEdge(v0.edge1) )
- return false;
-
- return true;
-}
-
-
-void Triangulate_r( CUtlVector<int> &out, const CUtlVector<int> &inIndices, const CUtlVector<face_vert_table_t> &poly )
-{
- Assert( inIndices.Count() > 2 );
-
- // one triangle left, return
- if ( inIndices.Count() == 3 )
- {
- for ( int i = 0; i < inIndices.Count(); i++ )
- {
- out.AddToTail( inIndices[i] );
- }
- return;
- }
-
- // check each pair of verts and see if they are diagonal (not on a shared edge)
- // if so, split & recurse
- for ( int i = 0; i < inIndices.Count(); i++ )
- {
- int count = inIndices.Count();
-
- // i + count is myself, i + count-1 is previous, so we need to stop at i+count-2
- for ( int j = 2; j < count-1; j++ )
- {
- // if these two form a diagonal, split the poly along
- // the diagonal and triangulate the two sub-polys
- int index = inIndices[i];
- int nextArray = (i+j)%count;
- int nextIndex = inIndices[nextArray];
- if ( IsDiagonal(poly[index], poly[nextIndex]) )
- {
- // add the poly up to the diagonal
- CUtlVector<int> in1;
- for ( int k = i; k != nextArray; k = (k+1)%count )
- {
- in1.AddToTail(inIndices[k]);
- }
- in1.AddToTail(nextIndex);
-
- // add the rest of the poly starting with the diagonal
- CUtlVector<int> in2;
- in2.AddToTail(index);
- for ( int l = nextArray; l != i; l = (l+1)%count )
- {
- in2.AddToTail(inIndices[l]);
- }
-
- // triangulate the sub-polys
- Triangulate_r( out, in1, poly );
- Triangulate_r( out, in2, poly );
- return;
- }
- }
- }
-
- // didn't find a diagonal
- Assert(0);
-}
-
-/*
-==================
-FixFaceEdges
-
-==================
-*/
-void FixFaceEdges (face_t **pList, face_t *f)
-{
- int p1, p2;
- int i;
- Vector e2;
- vec_t len;
- int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
- int base;
-
- if (f->merged || f->split[0] || f->split[1])
- return;
-
- numsuperverts = 0;
-
- int originalPoints = f->numpoints;
- for (i=0 ; i<f->numpoints ; i++)
- {
- p1 = f->vertexnums[i];
- p2 = f->vertexnums[(i+1)%f->numpoints];
-
- VectorCopy (dvertexes[p1].point, edge_start);
- VectorCopy (dvertexes[p2].point, e2);
-
- FindEdgeVerts (edge_start, e2);
-
- VectorSubtract (e2, edge_start, edge_dir);
- len = VectorNormalize (edge_dir);
-
- start[i] = numsuperverts;
- TestEdge (0, len, p1, p2, 0);
-
- count[i] = numsuperverts - start[i];
- }
-
- if (numsuperverts < 3)
- { // entire face collapsed
- f->numpoints = 0;
- c_facecollapse++;
- return;
- }
-
- // we want to pick a vertex that doesn't have tjunctions
- // on either side, which can cause artifacts on trifans,
- // especially underwater
- for (i=0 ; i<f->numpoints ; i++)
- {
- if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
- break;
- }
- if (i == f->numpoints)
- {
- f->badstartvert = true;
- c_badstartverts++;
- base = 0;
-
- }
- else
- { // rotate the vertex order
- base = start[i];
- }
-
- // this may fragment the face if > MAXEDGES
- FaceFromSuperverts (pList, f, base);
-
- // if this is the world, then re-triangulate to sew cracks
- if ( f->badstartvert && entity_num == 0 )
- {
- CUtlVector<face_vert_table_t> poly;
- CUtlVector<int> inIndices;
- CUtlVector<int> outIndices;
- poly.AddMultipleToTail( numsuperverts );
- for ( i = 0; i < originalPoints; i++ )
- {
- // edge may not have output any points. Don't mark
- if ( !count[i] )
- continue;
- // mark each edge the point is a member of
- // we'll use this as a fast "is collinear" test
- for ( int j = 0; j <= count[i]; j++ )
- {
- int polyIndex = (start[i] + j) % numsuperverts;
- poly[polyIndex].AddEdge( i );
- }
- }
- for ( i = 0; i < numsuperverts; i++ )
- {
- inIndices.AddToTail( i );
- }
- Triangulate_r( outIndices, inIndices, poly );
- dprimitive_t &newPrim = g_primitives[g_numprimitives];
- f->firstPrimID = g_numprimitives;
- g_numprimitives++;
- f->numPrims = 1;
- newPrim.firstIndex = g_numprimindices;
- newPrim.firstVert = g_numprimverts;
- newPrim.indexCount = outIndices.Count();
- newPrim.vertCount = 0;
- newPrim.type = PRIM_TRILIST;
- g_numprimindices += newPrim.indexCount;
- if ( g_numprimitives > MAX_MAP_PRIMITIVES || g_numprimindices > MAX_MAP_PRIMINDICES )
- {
- Error("Too many t-junctions to fix up! (%d prims, max %d :: %d indices, max %d)\n", g_numprimitives, MAX_MAP_PRIMITIVES, g_numprimindices, MAX_MAP_PRIMINDICES );
- }
- for ( i = 0; i < outIndices.Count(); i++ )
- {
- g_primindices[newPrim.firstIndex + i] = outIndices[i];
- }
- }
-}
-
-/*
-==================
-FixEdges_r
-==================
-*/
-void FixEdges_r (node_t *node)
-{
- int i;
- face_t *f;
-
- if (node->planenum == PLANENUM_LEAF)
- {
- return;
- }
-
- for (f=node->faces ; f ; f=f->next)
- FixFaceEdges (&node->faces, f);
-
- for (i=0 ; i<2 ; i++)
- FixEdges_r (node->children[i]);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Fix the t-junctions on detail faces
-//-----------------------------------------------------------------------------
-void FixLeafFaceEdges( face_t **ppLeafFaceList )
-{
- face_t *f;
-
- for ( f = *ppLeafFaceList; f; f = f->next )
- {
- FixFaceEdges( ppLeafFaceList, f );
- }
-}
-
-/*
-===========
-FixTjuncs
-
-===========
-*/
-
-face_t *FixTjuncs (node_t *headnode, face_t *pLeafFaceList)
-{
- // snap and merge all vertexes
- qprintf ("---- snap verts ----\n");
- memset (hashverts, 0, sizeof(hashverts));
- memset (vertexchain, 0, sizeof(vertexchain));
- c_totalverts = 0;
- c_uniqueverts = 0;
- c_faceoverflows = 0;
- EmitNodeFaceVertexes_r (headnode);
-
- // UNDONE: This count is wrong with tjuncs off on details - since
-
- // break edges on tjunctions
- qprintf ("---- tjunc ----\n");
- c_tryedges = 0;
- c_degenerate = 0;
- c_facecollapse = 0;
- c_tjunctions = 0;
-
- if ( g_bAllowDetailCracks )
- {
- FixEdges_r (headnode);
- EmitLeafFaceVertexes( &pLeafFaceList );
- FixLeafFaceEdges( &pLeafFaceList );
- }
- else
- {
- EmitLeafFaceVertexes( &pLeafFaceList );
- if (!notjunc)
- {
- FixEdges_r (headnode);
- FixLeafFaceEdges( &pLeafFaceList );
- }
- }
-
-
- qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
- qprintf ("%5i edges degenerated\n", c_degenerate);
- qprintf ("%5i faces degenerated\n", c_facecollapse);
- qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
- qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
- qprintf ("%5i bad start verts\n", c_badstartverts);
-
- return pLeafFaceList;
-}
-
-
-//========================================================
-
-int c_faces;
-
-face_t *AllocFace (void)
-{
- static int s_FaceId = 0;
-
- face_t *f;
-
- f = (face_t*)malloc(sizeof(*f));
- memset (f, 0, sizeof(*f));
- f->id = s_FaceId;
- ++s_FaceId;
-
- c_faces++;
-
- return f;
-}
-
-face_t *NewFaceFromFace (face_t *f)
-{
- face_t *newf;
-
- newf = AllocFace ();
- *newf = *f;
- newf->merged = NULL;
- newf->split[0] = newf->split[1] = NULL;
- newf->w = NULL;
- return newf;
-}
-
-void FreeFace (face_t *f)
-{
- if (f->w)
- FreeWinding (f->w);
- free (f);
- c_faces--;
-}
-
-
-void FreeFaceList( face_t *pFaces )
-{
- while ( pFaces )
- {
- face_t *next = pFaces->next;
-
- FreeFace( pFaces );
- pFaces = next;
- }
-}
-
-//========================================================
-
-void GetEdge2_InitOptimizedList()
-{
- for( int i=0; i < MAX_MAP_VERTS; i++ )
- g_VertEdgeList[i].RemoveAll();
-}
-
-
-void IntSort( CUtlVector<int> &theList )
-{
- for( int i=0; i < theList.Size()-1; i++ )
- {
- if( theList[i] > theList[i+1] )
- {
- int temp = theList[i];
- theList[i] = theList[i+1];
- theList[i+1] = temp;
- if( i > 0 )
- i -= 2;
- else
- i = -1;
- }
- }
-}
-
-
-int AddEdge( int v1, int v2, face_t *f )
-{
- if (numedges >= MAX_MAP_EDGES)
- Error ("Too many edges in map, max == %d", MAX_MAP_EDGES);
-
- g_VertEdgeList[v1].AddToTail( numedges );
- g_VertEdgeList[v2].AddToTail( numedges );
- IntSort( g_VertEdgeList[v1] );
- IntSort( g_VertEdgeList[v2] );
-
- dedge_t *edge = &dedges[numedges];
- numedges++;
-
- edge->v[0] = v1;
- edge->v[1] = v2;
- edgefaces[numedges-1][0] = f;
- return numedges - 1;
-}
-
-
-/*
-==================
-GetEdge
-
-Called by writebsp.
-Don't allow four way edges
-==================
-*/
-int GetEdge2 (int v1, int v2, face_t *f)
-{
- dedge_t *edge;
-
- c_tryedges++;
-
- if (!noshare)
- {
- // Check all edges connected to v1.
- CUtlVector<int> &theList = g_VertEdgeList[v1];
- for( int i=0; i < theList.Size(); i++ )
- {
- int iEdge = theList[i];
- edge = &dedges[iEdge];
- if (v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[iEdge][0]->contents == f->contents)
- {
- if (edgefaces[iEdge][1])
- continue;
-
- edgefaces[iEdge][1] = f;
- return -iEdge;
- }
- }
- }
-
- return AddEdge( v1, v2, f );
-}
-
-/*
-===========================================================================
-
-FACE MERGING
-
-===========================================================================
-*/
-
-#define CONTINUOUS_EPSILON 0.001
-
-/*
-=============
-TryMergeWinding
-
-If two polygons share a common edge and the edges that meet at the
-common points are both inside the other polygons, merge them
-
-Returns NULL if the faces couldn't be merged, or the new face.
-The originals will NOT be freed.
-=============
-*/
-winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, Vector& planenormal)
-{
- Vector *p1, *p2, *p3, *p4, *back;
- winding_t *newf;
- int i, j, k, l;
- Vector normal, delta;
- vec_t dot;
- qboolean keep1, keep2;
-
-
- //
- // find a common edge
- //
- p1 = p2 = NULL; // stop compiler warning
- j = 0; //
-
- for (i=0 ; i<f1->numpoints ; i++)
- {
- p1 = &f1->p[i];
- p2 = &f1->p[(i+1)%f1->numpoints];
- for (j=0 ; j<f2->numpoints ; j++)
- {
- p3 = &f2->p[j];
- p4 = &f2->p[(j+1)%f2->numpoints];
- for (k=0 ; k<3 ; k++)
- {
- if (fabs((*p1)[k] - (*p4)[k]) > EQUAL_EPSILON)
- break;
- if (fabs((*p2)[k] - (*p3)[k]) > EQUAL_EPSILON)
- break;
- }
- if (k==3)
- break;
- }
- if (j < f2->numpoints)
- break;
- }
-
- if (i == f1->numpoints)
- return NULL; // no matching edges
-
- //
- // check slope of connected lines
- // if the slopes are colinear, the point can be removed
- //
- back = &f1->p[(i+f1->numpoints-1)%f1->numpoints];
- VectorSubtract (*p1, *back, delta);
- CrossProduct (planenormal, delta, normal);
- VectorNormalize (normal);
-
- back = &f2->p[(j+2)%f2->numpoints];
- VectorSubtract (*back, *p1, delta);
- dot = DotProduct (delta, normal);
- if (dot > CONTINUOUS_EPSILON)
- return NULL; // not a convex polygon
- keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
-
- back = &f1->p[(i+2)%f1->numpoints];
- VectorSubtract (*back, *p2, delta);
- CrossProduct (planenormal, delta, normal);
- VectorNormalize (normal);
-
- back = &f2->p[(j+f2->numpoints-1)%f2->numpoints];
- VectorSubtract (*back, *p2, delta);
- dot = DotProduct (delta, normal);
- if (dot > CONTINUOUS_EPSILON)
- return NULL; // not a convex polygon
- keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
-
- //
- // build the new polygon
- //
- newf = AllocWinding (f1->numpoints + f2->numpoints);
-
- // copy first polygon
- for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
- {
- if (k==(i+1)%f1->numpoints && !keep2)
- continue;
-
- VectorCopy (f1->p[k], newf->p[newf->numpoints]);
- newf->numpoints++;
- }
-
- // copy second polygon
- for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
- {
- if (l==(j+1)%f2->numpoints && !keep1)
- continue;
- VectorCopy (f2->p[l], newf->p[newf->numpoints]);
- newf->numpoints++;
- }
-
- return newf;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool OverlaysAreEqual( face_t *f1, face_t *f2 )
-{
- // Check the overlay ids - see if they are the same.
- if ( f1->originalface->aOverlayIds.Count() != f2->originalface->aOverlayIds.Count() )
- return false;
-
- int nOverlayCount = f1->originalface->aOverlayIds.Count();
- for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
- {
- int nOverlayId = f1->originalface->aOverlayIds[iOverlay];
- if ( f2->originalface->aOverlayIds.Find( nOverlayId ) == -1 )
- return false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool FaceOnWaterBrush( face_t *face )
-{
- side_t *pSide = face->originalface;
- if ( !pSide )
- return false;
-
- if ( pSide->contents & ( CONTENTS_WATER | CONTENTS_SLIME ) )
- return true;
-
- return false;
-}
-
-/*
-=============
-TryMerge
-
-If two polygons share a common edge and the edges that meet at the
-common points are both inside the other polygons, merge them
-
-Returns NULL if the faces couldn't be merged, or the new face.
-The originals will NOT be freed.
-=============
-*/
-face_t *TryMerge (face_t *f1, face_t *f2, Vector& planenormal)
-{
- face_t *newf;
- winding_t *nw;
-
- if (!f1->w || !f2->w)
- return NULL;
- if (f1->texinfo != f2->texinfo)
- return NULL;
- if (f1->planenum != f2->planenum) // on front and back sides
- return NULL;
- if (f1->contents != f2->contents)
- return NULL;
- if ( f1->originalface->smoothingGroups != f2->originalface->smoothingGroups )
- return NULL;
- if ( !OverlaysAreEqual( f1, f2 ) )
- return NULL;
- if ( nomergewater && ( FaceOnWaterBrush( f1 ) || FaceOnWaterBrush( f2 ) ) )
- return NULL;
-
- nw = TryMergeWinding (f1->w, f2->w, planenormal);
- if (!nw)
- return NULL;
-
- c_merge++;
- newf = NewFaceFromFace (f1);
- newf->w = nw;
-
- f1->merged = newf;
- f2->merged = newf;
-
- return newf;
-}
-
-/*
-===============
-MergeFaceList
-===============
-*/
-void MergeFaceList(face_t **pList)
-{
- face_t *f1, *f2, *end;
- face_t *merged;
- plane_t *plane;
-
- merged = NULL;
-
- for (f1 = *pList; f1 ; f1 = f1->next)
- {
- if (f1->merged || f1->split[0] || f1->split[1])
- continue;
- for (f2 = *pList; f2 != f1 ; f2=f2->next)
- {
- if (f2->merged || f2->split[0] || f2->split[1])
- continue;
-
- plane = &g_MainMap->mapplanes[f1->planenum];
- merged = TryMerge (f1, f2, plane->normal);
- if (!merged)
- continue;
-
- // add merged to the end of the face list
- // so it will be checked against all the faces again
- for (end = *pList; end->next ; end = end->next)
- ;
- merged->next = NULL;
- end->next = merged;
- break;
- }
- }
-}
-
-//=====================================================================
-
-/*
-===============
-SubdivideFace
-
-Chop up faces that are larger than we want in the surface cache
-===============
-*/
-void SubdivideFace (face_t **pFaceList, face_t *f)
-{
- float mins, maxs;
- vec_t v;
- vec_t luxelsPerWorldUnit;
- int axis, i;
- texinfo_t *tex;
- Vector temp;
- vec_t dist;
- winding_t *w, *frontw, *backw;
-
- if ( f->merged || f->split[0] || f->split[1] )
- return;
-
-// special (non-surface cached) faces don't need subdivision
- tex = &texinfo[f->texinfo];
-
- if( tex->flags & SURF_NOLIGHT )
- {
- return;
- }
-
- for (axis = 0 ; axis < 2 ; axis++)
- {
- while (1)
- {
- mins = 999999;
- maxs = -999999;
-
- VECTOR_COPY (tex->lightmapVecsLuxelsPerWorldUnits[axis], temp);
- w = f->w;
- for (i=0 ; i<w->numpoints ; i++)
- {
- v = DotProduct (w->p[i], temp);
- if (v < mins)
- mins = v;
- if (v > maxs)
- maxs = v;
- }
-#if 0
- if (maxs - mins <= 0)
- Error ("zero extents");
-#endif
- if (maxs - mins <= g_maxLightmapDimension)
- break;
-
- // split it
- c_subdivide++;
-
- luxelsPerWorldUnit = VectorNormalize (temp);
-
- dist = ( mins + g_maxLightmapDimension - 1 ) / luxelsPerWorldUnit;
-
- ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
- if (!frontw || !backw)
- Error ("SubdivideFace: didn't split the polygon");
-
- f->split[0] = NewFaceFromFace (f);
- f->split[0]->w = frontw;
- f->split[0]->next = *pFaceList;
- *pFaceList = f->split[0];
-
- f->split[1] = NewFaceFromFace (f);
- f->split[1]->w = backw;
- f->split[1]->next = *pFaceList;
- *pFaceList = f->split[1];
-
- SubdivideFace (pFaceList, f->split[0]);
- SubdivideFace (pFaceList, f->split[1]);
- return;
- }
- }
-}
-
-void SubdivideFaceList(face_t **pFaceList)
-{
- face_t *f;
-
- for (f = *pFaceList ; f ; f=f->next)
- {
- SubdivideFace (pFaceList, f);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Assigns the bottom material to the bottom face
-//-----------------------------------------------------------------------------
-static bool AssignBottomWaterMaterialToFace( face_t *f )
-{
- // NOTE: This happens *after* cubemap fixup occurs, so we need to get the
- // fixed-up bottom material for this
- texinfo_t *pTexInfo = &texinfo[f->texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
-
- char pBottomMatName[512];
- if ( !GetValueFromPatchedMaterial( pMaterialName, "$bottommaterial", pBottomMatName, 512 ) )
- {
- if( !Q_stristr( pMaterialName, "nodraw" ) && !Q_stristr( pMaterialName, "toolsskip" ) )
- {
- Warning("error: material %s doesn't have a $bottommaterial\n", pMaterialName );
- }
- return false;
- }
-
- //Assert( mapplanes[f->planenum].normal.z < 0 );
- texinfo_t newTexInfo;
- newTexInfo.flags = pTexInfo->flags;
- int j, k;
- for (j=0 ; j<2 ; j++)
- {
- for (k=0 ; k<4 ; k++)
- {
- newTexInfo.textureVecsTexelsPerWorldUnits[j][k] = pTexInfo->textureVecsTexelsPerWorldUnits[j][k];
- newTexInfo.lightmapVecsLuxelsPerWorldUnits[j][k] = pTexInfo->lightmapVecsLuxelsPerWorldUnits[j][k];
- }
- }
- newTexInfo.texdata = FindOrCreateTexData( pBottomMatName );
- f->texinfo = FindOrCreateTexInfo( newTexInfo );
-
- return true;
-}
-
-
-//===========================================================================
-
-int c_nodefaces;
-
-static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize );
-void SubdivideFaceBySubdivSize( face_t *f );
-
-/*
-============
-FaceFromPortal
-
-============
-*/
-extern int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
-
-face_t *FaceFromPortal (portal_t *p, int pside)
-{
- face_t *f;
- side_t *side;
- int deltaContents;
-
- // portal does not bridge different visible contents
- side = p->side;
- if (!side)
- return NULL;
-
- // allocate a new face
- f = AllocFace();
-
- // save the original "side" from the map brush -- portal->side
- // see FindPortalSide(...)
- f->originalface = side;
-
- //
- // save material info
- //
- f->texinfo = side->texinfo;
- f->dispinfo = -1; // all faces with displacement info are created elsewhere
- f->smoothingGroups = side->smoothingGroups;
-
- // save plane info
- f->planenum = (side->planenum & ~1) | pside;
- if ( entity_num != 0 )
- {
- // the brush model renderer doesn't use PLANEBACK, so write the real plane
- // inside water faces can be flipped because they are generated on the inside of the brush
- if ( p->nodes[pside]->contents & (CONTENTS_WATER|CONTENTS_SLIME) )
- {
- f->planenum = (side->planenum & ~1) | pside;
- }
- else
- {
- f->planenum = side->planenum;
- }
- }
-
- // save portal info
- f->portal = p;
- f->fogVolumeLeaf = NULL;
-
- deltaContents = VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents);
-
- // don't show insides of windows or grates
- if ( ((p->nodes[pside]->contents & CONTENTS_WINDOW) && deltaContents == CONTENTS_WINDOW) ||
- ((p->nodes[pside]->contents & CONTENTS_GRATE) && deltaContents == CONTENTS_GRATE) )
- {
- FreeFace( f );
- return NULL;
- }
-
- if ( p->nodes[pside]->contents & MASK_WATER )
- {
- f->fogVolumeLeaf = p->nodes[pside];
- }
- else if ( p->nodes[!pside]->contents & MASK_WATER )
- {
- f->fogVolumeLeaf = p->nodes[!pside];
- }
-
- // If it's the underside of water, we need to figure out what material to use, etc.
- if( ( p->nodes[pside]->contents & CONTENTS_WATER ) && deltaContents == CONTENTS_WATER )
- {
- if ( !AssignBottomWaterMaterialToFace( f ) )
- {
- FreeFace( f );
- return NULL;
- }
- }
-
- //
- // generate the winding for the face and save face contents
- //
- if( pside )
- {
- f->w = ReverseWinding(p->winding);
- f->contents = p->nodes[1]->contents;
- }
- else
- {
- f->w = CopyWinding(p->winding);
- f->contents = p->nodes[0]->contents;
- }
-
- f->numPrims = 0;
- f->firstPrimID = 0;
-
- // return the created face
- return f;
-}
-
-/*
-===============
-MakeFaces_r
-
-If a portal will make a visible face,
-mark the side that originally created it
-
- solid / empty : solid
- solid / water : solid
- water / empty : water
- water / water : none
-===============
-*/
-void MakeFaces_r (node_t *node)
-{
- portal_t *p;
- int s;
-
- // recurse down to leafs
- if (node->planenum != PLANENUM_LEAF)
- {
- MakeFaces_r (node->children[0]);
- MakeFaces_r (node->children[1]);
-
- // merge together all visible faces on the node
- if (!nomerge)
- MergeFaceList(&node->faces);
- if (!nosubdiv)
- SubdivideFaceList(&node->faces);
-
- return;
- }
-
- // solid leafs never have visible faces
- if (node->contents & CONTENTS_SOLID)
- return;
-
- // see which portals are valid
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
-
- p->face[s] = FaceFromPortal (p, s);
- if (p->face[s])
- {
- c_nodefaces++;
- p->face[s]->next = p->onnode->faces;
- p->onnode->faces = p->face[s];
- }
- }
-}
-
-typedef winding_t *pwinding_t;
-
-static void PrintWinding( winding_t *w )
-{
- int i;
- Msg( "\t---\n" );
- for( i = 0; i < w->numpoints; i++ )
- {
- Msg( "\t%f %f %f\n", w->p[i].x, w->p[i].y, w->p[i].z );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Adds a winding to the current list of primverts
-// Input : *w - the winding
-// *pIndices - The output indices
-// vertStart - the starting vert index
-// vertCount - current count
-// Output : int - output count including new verts from this winding
-//-----------------------------------------------------------------------------
-int AddWindingToPrimverts( const winding_t *w, unsigned short *pIndices, int vertStart, int vertCount )
-{
- for( int i = 0; i < w->numpoints; i++ )
- {
- int j;
- for( j = vertStart; j < vertStart + vertCount; j++ )
- {
- Vector tmp = g_primverts[j].pos - w->p[i];
-
- if( tmp.LengthSqr() < POINT_EPSILON*POINT_EPSILON )
- {
- pIndices[i] = j;
- break;
- }
- }
- if ( j >= vertStart + vertCount )
- {
- pIndices[i] = j;
- g_primverts[j].pos = w->p[i];
- vertCount++;
- g_numprimverts++;
- if ( g_numprimverts > MAX_MAP_PRIMVERTS )
- {
- Error( "Exceeded max water verts.\nIncrease surface subdivision size or lower your subdivision size in vmt files! (%d>%d)\n",
- ( int )g_numprimverts, ( int )MAX_MAP_PRIMVERTS );
- }
- }
- }
-
- return vertCount;
-}
-
-
-
-#pragma optimize( "g", off )
-#define USE_TRISTRIPS
-
-// UNDONE: Should split this function into subdivide and primitive building parts
-// UNDONE: We should try building strips of shared verts for all water faces in a leaf
-// since those will be drawn concurrently anyway. It should be more efficient.
-static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize )
-{
- // garymcthack - REFACTOR ME!!!
-
- vec_t dummy;
- Vector hackNormal;
- WindingPlane( f->w, hackNormal, &dummy );
-
- // HACK - only subdivide stuff that is facing up or down (for water)
- if( fabs(hackNormal[2]) < .9f )
- {
- return;
- }
-
- // Get the extents of the surface.
- // garymcthack - this assumes a surface of constant z for now (for water). . can generalize later.
- subdivsize = ( int )subdivsize;
- winding_t *w;
- w = CopyWinding( f->w );
-
- Vector min, max;
- WindingBounds( w, min, max );
-
-#if 0
- Msg( "START WINDING: \n" );
- PrintWinding( w );
-#endif
- int xStart, yStart, xEnd, yEnd, xSteps, ySteps;
- xStart = ( int )subdivsize * ( int )( ( min[0] - subdivsize ) / subdivsize );
- xEnd = ( int )subdivsize * ( int )( ( max[0] + subdivsize ) / subdivsize );
- yStart = ( int )subdivsize * ( int )( ( min[1] - subdivsize ) / subdivsize );
- yEnd = ( int )subdivsize * ( int )( ( max[1] + subdivsize ) / subdivsize );
- xSteps = ( xEnd - xStart ) / subdivsize;
- ySteps = ( yEnd - yStart ) / subdivsize;
- int x, y;
- int xi, yi;
- winding_t **windings = ( winding_t ** )new pwinding_t[xSteps * ySteps];
- memset( windings, 0, sizeof( winding_t * ) * xSteps * ySteps );
-
- for( yi = 0, y = yStart; y < yEnd; y += ( int )subdivsize, yi++ )
- {
- for( xi = 0, x = xStart; x < xEnd; x += ( int )subdivsize, xi++ )
- {
- winding_t *tempWinding, *frontWinding, *backWinding;
- float planeDist;
- Vector normal;
- normal.Init( 1.0f, 0.0f, 0.0f );
- planeDist = ( float )x;
- tempWinding = CopyWinding( w );
- ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
- &frontWinding, &backWinding );
- if( tempWinding )
- {
- FreeWinding( tempWinding );
- }
- if( backWinding )
- {
- FreeWinding( backWinding );
- }
- if( !frontWinding )
- {
- continue;
- }
- tempWinding = frontWinding;
-
- normal.Init( -1.0f, 0.0f, 0.0f );
- planeDist = -( float )( x + subdivsize );
- ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
- &frontWinding, &backWinding );
- if( tempWinding )
- {
- FreeWinding( tempWinding );
- }
- if( backWinding )
- {
- FreeWinding( backWinding );
- }
- if( !frontWinding )
- {
- continue;
- }
- tempWinding = frontWinding;
-
- normal.Init( 0.0f, 1.0f, 0.0f );
- planeDist = ( float )y;
- ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
- &frontWinding, &backWinding );
- if( tempWinding )
- {
- FreeWinding( tempWinding );
- }
- if( backWinding )
- {
- FreeWinding( backWinding );
- }
- if( !frontWinding )
- {
- continue;
- }
- tempWinding = frontWinding;
-
- normal.Init( 0.0f, -1.0f, 0.0f );
- planeDist = -( float )( y + subdivsize );
- ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
- &frontWinding, &backWinding );
- if( tempWinding )
- {
- FreeWinding( tempWinding );
- }
- if( backWinding )
- {
- FreeWinding( backWinding );
- }
- if( !frontWinding )
- {
- continue;
- }
-
-#if 0
- Msg( "output winding:\n" );
- PrintWinding( frontWinding );
-#endif
-
- if( frontWinding )
- {
- windings[xi + yi * xSteps] = frontWinding;
- }
- }
- }
- FreeWinding( w );
- dprimitive_t &newPrim = g_primitives[g_numprimitives];
- f->firstPrimID = g_numprimitives;
- f->numPrims = 1;
- newPrim.firstIndex = g_numprimindices;
- newPrim.firstVert = g_numprimverts;
- newPrim.indexCount = 0;
- newPrim.vertCount = 0;
-#ifdef USE_TRISTRIPS
- newPrim.type = PRIM_TRISTRIP;
-#else
- newPrim.type = PRIM_TRILIST;
-#endif
-
- CUtlVector<WORD> triListIndices;
- int i;
- for( i = 0; i < xSteps * ySteps; i++ )
- {
- if( !windings[i] )
- {
- continue;
- }
- unsigned short *pIndices =
- ( unsigned short * )_alloca( windings[i]->numpoints * sizeof( unsigned short ) );
- // find indices for the verts.
- newPrim.vertCount = AddWindingToPrimverts( windings[i], pIndices, newPrim.firstVert, newPrim.vertCount );
-
- // Now that we have indices for the verts, fan-tesselate the polygon and spit out tris.
- for( int j = 0; j < windings[i]->numpoints - 2; j++ )
- {
- triListIndices.AddToTail( pIndices[0] );
- triListIndices.AddToTail( pIndices[j+1] );
- triListIndices.AddToTail( pIndices[j+2] );
- }
- }
-
- delete [] windings;
- // We've already updated the verts and have a trilist. . let's strip it!
- if( !triListIndices.Size() )
- {
- return;
- }
-
-#ifdef USE_TRISTRIPS
- int numTristripIndices;
- WORD *pStripIndices = NULL;
- Stripify( triListIndices.Size() / 3, triListIndices.Base(), &numTristripIndices,
- &pStripIndices );
- Assert( pStripIndices );
-
- // FIXME: Should also call ComputeVertexPermutation and reorder the verts.
-
- for( i = 0; i < numTristripIndices; i++ )
- {
- Assert( pStripIndices[i] >= newPrim.firstVert &&
- pStripIndices[i] < newPrim.firstVert + newPrim.vertCount );
- g_primindices[newPrim.firstIndex + newPrim.indexCount] = pStripIndices[i];
- newPrim.indexCount++;
- g_numprimindices++;
- if( g_numprimindices > MAX_MAP_PRIMINDICES )
- {
- Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
- }
- }
- delete [] pStripIndices;
-#else
- for( i = 0; i < triListIndices.Size(); i++ )
- {
- g_primindices[newPrim.firstIndex + newPrim.indexCount] = triListIndices[i];
- newPrim.indexCount++;
- g_numprimindices++;
- if( g_numprimindices > MAX_MAP_PRIMINDICES )
- {
- Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
- }
- }
-#endif
- g_numprimitives++; // don't increment until we get here and are sure that we have a primitive.
- if( g_numprimitives > MAX_MAP_PRIMITIVES )
- {
- Error( "Exceeded max water primitives.\nIncrease surface subdivision size! (%d>%d)\n", ( int )g_numprimitives, ( int )MAX_MAP_PRIMITIVES );
- }
-}
-
-void SubdivideFaceBySubdivSize( face_t *f )
-{
- if( f->numpoints == 0 || f->split[0] || f->split[1] || f->merged || !f->w )
- {
- return;
- }
- // see if the face needs to be subdivided.
- texinfo_t *pTexInfo = &texinfo[f->texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- bool bFound;
- const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- MaterialSystemMaterial_t matID =
- FindOriginalMaterial( pMaterialName, &bFound, false );
-
- if( !bFound )
- {
- return;
- }
- const char *subdivsizeString = GetMaterialVar( matID, "$subdivsize" );
- if( subdivsizeString )
- {
- float subdivSize = atof( subdivsizeString );
- if( subdivSize > 0.0f )
- {
- // NOTE: Subdivision is unsupported and should be phased out
- Warning("Using subdivision on %s\n", pMaterialName );
- SubdivideFaceBySubdivSize( f, subdivSize );
- }
- }
-}
-
-void SplitSubdividedFaces_Node_r( node_t *node )
-{
- if (node->planenum == PLANENUM_LEAF)
- {
- return;
- }
- face_t *f;
- for( f = node->faces; f ;f = f->next )
- {
- SubdivideFaceBySubdivSize( f );
- }
-
- //
- // recursively output the other nodes
- //
- SplitSubdividedFaces_Node_r( node->children[0] );
- SplitSubdividedFaces_Node_r( node->children[1] );
-}
-
-void SplitSubdividedFaces( face_t *pLeafFaceList, node_t *headnode )
-{
- // deal with leaf faces.
- face_t *f = pLeafFaceList;
- while ( f )
- {
- SubdivideFaceBySubdivSize( f );
- f = f->next;
- }
-
- // deal with node faces.
- SplitSubdividedFaces_Node_r( headnode );
-}
-
-#pragma optimize( "", on )
-
-/*
-============
-MakeFaces
-============
-*/
-void MakeFaces (node_t *node)
-{
- qprintf ("--- MakeFaces ---\n");
- c_merge = 0;
- c_subdivide = 0;
- c_nodefaces = 0;
-
- MakeFaces_r (node);
-
- qprintf ("%5i makefaces\n", c_nodefaces);
- qprintf ("%5i merged\n", c_merge);
- qprintf ("%5i subdivided\n", c_subdivide);
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// faces.c
+
+#include "vbsp.h"
+#include "utlvector.h"
+#include "utilmatlib.h"
+#include <float.h>
+#include "mstristrip.h"
+#include "tier1/strtools.h"
+#include "materialpatch.h"
+/*
+
+ some faces will be removed before saving, but still form nodes:
+
+ the insides of sky volumes
+ meeting planes of different water current volumes
+
+*/
+
+// undefine for dumb linear searches
+#define USE_HASHING
+
+#define INTEGRAL_EPSILON 0.01
+#define POINT_EPSILON 0.1
+#define OFF_EPSILON 0.25
+
+int c_merge;
+int c_subdivide;
+
+int c_totalverts;
+int c_uniqueverts;
+int c_degenerate;
+int c_tjunctions;
+int c_faceoverflows;
+int c_facecollapse;
+int c_badstartverts;
+
+#define MAX_SUPERVERTS 512
+int superverts[MAX_SUPERVERTS];
+int numsuperverts;
+
+face_t *edgefaces[MAX_MAP_EDGES][2];
+int firstmodeledge = 1;
+int firstmodelface;
+
+int c_tryedges;
+
+Vector edge_dir;
+Vector edge_start;
+vec_t edge_len;
+
+int num_edge_verts;
+int edge_verts[MAX_MAP_VERTS];
+
+
+float g_maxLightmapDimension = 32;
+
+
+face_t *NewFaceFromFace (face_t *f);
+
+// Used to speed up GetEdge2(). Holds a list of edges connected to each vert.
+CUtlVector<int> g_VertEdgeList[MAX_MAP_VERTS];
+
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+ struct hashvert_s *next;
+ int num;
+} hashvert_t;
+
+#define HASH_BITS 7
+#define HASH_SIZE (COORD_EXTENT>>HASH_BITS)
+
+
+int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain
+int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
+
+//face_t *edgefaces[MAX_MAP_EDGES][2];
+
+//============================================================================
+
+
+unsigned HashVec (Vector& vec)
+{
+ int x, y;
+
+ x = (MAX_COORD_INTEGER + (int)(vec[0]+0.5)) >> HASH_BITS;
+ y = (MAX_COORD_INTEGER + (int)(vec[1]+0.5)) >> HASH_BITS;
+
+ if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
+ Error ("HashVec: point outside valid range");
+
+ return y*HASH_SIZE + x;
+}
+
+#ifdef USE_HASHING
+/*
+=============
+GetVertex
+
+Uses hashing
+=============
+*/
+int GetVertexnum (Vector& in)
+{
+ int h;
+ int i;
+ Vector vert;
+ int vnum;
+
+ c_totalverts++;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(in[i] - (int)(in[i]+0.5)) < INTEGRAL_EPSILON)
+ vert[i] = (int)(in[i]+0.5);
+ else
+ vert[i] = in[i];
+ }
+
+ h = HashVec (vert);
+
+ for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
+ {
+ Vector& p = dvertexes[vnum].point;
+ if ( fabs(p[0]-vert[0])<POINT_EPSILON
+ && fabs(p[1]-vert[1])<POINT_EPSILON
+ && fabs(p[2]-vert[2])<POINT_EPSILON )
+ return vnum;
+ }
+
+// emit a vertex
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+
+ dvertexes[numvertexes].point[0] = vert[0];
+ dvertexes[numvertexes].point[1] = vert[1];
+ dvertexes[numvertexes].point[2] = vert[2];
+
+ vertexchain[numvertexes] = hashverts[h];
+ hashverts[h] = numvertexes;
+
+ c_uniqueverts++;
+
+ numvertexes++;
+
+ return numvertexes-1;
+}
+#else
+/*
+==================
+GetVertexnum
+
+Dumb linear search
+==================
+*/
+int GetVertexnum (Vector& v)
+{
+ int i, j;
+ dvertex_t *dv;
+ vec_t d;
+
+ c_totalverts++;
+
+ // make really close values exactly integral
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
+ v[i] = (int)(v[i]+0.5);
+ if (v[i] < MIN_COORD_INTEGER || v[i] > MAX_COORD_INTEGER)
+ Error ("GetVertexnum: outside world, vertex %.1f %.1f %.1f", v.x, v.y, v.z);
+ }
+
+ // search for an existing vertex match
+ for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ d = v[j] - dv->point[j];
+ if ( d > POINT_EPSILON || d < -POINT_EPSILON)
+ break;
+ }
+ if (j == 3)
+ return i; // a match
+ }
+
+ // new point
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+ VectorCopy (v, dv->point);
+ numvertexes++;
+ c_uniqueverts++;
+
+ return numvertexes-1;
+}
+#endif
+
+
+/*
+==================
+FaceFromSuperverts
+
+The faces vertexes have beeb added to the superverts[] array,
+and there may be more there than can be held in a face (MAXEDGES).
+
+If less, the faces vertexnums[] will be filled in, otherwise
+face will reference a tree of split[] faces until all of the
+vertexnums can be added.
+
+superverts[base] will become face->vertexnums[0], and the others
+will be circularly filled in.
+==================
+*/
+void FaceFromSuperverts (face_t **pListHead, face_t *f, int base)
+{
+ face_t *newf;
+ int remaining;
+ int i;
+
+ remaining = numsuperverts;
+ while (remaining > MAXEDGES)
+ { // must split into two faces, because of vertex overload
+ c_faceoverflows++;
+
+ newf = NewFaceFromFace (f);
+ f->split[0] = newf;
+
+ newf->next = *pListHead;
+ *pListHead = newf;
+
+ newf->numpoints = MAXEDGES;
+ for (i=0 ; i<MAXEDGES ; i++)
+ newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
+
+ f->split[1] = NewFaceFromFace (f);
+ f = f->split[1];
+
+ f->next = *pListHead;
+ *pListHead = f;
+
+ remaining -= (MAXEDGES-2);
+ base = (base+MAXEDGES-1)%numsuperverts;
+ }
+
+ // copy the vertexes back to the face
+ f->numpoints = remaining;
+ for (i=0 ; i<remaining ; i++)
+ f->vertexnums[i] = superverts[(i+base)%numsuperverts];
+}
+
+
+/*
+==================
+EmitFaceVertexes
+==================
+*/
+void EmitFaceVertexes (face_t **pListHead, face_t *f)
+{
+ winding_t *w;
+ int i;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ if (noweld)
+ { // make every point unique
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+ superverts[i] = numvertexes;
+ VectorCopy (w->p[i], dvertexes[numvertexes].point);
+ numvertexes++;
+ c_uniqueverts++;
+ c_totalverts++;
+ }
+ else
+ superverts[i] = GetVertexnum (w->p[i]);
+ }
+ numsuperverts = w->numpoints;
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (pListHead, f, 0);
+}
+
+/*
+==================
+EmitNodeFaceVertexes_r
+==================
+*/
+void EmitNodeFaceVertexes_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ // leaf faces are emitted in second pass
+ return;
+ }
+
+ for (f=node->faces ; f ; f=f->next)
+ {
+ EmitFaceVertexes (&node->faces, f);
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ EmitNodeFaceVertexes_r (node->children[i]);
+ }
+}
+
+void EmitLeafFaceVertexes( face_t **ppLeafFaceList )
+{
+ face_t *f = *ppLeafFaceList;
+
+ while ( f )
+ {
+ EmitFaceVertexes( ppLeafFaceList, f );
+ f = f->next;
+ }
+}
+
+
+#ifdef USE_HASHING
+/*
+==========
+FindEdgeVerts
+
+Uses the hash tables to cut down to a small number
+==========
+*/
+void FindEdgeVerts (Vector& v1, Vector& v2)
+{
+ int x1, x2, y1, y2, t;
+ int x, y;
+ int vnum;
+
+#if 0
+{
+ int i;
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<numvertexes-1 ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+ x1 = (MAX_COORD_INTEGER + (int)(v1[0]+0.5)) >> HASH_BITS;
+ y1 = (MAX_COORD_INTEGER + (int)(v1[1]+0.5)) >> HASH_BITS;
+ x2 = (MAX_COORD_INTEGER + (int)(v2[0]+0.5)) >> HASH_BITS;
+ y2 = (MAX_COORD_INTEGER + (int)(v2[1]+0.5)) >> HASH_BITS;
+
+ if (x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if (y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+#if 0
+ x1--;
+ x2++;
+ y1--;
+ y2++;
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 >= HASH_SIZE)
+ x2 = HASH_SIZE;
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 >= HASH_SIZE)
+ y2 = HASH_SIZE;
+#endif
+ num_edge_verts = 0;
+ for (x=x1 ; x <= x2 ; x++)
+ {
+ for (y=y1 ; y <= y2 ; y++)
+ {
+ for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
+ {
+ edge_verts[num_edge_verts++] = vnum;
+ }
+ }
+ }
+}
+
+#else
+/*
+==========
+FindEdgeVerts
+
+Forced a dumb check of everything
+==========
+*/
+void FindEdgeVerts (Vector& v1, Vector& v2)
+{
+ int i;
+
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<num_edge_verts ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+/*
+==========
+TestEdge
+
+Can be recursively reentered
+==========
+*/
+void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
+{
+ int j, k;
+ vec_t dist;
+ Vector delta;
+ Vector exact;
+ Vector off;
+ vec_t error;
+ Vector p;
+
+ if (p1 == p2)
+ {
+ c_degenerate++;
+ return; // degenerate edge
+ }
+
+ for (k=startvert ; k<num_edge_verts ; k++)
+ {
+ j = edge_verts[k];
+ if (j==p1 || j == p2)
+ continue;
+
+ VectorCopy (dvertexes[j].point, p);
+
+ VectorSubtract (p, edge_start, delta);
+ dist = DotProduct (delta, edge_dir);
+ if (dist <=start || dist >= end)
+ continue; // off an end
+ VectorMA (edge_start, dist, edge_dir, exact);
+ VectorSubtract (p, exact, off);
+ error = off.Length();
+
+ if (error > OFF_EPSILON)
+ continue; // not on the edge
+
+ // break the edge
+ c_tjunctions++;
+ TestEdge (start, dist, p1, j, k+1);
+ TestEdge (dist, end, j, p2, k+1);
+ return;
+ }
+
+ // the edge p1 to p2 is now free of tjunctions
+ if (numsuperverts >= MAX_SUPERVERTS)
+ Error ("Edge with too many vertices due to t-junctions. Max %d verts along an edge!\n", MAX_SUPERVERTS);
+ superverts[numsuperverts] = p1;
+ numsuperverts++;
+}
+
+
+// stores the edges that each vert is part of
+struct face_vert_table_t
+{
+ face_vert_table_t()
+ {
+ edge0 = -1;
+ edge1 = -1;
+ }
+
+ void AddEdge( int edge )
+ {
+ if ( edge0 == -1 )
+ {
+ edge0 = edge;
+ }
+ else
+ {
+ // can only have two edges
+ Assert(edge1==-1);
+ edge1 = edge;
+ }
+ }
+
+ bool HasEdge( int edge ) const
+ {
+ if ( edge >= 0 )
+ {
+ if ( edge0 == edge || edge1 == edge )
+ return true;
+ }
+ return false;
+ }
+
+ int edge0;
+ int edge1;
+};
+
+// if these two verts share an edge, they must be collinear
+bool IsDiagonal( const face_vert_table_t &v0, const face_vert_table_t &v1 )
+{
+ if ( v1.HasEdge(v0.edge0) || v1.HasEdge(v0.edge1) )
+ return false;
+
+ return true;
+}
+
+
+void Triangulate_r( CUtlVector<int> &out, const CUtlVector<int> &inIndices, const CUtlVector<face_vert_table_t> &poly )
+{
+ Assert( inIndices.Count() > 2 );
+
+ // one triangle left, return
+ if ( inIndices.Count() == 3 )
+ {
+ for ( int i = 0; i < inIndices.Count(); i++ )
+ {
+ out.AddToTail( inIndices[i] );
+ }
+ return;
+ }
+
+ // check each pair of verts and see if they are diagonal (not on a shared edge)
+ // if so, split & recurse
+ for ( int i = 0; i < inIndices.Count(); i++ )
+ {
+ int count = inIndices.Count();
+
+ // i + count is myself, i + count-1 is previous, so we need to stop at i+count-2
+ for ( int j = 2; j < count-1; j++ )
+ {
+ // if these two form a diagonal, split the poly along
+ // the diagonal and triangulate the two sub-polys
+ int index = inIndices[i];
+ int nextArray = (i+j)%count;
+ int nextIndex = inIndices[nextArray];
+ if ( IsDiagonal(poly[index], poly[nextIndex]) )
+ {
+ // add the poly up to the diagonal
+ CUtlVector<int> in1;
+ for ( int k = i; k != nextArray; k = (k+1)%count )
+ {
+ in1.AddToTail(inIndices[k]);
+ }
+ in1.AddToTail(nextIndex);
+
+ // add the rest of the poly starting with the diagonal
+ CUtlVector<int> in2;
+ in2.AddToTail(index);
+ for ( int l = nextArray; l != i; l = (l+1)%count )
+ {
+ in2.AddToTail(inIndices[l]);
+ }
+
+ // triangulate the sub-polys
+ Triangulate_r( out, in1, poly );
+ Triangulate_r( out, in2, poly );
+ return;
+ }
+ }
+ }
+
+ // didn't find a diagonal
+ Assert(0);
+}
+
+/*
+==================
+FixFaceEdges
+
+==================
+*/
+void FixFaceEdges (face_t **pList, face_t *f)
+{
+ int p1, p2;
+ int i;
+ Vector e2;
+ vec_t len;
+ int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
+ int base;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ numsuperverts = 0;
+
+ int originalPoints = f->numpoints;
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ p1 = f->vertexnums[i];
+ p2 = f->vertexnums[(i+1)%f->numpoints];
+
+ VectorCopy (dvertexes[p1].point, edge_start);
+ VectorCopy (dvertexes[p2].point, e2);
+
+ FindEdgeVerts (edge_start, e2);
+
+ VectorSubtract (e2, edge_start, edge_dir);
+ len = VectorNormalize (edge_dir);
+
+ start[i] = numsuperverts;
+ TestEdge (0, len, p1, p2, 0);
+
+ count[i] = numsuperverts - start[i];
+ }
+
+ if (numsuperverts < 3)
+ { // entire face collapsed
+ f->numpoints = 0;
+ c_facecollapse++;
+ return;
+ }
+
+ // we want to pick a vertex that doesn't have tjunctions
+ // on either side, which can cause artifacts on trifans,
+ // especially underwater
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
+ break;
+ }
+ if (i == f->numpoints)
+ {
+ f->badstartvert = true;
+ c_badstartverts++;
+ base = 0;
+
+ }
+ else
+ { // rotate the vertex order
+ base = start[i];
+ }
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (pList, f, base);
+
+ // if this is the world, then re-triangulate to sew cracks
+ if ( f->badstartvert && entity_num == 0 )
+ {
+ CUtlVector<face_vert_table_t> poly;
+ CUtlVector<int> inIndices;
+ CUtlVector<int> outIndices;
+ poly.AddMultipleToTail( numsuperverts );
+ for ( i = 0; i < originalPoints; i++ )
+ {
+ // edge may not have output any points. Don't mark
+ if ( !count[i] )
+ continue;
+ // mark each edge the point is a member of
+ // we'll use this as a fast "is collinear" test
+ for ( int j = 0; j <= count[i]; j++ )
+ {
+ int polyIndex = (start[i] + j) % numsuperverts;
+ poly[polyIndex].AddEdge( i );
+ }
+ }
+ for ( i = 0; i < numsuperverts; i++ )
+ {
+ inIndices.AddToTail( i );
+ }
+ Triangulate_r( outIndices, inIndices, poly );
+ dprimitive_t &newPrim = g_primitives[g_numprimitives];
+ f->firstPrimID = g_numprimitives;
+ g_numprimitives++;
+ f->numPrims = 1;
+ newPrim.firstIndex = g_numprimindices;
+ newPrim.firstVert = g_numprimverts;
+ newPrim.indexCount = outIndices.Count();
+ newPrim.vertCount = 0;
+ newPrim.type = PRIM_TRILIST;
+ g_numprimindices += newPrim.indexCount;
+ if ( g_numprimitives > MAX_MAP_PRIMITIVES || g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error("Too many t-junctions to fix up! (%d prims, max %d :: %d indices, max %d)\n", g_numprimitives, MAX_MAP_PRIMITIVES, g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ for ( i = 0; i < outIndices.Count(); i++ )
+ {
+ g_primindices[newPrim.firstIndex + i] = outIndices[i];
+ }
+ }
+}
+
+/*
+==================
+FixEdges_r
+==================
+*/
+void FixEdges_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ return;
+ }
+
+ for (f=node->faces ; f ; f=f->next)
+ FixFaceEdges (&node->faces, f);
+
+ for (i=0 ; i<2 ; i++)
+ FixEdges_r (node->children[i]);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fix the t-junctions on detail faces
+//-----------------------------------------------------------------------------
+void FixLeafFaceEdges( face_t **ppLeafFaceList )
+{
+ face_t *f;
+
+ for ( f = *ppLeafFaceList; f; f = f->next )
+ {
+ FixFaceEdges( ppLeafFaceList, f );
+ }
+}
+
+/*
+===========
+FixTjuncs
+
+===========
+*/
+
+face_t *FixTjuncs (node_t *headnode, face_t *pLeafFaceList)
+{
+ // snap and merge all vertexes
+ qprintf ("---- snap verts ----\n");
+ memset (hashverts, 0, sizeof(hashverts));
+ memset (vertexchain, 0, sizeof(vertexchain));
+ c_totalverts = 0;
+ c_uniqueverts = 0;
+ c_faceoverflows = 0;
+ EmitNodeFaceVertexes_r (headnode);
+
+ // UNDONE: This count is wrong with tjuncs off on details - since
+
+ // break edges on tjunctions
+ qprintf ("---- tjunc ----\n");
+ c_tryedges = 0;
+ c_degenerate = 0;
+ c_facecollapse = 0;
+ c_tjunctions = 0;
+
+ if ( g_bAllowDetailCracks )
+ {
+ FixEdges_r (headnode);
+ EmitLeafFaceVertexes( &pLeafFaceList );
+ FixLeafFaceEdges( &pLeafFaceList );
+ }
+ else
+ {
+ EmitLeafFaceVertexes( &pLeafFaceList );
+ if (!notjunc)
+ {
+ FixEdges_r (headnode);
+ FixLeafFaceEdges( &pLeafFaceList );
+ }
+ }
+
+
+ qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
+ qprintf ("%5i edges degenerated\n", c_degenerate);
+ qprintf ("%5i faces degenerated\n", c_facecollapse);
+ qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
+ qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
+ qprintf ("%5i bad start verts\n", c_badstartverts);
+
+ return pLeafFaceList;
+}
+
+
+//========================================================
+
+int c_faces;
+
+face_t *AllocFace (void)
+{
+ static int s_FaceId = 0;
+
+ face_t *f;
+
+ f = (face_t*)malloc(sizeof(*f));
+ memset (f, 0, sizeof(*f));
+ f->id = s_FaceId;
+ ++s_FaceId;
+
+ c_faces++;
+
+ return f;
+}
+
+face_t *NewFaceFromFace (face_t *f)
+{
+ face_t *newf;
+
+ newf = AllocFace ();
+ *newf = *f;
+ newf->merged = NULL;
+ newf->split[0] = newf->split[1] = NULL;
+ newf->w = NULL;
+ return newf;
+}
+
+void FreeFace (face_t *f)
+{
+ if (f->w)
+ FreeWinding (f->w);
+ free (f);
+ c_faces--;
+}
+
+
+void FreeFaceList( face_t *pFaces )
+{
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+
+ FreeFace( pFaces );
+ pFaces = next;
+ }
+}
+
+//========================================================
+
+void GetEdge2_InitOptimizedList()
+{
+ for( int i=0; i < MAX_MAP_VERTS; i++ )
+ g_VertEdgeList[i].RemoveAll();
+}
+
+
+void IntSort( CUtlVector<int> &theList )
+{
+ for( int i=0; i < theList.Size()-1; i++ )
+ {
+ if( theList[i] > theList[i+1] )
+ {
+ int temp = theList[i];
+ theList[i] = theList[i+1];
+ theList[i+1] = temp;
+ if( i > 0 )
+ i -= 2;
+ else
+ i = -1;
+ }
+ }
+}
+
+
+int AddEdge( int v1, int v2, face_t *f )
+{
+ if (numedges >= MAX_MAP_EDGES)
+ Error ("Too many edges in map, max == %d", MAX_MAP_EDGES);
+
+ g_VertEdgeList[v1].AddToTail( numedges );
+ g_VertEdgeList[v2].AddToTail( numedges );
+ IntSort( g_VertEdgeList[v1] );
+ IntSort( g_VertEdgeList[v2] );
+
+ dedge_t *edge = &dedges[numedges];
+ numedges++;
+
+ edge->v[0] = v1;
+ edge->v[1] = v2;
+ edgefaces[numedges-1][0] = f;
+ return numedges - 1;
+}
+
+
+/*
+==================
+GetEdge
+
+Called by writebsp.
+Don't allow four way edges
+==================
+*/
+int GetEdge2 (int v1, int v2, face_t *f)
+{
+ dedge_t *edge;
+
+ c_tryedges++;
+
+ if (!noshare)
+ {
+ // Check all edges connected to v1.
+ CUtlVector<int> &theList = g_VertEdgeList[v1];
+ for( int i=0; i < theList.Size(); i++ )
+ {
+ int iEdge = theList[i];
+ edge = &dedges[iEdge];
+ if (v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[iEdge][0]->contents == f->contents)
+ {
+ if (edgefaces[iEdge][1])
+ continue;
+
+ edgefaces[iEdge][1] = f;
+ return -iEdge;
+ }
+ }
+ }
+
+ return AddEdge( v1, v2, f );
+}
+
+/*
+===========================================================================
+
+FACE MERGING
+
+===========================================================================
+*/
+
+#define CONTINUOUS_EPSILON 0.001
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, Vector& planenormal)
+{
+ Vector *p1, *p2, *p3, *p4, *back;
+ winding_t *newf;
+ int i, j, k, l;
+ Vector normal, delta;
+ vec_t dot;
+ qboolean keep1, keep2;
+
+
+ //
+ // find a common edge
+ //
+ p1 = p2 = NULL; // stop compiler warning
+ j = 0; //
+
+ for (i=0 ; i<f1->numpoints ; i++)
+ {
+ p1 = &f1->p[i];
+ p2 = &f1->p[(i+1)%f1->numpoints];
+ for (j=0 ; j<f2->numpoints ; j++)
+ {
+ p3 = &f2->p[j];
+ p4 = &f2->p[(j+1)%f2->numpoints];
+ for (k=0 ; k<3 ; k++)
+ {
+ if (fabs((*p1)[k] - (*p4)[k]) > EQUAL_EPSILON)
+ break;
+ if (fabs((*p2)[k] - (*p3)[k]) > EQUAL_EPSILON)
+ break;
+ }
+ if (k==3)
+ break;
+ }
+ if (j < f2->numpoints)
+ break;
+ }
+
+ if (i == f1->numpoints)
+ return NULL; // no matching edges
+
+ //
+ // check slope of connected lines
+ // if the slopes are colinear, the point can be removed
+ //
+ back = &f1->p[(i+f1->numpoints-1)%f1->numpoints];
+ VectorSubtract (*p1, *back, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = &f2->p[(j+2)%f2->numpoints];
+ VectorSubtract (*back, *p1, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ back = &f1->p[(i+2)%f1->numpoints];
+ VectorSubtract (*back, *p2, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = &f2->p[(j+f2->numpoints-1)%f2->numpoints];
+ VectorSubtract (*back, *p2, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ //
+ // build the new polygon
+ //
+ newf = AllocWinding (f1->numpoints + f2->numpoints);
+
+ // copy first polygon
+ for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+ {
+ if (k==(i+1)%f1->numpoints && !keep2)
+ continue;
+
+ VectorCopy (f1->p[k], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ // copy second polygon
+ for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+ {
+ if (l==(j+1)%f2->numpoints && !keep1)
+ continue;
+ VectorCopy (f2->p[l], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ return newf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool OverlaysAreEqual( face_t *f1, face_t *f2 )
+{
+ // Check the overlay ids - see if they are the same.
+ if ( f1->originalface->aOverlayIds.Count() != f2->originalface->aOverlayIds.Count() )
+ return false;
+
+ int nOverlayCount = f1->originalface->aOverlayIds.Count();
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ int nOverlayId = f1->originalface->aOverlayIds[iOverlay];
+ if ( f2->originalface->aOverlayIds.Find( nOverlayId ) == -1 )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool FaceOnWaterBrush( face_t *face )
+{
+ side_t *pSide = face->originalface;
+ if ( !pSide )
+ return false;
+
+ if ( pSide->contents & ( CONTENTS_WATER | CONTENTS_SLIME ) )
+ return true;
+
+ return false;
+}
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2, Vector& planenormal)
+{
+ face_t *newf;
+ winding_t *nw;
+
+ if (!f1->w || !f2->w)
+ return NULL;
+ if (f1->texinfo != f2->texinfo)
+ return NULL;
+ if (f1->planenum != f2->planenum) // on front and back sides
+ return NULL;
+ if (f1->contents != f2->contents)
+ return NULL;
+ if ( f1->originalface->smoothingGroups != f2->originalface->smoothingGroups )
+ return NULL;
+ if ( !OverlaysAreEqual( f1, f2 ) )
+ return NULL;
+ if ( nomergewater && ( FaceOnWaterBrush( f1 ) || FaceOnWaterBrush( f2 ) ) )
+ return NULL;
+
+ nw = TryMergeWinding (f1->w, f2->w, planenormal);
+ if (!nw)
+ return NULL;
+
+ c_merge++;
+ newf = NewFaceFromFace (f1);
+ newf->w = nw;
+
+ f1->merged = newf;
+ f2->merged = newf;
+
+ return newf;
+}
+
+/*
+===============
+MergeFaceList
+===============
+*/
+void MergeFaceList(face_t **pList)
+{
+ face_t *f1, *f2, *end;
+ face_t *merged;
+ plane_t *plane;
+
+ merged = NULL;
+
+ for (f1 = *pList; f1 ; f1 = f1->next)
+ {
+ if (f1->merged || f1->split[0] || f1->split[1])
+ continue;
+ for (f2 = *pList; f2 != f1 ; f2=f2->next)
+ {
+ if (f2->merged || f2->split[0] || f2->split[1])
+ continue;
+
+ plane = &g_MainMap->mapplanes[f1->planenum];
+ merged = TryMerge (f1, f2, plane->normal);
+ if (!merged)
+ continue;
+
+ // add merged to the end of the face list
+ // so it will be checked against all the faces again
+ for (end = *pList; end->next ; end = end->next)
+ ;
+ merged->next = NULL;
+ end->next = merged;
+ break;
+ }
+ }
+}
+
+//=====================================================================
+
+/*
+===============
+SubdivideFace
+
+Chop up faces that are larger than we want in the surface cache
+===============
+*/
+void SubdivideFace (face_t **pFaceList, face_t *f)
+{
+ float mins, maxs;
+ vec_t v;
+ vec_t luxelsPerWorldUnit;
+ int axis, i;
+ texinfo_t *tex;
+ Vector temp;
+ vec_t dist;
+ winding_t *w, *frontw, *backw;
+
+ if ( f->merged || f->split[0] || f->split[1] )
+ return;
+
+// special (non-surface cached) faces don't need subdivision
+ tex = &texinfo[f->texinfo];
+
+ if( tex->flags & SURF_NOLIGHT )
+ {
+ return;
+ }
+
+ for (axis = 0 ; axis < 2 ; axis++)
+ {
+ while (1)
+ {
+ mins = 999999;
+ maxs = -999999;
+
+ VECTOR_COPY (tex->lightmapVecsLuxelsPerWorldUnits[axis], temp);
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ v = DotProduct (w->p[i], temp);
+ if (v < mins)
+ mins = v;
+ if (v > maxs)
+ maxs = v;
+ }
+#if 0
+ if (maxs - mins <= 0)
+ Error ("zero extents");
+#endif
+ if (maxs - mins <= g_maxLightmapDimension)
+ break;
+
+ // split it
+ c_subdivide++;
+
+ luxelsPerWorldUnit = VectorNormalize (temp);
+
+ dist = ( mins + g_maxLightmapDimension - 1 ) / luxelsPerWorldUnit;
+
+ ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
+ if (!frontw || !backw)
+ Error ("SubdivideFace: didn't split the polygon");
+
+ f->split[0] = NewFaceFromFace (f);
+ f->split[0]->w = frontw;
+ f->split[0]->next = *pFaceList;
+ *pFaceList = f->split[0];
+
+ f->split[1] = NewFaceFromFace (f);
+ f->split[1]->w = backw;
+ f->split[1]->next = *pFaceList;
+ *pFaceList = f->split[1];
+
+ SubdivideFace (pFaceList, f->split[0]);
+ SubdivideFace (pFaceList, f->split[1]);
+ return;
+ }
+ }
+}
+
+void SubdivideFaceList(face_t **pFaceList)
+{
+ face_t *f;
+
+ for (f = *pFaceList ; f ; f=f->next)
+ {
+ SubdivideFace (pFaceList, f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assigns the bottom material to the bottom face
+//-----------------------------------------------------------------------------
+static bool AssignBottomWaterMaterialToFace( face_t *f )
+{
+ // NOTE: This happens *after* cubemap fixup occurs, so we need to get the
+ // fixed-up bottom material for this
+ texinfo_t *pTexInfo = &texinfo[f->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ char pBottomMatName[512];
+ if ( !GetValueFromPatchedMaterial( pMaterialName, "$bottommaterial", pBottomMatName, 512 ) )
+ {
+ if( !Q_stristr( pMaterialName, "nodraw" ) && !Q_stristr( pMaterialName, "toolsskip" ) )
+ {
+ Warning("error: material %s doesn't have a $bottommaterial\n", pMaterialName );
+ }
+ return false;
+ }
+
+ //Assert( mapplanes[f->planenum].normal.z < 0 );
+ texinfo_t newTexInfo;
+ newTexInfo.flags = pTexInfo->flags;
+ int j, k;
+ for (j=0 ; j<2 ; j++)
+ {
+ for (k=0 ; k<4 ; k++)
+ {
+ newTexInfo.textureVecsTexelsPerWorldUnits[j][k] = pTexInfo->textureVecsTexelsPerWorldUnits[j][k];
+ newTexInfo.lightmapVecsLuxelsPerWorldUnits[j][k] = pTexInfo->lightmapVecsLuxelsPerWorldUnits[j][k];
+ }
+ }
+ newTexInfo.texdata = FindOrCreateTexData( pBottomMatName );
+ f->texinfo = FindOrCreateTexInfo( newTexInfo );
+
+ return true;
+}
+
+
+//===========================================================================
+
+int c_nodefaces;
+
+static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize );
+void SubdivideFaceBySubdivSize( face_t *f );
+
+/*
+============
+FaceFromPortal
+
+============
+*/
+extern int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
+
+face_t *FaceFromPortal (portal_t *p, int pside)
+{
+ face_t *f;
+ side_t *side;
+ int deltaContents;
+
+ // portal does not bridge different visible contents
+ side = p->side;
+ if (!side)
+ return NULL;
+
+ // allocate a new face
+ f = AllocFace();
+
+ // save the original "side" from the map brush -- portal->side
+ // see FindPortalSide(...)
+ f->originalface = side;
+
+ //
+ // save material info
+ //
+ f->texinfo = side->texinfo;
+ f->dispinfo = -1; // all faces with displacement info are created elsewhere
+ f->smoothingGroups = side->smoothingGroups;
+
+ // save plane info
+ f->planenum = (side->planenum & ~1) | pside;
+ if ( entity_num != 0 )
+ {
+ // the brush model renderer doesn't use PLANEBACK, so write the real plane
+ // inside water faces can be flipped because they are generated on the inside of the brush
+ if ( p->nodes[pside]->contents & (CONTENTS_WATER|CONTENTS_SLIME) )
+ {
+ f->planenum = (side->planenum & ~1) | pside;
+ }
+ else
+ {
+ f->planenum = side->planenum;
+ }
+ }
+
+ // save portal info
+ f->portal = p;
+ f->fogVolumeLeaf = NULL;
+
+ deltaContents = VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents);
+
+ // don't show insides of windows or grates
+ if ( ((p->nodes[pside]->contents & CONTENTS_WINDOW) && deltaContents == CONTENTS_WINDOW) ||
+ ((p->nodes[pside]->contents & CONTENTS_GRATE) && deltaContents == CONTENTS_GRATE) )
+ {
+ FreeFace( f );
+ return NULL;
+ }
+
+ if ( p->nodes[pside]->contents & MASK_WATER )
+ {
+ f->fogVolumeLeaf = p->nodes[pside];
+ }
+ else if ( p->nodes[!pside]->contents & MASK_WATER )
+ {
+ f->fogVolumeLeaf = p->nodes[!pside];
+ }
+
+ // If it's the underside of water, we need to figure out what material to use, etc.
+ if( ( p->nodes[pside]->contents & CONTENTS_WATER ) && deltaContents == CONTENTS_WATER )
+ {
+ if ( !AssignBottomWaterMaterialToFace( f ) )
+ {
+ FreeFace( f );
+ return NULL;
+ }
+ }
+
+ //
+ // generate the winding for the face and save face contents
+ //
+ if( pside )
+ {
+ f->w = ReverseWinding(p->winding);
+ f->contents = p->nodes[1]->contents;
+ }
+ else
+ {
+ f->w = CopyWinding(p->winding);
+ f->contents = p->nodes[0]->contents;
+ }
+
+ f->numPrims = 0;
+ f->firstPrimID = 0;
+
+ // return the created face
+ return f;
+}
+
+/*
+===============
+MakeFaces_r
+
+If a portal will make a visible face,
+mark the side that originally created it
+
+ solid / empty : solid
+ solid / water : solid
+ water / empty : water
+ water / water : none
+===============
+*/
+void MakeFaces_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ // recurse down to leafs
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MakeFaces_r (node->children[0]);
+ MakeFaces_r (node->children[1]);
+
+ // merge together all visible faces on the node
+ if (!nomerge)
+ MergeFaceList(&node->faces);
+ if (!nosubdiv)
+ SubdivideFaceList(&node->faces);
+
+ return;
+ }
+
+ // solid leafs never have visible faces
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ // see which portals are valid
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ p->face[s] = FaceFromPortal (p, s);
+ if (p->face[s])
+ {
+ c_nodefaces++;
+ p->face[s]->next = p->onnode->faces;
+ p->onnode->faces = p->face[s];
+ }
+ }
+}
+
+typedef winding_t *pwinding_t;
+
+static void PrintWinding( winding_t *w )
+{
+ int i;
+ Msg( "\t---\n" );
+ for( i = 0; i < w->numpoints; i++ )
+ {
+ Msg( "\t%f %f %f\n", w->p[i].x, w->p[i].y, w->p[i].z );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a winding to the current list of primverts
+// Input : *w - the winding
+// *pIndices - The output indices
+// vertStart - the starting vert index
+// vertCount - current count
+// Output : int - output count including new verts from this winding
+//-----------------------------------------------------------------------------
+int AddWindingToPrimverts( const winding_t *w, unsigned short *pIndices, int vertStart, int vertCount )
+{
+ for( int i = 0; i < w->numpoints; i++ )
+ {
+ int j;
+ for( j = vertStart; j < vertStart + vertCount; j++ )
+ {
+ Vector tmp = g_primverts[j].pos - w->p[i];
+
+ if( tmp.LengthSqr() < POINT_EPSILON*POINT_EPSILON )
+ {
+ pIndices[i] = j;
+ break;
+ }
+ }
+ if ( j >= vertStart + vertCount )
+ {
+ pIndices[i] = j;
+ g_primverts[j].pos = w->p[i];
+ vertCount++;
+ g_numprimverts++;
+ if ( g_numprimverts > MAX_MAP_PRIMVERTS )
+ {
+ Error( "Exceeded max water verts.\nIncrease surface subdivision size or lower your subdivision size in vmt files! (%d>%d)\n",
+ ( int )g_numprimverts, ( int )MAX_MAP_PRIMVERTS );
+ }
+ }
+ }
+
+ return vertCount;
+}
+
+
+
+#pragma optimize( "g", off )
+#define USE_TRISTRIPS
+
+// UNDONE: Should split this function into subdivide and primitive building parts
+// UNDONE: We should try building strips of shared verts for all water faces in a leaf
+// since those will be drawn concurrently anyway. It should be more efficient.
+static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize )
+{
+ // garymcthack - REFACTOR ME!!!
+
+ vec_t dummy;
+ Vector hackNormal;
+ WindingPlane( f->w, hackNormal, &dummy );
+
+ // HACK - only subdivide stuff that is facing up or down (for water)
+ if( fabs(hackNormal[2]) < .9f )
+ {
+ return;
+ }
+
+ // Get the extents of the surface.
+ // garymcthack - this assumes a surface of constant z for now (for water). . can generalize later.
+ subdivsize = ( int )subdivsize;
+ winding_t *w;
+ w = CopyWinding( f->w );
+
+ Vector min, max;
+ WindingBounds( w, min, max );
+
+#if 0
+ Msg( "START WINDING: \n" );
+ PrintWinding( w );
+#endif
+ int xStart, yStart, xEnd, yEnd, xSteps, ySteps;
+ xStart = ( int )subdivsize * ( int )( ( min[0] - subdivsize ) / subdivsize );
+ xEnd = ( int )subdivsize * ( int )( ( max[0] + subdivsize ) / subdivsize );
+ yStart = ( int )subdivsize * ( int )( ( min[1] - subdivsize ) / subdivsize );
+ yEnd = ( int )subdivsize * ( int )( ( max[1] + subdivsize ) / subdivsize );
+ xSteps = ( xEnd - xStart ) / subdivsize;
+ ySteps = ( yEnd - yStart ) / subdivsize;
+ int x, y;
+ int xi, yi;
+ winding_t **windings = ( winding_t ** )new pwinding_t[xSteps * ySteps];
+ memset( windings, 0, sizeof( winding_t * ) * xSteps * ySteps );
+
+ for( yi = 0, y = yStart; y < yEnd; y += ( int )subdivsize, yi++ )
+ {
+ for( xi = 0, x = xStart; x < xEnd; x += ( int )subdivsize, xi++ )
+ {
+ winding_t *tempWinding, *frontWinding, *backWinding;
+ float planeDist;
+ Vector normal;
+ normal.Init( 1.0f, 0.0f, 0.0f );
+ planeDist = ( float )x;
+ tempWinding = CopyWinding( w );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( -1.0f, 0.0f, 0.0f );
+ planeDist = -( float )( x + subdivsize );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( 0.0f, 1.0f, 0.0f );
+ planeDist = ( float )y;
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( 0.0f, -1.0f, 0.0f );
+ planeDist = -( float )( y + subdivsize );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+
+#if 0
+ Msg( "output winding:\n" );
+ PrintWinding( frontWinding );
+#endif
+
+ if( frontWinding )
+ {
+ windings[xi + yi * xSteps] = frontWinding;
+ }
+ }
+ }
+ FreeWinding( w );
+ dprimitive_t &newPrim = g_primitives[g_numprimitives];
+ f->firstPrimID = g_numprimitives;
+ f->numPrims = 1;
+ newPrim.firstIndex = g_numprimindices;
+ newPrim.firstVert = g_numprimverts;
+ newPrim.indexCount = 0;
+ newPrim.vertCount = 0;
+#ifdef USE_TRISTRIPS
+ newPrim.type = PRIM_TRISTRIP;
+#else
+ newPrim.type = PRIM_TRILIST;
+#endif
+
+ CUtlVector<WORD> triListIndices;
+ int i;
+ for( i = 0; i < xSteps * ySteps; i++ )
+ {
+ if( !windings[i] )
+ {
+ continue;
+ }
+ unsigned short *pIndices =
+ ( unsigned short * )_alloca( windings[i]->numpoints * sizeof( unsigned short ) );
+ // find indices for the verts.
+ newPrim.vertCount = AddWindingToPrimverts( windings[i], pIndices, newPrim.firstVert, newPrim.vertCount );
+
+ // Now that we have indices for the verts, fan-tesselate the polygon and spit out tris.
+ for( int j = 0; j < windings[i]->numpoints - 2; j++ )
+ {
+ triListIndices.AddToTail( pIndices[0] );
+ triListIndices.AddToTail( pIndices[j+1] );
+ triListIndices.AddToTail( pIndices[j+2] );
+ }
+ }
+
+ delete [] windings;
+ // We've already updated the verts and have a trilist. . let's strip it!
+ if( !triListIndices.Size() )
+ {
+ return;
+ }
+
+#ifdef USE_TRISTRIPS
+ int numTristripIndices;
+ WORD *pStripIndices = NULL;
+ Stripify( triListIndices.Size() / 3, triListIndices.Base(), &numTristripIndices,
+ &pStripIndices );
+ Assert( pStripIndices );
+
+ // FIXME: Should also call ComputeVertexPermutation and reorder the verts.
+
+ for( i = 0; i < numTristripIndices; i++ )
+ {
+ Assert( pStripIndices[i] >= newPrim.firstVert &&
+ pStripIndices[i] < newPrim.firstVert + newPrim.vertCount );
+ g_primindices[newPrim.firstIndex + newPrim.indexCount] = pStripIndices[i];
+ newPrim.indexCount++;
+ g_numprimindices++;
+ if( g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ }
+ delete [] pStripIndices;
+#else
+ for( i = 0; i < triListIndices.Size(); i++ )
+ {
+ g_primindices[newPrim.firstIndex + newPrim.indexCount] = triListIndices[i];
+ newPrim.indexCount++;
+ g_numprimindices++;
+ if( g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ }
+#endif
+ g_numprimitives++; // don't increment until we get here and are sure that we have a primitive.
+ if( g_numprimitives > MAX_MAP_PRIMITIVES )
+ {
+ Error( "Exceeded max water primitives.\nIncrease surface subdivision size! (%d>%d)\n", ( int )g_numprimitives, ( int )MAX_MAP_PRIMITIVES );
+ }
+}
+
+void SubdivideFaceBySubdivSize( face_t *f )
+{
+ if( f->numpoints == 0 || f->split[0] || f->split[1] || f->merged || !f->w )
+ {
+ return;
+ }
+ // see if the face needs to be subdivided.
+ texinfo_t *pTexInfo = &texinfo[f->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ bool bFound;
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ MaterialSystemMaterial_t matID =
+ FindOriginalMaterial( pMaterialName, &bFound, false );
+
+ if( !bFound )
+ {
+ return;
+ }
+ const char *subdivsizeString = GetMaterialVar( matID, "$subdivsize" );
+ if( subdivsizeString )
+ {
+ float subdivSize = atof( subdivsizeString );
+ if( subdivSize > 0.0f )
+ {
+ // NOTE: Subdivision is unsupported and should be phased out
+ Warning("Using subdivision on %s\n", pMaterialName );
+ SubdivideFaceBySubdivSize( f, subdivSize );
+ }
+ }
+}
+
+void SplitSubdividedFaces_Node_r( node_t *node )
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ return;
+ }
+ face_t *f;
+ for( f = node->faces; f ;f = f->next )
+ {
+ SubdivideFaceBySubdivSize( f );
+ }
+
+ //
+ // recursively output the other nodes
+ //
+ SplitSubdividedFaces_Node_r( node->children[0] );
+ SplitSubdividedFaces_Node_r( node->children[1] );
+}
+
+void SplitSubdividedFaces( face_t *pLeafFaceList, node_t *headnode )
+{
+ // deal with leaf faces.
+ face_t *f = pLeafFaceList;
+ while ( f )
+ {
+ SubdivideFaceBySubdivSize( f );
+ f = f->next;
+ }
+
+ // deal with node faces.
+ SplitSubdividedFaces_Node_r( headnode );
+}
+
+#pragma optimize( "", on )
+
+/*
+============
+MakeFaces
+============
+*/
+void MakeFaces (node_t *node)
+{
+ qprintf ("--- MakeFaces ---\n");
+ c_merge = 0;
+ c_subdivide = 0;
+ c_nodefaces = 0;
+
+ MakeFaces_r (node);
+
+ qprintf ("%5i makefaces\n", c_nodefaces);
+ qprintf ("%5i merged\n", c_merge);
+ qprintf ("%5i subdivided\n", c_subdivide);
} \ No newline at end of file
diff --git a/mp/src/utils/vbsp/faces.h b/mp/src/utils/vbsp/faces.h
index cfebc24f..79d78954 100644
--- a/mp/src/utils/vbsp/faces.h
+++ b/mp/src/utils/vbsp/faces.h
@@ -1,20 +1,20 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef FACES_H
-#define FACES_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-void GetEdge2_InitOptimizedList(); // Call this before calling GetEdge2() on a bunch of edges.
-int AddEdge( int v1, int v2, face_t *f );
-int GetEdge2(int v1, int v2, face_t *f);
-
-
-#endif // FACES_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef FACES_H
+#define FACES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void GetEdge2_InitOptimizedList(); // Call this before calling GetEdge2() on a bunch of edges.
+int AddEdge( int v1, int v2, face_t *f );
+int GetEdge2(int v1, int v2, face_t *f);
+
+
+#endif // FACES_H
diff --git a/mp/src/utils/vbsp/glfile.cpp b/mp/src/utils/vbsp/glfile.cpp
index 236845bc..361d40b0 100644
--- a/mp/src/utils/vbsp/glfile.cpp
+++ b/mp/src/utils/vbsp/glfile.cpp
@@ -1,219 +1,219 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-
-int c_glfaces;
-
-int PortalVisibleSides (portal_t *p)
-{
- int fcon, bcon;
-
- if (!p->onnode)
- return 0; // outside
-
- fcon = p->nodes[0]->contents;
- bcon = p->nodes[1]->contents;
-
- // same contents never create a face
- if (fcon == bcon)
- return 0;
-
- // FIXME: is this correct now?
- if (!fcon)
- return 1;
- if (!bcon)
- return 2;
- return 0;
-}
-
-void OutputWinding (winding_t *w, FileHandle_t glview)
-{
- static int level = 128;
- vec_t light;
- int i;
-
- CmdLib_FPrintf( glview, "%i\n", w->numpoints);
- level+=28;
- light = (level&255)/255.0;
- for (i=0 ; i<w->numpoints ; i++)
- {
- CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
- w->p[i][0],
- w->p[i][1],
- w->p[i][2],
- light,
- light,
- light);
- }
- //CmdLib_FPrintf(glview, "\n");
-}
-
-void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b)
-{
- int i;
-
- CmdLib_FPrintf( glview, "%i\n", w->numpoints);
- float lr = r * (1.0f/255.0f);
- float lg = g * (1.0f/255.0f);
- float lb = b * (1.0f/255.0f);
- for (i=0 ; i<w->numpoints ; i++)
- {
- CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
- w->p[i][0],
- w->p[i][1],
- w->p[i][2],
- lr,
- lg,
- lb);
- }
- //CmdLib_FPrintf(glview, "\n");
-}
-
-/*
-=============
-OutputPortal
-=============
-*/
-void OutputPortal (portal_t *p, FileHandle_t glview)
-{
- winding_t *w;
- int sides;
-
- sides = PortalVisibleSides (p);
- if (!sides)
- return;
-
- c_glfaces++;
-
- w = p->winding;
-
- if (sides == 2) // back side
- w = ReverseWinding (w);
-
- OutputWinding (w, glview);
-
- if (sides == 2)
- FreeWinding(w);
-}
-
-/*
-=============
-WriteGLView_r
-=============
-*/
-void WriteGLView_r (node_t *node, FileHandle_t glview)
-{
- portal_t *p, *nextp;
-
- if (node->planenum != PLANENUM_LEAF)
- {
- WriteGLView_r (node->children[0], glview);
- WriteGLView_r (node->children[1], glview);
- return;
- }
-
- // write all the portals
- for (p=node->portals ; p ; p=nextp)
- {
- if (p->nodes[0] == node)
- {
- OutputPortal (p, glview);
- nextp = p->next[0];
- }
- else
- nextp = p->next[1];
- }
-}
-
-
-void WriteGLViewFaces_r( node_t *node, FileHandle_t glview )
-{
- portal_t *p, *nextp;
-
- if (node->planenum != PLANENUM_LEAF)
- {
- WriteGLViewFaces_r (node->children[0], glview);
- WriteGLViewFaces_r (node->children[1], glview);
- return;
- }
-
- // write all the portals
- for (p=node->portals ; p ; p=nextp)
- {
- int s = (p->nodes[1] == node);
-
- if ( p->face[s] )
- {
- OutputWinding( p->face[s]->w, glview );
- }
- nextp = p->next[s];
- }
-}
-
-/*
-=============
-WriteGLView
-=============
-*/
-void WriteGLView (tree_t *tree, char *source)
-{
- char name[1024];
- FileHandle_t glview;
-
- c_glfaces = 0;
- sprintf (name, "%s%s.gl",outbase, source);
- Msg("Writing %s\n", name);
-
- glview = g_pFileSystem->Open( name, "w" );
- if (!glview)
- Error ("Couldn't open %s", name);
- WriteGLView_r (tree->headnode, glview);
- g_pFileSystem->Close( glview );
-
- Msg("%5i c_glfaces\n", c_glfaces);
-}
-
-
-void WriteGLViewFaces( tree_t *tree, const char *pName )
-{
- char name[1024];
- FileHandle_t glview;
-
- c_glfaces = 0;
- sprintf (name, "%s%s.gl", outbase, pName);
- Msg("Writing %s\n", name);
-
- glview = g_pFileSystem->Open( name, "w" );
- if (!glview)
- Error ("Couldn't open %s", name);
- WriteGLViewFaces_r (tree->headnode, glview);
- g_pFileSystem->Close( glview );
-
- Msg("%5i c_glfaces\n", c_glfaces);
-}
-
-
-void WriteGLViewBrushList( bspbrush_t *pList, const char *pName )
-{
- char name[1024];
- FileHandle_t glview;
-
- sprintf (name, "%s%s.gl", outbase, pName );
- Msg("Writing %s\n", name);
-
- glview = g_pFileSystem->Open( name, "w" );
- if (!glview)
- Error ("Couldn't open %s", name);
- for ( bspbrush_t *pBrush = pList; pBrush; pBrush = pBrush->next )
- {
- for (int i = 0; i < pBrush->numsides; i++ )
- OutputWinding( pBrush->sides[i].winding, glview );
- }
- g_pFileSystem->Close( glview );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+int c_glfaces;
+
+int PortalVisibleSides (portal_t *p)
+{
+ int fcon, bcon;
+
+ if (!p->onnode)
+ return 0; // outside
+
+ fcon = p->nodes[0]->contents;
+ bcon = p->nodes[1]->contents;
+
+ // same contents never create a face
+ if (fcon == bcon)
+ return 0;
+
+ // FIXME: is this correct now?
+ if (!fcon)
+ return 1;
+ if (!bcon)
+ return 2;
+ return 0;
+}
+
+void OutputWinding (winding_t *w, FileHandle_t glview)
+{
+ static int level = 128;
+ vec_t light;
+ int i;
+
+ CmdLib_FPrintf( glview, "%i\n", w->numpoints);
+ level+=28;
+ light = (level&255)/255.0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ light,
+ light,
+ light);
+ }
+ //CmdLib_FPrintf(glview, "\n");
+}
+
+void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b)
+{
+ int i;
+
+ CmdLib_FPrintf( glview, "%i\n", w->numpoints);
+ float lr = r * (1.0f/255.0f);
+ float lg = g * (1.0f/255.0f);
+ float lb = b * (1.0f/255.0f);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ lr,
+ lg,
+ lb);
+ }
+ //CmdLib_FPrintf(glview, "\n");
+}
+
+/*
+=============
+OutputPortal
+=============
+*/
+void OutputPortal (portal_t *p, FileHandle_t glview)
+{
+ winding_t *w;
+ int sides;
+
+ sides = PortalVisibleSides (p);
+ if (!sides)
+ return;
+
+ c_glfaces++;
+
+ w = p->winding;
+
+ if (sides == 2) // back side
+ w = ReverseWinding (w);
+
+ OutputWinding (w, glview);
+
+ if (sides == 2)
+ FreeWinding(w);
+}
+
+/*
+=============
+WriteGLView_r
+=============
+*/
+void WriteGLView_r (node_t *node, FileHandle_t glview)
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLView_r (node->children[0], glview);
+ WriteGLView_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ {
+ OutputPortal (p, glview);
+ nextp = p->next[0];
+ }
+ else
+ nextp = p->next[1];
+ }
+}
+
+
+void WriteGLViewFaces_r( node_t *node, FileHandle_t glview )
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLViewFaces_r (node->children[0], glview);
+ WriteGLViewFaces_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ int s = (p->nodes[1] == node);
+
+ if ( p->face[s] )
+ {
+ OutputWinding( p->face[s]->w, glview );
+ }
+ nextp = p->next[s];
+ }
+}
+
+/*
+=============
+WriteGLView
+=============
+*/
+void WriteGLView (tree_t *tree, char *source)
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl",outbase, source);
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLView_r (tree->headnode, glview);
+ g_pFileSystem->Close( glview );
+
+ Msg("%5i c_glfaces\n", c_glfaces);
+}
+
+
+void WriteGLViewFaces( tree_t *tree, const char *pName )
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl", outbase, pName);
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLViewFaces_r (tree->headnode, glview);
+ g_pFileSystem->Close( glview );
+
+ Msg("%5i c_glfaces\n", c_glfaces);
+}
+
+
+void WriteGLViewBrushList( bspbrush_t *pList, const char *pName )
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ sprintf (name, "%s%s.gl", outbase, pName );
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ for ( bspbrush_t *pBrush = pList; pBrush; pBrush = pBrush->next )
+ {
+ for (int i = 0; i < pBrush->numsides; i++ )
+ OutputWinding( pBrush->sides[i].winding, glview );
+ }
+ g_pFileSystem->Close( glview );
+}
diff --git a/mp/src/utils/vbsp/ivp.cpp b/mp/src/utils/vbsp/ivp.cpp
index 585f0904..3c234c2f 100644
--- a/mp/src/utils/vbsp/ivp.cpp
+++ b/mp/src/utils/vbsp/ivp.cpp
@@ -1,1656 +1,1656 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include <stdio.h>
-#include "mathlib/vector.h"
-#include "bspfile.h"
-#include "bsplib.h"
-#include "cmdlib.h"
-#include "physdll.h"
-#include "utlvector.h"
-#include "vbsp.h"
-#include "phyfile.h"
-#include <float.h>
-#include "KeyValues.h"
-#include "UtlBuffer.h"
-#include "utlsymbol.h"
-#include "utlrbtree.h"
-#include "ivp.h"
-#include "disp_ivp.h"
-#include "materialpatch.h"
-#include "bitvec.h"
-
-// bit per leaf
-typedef CBitVec<MAX_MAP_LEAFS> leafbitarray_t;
-
-// parameters for conversion to vphysics
-#define NO_SHRINK 0.0f
-// NOTE: vphysics maintains a minimum separation radius between objects
-// This radius is set to 0.25, but it's symmetric. So shrinking potentially moveable
-// brushes by 0.5 in every direction ensures that these brushes can be constructed
-// touching the world, and constrained in place without collisions or friction
-// UNDONE: Add a key to disable this shrinking if necessary
-#define VPHYSICS_SHRINK (0.5f) // shrink BSP brushes by this much for collision
-#define VPHYSICS_MERGE 0.01f // merge verts closer than this
-
-void EmitPhysCollision();
-
-IPhysicsCollision *physcollision = NULL;
-extern IPhysicsSurfaceProps *physprops;
-
-// a list of all of the materials in the world model
-static CUtlVector<int> s_WorldPropList;
-
-//-----------------------------------------------------------------------------
-// Purpose: Write key/value pairs out to a memory buffer
-//-----------------------------------------------------------------------------
-CTextBuffer::CTextBuffer( void )
-{
-}
-CTextBuffer::~CTextBuffer( void )
-{
-}
-
-void CTextBuffer::WriteText( const char *pText )
-{
- int len = strlen( pText );
- CopyData( pText, len );
-}
-
-void CTextBuffer::WriteIntKey( const char *pKeyName, int outputData )
-{
- char tmp[1024];
-
- // FAIL!
- if ( strlen(pKeyName) > 1000 )
- {
- Msg("Error writing collision data %s\n", pKeyName );
- return;
- }
- sprintf( tmp, "\"%s\" \"%d\"\n", pKeyName, outputData );
- CopyData( tmp, strlen(tmp) );
-}
-
-void CTextBuffer::WriteStringKey( const char *pKeyName, const char *outputData )
-{
- CopyStringQuotes( pKeyName );
- CopyData( " ", 1 );
- CopyStringQuotes( outputData );
- CopyData( "\n", 1 );
-}
-
-void CTextBuffer::WriteFloatKey( const char *pKeyName, float outputData )
-{
- char tmp[1024];
-
- // FAIL!
- if ( strlen(pKeyName) > 1000 )
- {
- Msg("Error writing collision data %s\n", pKeyName );
- return;
- }
- sprintf( tmp, "\"%s\" \"%f\"\n", pKeyName, outputData );
- CopyData( tmp, strlen(tmp) );
-}
-
-void CTextBuffer::WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count )
-{
- char tmp[1024];
-
- // FAIL!
- if ( strlen(pKeyName) > 1000 )
- {
- Msg("Error writing collision data %s\n", pKeyName );
- return;
- }
- sprintf( tmp, "\"%s\" \"", pKeyName );
- for ( int i = 0; i < count; i++ )
- {
- char buf[80];
-
- sprintf( buf, "%f ", outputData[i] );
- strcat( tmp, buf );
- }
- strcat( tmp, "\"\n" );
-
- CopyData( tmp, strlen(tmp) );
-}
-
-void CTextBuffer::CopyStringQuotes( const char *pString )
-{
- CopyData( "\"", 1 );
- CopyData( pString, strlen(pString) );
- CopyData( "\"", 1 );
-}
-
-void CTextBuffer::Terminate( void )
-{
- CopyData( "\0", 1 );
-}
-
-void CTextBuffer::CopyData( const char *pData, int len )
-{
- int offset = m_buffer.AddMultipleToTail( len );
- memcpy( m_buffer.Base() + offset, pData, len );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Writes a glview text file containing the collision surface in question
-// Input : *pCollide -
-// *pFilename -
-//-----------------------------------------------------------------------------
-void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename )
-{
- if ( !pCollide )
- return;
-
- Msg("Writing %s...\n", pFilename );
- Vector *outVerts;
- int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
- FILE *fp = fopen( pFilename, "w" );
- int triCount = vertCount / 3;
- int vert = 0;
- for ( int i = 0; i < triCount; i++ )
- {
- fprintf( fp, "3\n" );
- fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
- vert++;
- fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
- vert++;
- fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
- vert++;
- }
- fclose( fp );
- physcollision->DestroyDebugMesh( vertCount, outVerts );
-}
-
-
-void DumpCollideToPHY( CPhysCollide *pCollide, CTextBuffer *text, const char *pFilename )
-{
- Msg("Writing %s...\n", pFilename );
- FILE *fp = fopen( pFilename, "wb" );
- phyheader_t header;
- header.size = sizeof(header);
- header.id = 0;
- header.checkSum = 0;
- header.solidCount = 1;
- fwrite( &header, sizeof(header), 1, fp );
- int size = physcollision->CollideSize( pCollide );
- fwrite( &size, sizeof(int), 1, fp );
-
- char *buf = (char *)malloc( size );
- physcollision->CollideWrite( buf, pCollide );
- fwrite( buf, size, 1, fp );
-
- fwrite( text->GetData(), text->GetSize(), 1, fp );
- fclose( fp );
- free( buf );
-}
-
-CPhysCollisionEntry::CPhysCollisionEntry( CPhysCollide *pCollide )
-{
- m_pCollide = pCollide;
-}
-
-unsigned int CPhysCollisionEntry::GetCollisionBinarySize()
-{
- return physcollision->CollideSize( m_pCollide );
-}
-
-unsigned int CPhysCollisionEntry::WriteCollisionBinary( char *pDest )
-{
- return physcollision->CollideWrite( pDest, m_pCollide );
-}
-
-void CPhysCollisionEntry::DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer )
-{
- char tmp[128];
- sprintf( tmp, "%s%03d.phy", pName, modelIndex );
- DumpCollideToPHY( m_pCollide, pTextBuffer, tmp );
- sprintf( tmp, "%s%03d.txt", pName, modelIndex );
- DumpCollideToGlView( m_pCollide, tmp );
-}
-
-
-class CPhysCollisionEntrySolid : public CPhysCollisionEntry
-{
-public:
- CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass );
-
- virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
- virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
-
-private:
- float m_volume;
- float m_mass;
- const char *m_pMaterial;
-};
-
-
-CPhysCollisionEntrySolid::CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass )
- : CPhysCollisionEntry( pCollide )
-{
- m_volume = physcollision->CollideVolume( m_pCollide );
- m_mass = mass;
- m_pMaterial = pMaterialName;
-}
-
-void CPhysCollisionEntrySolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- DumpCollideFileName( "collide", modelIndex, pTextBuffer );
-}
-
-void CPhysCollisionEntrySolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- pTextBuffer->WriteText( "solid {\n" );
- pTextBuffer->WriteIntKey( "index", collideIndex );
- pTextBuffer->WriteFloatKey( "mass", m_mass );
- if ( m_pMaterial )
- {
- pTextBuffer->WriteStringKey( "surfaceprop", m_pMaterial );
- }
- if ( m_volume != 0.f )
- {
- pTextBuffer->WriteFloatKey( "volume", m_volume );
- }
- pTextBuffer->WriteText( "}\n" );
-}
-
-
-class CPhysCollisionEntryStaticSolid : public CPhysCollisionEntry
-{
-public:
- CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask );
-
- virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
- virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
-
-private:
- int m_contentsMask;
-};
-
-
-CPhysCollisionEntryStaticSolid ::CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask )
- : CPhysCollisionEntry( pCollide ), m_contentsMask(contentsMask)
-{
-}
-
-void CPhysCollisionEntryStaticSolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- char tmp[128];
- sprintf( tmp, "static%02d", modelIndex );
- DumpCollideFileName( tmp, collideIndex, pTextBuffer );
-}
-
-void CPhysCollisionEntryStaticSolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- pTextBuffer->WriteText( "staticsolid {\n" );
- pTextBuffer->WriteIntKey( "index", collideIndex );
- pTextBuffer->WriteIntKey( "contents", m_contentsMask );
- pTextBuffer->WriteText( "}\n" );
-}
-
-CPhysCollisionEntryStaticMesh::CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName )
- : CPhysCollisionEntry( pCollide )
-{
- m_pMaterial = pMaterialName;
-}
-
-void CPhysCollisionEntryStaticMesh::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- char tmp[128];
- sprintf( tmp, "mesh%02d", modelIndex );
- DumpCollideFileName( tmp, collideIndex, pTextBuffer );
-}
-
-void CPhysCollisionEntryStaticMesh::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- pTextBuffer->WriteText( "staticsolid {\n" );
- pTextBuffer->WriteIntKey( "index", collideIndex );
- pTextBuffer->WriteText( "}\n" );
-}
-
-class CPhysCollisionEntryFluid : public CPhysCollisionEntry
-{
-public:
- ~CPhysCollisionEntryFluid();
- CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents );
-
- virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
- virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
-
-private:
- char *m_pSurfaceProp;
- float m_damping;
- Vector m_surfaceNormal;
- float m_surfaceDist;
- int m_contentsMask;
-};
-
-
-CPhysCollisionEntryFluid::CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents )
- : CPhysCollisionEntry( pCollide )
-{
- m_surfaceNormal = normal;
- m_surfaceDist = dist;
- m_pSurfaceProp = new char[strlen(pSurfaceProp)+1];
- strcpy( m_pSurfaceProp, pSurfaceProp );
- m_damping = damping;
- m_contentsMask = nContents;
-}
-
-CPhysCollisionEntryFluid::~CPhysCollisionEntryFluid()
-{
- delete[] m_pSurfaceProp;
-}
-
-void CPhysCollisionEntryFluid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- char tmp[128];
- sprintf( tmp, "water%02d", modelIndex );
- DumpCollideFileName( tmp, collideIndex, pTextBuffer );
-}
-
-void CPhysCollisionEntryFluid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
-{
- pTextBuffer->WriteText( "fluid {\n" );
- pTextBuffer->WriteIntKey( "index", collideIndex );
- pTextBuffer->WriteStringKey( "surfaceprop", m_pSurfaceProp ); // write out water material
- pTextBuffer->WriteFloatKey( "damping", m_damping ); // write out water damping
- pTextBuffer->WriteIntKey( "contents", m_contentsMask ); // write out water contents
- float array[4];
- m_surfaceNormal.CopyToArray( array );
- array[3] = m_surfaceDist;
- pTextBuffer->WriteFloatArrayKey( "surfaceplane", array, 4 ); // write out water surface plane
- pTextBuffer->WriteFloatArrayKey( "currentvelocity", vec3_origin.Base(), 3 ); // write out water velocity
- pTextBuffer->WriteText( "}\n" );
-}
-
-// Get an index into the prop list of this prop (add it if necessary)
-static int PropIndex( CUtlVector<int> &propList, int propIndex )
-{
- for ( int i = 0; i < propList.Count(); i++ )
- {
- if ( propList[i] == propIndex )
- return i+1;
- }
-
- if ( propList.Count() < 126 )
- {
- return propList.AddToTail( propIndex )+1;
- }
-
- return 0;
-}
-
-int RemapWorldMaterial( int materialIndexIn )
-{
- return PropIndex( s_WorldPropList, materialIndexIn );
-}
-
-typedef struct
-{
- float normal[3];
- float dist;
-} listplane_t;
-
-static void AddListPlane( CUtlVector<listplane_t> *list, float x, float y, float z, float d )
-{
- listplane_t plane;
- plane.normal[0] = x;
- plane.normal[1] = y;
- plane.normal[2] = z;
- plane.dist = d;
-
- list->AddToTail( plane );
-}
-
-class CPlaneList
-{
-public:
-
- CPlaneList( float shrink, float merge );
- ~CPlaneList( void );
-
- void AddConvex( CPhysConvex *pConvex );
-
- // add the brushes to the model
- int AddBrushes( void );
-
- // Adds a single brush as a convex object
- void ReferenceBrush( int brushnumber );
- bool IsBrushReferenced( int brushnumber );
-
- void ReferenceLeaf( int leafIndex );
- bool IsLeafReferenced( int leafIndex );
- int GetFirstBrushSide();
-
-private:
-
- CPhysConvex *BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum );
-
-public:
- CUtlVector<CPhysConvex *> m_convex;
-
- CUtlVector<int> m_leafList;
- int m_contentsMask;
-
- float m_shrink;
- float m_merge;
- bool *m_brushAdded;
- float m_totalVolume;
-};
-
-CPlaneList::CPlaneList( float shrink, float merge )
-{
- m_shrink = shrink;
- m_merge = merge;
- m_contentsMask = MASK_SOLID;
- m_brushAdded = new bool[numbrushes];
- memset( m_brushAdded, 0, sizeof(bool) * numbrushes );
- m_totalVolume = 0;
- m_leafList.Purge();
-}
-
-
-CPlaneList::~CPlaneList( void )
-{
- delete[] m_brushAdded;
-}
-
-
-void CPlaneList::AddConvex( CPhysConvex *pConvex )
-{
- if ( pConvex )
- {
- m_totalVolume += physcollision->ConvexVolume( pConvex );
- m_convex.AddToTail( pConvex );
- }
-}
-
-// Adds a single brush as a convex object
-void CPlaneList::ReferenceBrush( int brushnumber )
-{
- if ( !(dbrushes[brushnumber].contents & m_contentsMask) )
- return;
-
- m_brushAdded[brushnumber] = true;
-
-}
-
-
-bool CPlaneList::IsBrushReferenced( int brushnumber )
-{
- return m_brushAdded[brushnumber];
-}
-
-CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum )
-{
- CUtlVector<listplane_t> temp( 0, 32 );
-
- for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
- {
- dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
- if ( pside->bevel )
- continue;
-
- dplane_t *pplane = dplanes + pside->planenum;
- float shrinkThisPlane = shrink;
-
- if ( i < g_MainMap->mapbrushes[brushnumber].numsides )
- {
- if ( !g_MainMap->mapbrushes[brushnumber].original_sides[i].visible )
- {
- // don't shrink brush sides with no visible components.
- // this produces something closer to the ideal shrink than simply shrinking all planes
- shrinkThisPlane = 0;
- }
- }
- // Make sure shrinking won't swallow geometry along this axis.
- if ( pCollideTest && shrinkThisPlane != 0 )
- {
- Vector start = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, pplane->normal );
- Vector end = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, -pplane->normal );
- float thick = DotProduct( (end-start), pplane->normal );
- // NOTE: The object must be at least "shrinkMinimum" inches wide on each axis
- if ( fabs(thick) < shrinkMinimum )
- {
-#if _DEBUG
- Warning("Can't shrink brush %d, plane %d (%.2f, %.2f, %.2f)\n", brushnumber, pside->planenum, pplane->normal[0], pplane->normal[1], pplane->normal[2] );
-#endif
- shrinkThisPlane = 0;
- }
- }
- AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist - shrinkThisPlane );
- }
- return physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), m_merge );
-}
-
-int CPlaneList::AddBrushes( void )
-{
- int count = 0;
- for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
- {
- if ( IsBrushReferenced(brushnumber) )
- {
- CPhysConvex *pBrushConvex = NULL;
- if ( m_shrink != 0 )
- {
- // Make sure shrinking won't swallow this brush.
- CPhysConvex *pConvex = BuildConvexForBrush( brushnumber, 0, NULL, 0 );
- CPhysCollide *pUnshrunkCollide = physcollision->ConvertConvexToCollide( &pConvex, 1 );
- pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, pUnshrunkCollide, m_shrink * 3 );
- physcollision->DestroyCollide( pUnshrunkCollide );
- }
- else
- {
- pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, NULL, 1.0 );
- }
-
- if ( pBrushConvex )
- {
- count++;
- physcollision->SetConvexGameData( pBrushConvex, brushnumber );
- AddConvex( pBrushConvex );
- }
- }
- }
- return count;
-}
-
-
-int CPlaneList::GetFirstBrushSide()
-{
- for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
- {
- if ( IsBrushReferenced(brushnumber) )
- {
- for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
- {
- int sideIndex = i + dbrushes[brushnumber].firstside;
- dbrushside_t *pside = dbrushsides + sideIndex;
- if ( pside->bevel )
- continue;
- return sideIndex;
- }
- }
- }
- return 0;
-}
-
-// UNDONE: Try using this kind of algorithm if we run into precision problems.
-// NOTE: ConvexFromPlanes will be doing a bunch of matrix inversions that can suffer
-// if plane normals are too close to each other...
-#if 0
-void CPlaneList::AddBrushes( void )
-{
- CUtlVector<listplane_t> temp;
- for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
- {
- if ( IsBrushReferenced(brushnumber) )
- {
- CUtlVector<winding_t *> windings;
-
- for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
- {
- dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
- if (pside->bevel)
- continue;
- dplane_t *pplane = dplanes + pside->planenum;
- winding_t *w = BaseWindingForPlane( pplane->normal, pplane->dist - m_shrink );
- for ( int j = 0; j < dbrushes[brushnumber].numsides && w; j++ )
- {
- if (i == j)
- continue;
- dbrushside_t *pClipSide = dbrushsides + j + dbrushes[brushnumber].firstside;
- if (pClipSide->bevel)
- continue;
- dplane_t *pClipPlane = dplanes + pClipSide->planenum;
- ChopWindingInPlace (&w, -pClipPlane->normal, -pClipPlane->dist+m_shrink, 0); //CLIP_EPSILON);
- }
- if ( w )
- {
- windings.AddToTail( w );
- }
- }
-
- CUtlVector<Vector *> vertList;
- for ( int p = 0; p < windings.Count(); p++ )
- {
- for ( int v = 0; v < windings[p]->numpoints; v++ )
- {
- vertList.AddToTail( windings[p]->p + v );
- }
- }
- CPhysConvex *pConvex = physcollision->ConvexFromVerts( vertList.Base(), vertList.Count() );
- if ( pConvex )
- {
- physcollision->SetConvexGameData( pConvex, brushnumber );
- AddConvex( pConvex );
- }
- temp.RemoveAll();
- }
- }
-}
-#endif
-
-// If I have a list of leaves, make sure this leaf is in it.
-// Otherwise, process all leaves
-bool CPlaneList::IsLeafReferenced( int leafIndex )
-{
- if ( !m_leafList.Count() )
- return true;
-
- for ( int i = 0; i < m_leafList.Count(); i++ )
- {
- if ( m_leafList[i] == leafIndex )
- return true;
- }
-
- return false;
-}
-
-// Add a leaf to my list of interesting leaves
-void CPlaneList::ReferenceLeaf( int leafIndex )
-{
- m_leafList.AddToTail( leafIndex );
-}
-
-static void VisitLeaves_r( CPlaneList &planes, int node )
-{
- if ( node < 0 )
- {
- int leafIndex = -1 - node;
- if ( planes.IsLeafReferenced(leafIndex) )
- {
- int i;
-
- // Add the solids in the "empty" leaf
- for ( i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
- {
- int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
- planes.ReferenceBrush( brushIndex );
- }
- }
- }
- else
- {
- dnode_t *pnode = dnodes + node;
-
- VisitLeaves_r( planes, pnode->children[0] );
- VisitLeaves_r( planes, pnode->children[1] );
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
-struct waterleaf_t
-{
- Vector surfaceNormal;
- float surfaceDist;
- float minZ;
- bool hasSurface;
- int waterLeafIndex;// this is the submerged leaf
- int planenum; //UNDONE: REMOVE
- int surfaceTexInfo; // if hasSurface == true, this is the texinfo index for the water material
- int outsideLeafIndex;// this is the leaf on the other side of the water surface
- node_t *pNode;
-};
-
-
-
-// returns true if newleaf should appear before currentleaf in the list
-static bool IsLowerLeaf( const waterleaf_t &newleaf, const waterleaf_t &currentleaf )
-{
- if ( newleaf.hasSurface && currentleaf.hasSurface )
- {
- // the one with the upmost pointing z goes first
- if ( currentleaf.surfaceNormal.z > newleaf.surfaceNormal.z )
- return false;
-
- if ( fabs(currentleaf.surfaceNormal.z - newleaf.surfaceNormal.z) < 0.01 )
- {
- if ( newleaf.surfaceDist < currentleaf.surfaceDist )
- return true;
- }
- return true;
- }
- else if ( newleaf.hasSurface ) // the leaf with a surface always goes first
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Water surfaces are stored in an RB tree and the tree is used to
-// create one-off .vmt files embedded in the .bsp for each surface so that the
-// water depth effect occurs on a per-water surface level.
-//-----------------------------------------------------------------------------
-struct WaterTexInfo
-{
- // The mangled new .vmt name ( materials/levelename/oldmaterial_depth_xxx ) where xxx is
- // the water depth (as an integer )
- CUtlSymbol m_FullName;
-
- // The original .vmt name
- CUtlSymbol m_MaterialName;
-
- // The depth of the water this texinfo refers to
- int m_nWaterDepth;
-
- // The texinfo id
- int m_nTexInfo;
-
- // The subdivision size for the water surface
-// float m_SubdivSize;
-};
-
-//-----------------------------------------------------------------------------
-// Purpose: Helper for RB tree operations ( we compare full mangled names )
-// Input : src1 -
-// src2 -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool WaterLessFunc( WaterTexInfo const& src1, WaterTexInfo const& src2 )
-{
- return src1.m_FullName < src2.m_FullName;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: A growable RB tree of water surfaces
-//-----------------------------------------------------------------------------
-static CUtlRBTree< WaterTexInfo, int > g_WaterTexInfos( 0, 32, WaterLessFunc );
-
-#if 0
-float GetSubdivSizeForFogVolume( int fogVolumeID )
-{
- Assert( fogVolumeID >= 0 && fogVolumeID < g_WaterTexInfos.Count() );
- return g_WaterTexInfos[fogVolumeID].m_SubdivSize;
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *mapname -
-// *materialname -
-// waterdepth -
-// *fullname -
-//-----------------------------------------------------------------------------
-void GetWaterTextureName( char const *mapname, char const *materialname, int waterdepth, char *fullname )
-{
- char temp[ 512 ];
-
- // Construct the full name (prepend mapname to reduce name collisions)
- sprintf( temp, "maps/%s/%s_depth_%i", mapname, materialname, (int)waterdepth );
-
- // Make sure it's lower case
- strlwr( temp );
-
- strcpy( fullname, temp );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called to write procedural materials in the rb tree to the embedded
-// pak file for this .bsp
-//-----------------------------------------------------------------------------
-void EmitWaterMaterialFile( WaterTexInfo *wti )
-{
- char waterTextureName[512];
- if ( !wti )
- {
- return;
- }
-
- GetWaterTextureName( mapbase, wti->m_MaterialName.String(), ( int )wti->m_nWaterDepth, waterTextureName );
-
- // Convert to string
- char szDepth[ 32 ];
- sprintf( szDepth, "%i", wti->m_nWaterDepth );
- CreateMaterialPatch( wti->m_MaterialName.String(), waterTextureName, "$waterdepth", szDepth, PATCH_INSERT );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Takes the texinfo_t referenced by the .vmt and the computed depth for the
-// surface and looks up or creates a texdata/texinfo for the mangled one-off water .vmt file
-// Input : *pBaseInfo -
-// depth -
-// Output : int
-//-----------------------------------------------------------------------------
-int FindOrCreateWaterTexInfo( texinfo_t *pBaseInfo, float depth )
-{
- char fullname[ 512 ];
- char materialname[ 512 ];
-
- // Get the base texture/material name
- char const *name = TexDataStringTable_GetString( GetTexData( pBaseInfo->texdata )->nameStringTableID );
-
- GetWaterTextureName( mapbase, name, (int)depth, fullname );
-
- // See if we already have an entry for this depth
- WaterTexInfo lookup;
- lookup.m_FullName = fullname;
- int idx = g_WaterTexInfos.Find( lookup );
-
- // If so, return the existing entry texinfo index
- if ( idx != g_WaterTexInfos.InvalidIndex() )
- {
- return g_WaterTexInfos[ idx ].m_nTexInfo;
- }
-
- // Otherwise, fill in the rest of the data
- lookup.m_nWaterDepth = (int)depth;
- // Remember the current material name
- sprintf( materialname, "%s", name );
- strlwr( materialname );
- lookup.m_MaterialName = materialname;
-
- texinfo_t ti;
- // Make a copy
- ti = *pBaseInfo;
- // Create a texdata that is based on the underlying existing entry
- ti.texdata = FindAliasedTexData( fullname, GetTexData( pBaseInfo->texdata ) );
-
- // Find or create a new index
- lookup.m_nTexInfo = FindOrCreateTexInfo( ti );
-
- // Add the new texinfo to the RB tree
- idx = g_WaterTexInfos.Insert( lookup );
-
- // Msg( "created texinfo for %s\n", lookup.m_FullName.String() );
-
- // Go ahead and create the new vmt file.
- EmitWaterMaterialFile( &g_WaterTexInfos[idx] );
-
- // Return the new texinfo
- return g_WaterTexInfos[ idx ].m_nTexInfo;
-}
-
-extern node_t *dfacenodes[MAX_MAP_FACES];
-static void WriteFogVolumeIDs( dmodel_t *pModel )
-{
- int i;
-
- // write fog volume ID to each face in this model
- for( i = pModel->firstface; i < pModel->firstface + pModel->numfaces; i++ )
- {
- dface_t *pFace = &dfaces[i];
- node_t *pFaceNode = dfacenodes[i];
- texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
- pFace->surfaceFogVolumeID = -1;
- if ( pFaceNode )
- {
- if ( (pTexInfo->flags & SURF_WARP ) && pFaceNode->planenum == PLANENUM_LEAF && pFaceNode->diskId >= 0 )
- {
- pFace->surfaceFogVolumeID = dleafs[pFaceNode->diskId].leafWaterDataID;
- dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[pFace->surfaceFogVolumeID];
-
- // HACKHACK: Should probably mark these faces as water bottom or "bottommaterial" faces.
- // HACKHACK: Use a heuristic, if it points up, it's the water top.
- if ( dplanes[pFace->planenum].normal.z > 0 )
- {
- pFace->texinfo = pLeafWaterData->surfaceTexInfoID;
- }
- }
- else
- {
- // missed this face somehow?
- Assert( !(pTexInfo->flags & SURF_WARP ) );
- }
-
- }
- }
-}
-
-
-static bool PortalCrossesWater( waterleaf_t &baseleaf, portal_t *portal )
-{
- if ( baseleaf.hasSurface )
- {
- int side = WindingOnPlaneSide( portal->winding, baseleaf.surfaceNormal, baseleaf.surfaceDist );
- if ( side == SIDE_CROSS || side == SIDE_FRONT )
- return true;
- }
-
- return false;
-}
-
-
-static int FindOrCreateLeafWaterData( float surfaceZ, float minZ, int surfaceTexInfoID )
-{
- int i;
- for( i = 0; i < numleafwaterdata; i++ )
- {
- dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[i];
- if( pLeafWaterData->surfaceZ == surfaceZ &&
- pLeafWaterData->minZ == minZ &&
- pLeafWaterData->surfaceTexInfoID == surfaceTexInfoID )
- {
- return i;
- }
- }
- dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[numleafwaterdata];
- pLeafWaterData->surfaceZ = surfaceZ;
- pLeafWaterData->minZ = minZ;
- pLeafWaterData->surfaceTexInfoID = surfaceTexInfoID;
- numleafwaterdata++;
- return numleafwaterdata - 1;
-}
-
-
-// Enumerate all leaves under node with contents in contentsMask and add them to list
-void EnumLeaves_r( CUtlVector<node_t *> &list, node_t *node, int contentsMask )
-{
- if ( node->planenum != PLANENUM_LEAF )
- {
- EnumLeaves_r( list, node->children[0], contentsMask );
- EnumLeaves_r( list, node->children[1], contentsMask );
- return;
- }
-
- if ( !(node->contents & contentsMask) )
- return;
-
-
- // has the contents, put it in the list
- list.AddToTail( node );
-}
-
-
-// Builds a waterleaf_t for the given leaf
-static void BuildWaterLeaf( node_t *pLeafIn, waterleaf_t &waterLeafOut )
-{
- waterLeafOut.pNode = pLeafIn;
- waterLeafOut.waterLeafIndex = pLeafIn->diskId;
- waterLeafOut.outsideLeafIndex = -1;
- waterLeafOut.hasSurface = false;
- waterLeafOut.surfaceDist = MAX_COORD_INTEGER;
- waterLeafOut.surfaceNormal.Init( 0.f, 0.f, 1.f );
- waterLeafOut.planenum = -1;
- waterLeafOut.surfaceTexInfo = -1;
- waterLeafOut.minZ = MAX_COORD_INTEGER;
-
- // search the list of portals out of this leaf for one that leaves water
- // If you find one, this leaf has a surface, so fill out the surface data
- int oppositeNodeIndex = 0;
- for (portal_t *p = pLeafIn->portals ; p ; p = p->next[!oppositeNodeIndex])
- {
- oppositeNodeIndex = (p->nodes[0] == pLeafIn) ? 1 : 0;
-
- // not visible, can't be the portals we're looking for...
- if ( !p->side )
- continue;
-
- // See if this portal crosses into air
- node_t *pOpposite = p->nodes[oppositeNodeIndex];
- if ( !(pOpposite->contents & MASK_WATER) && !(pOpposite->contents & MASK_SOLID) )
- {
- // it does, there must be a surface here
- plane_t *plane = &g_MainMap->mapplanes[p->side->planenum];
- if ( waterLeafOut.hasSurface )
- {
- // Sort to find the most upward facing normal (skips sides)
- if ( waterLeafOut.surfaceNormal.z > plane->normal.z )
- continue;
- if ( (waterLeafOut.surfaceNormal.z == plane->normal.z) && waterLeafOut.surfaceDist >= plane->dist )
- continue;
- }
- // water surface needs to point at least somewhat up, this is
- // probably a map error
- if ( plane->normal.z <= 0 )
- continue;
- waterLeafOut.surfaceDist = plane->dist;
- waterLeafOut.surfaceNormal = plane->normal;
- waterLeafOut.hasSurface = true;
- waterLeafOut.outsideLeafIndex = p->nodes[oppositeNodeIndex]->diskId;
- waterLeafOut.surfaceTexInfo = p->side->texinfo;
- }
- }
-}
-
-
-static void InsertSortWaterLeaf( CUtlVector<waterleaf_t> &list, const waterleaf_t &leafInsert )
-{
- // insertion sort the leaf (lowest leaves go first)
- // leaves that aren't actually on the surface of the water will have leaf.hasSurface == false.
- for ( int i = 0; i < list.Count(); i++ )
- {
- if ( IsLowerLeaf( leafInsert, list[i] ) )
- {
- list.InsertBefore( i, leafInsert );
- return;
- }
- }
-
- // must the highest one, so stick it at the end.
- list.AddToTail( leafInsert );
-}
-
-
-// Flood fill the tree, finding neighboring water volumes and connecting them to this list
-// Cut groups that try to cross the surface.
-// Mark leaves that are in a group as "visited" so they won't be chosen by subsequent fills
-static void Flood_FindConnectedWaterVolumes_r( CUtlVector<node_t *> &list, node_t *pLeaf, waterleaf_t &baseleaf, leafbitarray_t &visited )
-{
- // already visited, or not the same water contents
- if ( pLeaf->diskId < 0 || visited.Get(pLeaf->diskId) || !(pLeaf->contents & (baseleaf.pNode->contents & MASK_WATER) ) )
- return;
-
- int oppositeNodeIndex = 0;
- for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
- {
- oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
-
- // If any portal crosses the water surface, don't flow through this leaf
- if ( PortalCrossesWater( baseleaf, p ) )
- return;
- }
-
- visited.Set( pLeaf->diskId );
- list.AddToTail( pLeaf );
-
- baseleaf.minZ = min( pLeaf->mins.z, baseleaf.minZ );
-
- for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
- {
- oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
-
- Flood_FindConnectedWaterVolumes_r( list, p->nodes[oppositeNodeIndex], baseleaf, visited );
- }
-}
-
-// UNDONE: This is a bit of a hack to avoid crashing when we can't find an
-// appropriate texinfo for a water model (to get physics properties)
-int FirstWaterTexinfo( bspbrush_t *brushlist, int contents )
-{
- while (brushlist)
- {
- if ( brushlist->original->contents & contents )
- {
- for ( int i = 0; i < brushlist->original->numsides; i++ )
- {
- if ( brushlist->original->original_sides[i].contents & contents )
- {
- return brushlist->original->original_sides[i].texinfo;
- }
- }
- }
- brushlist = brushlist->next;
- }
-
- Assert(0);
- return 0;
-}
-
-// This is a list of water data that will be turned into physics models
-struct watermodel_t
-{
- int modelIndex;
- int contents;
- waterleaf_t waterLeafData;
- int depthTexinfo;
- int firstWaterLeafIndex;
- int waterLeafCount;
- int fogVolumeIndex;
-};
-
-static CUtlVector<watermodel_t> g_WaterModels;
-static CUtlVector<int> g_WaterLeafList;
-
-// Creates a list of watermodel_t for later processing by EmitPhysCollision
-void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *node )
-{
- CUtlVector<node_t *> leafListAnyWater;
- // build the list of all leaves containing water
- EnumLeaves_r( leafListAnyWater, node, MASK_WATER );
-
- // make a sorted list to flood fill
- CUtlVector<waterleaf_t> list;
-
- int i;
- for ( i = 0; i < leafListAnyWater.Count(); i++ )
- {
- waterleaf_t waterLeaf;
- BuildWaterLeaf( leafListAnyWater[i], waterLeaf );
- InsertSortWaterLeaf( list, waterLeaf );
- }
-
- leafbitarray_t visited;
- CUtlVector<node_t *> waterAreaList;
- for ( i = 0; i < list.Count(); i++ )
- {
- Flood_FindConnectedWaterVolumes_r( waterAreaList, list[i].pNode, list[i], visited );
-
- // did we find a list of leaves connected to this one?
- // remember the list is sorted, so this one may have been attached to a previous
- // leaf. So it could have nothing hanging off of it.
- if ( waterAreaList.Count() )
- {
- // yes, emit a watermodel
- watermodel_t tmp;
- tmp.modelIndex = nummodels;
- tmp.contents = list[i].pNode->contents;
- tmp.waterLeafData = list[i];
- tmp.firstWaterLeafIndex = g_WaterLeafList.Count();
- tmp.waterLeafCount = waterAreaList.Count();
-
- float waterDepth = tmp.waterLeafData.surfaceDist - tmp.waterLeafData.minZ;
- if ( tmp.waterLeafData.surfaceTexInfo < 0 )
- {
- // the map has probably leaked in this case, but output something anyway.
- Assert(list[i].pNode->planenum == PLANENUM_LEAF);
- tmp.waterLeafData.surfaceTexInfo = FirstWaterTexinfo( list[i].pNode->brushlist, tmp.contents );
- }
- tmp.depthTexinfo = FindOrCreateWaterTexInfo( &texinfo[ tmp.waterLeafData.surfaceTexInfo ], waterDepth );
- tmp.fogVolumeIndex = FindOrCreateLeafWaterData( tmp.waterLeafData.surfaceDist, tmp.waterLeafData.minZ, tmp.waterLeafData.surfaceTexInfo );
-
- for ( int j = 0; j < waterAreaList.Count(); j++ )
- {
- g_WaterLeafList.AddToTail( waterAreaList[j]->diskId );
- }
- waterAreaList.RemoveAll();
- g_WaterModels.AddToTail( tmp );
- }
- }
-
- WriteFogVolumeIDs( pModel );
-}
-
-
-static void ConvertWaterModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex,
- float shrinkSize, float mergeTolerance )
-{
- dmodel_t *pModel = dmodels + modelIndex;
-
- for ( int i = 0; i < g_WaterModels.Count(); i++ )
- {
- watermodel_t &waterModel = g_WaterModels[i];
- if ( waterModel.modelIndex != modelIndex )
- continue;
-
- CPlaneList planes( shrinkSize, mergeTolerance );
- int firstLeaf = waterModel.firstWaterLeafIndex;
- planes.m_contentsMask = waterModel.contents;
-
- // push all of the leaves into the collision list
- for ( int j = 0; j < waterModel.waterLeafCount; j++ )
- {
- int leafIndex = g_WaterLeafList[firstLeaf + j];
-
- dleaf_t *pLeaf = dleafs + leafIndex;
- // fixup waterdata
- pLeaf->leafWaterDataID = waterModel.fogVolumeIndex;
- planes.ReferenceLeaf( leafIndex );
- }
-
- // visit the referenced leaves that belong to this model
- VisitLeaves_r( planes, pModel->headnode );
-
- // Now add the brushes from those leaves as convex
-
- // BUGBUG: NOTE: If your map has a brush that crosses the surface, it will be added to two water
- // volumes. This only happens with connected water volumes with multiple surface heights
- // UNDONE: Right now map makers must cut such brushes. It could be automatically cut by adding the
- // surface plane to the list for each brush before calling ConvexFromPlanes()
- planes.AddBrushes();
-
- int count = planes.m_convex.Count();
- if ( !count )
- continue;
-
- // Save off the plane of the surface for this group as well as the collision model
- // for all convex objects in the group.
- CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
- if ( pCollide )
- {
- int waterSurfaceTexInfoID = -1;
- // use defaults
- const char *pSurfaceProp = "water";
- float damping = 0.01;
- if ( waterSurfaceTexInfoID >= 0 )
- {
- // material override
- int texdata = texinfo[waterSurfaceTexInfoID].texdata;
- int prop = g_SurfaceProperties[texdata];
- pSurfaceProp = physprops->GetPropName( prop );
- }
-
- if ( !waterModel.waterLeafData.hasSurface )
- {
- waterModel.waterLeafData.surfaceNormal.Init( 0,0,1 );
- Vector top = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, waterModel.waterLeafData.surfaceNormal );
- waterModel.waterLeafData.surfaceDist = top.z;
- }
- CPhysCollisionEntryFluid *pCollisionEntryFuild = new CPhysCollisionEntryFluid( pCollide,
- pSurfaceProp, damping, waterModel.waterLeafData.surfaceNormal, waterModel.waterLeafData.surfaceDist, waterModel.contents );
- collisionList.AddToTail( pCollisionEntryFuild );
- }
- }
-}
-
-// compute a normal for a triangle of the given three points (points are clockwise, normal points out)
-static Vector TriangleNormal( const Vector &p0, const Vector &p1, const Vector &p2 )
-{
- Vector e0 = p1 - p0;
- Vector e1 = p2 - p0;
- Vector normal = CrossProduct( e1, e0 );
- VectorNormalize( normal );
-
- return normal;
-}
-
-
-// find the side of the brush with the normal closest to the given normal
-static dbrushside_t *FindBrushSide( int brushIndex, const Vector &normal )
-{
- dbrush_t *pbrush = &dbrushes[brushIndex];
- dbrushside_t *out = NULL;
- float best = -1.f;
-
- for ( int i = 0; i < pbrush->numsides; i++ )
- {
- dbrushside_t *pside = dbrushsides + i + pbrush->firstside;
- dplane_t *pplane = dplanes + pside->planenum;
- float dot = DotProduct( normal, pplane->normal );
- if ( dot > best )
- {
- best = dot;
- out = pside;
- }
- }
-
- return out;
-}
-
-
-
-static void ConvertWorldBrushesToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance, int contentsMask )
-{
- CPlaneList planes( shrinkSize, mergeTolerance );
-
- planes.m_contentsMask = contentsMask;
-
- VisitLeaves_r( planes, dmodels[0].headnode );
- planes.AddBrushes();
-
- int count = planes.m_convex.Count();
- if ( count )
- {
- CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
-
- ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
- int convex = pQuery->ConvexCount();
- for ( int i = 0; i < convex; i++ )
- {
- int triCount = pQuery->TriangleCount( i );
- int brushIndex = pQuery->GetGameData( i );
-
- Vector points[3];
- for ( int j = 0; j < triCount; j++ )
- {
- pQuery->GetTriangleVerts( i, j, points );
- Vector normal = TriangleNormal( points[0], points[1], points[2] );
- dbrushside_t *pside = FindBrushSide( brushIndex, normal );
- if ( pside->texinfo != TEXINFO_NODE )
- {
- int prop = g_SurfaceProperties[texinfo[pside->texinfo].texdata];
- pQuery->SetTriangleMaterialIndex( i, j, RemapWorldMaterial( prop ) );
- }
- }
- }
- physcollision->DestroyQueryModel( pQuery );
- pQuery = NULL;
-
- collisionList.AddToTail( new CPhysCollisionEntryStaticSolid( pCollide, contentsMask ) );
- }
-}
-
-// adds any world, terrain, and water collision models to the collision list
-static void BuildWorldPhysModel( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance )
-{
- ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, MASK_SOLID );
- ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_PLAYERCLIP );
- ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_MONSTERCLIP );
-
- if ( !g_bNoVirtualMesh && Disp_HasPower4Displacements() )
- {
- Warning("WARNING: Map using power 4 displacements, terrain physics cannot be compressed, map will need additional memory and CPU.\n");
- g_bNoVirtualMesh = true;
- }
-
- // if there's terrain, save it off as a static mesh/polysoup
- if ( g_bNoVirtualMesh || !physcollision->SupportsVirtualMesh() )
- {
- Disp_AddCollisionModels( collisionList, &dmodels[0], MASK_SOLID );
- }
- else
- {
- Disp_BuildVirtualMesh( MASK_SOLID );
- }
- ConvertWaterModelToPhysCollide( collisionList, 0, shrinkSize, mergeTolerance );
-}
-
-
-// adds a collision entry for this brush model
-static void ConvertModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex, int contents, float shrinkSize, float mergeTolerance )
-{
- int i;
- CPlaneList planes( shrinkSize, mergeTolerance );
-
- planes.m_contentsMask = contents;
-
- dmodel_t *pModel = dmodels + modelIndex;
- VisitLeaves_r( planes, pModel->headnode );
- planes.AddBrushes();
- int count = planes.m_convex.Count();
- convertconvexparams_t params;
- params.Defaults();
- params.buildOuterConvexHull = count > 1 ? true : false;
- params.buildDragAxisAreas = true;
- Vector size = pModel->maxs - pModel->mins;
-
- float minSurfaceArea = -1.0f;
- for ( i = 0; i < 3; i++ )
- {
- int other = (i+1)%3;
- int cross = (i+2)%3;
- float surfaceArea = size[other] * size[cross];
- if ( minSurfaceArea < 0 || surfaceArea < minSurfaceArea )
- {
- minSurfaceArea = surfaceArea;
- }
- }
- // this can be really slow with super-large models and a low error tolerance
- // Basically you get a ray cast through each square of epsilon surface area on each OBB side
- // So compute it for 1% error (on the smallest side, less on larger sides)
- params.dragAreaEpsilon = clamp( minSurfaceArea * 1e-2f, 1.0f, 1024.0f );
- CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( planes.m_convex.Base(), count, params );
-
- if ( !pCollide )
- return;
-
- struct
- {
- int prop;
- float area;
- } proplist[256];
- int numprops = 1;
-
- proplist[0].prop = -1;
- proplist[0].area = 1;
- // compute the array of props on the surface of this model
-
- // NODRAW brushes no longer have any faces
- if ( !dmodels[modelIndex].numfaces )
- {
- int sideIndex = planes.GetFirstBrushSide();
- int texdata = texinfo[dbrushsides[sideIndex].texinfo].texdata;
- int prop = g_SurfaceProperties[texdata];
- proplist[numprops].prop = prop;
- proplist[numprops].area = 2;
- numprops++;
- }
-
- for ( i = 0; i < dmodels[modelIndex].numfaces; i++ )
- {
- dface_t *face = dfaces + i + dmodels[modelIndex].firstface;
- int texdata = texinfo[face->texinfo].texdata;
- int prop = g_SurfaceProperties[texdata];
- int j;
- for ( j = 0; j < numprops; j++ )
- {
- if ( proplist[j].prop == prop )
- {
- proplist[j].area += face->area;
- break;
- }
- }
-
- if ( (!numprops || j >= numprops) && numprops < ARRAYSIZE(proplist) )
- {
- proplist[numprops].prop = prop;
- proplist[numprops].area = face->area;
- numprops++;
- }
- }
-
-
- // choose the prop with the most surface area
- int maxIndex = -1;
- float maxArea = 0;
- float totalArea = 0;
-
- for ( i = 0; i < numprops; i++ )
- {
- if ( proplist[i].area > maxArea )
- {
- maxIndex = i;
- maxArea = proplist[i].area;
- }
- // add up the total surface area
- totalArea += proplist[i].area;
- }
-
- float mass = 1.0f;
- const char *pMaterial = "default";
- if ( maxIndex >= 0 )
- {
- int prop = proplist[maxIndex].prop;
-
- // use default if this material has no prop
- if ( prop < 0 )
- prop = 0;
-
- pMaterial = physprops->GetPropName( prop );
- float density, thickness;
- physprops->GetPhysicsProperties( prop, &density, &thickness, NULL, NULL );
-
- // if this is a "shell" material (it is hollow and encloses some empty space)
- // compute the mass with a constant surface thickness
- if ( thickness != 0 )
- {
- mass = totalArea * thickness * density * CUBIC_METERS_PER_CUBIC_INCH;
- }
- else
- {
- // material is completely solid, compute total mass as if constant density throughout.
- mass = planes.m_totalVolume * density * CUBIC_METERS_PER_CUBIC_INCH;
- }
- }
-
- // Clamp mass to 100,000 kg
- if ( mass > VPHYSICS_MAX_MASS )
- {
- mass = VPHYSICS_MAX_MASS;
- }
-
- collisionList.AddToTail( new CPhysCollisionEntrySolid( pCollide, pMaterial, mass ) );
-}
-
-static void ClearLeafWaterData( void )
-{
- int i;
-
- for( i = 0; i < numleafs; i++ )
- {
- dleafs[i].leafWaterDataID = -1;
- dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
- }
-}
-
-
-// This is the only public entry to this file.
-// The global data touched in the file is:
-// from bsplib.h:
-// g_pPhysCollide : This is an output from this file.
-// g_PhysCollideSize : This is set in this file.
-// g_numdispinfo : This is an input to this file.
-// g_dispinfo : This is an input to this file.
-// numnodewaterdata : This is an output from this file.
-// dleafwaterdata : This is an output from this file.
-// from vbsp.h:
-// g_SurfaceProperties : This is an input to this file.
-void EmitPhysCollision()
-{
- ClearLeafWaterData();
-
- CreateInterfaceFn physicsFactory = GetPhysicsFactory();
- if ( physicsFactory )
- {
- physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
- }
-
- if ( !physcollision )
- {
- Warning("!!! WARNING: Can't build collision data!\n" );
- return;
- }
-
- CUtlVector<CPhysCollisionEntry *> collisionList[MAX_MAP_MODELS];
- CTextBuffer *pTextBuffer[MAX_MAP_MODELS];
-
- int physModelCount = 0, totalSize = 0;
-
- int start = Plat_FloatTime();
-
- Msg("Building Physics collision data...\n" );
-
- int i, j;
- for ( i = 0; i < nummodels; i++ )
- {
- // Build a list of collision models for this brush model section
- if ( i == 0 )
- {
- // world is the only model that processes water separately.
- // other brushes are assumed to be completely solid or completely liquid
- BuildWorldPhysModel( collisionList[i], NO_SHRINK, VPHYSICS_MERGE);
- }
- else
- {
- ConvertModelToPhysCollide( collisionList[i], i, MASK_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|MASK_WATER, VPHYSICS_SHRINK, VPHYSICS_MERGE );
- }
-
- pTextBuffer[i] = NULL;
- if ( !collisionList[i].Count() )
- continue;
-
- // if we've got collision models, write their script for processing in the game
- pTextBuffer[i] = new CTextBuffer;
- for ( j = 0; j < collisionList[i].Count(); j++ )
- {
- // dump a text file for visualization
- if ( dumpcollide )
- {
- collisionList[i][j]->DumpCollide( pTextBuffer[i], i, j );
- }
- // each model knows how to write its script
- collisionList[i][j]->WriteToTextBuffer( pTextBuffer[i], i, j );
- // total up the binary section's size
- totalSize += collisionList[i][j]->GetCollisionBinarySize() + sizeof(int);
- }
-
- // These sections only appear in the world's collision text
- if ( i == 0 )
- {
- if ( !g_bNoVirtualMesh && physcollision->SupportsVirtualMesh() )
- {
- pTextBuffer[i]->WriteText("virtualterrain {}\n");
- }
- if ( s_WorldPropList.Count() )
- {
- pTextBuffer[i]->WriteText( "materialtable {\n" );
- for ( j = 0; j < s_WorldPropList.Count(); j++ )
- {
- int propIndex = s_WorldPropList[j];
- if ( propIndex < 0 )
- {
- pTextBuffer[i]->WriteIntKey( "default", j+1 );
- }
- else
- {
- pTextBuffer[i]->WriteIntKey( physprops->GetPropName( propIndex ), j+1 );
- }
- }
- pTextBuffer[i]->WriteText( "}\n" );
- }
- }
-
- pTextBuffer[i]->Terminate();
-
- // total lump size includes the text buffers (scripts)
- totalSize += pTextBuffer[i]->GetSize();
-
- physModelCount++;
- }
-
- // add one for tail of list marker
- physModelCount++;
-
- // DWORD align the lump because AddLump assumes that it is DWORD aligned.
- byte *ptr ;
- g_PhysCollideSize = totalSize + (physModelCount * sizeof(dphysmodel_t));
- g_pPhysCollide = (byte *)malloc(( g_PhysCollideSize + 3 ) & ~3 );
- memset( g_pPhysCollide, 0, g_PhysCollideSize );
- ptr = g_pPhysCollide;
-
- for ( i = 0; i < nummodels; i++ )
- {
- if ( pTextBuffer[i] )
- {
- int j;
-
- dphysmodel_t model;
-
- model.modelIndex = i;
- model.solidCount = collisionList[i].Count();
- model.dataSize = sizeof(int) * model.solidCount;
-
- for ( j = 0; j < model.solidCount; j++ )
- {
- model.dataSize += collisionList[i][j]->GetCollisionBinarySize();
- }
- model.keydataSize = pTextBuffer[i]->GetSize();
-
- // store the header
- memcpy( ptr, &model, sizeof(model) );
- ptr += sizeof(model);
-
- for ( j = 0; j < model.solidCount; j++ )
- {
- int collideSize = collisionList[i][j]->GetCollisionBinarySize();
-
- // write size
- memcpy( ptr, &collideSize, sizeof(int) );
- ptr += sizeof(int);
-
- // now write the collision model
- collisionList[i][j]->WriteCollisionBinary( reinterpret_cast<char *>(ptr) );
- ptr += collideSize;
- }
-
- memcpy( ptr, pTextBuffer[i]->GetData(), pTextBuffer[i]->GetSize() );
- ptr += pTextBuffer[i]->GetSize();
- }
-
- delete pTextBuffer[i];
- }
-
- dphysmodel_t model;
-
- // Mark end of list
- model.modelIndex = -1;
- model.dataSize = -1;
- model.keydataSize = 0;
- model.solidCount = 0;
- memcpy( ptr, &model, sizeof(model) );
- ptr += sizeof(model);
- Assert( (ptr-g_pPhysCollide) == g_PhysCollideSize);
- Msg("done (%d) (%d bytes)\n", (int)(Plat_FloatTime() - start), g_PhysCollideSize );
-
- // UNDONE: Collision models (collisionList) memory leak!
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#include "mathlib/vector.h"
+#include "bspfile.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+#include "physdll.h"
+#include "utlvector.h"
+#include "vbsp.h"
+#include "phyfile.h"
+#include <float.h>
+#include "KeyValues.h"
+#include "UtlBuffer.h"
+#include "utlsymbol.h"
+#include "utlrbtree.h"
+#include "ivp.h"
+#include "disp_ivp.h"
+#include "materialpatch.h"
+#include "bitvec.h"
+
+// bit per leaf
+typedef CBitVec<MAX_MAP_LEAFS> leafbitarray_t;
+
+// parameters for conversion to vphysics
+#define NO_SHRINK 0.0f
+// NOTE: vphysics maintains a minimum separation radius between objects
+// This radius is set to 0.25, but it's symmetric. So shrinking potentially moveable
+// brushes by 0.5 in every direction ensures that these brushes can be constructed
+// touching the world, and constrained in place without collisions or friction
+// UNDONE: Add a key to disable this shrinking if necessary
+#define VPHYSICS_SHRINK (0.5f) // shrink BSP brushes by this much for collision
+#define VPHYSICS_MERGE 0.01f // merge verts closer than this
+
+void EmitPhysCollision();
+
+IPhysicsCollision *physcollision = NULL;
+extern IPhysicsSurfaceProps *physprops;
+
+// a list of all of the materials in the world model
+static CUtlVector<int> s_WorldPropList;
+
+//-----------------------------------------------------------------------------
+// Purpose: Write key/value pairs out to a memory buffer
+//-----------------------------------------------------------------------------
+CTextBuffer::CTextBuffer( void )
+{
+}
+CTextBuffer::~CTextBuffer( void )
+{
+}
+
+void CTextBuffer::WriteText( const char *pText )
+{
+ int len = strlen( pText );
+ CopyData( pText, len );
+}
+
+void CTextBuffer::WriteIntKey( const char *pKeyName, int outputData )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"%d\"\n", pKeyName, outputData );
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::WriteStringKey( const char *pKeyName, const char *outputData )
+{
+ CopyStringQuotes( pKeyName );
+ CopyData( " ", 1 );
+ CopyStringQuotes( outputData );
+ CopyData( "\n", 1 );
+}
+
+void CTextBuffer::WriteFloatKey( const char *pKeyName, float outputData )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"%f\"\n", pKeyName, outputData );
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"", pKeyName );
+ for ( int i = 0; i < count; i++ )
+ {
+ char buf[80];
+
+ sprintf( buf, "%f ", outputData[i] );
+ strcat( tmp, buf );
+ }
+ strcat( tmp, "\"\n" );
+
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::CopyStringQuotes( const char *pString )
+{
+ CopyData( "\"", 1 );
+ CopyData( pString, strlen(pString) );
+ CopyData( "\"", 1 );
+}
+
+void CTextBuffer::Terminate( void )
+{
+ CopyData( "\0", 1 );
+}
+
+void CTextBuffer::CopyData( const char *pData, int len )
+{
+ int offset = m_buffer.AddMultipleToTail( len );
+ memcpy( m_buffer.Base() + offset, pData, len );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename )
+{
+ if ( !pCollide )
+ return;
+
+ Msg("Writing %s...\n", pFilename );
+ Vector *outVerts;
+ int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
+ FILE *fp = fopen( pFilename, "w" );
+ int triCount = vertCount / 3;
+ int vert = 0;
+ for ( int i = 0; i < triCount; i++ )
+ {
+ fprintf( fp, "3\n" );
+ fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ }
+ fclose( fp );
+ physcollision->DestroyDebugMesh( vertCount, outVerts );
+}
+
+
+void DumpCollideToPHY( CPhysCollide *pCollide, CTextBuffer *text, const char *pFilename )
+{
+ Msg("Writing %s...\n", pFilename );
+ FILE *fp = fopen( pFilename, "wb" );
+ phyheader_t header;
+ header.size = sizeof(header);
+ header.id = 0;
+ header.checkSum = 0;
+ header.solidCount = 1;
+ fwrite( &header, sizeof(header), 1, fp );
+ int size = physcollision->CollideSize( pCollide );
+ fwrite( &size, sizeof(int), 1, fp );
+
+ char *buf = (char *)malloc( size );
+ physcollision->CollideWrite( buf, pCollide );
+ fwrite( buf, size, 1, fp );
+
+ fwrite( text->GetData(), text->GetSize(), 1, fp );
+ fclose( fp );
+ free( buf );
+}
+
+CPhysCollisionEntry::CPhysCollisionEntry( CPhysCollide *pCollide )
+{
+ m_pCollide = pCollide;
+}
+
+unsigned int CPhysCollisionEntry::GetCollisionBinarySize()
+{
+ return physcollision->CollideSize( m_pCollide );
+}
+
+unsigned int CPhysCollisionEntry::WriteCollisionBinary( char *pDest )
+{
+ return physcollision->CollideWrite( pDest, m_pCollide );
+}
+
+void CPhysCollisionEntry::DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer )
+{
+ char tmp[128];
+ sprintf( tmp, "%s%03d.phy", pName, modelIndex );
+ DumpCollideToPHY( m_pCollide, pTextBuffer, tmp );
+ sprintf( tmp, "%s%03d.txt", pName, modelIndex );
+ DumpCollideToGlView( m_pCollide, tmp );
+}
+
+
+class CPhysCollisionEntrySolid : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ float m_volume;
+ float m_mass;
+ const char *m_pMaterial;
+};
+
+
+CPhysCollisionEntrySolid::CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_volume = physcollision->CollideVolume( m_pCollide );
+ m_mass = mass;
+ m_pMaterial = pMaterialName;
+}
+
+void CPhysCollisionEntrySolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ DumpCollideFileName( "collide", modelIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntrySolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "solid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteFloatKey( "mass", m_mass );
+ if ( m_pMaterial )
+ {
+ pTextBuffer->WriteStringKey( "surfaceprop", m_pMaterial );
+ }
+ if ( m_volume != 0.f )
+ {
+ pTextBuffer->WriteFloatKey( "volume", m_volume );
+ }
+ pTextBuffer->WriteText( "}\n" );
+}
+
+
+class CPhysCollisionEntryStaticSolid : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ int m_contentsMask;
+};
+
+
+CPhysCollisionEntryStaticSolid ::CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask )
+ : CPhysCollisionEntry( pCollide ), m_contentsMask(contentsMask)
+{
+}
+
+void CPhysCollisionEntryStaticSolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "static%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryStaticSolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "staticsolid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteIntKey( "contents", m_contentsMask );
+ pTextBuffer->WriteText( "}\n" );
+}
+
+CPhysCollisionEntryStaticMesh::CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_pMaterial = pMaterialName;
+}
+
+void CPhysCollisionEntryStaticMesh::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "mesh%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryStaticMesh::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "staticsolid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteText( "}\n" );
+}
+
+class CPhysCollisionEntryFluid : public CPhysCollisionEntry
+{
+public:
+ ~CPhysCollisionEntryFluid();
+ CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ char *m_pSurfaceProp;
+ float m_damping;
+ Vector m_surfaceNormal;
+ float m_surfaceDist;
+ int m_contentsMask;
+};
+
+
+CPhysCollisionEntryFluid::CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_surfaceNormal = normal;
+ m_surfaceDist = dist;
+ m_pSurfaceProp = new char[strlen(pSurfaceProp)+1];
+ strcpy( m_pSurfaceProp, pSurfaceProp );
+ m_damping = damping;
+ m_contentsMask = nContents;
+}
+
+CPhysCollisionEntryFluid::~CPhysCollisionEntryFluid()
+{
+ delete[] m_pSurfaceProp;
+}
+
+void CPhysCollisionEntryFluid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "water%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryFluid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "fluid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteStringKey( "surfaceprop", m_pSurfaceProp ); // write out water material
+ pTextBuffer->WriteFloatKey( "damping", m_damping ); // write out water damping
+ pTextBuffer->WriteIntKey( "contents", m_contentsMask ); // write out water contents
+ float array[4];
+ m_surfaceNormal.CopyToArray( array );
+ array[3] = m_surfaceDist;
+ pTextBuffer->WriteFloatArrayKey( "surfaceplane", array, 4 ); // write out water surface plane
+ pTextBuffer->WriteFloatArrayKey( "currentvelocity", vec3_origin.Base(), 3 ); // write out water velocity
+ pTextBuffer->WriteText( "}\n" );
+}
+
+// Get an index into the prop list of this prop (add it if necessary)
+static int PropIndex( CUtlVector<int> &propList, int propIndex )
+{
+ for ( int i = 0; i < propList.Count(); i++ )
+ {
+ if ( propList[i] == propIndex )
+ return i+1;
+ }
+
+ if ( propList.Count() < 126 )
+ {
+ return propList.AddToTail( propIndex )+1;
+ }
+
+ return 0;
+}
+
+int RemapWorldMaterial( int materialIndexIn )
+{
+ return PropIndex( s_WorldPropList, materialIndexIn );
+}
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+} listplane_t;
+
+static void AddListPlane( CUtlVector<listplane_t> *list, float x, float y, float z, float d )
+{
+ listplane_t plane;
+ plane.normal[0] = x;
+ plane.normal[1] = y;
+ plane.normal[2] = z;
+ plane.dist = d;
+
+ list->AddToTail( plane );
+}
+
+class CPlaneList
+{
+public:
+
+ CPlaneList( float shrink, float merge );
+ ~CPlaneList( void );
+
+ void AddConvex( CPhysConvex *pConvex );
+
+ // add the brushes to the model
+ int AddBrushes( void );
+
+ // Adds a single brush as a convex object
+ void ReferenceBrush( int brushnumber );
+ bool IsBrushReferenced( int brushnumber );
+
+ void ReferenceLeaf( int leafIndex );
+ bool IsLeafReferenced( int leafIndex );
+ int GetFirstBrushSide();
+
+private:
+
+ CPhysConvex *BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum );
+
+public:
+ CUtlVector<CPhysConvex *> m_convex;
+
+ CUtlVector<int> m_leafList;
+ int m_contentsMask;
+
+ float m_shrink;
+ float m_merge;
+ bool *m_brushAdded;
+ float m_totalVolume;
+};
+
+CPlaneList::CPlaneList( float shrink, float merge )
+{
+ m_shrink = shrink;
+ m_merge = merge;
+ m_contentsMask = MASK_SOLID;
+ m_brushAdded = new bool[numbrushes];
+ memset( m_brushAdded, 0, sizeof(bool) * numbrushes );
+ m_totalVolume = 0;
+ m_leafList.Purge();
+}
+
+
+CPlaneList::~CPlaneList( void )
+{
+ delete[] m_brushAdded;
+}
+
+
+void CPlaneList::AddConvex( CPhysConvex *pConvex )
+{
+ if ( pConvex )
+ {
+ m_totalVolume += physcollision->ConvexVolume( pConvex );
+ m_convex.AddToTail( pConvex );
+ }
+}
+
+// Adds a single brush as a convex object
+void CPlaneList::ReferenceBrush( int brushnumber )
+{
+ if ( !(dbrushes[brushnumber].contents & m_contentsMask) )
+ return;
+
+ m_brushAdded[brushnumber] = true;
+
+}
+
+
+bool CPlaneList::IsBrushReferenced( int brushnumber )
+{
+ return m_brushAdded[brushnumber];
+}
+
+CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum )
+{
+ CUtlVector<listplane_t> temp( 0, 32 );
+
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
+ if ( pside->bevel )
+ continue;
+
+ dplane_t *pplane = dplanes + pside->planenum;
+ float shrinkThisPlane = shrink;
+
+ if ( i < g_MainMap->mapbrushes[brushnumber].numsides )
+ {
+ if ( !g_MainMap->mapbrushes[brushnumber].original_sides[i].visible )
+ {
+ // don't shrink brush sides with no visible components.
+ // this produces something closer to the ideal shrink than simply shrinking all planes
+ shrinkThisPlane = 0;
+ }
+ }
+ // Make sure shrinking won't swallow geometry along this axis.
+ if ( pCollideTest && shrinkThisPlane != 0 )
+ {
+ Vector start = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, pplane->normal );
+ Vector end = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, -pplane->normal );
+ float thick = DotProduct( (end-start), pplane->normal );
+ // NOTE: The object must be at least "shrinkMinimum" inches wide on each axis
+ if ( fabs(thick) < shrinkMinimum )
+ {
+#if _DEBUG
+ Warning("Can't shrink brush %d, plane %d (%.2f, %.2f, %.2f)\n", brushnumber, pside->planenum, pplane->normal[0], pplane->normal[1], pplane->normal[2] );
+#endif
+ shrinkThisPlane = 0;
+ }
+ }
+ AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist - shrinkThisPlane );
+ }
+ return physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), m_merge );
+}
+
+int CPlaneList::AddBrushes( void )
+{
+ int count = 0;
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ CPhysConvex *pBrushConvex = NULL;
+ if ( m_shrink != 0 )
+ {
+ // Make sure shrinking won't swallow this brush.
+ CPhysConvex *pConvex = BuildConvexForBrush( brushnumber, 0, NULL, 0 );
+ CPhysCollide *pUnshrunkCollide = physcollision->ConvertConvexToCollide( &pConvex, 1 );
+ pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, pUnshrunkCollide, m_shrink * 3 );
+ physcollision->DestroyCollide( pUnshrunkCollide );
+ }
+ else
+ {
+ pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, NULL, 1.0 );
+ }
+
+ if ( pBrushConvex )
+ {
+ count++;
+ physcollision->SetConvexGameData( pBrushConvex, brushnumber );
+ AddConvex( pBrushConvex );
+ }
+ }
+ }
+ return count;
+}
+
+
+int CPlaneList::GetFirstBrushSide()
+{
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ int sideIndex = i + dbrushes[brushnumber].firstside;
+ dbrushside_t *pside = dbrushsides + sideIndex;
+ if ( pside->bevel )
+ continue;
+ return sideIndex;
+ }
+ }
+ }
+ return 0;
+}
+
+// UNDONE: Try using this kind of algorithm if we run into precision problems.
+// NOTE: ConvexFromPlanes will be doing a bunch of matrix inversions that can suffer
+// if plane normals are too close to each other...
+#if 0
+void CPlaneList::AddBrushes( void )
+{
+ CUtlVector<listplane_t> temp;
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ CUtlVector<winding_t *> windings;
+
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
+ if (pside->bevel)
+ continue;
+ dplane_t *pplane = dplanes + pside->planenum;
+ winding_t *w = BaseWindingForPlane( pplane->normal, pplane->dist - m_shrink );
+ for ( int j = 0; j < dbrushes[brushnumber].numsides && w; j++ )
+ {
+ if (i == j)
+ continue;
+ dbrushside_t *pClipSide = dbrushsides + j + dbrushes[brushnumber].firstside;
+ if (pClipSide->bevel)
+ continue;
+ dplane_t *pClipPlane = dplanes + pClipSide->planenum;
+ ChopWindingInPlace (&w, -pClipPlane->normal, -pClipPlane->dist+m_shrink, 0); //CLIP_EPSILON);
+ }
+ if ( w )
+ {
+ windings.AddToTail( w );
+ }
+ }
+
+ CUtlVector<Vector *> vertList;
+ for ( int p = 0; p < windings.Count(); p++ )
+ {
+ for ( int v = 0; v < windings[p]->numpoints; v++ )
+ {
+ vertList.AddToTail( windings[p]->p + v );
+ }
+ }
+ CPhysConvex *pConvex = physcollision->ConvexFromVerts( vertList.Base(), vertList.Count() );
+ if ( pConvex )
+ {
+ physcollision->SetConvexGameData( pConvex, brushnumber );
+ AddConvex( pConvex );
+ }
+ temp.RemoveAll();
+ }
+ }
+}
+#endif
+
+// If I have a list of leaves, make sure this leaf is in it.
+// Otherwise, process all leaves
+bool CPlaneList::IsLeafReferenced( int leafIndex )
+{
+ if ( !m_leafList.Count() )
+ return true;
+
+ for ( int i = 0; i < m_leafList.Count(); i++ )
+ {
+ if ( m_leafList[i] == leafIndex )
+ return true;
+ }
+
+ return false;
+}
+
+// Add a leaf to my list of interesting leaves
+void CPlaneList::ReferenceLeaf( int leafIndex )
+{
+ m_leafList.AddToTail( leafIndex );
+}
+
+static void VisitLeaves_r( CPlaneList &planes, int node )
+{
+ if ( node < 0 )
+ {
+ int leafIndex = -1 - node;
+ if ( planes.IsLeafReferenced(leafIndex) )
+ {
+ int i;
+
+ // Add the solids in the "empty" leaf
+ for ( i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
+ {
+ int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
+ planes.ReferenceBrush( brushIndex );
+ }
+ }
+ }
+ else
+ {
+ dnode_t *pnode = dnodes + node;
+
+ VisitLeaves_r( planes, pnode->children[0] );
+ VisitLeaves_r( planes, pnode->children[1] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+struct waterleaf_t
+{
+ Vector surfaceNormal;
+ float surfaceDist;
+ float minZ;
+ bool hasSurface;
+ int waterLeafIndex;// this is the submerged leaf
+ int planenum; //UNDONE: REMOVE
+ int surfaceTexInfo; // if hasSurface == true, this is the texinfo index for the water material
+ int outsideLeafIndex;// this is the leaf on the other side of the water surface
+ node_t *pNode;
+};
+
+
+
+// returns true if newleaf should appear before currentleaf in the list
+static bool IsLowerLeaf( const waterleaf_t &newleaf, const waterleaf_t &currentleaf )
+{
+ if ( newleaf.hasSurface && currentleaf.hasSurface )
+ {
+ // the one with the upmost pointing z goes first
+ if ( currentleaf.surfaceNormal.z > newleaf.surfaceNormal.z )
+ return false;
+
+ if ( fabs(currentleaf.surfaceNormal.z - newleaf.surfaceNormal.z) < 0.01 )
+ {
+ if ( newleaf.surfaceDist < currentleaf.surfaceDist )
+ return true;
+ }
+ return true;
+ }
+ else if ( newleaf.hasSurface ) // the leaf with a surface always goes first
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Water surfaces are stored in an RB tree and the tree is used to
+// create one-off .vmt files embedded in the .bsp for each surface so that the
+// water depth effect occurs on a per-water surface level.
+//-----------------------------------------------------------------------------
+struct WaterTexInfo
+{
+ // The mangled new .vmt name ( materials/levelename/oldmaterial_depth_xxx ) where xxx is
+ // the water depth (as an integer )
+ CUtlSymbol m_FullName;
+
+ // The original .vmt name
+ CUtlSymbol m_MaterialName;
+
+ // The depth of the water this texinfo refers to
+ int m_nWaterDepth;
+
+ // The texinfo id
+ int m_nTexInfo;
+
+ // The subdivision size for the water surface
+// float m_SubdivSize;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for RB tree operations ( we compare full mangled names )
+// Input : src1 -
+// src2 -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool WaterLessFunc( WaterTexInfo const& src1, WaterTexInfo const& src2 )
+{
+ return src1.m_FullName < src2.m_FullName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A growable RB tree of water surfaces
+//-----------------------------------------------------------------------------
+static CUtlRBTree< WaterTexInfo, int > g_WaterTexInfos( 0, 32, WaterLessFunc );
+
+#if 0
+float GetSubdivSizeForFogVolume( int fogVolumeID )
+{
+ Assert( fogVolumeID >= 0 && fogVolumeID < g_WaterTexInfos.Count() );
+ return g_WaterTexInfos[fogVolumeID].m_SubdivSize;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *mapname -
+// *materialname -
+// waterdepth -
+// *fullname -
+//-----------------------------------------------------------------------------
+void GetWaterTextureName( char const *mapname, char const *materialname, int waterdepth, char *fullname )
+{
+ char temp[ 512 ];
+
+ // Construct the full name (prepend mapname to reduce name collisions)
+ sprintf( temp, "maps/%s/%s_depth_%i", mapname, materialname, (int)waterdepth );
+
+ // Make sure it's lower case
+ strlwr( temp );
+
+ strcpy( fullname, temp );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called to write procedural materials in the rb tree to the embedded
+// pak file for this .bsp
+//-----------------------------------------------------------------------------
+void EmitWaterMaterialFile( WaterTexInfo *wti )
+{
+ char waterTextureName[512];
+ if ( !wti )
+ {
+ return;
+ }
+
+ GetWaterTextureName( mapbase, wti->m_MaterialName.String(), ( int )wti->m_nWaterDepth, waterTextureName );
+
+ // Convert to string
+ char szDepth[ 32 ];
+ sprintf( szDepth, "%i", wti->m_nWaterDepth );
+ CreateMaterialPatch( wti->m_MaterialName.String(), waterTextureName, "$waterdepth", szDepth, PATCH_INSERT );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes the texinfo_t referenced by the .vmt and the computed depth for the
+// surface and looks up or creates a texdata/texinfo for the mangled one-off water .vmt file
+// Input : *pBaseInfo -
+// depth -
+// Output : int
+//-----------------------------------------------------------------------------
+int FindOrCreateWaterTexInfo( texinfo_t *pBaseInfo, float depth )
+{
+ char fullname[ 512 ];
+ char materialname[ 512 ];
+
+ // Get the base texture/material name
+ char const *name = TexDataStringTable_GetString( GetTexData( pBaseInfo->texdata )->nameStringTableID );
+
+ GetWaterTextureName( mapbase, name, (int)depth, fullname );
+
+ // See if we already have an entry for this depth
+ WaterTexInfo lookup;
+ lookup.m_FullName = fullname;
+ int idx = g_WaterTexInfos.Find( lookup );
+
+ // If so, return the existing entry texinfo index
+ if ( idx != g_WaterTexInfos.InvalidIndex() )
+ {
+ return g_WaterTexInfos[ idx ].m_nTexInfo;
+ }
+
+ // Otherwise, fill in the rest of the data
+ lookup.m_nWaterDepth = (int)depth;
+ // Remember the current material name
+ sprintf( materialname, "%s", name );
+ strlwr( materialname );
+ lookup.m_MaterialName = materialname;
+
+ texinfo_t ti;
+ // Make a copy
+ ti = *pBaseInfo;
+ // Create a texdata that is based on the underlying existing entry
+ ti.texdata = FindAliasedTexData( fullname, GetTexData( pBaseInfo->texdata ) );
+
+ // Find or create a new index
+ lookup.m_nTexInfo = FindOrCreateTexInfo( ti );
+
+ // Add the new texinfo to the RB tree
+ idx = g_WaterTexInfos.Insert( lookup );
+
+ // Msg( "created texinfo for %s\n", lookup.m_FullName.String() );
+
+ // Go ahead and create the new vmt file.
+ EmitWaterMaterialFile( &g_WaterTexInfos[idx] );
+
+ // Return the new texinfo
+ return g_WaterTexInfos[ idx ].m_nTexInfo;
+}
+
+extern node_t *dfacenodes[MAX_MAP_FACES];
+static void WriteFogVolumeIDs( dmodel_t *pModel )
+{
+ int i;
+
+ // write fog volume ID to each face in this model
+ for( i = pModel->firstface; i < pModel->firstface + pModel->numfaces; i++ )
+ {
+ dface_t *pFace = &dfaces[i];
+ node_t *pFaceNode = dfacenodes[i];
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pFace->surfaceFogVolumeID = -1;
+ if ( pFaceNode )
+ {
+ if ( (pTexInfo->flags & SURF_WARP ) && pFaceNode->planenum == PLANENUM_LEAF && pFaceNode->diskId >= 0 )
+ {
+ pFace->surfaceFogVolumeID = dleafs[pFaceNode->diskId].leafWaterDataID;
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[pFace->surfaceFogVolumeID];
+
+ // HACKHACK: Should probably mark these faces as water bottom or "bottommaterial" faces.
+ // HACKHACK: Use a heuristic, if it points up, it's the water top.
+ if ( dplanes[pFace->planenum].normal.z > 0 )
+ {
+ pFace->texinfo = pLeafWaterData->surfaceTexInfoID;
+ }
+ }
+ else
+ {
+ // missed this face somehow?
+ Assert( !(pTexInfo->flags & SURF_WARP ) );
+ }
+
+ }
+ }
+}
+
+
+static bool PortalCrossesWater( waterleaf_t &baseleaf, portal_t *portal )
+{
+ if ( baseleaf.hasSurface )
+ {
+ int side = WindingOnPlaneSide( portal->winding, baseleaf.surfaceNormal, baseleaf.surfaceDist );
+ if ( side == SIDE_CROSS || side == SIDE_FRONT )
+ return true;
+ }
+
+ return false;
+}
+
+
+static int FindOrCreateLeafWaterData( float surfaceZ, float minZ, int surfaceTexInfoID )
+{
+ int i;
+ for( i = 0; i < numleafwaterdata; i++ )
+ {
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[i];
+ if( pLeafWaterData->surfaceZ == surfaceZ &&
+ pLeafWaterData->minZ == minZ &&
+ pLeafWaterData->surfaceTexInfoID == surfaceTexInfoID )
+ {
+ return i;
+ }
+ }
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[numleafwaterdata];
+ pLeafWaterData->surfaceZ = surfaceZ;
+ pLeafWaterData->minZ = minZ;
+ pLeafWaterData->surfaceTexInfoID = surfaceTexInfoID;
+ numleafwaterdata++;
+ return numleafwaterdata - 1;
+}
+
+
+// Enumerate all leaves under node with contents in contentsMask and add them to list
+void EnumLeaves_r( CUtlVector<node_t *> &list, node_t *node, int contentsMask )
+{
+ if ( node->planenum != PLANENUM_LEAF )
+ {
+ EnumLeaves_r( list, node->children[0], contentsMask );
+ EnumLeaves_r( list, node->children[1], contentsMask );
+ return;
+ }
+
+ if ( !(node->contents & contentsMask) )
+ return;
+
+
+ // has the contents, put it in the list
+ list.AddToTail( node );
+}
+
+
+// Builds a waterleaf_t for the given leaf
+static void BuildWaterLeaf( node_t *pLeafIn, waterleaf_t &waterLeafOut )
+{
+ waterLeafOut.pNode = pLeafIn;
+ waterLeafOut.waterLeafIndex = pLeafIn->diskId;
+ waterLeafOut.outsideLeafIndex = -1;
+ waterLeafOut.hasSurface = false;
+ waterLeafOut.surfaceDist = MAX_COORD_INTEGER;
+ waterLeafOut.surfaceNormal.Init( 0.f, 0.f, 1.f );
+ waterLeafOut.planenum = -1;
+ waterLeafOut.surfaceTexInfo = -1;
+ waterLeafOut.minZ = MAX_COORD_INTEGER;
+
+ // search the list of portals out of this leaf for one that leaves water
+ // If you find one, this leaf has a surface, so fill out the surface data
+ int oppositeNodeIndex = 0;
+ for (portal_t *p = pLeafIn->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeafIn) ? 1 : 0;
+
+ // not visible, can't be the portals we're looking for...
+ if ( !p->side )
+ continue;
+
+ // See if this portal crosses into air
+ node_t *pOpposite = p->nodes[oppositeNodeIndex];
+ if ( !(pOpposite->contents & MASK_WATER) && !(pOpposite->contents & MASK_SOLID) )
+ {
+ // it does, there must be a surface here
+ plane_t *plane = &g_MainMap->mapplanes[p->side->planenum];
+ if ( waterLeafOut.hasSurface )
+ {
+ // Sort to find the most upward facing normal (skips sides)
+ if ( waterLeafOut.surfaceNormal.z > plane->normal.z )
+ continue;
+ if ( (waterLeafOut.surfaceNormal.z == plane->normal.z) && waterLeafOut.surfaceDist >= plane->dist )
+ continue;
+ }
+ // water surface needs to point at least somewhat up, this is
+ // probably a map error
+ if ( plane->normal.z <= 0 )
+ continue;
+ waterLeafOut.surfaceDist = plane->dist;
+ waterLeafOut.surfaceNormal = plane->normal;
+ waterLeafOut.hasSurface = true;
+ waterLeafOut.outsideLeafIndex = p->nodes[oppositeNodeIndex]->diskId;
+ waterLeafOut.surfaceTexInfo = p->side->texinfo;
+ }
+ }
+}
+
+
+static void InsertSortWaterLeaf( CUtlVector<waterleaf_t> &list, const waterleaf_t &leafInsert )
+{
+ // insertion sort the leaf (lowest leaves go first)
+ // leaves that aren't actually on the surface of the water will have leaf.hasSurface == false.
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( IsLowerLeaf( leafInsert, list[i] ) )
+ {
+ list.InsertBefore( i, leafInsert );
+ return;
+ }
+ }
+
+ // must the highest one, so stick it at the end.
+ list.AddToTail( leafInsert );
+}
+
+
+// Flood fill the tree, finding neighboring water volumes and connecting them to this list
+// Cut groups that try to cross the surface.
+// Mark leaves that are in a group as "visited" so they won't be chosen by subsequent fills
+static void Flood_FindConnectedWaterVolumes_r( CUtlVector<node_t *> &list, node_t *pLeaf, waterleaf_t &baseleaf, leafbitarray_t &visited )
+{
+ // already visited, or not the same water contents
+ if ( pLeaf->diskId < 0 || visited.Get(pLeaf->diskId) || !(pLeaf->contents & (baseleaf.pNode->contents & MASK_WATER) ) )
+ return;
+
+ int oppositeNodeIndex = 0;
+ for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
+
+ // If any portal crosses the water surface, don't flow through this leaf
+ if ( PortalCrossesWater( baseleaf, p ) )
+ return;
+ }
+
+ visited.Set( pLeaf->diskId );
+ list.AddToTail( pLeaf );
+
+ baseleaf.minZ = min( pLeaf->mins.z, baseleaf.minZ );
+
+ for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
+
+ Flood_FindConnectedWaterVolumes_r( list, p->nodes[oppositeNodeIndex], baseleaf, visited );
+ }
+}
+
+// UNDONE: This is a bit of a hack to avoid crashing when we can't find an
+// appropriate texinfo for a water model (to get physics properties)
+int FirstWaterTexinfo( bspbrush_t *brushlist, int contents )
+{
+ while (brushlist)
+ {
+ if ( brushlist->original->contents & contents )
+ {
+ for ( int i = 0; i < brushlist->original->numsides; i++ )
+ {
+ if ( brushlist->original->original_sides[i].contents & contents )
+ {
+ return brushlist->original->original_sides[i].texinfo;
+ }
+ }
+ }
+ brushlist = brushlist->next;
+ }
+
+ Assert(0);
+ return 0;
+}
+
+// This is a list of water data that will be turned into physics models
+struct watermodel_t
+{
+ int modelIndex;
+ int contents;
+ waterleaf_t waterLeafData;
+ int depthTexinfo;
+ int firstWaterLeafIndex;
+ int waterLeafCount;
+ int fogVolumeIndex;
+};
+
+static CUtlVector<watermodel_t> g_WaterModels;
+static CUtlVector<int> g_WaterLeafList;
+
+// Creates a list of watermodel_t for later processing by EmitPhysCollision
+void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *node )
+{
+ CUtlVector<node_t *> leafListAnyWater;
+ // build the list of all leaves containing water
+ EnumLeaves_r( leafListAnyWater, node, MASK_WATER );
+
+ // make a sorted list to flood fill
+ CUtlVector<waterleaf_t> list;
+
+ int i;
+ for ( i = 0; i < leafListAnyWater.Count(); i++ )
+ {
+ waterleaf_t waterLeaf;
+ BuildWaterLeaf( leafListAnyWater[i], waterLeaf );
+ InsertSortWaterLeaf( list, waterLeaf );
+ }
+
+ leafbitarray_t visited;
+ CUtlVector<node_t *> waterAreaList;
+ for ( i = 0; i < list.Count(); i++ )
+ {
+ Flood_FindConnectedWaterVolumes_r( waterAreaList, list[i].pNode, list[i], visited );
+
+ // did we find a list of leaves connected to this one?
+ // remember the list is sorted, so this one may have been attached to a previous
+ // leaf. So it could have nothing hanging off of it.
+ if ( waterAreaList.Count() )
+ {
+ // yes, emit a watermodel
+ watermodel_t tmp;
+ tmp.modelIndex = nummodels;
+ tmp.contents = list[i].pNode->contents;
+ tmp.waterLeafData = list[i];
+ tmp.firstWaterLeafIndex = g_WaterLeafList.Count();
+ tmp.waterLeafCount = waterAreaList.Count();
+
+ float waterDepth = tmp.waterLeafData.surfaceDist - tmp.waterLeafData.minZ;
+ if ( tmp.waterLeafData.surfaceTexInfo < 0 )
+ {
+ // the map has probably leaked in this case, but output something anyway.
+ Assert(list[i].pNode->planenum == PLANENUM_LEAF);
+ tmp.waterLeafData.surfaceTexInfo = FirstWaterTexinfo( list[i].pNode->brushlist, tmp.contents );
+ }
+ tmp.depthTexinfo = FindOrCreateWaterTexInfo( &texinfo[ tmp.waterLeafData.surfaceTexInfo ], waterDepth );
+ tmp.fogVolumeIndex = FindOrCreateLeafWaterData( tmp.waterLeafData.surfaceDist, tmp.waterLeafData.minZ, tmp.waterLeafData.surfaceTexInfo );
+
+ for ( int j = 0; j < waterAreaList.Count(); j++ )
+ {
+ g_WaterLeafList.AddToTail( waterAreaList[j]->diskId );
+ }
+ waterAreaList.RemoveAll();
+ g_WaterModels.AddToTail( tmp );
+ }
+ }
+
+ WriteFogVolumeIDs( pModel );
+}
+
+
+static void ConvertWaterModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex,
+ float shrinkSize, float mergeTolerance )
+{
+ dmodel_t *pModel = dmodels + modelIndex;
+
+ for ( int i = 0; i < g_WaterModels.Count(); i++ )
+ {
+ watermodel_t &waterModel = g_WaterModels[i];
+ if ( waterModel.modelIndex != modelIndex )
+ continue;
+
+ CPlaneList planes( shrinkSize, mergeTolerance );
+ int firstLeaf = waterModel.firstWaterLeafIndex;
+ planes.m_contentsMask = waterModel.contents;
+
+ // push all of the leaves into the collision list
+ for ( int j = 0; j < waterModel.waterLeafCount; j++ )
+ {
+ int leafIndex = g_WaterLeafList[firstLeaf + j];
+
+ dleaf_t *pLeaf = dleafs + leafIndex;
+ // fixup waterdata
+ pLeaf->leafWaterDataID = waterModel.fogVolumeIndex;
+ planes.ReferenceLeaf( leafIndex );
+ }
+
+ // visit the referenced leaves that belong to this model
+ VisitLeaves_r( planes, pModel->headnode );
+
+ // Now add the brushes from those leaves as convex
+
+ // BUGBUG: NOTE: If your map has a brush that crosses the surface, it will be added to two water
+ // volumes. This only happens with connected water volumes with multiple surface heights
+ // UNDONE: Right now map makers must cut such brushes. It could be automatically cut by adding the
+ // surface plane to the list for each brush before calling ConvexFromPlanes()
+ planes.AddBrushes();
+
+ int count = planes.m_convex.Count();
+ if ( !count )
+ continue;
+
+ // Save off the plane of the surface for this group as well as the collision model
+ // for all convex objects in the group.
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
+ if ( pCollide )
+ {
+ int waterSurfaceTexInfoID = -1;
+ // use defaults
+ const char *pSurfaceProp = "water";
+ float damping = 0.01;
+ if ( waterSurfaceTexInfoID >= 0 )
+ {
+ // material override
+ int texdata = texinfo[waterSurfaceTexInfoID].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ pSurfaceProp = physprops->GetPropName( prop );
+ }
+
+ if ( !waterModel.waterLeafData.hasSurface )
+ {
+ waterModel.waterLeafData.surfaceNormal.Init( 0,0,1 );
+ Vector top = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, waterModel.waterLeafData.surfaceNormal );
+ waterModel.waterLeafData.surfaceDist = top.z;
+ }
+ CPhysCollisionEntryFluid *pCollisionEntryFuild = new CPhysCollisionEntryFluid( pCollide,
+ pSurfaceProp, damping, waterModel.waterLeafData.surfaceNormal, waterModel.waterLeafData.surfaceDist, waterModel.contents );
+ collisionList.AddToTail( pCollisionEntryFuild );
+ }
+ }
+}
+
+// compute a normal for a triangle of the given three points (points are clockwise, normal points out)
+static Vector TriangleNormal( const Vector &p0, const Vector &p1, const Vector &p2 )
+{
+ Vector e0 = p1 - p0;
+ Vector e1 = p2 - p0;
+ Vector normal = CrossProduct( e1, e0 );
+ VectorNormalize( normal );
+
+ return normal;
+}
+
+
+// find the side of the brush with the normal closest to the given normal
+static dbrushside_t *FindBrushSide( int brushIndex, const Vector &normal )
+{
+ dbrush_t *pbrush = &dbrushes[brushIndex];
+ dbrushside_t *out = NULL;
+ float best = -1.f;
+
+ for ( int i = 0; i < pbrush->numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + pbrush->firstside;
+ dplane_t *pplane = dplanes + pside->planenum;
+ float dot = DotProduct( normal, pplane->normal );
+ if ( dot > best )
+ {
+ best = dot;
+ out = pside;
+ }
+ }
+
+ return out;
+}
+
+
+
+static void ConvertWorldBrushesToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance, int contentsMask )
+{
+ CPlaneList planes( shrinkSize, mergeTolerance );
+
+ planes.m_contentsMask = contentsMask;
+
+ VisitLeaves_r( planes, dmodels[0].headnode );
+ planes.AddBrushes();
+
+ int count = planes.m_convex.Count();
+ if ( count )
+ {
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
+
+ ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
+ int convex = pQuery->ConvexCount();
+ for ( int i = 0; i < convex; i++ )
+ {
+ int triCount = pQuery->TriangleCount( i );
+ int brushIndex = pQuery->GetGameData( i );
+
+ Vector points[3];
+ for ( int j = 0; j < triCount; j++ )
+ {
+ pQuery->GetTriangleVerts( i, j, points );
+ Vector normal = TriangleNormal( points[0], points[1], points[2] );
+ dbrushside_t *pside = FindBrushSide( brushIndex, normal );
+ if ( pside->texinfo != TEXINFO_NODE )
+ {
+ int prop = g_SurfaceProperties[texinfo[pside->texinfo].texdata];
+ pQuery->SetTriangleMaterialIndex( i, j, RemapWorldMaterial( prop ) );
+ }
+ }
+ }
+ physcollision->DestroyQueryModel( pQuery );
+ pQuery = NULL;
+
+ collisionList.AddToTail( new CPhysCollisionEntryStaticSolid( pCollide, contentsMask ) );
+ }
+}
+
+// adds any world, terrain, and water collision models to the collision list
+static void BuildWorldPhysModel( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance )
+{
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, MASK_SOLID );
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_PLAYERCLIP );
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_MONSTERCLIP );
+
+ if ( !g_bNoVirtualMesh && Disp_HasPower4Displacements() )
+ {
+ Warning("WARNING: Map using power 4 displacements, terrain physics cannot be compressed, map will need additional memory and CPU.\n");
+ g_bNoVirtualMesh = true;
+ }
+
+ // if there's terrain, save it off as a static mesh/polysoup
+ if ( g_bNoVirtualMesh || !physcollision->SupportsVirtualMesh() )
+ {
+ Disp_AddCollisionModels( collisionList, &dmodels[0], MASK_SOLID );
+ }
+ else
+ {
+ Disp_BuildVirtualMesh( MASK_SOLID );
+ }
+ ConvertWaterModelToPhysCollide( collisionList, 0, shrinkSize, mergeTolerance );
+}
+
+
+// adds a collision entry for this brush model
+static void ConvertModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex, int contents, float shrinkSize, float mergeTolerance )
+{
+ int i;
+ CPlaneList planes( shrinkSize, mergeTolerance );
+
+ planes.m_contentsMask = contents;
+
+ dmodel_t *pModel = dmodels + modelIndex;
+ VisitLeaves_r( planes, pModel->headnode );
+ planes.AddBrushes();
+ int count = planes.m_convex.Count();
+ convertconvexparams_t params;
+ params.Defaults();
+ params.buildOuterConvexHull = count > 1 ? true : false;
+ params.buildDragAxisAreas = true;
+ Vector size = pModel->maxs - pModel->mins;
+
+ float minSurfaceArea = -1.0f;
+ for ( i = 0; i < 3; i++ )
+ {
+ int other = (i+1)%3;
+ int cross = (i+2)%3;
+ float surfaceArea = size[other] * size[cross];
+ if ( minSurfaceArea < 0 || surfaceArea < minSurfaceArea )
+ {
+ minSurfaceArea = surfaceArea;
+ }
+ }
+ // this can be really slow with super-large models and a low error tolerance
+ // Basically you get a ray cast through each square of epsilon surface area on each OBB side
+ // So compute it for 1% error (on the smallest side, less on larger sides)
+ params.dragAreaEpsilon = clamp( minSurfaceArea * 1e-2f, 1.0f, 1024.0f );
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( planes.m_convex.Base(), count, params );
+
+ if ( !pCollide )
+ return;
+
+ struct
+ {
+ int prop;
+ float area;
+ } proplist[256];
+ int numprops = 1;
+
+ proplist[0].prop = -1;
+ proplist[0].area = 1;
+ // compute the array of props on the surface of this model
+
+ // NODRAW brushes no longer have any faces
+ if ( !dmodels[modelIndex].numfaces )
+ {
+ int sideIndex = planes.GetFirstBrushSide();
+ int texdata = texinfo[dbrushsides[sideIndex].texinfo].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ proplist[numprops].prop = prop;
+ proplist[numprops].area = 2;
+ numprops++;
+ }
+
+ for ( i = 0; i < dmodels[modelIndex].numfaces; i++ )
+ {
+ dface_t *face = dfaces + i + dmodels[modelIndex].firstface;
+ int texdata = texinfo[face->texinfo].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ int j;
+ for ( j = 0; j < numprops; j++ )
+ {
+ if ( proplist[j].prop == prop )
+ {
+ proplist[j].area += face->area;
+ break;
+ }
+ }
+
+ if ( (!numprops || j >= numprops) && numprops < ARRAYSIZE(proplist) )
+ {
+ proplist[numprops].prop = prop;
+ proplist[numprops].area = face->area;
+ numprops++;
+ }
+ }
+
+
+ // choose the prop with the most surface area
+ int maxIndex = -1;
+ float maxArea = 0;
+ float totalArea = 0;
+
+ for ( i = 0; i < numprops; i++ )
+ {
+ if ( proplist[i].area > maxArea )
+ {
+ maxIndex = i;
+ maxArea = proplist[i].area;
+ }
+ // add up the total surface area
+ totalArea += proplist[i].area;
+ }
+
+ float mass = 1.0f;
+ const char *pMaterial = "default";
+ if ( maxIndex >= 0 )
+ {
+ int prop = proplist[maxIndex].prop;
+
+ // use default if this material has no prop
+ if ( prop < 0 )
+ prop = 0;
+
+ pMaterial = physprops->GetPropName( prop );
+ float density, thickness;
+ physprops->GetPhysicsProperties( prop, &density, &thickness, NULL, NULL );
+
+ // if this is a "shell" material (it is hollow and encloses some empty space)
+ // compute the mass with a constant surface thickness
+ if ( thickness != 0 )
+ {
+ mass = totalArea * thickness * density * CUBIC_METERS_PER_CUBIC_INCH;
+ }
+ else
+ {
+ // material is completely solid, compute total mass as if constant density throughout.
+ mass = planes.m_totalVolume * density * CUBIC_METERS_PER_CUBIC_INCH;
+ }
+ }
+
+ // Clamp mass to 100,000 kg
+ if ( mass > VPHYSICS_MAX_MASS )
+ {
+ mass = VPHYSICS_MAX_MASS;
+ }
+
+ collisionList.AddToTail( new CPhysCollisionEntrySolid( pCollide, pMaterial, mass ) );
+}
+
+static void ClearLeafWaterData( void )
+{
+ int i;
+
+ for( i = 0; i < numleafs; i++ )
+ {
+ dleafs[i].leafWaterDataID = -1;
+ dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
+ }
+}
+
+
+// This is the only public entry to this file.
+// The global data touched in the file is:
+// from bsplib.h:
+// g_pPhysCollide : This is an output from this file.
+// g_PhysCollideSize : This is set in this file.
+// g_numdispinfo : This is an input to this file.
+// g_dispinfo : This is an input to this file.
+// numnodewaterdata : This is an output from this file.
+// dleafwaterdata : This is an output from this file.
+// from vbsp.h:
+// g_SurfaceProperties : This is an input to this file.
+void EmitPhysCollision()
+{
+ ClearLeafWaterData();
+
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( physicsFactory )
+ {
+ physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ }
+
+ if ( !physcollision )
+ {
+ Warning("!!! WARNING: Can't build collision data!\n" );
+ return;
+ }
+
+ CUtlVector<CPhysCollisionEntry *> collisionList[MAX_MAP_MODELS];
+ CTextBuffer *pTextBuffer[MAX_MAP_MODELS];
+
+ int physModelCount = 0, totalSize = 0;
+
+ int start = Plat_FloatTime();
+
+ Msg("Building Physics collision data...\n" );
+
+ int i, j;
+ for ( i = 0; i < nummodels; i++ )
+ {
+ // Build a list of collision models for this brush model section
+ if ( i == 0 )
+ {
+ // world is the only model that processes water separately.
+ // other brushes are assumed to be completely solid or completely liquid
+ BuildWorldPhysModel( collisionList[i], NO_SHRINK, VPHYSICS_MERGE);
+ }
+ else
+ {
+ ConvertModelToPhysCollide( collisionList[i], i, MASK_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|MASK_WATER, VPHYSICS_SHRINK, VPHYSICS_MERGE );
+ }
+
+ pTextBuffer[i] = NULL;
+ if ( !collisionList[i].Count() )
+ continue;
+
+ // if we've got collision models, write their script for processing in the game
+ pTextBuffer[i] = new CTextBuffer;
+ for ( j = 0; j < collisionList[i].Count(); j++ )
+ {
+ // dump a text file for visualization
+ if ( dumpcollide )
+ {
+ collisionList[i][j]->DumpCollide( pTextBuffer[i], i, j );
+ }
+ // each model knows how to write its script
+ collisionList[i][j]->WriteToTextBuffer( pTextBuffer[i], i, j );
+ // total up the binary section's size
+ totalSize += collisionList[i][j]->GetCollisionBinarySize() + sizeof(int);
+ }
+
+ // These sections only appear in the world's collision text
+ if ( i == 0 )
+ {
+ if ( !g_bNoVirtualMesh && physcollision->SupportsVirtualMesh() )
+ {
+ pTextBuffer[i]->WriteText("virtualterrain {}\n");
+ }
+ if ( s_WorldPropList.Count() )
+ {
+ pTextBuffer[i]->WriteText( "materialtable {\n" );
+ for ( j = 0; j < s_WorldPropList.Count(); j++ )
+ {
+ int propIndex = s_WorldPropList[j];
+ if ( propIndex < 0 )
+ {
+ pTextBuffer[i]->WriteIntKey( "default", j+1 );
+ }
+ else
+ {
+ pTextBuffer[i]->WriteIntKey( physprops->GetPropName( propIndex ), j+1 );
+ }
+ }
+ pTextBuffer[i]->WriteText( "}\n" );
+ }
+ }
+
+ pTextBuffer[i]->Terminate();
+
+ // total lump size includes the text buffers (scripts)
+ totalSize += pTextBuffer[i]->GetSize();
+
+ physModelCount++;
+ }
+
+ // add one for tail of list marker
+ physModelCount++;
+
+ // DWORD align the lump because AddLump assumes that it is DWORD aligned.
+ byte *ptr ;
+ g_PhysCollideSize = totalSize + (physModelCount * sizeof(dphysmodel_t));
+ g_pPhysCollide = (byte *)malloc(( g_PhysCollideSize + 3 ) & ~3 );
+ memset( g_pPhysCollide, 0, g_PhysCollideSize );
+ ptr = g_pPhysCollide;
+
+ for ( i = 0; i < nummodels; i++ )
+ {
+ if ( pTextBuffer[i] )
+ {
+ int j;
+
+ dphysmodel_t model;
+
+ model.modelIndex = i;
+ model.solidCount = collisionList[i].Count();
+ model.dataSize = sizeof(int) * model.solidCount;
+
+ for ( j = 0; j < model.solidCount; j++ )
+ {
+ model.dataSize += collisionList[i][j]->GetCollisionBinarySize();
+ }
+ model.keydataSize = pTextBuffer[i]->GetSize();
+
+ // store the header
+ memcpy( ptr, &model, sizeof(model) );
+ ptr += sizeof(model);
+
+ for ( j = 0; j < model.solidCount; j++ )
+ {
+ int collideSize = collisionList[i][j]->GetCollisionBinarySize();
+
+ // write size
+ memcpy( ptr, &collideSize, sizeof(int) );
+ ptr += sizeof(int);
+
+ // now write the collision model
+ collisionList[i][j]->WriteCollisionBinary( reinterpret_cast<char *>(ptr) );
+ ptr += collideSize;
+ }
+
+ memcpy( ptr, pTextBuffer[i]->GetData(), pTextBuffer[i]->GetSize() );
+ ptr += pTextBuffer[i]->GetSize();
+ }
+
+ delete pTextBuffer[i];
+ }
+
+ dphysmodel_t model;
+
+ // Mark end of list
+ model.modelIndex = -1;
+ model.dataSize = -1;
+ model.keydataSize = 0;
+ model.solidCount = 0;
+ memcpy( ptr, &model, sizeof(model) );
+ ptr += sizeof(model);
+ Assert( (ptr-g_pPhysCollide) == g_PhysCollideSize);
+ Msg("done (%d) (%d bytes)\n", (int)(Plat_FloatTime() - start), g_PhysCollideSize );
+
+ // UNDONE: Collision models (collisionList) memory leak!
+}
diff --git a/mp/src/utils/vbsp/ivp.h b/mp/src/utils/vbsp/ivp.h
index d3c310dc..318a199d 100644
--- a/mp/src/utils/vbsp/ivp.h
+++ b/mp/src/utils/vbsp/ivp.h
@@ -1,75 +1,75 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef IVP_H
-#define IVP_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "utlvector.h"
-
-class CPhysCollide;
-class CTextBuffer;
-class IPhysicsCollision;
-extern IPhysicsCollision *physcollision;
-
-// a list of all of the materials in the world model
-extern int RemapWorldMaterial( int materialIndexIn );
-
-class CPhysCollisionEntry
-{
-public:
- CPhysCollisionEntry( CPhysCollide *pCollide );
-
- virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
- virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
-
- unsigned int GetCollisionBinarySize();
- unsigned int WriteCollisionBinary( char *pDest );
-
-protected:
- void DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer );
-
-protected:
- CPhysCollide *m_pCollide;
-};
-
-class CPhysCollisionEntryStaticMesh : public CPhysCollisionEntry
-{
-public:
- CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName );
-
- virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
- virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
-
-private:
- const char *m_pMaterial;
-};
-
-
-class CTextBuffer
-{
-public:
- CTextBuffer( void );
- ~CTextBuffer( void );
- inline int GetSize( void ) { return m_buffer.Count(); }
- inline char *GetData( void ) { return m_buffer.Base(); }
-
- void WriteText( const char *pText );
- void WriteIntKey( const char *pKeyName, int outputData );
- void WriteStringKey( const char *pKeyName, const char *outputData );
- void WriteFloatKey( const char *pKeyName, float outputData );
- void WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count );
-
- void CopyStringQuotes( const char *pString );
- void Terminate( void );
-private:
- void CopyData( const char *pData, int len );
- CUtlVector<char> m_buffer;
-};
-
-#endif // IVP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef IVP_H
+#define IVP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+
+class CPhysCollide;
+class CTextBuffer;
+class IPhysicsCollision;
+extern IPhysicsCollision *physcollision;
+
+// a list of all of the materials in the world model
+extern int RemapWorldMaterial( int materialIndexIn );
+
+class CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntry( CPhysCollide *pCollide );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
+
+ unsigned int GetCollisionBinarySize();
+ unsigned int WriteCollisionBinary( char *pDest );
+
+protected:
+ void DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer );
+
+protected:
+ CPhysCollide *m_pCollide;
+};
+
+class CPhysCollisionEntryStaticMesh : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ const char *m_pMaterial;
+};
+
+
+class CTextBuffer
+{
+public:
+ CTextBuffer( void );
+ ~CTextBuffer( void );
+ inline int GetSize( void ) { return m_buffer.Count(); }
+ inline char *GetData( void ) { return m_buffer.Base(); }
+
+ void WriteText( const char *pText );
+ void WriteIntKey( const char *pKeyName, int outputData );
+ void WriteStringKey( const char *pKeyName, const char *outputData );
+ void WriteFloatKey( const char *pKeyName, float outputData );
+ void WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count );
+
+ void CopyStringQuotes( const char *pString );
+ void Terminate( void );
+private:
+ void CopyData( const char *pData, int len );
+ CUtlVector<char> m_buffer;
+};
+
+#endif // IVP_H
diff --git a/mp/src/utils/vbsp/leakfile.cpp b/mp/src/utils/vbsp/leakfile.cpp
index bd8a9b28..d6038830 100644
--- a/mp/src/utils/vbsp/leakfile.cpp
+++ b/mp/src/utils/vbsp/leakfile.cpp
@@ -1,168 +1,168 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-#include "color.h"
-
-/*
-==============================================================================
-
-LEAF FILE GENERATION
-
-Save out name.line for qe3 to read
-==============================================================================
-*/
-
-
-/*
-=============
-LeakFile
-
-Finds the shortest possible chain of portals
-that leads from the outside leaf to a specifically
-occupied leaf
-=============
-*/
-void LeakFile (tree_t *tree)
-{
- Vector mid;
- FILE *linefile;
- char filename[1024];
- node_t *node;
- int count;
-
- if (!tree->outside_node.occupied)
- return;
-
- tree->leaked = true;
- qprintf ("--- LeakFile ---\n");
-
- //
- // write the points to the file
- //
- sprintf (filename, "%s.lin", source);
- linefile = fopen (filename, "w");
- if (!linefile)
- Error ("Couldn't open %s\n", filename);
-
- count = 0;
- node = &tree->outside_node;
- while (node->occupied > 1)
- {
- portal_t *nextportal = NULL;
- node_t *nextnode = NULL;
- int s = 0;
-
- // find the best portal exit
- int next = node->occupied;
- for (portal_t *p=node->portals ; p ; p = p->next[!s])
- {
- s = (p->nodes[0] == node);
- if (p->nodes[s]->occupied
- && p->nodes[s]->occupied < next)
- {
- nextportal = p;
- nextnode = p->nodes[s];
- next = nextnode->occupied;
- }
- }
- node = nextnode;
- WindingCenter (nextportal->winding, mid);
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
- count++;
- }
-
- // Add the occupant's origin to the leakfile.
- Vector origin;
- GetVectorForKey (node->occupant, "origin", origin);
-
- fprintf (linefile, "%f %f %f\n", origin[0], origin[1], origin[2]);
- qprintf ("%5i point linefile\n", count+1);
-
- fclose (linefile);
-
- // Emit a leak warning.
- const char *cl = ValueForKey (node->occupant, "classname");
- Color red(255,0,0,255);
- ColorSpewMessage( SPEW_MESSAGE, &red, "Entity %s (%.2f %.2f %.2f) leaked!\n", cl, origin[0], origin[1], origin[2] );
-}
-
-void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart )
-{
- Vector mid;
- FILE *linefile;
- char filename[1024];
- node_t *node;
- int count;
-
- // wrote a leak line file already, don't overwrite it with the areaportal leak file
- if ( tree->leaked )
- return;
-
- tree->leaked = true;
- qprintf ("--- LeakFile ---\n");
-
- //
- // write the points to the file
- //
- sprintf (filename, "%s.lin", source);
- linefile = fopen (filename, "w");
- if (!linefile)
- Error ("Couldn't open %s\n", filename);
-
- count = 2;
- WindingCenter (pEndPortal->winding, mid);
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
- mid = 0.5 * (pStart->mins + pStart->maxs);
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
-
- node = pStart;
- while (node->occupied >= 1)
- {
- portal_t *nextportal = NULL;
- node_t *nextnode = NULL;
- int s = 0;
-
- // find the best portal exit
- int next = node->occupied;
- for (portal_t *p=node->portals ; p ; p = p->next[!s])
- {
- s = (p->nodes[0] == node);
- if (p->nodes[s]->occupied
- && p->nodes[s]->occupied < next)
- {
- nextportal = p;
- nextnode = p->nodes[s];
- next = nextnode->occupied;
- }
- }
- if ( !nextnode )
- break;
- node = nextnode;
- WindingCenter (nextportal->winding, mid);
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
- count++;
- }
- // add the occupant center
- if ( node )
- {
- mid = 0.5 * (node->mins + node->maxs);
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
- count++;
- }
- WindingCenter (pStartPortal->winding, mid);
- count++;
- fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
-
- qprintf ("%5i point linefile\n", count);
-
- fclose (linefile);
- Warning( "Wrote %s\n", filename );
- Color red(255,0,0,255);
- ColorSpewMessage( SPEW_MESSAGE, &red, "Areaportal leak ! File: %s ", filename );
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "color.h"
+
+/*
+==============================================================================
+
+LEAF FILE GENERATION
+
+Save out name.line for qe3 to read
+==============================================================================
+*/
+
+
+/*
+=============
+LeakFile
+
+Finds the shortest possible chain of portals
+that leads from the outside leaf to a specifically
+occupied leaf
+=============
+*/
+void LeakFile (tree_t *tree)
+{
+ Vector mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ if (!tree->outside_node.occupied)
+ return;
+
+ tree->leaked = true;
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 0;
+ node = &tree->outside_node;
+ while (node->occupied > 1)
+ {
+ portal_t *nextportal = NULL;
+ node_t *nextnode = NULL;
+ int s = 0;
+
+ // find the best portal exit
+ int next = node->occupied;
+ for (portal_t *p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+
+ // Add the occupant's origin to the leakfile.
+ Vector origin;
+ GetVectorForKey (node->occupant, "origin", origin);
+
+ fprintf (linefile, "%f %f %f\n", origin[0], origin[1], origin[2]);
+ qprintf ("%5i point linefile\n", count+1);
+
+ fclose (linefile);
+
+ // Emit a leak warning.
+ const char *cl = ValueForKey (node->occupant, "classname");
+ Color red(255,0,0,255);
+ ColorSpewMessage( SPEW_MESSAGE, &red, "Entity %s (%.2f %.2f %.2f) leaked!\n", cl, origin[0], origin[1], origin[2] );
+}
+
+void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart )
+{
+ Vector mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ // wrote a leak line file already, don't overwrite it with the areaportal leak file
+ if ( tree->leaked )
+ return;
+
+ tree->leaked = true;
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 2;
+ WindingCenter (pEndPortal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ mid = 0.5 * (pStart->mins + pStart->maxs);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+
+ node = pStart;
+ while (node->occupied >= 1)
+ {
+ portal_t *nextportal = NULL;
+ node_t *nextnode = NULL;
+ int s = 0;
+
+ // find the best portal exit
+ int next = node->occupied;
+ for (portal_t *p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ if ( !nextnode )
+ break;
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ // add the occupant center
+ if ( node )
+ {
+ mid = 0.5 * (node->mins + node->maxs);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ WindingCenter (pStartPortal->winding, mid);
+ count++;
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+
+ qprintf ("%5i point linefile\n", count);
+
+ fclose (linefile);
+ Warning( "Wrote %s\n", filename );
+ Color red(255,0,0,255);
+ ColorSpewMessage( SPEW_MESSAGE, &red, "Areaportal leak ! File: %s ", filename );
} \ No newline at end of file
diff --git a/mp/src/utils/vbsp/manifest.cpp b/mp/src/utils/vbsp/manifest.cpp
index 44dd07b8..c72a9564 100644
--- a/mp/src/utils/vbsp/manifest.cpp
+++ b/mp/src/utils/vbsp/manifest.cpp
@@ -1,568 +1,568 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-#include "vbsp.h"
-#include "map_shared.h"
-#include "fgdlib/fgdlib.h"
-#include "manifest.h"
-#include "windows.h"
-
-//-----------------------------------------------------------------------------
-// Purpose: default constructor
-//-----------------------------------------------------------------------------
-CManifestMap::CManifestMap( void )
-{
- m_RelativeMapFileName[ 0 ] = 0;
- m_bTopLevelMap = false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: default constructor
-//-----------------------------------------------------------------------------
-CManifest::CManifest( void )
-{
- m_InstancePath[ 0 ] = 0;
- m_bIsCordoning = false;
- m_CordoningMapEnt = NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will parse through the known keys for the manifest map entry
-// Input : szKey - the key name
-// szValue - the value
-// pManifestMap - the manifest map this belongs to
-// Output : ChunkFileResult_t - result of the parsing
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap )
-{
- if ( !stricmp( szKey, "Name" ) )
- {
- // pManifestMap->m_FriendlyName = szValue;
- }
- else if ( !stricmp( szKey, "File" ) )
- {
- strcpy( pManifestMap->m_RelativeMapFileName, szValue );
- }
- else if ( !stricmp( szKey, "IsPrimary" ) )
- {
- // pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 );
- }
- else if ( !stricmp( szKey, "IsProtected" ) )
- {
- // pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 );
- }
- else if ( !stricmp( szKey, "TopLevel" ) )
- {
- pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 );
- }
-
- return ChunkFile_Ok;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function is responsible for setting up the manifest map about to be read in
-// Input : pFile - the chunk file being read
-// pDoc - the owning manifest document
-// Output : ChunkFileResult_t - result of the parsing
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest )
-{
- CManifestMap *pManifestMap = new CManifestMap();
-
- pManifest->m_Maps.AddToTail( pManifestMap );
-
- ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap );
-
- return( eResult );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will load the VMF chunk
-// Input : pFile - the chunk file being read
-// pDoc - the owning manifest document
-// Output : ChunkFileResult_t - result of the parsing
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest )
-{
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest );
- pFile->PushHandlers(&Handlers);
-
- ChunkFileResult_t eResult = ChunkFile_Ok;
-
- eResult = pFile->ReadChunk();
-
- pFile->PopHandlers();
-
- return( eResult );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon )
-{
- // Add a box to this cordon.
- pCordon->m_Boxes.AddToTail();
- BoundBox &box = pCordon->m_Boxes.Tail();
-
- // Fill it in with the data from the VMF.
- return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox )
-{
- if (!stricmp(szKey, "mins"))
- {
- CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins);
- }
- else if (!stricmp(szKey, "maxs"))
- {
- CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs);
- }
-
- return ChunkFile_Ok;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon )
-{
- if (!stricmp(szKey, "name"))
- {
- pCordon->m_szName.Set( szValue );
- }
- // Whether this particular cordon volume is active.
- else if (!stricmp(szKey, "active"))
- {
- CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive);
- }
-
- return ChunkFile_Ok;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest )
-{
- // Add a new cordon which will be filled in by the key callback
- pManifest->m_Cordons.AddToTail();
- Cordon_t &cordon = pManifest->m_Cordons.Tail();
-
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon );
-
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon );
- pFile->PopHandlers();
-
- return(eResult);
-}
-
-
-//-----------------------------------------------------------------------------------------------------------
-// Parses keys that are applicable to all cordons in the map.
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest )
-{
- // Whether the cordoning system is enabled or disabled.
- if ( !stricmp( szKey, "active" ) )
- {
- CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning );
- }
-
- return ChunkFile_Ok;
-}
-
-
-//-----------------------------------------------------------------------------
-// Parses the VMF chunk that pertains to all the cordons in the map:
-//
-// cordons
-// {
-// "active" "true"
-// cordon
-// {
-// "active" "true"
-// "box"
-// {
-// "mins" "-1024, -1024, -1024"
-// "maxs" "1024, 1024, 1024"
-// }
-// ...may be more boxes...
-// }
-// ...may be more cordons...
-// }
-//
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest )
-{
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest );
-
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest );
- pFile->PopHandlers();
-
- return(eResult);
-}
-
-extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
-
-ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc )
-{
- pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities];
- g_MainMap->num_entities++;
- memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) );
- pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes;
- pDoc->m_CordoningMapEnt->numbrushes = 0;
-
- LoadEntity_t LoadEntity;
- LoadEntity.pEntity = pDoc->m_CordoningMapEnt;
-
- // No default flags/contents
- LoadEntity.nBaseFlags = 0;
- LoadEntity.nBaseContents = 0;
-
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc );
- Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
- pFile->PushHandlers(&Handlers);
-
- ChunkFileResult_t eResult = ChunkFile_Ok;
-
- eResult = pFile->ReadChunk();
-
- pFile->PopHandlers();
-
- return( eResult );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will create a new entity pair
-// Input : pKey - the key of the pair
-// pValue - the value of the pair
-// Output : returns a newly created epair structure
-//-----------------------------------------------------------------------------
-epair_t *CManifest::CreateEPair( char *pKey, char *pValue )
-{
- epair_t *pEPair = new epair_t;
-
- pEPair->key = new char[ strlen( pKey ) + 1 ];
- pEPair->value = new char[ strlen( pValue ) + 1 ];
-
- strcpy( pEPair->key, pKey );
- strcpy( pEPair->value, pValue );
-
- return pEPair;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will load in all of the submaps belonging to this manifest,
-// except for the top level map, which is loaded separately.
-// Input : pMapFile - the top level map that was previously loaded
-// pszFileName - the absolute file name of the top level map file
-// Output : returns true if all submaps were loaded
-//-----------------------------------------------------------------------------
-bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName )
-{
- entity_t *InstanceEntity;
- epair_t *pEPair;
-
- InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
- pMapFile->num_entities++;
- memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
-
- InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
- pEPair = CreateEPair( "classname", "worldspawn" );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
-
- for( int i = 0; i < m_Maps.Count(); i++ )
- {
- // if ( m_Maps[ i ]->m_bTopLevelMap == false )
- {
- char FileName[ MAX_PATH ];
-
- sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName );
-
- InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
- pMapFile->num_entities++;
-
- memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
- InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
-
- pEPair = CreateEPair( "angles", "0 0 0" );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
-
- char temp[ 128 ];
- sprintf( temp, "%d", GameData::NAME_FIXUP_NONE );
-
- pEPair = CreateEPair( "fixup_style", temp );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
-
- pEPair = CreateEPair( "classname", "func_instance" );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
-
- pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
-
- if ( m_Maps[ i ]->m_bTopLevelMap == true )
- {
- pEPair = CreateEPair( "toplevel", "1" );
- pEPair->next = InstanceEntity->epairs;
- InstanceEntity->epairs = pEPair;
- }
- }
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName )
-{
- char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ];
- DWORD UserNameSize;
-
- UserNameSize = sizeof( UserName );
- if ( GetUserName( UserName, &UserNameSize ) == 0 )
- {
- strcpy( UserPrefsFileName, "default" );
- }
-
- sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName );
- V_StripExtension( pszFileName, FileName, sizeof( FileName ) );
- strcat( FileName, UserPrefsFileName );
-
- FILE *fp = fopen( FileName, "rb" );
- if ( !fp )
- {
- return false;
- }
-
- CChunkFile File;
- ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read );
-
- if ( eResult == ChunkFile_Ok )
- {
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this );
-
- // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this);
-
- File.PushHandlers(&Handlers);
-
- while( eResult == ChunkFile_Ok )
- {
- eResult = File.ReadChunk();
- }
-
- if ( eResult == ChunkFile_EOF )
- {
- eResult = ChunkFile_Ok;
- }
-
- File.PopHandlers();
- }
-
- if ( eResult == ChunkFile_Ok )
- {
- }
- else
- {
- // no pref message for now
- // GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION );
- }
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Loads a .VMM file.
-// Input : pszFileName - Full path of the map file to load.
-//-----------------------------------------------------------------------------
-bool CManifest::LoadVMFManifest( const char *pszFileName )
-{
- V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) );
- strcat( m_InstancePath, "\\" );
-
- CChunkFile File;
- ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read );
- if ( eResult != ChunkFile_Ok )
- {
- g_MapError.ReportError( File.GetErrorText( eResult ) );
- return false;
- }
-
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this );
-
- File.PushHandlers(&Handlers);
-
- while (eResult == ChunkFile_Ok)
- {
- eResult = File.ReadChunk();
- }
-
- if (eResult == ChunkFile_EOF)
- {
- eResult = ChunkFile_Ok;
- }
-
- File.PopHandlers();
-
- if ( eResult == ChunkFile_Ok )
- {
- int index = g_Maps.AddToTail( new CMapFile() );
- g_LoadingMap = g_Maps[ index ];
- if ( g_MainMap == NULL )
- {
- g_MainMap = g_LoadingMap;
- }
-
- LoadSubMaps( g_LoadingMap, pszFileName );
-
- LoadVMFManifestUserPrefs( pszFileName );
- }
-
- return ( eResult == ChunkFile_Ok );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CManifest::CordonWorld( )
-{
- if ( m_bIsCordoning == false )
- {
- return;
- }
-
- for ( int i = 0; i < g_MainMap->num_entities; i++ )
- {
- if ( i == 0 )
- { // for world spawn, we look at brushes
- for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ )
- {
- int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum;
-
- bool bRemove = true;
-
- for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
- {
- if ( m_Cordons[ nCordon ].m_bActive == false )
- {
- continue;
- }
-
- for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
- {
- if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true )
- {
- bRemove = false;
- break;
- }
- }
-
- if ( bRemove == false )
- {
- break;
- }
- }
-
- if ( bRemove )
- {
- int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] );
- memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize );
- g_MainMap->entities[ i ].numbrushes--;
- nBrushNum--;
- }
- }
- }
- else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt )
- { // for all other entities, even if they include brushes, we look at origin
- if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL )
- {
- continue;
- }
-
- bool bRemove = true;
-
- for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
- {
- if ( m_Cordons[ nCordon ].m_bActive == false )
- {
- continue;
- }
-
- for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
- {
- if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true )
- {
- bRemove = false;
- break;
- }
- }
-
- if ( bRemove == false )
- {
- break;
- }
- }
-
- if ( bRemove )
- {
- g_MainMap->entities[ i ].numbrushes = 0;
- g_MainMap->entities[ i ].epairs = NULL;
- }
- }
- }
-
- if ( m_CordoningMapEnt )
- {
- g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt );
- m_CordoningMapEnt->numbrushes = 0;
- m_CordoningMapEnt->epairs = NULL;
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "vbsp.h"
+#include "map_shared.h"
+#include "fgdlib/fgdlib.h"
+#include "manifest.h"
+#include "windows.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: default constructor
+//-----------------------------------------------------------------------------
+CManifestMap::CManifestMap( void )
+{
+ m_RelativeMapFileName[ 0 ] = 0;
+ m_bTopLevelMap = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: default constructor
+//-----------------------------------------------------------------------------
+CManifest::CManifest( void )
+{
+ m_InstancePath[ 0 ] = 0;
+ m_bIsCordoning = false;
+ m_CordoningMapEnt = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will parse through the known keys for the manifest map entry
+// Input : szKey - the key name
+// szValue - the value
+// pManifestMap - the manifest map this belongs to
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap )
+{
+ if ( !stricmp( szKey, "Name" ) )
+ {
+ // pManifestMap->m_FriendlyName = szValue;
+ }
+ else if ( !stricmp( szKey, "File" ) )
+ {
+ strcpy( pManifestMap->m_RelativeMapFileName, szValue );
+ }
+ else if ( !stricmp( szKey, "IsPrimary" ) )
+ {
+ // pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 );
+ }
+ else if ( !stricmp( szKey, "IsProtected" ) )
+ {
+ // pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 );
+ }
+ else if ( !stricmp( szKey, "TopLevel" ) )
+ {
+ pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 );
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function is responsible for setting up the manifest map about to be read in
+// Input : pFile - the chunk file being read
+// pDoc - the owning manifest document
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CManifestMap *pManifestMap = new CManifestMap();
+
+ pManifest->m_Maps.AddToTail( pManifestMap );
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap );
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will load the VMF chunk
+// Input : pFile - the chunk file being read
+// pDoc - the owning manifest document
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest );
+ pFile->PushHandlers(&Handlers);
+
+ ChunkFileResult_t eResult = ChunkFile_Ok;
+
+ eResult = pFile->ReadChunk();
+
+ pFile->PopHandlers();
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon )
+{
+ // Add a box to this cordon.
+ pCordon->m_Boxes.AddToTail();
+ BoundBox &box = pCordon->m_Boxes.Tail();
+
+ // Fill it in with the data from the VMF.
+ return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox )
+{
+ if (!stricmp(szKey, "mins"))
+ {
+ CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins);
+ }
+ else if (!stricmp(szKey, "maxs"))
+ {
+ CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon )
+{
+ if (!stricmp(szKey, "name"))
+ {
+ pCordon->m_szName.Set( szValue );
+ }
+ // Whether this particular cordon volume is active.
+ else if (!stricmp(szKey, "active"))
+ {
+ CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ // Add a new cordon which will be filled in by the key callback
+ pManifest->m_Cordons.AddToTail();
+ Cordon_t &cordon = pManifest->m_Cordons.Tail();
+
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon );
+
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon );
+ pFile->PopHandlers();
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------------------------------------
+// Parses keys that are applicable to all cordons in the map.
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest )
+{
+ // Whether the cordoning system is enabled or disabled.
+ if ( !stricmp( szKey, "active" ) )
+ {
+ CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning );
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses the VMF chunk that pertains to all the cordons in the map:
+//
+// cordons
+// {
+// "active" "true"
+// cordon
+// {
+// "active" "true"
+// "box"
+// {
+// "mins" "-1024, -1024, -1024"
+// "maxs" "1024, 1024, 1024"
+// }
+// ...may be more boxes...
+// }
+// ...may be more cordons...
+// }
+//
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest );
+
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest );
+ pFile->PopHandlers();
+
+ return(eResult);
+}
+
+extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc )
+{
+ pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities];
+ g_MainMap->num_entities++;
+ memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) );
+ pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes;
+ pDoc->m_CordoningMapEnt->numbrushes = 0;
+
+ LoadEntity_t LoadEntity;
+ LoadEntity.pEntity = pDoc->m_CordoningMapEnt;
+
+ // No default flags/contents
+ LoadEntity.nBaseFlags = 0;
+ LoadEntity.nBaseContents = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc );
+ Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
+ pFile->PushHandlers(&Handlers);
+
+ ChunkFileResult_t eResult = ChunkFile_Ok;
+
+ eResult = pFile->ReadChunk();
+
+ pFile->PopHandlers();
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will create a new entity pair
+// Input : pKey - the key of the pair
+// pValue - the value of the pair
+// Output : returns a newly created epair structure
+//-----------------------------------------------------------------------------
+epair_t *CManifest::CreateEPair( char *pKey, char *pValue )
+{
+ epair_t *pEPair = new epair_t;
+
+ pEPair->key = new char[ strlen( pKey ) + 1 ];
+ pEPair->value = new char[ strlen( pValue ) + 1 ];
+
+ strcpy( pEPair->key, pKey );
+ strcpy( pEPair->value, pValue );
+
+ return pEPair;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will load in all of the submaps belonging to this manifest,
+// except for the top level map, which is loaded separately.
+// Input : pMapFile - the top level map that was previously loaded
+// pszFileName - the absolute file name of the top level map file
+// Output : returns true if all submaps were loaded
+//-----------------------------------------------------------------------------
+bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName )
+{
+ entity_t *InstanceEntity;
+ epair_t *pEPair;
+
+ InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
+ pMapFile->num_entities++;
+ memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
+
+ InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
+ pEPair = CreateEPair( "classname", "worldspawn" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ for( int i = 0; i < m_Maps.Count(); i++ )
+ {
+ // if ( m_Maps[ i ]->m_bTopLevelMap == false )
+ {
+ char FileName[ MAX_PATH ];
+
+ sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName );
+
+ InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
+ pMapFile->num_entities++;
+
+ memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
+ InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
+
+ pEPair = CreateEPair( "angles", "0 0 0" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ char temp[ 128 ];
+ sprintf( temp, "%d", GameData::NAME_FIXUP_NONE );
+
+ pEPair = CreateEPair( "fixup_style", temp );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ pEPair = CreateEPair( "classname", "func_instance" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ if ( m_Maps[ i ]->m_bTopLevelMap == true )
+ {
+ pEPair = CreateEPair( "toplevel", "1" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName )
+{
+ char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ];
+ DWORD UserNameSize;
+
+ UserNameSize = sizeof( UserName );
+ if ( GetUserName( UserName, &UserNameSize ) == 0 )
+ {
+ strcpy( UserPrefsFileName, "default" );
+ }
+
+ sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName );
+ V_StripExtension( pszFileName, FileName, sizeof( FileName ) );
+ strcat( FileName, UserPrefsFileName );
+
+ FILE *fp = fopen( FileName, "rb" );
+ if ( !fp )
+ {
+ return false;
+ }
+
+ CChunkFile File;
+ ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read );
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this );
+
+ // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this);
+
+ File.PushHandlers(&Handlers);
+
+ while( eResult == ChunkFile_Ok )
+ {
+ eResult = File.ReadChunk();
+ }
+
+ if ( eResult == ChunkFile_EOF )
+ {
+ eResult = ChunkFile_Ok;
+ }
+
+ File.PopHandlers();
+ }
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ }
+ else
+ {
+ // no pref message for now
+ // GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads a .VMM file.
+// Input : pszFileName - Full path of the map file to load.
+//-----------------------------------------------------------------------------
+bool CManifest::LoadVMFManifest( const char *pszFileName )
+{
+ V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) );
+ strcat( m_InstancePath, "\\" );
+
+ CChunkFile File;
+ ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read );
+ if ( eResult != ChunkFile_Ok )
+ {
+ g_MapError.ReportError( File.GetErrorText( eResult ) );
+ return false;
+ }
+
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this );
+
+ File.PushHandlers(&Handlers);
+
+ while (eResult == ChunkFile_Ok)
+ {
+ eResult = File.ReadChunk();
+ }
+
+ if (eResult == ChunkFile_EOF)
+ {
+ eResult = ChunkFile_Ok;
+ }
+
+ File.PopHandlers();
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ int index = g_Maps.AddToTail( new CMapFile() );
+ g_LoadingMap = g_Maps[ index ];
+ if ( g_MainMap == NULL )
+ {
+ g_MainMap = g_LoadingMap;
+ }
+
+ LoadSubMaps( g_LoadingMap, pszFileName );
+
+ LoadVMFManifestUserPrefs( pszFileName );
+ }
+
+ return ( eResult == ChunkFile_Ok );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CManifest::CordonWorld( )
+{
+ if ( m_bIsCordoning == false )
+ {
+ return;
+ }
+
+ for ( int i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ if ( i == 0 )
+ { // for world spawn, we look at brushes
+ for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ )
+ {
+ int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum;
+
+ bool bRemove = true;
+
+ for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
+ {
+ if ( m_Cordons[ nCordon ].m_bActive == false )
+ {
+ continue;
+ }
+
+ for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
+ {
+ if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true )
+ {
+ bRemove = false;
+ break;
+ }
+ }
+
+ if ( bRemove == false )
+ {
+ break;
+ }
+ }
+
+ if ( bRemove )
+ {
+ int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] );
+ memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize );
+ g_MainMap->entities[ i ].numbrushes--;
+ nBrushNum--;
+ }
+ }
+ }
+ else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt )
+ { // for all other entities, even if they include brushes, we look at origin
+ if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL )
+ {
+ continue;
+ }
+
+ bool bRemove = true;
+
+ for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
+ {
+ if ( m_Cordons[ nCordon ].m_bActive == false )
+ {
+ continue;
+ }
+
+ for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
+ {
+ if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true )
+ {
+ bRemove = false;
+ break;
+ }
+ }
+
+ if ( bRemove == false )
+ {
+ break;
+ }
+ }
+
+ if ( bRemove )
+ {
+ g_MainMap->entities[ i ].numbrushes = 0;
+ g_MainMap->entities[ i ].epairs = NULL;
+ }
+ }
+ }
+
+ if ( m_CordoningMapEnt )
+ {
+ g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt );
+ m_CordoningMapEnt->numbrushes = 0;
+ m_CordoningMapEnt->epairs = NULL;
+ }
+}
diff --git a/mp/src/utils/vbsp/manifest.h b/mp/src/utils/vbsp/manifest.h
index f3b915a1..e7b801e1 100644
--- a/mp/src/utils/vbsp/manifest.h
+++ b/mp/src/utils/vbsp/manifest.h
@@ -1,73 +1,73 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=====================================================================================//
-
-#ifndef __MANIFEST_H
-#define __MANIFEST_H
-
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "boundbox.h"
-
-//
-// Each cordon is a named collection of bounding boxes.
-//
-struct Cordon_t
-{
- inline Cordon_t()
- {
- m_bActive = false;
- }
-
- CUtlString m_szName;
- bool m_bActive; // True means cull using this cordon when cordoning is enabled.
- CUtlVector<BoundBox> m_Boxes;
-};
-
-class CManifestMap
-{
-public:
- CManifestMap( void );
- char m_RelativeMapFileName[ MAX_PATH ];
- bool m_bTopLevelMap;
-};
-
-class CManifest
-{
-public:
- CManifest( void );
-
- static ChunkFileResult_t LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap );
- static ChunkFileResult_t LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest );
- static ChunkFileResult_t LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest );
- static ChunkFileResult_t LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon );
- static ChunkFileResult_t LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox );
- static ChunkFileResult_t LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon );
- static ChunkFileResult_t LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest );
- static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest );
- static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest );
- static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest );
-
- bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName );
- epair_t *CreateEPair( char *pKey, char *pValue );
- bool LoadVMFManifest( const char *pszFileName );
- const char *GetInstancePath( ) { return m_InstancePath; }
-
- void CordonWorld( );
-
-private:
- bool LoadVMFManifestUserPrefs( const char *pszFileName );
-
-
- CUtlVector< CManifestMap * > m_Maps;
- char m_InstancePath[ MAX_PATH ];
- bool m_bIsCordoning;
- CUtlVector< Cordon_t > m_Cordons;
- entity_t *m_CordoningMapEnt;
-};
-
-#endif // #ifndef __MANIFEST_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#ifndef __MANIFEST_H
+#define __MANIFEST_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "boundbox.h"
+
+//
+// Each cordon is a named collection of bounding boxes.
+//
+struct Cordon_t
+{
+ inline Cordon_t()
+ {
+ m_bActive = false;
+ }
+
+ CUtlString m_szName;
+ bool m_bActive; // True means cull using this cordon when cordoning is enabled.
+ CUtlVector<BoundBox> m_Boxes;
+};
+
+class CManifestMap
+{
+public:
+ CManifestMap( void );
+ char m_RelativeMapFileName[ MAX_PATH ];
+ bool m_bTopLevelMap;
+};
+
+class CManifest
+{
+public:
+ CManifest( void );
+
+ static ChunkFileResult_t LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap );
+ static ChunkFileResult_t LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon );
+ static ChunkFileResult_t LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox );
+ static ChunkFileResult_t LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon );
+ static ChunkFileResult_t LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest );
+
+ bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName );
+ epair_t *CreateEPair( char *pKey, char *pValue );
+ bool LoadVMFManifest( const char *pszFileName );
+ const char *GetInstancePath( ) { return m_InstancePath; }
+
+ void CordonWorld( );
+
+private:
+ bool LoadVMFManifestUserPrefs( const char *pszFileName );
+
+
+ CUtlVector< CManifestMap * > m_Maps;
+ char m_InstancePath[ MAX_PATH ];
+ bool m_bIsCordoning;
+ CUtlVector< Cordon_t > m_Cordons;
+ entity_t *m_CordoningMapEnt;
+};
+
+#endif // #ifndef __MANIFEST_H
diff --git a/mp/src/utils/vbsp/map.cpp b/mp/src/utils/vbsp/map.cpp
index 34219bd4..a5456c32 100644
--- a/mp/src/utils/vbsp/map.cpp
+++ b/mp/src/utils/vbsp/map.cpp
@@ -1,3304 +1,3304 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "map_shared.h"
-#include "disp_vbsp.h"
-#include "tier1/strtools.h"
-#include "builddisp.h"
-#include "tier0/icommandline.h"
-#include "KeyValues.h"
-#include "materialsub.h"
-#include "fgdlib/fgdlib.h"
-#include "manifest.h"
-
-#ifdef VSVMFIO
-#include "VmfImport.h"
-#endif // VSVMFIO
-
-
-// undefine to make plane finding use linear sort
-#define USE_HASHING
-
-#define RENDER_NORMAL_EPSILON 0.00001
-#define RENDER_DIST_EPSILON 0.01f
-
-#define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same
- // as clip epsilon, but it is 0.1f and I
- // currently don't know how that number was
- // come to (cab) - this is 0.01 of an inch
- // for clipping brush solids
-struct LoadSide_t
-{
- mapbrush_t *pBrush;
- side_t *pSide;
- int nSideIndex;
- int nBaseFlags;
- int nBaseContents;
- Vector planepts[3];
- brush_texture_t td;
-};
-
-
-extern qboolean onlyents;
-
-
-CUtlVector< CMapFile * > g_Maps;
-CMapFile *g_MainMap = NULL;
-CMapFile *g_LoadingMap = NULL;
-
-char CMapFile::m_InstancePath[ MAX_PATH ] = "";
-int CMapFile::m_InstanceCount = 0;
-int CMapFile::c_areaportals = 0;
-
-void CMapFile::Init( void )
-{
- entity_num = 0;
- num_entities = 0;
-
- nummapplanes = 0;
- memset( mapplanes, 0, sizeof( mapplanes ) );
-
- nummapbrushes = 0;
- memset( mapbrushes, 0, sizeof( mapbrushes ) );
-
- nummapbrushsides = 0;
- memset( brushsides, 0, sizeof( brushsides ) );
-
- memset( side_brushtextures, 0, sizeof( side_brushtextures ) );
-
- memset( planehash, 0, sizeof( planehash ) );
-
- m_ConnectionPairs = NULL;
-
- m_StartMapOverlays = g_aMapOverlays.Count();
- m_StartMapWaterOverlays = g_aMapWaterOverlays.Count();
-
- c_boxbevels = 0;
- c_edgebevels = 0;
- c_clipbrushes = 0;
- g_ClipTexinfo = -1;
-}
-
-
-// All the brush sides referenced by info_no_dynamic_shadow entities.
-CUtlVector<int> g_NoDynamicShadowSides;
-
-
-void TestExpandBrushes (void);
-
-ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo );
-ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-
-#ifdef VSVMFIO
-ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
-ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
-#endif // VSVMFIO
-
-ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
-ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
-
-ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
-ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
-
-ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
-ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush);
-
-ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
-ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo);
-
-
-
-/*
-=============================================================================
-
-PLANE FINDING
-
-=============================================================================
-*/
-
-
-/*
-=================
-PlaneTypeForNormal
-=================
-*/
-int PlaneTypeForNormal (Vector& normal)
-{
- vec_t ax, ay, az;
-
-// NOTE: should these have an epsilon around 1.0?
- if (normal[0] == 1.0 || normal[0] == -1.0)
- return PLANE_X;
- if (normal[1] == 1.0 || normal[1] == -1.0)
- return PLANE_Y;
- if (normal[2] == 1.0 || normal[2] == -1.0)
- return PLANE_Z;
-
- ax = fabs(normal[0]);
- ay = fabs(normal[1]);
- az = fabs(normal[2]);
-
- if (ax >= ay && ax >= az)
- return PLANE_ANYX;
- if (ay >= ax && ay >= az)
- return PLANE_ANYY;
- return PLANE_ANYZ;
-}
-
-/*
-================
-PlaneEqual
-================
-*/
-qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon)
-{
-#if 1
- if (
- fabs(p->normal[0] - normal[0]) < normalEpsilon
- && fabs(p->normal[1] - normal[1]) < normalEpsilon
- && fabs(p->normal[2] - normal[2]) < normalEpsilon
- && fabs(p->dist - dist) < distEpsilon )
- return true;
-#else
- if (p->normal[0] == normal[0]
- && p->normal[1] == normal[1]
- && p->normal[2] == normal[2]
- && p->dist == dist)
- return true;
-#endif
- return false;
-}
-
-/*
-================
-AddPlaneToHash
-================
-*/
-void CMapFile::AddPlaneToHash (plane_t *p)
-{
- int hash;
-
- hash = (int)fabs(p->dist) / 8;
- hash &= (PLANE_HASHES-1);
-
- p->hash_chain = planehash[hash];
- planehash[hash] = p;
-}
-
-/*
-================
-CreateNewFloatPlane
-================
-*/
-int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist)
-{
- plane_t *p, temp;
-
- if (VectorLength(normal) < 0.5)
- g_MapError.ReportError ("FloatPlane: bad normal");
- // create a new plane
- if (nummapplanes+2 > MAX_MAP_PLANES)
- g_MapError.ReportError ("MAX_MAP_PLANES");
-
- p = &mapplanes[nummapplanes];
- VectorCopy (normal, p->normal);
- p->dist = dist;
- p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
-
- VectorSubtract (vec3_origin, normal, (p+1)->normal);
- (p+1)->dist = -dist;
-
- nummapplanes += 2;
-
- // allways put axial planes facing positive first
- if (p->type < 3)
- {
- if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
- {
- // flip order
- temp = *p;
- *p = *(p+1);
- *(p+1) = temp;
-
- AddPlaneToHash (p);
- AddPlaneToHash (p+1);
- return nummapplanes - 1;
- }
- }
-
- AddPlaneToHash (p);
- AddPlaneToHash (p+1);
- return nummapplanes - 2;
-}
-
-
-/*
-==============
-SnapVector
-==============
-*/
-bool SnapVector (Vector& normal)
-{
- int i;
-
- for (i=0 ; i<3 ; i++)
- {
- if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON )
- {
- VectorClear (normal);
- normal[i] = 1;
- return true;
- }
-
- if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON )
- {
- VectorClear (normal);
- normal[i] = -1;
- return true;
- }
- }
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
-// Rounds dist to integer if it is within an epsilon of integer.
-// Input : normal - Plane normal vector (assumed to be unit length).
-// dist - Plane constant.
-//-----------------------------------------------------------------------------
-void SnapPlane(Vector &normal, vec_t &dist)
-{
- SnapVector(normal);
-
- if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
- {
- dist = RoundInt(dist);
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
-// Recalculates dist if the normal was snapped. Rounds dist to integer
-// if it is within an epsilon of integer.
-// Input : normal - Plane normal vector (assumed to be unit length).
-// dist - Plane constant.
-// p0, p1, p2 - Three points on the plane.
-//-----------------------------------------------------------------------------
-void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2)
-{
- if (SnapVector(normal))
- {
- //
- // Calculate a new plane constant using the snapped normal. Use the
- // centroid of the three plane points to minimize error. This is like
- // rotating the plane around the centroid.
- //
- Vector p3 = (p0 + p1 + p2) / 3.0f;
- dist = normal.Dot(p3);
- if ( g_snapAxialPlanes )
- {
- dist = RoundInt(dist);
- }
- }
-
- if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
- {
- dist = RoundInt(dist);
- }
-}
-
-
-/*
-=============
-FindFloatPlane
-
-=============
-*/
-#ifndef USE_HASHING
-int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
-{
- int i;
- plane_t *p;
-
- SnapPlane(normal, dist);
- for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
- {
- if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
- return i;
- }
-
- return CreateNewFloatPlane (normal, dist);
-}
-#else
-int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
-{
- int i;
- plane_t *p;
- int hash, h;
-
- SnapPlane(normal, dist);
- hash = (int)fabs(dist) / 8;
- hash &= (PLANE_HASHES-1);
-
- // search the border bins as well
- for (i=-1 ; i<=1 ; i++)
- {
- h = (hash+i)&(PLANE_HASHES-1);
- for (p = planehash[h] ; p ; p=p->hash_chain)
- {
- if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
- return p-mapplanes;
- }
- }
-
- return CreateNewFloatPlane (normal, dist);
-}
-#endif
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Builds a plane normal and distance from three points on the plane.
-// If the normal is nearly axial, it will be snapped to be axial. Looks
-// up the plane in the unique planes.
-// Input : p0, p1, p2 - Three points on the plane.
-// Output : Returns the index of the plane in the planes list.
-//-----------------------------------------------------------------------------
-int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2)
-{
- Vector t1, t2, normal;
- vec_t dist;
-
- VectorSubtract (p0, p1, t1);
- VectorSubtract (p2, p1, t2);
- CrossProduct (t1, t2, normal);
- VectorNormalize (normal);
-
- dist = DotProduct (p0, normal);
-
- SnapPlane(normal, dist, p0, p1, p2);
-
- return FindFloatPlane (normal, dist);
-}
-
-
-/*
-===========
-BrushContents
-===========
-*/
-int BrushContents (mapbrush_t *b)
-{
- int contents;
- int unionContents = 0;
- side_t *s;
- int i;
-
- s = &b->original_sides[0];
- contents = s->contents;
- unionContents = contents;
- for (i=1 ; i<b->numsides ; i++, s++)
- {
- s = &b->original_sides[i];
-
- unionContents |= s->contents;
-#if 0
- if (s->contents != contents)
- {
- Msg("Brush %i: mixed face contents\n", b->id);
- break;
- }
-#endif
- }
-
- // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface
- int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME);
- if ( transparentContents )
- {
- contents |= transparentContents | CONTENTS_TRANSLUCENT;
- contents &= ~CONTENTS_SOLID;
- }
-
- return contents;
-}
-
-
-//============================================================================
-
-bool IsAreaPortal( char const *pClassName )
-{
- // If the class name starts with "func_areaportal", then it's considered an area portal.
- char const *pBaseName = "func_areaportal";
- char const *pCur = pBaseName;
- while( *pCur && *pClassName )
- {
- if( *pCur != *pClassName )
- break;
-
- ++pCur;
- ++pClassName;
- }
-
- return *pCur == 0;
-}
-
-
-/*
-=================
-AddBrushBevels
-
-Adds any additional planes necessary to allow the brush to be expanded
-against axial bounding boxes
-=================
-*/
-void CMapFile::AddBrushBevels (mapbrush_t *b)
-{
- int axis, dir;
- int i, j, k, l, order;
- side_t sidetemp;
- brush_texture_t tdtemp;
- side_t *s, *s2;
- Vector normal;
- float dist;
- winding_t *w, *w2;
- Vector vec, vec2;
- float d;
-
- //
- // add the axial planes
- //
- order = 0;
- for (axis=0 ; axis <3 ; axis++)
- {
- for (dir=-1 ; dir <= 1 ; dir+=2, order++)
- {
- // see if the plane is allready present
- for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
- {
- if (mapplanes[s->planenum].normal[axis] == dir)
- break;
- }
-
- if (i == b->numsides)
- { // add a new side
- if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
- g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
- nummapbrushsides++;
- b->numsides++;
- VectorClear (normal);
- normal[axis] = dir;
- if (dir == 1)
- dist = b->maxs[axis];
- else
- dist = -b->mins[axis];
- s->planenum = FindFloatPlane (normal, dist);
- s->texinfo = b->original_sides[0].texinfo;
- s->contents = b->original_sides[0].contents;
- s->bevel = true;
- c_boxbevels++;
- }
-
- // if the plane is not in it canonical order, swap it
- if (i != order)
- {
- sidetemp = b->original_sides[order];
- b->original_sides[order] = b->original_sides[i];
- b->original_sides[i] = sidetemp;
-
- j = b->original_sides - brushsides;
- tdtemp = side_brushtextures[j+order];
- side_brushtextures[j+order] = side_brushtextures[j+i];
- side_brushtextures[j+i] = tdtemp;
- }
- }
- }
-
- //
- // add the edge bevels
- //
- if (b->numsides == 6)
- return; // pure axial
-
- // test the non-axial plane edges
- for (i=6 ; i<b->numsides ; i++)
- {
- s = b->original_sides + i;
- w = s->winding;
- if (!w)
- continue;
- for (j=0 ; j<w->numpoints ; j++)
- {
- k = (j+1)%w->numpoints;
- VectorSubtract (w->p[j], w->p[k], vec);
- if (VectorNormalize (vec) < 0.5)
- continue;
- SnapVector (vec);
- for (k=0 ; k<3 ; k++)
- if ( vec[k] == -1 || vec[k] == 1)
- break; // axial
- if (k != 3)
- continue; // only test non-axial edges
-
- // try the six possible slanted axials from this edge
- for (axis=0 ; axis <3 ; axis++)
- {
- for (dir=-1 ; dir <= 1 ; dir+=2)
- {
- // construct a plane
- VectorClear (vec2);
- vec2[axis] = dir;
- CrossProduct (vec, vec2, normal);
- if (VectorNormalize (normal) < 0.5)
- continue;
- dist = DotProduct (w->p[j], normal);
-
- // if all the points on all the sides are
- // behind this plane, it is a proper edge bevel
- for (k=0 ; k<b->numsides ; k++)
- {
- // if this plane has allready been used, skip it
- // NOTE: Use a larger tolerance for collision planes than for rendering planes
- if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) )
- break;
-
- w2 = b->original_sides[k].winding;
- if (!w2)
- continue;
- for (l=0 ; l<w2->numpoints ; l++)
- {
- d = DotProduct (w2->p[l], normal) - dist;
- if (d > 0.1)
- break; // point in front
- }
- if (l != w2->numpoints)
- break;
- }
-
- if (k != b->numsides)
- continue; // wasn't part of the outer hull
- // add this plane
- if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
- g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
- nummapbrushsides++;
- s2 = &b->original_sides[b->numsides];
- s2->planenum = FindFloatPlane (normal, dist);
- s2->texinfo = b->original_sides[0].texinfo;
- s2->contents = b->original_sides[0].contents;
- s2->bevel = true;
- c_edgebevels++;
- b->numsides++;
- }
- }
- }
- }
-}
-
-/*
-================
-MakeBrushWindings
-
-makes basewindigs for sides and mins / maxs for the brush
-================
-*/
-qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob)
-{
- int i, j;
- winding_t *w;
- side_t *side;
- plane_t *plane;
-
- ClearBounds (ob->mins, ob->maxs);
-
- for (i=0 ; i<ob->numsides ; i++)
- {
- plane = &mapplanes[ob->original_sides[i].planenum];
- w = BaseWindingForPlane (plane->normal, plane->dist);
- for (j=0 ; j<ob->numsides && w; j++)
- {
- if (i == j)
- continue;
- if (ob->original_sides[j].bevel)
- continue;
- plane = &mapplanes[ob->original_sides[j].planenum^1];
-// ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
- // adding an epsilon here, due to precision issues creating complex
- // displacement surfaces (cab)
- ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON );
- }
-
- side = &ob->original_sides[i];
- side->winding = w;
- if (w)
- {
- side->visible = true;
- for (j=0 ; j<w->numpoints ; j++)
- AddPointToBounds (w->p[j], ob->mins, ob->maxs);
- }
- }
-
- for (i=0 ; i<3 ; i++)
- {
- if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER)
- Msg("Brush %i: bounds out of range\n", ob->id);
- if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER)
- Msg("Brush %i: no visible sides on brush\n", ob->id);
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Takes all of the brushes from the current entity and adds them to the
-// world's brush list. Used by func_detail and func_areaportal.
-// THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING.
-// Input : mapent - Entity whose brushes are to be moved to the world.
-//-----------------------------------------------------------------------------
-void CMapFile::MoveBrushesToWorld( entity_t *mapent )
-{
- int newbrushes;
- int worldbrushes;
- mapbrush_t *temp;
- int i;
-
- // this is pretty gross, because the brushes are expected to be
- // in linear order for each entity
-
- newbrushes = mapent->numbrushes;
- worldbrushes = entities[0].numbrushes;
-
- temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
- memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
-
-#if 0 // let them keep their original brush numbers
- for (i=0 ; i<newbrushes ; i++)
- temp[i].entitynum = 0;
-#endif
-
- // make space to move the brushes (overlapped copy)
- memmove (mapbrushes + worldbrushes + newbrushes,
- mapbrushes + worldbrushes,
- sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
-
- // copy the new brushes down
- memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
-
- // fix up indexes
- entities[0].numbrushes += newbrushes;
- for (i=1 ; i<num_entities ; i++)
- entities[i].firstbrush += newbrushes;
- free (temp);
-
- mapent->numbrushes = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Takes all of the brushes from the current entity and adds them to the
-// world's brush list. Used by func_detail and func_areaportal.
-// Input : mapent - Entity whose brushes are to be moved to the world.
-//-----------------------------------------------------------------------------
-void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent )
-{
- int newbrushes;
- int worldbrushes;
- mapbrush_t *temp;
- int i;
-
- for( i = 0; i < nummapdispinfo; i++ )
- {
- if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) )
- {
- mapdispinfo[ i ].entitynum = 0;
- }
- }
-
- // this is pretty gross, because the brushes are expected to be
- // in linear order for each entity
- newbrushes = mapent->numbrushes;
- worldbrushes = entities[0].numbrushes;
-
- temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
- memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
-
-#if 0 // let them keep their original brush numbers
- for (i=0 ; i<newbrushes ; i++)
- temp[i].entitynum = 0;
-#endif
-
- // make space to move the brushes (overlapped copy)
- memmove (mapbrushes + worldbrushes + newbrushes,
- mapbrushes + worldbrushes,
- sizeof(mapbrush_t) * (mapent->firstbrush - worldbrushes) );
-
-
- // wwwxxxmmyyy
-
- // copy the new brushes down
- memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
-
- // fix up indexes
- entities[0].numbrushes += newbrushes;
- for (i=1 ; i<num_entities ; i++)
- {
- if ( entities[ i ].firstbrush < mapent->firstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to
- {
- entities[ i ].firstbrush += newbrushes;
- }
- }
- free (temp);
-
- mapent->numbrushes = 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side
-// Input : *brush -
-//-----------------------------------------------------------------------------
-void RemoveContentsDetailFromBrush( mapbrush_t *brush )
-{
- // Only valid on non-world brushes
- Assert( brush->entitynum != 0 );
-
- side_t *s;
- int i;
-
- s = &brush->original_sides[0];
- for ( i=0 ; i<brush->numsides ; i++, s++ )
- {
- if ( s->contents & CONTENTS_DETAIL )
- {
- s->contents &= ~CONTENTS_DETAIL;
- }
- }
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes
-// Input : *mapent -
-//-----------------------------------------------------------------------------
-void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent )
-{
- int i;
- for ( i = 0; i < mapent->numbrushes; i++ )
- {
- int brushnum = mapent->firstbrush + i;
-
- mapbrush_t *brush = &mapbrushes[ brushnum ];
- RemoveContentsDetailFromBrush( brush );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFile -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : szKey -
-// szValue -
-// pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!strnicmp(szKey, "row", 3))
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy(szBuf, szValue);
-
- int nCols = (1 << pMapDispInfo->power) + 1;
- int nRow = atoi(&szKey[3]);
-
- char *pszNext = strtok(szBuf, " ");
- int nIndex = nRow * nCols;
-
- while (pszNext != NULL)
- {
- pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext);
- pszNext = strtok(NULL, " ");
- nIndex++;
- }
- }
-
- return(ChunkFile_Ok);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: load in the displacement info "chunk" from the .map file into the
-// vbsp map displacement info data structure
-// Output : return the index of the map displacement info
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo )
-{
- //
- // check to see if we exceeded the maximum displacement info list size
- //
- if (nummapdispinfo > MAX_MAP_DISPINFO)
- {
- g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" );
- }
-
- // get a pointer to the next available displacement info slot
- mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo];
- nummapdispinfo++;
-
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo);
- Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo);
- Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo);
- Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo);
- Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo);
-
-#ifdef VSVMFIO
- Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo);
-#endif // VSVMFIO
-
- //
- // Read the displacement chunk.
- //
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo);
- pFile->PopHandlers();
-
- if (eResult == ChunkFile_Ok)
- {
- // return a pointer to the displacement info
- *ppMapDispInfo = pMapDispInfo;
- }
-
- return(eResult);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *mapent -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!stricmp(szKey, "power"))
- {
- CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power);
- }
-#ifdef VSVMFIO
- else if (!stricmp(szKey, "elevation"))
- {
- CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation);
- }
-#endif // VSVMFIO
- else if (!stricmp(szKey, "uaxis"))
- {
- CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis);
- }
- else if (!stricmp(szKey, "vaxis"))
- {
- CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis);
- }
- else if( !stricmp( szKey, "startposition" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition );
- }
- else if( !stricmp( szKey, "flags" ) )
- {
- CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags );
- }
-#if 0 // old data
- else if (!stricmp( szKey, "alpha" ) )
- {
- CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues );
- }
-#endif
- else if (!stricmp(szKey, "mintess"))
- {
- CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess);
- }
- else if (!stricmp(szKey, "smooth"))
- {
- CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle);
- }
-
- return(ChunkFile_Ok);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFile -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!strnicmp(szKey, "row", 3))
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy(szBuf, szValue);
-
- int nCols = (1 << pMapDispInfo->power) + 1;
- int nRow = atoi(&szKey[3]);
-
- char *pszNext0 = strtok(szBuf, " ");
- char *pszNext1 = strtok(NULL, " ");
- char *pszNext2 = strtok(NULL, " ");
-
- int nIndex = nRow * nCols;
-
- while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
- {
- pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0);
- pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1);
- pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2);
-
- pszNext0 = strtok(NULL, " ");
- pszNext1 = strtok(NULL, " ");
- pszNext2 = strtok(NULL, " ");
-
- nIndex++;
- }
- }
-
- return(ChunkFile_Ok);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!strnicmp(szKey, "row", 3))
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy(szBuf, szValue);
-
- int nCols = (1 << pMapDispInfo->power) + 1;
- int nRow = atoi(&szKey[3]);
-
- char *pszNext0 = strtok(szBuf, " ");
- char *pszNext1 = strtok(NULL, " ");
- char *pszNext2 = strtok(NULL, " ");
-
- int nIndex = nRow * nCols;
-
- while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
- {
- pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0);
- pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1);
- pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2);
-
- pszNext0 = strtok(NULL, " ");
- pszNext1 = strtok(NULL, " ");
- pszNext2 = strtok(NULL, " ");
-
- nIndex++;
- }
- }
-
- return(ChunkFile_Ok);
-}
-
-
-#ifdef VSVMFIO
-ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo));
-}
-
-
-ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!strnicmp(szKey, "row", 3))
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy(szBuf, szValue);
-
- int nCols = (1 << pMapDispInfo->power) + 1;
- int nRow = atoi(&szKey[3]);
-
- char *pszNext0 = strtok(szBuf, " ");
- char *pszNext1 = strtok(NULL, " ");
- char *pszNext2 = strtok(NULL, " ");
-
- int nIndex = nRow * nCols;
-
- while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
- {
- pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0);
- pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1);
- pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2);
-
- pszNext0 = strtok(NULL, " ");
- pszNext1 = strtok(NULL, " ");
- pszNext2 = strtok(NULL, " ");
-
- nIndex++;
- }
- }
-
- return(ChunkFile_Ok);
-}
-#endif // VSVMFIO
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szKey -
-// *szValue -
-// *pDisp -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if (!strnicmp(szKey, "row", 3))
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy(szBuf, szValue);
-
- int nCols = (1 << pMapDispInfo->power) + 1;
- int nRow = atoi(&szKey[3]);
-
- char *pszNext0 = strtok(szBuf, " ");
-
- int nIndex = nRow * nCols;
-
- while (pszNext0 != NULL)
- {
- pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0);
- pszNext0 = strtok(NULL, " ");
- nIndex++;
- }
- }
-
- return(ChunkFile_Ok);
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo));
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
-{
- if ( !strnicmp( szKey, "row", 3 ) )
- {
- char szBuf[MAX_KEYVALUE_LEN];
- strcpy( szBuf, szValue );
-
- int nCols = ( 1 << pMapDispInfo->power );
- int nRow = atoi( &szKey[3] );
-
- char *pszNext = strtok( szBuf, " " );
-
- int nIndex = nRow * nCols;
- int iTri = nIndex * 2;
-
- while ( pszNext != NULL )
- {
- // Collapse the tags here!
- unsigned short nTriTags = ( unsigned short )atoi( pszNext );
-
- // Walkable
- bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 );
- if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) )
- {
- bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 );
- }
-
- // Buildable
- bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 );
- if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) )
- {
- bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 );
- }
-
- nTriTags = 0;
- if ( bWalkable )
- {
- nTriTags |= DISPTRI_TAG_WALKABLE;
- }
-
- if ( bBuildable )
- {
- nTriTags |= DISPTRI_TAG_BUILDABLE;
- }
-
- pMapDispInfo->triTags[iTri] = nTriTags;
- pszNext = strtok( NULL, " " );
- iTri++;
- }
- }
-
- return( ChunkFile_Ok );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : brushSideID -
-// Output : int
-//-----------------------------------------------------------------------------
-int CMapFile::SideIDToIndex( int brushSideID )
-{
- int i;
- for ( i = 0; i < nummapbrushsides; i++ )
- {
- if ( brushsides[i].id == brushSideID )
- {
- return i;
- }
- }
- Assert( 0 );
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *mapent -
-// *key -
-//-----------------------------------------------------------------------------
-void ConvertSideList( entity_t *mapent, char *key )
-{
- char *pszSideList = ValueForKey( mapent, key );
-
- if (pszSideList)
- {
- char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 );
- strcpy( pszTmpList, pszSideList );
-
- bool bFirst = true;
- char szNewValue[1024];
- szNewValue[0] = '\0';
-
- const char *pszScan = strtok( pszTmpList, " " );
- if ( !pszScan )
- return;
- do
- {
- int nSideID;
-
- if ( sscanf( pszScan, "%d", &nSideID ) == 1 )
- {
- int nIndex = g_LoadingMap->SideIDToIndex(nSideID);
- if (nIndex != -1)
- {
- if (!bFirst)
- {
- strcat( szNewValue, " " );
- }
- else
- {
- bFirst = false;
- }
-
- char szIndex[15];
- itoa( nIndex, szIndex, 10 );
- strcat( szNewValue, szIndex );
- }
- }
- } while ( ( pszScan = strtok( NULL, " " ) ) );
-
- SetKeyValue( mapent, key, szNewValue );
- }
-}
-
-
-// Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides.
-ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt )
-{
- // Get the list of the sides.
- char *pSideList = ValueForKey( pMapEnt, "sides" );
-
- // Parse the side list.
- char *pScan = strtok( pSideList, " " );
- if( pScan )
- {
- do
- {
- int brushSideID;
- if( sscanf( pScan, "%d", &brushSideID ) == 1 )
- {
- if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 )
- g_NoDynamicShadowSides.AddToTail( brushSideID );
- }
- } while( ( pScan = strtok( NULL, " " ) ) );
- }
-
- // Clear out this entity.
- pMapEnt->epairs = NULL;
- return ( ChunkFile_Ok );
-}
-
-
-static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay )
-{
- if ( !stricmp( szKey, "material" ) )
- {
- // Get the material name.
- const char *pMaterialName = szValue;
- if( g_ReplaceMaterials )
- {
- pMaterialName = ReplaceMaterialName( szValue );
- }
-
- Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
- if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
- {
- Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
- return ChunkFile_Fail;
- }
- strcpy( pOverlay->szMaterialName, pMaterialName );
- }
- else if ( !stricmp( szKey, "StartU") )
- {
- CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] );
- }
- else if ( !stricmp( szKey, "EndU" ) )
- {
- CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] );
- }
- else if ( !stricmp( szKey, "StartV" ) )
- {
- CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] );
- }
- else if ( !stricmp( szKey, "EndV" ) )
- {
- CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] );
- }
- else if ( !stricmp( szKey, "BasisOrigin" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin );
- }
- else if ( !stricmp( szKey, "BasisU" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] );
- }
- else if ( !stricmp( szKey, "BasisV" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] );
- }
- else if ( !stricmp( szKey, "BasisNormal" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] );
- }
- else if ( !stricmp( szKey, "uv0" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] );
- }
- else if ( !stricmp( szKey, "uv1" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] );
- }
- else if ( !stricmp( szKey, "uv2" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] );
- }
- else if ( !stricmp( szKey, "uv3" ) )
- {
- CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] );
- }
- else if ( !stricmp( szKey, "sides" ) )
- {
- const char *pSideList = szValue;
- char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
- strcpy( pTmpList, pSideList );
- const char *pScan = strtok( pTmpList, " " );
- if ( !pScan )
- return ChunkFile_Fail;
-
- pOverlay->aSideList.Purge();
- pOverlay->aFaceList.Purge();
-
- do
- {
- int nSideId;
- if ( sscanf( pScan, "%d", &nSideId ) == 1 )
- {
- pOverlay->aSideList.AddToTail( nSideId );
- }
- } while ( ( pScan = strtok( NULL, " " ) ) );
- }
-
- return ChunkFile_Ok;
-}
-
-static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam )
-{
- int iOverlay = g_aMapWaterOverlays.AddToTail();
- mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay];
- if ( !pOverlay )
- return ChunkFile_Fail;
-
- pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1;
- pOverlay->m_nRenderOrder = 0;
-
- ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay );
- return eResult;
-}
-
-static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam )
-{
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 );
- pFile->PushHandlers( &Handlers );
-
- ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL );
-
- pFile->PopHandlers();
-
- return eResult;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs.
-// These are stored in the object, since the brushes are going to go away.
-// Input : *mapent -
-//-----------------------------------------------------------------------------
-void CMapFile::AddLadderKeys( entity_t *mapent )
-{
- Vector mins, maxs;
- ClearBounds( mins, maxs );
-
- int i;
- for ( i = 0; i < mapent->numbrushes; i++ )
- {
- int brushnum = mapent->firstbrush + i;
- mapbrush_t *brush = &mapbrushes[ brushnum ];
-
- AddPointToBounds( brush->mins, mins, maxs );
- AddPointToBounds( brush->maxs, mins, maxs );
- }
-
- char buf[16];
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x );
- SetKeyValue( mapent, "mins.x", buf );
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y );
- SetKeyValue( mapent, "mins.y", buf );
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z );
- SetKeyValue( mapent, "mins.z", buf );
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x );
- SetKeyValue( mapent, "maxs.x", buf );
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y );
- SetKeyValue( mapent, "maxs.y", buf );
-
- Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z );
- SetKeyValue( mapent, "maxs.z", buf );
-}
-
-ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam)
-{
- return g_LoadingMap->LoadEntityCallback( pFile, nParam );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pFile -
-// ulParam -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam)
-{
- if (num_entities == MAX_MAP_ENTITIES)
- {
- // Exits.
- g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES");
- }
-
- entity_t *mapent = &entities[num_entities];
- num_entities++;
- memset(mapent, 0, sizeof(*mapent));
- mapent->firstbrush = nummapbrushes;
- mapent->numbrushes = 0;
- //mapent->portalareas[0] = -1;
- //mapent->portalareas[1] = -1;
-
- LoadEntity_t LoadEntity;
- LoadEntity.pEntity = mapent;
-
- // 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("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
- Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity);
- Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 );
-
- //
- // Read the entity chunk.
- //
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
- pFile->PopHandlers();
-
- if (eResult == ChunkFile_Ok)
- {
- GetVectorForKey (mapent, "origin", mapent->origin);
-
- const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" );
- const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" );
- if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' )
- {
- int min = 0;
- int max = 0;
- if( *pMinDXLevelStr )
- {
- min = atoi( pMinDXLevelStr );
- }
- if( *pMaxDXLevelStr )
- {
- max = atoi( pMaxDXLevelStr );
- }
-
- // Set min and max to default values.
- if( min == 0 )
- {
- min = g_nDXLevel;
- }
- if( max == 0 )
- {
- max = g_nDXLevel;
- }
- if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) )
- {
- mapent->numbrushes = 0;
- mapent->epairs = NULL;
- return(ChunkFile_Ok);
- }
- }
-
- // offset all of the planes and texinfo
- if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] )
- {
- for (int i=0 ; i<mapent->numbrushes ; i++)
- {
- mapbrush_t *b = &mapbrushes[mapent->firstbrush + i];
- for (int j=0 ; j<b->numsides ; j++)
- {
- side_t *s = &b->original_sides[j];
- vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin);
- s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
- if ( !onlyents )
- {
- s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin);
- }
- }
- MakeBrushWindings (b);
- }
- }
-
- //
- // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader.
- //
- const char *pClassName = ValueForKey( mapent, "classname" );
-
- if ( !strcmp( "func_detail", pClassName ) )
- {
- MoveBrushesToWorld (mapent);
- mapent->numbrushes = 0;
-
- // clear out this entity
- mapent->epairs = NULL;
- return(ChunkFile_Ok);
- }
-
- // these get added to a list for processing the portal file
- // but aren't necessary to emit to the BSP
- if ( !strcmp( "func_viscluster", pClassName ) )
- {
- AddVisCluster(mapent);
- return(ChunkFile_Ok);
- }
-
- //
- // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder
- // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders.
- //
- if ( !strcmp( "func_ladder", pClassName ) )
- {
- AddLadderKeys( mapent );
-
- MoveBrushesToWorld (mapent);
-
- // Convert to info_ladder entity
- SetKeyValue( mapent, "classname", "info_ladder" );
-
- return(ChunkFile_Ok);
- }
-
- if( !strcmp( "env_cubemap", pClassName ) )
- {
- if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
- {
- const char *pSideListStr = ValueForKey( mapent, "sides" );
- int size;
- size = IntForKey( mapent, "cubemapsize" );
- Cubemap_InsertSample( mapent->origin, size );
- Cubemap_SaveBrushSides( pSideListStr );
- }
- // clear out this entity
- mapent->epairs = NULL;
- return(ChunkFile_Ok);
- }
-
- if ( !strcmp( "test_sidelist", pClassName ) )
- {
- ConvertSideList(mapent, "sides");
- return ChunkFile_Ok;
- }
-
- if ( !strcmp( "info_overlay", pClassName ) )
- {
- int iAccessorID = Overlay_GetFromEntity( mapent );
-
- if ( iAccessorID < 0 )
- {
- // Clear out this entity.
- mapent->epairs = NULL;
- }
- else
- {
- // Convert to info_overlay_accessor entity
- SetKeyValue( mapent, "classname", "info_overlay_accessor" );
-
- // Remember the id for accessing the overlay
- char buf[16];
- Q_snprintf( buf, sizeof(buf), "%i", iAccessorID );
- SetKeyValue( mapent, "OverlayID", buf );
- }
-
- return ( ChunkFile_Ok );
- }
-
- if ( !strcmp( "info_overlay_transition", pClassName ) )
- {
- // Clear out this entity.
- mapent->epairs = NULL;
- return ( ChunkFile_Ok );
- }
-
- if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 )
- {
- return HandleNoDynamicShadowsEnt( mapent );
- }
-
- if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 )
- {
- // Clear out this entity.
- mapent->epairs = NULL;
- return ( ChunkFile_Ok );
- }
-
- // areaportal entities move their brushes, but don't eliminate
- // the entity
- if( IsAreaPortal( pClassName ) )
- {
- char str[128];
-
- if (mapent->numbrushes != 1)
- {
- Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
- }
-
- mapbrush_t *b = &mapbrushes[nummapbrushes-1];
- b->contents = CONTENTS_AREAPORTAL;
- c_areaportals++;
- mapent->areaportalnum = c_areaportals;
-
- // set the portal number as "portalnumber"
- sprintf (str, "%i", c_areaportals);
- SetKeyValue (mapent, "portalnumber", str);
-
- MoveBrushesToWorld (mapent);
- return(ChunkFile_Ok);
- }
-
-#ifdef VSVMFIO
- if ( !Q_stricmp( pClassName, "light" ) )
- {
- CVmfImport::GetVmfImporter()->ImportLightCallback(
- ValueForKey( mapent, "hammerid" ),
- ValueForKey( mapent, "origin" ),
- ValueForKey( mapent, "_light" ),
- ValueForKey( mapent, "_lightHDR" ),
- ValueForKey( mapent, "_lightscaleHDR" ),
- ValueForKey( mapent, "_quadratic_attn" ) );
- }
-
- if ( !Q_stricmp( pClassName, "light_spot" ) )
- {
- CVmfImport::GetVmfImporter()->ImportLightSpotCallback(
- ValueForKey( mapent, "hammerid" ),
- ValueForKey( mapent, "origin" ),
- ValueForKey( mapent, "angles" ),
- ValueForKey( mapent, "pitch" ),
- ValueForKey( mapent, "_light" ),
- ValueForKey( mapent, "_lightHDR" ),
- ValueForKey( mapent, "_lightscaleHDR" ),
- ValueForKey( mapent, "_quadratic_attn" ),
- ValueForKey( mapent, "_inner_cone" ),
- ValueForKey( mapent, "_cone" ),
- ValueForKey( mapent, "_exponent" ) );
- }
-
- if ( !Q_stricmp( pClassName, "light_dynamic" ) )
- {
- CVmfImport::GetVmfImporter()->ImportLightDynamicCallback(
- ValueForKey( mapent, "hammerid" ),
- ValueForKey( mapent, "origin" ),
- ValueForKey( mapent, "angles" ),
- ValueForKey( mapent, "pitch" ),
- ValueForKey( mapent, "_light" ),
- ValueForKey( mapent, "_quadratic_attn" ),
- ValueForKey( mapent, "_inner_cone" ),
- ValueForKey( mapent, "_cone" ),
- ValueForKey( mapent, "brightness" ),
- ValueForKey( mapent, "distance" ),
- ValueForKey( mapent, "spotlight_radius" ) );
- }
-
- if ( !Q_stricmp( pClassName, "light_environment" ) )
- {
- CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback(
- ValueForKey( mapent, "hammerid" ),
- ValueForKey( mapent, "origin" ),
- ValueForKey( mapent, "angles" ),
- ValueForKey( mapent, "pitch" ),
- ValueForKey( mapent, "_light" ),
- ValueForKey( mapent, "_lightHDR" ),
- ValueForKey( mapent, "_lightscaleHDR" ),
- ValueForKey( mapent, "_ambient" ),
- ValueForKey( mapent, "_ambientHDR" ),
- ValueForKey( mapent, "_AmbientScaleHDR" ),
- ValueForKey( mapent, "SunSpreadAngle" ) );
- }
-
- const char *pModel = ValueForKey( mapent, "model" );
- if ( pModel && Q_strlen( pModel ) )
- {
- CVmfImport::GetVmfImporter()->ImportModelCallback(
- pModel,
- ValueForKey( mapent, "hammerid" ),
- ValueForKey( mapent, "angles" ),
- ValueForKey( mapent, "origin" ),
- MDagPath() );
- }
-#endif // VSVMFIO
-
- // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides...
- if ( mapent != &entities[ 0 ] )
- {
- RemoveContentsDetailFromEntity( mapent );
- }
-
- return(ChunkFile_Ok);
- }
-
- return(eResult);
-}
-
-
-entity_t* EntityByName( char const *pTestName )
-{
- if( !pTestName )
- return 0;
-
- for( int i=0; i < g_MainMap->num_entities; i++ )
- {
- entity_t *e = &g_MainMap->entities[i];
-
- const char *pName = ValueForKey( e, "targetname" );
- if( stricmp( pName, pTestName ) == 0 )
- return e;
- }
-
- return 0;
-}
-
-
-void CMapFile::ForceFuncAreaPortalWindowContents()
-{
- // Now go through all areaportal entities and force CONTENTS_WINDOW
- // on the brushes of the bmodels they point at.
- char *targets[] = {"target", "BackgroundBModel"};
- int nTargets = sizeof(targets) / sizeof(targets[0]);
-
- for( int i=0; i < num_entities; i++ )
- {
- entity_t *e = &entities[i];
-
- const char *pClassName = ValueForKey( e, "classname" );
-
- // Don't do this on "normal" func_areaportal entities. Those are tied to doors
- // and should be opaque when closed. But areaportal windows (and any other
- // distance-based areaportals) should be windows because they are normally open/transparent
- if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) )
- continue;
-
-// const char *pTestEntName = ValueForKey( e, "targetname" );
-
- for( int iTarget=0; iTarget < nTargets; iTarget++ )
- {
- char const *pEntName = ValueForKey( e, targets[iTarget] );
- if( !pEntName[0] )
- continue;
-
- entity_t *pBrushEnt = EntityByName( pEntName );
- if( !pBrushEnt )
- continue;
-
- for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ )
- {
- mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID;
- mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW;
- }
- }
- }
-}
-
-
-// ============ Instancing ============
-
-// #define MERGE_INSTANCE_DEBUG_INFO 1
-
-#define INSTANCE_VARIABLE_KEY "replace"
-
-static GameData GD;
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will read in a standard key / value file
-// Input : pFilename - the absolute name of the file to read
-// Output : returns the KeyValues of the file, NULL if the file could not be read.
-//-----------------------------------------------------------------------------
-static KeyValues *ReadKeyValuesFile( const char *pFilename )
-{
- // Read in the gameinfo.txt file and null-terminate it.
- FILE *fp = fopen( pFilename, "rb" );
- if ( !fp )
- return NULL;
- CUtlVector<char> buf;
- fseek( fp, 0, SEEK_END );
- buf.SetSize( ftell( fp ) + 1 );
- fseek( fp, 0, SEEK_SET );
- fread( buf.Base(), 1, buf.Count()-1, fp );
- fclose( fp );
- buf[buf.Count()-1] = 0;
-
- KeyValues *kv = new KeyValues( "" );
- if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
- {
- kv->deleteThis();
- return NULL;
- }
-
- return kv;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will set a secondary lookup path for instances.
-// Input : pszInstancePath - the secondary lookup path
-//-----------------------------------------------------------------------------
-void CMapFile::SetInstancePath( const char *pszInstancePath )
-{
- strcpy( m_InstancePath, pszInstancePath );
- V_strlower( m_InstancePath );
- V_FixSlashes( m_InstancePath );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: This function will attempt to find a full path given the base and relative names.
-// Input : pszBaseFileName - the base file that referenced this instance
-// pszInstanceFileName - the relative file name of this instance
-// Output : Returns true if it was able to locate the file
-// pszOutFileName - the full path to the file name if located
-//-----------------------------------------------------------------------------
-bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName )
-{
- char szInstanceFileNameFixed[ MAX_PATH ];
- const char *pszMapPath = "\\maps\\";
-
- strcpy( szInstanceFileNameFixed, pszInstanceFileName );
- V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) );
- V_FixSlashes( szInstanceFileNameFixed );
-
- // first, try to find a relative location based upon the Base file name
- strcpy( pszOutFileName, pszBaseFileName );
- V_StripFilename( pszOutFileName );
-
- strcat( pszOutFileName, "\\" );
- strcat( pszOutFileName, szInstanceFileNameFixed );
-
- if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
- {
- return true;
- }
-
- // second, try to find the master 'maps' directory and make it relative from that
- strcpy( pszOutFileName, pszBaseFileName );
- V_StripFilename( pszOutFileName );
- V_RemoveDotSlashes( pszOutFileName );
- V_FixDoubleSlashes( pszOutFileName );
- V_strlower( pszOutFileName );
- strcat( pszOutFileName, "\\" );
-
- char *pos = strstr( pszOutFileName, pszMapPath );
- if ( pos )
- {
- pos += strlen( pszMapPath );
- *pos = 0;
- strcat( pszOutFileName, szInstanceFileNameFixed );
-
- if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
- {
- return true;
- }
- }
-
- if ( m_InstancePath[ 0 ] != 0 )
- {
- sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName );
-
- if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) )
- {
- char FullPath[ MAX_PATH ];
- g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) );
- strcpy( pszOutFileName, FullPath );
-
- return true;
- }
- }
-
- pszOutFileName[ 0 ] = 0;
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will check the main map for any func_instances. It will
-// also attempt to load in the gamedata file for instancing remapping help.
-// Input : none
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::CheckForInstances( const char *pszFileName )
-{
- if ( this != g_MainMap )
- { // all sub-instances will be appended to the main map master list as they are read in
- // so the main loop below will naturally get to the appended ones.
- return;
- }
-
- char GameInfoPath[ MAX_PATH ];
-
- g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) );
- KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath );
- if ( !GameInfoKV )
- {
- Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath );
- return;
- }
-
- const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL );
- if ( InstancePath )
- {
- CMapFile::SetInstancePath( InstancePath );
- }
-
- const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL );
- if ( !GameDataFile )
- {
- Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath );
- return;
- }
-
- char FDGPath[ MAX_PATH ];
- if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) )
- {
- if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "", FDGPath, sizeof( FDGPath ) ) )
- {
- Msg( "Could not locate GameData file %s\n", GameDataFile );
- }
- }
-
- GD.Load( FDGPath );
-
- // this list will grow as instances are merged onto it. sub-instances are merged and
- // automatically done in this processing.
- for ( int i = 0; i < num_entities; i++ )
- {
- char *pEntity = ValueForKey( &entities[ i ], "classname" );
- if ( !strcmp( pEntity, "func_instance" ) )
- {
- char *pInstanceFile = ValueForKey( &entities[ i ], "file" );
- if ( pInstanceFile[ 0 ] )
- {
- char InstancePath[ MAX_PATH ];
- bool bLoaded = false;
-
- if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) )
- {
- if ( LoadMapFile( InstancePath ) )
- {
- MergeInstance( &entities[ i ], g_LoadingMap );
- delete g_LoadingMap;
- bLoaded = true;
- }
- }
-
- if ( bLoaded == false )
- {
- Color red( 255, 0, 0, 255 );
-
- ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile );
- }
- }
-
- entities[ i ].numbrushes = 0;
- entities[ i ].epairs = NULL;
- }
- }
-
- g_LoadingMap = this;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will do all of the necessary work to merge the instance
-// into the main map.
-// Input : pInstanceEntity - the entity of the func_instance
-// Instance - the map file of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance )
-{
- matrix3x4_t mat;
- QAngle angles;
- Vector OriginOffset = pInstanceEntity->origin;
-
- m_InstanceCount++;
-
- GetAnglesForKey( pInstanceEntity, "angles", angles );
- AngleMatrix( angles, OriginOffset, mat );
-
-#ifdef MERGE_INSTANCE_DEBUG_INFO
- Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z );
-#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
- MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat );
- MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat );
- MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat );
- MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat );
- MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will merge in the map planes from the instance into
-// the main map.
-// Input : pInstanceEntity - the entity of the func_instance
-// Instance - the map file of the instance
-// InstanceOrigin - the translation of the instance
-// InstanceAngle - the rotation of the instance
-// InstanceMatrix - the translation / rotation matrix of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
-{
- // Each pair of planes needs to be added to the main map
- for ( int i = 0; i < Instance->nummapplanes; i += 2 )
- {
- FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will merge in the map brushes from the instance into
-// the main map.
-// Input : pInstanceEntity - the entity of the func_instance
-// Instance - the map file of the instance
-// InstanceOrigin - the translation of the instance
-// InstanceAngle - the rotation of the instance
-// InstanceMatrix - the translation / rotation matrix of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
-{
- int max_brush_id = 0;
-
- for( int i = 0; i < nummapbrushes; i++ )
- {
- if ( mapbrushes[ i ].id > max_brush_id )
- {
- max_brush_id = mapbrushes[ i ].id;
- }
- }
-
- for( int i = 0; i < Instance->nummapbrushes; i++ )
- {
- mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ];
-
- mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ];
- brush->entitynum += num_entities;
- brush->brushnum += nummapbrushes;
-
- if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 )
- { // world spawn brushes as well as ladders we physically move
- Vector minsIn = brush->mins;
- Vector maxsIn = brush->maxs;
-
- TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs );
- }
- else
- {
- }
- brush->id += max_brush_id;
-
- int index = brush->original_sides - Instance->brushsides;
- brush->original_sides = &brushsides[ nummapbrushsides + index ];
- }
-
- nummapbrushes += Instance->nummapbrushes;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will merge in the map sides from the instance into
-// the main map.
-// Input : pInstanceEntity - the entity of the func_instance
-// Instance - the map file of the instance
-// InstanceOrigin - the translation of the instance
-// InstanceAngle - the rotation of the instance
-// InstanceMatrix - the translation / rotation matrix of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
-{
- int max_side_id = 0;
-
- for( int i = 0; i < nummapbrushsides; i++ )
- {
- if ( brushsides[ i ].id > max_side_id )
- {
- max_side_id = brushsides[ i ].id;
- }
- }
-
- for( int i = 0; i < Instance->nummapbrushsides; i++ )
- {
- brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ];
-
- side_t *side = &brushsides[ nummapbrushsides + i ];
- // The planes got merged & remapped. So you need to search for the output plane index on each side
- // NOTE: You could optimize this by saving off an index map in MergePlanes
- side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist );
- side->id += max_side_id;
-
- // this could be pre-processed into a list for quicker checking
- bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 );
- if ( !bNeedsTranslation )
- { // check for sides that are part of the world spawn - those need translating
- for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ )
- {
- int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
-
- if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) )
- {
- bNeedsTranslation = true;
- break;
- }
- }
- }
- if ( !bNeedsTranslation )
- { // sides for ladders are outside of the world spawn, but also need translating
- for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ )
- {
- int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
-
- if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 )
- {
- bNeedsTranslation = true;
- break;
- }
- }
- }
- if ( bNeedsTranslation )
- { // we only want to do the adjustment on world spawn brushes, not entity brushes
- if ( side->winding )
- {
- for( int point = 0; point < side->winding->numpoints; point++ )
- {
- Vector inPoint = side->winding->p[ point ];
- VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] );
- }
- }
-
- int planenum = side->planenum;
- cplane_t inPlane, outPlane;
- inPlane.normal = mapplanes[ planenum ].normal;
- inPlane.dist = mapplanes[ planenum ].dist;
-
- MatrixTransformPlane( InstanceMatrix, inPlane, outPlane );
- planenum = FindFloatPlane( outPlane.normal, outPlane.dist );
- side->planenum = planenum;
-
- brush_texture_t bt = Instance->side_brushtextures[ i ];
-
- VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis );
- VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis );
- bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ];
- bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ];
-
- if ( !onlyents )
- {
- side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin );
- }
- }
-
- if ( side->pMapDisp )
- {
- mapdispinfo_t *disp = side->pMapDisp;
-
- disp->brushSideID = side->id;
- Vector inPoint = disp->startPosition;
- VectorTransform( inPoint, InstanceMatrix, disp->startPosition );
-
- disp->face.originalface = side;
- disp->face.texinfo = side->texinfo;
- disp->face.planenum = side->planenum;
- disp->entitynum += num_entities;
-
- for( int point = 0; point < disp->face.w->numpoints; point++ )
- {
- Vector inPoint = disp->face.w->p[ point ];
- VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] );
- }
-
- }
- }
-
- nummapbrushsides += Instance->nummapbrushsides;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will look for replace parameters in the function instance
-// to see if there is anything in the epair that should be replaced.
-// Input : pPair - the epair with the value
-// pInstanceEntity - the func_instance that may ahve replace keywords
-// Output : pPair - the value field may be updated
-//-----------------------------------------------------------------------------
-void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity )
-{
- char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ];
- bool Overwritten = false;
-
- strcpy( NewValue, pPair->value );
- for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next )
- {
- if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 )
- {
- char InstanceVariable[ MAX_KEYVALUE_LEN ];
-
- strcpy( InstanceVariable, epInstance->value );
-
- char *ValuePos = strchr( InstanceVariable, ' ' );
- if ( !ValuePos )
- {
- continue;
- }
- *ValuePos = 0;
- ValuePos++;
-
- strcpy( Value, NewValue );
- if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) )
- {
- Overwritten = true;
- break;
- }
- }
- }
-
- if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 )
- {
- free( pPair->value );
- pPair->value = copystring( NewValue );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will merge in the entities from the instance into
-// the main map.
-// Input : pInstanceEntity - the entity of the func_instance
-// Instance - the map file of the instance
-// InstanceOrigin - the translation of the instance
-// InstanceAngle - the rotation of the instance
-// InstanceMatrix - the translation / rotation matrix of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
-{
- int max_entity_id = 0;
- char temp[ 2048 ];
- char NameFixup[ 128 ];
- entity_t *WorldspawnEnt = NULL;
- GameData::TNameFixup FixupStyle;
-
- char *pTargetName = ValueForKey( pInstanceEntity, "targetname" );
- char *pName = ValueForKey( pInstanceEntity, "name" );
- if ( pTargetName[ 0 ] )
- {
- sprintf( NameFixup, "%s", pTargetName );
- }
- else if ( pName[ 0 ] )
- {
- sprintf( NameFixup, "%s", pName );
- }
- else
- {
- sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount );
- }
-
- for( int i = 0; i < num_entities; i++ )
- {
- char *pID = ValueForKey( &entities[ i ], "hammerid" );
- if ( pID[ 0 ] )
- {
- int value = atoi( pID );
- if ( value > max_entity_id )
- {
- max_entity_id = value;
- }
- }
- }
-
- FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) );
-
- for( int i = 0; i < Instance->num_entities; i++ )
- {
- entities[ num_entities + i ] = Instance->entities[ i ];
-
- entity_t *entity = &entities[ num_entities + i ];
- entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes );
-
- char *pID = ValueForKey( entity, "hammerid" );
- if ( pID[ 0 ] )
- {
- int value = atoi( pID );
- value += max_entity_id;
- sprintf( temp, "%d", value );
-
- SetKeyValue( entity, "hammerid", temp );
- }
-
- char *pEntity = ValueForKey( entity, "classname" );
- if ( strcmpi( pEntity, "worldspawn" ) == 0 )
- {
- WorldspawnEnt = entity;
- }
- else
- {
- Vector inOrigin = entity->origin;
- VectorTransform( inOrigin, InstanceMatrix, entity->origin );
-
- // search for variables coming from the func_instance to replace inside of the instance
- // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet.
- for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next )
- {
- ReplaceInstancePair( ep, pInstanceEntity );
- }
-
-#ifdef MERGE_INSTANCE_DEBUG_INFO
- Msg( "Remapping class %s\n", pEntity );
-#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
- GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle );
- if ( EntClass )
- {
- for( int i = 0; i < EntClass->GetVariableCount(); i++ )
- {
- GDinputvariable *EntVar = EntClass->GetVariableAt( i );
- char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() );
- if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) )
- {
-#ifdef MERGE_INSTANCE_DEBUG_INFO
- Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp );
-#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
- SetKeyValue( entity, EntVar->GetName(), temp );
- }
- else
- {
-#ifdef MERGE_INSTANCE_DEBUG_INFO
- Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue );
-#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
- }
- }
- }
-
- if ( strcmpi( pEntity, "func_simpleladder" ) == 0 )
- { // hate having to do this, but the key values are so screwed up
- AddLadderKeys( entity );
-/* Vector vInNormal, vOutNormal;
-
- vInNormal.x = FloatForKey( entity, "normal.x" );
- vInNormal.y = FloatForKey( entity, "normal.y" );
- vInNormal.z = FloatForKey( entity, "normal.z" );
- VectorRotate( vInNormal, InstanceMatrix, vOutNormal );
-
- Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x );
- SetKeyValue( entity, "normal.x", temp );
-
- Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y );
- SetKeyValue( entity, "normal.y", temp );
-
- Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z );
- SetKeyValue( entity, "normal.z", temp );*/
- }
- }
-
-#ifdef MERGE_INSTANCE_DEBUG_INFO
- Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i );
- Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush );
- Msg( " KV Pairs:\n" );
- for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next )
- {
- Msg( " %s %s\n", ep->key, ep->value );
- }
-#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
- }
-
- // search for variables coming from the func_instance to replace inside of the instance
- // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet.
- for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
- {
- ReplaceInstancePair( Connection->m_Pair, pInstanceEntity );
- }
-
- for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
- {
- char *newValue, *oldValue;
- char origValue[ 4096 ];
- int extraLen = 0;
-
- oldValue = Connection->m_Pair->value;
- strcpy( origValue, oldValue );
- char *pos = strchr( origValue, ',' );
- if ( pos )
- { // null terminate the first field
- *pos = NULL;
- extraLen = strlen( pos + 1) + 1; // for the comma we just null'd
- }
-
- if ( GD.RemapNameField( origValue, temp, FixupStyle ) )
- {
- newValue = new char [ strlen( temp ) + extraLen + 1 ];
- strcpy( newValue, temp );
- if ( pos )
- {
- strcat( newValue, "," );
- strcat( newValue, pos + 1 );
- }
-
- Connection->m_Pair->value = newValue;
- delete oldValue;
- }
- }
-
- num_entities += Instance->num_entities;
-
- MoveBrushesToWorldGeneral( WorldspawnEnt );
- WorldspawnEnt->numbrushes = 0;
- WorldspawnEnt->epairs = NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function will translate overlays from the instance into
-// the main map.
-// Input : InstanceEntityNum - the entity number of the func_instance
-// Instance - the map file of the instance
-// InstanceOrigin - the translation of the instance
-// InstanceAngle - the rotation of the instance
-// InstanceMatrix - the translation / rotation matrix of the instance
-// Output : none
-//-----------------------------------------------------------------------------
-void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
-{
- for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ )
- {
- Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
- }
- for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ )
- {
- Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP
-// loader is used, otherwise the file is assumed to be in VMF format.
-// Input : pszFileName - Full path of the map file to load.
-//-----------------------------------------------------------------------------
-bool LoadMapFile( const char *pszFileName )
-{
- bool bLoadingManifest = false;
- CManifest *pMainManifest = NULL;
- ChunkFileResult_t eResult;
-
- //
- // Dummy this up for the texture handling. This can be removed when old .MAP file
- // support is removed.
- //
- g_nMapFileVersion = 400;
-
- const char *pszExtension =V_GetFileExtension( pszFileName );
- if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 )
- {
- pMainManifest = new CManifest();
- if ( pMainManifest->LoadVMFManifest( pszFileName ) )
- {
- eResult = ChunkFile_Ok;
- pszFileName = pMainManifest->GetInstancePath();
- }
- else
- {
- eResult = ChunkFile_Fail;
- }
- bLoadingManifest = true;
- }
- else
- {
- //
- // Open the file.
- //
- CChunkFile File;
- eResult = File.Open(pszFileName, ChunkFile_Read);
-
- //
- // Read the file.
- //
- if (eResult == ChunkFile_Ok)
- {
- int index = g_Maps.AddToTail( new CMapFile() );
- g_LoadingMap = g_Maps[ index ];
- if ( g_MainMap == NULL )
- {
- g_MainMap = g_LoadingMap;
- }
-
- if ( g_MainMap == g_LoadingMap || verbose )
- {
- Msg( "Loading %s\n", pszFileName );
- }
-
-
- // reset the displacement info count
- // nummapdispinfo = 0;
-
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0);
- Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0);
-
- File.PushHandlers(&Handlers);
-
- //
- // Read the sub-chunks. We ignore keys in the root of the file.
- //
- while (eResult == ChunkFile_Ok)
- {
- eResult = File.ReadChunk();
- }
-
- File.PopHandlers();
- }
- else
- {
- Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult));
- }
- }
-
- if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF))
- {
- // Update the overlay/side list(s).
- Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays );
- OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays );
-
- g_LoadingMap->CheckForInstances( pszFileName );
-
- if ( pMainManifest )
- {
- pMainManifest->CordonWorld();
- }
-
- ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
- for (int i=0 ; i<g_MainMap->entities[0].numbrushes ; i++)
- {
- // HLTOOLS: Raise map limits
- if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER)
- {
- continue; // no valid points
- }
-
- AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
- AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
- }
-
- qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes);
- qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes);
- qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides);
- qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels);
- qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels);
- qprintf ("%5i entities\n", g_LoadingMap->num_entities);
- qprintf ("%5i planes\n", g_LoadingMap->nummapplanes);
- qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals);
- qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2],
- g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]);
-
- //TestExpandBrushes();
-
- // Clear the error reporting
- g_MapError.ClearState();
- }
-
- if ( g_MainMap == g_LoadingMap )
- {
- num_entities = g_MainMap->num_entities;
- memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) );
- }
- g_LoadingMap->ForceFuncAreaPortalWindowContents();
-
- return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) );
-}
-
-ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
-{
- return g_LoadingMap->LoadSideCallback( pFile, pSideInfo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pFile -
-// pParent -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
-{
- if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
- {
- g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
- }
-
- pSideInfo->pSide = &brushsides[nummapbrushsides];
-
- side_t *side = pSideInfo->pSide;
- mapbrush_t *b = pSideInfo->pBrush;
- g_MapError.BrushSide( pSideInfo->nSideIndex++ );
-
- // initialize the displacement info
- pSideInfo->pSide->pMapDisp = NULL;
-
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp );
-
- //
- // Read the side chunk.
- //
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo);
- pFile->PopHandlers();
-
- if (eResult == ChunkFile_Ok)
- {
- side->contents |= pSideInfo->nBaseContents;
- side->surf |= pSideInfo->nBaseFlags;
- pSideInfo->td.flags |= pSideInfo->nBaseFlags;
-
- if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
- {
- side->contents |= CONTENTS_DETAIL;
- }
-
- if (fulldetail )
- {
- side->contents &= ~CONTENTS_DETAIL;
- }
-
- if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
- {
- side->contents |= CONTENTS_SOLID;
- }
-
- // hints and skips are never detail, and have no content
- if (side->surf & (SURF_HINT|SURF_SKIP) )
- {
- side->contents = 0;
- }
-
- //
- // find the plane number
- //
- int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]);
- if (planenum != -1)
- {
- //
- // See if the plane has been used already.
- //
- int k;
- for ( k = 0; k < b->numsides; k++)
- {
- side_t *s2 = b->original_sides + k;
- if (s2->planenum == planenum)
- {
- g_MapError.ReportWarning("duplicate plane");
- break;
- }
- if ( s2->planenum == (planenum^1) )
- {
- g_MapError.ReportWarning("mirrored plane");
- break;
- }
- }
-
- //
- // If the plane hasn't been used already, keep this side.
- //
- if (k == b->numsides)
- {
- side = b->original_sides + b->numsides;
- side->planenum = planenum;
- if ( !onlyents )
- {
- side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin);
- }
-
- // save the td off in case there is an origin brush and we
- // have to recalculate the texinfo
- if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
- g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
- side_brushtextures[nummapbrushsides] = pSideInfo->td;
- nummapbrushsides++;
- b->numsides++;
-
-#ifdef VSVMFIO
- // Tell Maya We Have Another Side
- if ( CVmfImport::GetVmfImporter() )
- {
- CVmfImport::GetVmfImporter()->AddSideCallback(
- b, side, pSideInfo->td,
- pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] );
- }
-#endif // VSVMFIO
-
- }
- }
- else
- {
- g_MapError.ReportWarning("plane with no normal");
- }
- }
-
- return(eResult);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : szKey -
-// szValue -
-// pSideInfo -
-// Output :
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo)
-{
- if (!stricmp(szKey, "plane"))
- {
- int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)",
- &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2],
- &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2],
- &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]);
-
- if (nRead != 9)
- {
- g_MapError.ReportError("parsing plane definition");
- }
- }
- else if (!stricmp(szKey, "material"))
- {
- // Get the material name.
- if( g_ReplaceMaterials )
- {
- szValue = ReplaceMaterialName( szValue );
- }
-
- strcpy(pSideInfo->td.name, szValue);
- g_MapError.TextureState(szValue);
-
- // Find default flags and values for this material.
- int mt = FindMiptex(pSideInfo->td.name);
- pSideInfo->td.flags = textureref[mt].flags;
- pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel;
-
- pSideInfo->pSide->contents = textureref[mt].contents;
- pSideInfo->pSide->surf = pSideInfo->td.flags;
- }
- else if (!stricmp(szKey, "uaxis"))
- {
- int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]);
- if (nRead != 5)
- {
- g_MapError.ReportError("parsing U axis definition");
- }
- }
- else if (!stricmp(szKey, "vaxis"))
- {
- int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]);
- if (nRead != 5)
- {
- g_MapError.ReportError("parsing V axis definition");
- }
- }
- else if (!stricmp(szKey, "lightmapscale"))
- {
- pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue);
- if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f)
- {
- g_MapError.ReportWarning("luxel size of 0");
- pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize;
- }
- pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale;
- if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale)
- {
- pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale;
- }
- }
- else if (!stricmp(szKey, "contents"))
- {
- pSideInfo->pSide->contents |= atoi(szValue);
- }
- else if (!stricmp(szKey, "flags"))
- {
- pSideInfo->td.flags |= atoi(szValue);
- pSideInfo->pSide->surf = pSideInfo->td.flags;
- }
- else if (!stricmp(szKey, "id"))
- {
- pSideInfo->pSide->id = atoi( szValue );
- }
- else if (!stricmp(szKey, "smoothing_groups"))
- {
- pSideInfo->pSide->smoothingGroups = atoi( szValue );
- }
-
- return(ChunkFile_Ok);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Reads the connections chunk of the entity.
-// Input : pFile - Chunk file to load from.
-// pLoadEntity - Structure to receive loaded entity information.
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
-{
- return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity));
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Parses a key/value pair from the entity connections chunk.
-// Input : szKey - Key indicating the name of the entity output.
-// szValue - Comma delimited fields in the following format:
-// <target>,<input>,<parameter>,<delay>,<times to fire>
-// pLoadEntity - Structure to receive loaded entity information.
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
-{
- return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity );
-}
-
-ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
-{
- //
- // Create new input and fill it out.
- //
- epair_t *pOutput = new epair_t;
-
- pOutput->key = new char [strlen(szKey) + 1];
- pOutput->value = new char [strlen(szValue) + 1];
-
- strcpy(pOutput->key, szKey);
- strcpy(pOutput->value, szValue);
-
- m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs );
-
- //
- // Append it to the end of epairs list.
- //
- pOutput->next = NULL;
-
- if (!pLoadEntity->pEntity->epairs)
- {
- pLoadEntity->pEntity->epairs = pOutput;
- }
- else
- {
- epair_t *ep;
- for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next )
- {
- }
- ep->next = pOutput;
- }
-
- return(ChunkFile_Ok);
-}
-
-
-ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
-{
- return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity );
-};
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pFile -
-// pParent -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
-{
- if (nummapbrushes == MAX_MAP_BRUSHES)
- {
- g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES");
- }
-
- mapbrush_t *b = &mapbrushes[nummapbrushes];
- b->original_sides = &brushsides[nummapbrushsides];
- b->entitynum = num_entities-1;
- b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush;
-
- LoadSide_t SideInfo;
- SideInfo.pBrush = b;
- SideInfo.nSideIndex = 0;
- SideInfo.nBaseContents = pLoadEntity->nBaseContents;
- SideInfo.nBaseFlags = pLoadEntity->nBaseFlags;
-
- //
- // Set up handlers for the subchunks that we are interested in.
- //
- CChunkHandlerMap Handlers;
- Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo);
-
- //
- // Read the solid chunk.
- //
- pFile->PushHandlers(&Handlers);
- ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b);
- pFile->PopHandlers();
-
- if (eResult == ChunkFile_Ok)
- {
- // get the content for the entire brush
- b->contents = BrushContents (b);
-
- // allow detail brushes to be removed
- if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) )
- {
- b->numsides = 0;
- return(ChunkFile_Ok);
- }
-
- // allow water brushes to be removed
- if (nowater && (b->contents & MASK_WATER) )
- {
- b->numsides = 0;
- return(ChunkFile_Ok);
- }
-
- // create windings for sides and bounds for brush
- MakeBrushWindings (b);
-
- //
- // brushes that will not be visible at all will never be
- // used as bsp splitters
- //
- // only do this on the world entity
- //
- if ( b->entitynum == 0 )
- {
- if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
- {
- if ( g_ClipTexinfo < 0 )
- {
- g_ClipTexinfo = b->original_sides[0].texinfo;
- }
- c_clipbrushes++;
- for (int i=0 ; i<b->numsides ; i++)
- {
- b->original_sides[i].texinfo = TEXINFO_NODE;
- }
- }
- }
-
- //
- // origin brushes are removed, but they set
- // the rotation origin for the rest of the brushes
- // in the entity. After the entire entity is parsed,
- // the planenums and texinfos will be adjusted for
- // the origin brush
- //
- if (b->contents & CONTENTS_ORIGIN)
- {
- char string[32];
- Vector origin;
-
- if (num_entities == 1)
- {
- Error("Brush %i: origin brushes not allowed in world", b->id);
- }
-
- VectorAdd (b->mins, b->maxs, origin);
- VectorScale (origin, 0.5, origin);
-
- sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
- SetKeyValue (&entities[b->entitynum], "origin", string);
-
- VectorCopy (origin, entities[b->entitynum].origin);
-
- // don't keep this brush
- b->numsides = 0;
-
- return(ChunkFile_Ok);
- }
-
-#ifdef VSVMFIO
- if ( CVmfImport::GetVmfImporter() )
- {
- CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b );
- }
-#endif // VSVMFIO
-
- //
- // find a map brushes with displacement surfaces and remove them from the "world"
- //
- if( HasDispInfo( b ) )
- {
- // add the base face data to the displacement surface
- DispGetFaceInfo( b );
-
- // don't keep this brush
- b->numsides = 0;
-
- return( ChunkFile_Ok );
- }
-
- AddBrushBevels (b);
-
- nummapbrushes++;
- pLoadEntity->pEntity->numbrushes++;
- }
- else
- {
- return eResult;
- }
-
- return(ChunkFile_Ok);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : pFile -
-// parent -
-// Output : ChunkFileResult_t
-//-----------------------------------------------------------------------------
-ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush)
-{
- if (!stricmp(szKey, "id"))
- {
- pLoadBrush->id = atoi(szValue);
- g_MapError.BrushState(pLoadBrush->id);
- }
-
- return ChunkFile_Ok;
-}
-
-
-/*
-================
-TestExpandBrushes
-
-Expands all the brush planes and saves a new map out
-================
-*/
-void CMapFile::TestExpandBrushes (void)
-{
- FILE *f;
- side_t *s;
- int i, j, bn;
- winding_t *w;
- char *name = "expanded.map";
- mapbrush_t *brush;
- vec_t dist;
-
- Msg ("writing %s\n", name);
- f = fopen (name, "wb");
- if (!f)
- Error ("Can't write %s\b", name);
-
- fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
- fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" );
-
-
- for (bn=0 ; bn<nummapbrushes ; bn++)
- {
- brush = &mapbrushes[bn];
- fprintf (f, "{\n");
- for (i=0 ; i<brush->numsides ; i++)
- {
- s = brush->original_sides + i;
- dist = mapplanes[s->planenum].dist;
- for (j=0 ; j<3 ; j++)
- dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
-
- w = BaseWindingForPlane (mapplanes[s->planenum].normal, 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 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n",
- TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
-
- FreeWinding (w);
- }
- fprintf (f, "}\n");
- }
- fprintf (f, "}\n");
-
- fclose (f);
-
- Error ("can't proceed after expanding brushes");
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: load in the displacement info "chunk" from the .map file into the
-// vbsp map displacement info data structure
-// Output: return the pointer to the displacement map
-//-----------------------------------------------------------------------------
-mapdispinfo_t *ParseDispInfoChunk( void )
-{
- int i, j;
- int vertCount;
- mapdispinfo_t *pMapDispInfo;
-
- //
- // check to see if we exceeded the maximum displacement info list size
- //
- if( nummapdispinfo > MAX_MAP_DISPINFO )
- g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO");
-
- // get a pointer to the next available displacement info slot
- pMapDispInfo = &mapdispinfo[nummapdispinfo];
- nummapdispinfo++;
-
- //
- // get the chunk opener - "{"
- //
- GetToken( false );
- if( strcmp( token, "{" ) )
- g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" );
-
- //
- //
- // get the displacement info attribs
- //
- //
-
- // power
- GetToken( true );
- pMapDispInfo->power = atoi( token );
-
- // u and v mapping axes
- for( i = 0; i < 2; i++ )
- {
- GetToken( false );
- if( strcmp( token, "[" ) )
- g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" );
-
- for( j = 0; j < 3; j++ )
- {
- GetToken( false );
-
- if( i == 0 )
- {
- pMapDispInfo->uAxis[j] = atof( token );
- }
- else
- {
- pMapDispInfo->vAxis[j] = atof( token );
- }
- }
-
- GetToken( false );
- if( strcmp( token, "]" ) )
- g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" );
- }
-
- // max displacement value
- if( g_nMapFileVersion < 350 )
- {
- GetToken( false );
- pMapDispInfo->maxDispDist = atof( token );
- }
-
- // minimum tesselation value
- GetToken( false );
- pMapDispInfo->minTess = atoi( token );
-
- // light smoothing angle
- GetToken( false );
- pMapDispInfo->smoothingAngle = atof( token );
-
- //
- // get the displacement info displacement normals
- //
- GetToken( true );
- pMapDispInfo->vectorDisps[0][0] = atof( token );
- GetToken( false );
- pMapDispInfo->vectorDisps[0][1] = atof( token );
- GetToken( false );
- pMapDispInfo->vectorDisps[0][2] = atof( token );
-
- vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) );
- for( i = 1; i < vertCount; i++ )
- {
- GetToken( false );
- pMapDispInfo->vectorDisps[i][0] = atof( token );
- GetToken( false );
- pMapDispInfo->vectorDisps[i][1] = atof( token );
- GetToken( false );
- pMapDispInfo->vectorDisps[i][2] = atof( token );
- }
-
- //
- // get the displacement info displacement values
- //
- GetToken( true );
- pMapDispInfo->dispDists[0] = atof( token );
-
- for( i = 1; i < vertCount; i++ )
- {
- GetToken( false );
- pMapDispInfo->dispDists[i] = atof( token );
- }
-
- //
- // get the chunk closer - "}"
- //
- GetToken( true );
- if( strcmp( token, "}" ) )
- g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" );
-
- // return the index of the displacement info slot
- return pMapDispInfo;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "map_shared.h"
+#include "disp_vbsp.h"
+#include "tier1/strtools.h"
+#include "builddisp.h"
+#include "tier0/icommandline.h"
+#include "KeyValues.h"
+#include "materialsub.h"
+#include "fgdlib/fgdlib.h"
+#include "manifest.h"
+
+#ifdef VSVMFIO
+#include "VmfImport.h"
+#endif // VSVMFIO
+
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+
+#define RENDER_NORMAL_EPSILON 0.00001
+#define RENDER_DIST_EPSILON 0.01f
+
+#define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same
+ // as clip epsilon, but it is 0.1f and I
+ // currently don't know how that number was
+ // come to (cab) - this is 0.01 of an inch
+ // for clipping brush solids
+struct LoadSide_t
+{
+ mapbrush_t *pBrush;
+ side_t *pSide;
+ int nSideIndex;
+ int nBaseFlags;
+ int nBaseContents;
+ Vector planepts[3];
+ brush_texture_t td;
+};
+
+
+extern qboolean onlyents;
+
+
+CUtlVector< CMapFile * > g_Maps;
+CMapFile *g_MainMap = NULL;
+CMapFile *g_LoadingMap = NULL;
+
+char CMapFile::m_InstancePath[ MAX_PATH ] = "";
+int CMapFile::m_InstanceCount = 0;
+int CMapFile::c_areaportals = 0;
+
+void CMapFile::Init( void )
+{
+ entity_num = 0;
+ num_entities = 0;
+
+ nummapplanes = 0;
+ memset( mapplanes, 0, sizeof( mapplanes ) );
+
+ nummapbrushes = 0;
+ memset( mapbrushes, 0, sizeof( mapbrushes ) );
+
+ nummapbrushsides = 0;
+ memset( brushsides, 0, sizeof( brushsides ) );
+
+ memset( side_brushtextures, 0, sizeof( side_brushtextures ) );
+
+ memset( planehash, 0, sizeof( planehash ) );
+
+ m_ConnectionPairs = NULL;
+
+ m_StartMapOverlays = g_aMapOverlays.Count();
+ m_StartMapWaterOverlays = g_aMapWaterOverlays.Count();
+
+ c_boxbevels = 0;
+ c_edgebevels = 0;
+ c_clipbrushes = 0;
+ g_ClipTexinfo = -1;
+}
+
+
+// All the brush sides referenced by info_no_dynamic_shadow entities.
+CUtlVector<int> g_NoDynamicShadowSides;
+
+
+void TestExpandBrushes (void);
+
+ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo );
+ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+
+#ifdef VSVMFIO
+ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+#endif // VSVMFIO
+
+ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
+ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush);
+
+ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
+ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo);
+
+
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int PlaneTypeForNormal (Vector& normal)
+{
+ vec_t ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+/*
+================
+PlaneEqual
+================
+*/
+qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon)
+{
+#if 1
+ if (
+ fabs(p->normal[0] - normal[0]) < normalEpsilon
+ && fabs(p->normal[1] - normal[1]) < normalEpsilon
+ && fabs(p->normal[2] - normal[2]) < normalEpsilon
+ && fabs(p->dist - dist) < distEpsilon )
+ return true;
+#else
+ if (p->normal[0] == normal[0]
+ && p->normal[1] == normal[1]
+ && p->normal[2] == normal[2]
+ && p->dist == dist)
+ return true;
+#endif
+ return false;
+}
+
+/*
+================
+AddPlaneToHash
+================
+*/
+void CMapFile::AddPlaneToHash (plane_t *p)
+{
+ int hash;
+
+ hash = (int)fabs(p->dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ g_MapError.ReportError ("FloatPlane: bad normal");
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAP_PLANES)
+ g_MapError.ReportError ("MAX_MAP_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+}
+
+
+/*
+==============
+SnapVector
+==============
+*/
+bool SnapVector (Vector& normal)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ return true;
+ }
+
+ if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = -1;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
+// Rounds dist to integer if it is within an epsilon of integer.
+// Input : normal - Plane normal vector (assumed to be unit length).
+// dist - Plane constant.
+//-----------------------------------------------------------------------------
+void SnapPlane(Vector &normal, vec_t &dist)
+{
+ SnapVector(normal);
+
+ if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
+ {
+ dist = RoundInt(dist);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
+// Recalculates dist if the normal was snapped. Rounds dist to integer
+// if it is within an epsilon of integer.
+// Input : normal - Plane normal vector (assumed to be unit length).
+// dist - Plane constant.
+// p0, p1, p2 - Three points on the plane.
+//-----------------------------------------------------------------------------
+void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2)
+{
+ if (SnapVector(normal))
+ {
+ //
+ // Calculate a new plane constant using the snapped normal. Use the
+ // centroid of the three plane points to minimize error. This is like
+ // rotating the plane around the centroid.
+ //
+ Vector p3 = (p0 + p1 + p2) / 3.0f;
+ dist = normal.Dot(p3);
+ if ( g_snapAxialPlanes )
+ {
+ dist = RoundInt(dist);
+ }
+ }
+
+ if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
+ {
+ dist = RoundInt(dist);
+ }
+}
+
+
+/*
+=============
+FindFloatPlane
+
+=============
+*/
+#ifndef USE_HASHING
+int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+
+ SnapPlane(normal, dist);
+ for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
+ {
+ if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
+ return i;
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#else
+int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+ int hash, h;
+
+ SnapPlane(normal, dist);
+ hash = (int)fabs(dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ // search the border bins as well
+ for (i=-1 ; i<=1 ; i++)
+ {
+ h = (hash+i)&(PLANE_HASHES-1);
+ for (p = planehash[h] ; p ; p=p->hash_chain)
+ {
+ if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
+ return p-mapplanes;
+ }
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds a plane normal and distance from three points on the plane.
+// If the normal is nearly axial, it will be snapped to be axial. Looks
+// up the plane in the unique planes.
+// Input : p0, p1, p2 - Three points on the plane.
+// Output : Returns the index of the plane in the planes list.
+//-----------------------------------------------------------------------------
+int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2)
+{
+ Vector t1, t2, normal;
+ vec_t dist;
+
+ VectorSubtract (p0, p1, t1);
+ VectorSubtract (p2, p1, t2);
+ CrossProduct (t1, t2, normal);
+ VectorNormalize (normal);
+
+ dist = DotProduct (p0, normal);
+
+ SnapPlane(normal, dist, p0, p1, p2);
+
+ return FindFloatPlane (normal, dist);
+}
+
+
+/*
+===========
+BrushContents
+===========
+*/
+int BrushContents (mapbrush_t *b)
+{
+ int contents;
+ int unionContents = 0;
+ side_t *s;
+ int i;
+
+ s = &b->original_sides[0];
+ contents = s->contents;
+ unionContents = contents;
+ for (i=1 ; i<b->numsides ; i++, s++)
+ {
+ s = &b->original_sides[i];
+
+ unionContents |= s->contents;
+#if 0
+ if (s->contents != contents)
+ {
+ Msg("Brush %i: mixed face contents\n", b->id);
+ break;
+ }
+#endif
+ }
+
+ // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface
+ int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME);
+ if ( transparentContents )
+ {
+ contents |= transparentContents | CONTENTS_TRANSLUCENT;
+ contents &= ~CONTENTS_SOLID;
+ }
+
+ return contents;
+}
+
+
+//============================================================================
+
+bool IsAreaPortal( char const *pClassName )
+{
+ // If the class name starts with "func_areaportal", then it's considered an area portal.
+ char const *pBaseName = "func_areaportal";
+ char const *pCur = pBaseName;
+ while( *pCur && *pClassName )
+ {
+ if( *pCur != *pClassName )
+ break;
+
+ ++pCur;
+ ++pClassName;
+ }
+
+ return *pCur == 0;
+}
+
+
+/*
+=================
+AddBrushBevels
+
+Adds any additional planes necessary to allow the brush to be expanded
+against axial bounding boxes
+=================
+*/
+void CMapFile::AddBrushBevels (mapbrush_t *b)
+{
+ int axis, dir;
+ int i, j, k, l, order;
+ side_t sidetemp;
+ brush_texture_t tdtemp;
+ side_t *s, *s2;
+ Vector normal;
+ float dist;
+ winding_t *w, *w2;
+ Vector vec, vec2;
+ float d;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+ {
+ // see if the plane is allready present
+ for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
+ {
+ if (mapplanes[s->planenum].normal[axis] == dir)
+ break;
+ }
+
+ if (i == b->numsides)
+ { // add a new side
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ b->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+ if (dir == 1)
+ dist = b->maxs[axis];
+ else
+ dist = -b->mins[axis];
+ s->planenum = FindFloatPlane (normal, dist);
+ s->texinfo = b->original_sides[0].texinfo;
+ s->contents = b->original_sides[0].contents;
+ s->bevel = true;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if (i != order)
+ {
+ sidetemp = b->original_sides[order];
+ b->original_sides[order] = b->original_sides[i];
+ b->original_sides[i] = sidetemp;
+
+ j = b->original_sides - brushsides;
+ tdtemp = side_brushtextures[j+order];
+ side_brushtextures[j+order] = side_brushtextures[j+i];
+ side_brushtextures[j+i] = tdtemp;
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if (b->numsides == 6)
+ return; // pure axial
+
+ // test the non-axial plane edges
+ for (i=6 ; i<b->numsides ; i++)
+ {
+ s = b->original_sides + i;
+ w = s->winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ k = (j+1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[k], vec);
+ if (VectorNormalize (vec) < 0.5)
+ continue;
+ SnapVector (vec);
+ for (k=0 ; k<3 ; k++)
+ if ( vec[k] == -1 || vec[k] == 1)
+ break; // axial
+ if (k != 3)
+ continue; // only test non-axial edges
+
+ // try the six possible slanted axials from this edge
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2)
+ {
+ // construct a plane
+ VectorClear (vec2);
+ vec2[axis] = dir;
+ CrossProduct (vec, vec2, normal);
+ if (VectorNormalize (normal) < 0.5)
+ continue;
+ dist = DotProduct (w->p[j], normal);
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for (k=0 ; k<b->numsides ; k++)
+ {
+ // if this plane has allready been used, skip it
+ // NOTE: Use a larger tolerance for collision planes than for rendering planes
+ if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) )
+ break;
+
+ w2 = b->original_sides[k].winding;
+ if (!w2)
+ continue;
+ for (l=0 ; l<w2->numpoints ; l++)
+ {
+ d = DotProduct (w2->p[l], normal) - dist;
+ if (d > 0.1)
+ break; // point in front
+ }
+ if (l != w2->numpoints)
+ break;
+ }
+
+ if (k != b->numsides)
+ continue; // wasn't part of the outer hull
+ // add this plane
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ s2 = &b->original_sides[b->numsides];
+ s2->planenum = FindFloatPlane (normal, dist);
+ s2->texinfo = b->original_sides[0].texinfo;
+ s2->contents = b->original_sides[0].contents;
+ s2->bevel = true;
+ c_edgebevels++;
+ b->numsides++;
+ }
+ }
+ }
+ }
+}
+
+/*
+================
+MakeBrushWindings
+
+makes basewindigs for sides and mins / maxs for the brush
+================
+*/
+qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ ClearBounds (ob->mins, ob->maxs);
+
+ for (i=0 ; i<ob->numsides ; i++)
+ {
+ plane = &mapplanes[ob->original_sides[i].planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (j=0 ; j<ob->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (ob->original_sides[j].bevel)
+ continue;
+ plane = &mapplanes[ob->original_sides[j].planenum^1];
+// ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ // adding an epsilon here, due to precision issues creating complex
+ // displacement surfaces (cab)
+ ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON );
+ }
+
+ side = &ob->original_sides[i];
+ side->winding = w;
+ if (w)
+ {
+ side->visible = true;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], ob->mins, ob->maxs);
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER)
+ Msg("Brush %i: bounds out of range\n", ob->id);
+ if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER)
+ Msg("Brush %i: no visible sides on brush\n", ob->id);
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes all of the brushes from the current entity and adds them to the
+// world's brush list. Used by func_detail and func_areaportal.
+// THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING.
+// Input : mapent - Entity whose brushes are to be moved to the world.
+//-----------------------------------------------------------------------------
+void CMapFile::MoveBrushesToWorld( entity_t *mapent )
+{
+ int newbrushes;
+ int worldbrushes;
+ mapbrush_t *temp;
+ int i;
+
+ // this is pretty gross, because the brushes are expected to be
+ // in linear order for each entity
+
+ newbrushes = mapent->numbrushes;
+ worldbrushes = entities[0].numbrushes;
+
+ temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
+ memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if 0 // let them keep their original brush numbers
+ for (i=0 ; i<newbrushes ; i++)
+ temp[i].entitynum = 0;
+#endif
+
+ // make space to move the brushes (overlapped copy)
+ memmove (mapbrushes + worldbrushes + newbrushes,
+ mapbrushes + worldbrushes,
+ sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
+
+ // copy the new brushes down
+ memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+ // fix up indexes
+ entities[0].numbrushes += newbrushes;
+ for (i=1 ; i<num_entities ; i++)
+ entities[i].firstbrush += newbrushes;
+ free (temp);
+
+ mapent->numbrushes = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes all of the brushes from the current entity and adds them to the
+// world's brush list. Used by func_detail and func_areaportal.
+// Input : mapent - Entity whose brushes are to be moved to the world.
+//-----------------------------------------------------------------------------
+void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent )
+{
+ int newbrushes;
+ int worldbrushes;
+ mapbrush_t *temp;
+ int i;
+
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) )
+ {
+ mapdispinfo[ i ].entitynum = 0;
+ }
+ }
+
+ // this is pretty gross, because the brushes are expected to be
+ // in linear order for each entity
+ newbrushes = mapent->numbrushes;
+ worldbrushes = entities[0].numbrushes;
+
+ temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
+ memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if 0 // let them keep their original brush numbers
+ for (i=0 ; i<newbrushes ; i++)
+ temp[i].entitynum = 0;
+#endif
+
+ // make space to move the brushes (overlapped copy)
+ memmove (mapbrushes + worldbrushes + newbrushes,
+ mapbrushes + worldbrushes,
+ sizeof(mapbrush_t) * (mapent->firstbrush - worldbrushes) );
+
+
+ // wwwxxxmmyyy
+
+ // copy the new brushes down
+ memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+ // fix up indexes
+ entities[0].numbrushes += newbrushes;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if ( entities[ i ].firstbrush < mapent->firstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to
+ {
+ entities[ i ].firstbrush += newbrushes;
+ }
+ }
+ free (temp);
+
+ mapent->numbrushes = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side
+// Input : *brush -
+//-----------------------------------------------------------------------------
+void RemoveContentsDetailFromBrush( mapbrush_t *brush )
+{
+ // Only valid on non-world brushes
+ Assert( brush->entitynum != 0 );
+
+ side_t *s;
+ int i;
+
+ s = &brush->original_sides[0];
+ for ( i=0 ; i<brush->numsides ; i++, s++ )
+ {
+ if ( s->contents & CONTENTS_DETAIL )
+ {
+ s->contents &= ~CONTENTS_DETAIL;
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes
+// Input : *mapent -
+//-----------------------------------------------------------------------------
+void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent )
+{
+ int i;
+ for ( i = 0; i < mapent->numbrushes; i++ )
+ {
+ int brushnum = mapent->firstbrush + i;
+
+ mapbrush_t *brush = &mapbrushes[ brushnum ];
+ RemoveContentsDetailFromBrush( brush );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : szKey -
+// szValue -
+// pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext = strtok(szBuf, " ");
+ int nIndex = nRow * nCols;
+
+ while (pszNext != NULL)
+ {
+ pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext);
+ pszNext = strtok(NULL, " ");
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load in the displacement info "chunk" from the .map file into the
+// vbsp map displacement info data structure
+// Output : return the index of the map displacement info
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo )
+{
+ //
+ // check to see if we exceeded the maximum displacement info list size
+ //
+ if (nummapdispinfo > MAX_MAP_DISPINFO)
+ {
+ g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" );
+ }
+
+ // get a pointer to the next available displacement info slot
+ mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo];
+ nummapdispinfo++;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo);
+ Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo);
+ Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo);
+ Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo);
+ Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo);
+
+#ifdef VSVMFIO
+ Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo);
+#endif // VSVMFIO
+
+ //
+ // Read the displacement chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ // return a pointer to the displacement info
+ *ppMapDispInfo = pMapDispInfo;
+ }
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *mapent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!stricmp(szKey, "power"))
+ {
+ CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power);
+ }
+#ifdef VSVMFIO
+ else if (!stricmp(szKey, "elevation"))
+ {
+ CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation);
+ }
+#endif // VSVMFIO
+ else if (!stricmp(szKey, "uaxis"))
+ {
+ CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis);
+ }
+ else if (!stricmp(szKey, "vaxis"))
+ {
+ CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis);
+ }
+ else if( !stricmp( szKey, "startposition" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition );
+ }
+ else if( !stricmp( szKey, "flags" ) )
+ {
+ CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags );
+ }
+#if 0 // old data
+ else if (!stricmp( szKey, "alpha" ) )
+ {
+ CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues );
+ }
+#endif
+ else if (!stricmp(szKey, "mintess"))
+ {
+ CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess);
+ }
+ else if (!stricmp(szKey, "smooth"))
+ {
+ CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle);
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+#ifdef VSVMFIO
+ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo));
+}
+
+
+ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+#endif // VSVMFIO
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+
+ int nIndex = nRow * nCols;
+
+ while (pszNext0 != NULL)
+ {
+ pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0);
+ pszNext0 = strtok(NULL, " ");
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo));
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if ( !strnicmp( szKey, "row", 3 ) )
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy( szBuf, szValue );
+
+ int nCols = ( 1 << pMapDispInfo->power );
+ int nRow = atoi( &szKey[3] );
+
+ char *pszNext = strtok( szBuf, " " );
+
+ int nIndex = nRow * nCols;
+ int iTri = nIndex * 2;
+
+ while ( pszNext != NULL )
+ {
+ // Collapse the tags here!
+ unsigned short nTriTags = ( unsigned short )atoi( pszNext );
+
+ // Walkable
+ bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 );
+ if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) )
+ {
+ bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 );
+ }
+
+ // Buildable
+ bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 );
+ if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) )
+ {
+ bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 );
+ }
+
+ nTriTags = 0;
+ if ( bWalkable )
+ {
+ nTriTags |= DISPTRI_TAG_WALKABLE;
+ }
+
+ if ( bBuildable )
+ {
+ nTriTags |= DISPTRI_TAG_BUILDABLE;
+ }
+
+ pMapDispInfo->triTags[iTri] = nTriTags;
+ pszNext = strtok( NULL, " " );
+ iTri++;
+ }
+ }
+
+ return( ChunkFile_Ok );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : brushSideID -
+// Output : int
+//-----------------------------------------------------------------------------
+int CMapFile::SideIDToIndex( int brushSideID )
+{
+ int i;
+ for ( i = 0; i < nummapbrushsides; i++ )
+ {
+ if ( brushsides[i].id == brushSideID )
+ {
+ return i;
+ }
+ }
+ Assert( 0 );
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *mapent -
+// *key -
+//-----------------------------------------------------------------------------
+void ConvertSideList( entity_t *mapent, char *key )
+{
+ char *pszSideList = ValueForKey( mapent, key );
+
+ if (pszSideList)
+ {
+ char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 );
+ strcpy( pszTmpList, pszSideList );
+
+ bool bFirst = true;
+ char szNewValue[1024];
+ szNewValue[0] = '\0';
+
+ const char *pszScan = strtok( pszTmpList, " " );
+ if ( !pszScan )
+ return;
+ do
+ {
+ int nSideID;
+
+ if ( sscanf( pszScan, "%d", &nSideID ) == 1 )
+ {
+ int nIndex = g_LoadingMap->SideIDToIndex(nSideID);
+ if (nIndex != -1)
+ {
+ if (!bFirst)
+ {
+ strcat( szNewValue, " " );
+ }
+ else
+ {
+ bFirst = false;
+ }
+
+ char szIndex[15];
+ itoa( nIndex, szIndex, 10 );
+ strcat( szNewValue, szIndex );
+ }
+ }
+ } while ( ( pszScan = strtok( NULL, " " ) ) );
+
+ SetKeyValue( mapent, key, szNewValue );
+ }
+}
+
+
+// Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides.
+ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt )
+{
+ // Get the list of the sides.
+ char *pSideList = ValueForKey( pMapEnt, "sides" );
+
+ // Parse the side list.
+ char *pScan = strtok( pSideList, " " );
+ if( pScan )
+ {
+ do
+ {
+ int brushSideID;
+ if( sscanf( pScan, "%d", &brushSideID ) == 1 )
+ {
+ if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 )
+ g_NoDynamicShadowSides.AddToTail( brushSideID );
+ }
+ } while( ( pScan = strtok( NULL, " " ) ) );
+ }
+
+ // Clear out this entity.
+ pMapEnt->epairs = NULL;
+ return ( ChunkFile_Ok );
+}
+
+
+static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay )
+{
+ if ( !stricmp( szKey, "material" ) )
+ {
+ // Get the material name.
+ const char *pMaterialName = szValue;
+ if( g_ReplaceMaterials )
+ {
+ pMaterialName = ReplaceMaterialName( szValue );
+ }
+
+ Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
+ if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
+ {
+ Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
+ return ChunkFile_Fail;
+ }
+ strcpy( pOverlay->szMaterialName, pMaterialName );
+ }
+ else if ( !stricmp( szKey, "StartU") )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] );
+ }
+ else if ( !stricmp( szKey, "EndU" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] );
+ }
+ else if ( !stricmp( szKey, "StartV" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] );
+ }
+ else if ( !stricmp( szKey, "EndV" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] );
+ }
+ else if ( !stricmp( szKey, "BasisOrigin" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin );
+ }
+ else if ( !stricmp( szKey, "BasisU" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] );
+ }
+ else if ( !stricmp( szKey, "BasisV" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] );
+ }
+ else if ( !stricmp( szKey, "BasisNormal" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] );
+ }
+ else if ( !stricmp( szKey, "uv0" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] );
+ }
+ else if ( !stricmp( szKey, "uv1" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] );
+ }
+ else if ( !stricmp( szKey, "uv2" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] );
+ }
+ else if ( !stricmp( szKey, "uv3" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] );
+ }
+ else if ( !stricmp( szKey, "sides" ) )
+ {
+ const char *pSideList = szValue;
+ char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
+ strcpy( pTmpList, pSideList );
+ const char *pScan = strtok( pTmpList, " " );
+ if ( !pScan )
+ return ChunkFile_Fail;
+
+ pOverlay->aSideList.Purge();
+ pOverlay->aFaceList.Purge();
+
+ do
+ {
+ int nSideId;
+ if ( sscanf( pScan, "%d", &nSideId ) == 1 )
+ {
+ pOverlay->aSideList.AddToTail( nSideId );
+ }
+ } while ( ( pScan = strtok( NULL, " " ) ) );
+ }
+
+ return ChunkFile_Ok;
+}
+
+static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam )
+{
+ int iOverlay = g_aMapWaterOverlays.AddToTail();
+ mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay];
+ if ( !pOverlay )
+ return ChunkFile_Fail;
+
+ pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1;
+ pOverlay->m_nRenderOrder = 0;
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay );
+ return eResult;
+}
+
+static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 );
+ pFile->PushHandlers( &Handlers );
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL );
+
+ pFile->PopHandlers();
+
+ return eResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs.
+// These are stored in the object, since the brushes are going to go away.
+// Input : *mapent -
+//-----------------------------------------------------------------------------
+void CMapFile::AddLadderKeys( entity_t *mapent )
+{
+ Vector mins, maxs;
+ ClearBounds( mins, maxs );
+
+ int i;
+ for ( i = 0; i < mapent->numbrushes; i++ )
+ {
+ int brushnum = mapent->firstbrush + i;
+ mapbrush_t *brush = &mapbrushes[ brushnum ];
+
+ AddPointToBounds( brush->mins, mins, maxs );
+ AddPointToBounds( brush->maxs, mins, maxs );
+ }
+
+ char buf[16];
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x );
+ SetKeyValue( mapent, "mins.x", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y );
+ SetKeyValue( mapent, "mins.y", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z );
+ SetKeyValue( mapent, "mins.z", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x );
+ SetKeyValue( mapent, "maxs.x", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y );
+ SetKeyValue( mapent, "maxs.y", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z );
+ SetKeyValue( mapent, "maxs.z", buf );
+}
+
+ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam)
+{
+ return g_LoadingMap->LoadEntityCallback( pFile, nParam );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// ulParam -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam)
+{
+ if (num_entities == MAX_MAP_ENTITIES)
+ {
+ // Exits.
+ g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES");
+ }
+
+ entity_t *mapent = &entities[num_entities];
+ num_entities++;
+ memset(mapent, 0, sizeof(*mapent));
+ mapent->firstbrush = nummapbrushes;
+ mapent->numbrushes = 0;
+ //mapent->portalareas[0] = -1;
+ //mapent->portalareas[1] = -1;
+
+ LoadEntity_t LoadEntity;
+ LoadEntity.pEntity = mapent;
+
+ // 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("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
+ Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity);
+ Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 );
+
+ //
+ // Read the entity chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ GetVectorForKey (mapent, "origin", mapent->origin);
+
+ const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" );
+ const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" );
+ if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' )
+ {
+ int min = 0;
+ int max = 0;
+ if( *pMinDXLevelStr )
+ {
+ min = atoi( pMinDXLevelStr );
+ }
+ if( *pMaxDXLevelStr )
+ {
+ max = atoi( pMaxDXLevelStr );
+ }
+
+ // Set min and max to default values.
+ if( min == 0 )
+ {
+ min = g_nDXLevel;
+ }
+ if( max == 0 )
+ {
+ max = g_nDXLevel;
+ }
+ if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) )
+ {
+ mapent->numbrushes = 0;
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+ }
+
+ // offset all of the planes and texinfo
+ if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] )
+ {
+ for (int i=0 ; i<mapent->numbrushes ; i++)
+ {
+ mapbrush_t *b = &mapbrushes[mapent->firstbrush + i];
+ for (int j=0 ; j<b->numsides ; j++)
+ {
+ side_t *s = &b->original_sides[j];
+ vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin);
+ s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
+ if ( !onlyents )
+ {
+ s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin);
+ }
+ }
+ MakeBrushWindings (b);
+ }
+ }
+
+ //
+ // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader.
+ //
+ const char *pClassName = ValueForKey( mapent, "classname" );
+
+ if ( !strcmp( "func_detail", pClassName ) )
+ {
+ MoveBrushesToWorld (mapent);
+ mapent->numbrushes = 0;
+
+ // clear out this entity
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+
+ // these get added to a list for processing the portal file
+ // but aren't necessary to emit to the BSP
+ if ( !strcmp( "func_viscluster", pClassName ) )
+ {
+ AddVisCluster(mapent);
+ return(ChunkFile_Ok);
+ }
+
+ //
+ // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder
+ // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders.
+ //
+ if ( !strcmp( "func_ladder", pClassName ) )
+ {
+ AddLadderKeys( mapent );
+
+ MoveBrushesToWorld (mapent);
+
+ // Convert to info_ladder entity
+ SetKeyValue( mapent, "classname", "info_ladder" );
+
+ return(ChunkFile_Ok);
+ }
+
+ if( !strcmp( "env_cubemap", pClassName ) )
+ {
+ if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
+ {
+ const char *pSideListStr = ValueForKey( mapent, "sides" );
+ int size;
+ size = IntForKey( mapent, "cubemapsize" );
+ Cubemap_InsertSample( mapent->origin, size );
+ Cubemap_SaveBrushSides( pSideListStr );
+ }
+ // clear out this entity
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+
+ if ( !strcmp( "test_sidelist", pClassName ) )
+ {
+ ConvertSideList(mapent, "sides");
+ return ChunkFile_Ok;
+ }
+
+ if ( !strcmp( "info_overlay", pClassName ) )
+ {
+ int iAccessorID = Overlay_GetFromEntity( mapent );
+
+ if ( iAccessorID < 0 )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ }
+ else
+ {
+ // Convert to info_overlay_accessor entity
+ SetKeyValue( mapent, "classname", "info_overlay_accessor" );
+
+ // Remember the id for accessing the overlay
+ char buf[16];
+ Q_snprintf( buf, sizeof(buf), "%i", iAccessorID );
+ SetKeyValue( mapent, "OverlayID", buf );
+ }
+
+ return ( ChunkFile_Ok );
+ }
+
+ if ( !strcmp( "info_overlay_transition", pClassName ) )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ return ( ChunkFile_Ok );
+ }
+
+ if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 )
+ {
+ return HandleNoDynamicShadowsEnt( mapent );
+ }
+
+ if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ return ( ChunkFile_Ok );
+ }
+
+ // areaportal entities move their brushes, but don't eliminate
+ // the entity
+ if( IsAreaPortal( pClassName ) )
+ {
+ char str[128];
+
+ if (mapent->numbrushes != 1)
+ {
+ Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
+ }
+
+ mapbrush_t *b = &mapbrushes[nummapbrushes-1];
+ b->contents = CONTENTS_AREAPORTAL;
+ c_areaportals++;
+ mapent->areaportalnum = c_areaportals;
+
+ // set the portal number as "portalnumber"
+ sprintf (str, "%i", c_areaportals);
+ SetKeyValue (mapent, "portalnumber", str);
+
+ MoveBrushesToWorld (mapent);
+ return(ChunkFile_Ok);
+ }
+
+#ifdef VSVMFIO
+ if ( !Q_stricmp( pClassName, "light" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_quadratic_attn" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_spot" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightSpotCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_quadratic_attn" ),
+ ValueForKey( mapent, "_inner_cone" ),
+ ValueForKey( mapent, "_cone" ),
+ ValueForKey( mapent, "_exponent" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_dynamic" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightDynamicCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_quadratic_attn" ),
+ ValueForKey( mapent, "_inner_cone" ),
+ ValueForKey( mapent, "_cone" ),
+ ValueForKey( mapent, "brightness" ),
+ ValueForKey( mapent, "distance" ),
+ ValueForKey( mapent, "spotlight_radius" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_environment" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_ambient" ),
+ ValueForKey( mapent, "_ambientHDR" ),
+ ValueForKey( mapent, "_AmbientScaleHDR" ),
+ ValueForKey( mapent, "SunSpreadAngle" ) );
+ }
+
+ const char *pModel = ValueForKey( mapent, "model" );
+ if ( pModel && Q_strlen( pModel ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportModelCallback(
+ pModel,
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "origin" ),
+ MDagPath() );
+ }
+#endif // VSVMFIO
+
+ // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides...
+ if ( mapent != &entities[ 0 ] )
+ {
+ RemoveContentsDetailFromEntity( mapent );
+ }
+
+ return(ChunkFile_Ok);
+ }
+
+ return(eResult);
+}
+
+
+entity_t* EntityByName( char const *pTestName )
+{
+ if( !pTestName )
+ return 0;
+
+ for( int i=0; i < g_MainMap->num_entities; i++ )
+ {
+ entity_t *e = &g_MainMap->entities[i];
+
+ const char *pName = ValueForKey( e, "targetname" );
+ if( stricmp( pName, pTestName ) == 0 )
+ return e;
+ }
+
+ return 0;
+}
+
+
+void CMapFile::ForceFuncAreaPortalWindowContents()
+{
+ // Now go through all areaportal entities and force CONTENTS_WINDOW
+ // on the brushes of the bmodels they point at.
+ char *targets[] = {"target", "BackgroundBModel"};
+ int nTargets = sizeof(targets) / sizeof(targets[0]);
+
+ for( int i=0; i < num_entities; i++ )
+ {
+ entity_t *e = &entities[i];
+
+ const char *pClassName = ValueForKey( e, "classname" );
+
+ // Don't do this on "normal" func_areaportal entities. Those are tied to doors
+ // and should be opaque when closed. But areaportal windows (and any other
+ // distance-based areaportals) should be windows because they are normally open/transparent
+ if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) )
+ continue;
+
+// const char *pTestEntName = ValueForKey( e, "targetname" );
+
+ for( int iTarget=0; iTarget < nTargets; iTarget++ )
+ {
+ char const *pEntName = ValueForKey( e, targets[iTarget] );
+ if( !pEntName[0] )
+ continue;
+
+ entity_t *pBrushEnt = EntityByName( pEntName );
+ if( !pBrushEnt )
+ continue;
+
+ for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ )
+ {
+ mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID;
+ mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW;
+ }
+ }
+ }
+}
+
+
+// ============ Instancing ============
+
+// #define MERGE_INSTANCE_DEBUG_INFO 1
+
+#define INSTANCE_VARIABLE_KEY "replace"
+
+static GameData GD;
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will read in a standard key / value file
+// Input : pFilename - the absolute name of the file to read
+// Output : returns the KeyValues of the file, NULL if the file could not be read.
+//-----------------------------------------------------------------------------
+static KeyValues *ReadKeyValuesFile( const char *pFilename )
+{
+ // Read in the gameinfo.txt file and null-terminate it.
+ FILE *fp = fopen( pFilename, "rb" );
+ if ( !fp )
+ return NULL;
+ CUtlVector<char> buf;
+ fseek( fp, 0, SEEK_END );
+ buf.SetSize( ftell( fp ) + 1 );
+ fseek( fp, 0, SEEK_SET );
+ fread( buf.Base(), 1, buf.Count()-1, fp );
+ fclose( fp );
+ buf[buf.Count()-1] = 0;
+
+ KeyValues *kv = new KeyValues( "" );
+ if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ return kv;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will set a secondary lookup path for instances.
+// Input : pszInstancePath - the secondary lookup path
+//-----------------------------------------------------------------------------
+void CMapFile::SetInstancePath( const char *pszInstancePath )
+{
+ strcpy( m_InstancePath, pszInstancePath );
+ V_strlower( m_InstancePath );
+ V_FixSlashes( m_InstancePath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This function will attempt to find a full path given the base and relative names.
+// Input : pszBaseFileName - the base file that referenced this instance
+// pszInstanceFileName - the relative file name of this instance
+// Output : Returns true if it was able to locate the file
+// pszOutFileName - the full path to the file name if located
+//-----------------------------------------------------------------------------
+bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName )
+{
+ char szInstanceFileNameFixed[ MAX_PATH ];
+ const char *pszMapPath = "\\maps\\";
+
+ strcpy( szInstanceFileNameFixed, pszInstanceFileName );
+ V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) );
+ V_FixSlashes( szInstanceFileNameFixed );
+
+ // first, try to find a relative location based upon the Base file name
+ strcpy( pszOutFileName, pszBaseFileName );
+ V_StripFilename( pszOutFileName );
+
+ strcat( pszOutFileName, "\\" );
+ strcat( pszOutFileName, szInstanceFileNameFixed );
+
+ if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
+ {
+ return true;
+ }
+
+ // second, try to find the master 'maps' directory and make it relative from that
+ strcpy( pszOutFileName, pszBaseFileName );
+ V_StripFilename( pszOutFileName );
+ V_RemoveDotSlashes( pszOutFileName );
+ V_FixDoubleSlashes( pszOutFileName );
+ V_strlower( pszOutFileName );
+ strcat( pszOutFileName, "\\" );
+
+ char *pos = strstr( pszOutFileName, pszMapPath );
+ if ( pos )
+ {
+ pos += strlen( pszMapPath );
+ *pos = 0;
+ strcat( pszOutFileName, szInstanceFileNameFixed );
+
+ if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
+ {
+ return true;
+ }
+ }
+
+ if ( m_InstancePath[ 0 ] != 0 )
+ {
+ sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName );
+
+ if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) )
+ {
+ char FullPath[ MAX_PATH ];
+ g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) );
+ strcpy( pszOutFileName, FullPath );
+
+ return true;
+ }
+ }
+
+ pszOutFileName[ 0 ] = 0;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will check the main map for any func_instances. It will
+// also attempt to load in the gamedata file for instancing remapping help.
+// Input : none
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::CheckForInstances( const char *pszFileName )
+{
+ if ( this != g_MainMap )
+ { // all sub-instances will be appended to the main map master list as they are read in
+ // so the main loop below will naturally get to the appended ones.
+ return;
+ }
+
+ char GameInfoPath[ MAX_PATH ];
+
+ g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) );
+ KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath );
+ if ( !GameInfoKV )
+ {
+ Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath );
+ return;
+ }
+
+ const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL );
+ if ( InstancePath )
+ {
+ CMapFile::SetInstancePath( InstancePath );
+ }
+
+ const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL );
+ if ( !GameDataFile )
+ {
+ Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath );
+ return;
+ }
+
+ char FDGPath[ MAX_PATH ];
+ if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) )
+ {
+ if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "", FDGPath, sizeof( FDGPath ) ) )
+ {
+ Msg( "Could not locate GameData file %s\n", GameDataFile );
+ }
+ }
+
+ GD.Load( FDGPath );
+
+ // this list will grow as instances are merged onto it. sub-instances are merged and
+ // automatically done in this processing.
+ for ( int i = 0; i < num_entities; i++ )
+ {
+ char *pEntity = ValueForKey( &entities[ i ], "classname" );
+ if ( !strcmp( pEntity, "func_instance" ) )
+ {
+ char *pInstanceFile = ValueForKey( &entities[ i ], "file" );
+ if ( pInstanceFile[ 0 ] )
+ {
+ char InstancePath[ MAX_PATH ];
+ bool bLoaded = false;
+
+ if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) )
+ {
+ if ( LoadMapFile( InstancePath ) )
+ {
+ MergeInstance( &entities[ i ], g_LoadingMap );
+ delete g_LoadingMap;
+ bLoaded = true;
+ }
+ }
+
+ if ( bLoaded == false )
+ {
+ Color red( 255, 0, 0, 255 );
+
+ ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile );
+ }
+ }
+
+ entities[ i ].numbrushes = 0;
+ entities[ i ].epairs = NULL;
+ }
+ }
+
+ g_LoadingMap = this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will do all of the necessary work to merge the instance
+// into the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance )
+{
+ matrix3x4_t mat;
+ QAngle angles;
+ Vector OriginOffset = pInstanceEntity->origin;
+
+ m_InstanceCount++;
+
+ GetAnglesForKey( pInstanceEntity, "angles", angles );
+ AngleMatrix( angles, OriginOffset, mat );
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map planes from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ // Each pair of planes needs to be added to the main map
+ for ( int i = 0; i < Instance->nummapplanes; i += 2 )
+ {
+ FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map brushes from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_brush_id = 0;
+
+ for( int i = 0; i < nummapbrushes; i++ )
+ {
+ if ( mapbrushes[ i ].id > max_brush_id )
+ {
+ max_brush_id = mapbrushes[ i ].id;
+ }
+ }
+
+ for( int i = 0; i < Instance->nummapbrushes; i++ )
+ {
+ mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ];
+
+ mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ];
+ brush->entitynum += num_entities;
+ brush->brushnum += nummapbrushes;
+
+ if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 )
+ { // world spawn brushes as well as ladders we physically move
+ Vector minsIn = brush->mins;
+ Vector maxsIn = brush->maxs;
+
+ TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs );
+ }
+ else
+ {
+ }
+ brush->id += max_brush_id;
+
+ int index = brush->original_sides - Instance->brushsides;
+ brush->original_sides = &brushsides[ nummapbrushsides + index ];
+ }
+
+ nummapbrushes += Instance->nummapbrushes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map sides from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_side_id = 0;
+
+ for( int i = 0; i < nummapbrushsides; i++ )
+ {
+ if ( brushsides[ i ].id > max_side_id )
+ {
+ max_side_id = brushsides[ i ].id;
+ }
+ }
+
+ for( int i = 0; i < Instance->nummapbrushsides; i++ )
+ {
+ brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ];
+
+ side_t *side = &brushsides[ nummapbrushsides + i ];
+ // The planes got merged & remapped. So you need to search for the output plane index on each side
+ // NOTE: You could optimize this by saving off an index map in MergePlanes
+ side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist );
+ side->id += max_side_id;
+
+ // this could be pre-processed into a list for quicker checking
+ bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 );
+ if ( !bNeedsTranslation )
+ { // check for sides that are part of the world spawn - those need translating
+ for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ )
+ {
+ int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
+
+ if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) )
+ {
+ bNeedsTranslation = true;
+ break;
+ }
+ }
+ }
+ if ( !bNeedsTranslation )
+ { // sides for ladders are outside of the world spawn, but also need translating
+ for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ )
+ {
+ int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
+
+ if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 )
+ {
+ bNeedsTranslation = true;
+ break;
+ }
+ }
+ }
+ if ( bNeedsTranslation )
+ { // we only want to do the adjustment on world spawn brushes, not entity brushes
+ if ( side->winding )
+ {
+ for( int point = 0; point < side->winding->numpoints; point++ )
+ {
+ Vector inPoint = side->winding->p[ point ];
+ VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] );
+ }
+ }
+
+ int planenum = side->planenum;
+ cplane_t inPlane, outPlane;
+ inPlane.normal = mapplanes[ planenum ].normal;
+ inPlane.dist = mapplanes[ planenum ].dist;
+
+ MatrixTransformPlane( InstanceMatrix, inPlane, outPlane );
+ planenum = FindFloatPlane( outPlane.normal, outPlane.dist );
+ side->planenum = planenum;
+
+ brush_texture_t bt = Instance->side_brushtextures[ i ];
+
+ VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis );
+ VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis );
+ bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ];
+ bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ];
+
+ if ( !onlyents )
+ {
+ side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin );
+ }
+ }
+
+ if ( side->pMapDisp )
+ {
+ mapdispinfo_t *disp = side->pMapDisp;
+
+ disp->brushSideID = side->id;
+ Vector inPoint = disp->startPosition;
+ VectorTransform( inPoint, InstanceMatrix, disp->startPosition );
+
+ disp->face.originalface = side;
+ disp->face.texinfo = side->texinfo;
+ disp->face.planenum = side->planenum;
+ disp->entitynum += num_entities;
+
+ for( int point = 0; point < disp->face.w->numpoints; point++ )
+ {
+ Vector inPoint = disp->face.w->p[ point ];
+ VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] );
+ }
+
+ }
+ }
+
+ nummapbrushsides += Instance->nummapbrushsides;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will look for replace parameters in the function instance
+// to see if there is anything in the epair that should be replaced.
+// Input : pPair - the epair with the value
+// pInstanceEntity - the func_instance that may ahve replace keywords
+// Output : pPair - the value field may be updated
+//-----------------------------------------------------------------------------
+void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity )
+{
+ char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ];
+ bool Overwritten = false;
+
+ strcpy( NewValue, pPair->value );
+ for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next )
+ {
+ if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 )
+ {
+ char InstanceVariable[ MAX_KEYVALUE_LEN ];
+
+ strcpy( InstanceVariable, epInstance->value );
+
+ char *ValuePos = strchr( InstanceVariable, ' ' );
+ if ( !ValuePos )
+ {
+ continue;
+ }
+ *ValuePos = 0;
+ ValuePos++;
+
+ strcpy( Value, NewValue );
+ if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) )
+ {
+ Overwritten = true;
+ break;
+ }
+ }
+ }
+
+ if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 )
+ {
+ free( pPair->value );
+ pPair->value = copystring( NewValue );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the entities from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_entity_id = 0;
+ char temp[ 2048 ];
+ char NameFixup[ 128 ];
+ entity_t *WorldspawnEnt = NULL;
+ GameData::TNameFixup FixupStyle;
+
+ char *pTargetName = ValueForKey( pInstanceEntity, "targetname" );
+ char *pName = ValueForKey( pInstanceEntity, "name" );
+ if ( pTargetName[ 0 ] )
+ {
+ sprintf( NameFixup, "%s", pTargetName );
+ }
+ else if ( pName[ 0 ] )
+ {
+ sprintf( NameFixup, "%s", pName );
+ }
+ else
+ {
+ sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount );
+ }
+
+ for( int i = 0; i < num_entities; i++ )
+ {
+ char *pID = ValueForKey( &entities[ i ], "hammerid" );
+ if ( pID[ 0 ] )
+ {
+ int value = atoi( pID );
+ if ( value > max_entity_id )
+ {
+ max_entity_id = value;
+ }
+ }
+ }
+
+ FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) );
+
+ for( int i = 0; i < Instance->num_entities; i++ )
+ {
+ entities[ num_entities + i ] = Instance->entities[ i ];
+
+ entity_t *entity = &entities[ num_entities + i ];
+ entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes );
+
+ char *pID = ValueForKey( entity, "hammerid" );
+ if ( pID[ 0 ] )
+ {
+ int value = atoi( pID );
+ value += max_entity_id;
+ sprintf( temp, "%d", value );
+
+ SetKeyValue( entity, "hammerid", temp );
+ }
+
+ char *pEntity = ValueForKey( entity, "classname" );
+ if ( strcmpi( pEntity, "worldspawn" ) == 0 )
+ {
+ WorldspawnEnt = entity;
+ }
+ else
+ {
+ Vector inOrigin = entity->origin;
+ VectorTransform( inOrigin, InstanceMatrix, entity->origin );
+
+ // search for variables coming from the func_instance to replace inside of the instance
+ // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet.
+ for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next )
+ {
+ ReplaceInstancePair( ep, pInstanceEntity );
+ }
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Remapping class %s\n", pEntity );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle );
+ if ( EntClass )
+ {
+ for( int i = 0; i < EntClass->GetVariableCount(); i++ )
+ {
+ GDinputvariable *EntVar = EntClass->GetVariableAt( i );
+ char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() );
+ if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) )
+ {
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ SetKeyValue( entity, EntVar->GetName(), temp );
+ }
+ else
+ {
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ }
+ }
+ }
+
+ if ( strcmpi( pEntity, "func_simpleladder" ) == 0 )
+ { // hate having to do this, but the key values are so screwed up
+ AddLadderKeys( entity );
+/* Vector vInNormal, vOutNormal;
+
+ vInNormal.x = FloatForKey( entity, "normal.x" );
+ vInNormal.y = FloatForKey( entity, "normal.y" );
+ vInNormal.z = FloatForKey( entity, "normal.z" );
+ VectorRotate( vInNormal, InstanceMatrix, vOutNormal );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x );
+ SetKeyValue( entity, "normal.x", temp );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y );
+ SetKeyValue( entity, "normal.y", temp );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z );
+ SetKeyValue( entity, "normal.z", temp );*/
+ }
+ }
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i );
+ Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush );
+ Msg( " KV Pairs:\n" );
+ for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next )
+ {
+ Msg( " %s %s\n", ep->key, ep->value );
+ }
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ }
+
+ // search for variables coming from the func_instance to replace inside of the instance
+ // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet.
+ for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
+ {
+ ReplaceInstancePair( Connection->m_Pair, pInstanceEntity );
+ }
+
+ for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
+ {
+ char *newValue, *oldValue;
+ char origValue[ 4096 ];
+ int extraLen = 0;
+
+ oldValue = Connection->m_Pair->value;
+ strcpy( origValue, oldValue );
+ char *pos = strchr( origValue, ',' );
+ if ( pos )
+ { // null terminate the first field
+ *pos = NULL;
+ extraLen = strlen( pos + 1) + 1; // for the comma we just null'd
+ }
+
+ if ( GD.RemapNameField( origValue, temp, FixupStyle ) )
+ {
+ newValue = new char [ strlen( temp ) + extraLen + 1 ];
+ strcpy( newValue, temp );
+ if ( pos )
+ {
+ strcat( newValue, "," );
+ strcat( newValue, pos + 1 );
+ }
+
+ Connection->m_Pair->value = newValue;
+ delete oldValue;
+ }
+ }
+
+ num_entities += Instance->num_entities;
+
+ MoveBrushesToWorldGeneral( WorldspawnEnt );
+ WorldspawnEnt->numbrushes = 0;
+ WorldspawnEnt->epairs = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will translate overlays from the instance into
+// the main map.
+// Input : InstanceEntityNum - the entity number of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ )
+ {
+ Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
+ }
+ for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ )
+ {
+ Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP
+// loader is used, otherwise the file is assumed to be in VMF format.
+// Input : pszFileName - Full path of the map file to load.
+//-----------------------------------------------------------------------------
+bool LoadMapFile( const char *pszFileName )
+{
+ bool bLoadingManifest = false;
+ CManifest *pMainManifest = NULL;
+ ChunkFileResult_t eResult;
+
+ //
+ // Dummy this up for the texture handling. This can be removed when old .MAP file
+ // support is removed.
+ //
+ g_nMapFileVersion = 400;
+
+ const char *pszExtension =V_GetFileExtension( pszFileName );
+ if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 )
+ {
+ pMainManifest = new CManifest();
+ if ( pMainManifest->LoadVMFManifest( pszFileName ) )
+ {
+ eResult = ChunkFile_Ok;
+ pszFileName = pMainManifest->GetInstancePath();
+ }
+ else
+ {
+ eResult = ChunkFile_Fail;
+ }
+ bLoadingManifest = true;
+ }
+ else
+ {
+ //
+ // Open the file.
+ //
+ CChunkFile File;
+ eResult = File.Open(pszFileName, ChunkFile_Read);
+
+ //
+ // Read the file.
+ //
+ if (eResult == ChunkFile_Ok)
+ {
+ int index = g_Maps.AddToTail( new CMapFile() );
+ g_LoadingMap = g_Maps[ index ];
+ if ( g_MainMap == NULL )
+ {
+ g_MainMap = g_LoadingMap;
+ }
+
+ if ( g_MainMap == g_LoadingMap || verbose )
+ {
+ Msg( "Loading %s\n", pszFileName );
+ }
+
+
+ // reset the displacement info count
+ // nummapdispinfo = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0);
+ Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0);
+
+ File.PushHandlers(&Handlers);
+
+ //
+ // Read the sub-chunks. We ignore keys in the root of the file.
+ //
+ while (eResult == ChunkFile_Ok)
+ {
+ eResult = File.ReadChunk();
+ }
+
+ File.PopHandlers();
+ }
+ else
+ {
+ Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult));
+ }
+ }
+
+ if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF))
+ {
+ // Update the overlay/side list(s).
+ Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays );
+ OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays );
+
+ g_LoadingMap->CheckForInstances( pszFileName );
+
+ if ( pMainManifest )
+ {
+ pMainManifest->CordonWorld();
+ }
+
+ ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ for (int i=0 ; i<g_MainMap->entities[0].numbrushes ; i++)
+ {
+ // HLTOOLS: Raise map limits
+ if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER)
+ {
+ continue; // no valid points
+ }
+
+ AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ }
+
+ qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes);
+ qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes);
+ qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides);
+ qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels);
+ qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels);
+ qprintf ("%5i entities\n", g_LoadingMap->num_entities);
+ qprintf ("%5i planes\n", g_LoadingMap->nummapplanes);
+ qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals);
+ qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2],
+ g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]);
+
+ //TestExpandBrushes();
+
+ // Clear the error reporting
+ g_MapError.ClearState();
+ }
+
+ if ( g_MainMap == g_LoadingMap )
+ {
+ num_entities = g_MainMap->num_entities;
+ memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) );
+ }
+ g_LoadingMap->ForceFuncAreaPortalWindowContents();
+
+ return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) );
+}
+
+ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
+{
+ return g_LoadingMap->LoadSideCallback( pFile, pSideInfo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// pParent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
+{
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ {
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ }
+
+ pSideInfo->pSide = &brushsides[nummapbrushsides];
+
+ side_t *side = pSideInfo->pSide;
+ mapbrush_t *b = pSideInfo->pBrush;
+ g_MapError.BrushSide( pSideInfo->nSideIndex++ );
+
+ // initialize the displacement info
+ pSideInfo->pSide->pMapDisp = NULL;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp );
+
+ //
+ // Read the side chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ side->contents |= pSideInfo->nBaseContents;
+ side->surf |= pSideInfo->nBaseFlags;
+ pSideInfo->td.flags |= pSideInfo->nBaseFlags;
+
+ if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ side->contents |= CONTENTS_DETAIL;
+ }
+
+ if (fulldetail )
+ {
+ side->contents &= ~CONTENTS_DETAIL;
+ }
+
+ if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
+ {
+ side->contents |= CONTENTS_SOLID;
+ }
+
+ // hints and skips are never detail, and have no content
+ if (side->surf & (SURF_HINT|SURF_SKIP) )
+ {
+ side->contents = 0;
+ }
+
+ //
+ // find the plane number
+ //
+ int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]);
+ if (planenum != -1)
+ {
+ //
+ // See if the plane has been used already.
+ //
+ int k;
+ for ( k = 0; k < b->numsides; k++)
+ {
+ side_t *s2 = b->original_sides + k;
+ if (s2->planenum == planenum)
+ {
+ g_MapError.ReportWarning("duplicate plane");
+ break;
+ }
+ if ( s2->planenum == (planenum^1) )
+ {
+ g_MapError.ReportWarning("mirrored plane");
+ break;
+ }
+ }
+
+ //
+ // If the plane hasn't been used already, keep this side.
+ //
+ if (k == b->numsides)
+ {
+ side = b->original_sides + b->numsides;
+ side->planenum = planenum;
+ if ( !onlyents )
+ {
+ side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin);
+ }
+
+ // save the td off in case there is an origin brush and we
+ // have to recalculate the texinfo
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ side_brushtextures[nummapbrushsides] = pSideInfo->td;
+ nummapbrushsides++;
+ b->numsides++;
+
+#ifdef VSVMFIO
+ // Tell Maya We Have Another Side
+ if ( CVmfImport::GetVmfImporter() )
+ {
+ CVmfImport::GetVmfImporter()->AddSideCallback(
+ b, side, pSideInfo->td,
+ pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] );
+ }
+#endif // VSVMFIO
+
+ }
+ }
+ else
+ {
+ g_MapError.ReportWarning("plane with no normal");
+ }
+ }
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : szKey -
+// szValue -
+// pSideInfo -
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo)
+{
+ if (!stricmp(szKey, "plane"))
+ {
+ int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)",
+ &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2],
+ &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2],
+ &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]);
+
+ if (nRead != 9)
+ {
+ g_MapError.ReportError("parsing plane definition");
+ }
+ }
+ else if (!stricmp(szKey, "material"))
+ {
+ // Get the material name.
+ if( g_ReplaceMaterials )
+ {
+ szValue = ReplaceMaterialName( szValue );
+ }
+
+ strcpy(pSideInfo->td.name, szValue);
+ g_MapError.TextureState(szValue);
+
+ // Find default flags and values for this material.
+ int mt = FindMiptex(pSideInfo->td.name);
+ pSideInfo->td.flags = textureref[mt].flags;
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel;
+
+ pSideInfo->pSide->contents = textureref[mt].contents;
+ pSideInfo->pSide->surf = pSideInfo->td.flags;
+ }
+ else if (!stricmp(szKey, "uaxis"))
+ {
+ int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]);
+ if (nRead != 5)
+ {
+ g_MapError.ReportError("parsing U axis definition");
+ }
+ }
+ else if (!stricmp(szKey, "vaxis"))
+ {
+ int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]);
+ if (nRead != 5)
+ {
+ g_MapError.ReportError("parsing V axis definition");
+ }
+ }
+ else if (!stricmp(szKey, "lightmapscale"))
+ {
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue);
+ if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f)
+ {
+ g_MapError.ReportWarning("luxel size of 0");
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize;
+ }
+ pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale;
+ if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale)
+ {
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale;
+ }
+ }
+ else if (!stricmp(szKey, "contents"))
+ {
+ pSideInfo->pSide->contents |= atoi(szValue);
+ }
+ else if (!stricmp(szKey, "flags"))
+ {
+ pSideInfo->td.flags |= atoi(szValue);
+ pSideInfo->pSide->surf = pSideInfo->td.flags;
+ }
+ else if (!stricmp(szKey, "id"))
+ {
+ pSideInfo->pSide->id = atoi( szValue );
+ }
+ else if (!stricmp(szKey, "smoothing_groups"))
+ {
+ pSideInfo->pSide->smoothingGroups = atoi( szValue );
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reads the connections chunk of the entity.
+// Input : pFile - Chunk file to load from.
+// pLoadEntity - Structure to receive loaded entity information.
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses a key/value pair from the entity connections chunk.
+// Input : szKey - Key indicating the name of the entity output.
+// szValue - Comma delimited fields in the following format:
+// <target>,<input>,<parameter>,<delay>,<times to fire>
+// pLoadEntity - Structure to receive loaded entity information.
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
+{
+ return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity );
+}
+
+ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
+{
+ //
+ // Create new input and fill it out.
+ //
+ epair_t *pOutput = new epair_t;
+
+ pOutput->key = new char [strlen(szKey) + 1];
+ pOutput->value = new char [strlen(szValue) + 1];
+
+ strcpy(pOutput->key, szKey);
+ strcpy(pOutput->value, szValue);
+
+ m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs );
+
+ //
+ // Append it to the end of epairs list.
+ //
+ pOutput->next = NULL;
+
+ if (!pLoadEntity->pEntity->epairs)
+ {
+ pLoadEntity->pEntity->epairs = pOutput;
+ }
+ else
+ {
+ epair_t *ep;
+ for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next )
+ {
+ }
+ ep->next = pOutput;
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// pParent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ if (nummapbrushes == MAX_MAP_BRUSHES)
+ {
+ g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES");
+ }
+
+ mapbrush_t *b = &mapbrushes[nummapbrushes];
+ b->original_sides = &brushsides[nummapbrushsides];
+ b->entitynum = num_entities-1;
+ b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush;
+
+ LoadSide_t SideInfo;
+ SideInfo.pBrush = b;
+ SideInfo.nSideIndex = 0;
+ SideInfo.nBaseContents = pLoadEntity->nBaseContents;
+ SideInfo.nBaseFlags = pLoadEntity->nBaseFlags;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo);
+
+ //
+ // Read the solid chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ // get the content for the entire brush
+ b->contents = BrushContents (b);
+
+ // allow detail brushes to be removed
+ if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) )
+ {
+ b->numsides = 0;
+ return(ChunkFile_Ok);
+ }
+
+ // allow water brushes to be removed
+ if (nowater && (b->contents & MASK_WATER) )
+ {
+ b->numsides = 0;
+ return(ChunkFile_Ok);
+ }
+
+ // create windings for sides and bounds for brush
+ MakeBrushWindings (b);
+
+ //
+ // brushes that will not be visible at all will never be
+ // used as bsp splitters
+ //
+ // only do this on the world entity
+ //
+ if ( b->entitynum == 0 )
+ {
+ if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ if ( g_ClipTexinfo < 0 )
+ {
+ g_ClipTexinfo = b->original_sides[0].texinfo;
+ }
+ c_clipbrushes++;
+ for (int i=0 ; i<b->numsides ; i++)
+ {
+ b->original_sides[i].texinfo = TEXINFO_NODE;
+ }
+ }
+ }
+
+ //
+ // origin brushes are removed, but they set
+ // the rotation origin for the rest of the brushes
+ // in the entity. After the entire entity is parsed,
+ // the planenums and texinfos will be adjusted for
+ // the origin brush
+ //
+ if (b->contents & CONTENTS_ORIGIN)
+ {
+ char string[32];
+ Vector origin;
+
+ if (num_entities == 1)
+ {
+ Error("Brush %i: origin brushes not allowed in world", b->id);
+ }
+
+ VectorAdd (b->mins, b->maxs, origin);
+ VectorScale (origin, 0.5, origin);
+
+ sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
+ SetKeyValue (&entities[b->entitynum], "origin", string);
+
+ VectorCopy (origin, entities[b->entitynum].origin);
+
+ // don't keep this brush
+ b->numsides = 0;
+
+ return(ChunkFile_Ok);
+ }
+
+#ifdef VSVMFIO
+ if ( CVmfImport::GetVmfImporter() )
+ {
+ CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b );
+ }
+#endif // VSVMFIO
+
+ //
+ // find a map brushes with displacement surfaces and remove them from the "world"
+ //
+ if( HasDispInfo( b ) )
+ {
+ // add the base face data to the displacement surface
+ DispGetFaceInfo( b );
+
+ // don't keep this brush
+ b->numsides = 0;
+
+ return( ChunkFile_Ok );
+ }
+
+ AddBrushBevels (b);
+
+ nummapbrushes++;
+ pLoadEntity->pEntity->numbrushes++;
+ }
+ else
+ {
+ return eResult;
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// parent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush)
+{
+ if (!stricmp(szKey, "id"))
+ {
+ pLoadBrush->id = atoi(szValue);
+ g_MapError.BrushState(pLoadBrush->id);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+/*
+================
+TestExpandBrushes
+
+Expands all the brush planes and saves a new map out
+================
+*/
+void CMapFile::TestExpandBrushes (void)
+{
+ FILE *f;
+ side_t *s;
+ int i, j, bn;
+ winding_t *w;
+ char *name = "expanded.map";
+ mapbrush_t *brush;
+ vec_t dist;
+
+ Msg ("writing %s\n", name);
+ f = fopen (name, "wb");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+ fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" );
+
+
+ for (bn=0 ; bn<nummapbrushes ; bn++)
+ {
+ brush = &mapbrushes[bn];
+ fprintf (f, "{\n");
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = brush->original_sides + i;
+ dist = mapplanes[s->planenum].dist;
+ for (j=0 ; j<3 ; j++)
+ dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
+
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, 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 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n",
+ TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
+
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+ Error ("can't proceed after expanding brushes");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load in the displacement info "chunk" from the .map file into the
+// vbsp map displacement info data structure
+// Output: return the pointer to the displacement map
+//-----------------------------------------------------------------------------
+mapdispinfo_t *ParseDispInfoChunk( void )
+{
+ int i, j;
+ int vertCount;
+ mapdispinfo_t *pMapDispInfo;
+
+ //
+ // check to see if we exceeded the maximum displacement info list size
+ //
+ if( nummapdispinfo > MAX_MAP_DISPINFO )
+ g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO");
+
+ // get a pointer to the next available displacement info slot
+ pMapDispInfo = &mapdispinfo[nummapdispinfo];
+ nummapdispinfo++;
+
+ //
+ // get the chunk opener - "{"
+ //
+ GetToken( false );
+ if( strcmp( token, "{" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" );
+
+ //
+ //
+ // get the displacement info attribs
+ //
+ //
+
+ // power
+ GetToken( true );
+ pMapDispInfo->power = atoi( token );
+
+ // u and v mapping axes
+ for( i = 0; i < 2; i++ )
+ {
+ GetToken( false );
+ if( strcmp( token, "[" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" );
+
+ for( j = 0; j < 3; j++ )
+ {
+ GetToken( false );
+
+ if( i == 0 )
+ {
+ pMapDispInfo->uAxis[j] = atof( token );
+ }
+ else
+ {
+ pMapDispInfo->vAxis[j] = atof( token );
+ }
+ }
+
+ GetToken( false );
+ if( strcmp( token, "]" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" );
+ }
+
+ // max displacement value
+ if( g_nMapFileVersion < 350 )
+ {
+ GetToken( false );
+ pMapDispInfo->maxDispDist = atof( token );
+ }
+
+ // minimum tesselation value
+ GetToken( false );
+ pMapDispInfo->minTess = atoi( token );
+
+ // light smoothing angle
+ GetToken( false );
+ pMapDispInfo->smoothingAngle = atof( token );
+
+ //
+ // get the displacement info displacement normals
+ //
+ GetToken( true );
+ pMapDispInfo->vectorDisps[0][0] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[0][1] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[0][2] = atof( token );
+
+ vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) );
+ for( i = 1; i < vertCount; i++ )
+ {
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][0] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][1] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][2] = atof( token );
+ }
+
+ //
+ // get the displacement info displacement values
+ //
+ GetToken( true );
+ pMapDispInfo->dispDists[0] = atof( token );
+
+ for( i = 1; i < vertCount; i++ )
+ {
+ GetToken( false );
+ pMapDispInfo->dispDists[i] = atof( token );
+ }
+
+ //
+ // get the chunk closer - "}"
+ //
+ GetToken( true );
+ if( strcmp( token, "}" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" );
+
+ // return the index of the displacement info slot
+ return pMapDispInfo;
+}
+
+
diff --git a/mp/src/utils/vbsp/map.h b/mp/src/utils/vbsp/map.h
index 44b1d934..c7b0e5b8 100644
--- a/mp/src/utils/vbsp/map.h
+++ b/mp/src/utils/vbsp/map.h
@@ -1,18 +1,18 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef MAP_H
-#define MAP_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-// All the brush sides referenced by info_no_dynamic_shadow entities.
-extern CUtlVector<int> g_NoDynamicShadowSides;
-
-
-#endif // MAP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MAP_H
+#define MAP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// All the brush sides referenced by info_no_dynamic_shadow entities.
+extern CUtlVector<int> g_NoDynamicShadowSides;
+
+
+#endif // MAP_H
diff --git a/mp/src/utils/vbsp/materialpatch.cpp b/mp/src/utils/vbsp/materialpatch.cpp
index e05b979d..96a30edb 100644
--- a/mp/src/utils/vbsp/materialpatch.cpp
+++ b/mp/src/utils/vbsp/materialpatch.cpp
@@ -1,440 +1,440 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "vbsp.h"
-#include "UtlBuffer.h"
-#include "utlsymbol.h"
-#include "utlrbtree.h"
-#include "KeyValues.h"
-#include "bsplib.h"
-#include "materialpatch.h"
-#include "tier1/strtools.h"
-
-// case insensitive
-static CUtlSymbolTable s_SymbolTable( 0, 32, true );
-
-struct NameTranslationLookup_t
-{
- CUtlSymbol m_OriginalFileName;
- CUtlSymbol m_PatchFileName;
-};
-
-static bool NameTranslationLessFunc( NameTranslationLookup_t const& src1,
- NameTranslationLookup_t const& src2 )
-{
- return src1.m_PatchFileName < src2.m_PatchFileName;
-}
-
-CUtlRBTree<NameTranslationLookup_t, int> s_MapPatchedMatToOriginalMat( 0, 256, NameTranslationLessFunc );
-
-void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName )
-{
- NameTranslationLookup_t newEntry;
-
- newEntry.m_OriginalFileName = s_SymbolTable.AddString( pOriginalMaterialName );
- newEntry.m_PatchFileName = s_SymbolTable.AddString( pNewMaterialName );
-
- s_MapPatchedMatToOriginalMat.Insert( newEntry );
-}
-
-const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName )
-{
- const char *pRetName = NULL;
- int id;
- NameTranslationLookup_t lookup;
- lookup.m_PatchFileName = s_SymbolTable.AddString( pPatchMaterialName );
- do
- {
- id = s_MapPatchedMatToOriginalMat.Find( lookup );
- if( id >= 0 )
- {
- NameTranslationLookup_t &found = s_MapPatchedMatToOriginalMat[id];
- lookup.m_PatchFileName = found.m_OriginalFileName;
- pRetName = s_SymbolTable.String( found.m_OriginalFileName );
- }
- } while( id >= 0 );
- if( !pRetName )
- {
- // This isn't a patched material, so just return the original name.
- return pPatchMaterialName;
- }
- return pRetName;
-}
-
-
-void CreateMaterialPatchRecursive( KeyValues *pOriginalKeyValues, KeyValues *pPatchKeyValues, int nKeys, const MaterialPatchInfo_t *pInfo )
-{
- int i;
- for( i = 0; i < nKeys; i++ )
- {
- const char *pVal = pOriginalKeyValues->GetString( pInfo[i].m_pKey, NULL );
- if( !pVal )
- continue;
- if( pInfo[i].m_pRequiredOriginalValue && Q_stricmp( pVal, pInfo[i].m_pRequiredOriginalValue ) != 0 )
- continue;
- pPatchKeyValues->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
- }
- KeyValues *pScan;
- for( pScan = pOriginalKeyValues->GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
- {
- CreateMaterialPatchRecursive( pScan, pPatchKeyValues->FindKey( pScan->GetName(), true ), nKeys, pInfo );
- }
-}
-
-//-----------------------------------------------------------------------------
-// A version which allows you to patch multiple key values
-//-----------------------------------------------------------------------------
-void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
- int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType )
-{
- char pOldVMTFile[ 512 ];
- char pNewVMTFile[ 512 ];
-
- AddNewTranslation( pOriginalMaterialName, pNewMaterialName );
-
- Q_snprintf( pOldVMTFile, 512, "materials/%s.vmt", pOriginalMaterialName );
- Q_snprintf( pNewVMTFile, 512, "materials/%s.vmt", pNewMaterialName );
-
-// printf( "Creating material patch file %s which points at %s\n", newVMTFile, oldVMTFile );
-
- KeyValues *kv = new KeyValues( "patch" );
- if ( !kv )
- {
- Error( "Couldn't allocate KeyValues for %s!!!", pNewMaterialName );
- }
-
- kv->SetString( "include", pOldVMTFile );
-
- const char *pSectionName = (nPatchType == PATCH_INSERT) ? "insert" : "replace";
- KeyValues *section = kv->FindKey( pSectionName, true );
-
- if( nPatchType == PATCH_REPLACE )
- {
- char name[512];
- Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pOriginalMaterialName ) );
- KeyValues *origkv = new KeyValues( "blah" );
-
- if ( !origkv->LoadFromFile( g_pFileSystem, name ) )
- {
- origkv->deleteThis();
- Assert( 0 );
- return;
- }
-
- CreateMaterialPatchRecursive( origkv, section, nKeys, pInfo );
- origkv->deleteThis();
- }
- else
- {
- for ( int i = 0; i < nKeys; ++i )
- {
- section->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
- }
- }
-
- // Write patched .vmt into a memory buffer
- CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
- kv->RecursiveSaveToFile( buf, 0 );
-
- // Add to pak file for this .bsp
- AddBufferToPak( GetPakFile(), pNewVMTFile, (void*)buf.Base(), buf.TellPut(), true );
-
- // Cleanup
- kv->deleteThis();
-}
-
-
-//-----------------------------------------------------------------------------
-// Patches a single keyvalue in a material
-//-----------------------------------------------------------------------------
-void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
- const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType )
-{
- MaterialPatchInfo_t info;
- info.m_pKey = pNewKey;
- info.m_pValue = pNewValue;
- CreateMaterialPatch( pOriginalMaterialName, pNewMaterialName, 1, &info, nPatchType );
-}
-
-
-//-----------------------------------------------------------------------------
-// Scan material + all subsections for key
-//-----------------------------------------------------------------------------
-static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName )
-{
- const char *pVal;
- pVal = pKeyValues->GetString( pKeyName, NULL );
- if ( pVal != NULL )
- return true;
-
- for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
- {
- if ( DoesMaterialHaveKey( pSubKey, pKeyName) )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Scan material + all subsections for key/value pair
-//-----------------------------------------------------------------------------
-static bool DoesMaterialHaveKeyValuePair( KeyValues *pKeyValues, const char *pKeyName, const char *pSearchValue )
-{
- const char *pVal;
- pVal = pKeyValues->GetString( pKeyName, NULL );
- if ( pVal != NULL && ( Q_stricmp( pSearchValue, pVal ) == 0 ) )
- return true;
-
- for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
- {
- if ( DoesMaterialHaveKeyValuePair( pSubKey, pKeyName, pSearchValue ) )
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Scan material + all subsections for key
-//-----------------------------------------------------------------------------
-bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName )
-{
- char name[512];
- Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
- KeyValues *kv = new KeyValues( "blah" );
-
- if ( !kv->LoadFromFile( g_pFileSystem, name ) )
- {
- kv->deleteThis();
- return NULL;
- }
-
- bool retVal = DoesMaterialHaveKey( kv, pKeyName );
-
- kv->deleteThis();
- return retVal;
-}
-
-//-----------------------------------------------------------------------------
-// Scan material + all subsections for key/value pair
-//-----------------------------------------------------------------------------
-bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue )
-{
- char name[512];
- Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
- KeyValues *kv = new KeyValues( "blah" );
-
- if ( !kv->LoadFromFile( g_pFileSystem, name ) )
- {
- kv->deleteThis();
- return NULL;
- }
-
- bool retVal = DoesMaterialHaveKeyValuePair( kv, pKeyName, pSearchValue );
-
- kv->deleteThis();
- return retVal;
-}
-
-//-----------------------------------------------------------------------------
-// Gets a material value from a material. Ignores all patches
-//-----------------------------------------------------------------------------
-bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
-{
- char name[512];
- Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
- KeyValues *kv = new KeyValues( "blah" );
-
- if ( !kv->LoadFromFile( g_pFileSystem, name ) )
- {
-// Assert( 0 );
- kv->deleteThis();
- return NULL;
- }
-
- const char *pTmpValue = kv->GetString( pKey, NULL );
- if( pTmpValue )
- {
- Q_strncpy( pValue, pTmpValue, len );
- }
-
- kv->deleteThis();
- return ( pTmpValue != NULL );
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds the original material associated with a patched material
-//-----------------------------------------------------------------------------
-MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain )
-{
- MaterialSystemMaterial_t matID;
- matID = FindMaterial( GetOriginalMaterialNameForPatchedMaterial( materialName ), pFound, bComplain );
- return matID;
-}
-
-
-//-----------------------------------------------------------------------------
-// Load keyvalues from the local pack file, or from a file
-//-----------------------------------------------------------------------------
-bool LoadKeyValuesFromPackOrFile( const char *pFileName, KeyValues *pKeyValues )
-{
- CUtlBuffer buf;
- if ( ReadFileFromPak( GetPakFile(), pFileName, true, buf ) )
- {
- return pKeyValues->LoadFromBuffer( pFileName, buf );
- }
-
- return pKeyValues->LoadFromFile( g_pFileSystem, pFileName );
-}
-
-
-//-----------------------------------------------------------------------------
-// VMT parser
-//-----------------------------------------------------------------------------
-static void InsertKeyValues( KeyValues &dst, KeyValues& src, bool bCheckForExistence )
-{
- KeyValues *pSrcVar = src.GetFirstSubKey();
- while( pSrcVar )
- {
- if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
- {
- switch( pSrcVar->GetDataType() )
- {
- case KeyValues::TYPE_STRING:
- dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
- break;
- case KeyValues::TYPE_INT:
- dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
- break;
- case KeyValues::TYPE_FLOAT:
- dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
- break;
- case KeyValues::TYPE_PTR:
- dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
- break;
- }
- }
- pSrcVar = pSrcVar->GetNextKey();
- }
-}
-
-static void ExpandPatchFile( KeyValues &keyValues )
-{
- int nCount = 0;
- while( nCount < 10 && stricmp( keyValues.GetName(), "patch" ) == 0 )
- {
-// WriteKeyValuesToFile( "patch.txt", keyValues );
- const char *pIncludeFileName = keyValues.GetString( "include" );
- if( !pIncludeFileName )
- return;
-
- KeyValues * includeKeyValues = new KeyValues( "vmt" );
- int nBufLen = Q_strlen( pIncludeFileName ) + Q_strlen( "materials/.vmt" ) + 1;
- char *pFileName = ( char * )stackalloc( nBufLen );
- Q_strncpy( pFileName, pIncludeFileName, nBufLen );
- bool bSuccess = LoadKeyValuesFromPackOrFile( pFileName, includeKeyValues );
- if ( !bSuccess )
- {
- includeKeyValues->deleteThis();
- return;
- }
-
- KeyValues *pInsertSection = keyValues.FindKey( "insert" );
- if( pInsertSection )
- {
- InsertKeyValues( *includeKeyValues, *pInsertSection, false );
- keyValues = *includeKeyValues;
- }
-
- KeyValues *pReplaceSection = keyValues.FindKey( "replace" );
- if( pReplaceSection )
- {
- InsertKeyValues( *includeKeyValues, *pReplaceSection, true );
- keyValues = *includeKeyValues;
- }
-
- // Could add other commands here, like "delete", "rename", etc.
-
- includeKeyValues->deleteThis();
- nCount++;
- }
-
- if( nCount >= 10 )
- {
- Warning( "Infinite recursion in patch file?\n" );
- }
-}
-
-KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags )
-{
- // Load the underlying file
- KeyValues *kv = new KeyValues( "blah" );
-
- char pFullMaterialName[512];
- Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
- if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
- {
- // Assert( 0 );
- kv->deleteThis();
- return NULL;
- }
-
- if( nFlags & LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH )
- {
- ExpandPatchFile( *kv );
- }
-
- return kv;
-}
-
-void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv )
-{
- char pFullMaterialName[512];
- Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
-
- // Write patched .vmt into a memory buffer
- CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
- kv->RecursiveSaveToFile( buf, 0 );
-
- // Add to pak file for this .bsp
- AddBufferToPak( GetPakFile(), pFullMaterialName, (void*)buf.Base(), buf.TellPut(), true );
-
- // Cleanup
- kv->deleteThis();
-}
-
-
-//-----------------------------------------------------------------------------
-// Gets a keyvalue from a *patched* material
-//-----------------------------------------------------------------------------
-bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
-{
- // Load the underlying file so that we can check if env_cubemap is in there.
- KeyValues *kv = new KeyValues( "blah" );
-
- char pFullMaterialName[512];
- Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
- if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
- {
-// Assert( 0 );
- kv->deleteThis();
- return NULL;
- }
-
- ExpandPatchFile( *kv );
-
- const char *pTmpValue = kv->GetString( pKey, NULL );
- if( pTmpValue )
- {
- Q_strncpy( pValue, pTmpValue, len );
- }
-
- kv->deleteThis();
- return ( pTmpValue != NULL );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "vbsp.h"
+#include "UtlBuffer.h"
+#include "utlsymbol.h"
+#include "utlrbtree.h"
+#include "KeyValues.h"
+#include "bsplib.h"
+#include "materialpatch.h"
+#include "tier1/strtools.h"
+
+// case insensitive
+static CUtlSymbolTable s_SymbolTable( 0, 32, true );
+
+struct NameTranslationLookup_t
+{
+ CUtlSymbol m_OriginalFileName;
+ CUtlSymbol m_PatchFileName;
+};
+
+static bool NameTranslationLessFunc( NameTranslationLookup_t const& src1,
+ NameTranslationLookup_t const& src2 )
+{
+ return src1.m_PatchFileName < src2.m_PatchFileName;
+}
+
+CUtlRBTree<NameTranslationLookup_t, int> s_MapPatchedMatToOriginalMat( 0, 256, NameTranslationLessFunc );
+
+void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName )
+{
+ NameTranslationLookup_t newEntry;
+
+ newEntry.m_OriginalFileName = s_SymbolTable.AddString( pOriginalMaterialName );
+ newEntry.m_PatchFileName = s_SymbolTable.AddString( pNewMaterialName );
+
+ s_MapPatchedMatToOriginalMat.Insert( newEntry );
+}
+
+const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName )
+{
+ const char *pRetName = NULL;
+ int id;
+ NameTranslationLookup_t lookup;
+ lookup.m_PatchFileName = s_SymbolTable.AddString( pPatchMaterialName );
+ do
+ {
+ id = s_MapPatchedMatToOriginalMat.Find( lookup );
+ if( id >= 0 )
+ {
+ NameTranslationLookup_t &found = s_MapPatchedMatToOriginalMat[id];
+ lookup.m_PatchFileName = found.m_OriginalFileName;
+ pRetName = s_SymbolTable.String( found.m_OriginalFileName );
+ }
+ } while( id >= 0 );
+ if( !pRetName )
+ {
+ // This isn't a patched material, so just return the original name.
+ return pPatchMaterialName;
+ }
+ return pRetName;
+}
+
+
+void CreateMaterialPatchRecursive( KeyValues *pOriginalKeyValues, KeyValues *pPatchKeyValues, int nKeys, const MaterialPatchInfo_t *pInfo )
+{
+ int i;
+ for( i = 0; i < nKeys; i++ )
+ {
+ const char *pVal = pOriginalKeyValues->GetString( pInfo[i].m_pKey, NULL );
+ if( !pVal )
+ continue;
+ if( pInfo[i].m_pRequiredOriginalValue && Q_stricmp( pVal, pInfo[i].m_pRequiredOriginalValue ) != 0 )
+ continue;
+ pPatchKeyValues->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
+ }
+ KeyValues *pScan;
+ for( pScan = pOriginalKeyValues->GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
+ {
+ CreateMaterialPatchRecursive( pScan, pPatchKeyValues->FindKey( pScan->GetName(), true ), nKeys, pInfo );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A version which allows you to patch multiple key values
+//-----------------------------------------------------------------------------
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType )
+{
+ char pOldVMTFile[ 512 ];
+ char pNewVMTFile[ 512 ];
+
+ AddNewTranslation( pOriginalMaterialName, pNewMaterialName );
+
+ Q_snprintf( pOldVMTFile, 512, "materials/%s.vmt", pOriginalMaterialName );
+ Q_snprintf( pNewVMTFile, 512, "materials/%s.vmt", pNewMaterialName );
+
+// printf( "Creating material patch file %s which points at %s\n", newVMTFile, oldVMTFile );
+
+ KeyValues *kv = new KeyValues( "patch" );
+ if ( !kv )
+ {
+ Error( "Couldn't allocate KeyValues for %s!!!", pNewMaterialName );
+ }
+
+ kv->SetString( "include", pOldVMTFile );
+
+ const char *pSectionName = (nPatchType == PATCH_INSERT) ? "insert" : "replace";
+ KeyValues *section = kv->FindKey( pSectionName, true );
+
+ if( nPatchType == PATCH_REPLACE )
+ {
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pOriginalMaterialName ) );
+ KeyValues *origkv = new KeyValues( "blah" );
+
+ if ( !origkv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ origkv->deleteThis();
+ Assert( 0 );
+ return;
+ }
+
+ CreateMaterialPatchRecursive( origkv, section, nKeys, pInfo );
+ origkv->deleteThis();
+ }
+ else
+ {
+ for ( int i = 0; i < nKeys; ++i )
+ {
+ section->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
+ }
+ }
+
+ // Write patched .vmt into a memory buffer
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ kv->RecursiveSaveToFile( buf, 0 );
+
+ // Add to pak file for this .bsp
+ AddBufferToPak( GetPakFile(), pNewVMTFile, (void*)buf.Base(), buf.TellPut(), true );
+
+ // Cleanup
+ kv->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Patches a single keyvalue in a material
+//-----------------------------------------------------------------------------
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType )
+{
+ MaterialPatchInfo_t info;
+ info.m_pKey = pNewKey;
+ info.m_pValue = pNewValue;
+ CreateMaterialPatch( pOriginalMaterialName, pNewMaterialName, 1, &info, nPatchType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key
+//-----------------------------------------------------------------------------
+static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName )
+{
+ const char *pVal;
+ pVal = pKeyValues->GetString( pKeyName, NULL );
+ if ( pVal != NULL )
+ return true;
+
+ for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ if ( DoesMaterialHaveKey( pSubKey, pKeyName) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key/value pair
+//-----------------------------------------------------------------------------
+static bool DoesMaterialHaveKeyValuePair( KeyValues *pKeyValues, const char *pKeyName, const char *pSearchValue )
+{
+ const char *pVal;
+ pVal = pKeyValues->GetString( pKeyName, NULL );
+ if ( pVal != NULL && ( Q_stricmp( pSearchValue, pVal ) == 0 ) )
+ return true;
+
+ for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ if ( DoesMaterialHaveKeyValuePair( pSubKey, pKeyName, pSearchValue ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key
+//-----------------------------------------------------------------------------
+bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ bool retVal = DoesMaterialHaveKey( kv, pKeyName );
+
+ kv->deleteThis();
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key/value pair
+//-----------------------------------------------------------------------------
+bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ bool retVal = DoesMaterialHaveKeyValuePair( kv, pKeyName, pSearchValue );
+
+ kv->deleteThis();
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// Gets a material value from a material. Ignores all patches
+//-----------------------------------------------------------------------------
+bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+// Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ const char *pTmpValue = kv->GetString( pKey, NULL );
+ if( pTmpValue )
+ {
+ Q_strncpy( pValue, pTmpValue, len );
+ }
+
+ kv->deleteThis();
+ return ( pTmpValue != NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the original material associated with a patched material
+//-----------------------------------------------------------------------------
+MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain )
+{
+ MaterialSystemMaterial_t matID;
+ matID = FindMaterial( GetOriginalMaterialNameForPatchedMaterial( materialName ), pFound, bComplain );
+ return matID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Load keyvalues from the local pack file, or from a file
+//-----------------------------------------------------------------------------
+bool LoadKeyValuesFromPackOrFile( const char *pFileName, KeyValues *pKeyValues )
+{
+ CUtlBuffer buf;
+ if ( ReadFileFromPak( GetPakFile(), pFileName, true, buf ) )
+ {
+ return pKeyValues->LoadFromBuffer( pFileName, buf );
+ }
+
+ return pKeyValues->LoadFromFile( g_pFileSystem, pFileName );
+}
+
+
+//-----------------------------------------------------------------------------
+// VMT parser
+//-----------------------------------------------------------------------------
+static void InsertKeyValues( KeyValues &dst, KeyValues& src, bool bCheckForExistence )
+{
+ KeyValues *pSrcVar = src.GetFirstSubKey();
+ while( pSrcVar )
+ {
+ if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
+ {
+ switch( pSrcVar->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
+ break;
+ case KeyValues::TYPE_INT:
+ dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
+ break;
+ case KeyValues::TYPE_FLOAT:
+ dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
+ break;
+ case KeyValues::TYPE_PTR:
+ dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
+ break;
+ }
+ }
+ pSrcVar = pSrcVar->GetNextKey();
+ }
+}
+
+static void ExpandPatchFile( KeyValues &keyValues )
+{
+ int nCount = 0;
+ while( nCount < 10 && stricmp( keyValues.GetName(), "patch" ) == 0 )
+ {
+// WriteKeyValuesToFile( "patch.txt", keyValues );
+ const char *pIncludeFileName = keyValues.GetString( "include" );
+ if( !pIncludeFileName )
+ return;
+
+ KeyValues * includeKeyValues = new KeyValues( "vmt" );
+ int nBufLen = Q_strlen( pIncludeFileName ) + Q_strlen( "materials/.vmt" ) + 1;
+ char *pFileName = ( char * )stackalloc( nBufLen );
+ Q_strncpy( pFileName, pIncludeFileName, nBufLen );
+ bool bSuccess = LoadKeyValuesFromPackOrFile( pFileName, includeKeyValues );
+ if ( !bSuccess )
+ {
+ includeKeyValues->deleteThis();
+ return;
+ }
+
+ KeyValues *pInsertSection = keyValues.FindKey( "insert" );
+ if( pInsertSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pInsertSection, false );
+ keyValues = *includeKeyValues;
+ }
+
+ KeyValues *pReplaceSection = keyValues.FindKey( "replace" );
+ if( pReplaceSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pReplaceSection, true );
+ keyValues = *includeKeyValues;
+ }
+
+ // Could add other commands here, like "delete", "rename", etc.
+
+ includeKeyValues->deleteThis();
+ nCount++;
+ }
+
+ if( nCount >= 10 )
+ {
+ Warning( "Infinite recursion in patch file?\n" );
+ }
+}
+
+KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags )
+{
+ // Load the underlying file
+ KeyValues *kv = new KeyValues( "blah" );
+
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+ if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
+ {
+ // Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ if( nFlags & LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH )
+ {
+ ExpandPatchFile( *kv );
+ }
+
+ return kv;
+}
+
+void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv )
+{
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+
+ // Write patched .vmt into a memory buffer
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ kv->RecursiveSaveToFile( buf, 0 );
+
+ // Add to pak file for this .bsp
+ AddBufferToPak( GetPakFile(), pFullMaterialName, (void*)buf.Base(), buf.TellPut(), true );
+
+ // Cleanup
+ kv->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a keyvalue from a *patched* material
+//-----------------------------------------------------------------------------
+bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
+{
+ // Load the underlying file so that we can check if env_cubemap is in there.
+ KeyValues *kv = new KeyValues( "blah" );
+
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+ if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
+ {
+// Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ ExpandPatchFile( *kv );
+
+ const char *pTmpValue = kv->GetString( pKey, NULL );
+ if( pTmpValue )
+ {
+ Q_strncpy( pValue, pTmpValue, len );
+ }
+
+ kv->deleteThis();
+ return ( pTmpValue != NULL );
+}
diff --git a/mp/src/utils/vbsp/materialpatch.h b/mp/src/utils/vbsp/materialpatch.h
index 334fe013..f04a95d9 100644
--- a/mp/src/utils/vbsp/materialpatch.h
+++ b/mp/src/utils/vbsp/materialpatch.h
@@ -1,60 +1,60 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef MATERIALPATCH_H
-#define MATERIALPATCH_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "utilmatlib.h"
-
-struct MaterialPatchInfo_t
-{
- const char *m_pKey;
- const char *m_pRequiredOriginalValue; // NULL if you don't require one.
- const char *m_pValue;
- MaterialPatchInfo_t()
- {
- memset( this, 0, sizeof( *this ) );
- }
-};
-
-enum MaterialPatchType_t
-{
- PATCH_INSERT = 0, // Add the key no matter what
- PATCH_REPLACE, // Add the key only if it exists
-};
-
-void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
- const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType );
-
-// A version which allows you to use multiple key values
-void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
- int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType );
-
-// This gets a keyvalue from the *unpatched* version of the passed-in material
-bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
-
-// Gets a keyvalue from a *patched* material
-bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
-
-const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName );
-
-MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain = true );
-
-bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue );
-bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName );
-
-enum LoadMaterialKeyValuesFlags_t
-{
- LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH = 1,
-};
-
-KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags );
-void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv );
-
-#endif // MATERIALPATCH_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MATERIALPATCH_H
+#define MATERIALPATCH_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utilmatlib.h"
+
+struct MaterialPatchInfo_t
+{
+ const char *m_pKey;
+ const char *m_pRequiredOriginalValue; // NULL if you don't require one.
+ const char *m_pValue;
+ MaterialPatchInfo_t()
+ {
+ memset( this, 0, sizeof( *this ) );
+ }
+};
+
+enum MaterialPatchType_t
+{
+ PATCH_INSERT = 0, // Add the key no matter what
+ PATCH_REPLACE, // Add the key only if it exists
+};
+
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType );
+
+// A version which allows you to use multiple key values
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType );
+
+// This gets a keyvalue from the *unpatched* version of the passed-in material
+bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
+
+// Gets a keyvalue from a *patched* material
+bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
+
+const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName );
+
+MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain = true );
+
+bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue );
+bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName );
+
+enum LoadMaterialKeyValuesFlags_t
+{
+ LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH = 1,
+};
+
+KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags );
+void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv );
+
+#endif // MATERIALPATCH_H
diff --git a/mp/src/utils/vbsp/materialsub.cpp b/mp/src/utils/vbsp/materialsub.cpp
index d8eb40df..b02063ef 100644
--- a/mp/src/utils/vbsp/materialsub.cpp
+++ b/mp/src/utils/vbsp/materialsub.cpp
@@ -1,90 +1,90 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: This file loads a KeyValues file containing material name mappings.
-// When the bsp is compiled, all materials listed in the file will
-// be replaced by the second material in the pair.
-//
-//=============================================================================
-
-#include "vbsp.h"
-#include "materialsub.h"
-#include "KeyValues.h"
-#include "tier1/strtools.h"
-
-bool g_ReplaceMaterials = false;
-
-static KeyValues *kv = 0;
-static KeyValues *allMapKeys = 0;
-static KeyValues *curMapKeys = 0;
-
-//-----------------------------------------------------------------------------
-// Purpose: Loads the KeyValues file for materials replacements
-//-----------------------------------------------------------------------------
-void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname )
-{
- // Careful with static variables
- if( kv )
- {
- kv->deleteThis();
- kv = 0;
- }
- if( allMapKeys )
- allMapKeys = 0;
- if( curMapKeys )
- curMapKeys = 0;
-
- Msg( "Loading Replacement Keys\n" );
-
- // Attach the path to the keyValues file
- char path[1024];
- Q_snprintf( path, sizeof( path ), "%scfg\\materialsub.cfg", gamedir );
-
- // Load the keyvalues file
- kv = new KeyValues( "MaterialReplacements" );
-
- Msg( "File path: %s", path );
- if( !kv->LoadFromFile( g_pFileSystem, path ) )
- {
- Msg( "Failed to load KeyValues file!\n" );
- g_ReplaceMaterials = false;
- kv->deleteThis();
- kv = 0;
- return;
- }
-
- // Load global replace keys
- allMapKeys = kv->FindKey( "AllMaps", true );
-
- // Load keys for the current map
- curMapKeys = kv->FindKey( mapname );
-
- allMapKeys->ChainKeyValue( curMapKeys );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Deletes all keys
-//-----------------------------------------------------------------------------
-void DeleteMaterialReplacementKeys( void )
-{
- if( kv )
- {
- kv->deleteThis();
- kv = 0;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Replace the passed-in material name with a replacement name, if one exists
-//-----------------------------------------------------------------------------
-const char* ReplaceMaterialName( const char *name )
-{
- // Look for the material name in the global and map KeyValues
- // If it's not there, just return the original name
-
- // HACK: This stinks - KeyValues won't take a string with '/' in it.
- // If they did, this could be a simple pointer swap.
- char newName[1024];
- Q_strncpy( newName, name, sizeof( newName ) );
- Q_FixSlashes( newName );
- return allMapKeys->GetString( newName, name );
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: This file loads a KeyValues file containing material name mappings.
+// When the bsp is compiled, all materials listed in the file will
+// be replaced by the second material in the pair.
+//
+//=============================================================================
+
+#include "vbsp.h"
+#include "materialsub.h"
+#include "KeyValues.h"
+#include "tier1/strtools.h"
+
+bool g_ReplaceMaterials = false;
+
+static KeyValues *kv = 0;
+static KeyValues *allMapKeys = 0;
+static KeyValues *curMapKeys = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads the KeyValues file for materials replacements
+//-----------------------------------------------------------------------------
+void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname )
+{
+ // Careful with static variables
+ if( kv )
+ {
+ kv->deleteThis();
+ kv = 0;
+ }
+ if( allMapKeys )
+ allMapKeys = 0;
+ if( curMapKeys )
+ curMapKeys = 0;
+
+ Msg( "Loading Replacement Keys\n" );
+
+ // Attach the path to the keyValues file
+ char path[1024];
+ Q_snprintf( path, sizeof( path ), "%scfg\\materialsub.cfg", gamedir );
+
+ // Load the keyvalues file
+ kv = new KeyValues( "MaterialReplacements" );
+
+ Msg( "File path: %s", path );
+ if( !kv->LoadFromFile( g_pFileSystem, path ) )
+ {
+ Msg( "Failed to load KeyValues file!\n" );
+ g_ReplaceMaterials = false;
+ kv->deleteThis();
+ kv = 0;
+ return;
+ }
+
+ // Load global replace keys
+ allMapKeys = kv->FindKey( "AllMaps", true );
+
+ // Load keys for the current map
+ curMapKeys = kv->FindKey( mapname );
+
+ allMapKeys->ChainKeyValue( curMapKeys );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes all keys
+//-----------------------------------------------------------------------------
+void DeleteMaterialReplacementKeys( void )
+{
+ if( kv )
+ {
+ kv->deleteThis();
+ kv = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Replace the passed-in material name with a replacement name, if one exists
+//-----------------------------------------------------------------------------
+const char* ReplaceMaterialName( const char *name )
+{
+ // Look for the material name in the global and map KeyValues
+ // If it's not there, just return the original name
+
+ // HACK: This stinks - KeyValues won't take a string with '/' in it.
+ // If they did, this could be a simple pointer swap.
+ char newName[1024];
+ Q_strncpy( newName, name, sizeof( newName ) );
+ Q_FixSlashes( newName );
+ return allMapKeys->GetString( newName, name );
} \ No newline at end of file
diff --git a/mp/src/utils/vbsp/materialsub.h b/mp/src/utils/vbsp/materialsub.h
index c1de0698..ce6cac42 100644
--- a/mp/src/utils/vbsp/materialsub.h
+++ b/mp/src/utils/vbsp/materialsub.h
@@ -1,25 +1,25 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: This file loads a KeyValues file containing material name mappings.
-// When the bsp is compiled, all materials listed in the file will
-// be replaced by the second material in the pair.
-//
-//=============================================================================
-
-#ifndef MATERIALSUB_H
-#define MATERIALSUB_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-extern bool g_ReplaceMaterials;
-
-// Setup / Cleanup
-void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname );
-void DeleteMaterialReplacementKeys( void );
-
-// Takes a material name and returns it's replacement, if there is one.
-// If there isn't a replacement, it returns the original.
-const char* ReplaceMaterialName( const char *name );
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: This file loads a KeyValues file containing material name mappings.
+// When the bsp is compiled, all materials listed in the file will
+// be replaced by the second material in the pair.
+//
+//=============================================================================
+
+#ifndef MATERIALSUB_H
+#define MATERIALSUB_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+extern bool g_ReplaceMaterials;
+
+// Setup / Cleanup
+void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname );
+void DeleteMaterialReplacementKeys( void );
+
+// Takes a material name and returns it's replacement, if there is one.
+// If there isn't a replacement, it returns the original.
+const char* ReplaceMaterialName( const char *name );
+
#endif // MATERIALSUB_H \ No newline at end of file
diff --git a/mp/src/utils/vbsp/nodraw.cpp b/mp/src/utils/vbsp/nodraw.cpp
index 4fa493ea..954ea671 100644
--- a/mp/src/utils/vbsp/nodraw.cpp
+++ b/mp/src/utils/vbsp/nodraw.cpp
@@ -1,32 +1,32 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-
-Vector draw_mins, draw_maxs;
-
-void Draw_ClearWindow (void)
-{
-}
-
-//============================================================
-
-#define GLSERV_PORT 25001
-
-
-void GLS_BeginScene (void)
-{
-}
-
-void GLS_Winding (winding_t *w, int code)
-{
-}
-
-void GLS_EndScene (void)
-{
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+Vector draw_mins, draw_maxs;
+
+void Draw_ClearWindow (void)
+{
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+
+void GLS_BeginScene (void)
+{
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+}
+
+void GLS_EndScene (void)
+{
+}
diff --git a/mp/src/utils/vbsp/normals.cpp b/mp/src/utils/vbsp/normals.cpp
index 1696342c..3a910cb6 100644
--- a/mp/src/utils/vbsp/normals.cpp
+++ b/mp/src/utils/vbsp/normals.cpp
@@ -1,50 +1,50 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "bsplib.h"
-#include "vbsp.h"
-
-
-void SaveVertexNormals( void )
-{
- int i, j;
- dface_t *f;
- texinfo_t *tex;
-
-
- g_numvertnormalindices = 0;
- g_numvertnormals = 0;
-
- for( i = 0 ;i<numfaces ; i++ )
- {
- f = &dfaces[i];
- tex = &texinfo[f->texinfo];
-
- for( j = 0; j < f->numedges; j++ )
- {
- if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
- {
- Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES (%d)", MAX_MAP_VERTNORMALINDICES );
- }
-
- g_vertnormalindices[g_numvertnormalindices] = g_numvertnormals;
- g_numvertnormalindices++;
- }
-
- // Add this face plane's normal.
- // Note: this doesn't do an exhaustive vertex normal match because the vrad does it.
- // The result is that a little extra memory is wasted coming out of vbsp, but it
- // goes away after vrad.
- if( g_numvertnormals == MAX_MAP_VERTNORMALS )
- {
- Error( "g_numvertnormals == MAX_MAP_VERTNORMALS (%d)", MAX_MAP_VERTNORMALS );
- }
-
- g_vertnormals[g_numvertnormals] = dplanes[f->planenum].normal;
- g_numvertnormals++;
- }
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "bsplib.h"
+#include "vbsp.h"
+
+
+void SaveVertexNormals( void )
+{
+ int i, j;
+ dface_t *f;
+ texinfo_t *tex;
+
+
+ g_numvertnormalindices = 0;
+ g_numvertnormals = 0;
+
+ for( i = 0 ;i<numfaces ; i++ )
+ {
+ f = &dfaces[i];
+ tex = &texinfo[f->texinfo];
+
+ for( j = 0; j < f->numedges; j++ )
+ {
+ if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
+ {
+ Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES (%d)", MAX_MAP_VERTNORMALINDICES );
+ }
+
+ g_vertnormalindices[g_numvertnormalindices] = g_numvertnormals;
+ g_numvertnormalindices++;
+ }
+
+ // Add this face plane's normal.
+ // Note: this doesn't do an exhaustive vertex normal match because the vrad does it.
+ // The result is that a little extra memory is wasted coming out of vbsp, but it
+ // goes away after vrad.
+ if( g_numvertnormals == MAX_MAP_VERTNORMALS )
+ {
+ Error( "g_numvertnormals == MAX_MAP_VERTNORMALS (%d)", MAX_MAP_VERTNORMALS );
+ }
+
+ g_vertnormals[g_numvertnormals] = dplanes[f->planenum].normal;
+ g_numvertnormals++;
+ }
+}
diff --git a/mp/src/utils/vbsp/overlay.cpp b/mp/src/utils/vbsp/overlay.cpp
index fcd2e924..edceba9a 100644
--- a/mp/src/utils/vbsp/overlay.cpp
+++ b/mp/src/utils/vbsp/overlay.cpp
@@ -1,487 +1,487 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "disp_vbsp.h"
-#include "builddisp.h"
-#include "mathlib/vmatrix.h"
-
-void Overlay_BuildBasisOrigin( doverlay_t *pOverlay );
-
-// Overlay list.
-CUtlVector<mapoverlay_t> g_aMapOverlays;
-CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int Overlay_GetFromEntity( entity_t *pMapEnt )
-{
- int iAccessorID = -1;
-
- // Allocate the new overlay.
- int iOverlay = g_aMapOverlays.AddToTail();
- mapoverlay_t *pMapOverlay = &g_aMapOverlays[iOverlay];
-
- // Get the overlay data.
- pMapOverlay->nId = g_aMapOverlays.Count() - 1;
-
- if ( ValueForKey( pMapEnt, "targetname" )[ 0 ] != '\0' )
- {
- // Overlay has a name, remember it's ID for accessing
- iAccessorID = pMapOverlay->nId;
- }
-
- pMapOverlay->flU[0] = FloatForKey( pMapEnt, "StartU" );
- pMapOverlay->flU[1] = FloatForKey( pMapEnt, "EndU" );
- pMapOverlay->flV[0] = FloatForKey( pMapEnt, "StartV" );
- pMapOverlay->flV[1] = FloatForKey( pMapEnt, "EndV" );
-
- pMapOverlay->flFadeDistMinSq = FloatForKey( pMapEnt, "fademindist" );
- if ( pMapOverlay->flFadeDistMinSq > 0 )
- {
- pMapOverlay->flFadeDistMinSq *= pMapOverlay->flFadeDistMinSq;
- }
-
- pMapOverlay->flFadeDistMaxSq = FloatForKey( pMapEnt, "fademaxdist" );
- if ( pMapOverlay->flFadeDistMaxSq > 0 )
- {
- pMapOverlay->flFadeDistMaxSq *= pMapOverlay->flFadeDistMaxSq;
- }
-
- GetVectorForKey( pMapEnt, "BasisOrigin", pMapOverlay->vecOrigin );
-
- pMapOverlay->m_nRenderOrder = IntForKey( pMapEnt, "RenderOrder" );
- if ( pMapOverlay->m_nRenderOrder < 0 || pMapOverlay->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
- Error( "Overlay (%s) at %f %f %f has invalid render order (%d).\n", ValueForKey( pMapEnt, "material" ),
- pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z,
- pMapOverlay->m_nRenderOrder );
-
- GetVectorForKey( pMapEnt, "uv0", pMapOverlay->vecUVPoints[0] );
- GetVectorForKey( pMapEnt, "uv1", pMapOverlay->vecUVPoints[1] );
- GetVectorForKey( pMapEnt, "uv2", pMapOverlay->vecUVPoints[2] );
- GetVectorForKey( pMapEnt, "uv3", pMapOverlay->vecUVPoints[3] );
-
- GetVectorForKey( pMapEnt, "BasisU", pMapOverlay->vecBasis[0] );
- GetVectorForKey( pMapEnt, "BasisV", pMapOverlay->vecBasis[1] );
- GetVectorForKey( pMapEnt, "BasisNormal", pMapOverlay->vecBasis[2] );
-
- const char *pMaterialName = ValueForKey( pMapEnt, "material" );
- Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
- if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
- {
- Error( "Overlay Material Name (%s) too long! > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
- return -1;
- }
- strcpy( pMapOverlay->szMaterialName, pMaterialName );
-
- // Convert the sidelist to side id(s).
- const char *pSideList = ValueForKey( pMapEnt, "sides" );
- char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
- strcpy( pTmpList, pSideList );
- const char *pScan = strtok( pTmpList, " " );
- if ( !pScan )
- return iAccessorID;
-
- pMapOverlay->aSideList.Purge();
- pMapOverlay->aFaceList.Purge();
-
- do
- {
- int nSideId;
- if ( sscanf( pScan, "%d", &nSideId ) == 1 )
- {
- pMapOverlay->aSideList.AddToTail( nSideId );
- }
- } while ( ( pScan = strtok( NULL, " " ) ) );
-
- return iAccessorID;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-side_t *GetSide( int nSideId )
-{
- for( int iSide = 0; iSide < g_LoadingMap->nummapbrushsides; ++iSide )
- {
- if ( g_LoadingMap->brushsides[iSide].id == nSideId )
- return &g_LoadingMap->brushsides[iSide];
- }
-
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void Overlay_UpdateSideLists( int StartIndex )
-{
- int nMapOverlayCount = g_aMapOverlays.Count();
- for( int iMapOverlay = StartIndex; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
- {
- mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( iMapOverlay );
- if ( pMapOverlay )
- {
- int nSideCount = pMapOverlay->aSideList.Count();
- for( int iSide = 0; iSide < nSideCount; ++iSide )
- {
- side_t *pSide = GetSide( pMapOverlay->aSideList[iSide] );
- if ( pSide )
- {
- if ( pSide->aOverlayIds.Find( pMapOverlay->nId ) == -1 )
- {
- pSide->aOverlayIds.AddToTail( pMapOverlay->nId );
- }
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void OverlayTransition_UpdateSideLists( int StartIndex )
-{
- int nOverlayCount = g_aMapWaterOverlays.Count();
- for( int iOverlay = StartIndex; iOverlay < nOverlayCount; ++iOverlay )
- {
- mapoverlay_t *pOverlay = &g_aMapWaterOverlays.Element( iOverlay );
- if ( pOverlay )
- {
- int nSideCount = pOverlay->aSideList.Count();
- for( int iSide = 0; iSide < nSideCount; ++iSide )
- {
- side_t *pSide = GetSide( pOverlay->aSideList[iSide] );
- if ( pSide )
- {
- if ( pSide->aWaterOverlayIds.Find( pOverlay->nId ) == -1 )
- {
- pSide->aWaterOverlayIds.AddToTail( pOverlay->nId );
- }
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void Overlay_AddFaceToLists( int iFace, side_t *pSide )
-{
- int nOverlayIdCount = pSide->aOverlayIds.Count();
- for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
- {
- mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( pSide->aOverlayIds[iOverlayId] );
- if ( pMapOverlay )
- {
- if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
- {
- pMapOverlay->aFaceList.AddToTail( iFace );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide )
-{
- int nOverlayIdCount = pSide->aWaterOverlayIds.Count();
- for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
- {
- mapoverlay_t *pMapOverlay = &g_aMapWaterOverlays.Element( pSide->aWaterOverlayIds[iOverlayId] - ( MAX_MAP_OVERLAYS + 1 ) );
- if ( pMapOverlay )
- {
- if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
- {
- pMapOverlay->aFaceList.AddToTail( iFace );
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void Overlay_EmitOverlayFace( mapoverlay_t *pMapOverlay )
-{
- Assert( g_nOverlayCount < MAX_MAP_OVERLAYS );
- if ( g_nOverlayCount >= MAX_MAP_OVERLAYS )
- {
- Error ( "Too Many Overlays!\nMAX_MAP_OVERLAYS = %d", MAX_MAP_OVERLAYS );
- return;
- }
-
- doverlay_t *pOverlay = &g_Overlays[g_nOverlayCount];
- doverlayfade_t *pOverlayFade = &g_OverlayFades[g_nOverlayCount];
-
- g_nOverlayCount++;
-
- // Conver the map overlay into a .bsp overlay (doverlay_t).
- if ( pOverlay )
- {
- pOverlay->nId = pMapOverlay->nId;
-
- pOverlay->flU[0] = pMapOverlay->flU[0];
- pOverlay->flU[1] = pMapOverlay->flU[1];
- pOverlay->flV[0] = pMapOverlay->flV[0];
- pOverlay->flV[1] = pMapOverlay->flV[1];
-
- VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
- VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
- VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
- VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
-
- VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
-
- VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
-
- pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
-
- // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
- pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
- pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
- pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
-
- // Encode whether or not the v axis should be flipped.
- Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
- if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
- {
- pOverlay->vecUVPoints[3].z = 1.0f;
- }
-
- // Texinfo.
- texinfo_t texInfo;
- texInfo.flags = 0;
- texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
- for( int iVec = 0; iVec < 2; ++iVec )
- {
- for( int iAxis = 0; iAxis < 3; ++iAxis )
- {
- texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
- texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
- }
-
- texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
- texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
- }
- pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
-
- // Face List
- int nFaceCount = pMapOverlay->aFaceList.Count();
- Assert( nFaceCount < OVERLAY_BSP_FACE_COUNT );
- if ( nFaceCount >= OVERLAY_BSP_FACE_COUNT )
- {
- Error( "Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
- return;
- }
-
- pOverlay->SetFaceCount( nFaceCount );
- for( int iFace = 0; iFace < nFaceCount; ++iFace )
- {
- pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
- }
- }
-
- // Convert the map overlay fade data into a .bsp overlay fade (doverlayfade_t).
- if ( pOverlayFade )
- {
- pOverlayFade->flFadeDistMinSq = pMapOverlay->flFadeDistMinSq;
- pOverlayFade->flFadeDistMaxSq = pMapOverlay->flFadeDistMaxSq;
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void OverlayTransition_EmitOverlayFace( mapoverlay_t *pMapOverlay )
-{
- Assert( g_nWaterOverlayCount < MAX_MAP_WATEROVERLAYS );
- if ( g_nWaterOverlayCount >= MAX_MAP_WATEROVERLAYS )
- {
- Error ( "Too many water overlays!\nMAX_MAP_WATEROVERLAYS = %d", MAX_MAP_WATEROVERLAYS );
- return;
- }
-
- dwateroverlay_t *pOverlay = &g_WaterOverlays[g_nWaterOverlayCount];
- g_nWaterOverlayCount++;
-
- // Conver the map overlay into a .bsp overlay (doverlay_t).
- if ( pOverlay )
- {
- pOverlay->nId = pMapOverlay->nId;
-
- pOverlay->flU[0] = pMapOverlay->flU[0];
- pOverlay->flU[1] = pMapOverlay->flU[1];
- pOverlay->flV[0] = pMapOverlay->flV[0];
- pOverlay->flV[1] = pMapOverlay->flV[1];
-
- VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
- VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
- VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
- VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
-
- VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
-
- VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
-
- pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
-
- // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
- pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
- pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
- pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
-
- // Encode whether or not the v axis should be flipped.
- Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
- if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
- {
- pOverlay->vecUVPoints[3].z = 1.0f;
- }
-
- // Texinfo.
- texinfo_t texInfo;
- texInfo.flags = 0;
- texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
- for( int iVec = 0; iVec < 2; ++iVec )
- {
- for( int iAxis = 0; iAxis < 3; ++iAxis )
- {
- texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
- texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
- }
-
- texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
- texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
- }
- pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
-
- // Face List
- int nFaceCount = pMapOverlay->aFaceList.Count();
- Assert( nFaceCount < WATEROVERLAY_BSP_FACE_COUNT );
- if ( nFaceCount >= WATEROVERLAY_BSP_FACE_COUNT )
- {
- Error( "Water Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
- return;
- }
-
- pOverlay->SetFaceCount( nFaceCount );
- for( int iFace = 0; iFace < nFaceCount; ++iFace )
- {
- pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void Overlay_EmitOverlayFaces( void )
-{
- int nMapOverlayCount = g_aMapOverlays.Count();
- for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
- {
- Overlay_EmitOverlayFace( &g_aMapOverlays.Element( iMapOverlay ) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void OverlayTransition_EmitOverlayFaces( void )
-{
- int nMapOverlayCount = g_aMapWaterOverlays.Count();
- for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
- {
- OverlayTransition_EmitOverlayFace( &g_aMapWaterOverlays.Element( iMapOverlay ) );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// These routines were mostly stolen from MapOverlay.cpp in Hammer
-//-----------------------------------------------------------------------------
-#define OVERLAY_BASIS_U 0
-#define OVERLAY_BASIS_V 1
-#define OVERLAY_BASIS_NORMAL 2
-#define OVERLAY_HANDLES_COUNT 4
-
-
-inline void TransformPoint( const VMatrix& matrix, Vector &point )
-{
- Vector orgVector = point;
- matrix.V3Mul( orgVector, point );
-}
-
-
-inline bool fequal( float value, float target, float delta) { return ( (value<(target+delta))&&(value>(target-delta)) ); }
-
-
-//-----------------------------------------------------------------------------
-// Purpose: this function translate / rotate an overlay.
-// Input : pOverlay - the overlay to be translated
-// OriginOffset - the translation
-// AngleOffset - the rotation
-// Matrix - the translation / rotation matrix
-// Output : none
-//-----------------------------------------------------------------------------
-void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix )
-{
- VMatrix tmpMatrix( Matrix );
-
- Vector temp = pOverlay->vecOrigin;
- VectorTransform( temp, Matrix, pOverlay->vecOrigin );
-
- // erase move component
- tmpMatrix.SetTranslation( vec3_origin );
-
- // check if matrix would still change something
- if ( !tmpMatrix.IsIdentity() )
- {
- // make sure axes are normalized (they should be anyways)
- pOverlay->vecBasis[OVERLAY_BASIS_U].NormalizeInPlace();
- pOverlay->vecBasis[OVERLAY_BASIS_V].NormalizeInPlace();
-
- Vector vecU = pOverlay->vecBasis[OVERLAY_BASIS_U];
- Vector vecV = pOverlay->vecBasis[OVERLAY_BASIS_V];
- Vector vecNormal = pOverlay->vecBasis[OVERLAY_BASIS_NORMAL];
-
- TransformPoint( tmpMatrix, vecU );
- TransformPoint( tmpMatrix, vecV );
- TransformPoint( tmpMatrix, vecNormal );
-
- float fScaleU = vecU.Length();
- float fScaleV = vecV.Length();
- float flScaleNormal = vecNormal.Length();
-
- bool bIsUnit = ( fequal( fScaleU, 1.0f, 0.0001 ) && fequal( fScaleV, 1.0f, 0.0001 ) && fequal( flScaleNormal, 1.0f, 0.0001 ) );
- bool bIsPerp = ( fequal( DotProduct( vecU, vecV ), 0.0f, 0.0025 ) && fequal( DotProduct( vecU, vecNormal ), 0.0f, 0.0025 ) && fequal( DotProduct( vecV, vecNormal ), 0.0f, 0.0025 ) );
-
- // if ( fequal(fScaleU,1,0.0001) && fequal(fScaleV,1,0.0001) && fequal(DotProduct( vecU, vecV ),0,0.0025) )
- if ( bIsUnit && bIsPerp )
- {
- // transformation doesnt scale or shear anything, so just update base axes
- pOverlay->vecBasis[OVERLAY_BASIS_U] = vecU;
- pOverlay->vecBasis[OVERLAY_BASIS_V] = vecV;
- pOverlay->vecBasis[OVERLAY_BASIS_NORMAL] = vecNormal;
- }
- else
- {
- // more complex transformation, move UV coordinates, but leave base axes
- for ( int iHandle=0; iHandle<OVERLAY_HANDLES_COUNT;iHandle++)
- {
- Vector vecUV = pOverlay->vecUVPoints[iHandle];
- Vector vecPos = ( vecUV.x * pOverlay->vecBasis[OVERLAY_BASIS_U] + vecUV.y * pOverlay->vecBasis[OVERLAY_BASIS_V] );
-
- // to transform in world space
- TransformPoint( tmpMatrix, vecPos );
-
- vecUV.x = pOverlay->vecBasis[OVERLAY_BASIS_U].Dot( vecPos );
- vecUV.y = pOverlay->vecBasis[OVERLAY_BASIS_V].Dot( vecPos );
-
- pOverlay->vecUVPoints[iHandle] = vecUV;
- }
- }
- }
-
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "builddisp.h"
+#include "mathlib/vmatrix.h"
+
+void Overlay_BuildBasisOrigin( doverlay_t *pOverlay );
+
+// Overlay list.
+CUtlVector<mapoverlay_t> g_aMapOverlays;
+CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int Overlay_GetFromEntity( entity_t *pMapEnt )
+{
+ int iAccessorID = -1;
+
+ // Allocate the new overlay.
+ int iOverlay = g_aMapOverlays.AddToTail();
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays[iOverlay];
+
+ // Get the overlay data.
+ pMapOverlay->nId = g_aMapOverlays.Count() - 1;
+
+ if ( ValueForKey( pMapEnt, "targetname" )[ 0 ] != '\0' )
+ {
+ // Overlay has a name, remember it's ID for accessing
+ iAccessorID = pMapOverlay->nId;
+ }
+
+ pMapOverlay->flU[0] = FloatForKey( pMapEnt, "StartU" );
+ pMapOverlay->flU[1] = FloatForKey( pMapEnt, "EndU" );
+ pMapOverlay->flV[0] = FloatForKey( pMapEnt, "StartV" );
+ pMapOverlay->flV[1] = FloatForKey( pMapEnt, "EndV" );
+
+ pMapOverlay->flFadeDistMinSq = FloatForKey( pMapEnt, "fademindist" );
+ if ( pMapOverlay->flFadeDistMinSq > 0 )
+ {
+ pMapOverlay->flFadeDistMinSq *= pMapOverlay->flFadeDistMinSq;
+ }
+
+ pMapOverlay->flFadeDistMaxSq = FloatForKey( pMapEnt, "fademaxdist" );
+ if ( pMapOverlay->flFadeDistMaxSq > 0 )
+ {
+ pMapOverlay->flFadeDistMaxSq *= pMapOverlay->flFadeDistMaxSq;
+ }
+
+ GetVectorForKey( pMapEnt, "BasisOrigin", pMapOverlay->vecOrigin );
+
+ pMapOverlay->m_nRenderOrder = IntForKey( pMapEnt, "RenderOrder" );
+ if ( pMapOverlay->m_nRenderOrder < 0 || pMapOverlay->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
+ Error( "Overlay (%s) at %f %f %f has invalid render order (%d).\n", ValueForKey( pMapEnt, "material" ),
+ pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z,
+ pMapOverlay->m_nRenderOrder );
+
+ GetVectorForKey( pMapEnt, "uv0", pMapOverlay->vecUVPoints[0] );
+ GetVectorForKey( pMapEnt, "uv1", pMapOverlay->vecUVPoints[1] );
+ GetVectorForKey( pMapEnt, "uv2", pMapOverlay->vecUVPoints[2] );
+ GetVectorForKey( pMapEnt, "uv3", pMapOverlay->vecUVPoints[3] );
+
+ GetVectorForKey( pMapEnt, "BasisU", pMapOverlay->vecBasis[0] );
+ GetVectorForKey( pMapEnt, "BasisV", pMapOverlay->vecBasis[1] );
+ GetVectorForKey( pMapEnt, "BasisNormal", pMapOverlay->vecBasis[2] );
+
+ const char *pMaterialName = ValueForKey( pMapEnt, "material" );
+ Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
+ if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
+ {
+ Error( "Overlay Material Name (%s) too long! > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
+ return -1;
+ }
+ strcpy( pMapOverlay->szMaterialName, pMaterialName );
+
+ // Convert the sidelist to side id(s).
+ const char *pSideList = ValueForKey( pMapEnt, "sides" );
+ char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
+ strcpy( pTmpList, pSideList );
+ const char *pScan = strtok( pTmpList, " " );
+ if ( !pScan )
+ return iAccessorID;
+
+ pMapOverlay->aSideList.Purge();
+ pMapOverlay->aFaceList.Purge();
+
+ do
+ {
+ int nSideId;
+ if ( sscanf( pScan, "%d", &nSideId ) == 1 )
+ {
+ pMapOverlay->aSideList.AddToTail( nSideId );
+ }
+ } while ( ( pScan = strtok( NULL, " " ) ) );
+
+ return iAccessorID;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+side_t *GetSide( int nSideId )
+{
+ for( int iSide = 0; iSide < g_LoadingMap->nummapbrushsides; ++iSide )
+ {
+ if ( g_LoadingMap->brushsides[iSide].id == nSideId )
+ return &g_LoadingMap->brushsides[iSide];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_UpdateSideLists( int StartIndex )
+{
+ int nMapOverlayCount = g_aMapOverlays.Count();
+ for( int iMapOverlay = StartIndex; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( iMapOverlay );
+ if ( pMapOverlay )
+ {
+ int nSideCount = pMapOverlay->aSideList.Count();
+ for( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ side_t *pSide = GetSide( pMapOverlay->aSideList[iSide] );
+ if ( pSide )
+ {
+ if ( pSide->aOverlayIds.Find( pMapOverlay->nId ) == -1 )
+ {
+ pSide->aOverlayIds.AddToTail( pMapOverlay->nId );
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_UpdateSideLists( int StartIndex )
+{
+ int nOverlayCount = g_aMapWaterOverlays.Count();
+ for( int iOverlay = StartIndex; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ mapoverlay_t *pOverlay = &g_aMapWaterOverlays.Element( iOverlay );
+ if ( pOverlay )
+ {
+ int nSideCount = pOverlay->aSideList.Count();
+ for( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ side_t *pSide = GetSide( pOverlay->aSideList[iSide] );
+ if ( pSide )
+ {
+ if ( pSide->aWaterOverlayIds.Find( pOverlay->nId ) == -1 )
+ {
+ pSide->aWaterOverlayIds.AddToTail( pOverlay->nId );
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_AddFaceToLists( int iFace, side_t *pSide )
+{
+ int nOverlayIdCount = pSide->aOverlayIds.Count();
+ for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( pSide->aOverlayIds[iOverlayId] );
+ if ( pMapOverlay )
+ {
+ if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
+ {
+ pMapOverlay->aFaceList.AddToTail( iFace );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide )
+{
+ int nOverlayIdCount = pSide->aWaterOverlayIds.Count();
+ for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapWaterOverlays.Element( pSide->aWaterOverlayIds[iOverlayId] - ( MAX_MAP_OVERLAYS + 1 ) );
+ if ( pMapOverlay )
+ {
+ if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
+ {
+ pMapOverlay->aFaceList.AddToTail( iFace );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_EmitOverlayFace( mapoverlay_t *pMapOverlay )
+{
+ Assert( g_nOverlayCount < MAX_MAP_OVERLAYS );
+ if ( g_nOverlayCount >= MAX_MAP_OVERLAYS )
+ {
+ Error ( "Too Many Overlays!\nMAX_MAP_OVERLAYS = %d", MAX_MAP_OVERLAYS );
+ return;
+ }
+
+ doverlay_t *pOverlay = &g_Overlays[g_nOverlayCount];
+ doverlayfade_t *pOverlayFade = &g_OverlayFades[g_nOverlayCount];
+
+ g_nOverlayCount++;
+
+ // Conver the map overlay into a .bsp overlay (doverlay_t).
+ if ( pOverlay )
+ {
+ pOverlay->nId = pMapOverlay->nId;
+
+ pOverlay->flU[0] = pMapOverlay->flU[0];
+ pOverlay->flU[1] = pMapOverlay->flU[1];
+ pOverlay->flV[0] = pMapOverlay->flV[0];
+ pOverlay->flV[1] = pMapOverlay->flV[1];
+
+ VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
+ VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
+ VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
+ VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
+
+ VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
+
+ VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
+
+ pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
+
+ // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
+ pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
+ pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
+ pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
+
+ // Encode whether or not the v axis should be flipped.
+ Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
+ if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
+ {
+ pOverlay->vecUVPoints[3].z = 1.0f;
+ }
+
+ // Texinfo.
+ texinfo_t texInfo;
+ texInfo.flags = 0;
+ texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
+ for( int iVec = 0; iVec < 2; ++iVec )
+ {
+ for( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ }
+
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
+ }
+ pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
+
+ // Face List
+ int nFaceCount = pMapOverlay->aFaceList.Count();
+ Assert( nFaceCount < OVERLAY_BSP_FACE_COUNT );
+ if ( nFaceCount >= OVERLAY_BSP_FACE_COUNT )
+ {
+ Error( "Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
+ return;
+ }
+
+ pOverlay->SetFaceCount( nFaceCount );
+ for( int iFace = 0; iFace < nFaceCount; ++iFace )
+ {
+ pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
+ }
+ }
+
+ // Convert the map overlay fade data into a .bsp overlay fade (doverlayfade_t).
+ if ( pOverlayFade )
+ {
+ pOverlayFade->flFadeDistMinSq = pMapOverlay->flFadeDistMinSq;
+ pOverlayFade->flFadeDistMaxSq = pMapOverlay->flFadeDistMaxSq;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_EmitOverlayFace( mapoverlay_t *pMapOverlay )
+{
+ Assert( g_nWaterOverlayCount < MAX_MAP_WATEROVERLAYS );
+ if ( g_nWaterOverlayCount >= MAX_MAP_WATEROVERLAYS )
+ {
+ Error ( "Too many water overlays!\nMAX_MAP_WATEROVERLAYS = %d", MAX_MAP_WATEROVERLAYS );
+ return;
+ }
+
+ dwateroverlay_t *pOverlay = &g_WaterOverlays[g_nWaterOverlayCount];
+ g_nWaterOverlayCount++;
+
+ // Conver the map overlay into a .bsp overlay (doverlay_t).
+ if ( pOverlay )
+ {
+ pOverlay->nId = pMapOverlay->nId;
+
+ pOverlay->flU[0] = pMapOverlay->flU[0];
+ pOverlay->flU[1] = pMapOverlay->flU[1];
+ pOverlay->flV[0] = pMapOverlay->flV[0];
+ pOverlay->flV[1] = pMapOverlay->flV[1];
+
+ VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
+ VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
+ VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
+ VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
+
+ VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
+
+ VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
+
+ pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
+
+ // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
+ pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
+ pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
+ pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
+
+ // Encode whether or not the v axis should be flipped.
+ Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
+ if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
+ {
+ pOverlay->vecUVPoints[3].z = 1.0f;
+ }
+
+ // Texinfo.
+ texinfo_t texInfo;
+ texInfo.flags = 0;
+ texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
+ for( int iVec = 0; iVec < 2; ++iVec )
+ {
+ for( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ }
+
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
+ }
+ pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
+
+ // Face List
+ int nFaceCount = pMapOverlay->aFaceList.Count();
+ Assert( nFaceCount < WATEROVERLAY_BSP_FACE_COUNT );
+ if ( nFaceCount >= WATEROVERLAY_BSP_FACE_COUNT )
+ {
+ Error( "Water Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
+ return;
+ }
+
+ pOverlay->SetFaceCount( nFaceCount );
+ for( int iFace = 0; iFace < nFaceCount; ++iFace )
+ {
+ pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_EmitOverlayFaces( void )
+{
+ int nMapOverlayCount = g_aMapOverlays.Count();
+ for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ Overlay_EmitOverlayFace( &g_aMapOverlays.Element( iMapOverlay ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void OverlayTransition_EmitOverlayFaces( void )
+{
+ int nMapOverlayCount = g_aMapWaterOverlays.Count();
+ for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ OverlayTransition_EmitOverlayFace( &g_aMapWaterOverlays.Element( iMapOverlay ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// These routines were mostly stolen from MapOverlay.cpp in Hammer
+//-----------------------------------------------------------------------------
+#define OVERLAY_BASIS_U 0
+#define OVERLAY_BASIS_V 1
+#define OVERLAY_BASIS_NORMAL 2
+#define OVERLAY_HANDLES_COUNT 4
+
+
+inline void TransformPoint( const VMatrix& matrix, Vector &point )
+{
+ Vector orgVector = point;
+ matrix.V3Mul( orgVector, point );
+}
+
+
+inline bool fequal( float value, float target, float delta) { return ( (value<(target+delta))&&(value>(target-delta)) ); }
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function translate / rotate an overlay.
+// Input : pOverlay - the overlay to be translated
+// OriginOffset - the translation
+// AngleOffset - the rotation
+// Matrix - the translation / rotation matrix
+// Output : none
+//-----------------------------------------------------------------------------
+void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix )
+{
+ VMatrix tmpMatrix( Matrix );
+
+ Vector temp = pOverlay->vecOrigin;
+ VectorTransform( temp, Matrix, pOverlay->vecOrigin );
+
+ // erase move component
+ tmpMatrix.SetTranslation( vec3_origin );
+
+ // check if matrix would still change something
+ if ( !tmpMatrix.IsIdentity() )
+ {
+ // make sure axes are normalized (they should be anyways)
+ pOverlay->vecBasis[OVERLAY_BASIS_U].NormalizeInPlace();
+ pOverlay->vecBasis[OVERLAY_BASIS_V].NormalizeInPlace();
+
+ Vector vecU = pOverlay->vecBasis[OVERLAY_BASIS_U];
+ Vector vecV = pOverlay->vecBasis[OVERLAY_BASIS_V];
+ Vector vecNormal = pOverlay->vecBasis[OVERLAY_BASIS_NORMAL];
+
+ TransformPoint( tmpMatrix, vecU );
+ TransformPoint( tmpMatrix, vecV );
+ TransformPoint( tmpMatrix, vecNormal );
+
+ float fScaleU = vecU.Length();
+ float fScaleV = vecV.Length();
+ float flScaleNormal = vecNormal.Length();
+
+ bool bIsUnit = ( fequal( fScaleU, 1.0f, 0.0001 ) && fequal( fScaleV, 1.0f, 0.0001 ) && fequal( flScaleNormal, 1.0f, 0.0001 ) );
+ bool bIsPerp = ( fequal( DotProduct( vecU, vecV ), 0.0f, 0.0025 ) && fequal( DotProduct( vecU, vecNormal ), 0.0f, 0.0025 ) && fequal( DotProduct( vecV, vecNormal ), 0.0f, 0.0025 ) );
+
+ // if ( fequal(fScaleU,1,0.0001) && fequal(fScaleV,1,0.0001) && fequal(DotProduct( vecU, vecV ),0,0.0025) )
+ if ( bIsUnit && bIsPerp )
+ {
+ // transformation doesnt scale or shear anything, so just update base axes
+ pOverlay->vecBasis[OVERLAY_BASIS_U] = vecU;
+ pOverlay->vecBasis[OVERLAY_BASIS_V] = vecV;
+ pOverlay->vecBasis[OVERLAY_BASIS_NORMAL] = vecNormal;
+ }
+ else
+ {
+ // more complex transformation, move UV coordinates, but leave base axes
+ for ( int iHandle=0; iHandle<OVERLAY_HANDLES_COUNT;iHandle++)
+ {
+ Vector vecUV = pOverlay->vecUVPoints[iHandle];
+ Vector vecPos = ( vecUV.x * pOverlay->vecBasis[OVERLAY_BASIS_U] + vecUV.y * pOverlay->vecBasis[OVERLAY_BASIS_V] );
+
+ // to transform in world space
+ TransformPoint( tmpMatrix, vecPos );
+
+ vecUV.x = pOverlay->vecBasis[OVERLAY_BASIS_U].Dot( vecPos );
+ vecUV.y = pOverlay->vecBasis[OVERLAY_BASIS_V].Dot( vecPos );
+
+ pOverlay->vecUVPoints[iHandle] = vecUV;
+ }
+ }
+ }
+
+}
diff --git a/mp/src/utils/vbsp/portals.cpp b/mp/src/utils/vbsp/portals.cpp
index ec2a2695..6c59cffa 100644
--- a/mp/src/utils/vbsp/portals.cpp
+++ b/mp/src/utils/vbsp/portals.cpp
@@ -1,1684 +1,1684 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-#include "utlvector.h"
-#include "mathlib/vmatrix.h"
-#include "iscratchpad3d.h"
-#include "csg.h"
-#include "fmtstr.h"
-
-int c_active_portals;
-int c_peak_portals;
-int c_boundary;
-int c_boundary_sides;
-
-/*
-===========
-AllocPortal
-===========
-*/
-portal_t *AllocPortal (void)
-{
- static int s_PortalCount = 0;
-
- portal_t *p;
-
- if (numthreads == 1)
- c_active_portals++;
- if (c_active_portals > c_peak_portals)
- c_peak_portals = c_active_portals;
-
- p = (portal_t*)malloc (sizeof(portal_t));
- memset (p, 0, sizeof(portal_t));
- p->id = s_PortalCount;
- ++s_PortalCount;
-
- return p;
-}
-
-void FreePortal (portal_t *p)
-{
- if (p->winding)
- FreeWinding (p->winding);
- if (numthreads == 1)
- c_active_portals--;
- free (p);
-}
-
-//==============================================================
-
-/*
-==============
-VisibleContents
-
-Returns the single content bit of the
-strongest visible content present
-==============
-*/
-int VisibleContents (int contents)
-{
- int i;
-
- for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
- {
- if (contents & i )
- {
- return i;
- }
- }
-
- return 0;
-}
-
-
-/*
-===============
-ClusterContents
-===============
-*/
-int ClusterContents (node_t *node)
-{
- int c1, c2, c;
-
- if (node->planenum == PLANENUM_LEAF)
- return node->contents;
-
- c1 = ClusterContents(node->children[0]);
- c2 = ClusterContents(node->children[1]);
- c = c1|c2;
-
- // a cluster may include some solid detail areas, but
- // still be seen into
- if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
- c &= ~CONTENTS_SOLID;
- return c;
-}
-
-/*
-=============
-Portal_VisFlood
-
-Returns true if the portal is empty or translucent, allowing
-the PVS calculation to see through it.
-The nodes on either side of the portal may actually be clusters,
-not leafs, so all contents should be ored together
-=============
-*/
-qboolean Portal_VisFlood (portal_t *p)
-{
- int c1, c2;
-
- if (!p->onnode)
- return false; // to global outsideleaf
-
- c1 = ClusterContents(p->nodes[0]);
- c2 = ClusterContents(p->nodes[1]);
-
- if (!VisibleContents (c1^c2))
- return true;
-
- if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
- c1 = 0;
- if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
- c2 = 0;
-
- if ( (c1|c2) & CONTENTS_SOLID )
- return false; // can't see through solid
-
- if (! (c1 ^ c2))
- return true; // identical on both sides
-
- if (!VisibleContents (c1^c2))
- return true;
- return false;
-}
-
-
-/*
-===============
-Portal_EntityFlood
-
-The entity flood determines which areas are
-"outside" on the map, which are then filled in.
-Flowing from side s to side !s
-===============
-*/
-qboolean Portal_EntityFlood (portal_t *p, int s)
-{
- if (p->nodes[0]->planenum != PLANENUM_LEAF
- || p->nodes[1]->planenum != PLANENUM_LEAF)
- Error ("Portal_EntityFlood: not a leaf");
-
- // can never cross to a solid
- if ( (p->nodes[0]->contents & CONTENTS_SOLID)
- || (p->nodes[1]->contents & CONTENTS_SOLID) )
- return false;
-
- // can flood through everything else
- return true;
-}
-
-qboolean Portal_AreaLeakFlood (portal_t *p, int s)
-{
- if ( !Portal_EntityFlood( p, s ) )
- return false;
-
- // can never cross through areaportal
- if ( (p->nodes[0]->contents & CONTENTS_AREAPORTAL)
- || (p->nodes[1]->contents & CONTENTS_AREAPORTAL) )
- return false;
-
- // can flood through everything else
- return true;
-}
-
-
-//=============================================================================
-
-int c_tinyportals;
-
-/*
-=============
-AddPortalToNodes
-=============
-*/
-void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
-{
- if (p->nodes[0] || p->nodes[1])
- Error ("AddPortalToNode: allready included");
-
- p->nodes[0] = front;
- p->next[0] = front->portals;
- front->portals = p;
-
- p->nodes[1] = back;
- p->next[1] = back->portals;
- back->portals = p;
-}
-
-
-/*
-=============
-RemovePortalFromNode
-=============
-*/
-void RemovePortalFromNode (portal_t *portal, node_t *l)
-{
- portal_t **pp, *t;
-
-// remove reference to the current portal
- pp = &l->portals;
- while (1)
- {
- t = *pp;
- if (!t)
- Error ("RemovePortalFromNode: portal not in leaf");
-
- if ( t == portal )
- break;
-
- if (t->nodes[0] == l)
- pp = &t->next[0];
- else if (t->nodes[1] == l)
- pp = &t->next[1];
- else
- Error ("RemovePortalFromNode: portal not bounding leaf");
- }
-
- if (portal->nodes[0] == l)
- {
- *pp = portal->next[0];
- portal->nodes[0] = NULL;
- }
- else if (portal->nodes[1] == l)
- {
- *pp = portal->next[1];
- portal->nodes[1] = NULL;
- }
-}
-
-//============================================================================
-
-void PrintPortal (portal_t *p)
-{
- int i;
- winding_t *w;
-
- w = p->winding;
- for (i=0 ; i<w->numpoints ; i++)
- Msg ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
- , w->p[i][1], w->p[i][2]);
-}
-
-// because of water areaportals support, the areaportal may not be the only brush on this node
-bspbrush_t *AreaportalBrushForNode( node_t *node )
-{
- bspbrush_t *b = node->brushlist;
- while ( b && !(b->original->contents & CONTENTS_AREAPORTAL) )
- {
- b = b->next;
- }
- Assert( b->original->entitynum != 0 );
- return b;
-}
-
-/*
-================
-MakeHeadnodePortals
-
-The created portals will face the global outside_node
-================
-*/
-// buffer space around sides of nodes
-#define SIDESPACE 8
-void MakeHeadnodePortals (tree_t *tree)
-{
- Vector bounds[2];
- int i, j, n;
- portal_t *p, *portals[6];
- plane_t bplanes[6], *pl;
- node_t *node;
-
- node = tree->headnode;
-
-// pad with some space so there will never be null volume leafs
- for (i=0 ; i<3 ; i++)
- {
- bounds[0][i] = tree->mins[i] - SIDESPACE;
- bounds[1][i] = tree->maxs[i] + SIDESPACE;
- }
-
- tree->outside_node.planenum = PLANENUM_LEAF;
- tree->outside_node.brushlist = NULL;
- tree->outside_node.portals = NULL;
- tree->outside_node.contents = 0;
-
- for (i=0 ; i<3 ; i++)
- for (j=0 ; j<2 ; j++)
- {
- n = j*3 + i;
-
- p = AllocPortal ();
- portals[n] = p;
-
- pl = &bplanes[n];
- memset (pl, 0, sizeof(*pl));
- if (j)
- {
- pl->normal[i] = -1;
- pl->dist = -bounds[j][i];
- }
- else
- {
- pl->normal[i] = 1;
- pl->dist = bounds[j][i];
- }
- p->plane = *pl;
- p->winding = BaseWindingForPlane (pl->normal, pl->dist);
- AddPortalToNodes (p, node, &tree->outside_node);
- }
-
-// clip the basewindings by all the other planes
- for (i=0 ; i<6 ; i++)
- {
- for (j=0 ; j<6 ; j++)
- {
- if (j == i)
- continue;
- ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
- }
- }
-}
-
-//===================================================
-
-
-/*
-================
-BaseWindingForNode
-================
-*/
-#define BASE_WINDING_EPSILON 0.001
-#define SPLIT_WINDING_EPSILON 0.001
-
-winding_t *BaseWindingForNode (node_t *node)
-{
- winding_t *w;
- node_t *n;
- plane_t *plane;
- Vector normal;
- vec_t dist;
-
- w = BaseWindingForPlane (g_MainMap->mapplanes[node->planenum].normal, g_MainMap->mapplanes[node->planenum].dist);
-
- // clip by all the parents
- for (n=node->parent ; n && w ; )
- {
- plane = &g_MainMap->mapplanes[n->planenum];
-
- if (n->children[0] == node)
- { // take front
- ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
- }
- else
- { // take back
- VectorSubtract (vec3_origin, plane->normal, normal);
- dist = -plane->dist;
- ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
- }
- node = n;
- n = n->parent;
- }
-
- return w;
-}
-
-//============================================================
-
-/*
-==================
-MakeNodePortal
-
-create the new portal by taking the full plane winding for the cutting plane
-and clipping it by all of parents of this node
-==================
-*/
-void MakeNodePortal (node_t *node)
-{
- portal_t *new_portal, *p;
- winding_t *w;
- Vector normal;
- float dist = 0.0f;
- int side = 0;
-
- w = BaseWindingForNode (node);
-
- // clip the portal by all the other portals in the node
- for (p = node->portals ; p && w; p = p->next[side])
- {
- if (p->nodes[0] == node)
- {
- side = 0;
- VectorCopy (p->plane.normal, normal);
- dist = p->plane.dist;
- }
- else if (p->nodes[1] == node)
- {
- side = 1;
- VectorSubtract (vec3_origin, p->plane.normal, normal);
- dist = -p->plane.dist;
- }
- else
- {
- Error ("CutNodePortals_r: mislinked portal");
- }
-
- ChopWindingInPlace (&w, normal, dist, 0.1);
- }
-
- if (!w)
- {
- return;
- }
-
- if (WindingIsTiny (w))
- {
- c_tinyportals++;
- FreeWinding (w);
- return;
- }
-
-
- new_portal = AllocPortal ();
- new_portal->plane = g_MainMap->mapplanes[node->planenum];
- new_portal->onnode = node;
- new_portal->winding = w;
-
- AddPortalToNodes (new_portal, node->children[0], node->children[1]);
-}
-
-
-/*
-==============
-SplitNodePortals
-
-Move or split the portals that bound node so that the node's
-children have portals instead of node.
-==============
-*/
-void SplitNodePortals (node_t *node)
-{
- portal_t *p, *next_portal, *new_portal;
- node_t *f, *b, *other_node;
- int side = 0;
- plane_t *plane;
- winding_t *frontwinding, *backwinding;
-
- plane = &g_MainMap->mapplanes[node->planenum];
- f = node->children[0];
- b = node->children[1];
-
- for (p = node->portals ; p ; p = next_portal)
- {
- if (p->nodes[0] == node)
- side = 0;
- else if (p->nodes[1] == node)
- side = 1;
- else
- Error ("CutNodePortals_r: mislinked portal");
- next_portal = p->next[side];
-
- other_node = p->nodes[!side];
- RemovePortalFromNode (p, p->nodes[0]);
- RemovePortalFromNode (p, p->nodes[1]);
-
-//
-// cut the portal into two portals, one on each side of the cut plane
-//
- ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
- SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
-
- if (frontwinding && WindingIsTiny(frontwinding))
- {
- FreeWinding (frontwinding);
- frontwinding = NULL;
- c_tinyportals++;
- }
-
- if (backwinding && WindingIsTiny(backwinding))
- {
- FreeWinding (backwinding);
- backwinding = NULL;
- c_tinyportals++;
- }
-
- if (!frontwinding && !backwinding)
- { // tiny windings on both sides
- continue;
- }
-
- if (!frontwinding)
- {
- FreeWinding (backwinding);
- if (side == 0)
- AddPortalToNodes (p, b, other_node);
- else
- AddPortalToNodes (p, other_node, b);
- continue;
- }
- if (!backwinding)
- {
- FreeWinding (frontwinding);
- if (side == 0)
- AddPortalToNodes (p, f, other_node);
- else
- AddPortalToNodes (p, other_node, f);
- continue;
- }
-
- // the winding is split
- new_portal = AllocPortal ();
- *new_portal = *p;
- new_portal->winding = backwinding;
- FreeWinding (p->winding);
- p->winding = frontwinding;
-
- if (side == 0)
- {
- AddPortalToNodes (p, f, other_node);
- AddPortalToNodes (new_portal, b, other_node);
- }
- else
- {
- AddPortalToNodes (p, other_node, f);
- AddPortalToNodes (new_portal, other_node, b);
- }
- }
-
- node->portals = NULL;
-}
-
-
-/*
-================
-CalcNodeBounds
-================
-*/
-void CalcNodeBounds (node_t *node)
-{
- portal_t *p;
- int s;
- int i;
-
- // calc mins/maxs for both leafs and nodes
- ClearBounds (node->mins, node->maxs);
- for (p = node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
- for (i=0 ; i<p->winding->numpoints ; i++)
- AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
- }
-}
-
-
-/*
-==================
-MakeTreePortals_r
-==================
-*/
-void MakeTreePortals_r (node_t *node)
-{
- int i;
-
- CalcNodeBounds (node);
- if (node->mins[0] >= node->maxs[0])
- {
- Warning("WARNING: node without a volume\n");
- }
-
- for (i=0 ; i<3 ; i++)
- {
- if (node->mins[i] < (MIN_COORD_INTEGER-SIDESPACE) || node->maxs[i] > (MAX_COORD_INTEGER+SIDESPACE))
- {
- const char *pMatName = "<NO BRUSH>";
- // split by brush side
- if ( node->side )
- {
- texinfo_t *pTexInfo = &texinfo[node->side->texinfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- }
- Vector point = node->portals->winding->p[0];
- Warning("WARNING: BSP node with unbounded volume (material: %s, near %s)\n", pMatName, VecToString(point) );
- break;
- }
- }
- if (node->planenum == PLANENUM_LEAF)
- return;
-
- MakeNodePortal (node);
- SplitNodePortals (node);
-
- MakeTreePortals_r (node->children[0]);
- MakeTreePortals_r (node->children[1]);
-}
-
-/*
-==================
-MakeTreePortals
-==================
-*/
-void MakeTreePortals (tree_t *tree)
-{
- MakeHeadnodePortals (tree);
- MakeTreePortals_r (tree->headnode);
-}
-
-/*
-=========================================================
-
-FLOOD ENTITIES
-
-=========================================================
-*/
-
-//-----------------------------------------------------------------------------
-// Purpose: Floods outward from the given node, marking visited nodes with
-// the number of hops from a node with an entity. If we ever mark
-// the outside_node for this tree, we've leaked.
-// Input : node -
-// dist -
-//-----------------------------------------------------------------------------
-void FloodPortals_r (node_t *node, int dist)
-{
- portal_t *p;
- int s;
-
- node->occupied = dist;
-
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
-
- // Skip nodes that have already been marked.
- if (p->nodes[!s]->occupied)
- continue;
-
- // Skip portals that lead to or from nodes with solid contents.
- if (!Portal_EntityFlood (p, s))
- continue;
-
- FloodPortals_r (p->nodes[!s], dist+1);
- }
-}
-
-void FloodAreaLeak_r( node_t *node, int dist )
-{
- portal_t *p;
- int s;
-
- node->occupied = dist;
-
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
-
- if (p->nodes[!s]->occupied)
- continue;
-
- if (!Portal_AreaLeakFlood (p, s))
- continue;
-
- FloodAreaLeak_r( p->nodes[!s], dist+1 );
- }
-}
-
-void ClearOccupied_r( node_t *headnode )
-{
- if ( !headnode )
- return;
-
- headnode->occupied = 0;
- ClearOccupied_r( headnode->children[0] );
- ClearOccupied_r( headnode->children[1] );
-}
-
-void FloodAreaLeak( node_t *headnode, node_t *pFirstSide )
-{
- ClearOccupied_r( headnode );
- FloodAreaLeak_r( pFirstSide, 2 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: For the given entity at the given origin, finds the leaf node in the
-// BSP tree that the entity occupies.
-//
-// We then flood outward from that leaf to see if the entity leaks.
-// Input : headnode -
-// origin -
-// occupant -
-// Output : Returns false if the entity is in solid, true if it is not.
-//-----------------------------------------------------------------------------
-qboolean PlaceOccupant (node_t *headnode, Vector& origin, entity_t *occupant)
-{
- node_t *node;
- vec_t d;
- plane_t *plane;
-
- // find the leaf to start in
- node = headnode;
- 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];
- }
-
- if (node->contents == CONTENTS_SOLID)
- return false;
-
- node->occupant = occupant;
-
- // Flood outward from here to see if this entity leaks.
- FloodPortals_r (node, 1);
-
- return true;
-}
-
-/*
-=============
-FloodEntities
-
-Marks all nodes that can be reached by entites
-=============
-*/
-qboolean FloodEntities (tree_t *tree)
-{
- int i;
- Vector origin;
- char *cl;
- qboolean inside;
- node_t *headnode;
-
- headnode = tree->headnode;
- qprintf ("--- FloodEntities ---\n");
- inside = false;
- tree->outside_node.occupied = 0;
-
- for (i=1 ; i<num_entities ; i++)
- {
- GetVectorForKey (&entities[i], "origin", origin);
- if (VectorCompare(origin, vec3_origin))
- continue;
-
- cl = ValueForKey (&entities[i], "classname");
-
- origin[2] += 1; // so objects on floor are ok
-
- // nudge playerstart around if needed so clipping hulls allways
- // have a valid point
- if (!strcmp (cl, "info_player_start"))
- {
- int x, y;
-
- for (x=-16 ; x<=16 ; x += 16)
- {
- for (y=-16 ; y<=16 ; y += 16)
- {
- origin[0] += x;
- origin[1] += y;
- if (PlaceOccupant (headnode, origin, &entities[i]))
- {
- inside = true;
- goto gotit;
- }
- origin[0] -= x;
- origin[1] -= y;
- }
- }
-gotit: ;
- }
- else
- {
- if (PlaceOccupant (headnode, origin, &entities[i]))
- inside = true;
- }
- }
-
- if (!inside)
- {
- qprintf ("no entities in open -- no filling\n");
- }
-
- if (tree->outside_node.occupied)
- {
- qprintf ("entity reached from outside -- no filling\n" );
- }
-
- return (qboolean)(inside && !tree->outside_node.occupied);
-}
-
-
-/*
-=========================================================
-
-FLOOD AREAS
-
-=========================================================
-*/
-
-int c_areas;
-
-bool IsAreaportalNode( node_t *node )
-{
- return ( node->contents & CONTENTS_AREAPORTAL ) ? true : false;
-}
-/*
-=============
-FloodAreas_r
-=============
-*/
-
-void FloodAreas_r (node_t *node, portal_t *pSeeThrough)
-{
- portal_t *p;
- int s;
- bspbrush_t *b;
- entity_t *e;
-
- if ( IsAreaportalNode(node) )
- {
- // this node is part of an area portal
- b = AreaportalBrushForNode( node );
- e = &entities[b->original->entitynum];
-
- // if the current area has allready touched this
- // portal, we are done
- if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
- return;
-
- // note the current area as bounding the portal
- if (e->portalareas[1])
- {
- Warning("WARNING: areaportal entity %i (brush %i) touches > 2 areas\n", b->original->entitynum, b->original->id );
- return;
- }
-
- if (e->portalareas[0])
- {
- e->portalareas[1] = c_areas;
- e->m_pPortalsLeadingIntoAreas[1] = pSeeThrough;
- }
- else
- {
- e->portalareas[0] = c_areas;
- e->m_pPortalsLeadingIntoAreas[0] = pSeeThrough;
- }
-
- return;
- }
-
- if (node->area)
- return; // allready got it
- node->area = c_areas;
-
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
-#if 0
- if (p->nodes[!s]->occupied)
- continue;
-#endif
- if (!Portal_EntityFlood (p, s))
- continue;
-
- FloodAreas_r (p->nodes[!s], p);
- }
-}
-
-/*
-=============
-FindAreas_r
-
-Just decend the tree, and for each node that hasn't had an
-area set, flood fill out from there
-=============
-*/
-void FindAreas_r (node_t *node)
-{
- if (node->planenum != PLANENUM_LEAF)
- {
- FindAreas_r (node->children[0]);
- FindAreas_r (node->children[1]);
- return;
- }
-
- if (node->area)
- return; // allready got it
-
- if (node->contents & CONTENTS_SOLID)
- return;
-
- if (!node->occupied)
- return; // not reachable by entities
-
- // area portals are allways only flooded into, never
- // out of
- if (IsAreaportalNode(node))
- return;
-
- c_areas++;
- FloodAreas_r (node, NULL);
-}
-
-
-void ReportAreaportalLeak( tree_t *tree, node_t *node )
-{
- portal_t *p, *pStart = NULL;
- int s;
-
- // Find a portal out of this areaportal into empty space
- for (p=node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
- if ( !Portal_EntityFlood( p, !s ) )
- continue;
- if ( p->nodes[!s]->contents & CONTENTS_AREAPORTAL )
- continue;
-
- pStart = p;
- break;
- }
-
- if ( pStart )
- {
- s = pStart->nodes[0] == node;
- Assert(!(pStart->nodes[s]->contents & CONTENTS_AREAPORTAL) );
- // flood fill the area outside this areaportal brush
- FloodAreaLeak( tree->headnode, pStart->nodes[s] );
-
- // find the portal into the longest path around the portal
- portal_t *pBest = NULL;
- int bestDist = 0;
- for (p=node->portals ; p ; p = p->next[s])
- {
- if ( p == pStart )
- continue;
-
- s = (p->nodes[1] == node);
- if ( p->nodes[!s]->occupied > bestDist )
- {
- pBest = p;
- bestDist = p->nodes[!s]->occupied;
- }
- }
- if ( pBest )
- {
- s = (pBest->nodes[0] == node);
- // write the linefile that goes from pBest to pStart
- AreaportalLeakFile( tree, pStart, pBest, pBest->nodes[s] );
- }
- }
-}
-
-
-/*
-=============
-SetAreaPortalAreas_r
-
-Just decend the tree, and for each node that hasn't had an
-area set, flood fill out from there
-=============
-*/
-void SetAreaPortalAreas_r (tree_t *tree, node_t *node)
-{
- bspbrush_t *b;
- entity_t *e;
-
- if (node->planenum != PLANENUM_LEAF)
- {
- SetAreaPortalAreas_r (tree, node->children[0]);
- SetAreaPortalAreas_r (tree, node->children[1]);
- return;
- }
-
- if (IsAreaportalNode(node))
- {
- if (node->area)
- return; // already set
-
- b = AreaportalBrushForNode( node );
- e = &entities[b->original->entitynum];
- node->area = e->portalareas[0];
- if (!e->portalareas[1])
- {
- ReportAreaportalLeak( tree, node );
- Warning("\nBrush %i: areaportal brush doesn't touch two areas\n", b->original->id);
- return;
- }
- }
-}
-
-
-// Return a positive value between 0 and 2*PI telling the angle distance
-// from flBaseAngle to flTestAngle.
-float AngleOffset( float flBaseAngle, float flTestAngle )
-{
- while( flTestAngle > flBaseAngle )
- flTestAngle -= 2 * M_PI;
-
- return fmod( flBaseAngle - flTestAngle, (float) (2 * M_PI) );
-}
-
-
-int FindUniquePoints( const Vector2D *pPoints, int nPoints, int *indexMap, int nMaxIndexMapPoints, float flTolerance )
-{
- float flToleranceSqr = flTolerance * flTolerance;
-
- // This could be slightly more efficient.
- int nUniquePoints = 0;
- for ( int i=0; i < nPoints; i++ )
- {
- int j;
- for ( j=0; j < nUniquePoints; j++ )
- {
- if ( pPoints[i].DistToSqr( pPoints[indexMap[j]] ) < flToleranceSqr )
- break;
- }
- if ( j == nUniquePoints )
- {
- if ( nUniquePoints >= nMaxIndexMapPoints )
- Error( "FindUniquePoints: overflowed unique point list (size %d).", nMaxIndexMapPoints );
-
- indexMap[nUniquePoints++] = i;
- }
- }
-
- return nUniquePoints;
-}
-
-// Build a 2D convex hull of the set of points.
-// This essentially giftwraps the points as it walks around the perimeter.
-int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndices )
-{
- int nIndices = 0;
- bool touched[512];
- int indexMap[512];
-
- if( nPoints == 0 )
- return 0;
-
-
- // If we don't collapse the points into a unique set, we can loop around forever
- // and max out nMaxIndices.
- nPoints = FindUniquePoints( pPoints, nPoints, indexMap, ARRAYSIZE( indexMap ), 0.1f );
- memset( touched, 0, nPoints*sizeof(touched[0]) );
-
- // Find the (lower) left side.
- int i;
- int iBest = 0;
- for( i=1; i < nPoints; i++ )
- {
- if( pPoints[indexMap[i]].x < pPoints[indexMap[iBest]].x ||
- (pPoints[indexMap[i]].x == pPoints[indexMap[iBest]].x && pPoints[indexMap[i]].y < pPoints[indexMap[iBest]].y) )
- {
- iBest = i;
- }
- }
-
- touched[iBest] = true;
- indices[0] = indexMap[iBest];
- nIndices = 1;
-
- Vector2D curEdge( 0, 1 );
-
- // Wind around clockwise.
- while( 1 )
- {
- Vector2D const *pStartPoint = &pPoints[ indices[nIndices-1] ];
-
- float flEdgeAngle = atan2( curEdge.y, curEdge.x );
-
- int iMinAngle = -1;
- float flMinAngle = 5000;
-
- for( i=0; i < nPoints; i++ )
- {
- Vector2D vTo = pPoints[indexMap[i]] - *pStartPoint;
- float flDistToSqr = vTo.LengthSqr();
- if ( flDistToSqr <= 0.1f )
- continue;
-
- // Get the angle from the edge to this point.
- float flAngle = atan2( vTo.y, vTo.x );
- flAngle = AngleOffset( flEdgeAngle, flAngle );
-
- if( fabs( flAngle - flMinAngle ) < 0.00001f )
- {
- float flDistToTestSqr = pStartPoint->DistToSqr( pPoints[iMinAngle] );
-
- // If the angle is the same, pick the point farthest away.
- // unless the current one is closing the face loop
- if ( iMinAngle != indices[0] && flDistToSqr > flDistToTestSqr )
- {
- flMinAngle = flAngle;
- iMinAngle = indexMap[i];
- }
- }
- else if( flAngle < flMinAngle )
- {
- flMinAngle = flAngle;
- iMinAngle = indexMap[i];
- }
- }
-
- if( iMinAngle == -1 )
- {
- // Couldn't find a point?
- Assert( false );
- break;
- }
- else if( iMinAngle == indices[0] )
- {
- // Finished.
- break;
- }
- else
- {
- // Add this point.
- if( nIndices >= nMaxIndices )
- break;
-
- for ( int jj = 0; jj < nIndices; jj++ )
- {
- // if this assert hits, this routine is broken and is generating a spiral
- // rather than a closed polygon - basically an edge overlap of some kind
- Assert(indices[jj] != iMinAngle );
- }
-
- indices[nIndices] = iMinAngle;
- ++nIndices;
- }
-
- curEdge = pPoints[indices[nIndices-1]] - pPoints[indices[nIndices-2]];
- }
-
- return nIndices;
-}
-
-void FindPortalsLeadingToArea_R(
- node_t *pHeadNode,
- int iSrcArea,
- int iDestArea,
- plane_t *pPlane,
- CUtlVector<portal_t*> &portals )
-{
- if (pHeadNode->planenum != PLANENUM_LEAF)
- {
- FindPortalsLeadingToArea_R( pHeadNode->children[0], iSrcArea, iDestArea, pPlane, portals );
- FindPortalsLeadingToArea_R( pHeadNode->children[1], iSrcArea, iDestArea, pPlane, portals );
- return;
- }
-
- // Ok.. this is a leaf, check its portals.
- int s;
- for( portal_t *p = pHeadNode->portals; p ;p = p->next[!s] )
- {
- s = (p->nodes[0] == pHeadNode);
-
- if( !p->nodes[0]->occupied || !p->nodes[1]->occupied )
- continue;
-
- if( p->nodes[1]->area == iDestArea && p->nodes[0]->area == iSrcArea ||
- p->nodes[0]->area == iDestArea && p->nodes[1]->area == iSrcArea )
- {
- // Make sure the plane normals point the same way.
- plane_t *pMapPlane = &g_MainMap->mapplanes[p->onnode->planenum];
- float flDot = fabs( pMapPlane->normal.Dot( pPlane->normal ) );
- if( fabs( 1 - flDot ) < 0.01f )
- {
- Vector vPlanePt1 = pPlane->normal * pPlane->dist;
- Vector vPlanePt2 = pMapPlane->normal * pMapPlane->dist;
-
- if( vPlanePt1.DistToSqr( vPlanePt2 ) < 0.01f )
- {
- portals.AddToTail( p );
- }
- }
- }
- }
-}
-
-
-void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, dareaportal_t *dp )
-{
- // Build a list of all the points in portals from the same original face.
- CUtlVector<portal_t*> portals;
- FindPortalsLeadingToArea_R(
- pHeadNode,
- iSrcArea,
- dp->otherarea,
- &pPortal->plane,
- portals );
-
- CUtlVector<Vector> points;
- for( int iPortal=0; iPortal < portals.Size(); iPortal++ )
- {
- portal_t *pPointPortal = portals[iPortal];
- winding_t *pWinding = pPointPortal->winding;
- for( int i=0; i < pWinding->numpoints; i++ )
- {
- points.AddToTail( pWinding->p[i] );
- }
- }
-
- // Get the 2D convex hull.
-
- //// First transform them into a plane.
- QAngle vAngles;
- Vector vecs[3];
-
- VectorAngles( pPortal->plane.normal, vAngles );
- AngleVectors( vAngles, &vecs[0], &vecs[1], &vecs[2] );
- VMatrix mTransform;
- mTransform.Identity();
- mTransform.SetBasisVectors( vecs[0], vecs[1], vecs[2] );
- VMatrix mInvTransform = mTransform.Transpose();
-
- int i;
- CUtlVector<Vector2D> points2D;
- for( i=0; i < points.Size(); i++ )
- {
- Vector vTest = mTransform * points[i];
- points2D.AddToTail( Vector2D( vTest.y, vTest.z ) );
- }
-
- // Build the hull.
- int indices[512];
- int nIndices = Convex2D( points2D.Base(), points2D.Size(), indices, 512 );
-
- // Output the hull.
- dp->m_FirstClipPortalVert = g_nClipPortalVerts;
- dp->m_nClipPortalVerts = nIndices;
-
- if ( nIndices >= 32 )
- {
- Warning( "Warning: area portal has %d verts. Could be a vbsp bug.\n", nIndices );
- }
-
- if( dp->m_FirstClipPortalVert + dp->m_nClipPortalVerts >= MAX_MAP_PORTALVERTS )
- {
- Vector *p = pPortal->winding->p;
- Error( "MAX_MAP_PORTALVERTS (probably a broken areaportal near %.1f %.1f %.1f ", p->x, p->y, p->z );
- }
-
- for( i=0; i < nIndices; i++ )
- {
- g_ClipPortalVerts[g_nClipPortalVerts] = points[ indices[i] ];
- ++g_nClipPortalVerts;
- }
-}
-
-
-// Sets node_t::area for non-leaf nodes (this allows an optimization in the renderer).
-void SetNodeAreaIndices_R( node_t *node )
-{
- // All leaf area indices should already be set.
- if( node->planenum == PLANENUM_LEAF )
- return;
-
- // Have the children set their area indices.
- SetNodeAreaIndices_R( node->children[0] );
- SetNodeAreaIndices_R( node->children[1] );
-
- // If all children (leaves or nodes) are in the same area, then set our area
- // to this area as well. Otherwise, set it to -1.
- if( node->children[0]->area == node->children[1]->area )
- node->area = node->children[0]->area;
- else
- node->area = -1;
-}
-
-
-/*
-=============
-EmitAreaPortals
-
-=============
-*/
-void EmitAreaPortals (node_t *headnode)
-{
- entity_t *e;
- dareaportal_t *dp;
-
- if (c_areas > MAX_MAP_AREAS)
- Error ("Map is split into too many unique areas (max = %d)\nProbably too many areaportals", MAX_MAP_AREAS);
- numareas = c_areas+1;
- numareaportals = 1; // leave 0 as an error
-
- // Reset the clip portal vert info.
- g_nClipPortalVerts = 0;
-
- for (int iSrcArea=1 ; iSrcArea<=c_areas ; iSrcArea++)
- {
- dareas[iSrcArea].firstareaportal = numareaportals;
- for (int j=0 ; j<num_entities ; j++)
- {
- e = &entities[j];
- if (!e->areaportalnum)
- continue;
-
- if (e->portalareas[0] == iSrcArea || e->portalareas[1] == iSrcArea)
- {
- int iSide = (e->portalareas[0] == iSrcArea);
-
- // We're only interested in the portal that divides the two areas.
- // One of the portals that leads into the CONTENTS_AREAPORTAL just bounds
- // the same two areas but the other bounds two different ones.
- portal_t *pLeadingPortal = e->m_pPortalsLeadingIntoAreas[0];
- if( pLeadingPortal->nodes[0]->area == pLeadingPortal->nodes[1]->area )
- pLeadingPortal = e->m_pPortalsLeadingIntoAreas[1];
-
- if( pLeadingPortal )
- {
- Assert( pLeadingPortal->nodes[0]->area != pLeadingPortal->nodes[1]->area );
-
- dp = &dareaportals[numareaportals];
- numareaportals++;
-
- dp->m_PortalKey = e->areaportalnum;
- dp->otherarea = e->portalareas[iSide];
- dp->planenum = pLeadingPortal->onnode->planenum;
-
- Assert( pLeadingPortal->nodes[0]->planenum == PLANENUM_LEAF );
- Assert( pLeadingPortal->nodes[1]->planenum == PLANENUM_LEAF );
-
- if( pLeadingPortal->nodes[0]->area == dp->otherarea )
- {
- // Use the flipped version of the plane.
- dp->planenum = (dp->planenum & ~1) | (~dp->planenum & 1);
- }
-
- EmitClipPortalGeometry( headnode, pLeadingPortal, iSrcArea, dp );
- }
- }
- }
-
- dareas[iSrcArea].numareaportals = numareaportals - dareas[iSrcArea].firstareaportal;
- }
-
- SetNodeAreaIndices_R( headnode );
-
- qprintf ("%5i numareas\n", numareas);
- qprintf ("%5i numareaportals\n", numareaportals);
-}
-
-/*
-=============
-FloodAreas
-
-Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
-=============
-*/
-void FloodAreas (tree_t *tree)
-{
- int start = Plat_FloatTime();
- qprintf ("--- FloodAreas ---\n");
- Msg("Processing areas...");
- FindAreas_r (tree->headnode);
- SetAreaPortalAreas_r (tree, tree->headnode);
- qprintf ("%5i areas\n", c_areas);
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
-}
-
-//======================================================
-
-int c_outside;
-int c_inside;
-int c_solid;
-
-void FillOutside_r (node_t *node)
-{
- if (node->planenum != PLANENUM_LEAF)
- {
- FillOutside_r (node->children[0]);
- FillOutside_r (node->children[1]);
- return;
- }
-
- // anything not reachable by an entity
- // can be filled away
- if (!node->occupied)
- {
- if (node->contents != CONTENTS_SOLID)
- {
- c_outside++;
- node->contents = CONTENTS_SOLID;
- }
- else
- c_solid++;
- }
- else
- c_inside++;
-
-}
-
-/*
-=============
-FillOutside
-
-Fill all nodes that can't be reached by entities
-=============
-*/
-void FillOutside (node_t *headnode)
-{
- c_outside = 0;
- c_inside = 0;
- c_solid = 0;
- qprintf ("--- FillOutside ---\n");
- FillOutside_r (headnode);
- qprintf ("%5i solid leafs\n", c_solid);
- qprintf ("%5i leafs filled\n", c_outside);
- qprintf ("%5i inside leafs\n", c_inside);
-}
-
-
-static float ComputeDistFromPlane( winding_t *pWinding, plane_t *pPlane, float maxdist )
-{
- float totaldist = 0.0f;
- for (int i = 0; i < pWinding->numpoints; ++i)
- {
- totaldist += fabs(DotProduct( pPlane->normal, pWinding->p[i] ) - pPlane->dist);
- if (totaldist > maxdist)
- return totaldist;
- }
- return totaldist;
-}
-
-
-//-----------------------------------------------------------------------------
-// Display portal error
-//-----------------------------------------------------------------------------
-static void DisplayPortalError( portal_t *p, int viscontents )
-{
- char contents[3][1024];
- PrintBrushContentsToString( p->nodes[0]->contents, contents[0], sizeof( contents[0] ) );
- PrintBrushContentsToString( p->nodes[1]->contents, contents[1], sizeof( contents[1] ) );
- PrintBrushContentsToString( viscontents, contents[2], sizeof( contents[2] ) );
-
- Vector center;
- WindingCenter( p->winding, center );
- Warning( "\nFindPortalSide: Couldn't find a good match for which brush to assign to a portal near (%.1f %.1f %.1f)\n", center.x, center.y, center.z);
- Warning( "Leaf 0 contents: %s\n", contents[0] );
- Warning( "Leaf 1 contents: %s\n", contents[1] );
- Warning( "viscontents (node 0 contents ^ node 1 contents): %s\n", contents[2] );
- Warning( "This means that none of the brushes in leaf 0 or 1 that touches the portal has %s\n", contents[2] );
- Warning( "Check for a huge brush enclosing the coordinates above that has contents %s\n", contents[2] );
- Warning( "Candidate brush IDs: " );
-
- CUtlVector<int> listed;
- for (int j=0 ; j<2 ; j++)
- {
- node_t *n = p->nodes[j];
- for (bspbrush_t *bb=n->brushlist ; bb ; bb=bb->next)
- {
- mapbrush_t *brush = bb->original;
- if ( brush->contents & viscontents )
- {
- if ( listed.Find( brush->brushnum ) == -1 )
- {
- listed.AddToTail( brush->brushnum );
- Warning( "Brush %d: ", brush->id );
- }
- }
- }
- }
- Warning( "\n\n" );
-}
-
-
-//==============================================================
-
-/*
-============
-FindPortalSide
-
-Finds a brush side to use for texturing the given portal
-============
-*/
-void FindPortalSide (portal_t *p)
-{
- int viscontents;
- bspbrush_t *bb;
- mapbrush_t *brush;
- node_t *n;
- int i,j;
- int planenum;
- side_t *side, *bestside;
- float bestdist;
- plane_t *p1, *p2;
-
- // decide which content change is strongest
- // solid > lava > water, etc
- viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
- if (!viscontents)
- {
- return;
- }
-
- planenum = p->onnode->planenum;
- bestside = NULL;
- bestdist = 1000000;
-
- for (j=0 ; j<2 ; j++)
- {
- n = p->nodes[j];
- p1 = &g_MainMap->mapplanes[p->onnode->planenum];
-
- for (bb=n->brushlist ; bb ; bb=bb->next)
- {
- brush = bb->original;
- if ( !(brush->contents & viscontents) )
- continue;
-
- for (i=0 ; i<brush->numsides ; i++)
- {
- side = &brush->original_sides[i];
- if (side->bevel)
- continue;
- if (side->texinfo == TEXINFO_NODE)
- continue; // non-visible
-
- if ((side->planenum&~1) == planenum)
- { // exact match
- bestside = &brush->original_sides[i];
- bestdist = 0.0f;
- goto gotit;
- }
-
- p2 = &g_MainMap->mapplanes[side->planenum&~1];
-
- float dist = ComputeDistFromPlane( p->winding, p2, bestdist );
- if (dist < bestdist)
- {
- bestside = side;
- bestdist = dist;
- }
- }
- }
- }
-
-gotit:
- if (!bestside)
- qprintf ("WARNING: side not found for portal\n");
-
- // Compute average dist, check for problems...
- if ((bestdist / p->winding->numpoints) > 2)
- {
- static int nWarnCount = 0;
- if ( nWarnCount < 8 )
- {
- DisplayPortalError( p, viscontents );
- if ( ++nWarnCount == 8 )
- {
- Warning("*** Suppressing further FindPortalSide errors.... ***\n" );
- }
- }
- }
-
- p->sidefound = true;
- p->side = bestside;
-}
-
-
-/*
-===============
-MarkVisibleSides_r
-
-===============
-*/
-void MarkVisibleSides_r (node_t *node)
-{
- portal_t *p;
- int s;
-
- if (node->planenum != PLANENUM_LEAF)
- {
- MarkVisibleSides_r (node->children[0]);
- MarkVisibleSides_r (node->children[1]);
- return;
- }
-
- // empty leafs are never boundary leafs
- if (!node->contents)
- return;
-
- // see if there is a visible face
- for (p=node->portals ; p ; p = p->next[!s])
- {
- s = (p->nodes[0] == node);
- if (!p->onnode)
- continue; // edge of world
- if (!p->sidefound)
- FindPortalSide (p);
- if (p->side)
- p->side->visible = true;
- }
-
-}
-
-/*
-=============
-MarkVisibleSides
-
-=============
-*/
-// UNDONE: Put detail brushes in a separate list (not mapbrushes) ?
-void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush, int detailScreen)
-{
- int i, j;
- mapbrush_t *mb;
- int numsides;
- qboolean detail;
-
- qprintf ("--- MarkVisibleSides ---\n");
-
- // clear all the visible flags
- for (i=startbrush ; i<endbrush ; i++)
- {
- mb = &g_MainMap->mapbrushes[i];
-
- if ( detailScreen != FULL_DETAIL )
- {
- qboolean onlyDetail = (detailScreen==ONLY_DETAIL)?true:false;
- // true for detail brushes
- detail = (mb->contents & CONTENTS_DETAIL) ? true : false;
- if ( onlyDetail ^ detail )
- {
- // both of these must have the same value or we're not interested in this brush
- continue;
- }
- }
-
- numsides = mb->numsides;
- for (j=0 ; j<numsides ; j++)
- mb->original_sides[j].visible = false;
- }
-
- // set visible flags on the sides that are used by portals
- MarkVisibleSides_r (tree->headnode);
-}
-
-
-//-----------------------------------------------------------------------------
-// Used to determine which sides are visible
-//-----------------------------------------------------------------------------
-void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount )
-{
- qprintf ("--- MarkVisibleSides ---\n");
-
- // clear all the visible flags
- int i, j;
- for ( i=0; i < nCount; ++i )
- {
- mapbrush_t *mb = ppBrushes[i];
- int numsides = mb->numsides;
- for (j=0 ; j<numsides ; j++)
- {
- mb->original_sides[j].visible = false;
- }
- }
-
- // set visible flags on the sides that are used by portals
- MarkVisibleSides_r( tree->headnode );
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "utlvector.h"
+#include "mathlib/vmatrix.h"
+#include "iscratchpad3d.h"
+#include "csg.h"
+#include "fmtstr.h"
+
+int c_active_portals;
+int c_peak_portals;
+int c_boundary;
+int c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+ static int s_PortalCount = 0;
+
+ portal_t *p;
+
+ if (numthreads == 1)
+ c_active_portals++;
+ if (c_active_portals > c_peak_portals)
+ c_peak_portals = c_active_portals;
+
+ p = (portal_t*)malloc (sizeof(portal_t));
+ memset (p, 0, sizeof(portal_t));
+ p->id = s_PortalCount;
+ ++s_PortalCount;
+
+ return p;
+}
+
+void FreePortal (portal_t *p)
+{
+ if (p->winding)
+ FreeWinding (p->winding);
+ if (numthreads == 1)
+ c_active_portals--;
+ free (p);
+}
+
+//==============================================================
+
+/*
+==============
+VisibleContents
+
+Returns the single content bit of the
+strongest visible content present
+==============
+*/
+int VisibleContents (int contents)
+{
+ int i;
+
+ for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
+ {
+ if (contents & i )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+===============
+ClusterContents
+===============
+*/
+int ClusterContents (node_t *node)
+{
+ int c1, c2, c;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return node->contents;
+
+ c1 = ClusterContents(node->children[0]);
+ c2 = ClusterContents(node->children[1]);
+ c = c1|c2;
+
+ // a cluster may include some solid detail areas, but
+ // still be seen into
+ if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
+ c &= ~CONTENTS_SOLID;
+ return c;
+}
+
+/*
+=============
+Portal_VisFlood
+
+Returns true if the portal is empty or translucent, allowing
+the PVS calculation to see through it.
+The nodes on either side of the portal may actually be clusters,
+not leafs, so all contents should be ored together
+=============
+*/
+qboolean Portal_VisFlood (portal_t *p)
+{
+ int c1, c2;
+
+ if (!p->onnode)
+ return false; // to global outsideleaf
+
+ c1 = ClusterContents(p->nodes[0]);
+ c2 = ClusterContents(p->nodes[1]);
+
+ if (!VisibleContents (c1^c2))
+ return true;
+
+ if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c1 = 0;
+ if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c2 = 0;
+
+ if ( (c1|c2) & CONTENTS_SOLID )
+ return false; // can't see through solid
+
+ if (! (c1 ^ c2))
+ return true; // identical on both sides
+
+ if (!VisibleContents (c1^c2))
+ return true;
+ return false;
+}
+
+
+/*
+===============
+Portal_EntityFlood
+
+The entity flood determines which areas are
+"outside" on the map, which are then filled in.
+Flowing from side s to side !s
+===============
+*/
+qboolean Portal_EntityFlood (portal_t *p, int s)
+{
+ if (p->nodes[0]->planenum != PLANENUM_LEAF
+ || p->nodes[1]->planenum != PLANENUM_LEAF)
+ Error ("Portal_EntityFlood: not a leaf");
+
+ // can never cross to a solid
+ if ( (p->nodes[0]->contents & CONTENTS_SOLID)
+ || (p->nodes[1]->contents & CONTENTS_SOLID) )
+ return false;
+
+ // can flood through everything else
+ return true;
+}
+
+qboolean Portal_AreaLeakFlood (portal_t *p, int s)
+{
+ if ( !Portal_EntityFlood( p, s ) )
+ return false;
+
+ // can never cross through areaportal
+ if ( (p->nodes[0]->contents & CONTENTS_AREAPORTAL)
+ || (p->nodes[1]->contents & CONTENTS_AREAPORTAL) )
+ return false;
+
+ // can flood through everything else
+ return true;
+}
+
+
+//=============================================================================
+
+int c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+ if (p->nodes[0] || p->nodes[1])
+ Error ("AddPortalToNode: allready included");
+
+ p->nodes[0] = front;
+ p->next[0] = front->portals;
+ front->portals = p;
+
+ p->nodes[1] = back;
+ p->next[1] = back->portals;
+ back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+ portal_t **pp, *t;
+
+// remove reference to the current portal
+ pp = &l->portals;
+ while (1)
+ {
+ t = *pp;
+ if (!t)
+ Error ("RemovePortalFromNode: portal not in leaf");
+
+ if ( t == portal )
+ break;
+
+ if (t->nodes[0] == l)
+ pp = &t->next[0];
+ else if (t->nodes[1] == l)
+ pp = &t->next[1];
+ else
+ Error ("RemovePortalFromNode: portal not bounding leaf");
+ }
+
+ if (portal->nodes[0] == l)
+ {
+ *pp = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == l)
+ {
+ *pp = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+ int i;
+ winding_t *w;
+
+ w = p->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ Msg ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+ , w->p[i][1], w->p[i][2]);
+}
+
+// because of water areaportals support, the areaportal may not be the only brush on this node
+bspbrush_t *AreaportalBrushForNode( node_t *node )
+{
+ bspbrush_t *b = node->brushlist;
+ while ( b && !(b->original->contents & CONTENTS_AREAPORTAL) )
+ {
+ b = b->next;
+ }
+ Assert( b->original->entitynum != 0 );
+ return b;
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+// buffer space around sides of nodes
+#define SIDESPACE 8
+void MakeHeadnodePortals (tree_t *tree)
+{
+ Vector bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ node_t *node;
+
+ node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = tree->mins[i] - SIDESPACE;
+ bounds[1][i] = tree->maxs[i] + SIDESPACE;
+ }
+
+ tree->outside_node.planenum = PLANENUM_LEAF;
+ tree->outside_node.brushlist = NULL;
+ tree->outside_node.portals = NULL;
+ tree->outside_node.contents = 0;
+
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<2 ; j++)
+ {
+ n = j*3 + i;
+
+ p = AllocPortal ();
+ portals[n] = p;
+
+ pl = &bplanes[n];
+ memset (pl, 0, sizeof(*pl));
+ if (j)
+ {
+ pl->normal[i] = -1;
+ pl->dist = -bounds[j][i];
+ }
+ else
+ {
+ pl->normal[i] = 1;
+ pl->dist = bounds[j][i];
+ }
+ p->plane = *pl;
+ p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+ AddPortalToNodes (p, node, &tree->outside_node);
+ }
+
+// clip the basewindings by all the other planes
+ for (i=0 ; i<6 ; i++)
+ {
+ for (j=0 ; j<6 ; j++)
+ {
+ if (j == i)
+ continue;
+ ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+ }
+ }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define BASE_WINDING_EPSILON 0.001
+#define SPLIT_WINDING_EPSILON 0.001
+
+winding_t *BaseWindingForNode (node_t *node)
+{
+ winding_t *w;
+ node_t *n;
+ plane_t *plane;
+ Vector normal;
+ vec_t dist;
+
+ w = BaseWindingForPlane (g_MainMap->mapplanes[node->planenum].normal, g_MainMap->mapplanes[node->planenum].dist);
+
+ // clip by all the parents
+ for (n=node->parent ; n && w ; )
+ {
+ plane = &g_MainMap->mapplanes[n->planenum];
+
+ if (n->children[0] == node)
+ { // take front
+ ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+ }
+ else
+ { // take back
+ VectorSubtract (vec3_origin, plane->normal, normal);
+ dist = -plane->dist;
+ ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+ }
+ node = n;
+ n = n->parent;
+ }
+
+ return w;
+}
+
+//============================================================
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+ portal_t *new_portal, *p;
+ winding_t *w;
+ Vector normal;
+ float dist = 0.0f;
+ int side = 0;
+
+ w = BaseWindingForNode (node);
+
+ // clip the portal by all the other portals in the node
+ for (p = node->portals ; p && w; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ {
+ side = 0;
+ VectorCopy (p->plane.normal, normal);
+ dist = p->plane.dist;
+ }
+ else if (p->nodes[1] == node)
+ {
+ side = 1;
+ VectorSubtract (vec3_origin, p->plane.normal, normal);
+ dist = -p->plane.dist;
+ }
+ else
+ {
+ Error ("CutNodePortals_r: mislinked portal");
+ }
+
+ ChopWindingInPlace (&w, normal, dist, 0.1);
+ }
+
+ if (!w)
+ {
+ return;
+ }
+
+ if (WindingIsTiny (w))
+ {
+ c_tinyportals++;
+ FreeWinding (w);
+ return;
+ }
+
+
+ new_portal = AllocPortal ();
+ new_portal->plane = g_MainMap->mapplanes[node->planenum];
+ new_portal->onnode = node;
+ new_portal->winding = w;
+
+ AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+ portal_t *p, *next_portal, *new_portal;
+ node_t *f, *b, *other_node;
+ int side = 0;
+ plane_t *plane;
+ winding_t *frontwinding, *backwinding;
+
+ plane = &g_MainMap->mapplanes[node->planenum];
+ f = node->children[0];
+ b = node->children[1];
+
+ for (p = node->portals ; p ; p = next_portal)
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ next_portal = p->next[side];
+
+ other_node = p->nodes[!side];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+ ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+ SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+ if (frontwinding && WindingIsTiny(frontwinding))
+ {
+ FreeWinding (frontwinding);
+ frontwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (backwinding && WindingIsTiny(backwinding))
+ {
+ FreeWinding (backwinding);
+ backwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (!frontwinding && !backwinding)
+ { // tiny windings on both sides
+ continue;
+ }
+
+ if (!frontwinding)
+ {
+ FreeWinding (backwinding);
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ FreeWinding (frontwinding);
+ if (side == 0)
+ AddPortalToNodes (p, f, other_node);
+ else
+ AddPortalToNodes (p, other_node, f);
+ continue;
+ }
+
+ // the winding is split
+ new_portal = AllocPortal ();
+ *new_portal = *p;
+ new_portal->winding = backwinding;
+ FreeWinding (p->winding);
+ p->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (p, f, other_node);
+ AddPortalToNodes (new_portal, b, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (p, other_node, f);
+ AddPortalToNodes (new_portal, other_node, b);
+ }
+ }
+
+ node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+ portal_t *p;
+ int s;
+ int i;
+
+ // calc mins/maxs for both leafs and nodes
+ ClearBounds (node->mins, node->maxs);
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ for (i=0 ; i<p->winding->numpoints ; i++)
+ AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+ }
+}
+
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+ int i;
+
+ CalcNodeBounds (node);
+ if (node->mins[0] >= node->maxs[0])
+ {
+ Warning("WARNING: node without a volume\n");
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (node->mins[i] < (MIN_COORD_INTEGER-SIDESPACE) || node->maxs[i] > (MAX_COORD_INTEGER+SIDESPACE))
+ {
+ const char *pMatName = "<NO BRUSH>";
+ // split by brush side
+ if ( node->side )
+ {
+ texinfo_t *pTexInfo = &texinfo[node->side->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ }
+ Vector point = node->portals->winding->p[0];
+ Warning("WARNING: BSP node with unbounded volume (material: %s, near %s)\n", pMatName, VecToString(point) );
+ break;
+ }
+ }
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ MakeTreePortals_r (node->children[0]);
+ MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+ MakeHeadnodePortals (tree);
+ MakeTreePortals_r (tree->headnode);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+//-----------------------------------------------------------------------------
+// Purpose: Floods outward from the given node, marking visited nodes with
+// the number of hops from a node with an entity. If we ever mark
+// the outside_node for this tree, we've leaked.
+// Input : node -
+// dist -
+//-----------------------------------------------------------------------------
+void FloodPortals_r (node_t *node, int dist)
+{
+ portal_t *p;
+ int s;
+
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ // Skip nodes that have already been marked.
+ if (p->nodes[!s]->occupied)
+ continue;
+
+ // Skip portals that lead to or from nodes with solid contents.
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodPortals_r (p->nodes[!s], dist+1);
+ }
+}
+
+void FloodAreaLeak_r( node_t *node, int dist )
+{
+ portal_t *p;
+ int s;
+
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ if (p->nodes[!s]->occupied)
+ continue;
+
+ if (!Portal_AreaLeakFlood (p, s))
+ continue;
+
+ FloodAreaLeak_r( p->nodes[!s], dist+1 );
+ }
+}
+
+void ClearOccupied_r( node_t *headnode )
+{
+ if ( !headnode )
+ return;
+
+ headnode->occupied = 0;
+ ClearOccupied_r( headnode->children[0] );
+ ClearOccupied_r( headnode->children[1] );
+}
+
+void FloodAreaLeak( node_t *headnode, node_t *pFirstSide )
+{
+ ClearOccupied_r( headnode );
+ FloodAreaLeak_r( pFirstSide, 2 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For the given entity at the given origin, finds the leaf node in the
+// BSP tree that the entity occupies.
+//
+// We then flood outward from that leaf to see if the entity leaks.
+// Input : headnode -
+// origin -
+// occupant -
+// Output : Returns false if the entity is in solid, true if it is not.
+//-----------------------------------------------------------------------------
+qboolean PlaceOccupant (node_t *headnode, Vector& origin, entity_t *occupant)
+{
+ node_t *node;
+ vec_t d;
+ plane_t *plane;
+
+ // find the leaf to start in
+ node = headnode;
+ 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];
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ return false;
+
+ node->occupant = occupant;
+
+ // Flood outward from here to see if this entity leaks.
+ FloodPortals_r (node, 1);
+
+ return true;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities (tree_t *tree)
+{
+ int i;
+ Vector origin;
+ char *cl;
+ qboolean inside;
+ node_t *headnode;
+
+ headnode = tree->headnode;
+ qprintf ("--- FloodEntities ---\n");
+ inside = false;
+ tree->outside_node.occupied = 0;
+
+ for (i=1 ; i<num_entities ; i++)
+ {
+ GetVectorForKey (&entities[i], "origin", origin);
+ if (VectorCompare(origin, vec3_origin))
+ continue;
+
+ cl = ValueForKey (&entities[i], "classname");
+
+ origin[2] += 1; // so objects on floor are ok
+
+ // nudge playerstart around if needed so clipping hulls allways
+ // have a valid point
+ if (!strcmp (cl, "info_player_start"))
+ {
+ int x, y;
+
+ for (x=-16 ; x<=16 ; x += 16)
+ {
+ for (y=-16 ; y<=16 ; y += 16)
+ {
+ origin[0] += x;
+ origin[1] += y;
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ {
+ inside = true;
+ goto gotit;
+ }
+ origin[0] -= x;
+ origin[1] -= y;
+ }
+ }
+gotit: ;
+ }
+ else
+ {
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ inside = true;
+ }
+ }
+
+ if (!inside)
+ {
+ qprintf ("no entities in open -- no filling\n");
+ }
+
+ if (tree->outside_node.occupied)
+ {
+ qprintf ("entity reached from outside -- no filling\n" );
+ }
+
+ return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int c_areas;
+
+bool IsAreaportalNode( node_t *node )
+{
+ return ( node->contents & CONTENTS_AREAPORTAL ) ? true : false;
+}
+/*
+=============
+FloodAreas_r
+=============
+*/
+
+void FloodAreas_r (node_t *node, portal_t *pSeeThrough)
+{
+ portal_t *p;
+ int s;
+ bspbrush_t *b;
+ entity_t *e;
+
+ if ( IsAreaportalNode(node) )
+ {
+ // this node is part of an area portal
+ b = AreaportalBrushForNode( node );
+ e = &entities[b->original->entitynum];
+
+ // if the current area has allready touched this
+ // portal, we are done
+ if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
+ return;
+
+ // note the current area as bounding the portal
+ if (e->portalareas[1])
+ {
+ Warning("WARNING: areaportal entity %i (brush %i) touches > 2 areas\n", b->original->entitynum, b->original->id );
+ return;
+ }
+
+ if (e->portalareas[0])
+ {
+ e->portalareas[1] = c_areas;
+ e->m_pPortalsLeadingIntoAreas[1] = pSeeThrough;
+ }
+ else
+ {
+ e->portalareas[0] = c_areas;
+ e->m_pPortalsLeadingIntoAreas[0] = pSeeThrough;
+ }
+
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+ node->area = c_areas;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+#if 0
+ if (p->nodes[!s]->occupied)
+ continue;
+#endif
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodAreas_r (p->nodes[!s], p);
+ }
+}
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FindAreas_r (node->children[0]);
+ FindAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ if (!node->occupied)
+ return; // not reachable by entities
+
+ // area portals are allways only flooded into, never
+ // out of
+ if (IsAreaportalNode(node))
+ return;
+
+ c_areas++;
+ FloodAreas_r (node, NULL);
+}
+
+
+void ReportAreaportalLeak( tree_t *tree, node_t *node )
+{
+ portal_t *p, *pStart = NULL;
+ int s;
+
+ // Find a portal out of this areaportal into empty space
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ if ( !Portal_EntityFlood( p, !s ) )
+ continue;
+ if ( p->nodes[!s]->contents & CONTENTS_AREAPORTAL )
+ continue;
+
+ pStart = p;
+ break;
+ }
+
+ if ( pStart )
+ {
+ s = pStart->nodes[0] == node;
+ Assert(!(pStart->nodes[s]->contents & CONTENTS_AREAPORTAL) );
+ // flood fill the area outside this areaportal brush
+ FloodAreaLeak( tree->headnode, pStart->nodes[s] );
+
+ // find the portal into the longest path around the portal
+ portal_t *pBest = NULL;
+ int bestDist = 0;
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ if ( p == pStart )
+ continue;
+
+ s = (p->nodes[1] == node);
+ if ( p->nodes[!s]->occupied > bestDist )
+ {
+ pBest = p;
+ bestDist = p->nodes[!s]->occupied;
+ }
+ }
+ if ( pBest )
+ {
+ s = (pBest->nodes[0] == node);
+ // write the linefile that goes from pBest to pStart
+ AreaportalLeakFile( tree, pStart, pBest, pBest->nodes[s] );
+ }
+ }
+}
+
+
+/*
+=============
+SetAreaPortalAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void SetAreaPortalAreas_r (tree_t *tree, node_t *node)
+{
+ bspbrush_t *b;
+ entity_t *e;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ SetAreaPortalAreas_r (tree, node->children[0]);
+ SetAreaPortalAreas_r (tree, node->children[1]);
+ return;
+ }
+
+ if (IsAreaportalNode(node))
+ {
+ if (node->area)
+ return; // already set
+
+ b = AreaportalBrushForNode( node );
+ e = &entities[b->original->entitynum];
+ node->area = e->portalareas[0];
+ if (!e->portalareas[1])
+ {
+ ReportAreaportalLeak( tree, node );
+ Warning("\nBrush %i: areaportal brush doesn't touch two areas\n", b->original->id);
+ return;
+ }
+ }
+}
+
+
+// Return a positive value between 0 and 2*PI telling the angle distance
+// from flBaseAngle to flTestAngle.
+float AngleOffset( float flBaseAngle, float flTestAngle )
+{
+ while( flTestAngle > flBaseAngle )
+ flTestAngle -= 2 * M_PI;
+
+ return fmod( flBaseAngle - flTestAngle, (float) (2 * M_PI) );
+}
+
+
+int FindUniquePoints( const Vector2D *pPoints, int nPoints, int *indexMap, int nMaxIndexMapPoints, float flTolerance )
+{
+ float flToleranceSqr = flTolerance * flTolerance;
+
+ // This could be slightly more efficient.
+ int nUniquePoints = 0;
+ for ( int i=0; i < nPoints; i++ )
+ {
+ int j;
+ for ( j=0; j < nUniquePoints; j++ )
+ {
+ if ( pPoints[i].DistToSqr( pPoints[indexMap[j]] ) < flToleranceSqr )
+ break;
+ }
+ if ( j == nUniquePoints )
+ {
+ if ( nUniquePoints >= nMaxIndexMapPoints )
+ Error( "FindUniquePoints: overflowed unique point list (size %d).", nMaxIndexMapPoints );
+
+ indexMap[nUniquePoints++] = i;
+ }
+ }
+
+ return nUniquePoints;
+}
+
+// Build a 2D convex hull of the set of points.
+// This essentially giftwraps the points as it walks around the perimeter.
+int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndices )
+{
+ int nIndices = 0;
+ bool touched[512];
+ int indexMap[512];
+
+ if( nPoints == 0 )
+ return 0;
+
+
+ // If we don't collapse the points into a unique set, we can loop around forever
+ // and max out nMaxIndices.
+ nPoints = FindUniquePoints( pPoints, nPoints, indexMap, ARRAYSIZE( indexMap ), 0.1f );
+ memset( touched, 0, nPoints*sizeof(touched[0]) );
+
+ // Find the (lower) left side.
+ int i;
+ int iBest = 0;
+ for( i=1; i < nPoints; i++ )
+ {
+ if( pPoints[indexMap[i]].x < pPoints[indexMap[iBest]].x ||
+ (pPoints[indexMap[i]].x == pPoints[indexMap[iBest]].x && pPoints[indexMap[i]].y < pPoints[indexMap[iBest]].y) )
+ {
+ iBest = i;
+ }
+ }
+
+ touched[iBest] = true;
+ indices[0] = indexMap[iBest];
+ nIndices = 1;
+
+ Vector2D curEdge( 0, 1 );
+
+ // Wind around clockwise.
+ while( 1 )
+ {
+ Vector2D const *pStartPoint = &pPoints[ indices[nIndices-1] ];
+
+ float flEdgeAngle = atan2( curEdge.y, curEdge.x );
+
+ int iMinAngle = -1;
+ float flMinAngle = 5000;
+
+ for( i=0; i < nPoints; i++ )
+ {
+ Vector2D vTo = pPoints[indexMap[i]] - *pStartPoint;
+ float flDistToSqr = vTo.LengthSqr();
+ if ( flDistToSqr <= 0.1f )
+ continue;
+
+ // Get the angle from the edge to this point.
+ float flAngle = atan2( vTo.y, vTo.x );
+ flAngle = AngleOffset( flEdgeAngle, flAngle );
+
+ if( fabs( flAngle - flMinAngle ) < 0.00001f )
+ {
+ float flDistToTestSqr = pStartPoint->DistToSqr( pPoints[iMinAngle] );
+
+ // If the angle is the same, pick the point farthest away.
+ // unless the current one is closing the face loop
+ if ( iMinAngle != indices[0] && flDistToSqr > flDistToTestSqr )
+ {
+ flMinAngle = flAngle;
+ iMinAngle = indexMap[i];
+ }
+ }
+ else if( flAngle < flMinAngle )
+ {
+ flMinAngle = flAngle;
+ iMinAngle = indexMap[i];
+ }
+ }
+
+ if( iMinAngle == -1 )
+ {
+ // Couldn't find a point?
+ Assert( false );
+ break;
+ }
+ else if( iMinAngle == indices[0] )
+ {
+ // Finished.
+ break;
+ }
+ else
+ {
+ // Add this point.
+ if( nIndices >= nMaxIndices )
+ break;
+
+ for ( int jj = 0; jj < nIndices; jj++ )
+ {
+ // if this assert hits, this routine is broken and is generating a spiral
+ // rather than a closed polygon - basically an edge overlap of some kind
+ Assert(indices[jj] != iMinAngle );
+ }
+
+ indices[nIndices] = iMinAngle;
+ ++nIndices;
+ }
+
+ curEdge = pPoints[indices[nIndices-1]] - pPoints[indices[nIndices-2]];
+ }
+
+ return nIndices;
+}
+
+void FindPortalsLeadingToArea_R(
+ node_t *pHeadNode,
+ int iSrcArea,
+ int iDestArea,
+ plane_t *pPlane,
+ CUtlVector<portal_t*> &portals )
+{
+ if (pHeadNode->planenum != PLANENUM_LEAF)
+ {
+ FindPortalsLeadingToArea_R( pHeadNode->children[0], iSrcArea, iDestArea, pPlane, portals );
+ FindPortalsLeadingToArea_R( pHeadNode->children[1], iSrcArea, iDestArea, pPlane, portals );
+ return;
+ }
+
+ // Ok.. this is a leaf, check its portals.
+ int s;
+ for( portal_t *p = pHeadNode->portals; p ;p = p->next[!s] )
+ {
+ s = (p->nodes[0] == pHeadNode);
+
+ if( !p->nodes[0]->occupied || !p->nodes[1]->occupied )
+ continue;
+
+ if( p->nodes[1]->area == iDestArea && p->nodes[0]->area == iSrcArea ||
+ p->nodes[0]->area == iDestArea && p->nodes[1]->area == iSrcArea )
+ {
+ // Make sure the plane normals point the same way.
+ plane_t *pMapPlane = &g_MainMap->mapplanes[p->onnode->planenum];
+ float flDot = fabs( pMapPlane->normal.Dot( pPlane->normal ) );
+ if( fabs( 1 - flDot ) < 0.01f )
+ {
+ Vector vPlanePt1 = pPlane->normal * pPlane->dist;
+ Vector vPlanePt2 = pMapPlane->normal * pMapPlane->dist;
+
+ if( vPlanePt1.DistToSqr( vPlanePt2 ) < 0.01f )
+ {
+ portals.AddToTail( p );
+ }
+ }
+ }
+ }
+}
+
+
+void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, dareaportal_t *dp )
+{
+ // Build a list of all the points in portals from the same original face.
+ CUtlVector<portal_t*> portals;
+ FindPortalsLeadingToArea_R(
+ pHeadNode,
+ iSrcArea,
+ dp->otherarea,
+ &pPortal->plane,
+ portals );
+
+ CUtlVector<Vector> points;
+ for( int iPortal=0; iPortal < portals.Size(); iPortal++ )
+ {
+ portal_t *pPointPortal = portals[iPortal];
+ winding_t *pWinding = pPointPortal->winding;
+ for( int i=0; i < pWinding->numpoints; i++ )
+ {
+ points.AddToTail( pWinding->p[i] );
+ }
+ }
+
+ // Get the 2D convex hull.
+
+ //// First transform them into a plane.
+ QAngle vAngles;
+ Vector vecs[3];
+
+ VectorAngles( pPortal->plane.normal, vAngles );
+ AngleVectors( vAngles, &vecs[0], &vecs[1], &vecs[2] );
+ VMatrix mTransform;
+ mTransform.Identity();
+ mTransform.SetBasisVectors( vecs[0], vecs[1], vecs[2] );
+ VMatrix mInvTransform = mTransform.Transpose();
+
+ int i;
+ CUtlVector<Vector2D> points2D;
+ for( i=0; i < points.Size(); i++ )
+ {
+ Vector vTest = mTransform * points[i];
+ points2D.AddToTail( Vector2D( vTest.y, vTest.z ) );
+ }
+
+ // Build the hull.
+ int indices[512];
+ int nIndices = Convex2D( points2D.Base(), points2D.Size(), indices, 512 );
+
+ // Output the hull.
+ dp->m_FirstClipPortalVert = g_nClipPortalVerts;
+ dp->m_nClipPortalVerts = nIndices;
+
+ if ( nIndices >= 32 )
+ {
+ Warning( "Warning: area portal has %d verts. Could be a vbsp bug.\n", nIndices );
+ }
+
+ if( dp->m_FirstClipPortalVert + dp->m_nClipPortalVerts >= MAX_MAP_PORTALVERTS )
+ {
+ Vector *p = pPortal->winding->p;
+ Error( "MAX_MAP_PORTALVERTS (probably a broken areaportal near %.1f %.1f %.1f ", p->x, p->y, p->z );
+ }
+
+ for( i=0; i < nIndices; i++ )
+ {
+ g_ClipPortalVerts[g_nClipPortalVerts] = points[ indices[i] ];
+ ++g_nClipPortalVerts;
+ }
+}
+
+
+// Sets node_t::area for non-leaf nodes (this allows an optimization in the renderer).
+void SetNodeAreaIndices_R( node_t *node )
+{
+ // All leaf area indices should already be set.
+ if( node->planenum == PLANENUM_LEAF )
+ return;
+
+ // Have the children set their area indices.
+ SetNodeAreaIndices_R( node->children[0] );
+ SetNodeAreaIndices_R( node->children[1] );
+
+ // If all children (leaves or nodes) are in the same area, then set our area
+ // to this area as well. Otherwise, set it to -1.
+ if( node->children[0]->area == node->children[1]->area )
+ node->area = node->children[0]->area;
+ else
+ node->area = -1;
+}
+
+
+/*
+=============
+EmitAreaPortals
+
+=============
+*/
+void EmitAreaPortals (node_t *headnode)
+{
+ entity_t *e;
+ dareaportal_t *dp;
+
+ if (c_areas > MAX_MAP_AREAS)
+ Error ("Map is split into too many unique areas (max = %d)\nProbably too many areaportals", MAX_MAP_AREAS);
+ numareas = c_areas+1;
+ numareaportals = 1; // leave 0 as an error
+
+ // Reset the clip portal vert info.
+ g_nClipPortalVerts = 0;
+
+ for (int iSrcArea=1 ; iSrcArea<=c_areas ; iSrcArea++)
+ {
+ dareas[iSrcArea].firstareaportal = numareaportals;
+ for (int j=0 ; j<num_entities ; j++)
+ {
+ e = &entities[j];
+ if (!e->areaportalnum)
+ continue;
+
+ if (e->portalareas[0] == iSrcArea || e->portalareas[1] == iSrcArea)
+ {
+ int iSide = (e->portalareas[0] == iSrcArea);
+
+ // We're only interested in the portal that divides the two areas.
+ // One of the portals that leads into the CONTENTS_AREAPORTAL just bounds
+ // the same two areas but the other bounds two different ones.
+ portal_t *pLeadingPortal = e->m_pPortalsLeadingIntoAreas[0];
+ if( pLeadingPortal->nodes[0]->area == pLeadingPortal->nodes[1]->area )
+ pLeadingPortal = e->m_pPortalsLeadingIntoAreas[1];
+
+ if( pLeadingPortal )
+ {
+ Assert( pLeadingPortal->nodes[0]->area != pLeadingPortal->nodes[1]->area );
+
+ dp = &dareaportals[numareaportals];
+ numareaportals++;
+
+ dp->m_PortalKey = e->areaportalnum;
+ dp->otherarea = e->portalareas[iSide];
+ dp->planenum = pLeadingPortal->onnode->planenum;
+
+ Assert( pLeadingPortal->nodes[0]->planenum == PLANENUM_LEAF );
+ Assert( pLeadingPortal->nodes[1]->planenum == PLANENUM_LEAF );
+
+ if( pLeadingPortal->nodes[0]->area == dp->otherarea )
+ {
+ // Use the flipped version of the plane.
+ dp->planenum = (dp->planenum & ~1) | (~dp->planenum & 1);
+ }
+
+ EmitClipPortalGeometry( headnode, pLeadingPortal, iSrcArea, dp );
+ }
+ }
+ }
+
+ dareas[iSrcArea].numareaportals = numareaportals - dareas[iSrcArea].firstareaportal;
+ }
+
+ SetNodeAreaIndices_R( headnode );
+
+ qprintf ("%5i numareas\n", numareas);
+ qprintf ("%5i numareaportals\n", numareaportals);
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+ int start = Plat_FloatTime();
+ qprintf ("--- FloodAreas ---\n");
+ Msg("Processing areas...");
+ FindAreas_r (tree->headnode);
+ SetAreaPortalAreas_r (tree, tree->headnode);
+ qprintf ("%5i areas\n", c_areas);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+}
+
+//======================================================
+
+int c_outside;
+int c_inside;
+int c_solid;
+
+void FillOutside_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FillOutside_r (node->children[0]);
+ FillOutside_r (node->children[1]);
+ return;
+ }
+
+ // anything not reachable by an entity
+ // can be filled away
+ if (!node->occupied)
+ {
+ if (node->contents != CONTENTS_SOLID)
+ {
+ c_outside++;
+ node->contents = CONTENTS_SOLID;
+ }
+ else
+ c_solid++;
+ }
+ else
+ c_inside++;
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+ c_outside = 0;
+ c_inside = 0;
+ c_solid = 0;
+ qprintf ("--- FillOutside ---\n");
+ FillOutside_r (headnode);
+ qprintf ("%5i solid leafs\n", c_solid);
+ qprintf ("%5i leafs filled\n", c_outside);
+ qprintf ("%5i inside leafs\n", c_inside);
+}
+
+
+static float ComputeDistFromPlane( winding_t *pWinding, plane_t *pPlane, float maxdist )
+{
+ float totaldist = 0.0f;
+ for (int i = 0; i < pWinding->numpoints; ++i)
+ {
+ totaldist += fabs(DotProduct( pPlane->normal, pWinding->p[i] ) - pPlane->dist);
+ if (totaldist > maxdist)
+ return totaldist;
+ }
+ return totaldist;
+}
+
+
+//-----------------------------------------------------------------------------
+// Display portal error
+//-----------------------------------------------------------------------------
+static void DisplayPortalError( portal_t *p, int viscontents )
+{
+ char contents[3][1024];
+ PrintBrushContentsToString( p->nodes[0]->contents, contents[0], sizeof( contents[0] ) );
+ PrintBrushContentsToString( p->nodes[1]->contents, contents[1], sizeof( contents[1] ) );
+ PrintBrushContentsToString( viscontents, contents[2], sizeof( contents[2] ) );
+
+ Vector center;
+ WindingCenter( p->winding, center );
+ Warning( "\nFindPortalSide: Couldn't find a good match for which brush to assign to a portal near (%.1f %.1f %.1f)\n", center.x, center.y, center.z);
+ Warning( "Leaf 0 contents: %s\n", contents[0] );
+ Warning( "Leaf 1 contents: %s\n", contents[1] );
+ Warning( "viscontents (node 0 contents ^ node 1 contents): %s\n", contents[2] );
+ Warning( "This means that none of the brushes in leaf 0 or 1 that touches the portal has %s\n", contents[2] );
+ Warning( "Check for a huge brush enclosing the coordinates above that has contents %s\n", contents[2] );
+ Warning( "Candidate brush IDs: " );
+
+ CUtlVector<int> listed;
+ for (int j=0 ; j<2 ; j++)
+ {
+ node_t *n = p->nodes[j];
+ for (bspbrush_t *bb=n->brushlist ; bb ; bb=bb->next)
+ {
+ mapbrush_t *brush = bb->original;
+ if ( brush->contents & viscontents )
+ {
+ if ( listed.Find( brush->brushnum ) == -1 )
+ {
+ listed.AddToTail( brush->brushnum );
+ Warning( "Brush %d: ", brush->id );
+ }
+ }
+ }
+ }
+ Warning( "\n\n" );
+}
+
+
+//==============================================================
+
+/*
+============
+FindPortalSide
+
+Finds a brush side to use for texturing the given portal
+============
+*/
+void FindPortalSide (portal_t *p)
+{
+ int viscontents;
+ bspbrush_t *bb;
+ mapbrush_t *brush;
+ node_t *n;
+ int i,j;
+ int planenum;
+ side_t *side, *bestside;
+ float bestdist;
+ plane_t *p1, *p2;
+
+ // decide which content change is strongest
+ // solid > lava > water, etc
+ viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
+ if (!viscontents)
+ {
+ return;
+ }
+
+ planenum = p->onnode->planenum;
+ bestside = NULL;
+ bestdist = 1000000;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ n = p->nodes[j];
+ p1 = &g_MainMap->mapplanes[p->onnode->planenum];
+
+ for (bb=n->brushlist ; bb ; bb=bb->next)
+ {
+ brush = bb->original;
+ if ( !(brush->contents & viscontents) )
+ continue;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->original_sides[i];
+ if (side->bevel)
+ continue;
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // non-visible
+
+ if ((side->planenum&~1) == planenum)
+ { // exact match
+ bestside = &brush->original_sides[i];
+ bestdist = 0.0f;
+ goto gotit;
+ }
+
+ p2 = &g_MainMap->mapplanes[side->planenum&~1];
+
+ float dist = ComputeDistFromPlane( p->winding, p2, bestdist );
+ if (dist < bestdist)
+ {
+ bestside = side;
+ bestdist = dist;
+ }
+ }
+ }
+ }
+
+gotit:
+ if (!bestside)
+ qprintf ("WARNING: side not found for portal\n");
+
+ // Compute average dist, check for problems...
+ if ((bestdist / p->winding->numpoints) > 2)
+ {
+ static int nWarnCount = 0;
+ if ( nWarnCount < 8 )
+ {
+ DisplayPortalError( p, viscontents );
+ if ( ++nWarnCount == 8 )
+ {
+ Warning("*** Suppressing further FindPortalSide errors.... ***\n" );
+ }
+ }
+ }
+
+ p->sidefound = true;
+ p->side = bestside;
+}
+
+
+/*
+===============
+MarkVisibleSides_r
+
+===============
+*/
+void MarkVisibleSides_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MarkVisibleSides_r (node->children[0]);
+ MarkVisibleSides_r (node->children[1]);
+ return;
+ }
+
+ // empty leafs are never boundary leafs
+ if (!node->contents)
+ return;
+
+ // see if there is a visible face
+ for (p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (!p->onnode)
+ continue; // edge of world
+ if (!p->sidefound)
+ FindPortalSide (p);
+ if (p->side)
+ p->side->visible = true;
+ }
+
+}
+
+/*
+=============
+MarkVisibleSides
+
+=============
+*/
+// UNDONE: Put detail brushes in a separate list (not mapbrushes) ?
+void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush, int detailScreen)
+{
+ int i, j;
+ mapbrush_t *mb;
+ int numsides;
+ qboolean detail;
+
+ qprintf ("--- MarkVisibleSides ---\n");
+
+ // clear all the visible flags
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mb = &g_MainMap->mapbrushes[i];
+
+ if ( detailScreen != FULL_DETAIL )
+ {
+ qboolean onlyDetail = (detailScreen==ONLY_DETAIL)?true:false;
+ // true for detail brushes
+ detail = (mb->contents & CONTENTS_DETAIL) ? true : false;
+ if ( onlyDetail ^ detail )
+ {
+ // both of these must have the same value or we're not interested in this brush
+ continue;
+ }
+ }
+
+ numsides = mb->numsides;
+ for (j=0 ; j<numsides ; j++)
+ mb->original_sides[j].visible = false;
+ }
+
+ // set visible flags on the sides that are used by portals
+ MarkVisibleSides_r (tree->headnode);
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to determine which sides are visible
+//-----------------------------------------------------------------------------
+void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount )
+{
+ qprintf ("--- MarkVisibleSides ---\n");
+
+ // clear all the visible flags
+ int i, j;
+ for ( i=0; i < nCount; ++i )
+ {
+ mapbrush_t *mb = ppBrushes[i];
+ int numsides = mb->numsides;
+ for (j=0 ; j<numsides ; j++)
+ {
+ mb->original_sides[j].visible = false;
+ }
+ }
+
+ // set visible flags on the sides that are used by portals
+ MarkVisibleSides_r( tree->headnode );
+}
+
diff --git a/mp/src/utils/vbsp/portals.h b/mp/src/utils/vbsp/portals.h
index e8b45c7b..cf65ac7c 100644
--- a/mp/src/utils/vbsp/portals.h
+++ b/mp/src/utils/vbsp/portals.h
@@ -1,19 +1,19 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef PORTALS_H
-#define PORTALS_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-// Sets up the g_ClipPortalIndices array.
-void TranslateClipPortalIndices();
-
-
-#endif // PORTALS_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PORTALS_H
+#define PORTALS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Sets up the g_ClipPortalIndices array.
+void TranslateClipPortalIndices();
+
+
+#endif // PORTALS_H
diff --git a/mp/src/utils/vbsp/prtfile.cpp b/mp/src/utils/vbsp/prtfile.cpp
index c192176b..71d7e5cc 100644
--- a/mp/src/utils/vbsp/prtfile.cpp
+++ b/mp/src/utils/vbsp/prtfile.cpp
@@ -1,374 +1,374 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-#include "collisionutils.h"
-/*
-==============================================================================
-
-PORTAL FILE GENERATION
-
-Save out name.prt for qvis to read
-==============================================================================
-*/
-
-
-#define PORTALFILE "PRT1"
-
-struct cluster_portals_t
-{
- CUtlVector<portal_t *> portals;
-};
-
-int num_visclusters; // clusters the player can be in
-int num_visportals;
-
-int g_SkyCluster = -1;
-
-void WriteFloat (FILE *f, vec_t v)
-{
- if ( fabs(v - RoundInt(v)) < 0.001 )
- fprintf (f,"%i ",(int)RoundInt(v));
- else
- fprintf (f,"%f ",v);
-}
-
-
-/*
-=================
-WritePortalFile_r
-=================
-*/
-void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list)
-{
- portal_t *p;
- winding_t *w;
- Vector normal;
- vec_t dist;
-
- for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ )
- {
- for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ )
- {
- p = list[clusterIndex].portals[j];
- w = p->winding;
- // write out to the file
-
- // sometimes planes get turned around when they are very near
- // the changeover point between different axis. interpret the
- // plane the same way vis will, and flip the side orders if needed
- // FIXME: is this still relevent?
- WindingPlane (w, normal, &dist);
- if ( DotProduct (p->plane.normal, normal) < 0.99 )
- { // backwards...
- fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
- }
- else
- {
- fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
- }
-
- for (int i=0 ; i<w->numpoints ; i++)
- {
- fprintf (pFile,"(");
- WriteFloat (pFile, w->p[i][0]);
- WriteFloat (pFile, w->p[i][1]);
- WriteFloat (pFile, w->p[i][2]);
- fprintf (pFile,") ");
- }
- fprintf (pFile,"\n");
- }
- }
-}
-
-struct viscluster_t
-{
- bspbrush_t *pBrushes;
- int clusterIndex;
- int leafCount;
- int leafStart;
-};
-
-static CUtlVector<viscluster_t> g_VisClusters;
-
-// add to the list of brushes the merge leaves into single vis clusters
-void AddVisCluster( entity_t *pFuncVisCluster )
-{
- viscluster_t tmp;
- Vector clipMins, clipMaxs;
- clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER;
- clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER;
-
- // build the map brushes out into the minimum non-overlapping set of brushes
- bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes,
- clipMins, clipMaxs, NO_DETAIL);
- tmp.pBrushes = ChopBrushes( pBSPBrush );
-
- // store the entry in the list
- tmp.clusterIndex = -1;
- tmp.leafCount = 0;
- tmp.leafStart = 0;
-
-#if DEBUG_VISUALIZE_CLUSTERS
- int debug = atoi(ValueForKey(pFuncVisCluster,"debug"));
- if ( debug )
- {
- Msg("Debug vis cluster %d\n", g_VisClusters.Count() );
- }
-#endif
-
- g_VisClusters.AddToTail( tmp );
-
- // clear out this entity so it won't get written to the bsp
- pFuncVisCluster->epairs = NULL;
- pFuncVisCluster->numbrushes = 0;
-}
-
-// returns the total overlapping volume of intersection between the node and the brush list
-float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode )
-{
- float volume = 0.0f;
- for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next )
- {
- if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) )
- {
- bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush );
- if ( pIntersect )
- {
- volume += BrushVolume( pIntersect );
- FreeBrush( pIntersect );
- }
- }
- }
-
- return volume;
-}
-
-// Search for a forced cluster that this node is within
-// NOTE: Returns the first one found, these won't merge themselves together
-int GetVisCluster( node_t *pNode )
-{
- float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap
- int maxIndex = -1;
- // UNDONE: This could get slow
- for ( int i = g_VisClusters.Count(); --i >= 0; )
- {
- float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode );
- if ( volume > maxVolume )
- {
- volume = maxVolume;
- maxIndex = i;
- }
- }
- return maxIndex;
-}
-/*
-================
-NumberLeafs_r
-================
-*/
-void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves)
-{
- if (node->planenum != PLANENUM_LEAF)
- { // decision node
- node->cluster = -99;
- BuildVisLeafList_r (node->children[0], leaves);
- BuildVisLeafList_r (node->children[1], leaves);
- return;
- }
-
- if ( node->contents & CONTENTS_SOLID )
- { // solid block, viewpoint never inside
- node->cluster = -1;
- return;
- }
- leaves.AddToTail(node);
-}
-
-// Give each leaf in the list of empty leaves a vis cluster number
-// some are within func_viscluster volumes and will be merged together
-// every other leaf gets its own unique number
-void NumberLeafs( const CUtlVector<node_t *> &leaves )
-{
- for ( int i = 0; i < leaves.Count(); i++ )
- {
- node_t *node = leaves[i];
- int visCluster = GetVisCluster( node );
- if ( visCluster >= 0 )
- {
- if ( g_VisClusters[visCluster].clusterIndex < 0 )
- {
- g_VisClusters[visCluster].clusterIndex = num_visclusters;
- num_visclusters++;
- }
- g_VisClusters[visCluster].leafCount++;
- node->cluster = g_VisClusters[visCluster].clusterIndex;
- }
- else
- {
- if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) )
- {
- if ( g_SkyCluster < 0 )
- {
- // allocate a cluster for the sky
- g_SkyCluster = num_visclusters;
- num_visclusters++;
- }
- node->cluster = g_SkyCluster;
- }
- else
- {
- node->cluster = num_visclusters;
- num_visclusters++;
- }
- }
- }
-
-#if DEBUG_VISUALIZE_CLUSTERS
- for ( int i = 0; i < g_VisClusters.Count(); i++ )
- {
- char name[256];
- sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i );
- FileHandle_t fp = g_pFileSystem->Open( name, "w" );
- Msg("Writing %s\n", name );
- for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next )
- {
- for (int i = 0; i < pBrush->numsides; i++ )
- OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 );
- }
- for ( int j = 0; j < leaves.Count(); j++ )
- {
- if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex )
- {
- bspbrush_t *pBrush = leaves[j]->volume;
- for (int k = 0; k < pBrush->numsides; k++ )
- OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) );
- }
- }
- g_pFileSystem->Close(fp);
- }
-#endif
-}
-
-// build a list of all vis portals that connect clusters
-int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves )
-{
- int portalCount = 0;
- for ( int i = 0; i < leaves.Count(); i++ )
- {
- node_t *node = leaves[i];
- // count the portals
- for (portal_t *p = node->portals ; p ; )
- {
- if (p->nodes[0] == node) // only write out from first leaf
- {
- if ( p->nodes[0]->cluster != p->nodes[1]->cluster )
- {
- if (Portal_VisFlood (p))
- {
- portalCount++;
- portalList[node->cluster].portals.AddToTail(p);
- }
- }
- p = p->next[0];
- }
- else
- p = p->next[1];
- }
- }
- return portalCount;
-}
-
-
-/*
-================
-CreateVisPortals_r
-================
-*/
-void CreateVisPortals_r (node_t *node)
-{
- // stop as soon as we get to a leaf
- if (node->planenum == PLANENUM_LEAF )
- return;
-
- MakeNodePortal (node);
- SplitNodePortals (node);
-
- CreateVisPortals_r (node->children[0]);
- CreateVisPortals_r (node->children[1]);
-}
-
-int clusterleaf;
-void SaveClusters_r (node_t *node)
-{
- if (node->planenum == PLANENUM_LEAF)
- {
- dleafs[clusterleaf++].cluster = node->cluster;
- return;
- }
- SaveClusters_r (node->children[0]);
- SaveClusters_r (node->children[1]);
-}
-
-/*
-================
-WritePortalFile
-================
-*/
-void WritePortalFile (tree_t *tree)
-{
- char filename[1024];
- node_t *headnode;
- int start = Plat_FloatTime();
-
- qprintf ("--- WritePortalFile ---\n");
-
- sprintf (filename, "%s.prt", source);
- Msg ("writing %s...", filename);
-
- headnode = tree->headnode;
-
- FreeTreePortals_r (headnode);
- MakeHeadnodePortals (tree);
-
- CreateVisPortals_r (headnode);
-
-// set the cluster field in every leaf and count the total number of portals
- num_visclusters = 0;
- Msg("Building visibility clusters...\n");
- CUtlVector<node_t *> leaves;
- BuildVisLeafList_r( headnode, leaves );
-
- NumberLeafs (leaves);
- CUtlVector<cluster_portals_t> portalList;
- portalList.SetCount( num_visclusters );
- num_visportals = BuildPortalList( portalList, leaves );
-// write the file
- FILE *pf = fopen (filename, "w");
- if (!pf)
- Error ("Error opening %s", filename);
-
- fprintf (pf, "%s\n", PORTALFILE);
- fprintf (pf, "%i\n", num_visclusters);
- fprintf (pf, "%i\n", num_visportals);
-
- qprintf ("%5i visclusters\n", num_visclusters);
- qprintf ("%5i visportals\n", num_visportals);
-
- WritePortalFile(pf, portalList);
-
- fclose (pf);
-
- // we need to store the clusters out now because ordering
- // issues made us do this after writebsp...
- clusterleaf = 1;
- SaveClusters_r (headnode);
-
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "collisionutils.h"
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+Save out name.prt for qvis to read
+==============================================================================
+*/
+
+
+#define PORTALFILE "PRT1"
+
+struct cluster_portals_t
+{
+ CUtlVector<portal_t *> portals;
+};
+
+int num_visclusters; // clusters the player can be in
+int num_visportals;
+
+int g_SkyCluster = -1;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+ if ( fabs(v - RoundInt(v)) < 0.001 )
+ fprintf (f,"%i ",(int)RoundInt(v));
+ else
+ fprintf (f,"%f ",v);
+}
+
+
+/*
+=================
+WritePortalFile_r
+=================
+*/
+void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list)
+{
+ portal_t *p;
+ winding_t *w;
+ Vector normal;
+ vec_t dist;
+
+ for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ )
+ {
+ for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ )
+ {
+ p = list[clusterIndex].portals[j];
+ w = p->winding;
+ // write out to the file
+
+ // sometimes planes get turned around when they are very near
+ // the changeover point between different axis. interpret the
+ // plane the same way vis will, and flip the side orders if needed
+ // FIXME: is this still relevent?
+ WindingPlane (w, normal, &dist);
+ if ( DotProduct (p->plane.normal, normal) < 0.99 )
+ { // backwards...
+ fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
+ }
+ else
+ {
+ fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
+ }
+
+ for (int i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pFile,"(");
+ WriteFloat (pFile, w->p[i][0]);
+ WriteFloat (pFile, w->p[i][1]);
+ WriteFloat (pFile, w->p[i][2]);
+ fprintf (pFile,") ");
+ }
+ fprintf (pFile,"\n");
+ }
+ }
+}
+
+struct viscluster_t
+{
+ bspbrush_t *pBrushes;
+ int clusterIndex;
+ int leafCount;
+ int leafStart;
+};
+
+static CUtlVector<viscluster_t> g_VisClusters;
+
+// add to the list of brushes the merge leaves into single vis clusters
+void AddVisCluster( entity_t *pFuncVisCluster )
+{
+ viscluster_t tmp;
+ Vector clipMins, clipMaxs;
+ clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER;
+ clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER;
+
+ // build the map brushes out into the minimum non-overlapping set of brushes
+ bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes,
+ clipMins, clipMaxs, NO_DETAIL);
+ tmp.pBrushes = ChopBrushes( pBSPBrush );
+
+ // store the entry in the list
+ tmp.clusterIndex = -1;
+ tmp.leafCount = 0;
+ tmp.leafStart = 0;
+
+#if DEBUG_VISUALIZE_CLUSTERS
+ int debug = atoi(ValueForKey(pFuncVisCluster,"debug"));
+ if ( debug )
+ {
+ Msg("Debug vis cluster %d\n", g_VisClusters.Count() );
+ }
+#endif
+
+ g_VisClusters.AddToTail( tmp );
+
+ // clear out this entity so it won't get written to the bsp
+ pFuncVisCluster->epairs = NULL;
+ pFuncVisCluster->numbrushes = 0;
+}
+
+// returns the total overlapping volume of intersection between the node and the brush list
+float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode )
+{
+ float volume = 0.0f;
+ for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next )
+ {
+ if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) )
+ {
+ bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush );
+ if ( pIntersect )
+ {
+ volume += BrushVolume( pIntersect );
+ FreeBrush( pIntersect );
+ }
+ }
+ }
+
+ return volume;
+}
+
+// Search for a forced cluster that this node is within
+// NOTE: Returns the first one found, these won't merge themselves together
+int GetVisCluster( node_t *pNode )
+{
+ float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap
+ int maxIndex = -1;
+ // UNDONE: This could get slow
+ for ( int i = g_VisClusters.Count(); --i >= 0; )
+ {
+ float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode );
+ if ( volume > maxVolume )
+ {
+ volume = maxVolume;
+ maxIndex = i;
+ }
+ }
+ return maxIndex;
+}
+/*
+================
+NumberLeafs_r
+================
+*/
+void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ { // decision node
+ node->cluster = -99;
+ BuildVisLeafList_r (node->children[0], leaves);
+ BuildVisLeafList_r (node->children[1], leaves);
+ return;
+ }
+
+ if ( node->contents & CONTENTS_SOLID )
+ { // solid block, viewpoint never inside
+ node->cluster = -1;
+ return;
+ }
+ leaves.AddToTail(node);
+}
+
+// Give each leaf in the list of empty leaves a vis cluster number
+// some are within func_viscluster volumes and will be merged together
+// every other leaf gets its own unique number
+void NumberLeafs( const CUtlVector<node_t *> &leaves )
+{
+ for ( int i = 0; i < leaves.Count(); i++ )
+ {
+ node_t *node = leaves[i];
+ int visCluster = GetVisCluster( node );
+ if ( visCluster >= 0 )
+ {
+ if ( g_VisClusters[visCluster].clusterIndex < 0 )
+ {
+ g_VisClusters[visCluster].clusterIndex = num_visclusters;
+ num_visclusters++;
+ }
+ g_VisClusters[visCluster].leafCount++;
+ node->cluster = g_VisClusters[visCluster].clusterIndex;
+ }
+ else
+ {
+ if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) )
+ {
+ if ( g_SkyCluster < 0 )
+ {
+ // allocate a cluster for the sky
+ g_SkyCluster = num_visclusters;
+ num_visclusters++;
+ }
+ node->cluster = g_SkyCluster;
+ }
+ else
+ {
+ node->cluster = num_visclusters;
+ num_visclusters++;
+ }
+ }
+ }
+
+#if DEBUG_VISUALIZE_CLUSTERS
+ for ( int i = 0; i < g_VisClusters.Count(); i++ )
+ {
+ char name[256];
+ sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i );
+ FileHandle_t fp = g_pFileSystem->Open( name, "w" );
+ Msg("Writing %s\n", name );
+ for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next )
+ {
+ for (int i = 0; i < pBrush->numsides; i++ )
+ OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 );
+ }
+ for ( int j = 0; j < leaves.Count(); j++ )
+ {
+ if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex )
+ {
+ bspbrush_t *pBrush = leaves[j]->volume;
+ for (int k = 0; k < pBrush->numsides; k++ )
+ OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) );
+ }
+ }
+ g_pFileSystem->Close(fp);
+ }
+#endif
+}
+
+// build a list of all vis portals that connect clusters
+int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves )
+{
+ int portalCount = 0;
+ for ( int i = 0; i < leaves.Count(); i++ )
+ {
+ node_t *node = leaves[i];
+ // count the portals
+ for (portal_t *p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if ( p->nodes[0]->cluster != p->nodes[1]->cluster )
+ {
+ if (Portal_VisFlood (p))
+ {
+ portalCount++;
+ portalList[node->cluster].portals.AddToTail(p);
+ }
+ }
+ p = p->next[0];
+ }
+ else
+ p = p->next[1];
+ }
+ }
+ return portalCount;
+}
+
+
+/*
+================
+CreateVisPortals_r
+================
+*/
+void CreateVisPortals_r (node_t *node)
+{
+ // stop as soon as we get to a leaf
+ if (node->planenum == PLANENUM_LEAF )
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ CreateVisPortals_r (node->children[0]);
+ CreateVisPortals_r (node->children[1]);
+}
+
+int clusterleaf;
+void SaveClusters_r (node_t *node)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ dleafs[clusterleaf++].cluster = node->cluster;
+ return;
+ }
+ SaveClusters_r (node->children[0]);
+ SaveClusters_r (node->children[1]);
+}
+
+/*
+================
+WritePortalFile
+================
+*/
+void WritePortalFile (tree_t *tree)
+{
+ char filename[1024];
+ node_t *headnode;
+ int start = Plat_FloatTime();
+
+ qprintf ("--- WritePortalFile ---\n");
+
+ sprintf (filename, "%s.prt", source);
+ Msg ("writing %s...", filename);
+
+ headnode = tree->headnode;
+
+ FreeTreePortals_r (headnode);
+ MakeHeadnodePortals (tree);
+
+ CreateVisPortals_r (headnode);
+
+// set the cluster field in every leaf and count the total number of portals
+ num_visclusters = 0;
+ Msg("Building visibility clusters...\n");
+ CUtlVector<node_t *> leaves;
+ BuildVisLeafList_r( headnode, leaves );
+
+ NumberLeafs (leaves);
+ CUtlVector<cluster_portals_t> portalList;
+ portalList.SetCount( num_visclusters );
+ num_visportals = BuildPortalList( portalList, leaves );
+// write the file
+ FILE *pf = fopen (filename, "w");
+ if (!pf)
+ Error ("Error opening %s", filename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visclusters);
+ fprintf (pf, "%i\n", num_visportals);
+
+ qprintf ("%5i visclusters\n", num_visclusters);
+ qprintf ("%5i visportals\n", num_visportals);
+
+ WritePortalFile(pf, portalList);
+
+ fclose (pf);
+
+ // we need to store the clusters out now because ordering
+ // issues made us do this after writebsp...
+ clusterleaf = 1;
+ SaveClusters_r (headnode);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+}
+
diff --git a/mp/src/utils/vbsp/staticprop.cpp b/mp/src/utils/vbsp/staticprop.cpp
index 810465b3..b7b9b6cb 100644
--- a/mp/src/utils/vbsp/staticprop.cpp
+++ b/mp/src/utils/vbsp/staticprop.cpp
@@ -1,741 +1,741 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Places "detail" objects which are client-only renderable things
-//
-// $Revision: $
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "bsplib.h"
-#include "utlvector.h"
-#include "bspfile.h"
-#include "gamebspfile.h"
-#include "VPhysics_Interface.h"
-#include "Studio.h"
-#include "byteswap.h"
-#include "UtlBuffer.h"
-#include "CollisionUtils.h"
-#include <float.h>
-#include "CModel.h"
-#include "PhysDll.h"
-#include "utlsymbol.h"
-#include "tier1/strtools.h"
-#include "KeyValues.h"
-
-static void SetCurrentModel( studiohdr_t *pStudioHdr );
-static void FreeCurrentModelVertexes();
-
-IPhysicsCollision *s_pPhysCollision = NULL;
-
-//-----------------------------------------------------------------------------
-// These puppies are used to construct the game lumps
-//-----------------------------------------------------------------------------
-static CUtlVector<StaticPropDictLump_t> s_StaticPropDictLump;
-static CUtlVector<StaticPropLump_t> s_StaticPropLump;
-static CUtlVector<StaticPropLeafLump_t> s_StaticPropLeafLump;
-
-
-//-----------------------------------------------------------------------------
-// Used to build the static prop
-//-----------------------------------------------------------------------------
-struct StaticPropBuild_t
-{
- char const* m_pModelName;
- char const* m_pLightingOrigin;
- Vector m_Origin;
- QAngle m_Angles;
- int m_Solid;
- int m_Skin;
- int m_Flags;
- float m_FadeMinDist;
- float m_FadeMaxDist;
- bool m_FadesOut;
- float m_flForcedFadeScale;
- unsigned short m_nMinDXLevel;
- unsigned short m_nMaxDXLevel;
-};
-
-
-//-----------------------------------------------------------------------------
-// Used to cache collision model generation
-//-----------------------------------------------------------------------------
-struct ModelCollisionLookup_t
-{
- CUtlSymbol m_Name;
- CPhysCollide* m_pCollide;
-};
-
-static bool ModelLess( ModelCollisionLookup_t const& src1, ModelCollisionLookup_t const& src2 )
-{
- return src1.m_Name < src2.m_Name;
-}
-
-static CUtlRBTree<ModelCollisionLookup_t, unsigned short> s_ModelCollisionCache( 0, 32, ModelLess );
-static CUtlVector<int> s_LightingInfo;
-
-
-//-----------------------------------------------------------------------------
-// Gets the keyvalues from a studiohdr
-//-----------------------------------------------------------------------------
-bool StudioKeyValues( studiohdr_t* pStudioHdr, KeyValues *pValue )
-{
- if ( !pStudioHdr )
- return false;
-
- return pValue->LoadFromBuffer( pStudioHdr->pszName(), pStudioHdr->KeyValueText() );
-}
-
-
-//-----------------------------------------------------------------------------
-// Makes sure the studio model is a static prop
-//-----------------------------------------------------------------------------
-enum isstaticprop_ret
-{
- RET_VALID,
- RET_FAIL_NOT_MARKED_STATIC_PROP,
- RET_FAIL_DYNAMIC,
-};
-
-isstaticprop_ret IsStaticProp( studiohdr_t* pHdr )
-{
- if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
- return RET_FAIL_NOT_MARKED_STATIC_PROP;
-
- // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static
- KeyValues *modelKeyValues = new KeyValues(pHdr->pszName());
- if ( StudioKeyValues( pHdr, modelKeyValues ) )
- {
- KeyValues *sub = modelKeyValues->FindKey("prop_data");
- if ( sub )
- {
- if ( !(sub->GetInt( "allowstatic", 0 )) )
- {
- modelKeyValues->deleteThis();
- return RET_FAIL_DYNAMIC;
- }
- }
- }
- modelKeyValues->deleteThis();
-
- return RET_VALID;
-}
-
-
-//-----------------------------------------------------------------------------
-// Add static prop model to the list of models
-//-----------------------------------------------------------------------------
-
-static int AddStaticPropDictLump( char const* pModelName )
-{
- StaticPropDictLump_t dictLump;
- strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
-
- for (int i = s_StaticPropDictLump.Size(); --i >= 0; )
- {
- if (!memcmp(&s_StaticPropDictLump[i], &dictLump, sizeof(dictLump) ))
- return i;
- }
-
- return s_StaticPropDictLump.AddToTail( dictLump );
-}
-
-
-//-----------------------------------------------------------------------------
-// Load studio model vertex data from a file...
-//-----------------------------------------------------------------------------
-bool LoadStudioModel( char const* pModelName, char const* pEntityType, CUtlBuffer& buf )
-{
- if ( !g_pFullFileSystem->ReadFile( pModelName, NULL, buf ) )
- return false;
-
- // Check that it's valid
- if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
- strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
- {
- return false;
- }
-
- studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
-
- Studio_ConvertStudioHdrToNewVersion( pHdr );
-
- if (pHdr->version != STUDIO_VERSION)
- {
- return false;
- }
-
- isstaticprop_ret isStaticProp = IsStaticProp(pHdr);
- if ( isStaticProp != RET_VALID )
- {
- if ( isStaticProp == RET_FAIL_NOT_MARKED_STATIC_PROP )
- {
- Warning("Error! To use model \"%s\"\n"
- " with %s, it must be compiled with $staticprop!\n", pModelName, pEntityType );
- }
- else if ( isStaticProp == RET_FAIL_DYNAMIC )
- {
- Warning("Error! %s using model \"%s\", which must be used on a dynamic entity (i.e. prop_physics). Deleted.\n", pEntityType, pModelName );
- }
- return false;
- }
-
- // ensure reset
- pHdr->pVertexBase = NULL;
- pHdr->pIndexBase = NULL;
-
- return true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes a convex hull from a studio mesh
-//-----------------------------------------------------------------------------
-static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh )
-{
- // Generate a list of all verts in the mesh
- Vector** ppVerts = (Vector**)stackalloc(pMesh->numvertices * sizeof(Vector*) );
- const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData();
- Assert( vertData ); // This can only return NULL on X360 for now
- for (int i = 0; i < pMesh->numvertices; ++i)
- {
- ppVerts[i] = vertData->Position(i);
- }
-
- // Generate a convex hull from the verts
- return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes a convex hull from the studio model
-//-----------------------------------------------------------------------------
-CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
-{
- CUtlVector<CPhysConvex*> convexHulls;
-
- for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
- {
- mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
- for( int model = 0; model < pBodyPart->nummodels; ++model )
- {
- mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
- for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
- {
- // Make a convex hull for each mesh
- // NOTE: This won't work unless the model has been compiled
- // with $staticprop
- mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
- convexHulls.AddToTail( ComputeConvexHull( pStudioMesh ) );
- }
- }
- }
-
- // Convert an array of convex elements to a compiled collision model
- // (this deletes the convex elements)
- return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
-}
-
-
-//-----------------------------------------------------------------------------
-// Add, find collision model in cache
-//-----------------------------------------------------------------------------
-static CPhysCollide* GetCollisionModel( char const* pModelName )
-{
- // Convert to a common string
- char* pTemp = (char*)_alloca(strlen(pModelName) + 1);
- strcpy( pTemp, pModelName );
- _strlwr( pTemp );
-
- char* pSlash = strchr( pTemp, '\\' );
- while( pSlash )
- {
- *pSlash = '/';
- pSlash = strchr( pTemp, '\\' );
- }
-
- // Find it in the cache
- ModelCollisionLookup_t lookup;
- lookup.m_Name = pTemp;
- int i = s_ModelCollisionCache.Find( lookup );
- if (i != s_ModelCollisionCache.InvalidIndex())
- return s_ModelCollisionCache[i].m_pCollide;
-
- // Load the studio model file
- CUtlBuffer buf;
- if (!LoadStudioModel(pModelName, "prop_static", buf))
- {
- Warning("Error loading studio model \"%s\"!\n", pModelName );
-
- // This way we don't try to load it multiple times
- lookup.m_pCollide = 0;
- s_ModelCollisionCache.Insert( lookup );
-
- return 0;
- }
-
- // Compute the convex hull of the model...
- studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet();
-
- // necessary for vertex access
- SetCurrentModel( pStudioHdr );
-
- lookup.m_pCollide = ComputeConvexHull( pStudioHdr );
- s_ModelCollisionCache.Insert( lookup );
-
- if ( !lookup.m_pCollide )
- {
- Warning("Bad geometry on \"%s\"!\n", pModelName );
- }
-
- // Debugging
- if (g_DumpStaticProps)
- {
- static int propNum = 0;
- char tmp[128];
- sprintf( tmp, "staticprop%03d.txt", propNum );
- DumpCollideToGlView( lookup.m_pCollide, tmp );
- ++propNum;
- }
-
- FreeCurrentModelVertexes();
-
- // Insert into cache...
- return lookup.m_pCollide;
-}
-
-
-//-----------------------------------------------------------------------------
-// Tests a single leaf against the static prop
-//-----------------------------------------------------------------------------
-
-static bool TestLeafAgainstCollide( int depth, int* pNodeList,
- Vector const& origin, QAngle const& angles, CPhysCollide* pCollide )
-{
- // Copy the planes in the node list into a list of planes
- float* pPlanes = (float*)_alloca(depth * 4 * sizeof(float) );
- int idx = 0;
- for (int i = depth; --i >= 0; ++idx )
- {
- int sign = (pNodeList[i] < 0) ? -1 : 1;
- int node = (sign < 0) ? - pNodeList[i] - 1 : pNodeList[i];
- dnode_t* pNode = &dnodes[node];
- dplane_t* pPlane = &dplanes[pNode->planenum];
-
- pPlanes[idx*4] = sign * pPlane->normal[0];
- pPlanes[idx*4+1] = sign * pPlane->normal[1];
- pPlanes[idx*4+2] = sign * pPlane->normal[2];
- pPlanes[idx*4+3] = sign * pPlane->dist;
- }
-
- // Make a convex solid out of the planes
- CPhysConvex* pPhysConvex = s_pPhysCollision->ConvexFromPlanes( pPlanes, depth, 0.0f );
-
- // This should never happen, but if it does, return no collision
- Assert( pPhysConvex );
- if (!pPhysConvex)
- return false;
-
- CPhysCollide* pLeafCollide = s_pPhysCollision->ConvertConvexToCollide( &pPhysConvex, 1 );
-
- // Collide the leaf solid with the static prop solid
- trace_t tr;
- s_pPhysCollision->TraceCollide( vec3_origin, vec3_origin, pLeafCollide, vec3_angle,
- pCollide, origin, angles, &tr );
-
- s_pPhysCollision->DestroyCollide( pLeafCollide );
-
- return (tr.startsolid != 0);
-}
-
-//-----------------------------------------------------------------------------
-// Find all leaves that intersect with this bbox + test against the static prop..
-//-----------------------------------------------------------------------------
-
-static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList,
- Vector const& mins, Vector const& maxs,
- Vector const& origin, QAngle const& angles, CPhysCollide* pCollide,
- CUtlVector<unsigned short>& leafList )
-{
- Assert( pNodeList && pCollide );
- Vector cornermin, cornermax;
-
- while( node >= 0 )
- {
- dnode_t* pNode = &dnodes[node];
- dplane_t* pPlane = &dplanes[pNode->planenum];
-
- // Arbitrary split plane here
- for (int i = 0; i < 3; ++i)
- {
- if (pPlane->normal[i] >= 0)
- {
- cornermin[i] = mins[i];
- cornermax[i] = maxs[i];
- }
- else
- {
- cornermin[i] = maxs[i];
- cornermax[i] = mins[i];
- }
- }
-
- if (DotProduct( pPlane->normal, cornermax ) <= pPlane->dist)
- {
- // Add the node to the list of nodes
- pNodeList[depth] = node;
- ++depth;
-
- node = pNode->children[1];
- }
- else if (DotProduct( pPlane->normal, cornermin ) >= pPlane->dist)
- {
- // In this case, we are going in front of the plane. That means that
- // this plane must have an outward normal facing in the oppisite direction
- // We indicate this be storing a negative node index in the node list
- pNodeList[depth] = - node - 1;
- ++depth;
-
- node = pNode->children[0];
- }
- else
- {
- // Here the box is split by the node. First, we'll add the plane as if its
- // outward facing normal is in the direction of the node plane, then
- // we'll have to reverse it for the other child...
- pNodeList[depth] = node;
- ++depth;
-
- ComputeConvexHullLeaves_R( pNode->children[1],
- depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
-
- pNodeList[depth - 1] = - node - 1;
- ComputeConvexHullLeaves_R( pNode->children[0],
- depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
- return;
- }
- }
-
- Assert( pNodeList && pCollide );
-
- // Never add static props to solid leaves
- if ( (dleafs[-node-1].contents & CONTENTS_SOLID) == 0 )
- {
- if (TestLeafAgainstCollide( depth, pNodeList, origin, angles, pCollide ))
- {
- leafList.AddToTail( -node - 1 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Places Static Props in the level
-//-----------------------------------------------------------------------------
-
-static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin,
- QAngle const& angles, CUtlVector<unsigned short>& leafList )
-{
- // Compute an axis-aligned bounding box for the collide
- Vector mins, maxs;
- s_pPhysCollision->CollideGetAABB( &mins, &maxs, pCollide, origin, angles );
-
- // Find all leaves that intersect with the bounds
- int tempNodeList[1024];
- ComputeConvexHullLeaves_R( 0, 0, tempNodeList, mins, maxs,
- origin, angles, pCollide, leafList );
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the lighting origin
-//-----------------------------------------------------------------------------
-static bool ComputeLightingOrigin( StaticPropBuild_t const& build, Vector& lightingOrigin )
-{
- for (int i = s_LightingInfo.Count(); --i >= 0; )
- {
- int entIndex = s_LightingInfo[i];
-
- // Check against all lighting info entities
- char const* pTargetName = ValueForKey( &entities[entIndex], "targetname" );
- if (!Q_strcmp(pTargetName, build.m_pLightingOrigin))
- {
- GetVectorForKey( &entities[entIndex], "origin", lightingOrigin );
- return true;
- }
- }
-
- return false;
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Static Props in the level
-//-----------------------------------------------------------------------------
-static void AddStaticPropToLump( StaticPropBuild_t const& build )
-{
- // Get the collision model
- CPhysCollide* pConvexHull = GetCollisionModel( build.m_pModelName );
- if (!pConvexHull)
- return;
-
- // Compute the leaves the static prop's convex hull hits
- CUtlVector< unsigned short > leafList;
- ComputeStaticPropLeaves( pConvexHull, build.m_Origin, build.m_Angles, leafList );
-
- if ( !leafList.Count() )
- {
- Warning( "Static prop %s outside the map (%.2f, %.2f, %.2f)\n", build.m_pModelName, build.m_Origin.x, build.m_Origin.y, build.m_Origin.z );
- return;
- }
- // Insert an element into the lump data...
- int i = s_StaticPropLump.AddToTail( );
- StaticPropLump_t& propLump = s_StaticPropLump[i];
- propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName );
- VectorCopy( build.m_Origin, propLump.m_Origin );
- VectorCopy( build.m_Angles, propLump.m_Angles );
- propLump.m_FirstLeaf = s_StaticPropLeafLump.Count();
- propLump.m_LeafCount = leafList.Count();
- propLump.m_Solid = build.m_Solid;
- propLump.m_Skin = build.m_Skin;
- propLump.m_Flags = build.m_Flags;
- if (build.m_FadesOut)
- {
- propLump.m_Flags |= STATIC_PROP_FLAG_FADES;
- }
- propLump.m_FadeMinDist = build.m_FadeMinDist;
- propLump.m_FadeMaxDist = build.m_FadeMaxDist;
- propLump.m_flForcedFadeScale = build.m_flForcedFadeScale;
- propLump.m_nMinDXLevel = build.m_nMinDXLevel;
- propLump.m_nMaxDXLevel = build.m_nMaxDXLevel;
-
- if (build.m_pLightingOrigin && *build.m_pLightingOrigin)
- {
- if (ComputeLightingOrigin( build, propLump.m_LightingOrigin ))
- {
- propLump.m_Flags |= STATIC_PROP_USE_LIGHTING_ORIGIN;
- }
- }
-
- // Add the leaves to the leaf lump
- for (int j = 0; j < leafList.Size(); ++j)
- {
- StaticPropLeafLump_t insert;
- insert.m_Leaf = leafList[j];
- s_StaticPropLeafLump.AddToTail( insert );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Places static props in the lump
-//-----------------------------------------------------------------------------
-
-static void SetLumpData( )
-{
- GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_STATIC_PROPS);
- if (handle != g_GameLumps.InvalidGameLump())
- g_GameLumps.DestroyGameLump(handle);
-
- int dictsize = s_StaticPropDictLump.Size() * sizeof(StaticPropDictLump_t);
- int objsize = s_StaticPropLump.Size() * sizeof(StaticPropLump_t);
- int leafsize = s_StaticPropLeafLump.Size() * sizeof(StaticPropLeafLump_t);
- int size = dictsize + objsize + leafsize + 3 * sizeof(int);
-
- handle = g_GameLumps.CreateGameLump( GAMELUMP_STATIC_PROPS, size, 0, GAMELUMP_STATIC_PROPS_VERSION );
-
- // Serialize the data
- CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size );
- buf.PutInt( s_StaticPropDictLump.Size() );
- if (dictsize)
- buf.Put( s_StaticPropDictLump.Base(), dictsize );
- buf.PutInt( s_StaticPropLeafLump.Size() );
- if (leafsize)
- buf.Put( s_StaticPropLeafLump.Base(), leafsize );
- buf.PutInt( s_StaticPropLump.Size() );
- if (objsize)
- buf.Put( s_StaticPropLump.Base(), objsize );
-}
-
-
-//-----------------------------------------------------------------------------
-// Places Static Props in the level
-//-----------------------------------------------------------------------------
-
-void EmitStaticProps()
-{
- CreateInterfaceFn physicsFactory = GetPhysicsFactory();
- if ( physicsFactory )
- {
- s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
- if( !s_pPhysCollision )
- return;
- }
-
- // Generate a list of lighting origins, and strip them out
- int i;
- for ( i = 0; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!Q_strcmp(pEntity, "info_lighting"))
- {
- s_LightingInfo.AddToTail(i);
- }
- }
-
- // Emit specifically specified static props
- for ( i = 0; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static"))
- {
- StaticPropBuild_t build;
-
- GetVectorForKey( &entities[i], "origin", build.m_Origin );
- GetAnglesForKey( &entities[i], "angles", build.m_Angles );
- build.m_pModelName = ValueForKey( &entities[i], "model" );
- build.m_Solid = IntForKey( &entities[i], "solid" );
- build.m_Skin = IntForKey( &entities[i], "skin" );
- build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" );
- build.m_Flags = 0;//IntForKey( &entities[i], "spawnflags" ) & STATIC_PROP_WC_MASK;
- if (IntForKey( &entities[i], "ignorenormals" ) == 1)
- {
- build.m_Flags |= STATIC_PROP_IGNORE_NORMALS;
- }
- if (IntForKey( &entities[i], "disableshadows" ) == 1)
- {
- build.m_Flags |= STATIC_PROP_NO_SHADOW;
- }
- if (IntForKey( &entities[i], "disablevertexlighting" ) == 1)
- {
- build.m_Flags |= STATIC_PROP_NO_PER_VERTEX_LIGHTING;
- }
- if (IntForKey( &entities[i], "disableselfshadowing" ) == 1)
- {
- build.m_Flags |= STATIC_PROP_NO_SELF_SHADOWING;
- }
-
- if (IntForKey( &entities[i], "screenspacefade" ) == 1)
- {
- build.m_Flags |= STATIC_PROP_SCREEN_SPACE_FADE;
- }
-
- const char *pKey = ValueForKey( &entities[i], "fadescale" );
- if ( pKey && pKey[0] )
- {
- build.m_flForcedFadeScale = FloatForKey( &entities[i], "fadescale" );
- }
- else
- {
- build.m_flForcedFadeScale = 1;
- }
- build.m_FadesOut = (build.m_FadeMaxDist > 0);
- build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" );
- if (build.m_FadesOut)
- {
- build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" );
- if (build.m_FadeMinDist < 0)
- {
- build.m_FadeMinDist = build.m_FadeMaxDist;
- }
- }
- else
- {
- build.m_FadeMinDist = 0;
- }
- build.m_nMinDXLevel = (unsigned short)IntForKey( &entities[i], "mindxlevel" );
- build.m_nMaxDXLevel = (unsigned short)IntForKey( &entities[i], "maxdxlevel" );
- AddStaticPropToLump( build );
-
- // strip this ent from the .bsp file
- entities[i].epairs = 0;
- }
- }
-
- // Strip out lighting origins; has to be done here because they are used when
- // static props are made
- for ( i = s_LightingInfo.Count(); --i >= 0; )
- {
- // strip this ent from the .bsp file
- entities[s_LightingInfo[i]].epairs = 0;
- }
-
-
- SetLumpData( );
-}
-
-static studiohdr_t *g_pActiveStudioHdr;
-static void SetCurrentModel( studiohdr_t *pStudioHdr )
-{
- // track the correct model
- g_pActiveStudioHdr = pStudioHdr;
-}
-
-static void FreeCurrentModelVertexes()
-{
- Assert( g_pActiveStudioHdr );
-
- if ( g_pActiveStudioHdr->pVertexBase )
- {
- free( g_pActiveStudioHdr->pVertexBase );
- g_pActiveStudioHdr->pVertexBase = NULL;
- }
-}
-
-const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
-{
- char fileName[260];
- FileHandle_t fileHandle;
- vertexFileHeader_t *pVvdHdr;
-
- Assert( pModelData == NULL );
- Assert( g_pActiveStudioHdr );
-
- if ( g_pActiveStudioHdr->pVertexBase )
- {
- return (vertexFileHeader_t *)g_pActiveStudioHdr->pVertexBase;
- }
-
- // mandatory callback to make requested data resident
- // load and persist the vertex file
- strcpy( fileName, "models/" );
- strcat( fileName, g_pActiveStudioHdr->pszName() );
- Q_StripExtension( fileName, fileName, sizeof( fileName ) );
- strcat( fileName, ".vvd" );
-
- // load the model
- fileHandle = g_pFileSystem->Open( fileName, "rb" );
- if ( !fileHandle )
- {
- Error( "Unable to load vertex data \"%s\"\n", fileName );
- }
-
- // Get the file size
- int size = g_pFileSystem->Size( fileHandle );
- if (size == 0)
- {
- g_pFileSystem->Close( fileHandle );
- Error( "Bad size for vertex data \"%s\"\n", fileName );
- }
-
- pVvdHdr = (vertexFileHeader_t *)malloc(size);
- g_pFileSystem->Read( pVvdHdr, size, fileHandle );
- g_pFileSystem->Close( fileHandle );
-
- // check header
- if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
- {
- Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
- }
- if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
- {
- Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
- }
- if (pVvdHdr->checksum != g_pActiveStudioHdr->checksum)
- {
- Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, g_pActiveStudioHdr->checksum);
- }
-
- g_pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
- return pVvdHdr;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Places "detail" objects which are client-only renderable things
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "bsplib.h"
+#include "utlvector.h"
+#include "bspfile.h"
+#include "gamebspfile.h"
+#include "VPhysics_Interface.h"
+#include "Studio.h"
+#include "byteswap.h"
+#include "UtlBuffer.h"
+#include "CollisionUtils.h"
+#include <float.h>
+#include "CModel.h"
+#include "PhysDll.h"
+#include "utlsymbol.h"
+#include "tier1/strtools.h"
+#include "KeyValues.h"
+
+static void SetCurrentModel( studiohdr_t *pStudioHdr );
+static void FreeCurrentModelVertexes();
+
+IPhysicsCollision *s_pPhysCollision = NULL;
+
+//-----------------------------------------------------------------------------
+// These puppies are used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<StaticPropDictLump_t> s_StaticPropDictLump;
+static CUtlVector<StaticPropLump_t> s_StaticPropLump;
+static CUtlVector<StaticPropLeafLump_t> s_StaticPropLeafLump;
+
+
+//-----------------------------------------------------------------------------
+// Used to build the static prop
+//-----------------------------------------------------------------------------
+struct StaticPropBuild_t
+{
+ char const* m_pModelName;
+ char const* m_pLightingOrigin;
+ Vector m_Origin;
+ QAngle m_Angles;
+ int m_Solid;
+ int m_Skin;
+ int m_Flags;
+ float m_FadeMinDist;
+ float m_FadeMaxDist;
+ bool m_FadesOut;
+ float m_flForcedFadeScale;
+ unsigned short m_nMinDXLevel;
+ unsigned short m_nMaxDXLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// Used to cache collision model generation
+//-----------------------------------------------------------------------------
+struct ModelCollisionLookup_t
+{
+ CUtlSymbol m_Name;
+ CPhysCollide* m_pCollide;
+};
+
+static bool ModelLess( ModelCollisionLookup_t const& src1, ModelCollisionLookup_t const& src2 )
+{
+ return src1.m_Name < src2.m_Name;
+}
+
+static CUtlRBTree<ModelCollisionLookup_t, unsigned short> s_ModelCollisionCache( 0, 32, ModelLess );
+static CUtlVector<int> s_LightingInfo;
+
+
+//-----------------------------------------------------------------------------
+// Gets the keyvalues from a studiohdr
+//-----------------------------------------------------------------------------
+bool StudioKeyValues( studiohdr_t* pStudioHdr, KeyValues *pValue )
+{
+ if ( !pStudioHdr )
+ return false;
+
+ return pValue->LoadFromBuffer( pStudioHdr->pszName(), pStudioHdr->KeyValueText() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Makes sure the studio model is a static prop
+//-----------------------------------------------------------------------------
+enum isstaticprop_ret
+{
+ RET_VALID,
+ RET_FAIL_NOT_MARKED_STATIC_PROP,
+ RET_FAIL_DYNAMIC,
+};
+
+isstaticprop_ret IsStaticProp( studiohdr_t* pHdr )
+{
+ if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
+ return RET_FAIL_NOT_MARKED_STATIC_PROP;
+
+ // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static
+ KeyValues *modelKeyValues = new KeyValues(pHdr->pszName());
+ if ( StudioKeyValues( pHdr, modelKeyValues ) )
+ {
+ KeyValues *sub = modelKeyValues->FindKey("prop_data");
+ if ( sub )
+ {
+ if ( !(sub->GetInt( "allowstatic", 0 )) )
+ {
+ modelKeyValues->deleteThis();
+ return RET_FAIL_DYNAMIC;
+ }
+ }
+ }
+ modelKeyValues->deleteThis();
+
+ return RET_VALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add static prop model to the list of models
+//-----------------------------------------------------------------------------
+
+static int AddStaticPropDictLump( char const* pModelName )
+{
+ StaticPropDictLump_t dictLump;
+ strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
+
+ for (int i = s_StaticPropDictLump.Size(); --i >= 0; )
+ {
+ if (!memcmp(&s_StaticPropDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_StaticPropDictLump.AddToTail( dictLump );
+}
+
+
+//-----------------------------------------------------------------------------
+// Load studio model vertex data from a file...
+//-----------------------------------------------------------------------------
+bool LoadStudioModel( char const* pModelName, char const* pEntityType, CUtlBuffer& buf )
+{
+ if ( !g_pFullFileSystem->ReadFile( pModelName, NULL, buf ) )
+ return false;
+
+ // Check that it's valid
+ if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
+ strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
+ {
+ return false;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
+
+ Studio_ConvertStudioHdrToNewVersion( pHdr );
+
+ if (pHdr->version != STUDIO_VERSION)
+ {
+ return false;
+ }
+
+ isstaticprop_ret isStaticProp = IsStaticProp(pHdr);
+ if ( isStaticProp != RET_VALID )
+ {
+ if ( isStaticProp == RET_FAIL_NOT_MARKED_STATIC_PROP )
+ {
+ Warning("Error! To use model \"%s\"\n"
+ " with %s, it must be compiled with $staticprop!\n", pModelName, pEntityType );
+ }
+ else if ( isStaticProp == RET_FAIL_DYNAMIC )
+ {
+ Warning("Error! %s using model \"%s\", which must be used on a dynamic entity (i.e. prop_physics). Deleted.\n", pEntityType, pModelName );
+ }
+ return false;
+ }
+
+ // ensure reset
+ pHdr->pVertexBase = NULL;
+ pHdr->pIndexBase = NULL;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from a studio mesh
+//-----------------------------------------------------------------------------
+static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh )
+{
+ // Generate a list of all verts in the mesh
+ Vector** ppVerts = (Vector**)stackalloc(pMesh->numvertices * sizeof(Vector*) );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData();
+ Assert( vertData ); // This can only return NULL on X360 for now
+ for (int i = 0; i < pMesh->numvertices; ++i)
+ {
+ ppVerts[i] = vertData->Position(i);
+ }
+
+ // Generate a convex hull from the verts
+ return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from the studio model
+//-----------------------------------------------------------------------------
+CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
+{
+ CUtlVector<CPhysConvex*> convexHulls;
+
+ for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
+ for( int model = 0; model < pBodyPart->nummodels; ++model )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
+ for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
+ {
+ // Make a convex hull for each mesh
+ // NOTE: This won't work unless the model has been compiled
+ // with $staticprop
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
+ convexHulls.AddToTail( ComputeConvexHull( pStudioMesh ) );
+ }
+ }
+ }
+
+ // Convert an array of convex elements to a compiled collision model
+ // (this deletes the convex elements)
+ return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, find collision model in cache
+//-----------------------------------------------------------------------------
+static CPhysCollide* GetCollisionModel( char const* pModelName )
+{
+ // Convert to a common string
+ char* pTemp = (char*)_alloca(strlen(pModelName) + 1);
+ strcpy( pTemp, pModelName );
+ _strlwr( pTemp );
+
+ char* pSlash = strchr( pTemp, '\\' );
+ while( pSlash )
+ {
+ *pSlash = '/';
+ pSlash = strchr( pTemp, '\\' );
+ }
+
+ // Find it in the cache
+ ModelCollisionLookup_t lookup;
+ lookup.m_Name = pTemp;
+ int i = s_ModelCollisionCache.Find( lookup );
+ if (i != s_ModelCollisionCache.InvalidIndex())
+ return s_ModelCollisionCache[i].m_pCollide;
+
+ // Load the studio model file
+ CUtlBuffer buf;
+ if (!LoadStudioModel(pModelName, "prop_static", buf))
+ {
+ Warning("Error loading studio model \"%s\"!\n", pModelName );
+
+ // This way we don't try to load it multiple times
+ lookup.m_pCollide = 0;
+ s_ModelCollisionCache.Insert( lookup );
+
+ return 0;
+ }
+
+ // Compute the convex hull of the model...
+ studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet();
+
+ // necessary for vertex access
+ SetCurrentModel( pStudioHdr );
+
+ lookup.m_pCollide = ComputeConvexHull( pStudioHdr );
+ s_ModelCollisionCache.Insert( lookup );
+
+ if ( !lookup.m_pCollide )
+ {
+ Warning("Bad geometry on \"%s\"!\n", pModelName );
+ }
+
+ // Debugging
+ if (g_DumpStaticProps)
+ {
+ static int propNum = 0;
+ char tmp[128];
+ sprintf( tmp, "staticprop%03d.txt", propNum );
+ DumpCollideToGlView( lookup.m_pCollide, tmp );
+ ++propNum;
+ }
+
+ FreeCurrentModelVertexes();
+
+ // Insert into cache...
+ return lookup.m_pCollide;
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests a single leaf against the static prop
+//-----------------------------------------------------------------------------
+
+static bool TestLeafAgainstCollide( int depth, int* pNodeList,
+ Vector const& origin, QAngle const& angles, CPhysCollide* pCollide )
+{
+ // Copy the planes in the node list into a list of planes
+ float* pPlanes = (float*)_alloca(depth * 4 * sizeof(float) );
+ int idx = 0;
+ for (int i = depth; --i >= 0; ++idx )
+ {
+ int sign = (pNodeList[i] < 0) ? -1 : 1;
+ int node = (sign < 0) ? - pNodeList[i] - 1 : pNodeList[i];
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ pPlanes[idx*4] = sign * pPlane->normal[0];
+ pPlanes[idx*4+1] = sign * pPlane->normal[1];
+ pPlanes[idx*4+2] = sign * pPlane->normal[2];
+ pPlanes[idx*4+3] = sign * pPlane->dist;
+ }
+
+ // Make a convex solid out of the planes
+ CPhysConvex* pPhysConvex = s_pPhysCollision->ConvexFromPlanes( pPlanes, depth, 0.0f );
+
+ // This should never happen, but if it does, return no collision
+ Assert( pPhysConvex );
+ if (!pPhysConvex)
+ return false;
+
+ CPhysCollide* pLeafCollide = s_pPhysCollision->ConvertConvexToCollide( &pPhysConvex, 1 );
+
+ // Collide the leaf solid with the static prop solid
+ trace_t tr;
+ s_pPhysCollision->TraceCollide( vec3_origin, vec3_origin, pLeafCollide, vec3_angle,
+ pCollide, origin, angles, &tr );
+
+ s_pPhysCollision->DestroyCollide( pLeafCollide );
+
+ return (tr.startsolid != 0);
+}
+
+//-----------------------------------------------------------------------------
+// Find all leaves that intersect with this bbox + test against the static prop..
+//-----------------------------------------------------------------------------
+
+static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList,
+ Vector const& mins, Vector const& maxs,
+ Vector const& origin, QAngle const& angles, CPhysCollide* pCollide,
+ CUtlVector<unsigned short>& leafList )
+{
+ Assert( pNodeList && pCollide );
+ Vector cornermin, cornermax;
+
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ // Arbitrary split plane here
+ for (int i = 0; i < 3; ++i)
+ {
+ if (pPlane->normal[i] >= 0)
+ {
+ cornermin[i] = mins[i];
+ cornermax[i] = maxs[i];
+ }
+ else
+ {
+ cornermin[i] = maxs[i];
+ cornermax[i] = mins[i];
+ }
+ }
+
+ if (DotProduct( pPlane->normal, cornermax ) <= pPlane->dist)
+ {
+ // Add the node to the list of nodes
+ pNodeList[depth] = node;
+ ++depth;
+
+ node = pNode->children[1];
+ }
+ else if (DotProduct( pPlane->normal, cornermin ) >= pPlane->dist)
+ {
+ // In this case, we are going in front of the plane. That means that
+ // this plane must have an outward normal facing in the oppisite direction
+ // We indicate this be storing a negative node index in the node list
+ pNodeList[depth] = - node - 1;
+ ++depth;
+
+ node = pNode->children[0];
+ }
+ else
+ {
+ // Here the box is split by the node. First, we'll add the plane as if its
+ // outward facing normal is in the direction of the node plane, then
+ // we'll have to reverse it for the other child...
+ pNodeList[depth] = node;
+ ++depth;
+
+ ComputeConvexHullLeaves_R( pNode->children[1],
+ depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
+
+ pNodeList[depth - 1] = - node - 1;
+ ComputeConvexHullLeaves_R( pNode->children[0],
+ depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
+ return;
+ }
+ }
+
+ Assert( pNodeList && pCollide );
+
+ // Never add static props to solid leaves
+ if ( (dleafs[-node-1].contents & CONTENTS_SOLID) == 0 )
+ {
+ if (TestLeafAgainstCollide( depth, pNodeList, origin, angles, pCollide ))
+ {
+ leafList.AddToTail( -node - 1 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+
+static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin,
+ QAngle const& angles, CUtlVector<unsigned short>& leafList )
+{
+ // Compute an axis-aligned bounding box for the collide
+ Vector mins, maxs;
+ s_pPhysCollision->CollideGetAABB( &mins, &maxs, pCollide, origin, angles );
+
+ // Find all leaves that intersect with the bounds
+ int tempNodeList[1024];
+ ComputeConvexHullLeaves_R( 0, 0, tempNodeList, mins, maxs,
+ origin, angles, pCollide, leafList );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the lighting origin
+//-----------------------------------------------------------------------------
+static bool ComputeLightingOrigin( StaticPropBuild_t const& build, Vector& lightingOrigin )
+{
+ for (int i = s_LightingInfo.Count(); --i >= 0; )
+ {
+ int entIndex = s_LightingInfo[i];
+
+ // Check against all lighting info entities
+ char const* pTargetName = ValueForKey( &entities[entIndex], "targetname" );
+ if (!Q_strcmp(pTargetName, build.m_pLightingOrigin))
+ {
+ GetVectorForKey( &entities[entIndex], "origin", lightingOrigin );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+static void AddStaticPropToLump( StaticPropBuild_t const& build )
+{
+ // Get the collision model
+ CPhysCollide* pConvexHull = GetCollisionModel( build.m_pModelName );
+ if (!pConvexHull)
+ return;
+
+ // Compute the leaves the static prop's convex hull hits
+ CUtlVector< unsigned short > leafList;
+ ComputeStaticPropLeaves( pConvexHull, build.m_Origin, build.m_Angles, leafList );
+
+ if ( !leafList.Count() )
+ {
+ Warning( "Static prop %s outside the map (%.2f, %.2f, %.2f)\n", build.m_pModelName, build.m_Origin.x, build.m_Origin.y, build.m_Origin.z );
+ return;
+ }
+ // Insert an element into the lump data...
+ int i = s_StaticPropLump.AddToTail( );
+ StaticPropLump_t& propLump = s_StaticPropLump[i];
+ propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName );
+ VectorCopy( build.m_Origin, propLump.m_Origin );
+ VectorCopy( build.m_Angles, propLump.m_Angles );
+ propLump.m_FirstLeaf = s_StaticPropLeafLump.Count();
+ propLump.m_LeafCount = leafList.Count();
+ propLump.m_Solid = build.m_Solid;
+ propLump.m_Skin = build.m_Skin;
+ propLump.m_Flags = build.m_Flags;
+ if (build.m_FadesOut)
+ {
+ propLump.m_Flags |= STATIC_PROP_FLAG_FADES;
+ }
+ propLump.m_FadeMinDist = build.m_FadeMinDist;
+ propLump.m_FadeMaxDist = build.m_FadeMaxDist;
+ propLump.m_flForcedFadeScale = build.m_flForcedFadeScale;
+ propLump.m_nMinDXLevel = build.m_nMinDXLevel;
+ propLump.m_nMaxDXLevel = build.m_nMaxDXLevel;
+
+ if (build.m_pLightingOrigin && *build.m_pLightingOrigin)
+ {
+ if (ComputeLightingOrigin( build, propLump.m_LightingOrigin ))
+ {
+ propLump.m_Flags |= STATIC_PROP_USE_LIGHTING_ORIGIN;
+ }
+ }
+
+ // Add the leaves to the leaf lump
+ for (int j = 0; j < leafList.Size(); ++j)
+ {
+ StaticPropLeafLump_t insert;
+ insert.m_Leaf = leafList[j];
+ s_StaticPropLeafLump.AddToTail( insert );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places static props in the lump
+//-----------------------------------------------------------------------------
+
+static void SetLumpData( )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_STATIC_PROPS);
+ if (handle != g_GameLumps.InvalidGameLump())
+ g_GameLumps.DestroyGameLump(handle);
+
+ int dictsize = s_StaticPropDictLump.Size() * sizeof(StaticPropDictLump_t);
+ int objsize = s_StaticPropLump.Size() * sizeof(StaticPropLump_t);
+ int leafsize = s_StaticPropLeafLump.Size() * sizeof(StaticPropLeafLump_t);
+ int size = dictsize + objsize + leafsize + 3 * sizeof(int);
+
+ handle = g_GameLumps.CreateGameLump( GAMELUMP_STATIC_PROPS, size, 0, GAMELUMP_STATIC_PROPS_VERSION );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size );
+ buf.PutInt( s_StaticPropDictLump.Size() );
+ if (dictsize)
+ buf.Put( s_StaticPropDictLump.Base(), dictsize );
+ buf.PutInt( s_StaticPropLeafLump.Size() );
+ if (leafsize)
+ buf.Put( s_StaticPropLeafLump.Base(), leafsize );
+ buf.PutInt( s_StaticPropLump.Size() );
+ if (objsize)
+ buf.Put( s_StaticPropLump.Base(), objsize );
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+
+void EmitStaticProps()
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( physicsFactory )
+ {
+ s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ if( !s_pPhysCollision )
+ return;
+ }
+
+ // Generate a list of lighting origins, and strip them out
+ int i;
+ for ( i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!Q_strcmp(pEntity, "info_lighting"))
+ {
+ s_LightingInfo.AddToTail(i);
+ }
+ }
+
+ // Emit specifically specified static props
+ for ( i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static"))
+ {
+ StaticPropBuild_t build;
+
+ GetVectorForKey( &entities[i], "origin", build.m_Origin );
+ GetAnglesForKey( &entities[i], "angles", build.m_Angles );
+ build.m_pModelName = ValueForKey( &entities[i], "model" );
+ build.m_Solid = IntForKey( &entities[i], "solid" );
+ build.m_Skin = IntForKey( &entities[i], "skin" );
+ build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" );
+ build.m_Flags = 0;//IntForKey( &entities[i], "spawnflags" ) & STATIC_PROP_WC_MASK;
+ if (IntForKey( &entities[i], "ignorenormals" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_IGNORE_NORMALS;
+ }
+ if (IntForKey( &entities[i], "disableshadows" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_SHADOW;
+ }
+ if (IntForKey( &entities[i], "disablevertexlighting" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_PER_VERTEX_LIGHTING;
+ }
+ if (IntForKey( &entities[i], "disableselfshadowing" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_SELF_SHADOWING;
+ }
+
+ if (IntForKey( &entities[i], "screenspacefade" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_SCREEN_SPACE_FADE;
+ }
+
+ const char *pKey = ValueForKey( &entities[i], "fadescale" );
+ if ( pKey && pKey[0] )
+ {
+ build.m_flForcedFadeScale = FloatForKey( &entities[i], "fadescale" );
+ }
+ else
+ {
+ build.m_flForcedFadeScale = 1;
+ }
+ build.m_FadesOut = (build.m_FadeMaxDist > 0);
+ build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" );
+ if (build.m_FadesOut)
+ {
+ build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" );
+ if (build.m_FadeMinDist < 0)
+ {
+ build.m_FadeMinDist = build.m_FadeMaxDist;
+ }
+ }
+ else
+ {
+ build.m_FadeMinDist = 0;
+ }
+ build.m_nMinDXLevel = (unsigned short)IntForKey( &entities[i], "mindxlevel" );
+ build.m_nMaxDXLevel = (unsigned short)IntForKey( &entities[i], "maxdxlevel" );
+ AddStaticPropToLump( build );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ }
+ }
+
+ // Strip out lighting origins; has to be done here because they are used when
+ // static props are made
+ for ( i = s_LightingInfo.Count(); --i >= 0; )
+ {
+ // strip this ent from the .bsp file
+ entities[s_LightingInfo[i]].epairs = 0;
+ }
+
+
+ SetLumpData( );
+}
+
+static studiohdr_t *g_pActiveStudioHdr;
+static void SetCurrentModel( studiohdr_t *pStudioHdr )
+{
+ // track the correct model
+ g_pActiveStudioHdr = pStudioHdr;
+}
+
+static void FreeCurrentModelVertexes()
+{
+ Assert( g_pActiveStudioHdr );
+
+ if ( g_pActiveStudioHdr->pVertexBase )
+ {
+ free( g_pActiveStudioHdr->pVertexBase );
+ g_pActiveStudioHdr->pVertexBase = NULL;
+ }
+}
+
+const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
+{
+ char fileName[260];
+ FileHandle_t fileHandle;
+ vertexFileHeader_t *pVvdHdr;
+
+ Assert( pModelData == NULL );
+ Assert( g_pActiveStudioHdr );
+
+ if ( g_pActiveStudioHdr->pVertexBase )
+ {
+ return (vertexFileHeader_t *)g_pActiveStudioHdr->pVertexBase;
+ }
+
+ // mandatory callback to make requested data resident
+ // load and persist the vertex file
+ strcpy( fileName, "models/" );
+ strcat( fileName, g_pActiveStudioHdr->pszName() );
+ Q_StripExtension( fileName, fileName, sizeof( fileName ) );
+ strcat( fileName, ".vvd" );
+
+ // load the model
+ fileHandle = g_pFileSystem->Open( fileName, "rb" );
+ if ( !fileHandle )
+ {
+ Error( "Unable to load vertex data \"%s\"\n", fileName );
+ }
+
+ // Get the file size
+ int size = g_pFileSystem->Size( fileHandle );
+ if (size == 0)
+ {
+ g_pFileSystem->Close( fileHandle );
+ Error( "Bad size for vertex data \"%s\"\n", fileName );
+ }
+
+ pVvdHdr = (vertexFileHeader_t *)malloc(size);
+ g_pFileSystem->Read( pVvdHdr, size, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+
+ // check header
+ if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
+ {
+ Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
+ }
+ if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
+ {
+ Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
+ }
+ if (pVvdHdr->checksum != g_pActiveStudioHdr->checksum)
+ {
+ Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, g_pActiveStudioHdr->checksum);
+ }
+
+ g_pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
+ return pVvdHdr;
+}
+
diff --git a/mp/src/utils/vbsp/textures.cpp b/mp/src/utils/vbsp/textures.cpp
index fc2034eb..4f49c5d4 100644
--- a/mp/src/utils/vbsp/textures.cpp
+++ b/mp/src/utils/vbsp/textures.cpp
@@ -1,737 +1,737 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-#include "utilmatlib.h"
-#include "physdll.h"
-#include <assert.h>
-#include <malloc.h>
-#include "tier1/strtools.h"
-#include "materialpatch.h"
-#include "KeyValues.h"
-
-void LoadSurfaceProperties( void );
-
-IPhysicsSurfaceProps *physprops = NULL;
-
-int nummiptex;
-textureref_t textureref[MAX_MAP_TEXTURES];
-
-bool g_bHasWater = false;
-
-extern qboolean onlyents;
-
-dtexdata_t *GetTexData( int index )
-{
- if ( index < 0 )
- return NULL;
- Assert( !onlyents );
- return &dtexdata[ index ];
-}
-
-static qboolean StringIsTrue( const char *str )
-{
- if( Q_strcasecmp( str, "true" ) == 0 )
- {
- return true;
- }
- if( Q_strcasecmp( str, "1" ) == 0 )
- {
- return true;
- }
- return false;
-}
-
-int FindMiptex (const char *name)
-{
- int i;
- MaterialSystemMaterial_t matID;
- const char *propVal, *propVal2;
- int opacity;
- bool found;
-
- for (i=0 ; i<nummiptex ; i++)
- {
- if (!strcmp (name, textureref[i].name))
- {
- return i;
- }
- }
- if (nummiptex == MAX_MAP_TEXTURES)
- Error ("Too many unique textures, max %d", MAX_MAP_TEXTURES);
- strcpy (textureref[i].name, name);
-
- textureref[i].lightmapWorldUnitsPerLuxel = 0.0f;
- textureref[i].flags = 0;
- textureref[i].contents = 0;
-
- matID = FindOriginalMaterial( name, &found );
- if( matID == MATERIAL_NOT_FOUND )
- {
- return 0;
- }
-
- if (!found)
- Warning("Material not found!: %s\n", name );
-
- // HANDLE ALL OF THE STUFF THAT ISN'T RENDERED WITH THE MATERIAL THAT IS ONE IT.
-
- // handle sky
- if( ( propVal = GetMaterialVar( matID, "%compileSky" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_SKY | SURF_NOLIGHT;
- }
- else if( ( propVal = GetMaterialVar( matID, "%compile2DSky" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_SKY | SURF_SKY2D | SURF_NOLIGHT;
- }
- // handle hint brushes
- else if ( ( propVal = GetMaterialVar( matID, "%compileHint" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_HINT;
- }
- // handle skip faces
- else if ( ( propVal = GetMaterialVar( matID, "%compileSkip" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP;
- }
- // handle origin brushes
- else if ( ( propVal = GetMaterialVar( matID, "%compileOrigin" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_ORIGIN | CONTENTS_DETAIL;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
- // handle clip brushes
- else if ( ( propVal = GetMaterialVar( matID, "%compileClip" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
- else if ( ( propVal = GetMaterialVar( matID, "%playerClip" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_PLAYERCLIP;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
- // handle npc clip brushes
- else if ( ( propVal = GetMaterialVar( matID, "%compileNpcClip" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_MONSTERCLIP;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
- // handle surface lights which are meant to
- else if ( ( propVal = GetMaterialVar( matID, "%compileNoChop" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_NOCHOP;
- }
- // handle triggers
- else if ( ( propVal = GetMaterialVar( matID, "%compileTrigger" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= ( SURF_NOLIGHT | SURF_TRIGGER );
- if ( g_NodrawTriggers )
- {
- textureref[i].flags |= SURF_NODRAW;
- }
- }
- // handle nolight surfs (except water)
- else if ( (( propVal = GetMaterialVar( matID, "%compileNoLight" ) ) && StringIsTrue( propVal )) &&
- !(( propVal2 = GetMaterialVar( matID, "%compileWater" ) ) && StringIsTrue( propVal2 ) ) )
- {
- textureref[i].flags |= SURF_NOLIGHT;
- }
- else
- {
- // HANDLE ALL OF THE STUFF THAT IS RENDERED WITH THE MATERIAL THAT IS ON IT.
-
- // Handle ladders.
- if ( ( propVal = GetMaterialVar( matID, "%compileLadder" ) ) && StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_LADDER;
- }
-
- // handle wet materials
- if ( ( propVal = GetMaterialVar( matID, "%noPortal" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].flags |= SURF_NOPORTAL;
- }
-
- if ( ( propVal = GetMaterialVar( matID, "%compilePassBullets" ) ) && StringIsTrue( propVal ) )
- {
- // change contents to grate, so bullets pass through
- // NOTE: This has effects on visibility too!
- textureref[i].contents &= ~CONTENTS_SOLID;
- textureref[i].contents |= CONTENTS_GRATE;
- }
-
- if( g_BumpAll || GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS ) )
- {
- textureref[i].flags |= SURF_BUMPLIGHT;
- }
-
- if( GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_LIGHTMAP ) )
- {
- textureref[i].flags &= ~SURF_NOLIGHT;
- }
- else if( !g_bLightIfMissing )
- {
- textureref[i].flags |= SURF_NOLIGHT;
- }
- // handle nodraw faces/brushes
- if ( ( propVal = GetMaterialVar( matID, "%compileNoDraw" ) ) && StringIsTrue( propVal ) )
- {
- // textureref[i].contents |= CONTENTS_DETAIL;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
-
- // Just a combination of nodraw + pass bullets, makes things easier
- if ( ( propVal = GetMaterialVar( matID, "%compileInvisible" ) ) && StringIsTrue( propVal ) )
- {
- // change contents to grate, so bullets pass through
- // NOTE: This has effects on visibility too!
- textureref[i].contents &= ~CONTENTS_SOLID;
- textureref[i].contents |= CONTENTS_GRATE;
- textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
- }
-
- bool checkWindow = true;
- // handle non solid
- if ( ( propVal = GetMaterialVar( matID, "%compileNonsolid" ) ) && StringIsTrue( propVal ) )
- {
- textureref[i].contents = CONTENTS_OPAQUE;
- // Non-Solid can't be a window either!
- checkWindow = false;
- }
- // handle block LOS
- if ( ( propVal = GetMaterialVar( matID, "%compileBlockLOS" ) ) && StringIsTrue( propVal ) )
- {
- textureref[i].contents = CONTENTS_BLOCKLOS;
-
- // BlockLOS can't be a window either!
- checkWindow = false;
- }
-
- if ( ( propVal = GetMaterialVar( matID, "%compileDetail" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents |= CONTENTS_DETAIL;
- }
-
- bool bKeepLighting = ( ( propVal = GetMaterialVar( matID, "%compileKeepLight" ) ) &&
- StringIsTrue( propVal ) );
-
- // handle materials that want to be treated as water.
- if ( ( propVal = GetMaterialVar( matID, "%compileWater" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
- textureref[i].contents |= CONTENTS_WATER;
- textureref[i].flags |= SURF_WARP | SURF_NOSHADOWS | SURF_NODECALS;
-
- if ( g_DisableWaterLighting && !bKeepLighting )
- {
- textureref[i].flags |= SURF_NOLIGHT;
- }
-
- // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
- g_bHasWater = true;
- }
- const char *pShaderName = GetMaterialShaderName(matID);
- if ( !bKeepLighting && !Q_strncasecmp( pShaderName, "water", 5 ) || !Q_strncasecmp( pShaderName, "UnlitGeneric", 12 ) )
- {
- //if ( !(textureref[i].flags & SURF_NOLIGHT) )
- // Warning("Forcing lit materal %s to nolight\n", name );
- textureref[i].flags |= SURF_NOLIGHT;
- }
-
- if ( ( propVal = GetMaterialVar( matID, "%compileSlime" ) ) &&
- StringIsTrue( propVal ) )
- {
- textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
- textureref[i].contents |= CONTENTS_SLIME;
- textureref[i].flags |= SURF_NODECALS;
- // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
- g_bHasWater = true;
- }
-
- opacity = GetMaterialShaderPropertyInt( matID, UTILMATLIB_OPACITY );
-
- if ( checkWindow && opacity != UTILMATLIB_OPAQUE )
- {
- // transparent *and solid* brushes that aren't grates or water must be windows
- if ( !(textureref[i].contents & (CONTENTS_GRATE|CONTENTS_WATER)) )
- {
- textureref[i].contents |= CONTENTS_WINDOW;
- }
-
- textureref[i].contents &= ~CONTENTS_SOLID;
-
- // this affects engine primitive sorting, SURF_TRANS means sort as a translucent primitive
- if ( opacity == UTILMATLIB_TRANSLUCENT )
- {
- textureref[i].flags |= SURF_TRANS;
- }
-
- }
- if ( textureref[i].flags & SURF_NOLIGHT )
- {
- textureref[i].flags &= ~SURF_BUMPLIGHT;
- }
- }
-
- nummiptex++;
-
- return i;
-}
-
-/*
-==================
-textureAxisFromPlane
-==================
-*/
-Vector baseaxis[18] =
-{
- Vector(0,0,1), Vector(1,0,0), Vector(0,-1,0), // floor
- Vector(0,0,-1), Vector(1,0,0), Vector(0,-1,0), // ceiling
- Vector(1,0,0), Vector(0,1,0), Vector(0,0,-1), // west wall
- Vector(-1,0,0), Vector(0,1,0), Vector(0,0,-1), // east wall
- Vector(0,1,0), Vector(1,0,0), Vector(0,0,-1), // south wall
- Vector(0,-1,0), Vector(1,0,0), Vector(0,0,-1) // north wall
-};
-
-void TextureAxisFromPlane(plane_t *pln, Vector& xv, Vector& yv)
-{
- int bestaxis;
- vec_t dot,best;
- int i;
-
- best = 0;
- bestaxis = 0;
-
- for (i=0 ; i<6 ; i++)
- {
- dot = DotProduct (pln->normal, baseaxis[i*3]);
- if (dot > best)
- {
- best = dot;
- bestaxis = i;
- }
- }
-
- VectorCopy (baseaxis[bestaxis*3+1], xv);
- VectorCopy (baseaxis[bestaxis*3+2], yv);
-}
-
-
-
-int g_SurfaceProperties[MAX_MAP_TEXDATA];
-
-
-int GetSurfaceProperties( MaterialSystemMaterial_t matID, const char *pMatName )
-{
- const char *pPropString = NULL;
- int surfaceIndex = -1;
-
- if ( physprops )
- {
- pPropString = GetMaterialVar( matID, "$surfaceprop" );
- if ( pPropString )
- {
- surfaceIndex = physprops->GetSurfaceIndex( pPropString );
- if ( surfaceIndex < 0 )
- {
- Msg("Can't find surfaceprop %s for material %s, using default\n", pPropString, pMatName );
- surfaceIndex = physprops->GetSurfaceIndex( pPropString );
- surfaceIndex = physprops->GetSurfaceIndex( "default" );
- }
- }
- }
-
- return surfaceIndex;
-}
-
-int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName )
-{
- const char *pPropString = NULL;
- int surfaceIndex = -1;
-
- if ( physprops )
- {
- pPropString = GetMaterialVar( matID, "$surfaceprop2" );
- if ( pPropString )
- {
- surfaceIndex = physprops->GetSurfaceIndex( pPropString );
- if ( surfaceIndex < 0 )
- {
- Msg("Can't find surfacepropblend %s for material %s, using default\n", pPropString, pMatName );
- surfaceIndex = physprops->GetSurfaceIndex( "default" );
- }
- }
- else
- {
- // No surface property 2.
- return -1;
- }
- }
-
- return surfaceIndex;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds or adds a texdata for the specified name ( same as below except
-// instead of finding the named texture, copies the settings from the passed
-// in sourceTexture. )
-// Used for creation of one off .vmt files for water surface textures
-// Input : *pName - texture name
-// Output : int index into dtexdata array
-//-----------------------------------------------------------------------------
-int FindAliasedTexData( const char *pName_, dtexdata_t *sourceTexture )
-{
- char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
- strcpy( pName, pName_ );
- strlwr( pName );
- int i, output;
- bool found;
- dtexdata_t *pTexData;
- MaterialSystemMaterial_t matID;
-
- for ( i = 0; i < numtexdata; i++ )
- {
- if ( !strcmp( pName, TexDataStringTable_GetString( GetTexData( i )->nameStringTableID ) ) )
- return i;
- }
-
-
- output = numtexdata;
- if ( numtexdata >= MAX_MAP_TEXDATA )
- {
- Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
- }
- pTexData = GetTexData( output );
- numtexdata++;
-
- // Save the name of the material.
- pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
-
- // Get the width, height, view_width, view_height, and reflectivity from the material system.
- matID = FindOriginalMaterial( TexDataStringTable_GetString( sourceTexture->nameStringTableID ), &found, false );
- if( matID == MATERIAL_NOT_FOUND || (!found) )
- {
- qprintf( "WARNING: material not found: \"%s\"\n", pName );
- return -1;
- }
-
- GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
- pTexData->view_width = pTexData->width; // undone: what is this?
- pTexData->view_height = pTexData->height; // undone: what is this?
-
- GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
- g_SurfaceProperties[output] = GetSurfaceProperties( matID, pName );
-
- return output;
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds a texdata for the specified name, returns -1 if not found
-//-----------------------------------------------------------------------------
-int FindTexData( const char *pName )
-{
- // Make sure the texdata doesn't already exist.
- for( int i = 0; i < numtexdata; i++ )
- {
- char const *pTexDataName = TexDataStringTable_GetString( GetTexData( i )->nameStringTableID );
- if ( !Q_stricmp( pTexDataName, pName ) )
- return i;
- }
- return -1;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Finds or adds a texdata for the specified name
-// Input : *pName - texture name
-// Output : int index into dtexdata array
-//-----------------------------------------------------------------------------
-int FindOrCreateTexData( const char *pName_ )
-{
- char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
- strcpy( pName, pName_ );
-
- int nOutput = FindTexData( pName );
- if ( nOutput >= 0 )
- return nOutput;
-
- // Didn't find it, add a new one
- nOutput = numtexdata;
- if ( numtexdata >= MAX_MAP_TEXDATA )
- {
- Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
- }
- dtexdata_t *pTexData = GetTexData( nOutput );
- numtexdata++;
-
- // Save the name of the material.
- pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
-
- // Get the width, height, view_width, view_height, and reflectivity from the material system.
- bool bFound;
- MaterialSystemMaterial_t matID = FindOriginalMaterial( pName, &bFound );
- if ( matID == MATERIAL_NOT_FOUND || (!bFound) )
- {
- qprintf( "WARNING: material not found: \"%s\"\n", pName );
- return nOutput;
- }
-
- GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
- pTexData->view_width = pTexData->width; // undone: what is this?
- pTexData->view_height = pTexData->height; // undone: what is this?
-
- GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
- g_SurfaceProperties[nOutput] = GetSurfaceProperties( matID, pName );
-
-#if 0
- Msg( "reflectivity: %f %f %f\n",
- pTexData->reflectivity[0],
- pTexData->reflectivity[1],
- pTexData->reflectivity[2] );
-#endif
-
- return nOutput;
-}
-
-int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName )
-{
- int existingIndex = pExistingTexData - GetTexData( 0 );
- dtexdata_t *pNewTexData = GetTexData( numtexdata );
- int newIndex = numtexdata;
- numtexdata++;
-
- *pNewTexData = *pExistingTexData;
- pNewTexData->nameStringTableID = TexDataStringTable_AddOrFindString( cloneTexDataName );
- g_SurfaceProperties[newIndex] = g_SurfaceProperties[existingIndex];
-
- return newIndex;
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds a texinfo that exactly matches the passed in texinfo
-//-----------------------------------------------------------------------------
-int FindTexInfo( const texinfo_t &searchTexInfo )
-{
- for( int i = 0; i < texinfo.Count(); i++ )
- {
- // Just an early-out for performance
- if ( texinfo[i].texdata != searchTexInfo.texdata )
- continue;
-
- if ( !memcmp( &texinfo[i], &searchTexInfo, sizeof( texinfo_t ) ) )
- return i;
- }
-
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds or creates a texinfo that exactly matches the passed in texinfo
-//-----------------------------------------------------------------------------
-int FindOrCreateTexInfo( const texinfo_t &searchTexInfo )
-{
- int i = FindTexInfo( searchTexInfo );
- if ( i >= 0 )
- return i;
-
- i = texinfo.AddToTail( searchTexInfo );
-
- if ( onlyents )
- {
- Error( "FindOrCreateTexInfo: Tried to create new texinfo during -onlyents compile!\nMust compile without -onlyents" );
- }
-
- return i;
-}
-
-int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin)
-{
- Vector vecs[2];
- int sv, tv;
- vec_t ang, sinv, cosv;
- vec_t ns, nt;
- texinfo_t tx;
- int i, j;
-
- if (!bt->name[0])
- return 0;
-
- memset (&tx, 0, sizeof(tx));
-
- // HLTOOLS - add support for texture vectors stored in the map file
- if (g_nMapFileVersion < 220)
- {
- TextureAxisFromPlane(plane, vecs[0], vecs[1]);
- }
-
- if (!bt->textureWorldUnitsPerTexel[0])
- bt->textureWorldUnitsPerTexel[0] = 1;
- if (!bt->textureWorldUnitsPerTexel[1])
- bt->textureWorldUnitsPerTexel[1] = 1;
-
-
- float shiftScaleU = 1.0f / 16.0f;
- float shiftScaleV = 1.0f / 16.0f;
-
- if (g_nMapFileVersion < 220)
- {
- // rotate axis
- if (bt->rotate == 0)
- { sinv = 0 ; cosv = 1; }
- else if (bt->rotate == 90)
- { sinv = 1 ; cosv = 0; }
- else if (bt->rotate == 180)
- { sinv = 0 ; cosv = -1; }
- else if (bt->rotate == 270)
- { sinv = -1 ; cosv = 0; }
- else
- {
- ang = bt->rotate / 180 * M_PI;
- sinv = sin(ang);
- cosv = cos(ang);
- }
-
- if (vecs[0][0])
- sv = 0;
- else if (vecs[0][1])
- sv = 1;
- else
- sv = 2;
-
- if (vecs[1][0])
- tv = 0;
- else if (vecs[1][1])
- tv = 1;
- else
- tv = 2;
-
- for (i=0 ; i<2 ; i++)
- {
- ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
- nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
- vecs[i][sv] = ns;
- vecs[i][tv] = nt;
- }
-
- for (i=0 ; i<2 ; i++)
- {
- for (j=0 ; j<3 ; j++)
- {
- tx.textureVecsTexelsPerWorldUnits[i][j] = vecs[i][j] / bt->textureWorldUnitsPerTexel[i];
- tx.lightmapVecsLuxelsPerWorldUnits[i][j] = tx.textureVecsTexelsPerWorldUnits[i][j] / 16.0f;
- }
- }
- }
- else
- {
- tx.textureVecsTexelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->textureWorldUnitsPerTexel[0];
- tx.textureVecsTexelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->textureWorldUnitsPerTexel[0];
- tx.textureVecsTexelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->textureWorldUnitsPerTexel[0];
-
- tx.textureVecsTexelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->textureWorldUnitsPerTexel[1];
- tx.textureVecsTexelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->textureWorldUnitsPerTexel[1];
- tx.textureVecsTexelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->textureWorldUnitsPerTexel[1];
-
- tx.lightmapVecsLuxelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->lightmapWorldUnitsPerLuxel;
- tx.lightmapVecsLuxelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->lightmapWorldUnitsPerLuxel;
- tx.lightmapVecsLuxelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->lightmapWorldUnitsPerLuxel;
-
- tx.lightmapVecsLuxelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->lightmapWorldUnitsPerLuxel;
- tx.lightmapVecsLuxelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->lightmapWorldUnitsPerLuxel;
- tx.lightmapVecsLuxelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->lightmapWorldUnitsPerLuxel;
-
- shiftScaleU = bt->textureWorldUnitsPerTexel[0] / bt->lightmapWorldUnitsPerLuxel;
- shiftScaleV = bt->textureWorldUnitsPerTexel[1] / bt->lightmapWorldUnitsPerLuxel;
- }
-
- tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] +
- DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[0] );
- tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] +
- DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[1] );
-
- tx.lightmapVecsLuxelsPerWorldUnits[0][3] = shiftScaleU * bt->shift[0] +
- DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[0] );
- tx.lightmapVecsLuxelsPerWorldUnits[1][3] = shiftScaleV * bt->shift[1] +
- DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[1] );
-
- tx.flags = bt->flags;
- tx.texdata = FindOrCreateTexData( bt->name );
-
- // find the texinfo
- return FindOrCreateTexInfo( tx );
-}
-
-
-void LoadSurfacePropFile( const char *pMaterialFilename )
-{
- FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
-
- if ( fp == FILESYSTEM_INVALID_HANDLE )
- {
- return;
- }
-
- int len = g_pFileSystem->Size( fp );
-
- char *pText = new char[len];
- g_pFileSystem->Read( pText, len, fp );
- g_pFileSystem->Close( fp );
-
- physprops->ParseSurfaceData( pMaterialFilename, pText );
-
- delete[] pText;
-}
-//-----------------------------------------------------------------------------
-// Purpose: Loads the surface properties database into the physics DLL
-//-----------------------------------------------------------------------------
-void LoadSurfaceProperties( void )
-{
- CreateInterfaceFn physicsFactory = GetPhysicsFactory();
- if ( !physicsFactory )
- return;
-
- physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
-
- const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
- KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
- if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
- {
- for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
- {
- if ( !Q_stricmp( sub->GetName(), "file" ) )
- {
- // Add
- LoadSurfacePropFile( sub->GetString() );
- continue;
- }
- }
- }
-
- manifest->deleteThis();
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "utilmatlib.h"
+#include "physdll.h"
+#include <assert.h>
+#include <malloc.h>
+#include "tier1/strtools.h"
+#include "materialpatch.h"
+#include "KeyValues.h"
+
+void LoadSurfaceProperties( void );
+
+IPhysicsSurfaceProps *physprops = NULL;
+
+int nummiptex;
+textureref_t textureref[MAX_MAP_TEXTURES];
+
+bool g_bHasWater = false;
+
+extern qboolean onlyents;
+
+dtexdata_t *GetTexData( int index )
+{
+ if ( index < 0 )
+ return NULL;
+ Assert( !onlyents );
+ return &dtexdata[ index ];
+}
+
+static qboolean StringIsTrue( const char *str )
+{
+ if( Q_strcasecmp( str, "true" ) == 0 )
+ {
+ return true;
+ }
+ if( Q_strcasecmp( str, "1" ) == 0 )
+ {
+ return true;
+ }
+ return false;
+}
+
+int FindMiptex (const char *name)
+{
+ int i;
+ MaterialSystemMaterial_t matID;
+ const char *propVal, *propVal2;
+ int opacity;
+ bool found;
+
+ for (i=0 ; i<nummiptex ; i++)
+ {
+ if (!strcmp (name, textureref[i].name))
+ {
+ return i;
+ }
+ }
+ if (nummiptex == MAX_MAP_TEXTURES)
+ Error ("Too many unique textures, max %d", MAX_MAP_TEXTURES);
+ strcpy (textureref[i].name, name);
+
+ textureref[i].lightmapWorldUnitsPerLuxel = 0.0f;
+ textureref[i].flags = 0;
+ textureref[i].contents = 0;
+
+ matID = FindOriginalMaterial( name, &found );
+ if( matID == MATERIAL_NOT_FOUND )
+ {
+ return 0;
+ }
+
+ if (!found)
+ Warning("Material not found!: %s\n", name );
+
+ // HANDLE ALL OF THE STUFF THAT ISN'T RENDERED WITH THE MATERIAL THAT IS ONE IT.
+
+ // handle sky
+ if( ( propVal = GetMaterialVar( matID, "%compileSky" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_SKY | SURF_NOLIGHT;
+ }
+ else if( ( propVal = GetMaterialVar( matID, "%compile2DSky" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_SKY | SURF_SKY2D | SURF_NOLIGHT;
+ }
+ // handle hint brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileHint" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_HINT;
+ }
+ // handle skip faces
+ else if ( ( propVal = GetMaterialVar( matID, "%compileSkip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP;
+ }
+ // handle origin brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileOrigin" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_ORIGIN | CONTENTS_DETAIL;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle clip brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ else if ( ( propVal = GetMaterialVar( matID, "%playerClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_PLAYERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle npc clip brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileNpcClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_MONSTERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle surface lights which are meant to
+ else if ( ( propVal = GetMaterialVar( matID, "%compileNoChop" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NOCHOP;
+ }
+ // handle triggers
+ else if ( ( propVal = GetMaterialVar( matID, "%compileTrigger" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= ( SURF_NOLIGHT | SURF_TRIGGER );
+ if ( g_NodrawTriggers )
+ {
+ textureref[i].flags |= SURF_NODRAW;
+ }
+ }
+ // handle nolight surfs (except water)
+ else if ( (( propVal = GetMaterialVar( matID, "%compileNoLight" ) ) && StringIsTrue( propVal )) &&
+ !(( propVal2 = GetMaterialVar( matID, "%compileWater" ) ) && StringIsTrue( propVal2 ) ) )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+ else
+ {
+ // HANDLE ALL OF THE STUFF THAT IS RENDERED WITH THE MATERIAL THAT IS ON IT.
+
+ // Handle ladders.
+ if ( ( propVal = GetMaterialVar( matID, "%compileLadder" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_LADDER;
+ }
+
+ // handle wet materials
+ if ( ( propVal = GetMaterialVar( matID, "%noPortal" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NOPORTAL;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compilePassBullets" ) ) && StringIsTrue( propVal ) )
+ {
+ // change contents to grate, so bullets pass through
+ // NOTE: This has effects on visibility too!
+ textureref[i].contents &= ~CONTENTS_SOLID;
+ textureref[i].contents |= CONTENTS_GRATE;
+ }
+
+ if( g_BumpAll || GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS ) )
+ {
+ textureref[i].flags |= SURF_BUMPLIGHT;
+ }
+
+ if( GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_LIGHTMAP ) )
+ {
+ textureref[i].flags &= ~SURF_NOLIGHT;
+ }
+ else if( !g_bLightIfMissing )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+ // handle nodraw faces/brushes
+ if ( ( propVal = GetMaterialVar( matID, "%compileNoDraw" ) ) && StringIsTrue( propVal ) )
+ {
+ // textureref[i].contents |= CONTENTS_DETAIL;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+
+ // Just a combination of nodraw + pass bullets, makes things easier
+ if ( ( propVal = GetMaterialVar( matID, "%compileInvisible" ) ) && StringIsTrue( propVal ) )
+ {
+ // change contents to grate, so bullets pass through
+ // NOTE: This has effects on visibility too!
+ textureref[i].contents &= ~CONTENTS_SOLID;
+ textureref[i].contents |= CONTENTS_GRATE;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+
+ bool checkWindow = true;
+ // handle non solid
+ if ( ( propVal = GetMaterialVar( matID, "%compileNonsolid" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents = CONTENTS_OPAQUE;
+ // Non-Solid can't be a window either!
+ checkWindow = false;
+ }
+ // handle block LOS
+ if ( ( propVal = GetMaterialVar( matID, "%compileBlockLOS" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents = CONTENTS_BLOCKLOS;
+
+ // BlockLOS can't be a window either!
+ checkWindow = false;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compileDetail" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_DETAIL;
+ }
+
+ bool bKeepLighting = ( ( propVal = GetMaterialVar( matID, "%compileKeepLight" ) ) &&
+ StringIsTrue( propVal ) );
+
+ // handle materials that want to be treated as water.
+ if ( ( propVal = GetMaterialVar( matID, "%compileWater" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
+ textureref[i].contents |= CONTENTS_WATER;
+ textureref[i].flags |= SURF_WARP | SURF_NOSHADOWS | SURF_NODECALS;
+
+ if ( g_DisableWaterLighting && !bKeepLighting )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+
+ // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
+ g_bHasWater = true;
+ }
+ const char *pShaderName = GetMaterialShaderName(matID);
+ if ( !bKeepLighting && !Q_strncasecmp( pShaderName, "water", 5 ) || !Q_strncasecmp( pShaderName, "UnlitGeneric", 12 ) )
+ {
+ //if ( !(textureref[i].flags & SURF_NOLIGHT) )
+ // Warning("Forcing lit materal %s to nolight\n", name );
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compileSlime" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
+ textureref[i].contents |= CONTENTS_SLIME;
+ textureref[i].flags |= SURF_NODECALS;
+ // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
+ g_bHasWater = true;
+ }
+
+ opacity = GetMaterialShaderPropertyInt( matID, UTILMATLIB_OPACITY );
+
+ if ( checkWindow && opacity != UTILMATLIB_OPAQUE )
+ {
+ // transparent *and solid* brushes that aren't grates or water must be windows
+ if ( !(textureref[i].contents & (CONTENTS_GRATE|CONTENTS_WATER)) )
+ {
+ textureref[i].contents |= CONTENTS_WINDOW;
+ }
+
+ textureref[i].contents &= ~CONTENTS_SOLID;
+
+ // this affects engine primitive sorting, SURF_TRANS means sort as a translucent primitive
+ if ( opacity == UTILMATLIB_TRANSLUCENT )
+ {
+ textureref[i].flags |= SURF_TRANS;
+ }
+
+ }
+ if ( textureref[i].flags & SURF_NOLIGHT )
+ {
+ textureref[i].flags &= ~SURF_BUMPLIGHT;
+ }
+ }
+
+ nummiptex++;
+
+ return i;
+}
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+Vector baseaxis[18] =
+{
+ Vector(0,0,1), Vector(1,0,0), Vector(0,-1,0), // floor
+ Vector(0,0,-1), Vector(1,0,0), Vector(0,-1,0), // ceiling
+ Vector(1,0,0), Vector(0,1,0), Vector(0,0,-1), // west wall
+ Vector(-1,0,0), Vector(0,1,0), Vector(0,0,-1), // east wall
+ Vector(0,1,0), Vector(1,0,0), Vector(0,0,-1), // south wall
+ Vector(0,-1,0), Vector(1,0,0), Vector(0,0,-1) // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, Vector& xv, Vector& yv)
+{
+ int bestaxis;
+ vec_t dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+int g_SurfaceProperties[MAX_MAP_TEXDATA];
+
+
+int GetSurfaceProperties( MaterialSystemMaterial_t matID, const char *pMatName )
+{
+ const char *pPropString = NULL;
+ int surfaceIndex = -1;
+
+ if ( physprops )
+ {
+ pPropString = GetMaterialVar( matID, "$surfaceprop" );
+ if ( pPropString )
+ {
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ if ( surfaceIndex < 0 )
+ {
+ Msg("Can't find surfaceprop %s for material %s, using default\n", pPropString, pMatName );
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ surfaceIndex = physprops->GetSurfaceIndex( "default" );
+ }
+ }
+ }
+
+ return surfaceIndex;
+}
+
+int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName )
+{
+ const char *pPropString = NULL;
+ int surfaceIndex = -1;
+
+ if ( physprops )
+ {
+ pPropString = GetMaterialVar( matID, "$surfaceprop2" );
+ if ( pPropString )
+ {
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ if ( surfaceIndex < 0 )
+ {
+ Msg("Can't find surfacepropblend %s for material %s, using default\n", pPropString, pMatName );
+ surfaceIndex = physprops->GetSurfaceIndex( "default" );
+ }
+ }
+ else
+ {
+ // No surface property 2.
+ return -1;
+ }
+ }
+
+ return surfaceIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds or adds a texdata for the specified name ( same as below except
+// instead of finding the named texture, copies the settings from the passed
+// in sourceTexture. )
+// Used for creation of one off .vmt files for water surface textures
+// Input : *pName - texture name
+// Output : int index into dtexdata array
+//-----------------------------------------------------------------------------
+int FindAliasedTexData( const char *pName_, dtexdata_t *sourceTexture )
+{
+ char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
+ strcpy( pName, pName_ );
+ strlwr( pName );
+ int i, output;
+ bool found;
+ dtexdata_t *pTexData;
+ MaterialSystemMaterial_t matID;
+
+ for ( i = 0; i < numtexdata; i++ )
+ {
+ if ( !strcmp( pName, TexDataStringTable_GetString( GetTexData( i )->nameStringTableID ) ) )
+ return i;
+ }
+
+
+ output = numtexdata;
+ if ( numtexdata >= MAX_MAP_TEXDATA )
+ {
+ Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
+ }
+ pTexData = GetTexData( output );
+ numtexdata++;
+
+ // Save the name of the material.
+ pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
+
+ // Get the width, height, view_width, view_height, and reflectivity from the material system.
+ matID = FindOriginalMaterial( TexDataStringTable_GetString( sourceTexture->nameStringTableID ), &found, false );
+ if( matID == MATERIAL_NOT_FOUND || (!found) )
+ {
+ qprintf( "WARNING: material not found: \"%s\"\n", pName );
+ return -1;
+ }
+
+ GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
+ pTexData->view_width = pTexData->width; // undone: what is this?
+ pTexData->view_height = pTexData->height; // undone: what is this?
+
+ GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
+ g_SurfaceProperties[output] = GetSurfaceProperties( matID, pName );
+
+ return output;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texdata for the specified name, returns -1 if not found
+//-----------------------------------------------------------------------------
+int FindTexData( const char *pName )
+{
+ // Make sure the texdata doesn't already exist.
+ for( int i = 0; i < numtexdata; i++ )
+ {
+ char const *pTexDataName = TexDataStringTable_GetString( GetTexData( i )->nameStringTableID );
+ if ( !Q_stricmp( pTexDataName, pName ) )
+ return i;
+ }
+ return -1;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds or adds a texdata for the specified name
+// Input : *pName - texture name
+// Output : int index into dtexdata array
+//-----------------------------------------------------------------------------
+int FindOrCreateTexData( const char *pName_ )
+{
+ char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
+ strcpy( pName, pName_ );
+
+ int nOutput = FindTexData( pName );
+ if ( nOutput >= 0 )
+ return nOutput;
+
+ // Didn't find it, add a new one
+ nOutput = numtexdata;
+ if ( numtexdata >= MAX_MAP_TEXDATA )
+ {
+ Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
+ }
+ dtexdata_t *pTexData = GetTexData( nOutput );
+ numtexdata++;
+
+ // Save the name of the material.
+ pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
+
+ // Get the width, height, view_width, view_height, and reflectivity from the material system.
+ bool bFound;
+ MaterialSystemMaterial_t matID = FindOriginalMaterial( pName, &bFound );
+ if ( matID == MATERIAL_NOT_FOUND || (!bFound) )
+ {
+ qprintf( "WARNING: material not found: \"%s\"\n", pName );
+ return nOutput;
+ }
+
+ GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
+ pTexData->view_width = pTexData->width; // undone: what is this?
+ pTexData->view_height = pTexData->height; // undone: what is this?
+
+ GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
+ g_SurfaceProperties[nOutput] = GetSurfaceProperties( matID, pName );
+
+#if 0
+ Msg( "reflectivity: %f %f %f\n",
+ pTexData->reflectivity[0],
+ pTexData->reflectivity[1],
+ pTexData->reflectivity[2] );
+#endif
+
+ return nOutput;
+}
+
+int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName )
+{
+ int existingIndex = pExistingTexData - GetTexData( 0 );
+ dtexdata_t *pNewTexData = GetTexData( numtexdata );
+ int newIndex = numtexdata;
+ numtexdata++;
+
+ *pNewTexData = *pExistingTexData;
+ pNewTexData->nameStringTableID = TexDataStringTable_AddOrFindString( cloneTexDataName );
+ g_SurfaceProperties[newIndex] = g_SurfaceProperties[existingIndex];
+
+ return newIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texinfo that exactly matches the passed in texinfo
+//-----------------------------------------------------------------------------
+int FindTexInfo( const texinfo_t &searchTexInfo )
+{
+ for( int i = 0; i < texinfo.Count(); i++ )
+ {
+ // Just an early-out for performance
+ if ( texinfo[i].texdata != searchTexInfo.texdata )
+ continue;
+
+ if ( !memcmp( &texinfo[i], &searchTexInfo, sizeof( texinfo_t ) ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or creates a texinfo that exactly matches the passed in texinfo
+//-----------------------------------------------------------------------------
+int FindOrCreateTexInfo( const texinfo_t &searchTexInfo )
+{
+ int i = FindTexInfo( searchTexInfo );
+ if ( i >= 0 )
+ return i;
+
+ i = texinfo.AddToTail( searchTexInfo );
+
+ if ( onlyents )
+ {
+ Error( "FindOrCreateTexInfo: Tried to create new texinfo during -onlyents compile!\nMust compile without -onlyents" );
+ }
+
+ return i;
+}
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin)
+{
+ Vector vecs[2];
+ int sv, tv;
+ vec_t ang, sinv, cosv;
+ vec_t ns, nt;
+ texinfo_t tx;
+ int i, j;
+
+ if (!bt->name[0])
+ return 0;
+
+ memset (&tx, 0, sizeof(tx));
+
+ // HLTOOLS - add support for texture vectors stored in the map file
+ if (g_nMapFileVersion < 220)
+ {
+ TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+ }
+
+ if (!bt->textureWorldUnitsPerTexel[0])
+ bt->textureWorldUnitsPerTexel[0] = 1;
+ if (!bt->textureWorldUnitsPerTexel[1])
+ bt->textureWorldUnitsPerTexel[1] = 1;
+
+
+ float shiftScaleU = 1.0f / 16.0f;
+ float shiftScaleV = 1.0f / 16.0f;
+
+ if (g_nMapFileVersion < 220)
+ {
+ // rotate axis
+ if (bt->rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (bt->rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (bt->rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (bt->rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = bt->rotate / 180 * M_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ tx.textureVecsTexelsPerWorldUnits[i][j] = vecs[i][j] / bt->textureWorldUnitsPerTexel[i];
+ tx.lightmapVecsLuxelsPerWorldUnits[i][j] = tx.textureVecsTexelsPerWorldUnits[i][j] / 16.0f;
+ }
+ }
+ }
+ else
+ {
+ tx.textureVecsTexelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->textureWorldUnitsPerTexel[0];
+ tx.textureVecsTexelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->textureWorldUnitsPerTexel[0];
+ tx.textureVecsTexelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->textureWorldUnitsPerTexel[0];
+
+ tx.textureVecsTexelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->textureWorldUnitsPerTexel[1];
+ tx.textureVecsTexelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->textureWorldUnitsPerTexel[1];
+ tx.textureVecsTexelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->textureWorldUnitsPerTexel[1];
+
+ tx.lightmapVecsLuxelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->lightmapWorldUnitsPerLuxel;
+
+ tx.lightmapVecsLuxelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->lightmapWorldUnitsPerLuxel;
+
+ shiftScaleU = bt->textureWorldUnitsPerTexel[0] / bt->lightmapWorldUnitsPerLuxel;
+ shiftScaleV = bt->textureWorldUnitsPerTexel[1] / bt->lightmapWorldUnitsPerLuxel;
+ }
+
+ tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] +
+ DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[0] );
+ tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] +
+ DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[1] );
+
+ tx.lightmapVecsLuxelsPerWorldUnits[0][3] = shiftScaleU * bt->shift[0] +
+ DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[0] );
+ tx.lightmapVecsLuxelsPerWorldUnits[1][3] = shiftScaleV * bt->shift[1] +
+ DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[1] );
+
+ tx.flags = bt->flags;
+ tx.texdata = FindOrCreateTexData( bt->name );
+
+ // find the texinfo
+ return FindOrCreateTexInfo( tx );
+}
+
+
+void LoadSurfacePropFile( const char *pMaterialFilename )
+{
+ FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
+
+ if ( fp == FILESYSTEM_INVALID_HANDLE )
+ {
+ return;
+ }
+
+ int len = g_pFileSystem->Size( fp );
+
+ char *pText = new char[len];
+ g_pFileSystem->Read( pText, len, fp );
+ g_pFileSystem->Close( fp );
+
+ physprops->ParseSurfaceData( pMaterialFilename, pText );
+
+ delete[] pText;
+}
+//-----------------------------------------------------------------------------
+// Purpose: Loads the surface properties database into the physics DLL
+//-----------------------------------------------------------------------------
+void LoadSurfaceProperties( void )
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( !physicsFactory )
+ return;
+
+ physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
+
+ const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
+ KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
+ if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
+ {
+ for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
+ {
+ if ( !Q_stricmp( sub->GetName(), "file" ) )
+ {
+ // Add
+ LoadSurfacePropFile( sub->GetString() );
+ continue;
+ }
+ }
+ }
+
+ manifest->deleteThis();
+}
+
+
diff --git a/mp/src/utils/vbsp/tree.cpp b/mp/src/utils/vbsp/tree.cpp
index 0f72844c..529e9838 100644
--- a/mp/src/utils/vbsp/tree.cpp
+++ b/mp/src/utils/vbsp/tree.cpp
@@ -1,207 +1,207 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "vbsp.h"
-
-extern int c_nodes;
-
-void RemovePortalFromNode (portal_t *portal, node_t *l);
-
-node_t *NodeForPoint (node_t *node, Vector& origin)
-{
- plane_t *plane;
- vec_t d;
-
- while (node->planenum != PLANENUM_LEAF)
- {
- plane = &g_MainMap->mapplanes[node->planenum];
- d = DotProduct (origin, plane->normal) - plane->dist;
- if (d >= 0)
- node = node->children[0];
- else
- node = node->children[1];
- }
-
- return node;
-}
-
-
-
-/*
-=============
-FreeTreePortals_r
-=============
-*/
-void FreeTreePortals_r (node_t *node)
-{
- portal_t *p, *nextp;
- int s;
-
- // free children
- if (node->planenum != PLANENUM_LEAF)
- {
- FreeTreePortals_r (node->children[0]);
- FreeTreePortals_r (node->children[1]);
- }
-
- // free portals
- for (p=node->portals ; p ; p=nextp)
- {
- s = (p->nodes[1] == node);
- nextp = p->next[s];
-
- RemovePortalFromNode (p, p->nodes[!s]);
- FreePortal (p);
- }
- node->portals = NULL;
-}
-
-/*
-=============
-FreeTree_r
-=============
-*/
-void FreeTree_r (node_t *node)
-{
- face_t *f, *nextf;
-
- // free children
- if (node->planenum != PLANENUM_LEAF)
- {
- FreeTree_r (node->children[0]);
- FreeTree_r (node->children[1]);
- }
-
- // free bspbrushes
- FreeBrushList (node->brushlist);
-
- // free faces
- for (f=node->faces ; f ; f=nextf)
- {
- nextf = f->next;
- FreeFace (f);
- }
-
- // free the node
- if (node->volume)
- FreeBrush (node->volume);
-
- if (numthreads == 1)
- c_nodes--;
- free (node);
-}
-
-
-/*
-=============
-FreeTree
-=============
-*/
-void FreeTree (tree_t *tree)
-{
- if ( !tree )
- return;
-
- FreeTreePortals_r (tree->headnode);
- FreeTree_r (tree->headnode);
- free (tree);
-}
-
-//===============================================================
-
-void PrintTree_r (node_t *node, int depth)
-{
- int i;
- plane_t *plane;
- bspbrush_t *bb;
-
- for (i=0 ; i<depth ; i++)
- Msg (" ");
- if (node->planenum == PLANENUM_LEAF)
- {
- if (!node->brushlist)
- Msg ("NULL\n");
- else
- {
- for (bb=node->brushlist ; bb ; bb=bb->next)
- Msg ("%i ", bb->original->brushnum);
- Msg ("\n");
- }
- return;
- }
-
- plane = &g_MainMap->mapplanes[node->planenum];
- Msg ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
- plane->normal[0], plane->normal[1], plane->normal[2],
- plane->dist);
- PrintTree_r (node->children[0], depth+1);
- PrintTree_r (node->children[1], depth+1);
-}
-
-/*
-=========================================================
-
-NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
-
-=========================================================
-*/
-
-int c_pruned;
-
-/*
-============
-PruneNodes_r
-============
-*/
-void PruneNodes_r (node_t *node)
-{
- bspbrush_t *b, *next;
-
- if (node->planenum == PLANENUM_LEAF)
- return;
- PruneNodes_r (node->children[0]);
- PruneNodes_r (node->children[1]);
-
- if ( (node->children[0]->contents & CONTENTS_SOLID)
- && (node->children[1]->contents & CONTENTS_SOLID) )
- {
- if (node->faces)
- Error ("node->faces seperating CONTENTS_SOLID");
- if (node->children[0]->faces || node->children[1]->faces)
- Error ("!node->faces with children");
-
- // FIXME: free stuff
- node->planenum = PLANENUM_LEAF;
- node->contents = CONTENTS_SOLID;
-
- if (node->brushlist)
- Error ("PruneNodes: node->brushlist");
-
- // combine brush lists
- node->brushlist = node->children[1]->brushlist;
-
- for (b=node->children[0]->brushlist ; b ; b=next)
- {
- next = b->next;
- b->next = node->brushlist;
- node->brushlist = b;
- }
-
- c_pruned++;
- }
-}
-
-
-void PruneNodes (node_t *node)
-{
- qprintf ("--- PruneNodes ---\n");
- c_pruned = 0;
- PruneNodes_r (node);
- qprintf ("%5i pruned nodes\n", c_pruned);
-}
-
-//===========================================================
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "vbsp.h"
+
+extern int c_nodes;
+
+void RemovePortalFromNode (portal_t *portal, node_t *l);
+
+node_t *NodeForPoint (node_t *node, Vector& origin)
+{
+ plane_t *plane;
+ vec_t d;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &g_MainMap->mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+
+
+/*
+=============
+FreeTreePortals_r
+=============
+*/
+void FreeTreePortals_r (node_t *node)
+{
+ portal_t *p, *nextp;
+ int s;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTreePortals_r (node->children[0]);
+ FreeTreePortals_r (node->children[1]);
+ }
+
+ // free portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ s = (p->nodes[1] == node);
+ nextp = p->next[s];
+
+ RemovePortalFromNode (p, p->nodes[!s]);
+ FreePortal (p);
+ }
+ node->portals = NULL;
+}
+
+/*
+=============
+FreeTree_r
+=============
+*/
+void FreeTree_r (node_t *node)
+{
+ face_t *f, *nextf;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTree_r (node->children[0]);
+ FreeTree_r (node->children[1]);
+ }
+
+ // free bspbrushes
+ FreeBrushList (node->brushlist);
+
+ // free faces
+ for (f=node->faces ; f ; f=nextf)
+ {
+ nextf = f->next;
+ FreeFace (f);
+ }
+
+ // free the node
+ if (node->volume)
+ FreeBrush (node->volume);
+
+ if (numthreads == 1)
+ c_nodes--;
+ free (node);
+}
+
+
+/*
+=============
+FreeTree
+=============
+*/
+void FreeTree (tree_t *tree)
+{
+ if ( !tree )
+ return;
+
+ FreeTreePortals_r (tree->headnode);
+ FreeTree_r (tree->headnode);
+ free (tree);
+}
+
+//===============================================================
+
+void PrintTree_r (node_t *node, int depth)
+{
+ int i;
+ plane_t *plane;
+ bspbrush_t *bb;
+
+ for (i=0 ; i<depth ; i++)
+ Msg (" ");
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ if (!node->brushlist)
+ Msg ("NULL\n");
+ else
+ {
+ for (bb=node->brushlist ; bb ; bb=bb->next)
+ Msg ("%i ", bb->original->brushnum);
+ Msg ("\n");
+ }
+ return;
+ }
+
+ plane = &g_MainMap->mapplanes[node->planenum];
+ Msg ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
+ plane->normal[0], plane->normal[1], plane->normal[2],
+ plane->dist);
+ PrintTree_r (node->children[0], depth+1);
+ PrintTree_r (node->children[1], depth+1);
+}
+
+/*
+=========================================================
+
+NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
+
+=========================================================
+*/
+
+int c_pruned;
+
+/*
+============
+PruneNodes_r
+============
+*/
+void PruneNodes_r (node_t *node)
+{
+ bspbrush_t *b, *next;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+ PruneNodes_r (node->children[0]);
+ PruneNodes_r (node->children[1]);
+
+ if ( (node->children[0]->contents & CONTENTS_SOLID)
+ && (node->children[1]->contents & CONTENTS_SOLID) )
+ {
+ if (node->faces)
+ Error ("node->faces seperating CONTENTS_SOLID");
+ if (node->children[0]->faces || node->children[1]->faces)
+ Error ("!node->faces with children");
+
+ // FIXME: free stuff
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+
+ if (node->brushlist)
+ Error ("PruneNodes: node->brushlist");
+
+ // combine brush lists
+ node->brushlist = node->children[1]->brushlist;
+
+ for (b=node->children[0]->brushlist ; b ; b=next)
+ {
+ next = b->next;
+ b->next = node->brushlist;
+ node->brushlist = b;
+ }
+
+ c_pruned++;
+ }
+}
+
+
+void PruneNodes (node_t *node)
+{
+ qprintf ("--- PruneNodes ---\n");
+ c_pruned = 0;
+ PruneNodes_r (node);
+ qprintf ("%5i pruned nodes\n", c_pruned);
+}
+
+//===========================================================
diff --git a/mp/src/utils/vbsp/vbsp.cpp b/mp/src/utils/vbsp/vbsp.cpp
index 1c7b7ec9..97a4df4c 100644
--- a/mp/src/utils/vbsp/vbsp.cpp
+++ b/mp/src/utils/vbsp/vbsp.cpp
@@ -1,1404 +1,1404 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: BSP Building tool
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "vbsp.h"
-#include "detail.h"
-#include "physdll.h"
-#include "utilmatlib.h"
-#include "disp_vbsp.h"
-#include "writebsp.h"
-#include "tier0/icommandline.h"
-#include "materialsystem/imaterialsystem.h"
-#include "map.h"
-#include "tools_minidump.h"
-#include "materialsub.h"
-#include "loadcmdline.h"
-#include "byteswap.h"
-#include "worldvertextransitionfixup.h"
-
-extern float g_maxLightmapDimension;
-
-char source[1024];
-char mapbase[ 64 ];
-char name[1024];
-char materialPath[1024];
-
-vec_t microvolume = 1.0;
-qboolean noprune;
-qboolean glview;
-qboolean nodetail;
-qboolean fulldetail;
-qboolean onlyents;
-bool onlyprops;
-qboolean nomerge;
-qboolean nomergewater = false;
-qboolean nowater;
-qboolean nocsg;
-qboolean noweld;
-qboolean noshare;
-qboolean nosubdiv;
-qboolean notjunc;
-qboolean noopt;
-qboolean leaktest;
-qboolean verboseentities;
-qboolean dumpcollide = false;
-qboolean g_bLowPriority = false;
-qboolean g_DumpStaticProps = false;
-qboolean g_bSkyVis = false; // skybox vis is off by default, toggle this to enable it
-bool g_bLightIfMissing = false;
-bool g_snapAxialPlanes = false;
-bool g_bKeepStaleZip = false;
-bool g_NodrawTriggers = false;
-bool g_DisableWaterLighting = false;
-bool g_bAllowDetailCracks = false;
-bool g_bNoVirtualMesh = false;
-
-float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE;
-float g_luxelScale = 1.0f;
-float g_minLuxelScale = 1.0f;
-bool g_BumpAll = false;
-
-int g_nDXLevel = 0; // default dxlevel if you don't specify it on the command-line.
-CUtlVector<int> g_SkyAreas;
-char outbase[32];
-
-// HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper
-// world coordinate extents. Assumes square spatial constraints.
-#define BLOCKS_SIZE 1024
-#define BLOCKS_SPACE (COORD_EXTENT/BLOCKS_SIZE)
-#define BLOCKX_OFFSET ((BLOCKS_SPACE/2)+1)
-#define BLOCKY_OFFSET ((BLOCKS_SPACE/2)+1)
-#define BLOCKS_MIN (-(BLOCKS_SPACE/2))
-#define BLOCKS_MAX ((BLOCKS_SPACE/2)-1)
-
-int block_xl = BLOCKS_MIN, block_xh = BLOCKS_MAX, block_yl = BLOCKS_MIN, block_yh = BLOCKS_MAX;
-
-int entity_num;
-
-
-node_t *block_nodes[BLOCKS_SPACE+2][BLOCKS_SPACE+2];
-
-//-----------------------------------------------------------------------------
-// Assign occluder areas (must happen *after* the world model is processed)
-//-----------------------------------------------------------------------------
-void AssignOccluderAreas( tree_t *pTree );
-static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas );
-
-
-/*
-============
-BlockTree
-
-============
-*/
-node_t *BlockTree (int xl, int yl, int xh, int yh)
-{
- node_t *node;
- Vector normal;
- float dist;
- int mid;
-
- if (xl == xh && yl == yh)
- {
- node = block_nodes[xl+BLOCKX_OFFSET][yl+BLOCKY_OFFSET];
- if (!node)
- { // return an empty leaf
- node = AllocNode ();
- node->planenum = PLANENUM_LEAF;
- node->contents = 0; //CONTENTS_SOLID;
- return node;
- }
- return node;
- }
-
- // create a seperator along the largest axis
- node = AllocNode ();
-
- if (xh - xl > yh - yl)
- { // split x axis
- mid = xl + (xh-xl)/2 + 1;
- normal[0] = 1;
- normal[1] = 0;
- normal[2] = 0;
- dist = mid*BLOCKS_SIZE;
- node->planenum = g_MainMap->FindFloatPlane (normal, dist);
- node->children[0] = BlockTree ( mid, yl, xh, yh);
- node->children[1] = BlockTree ( xl, yl, mid-1, yh);
- }
- else
- {
- mid = yl + (yh-yl)/2 + 1;
- normal[0] = 0;
- normal[1] = 1;
- normal[2] = 0;
- dist = mid*BLOCKS_SIZE;
- node->planenum = g_MainMap->FindFloatPlane (normal, dist);
- node->children[0] = BlockTree ( xl, mid, xh, yh);
- node->children[1] = BlockTree ( xl, yl, xh, mid-1);
- }
-
- return node;
-}
-
-/*
-============
-ProcessBlock_Thread
-
-============
-*/
-int brush_start, brush_end;
-void ProcessBlock_Thread (int threadnum, int blocknum)
-{
- int xblock, yblock;
- Vector mins, maxs;
- bspbrush_t *brushes;
- tree_t *tree;
- node_t *node;
-
- yblock = block_yl + blocknum / (block_xh-block_xl+1);
- xblock = block_xl + blocknum % (block_xh-block_xl+1);
-
- qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
-
- mins[0] = xblock*BLOCKS_SIZE;
- mins[1] = yblock*BLOCKS_SIZE;
- mins[2] = MIN_COORD_INTEGER;
- maxs[0] = (xblock+1)*BLOCKS_SIZE;
- maxs[1] = (yblock+1)*BLOCKS_SIZE;
- maxs[2] = MAX_COORD_INTEGER;
-
- // the makelist and chopbrushes could be cached between the passes...
- brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs, NO_DETAIL);
- if (!brushes)
- {
- node = AllocNode ();
- node->planenum = PLANENUM_LEAF;
- node->contents = CONTENTS_SOLID;
- block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = node;
- return;
- }
-
- FixupAreaportalWaterBrushes( brushes );
- if (!nocsg)
- brushes = ChopBrushes (brushes);
-
- tree = BrushBSP (brushes, mins, maxs);
-
- block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = tree->headnode;
-}
-
-
-/*
-============
-ProcessWorldModel
-
-============
-*/
-void SplitSubdividedFaces( node_t *headnode ); // garymcthack
-void ProcessWorldModel (void)
-{
- entity_t *e;
- tree_t *tree = NULL;
- qboolean leaked;
- int optimize;
- int start;
-
- e = &entities[entity_num];
-
- brush_start = e->firstbrush;
- brush_end = brush_start + e->numbrushes;
- leaked = false;
-
- //
- // perform per-block operations
- //
- if (block_xh * BLOCKS_SIZE > g_MainMap->map_maxs[0])
- {
- block_xh = floor(g_MainMap->map_maxs[0]/BLOCKS_SIZE);
- }
- if ( (block_xl+1) * BLOCKS_SIZE < g_MainMap->map_mins[0])
- {
- block_xl = floor(g_MainMap->map_mins[0]/BLOCKS_SIZE);
- }
- if (block_yh * BLOCKS_SIZE > g_MainMap->map_maxs[1])
- {
- block_yh = floor(g_MainMap->map_maxs[1]/BLOCKS_SIZE);
- }
- if ( (block_yl+1) * BLOCKS_SIZE < g_MainMap->map_mins[1])
- {
- block_yl = floor(g_MainMap->map_mins[1]/BLOCKS_SIZE);
- }
-
- // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h )
- if (block_xl < BLOCKS_MIN)
- {
- block_xl = BLOCKS_MIN;
- }
- if (block_yl < BLOCKS_MIN)
- {
- block_yl = BLOCKS_MIN;
- }
- if (block_xh > BLOCKS_MAX)
- {
- block_xh = BLOCKS_MAX;
- }
- if (block_yh > BLOCKS_MAX)
- {
- block_yh = BLOCKS_MAX;
- }
-
- for (optimize = 0 ; optimize <= 1 ; optimize++)
- {
- qprintf ("--------------------------------------------\n");
-
- RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
- !verbose, ProcessBlock_Thread);
-
- //
- // build the division tree
- // oversizing the blocks guarantees that all the boundaries
- // will also get nodes.
- //
-
- qprintf ("--------------------------------------------\n");
-
- tree = AllocTree ();
- tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
-
- tree->mins[0] = (block_xl)*BLOCKS_SIZE;
- tree->mins[1] = (block_yl)*BLOCKS_SIZE;
- tree->mins[2] = g_MainMap->map_mins[2] - 8;
-
- tree->maxs[0] = (block_xh+1)*BLOCKS_SIZE;
- tree->maxs[1] = (block_yh+1)*BLOCKS_SIZE;
- tree->maxs[2] = g_MainMap->map_maxs[2] + 8;
-
- //
- // perform the global operations
- //
-
- // make the portals/faces by traversing down to each empty leaf
- MakeTreePortals (tree);
-
- if (FloodEntities (tree))
- {
- // turns everthing outside into solid
- FillOutside (tree->headnode);
- }
- else
- {
- Warning( ("**** leaked ****\n") );
- leaked = true;
- LeakFile (tree);
- if (leaktest)
- {
- Warning( ("--- MAP LEAKED ---\n") );
- exit (0);
- }
- }
-
- // mark the brush sides that actually turned into faces
- MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL);
- if (noopt || leaked)
- break;
- if (!optimize)
- {
- // If we are optimizing, free the tree. Next time we will construct it again, but
- // we'll use the information in MarkVisibleSides() so we'll only split with planes that
- // actually contribute renderable geometry
- FreeTree (tree);
- }
- }
-
- FloodAreas (tree);
-
- RemoveAreaPortalBrushes_R( tree->headnode );
-
- start = Plat_FloatTime();
- Msg("Building Faces...");
- // this turns portals with one solid side into faces
- // it also subdivides each face if necessary to fit max lightmap dimensions
- MakeFaces (tree->headnode);
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
-
- if (glview)
- {
- WriteGLView (tree, source);
- }
-
- AssignOccluderAreas( tree );
- Compute3DSkyboxAreas( tree->headnode, g_SkyAreas );
- face_t *pLeafFaceList = NULL;
- if ( !nodetail )
- {
- pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end );
- }
-
- start = Plat_FloatTime();
-
- Msg("FixTjuncs...\n");
-
- // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions)
- // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer
- pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList);
-
- // this merges all of the solid nodes that have separating planes
- if (!noprune)
- {
- Msg("PruneNodes...\n");
- PruneNodes (tree->headnode);
- }
-
-// Msg( "SplitSubdividedFaces...\n" );
-// SplitSubdividedFaces( tree->headnode );
-
- Msg("WriteBSP...\n");
- WriteBSP (tree->headnode, pLeafFaceList);
- Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
-
- if (!leaked)
- {
- WritePortalFile (tree);
- }
-
- FreeTree( tree );
- FreeLeafFaces( pLeafFaceList );
-}
-
-/*
-============
-ProcessSubModel
-
-============
-*/
-void ProcessSubModel( )
-{
- entity_t *e;
- int start, end;
- tree_t *tree;
- bspbrush_t *list;
- Vector mins, maxs;
-
- e = &entities[entity_num];
-
- start = e->firstbrush;
- end = start + e->numbrushes;
-
- mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
- maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
- list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL);
-
- if (!nocsg)
- list = ChopBrushes (list);
- tree = BrushBSP (list, mins, maxs);
-
- // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode.
- if ( tree->headnode->planenum == PLANENUM_LEAF )
- {
- const char *pClassName = ValueForKey( e, "classname" );
- const char *pTargetName = ValueForKey( e, "targetname" );
- Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName );
- }
-
- MakeTreePortals (tree);
-
-#if DEBUG_BRUSHMODEL
- if ( entity_num == DEBUG_BRUSHMODEL )
- WriteGLView( tree, "tree_all" );
-#endif
-
- MarkVisibleSides (tree, start, end, FULL_DETAIL);
- MakeFaces (tree->headnode);
-
- FixTjuncs( tree->headnode, NULL );
- WriteBSP( tree->headnode, NULL );
-
-#if DEBUG_BRUSHMODEL
- if ( entity_num == DEBUG_BRUSHMODEL )
- {
- WriteGLView( tree, "tree_vis" );
- WriteGLViewFaces( tree, "tree_faces" );
- }
-#endif
-
- FreeTree (tree);
-}
-
-
-//-----------------------------------------------------------------------------
-// Returns true if the entity is a func_occluder
-//-----------------------------------------------------------------------------
-bool IsFuncOccluder( int entity_num )
-{
- entity_t *mapent = &entities[entity_num];
- const char *pClassName = ValueForKey( mapent, "classname" );
- return (strcmp("func_occluder", pClassName) == 0);
-}
-
-
-//-----------------------------------------------------------------------------
-// Computes the area of a brush's occluders
-//-----------------------------------------------------------------------------
-float ComputeOccluderBrushArea( mapbrush_t *pBrush )
-{
- float flArea = 0.0f;
- for ( int j = 0; j < pBrush->numsides; ++j )
- {
- side_t *pSide = &(pBrush->original_sides[j]);
-
- // Skip nodraw surfaces
- if ( texinfo[pSide->texinfo].flags & SURF_NODRAW )
- continue;
-
- if ( !pSide->winding )
- continue;
-
- flArea += WindingArea( pSide->winding );
- }
-
- return flArea;
-}
-
-
-//-----------------------------------------------------------------------------
-// Clips all occluder brushes against each other
-//-----------------------------------------------------------------------------
-static tree_t *ClipOccluderBrushes( )
-{
- // Create a list of all occluder brushes in the level
- CUtlVector< mapbrush_t * > mapBrushes( 1024, 1024 );
- for ( entity_num=0; entity_num < g_MainMap->num_entities; ++entity_num )
- {
- if (!IsFuncOccluder(entity_num))
- continue;
-
- entity_t *e = &entities[entity_num];
- int end = e->firstbrush + e->numbrushes;
- int i;
- for ( i = e->firstbrush; i < end; ++i )
- {
- mapBrushes.AddToTail( &g_MainMap->mapbrushes[i] );
- }
- }
-
- int nBrushCount = mapBrushes.Count();
- if ( nBrushCount == 0 )
- return NULL;
-
- Vector mins, maxs;
- mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
- maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
-
- bspbrush_t *list = MakeBspBrushList( mapBrushes.Base(), nBrushCount, mins, maxs );
-
- if (!nocsg)
- list = ChopBrushes (list);
- tree_t *tree = BrushBSP (list, mins, maxs);
- MakeTreePortals (tree);
- MarkVisibleSides (tree, mapBrushes.Base(), nBrushCount);
- MakeFaces( tree->headnode );
-
- // NOTE: This will output the occluder face vertices + planes
- FixTjuncs( tree->headnode, NULL );
-
- return tree;
-}
-
-
-//-----------------------------------------------------------------------------
-// Generate a list of unique sides in the occluder tree
-//-----------------------------------------------------------------------------
-static void GenerateOccluderSideList( int nEntity, CUtlVector<side_t*> &occluderSides )
-{
- entity_t *e = &entities[nEntity];
- int end = e->firstbrush + e->numbrushes;
- int i, j;
- for ( i = e->firstbrush; i < end; ++i )
- {
- mapbrush_t *mb = &g_MainMap->mapbrushes[i];
- for ( j = 0; j < mb->numsides; ++j )
- {
- occluderSides.AddToTail( &(mb->original_sides[j]) );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Generate a list of unique faces in the occluder tree
-//-----------------------------------------------------------------------------
-static void GenerateOccluderFaceList( node_t *pOccluderNode, CUtlVector<face_t*> &occluderFaces )
-{
- if (pOccluderNode->planenum == PLANENUM_LEAF)
- return;
-
- for ( face_t *f=pOccluderNode->faces ; f ; f = f->next )
- {
- occluderFaces.AddToTail( f );
- }
-
- GenerateOccluderFaceList( pOccluderNode->children[0], occluderFaces );
- GenerateOccluderFaceList( pOccluderNode->children[1], occluderFaces );
-}
-
-
-//-----------------------------------------------------------------------------
-// For occluder area assignment
-//-----------------------------------------------------------------------------
-struct OccluderInfo_t
-{
- int m_nOccluderEntityIndex;
-};
-
-static CUtlVector< OccluderInfo_t > g_OccluderInfo;
-
-
-//-----------------------------------------------------------------------------
-// Emits occluder brushes
-//-----------------------------------------------------------------------------
-static void EmitOccluderBrushes()
-{
- char str[64];
-
- g_OccluderData.RemoveAll();
- g_OccluderPolyData.RemoveAll();
- g_OccluderVertexIndices.RemoveAll();
-
- tree_t *pOccluderTree = ClipOccluderBrushes();
- if (!pOccluderTree)
- return;
-
- CUtlVector<face_t*> faceList( 1024, 1024 );
- CUtlVector<side_t*> sideList( 1024, 1024 );
- GenerateOccluderFaceList( pOccluderTree->headnode, faceList );
-
-#ifdef _DEBUG
- int *pEmitted = (int*)stackalloc( faceList.Count() * sizeof(int) );
- memset( pEmitted, 0, faceList.Count() * sizeof(int) );
-#endif
-
- for ( entity_num=1; entity_num < num_entities; ++entity_num )
- {
- if (!IsFuncOccluder(entity_num))
- continue;
-
- // Output only those parts of the occluder tree which are a part of the brush
- int nOccluder = g_OccluderData.AddToTail();
- doccluderdata_t &occluderData = g_OccluderData[ nOccluder ];
- occluderData.firstpoly = g_OccluderPolyData.Count();
- occluderData.mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
- occluderData.maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
- occluderData.flags = 0;
- occluderData.area = -1;
-
- // NOTE: If you change the algorithm by which occluder numbers are allocated,
- // then you must also change FixupOnlyEntsOccluderEntities() below
- sprintf (str, "%i", nOccluder);
- SetKeyValue (&entities[entity_num], "occludernumber", str);
-
- int nIndex = g_OccluderInfo.AddToTail();
- g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num;
-
- sideList.RemoveAll();
- GenerateOccluderSideList( entity_num, sideList );
- for ( int i = faceList.Count(); --i >= 0; )
- {
- // Skip nodraw surfaces, but not triggers that have been marked as nodraw
- face_t *f = faceList[i];
- if ( ( texinfo[f->texinfo].flags & SURF_NODRAW ) &&
- (( texinfo[f->texinfo].flags & SURF_TRIGGER ) == 0 ) )
- continue;
-
- // Only emit faces that appear in the side list of the occluder
- for ( int j = sideList.Count(); --j >= 0; )
- {
- if ( sideList[j] != f->originalface )
- continue;
-
- if ( f->numpoints < 3 )
- continue;
-
- // not a final face
- Assert ( !f->merged && !f->split[0] && !f->split[1] );
-
-#ifdef _DEBUG
- Assert( !pEmitted[i] );
- pEmitted[i] = entity_num;
-#endif
-
- int k = g_OccluderPolyData.AddToTail();
- doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[k];
-
- pOccluderPoly->planenum = f->planenum;
- pOccluderPoly->vertexcount = f->numpoints;
- pOccluderPoly->firstvertexindex = g_OccluderVertexIndices.Count();
- for( k = 0; k < f->numpoints; ++k )
- {
- g_OccluderVertexIndices.AddToTail( f->vertexnums[k] );
-
- const Vector &p = dvertexes[f->vertexnums[k]].point;
- VectorMin( occluderData.mins, p, occluderData.mins );
- VectorMax( occluderData.maxs, p, occluderData.maxs );
- }
-
- break;
- }
- }
-
- occluderData.polycount = g_OccluderPolyData.Count() - occluderData.firstpoly;
-
- // Mark this brush as not having brush geometry so it won't be re-emitted with a brush model
- entities[entity_num].numbrushes = 0;
- }
-
- FreeTree( pOccluderTree );
-}
-
-
-//-----------------------------------------------------------------------------
-// Set occluder area
-//-----------------------------------------------------------------------------
-void SetOccluderArea( int nOccluder, int nArea, int nEntityNum )
-{
- if ( g_OccluderData[nOccluder].area <= 0 )
- {
- g_OccluderData[nOccluder].area = nArea;
- }
- else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) )
- {
- const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" );
- if (!pTargetName)
- {
- pTargetName = "<no name>";
- }
- Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Assign occluder areas (must happen *after* the world model is processed)
-//-----------------------------------------------------------------------------
-void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals )
-{
- int nFirstPoly = g_OccluderData[nOccluder].firstpoly;
- int nEntityNum = g_OccluderInfo[nOccluder].m_nOccluderEntityIndex;
- for ( int j = 0; j < g_OccluderData[nOccluder].polycount; ++j )
- {
- doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[nFirstPoly + j];
- int nFirstVertex = pOccluderPoly->firstvertexindex;
- for ( int k = 0; k < pOccluderPoly->vertexcount; ++k )
- {
- int nVertexIndex = g_OccluderVertexIndices[nFirstVertex + k];
- node_t *pNode = NodeForPoint( pTree->headnode, dvertexes[ nVertexIndex ].point );
-
- SetOccluderArea( nOccluder, pNode->area, nEntityNum );
-
- int nOtherSideIndex;
- portal_t *pPortal;
- for ( pPortal = pNode->portals; pPortal; pPortal = pPortal->next[!nOtherSideIndex] )
- {
- nOtherSideIndex = (pPortal->nodes[0] == pNode) ? 1 : 0;
- if (!pPortal->onnode)
- continue; // edge of world
-
- // Don't cross over area portals for the area check
- if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL)
- continue;
-
- int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0;
- SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum );
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Assign occluder areas (must happen *after* the world model is processed)
-//-----------------------------------------------------------------------------
-void AssignOccluderAreas( tree_t *pTree )
-{
- for ( int i = 0; i < g_OccluderData.Count(); ++i )
- {
- AssignAreaToOccluder( i, pTree, false );
-
- // This can only have happened if the only valid portal out leads into an areaportal
- if ( g_OccluderData[i].area <= 0 )
- {
- AssignAreaToOccluder( i, pTree, true );
- }
- }
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Make sure the func_occluders have the appropriate data set
-//-----------------------------------------------------------------------------
-void FixupOnlyEntsOccluderEntities()
-{
- char str[64];
- int nOccluder = 0;
- for ( entity_num=1; entity_num < num_entities; ++entity_num )
- {
- if (!IsFuncOccluder(entity_num))
- continue;
-
- // NOTE: If you change the algorithm by which occluder numbers are allocated above,
- // then you must also change this
- sprintf (str, "%i", nOccluder);
- SetKeyValue (&entities[entity_num], "occludernumber", str);
- ++nOccluder;
- }
-}
-
-
-void MarkNoDynamicShadowSides()
-{
- for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
- {
- g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = true;
- }
-
- for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ )
- {
- int brushSideID = g_NoDynamicShadowSides[i];
-
- // Find the side with this ID.
- for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
- {
- if ( g_MainMap->brushsides[iSide].id == brushSideID )
- g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = false;
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Compute the 3D skybox areas
-//-----------------------------------------------------------------------------
-static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas )
-{
- for (int i = 0; i < g_MainMap->num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!strcmp(pEntity, "sky_camera"))
- {
- // Found a 3D skybox camera, get a leaf that lies in it
- node_t *pLeaf = PointInLeaf( headnode, entities[i].origin );
- if (pLeaf->contents & CONTENTS_SOLID)
- {
- Error ("Error! Entity sky_camera in solid volume! at %.1f %.1f %.1f\n", entities[i].origin.x, entities[i].origin.y, entities[i].origin.z);
- }
- areas.AddToTail( pLeaf->area );
- }
- }
-}
-
-bool Is3DSkyboxArea( int area )
-{
- for ( int i = g_SkyAreas.Count(); --i >=0; )
- {
- if ( g_SkyAreas[i] == area )
- return true;
- }
- return false;
-}
-
-
-/*
-============
-ProcessModels
-============
-*/
-void ProcessModels (void)
-{
- BeginBSPFile ();
-
- // Mark sides that have no dynamic shadows.
- MarkNoDynamicShadowSides();
-
- // emit the displacement surfaces
- EmitInitialDispInfos();
-
- // Clip occluder brushes against each other,
- // Remove them from the list of models to process below
- EmitOccluderBrushes( );
-
- for ( entity_num=0; entity_num < num_entities; ++entity_num )
- {
- entity_t *pEntity = &entities[entity_num];
- if ( !pEntity->numbrushes )
- continue;
-
- qprintf ("############### model %i ###############\n", nummodels);
-
- BeginModel ();
-
- if (entity_num == 0)
- {
- ProcessWorldModel();
- }
- else
- {
- ProcessSubModel( );
- }
-
- EndModel ();
-
- if (!verboseentities)
- {
- verbose = false; // don't bother printing submodels
- }
- }
-
- // Turn the skybox into a cubemap in case we don't build env_cubemap textures.
- Cubemap_CreateDefaultCubemaps();
- EndBSPFile ();
-}
-
-
-void LoadPhysicsDLL( void )
-{
- PhysicsDLLPath( "vphysics.dll" );
-}
-
-
-void PrintCommandLine( int argc, char **argv )
-{
- Warning( "Command line: " );
- for ( int z=0; z < argc; z++ )
- {
- Warning( "\"%s\" ", argv[z] );
- }
- Warning( "\n\n" );
-}
-
-
-int RunVBSP( int argc, char **argv )
-{
- int i;
- double start, end;
- char path[1024];
-
- CommandLine()->CreateCmdLine( argc, argv );
- MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false );
- InstallSpewFunction();
- SpewActivate( "developer", 1 );
-
- CmdLib_InitFileSystem( argv[ argc-1 ] );
-
- Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) );
- Q_FileBase( source, mapbase, sizeof( mapbase ) );
- strlwr( mapbase );
-
- LoadCmdLineFromFile( argc, argv, mapbase, "vbsp" );
-
- Msg( "Valve Software - vbsp.exe (%s)\n", __DATE__ );
-
- for (i=1 ; i<argc ; i++)
- {
- if (!stricmp(argv[i],"-threads"))
- {
- numthreads = atoi (argv[i+1]);
- i++;
- }
- else if (!Q_stricmp(argv[i],"-glview"))
- {
- glview = true;
- }
- else if ( !Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose") )
- {
- Msg("verbose = true\n");
- verbose = true;
- }
- else if (!Q_stricmp(argv[i], "-noweld"))
- {
- Msg ("noweld = true\n");
- noweld = true;
- }
- else if (!Q_stricmp(argv[i], "-nocsg"))
- {
- Msg ("nocsg = true\n");
- nocsg = true;
- }
- else if (!Q_stricmp(argv[i], "-noshare"))
- {
- Msg ("noshare = true\n");
- noshare = true;
- }
- else if (!Q_stricmp(argv[i], "-notjunc"))
- {
- Msg ("notjunc = true\n");
- notjunc = true;
- }
- else if (!Q_stricmp(argv[i], "-nowater"))
- {
- Msg ("nowater = true\n");
- nowater = true;
- }
- else if (!Q_stricmp(argv[i], "-noopt"))
- {
- Msg ("noopt = true\n");
- noopt = true;
- }
- else if (!Q_stricmp(argv[i], "-noprune"))
- {
- Msg ("noprune = true\n");
- noprune = true;
- }
- else if (!Q_stricmp(argv[i], "-nomerge"))
- {
- Msg ("nomerge = true\n");
- nomerge = true;
- }
- else if (!Q_stricmp(argv[i], "-nomergewater"))
- {
- Msg ("nomergewater = true\n");
- nomergewater = true;
- }
- else if (!Q_stricmp(argv[i], "-nosubdiv"))
- {
- Msg ("nosubdiv = true\n");
- nosubdiv = true;
- }
- else if (!Q_stricmp(argv[i], "-nodetail"))
- {
- Msg ("nodetail = true\n");
- nodetail = true;
- }
- else if (!Q_stricmp(argv[i], "-fulldetail"))
- {
- Msg ("fulldetail = true\n");
- fulldetail = true;
- }
- else if (!Q_stricmp(argv[i], "-onlyents"))
- {
- Msg ("onlyents = true\n");
- onlyents = true;
- }
- else if (!Q_stricmp(argv[i], "-onlyprops"))
- {
- Msg ("onlyprops = true\n");
- onlyprops = true;
- }
- else if (!Q_stricmp(argv[i], "-micro"))
- {
- microvolume = atof(argv[i+1]);
- Msg ("microvolume = %f\n", microvolume);
- i++;
- }
- else if (!Q_stricmp(argv[i], "-leaktest"))
- {
- Msg ("leaktest = true\n");
- leaktest = true;
- }
- else if (!Q_stricmp(argv[i], "-verboseentities"))
- {
- Msg ("verboseentities = true\n");
- verboseentities = true;
- }
- else if (!Q_stricmp(argv[i], "-snapaxial"))
- {
- Msg ("snap axial = true\n");
- g_snapAxialPlanes = true;
- }
-#if 0
- else if (!Q_stricmp(argv[i], "-maxlightmapdim"))
- {
- g_maxLightmapDimension = atof(argv[i+1]);
- Msg ("g_maxLightmapDimension = %f\n", g_maxLightmapDimension);
- i++;
- }
-#endif
- else if (!Q_stricmp(argv[i], "-block"))
- {
- block_xl = block_xh = atoi(argv[i+1]);
- block_yl = block_yh = atoi(argv[i+2]);
- Msg ("block: %i,%i\n", block_xl, block_yl);
- i+=2;
- }
- else if (!Q_stricmp(argv[i], "-blocks"))
- {
- block_xl = atoi(argv[i+1]);
- block_yl = atoi(argv[i+2]);
- block_xh = atoi(argv[i+3]);
- block_yh = atoi(argv[i+4]);
- Msg ("blocks: %i,%i to %i,%i\n",
- block_xl, block_yl, block_xh, block_yh);
- i+=4;
- }
- else if ( !Q_stricmp( argv[i], "-dumpcollide" ) )
- {
- Msg("Dumping collision models to collideXXX.txt\n" );
- dumpcollide = true;
- }
- else if ( !Q_stricmp( argv[i], "-dumpstaticprop" ) )
- {
- Msg("Dumping static props to staticpropXXX.txt\n" );
- g_DumpStaticProps = true;
- }
- else if ( !Q_stricmp( argv[i], "-forceskyvis" ) )
- {
- Msg("Enabled vis in 3d skybox\n" );
- g_bSkyVis = true;
- }
- else if (!Q_stricmp (argv[i],"-tmpout"))
- {
- strcpy (outbase, "/tmp");
- }
-#if 0
- else if( !Q_stricmp( argv[i], "-defaultluxelsize" ) )
- {
- g_defaultLuxelSize = atof( argv[i+1] );
- i++;
- }
-#endif
- else if( !Q_stricmp( argv[i], "-luxelscale" ) )
- {
- g_luxelScale = atof( argv[i+1] );
- i++;
- }
- else if( !strcmp( argv[i], "-minluxelscale" ) )
- {
- g_minLuxelScale = atof( argv[i+1] );
- if (g_minLuxelScale < 1)
- g_minLuxelScale = 1;
- i++;
- }
- else if( !Q_stricmp( argv[i], "-dxlevel" ) )
- {
- g_nDXLevel = atoi( argv[i+1] );
- Msg( "DXLevel = %d\n", g_nDXLevel );
- i++;
- }
- else if( !Q_stricmp( argv[i], "-bumpall" ) )
- {
- g_BumpAll = true;
- }
- else if( !Q_stricmp( argv[i], "-low" ) )
- {
- g_bLowPriority = true;
- }
- else if( !Q_stricmp( argv[i], "-lightifmissing" ) )
- {
- g_bLightIfMissing = true;
- }
- else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
- {
- }
- else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
- {
- // nothing to do here, but don't bail on this option
- }
- else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
- {
- ++i;
- }
- else if ( !Q_stricmp( argv[i], "-keepstalezip" ) )
- {
- g_bKeepStaleZip = true;
- }
- else if ( !Q_stricmp( argv[i], "-xbox" ) )
- {
- // enable mandatory xbox extensions
- g_NodrawTriggers = true;
- g_DisableWaterLighting = true;
- }
- else if ( !Q_stricmp( argv[i], "-allowdetailcracks"))
- {
- g_bAllowDetailCracks = true;
- }
- else if ( !Q_stricmp( argv[i], "-novirtualmesh"))
- {
- g_bNoVirtualMesh = true;
- }
- else if ( !Q_stricmp( argv[i], "-replacematerials" ) )
- {
- g_ReplaceMaterials = true;
- }
- else if ( !Q_stricmp(argv[i], "-nodrawtriggers") )
- {
- g_NodrawTriggers = true;
- }
- else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
- {
- EnableFullMinidumps( true );
- }
- else if (argv[i][0] == '-')
- {
- Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
- i = 100000; // force it to print the usage
- break;
- }
- else
- break;
- }
-
- if (i != argc - 1)
- {
- PrintCommandLine( argc, argv );
-
- Warning(
- "usage : vbsp [options...] mapfile\n"
- "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n"
- "\n"
- "Common options (use -v to see all options):\n"
- "\n"
- " -v (or -verbose): Turn on verbose output (also shows more command\n"
- " line options).\n"
- "\n"
- " -onlyents : This option causes vbsp only import the entities from the .vmf\n"
- " file. -onlyents won't reimport brush models.\n"
- " -onlyprops : Only update the static props and detail props.\n"
- " -glview : Writes .gl files in the current directory that can be viewed\n"
- " with glview.exe. If you use -tmpout, it will write the files\n"
- " into the \\tmp folder.\n"
- " -nodetail : Get rid of all detail geometry. The geometry left over is\n"
- " what affects visibility.\n"
- " -nowater : Get rid of water brushes.\n"
- " -low : Run as an idle-priority process.\n"
- "\n"
- " -vproject <directory> : Override the VPROJECT environment variable.\n"
- " -game <directory> : Same as -vproject.\n"
- "\n" );
-
- if ( verbose )
- {
- Warning(
- "Other options :\n"
- " -novconfig : Don't bring up graphical UI on vproject errors.\n"
- " -threads : Control the number of threads vbsp uses (defaults to the # of\n"
- " processors on your machine).\n"
- " -verboseentities: If -v is on, this disables verbose output for submodels.\n"
- " -noweld : Don't join face vertices together.\n"
- " -nocsg : Don't chop out intersecting brush areas.\n"
- " -noshare : Emit unique face edges instead of sharing them.\n"
- " -notjunc : Don't fixup t-junctions.\n"
- " -noopt : By default, vbsp removes the 'outer shell' of the map, which\n"
- " are all the faces you can't see because you can never get\n"
- " outside the map. -noopt disables this behaviour.\n"
- " -noprune : Don't prune neighboring solid nodes.\n"
- " -nomerge : Don't merge together chopped faces on nodes.\n"
- " -nomergewater: Don't merge together chopped faces on water.\n"
- " -nosubdiv : Don't subdivide faces for lightmapping.\n"
- " -micro <#> : vbsp will warn when brushes are output with a volume less\n"
- " than this number (default: 1.0).\n"
- " -fulldetail : Mark all detail geometry as normal geometry (so all detail\n"
- " geometry will affect visibility).\n"
- " -leaktest : Stop processing the map if a leak is detected. Whether or not\n"
- " this flag is set, a leak file will be written out at\n"
- " <vmf filename>.lin, and it can be imported into Hammer.\n"
- " -bumpall : Force all surfaces to be bump mapped.\n"
- " -snapaxial : Snap axial planes to integer coordinates.\n"
- " -block # # : Control the grid size mins that vbsp chops the level on.\n"
- " -blocks # # # # : Enter the mins and maxs for the grid size vbsp uses.\n"
- " -dumpstaticprops: Dump static props to staticprop*.txt\n"
- " -dumpcollide : Write files with collision info.\n"
- " -forceskyvis : Enable vis calculations in 3d skybox leaves\n"
- " -luxelscale # : Scale all lightmaps by this amount (default: 1.0).\n"
- " -minluxelscale #: No luxel scale will be lower than this amount (default: 1.0).\n"
- " -lightifmissing : Force lightmaps to be generated for all surfaces even if\n"
- " they don't need lightmaps.\n"
- " -keepstalezip : Keep the BSP's zip files intact but regenerate everything\n"
- " else.\n"
- " -virtualdispphysics : Use virtual (not precomputed) displacement collision models\n"
- " -xbox : Enable mandatory xbox options\n"
- " -x360 : Generate Xbox360 version of vsp\n"
- " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
- " -replacematerials : Substitute materials according to materialsub.txt in content\\maps\n"
- " -FullMinidumps : Write large minidumps on crash.\n"
- );
- }
-
- DeleteCmdLine( argc, argv );
- CmdLib_Cleanup();
- CmdLib_Exit( 1 );
- }
-
- start = Plat_FloatTime();
-
- // Run in the background?
- if( g_bLowPriority )
- {
- SetLowPriority();
- }
-
- if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < 80 ) )
- {
- g_BumpAll = false;
- }
-
- if( g_luxelScale == 1.0f )
- {
- if ( g_nDXLevel == 70 )
- {
- g_luxelScale = 4.0f;
- }
- }
-
- ThreadSetDefault ();
- numthreads = 1; // multiple threads aren't helping...
-
- // Setup the logfile.
- char logFile[512];
- _snprintf( logFile, sizeof(logFile), "%s.log", source );
- SetSpewFunctionLogFile( logFile );
-
- LoadPhysicsDLL();
- LoadSurfaceProperties();
-
-#if 0
- Msg( "qdir: %s This is the the path of the initial source file \n", qdir );
- Msg( "gamedir: %s This is the base engine + mod-specific game dir (e.g. d:/tf2/mytfmod/) \n", gamedir );
- Msg( "basegamedir: %s This is the base engine + base game directory (e.g. e:/hl2/hl2/, or d:/tf2/tf2/ )\n", basegamedir );
-#endif
-
- sprintf( materialPath, "%smaterials", gamedir );
- InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() );
- Msg( "materialPath: %s\n", materialPath );
-
- // delete portal and line files
- sprintf (path, "%s.prt", source);
- remove (path);
- sprintf (path, "%s.lin", source);
- remove (path);
-
- strcpy (name, ExpandArg (argv[i]));
-
- const char *pszExtension = V_GetFileExtension( name );
- if ( !pszExtension )
- {
- V_SetExtension( name, ".vmm", sizeof( name ) );
- if ( !FileExists( name ) )
- {
- V_SetExtension( name, ".vmf", sizeof( name ) );
- }
- }
-
- char platformBSPFileName[1024];
- GetPlatformMapPath( source, platformBSPFileName, g_nDXLevel, 1024 );
-
- // if we're combining materials, load the script file
- if ( g_ReplaceMaterials )
- {
- LoadMaterialReplacementKeys( gamedir, mapbase );
- }
-
- //
- // if onlyents, just grab the entites and resave
- //
- if (onlyents)
- {
- LoadBSPFile (platformBSPFileName);
- num_entities = 0;
- // Clear out the cubemap samples since they will be reparsed even with -onlyents
- g_nCubemapSamples = 0;
-
- // Mark as stale since the lighting could be screwed with new ents.
- AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
-
- LoadMapFile (name);
- SetModelNumbers ();
- SetLightStyles ();
-
- // NOTE: If we ever precompute lighting for static props in
- // vrad, EmitStaticProps should be removed here
-
- // Emit static props found in the .vmf file
- EmitStaticProps();
-
- // NOTE: Don't deal with detail props here, it blows away lighting
-
- // Recompute the skybox
- ComputeBoundsNoSkybox();
-
- // Make sure that we have a water lod control eneity if we have water in the map.
- EnsurePresenceOfWaterLODControlEntity();
-
- // Make sure the func_occluders have the appropriate data set
- FixupOnlyEntsOccluderEntities();
-
- // Doing this here because stuff abov may filter out entities
- UnparseEntities ();
-
- WriteBSPFile (platformBSPFileName);
- }
- else if (onlyprops)
- {
- // In the only props case, deal with static + detail props only
- LoadBSPFile (platformBSPFileName);
-
- LoadMapFile(name);
- SetModelNumbers();
- SetLightStyles();
-
- // Emit static props found in the .vmf file
- EmitStaticProps();
-
- // Place detail props found in .vmf and based on material properties
- LoadEmitDetailObjectDictionary( gamedir );
- EmitDetailObjects();
-
- WriteBSPFile (platformBSPFileName);
- }
- else
- {
- //
- // start from scratch
- //
-
- // Load just the file system from the bsp
- if( g_bKeepStaleZip && FileExists( platformBSPFileName ) )
- {
- LoadBSPFile_FileSystemOnly (platformBSPFileName);
- // Mark as stale since the lighting could be screwed with new ents.
- AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
- }
-
- LoadMapFile (name);
- WorldVertexTransitionFixup();
- if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
- {
- Cubemap_FixupBrushSidesMaterials();
- Cubemap_AttachDefaultCubemapToSpecularSides();
- Cubemap_AddUnreferencedCubemaps();
- }
- SetModelNumbers ();
- SetLightStyles ();
- LoadEmitDetailObjectDictionary( gamedir );
- ProcessModels ();
- }
-
- end = Plat_FloatTime();
-
- char str[512];
- GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
- Msg( "%s elapsed\n", str );
-
- DeleteCmdLine( argc, argv );
- ReleasePakFileLumps();
- DeleteMaterialReplacementKeys();
- ShutdownMaterialSystem();
- CmdLib_Cleanup();
- return 0;
-}
-
-
-/*
-=============
-main
-============
-*/
-int main (int argc, char **argv)
-{
- // Install an exception handler.
- SetupDefaultToolsMinidumpHandler();
- return RunVBSP( argc, argv );
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: BSP Building tool
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "detail.h"
+#include "physdll.h"
+#include "utilmatlib.h"
+#include "disp_vbsp.h"
+#include "writebsp.h"
+#include "tier0/icommandline.h"
+#include "materialsystem/imaterialsystem.h"
+#include "map.h"
+#include "tools_minidump.h"
+#include "materialsub.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+#include "worldvertextransitionfixup.h"
+
+extern float g_maxLightmapDimension;
+
+char source[1024];
+char mapbase[ 64 ];
+char name[1024];
+char materialPath[1024];
+
+vec_t microvolume = 1.0;
+qboolean noprune;
+qboolean glview;
+qboolean nodetail;
+qboolean fulldetail;
+qboolean onlyents;
+bool onlyprops;
+qboolean nomerge;
+qboolean nomergewater = false;
+qboolean nowater;
+qboolean nocsg;
+qboolean noweld;
+qboolean noshare;
+qboolean nosubdiv;
+qboolean notjunc;
+qboolean noopt;
+qboolean leaktest;
+qboolean verboseentities;
+qboolean dumpcollide = false;
+qboolean g_bLowPriority = false;
+qboolean g_DumpStaticProps = false;
+qboolean g_bSkyVis = false; // skybox vis is off by default, toggle this to enable it
+bool g_bLightIfMissing = false;
+bool g_snapAxialPlanes = false;
+bool g_bKeepStaleZip = false;
+bool g_NodrawTriggers = false;
+bool g_DisableWaterLighting = false;
+bool g_bAllowDetailCracks = false;
+bool g_bNoVirtualMesh = false;
+
+float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE;
+float g_luxelScale = 1.0f;
+float g_minLuxelScale = 1.0f;
+bool g_BumpAll = false;
+
+int g_nDXLevel = 0; // default dxlevel if you don't specify it on the command-line.
+CUtlVector<int> g_SkyAreas;
+char outbase[32];
+
+// HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper
+// world coordinate extents. Assumes square spatial constraints.
+#define BLOCKS_SIZE 1024
+#define BLOCKS_SPACE (COORD_EXTENT/BLOCKS_SIZE)
+#define BLOCKX_OFFSET ((BLOCKS_SPACE/2)+1)
+#define BLOCKY_OFFSET ((BLOCKS_SPACE/2)+1)
+#define BLOCKS_MIN (-(BLOCKS_SPACE/2))
+#define BLOCKS_MAX ((BLOCKS_SPACE/2)-1)
+
+int block_xl = BLOCKS_MIN, block_xh = BLOCKS_MAX, block_yl = BLOCKS_MIN, block_yh = BLOCKS_MAX;
+
+int entity_num;
+
+
+node_t *block_nodes[BLOCKS_SPACE+2][BLOCKS_SPACE+2];
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignOccluderAreas( tree_t *pTree );
+static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas );
+
+
+/*
+============
+BlockTree
+
+============
+*/
+node_t *BlockTree (int xl, int yl, int xh, int yh)
+{
+ node_t *node;
+ Vector normal;
+ float dist;
+ int mid;
+
+ if (xl == xh && yl == yh)
+ {
+ node = block_nodes[xl+BLOCKX_OFFSET][yl+BLOCKY_OFFSET];
+ if (!node)
+ { // return an empty leaf
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0; //CONTENTS_SOLID;
+ return node;
+ }
+ return node;
+ }
+
+ // create a seperator along the largest axis
+ node = AllocNode ();
+
+ if (xh - xl > yh - yl)
+ { // split x axis
+ mid = xl + (xh-xl)/2 + 1;
+ normal[0] = 1;
+ normal[1] = 0;
+ normal[2] = 0;
+ dist = mid*BLOCKS_SIZE;
+ node->planenum = g_MainMap->FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( mid, yl, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, mid-1, yh);
+ }
+ else
+ {
+ mid = yl + (yh-yl)/2 + 1;
+ normal[0] = 0;
+ normal[1] = 1;
+ normal[2] = 0;
+ dist = mid*BLOCKS_SIZE;
+ node->planenum = g_MainMap->FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( xl, mid, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, xh, mid-1);
+ }
+
+ return node;
+}
+
+/*
+============
+ProcessBlock_Thread
+
+============
+*/
+int brush_start, brush_end;
+void ProcessBlock_Thread (int threadnum, int blocknum)
+{
+ int xblock, yblock;
+ Vector mins, maxs;
+ bspbrush_t *brushes;
+ tree_t *tree;
+ node_t *node;
+
+ yblock = block_yl + blocknum / (block_xh-block_xl+1);
+ xblock = block_xl + blocknum % (block_xh-block_xl+1);
+
+ qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
+
+ mins[0] = xblock*BLOCKS_SIZE;
+ mins[1] = yblock*BLOCKS_SIZE;
+ mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = (xblock+1)*BLOCKS_SIZE;
+ maxs[1] = (yblock+1)*BLOCKS_SIZE;
+ maxs[2] = MAX_COORD_INTEGER;
+
+ // the makelist and chopbrushes could be cached between the passes...
+ brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs, NO_DETAIL);
+ if (!brushes)
+ {
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+ block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = node;
+ return;
+ }
+
+ FixupAreaportalWaterBrushes( brushes );
+ if (!nocsg)
+ brushes = ChopBrushes (brushes);
+
+ tree = BrushBSP (brushes, mins, maxs);
+
+ block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = tree->headnode;
+}
+
+
+/*
+============
+ProcessWorldModel
+
+============
+*/
+void SplitSubdividedFaces( node_t *headnode ); // garymcthack
+void ProcessWorldModel (void)
+{
+ entity_t *e;
+ tree_t *tree = NULL;
+ qboolean leaked;
+ int optimize;
+ int start;
+
+ e = &entities[entity_num];
+
+ brush_start = e->firstbrush;
+ brush_end = brush_start + e->numbrushes;
+ leaked = false;
+
+ //
+ // perform per-block operations
+ //
+ if (block_xh * BLOCKS_SIZE > g_MainMap->map_maxs[0])
+ {
+ block_xh = floor(g_MainMap->map_maxs[0]/BLOCKS_SIZE);
+ }
+ if ( (block_xl+1) * BLOCKS_SIZE < g_MainMap->map_mins[0])
+ {
+ block_xl = floor(g_MainMap->map_mins[0]/BLOCKS_SIZE);
+ }
+ if (block_yh * BLOCKS_SIZE > g_MainMap->map_maxs[1])
+ {
+ block_yh = floor(g_MainMap->map_maxs[1]/BLOCKS_SIZE);
+ }
+ if ( (block_yl+1) * BLOCKS_SIZE < g_MainMap->map_mins[1])
+ {
+ block_yl = floor(g_MainMap->map_mins[1]/BLOCKS_SIZE);
+ }
+
+ // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h )
+ if (block_xl < BLOCKS_MIN)
+ {
+ block_xl = BLOCKS_MIN;
+ }
+ if (block_yl < BLOCKS_MIN)
+ {
+ block_yl = BLOCKS_MIN;
+ }
+ if (block_xh > BLOCKS_MAX)
+ {
+ block_xh = BLOCKS_MAX;
+ }
+ if (block_yh > BLOCKS_MAX)
+ {
+ block_yh = BLOCKS_MAX;
+ }
+
+ for (optimize = 0 ; optimize <= 1 ; optimize++)
+ {
+ qprintf ("--------------------------------------------\n");
+
+ RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
+ !verbose, ProcessBlock_Thread);
+
+ //
+ // build the division tree
+ // oversizing the blocks guarantees that all the boundaries
+ // will also get nodes.
+ //
+
+ qprintf ("--------------------------------------------\n");
+
+ tree = AllocTree ();
+ tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
+
+ tree->mins[0] = (block_xl)*BLOCKS_SIZE;
+ tree->mins[1] = (block_yl)*BLOCKS_SIZE;
+ tree->mins[2] = g_MainMap->map_mins[2] - 8;
+
+ tree->maxs[0] = (block_xh+1)*BLOCKS_SIZE;
+ tree->maxs[1] = (block_yh+1)*BLOCKS_SIZE;
+ tree->maxs[2] = g_MainMap->map_maxs[2] + 8;
+
+ //
+ // perform the global operations
+ //
+
+ // make the portals/faces by traversing down to each empty leaf
+ MakeTreePortals (tree);
+
+ if (FloodEntities (tree))
+ {
+ // turns everthing outside into solid
+ FillOutside (tree->headnode);
+ }
+ else
+ {
+ Warning( ("**** leaked ****\n") );
+ leaked = true;
+ LeakFile (tree);
+ if (leaktest)
+ {
+ Warning( ("--- MAP LEAKED ---\n") );
+ exit (0);
+ }
+ }
+
+ // mark the brush sides that actually turned into faces
+ MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL);
+ if (noopt || leaked)
+ break;
+ if (!optimize)
+ {
+ // If we are optimizing, free the tree. Next time we will construct it again, but
+ // we'll use the information in MarkVisibleSides() so we'll only split with planes that
+ // actually contribute renderable geometry
+ FreeTree (tree);
+ }
+ }
+
+ FloodAreas (tree);
+
+ RemoveAreaPortalBrushes_R( tree->headnode );
+
+ start = Plat_FloatTime();
+ Msg("Building Faces...");
+ // this turns portals with one solid side into faces
+ // it also subdivides each face if necessary to fit max lightmap dimensions
+ MakeFaces (tree->headnode);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ if (glview)
+ {
+ WriteGLView (tree, source);
+ }
+
+ AssignOccluderAreas( tree );
+ Compute3DSkyboxAreas( tree->headnode, g_SkyAreas );
+ face_t *pLeafFaceList = NULL;
+ if ( !nodetail )
+ {
+ pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end );
+ }
+
+ start = Plat_FloatTime();
+
+ Msg("FixTjuncs...\n");
+
+ // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions)
+ // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer
+ pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList);
+
+ // this merges all of the solid nodes that have separating planes
+ if (!noprune)
+ {
+ Msg("PruneNodes...\n");
+ PruneNodes (tree->headnode);
+ }
+
+// Msg( "SplitSubdividedFaces...\n" );
+// SplitSubdividedFaces( tree->headnode );
+
+ Msg("WriteBSP...\n");
+ WriteBSP (tree->headnode, pLeafFaceList);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ if (!leaked)
+ {
+ WritePortalFile (tree);
+ }
+
+ FreeTree( tree );
+ FreeLeafFaces( pLeafFaceList );
+}
+
+/*
+============
+ProcessSubModel
+
+============
+*/
+void ProcessSubModel( )
+{
+ entity_t *e;
+ int start, end;
+ tree_t *tree;
+ bspbrush_t *list;
+ Vector mins, maxs;
+
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+
+ mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
+ list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL);
+
+ if (!nocsg)
+ list = ChopBrushes (list);
+ tree = BrushBSP (list, mins, maxs);
+
+ // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode.
+ if ( tree->headnode->planenum == PLANENUM_LEAF )
+ {
+ const char *pClassName = ValueForKey( e, "classname" );
+ const char *pTargetName = ValueForKey( e, "targetname" );
+ Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName );
+ }
+
+ MakeTreePortals (tree);
+
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ WriteGLView( tree, "tree_all" );
+#endif
+
+ MarkVisibleSides (tree, start, end, FULL_DETAIL);
+ MakeFaces (tree->headnode);
+
+ FixTjuncs( tree->headnode, NULL );
+ WriteBSP( tree->headnode, NULL );
+
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ {
+ WriteGLView( tree, "tree_vis" );
+ WriteGLViewFaces( tree, "tree_faces" );
+ }
+#endif
+
+ FreeTree (tree);
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the entity is a func_occluder
+//-----------------------------------------------------------------------------
+bool IsFuncOccluder( int entity_num )
+{
+ entity_t *mapent = &entities[entity_num];
+ const char *pClassName = ValueForKey( mapent, "classname" );
+ return (strcmp("func_occluder", pClassName) == 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the area of a brush's occluders
+//-----------------------------------------------------------------------------
+float ComputeOccluderBrushArea( mapbrush_t *pBrush )
+{
+ float flArea = 0.0f;
+ for ( int j = 0; j < pBrush->numsides; ++j )
+ {
+ side_t *pSide = &(pBrush->original_sides[j]);
+
+ // Skip nodraw surfaces
+ if ( texinfo[pSide->texinfo].flags & SURF_NODRAW )
+ continue;
+
+ if ( !pSide->winding )
+ continue;
+
+ flArea += WindingArea( pSide->winding );
+ }
+
+ return flArea;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clips all occluder brushes against each other
+//-----------------------------------------------------------------------------
+static tree_t *ClipOccluderBrushes( )
+{
+ // Create a list of all occluder brushes in the level
+ CUtlVector< mapbrush_t * > mapBrushes( 1024, 1024 );
+ for ( entity_num=0; entity_num < g_MainMap->num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ entity_t *e = &entities[entity_num];
+ int end = e->firstbrush + e->numbrushes;
+ int i;
+ for ( i = e->firstbrush; i < end; ++i )
+ {
+ mapBrushes.AddToTail( &g_MainMap->mapbrushes[i] );
+ }
+ }
+
+ int nBrushCount = mapBrushes.Count();
+ if ( nBrushCount == 0 )
+ return NULL;
+
+ Vector mins, maxs;
+ mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
+
+ bspbrush_t *list = MakeBspBrushList( mapBrushes.Base(), nBrushCount, mins, maxs );
+
+ if (!nocsg)
+ list = ChopBrushes (list);
+ tree_t *tree = BrushBSP (list, mins, maxs);
+ MakeTreePortals (tree);
+ MarkVisibleSides (tree, mapBrushes.Base(), nBrushCount);
+ MakeFaces( tree->headnode );
+
+ // NOTE: This will output the occluder face vertices + planes
+ FixTjuncs( tree->headnode, NULL );
+
+ return tree;
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate a list of unique sides in the occluder tree
+//-----------------------------------------------------------------------------
+static void GenerateOccluderSideList( int nEntity, CUtlVector<side_t*> &occluderSides )
+{
+ entity_t *e = &entities[nEntity];
+ int end = e->firstbrush + e->numbrushes;
+ int i, j;
+ for ( i = e->firstbrush; i < end; ++i )
+ {
+ mapbrush_t *mb = &g_MainMap->mapbrushes[i];
+ for ( j = 0; j < mb->numsides; ++j )
+ {
+ occluderSides.AddToTail( &(mb->original_sides[j]) );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate a list of unique faces in the occluder tree
+//-----------------------------------------------------------------------------
+static void GenerateOccluderFaceList( node_t *pOccluderNode, CUtlVector<face_t*> &occluderFaces )
+{
+ if (pOccluderNode->planenum == PLANENUM_LEAF)
+ return;
+
+ for ( face_t *f=pOccluderNode->faces ; f ; f = f->next )
+ {
+ occluderFaces.AddToTail( f );
+ }
+
+ GenerateOccluderFaceList( pOccluderNode->children[0], occluderFaces );
+ GenerateOccluderFaceList( pOccluderNode->children[1], occluderFaces );
+}
+
+
+//-----------------------------------------------------------------------------
+// For occluder area assignment
+//-----------------------------------------------------------------------------
+struct OccluderInfo_t
+{
+ int m_nOccluderEntityIndex;
+};
+
+static CUtlVector< OccluderInfo_t > g_OccluderInfo;
+
+
+//-----------------------------------------------------------------------------
+// Emits occluder brushes
+//-----------------------------------------------------------------------------
+static void EmitOccluderBrushes()
+{
+ char str[64];
+
+ g_OccluderData.RemoveAll();
+ g_OccluderPolyData.RemoveAll();
+ g_OccluderVertexIndices.RemoveAll();
+
+ tree_t *pOccluderTree = ClipOccluderBrushes();
+ if (!pOccluderTree)
+ return;
+
+ CUtlVector<face_t*> faceList( 1024, 1024 );
+ CUtlVector<side_t*> sideList( 1024, 1024 );
+ GenerateOccluderFaceList( pOccluderTree->headnode, faceList );
+
+#ifdef _DEBUG
+ int *pEmitted = (int*)stackalloc( faceList.Count() * sizeof(int) );
+ memset( pEmitted, 0, faceList.Count() * sizeof(int) );
+#endif
+
+ for ( entity_num=1; entity_num < num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ // Output only those parts of the occluder tree which are a part of the brush
+ int nOccluder = g_OccluderData.AddToTail();
+ doccluderdata_t &occluderData = g_OccluderData[ nOccluder ];
+ occluderData.firstpoly = g_OccluderPolyData.Count();
+ occluderData.mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ occluderData.maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+ occluderData.flags = 0;
+ occluderData.area = -1;
+
+ // NOTE: If you change the algorithm by which occluder numbers are allocated,
+ // then you must also change FixupOnlyEntsOccluderEntities() below
+ sprintf (str, "%i", nOccluder);
+ SetKeyValue (&entities[entity_num], "occludernumber", str);
+
+ int nIndex = g_OccluderInfo.AddToTail();
+ g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num;
+
+ sideList.RemoveAll();
+ GenerateOccluderSideList( entity_num, sideList );
+ for ( int i = faceList.Count(); --i >= 0; )
+ {
+ // Skip nodraw surfaces, but not triggers that have been marked as nodraw
+ face_t *f = faceList[i];
+ if ( ( texinfo[f->texinfo].flags & SURF_NODRAW ) &&
+ (( texinfo[f->texinfo].flags & SURF_TRIGGER ) == 0 ) )
+ continue;
+
+ // Only emit faces that appear in the side list of the occluder
+ for ( int j = sideList.Count(); --j >= 0; )
+ {
+ if ( sideList[j] != f->originalface )
+ continue;
+
+ if ( f->numpoints < 3 )
+ continue;
+
+ // not a final face
+ Assert ( !f->merged && !f->split[0] && !f->split[1] );
+
+#ifdef _DEBUG
+ Assert( !pEmitted[i] );
+ pEmitted[i] = entity_num;
+#endif
+
+ int k = g_OccluderPolyData.AddToTail();
+ doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[k];
+
+ pOccluderPoly->planenum = f->planenum;
+ pOccluderPoly->vertexcount = f->numpoints;
+ pOccluderPoly->firstvertexindex = g_OccluderVertexIndices.Count();
+ for( k = 0; k < f->numpoints; ++k )
+ {
+ g_OccluderVertexIndices.AddToTail( f->vertexnums[k] );
+
+ const Vector &p = dvertexes[f->vertexnums[k]].point;
+ VectorMin( occluderData.mins, p, occluderData.mins );
+ VectorMax( occluderData.maxs, p, occluderData.maxs );
+ }
+
+ break;
+ }
+ }
+
+ occluderData.polycount = g_OccluderPolyData.Count() - occluderData.firstpoly;
+
+ // Mark this brush as not having brush geometry so it won't be re-emitted with a brush model
+ entities[entity_num].numbrushes = 0;
+ }
+
+ FreeTree( pOccluderTree );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set occluder area
+//-----------------------------------------------------------------------------
+void SetOccluderArea( int nOccluder, int nArea, int nEntityNum )
+{
+ if ( g_OccluderData[nOccluder].area <= 0 )
+ {
+ g_OccluderData[nOccluder].area = nArea;
+ }
+ else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) )
+ {
+ const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" );
+ if (!pTargetName)
+ {
+ pTargetName = "<no name>";
+ }
+ Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals )
+{
+ int nFirstPoly = g_OccluderData[nOccluder].firstpoly;
+ int nEntityNum = g_OccluderInfo[nOccluder].m_nOccluderEntityIndex;
+ for ( int j = 0; j < g_OccluderData[nOccluder].polycount; ++j )
+ {
+ doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[nFirstPoly + j];
+ int nFirstVertex = pOccluderPoly->firstvertexindex;
+ for ( int k = 0; k < pOccluderPoly->vertexcount; ++k )
+ {
+ int nVertexIndex = g_OccluderVertexIndices[nFirstVertex + k];
+ node_t *pNode = NodeForPoint( pTree->headnode, dvertexes[ nVertexIndex ].point );
+
+ SetOccluderArea( nOccluder, pNode->area, nEntityNum );
+
+ int nOtherSideIndex;
+ portal_t *pPortal;
+ for ( pPortal = pNode->portals; pPortal; pPortal = pPortal->next[!nOtherSideIndex] )
+ {
+ nOtherSideIndex = (pPortal->nodes[0] == pNode) ? 1 : 0;
+ if (!pPortal->onnode)
+ continue; // edge of world
+
+ // Don't cross over area portals for the area check
+ if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL)
+ continue;
+
+ int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0;
+ SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignOccluderAreas( tree_t *pTree )
+{
+ for ( int i = 0; i < g_OccluderData.Count(); ++i )
+ {
+ AssignAreaToOccluder( i, pTree, false );
+
+ // This can only have happened if the only valid portal out leads into an areaportal
+ if ( g_OccluderData[i].area <= 0 )
+ {
+ AssignAreaToOccluder( i, pTree, true );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Make sure the func_occluders have the appropriate data set
+//-----------------------------------------------------------------------------
+void FixupOnlyEntsOccluderEntities()
+{
+ char str[64];
+ int nOccluder = 0;
+ for ( entity_num=1; entity_num < num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ // NOTE: If you change the algorithm by which occluder numbers are allocated above,
+ // then you must also change this
+ sprintf (str, "%i", nOccluder);
+ SetKeyValue (&entities[entity_num], "occludernumber", str);
+ ++nOccluder;
+ }
+}
+
+
+void MarkNoDynamicShadowSides()
+{
+ for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
+ {
+ g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = true;
+ }
+
+ for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ )
+ {
+ int brushSideID = g_NoDynamicShadowSides[i];
+
+ // Find the side with this ID.
+ for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
+ {
+ if ( g_MainMap->brushsides[iSide].id == brushSideID )
+ g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute the 3D skybox areas
+//-----------------------------------------------------------------------------
+static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas )
+{
+ for (int i = 0; i < g_MainMap->num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "sky_camera"))
+ {
+ // Found a 3D skybox camera, get a leaf that lies in it
+ node_t *pLeaf = PointInLeaf( headnode, entities[i].origin );
+ if (pLeaf->contents & CONTENTS_SOLID)
+ {
+ Error ("Error! Entity sky_camera in solid volume! at %.1f %.1f %.1f\n", entities[i].origin.x, entities[i].origin.y, entities[i].origin.z);
+ }
+ areas.AddToTail( pLeaf->area );
+ }
+ }
+}
+
+bool Is3DSkyboxArea( int area )
+{
+ for ( int i = g_SkyAreas.Count(); --i >=0; )
+ {
+ if ( g_SkyAreas[i] == area )
+ return true;
+ }
+ return false;
+}
+
+
+/*
+============
+ProcessModels
+============
+*/
+void ProcessModels (void)
+{
+ BeginBSPFile ();
+
+ // Mark sides that have no dynamic shadows.
+ MarkNoDynamicShadowSides();
+
+ // emit the displacement surfaces
+ EmitInitialDispInfos();
+
+ // Clip occluder brushes against each other,
+ // Remove them from the list of models to process below
+ EmitOccluderBrushes( );
+
+ for ( entity_num=0; entity_num < num_entities; ++entity_num )
+ {
+ entity_t *pEntity = &entities[entity_num];
+ if ( !pEntity->numbrushes )
+ continue;
+
+ qprintf ("############### model %i ###############\n", nummodels);
+
+ BeginModel ();
+
+ if (entity_num == 0)
+ {
+ ProcessWorldModel();
+ }
+ else
+ {
+ ProcessSubModel( );
+ }
+
+ EndModel ();
+
+ if (!verboseentities)
+ {
+ verbose = false; // don't bother printing submodels
+ }
+ }
+
+ // Turn the skybox into a cubemap in case we don't build env_cubemap textures.
+ Cubemap_CreateDefaultCubemaps();
+ EndBSPFile ();
+}
+
+
+void LoadPhysicsDLL( void )
+{
+ PhysicsDLLPath( "vphysics.dll" );
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+int RunVBSP( int argc, char **argv )
+{
+ int i;
+ double start, end;
+ char path[1024];
+
+ CommandLine()->CreateCmdLine( argc, argv );
+ MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false );
+ InstallSpewFunction();
+ SpewActivate( "developer", 1 );
+
+ CmdLib_InitFileSystem( argv[ argc-1 ] );
+
+ Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) );
+ Q_FileBase( source, mapbase, sizeof( mapbase ) );
+ strlwr( mapbase );
+
+ LoadCmdLineFromFile( argc, argv, mapbase, "vbsp" );
+
+ Msg( "Valve Software - vbsp.exe (%s)\n", __DATE__ );
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!stricmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i],"-glview"))
+ {
+ glview = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose") )
+ {
+ Msg("verbose = true\n");
+ verbose = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noweld"))
+ {
+ Msg ("noweld = true\n");
+ noweld = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nocsg"))
+ {
+ Msg ("nocsg = true\n");
+ nocsg = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noshare"))
+ {
+ Msg ("noshare = true\n");
+ noshare = true;
+ }
+ else if (!Q_stricmp(argv[i], "-notjunc"))
+ {
+ Msg ("notjunc = true\n");
+ notjunc = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nowater"))
+ {
+ Msg ("nowater = true\n");
+ nowater = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noopt"))
+ {
+ Msg ("noopt = true\n");
+ noopt = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noprune"))
+ {
+ Msg ("noprune = true\n");
+ noprune = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nomerge"))
+ {
+ Msg ("nomerge = true\n");
+ nomerge = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nomergewater"))
+ {
+ Msg ("nomergewater = true\n");
+ nomergewater = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nosubdiv"))
+ {
+ Msg ("nosubdiv = true\n");
+ nosubdiv = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nodetail"))
+ {
+ Msg ("nodetail = true\n");
+ nodetail = true;
+ }
+ else if (!Q_stricmp(argv[i], "-fulldetail"))
+ {
+ Msg ("fulldetail = true\n");
+ fulldetail = true;
+ }
+ else if (!Q_stricmp(argv[i], "-onlyents"))
+ {
+ Msg ("onlyents = true\n");
+ onlyents = true;
+ }
+ else if (!Q_stricmp(argv[i], "-onlyprops"))
+ {
+ Msg ("onlyprops = true\n");
+ onlyprops = true;
+ }
+ else if (!Q_stricmp(argv[i], "-micro"))
+ {
+ microvolume = atof(argv[i+1]);
+ Msg ("microvolume = %f\n", microvolume);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i], "-leaktest"))
+ {
+ Msg ("leaktest = true\n");
+ leaktest = true;
+ }
+ else if (!Q_stricmp(argv[i], "-verboseentities"))
+ {
+ Msg ("verboseentities = true\n");
+ verboseentities = true;
+ }
+ else if (!Q_stricmp(argv[i], "-snapaxial"))
+ {
+ Msg ("snap axial = true\n");
+ g_snapAxialPlanes = true;
+ }
+#if 0
+ else if (!Q_stricmp(argv[i], "-maxlightmapdim"))
+ {
+ g_maxLightmapDimension = atof(argv[i+1]);
+ Msg ("g_maxLightmapDimension = %f\n", g_maxLightmapDimension);
+ i++;
+ }
+#endif
+ else if (!Q_stricmp(argv[i], "-block"))
+ {
+ block_xl = block_xh = atoi(argv[i+1]);
+ block_yl = block_yh = atoi(argv[i+2]);
+ Msg ("block: %i,%i\n", block_xl, block_yl);
+ i+=2;
+ }
+ else if (!Q_stricmp(argv[i], "-blocks"))
+ {
+ block_xl = atoi(argv[i+1]);
+ block_yl = atoi(argv[i+2]);
+ block_xh = atoi(argv[i+3]);
+ block_yh = atoi(argv[i+4]);
+ Msg ("blocks: %i,%i to %i,%i\n",
+ block_xl, block_yl, block_xh, block_yh);
+ i+=4;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpcollide" ) )
+ {
+ Msg("Dumping collision models to collideXXX.txt\n" );
+ dumpcollide = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpstaticprop" ) )
+ {
+ Msg("Dumping static props to staticpropXXX.txt\n" );
+ g_DumpStaticProps = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-forceskyvis" ) )
+ {
+ Msg("Enabled vis in 3d skybox\n" );
+ g_bSkyVis = true;
+ }
+ else if (!Q_stricmp (argv[i],"-tmpout"))
+ {
+ strcpy (outbase, "/tmp");
+ }
+#if 0
+ else if( !Q_stricmp( argv[i], "-defaultluxelsize" ) )
+ {
+ g_defaultLuxelSize = atof( argv[i+1] );
+ i++;
+ }
+#endif
+ else if( !Q_stricmp( argv[i], "-luxelscale" ) )
+ {
+ g_luxelScale = atof( argv[i+1] );
+ i++;
+ }
+ else if( !strcmp( argv[i], "-minluxelscale" ) )
+ {
+ g_minLuxelScale = atof( argv[i+1] );
+ if (g_minLuxelScale < 1)
+ g_minLuxelScale = 1;
+ i++;
+ }
+ else if( !Q_stricmp( argv[i], "-dxlevel" ) )
+ {
+ g_nDXLevel = atoi( argv[i+1] );
+ Msg( "DXLevel = %d\n", g_nDXLevel );
+ i++;
+ }
+ else if( !Q_stricmp( argv[i], "-bumpall" ) )
+ {
+ g_BumpAll = true;
+ }
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if( !Q_stricmp( argv[i], "-lightifmissing" ) )
+ {
+ g_bLightIfMissing = true;
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
+ {
+ // nothing to do here, but don't bail on this option
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-keepstalezip" ) )
+ {
+ g_bKeepStaleZip = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-xbox" ) )
+ {
+ // enable mandatory xbox extensions
+ g_NodrawTriggers = true;
+ g_DisableWaterLighting = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdetailcracks"))
+ {
+ g_bAllowDetailCracks = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-novirtualmesh"))
+ {
+ g_bNoVirtualMesh = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-replacematerials" ) )
+ {
+ g_ReplaceMaterials = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-nodrawtriggers") )
+ {
+ g_NodrawTriggers = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if (argv[i][0] == '-')
+ {
+ Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
+ i = 100000; // force it to print the usage
+ break;
+ }
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ {
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vbsp [options...] mapfile\n"
+ "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options (use -v to see all options):\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " line options).\n"
+ "\n"
+ " -onlyents : This option causes vbsp only import the entities from the .vmf\n"
+ " file. -onlyents won't reimport brush models.\n"
+ " -onlyprops : Only update the static props and detail props.\n"
+ " -glview : Writes .gl files in the current directory that can be viewed\n"
+ " with glview.exe. If you use -tmpout, it will write the files\n"
+ " into the \\tmp folder.\n"
+ " -nodetail : Get rid of all detail geometry. The geometry left over is\n"
+ " what affects visibility.\n"
+ " -nowater : Get rid of water brushes.\n"
+ " -low : Run as an idle-priority process.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n" );
+
+ if ( verbose )
+ {
+ Warning(
+ "Other options :\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the # of\n"
+ " processors on your machine).\n"
+ " -verboseentities: If -v is on, this disables verbose output for submodels.\n"
+ " -noweld : Don't join face vertices together.\n"
+ " -nocsg : Don't chop out intersecting brush areas.\n"
+ " -noshare : Emit unique face edges instead of sharing them.\n"
+ " -notjunc : Don't fixup t-junctions.\n"
+ " -noopt : By default, vbsp removes the 'outer shell' of the map, which\n"
+ " are all the faces you can't see because you can never get\n"
+ " outside the map. -noopt disables this behaviour.\n"
+ " -noprune : Don't prune neighboring solid nodes.\n"
+ " -nomerge : Don't merge together chopped faces on nodes.\n"
+ " -nomergewater: Don't merge together chopped faces on water.\n"
+ " -nosubdiv : Don't subdivide faces for lightmapping.\n"
+ " -micro <#> : vbsp will warn when brushes are output with a volume less\n"
+ " than this number (default: 1.0).\n"
+ " -fulldetail : Mark all detail geometry as normal geometry (so all detail\n"
+ " geometry will affect visibility).\n"
+ " -leaktest : Stop processing the map if a leak is detected. Whether or not\n"
+ " this flag is set, a leak file will be written out at\n"
+ " <vmf filename>.lin, and it can be imported into Hammer.\n"
+ " -bumpall : Force all surfaces to be bump mapped.\n"
+ " -snapaxial : Snap axial planes to integer coordinates.\n"
+ " -block # # : Control the grid size mins that vbsp chops the level on.\n"
+ " -blocks # # # # : Enter the mins and maxs for the grid size vbsp uses.\n"
+ " -dumpstaticprops: Dump static props to staticprop*.txt\n"
+ " -dumpcollide : Write files with collision info.\n"
+ " -forceskyvis : Enable vis calculations in 3d skybox leaves\n"
+ " -luxelscale # : Scale all lightmaps by this amount (default: 1.0).\n"
+ " -minluxelscale #: No luxel scale will be lower than this amount (default: 1.0).\n"
+ " -lightifmissing : Force lightmaps to be generated for all surfaces even if\n"
+ " they don't need lightmaps.\n"
+ " -keepstalezip : Keep the BSP's zip files intact but regenerate everything\n"
+ " else.\n"
+ " -virtualdispphysics : Use virtual (not precomputed) displacement collision models\n"
+ " -xbox : Enable mandatory xbox options\n"
+ " -x360 : Generate Xbox360 version of vsp\n"
+ " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
+ " -replacematerials : Substitute materials according to materialsub.txt in content\\maps\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ );
+ }
+
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ CmdLib_Exit( 1 );
+ }
+
+ start = Plat_FloatTime();
+
+ // Run in the background?
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < 80 ) )
+ {
+ g_BumpAll = false;
+ }
+
+ if( g_luxelScale == 1.0f )
+ {
+ if ( g_nDXLevel == 70 )
+ {
+ g_luxelScale = 4.0f;
+ }
+ }
+
+ ThreadSetDefault ();
+ numthreads = 1; // multiple threads aren't helping...
+
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+
+ LoadPhysicsDLL();
+ LoadSurfaceProperties();
+
+#if 0
+ Msg( "qdir: %s This is the the path of the initial source file \n", qdir );
+ Msg( "gamedir: %s This is the base engine + mod-specific game dir (e.g. d:/tf2/mytfmod/) \n", gamedir );
+ Msg( "basegamedir: %s This is the base engine + base game directory (e.g. e:/hl2/hl2/, or d:/tf2/tf2/ )\n", basegamedir );
+#endif
+
+ sprintf( materialPath, "%smaterials", gamedir );
+ InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() );
+ Msg( "materialPath: %s\n", materialPath );
+
+ // delete portal and line files
+ sprintf (path, "%s.prt", source);
+ remove (path);
+ sprintf (path, "%s.lin", source);
+ remove (path);
+
+ strcpy (name, ExpandArg (argv[i]));
+
+ const char *pszExtension = V_GetFileExtension( name );
+ if ( !pszExtension )
+ {
+ V_SetExtension( name, ".vmm", sizeof( name ) );
+ if ( !FileExists( name ) )
+ {
+ V_SetExtension( name, ".vmf", sizeof( name ) );
+ }
+ }
+
+ char platformBSPFileName[1024];
+ GetPlatformMapPath( source, platformBSPFileName, g_nDXLevel, 1024 );
+
+ // if we're combining materials, load the script file
+ if ( g_ReplaceMaterials )
+ {
+ LoadMaterialReplacementKeys( gamedir, mapbase );
+ }
+
+ //
+ // if onlyents, just grab the entites and resave
+ //
+ if (onlyents)
+ {
+ LoadBSPFile (platformBSPFileName);
+ num_entities = 0;
+ // Clear out the cubemap samples since they will be reparsed even with -onlyents
+ g_nCubemapSamples = 0;
+
+ // Mark as stale since the lighting could be screwed with new ents.
+ AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
+
+ LoadMapFile (name);
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ // NOTE: If we ever precompute lighting for static props in
+ // vrad, EmitStaticProps should be removed here
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // NOTE: Don't deal with detail props here, it blows away lighting
+
+ // Recompute the skybox
+ ComputeBoundsNoSkybox();
+
+ // Make sure that we have a water lod control eneity if we have water in the map.
+ EnsurePresenceOfWaterLODControlEntity();
+
+ // Make sure the func_occluders have the appropriate data set
+ FixupOnlyEntsOccluderEntities();
+
+ // Doing this here because stuff abov may filter out entities
+ UnparseEntities ();
+
+ WriteBSPFile (platformBSPFileName);
+ }
+ else if (onlyprops)
+ {
+ // In the only props case, deal with static + detail props only
+ LoadBSPFile (platformBSPFileName);
+
+ LoadMapFile(name);
+ SetModelNumbers();
+ SetLightStyles();
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // Place detail props found in .vmf and based on material properties
+ LoadEmitDetailObjectDictionary( gamedir );
+ EmitDetailObjects();
+
+ WriteBSPFile (platformBSPFileName);
+ }
+ else
+ {
+ //
+ // start from scratch
+ //
+
+ // Load just the file system from the bsp
+ if( g_bKeepStaleZip && FileExists( platformBSPFileName ) )
+ {
+ LoadBSPFile_FileSystemOnly (platformBSPFileName);
+ // Mark as stale since the lighting could be screwed with new ents.
+ AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
+ }
+
+ LoadMapFile (name);
+ WorldVertexTransitionFixup();
+ if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
+ {
+ Cubemap_FixupBrushSidesMaterials();
+ Cubemap_AttachDefaultCubemapToSpecularSides();
+ Cubemap_AddUnreferencedCubemaps();
+ }
+ SetModelNumbers ();
+ SetLightStyles ();
+ LoadEmitDetailObjectDictionary( gamedir );
+ ProcessModels ();
+ }
+
+ end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ DeleteCmdLine( argc, argv );
+ ReleasePakFileLumps();
+ DeleteMaterialReplacementKeys();
+ ShutdownMaterialSystem();
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+/*
+=============
+main
+============
+*/
+int main (int argc, char **argv)
+{
+ // Install an exception handler.
+ SetupDefaultToolsMinidumpHandler();
+ return RunVBSP( argc, argv );
+}
+
+
diff --git a/mp/src/utils/vbsp/vbsp.h b/mp/src/utils/vbsp/vbsp.h
index 9b994d38..689bbaa8 100644
--- a/mp/src/utils/vbsp/vbsp.h
+++ b/mp/src/utils/vbsp/vbsp.h
@@ -1,657 +1,657 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#if !defined( VBSP_H )
-#define VBSP_H
-
-
-#include "cmdlib.h"
-#include "mathlib/vector.h"
-#include "scriplib.h"
-#include "polylib.h"
-#include "threads.h"
-#include "bsplib.h"
-#include "qfiles.h"
-#include "utilmatlib.h"
-#include "ChunkFile.h"
-
-#ifdef WIN32
-#pragma warning( disable: 4706 )
-#endif
-
-class CUtlBuffer;
-
-#define MAX_BRUSH_SIDES 128
-#define CLIP_EPSILON 0.1
-
-#define TEXINFO_NODE -1 // side is allready on a node
-
-// this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc.
-#define DEBUG_BRUSHMODEL 0
-
-struct portal_t;
-struct node_t;
-
-struct plane_t : public dplane_t
-{
- plane_t *hash_chain;
-
- plane_t() { normal.Init(); }
-};
-
-
-struct brush_texture_t
-{
- Vector UAxis;
- Vector VAxis;
- vec_t shift[2];
- vec_t rotate;
- vec_t textureWorldUnitsPerTexel[2];
- vec_t lightmapWorldUnitsPerLuxel;
- char name[TEXTURE_NAME_LENGTH];
- int flags;
-
- brush_texture_t() : UAxis(0,0,0), VAxis(0,0,0) {}
-};
-
-struct mapdispinfo_t;
-
-struct side_t
-{
- int planenum;
- int texinfo;
- mapdispinfo_t *pMapDisp;
-
- winding_t *winding;
- side_t *original; // bspbrush_t sides will reference the mapbrush_t sides
- int contents; // from miptex
- int surf; // from miptex
- qboolean visible; // choose visble planes first
- qboolean tested; // this plane allready checked as a split
- qboolean bevel; // don't ever use for bsp splitting
-
- side_t *next;
- int origIndex;
- int id; // This is the unique id generated by worldcraft for this side.
- unsigned int smoothingGroups;
- CUtlVector<int> aOverlayIds; // List of overlays that reside on this side.
- CUtlVector<int> aWaterOverlayIds; // List of water overlays that reside on this side.
- bool m_bDynamicShadowsEnabled; // Goes into dface_t::SetDynamicShadowsEnabled().
-};
-
-struct mapbrush_t
-{
- int entitynum;
- int brushnum;
- int id; // The unique ID of this brush in the editor, used for reporting errors.
- int contents;
- Vector mins, maxs;
- int numsides;
- side_t *original_sides;
-};
-
-#define PLANENUM_LEAF -1
-
-#define MAXEDGES 32
-
-struct face_t
-{
- int id;
-
- face_t *next; // on node
-
- // the chain of faces off of a node can be merged or split,
- // but each face_t along the way will remain in the chain
- // until the entire tree is freed
- face_t *merged; // if set, this face isn't valid anymore
- face_t *split[2]; // if set, this face isn't valid anymore
-
- portal_t *portal;
- int texinfo;
- int dispinfo;
- // This is only for surfaces that are the boundaries of fog volumes
- // (ie. water surfaces)
- // All of the rest of the surfaces can look at their leaf to find out
- // what fog volume they are in.
- node_t *fogVolumeLeaf;
-
- int planenum;
- int contents; // faces in different contents can't merge
- int outputnumber;
- winding_t *w;
- int numpoints;
- qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
- int vertexnums[MAXEDGES];
- side_t *originalface; // save the "side" this face came from
- int firstPrimID;
- int numPrims;
- unsigned int smoothingGroups;
-};
-
-void EmitFace( face_t *f, qboolean onNode );
-
-struct mapdispinfo_t
-{
- face_t face;
- int entitynum;
- int power;
- int minTess;
- float smoothingAngle;
- Vector uAxis;
- Vector vAxis;
- Vector startPosition;
- float alphaValues[MAX_DISPVERTS];
- float maxDispDist;
- float dispDists[MAX_DISPVERTS];
- Vector vectorDisps[MAX_DISPVERTS];
- Vector vectorOffsets[MAX_DISPVERTS];
- int contents;
- int brushSideID;
- unsigned short triTags[MAX_DISPTRIS];
- int flags;
-
-#ifdef VSVMFIO
- float m_elevation; // "elevation"
- Vector m_offsetNormals[ MAX_DISPTRIS ]; // "offset_normals"
-#endif // VSVMFIO
-
-};
-
-extern int nummapdispinfo;
-extern mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
-
-extern float g_defaultLuxelSize;
-extern float g_luxelScale;
-extern float g_minLuxelScale;
-extern bool g_BumpAll;
-extern int g_nDXLevel;
-
-int GetDispInfoEntityNum( mapdispinfo_t *pDisp );
-void ComputeBoundsNoSkybox( );
-
-struct bspbrush_t
-{
- int id;
- bspbrush_t *next;
- Vector mins, maxs;
- int side, testside; // side of node during construction
- mapbrush_t *original;
- int numsides;
- side_t sides[6]; // variably sized
-};
-
-
-#define MAX_NODE_BRUSHES 8
-
-struct leafface_t
-{
- face_t *pFace;
- leafface_t *pNext;
-};
-
-struct node_t
-{
- int id;
-
- // both leafs and nodes
- int planenum; // -1 = leaf node
- node_t *parent;
- Vector mins, maxs; // valid after portalization
- bspbrush_t *volume; // one for each leaf/node
-
- // nodes only
- side_t *side; // the side that created the node
- node_t *children[2];
- face_t *faces; // these are the cutup ones that live in the plane of "side".
-
- // leafs only
- bspbrush_t *brushlist; // fragments of all brushes in this leaf
- leafface_t *leaffacelist;
- int contents; // OR of all brush contents
- int occupied; // 1 or greater can reach entity
- entity_t *occupant; // for leak file testing
- int cluster; // for portalfile writing
- int area; // for areaportals
- portal_t *portals; // also on nodes during construction
- int diskId; // dnodes or dleafs index after this has been emitted
-};
-
-
-struct portal_t
-{
- int id;
- plane_t plane;
- node_t *onnode; // NULL = outside box
- node_t *nodes[2]; // [0] = front side of plane
- portal_t *next[2];
- winding_t *winding;
- qboolean sidefound; // false if ->side hasn't been checked
- side_t *side; // NULL = non-visible
- face_t *face[2]; // output face in bsp file
-};
-
-
-struct tree_t
-{
- node_t *headnode;
- node_t outside_node;
- Vector mins, maxs;
- bool leaked;
-};
-
-
-extern int entity_num;
-
-struct LoadSide_t;
-struct LoadEntity_t;
-class CManifest;
-
-class CMapFile
-{
-public:
- CMapFile( void ) { Init(); }
-
- void Init( void );
-
- void AddPlaneToHash (plane_t *p);
- int CreateNewFloatPlane (Vector& normal, vec_t dist);
- int FindFloatPlane (Vector& normal, vec_t dist);
- int PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2);
- void AddBrushBevels (mapbrush_t *b);
- qboolean MakeBrushWindings (mapbrush_t *ob);
- void MoveBrushesToWorld( entity_t *mapent );
- void MoveBrushesToWorldGeneral( entity_t *mapent );
- void RemoveContentsDetailFromEntity( entity_t *mapent );
- int SideIDToIndex( int brushSideID );
- void AddLadderKeys( entity_t *mapent );
- ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
- void ForceFuncAreaPortalWindowContents();
- ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
- ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
- ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
- void TestExpandBrushes(void);
-
- static char m_InstancePath[ MAX_PATH ];
- static void SetInstancePath( const char *pszInstancePath );
- static const char *GetInstancePath( void ) { return m_InstancePath; }
- static bool DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName );
-
- void CheckForInstances( const char *pszFileName );
- void MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance );
- void MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
- void MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
- void MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
- void ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity );
- void MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
- void MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
-
- static int m_InstanceCount;
- static int c_areaportals;
-
- plane_t mapplanes[MAX_MAP_PLANES];
- int nummapplanes;
-
- #define PLANE_HASHES 1024
- plane_t *planehash[PLANE_HASHES];
-
- int nummapbrushes;
- mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
-
- Vector map_mins, map_maxs;
-
- int nummapbrushsides;
- side_t brushsides[MAX_MAP_BRUSHSIDES];
-
- brush_texture_t side_brushtextures[MAX_MAP_BRUSHSIDES];
-
- int num_entities;
- entity_t entities[MAX_MAP_ENTITIES];
-
- int c_boxbevels;
- int c_edgebevels;
- int c_clipbrushes;
- int g_ClipTexinfo;
-
- class CConnectionPairs
- {
- public:
- CConnectionPairs( epair_t *pair, CConnectionPairs *next )
- {
- m_Pair = pair;
- m_Next = next;
- }
-
- epair_t *m_Pair;
- CConnectionPairs *m_Next;
- };
-
- CConnectionPairs *m_ConnectionPairs;
-
- int m_StartMapOverlays;
- int m_StartMapWaterOverlays;
-};
-
-extern CMapFile *g_MainMap;
-extern CMapFile *g_LoadingMap;
-
-extern CUtlVector< CMapFile * > g_Maps;
-
-extern int g_nMapFileVersion;
-
-extern qboolean noprune;
-extern qboolean nodetail;
-extern qboolean fulldetail;
-extern qboolean nomerge;
-extern qboolean nomergewater;
-extern qboolean nosubdiv;
-extern qboolean nowater;
-extern qboolean noweld;
-extern qboolean noshare;
-extern qboolean notjunc;
-extern qboolean nocsg;
-extern qboolean noopt;
-extern qboolean dumpcollide;
-extern qboolean nodetailcuts;
-extern qboolean g_DumpStaticProps;
-extern qboolean g_bSkyVis;
-extern vec_t microvolume;
-extern bool g_snapAxialPlanes;
-extern bool g_NodrawTriggers;
-extern bool g_DisableWaterLighting;
-extern bool g_bAllowDetailCracks;
-extern bool g_bNoVirtualMesh;
-extern char outbase[32];
-
-extern char source[1024];
-extern char mapbase[ 64 ];
-extern CUtlVector<int> g_SkyAreas;
-
-bool LoadMapFile( const char *pszFileName );
-int GetVertexnum( Vector& v );
-bool Is3DSkyboxArea( int area );
-
-//=============================================================================
-
-// textures.c
-
-struct textureref_t
-{
- char name[TEXTURE_NAME_LENGTH];
- int flags;
- float lightmapWorldUnitsPerLuxel;
- int contents;
-};
-
-extern textureref_t textureref[MAX_MAP_TEXTURES];
-
-int FindMiptex (const char *name);
-
-int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin);
-int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName );
-
-extern int g_SurfaceProperties[MAX_MAP_TEXDATA];
-void LoadSurfaceProperties( void );
-
-int PointLeafnum ( dmodel_t* pModel, const Vector& p );
-
-//=============================================================================
-
-void FindGCD (int *v);
-
-mapbrush_t *Brush_LoadEntity (entity_t *ent);
-int PlaneTypeForNormal (Vector& normal);
-qboolean MakeBrushPlanes (mapbrush_t *b);
-int FindIntPlane (int *inormal, int *iorigin);
-void CreateBrush (int brushnum);
-
-
-//=============================================================================
-// detail objects
-//=============================================================================
-
-void LoadEmitDetailObjectDictionary( char const* pGameDir );
-void EmitDetailObjects();
-
-//=============================================================================
-// static props
-//=============================================================================
-
-void EmitStaticProps();
-bool LoadStudioModel( char const* pFileName, char const* pEntityType, CUtlBuffer& buf );
-
-//=============================================================================
-//=============================================================================
-// procedurally created .vmt files
-//=============================================================================
-
-void EmitStaticProps();
-
-// draw.c
-
-extern Vector draw_mins, draw_maxs;
-extern bool g_bLightIfMissing;
-
-void Draw_ClearWindow (void);
-void DrawWinding (winding_t *w);
-
-void GLS_BeginScene (void);
-void GLS_Winding (winding_t *w, int code);
-void GLS_EndScene (void);
-
-//=============================================================================
-
-// csg
-
-enum detailscreen_e
-{
- FULL_DETAIL = 0,
- ONLY_DETAIL = 1,
- NO_DETAIL = 2,
-};
-
-#define TRANSPARENT_CONTENTS (CONTENTS_GRATE|CONTENTS_WINDOW)
-
-#include "csg.h"
-
-//=============================================================================
-
-// brushbsp
-
-void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
-
-bspbrush_t *CopyBrush (bspbrush_t *brush);
-
-void SplitBrush (bspbrush_t *brush, int planenum,
- bspbrush_t **front, bspbrush_t **back);
-
-tree_t *AllocTree (void);
-node_t *AllocNode (void);
-bspbrush_t *AllocBrush (int numsides);
-int CountBrushList (bspbrush_t *brushes);
-void FreeBrush (bspbrush_t *brushes);
-vec_t BrushVolume (bspbrush_t *brush);
-node_t *NodeForPoint (node_t *node, Vector& origin);
-
-void BoundBrush (bspbrush_t *brush);
-void FreeBrushList (bspbrush_t *brushes);
-node_t *PointInLeaf (node_t *node, Vector& point);
-
-tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs);
-
-#define PSIDE_FRONT 1
-#define PSIDE_BACK 2
-#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
-#define PSIDE_FACING 4
-int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane);
-extern qboolean WindingIsTiny (winding_t *w);
-
-//=============================================================================
-
-// portals.c
-
-int VisibleContents (int contents);
-
-void MakeHeadnodePortals (tree_t *tree);
-void MakeNodePortal (node_t *node);
-void SplitNodePortals (node_t *node);
-
-qboolean Portal_VisFlood (portal_t *p);
-
-qboolean FloodEntities (tree_t *tree);
-void FillOutside (node_t *headnode);
-void FloodAreas (tree_t *tree);
-void MarkVisibleSides (tree_t *tree, int start, int end, int detailScreen);
-void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount );
-void FreePortal (portal_t *p);
-void EmitAreaPortals (node_t *headnode);
-
-void MakeTreePortals (tree_t *tree);
-
-//=============================================================================
-
-// glfile.c
-
-void OutputWinding (winding_t *w, FileHandle_t glview);
-void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b);
-void WriteGLView (tree_t *tree, char *source);
-void WriteGLViewFaces (tree_t *tree, const char *source);
-void WriteGLViewBrushList( bspbrush_t *pList, const char *pName );
-//=============================================================================
-
-// leakfile.c
-
-void LeakFile (tree_t *tree);
-void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart );
-
-//=============================================================================
-
-// prtfile.c
-
-void AddVisCluster( entity_t *pFuncVisCluster );
-void WritePortalFile (tree_t *tree);
-
-//=============================================================================
-
-// writebsp.c
-
-void SetModelNumbers (void);
-void SetLightStyles (void);
-
-void BeginBSPFile (void);
-void WriteBSP (node_t *headnode, face_t *pLeafFaceList);
-void EndBSPFile (void);
-void BeginModel (void);
-void EndModel (void);
-
-extern int firstmodeledge;
-extern int firstmodelface;
-
-//=============================================================================
-
-// faces.c
-
-void MakeFaces (node_t *headnode);
-void MakeDetailFaces (node_t *headnode);
-face_t *FixTjuncs( node_t *headnode, face_t *pLeafFaceList );
-
-face_t *AllocFace (void);
-void FreeFace (face_t *f);
-void FreeFaceList( face_t *pFaces );
-
-void MergeFaceList(face_t **pFaceList);
-void SubdivideFaceList(face_t **pFaceList);
-
-extern face_t *edgefaces[MAX_MAP_EDGES][2];
-
-
-//=============================================================================
-
-// tree.c
-
-void FreeTree (tree_t *tree);
-void FreeTree_r (node_t *node);
-void PrintTree_r (node_t *node, int depth);
-void FreeTreePortals_r (node_t *node);
-void PruneNodes_r (node_t *node);
-void PruneNodes (node_t *node);
-
-// Returns true if the entity is a func_occluder
-bool IsFuncOccluder( int entity_num );
-
-
-//=============================================================================
-// ivp.cpp
-class CPhysCollide;
-void EmitPhysCollision();
-void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename );
-void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *headnode );
-
-//=============================================================================
-// find + find or create the texdata
-int FindTexData( const char *pName );
-int FindOrCreateTexData( const char *pName );
-// Add a clone of an existing texdata with a new name
-int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName );
-int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
-int FindAliasedTexData( const char *pName, dtexdata_t *sourceTexture );
-int FindTexInfo( const texinfo_t &searchTexInfo );
-
-//=============================================================================
-// normals.c
-void SaveVertexNormals( void );
-
-//=============================================================================
-// cubemap.cpp
-void Cubemap_InsertSample( const Vector& origin, int size );
-void Cubemap_CreateDefaultCubemaps( void );
-void Cubemap_SaveBrushSides( const char *pSideListStr );
-void Cubemap_FixupBrushSidesMaterials( void );
-void Cubemap_AttachDefaultCubemapToSpecularSides( void );
-// Add skipped cubemaps that are referenced by the engine
-void Cubemap_AddUnreferencedCubemaps( void );
-
-//=============================================================================
-// overlay.cpp
-#define OVERLAY_MAP_STRLEN 256
-
-struct mapoverlay_t
-{
- int nId;
- unsigned short m_nRenderOrder;
- char szMaterialName[OVERLAY_MAP_STRLEN];
- float flU[2];
- float flV[2];
- float flFadeDistMinSq;
- float flFadeDistMaxSq;
- Vector vecUVPoints[4];
- Vector vecOrigin;
- Vector vecBasis[3];
- CUtlVector<int> aSideList;
- CUtlVector<int> aFaceList;
-};
-
-extern CUtlVector<mapoverlay_t> g_aMapOverlays;
-extern CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
-
-int Overlay_GetFromEntity( entity_t *pMapEnt );
-void Overlay_UpdateSideLists( int StartIndex );
-void Overlay_AddFaceToLists( int iFace, side_t *pSide );
-void Overlay_EmitOverlayFaces( void );
-void OverlayTransition_UpdateSideLists( int StartIndex );
-void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide );
-void OverlayTransition_EmitOverlayFaces( void );
-void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix );
-
-//=============================================================================
-
-void RemoveAreaPortalBrushes_R( node_t *node );
-
-dtexdata_t *GetTexData( int index );
-
-#endif
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#if !defined( VBSP_H )
+#define VBSP_H
+
+
+#include "cmdlib.h"
+#include "mathlib/vector.h"
+#include "scriplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "bsplib.h"
+#include "qfiles.h"
+#include "utilmatlib.h"
+#include "ChunkFile.h"
+
+#ifdef WIN32
+#pragma warning( disable: 4706 )
+#endif
+
+class CUtlBuffer;
+
+#define MAX_BRUSH_SIDES 128
+#define CLIP_EPSILON 0.1
+
+#define TEXINFO_NODE -1 // side is allready on a node
+
+// this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc.
+#define DEBUG_BRUSHMODEL 0
+
+struct portal_t;
+struct node_t;
+
+struct plane_t : public dplane_t
+{
+ plane_t *hash_chain;
+
+ plane_t() { normal.Init(); }
+};
+
+
+struct brush_texture_t
+{
+ Vector UAxis;
+ Vector VAxis;
+ vec_t shift[2];
+ vec_t rotate;
+ vec_t textureWorldUnitsPerTexel[2];
+ vec_t lightmapWorldUnitsPerLuxel;
+ char name[TEXTURE_NAME_LENGTH];
+ int flags;
+
+ brush_texture_t() : UAxis(0,0,0), VAxis(0,0,0) {}
+};
+
+struct mapdispinfo_t;
+
+struct side_t
+{
+ int planenum;
+ int texinfo;
+ mapdispinfo_t *pMapDisp;
+
+ winding_t *winding;
+ side_t *original; // bspbrush_t sides will reference the mapbrush_t sides
+ int contents; // from miptex
+ int surf; // from miptex
+ qboolean visible; // choose visble planes first
+ qboolean tested; // this plane allready checked as a split
+ qboolean bevel; // don't ever use for bsp splitting
+
+ side_t *next;
+ int origIndex;
+ int id; // This is the unique id generated by worldcraft for this side.
+ unsigned int smoothingGroups;
+ CUtlVector<int> aOverlayIds; // List of overlays that reside on this side.
+ CUtlVector<int> aWaterOverlayIds; // List of water overlays that reside on this side.
+ bool m_bDynamicShadowsEnabled; // Goes into dface_t::SetDynamicShadowsEnabled().
+};
+
+struct mapbrush_t
+{
+ int entitynum;
+ int brushnum;
+ int id; // The unique ID of this brush in the editor, used for reporting errors.
+ int contents;
+ Vector mins, maxs;
+ int numsides;
+ side_t *original_sides;
+};
+
+#define PLANENUM_LEAF -1
+
+#define MAXEDGES 32
+
+struct face_t
+{
+ int id;
+
+ face_t *next; // on node
+
+ // the chain of faces off of a node can be merged or split,
+ // but each face_t along the way will remain in the chain
+ // until the entire tree is freed
+ face_t *merged; // if set, this face isn't valid anymore
+ face_t *split[2]; // if set, this face isn't valid anymore
+
+ portal_t *portal;
+ int texinfo;
+ int dispinfo;
+ // This is only for surfaces that are the boundaries of fog volumes
+ // (ie. water surfaces)
+ // All of the rest of the surfaces can look at their leaf to find out
+ // what fog volume they are in.
+ node_t *fogVolumeLeaf;
+
+ int planenum;
+ int contents; // faces in different contents can't merge
+ int outputnumber;
+ winding_t *w;
+ int numpoints;
+ qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
+ int vertexnums[MAXEDGES];
+ side_t *originalface; // save the "side" this face came from
+ int firstPrimID;
+ int numPrims;
+ unsigned int smoothingGroups;
+};
+
+void EmitFace( face_t *f, qboolean onNode );
+
+struct mapdispinfo_t
+{
+ face_t face;
+ int entitynum;
+ int power;
+ int minTess;
+ float smoothingAngle;
+ Vector uAxis;
+ Vector vAxis;
+ Vector startPosition;
+ float alphaValues[MAX_DISPVERTS];
+ float maxDispDist;
+ float dispDists[MAX_DISPVERTS];
+ Vector vectorDisps[MAX_DISPVERTS];
+ Vector vectorOffsets[MAX_DISPVERTS];
+ int contents;
+ int brushSideID;
+ unsigned short triTags[MAX_DISPTRIS];
+ int flags;
+
+#ifdef VSVMFIO
+ float m_elevation; // "elevation"
+ Vector m_offsetNormals[ MAX_DISPTRIS ]; // "offset_normals"
+#endif // VSVMFIO
+
+};
+
+extern int nummapdispinfo;
+extern mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
+
+extern float g_defaultLuxelSize;
+extern float g_luxelScale;
+extern float g_minLuxelScale;
+extern bool g_BumpAll;
+extern int g_nDXLevel;
+
+int GetDispInfoEntityNum( mapdispinfo_t *pDisp );
+void ComputeBoundsNoSkybox( );
+
+struct bspbrush_t
+{
+ int id;
+ bspbrush_t *next;
+ Vector mins, maxs;
+ int side, testside; // side of node during construction
+ mapbrush_t *original;
+ int numsides;
+ side_t sides[6]; // variably sized
+};
+
+
+#define MAX_NODE_BRUSHES 8
+
+struct leafface_t
+{
+ face_t *pFace;
+ leafface_t *pNext;
+};
+
+struct node_t
+{
+ int id;
+
+ // both leafs and nodes
+ int planenum; // -1 = leaf node
+ node_t *parent;
+ Vector mins, maxs; // valid after portalization
+ bspbrush_t *volume; // one for each leaf/node
+
+ // nodes only
+ side_t *side; // the side that created the node
+ node_t *children[2];
+ face_t *faces; // these are the cutup ones that live in the plane of "side".
+
+ // leafs only
+ bspbrush_t *brushlist; // fragments of all brushes in this leaf
+ leafface_t *leaffacelist;
+ int contents; // OR of all brush contents
+ int occupied; // 1 or greater can reach entity
+ entity_t *occupant; // for leak file testing
+ int cluster; // for portalfile writing
+ int area; // for areaportals
+ portal_t *portals; // also on nodes during construction
+ int diskId; // dnodes or dleafs index after this has been emitted
+};
+
+
+struct portal_t
+{
+ int id;
+ plane_t plane;
+ node_t *onnode; // NULL = outside box
+ node_t *nodes[2]; // [0] = front side of plane
+ portal_t *next[2];
+ winding_t *winding;
+ qboolean sidefound; // false if ->side hasn't been checked
+ side_t *side; // NULL = non-visible
+ face_t *face[2]; // output face in bsp file
+};
+
+
+struct tree_t
+{
+ node_t *headnode;
+ node_t outside_node;
+ Vector mins, maxs;
+ bool leaked;
+};
+
+
+extern int entity_num;
+
+struct LoadSide_t;
+struct LoadEntity_t;
+class CManifest;
+
+class CMapFile
+{
+public:
+ CMapFile( void ) { Init(); }
+
+ void Init( void );
+
+ void AddPlaneToHash (plane_t *p);
+ int CreateNewFloatPlane (Vector& normal, vec_t dist);
+ int FindFloatPlane (Vector& normal, vec_t dist);
+ int PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2);
+ void AddBrushBevels (mapbrush_t *b);
+ qboolean MakeBrushWindings (mapbrush_t *ob);
+ void MoveBrushesToWorld( entity_t *mapent );
+ void MoveBrushesToWorldGeneral( entity_t *mapent );
+ void RemoveContentsDetailFromEntity( entity_t *mapent );
+ int SideIDToIndex( int brushSideID );
+ void AddLadderKeys( entity_t *mapent );
+ ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
+ void ForceFuncAreaPortalWindowContents();
+ ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
+ ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+ ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ void TestExpandBrushes(void);
+
+ static char m_InstancePath[ MAX_PATH ];
+ static void SetInstancePath( const char *pszInstancePath );
+ static const char *GetInstancePath( void ) { return m_InstancePath; }
+ static bool DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName );
+
+ void CheckForInstances( const char *pszFileName );
+ void MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance );
+ void MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity );
+ void MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+
+ static int m_InstanceCount;
+ static int c_areaportals;
+
+ plane_t mapplanes[MAX_MAP_PLANES];
+ int nummapplanes;
+
+ #define PLANE_HASHES 1024
+ plane_t *planehash[PLANE_HASHES];
+
+ int nummapbrushes;
+ mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+ Vector map_mins, map_maxs;
+
+ int nummapbrushsides;
+ side_t brushsides[MAX_MAP_BRUSHSIDES];
+
+ brush_texture_t side_brushtextures[MAX_MAP_BRUSHSIDES];
+
+ int num_entities;
+ entity_t entities[MAX_MAP_ENTITIES];
+
+ int c_boxbevels;
+ int c_edgebevels;
+ int c_clipbrushes;
+ int g_ClipTexinfo;
+
+ class CConnectionPairs
+ {
+ public:
+ CConnectionPairs( epair_t *pair, CConnectionPairs *next )
+ {
+ m_Pair = pair;
+ m_Next = next;
+ }
+
+ epair_t *m_Pair;
+ CConnectionPairs *m_Next;
+ };
+
+ CConnectionPairs *m_ConnectionPairs;
+
+ int m_StartMapOverlays;
+ int m_StartMapWaterOverlays;
+};
+
+extern CMapFile *g_MainMap;
+extern CMapFile *g_LoadingMap;
+
+extern CUtlVector< CMapFile * > g_Maps;
+
+extern int g_nMapFileVersion;
+
+extern qboolean noprune;
+extern qboolean nodetail;
+extern qboolean fulldetail;
+extern qboolean nomerge;
+extern qboolean nomergewater;
+extern qboolean nosubdiv;
+extern qboolean nowater;
+extern qboolean noweld;
+extern qboolean noshare;
+extern qboolean notjunc;
+extern qboolean nocsg;
+extern qboolean noopt;
+extern qboolean dumpcollide;
+extern qboolean nodetailcuts;
+extern qboolean g_DumpStaticProps;
+extern qboolean g_bSkyVis;
+extern vec_t microvolume;
+extern bool g_snapAxialPlanes;
+extern bool g_NodrawTriggers;
+extern bool g_DisableWaterLighting;
+extern bool g_bAllowDetailCracks;
+extern bool g_bNoVirtualMesh;
+extern char outbase[32];
+
+extern char source[1024];
+extern char mapbase[ 64 ];
+extern CUtlVector<int> g_SkyAreas;
+
+bool LoadMapFile( const char *pszFileName );
+int GetVertexnum( Vector& v );
+bool Is3DSkyboxArea( int area );
+
+//=============================================================================
+
+// textures.c
+
+struct textureref_t
+{
+ char name[TEXTURE_NAME_LENGTH];
+ int flags;
+ float lightmapWorldUnitsPerLuxel;
+ int contents;
+};
+
+extern textureref_t textureref[MAX_MAP_TEXTURES];
+
+int FindMiptex (const char *name);
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin);
+int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName );
+
+extern int g_SurfaceProperties[MAX_MAP_TEXDATA];
+void LoadSurfaceProperties( void );
+
+int PointLeafnum ( dmodel_t* pModel, const Vector& p );
+
+//=============================================================================
+
+void FindGCD (int *v);
+
+mapbrush_t *Brush_LoadEntity (entity_t *ent);
+int PlaneTypeForNormal (Vector& normal);
+qboolean MakeBrushPlanes (mapbrush_t *b);
+int FindIntPlane (int *inormal, int *iorigin);
+void CreateBrush (int brushnum);
+
+
+//=============================================================================
+// detail objects
+//=============================================================================
+
+void LoadEmitDetailObjectDictionary( char const* pGameDir );
+void EmitDetailObjects();
+
+//=============================================================================
+// static props
+//=============================================================================
+
+void EmitStaticProps();
+bool LoadStudioModel( char const* pFileName, char const* pEntityType, CUtlBuffer& buf );
+
+//=============================================================================
+//=============================================================================
+// procedurally created .vmt files
+//=============================================================================
+
+void EmitStaticProps();
+
+// draw.c
+
+extern Vector draw_mins, draw_maxs;
+extern bool g_bLightIfMissing;
+
+void Draw_ClearWindow (void);
+void DrawWinding (winding_t *w);
+
+void GLS_BeginScene (void);
+void GLS_Winding (winding_t *w, int code);
+void GLS_EndScene (void);
+
+//=============================================================================
+
+// csg
+
+enum detailscreen_e
+{
+ FULL_DETAIL = 0,
+ ONLY_DETAIL = 1,
+ NO_DETAIL = 2,
+};
+
+#define TRANSPARENT_CONTENTS (CONTENTS_GRATE|CONTENTS_WINDOW)
+
+#include "csg.h"
+
+//=============================================================================
+
+// brushbsp
+
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
+
+bspbrush_t *CopyBrush (bspbrush_t *brush);
+
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back);
+
+tree_t *AllocTree (void);
+node_t *AllocNode (void);
+bspbrush_t *AllocBrush (int numsides);
+int CountBrushList (bspbrush_t *brushes);
+void FreeBrush (bspbrush_t *brushes);
+vec_t BrushVolume (bspbrush_t *brush);
+node_t *NodeForPoint (node_t *node, Vector& origin);
+
+void BoundBrush (bspbrush_t *brush);
+void FreeBrushList (bspbrush_t *brushes);
+node_t *PointInLeaf (node_t *node, Vector& point);
+
+tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs);
+
+#define PSIDE_FRONT 1
+#define PSIDE_BACK 2
+#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
+#define PSIDE_FACING 4
+int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane);
+extern qboolean WindingIsTiny (winding_t *w);
+
+//=============================================================================
+
+// portals.c
+
+int VisibleContents (int contents);
+
+void MakeHeadnodePortals (tree_t *tree);
+void MakeNodePortal (node_t *node);
+void SplitNodePortals (node_t *node);
+
+qboolean Portal_VisFlood (portal_t *p);
+
+qboolean FloodEntities (tree_t *tree);
+void FillOutside (node_t *headnode);
+void FloodAreas (tree_t *tree);
+void MarkVisibleSides (tree_t *tree, int start, int end, int detailScreen);
+void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount );
+void FreePortal (portal_t *p);
+void EmitAreaPortals (node_t *headnode);
+
+void MakeTreePortals (tree_t *tree);
+
+//=============================================================================
+
+// glfile.c
+
+void OutputWinding (winding_t *w, FileHandle_t glview);
+void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b);
+void WriteGLView (tree_t *tree, char *source);
+void WriteGLViewFaces (tree_t *tree, const char *source);
+void WriteGLViewBrushList( bspbrush_t *pList, const char *pName );
+//=============================================================================
+
+// leakfile.c
+
+void LeakFile (tree_t *tree);
+void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart );
+
+//=============================================================================
+
+// prtfile.c
+
+void AddVisCluster( entity_t *pFuncVisCluster );
+void WritePortalFile (tree_t *tree);
+
+//=============================================================================
+
+// writebsp.c
+
+void SetModelNumbers (void);
+void SetLightStyles (void);
+
+void BeginBSPFile (void);
+void WriteBSP (node_t *headnode, face_t *pLeafFaceList);
+void EndBSPFile (void);
+void BeginModel (void);
+void EndModel (void);
+
+extern int firstmodeledge;
+extern int firstmodelface;
+
+//=============================================================================
+
+// faces.c
+
+void MakeFaces (node_t *headnode);
+void MakeDetailFaces (node_t *headnode);
+face_t *FixTjuncs( node_t *headnode, face_t *pLeafFaceList );
+
+face_t *AllocFace (void);
+void FreeFace (face_t *f);
+void FreeFaceList( face_t *pFaces );
+
+void MergeFaceList(face_t **pFaceList);
+void SubdivideFaceList(face_t **pFaceList);
+
+extern face_t *edgefaces[MAX_MAP_EDGES][2];
+
+
+//=============================================================================
+
+// tree.c
+
+void FreeTree (tree_t *tree);
+void FreeTree_r (node_t *node);
+void PrintTree_r (node_t *node, int depth);
+void FreeTreePortals_r (node_t *node);
+void PruneNodes_r (node_t *node);
+void PruneNodes (node_t *node);
+
+// Returns true if the entity is a func_occluder
+bool IsFuncOccluder( int entity_num );
+
+
+//=============================================================================
+// ivp.cpp
+class CPhysCollide;
+void EmitPhysCollision();
+void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename );
+void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *headnode );
+
+//=============================================================================
+// find + find or create the texdata
+int FindTexData( const char *pName );
+int FindOrCreateTexData( const char *pName );
+// Add a clone of an existing texdata with a new name
+int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName );
+int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
+int FindAliasedTexData( const char *pName, dtexdata_t *sourceTexture );
+int FindTexInfo( const texinfo_t &searchTexInfo );
+
+//=============================================================================
+// normals.c
+void SaveVertexNormals( void );
+
+//=============================================================================
+// cubemap.cpp
+void Cubemap_InsertSample( const Vector& origin, int size );
+void Cubemap_CreateDefaultCubemaps( void );
+void Cubemap_SaveBrushSides( const char *pSideListStr );
+void Cubemap_FixupBrushSidesMaterials( void );
+void Cubemap_AttachDefaultCubemapToSpecularSides( void );
+// Add skipped cubemaps that are referenced by the engine
+void Cubemap_AddUnreferencedCubemaps( void );
+
+//=============================================================================
+// overlay.cpp
+#define OVERLAY_MAP_STRLEN 256
+
+struct mapoverlay_t
+{
+ int nId;
+ unsigned short m_nRenderOrder;
+ char szMaterialName[OVERLAY_MAP_STRLEN];
+ float flU[2];
+ float flV[2];
+ float flFadeDistMinSq;
+ float flFadeDistMaxSq;
+ Vector vecUVPoints[4];
+ Vector vecOrigin;
+ Vector vecBasis[3];
+ CUtlVector<int> aSideList;
+ CUtlVector<int> aFaceList;
+};
+
+extern CUtlVector<mapoverlay_t> g_aMapOverlays;
+extern CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
+
+int Overlay_GetFromEntity( entity_t *pMapEnt );
+void Overlay_UpdateSideLists( int StartIndex );
+void Overlay_AddFaceToLists( int iFace, side_t *pSide );
+void Overlay_EmitOverlayFaces( void );
+void OverlayTransition_UpdateSideLists( int StartIndex );
+void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide );
+void OverlayTransition_EmitOverlayFaces( void );
+void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix );
+
+//=============================================================================
+
+void RemoveAreaPortalBrushes_R( node_t *node );
+
+dtexdata_t *GetTexData( int index );
+
+#endif
+
diff --git a/mp/src/utils/vbsp/vbsp.vpc b/mp/src/utils/vbsp/vbsp.vpc
index 4c6d1862..7065f0a5 100644
--- a/mp/src/utils/vbsp/vbsp.vpc
+++ b/mp/src/utils/vbsp/vbsp.vpc
@@ -1,184 +1,184 @@
-//-----------------------------------------------------------------------------
-// VBSP.VPC
-//
-// Project Script
-//-----------------------------------------------------------------------------
-
-$Macro SRCDIR "..\.."
-$Macro OUTBINDIR "$SRCDIR\..\game\bin"
-
-$Include "$SRCDIR\vpc_scripts\source_exe_con_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"
- }
- }
-
- $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 "$SRCDIR\public\tier1\interface.h"
- $File "ivp.h"
- $File "..\common\map_shared.h"
- $File "..\common\pacifier.h"
- $File "..\common\polylib.h"
- $File "$SRCDIR\public\tier1\tokenreader.h"
- $File "..\common\utilmatlib.h"
- $File "..\vmpi\vmpi.h"
- $File "$SRCDIR\public\zip_uncompressed.h"
- }
- }
-
- $Folder "Public Headers"
- {
- $File "$SRCDIR\public\mathlib\amd3dx.h"
- $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\commonmacros.h"
- $File "$SRCDIR\public\tier0\dbg.h"
- $File "$SRCDIR\public\disp_common.h"
- $File "$SRCDIR\public\IScratchPad3D.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"
- {
- $Lib bitmap
- $Lib fgdlib
- $Lib mathlib
- $Lib tier2
- $Lib vtf
- }
-
- $File "notes.txt"
-}
+//-----------------------------------------------------------------------------
+// VBSP.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_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"
+ }
+ }
+
+ $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 "$SRCDIR\public\tier1\interface.h"
+ $File "ivp.h"
+ $File "..\common\map_shared.h"
+ $File "..\common\pacifier.h"
+ $File "..\common\polylib.h"
+ $File "$SRCDIR\public\tier1\tokenreader.h"
+ $File "..\common\utilmatlib.h"
+ $File "..\vmpi\vmpi.h"
+ $File "$SRCDIR\public\zip_uncompressed.h"
+ }
+ }
+
+ $Folder "Public Headers"
+ {
+ $File "$SRCDIR\public\mathlib\amd3dx.h"
+ $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\commonmacros.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "$SRCDIR\public\disp_common.h"
+ $File "$SRCDIR\public\IScratchPad3D.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"
+ {
+ $Lib bitmap
+ $Lib fgdlib
+ $Lib mathlib
+ $Lib tier2
+ $Lib vtf
+ }
+
+ $File "notes.txt"
+}
diff --git a/mp/src/utils/vbsp/worldvertextransitionfixup.cpp b/mp/src/utils/vbsp/worldvertextransitionfixup.cpp
index 5d00f3f5..f97f3933 100644
--- a/mp/src/utils/vbsp/worldvertextransitionfixup.cpp
+++ b/mp/src/utils/vbsp/worldvertextransitionfixup.cpp
@@ -1,212 +1,212 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================
-
-#include "bsplib.h"
-#include "vbsp.h"
-#include "tier1/UtlBuffer.h"
-#include "tier1/utlvector.h"
-#include "KeyValues.h"
-#include "materialpatch.h"
-
-struct entitySideList_t
-{
- int firstBrushSide;
- int brushSideCount;
-};
-
-static bool SideIsNotDispAndHasDispMaterial( int iSide )
-{
- side_t *pSide = &g_MainMap->brushsides[iSide];
-
- // If it's a displacement, then it's fine to have a displacement-only material.
- if ( pSide->pMapDisp )
- {
- return false;
- }
-
- pSide->texinfo;
-
- return true;
-}
-
-static void BackSlashToForwardSlash( char *pname )
-{
- while ( *pname ) {
- if ( *pname == '\\' )
- *pname = '/';
- pname++;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Generate patched material name
-//-----------------------------------------------------------------------------
-static void GeneratePatchedMaterialName( const char *pMaterialName, char *pBuffer, int nMaxLen )
-{
- int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s_wvt_patch", mapbase, pMaterialName );
-
- Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
- if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
- {
- Error( "Generated worldvertextransition patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
- }
-
- BackSlashToForwardSlash( pBuffer );
- Q_strlower( pBuffer );
-}
-
-static void RemoveKey( KeyValues *kv, const char *pSubKeyName )
-{
- KeyValues *pSubKey = kv->FindKey( pSubKeyName );
- if( pSubKey )
- {
- kv->RemoveSubKey( pSubKey );
- pSubKey->deleteThis();
- }
-}
-
-void CreateWorldVertexTransitionPatchedMaterial( const char *pOriginalMaterialName, const char *pPatchedMaterialName )
-{
- KeyValues *kv = LoadMaterialKeyValues( pOriginalMaterialName, 0 );
- if( kv )
- {
- // change shader to Lightmappedgeneric (from worldvertextransition*)
- kv->SetName( "LightmappedGeneric" );
- // don't need no stinking $basetexture2 or any other second texture vars
- RemoveKey( kv, "$basetexture2" );
- RemoveKey( kv, "$bumpmap2" );
- RemoveKey( kv, "$bumpframe2" );
- RemoveKey( kv, "$basetexture2noenvmap" );
- RemoveKey( kv, "$blendmodulatetexture" );
- RemoveKey( kv, "$maskedblending" );
- RemoveKey( kv, "$surfaceprop2" );
- // If we didn't want a basetexture on the first texture in the blend, we don't want an envmap at all.
- KeyValues *basetexturenoenvmap = kv->FindKey( "$BASETEXTURENOENVMAP" );
- if( basetexturenoenvmap->GetInt() )
- {
- RemoveKey( kv, "$envmap" );
- }
-
- Warning( "Patching WVT material: %s\n", pPatchedMaterialName );
- WriteMaterialKeyValuesToPak( pPatchedMaterialName, kv );
- }
-}
-
-int CreateBrushVersionOfWorldVertexTransitionMaterial( int originalTexInfo )
-{
- // Don't make cubemap tex infos for nodes
- if ( originalTexInfo == TEXINFO_NODE )
- return originalTexInfo;
-
- texinfo_t *pTexInfo = &texinfo[originalTexInfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pOriginalMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
-
- // Get out of here if the originalTexInfo is already a patched wvt material
- if ( Q_stristr( pOriginalMaterialName, "_wvt_patch" ) )
- return originalTexInfo;
-
- char patchedMaterialName[1024];
- GeneratePatchedMaterialName( pOriginalMaterialName, patchedMaterialName, 1024 );
-// Warning( "GeneratePatchedMaterialName: %s %s\n", pMaterialName, patchedMaterialName );
-
- // Make sure the texdata doesn't already exist.
- int nTexDataID = FindTexData( patchedMaterialName );
- bool bHasTexData = (nTexDataID != -1);
- if( !bHasTexData )
- {
- // Create the new vmt material file
- CreateWorldVertexTransitionPatchedMaterial( pOriginalMaterialName, patchedMaterialName );
-
- // Make a new texdata
- nTexDataID = AddCloneTexData( pTexData, patchedMaterialName );
- }
-
- Assert( nTexDataID != -1 );
-
- texinfo_t newTexInfo;
- newTexInfo = *pTexInfo;
- newTexInfo.texdata = nTexDataID;
-
- int nTexInfoID = -1;
-
- // See if we need to make a new texinfo
- bool bHasTexInfo = false;
- if( bHasTexData )
- {
- nTexInfoID = FindTexInfo( newTexInfo );
- bHasTexInfo = (nTexInfoID != -1);
- }
-
- // Make a new texinfo if we need to.
- if( !bHasTexInfo )
- {
- nTexInfoID = texinfo.AddToTail( newTexInfo );
- }
-
- Assert( nTexInfoID != -1 );
- return nTexInfoID;
-}
-
-const char *GetShaderNameForTexInfo( int iTexInfo )
-{
- texinfo_t *pTexInfo = &texinfo[iTexInfo];
- dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
- const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
- MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
- const char *pShaderName = GetMaterialShaderName( hMaterial );
- return pShaderName;
-}
-
-void WorldVertexTransitionFixup( void )
-{
- CUtlVector<entitySideList_t> sideList;
- sideList.SetCount( g_MainMap->num_entities );
- int i;
- for ( i = 0; i < g_MainMap->num_entities; i++ )
- {
- sideList[i].firstBrushSide = 0;
- sideList[i].brushSideCount = 0;
- }
-
- for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
- {
- sideList[g_MainMap->mapbrushes[i].entitynum].brushSideCount += g_MainMap->mapbrushes[i].numsides;
- }
- int curSide = 0;
- for ( i = 0; i < g_MainMap->num_entities; i++ )
- {
- sideList[i].firstBrushSide = curSide;
- curSide += sideList[i].brushSideCount;
- }
-
- int currentEntity = 0;
- for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
- {
- side_t *pSide = &g_MainMap->brushsides[iSide];
-
- // skip displacments
- if ( pSide->pMapDisp )
- continue;
-
- if( pSide->texinfo < 0 )
- continue;
-
- const char *pShaderName = GetShaderNameForTexInfo( pSide->texinfo );
- if ( !pShaderName || !Q_stristr( pShaderName, "worldvertextransition" ) )
- {
- continue;
- }
-
- while ( currentEntity < g_MainMap->num_entities-1 &&
- iSide > sideList[currentEntity].firstBrushSide + sideList[currentEntity].brushSideCount )
- {
- currentEntity++;
- }
-
- pSide->texinfo = CreateBrushVersionOfWorldVertexTransitionMaterial( pSide->texinfo );
- }
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "bsplib.h"
+#include "vbsp.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlvector.h"
+#include "KeyValues.h"
+#include "materialpatch.h"
+
+struct entitySideList_t
+{
+ int firstBrushSide;
+ int brushSideCount;
+};
+
+static bool SideIsNotDispAndHasDispMaterial( int iSide )
+{
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+
+ // If it's a displacement, then it's fine to have a displacement-only material.
+ if ( pSide->pMapDisp )
+ {
+ return false;
+ }
+
+ pSide->texinfo;
+
+ return true;
+}
+
+static void BackSlashToForwardSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '\\' )
+ *pname = '/';
+ pname++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Generate patched material name
+//-----------------------------------------------------------------------------
+static void GeneratePatchedMaterialName( const char *pMaterialName, char *pBuffer, int nMaxLen )
+{
+ int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s_wvt_patch", mapbase, pMaterialName );
+
+ Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
+ if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
+ {
+ Error( "Generated worldvertextransition patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
+ }
+
+ BackSlashToForwardSlash( pBuffer );
+ Q_strlower( pBuffer );
+}
+
+static void RemoveKey( KeyValues *kv, const char *pSubKeyName )
+{
+ KeyValues *pSubKey = kv->FindKey( pSubKeyName );
+ if( pSubKey )
+ {
+ kv->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+}
+
+void CreateWorldVertexTransitionPatchedMaterial( const char *pOriginalMaterialName, const char *pPatchedMaterialName )
+{
+ KeyValues *kv = LoadMaterialKeyValues( pOriginalMaterialName, 0 );
+ if( kv )
+ {
+ // change shader to Lightmappedgeneric (from worldvertextransition*)
+ kv->SetName( "LightmappedGeneric" );
+ // don't need no stinking $basetexture2 or any other second texture vars
+ RemoveKey( kv, "$basetexture2" );
+ RemoveKey( kv, "$bumpmap2" );
+ RemoveKey( kv, "$bumpframe2" );
+ RemoveKey( kv, "$basetexture2noenvmap" );
+ RemoveKey( kv, "$blendmodulatetexture" );
+ RemoveKey( kv, "$maskedblending" );
+ RemoveKey( kv, "$surfaceprop2" );
+ // If we didn't want a basetexture on the first texture in the blend, we don't want an envmap at all.
+ KeyValues *basetexturenoenvmap = kv->FindKey( "$BASETEXTURENOENVMAP" );
+ if( basetexturenoenvmap->GetInt() )
+ {
+ RemoveKey( kv, "$envmap" );
+ }
+
+ Warning( "Patching WVT material: %s\n", pPatchedMaterialName );
+ WriteMaterialKeyValuesToPak( pPatchedMaterialName, kv );
+ }
+}
+
+int CreateBrushVersionOfWorldVertexTransitionMaterial( int originalTexInfo )
+{
+ // Don't make cubemap tex infos for nodes
+ if ( originalTexInfo == TEXINFO_NODE )
+ return originalTexInfo;
+
+ texinfo_t *pTexInfo = &texinfo[originalTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pOriginalMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ // Get out of here if the originalTexInfo is already a patched wvt material
+ if ( Q_stristr( pOriginalMaterialName, "_wvt_patch" ) )
+ return originalTexInfo;
+
+ char patchedMaterialName[1024];
+ GeneratePatchedMaterialName( pOriginalMaterialName, patchedMaterialName, 1024 );
+// Warning( "GeneratePatchedMaterialName: %s %s\n", pMaterialName, patchedMaterialName );
+
+ // Make sure the texdata doesn't already exist.
+ int nTexDataID = FindTexData( patchedMaterialName );
+ bool bHasTexData = (nTexDataID != -1);
+ if( !bHasTexData )
+ {
+ // Create the new vmt material file
+ CreateWorldVertexTransitionPatchedMaterial( pOriginalMaterialName, patchedMaterialName );
+
+ // Make a new texdata
+ nTexDataID = AddCloneTexData( pTexData, patchedMaterialName );
+ }
+
+ Assert( nTexDataID != -1 );
+
+ texinfo_t newTexInfo;
+ newTexInfo = *pTexInfo;
+ newTexInfo.texdata = nTexDataID;
+
+ int nTexInfoID = -1;
+
+ // See if we need to make a new texinfo
+ bool bHasTexInfo = false;
+ if( bHasTexData )
+ {
+ nTexInfoID = FindTexInfo( newTexInfo );
+ bHasTexInfo = (nTexInfoID != -1);
+ }
+
+ // Make a new texinfo if we need to.
+ if( !bHasTexInfo )
+ {
+ nTexInfoID = texinfo.AddToTail( newTexInfo );
+ }
+
+ Assert( nTexInfoID != -1 );
+ return nTexInfoID;
+}
+
+const char *GetShaderNameForTexInfo( int iTexInfo )
+{
+ texinfo_t *pTexInfo = &texinfo[iTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
+ const char *pShaderName = GetMaterialShaderName( hMaterial );
+ return pShaderName;
+}
+
+void WorldVertexTransitionFixup( void )
+{
+ CUtlVector<entitySideList_t> sideList;
+ sideList.SetCount( g_MainMap->num_entities );
+ int i;
+ for ( i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ sideList[i].firstBrushSide = 0;
+ sideList[i].brushSideCount = 0;
+ }
+
+ for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
+ {
+ sideList[g_MainMap->mapbrushes[i].entitynum].brushSideCount += g_MainMap->mapbrushes[i].numsides;
+ }
+ int curSide = 0;
+ for ( i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ sideList[i].firstBrushSide = curSide;
+ curSide += sideList[i].brushSideCount;
+ }
+
+ int currentEntity = 0;
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+
+ // skip displacments
+ if ( pSide->pMapDisp )
+ continue;
+
+ if( pSide->texinfo < 0 )
+ continue;
+
+ const char *pShaderName = GetShaderNameForTexInfo( pSide->texinfo );
+ if ( !pShaderName || !Q_stristr( pShaderName, "worldvertextransition" ) )
+ {
+ continue;
+ }
+
+ while ( currentEntity < g_MainMap->num_entities-1 &&
+ iSide > sideList[currentEntity].firstBrushSide + sideList[currentEntity].brushSideCount )
+ {
+ currentEntity++;
+ }
+
+ pSide->texinfo = CreateBrushVersionOfWorldVertexTransitionMaterial( pSide->texinfo );
+ }
} \ No newline at end of file
diff --git a/mp/src/utils/vbsp/worldvertextransitionfixup.h b/mp/src/utils/vbsp/worldvertextransitionfixup.h
index 6297dca5..9a1f0d38 100644
--- a/mp/src/utils/vbsp/worldvertextransitionfixup.h
+++ b/mp/src/utils/vbsp/worldvertextransitionfixup.h
@@ -1,15 +1,15 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================
-
-#ifndef WORLDVERTEXTRANSITIONFIXUP_H
-#define WORLDVERTEXTRANSITIONFIXUP_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-void WorldVertexTransitionFixup( void );
-
-#endif // WORLDVERTEXTRANSITIONFIXUP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef WORLDVERTEXTRANSITIONFIXUP_H
+#define WORLDVERTEXTRANSITIONFIXUP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+void WorldVertexTransitionFixup( void );
+
+#endif // WORLDVERTEXTRANSITIONFIXUP_H
diff --git a/mp/src/utils/vbsp/writebsp.cpp b/mp/src/utils/vbsp/writebsp.cpp
index c776bede..60d05980 100644
--- a/mp/src/utils/vbsp/writebsp.cpp
+++ b/mp/src/utils/vbsp/writebsp.cpp
@@ -1,1552 +1,1552 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "vbsp.h"
-#include "disp_vbsp.h"
-#include "utlvector.h"
-#include "faces.h"
-#include "builddisp.h"
-#include "tier1/strtools.h"
-#include "utilmatlib.h"
-#include "utldict.h"
-#include "map.h"
-
-int c_nofaces;
-int c_facenodes;
-
-// NOTE: This is a global used to link faces back to the tree node/portals they came from
-// it's used when filling water volumes
-node_t *dfacenodes[MAX_MAP_FACES];
-
-
-/*
-=========================================================
-
-ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
-
-=========================================================
-*/
-
-void EmitFaceVertexes (face_t **list, face_t *f);
-void AssignOccluderAreas();
-
-/*
-============
-EmitPlanes
-
-There is no oportunity to discard planes, because all of the original
-brushes will be saved in the map.
-============
-*/
-void EmitPlanes (void)
-{
- int i;
- dplane_t *dp;
- plane_t *mp;
- int planetranslate[MAX_MAP_PLANES];
-
- mp = g_MainMap->mapplanes;
- for (i=0 ; i<g_MainMap->nummapplanes ; i++, mp++)
- {
- dp = &dplanes[numplanes];
- planetranslate[i] = numplanes;
- VectorCopy ( mp->normal, dp->normal);
- dp->dist = mp->dist;
- dp->type = mp->type;
- numplanes++;
- }
-}
-
-
-//========================================================
-
-void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
-{
- int i;
- int facenum;
-
- while (f->merged)
- f = f->merged;
-
- if (f->split[0])
- {
- EmitMarkFace (leaf_p, f->split[0]);
- EmitMarkFace (leaf_p, f->split[1]);
- return;
- }
-
- facenum = f->outputnumber;
- if (facenum == -1)
- return; // degenerate face
-
- if (facenum < 0 || facenum >= numfaces)
- Error ("Bad leafface");
- for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
- if (dleaffaces[i] == facenum)
- break; // merged out face
- if (i == numleaffaces)
- {
- if (numleaffaces >= MAX_MAP_LEAFFACES)
- Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES);
-
- dleaffaces[numleaffaces] = facenum;
- numleaffaces++;
- }
-
-}
-
-
-/*
-==================
-EmitLeaf
-==================
-*/
-void EmitLeaf (node_t *node)
-{
- dleaf_t *leaf_p;
- portal_t *p;
- int s;
- face_t *f;
- bspbrush_t *b;
- int i;
- int brushnum;
- leafface_t *pList;
-
- // emit a leaf
- if (numleafs >= MAX_MAP_LEAFS)
- Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS);
-
- node->diskId = numleafs;
- leaf_p = &dleafs[numleafs];
- numleafs++;
-
- if( nummodels == 0 )
- {
- leaf_p->cluster = node->cluster;
- }
- else
- {
- // Submodels don't have clusters. If this isn't set to -1 here, then there
- // will be multiple leaves (albeit from different models) that reference
- // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide
- // won't work.
- leaf_p->cluster = -1;
- }
-
- leaf_p->contents = node->contents;
- leaf_p->area = node->area;
-
- // By default, assume the leaf can see the skybox.
- // VRAD will do the actual computation to see if it really can see the skybox
- leaf_p->flags = LEAF_FLAGS_SKY;
-
- //
- // write bounding box info
- //
- VECTOR_COPY (node->mins, leaf_p->mins);
- VECTOR_COPY (node->maxs, leaf_p->maxs);
-
- //
- // write the leafbrushes
- //
- leaf_p->firstleafbrush = numleafbrushes;
- for (b=node->brushlist ; b ; b=b->next)
- {
- if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
- Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES);
-
- brushnum = b->original - g_MainMap->mapbrushes;
- for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
- {
- if (dleafbrushes[i] == brushnum)
- break;
- }
-
- if (i == numleafbrushes)
- {
- dleafbrushes[numleafbrushes] = brushnum;
- numleafbrushes++;
- }
- }
- leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
-
- //
- // write the leaffaces
- //
- if (leaf_p->contents & CONTENTS_SOLID)
- return; // no leaffaces in solids
-
- leaf_p->firstleafface = numleaffaces;
-
- for (p = node->portals ; p ; p = p->next[s])
- {
- s = (p->nodes[1] == node);
- f = p->face[s];
- if (!f)
- continue; // not a visible portal
-
- EmitMarkFace (leaf_p, f);
- }
-
- // emit the detail faces
- for ( pList = node->leaffacelist; pList; pList = pList->pNext )
- {
- EmitMarkFace( leaf_p, pList->pFace );
- }
-
-
- leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
-}
-
-// per face plane - original face "side" list
-side_t *pOrigFaceSideList[MAX_MAP_PLANES];
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-int CreateOrigFace( face_t *f )
-{
- int i, j;
- dface_t *of;
- side_t *side;
- int vIndices[128];
- int eIndex[2];
- winding_t *pWinding;
-
- // not a real face!
- if( !f->w )
- return -1;
-
- // get the original face -- the "side"
- side = f->originalface;
-
- // get the original face winding
- if( !side->winding )
- {
- return -1;
- }
-
- //
- // get the next original face
- //
- if( numorigfaces >= MAX_MAP_FACES )
- Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
- of = &dorigfaces[numorigfaces];
- numorigfaces++;
-
- // set original face to -1 -- it is an origianl face!
- of->origFace = -1;
-
- //
- // add side to plane list
- //
- side->next = pOrigFaceSideList[f->planenum];
- pOrigFaceSideList[f->planenum] = side;
- side->origIndex = numorigfaces - 1;
-
- pWinding = CopyWinding( side->winding );
-
- //
- // plane info
- //
- of->planenum = side->planenum;
- if ( side->contents & CONTENTS_DETAIL )
- of->onNode = 0;
- else
- of->onNode = 1;
- of->side = side->planenum & 1;
-
- //
- // edge info
- //
- of->firstedge = numsurfedges;
- of->numedges = side->winding->numpoints;
-
- //
- // material info
- //
- of->texinfo = side->texinfo;
- of->dispinfo = f->dispinfo;
-
- //
- // save the vertices
- //
- for( i = 0; i < pWinding->numpoints; i++ )
- {
- //
- // compare vertices
- //
- vIndices[i] = GetVertexnum( pWinding->p[i] );
- }
-
- //
- // save off points -- as edges
- //
- for( i = 0; i < pWinding->numpoints; i++ )
- {
- //
- // look for matching edges first
- //
- eIndex[0] = vIndices[i];
- eIndex[1] = vIndices[(i+1)%pWinding->numpoints];
-
- for( j = firstmodeledge; j < numedges; j++ )
- {
- if( ( eIndex[0] == dedges[j].v[1] ) &&
- ( eIndex[1] == dedges[j].v[0] ) &&
- ( edgefaces[j][0]->contents == f->contents ) )
- {
- // check for multiple backward edges!! -- shouldn't have
- if( edgefaces[j][1] )
- continue;
-
- // set back edge
- edgefaces[j][1] = f;
-
- //
- // get next surface edge
- //
- if( numsurfedges >= MAX_MAP_SURFEDGES )
- Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
- dsurfedges[numsurfedges] = -j;
- numsurfedges++;
- break;
- }
- }
-
- if( j == numedges )
- {
- //
- // get next edge
- //
- AddEdge( eIndex[0], eIndex[1], f );
-
- //
- // get next surface edge
- //
- if( numsurfedges >= MAX_MAP_SURFEDGES )
- Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
- dsurfedges[numsurfedges] = ( numedges - 1 );
- numsurfedges++;
- }
- }
-
- // return the index
- return ( numorigfaces - 1 );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: search for a face within the origface list and return the index if
-// found
-// Input: f - the face to compare
-// Output: the index of the face it found, -1 if not found
-//-----------------------------------------------------------------------------
-int FindOrigFace( face_t *f )
-{
- int i;
- static int bClear = 0;
- side_t *pSide;
-
- //
- // initially clear the face side lists (per face plane)
- //
- if( !bClear )
- {
- for( i = 0; i < MAX_MAP_PLANES; i++ )
- {
- pOrigFaceSideList[i] = NULL;
- }
- bClear = 1;
- }
-
- //
- // compare the sides
- //
- for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next )
- {
- if( pSide == f->originalface )
- return pSide->origIndex;
- }
-
- // original face not found in list
- return -1;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: to find an the original face within the list of original faces, if
-// a match is not found then create a new origFace -- either way pass
-// back the index of the origface in the list
-// Input: f - face containing the original face information
-// Output: the index of the origface in the origface list
-//-----------------------------------------------------------------------------
-int FindOrCreateOrigFace( face_t *f )
-{
- int index;
-
- // check for an original face
- if( !f->originalface )
- return -1;
-
- //
- // find or create a orig face and return the index
- //
- index = FindOrigFace( f );
-
- if( index == -1 )
- return CreateOrigFace( f );
- else if( index == -2 )
- return -1;
-
- return index;
-}
-
-/*
-==================
-EmitFace
-==================
-*/
-void EmitFace( face_t *f, qboolean onNode )
-{
- dface_t *df;
- int i;
- int e;
-
-// void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack
-// SubdivideFaceBySubdivSize( f );
-
- // set initial output number
- f->outputnumber = -1;
-
- // degenerated
- if( f->numpoints < 3 )
- return;
-
- // not a final face
- if( f->merged || f->split[0] || f->split[1] )
- return;
-
- // don't emit NODRAW faces for runtime
- if ( texinfo[f->texinfo].flags & SURF_NODRAW )
- {
- // keep NODRAW terrain surfaces though
- if ( f->dispinfo == -1 )
- return;
- Warning("NODRAW on terrain surface!\n");
- }
-
- // save output number so leaffaces can use
- f->outputnumber = numfaces;
-
- //
- // get the next available .bsp face slot
- //
- if (numfaces >= MAX_MAP_FACES)
- Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
- df = &dfaces[numfaces];
-
- // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id
- dfaceids.AddToTail();
- dfaceids[numfaces].hammerfaceid = f->originalface->id;
-
- numfaces++;
-
- //
- // plane info - planenum is used by qlight, but not quake
- //
- df->planenum = f->planenum;
- df->onNode = onNode;
- df->side = f->planenum & 1;
-
- //
- // material info
- //
- df->texinfo = f->texinfo;
- df->dispinfo = f->dispinfo;
- df->smoothingGroups = f->smoothingGroups;
-
- // save the original "side"/face data
- df->origFace = FindOrCreateOrigFace( f );
- df->surfaceFogVolumeID = -1;
- dfacenodes[numfaces-1] = f->fogVolumeLeaf;
- if ( f->fogVolumeLeaf )
- {
- Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF );
- }
-
- //
- // edge info
- //
- df->firstedge = numsurfedges;
- df->numedges = f->numpoints;
-
- // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary
- if ( f->w )
- {
- df->area = WindingArea( f->w );
- }
- else
- {
- df->area = 0;
- }
-
- df->firstPrimID = f->firstPrimID;
- df->SetNumPrims( f->numPrims );
- df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled );
-
- //
- // save off points -- as edges
- //
- for( i = 0; i < f->numpoints; i++ )
- {
- //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
- e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
-
- if (numsurfedges >= MAX_MAP_SURFEDGES)
- Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
- dsurfedges[numsurfedges] = e;
- numsurfedges++;
- }
-
- // Create overlay face lists.
- side_t *pSide = f->originalface;
- if ( pSide )
- {
- int nOverlayCount = pSide->aOverlayIds.Count();
- if ( nOverlayCount > 0 )
- {
- Overlay_AddFaceToLists( ( numfaces - 1 ), pSide );
- }
-
- nOverlayCount = pSide->aWaterOverlayIds.Count();
- if ( nOverlayCount > 0 )
- {
- OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Emit all of the faces stored at the leaves (faces from detail brushes)
-//-----------------------------------------------------------------------------
-void EmitLeafFaces( face_t *pLeafFaceList )
-{
- face_t *f = pLeafFaceList;
- while ( f )
- {
- EmitFace( f, false );
- f = f->next;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Free the list of faces stored at the leaves
-//-----------------------------------------------------------------------------
-void FreeLeafFaces( face_t *pLeafFaceList )
-{
- int count = 0;
- face_t *f, *next;
-
- f = pLeafFaceList;
-
- while ( f )
- {
- next = f->next;
- FreeFace( f );
- f = next;
- count++;
- }
-}
-
-/*
-============
-EmitDrawingNode_r
-============
-*/
-int EmitDrawNode_r (node_t *node)
-{
- dnode_t *n;
- face_t *f;
- int i;
-
- if (node->planenum == PLANENUM_LEAF)
- {
- EmitLeaf (node);
- return -numleafs;
- }
-
- // emit a node
- if (numnodes == MAX_MAP_NODES)
- Error ("MAX_MAP_NODES");
- node->diskId = numnodes;
-
- n = &dnodes[numnodes];
- numnodes++;
-
- VECTOR_COPY (node->mins, n->mins);
- VECTOR_COPY (node->maxs, n->maxs);
-
- if (node->planenum & 1)
- Error ("WriteDrawNodes_r: odd planenum");
- n->planenum = node->planenum;
- n->firstface = numfaces;
- n->area = node->area;
-
- if (!node->faces)
- c_nofaces++;
- else
- c_facenodes++;
-
- for (f=node->faces ; f ; f=f->next)
- EmitFace (f, true);
-
- n->numfaces = numfaces - n->firstface;
-
-
- //
- // recursively output the other nodes
- //
- for (i=0 ; i<2 ; i++)
- {
- if (node->children[i]->planenum == PLANENUM_LEAF)
- {
- n->children[i] = -(numleafs + 1);
- EmitLeaf (node->children[i]);
- }
- else
- {
- n->children[i] = numnodes;
- EmitDrawNode_r (node->children[i]);
- }
- }
-
- return n - dnodes;
-}
-
-
-//=========================================================
-
-// This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red.
-// #define SCRATCHPAD_NO_SHADOW_FACES
-#if defined( SCRATCHPAD_NO_SHADOW_FACES )
- #include "scratchpad_helpers.h"
- IScratchPad3D *g_pPad;
-#endif
-
-
-void MarkNoShadowFaces()
-{
-#if defined( SCRATCHPAD_NO_SHADOW_FACES )
- g_pPad = ScratchPad3D_Create();
- ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) );
-
- for ( int iFace=0; iFace < numfaces; iFace++ )
- {
- dface_t *pFace = &dfaces[iFace];
-
- if ( !pFace->AreDynamicShadowsEnabled() )
- {
- ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) );
- ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) );
- ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) );
- }
- }
- g_pPad->Release();
-#endif
-}
-
-struct texinfomap_t
-{
- int refCount;
- int outputIndex;
-};
-struct texdatamap_t
-{
- int refCount;
- int outputIndex;
-};
-
-// Find the best used texinfo to remap this brush side
-int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap )
-{
- dbrushside_t &side = dbrushsides[sideIndex];
- // find one with the same flags & surfaceprops (even if the texture name is different)
- int sideTexFlags = texinfo[side.texinfo].flags;
- int sideTexData = texinfo[side.texinfo].texdata;
- int sideSurfaceProp = g_SurfaceProperties[sideTexData];
- for ( int j = 0; j < texinfo.Count(); j++ )
- {
- if ( pMap[j].refCount > 0 &&
- texinfo[j].flags == sideTexFlags &&
- g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp )
- {
- // found one
- return j;
- }
- }
-
- // can't find a better match
- return side.texinfo;
-}
-
-// Remove all unused texinfos and rebuild array
-void ComapctTexinfoArray( texinfomap_t *pMap )
-{
- CUtlVector<texinfo_t> old;
- old.CopyArray( texinfo.Base(), texinfo.Count() );
- texinfo.RemoveAll();
- int firstSky = -1;
- int first2DSky = -1;
- for ( int i = 0; i < old.Count(); i++ )
- {
- if ( !pMap[i].refCount )
- {
- pMap[i].outputIndex = -1;
- continue;
- }
- // only add one sky texinfo + one 2D sky texinfo
- if ( old[i].flags & SURF_SKY2D )
- {
- if ( first2DSky < 0 )
- {
- first2DSky = texinfo.AddToTail( old[i] );
- }
- pMap[i].outputIndex = first2DSky;
- continue;
- }
- if ( old[i].flags & SURF_SKY )
- {
- if ( firstSky < 0 )
- {
- firstSky = texinfo.AddToTail( old[i] );
- }
- pMap[i].outputIndex = firstSky;
- continue;
- }
- pMap[i].outputIndex = texinfo.AddToTail( old[i] );
- }
-}
-
-void CompactTexdataArray( texdatamap_t *pMap )
-{
- CUtlVector<char> oldStringData;
- oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() );
- g_TexDataStringData.RemoveAll();
- CUtlVector<int> oldStringTable;
- oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() );
- g_TexDataStringTable.RemoveAll();
- CUtlVector<dtexdata_t> oldTexData;
- oldTexData.CopyArray( dtexdata, numtexdata );
- // clear current table and rebuild
- numtexdata = 0;
- for ( int i = 0; i < oldTexData.Count(); i++ )
- {
- // unreferenced, note in map and skip
- if ( !pMap[i].refCount )
- {
- pMap[i].outputIndex = -1;
- continue;
- }
- pMap[i].outputIndex = numtexdata;
-
- // get old string and re-add to table
- const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]];
- int nameIndex = TexDataStringTable_AddOrFindString( pString );
- // copy old texdata and fixup with new name in compacted table
- dtexdata[numtexdata] = oldTexData[i];
- dtexdata[numtexdata].nameStringTableID = nameIndex;
- numtexdata++;
- }
-}
-
-void CompactTexinfos()
-{
- Msg("Compacting texture/material tables...\n");
- texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()];
- texdatamap_t *texdataMap = new texdatamap_t[numtexdata];
- memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() );
- memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata );
- int i;
- // get texinfos referenced by faces
- for ( i = 0; i < numfaces; i++ )
- {
- texinfoMap[dfaces[i].texinfo].refCount++;
- }
- // get texinfos referenced by brush sides
- for ( i = 0; i < numbrushsides; i++ )
- {
- // not referenced by any visible geometry
- Assert( dbrushsides[i].texinfo >= 0 );
- if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
- {
- dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap );
- // didn't find anything suitable, go ahead and reference it
- if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
- {
- texinfoMap[dbrushsides[i].texinfo].refCount++;
- }
- }
- }
- // get texinfos referenced by overlays
- for ( i = 0; i < g_nOverlayCount; i++ )
- {
- texinfoMap[g_Overlays[i].nTexInfo].refCount++;
- }
- for ( i = 0; i < numleafwaterdata; i++ )
- {
- if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
- {
- texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++;
- }
- }
- for ( i = 0; i < *pNumworldlights; i++ )
- {
- if ( dworldlights[i].texinfo >= 0 )
- {
- texinfoMap[dworldlights[i].texinfo].refCount++;
- }
- }
- for ( i = 0; i < g_nWaterOverlayCount; i++ )
- {
- if ( g_WaterOverlays[i].nTexInfo >= 0 )
- {
- texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++;
- }
- }
- // reference all used texdatas
- for ( i = 0; i < texinfo.Count(); i++ )
- {
- if ( texinfoMap[i].refCount > 0 )
- {
- texdataMap[texinfo[i].texdata].refCount++;
- }
- }
-
- int oldCount = texinfo.Count();
- int oldTexdataCount = numtexdata;
- int oldTexdataString = g_TexDataStringData.Count();
- ComapctTexinfoArray( texinfoMap );
- CompactTexdataArray( texdataMap );
- for ( i = 0; i < texinfo.Count(); i++ )
- {
- int mapIndex = texdataMap[texinfo[i].texdata].outputIndex;
- Assert( mapIndex >= 0 );
- texinfo[i].texdata = mapIndex;
- //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID );
- }
- // remap texinfos on faces
- for ( i = 0; i < numfaces; i++ )
- {
- Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 );
- dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex;
- }
- // remap texinfos on brushsides
- for ( i = 0; i < numbrushsides; i++ )
- {
- Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 );
- dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex;
- }
- // remap texinfos on overlays
- for ( i = 0; i < g_nOverlayCount; i++ )
- {
- g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex;
- }
- // remap leaf water data
- for ( i = 0; i < numleafwaterdata; i++ )
- {
- if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
- {
- dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex;
- }
- }
- // remap world lights
- for ( i = 0; i < *pNumworldlights; i++ )
- {
- if ( dworldlights[i].texinfo >= 0 )
- {
- dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex;
- }
- }
- // remap water overlays
- for ( i = 0; i < g_nWaterOverlayCount; i++ )
- {
- if ( g_WaterOverlays[i].nTexInfo >= 0 )
- {
- g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex;
- }
- }
-
- Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() );
- Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() );
-
- delete[] texinfoMap;
- delete[] texdataMap;
-}
-
-/*
-============
-WriteBSP
-============
-*/
-void WriteBSP (node_t *headnode, face_t *pLeafFaceList )
-{
- int i;
- int oldfaces;
- int oldorigfaces;
-
- c_nofaces = 0;
- c_facenodes = 0;
-
- qprintf ("--- WriteBSP ---\n");
-
- oldfaces = numfaces;
- oldorigfaces = numorigfaces;
-
- GetEdge2_InitOptimizedList();
- EmitLeafFaces( pLeafFaceList );
- dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
-
- // Only emit area portals for the main world.
- if( nummodels == 0 )
- {
- EmitAreaPortals (headnode);
- }
-
- //
- // add all displacement faces for the particular model
- //
- for( i = 0; i < nummapdispinfo; i++ )
- {
- int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] );
- if( entityIndex == entity_num )
- {
- EmitFaceVertexes( NULL, &mapdispinfo[i].face );
- EmitFace( &mapdispinfo[i].face, FALSE );
- }
- }
-
- EmitWaterVolumesForBSP( &dmodels[nummodels], headnode );
- qprintf ("%5i nodes with faces\n", c_facenodes);
- qprintf ("%5i nodes without faces\n", c_nofaces);
- qprintf ("%5i faces\n", numfaces-oldfaces);
- qprintf( "%5i original faces\n", numorigfaces-oldorigfaces );
-}
-
-
-
-//===========================================================
-
-/*
-============
-SetModelNumbers
-============
-*/
-void SetModelNumbers (void)
-{
- int i;
- int models;
- char value[10];
-
- models = 1;
- for (i=1 ; i<num_entities ; i++)
- {
- if (!entities[i].numbrushes)
- continue;
-
- if ( !IsFuncOccluder(i) )
- {
- sprintf (value, "*%i", models);
- models++;
- }
- else
- {
- sprintf (value, "");
- }
- SetKeyValue (&entities[i], "model", value);
- }
-}
-
-
-/*
-============
-SetLightStyles
-============
-*/
-#define MAX_SWITCHED_LIGHTS 32
-void SetLightStyles (void)
-{
- int stylenum;
- char *t;
- entity_t *e;
- int i, j;
- char value[10];
- char lighttargets[MAX_SWITCHED_LIGHTS][64];
-
-
- // any light that is controlled (has a targetname)
- // must have a unique style number generated for it
-
- stylenum = 0;
- for (i=1 ; i<num_entities ; i++)
- {
- e = &entities[i];
-
- t = ValueForKey (e, "classname");
- if (Q_strncasecmp (t, "light", 5))
- continue;
-
- // This is not true for dynamic lights
- if (!Q_strcasecmp (t, "light_dynamic"))
- continue;
-
- t = ValueForKey (e, "targetname");
- if (!t[0])
- continue;
-
- // find this targetname
- for (j=0 ; j<stylenum ; j++)
- if (!strcmp (lighttargets[j], t))
- break;
- if (j == stylenum)
- {
- if (stylenum == MAX_SWITCHED_LIGHTS)
- Error ("Too many switched lights (error at light %s), max = %d", t, MAX_SWITCHED_LIGHTS);
- strcpy (lighttargets[j], t);
- stylenum++;
- }
- sprintf (value, "%i", 32 + j);
- char *pCurrentStyle = ValueForKey( e, "style" );
- // the designer has set a default lightstyle as well as making the light switchable
- if ( pCurrentStyle )
- {
- int oldStyle = atoi(pCurrentStyle);
- if ( oldStyle != 0 )
- {
- // save off the default style so the game code can make a switchable copy of it
- SetKeyValue( e, "defaultstyle", pCurrentStyle );
- }
- }
- SetKeyValue (e, "style", value);
- }
-
-}
-
-/*
-============
-EmitBrushes
-============
-*/
-void EmitBrushes (void)
-{
- int i, j, bnum, s, x;
- dbrush_t *db;
- mapbrush_t *b;
- dbrushside_t *cp;
- Vector normal;
- vec_t dist;
- int planenum;
-
- numbrushsides = 0;
- numbrushes = g_MainMap->nummapbrushes;
-
- for (bnum=0 ; bnum<g_MainMap->nummapbrushes ; bnum++)
- {
- b = &g_MainMap->mapbrushes[bnum];
- db = &dbrushes[bnum];
-
- db->contents = b->contents;
- db->firstside = numbrushsides;
- db->numsides = b->numsides;
- for (j=0 ; j<b->numsides ; j++)
- {
- if (numbrushsides == MAX_MAP_BRUSHSIDES)
- Error ("MAX_MAP_BRUSHSIDES");
- cp = &dbrushsides[numbrushsides];
- numbrushsides++;
- cp->planenum = b->original_sides[j].planenum;
- cp->texinfo = b->original_sides[j].texinfo;
- if ( cp->texinfo == -1 )
- {
- cp->texinfo = g_MainMap->g_ClipTexinfo;
- }
- cp->bevel = b->original_sides[j].bevel;
- }
-
- // add any axis planes not contained in the brush to bevel off corners
- for (x=0 ; x<3 ; x++)
- for (s=-1 ; s<=1 ; s+=2)
- {
- // add the plane
- VectorCopy (vec3_origin, normal);
- normal[x] = s;
- if (s == -1)
- dist = -b->mins[x];
- else
- dist = b->maxs[x];
- planenum = g_MainMap->FindFloatPlane (normal, dist);
- for (i=0 ; i<b->numsides ; i++)
- if (b->original_sides[i].planenum == planenum)
- break;
- if (i == b->numsides)
- {
- if (numbrushsides >= MAX_MAP_BRUSHSIDES)
- Error ("MAX_MAP_BRUSHSIDES");
-
- dbrushsides[numbrushsides].planenum = planenum;
- dbrushsides[numbrushsides].texinfo =
- dbrushsides[numbrushsides-1].texinfo;
- numbrushsides++;
- db->numsides++;
- }
- }
- }
-}
-
-
-
-/*
-==================
-BeginBSPFile
-==================
-*/
-void BeginBSPFile (void)
-{
- // these values may actually be initialized
- // if the file existed when loaded, so clear them explicitly
- nummodels = 0;
- numfaces = 0;
- numnodes = 0;
- numbrushsides = 0;
- numvertexes = 0;
- numleaffaces = 0;
- numleafbrushes = 0;
- numsurfedges = 0;
-
- // edge 0 is not used, because 0 can't be negated
- numedges = 1;
-
- // leave vertex 0 as an error
- numvertexes = 1;
-
- // leave leaf 0 as an error
- numleafs = 1;
- dleafs[0].contents = CONTENTS_SOLID;
-
- // BUGBUG: This doesn't work!
-#if 0
- // make a default empty leaf for the tracing code
- memset( &dleafs[1], 0, sizeof(dleafs[1]) );
- dleafs[1].contents = CONTENTS_EMPTY;
-#endif
-}
-
-// We can't calculate this properly until vvis (since we need vis to do this), so we set
-// to zero everywhere by default.
-static void ClearDistToClosestWater( void )
-{
- int i;
- for( i = 0; i < numleafs; i++ )
- {
- g_LeafMinDistToWater[i] = 0;
- }
-}
-
-
-void DiscoverMacroTextures()
-{
- CUtlDict<int,int> tempDict;
-
- g_FaceMacroTextureInfos.SetSize( numfaces );
- for ( int iFace=0; iFace < numfaces; iFace++ )
- {
- texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo];
- if ( pTexInfo->texdata < 0 )
- continue;
-
- dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata];
- const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ];
-
- MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
-
- const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" );
- if ( pMacroTextureName )
- {
- if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() )
- {
- Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName );
- tempDict.Insert( pMacroTextureName, 0 );
- }
-
- int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName );
- g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID;
- }
- else
- {
- g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF;
- }
- }
-}
-
-
-// Make sure that we have a water lod control entity if we have water in the map.
-void EnsurePresenceOfWaterLODControlEntity( void )
-{
- extern bool g_bHasWater;
- if( !g_bHasWater )
- {
- // Don't bother if there isn't any water in the map.
- return;
- }
- for( int i=0; i < num_entities; i++ )
- {
- entity_t *e = &entities[i];
-
- const char *pClassName = ValueForKey( e, "classname" );
- if( !Q_stricmp( pClassName, "water_lod_control" ) )
- {
- // Found one!!!!
- return;
- }
- }
-
- // None found, add one.
- Warning( "Water found with no water_lod_control entity, creating a default one.\n" );
-
- entity_t *mapent = &entities[num_entities];
- num_entities++;
- memset(mapent, 0, sizeof(*mapent));
- mapent->firstbrush = g_MainMap->nummapbrushes;
- mapent->numbrushes = 0;
-
- SetKeyValue( mapent, "classname", "water_lod_control" );
- SetKeyValue( mapent, "cheapwaterstartdistance", "1000" );
- SetKeyValue( mapent, "cheapwaterenddistance", "2000" );
-}
-
-
-/*
-============
-EndBSPFile
-============
-*/
-void EndBSPFile (void)
-{
- // Mark noshadow faces.
- MarkNoShadowFaces();
-
- EmitBrushes ();
- EmitPlanes ();
-
- // stick flat normals at the verts
- SaveVertexNormals();
-
- // Figure out lightmap extents for all faces.
- UpdateAllFaceLightmapExtents();
-
- // Generate geometry and lightmap alpha for displacements.
- EmitDispLMAlphaAndNeighbors();
-
- // Emit overlay data.
- Overlay_EmitOverlayFaces();
- OverlayTransition_EmitOverlayFaces();
-
- // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs)
- EmitPhysCollision();
-
- // We can't calculate this properly until vvis (since we need vis to do this), so we set
- // to zero everywhere by default.
- ClearDistToClosestWater();
-
- // Emit static props found in the .vmf file
- EmitStaticProps();
-
- // Place detail props found in .vmf and based on material properties
- EmitDetailObjects();
-
- // Compute bounds after creating disp info because we need to reference it
- ComputeBoundsNoSkybox();
-
- // Make sure that we have a water lod control eneity if we have water in the map.
- EnsurePresenceOfWaterLODControlEntity();
-
- // Doing this here because stuff about may filter out entities
- UnparseEntities ();
-
- // remove unused texinfos
- CompactTexinfos();
-
- // Figure out which faces want macro textures.
- DiscoverMacroTextures();
-
- char targetPath[1024];
- GetPlatformMapPath( source, targetPath, g_nDXLevel, 1024 );
- Msg ("Writing %s\n", targetPath);
- WriteBSPFile (targetPath);
-}
-
-
-/*
-==================
-BeginModel
-==================
-*/
-int firstmodleaf;
-void BeginModel (void)
-{
- dmodel_t *mod;
- int start, end;
- mapbrush_t *b;
- int j;
- entity_t *e;
- Vector mins, maxs;
-
- if (nummodels == MAX_MAP_MODELS)
- Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS);
- mod = &dmodels[nummodels];
-
- mod->firstface = numfaces;
-
- firstmodleaf = numleafs;
- firstmodeledge = numedges;
- firstmodelface = numfaces;
-
- //
- // bound the brushes
- //
- e = &entities[entity_num];
-
- start = e->firstbrush;
- end = start + e->numbrushes;
- ClearBounds (mins, maxs);
-
- for (j=start ; j<end ; j++)
- {
- b = &g_MainMap->mapbrushes[j];
- if (!b->numsides)
- continue; // not a real brush (origin brush)
- AddPointToBounds (b->mins, mins, maxs);
- AddPointToBounds (b->maxs, mins, maxs);
- }
-
- VectorCopy (mins, mod->mins);
- VectorCopy (maxs, mod->maxs);
-}
-
-
-/*
-==================
-EndModel
-==================
-*/
-void EndModel (void)
-{
- dmodel_t *mod;
-
- mod = &dmodels[nummodels];
-
- mod->numfaces = numfaces - mod->firstface;
-
- nummodels++;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// figure out which leaf a point is in
-//-----------------------------------------------------------------------------
-static int PointLeafnum_r (const Vector& p, int num)
-{
- float d;
- while (num >= 0)
- {
- dnode_t* node = dnodes + num;
- dplane_t* plane = dplanes + node->planenum;
-
- if (plane->type < 3)
- d = p[plane->type] - plane->dist;
- else
- d = DotProduct (plane->normal, p) - plane->dist;
- if (d < 0)
- num = node->children[1];
- else
- num = node->children[0];
- }
-
- return -1 - num;
-}
-
-int PointLeafnum ( dmodel_t* pModel, const Vector& p )
-{
- return PointLeafnum_r (p, pModel->headnode);
-}
-
-
-//-----------------------------------------------------------------------------
-// Adds a noew to the bounding box
-//-----------------------------------------------------------------------------
-static void AddNodeToBounds(int node, CUtlVector<int>& skipAreas, Vector& mins, Vector& maxs)
-{
- // not a leaf
- if (node >= 0)
- {
- AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs );
- AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs );
- }
- else
- {
- int leaf = - 1 - node;
-
- // Don't bother with solid leaves
- if (dleafs[leaf].contents & CONTENTS_SOLID)
- return;
-
- // Skip 3D skybox
- int i;
- for ( i = skipAreas.Count(); --i >= 0; )
- {
- if (dleafs[leaf].area == skipAreas[i])
- return;
- }
-
- unsigned int firstface = dleafs[leaf].firstleafface;
- for ( i = 0; i < dleafs[leaf].numleaffaces; ++i )
- {
- unsigned int face = dleaffaces[ firstface + i ];
-
- // Skip skyboxes + nodraw
- texinfo_t& tex = texinfo[dfaces[face].texinfo];
- if (tex.flags & (SURF_SKY | SURF_NODRAW))
- continue;
-
- unsigned int firstedge = dfaces[face].firstedge;
- Assert( firstedge >= 0 );
-
- for (int j = 0; j < dfaces[face].numedges; ++j)
- {
- Assert( firstedge+j < numsurfedges );
- int edge = abs(dsurfedges[firstedge+j]);
- dedge_t* pEdge = &dedges[edge];
- Assert( pEdge->v[0] >= 0 );
- Assert( pEdge->v[1] >= 0 );
- AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs);
- AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs);
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Check to see if a displacement lives in any leaves that are not
-// in the 3d skybox
-//-----------------------------------------------------------------------------
-bool IsBoxInsideWorld( int node, CUtlVector<int> &skipAreas, const Vector &vecMins, const Vector &vecMaxs )
-{
- while( 1 )
- {
- // leaf
- if (node < 0)
- {
- // get the leaf
- int leaf = - 1 - node;
-
- // Don't bother with solid leaves
- if (dleafs[leaf].contents & CONTENTS_SOLID)
- return false;
-
- // Skip 3D skybox
- int i;
- for ( i = skipAreas.Count(); --i >= 0; )
- {
- if ( dleafs[leaf].area == skipAreas[i] )
- return false;
- }
-
- return true;
- }
-
- //
- // get displacement bounding box position relative to the node plane
- //
- dnode_t *pNode = &dnodes[ node ];
- dplane_t *pPlane = &dplanes[ pNode->planenum ];
-
- int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane );
-
- // front side
- if( sideResult == 1 )
- {
- node = pNode->children[0];
- }
- // back side
- else if( sideResult == 2 )
- {
- node = pNode->children[1];
- }
- //split
- else
- {
- if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) )
- return true;
-
- node = pNode->children[1];
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Adds the displacement surfaces in the world to the bounds
-//-----------------------------------------------------------------------------
-void AddDispsToBounds( int nHeadNode, CUtlVector<int>& skipAreas, Vector &vecMins, Vector &vecMaxs )
-{
- Vector vecDispMins, vecDispMaxs;
-
- // first determine how many displacement surfaces there will be per leaf
- int i;
- for ( i = 0; i < g_dispinfo.Count(); ++i )
- {
- ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs );
- if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) )
- {
- AddPointToBounds( vecDispMins, vecMins, vecMaxs );
- AddPointToBounds( vecDispMaxs, vecMins, vecMaxs );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
-//-----------------------------------------------------------------------------
-void ComputeBoundsNoSkybox( )
-{
- // Iterate over all world leaves, skip those which are part of skybox
- Vector mins, maxs;
- ClearBounds (mins, maxs);
- AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
- AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
-
- // Add the bounds to the worldspawn data
- for (int i = 0; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!strcmp(pEntity, "worldspawn"))
- {
- char string[32];
- sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]);
- SetKeyValue (&entities[i], "world_mins", string);
- sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]);
- SetKeyValue (&entities[i], "world_maxs", string);
- break;
- }
- }
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "utlvector.h"
+#include "faces.h"
+#include "builddisp.h"
+#include "tier1/strtools.h"
+#include "utilmatlib.h"
+#include "utldict.h"
+#include "map.h"
+
+int c_nofaces;
+int c_facenodes;
+
+// NOTE: This is a global used to link faces back to the tree node/portals they came from
+// it's used when filling water volumes
+node_t *dfacenodes[MAX_MAP_FACES];
+
+
+/*
+=========================================================
+
+ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
+
+=========================================================
+*/
+
+void EmitFaceVertexes (face_t **list, face_t *f);
+void AssignOccluderAreas();
+
+/*
+============
+EmitPlanes
+
+There is no oportunity to discard planes, because all of the original
+brushes will be saved in the map.
+============
+*/
+void EmitPlanes (void)
+{
+ int i;
+ dplane_t *dp;
+ plane_t *mp;
+ int planetranslate[MAX_MAP_PLANES];
+
+ mp = g_MainMap->mapplanes;
+ for (i=0 ; i<g_MainMap->nummapplanes ; i++, mp++)
+ {
+ dp = &dplanes[numplanes];
+ planetranslate[i] = numplanes;
+ VectorCopy ( mp->normal, dp->normal);
+ dp->dist = mp->dist;
+ dp->type = mp->type;
+ numplanes++;
+ }
+}
+
+
+//========================================================
+
+void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
+{
+ int i;
+ int facenum;
+
+ while (f->merged)
+ f = f->merged;
+
+ if (f->split[0])
+ {
+ EmitMarkFace (leaf_p, f->split[0]);
+ EmitMarkFace (leaf_p, f->split[1]);
+ return;
+ }
+
+ facenum = f->outputnumber;
+ if (facenum == -1)
+ return; // degenerate face
+
+ if (facenum < 0 || facenum >= numfaces)
+ Error ("Bad leafface");
+ for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
+ if (dleaffaces[i] == facenum)
+ break; // merged out face
+ if (i == numleaffaces)
+ {
+ if (numleaffaces >= MAX_MAP_LEAFFACES)
+ Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES);
+
+ dleaffaces[numleaffaces] = facenum;
+ numleaffaces++;
+ }
+
+}
+
+
+/*
+==================
+EmitLeaf
+==================
+*/
+void EmitLeaf (node_t *node)
+{
+ dleaf_t *leaf_p;
+ portal_t *p;
+ int s;
+ face_t *f;
+ bspbrush_t *b;
+ int i;
+ int brushnum;
+ leafface_t *pList;
+
+ // emit a leaf
+ if (numleafs >= MAX_MAP_LEAFS)
+ Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS);
+
+ node->diskId = numleafs;
+ leaf_p = &dleafs[numleafs];
+ numleafs++;
+
+ if( nummodels == 0 )
+ {
+ leaf_p->cluster = node->cluster;
+ }
+ else
+ {
+ // Submodels don't have clusters. If this isn't set to -1 here, then there
+ // will be multiple leaves (albeit from different models) that reference
+ // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide
+ // won't work.
+ leaf_p->cluster = -1;
+ }
+
+ leaf_p->contents = node->contents;
+ leaf_p->area = node->area;
+
+ // By default, assume the leaf can see the skybox.
+ // VRAD will do the actual computation to see if it really can see the skybox
+ leaf_p->flags = LEAF_FLAGS_SKY;
+
+ //
+ // write bounding box info
+ //
+ VECTOR_COPY (node->mins, leaf_p->mins);
+ VECTOR_COPY (node->maxs, leaf_p->maxs);
+
+ //
+ // write the leafbrushes
+ //
+ leaf_p->firstleafbrush = numleafbrushes;
+ for (b=node->brushlist ; b ; b=b->next)
+ {
+ if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
+ Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES);
+
+ brushnum = b->original - g_MainMap->mapbrushes;
+ for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
+ {
+ if (dleafbrushes[i] == brushnum)
+ break;
+ }
+
+ if (i == numleafbrushes)
+ {
+ dleafbrushes[numleafbrushes] = brushnum;
+ numleafbrushes++;
+ }
+ }
+ leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
+
+ //
+ // write the leaffaces
+ //
+ if (leaf_p->contents & CONTENTS_SOLID)
+ return; // no leaffaces in solids
+
+ leaf_p->firstleafface = numleaffaces;
+
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ f = p->face[s];
+ if (!f)
+ continue; // not a visible portal
+
+ EmitMarkFace (leaf_p, f);
+ }
+
+ // emit the detail faces
+ for ( pList = node->leaffacelist; pList; pList = pList->pNext )
+ {
+ EmitMarkFace( leaf_p, pList->pFace );
+ }
+
+
+ leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
+}
+
+// per face plane - original face "side" list
+side_t *pOrigFaceSideList[MAX_MAP_PLANES];
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CreateOrigFace( face_t *f )
+{
+ int i, j;
+ dface_t *of;
+ side_t *side;
+ int vIndices[128];
+ int eIndex[2];
+ winding_t *pWinding;
+
+ // not a real face!
+ if( !f->w )
+ return -1;
+
+ // get the original face -- the "side"
+ side = f->originalface;
+
+ // get the original face winding
+ if( !side->winding )
+ {
+ return -1;
+ }
+
+ //
+ // get the next original face
+ //
+ if( numorigfaces >= MAX_MAP_FACES )
+ Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
+ of = &dorigfaces[numorigfaces];
+ numorigfaces++;
+
+ // set original face to -1 -- it is an origianl face!
+ of->origFace = -1;
+
+ //
+ // add side to plane list
+ //
+ side->next = pOrigFaceSideList[f->planenum];
+ pOrigFaceSideList[f->planenum] = side;
+ side->origIndex = numorigfaces - 1;
+
+ pWinding = CopyWinding( side->winding );
+
+ //
+ // plane info
+ //
+ of->planenum = side->planenum;
+ if ( side->contents & CONTENTS_DETAIL )
+ of->onNode = 0;
+ else
+ of->onNode = 1;
+ of->side = side->planenum & 1;
+
+ //
+ // edge info
+ //
+ of->firstedge = numsurfedges;
+ of->numedges = side->winding->numpoints;
+
+ //
+ // material info
+ //
+ of->texinfo = side->texinfo;
+ of->dispinfo = f->dispinfo;
+
+ //
+ // save the vertices
+ //
+ for( i = 0; i < pWinding->numpoints; i++ )
+ {
+ //
+ // compare vertices
+ //
+ vIndices[i] = GetVertexnum( pWinding->p[i] );
+ }
+
+ //
+ // save off points -- as edges
+ //
+ for( i = 0; i < pWinding->numpoints; i++ )
+ {
+ //
+ // look for matching edges first
+ //
+ eIndex[0] = vIndices[i];
+ eIndex[1] = vIndices[(i+1)%pWinding->numpoints];
+
+ for( j = firstmodeledge; j < numedges; j++ )
+ {
+ if( ( eIndex[0] == dedges[j].v[1] ) &&
+ ( eIndex[1] == dedges[j].v[0] ) &&
+ ( edgefaces[j][0]->contents == f->contents ) )
+ {
+ // check for multiple backward edges!! -- shouldn't have
+ if( edgefaces[j][1] )
+ continue;
+
+ // set back edge
+ edgefaces[j][1] = f;
+
+ //
+ // get next surface edge
+ //
+ if( numsurfedges >= MAX_MAP_SURFEDGES )
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = -j;
+ numsurfedges++;
+ break;
+ }
+ }
+
+ if( j == numedges )
+ {
+ //
+ // get next edge
+ //
+ AddEdge( eIndex[0], eIndex[1], f );
+
+ //
+ // get next surface edge
+ //
+ if( numsurfedges >= MAX_MAP_SURFEDGES )
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = ( numedges - 1 );
+ numsurfedges++;
+ }
+ }
+
+ // return the index
+ return ( numorigfaces - 1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: search for a face within the origface list and return the index if
+// found
+// Input: f - the face to compare
+// Output: the index of the face it found, -1 if not found
+//-----------------------------------------------------------------------------
+int FindOrigFace( face_t *f )
+{
+ int i;
+ static int bClear = 0;
+ side_t *pSide;
+
+ //
+ // initially clear the face side lists (per face plane)
+ //
+ if( !bClear )
+ {
+ for( i = 0; i < MAX_MAP_PLANES; i++ )
+ {
+ pOrigFaceSideList[i] = NULL;
+ }
+ bClear = 1;
+ }
+
+ //
+ // compare the sides
+ //
+ for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next )
+ {
+ if( pSide == f->originalface )
+ return pSide->origIndex;
+ }
+
+ // original face not found in list
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: to find an the original face within the list of original faces, if
+// a match is not found then create a new origFace -- either way pass
+// back the index of the origface in the list
+// Input: f - face containing the original face information
+// Output: the index of the origface in the origface list
+//-----------------------------------------------------------------------------
+int FindOrCreateOrigFace( face_t *f )
+{
+ int index;
+
+ // check for an original face
+ if( !f->originalface )
+ return -1;
+
+ //
+ // find or create a orig face and return the index
+ //
+ index = FindOrigFace( f );
+
+ if( index == -1 )
+ return CreateOrigFace( f );
+ else if( index == -2 )
+ return -1;
+
+ return index;
+}
+
+/*
+==================
+EmitFace
+==================
+*/
+void EmitFace( face_t *f, qboolean onNode )
+{
+ dface_t *df;
+ int i;
+ int e;
+
+// void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack
+// SubdivideFaceBySubdivSize( f );
+
+ // set initial output number
+ f->outputnumber = -1;
+
+ // degenerated
+ if( f->numpoints < 3 )
+ return;
+
+ // not a final face
+ if( f->merged || f->split[0] || f->split[1] )
+ return;
+
+ // don't emit NODRAW faces for runtime
+ if ( texinfo[f->texinfo].flags & SURF_NODRAW )
+ {
+ // keep NODRAW terrain surfaces though
+ if ( f->dispinfo == -1 )
+ return;
+ Warning("NODRAW on terrain surface!\n");
+ }
+
+ // save output number so leaffaces can use
+ f->outputnumber = numfaces;
+
+ //
+ // get the next available .bsp face slot
+ //
+ if (numfaces >= MAX_MAP_FACES)
+ Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
+ df = &dfaces[numfaces];
+
+ // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id
+ dfaceids.AddToTail();
+ dfaceids[numfaces].hammerfaceid = f->originalface->id;
+
+ numfaces++;
+
+ //
+ // plane info - planenum is used by qlight, but not quake
+ //
+ df->planenum = f->planenum;
+ df->onNode = onNode;
+ df->side = f->planenum & 1;
+
+ //
+ // material info
+ //
+ df->texinfo = f->texinfo;
+ df->dispinfo = f->dispinfo;
+ df->smoothingGroups = f->smoothingGroups;
+
+ // save the original "side"/face data
+ df->origFace = FindOrCreateOrigFace( f );
+ df->surfaceFogVolumeID = -1;
+ dfacenodes[numfaces-1] = f->fogVolumeLeaf;
+ if ( f->fogVolumeLeaf )
+ {
+ Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF );
+ }
+
+ //
+ // edge info
+ //
+ df->firstedge = numsurfedges;
+ df->numedges = f->numpoints;
+
+ // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary
+ if ( f->w )
+ {
+ df->area = WindingArea( f->w );
+ }
+ else
+ {
+ df->area = 0;
+ }
+
+ df->firstPrimID = f->firstPrimID;
+ df->SetNumPrims( f->numPrims );
+ df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled );
+
+ //
+ // save off points -- as edges
+ //
+ for( i = 0; i < f->numpoints; i++ )
+ {
+ //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
+ e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
+
+ if (numsurfedges >= MAX_MAP_SURFEDGES)
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = e;
+ numsurfedges++;
+ }
+
+ // Create overlay face lists.
+ side_t *pSide = f->originalface;
+ if ( pSide )
+ {
+ int nOverlayCount = pSide->aOverlayIds.Count();
+ if ( nOverlayCount > 0 )
+ {
+ Overlay_AddFaceToLists( ( numfaces - 1 ), pSide );
+ }
+
+ nOverlayCount = pSide->aWaterOverlayIds.Count();
+ if ( nOverlayCount > 0 )
+ {
+ OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Emit all of the faces stored at the leaves (faces from detail brushes)
+//-----------------------------------------------------------------------------
+void EmitLeafFaces( face_t *pLeafFaceList )
+{
+ face_t *f = pLeafFaceList;
+ while ( f )
+ {
+ EmitFace( f, false );
+ f = f->next;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Free the list of faces stored at the leaves
+//-----------------------------------------------------------------------------
+void FreeLeafFaces( face_t *pLeafFaceList )
+{
+ int count = 0;
+ face_t *f, *next;
+
+ f = pLeafFaceList;
+
+ while ( f )
+ {
+ next = f->next;
+ FreeFace( f );
+ f = next;
+ count++;
+ }
+}
+
+/*
+============
+EmitDrawingNode_r
+============
+*/
+int EmitDrawNode_r (node_t *node)
+{
+ dnode_t *n;
+ face_t *f;
+ int i;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ EmitLeaf (node);
+ return -numleafs;
+ }
+
+ // emit a node
+ if (numnodes == MAX_MAP_NODES)
+ Error ("MAX_MAP_NODES");
+ node->diskId = numnodes;
+
+ n = &dnodes[numnodes];
+ numnodes++;
+
+ VECTOR_COPY (node->mins, n->mins);
+ VECTOR_COPY (node->maxs, n->maxs);
+
+ if (node->planenum & 1)
+ Error ("WriteDrawNodes_r: odd planenum");
+ n->planenum = node->planenum;
+ n->firstface = numfaces;
+ n->area = node->area;
+
+ if (!node->faces)
+ c_nofaces++;
+ else
+ c_facenodes++;
+
+ for (f=node->faces ; f ; f=f->next)
+ EmitFace (f, true);
+
+ n->numfaces = numfaces - n->firstface;
+
+
+ //
+ // recursively output the other nodes
+ //
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i]->planenum == PLANENUM_LEAF)
+ {
+ n->children[i] = -(numleafs + 1);
+ EmitLeaf (node->children[i]);
+ }
+ else
+ {
+ n->children[i] = numnodes;
+ EmitDrawNode_r (node->children[i]);
+ }
+ }
+
+ return n - dnodes;
+}
+
+
+//=========================================================
+
+// This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red.
+// #define SCRATCHPAD_NO_SHADOW_FACES
+#if defined( SCRATCHPAD_NO_SHADOW_FACES )
+ #include "scratchpad_helpers.h"
+ IScratchPad3D *g_pPad;
+#endif
+
+
+void MarkNoShadowFaces()
+{
+#if defined( SCRATCHPAD_NO_SHADOW_FACES )
+ g_pPad = ScratchPad3D_Create();
+ ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) );
+
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ dface_t *pFace = &dfaces[iFace];
+
+ if ( !pFace->AreDynamicShadowsEnabled() )
+ {
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) );
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) );
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) );
+ }
+ }
+ g_pPad->Release();
+#endif
+}
+
+struct texinfomap_t
+{
+ int refCount;
+ int outputIndex;
+};
+struct texdatamap_t
+{
+ int refCount;
+ int outputIndex;
+};
+
+// Find the best used texinfo to remap this brush side
+int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap )
+{
+ dbrushside_t &side = dbrushsides[sideIndex];
+ // find one with the same flags & surfaceprops (even if the texture name is different)
+ int sideTexFlags = texinfo[side.texinfo].flags;
+ int sideTexData = texinfo[side.texinfo].texdata;
+ int sideSurfaceProp = g_SurfaceProperties[sideTexData];
+ for ( int j = 0; j < texinfo.Count(); j++ )
+ {
+ if ( pMap[j].refCount > 0 &&
+ texinfo[j].flags == sideTexFlags &&
+ g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp )
+ {
+ // found one
+ return j;
+ }
+ }
+
+ // can't find a better match
+ return side.texinfo;
+}
+
+// Remove all unused texinfos and rebuild array
+void ComapctTexinfoArray( texinfomap_t *pMap )
+{
+ CUtlVector<texinfo_t> old;
+ old.CopyArray( texinfo.Base(), texinfo.Count() );
+ texinfo.RemoveAll();
+ int firstSky = -1;
+ int first2DSky = -1;
+ for ( int i = 0; i < old.Count(); i++ )
+ {
+ if ( !pMap[i].refCount )
+ {
+ pMap[i].outputIndex = -1;
+ continue;
+ }
+ // only add one sky texinfo + one 2D sky texinfo
+ if ( old[i].flags & SURF_SKY2D )
+ {
+ if ( first2DSky < 0 )
+ {
+ first2DSky = texinfo.AddToTail( old[i] );
+ }
+ pMap[i].outputIndex = first2DSky;
+ continue;
+ }
+ if ( old[i].flags & SURF_SKY )
+ {
+ if ( firstSky < 0 )
+ {
+ firstSky = texinfo.AddToTail( old[i] );
+ }
+ pMap[i].outputIndex = firstSky;
+ continue;
+ }
+ pMap[i].outputIndex = texinfo.AddToTail( old[i] );
+ }
+}
+
+void CompactTexdataArray( texdatamap_t *pMap )
+{
+ CUtlVector<char> oldStringData;
+ oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() );
+ g_TexDataStringData.RemoveAll();
+ CUtlVector<int> oldStringTable;
+ oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() );
+ g_TexDataStringTable.RemoveAll();
+ CUtlVector<dtexdata_t> oldTexData;
+ oldTexData.CopyArray( dtexdata, numtexdata );
+ // clear current table and rebuild
+ numtexdata = 0;
+ for ( int i = 0; i < oldTexData.Count(); i++ )
+ {
+ // unreferenced, note in map and skip
+ if ( !pMap[i].refCount )
+ {
+ pMap[i].outputIndex = -1;
+ continue;
+ }
+ pMap[i].outputIndex = numtexdata;
+
+ // get old string and re-add to table
+ const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]];
+ int nameIndex = TexDataStringTable_AddOrFindString( pString );
+ // copy old texdata and fixup with new name in compacted table
+ dtexdata[numtexdata] = oldTexData[i];
+ dtexdata[numtexdata].nameStringTableID = nameIndex;
+ numtexdata++;
+ }
+}
+
+void CompactTexinfos()
+{
+ Msg("Compacting texture/material tables...\n");
+ texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()];
+ texdatamap_t *texdataMap = new texdatamap_t[numtexdata];
+ memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() );
+ memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata );
+ int i;
+ // get texinfos referenced by faces
+ for ( i = 0; i < numfaces; i++ )
+ {
+ texinfoMap[dfaces[i].texinfo].refCount++;
+ }
+ // get texinfos referenced by brush sides
+ for ( i = 0; i < numbrushsides; i++ )
+ {
+ // not referenced by any visible geometry
+ Assert( dbrushsides[i].texinfo >= 0 );
+ if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
+ {
+ dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap );
+ // didn't find anything suitable, go ahead and reference it
+ if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
+ {
+ texinfoMap[dbrushsides[i].texinfo].refCount++;
+ }
+ }
+ }
+ // get texinfos referenced by overlays
+ for ( i = 0; i < g_nOverlayCount; i++ )
+ {
+ texinfoMap[g_Overlays[i].nTexInfo].refCount++;
+ }
+ for ( i = 0; i < numleafwaterdata; i++ )
+ {
+ if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
+ {
+ texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++;
+ }
+ }
+ for ( i = 0; i < *pNumworldlights; i++ )
+ {
+ if ( dworldlights[i].texinfo >= 0 )
+ {
+ texinfoMap[dworldlights[i].texinfo].refCount++;
+ }
+ }
+ for ( i = 0; i < g_nWaterOverlayCount; i++ )
+ {
+ if ( g_WaterOverlays[i].nTexInfo >= 0 )
+ {
+ texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++;
+ }
+ }
+ // reference all used texdatas
+ for ( i = 0; i < texinfo.Count(); i++ )
+ {
+ if ( texinfoMap[i].refCount > 0 )
+ {
+ texdataMap[texinfo[i].texdata].refCount++;
+ }
+ }
+
+ int oldCount = texinfo.Count();
+ int oldTexdataCount = numtexdata;
+ int oldTexdataString = g_TexDataStringData.Count();
+ ComapctTexinfoArray( texinfoMap );
+ CompactTexdataArray( texdataMap );
+ for ( i = 0; i < texinfo.Count(); i++ )
+ {
+ int mapIndex = texdataMap[texinfo[i].texdata].outputIndex;
+ Assert( mapIndex >= 0 );
+ texinfo[i].texdata = mapIndex;
+ //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID );
+ }
+ // remap texinfos on faces
+ for ( i = 0; i < numfaces; i++ )
+ {
+ Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 );
+ dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex;
+ }
+ // remap texinfos on brushsides
+ for ( i = 0; i < numbrushsides; i++ )
+ {
+ Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 );
+ dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex;
+ }
+ // remap texinfos on overlays
+ for ( i = 0; i < g_nOverlayCount; i++ )
+ {
+ g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex;
+ }
+ // remap leaf water data
+ for ( i = 0; i < numleafwaterdata; i++ )
+ {
+ if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
+ {
+ dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex;
+ }
+ }
+ // remap world lights
+ for ( i = 0; i < *pNumworldlights; i++ )
+ {
+ if ( dworldlights[i].texinfo >= 0 )
+ {
+ dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex;
+ }
+ }
+ // remap water overlays
+ for ( i = 0; i < g_nWaterOverlayCount; i++ )
+ {
+ if ( g_WaterOverlays[i].nTexInfo >= 0 )
+ {
+ g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex;
+ }
+ }
+
+ Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() );
+ Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() );
+
+ delete[] texinfoMap;
+ delete[] texdataMap;
+}
+
+/*
+============
+WriteBSP
+============
+*/
+void WriteBSP (node_t *headnode, face_t *pLeafFaceList )
+{
+ int i;
+ int oldfaces;
+ int oldorigfaces;
+
+ c_nofaces = 0;
+ c_facenodes = 0;
+
+ qprintf ("--- WriteBSP ---\n");
+
+ oldfaces = numfaces;
+ oldorigfaces = numorigfaces;
+
+ GetEdge2_InitOptimizedList();
+ EmitLeafFaces( pLeafFaceList );
+ dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
+
+ // Only emit area portals for the main world.
+ if( nummodels == 0 )
+ {
+ EmitAreaPortals (headnode);
+ }
+
+ //
+ // add all displacement faces for the particular model
+ //
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] );
+ if( entityIndex == entity_num )
+ {
+ EmitFaceVertexes( NULL, &mapdispinfo[i].face );
+ EmitFace( &mapdispinfo[i].face, FALSE );
+ }
+ }
+
+ EmitWaterVolumesForBSP( &dmodels[nummodels], headnode );
+ qprintf ("%5i nodes with faces\n", c_facenodes);
+ qprintf ("%5i nodes without faces\n", c_nofaces);
+ qprintf ("%5i faces\n", numfaces-oldfaces);
+ qprintf( "%5i original faces\n", numorigfaces-oldorigfaces );
+}
+
+
+
+//===========================================================
+
+/*
+============
+SetModelNumbers
+============
+*/
+void SetModelNumbers (void)
+{
+ int i;
+ int models;
+ char value[10];
+
+ models = 1;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if (!entities[i].numbrushes)
+ continue;
+
+ if ( !IsFuncOccluder(i) )
+ {
+ sprintf (value, "*%i", models);
+ models++;
+ }
+ else
+ {
+ sprintf (value, "");
+ }
+ SetKeyValue (&entities[i], "model", value);
+ }
+}
+
+
+/*
+============
+SetLightStyles
+============
+*/
+#define MAX_SWITCHED_LIGHTS 32
+void SetLightStyles (void)
+{
+ int stylenum;
+ char *t;
+ entity_t *e;
+ int i, j;
+ char value[10];
+ char lighttargets[MAX_SWITCHED_LIGHTS][64];
+
+
+ // any light that is controlled (has a targetname)
+ // must have a unique style number generated for it
+
+ stylenum = 0;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ e = &entities[i];
+
+ t = ValueForKey (e, "classname");
+ if (Q_strncasecmp (t, "light", 5))
+ continue;
+
+ // This is not true for dynamic lights
+ if (!Q_strcasecmp (t, "light_dynamic"))
+ continue;
+
+ t = ValueForKey (e, "targetname");
+ if (!t[0])
+ continue;
+
+ // find this targetname
+ for (j=0 ; j<stylenum ; j++)
+ if (!strcmp (lighttargets[j], t))
+ break;
+ if (j == stylenum)
+ {
+ if (stylenum == MAX_SWITCHED_LIGHTS)
+ Error ("Too many switched lights (error at light %s), max = %d", t, MAX_SWITCHED_LIGHTS);
+ strcpy (lighttargets[j], t);
+ stylenum++;
+ }
+ sprintf (value, "%i", 32 + j);
+ char *pCurrentStyle = ValueForKey( e, "style" );
+ // the designer has set a default lightstyle as well as making the light switchable
+ if ( pCurrentStyle )
+ {
+ int oldStyle = atoi(pCurrentStyle);
+ if ( oldStyle != 0 )
+ {
+ // save off the default style so the game code can make a switchable copy of it
+ SetKeyValue( e, "defaultstyle", pCurrentStyle );
+ }
+ }
+ SetKeyValue (e, "style", value);
+ }
+
+}
+
+/*
+============
+EmitBrushes
+============
+*/
+void EmitBrushes (void)
+{
+ int i, j, bnum, s, x;
+ dbrush_t *db;
+ mapbrush_t *b;
+ dbrushside_t *cp;
+ Vector normal;
+ vec_t dist;
+ int planenum;
+
+ numbrushsides = 0;
+ numbrushes = g_MainMap->nummapbrushes;
+
+ for (bnum=0 ; bnum<g_MainMap->nummapbrushes ; bnum++)
+ {
+ b = &g_MainMap->mapbrushes[bnum];
+ db = &dbrushes[bnum];
+
+ db->contents = b->contents;
+ db->firstside = numbrushsides;
+ db->numsides = b->numsides;
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (numbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ cp = &dbrushsides[numbrushsides];
+ numbrushsides++;
+ cp->planenum = b->original_sides[j].planenum;
+ cp->texinfo = b->original_sides[j].texinfo;
+ if ( cp->texinfo == -1 )
+ {
+ cp->texinfo = g_MainMap->g_ClipTexinfo;
+ }
+ cp->bevel = b->original_sides[j].bevel;
+ }
+
+ // add any axis planes not contained in the brush to bevel off corners
+ for (x=0 ; x<3 ; x++)
+ for (s=-1 ; s<=1 ; s+=2)
+ {
+ // add the plane
+ VectorCopy (vec3_origin, normal);
+ normal[x] = s;
+ if (s == -1)
+ dist = -b->mins[x];
+ else
+ dist = b->maxs[x];
+ planenum = g_MainMap->FindFloatPlane (normal, dist);
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->original_sides[i].planenum == planenum)
+ break;
+ if (i == b->numsides)
+ {
+ if (numbrushsides >= MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+
+ dbrushsides[numbrushsides].planenum = planenum;
+ dbrushsides[numbrushsides].texinfo =
+ dbrushsides[numbrushsides-1].texinfo;
+ numbrushsides++;
+ db->numsides++;
+ }
+ }
+ }
+}
+
+
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile (void)
+{
+ // these values may actually be initialized
+ // if the file existed when loaded, so clear them explicitly
+ nummodels = 0;
+ numfaces = 0;
+ numnodes = 0;
+ numbrushsides = 0;
+ numvertexes = 0;
+ numleaffaces = 0;
+ numleafbrushes = 0;
+ numsurfedges = 0;
+
+ // edge 0 is not used, because 0 can't be negated
+ numedges = 1;
+
+ // leave vertex 0 as an error
+ numvertexes = 1;
+
+ // leave leaf 0 as an error
+ numleafs = 1;
+ dleafs[0].contents = CONTENTS_SOLID;
+
+ // BUGBUG: This doesn't work!
+#if 0
+ // make a default empty leaf for the tracing code
+ memset( &dleafs[1], 0, sizeof(dleafs[1]) );
+ dleafs[1].contents = CONTENTS_EMPTY;
+#endif
+}
+
+// We can't calculate this properly until vvis (since we need vis to do this), so we set
+// to zero everywhere by default.
+static void ClearDistToClosestWater( void )
+{
+ int i;
+ for( i = 0; i < numleafs; i++ )
+ {
+ g_LeafMinDistToWater[i] = 0;
+ }
+}
+
+
+void DiscoverMacroTextures()
+{
+ CUtlDict<int,int> tempDict;
+
+ g_FaceMacroTextureInfos.SetSize( numfaces );
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo];
+ if ( pTexInfo->texdata < 0 )
+ continue;
+
+ dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata];
+ const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ];
+
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
+
+ const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" );
+ if ( pMacroTextureName )
+ {
+ if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() )
+ {
+ Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName );
+ tempDict.Insert( pMacroTextureName, 0 );
+ }
+
+ int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName );
+ g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID;
+ }
+ else
+ {
+ g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF;
+ }
+ }
+}
+
+
+// Make sure that we have a water lod control entity if we have water in the map.
+void EnsurePresenceOfWaterLODControlEntity( void )
+{
+ extern bool g_bHasWater;
+ if( !g_bHasWater )
+ {
+ // Don't bother if there isn't any water in the map.
+ return;
+ }
+ for( int i=0; i < num_entities; i++ )
+ {
+ entity_t *e = &entities[i];
+
+ const char *pClassName = ValueForKey( e, "classname" );
+ if( !Q_stricmp( pClassName, "water_lod_control" ) )
+ {
+ // Found one!!!!
+ return;
+ }
+ }
+
+ // None found, add one.
+ Warning( "Water found with no water_lod_control entity, creating a default one.\n" );
+
+ entity_t *mapent = &entities[num_entities];
+ num_entities++;
+ memset(mapent, 0, sizeof(*mapent));
+ mapent->firstbrush = g_MainMap->nummapbrushes;
+ mapent->numbrushes = 0;
+
+ SetKeyValue( mapent, "classname", "water_lod_control" );
+ SetKeyValue( mapent, "cheapwaterstartdistance", "1000" );
+ SetKeyValue( mapent, "cheapwaterenddistance", "2000" );
+}
+
+
+/*
+============
+EndBSPFile
+============
+*/
+void EndBSPFile (void)
+{
+ // Mark noshadow faces.
+ MarkNoShadowFaces();
+
+ EmitBrushes ();
+ EmitPlanes ();
+
+ // stick flat normals at the verts
+ SaveVertexNormals();
+
+ // Figure out lightmap extents for all faces.
+ UpdateAllFaceLightmapExtents();
+
+ // Generate geometry and lightmap alpha for displacements.
+ EmitDispLMAlphaAndNeighbors();
+
+ // Emit overlay data.
+ Overlay_EmitOverlayFaces();
+ OverlayTransition_EmitOverlayFaces();
+
+ // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs)
+ EmitPhysCollision();
+
+ // We can't calculate this properly until vvis (since we need vis to do this), so we set
+ // to zero everywhere by default.
+ ClearDistToClosestWater();
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // Place detail props found in .vmf and based on material properties
+ EmitDetailObjects();
+
+ // Compute bounds after creating disp info because we need to reference it
+ ComputeBoundsNoSkybox();
+
+ // Make sure that we have a water lod control eneity if we have water in the map.
+ EnsurePresenceOfWaterLODControlEntity();
+
+ // Doing this here because stuff about may filter out entities
+ UnparseEntities ();
+
+ // remove unused texinfos
+ CompactTexinfos();
+
+ // Figure out which faces want macro textures.
+ DiscoverMacroTextures();
+
+ char targetPath[1024];
+ GetPlatformMapPath( source, targetPath, g_nDXLevel, 1024 );
+ Msg ("Writing %s\n", targetPath);
+ WriteBSPFile (targetPath);
+}
+
+
+/*
+==================
+BeginModel
+==================
+*/
+int firstmodleaf;
+void BeginModel (void)
+{
+ dmodel_t *mod;
+ int start, end;
+ mapbrush_t *b;
+ int j;
+ entity_t *e;
+ Vector mins, maxs;
+
+ if (nummodels == MAX_MAP_MODELS)
+ Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS);
+ mod = &dmodels[nummodels];
+
+ mod->firstface = numfaces;
+
+ firstmodleaf = numleafs;
+ firstmodeledge = numedges;
+ firstmodelface = numfaces;
+
+ //
+ // bound the brushes
+ //
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+ ClearBounds (mins, maxs);
+
+ for (j=start ; j<end ; j++)
+ {
+ b = &g_MainMap->mapbrushes[j];
+ if (!b->numsides)
+ continue; // not a real brush (origin brush)
+ AddPointToBounds (b->mins, mins, maxs);
+ AddPointToBounds (b->maxs, mins, maxs);
+ }
+
+ VectorCopy (mins, mod->mins);
+ VectorCopy (maxs, mod->maxs);
+}
+
+
+/*
+==================
+EndModel
+==================
+*/
+void EndModel (void)
+{
+ dmodel_t *mod;
+
+ mod = &dmodels[nummodels];
+
+ mod->numfaces = numfaces - mod->firstface;
+
+ nummodels++;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// figure out which leaf a point is in
+//-----------------------------------------------------------------------------
+static int PointLeafnum_r (const Vector& p, int num)
+{
+ float d;
+ while (num >= 0)
+ {
+ dnode_t* node = dnodes + num;
+ dplane_t* plane = dplanes + node->planenum;
+
+ if (plane->type < 3)
+ d = p[plane->type] - plane->dist;
+ else
+ d = DotProduct (plane->normal, p) - plane->dist;
+ if (d < 0)
+ num = node->children[1];
+ else
+ num = node->children[0];
+ }
+
+ return -1 - num;
+}
+
+int PointLeafnum ( dmodel_t* pModel, const Vector& p )
+{
+ return PointLeafnum_r (p, pModel->headnode);
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a noew to the bounding box
+//-----------------------------------------------------------------------------
+static void AddNodeToBounds(int node, CUtlVector<int>& skipAreas, Vector& mins, Vector& maxs)
+{
+ // not a leaf
+ if (node >= 0)
+ {
+ AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs );
+ AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs );
+ }
+ else
+ {
+ int leaf = - 1 - node;
+
+ // Don't bother with solid leaves
+ if (dleafs[leaf].contents & CONTENTS_SOLID)
+ return;
+
+ // Skip 3D skybox
+ int i;
+ for ( i = skipAreas.Count(); --i >= 0; )
+ {
+ if (dleafs[leaf].area == skipAreas[i])
+ return;
+ }
+
+ unsigned int firstface = dleafs[leaf].firstleafface;
+ for ( i = 0; i < dleafs[leaf].numleaffaces; ++i )
+ {
+ unsigned int face = dleaffaces[ firstface + i ];
+
+ // Skip skyboxes + nodraw
+ texinfo_t& tex = texinfo[dfaces[face].texinfo];
+ if (tex.flags & (SURF_SKY | SURF_NODRAW))
+ continue;
+
+ unsigned int firstedge = dfaces[face].firstedge;
+ Assert( firstedge >= 0 );
+
+ for (int j = 0; j < dfaces[face].numedges; ++j)
+ {
+ Assert( firstedge+j < numsurfedges );
+ int edge = abs(dsurfedges[firstedge+j]);
+ dedge_t* pEdge = &dedges[edge];
+ Assert( pEdge->v[0] >= 0 );
+ Assert( pEdge->v[1] >= 0 );
+ AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs);
+ AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs);
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Check to see if a displacement lives in any leaves that are not
+// in the 3d skybox
+//-----------------------------------------------------------------------------
+bool IsBoxInsideWorld( int node, CUtlVector<int> &skipAreas, const Vector &vecMins, const Vector &vecMaxs )
+{
+ while( 1 )
+ {
+ // leaf
+ if (node < 0)
+ {
+ // get the leaf
+ int leaf = - 1 - node;
+
+ // Don't bother with solid leaves
+ if (dleafs[leaf].contents & CONTENTS_SOLID)
+ return false;
+
+ // Skip 3D skybox
+ int i;
+ for ( i = skipAreas.Count(); --i >= 0; )
+ {
+ if ( dleafs[leaf].area == skipAreas[i] )
+ return false;
+ }
+
+ return true;
+ }
+
+ //
+ // get displacement bounding box position relative to the node plane
+ //
+ dnode_t *pNode = &dnodes[ node ];
+ dplane_t *pPlane = &dplanes[ pNode->planenum ];
+
+ int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane );
+
+ // front side
+ if( sideResult == 1 )
+ {
+ node = pNode->children[0];
+ }
+ // back side
+ else if( sideResult == 2 )
+ {
+ node = pNode->children[1];
+ }
+ //split
+ else
+ {
+ if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) )
+ return true;
+
+ node = pNode->children[1];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the displacement surfaces in the world to the bounds
+//-----------------------------------------------------------------------------
+void AddDispsToBounds( int nHeadNode, CUtlVector<int>& skipAreas, Vector &vecMins, Vector &vecMaxs )
+{
+ Vector vecDispMins, vecDispMaxs;
+
+ // first determine how many displacement surfaces there will be per leaf
+ int i;
+ for ( i = 0; i < g_dispinfo.Count(); ++i )
+ {
+ ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs );
+ if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) )
+ {
+ AddPointToBounds( vecDispMins, vecMins, vecMaxs );
+ AddPointToBounds( vecDispMaxs, vecMins, vecMaxs );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
+//-----------------------------------------------------------------------------
+void ComputeBoundsNoSkybox( )
+{
+ // Iterate over all world leaves, skip those which are part of skybox
+ Vector mins, maxs;
+ ClearBounds (mins, maxs);
+ AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
+ AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
+
+ // Add the bounds to the worldspawn data
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "worldspawn"))
+ {
+ char string[32];
+ sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]);
+ SetKeyValue (&entities[i], "world_mins", string);
+ sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]);
+ SetKeyValue (&entities[i], "world_maxs", string);
+ break;
+ }
+ }
+}
+
+
diff --git a/mp/src/utils/vbsp/writebsp.h b/mp/src/utils/vbsp/writebsp.h
index e1ad084e..5dfce099 100644
--- a/mp/src/utils/vbsp/writebsp.h
+++ b/mp/src/utils/vbsp/writebsp.h
@@ -1,34 +1,34 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef WRITEBSP_H
-#define WRITEBSP_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "bspfile.h"
-#include "utlmap.h"
-
-struct node_t;
-
-//-----------------------------------------------------------------------------
-// Emits occluder faces
-//-----------------------------------------------------------------------------
-void EmitOccluderFaces (node_t *node);
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Free the list of faces stored at the leaves
-//-----------------------------------------------------------------------------
-void FreeLeafFaces( face_t *pLeafFaceList );
-
-//-----------------------------------------------------------------------------
-// Purpose: Make sure that we have a water lod control entity if we have water in the map.
-//-----------------------------------------------------------------------------
-void EnsurePresenceOfWaterLODControlEntity( void );
-
-#endif // WRITEBSP_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WRITEBSP_H
+#define WRITEBSP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "bspfile.h"
+#include "utlmap.h"
+
+struct node_t;
+
+//-----------------------------------------------------------------------------
+// Emits occluder faces
+//-----------------------------------------------------------------------------
+void EmitOccluderFaces (node_t *node);
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Free the list of faces stored at the leaves
+//-----------------------------------------------------------------------------
+void FreeLeafFaces( face_t *pLeafFaceList );
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure that we have a water lod control entity if we have water in the map.
+//-----------------------------------------------------------------------------
+void EnsurePresenceOfWaterLODControlEntity( void );
+
+#endif // WRITEBSP_H