Skip to content

Commit

Permalink
camera rework and related stuff
Browse files Browse the repository at this point in the history
yet another attempt at making cameras more usable with some better code but also raw hackery
-view camera when activating a pac_event related to it
-manually view camera via partmenu option, some other part classes have similar activation/preview actions
-better auto-switching between cameras in various cases

PART:SetSmallIcon(str) overrides the tiny event indicator on part icons
used when manually viewing a camera to display an eye showing that this camera is the one being viewed, might be used later

PART:GetReasonsHidden() gives a whole list of reasons as a table of part-indexed strings instead of doing early returns as soon as one reason is found. there are often multiple reasons, a common one is parent hiding
also added parent hiding reason to PART:GetReasonHidden()

use interpolated_multibone's friendlyname in nicename to reflect that we'll refer to interpolated_multibone as interpolator, and added a better nicename showing the nodes

event seen_by_player detects other players' eyeangle raycasts on an adjustable bounding box
  • Loading branch information
pingu7867 committed Mar 1, 2024
1 parent 00d958f commit bc00adb
Show file tree
Hide file tree
Showing 9 changed files with 563 additions and 123 deletions.
85 changes: 85 additions & 0 deletions lua/pac3/core/client/base_part.lua
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,35 @@ do -- scene graph
end
end

function PART:SetSmallIcon(str)
if str == "event" then str = "icon16/clock_red.png" end
if self.pace_tree_node then
if self.pace_tree_node.Icon then
if not self.pace_tree_node.Icon.event_icon then
local pnl = vgui.Create("DImage", self.pace_tree_node.Icon)
self.pace_tree_node.Icon.event_icon_alt = true
self.pace_tree_node.Icon.event_icon = pnl
pnl:SetSize(8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)), 8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)))
pnl:SetPos(8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)), 8*(1 + 0.5*(GetConVar("pac_editor_scale"):GetFloat()-1)))
end
self.pace_tree_node.Icon.event_icon_alt = true
self.pace_tree_node.Icon.event_icon:SetImage(str)
self.pace_tree_node.Icon.event_icon:SetVisible(true)
end
end
end
function PART:RemoveSmallIcon()
if self.pace_tree_node then
if self.pace_tree_node.Icon then
if self.pace_tree_node.Icon.event_icon then
self.pace_tree_node.Icon.event_icon_alt = false
self.pace_tree_node.Icon.event_icon:SetImage("icon16/clock_red.png")
self.pace_tree_node.Icon.event_icon:SetVisible(false)
end
end
end
end

end

do -- hidden / events
Expand Down Expand Up @@ -669,13 +698,30 @@ do -- hidden / events
self.active_events[event_part] = event_part
self.active_events_ref_count = self.active_events_ref_count + 1
self:CallRecursive("CalcShowHide", false)
if self.ClassName == "camera" and pac.LocalPlayer == self:GetPlayerOwner() then
if event_part.Event == "command" then
pac.camera_linked_command_events[string.Split(event_part.Arguments,"@@")[1]] = true
end
if pac.active_camera_manual == self then --we're force-viewing this camera on the editor, assume we want to swap
pace.ManuallySelectCamera(self, false)
else
pac.TryToAwakenDormantCameras(self)
end
self:SetSmallIcon("event")
end
end
else
if self.active_events[event_part] then
self.active_events[event_part] = nil
self.active_events_ref_count = self.active_events_ref_count - 1
self:CallRecursive("CalcShowHide", false)
end
if self.ClassName == "camera" and pac.LocalPlayer == self:GetPlayerOwner() then
if pac.active_camera_manual then --we're force-viewing another camera on the editor, since we're showing a new camera, assume we want to swap
pace.ManuallySelectCamera(self, true)
end
self:SetSmallIcon("event")
end
end
end

Expand All @@ -698,9 +744,48 @@ do -- hidden / events
return "pac_hide_disturbing is set to 1"
end

for i,part in ipairs(self:GetParentList()) do
if part:IsHidden() then
table_insert(found, tostring(part) .. " is parent hiding")
end
end
if found[1] then
return table.concat(found, "\n")
end

return ""
end

function PART:GetReasonsHidden()
local found = {}

for part in pairs(self.active_events) do
found[part] = "event hiding"
end

if self.Hide then
found[self] = "self hiding"
end

if self.hide_disturbing then
if self.Hide then
found[self] = "self hiding and disturbing"
else
found[self] = "disturbing"
end
end

for i,part in ipairs(self:GetParentList()) do
if not found[part] then
if part:IsHidden() then
found[part] = "parent hidden"
end
end
end

return found
end

function PART:CalcShowHide(from_rendering)
local b = self:IsHidden()

Expand Down
180 changes: 147 additions & 33 deletions lua/pac3/core/client/parts/camera.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,76 @@ for i, ply in ipairs(player.GetAll()) do
ply.pac_cameras = nil
end

function PART:OnShow()
local owner = self:GetPlayerOwner()
if not owner:IsValid() then return end
self.inactive = false
pac.client_camera_parts = {}

owner.pac_cameras = owner.pac_cameras or {}
owner.pac_cameras[self] = self
local function CheckCamerasAgain(ply)
local cams = ply.pac_cameras or {}
local fpos, fang, ffov, fnearz, ffarz

--the policy is that a shown camera takes priority over all others
for _, part in pairs(owner.pac_cameras) do
for _, part in pairs(cams) do
if (not part.inactive or part.priority) and not part:IsHidden() then
return true
end
end
end

function pac.RebuildCameras(restricted_search)
local found_cams = false
pac.LocalPlayer.pac_cameras = {}
pac.client_camera_parts = {}
local parts_to_check
if restricted_search then parts_to_check = pac.client_camera_parts else parts_to_check = pac.GetLocalParts() end
if table.IsEmpty(pac.client_camera_parts) then
parts_to_check = pac.GetLocalParts()
end
for _,part in pairs(parts_to_check) do
if part:IsValid() then
part.inactive = nil
if part.ClassName == "camera" then
pac.nocams = false
found_cams = true
pac.client_camera_parts[part.UniqueID] = part
if not part.inactive or not part:IsHidden() or part.priority then
pac.LocalPlayer.pac_cameras[part] = part
end
end

end
end
if not found_cams then
pac.nocams = true
end
end

function PART:CameraTakePriority(then_view)
self:GetPlayerOwner().pac_cameras = self:GetPlayerOwner().pac_cameras or {}
for _, part in pairs(self:GetPlayerOwner().pac_cameras) do
if part ~= self then
part.priority = false
part.inactive = true
part:RemoveSmallIcon()
end
end
self.priority = true
self.inactive = false
timer.Simple(0.02, function()
self.priority = true
end)
if then_view then
timer.Simple(0.2, function() pace.CameraPartSwapView(true) end)
end
end

function PART:OnShow()
local owner = self:GetPlayerOwner()
if not owner:IsValid() then return end
self.inactive = false

owner.pac_cameras = owner.pac_cameras or {}
owner.pac_cameras[self] = self

--the policy is that a shown camera takes priority over all others
self:CameraTakePriority()
end

function PART:OnHide()
Expand All @@ -54,6 +106,44 @@ function PART:OnHide()
self.inactive = true
self.priority = false
owner.pac_cameras[self] = nil
pac.TryToAwakenDormantCameras()
end

function PART:OnRemove()
local owner = self:GetPlayerOwner()

if LocalPlayer() == owner then
owner.pac_cameras = owner.pac_cameras or {}
pac.client_camera_parts[self.UniqueID] = nil
local other_visible_cameras = 0
--this camera cedes priority to others that may be active
for _, part in pairs(owner.pac_cameras) do
if part.UniqueID ~= self.UniqueID and not part:IsHidden() then
part.priority = true
other_visible_cameras = other_visible_cameras + 1
end
end
owner.pac_cameras[self] = nil
if not pace.hack_camera_part_donot_treat_wear_as_creating_part and not pace.is_still_loading_wearing then
timer.Simple(0.2, function()
pace.EnableView(true)
end)
timer.Simple(0.4, function()
pace.ResetView()
pace.CameraPartSwapView(true)
end)
end
if pac.active_camera == self then pac.active_camera = nil end
if pac.active_camera_manual == self then pac.active_camera_manual = nil end
pac.RebuildCameras()
end
end

function PART:Initialize()
if pac.LocalPlayer == self:GetPlayerOwner() then
pac.nocams = false
pac.client_camera_parts[self.UniqueID] = self
end
end

--[[function PART:OnHide()
Expand Down Expand Up @@ -88,47 +178,55 @@ local temp = {}
local remaining_camera = false
local remaining_camera_time_buffer = CurTime()

local function CheckCamerasAgain(ply)
local cams = ply.pac_cameras or {}
local fpos, fang, ffov, fnearz, ffarz

for _, part in pairs(cams) do
if (not part.inactive or part.priority) and not part:IsHidden() then
return true
end
function pac.TryToAwakenDormantCameras(calling_part)
if pace.Editor:IsValid() then return end

if not isbool(calling_part) then
pac.RebuildCameras()
end
end

local function RebuildCameras(ply)
ply.pac_cameras = {}
for _,part in pairs(pac.GetLocalParts()) do
for _,part in pairs(pac.client_camera_parts) do
if part:IsValid() then
if part.ClassName == "camera" and (not part.inactive or not part:IsHidden() or part.priority) then
if part:GetPlayerOwner() == ply then
ply.pac_cameras[part] = part
end
if part.ClassName == "camera" and part ~= calling_part then
part:GetRootPart():CallRecursive("Think")
end
end
end

pace.EnableView(false)
end

pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
pac.nocams = true
pac.nocam_counter = 0

function pac.HandleCameraPart(ply, pos, ang, fov, nearz, farz)
local chosen_part
local fpos, fang, ffov, fnearz, ffarz
local warning_state = not ply.pac_cameras
local ply = pac.LocalPlayer
if pac.nocams then return end
ply.pac_cameras = ply.pac_cameras or {}

local warning_state = ply.pac_cameras == nil
if not warning_state then warning_state = table.IsEmpty(ply.pac_cameras) end
if ply:GetViewEntity() ~= ply then return end

remaining_camera = false
remaining_camera_time_buffer = remaining_camera_time_buffer or CurTime()
pace.delaymovement = RealTime() + 1 --we need to do that so that while testing cameras, you don't fly off when walking and end up far from your character
if warning_state then
RebuildCameras(ply)
pac.RebuildCameras(true)
pac.nocam_counter = pac.nocam_counter + 1
--go back to early returns to avoid looping through localparts when no cameras are active checked 500 times
if pac.nocam_counter > 500 then pac.nocams = true return end
else
if not IsValid(pac.active_camera) then pac.active_camera = nil pac.RebuildCameras(true) end
pac.nocam_counter = 0
local chosen_camera
for _, part in pairs(ply.pac_cameras) do
if part.ClassName ~= "camera" then
ply.pac_cameras[part] = nil
end

if part.ClassName == "camera" and part:IsValid() then
if not part:IsHidden() then
remaining_camera = true
Expand All @@ -138,7 +236,7 @@ pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
part:CalcShowHide()
if not part.inactive then
--calculate values ahead of the return, used as a fallback just in case
fpos, fang, ffov, fnearz, ffarz = part:CalcView(ply, pos, ang, fov, nearz, farz)
fpos, fang, ffov, fnearz, ffarz = part:CalcView(_,_,ply:EyeAngles())
temp.origin = fpos
temp.angles = fang
temp.fov = ffov
Expand All @@ -147,28 +245,44 @@ pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
temp.drawviewer = false

if not part:IsHidden() and not part.inactive and part.priority then
pac.active_camera = part
temp.drawviewer = not part.DrawViewModel
return temp
chosen_camera = part
break
end
end
else
ply.pac_cameras[part] = nil
end
end


if chosen_camera then
chosen_camera:SetSmallIcon("icon16/eye.png")
return temp
end
end

if not pac.active_camera then
pac.RebuildCameras()
end
if remaining_camera or CurTime() < remaining_camera_time_buffer then
return temp
end

--final fallback, just give us any valid pac camera to preserve the view! priority will be handled elsewhere
if CheckCamerasAgain(ply) then
return temp
else
return
end

return
--only time to return to first person is if all camera parts are hidden AFTER we pass the buffer time filter
--until we make reversible first person a thing, letting some non-drawable parts think, this is the best solution I could come up with
end

function pac.HasRemainingCameraPart()
pac.RebuildCameras()
return table.Count(pac.LocalPlayer.pac_cameras) ~= 0
end

pac.AddHook("CalcView", "camera_part", function(ply, pos, ang, fov, nearz, farz)
pac.HandleCameraPart(ply, pos, ang, fov, nearz, farz)
end)
Loading

0 comments on commit bc00adb

Please sign in to comment.