--[[ | 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 (C) 2016 DBot Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ]] --ADOF - Advanced Depth of Field local DRAW_ON_SCREEN = CreateClientConVar('adof_screen', '1', true, false, 'Draw ADOF on Screen') local ADOF_PASSES = CreateClientConVar('adof_passes', '16', true, false, 'ADOF Passes') local ALWAYS_DOF = CreateClientConVar('adof_always', '1', true, false, 'Always draw DoF') local DRAW_MODE = CreateClientConVar('adof_mode', '0', true, false, 'Draw Mode - 0 as Renderable, 1 as effect') local ENABLE_BOKEN = CreateClientConVar('adof_boken', '0', true, false, 'Enable Boken DOF. Breaks things!') ADOF = ADOF or {} ADOF.Ents = ADOF.Ents or {} ADOF.SPACING = 50 ADOF.OFFSET = 1000 ADOF.REAL_NUM_DOF_NODES = 16 ADOF.NUM_DOF_NODES = 16 ADOF.Render = true ADOF.Max = 6000 ADOF.Critical = ADOF.Max * 0.5 local function IsCurrVehicle(ent) local ply = LocalPlayer() return IsValid(ent) and ent:IsVehicle() and ply:InVehicle() and IsValid(ply:GetVehicle()) and (ply:GetVehicle() == ent or ply:GetVehicle():GetParent() == ent) end local blur_mat = Material('pp/bokehblur') local fmat = CreateMaterial('ADOF_Material', 'Refract', { ['$model'] = '1', ["$normalmap"] = "effects/flat_normal", ["$refractamount"] = "0", ["$vertexalpha"] = "1", ["$vertexcolor"] = "1", ["$translucent"] = "1", ["$forcerefract"] = '1', ["$bluramount"] = "1", ["$nofog"] = "1", }) local TotalRenderTime = 0 local NextCleanup = 0 local Hits = 0 local LastHit = 0 local SPACE, SPACING = 0, 0 local ShouldDrawOnScreen = false local Rendering = false local function DrawOnScreen() if not ADOF.Render then return end if not DRAW_ON_SCREEN:GetBool() then return end if not ShouldDrawOnScreen then return end if not system.HasFocus() then return end surface.SetDrawColor(255, 255, 255, 255) surface.SetMaterial(fmat) local W, H = ScrW(), ScrH() local toupdate = LocalPlayer():WaterLevel() < 1 for i=0, ADOF.NUM_DOF_NODES do if toupdate then render.UpdateScreenEffectTexture() else render.UpdateRefractTexture() end surface.DrawTexturedRect(0, H / 2 + SPACE + i * SPACING, W, H) surface.DrawTexturedRect(0, 0, W, H / 2 - SPACE - i * SPACING) end end local function HUDPaintBackground() DrawOnScreen() Rendering = true end local function PostDrawHUD() if Rendering then Rendering = false return end DrawOnScreen() end -- Note: UpdateScreenEffectTexture fucks up the water, RefractTexture is lower quality local function Render() if not ADOF.Render then return end if bDrawingDepth or bDrawingSkybox then return end if not system.HasFocus() then return end if not ALWAYS_DOF:GetBool() and ADOF.OFFSET == ADOF.Max then return end local ply = LocalPlayer() local pos = ply:EyePos() local langles = EyeAngles() local fwd = langles:Forward() if ply:GetViewEntity() ~= ply then pos = ply:GetViewEntity():GetPos() fwd = ply:GetViewEntity():GetForward() end local toupdate = ply:WaterLevel() < 1 local CurrentAlpha = 0.1 render.SetMaterial(fmat) for i=0, ADOF.NUM_DOF_NODES do if toupdate then render.UpdateScreenEffectTexture() else render.UpdateRefractTexture() end local npos = pos + fwd * ADOF.SPACING * i + fwd * ADOF.OFFSET local SpriteSize = (ADOF.SPACING * i + ADOF.OFFSET) * 8 if pos:Distance(npos) > ADOF.Max * 2 then break end CurrentAlpha = CurrentAlpha + 0.1 render.DrawSprite(npos, SpriteSize, SpriteSize, Color( 255, 255, 255, CurrentAlpha * 255 ) ) end end local SHOULD_DRAW_BOKEN = false local BOKEN_FOCUS = 0.1 local BOKEN_FOCUS_D = 0.1 local BOKEN_FORCE = 1 local BOKEN_STEP = 0.03 local function RenderScreenspaceEffects() if not ADOF.Render then return end if not SHOULD_DRAW_BOKEN then return end if not ENABLE_BOKEN:GetBool() then return end if not system.HasFocus() then return end local ply = LocalPlayer() if ply:WaterLevel() > 0 then return end render.UpdateScreenEffectTexture() blur_mat:SetTexture("$BASETEXTURE", render.GetScreenEffectTexture()) blur_mat:SetTexture("$DEPTHTEXTURE", render.GetResolvedFullFrameDepth()) blur_mat:SetFloat("$size", BOKEN_FOCUS * 3) blur_mat:SetFloat("$focus", BOKEN_FOCUS) blur_mat:SetFloat("$focusradius", 2 - BOKEN_FORCE * 2) render.SetMaterial(blur_mat) render.DrawScreenQuad() end local function NeedsDepthPass() if not ADOF.Render then return end if not SHOULD_DRAW_BOKEN then return end if not ENABLE_BOKEN:GetBool() then return end return true end hook.Add('PostDrawTranslucentRenderables', 'ADOF.Draw', function(a, b) if a or b then return end if not DRAW_MODE:GetBool() then Render() end end, 2) --Lower priority hook.Add('PreDrawEffects', 'ADOF.Draw', function() if DRAW_MODE:GetBool() then Render() end end, 2) --Lower priority hook.Add('RenderScreenspaceEffects', 'ADOF.Draw', RenderScreenspaceEffects) hook.Add('HUDPaintBackground', 'ADOF.Draw', HUDPaintBackground) hook.Add('PostDrawHUD', 'ADOF.Draw', PostDrawHUD) hook.Add('NeedsDepthPass', 'ADOF.Draw', NeedsDepthPass) local last = 0 local lastdist = 0 local LastHitWasEntity = false local EntityHitCooldown = 0 local FocusCooldown = 0 local Focused = false local mult = 5 local BokenCooldown = 0 local function Change(old, new) local delta = new - old if delta >= 0 then return math.Clamp(old + delta^(1/2), old, new) else return math.Clamp(old - (-delta)^(1/2), new, old) end end local function ShouldEnabledScreen() if pace and pace.IsActive and pace.IsActive() then return false end return true end local function pointInsideBox(point, mins, maxs) return mins.x < point.x and point.x < maxs.x and mins.y < point.y and point.y < maxs.y and mins.z < point.z and point.z < maxs.z end local function Think() if not ix.option.Get("enableADOF", false) then ADOF.Render = false return else ADOF.Render = true end if not system.HasFocus() then return end local ply = LocalPlayer() local obs = ply:GetObserverTarget() local observer = false if IsValid(obs) and obs:IsPlayer() then ply = obs observer = true end local trace = {} local eye, langles, ignoreEnts if observer then eye = ply:EyePos() langles = ply:EyeAngles() else if not ply:ShouldDrawLocalPlayer() then eye = ply:EyePos() langles = ply:EyeAngles() if ply:InVehicle() then langles = ply:GetVehicle():GetAngles() + langles end else eye = EyePos() langles = EyeAngles() ignoreEnts = true end end local FILTER = {} trace.start = eye trace.endpos = langles:Forward() * 300 + eye trace.filter = function(ent) if ent == ply then return false end if ent:GetClass() == 'func_breakable_surf' then return false end if FILTER[ent] then return false end if ignoreEnts and (pointInsideBox(eye, ent:WorldSpaceAABB()) or eye:DistToSqr(ent:GetPos()) < 400) then return false end return true end if ply:InVehicle() then if IsValid(ply:GetVehicle()) then FILTER[ply:GetVehicle()] = true if IsValid(ply:GetVehicle():GetParent()) then FILTER[ply:GetVehicle():GetParent()] = true end end end for _, ent in pairs(ents.FindInSphere(eye, 32)) do --Finding ents what are parented to player (player seats on player, etc.) if ent:GetParent() == ply then FILTER[ent] = true end if ent:IsPlayer() and ent ~= ply and ent:InVehicle() and IsValid(ent:GetVehicle()) and ent:GetVehicle():GetParent() == ply then FILTER[ent] = true end end local tr = util.TraceLine(trace) local dist = tr.HitPos:Distance(ply:GetPos()) if IsValid(tr.Entity) then if tr.Entity:IsPlayer() and langles.p > 20 and tr.Entity:GetPos().z - LocalPlayer():GetPos().z < -30 then --We are looking at player when standing on him dist = dist*4 elseif langles.p > 25 then --Small thing dist = dist*2 end if tr.Entity:IsPlayer() then dist = math.max(dist, 50) end end if ((IsValid(tr.Entity) and dist < 300) or dist < 200) and FocusCooldown < CurTime() then if not Focused then FocusCooldown = CurTime() + 0.3 Focused = true end local offset = 40 last = CurTime() + 0.4 if not IsValid(tr.Entity) then offset = 20 if ShouldDrawOnScreen then SPACING = SPACING * 1.05 SPACE = SPACE * 1.05 if SPACE > 2000 then ShouldDrawOnScreen = false end end else LastHitWasEntity = true EntityHitCooldown = CurTime() + 1 local dist = dist / 2 if tr.Entity:IsPlayer() and dist < 60 and ShouldEnabledScreen() then if tr.Entity:InVehicle() then dist = dist * .75 end SPACE = Change(SPACE, math.max(dist * 6, 100)) SPACING = Change(SPACING, dist / 3) ShouldDrawOnScreen = true else if ShouldDrawOnScreen then SPACING = SPACING * 1.05 SPACE = SPACE * 1.05 if SPACE > 2000 then ShouldDrawOnScreen = false end end end end if not (ADOF.OFFSET - ADOF.OFFSET * FrameTime()*mult < dist + dist - offset and ADOF.OFFSET + ADOF.OFFSET * FrameTime() * mult > dist + dist - offset) then if ADOF.OFFSET - ADOF.OFFSET * FrameTime() * mult < dist + dist - offset then ADOF.OFFSET = math.max(ADOF.OFFSET + ADOF.OFFSET * FrameTime() * mult, dist + dist - offset) else ADOF.OFFSET = math.max(ADOF.OFFSET - ADOF.OFFSET * FrameTime() * mult, dist + dist - offset) end end if ENABLE_BOKEN:GetBool() then if dist < 150 and (not IsValid(tr.Entity) or not (tr.Entity:IsPlayer() or tr.Entity:GetClass() == 'prop_door_rotating')) then --We are looking at the thing, not player SHOULD_DRAW_BOKEN = true BOKEN_FOCUS = math.Clamp(0.6 - dist / 150, 0,1) BokenCooldown = CurTime() + 2 BOKEN_FORCE = math.Clamp(BOKEN_FORCE + BOKEN_STEP * (FrameTime() * 66), 0,1) elseif dist < 150 and IsValid(tr.Entity) then SHOULD_DRAW_BOKEN = true BOKEN_FOCUS = math.Clamp(0.5 - dist / 90, 0,1) BokenCooldown = CurTime() + 2 BOKEN_FORCE = math.Clamp(BOKEN_FORCE + BOKEN_STEP * (FrameTime() * 66), 0,1) else --We are too far or not looking at anything if BokenCooldown < CurTime() then SHOULD_DRAW_BOKEN = false BOKEN_FOCUS = 0.1 else BOKEN_FORCE = math.Clamp(BOKEN_FORCE - BOKEN_STEP * (FrameTime() * 66), 0,1) end end end if not IsValid(tr.Entity) and LastHitWasEntity and EntityHitCooldown < CurTime() then lastdist = dist LastHitWasEntity = false elseif not IsValid(tr.Entity) and not LastHitWasEntity then lastdist = dist elseif IsValid(tr.Entity) then lastdist = dist end ADOF.SPACING = Change(ADOF.SPACING, ((lastdist - 40)^2)/6) elseif last < CurTime() then ADOF.OFFSET = math.min(ADOF.OFFSET + 80 * (FrameTime() * 66), ADOF.Max) ADOF.SPACING = math.min(ADOF.SPACING + 80 * (FrameTime() * 66), 400) LastHitWasEntity = false if ENABLE_BOKEN:GetBool() then if ADOF.OFFSET > 200 and BokenCooldown < CurTime() then SHOULD_DRAW_BOKEN = false BOKEN_FOCUS = 0.1 BOKEN_FORCE = math.Clamp(BOKEN_FORCE - BOKEN_STEP * (FrameTime() * 66), 0,1) elseif BokenCooldown > CurTime() and ADOF.OFFSET > 200 then BOKEN_FORCE = math.Clamp(BOKEN_FORCE - BOKEN_STEP * (FrameTime() * 66), 0,1) end end if ShouldDrawOnScreen then SPACING = SPACING * 1.2 SPACE = SPACE * 1.2 if SPACE > 2000 then ShouldDrawOnScreen = false end end if Focused then FocusCooldown = 0 Focused = false end end if ADOF.OFFSET > ADOF.Critical then local delta = (ADOF.OFFSET - ADOF.Critical) / (ADOF.Max - ADOF.Critical) local MAX = ADOF_PASSES:GetInt() local count = math.ceil(delta * MAX / 1.2) ADOF.NUM_DOF_NODES = MAX - count else ADOF.NUM_DOF_NODES = ADOF_PASSES:GetInt() end end hook.Add("Think", "ADOF", Think)