File size: 13,395 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
-- make objects that obstruct the view transparent (camera3p)
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]
	-- move fade_out objects to fade_in
	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
	-- set the new fade_out
	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)
	-- logic for objects for which hide/show is immediate
	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) -- show what was hidden in the previous tick 
			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
		-- restore opacity of fade_in/fade_out objects
		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()
	-- create unique GetAction and GetActionEnd functions per class
	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
			-- inclined more then 45 degrees
			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