PVS

From Valve Developer Community
Revision as of 05:06, 6 August 2011 by Artfunkel (talk | contribs)
Jump to navigation Jump to search

A Potentially Visible Set (or Potential Visibility Set) is a collection of visleaves which might be visible from a given location. It is not a precise measure of visibility but thanks to being pre-compiled into a map is a very fast one. It forms the basis for many run-time tests, including networking filtering and rendering control.

See Visibility optimization for in-depth discussion of how leaves and the PVS work.

Console commands

r_novis  <boolean>
Disables the PVS system, causing the whole world to be rendered at once.
r_lockpvs  <boolean>
Prevents the PVS system from updating. Allows direct inspection "behind the scenes".
mat_wireframe  <choices>
While not strictly PVS-related, wireframe mode does allow you to see leaves popping in and out of view.
sv_strict_notarget  <boolean>
If set, notarget will cause entities to never think they are in the PVS.
test_randomizeinpvs  <void>
[Todo]

Programming

PVS is handled by the server only. Clients do not know anything about it (but see NotifyShouldTransmit()).

Clusters

Todo: Difference between a cluster and a leaf.
int engine->GetClusterForOrigin( Vector origin )
Gets the cluster for the given origin.
int engine->GetClusterCount()
The number of clusters in the map.
Tip.pngTip:ceil(engine->GetClusterCount() / 8.0f) returns the appropriate length for a PVS buffer in the current map.
int engine->GetAllClusterBounds( bbox_t *pBBoxList, int maxBBox )
Gets a list of all clusters' bounds. Returns total number of clusters. bbox_t is a struct containing a min and max Vector.

PVS

A PVS is stored in a byte buffer in which each bit represents the visibility of a cluster.

Icon-Bug.pngBug:Testing an empty PVS can have unreliable results in some situations.  [todo tested in ?]
int engine->GetPVSForCluster( int cluster, int outputpvslength, byte* outputpvs )
Places the PVS for the given cluster in outputpvs. The return value is the number of bytes that were written; values in the rest of the buffer are undefined.
bool engine->CheckOriginInPVS( Vector origin, byte *checkpvs, int checkpvssize )
Determines whether the given location is within the given PVS.
bool engine->CheckBoxInPVS( Vector mins, Vector maxs, byte *checkpvs, int checkpvssize )
Determines whether the given box is within the given PVS at any point.
void engine->ResetPVS( byte *pvs, int pvssize )
Resets pvssize bytes to 0, starting at pvs.
void engine->AddOriginToPVS( Vector origin )
Deprecated? Adds the PVS of the given origin to the "current accumulated pvs" with an "8 unit fudge factor".

Example

This function bypasses the problem of a dynamic light entity being processed needlessly when its bounding box pokes through a wall. The code is quite expensive, but not as expensive as a dynamic light!

int CMyEnt::ShouldTransmit(const CCheckTransmitInfo *pInfo)
{
	Vector vecSurroundMins, vecSurroundMaxs;
	CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
	
	// Are our surrounding bounds in PVS?
	if ( !engine->CheckBoxInPVS(vecSurroundMins, vecSurroundMaxs, pInfo->m_PVS, pInfo->m_nPVSSize) )
		return FL_EDICT_DONTSEND;

	// Get our own PVS (cache the buffer if you use this code)
	int pvsSize = ceil(engine->GetClusterCount() / 8.0f); // bits to bytes
	byte* pvs = new byte[pvsSize];
	int cluster = engine->GetClusterForOrigin(GetAbsOrigin());
	engine->GetPVSForCluster(cluster, pvsSize, pvs);

	// Find the intersection of our PVS and the player's
	byte* pvs_shared = new byte[pvsSize];
	bool found_overlap = false;
	for (int i=0; i < pvsSize; i++)
	{
		pvs_shared[i] = pvs[i] & pInfo->PVS[i]; // bitwise AND
		
		if (!found_overlap && pvs_shared[i] != 0)
			found_overlap = true;
	}

	// transmit if we are in the shared PVS
	if (found_overlap && engine->CheckBoxInPVS( mins, maxs, pvs, pvsSize ))
		return FL_EDICT_ALWAYS;

	return FL_EDICT_DONTSEND;
}

Areas

Areas are completely separate collections of leaves, either set in stone by brushwork or created dynamically by closed areaportals (which PVS alone cannot account for).

int engine->GetArea( Vector origin )
Determine which area the origin is within.
void engine->GetAreaBits( int area, byte *bits, int buflen )
Get a bit buffer of which areas the given area can flow into.
int engine->CheckAreasConnected( int area1, int area2 )
Check whether area1 flows into area2 and vice versa. Result depends on areaportal state.
Note.pngNote:Areaportals will close of their own accord depending on the position of the player. Area testing is therefore not always a good idea!
bool engine->GetAreaPortalPlane( Vector vViewOrigin, int portalKey, VPlane *pPlane )
Given a view origin (which tells us the area to start looking in) and a portal key, fill in the plane that leads out of this area (it points into whatever area it leads to).
void engine->SetAreaPortalState( int portalNumber, int isOpen )
void engine->SetAreaPortalStates( int *portalNumbers, int *isOpen, int nPortals )
Open or close one or many areaportals.

See Also