PVS: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(redone; aimed at coders given that mappers have Visibility optimization)
Line 1: Line 1:
A [[PVS]] or '''[http://en.wikipedia.org/wiki/Potentially_visible_set Potentially Visible Set]''' is used as a somewhat abstract filter by [[NPC Sensing]] and the clientside [[Rendering Engine]] when deciding which parts of the World are visible to an Entity during gameplay.
{{toc-right}}


A [[PVS]] is the specific ''set of [[Visleaf]]s'' which are visible from a specific [[Visleaf]]; every Visleaf has it's own PVS. Each '''Visleaf's PVS''' is calculated and recorded during [[vvis|VVIS]] compiling, and cannot be changed at runtime.  
The '''Potentially Visible Set''' (or '''Potential Visibility Set''') is a collection of [[Visleaf|visleaves]] which the player might be able to see. It is not an accurate 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 of visibility, including networking and rendering control.


An '''Entity's PVS''' (including the '''Player's PVS''') is the PVS of whichever Visleaf the Entity is currently in. As every part of a [http://en.wikipedia.org/wiki/BSP_%28file_extension%29 BSP] World is divided into exclusive Visleafs, any Entity will always be located in one particular Visleaf or another.  If the Entity moves around the World, it will probably move from one Visleaf to another. Each time it moves to a different Visleaf, it will 'inherit' a different PVS from its current Visleaf.
See [[Visibility optimization]] for a more in-depth discussion of how leaves and the PVS work.


A '''PVS test''' is a simple look-up to determine which [[Visleaf]]s should be tested against this Entity's [[Viewcone]].
== Console commands ==
 
{{ScriptVar|r_novis|bool|Bypasses the PVS system, causing the whole world to be rendered at once.}}
{{ScriptVar|r_lockpvs|bool|Prevents the PVS system from updating. Allows inspection "behind the scenes".}}
{{ScriptVar|sv_strict_notarget|bool|If set, [[notarget]] will cause entities to never think they are in the PVS.}}
{{ScriptVar|test_randomizeinpvs|void|{{todo}}}}
 
== Programming ==
 
PVS is handled by the server only. Clients do not know anything about it (but see <code>[[NotifyShouldTransmit()]]</code>).


== Console commands ==
=== Clusters ===
;[[r_novis]]:
 
:Toggle the PVS handling on/off. (Cheat.)
''{{todo|Difference between a cluster and a leaf.}}''
;[[r_lockpvs]]:
 
:Allows you to lock the PVS. (Cheat.)
; <code>[[int]] engine->GetClusterForOrigin( [[Vector]] origin )</code>
;[[sv_strict_notarget]]
: Gets the cluster for the given origin.
:If set, notarget will cause entities to never think they are in the PVS.
; <code>[[int]] engine->GetClusterCount()</code>
;[[Test_RandomizeInPVS]]
: The number of clusters in the map. {{confirm|This can be used to determine an appropriate length for a PVS buffer.}}
; <code>[[int]] engine->GetAllClusterBounds( bbox_t *pBBoxList, [[int]] maxBBox )</code>
: Gets a list of all clusters' bounds. Returns total number of clusters. <code>bbox_t</code> is a struct containing a min and max <code>[[Vector]]</code>.
 
=== PVS ===
 
''A PVS is stored in a [[byte]] buffer in which each bit represents the visibility of a leaf ({{todo|or cluster?}}).''
 
{{bug|Testing an empty PVS can have unreliable results in some situations.}}
 
; <code>[[int]] engine->GetPVSForCluster( [[int]] cluster, [[int]] outputpvslength, [[byte]]* outputpvs )</code>
: Places the PVS for the given cluster in <code>outputpvs</code>. The return value is the number of bytes that were written; values in the rest of the buffer are undefined.
; <code>[[bool]] engine->CheckOriginInPVS( [[Vector]] origin, [[byte]] *checkpvs, [[int]] checkpvssize )</code>
: Determines whether the given location is within the given PVS.
; <code>[[bool]] engine->CheckBoxInPVS( [[Vector]] mins, [[Vector]] maxs, [[byte]] *checkpvs, [[int]] checkpvssize )</code>
: Determines whether the given box is within the given PVS at any point.
; <code>void engine->ResetPVS( [[byte]] *pvs, [[int]] pvssize )</code>
: Resets <code>pvssize</code> bytes to 0, starting at <code>pvs</code>.
; <code>void engine->AddOriginToPVS( [[Vector]] origin )</code>
: 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 an dynamic light entity being processed needlessly when its [[bounding box]] pokes through a wall. It is quite [[expensive]]! {{todo|Add area connection testing.}}
 
<source lang=cpp>
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)
return FL_EDICT_DONTSEND;
if (engine->CheckBoxInPVS( mins, maxs, pvs, pvsSize ))
return FL_EDICT_ALWAYS;
 
return FL_EDICT_DONTSEND;
}
</source>
 
=== Areas ===
 
''Areas are created by [[areaportal]]s. A map without areaportals is one large area. {{todo|Move this to its own page?}}''
 
; <code>[[int]] engine->GetArea( [[Vector]] origin )</code>
: Determine which area the origin is within.
; <code>void engine->GetAreaBits( [[int]] area, [[byte]] *bits, [[int]] buflen )</code>
: Get a bit buffer of which areas the given area can flow into.
; <code>[[int]] engine->CheckAreasConnected( [[int]] area1, [[int]] area2 )</code>
: Check whether area1 flows into area2 and vice versa. Result depends on areaportal state.
; <code>[[bool]] engine->GetAreaPortalPlane( [[Vector]] vViewOrigin, [[int]] portalKey, [[VPlane]] *pPlane )</code>
: 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).
; <code>void engine->SetAreaPortalState( [[int]] portalNumber, [[int]] isOpen )</code>
; <code>void engine->SetAreaPortalStates( [[int]] *portalNumbers, [[int]] *isOpen, [[int]] nPortals )</code>
: Open or close one or many areaportals.


== See Also ==
== See Also ==
*[[Visleaf]] and [http://www.student.ru.nl/rvanhoorn/optimization.php?chapter=visleafs Ralph van Hoorn's excellent explanation of visleafs].
*[[NPC_Sensing]] includes PVS and other vis-tests such as Viewcone and LOS tests.
*[[vvis|VVIS]] and [[Controlling Geometry Visibility and Compile Times]].
*[[PAS]] (Potentially Audible Set).


* [[Visleaf]]
* [[Visibility optimization]]
* [http://rvanhoorn.ruhosting.nl/optimization.php?chapter=visleafs Half-Life 2 Map Editing Optimization Guide: Visleafs] <!-- sic -->
* [[VBSP]], which generates leaves
* [[VVIS]], which determines visibility between leaves
* [[PAS]], the Potentially Audible Set
* [[Wikipedia:Potentially Visible Set]]


[[Category:Programming]]
[[Category:Glossary]]
[[Category:Glossary]]

Revision as of 12:58, 4 August 2011

The Potentially Visible Set (or Potential Visibility Set) is a collection of visleaves which the player might be able to see. It is not an accurate 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 of visibility, including networking and rendering control.

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

Console commands

r_novis  <boolean>
Bypasses the PVS system, causing the whole world to be rendered at once.
r_lockpvs  <boolean>
Prevents the PVS system from updating. Allows inspection "behind the scenes".
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.
Confirm:This can be used to determine an appropriate length for a PVS buffer.
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 leaf (

Todo: or 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 an dynamic light entity being processed needlessly when its bounding box pokes through a wall. It is quite expensive!

Todo: Add area connection testing.
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)
		return FL_EDICT_DONTSEND;
	if (engine->CheckBoxInPVS( mins, maxs, pvs, pvsSize ))
		return FL_EDICT_ALWAYS;

	return FL_EDICT_DONTSEND;
}

Areas

Areas are created by areaportals. A map without areaportals is one large area.

Todo: Move this to its own page?
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.
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