|
|
|
if FirstLoad then |
|
g_CameraMakeTransparentEnabled = false |
|
g_updateStepOpacityThread = false |
|
g_CameraMakeTransparentThread = false |
|
g_CMT_fade_out = false |
|
g_CMT_fade_in = false |
|
g_CMT_hidden = false |
|
g_CMT_replaced = false |
|
g_CMT_replaced_destroy = false |
|
end |
|
|
|
local CMT_fade_out = g_CMT_fade_out |
|
local CMT_fade_in = g_CMT_fade_in |
|
local CMT_hidden = g_CMT_hidden |
|
local CMT_replaced = g_CMT_replaced |
|
local CMT_replaced_destroy = g_CMT_replaced_destroy |
|
|
|
local transparency_enum_flags = const.efCameraMakeTransparent |
|
local transparency_surf_flags = EntitySurfaces.Walk + EntitySurfaces.Collision |
|
local obstruct_view_refresh_time = const.ObstructViewRefreshTime |
|
local fade_in_time = const.ObstructOpacityFadeInTime |
|
local fade_out_time = const.ObstructOpacityFadeOutTime |
|
local obstruct_opacity = const.ObstructOpacity |
|
local obstruct_opacity_refresh_time = const.ObstructOpacityRefreshTime |
|
local refresh_time = Max(obstruct_opacity_refresh_time, Max(fade_out_time, fade_in_time) / (100 - Clamp(obstruct_opacity, 0, 99))) |
|
local opacity_change_fadein = fade_in_time <= 0 and 100 or (100 - obstruct_opacity) * refresh_time / fade_in_time |
|
local opacity_change_fadeout = fade_out_time <= 0 and 100 or (100 - obstruct_opacity) * refresh_time / fade_out_time |
|
|
|
local function ResetLists() |
|
g_CMT_fade_out = {} |
|
g_CMT_fade_in = {} |
|
g_CMT_hidden = {} |
|
g_CMT_replaced = {} |
|
g_CMT_replaced_destroy = {} |
|
CMT_fade_out = g_CMT_fade_out |
|
CMT_fade_in = g_CMT_fade_in |
|
CMT_hidden = g_CMT_hidden |
|
CMT_replaced = g_CMT_replaced |
|
CMT_replaced_destroy = g_CMT_replaced_destroy |
|
end |
|
|
|
if FirstLoad then |
|
ResetLists() |
|
end |
|
|
|
function OnMsg.DoneMap() |
|
g_updateStepOpacityThread = false |
|
g_CameraMakeTransparentThread = false |
|
ResetLists() |
|
end |
|
|
|
local function UpdateObstructors_StepOpacity(obstructors) |
|
local view = 1 |
|
CMT_fade_in[view] = CMT_fade_in[view] or {} |
|
CMT_fade_out[view] = CMT_fade_out[view] or {} |
|
local vfade_in = CMT_fade_in[view] |
|
local vfade_out = CMT_fade_out[view] |
|
|
|
for i = #vfade_out, 1, -1 do |
|
local o = vfade_out[i] |
|
if not (obstructors and obstructors[o]) then |
|
assert(not vfade_in[o]) |
|
table.remove(vfade_out, i) |
|
vfade_out[o] = nil |
|
if o:GetOpacity() < 100 then |
|
vfade_in[#vfade_in + 1] = o |
|
vfade_in[o] = true |
|
end |
|
end |
|
end |
|
|
|
if obstructors then |
|
for i = 1, #obstructors do |
|
local o = obstructors[i] |
|
if not vfade_out[o] then |
|
vfade_out[#vfade_out + 1] = o |
|
vfade_out[o] = true |
|
end |
|
if vfade_in[o] then |
|
table.remove_entry(vfade_in, o) |
|
vfade_in[o] = nil |
|
end |
|
end |
|
end |
|
end |
|
|
|
local function UpdateObstructors_Hidden(view, obstructors) |
|
|
|
local hidden_for_view = CMT_hidden[view] |
|
CMT_hidden[view] = obstructors |
|
if obstructors then |
|
for i = 1, #obstructors do |
|
local o = obstructors[i] |
|
o:SetOpacity(0) |
|
obstructors[o] = true |
|
end |
|
end |
|
if hidden_for_view then |
|
for i = 1, #hidden_for_view do |
|
local o = hidden_for_view[i] |
|
if IsValid(o) and not (obstructors and obstructors[o]) then |
|
o:SetOpacity(100) |
|
end |
|
end |
|
end |
|
end |
|
|
|
local function ClearObstructors() |
|
for o in pairs(CMT_replaced) do |
|
o:DestroyReplacement() |
|
end |
|
for view = 1, camera.GetViewCount() do |
|
local vfade_out = CMT_fade_out[view] |
|
if vfade_out then |
|
for i = 1, #vfade_out do |
|
local o = vfade_out[i] |
|
if IsValid(o) then |
|
o:SetOpacity(100) |
|
end |
|
end |
|
end |
|
local vfade_in = CMT_fade_in[view] |
|
if vfade_in then |
|
for i = 1, #vfade_in do |
|
local o = vfade_in[i] |
|
if IsValid(o) then |
|
o:SetOpacity(100) |
|
end |
|
end |
|
end |
|
local hv = CMT_hidden[view] |
|
if hv then |
|
for i = 1, #hv do |
|
local o = hv[i] |
|
if IsValid(o) then |
|
o:SetOpacity(100) |
|
end |
|
end |
|
end |
|
end |
|
ResetLists() |
|
end |
|
|
|
local function UpdateObstructors(view, get_obstructors) |
|
local success, obstructors, obstructors_immediate = procall(get_obstructors, view) |
|
UpdateObstructors_StepOpacity(obstructors) |
|
UpdateObstructors_Hidden(view, obstructors_immediate) |
|
end |
|
|
|
local function UpdateObstructorsRefresh(cam, get_obstructors) |
|
local refresh_time = obstruct_view_refresh_time |
|
while true do |
|
while IsEditorActive() do |
|
Sleep(2 * refresh_time) |
|
end |
|
|
|
if not g_CameraMakeTransparentEnabled or not cam.IsActive() then |
|
ClearObstructors() |
|
while not g_CameraMakeTransparentEnabled or not cam.IsActive() do |
|
Sleep(refresh_time) |
|
end |
|
end |
|
for view = 1, camera.GetViewCount() do |
|
UpdateObstructors(view, get_obstructors) |
|
end |
|
Sleep(refresh_time) |
|
end |
|
end |
|
|
|
local function UpdateStepOpacity(view) |
|
local vfade_out = CMT_fade_out[view] |
|
if vfade_out then |
|
for i = #vfade_out, 1, -1 do |
|
local o = vfade_out[i] |
|
if not IsValid(o) then |
|
vfade_out[o] = nil |
|
table.remove(vfade_out, i) |
|
else |
|
local new_opacity = o:GetOpacity() - opacity_change_fadeout |
|
if new_opacity < obstruct_opacity then |
|
new_opacity = obstruct_opacity |
|
end |
|
o:SetOpacity(new_opacity) |
|
end |
|
end |
|
end |
|
local vfade_in = CMT_fade_in[view] |
|
if vfade_in then |
|
for i = #vfade_in, 1, -1 do |
|
local o = vfade_in[i] |
|
local keep |
|
if IsValid(o) then |
|
local new_opacity = Min(100, o:GetOpacity() + opacity_change_fadein) |
|
o:SetOpacity(new_opacity) |
|
keep = new_opacity < 100 |
|
end |
|
if not keep then |
|
vfade_in[o] = nil |
|
table.remove(vfade_in, i) |
|
end |
|
end |
|
end |
|
end |
|
|
|
local function UpdateStepOpacityRefresh() |
|
local refresh_time = refresh_time |
|
while true do |
|
for view = 1, camera.GetViewCount() do |
|
UpdateStepOpacity(view) |
|
end |
|
Sleep(refresh_time) |
|
end |
|
end |
|
|
|
local DistSegmentToPt = DistSegmentToPt |
|
local camera_clip_extend_radius = const.CameraClipExtendRadius |
|
local offset_z_150cm = 150*guic |
|
local cone_radius_max = config.CameraTransparencyConeRadiusMax |
|
local cone_radius_min = config.CameraTransparencyConeRadiusMin |
|
if FirstLoad then |
|
draw_transparency_cone = false |
|
end |
|
|
|
function ToggleTransparencyCone() |
|
DbgClearVectors() |
|
draw_transparency_cone = not draw_transparency_cone |
|
end |
|
|
|
local hide_filter = function(u, eye) |
|
local posx, posy, posz = u:GetVisualPosXYZ() |
|
local scale = u:GetScale() |
|
local dist_to_eye = DistSegmentToPt(posx, posy, posz, 0, 0, u.height * scale / 100, eye, true) |
|
return dist_to_eye < u.camera_radius * scale / 100 + camera_clip_extend_radius |
|
end |
|
local col_exec = function(o, list) |
|
if not list[o] then |
|
list[#list + 1] = o |
|
list[o] = true |
|
end |
|
end |
|
local function GetViewObstructorsCamera3p(view) |
|
local eye = camera.GetEye(view) |
|
local lookat = camera3p.GetLookAt(view) |
|
if not eye or not eye:IsValid() then |
|
return |
|
end |
|
local to_fade, to_fade_count |
|
local to_hide = MapGet(eye, 4*guim, "Unit", hide_filter, eye) or {} |
|
for i = 1, #to_hide do |
|
to_hide[ to_hide[i] ] = true |
|
end |
|
|
|
for loc_player = 1, LocalPlayersCount do |
|
local obj = GetPlayerControlCameraAttachedObj(loc_player) |
|
if obj and obj:IsValidPos() then |
|
local posx, posy, posz = obj:GetVisualPosXYZ() |
|
local err1, to_fade1 = AsyncIntersectConeWithObstacles( |
|
eye, point(posx, posy, posz + offset_z_150cm), |
|
cone_radius_max, cone_radius_min, |
|
transparency_enum_flags, |
|
transparency_surf_flags, |
|
draw_transparency_cone) |
|
assert(not err1, err1) |
|
if to_fade1 then |
|
if to_fade then |
|
for i = 1, #to_fade1 do |
|
local o = to_fade1[i] |
|
if not to_fade[o] then |
|
to_fade_count = to_fade_count + 1 |
|
to_fade[to_fade_count] = o |
|
to_fade[o] = true |
|
end |
|
end |
|
else |
|
to_fade = to_fade1 |
|
to_fade_count = #to_fade |
|
for i = 1, to_fade_count do |
|
to_fade[ to_fade[i] ] = true |
|
end |
|
end |
|
end |
|
end |
|
end |
|
if to_fade then |
|
for i = 1, to_fade_count do |
|
local col = to_fade[i]:GetRootCollection() |
|
if col and not to_fade[col] then |
|
to_fade[col] = true |
|
local col_areapoint1 = eye |
|
local col_areapoint2 = lookat |
|
MapForEach( |
|
col_areapoint1, col_areapoint2, 50*guim, |
|
"attached", false, "collection", col.Index, true, |
|
const.efVisible, col_exec , to_fade) |
|
end |
|
end |
|
end |
|
return to_fade, to_hide |
|
end |
|
|
|
function RestartCameraMakeTransparent() |
|
StopCameraMakeTransparent() |
|
if g_CameraMakeTransparentEnabled then |
|
g_CameraMakeTransparentThread = CreateMapRealTimeThread(UpdateObstructorsRefresh, camera3p, GetViewObstructorsCamera3p) |
|
g_updateStepOpacityThread = CreateMapRealTimeThread(UpdateStepOpacityRefresh) |
|
end |
|
end |
|
|
|
function StopCameraMakeTransparent() |
|
ClearObstructors() |
|
if g_updateStepOpacityThread then |
|
DeleteThread(g_updateStepOpacityThread) |
|
g_updateStepOpacityThread = false |
|
end |
|
if g_CameraMakeTransparentThread then |
|
DeleteThread(g_CameraMakeTransparentThread) |
|
g_CameraMakeTransparentThread = false |
|
end |
|
end |
|
|
|
OnMsg.NewMapLoaded = RestartCameraMakeTransparent |
|
OnMsg.LoadGame = RestartCameraMakeTransparent |
|
OnMsg.GameEnterEditor = StopCameraMakeTransparent |
|
|
|
DefineClass.CameraTransparentWallReplacement = { |
|
__parents = { "CObject", "ComponentAttach" }, |
|
flags = { efCameraMakeTransparent = false, efCameraRepulse = true, efSelectable = false, efWalkable = false, efCollision = false, efApplyToGrids = false, efShadow = false }, |
|
properties = |
|
{ |
|
{ id = "CastShadow", name = "Shadow from All", editor = "bool", default = false }, |
|
}, |
|
} |
|
|
|
local function CameraSpecialWallReplaceObjects(o) |
|
return { "(default)", "place_default", "" } |
|
end |
|
|
|
DefineClass.CameraSpecialWall = { |
|
__parents = { "Object" }, |
|
flags = { efCameraMakeTransparent = true, efCameraRepulse = false }, |
|
properties = { |
|
{ id = "TransparentReplace", editor = "combo", items = CameraSpecialWallReplaceObjects }, |
|
}, |
|
TransparentReplace = "(default)", |
|
replace_default = "", |
|
replace_height_min = -guim, |
|
replace_height_max = guim, |
|
} |
|
|
|
function OnMsg.ClassesPostprocess() |
|
|
|
local replace_default = {} |
|
ClassDescendants("CameraSpecialWall", function(class_name, class, replace_default) |
|
if class.replace_default == "" then |
|
local classname = class:GetEntity() .. "_Base" |
|
if g_Classes[classname] then |
|
replace_default[class] = classname |
|
end |
|
end |
|
local properties = class.properties |
|
local idx = table.find(properties, "id", "OnCollisionWithCamera") |
|
if idx then |
|
local idx_old = table.find(properties, "id", "TransparentReplace") |
|
local prop = properties[idx_old] |
|
table.remove(properties, idx_old) |
|
table.insert(properties, idx + (idx < idx_old and 1 or 0), prop) |
|
end |
|
end, replace_default) |
|
for class, value in pairs(replace_default) do |
|
class.replace_default = value |
|
end |
|
end |
|
|
|
local default_color = RGBA(128, 128, 128, 0) |
|
local default_roughness = 0 |
|
local default_metallic = 0 |
|
|
|
function CameraSpecialWall:PlaceReplacement() |
|
local replacement = CMT_replaced[self] |
|
if replacement then |
|
CMT_replaced_destroy[self] = nil |
|
return |
|
end |
|
local classname = self.TransparentReplace |
|
if classname == "place_default" then |
|
classname = self.replace_default |
|
elseif classname == "(default)" then |
|
classname = self.replace_default |
|
local pos = self:GetPos() |
|
local height = pos:z() and pos:z() - GetWalkableZ(pos) or 0 |
|
if height < self.replace_height_min or height > self.replace_height_max then |
|
classname = "" |
|
elseif self:RotateAxis(0,0,4096):z() < 2048 then |
|
|
|
classname = "" |
|
end |
|
end |
|
local replaced_base |
|
if classname ~= "" then |
|
local color1, roughness1, metallic1 = self:GetColorizationMaterial(1) |
|
local color2, roughness2, metallic2 = self:GetColorizationMaterial(2) |
|
local color3, roughness3, metallic3 = self:GetColorizationMaterial(3) |
|
local components = 0 |
|
if (color1 ~= default_color or roughness1 ~= default_roughness or metallic1 ~= default_metallic) or |
|
(color2 ~= default_color or roughness2 ~= default_roughness or metallic2 ~= default_metallic) or |
|
(color3 ~= default_color or roughness3 ~= default_roughness or metallic3 ~= default_metallic) then |
|
components = const.cofComponentColorizationMaterial |
|
end |
|
replaced_base = PlaceObject(classname, nil, components) |
|
replaced_base:SetMirrored(self:GetMirrored()) |
|
replaced_base:SetAxis(self:GetAxis()) |
|
replaced_base:SetAngle(self:GetAngle()) |
|
replaced_base:SetScale(self:GetScale()) |
|
replaced_base:SetColorModifier(self:GetColorModifier()) |
|
if components == const.cofComponentColorizationMaterial then |
|
replaced_base:SetColorizationMaterial(1, color1, roughness1, metallic1) |
|
replaced_base:SetColorizationMaterial(2, color2, roughness2, metallic2) |
|
replaced_base:SetColorizationMaterial(3, color3, roughness3, metallic3) |
|
end |
|
local anim = self:GetStateText() |
|
if anim ~= "idle" and replaced_base:HasState(anim) and not replaced_base:IsErrorState(anim) then |
|
replaced_base:SetState(anim) |
|
end |
|
replaced_base:SetPos(self:GetVisualPosXYZ()) |
|
end |
|
CMT_replaced[self] = replaced_base or true |
|
end |
|
|
|
function CameraSpecialWall:DestroyReplacement(delay) |
|
local obj = CMT_replaced[self] |
|
if obj then |
|
if obj == true then |
|
CMT_replaced[self] = nil |
|
return |
|
end |
|
if (delay or 0) == 0 then |
|
CMT_replaced[self] = nil |
|
CMT_replaced_destroy[self] = nil |
|
DoneObject(obj) |
|
elseif not CMT_replaced_destroy[self] then |
|
CMT_replaced_destroy[self] = RealTime() + delay |
|
end |
|
end |
|
end |
|
|
|
function CameraSpecialWall:SetOpacity(opacity) |
|
if opacity < 100 then |
|
self:PlaceReplacement() |
|
else |
|
self:DestroyReplacement() |
|
end |
|
Object.SetOpacity(self, opacity) |
|
end |
|
|