--[[ | 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