Files
wnsrc/lua/niknaks/modules/sh_bsp_pvspas.lua

285 lines
6.8 KiB
Lua
Raw Normal View History

2024-08-04 23:54:45 +03:00
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- Copyright © 2022-2072, Nak, https://steamcommunity.com/id/Nak2/
-- All Rights Reserved. Not allowed to be reuploaded.
-- License: https://github.com/Nak2/NikNaks/blob/main/LICENSE
local obj_tostring = "BSP %s"
local format = string.format
--- @class BSPObject
local meta = NikNaks.__metatables["BSP"]
local meta_leaf = NikNaks.__metatables["BSP Leaf"]
--[[The data is stored as an array of bit-vectors; for each cluster, a list of which other clusters are visible
from it are stored as individual bits (1 if visible, 0 if occluded) in an array, with the nth bit position
corresponding to the nth cluster. ]]
--- @param vis VisibilityInfo
--- @param offset number
local function getClusters( vis, offset, PVS )
local c = 0
local v = offset
local pvs_buffer = vis._bytebuff
local num_clusters = vis.num_clusters
while c <= num_clusters do
if pvs_buffer[v] == 0 then
v = v + 1
c = c + 8 * pvs_buffer[v]
else
local b = 1
while b ~= 0 do
if bit.band( pvs_buffer[v], b ) ~= 0 then
PVS[c] = true
end
b = bit.band( b * 2, 0xFF )
c = c + 1
end
end
v = v + 1
end
end
--- PVS ( Potentially Visible Set )
do
--- @class PVSObject
--- @field __map BSPObject
local meta_pvs = {}
meta_pvs.__index = meta_pvs
meta_pvs.__tostring = "BSP PVS"
meta_pvs.MetaName = "BSP PVS"
NikNaks.__metatables["BSP PVS"] = meta_pvs
local DVIS_PVS = 1
--- Creates a new empty PVS-object.
--- @return PVSObject
function meta:CreatePVS()
local t = {}
t.__map = self
setmetatable( t, meta_pvs )
return t
end
--- Uses the given ( or creates a new PVS-object ) and adds the position to it.
--- @param position Vector
--- @param PVS PVSObject?
--- @return PVSObject
function meta:PVSForOrigin( position, PVS )
PVS = PVS or self:CreatePVS()
PVS.__map = self
local cluster = self:ClusterFromPoint( position )
if cluster < 0 then return PVS end -- Empty cluster position.
local vis = self:GetVisibility()
local visofs = vis.VisData[cluster].PVS
getClusters( vis, visofs, PVS )
return PVS
end
--- Returns true if the two positions are in same PVS.
--- @param position Vector
--- @param position2 Vector
--- @return boolean
function meta:PVSCheck( position, position2 )
local PVS = self:PVSForOrigin( position )
local cluster = self:ClusterFromPoint( position2 )
return PVS[cluster] or false
end
--- Adds the position to PVS
--- @param position Vector
--- @return self
function meta_pvs:AddPVS( position )
self.__map:PVSForOrigin( position, self )
return self
end
--- Removes the position from PVS
--- @param position Vector
--- @return self
function meta_pvs:RemovePVS( position )
for id in pairs( self.__map:PVSForOrigin( position ) ) do
if id ~= "__map" then self[id] = nil end
end
return self
end
--- Removes the leaf from PVS
--- @param leaf LeafObject
--- @return self PVSObject
function meta_pvs:RemoveLeaf( leaf )
self[leaf.cluster] = nil
return self
end
--- Returns true if the position is visible in the PVS
--- @param position Vector
--- @return boolean
function meta_pvs:TestPosition( position )
local cluster = self.__map:ClusterFromPoint( position )
return self[cluster] or false
end
--- Create PVS from Leaf
--- @return PVSObject
function meta_leaf:CreatePVS()
local PVS = {}
PVS.__map = self.__map
setmetatable( PVS, meta_pvs )
if self.cluster < 0 then return PVS end -- Leaf invalid. Return empty PVS.
local vis = self.__map:GetVisibility()
local visofs = vis.VisData[self.cluster].PVS
getClusters( vis, visofs, PVS )
return PVS
end
--- Returns a list of leafs within this PVS. Note: This is a bit slow.
function meta_pvs:GetLeafs()
local t = {}
local n = 1
local leafs = self.__map:GetLeafs()
for i = 1, #leafs do
local leaf = leafs[i]
local cluster = leaf.cluster
if cluster >= 0 and self[cluster] then
t[n] = leaf
n = n + 1
end
end
return t
end
--- Returns true if the PVS has the given leaf
--- @param leaf LeafObject
--- @return boolean
function meta_pvs:HasLeaf( leaf )
if leaf.cluster < 0 then return false end
return self[leaf.cluster]
end
end
-- PAS
do
---@class PASObject
local meta_pas = {}
meta_pas.__index = meta_pas
meta_pas.__tostring = "BSP PAS"
meta_pas.MetaName = "BSP PAS"
NikNaks.__metatables["BSP PAS"] = meta_pas
local DVIS_PAS = 2
--- Creates a new empty PAS-object.
--- @return PASObject
function meta:CreatePAS()
return setmetatable( {}, meta_pas )
end
--- Uses the given ( or creates a new PAS-object ) and adds the position to it.
--- @param position Vector
--- @param PAS PASObject?
--- @return PASObject?
function meta:PASForOrigin( position, PAS )
PAS = PAS or self:CreatePAS()
PAS.__map = self
local cluster = self:ClusterFromPoint( position )
local vis = self:GetVisibility()
if cluster < 0 then return end -- err
local visofs = vis.VisData[cluster].PAS
getClusters( vis, visofs, PAS )
return PAS
end
--- Returns true if the two positions are in same PAS
--- @param position Vector
--- @param position2 Vector
--- @return boolean
function meta:PASCheck( position, position2 )
local PAS = self:PASForOrigin( position )
return PAS[self:ClusterFromPoint( position2 )] or false
end
--- Adds the position to PAS
--- @param position Vector
--- @return PASObject self
function meta_pas:AddPAS( position )
self.__map:PASForOrigin( position, self )
return self
end
--- Removes the position from PAS
--- @param position Vector
--- @return PASObject self
function meta_pas:RemovePAS( position )
for id in pairs( self.__map:PASForOrigin( position ) ) do
if id ~= "__map" then self[id] = nil end
end
return self
end
--- Removes the leaf from PVS
--- @param leaf LeafObject
--- @return PASObject self
function meta_pas:RemoveLeaf( leaf )
self[leaf.cluster] = nil
return self
end
--- Returns true if the position is visible in the PAS
--- @param position Vector
--- @return boolean
function meta_pas:TestPosition( position )
local cluster = self.__map:ClusterFromPoint( position )
return self[cluster] or false
end
--- Create PAS from Leaf
--- @return PASObject
function meta_leaf:CreatePAS()
local PAS = setmetatable( {}, meta_pas )
if self.cluster < 0 then return PAS end -- Leaf invalid. Return empty PVS.
local vis = self.__map:GetVisibility()
local visofs = vis[ self.cluster ][ DVIS_PAS ]
getClusters( vis, visofs, PAS )
return PAS
end
--- Returns true if the PAS has the given leaf
--- @param leaf LeafObject
--- @return boolean
function meta_pas:HasLeaf( leaf )
if leaf.cluster < 0 then return false end
return self[leaf.cluster]
end
end