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

--[[-------------------------------------------------------------------------
Render clouds
---------------------------------------------------------------------------]]
local cos,sin,rad = math.cos,math.sin,math.rad
local max,min,clamp,ceil,abs = math.max,math.min,math.Clamp,math.ceil,math.abs
local z_level = -.8
local eye_mult = -.0001

-- Generate dome mesh
	local Render_Dome = Mesh()
	local top_height = 20
	local sc = 20

	local stage = 0
	local e_r = rad(45)
	local t_s = 1
	local function UVMulti(uv,mul)
		return (uv - 0.5) * mul + 0.5
	end
	mesh.Begin( Render_Dome, MATERIAL_TRIANGLES, 24 )
		for i = 1,8 do
			local yaw = rad(45 * i)
			-- Generate the top
			-- L
			local c,s = cos(yaw),sin(yaw)
			local L = {Vector(c * sc,s * sc,0.1 * -sc),(1 + c) / 2 * t_s,(1 + s) / 2 * t_s}
			mesh.Position(L[1])
			mesh.TexCoord( stage, L[2], L[3])
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()
			-- R
			local c,s = cos(yaw + e_r),sin(yaw + e_r)
			local R = {Vector(c * sc,s * sc,0.1 * -sc),(1 + c) / 2 * t_s,  (1 + s) / 2 * t_s}
			mesh.Position(R[1])
			mesh.TexCoord( stage, R[2],R[3] )
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()
			-- T
			mesh.Position(Vector(0,0,0.1 * top_height))
			mesh.TexCoord( stage, 0.5 * t_s,0.5 * t_s )
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()

			-- Generate side1
			mesh.Position(L[1])
			mesh.TexCoord( stage, L[2], L[3])
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()

			local R2 = {R[1] * 1.4 - Vector(0,0,4),UVMulti(R[2],1.4),UVMulti(R[3],1.4)}
			mesh.Position(R2[1])
			mesh.TexCoord( stage, R2[2],R2[3] )
			mesh.Color(255,255,255,0)
			mesh.AdvanceVertex()

			mesh.Position(R[1])
			mesh.TexCoord( stage, R[2],R[3] )
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()

			-- Generate side 2
			mesh.Position(L[1])
			mesh.TexCoord( stage, L[2], L[3])
			mesh.Color(255,255,255,255)
			mesh.AdvanceVertex()

			mesh.Position(L[1] * 1.4 - Vector(0,0,4))
			mesh.TexCoord( stage, UVMulti(L[2], 1.4), UVMulti(L[3],1.4))
			mesh.Color(255,255,255,0)
			mesh.AdvanceVertex()

			mesh.Position(R2[1])
			mesh.TexCoord( stage, R2[2],R2[3] )
			mesh.Color(255,255,255,0)
			mesh.AdvanceVertex()
		end
	mesh.End()
-- Local functions
	local matrix = Matrix()
	local function RenderDome(pos,mat,alpha)
		matrix:Identity()
		matrix:Translate( vector_origin + pos )
		--mat:SetAlpha(alpha)
		cam.PushModelMatrix(matrix)
			render.SetBlend(alpha / 255)
			render.SetMaterial(mat)
			Render_Dome:Draw()
			render.SetBlend(1)
		cam.PopModelMatrix()
	end
	local lastRT
	local function RTRender(RT,blend)
		lastRT = RT
		render.PushRenderTarget( RT )
			render.ClearDepth()
			render.Clear( 0, 0, 0, 0 )
			cam.Start2D()
		if not blend then return end
			render.OverrideAlphaWriteEnable( true, true )
	end
	local function RTMask(srcBlend,destBlend,srcBlendAlpha,destBlendAlpha)
		local srcBlend = 		srcBlend or BLEND_ZERO
		local destBlend = 		destBlend or BLEND_SRC_ALPHA	-- 
		local blendFunc = 		0	-- The blend mode used for drawing the color layer 
		local srcBlendAlpha = 	srcBlendAlpha or BLEND_DST_ALPHA	-- Determines how a rendered texture's final alpha should be calculated.
		local destBlendAlpha = 	destBlendAlpha or BLEND_ZERO	-- 
		local blendFuncAlpha = 	0	-- 
		render.OverrideBlend( true, srcBlend, destBlend, blendFunc, srcBlendAlpha, destBlendAlpha, blendFuncAlpha)
	end
	local function RTEnd(Mat_Output)
		render.OverrideBlend( false )
		render.OverrideAlphaWriteEnable( false )
		cam.End2D()
		render.PopRenderTarget()
		-- Apply changes
			Mat_Output:SetTexture("$basetexture",lastRT)
	end
	local function DrawTextureRectWindow(w,h,o_x,o_y) -- Render function that supports fractions (surface libary is whole numbers only)
		if o_x < 0 then o_x = o_x + w end
		if o_y < 0 then o_y = o_y + h end
		o_x = o_x % w
		o_y = o_y % h
		local m = Matrix()
		m:Identity()
		m:Translate(Vector(o_x % w,o_y % h))
		cam.PushModelMatrix(m)
			surface.DrawTexturedRect(0,0,w,h)
			surface.DrawTexturedRect(-w,0,w,h)
			surface.DrawTexturedRect(0,-h,w,h)
			surface.DrawTexturedRect(-w,-h,w,h)
		cam.PopModelMatrix()
	end
-- Load materials
	-- Side clouds
	local side_clouds = {}
	for _,fil in ipairs(file.Find("materials/stormfox2/effects/clouds/side_cloud*.png","GAME")) do
		local png = Material("stormfox2/effects/clouds/" .. fil,"nocull noclamp alphatest")
		png:SetInt("$flags",2099250)
		table.insert(side_clouds,{png,png:GetInt("$realwidth") / png:GetInt("$realheight")})
	end
	-- Top clouds
	local layers = 4
	local sky_mats = {}
	local offset = {}
	local params = {}
		params[ "$basetexture" ] = ""
		params[ "$translucent" ] = 0
		params[ "$vertexalpha" ] = 1
		params[ "$vertexcolor" ] = 1
		params[ "$nofog" ] = 1
		params[ "$nolod" ] = 1
		params[ "$nomip" ] = 1
		params["$additive"] = 0
	for i = 1,layers do
		sky_mats[i] = CreateMaterial("StormFox_RTSKY" .. i,"UnlitGeneric",params)
	end
	local cloudbig = Material("stormfox2/effects/clouds/clouds_big.png","nocull noclamp smooth")
	-- 8240
		cloudbig:SetInt("$flags",2099250)
		cloudbig:SetFloat("$nocull",1)
		cloudbig:SetFloat("$nocull",1)
		cloudbig:SetFloat("$additive",0)
	local sky_rts = {}
	local texscale = 512
	for i = 1,layers do
		sky_rts[i] = GetRenderTargetEx( "StormFox_Sky" .. i, texscale, texscale, 1, MATERIAL_RT_DEPTH_NONE, 2, CREATERENDERTARGETFLAGS_UNFILTERABLE_OK, IMAGE_FORMAT_RGBA8888)
		offset[i] = {i * 99,i * 33}
	end
	local function safeCall(...)
		hook.Run("StormFox2.2DSkybox.CloudLayerRender", ...)
	end
	local function UpdateCloudMaterial(layer,cloud_alpha)
		local blend = true
		local d_seed = layer * 33
		render.PushFilterMag( TEXFILTER.ANISOTROPIC )
		render.PushFilterMin( TEXFILTER.ANISOTROPIC )
		-- Start RT render
			RTRender(sky_rts[layer],blend)
		-- Render RT texture
			surface.SetMaterial(cloudbig)
			surface.SetDrawColor(Color(255,255,255,cloud_alpha))
			--surface.DrawTexturedRect(0,0,texscale,texscale)
			DrawTextureRectWindow(texscale,texscale,offset[layer][1] + d_seed,offset[layer][2] + d_seed)
			-- If we error in here, gmod will crash.
			local b, reason = pcall(safeCall, texscale, texscale, layer)
			if not b then ErrorNoHalt(reason) end
		-- Mask RT tex
		--	RTMask()
		--		surface.SetDrawColor(Color(255,255,255,255 - cloud_alpha))
		--		surface.SetMaterial(cloudbig)
		--		DrawTextureRectWindow(texscale,texscale,offset[layer][1] + d_seed,offset[layer][2] + d_seed)

		-- End RT tex
			RTEnd(sky_mats[layer])
		render.PopFilterMag()
	render.PopFilterMin()
	end
	local col = Color(255,255,255,175)
	local v = Vector(0,0,-20)
	local function RenderCloud(mat_id,yaw,s_size,alpha, pos)
		local mat = side_clouds[mat_id]
		if not mat then return end
		render.SetMaterial(mat[1])
		local pitch = 0.11 * s_size
		local n = Angle(pitch,yaw,0):Forward()
		col.a = math.max(175 * alpha, 255)
		render.DrawQuadEasy( n * -200 + pos + v, n, s_size * mat[2] , s_size, col, 180 )
	end
	local function LerpColor(f, col1, col2)
		return Color( Lerp(f, col1.r, col2.r), Lerp(f, col1.g, col2.g), Lerp(f, col1.b, col2.b) )
	end
-- Cloud movement
	hook.Add("PreRender","StormFox2.Client.CloudMove",function()
		local w_ang = rad(StormFox2.Wind.GetYaw())
		local w_force = max(StormFox2.Wind.GetForce(),0.1) * 0.08 * RealFrameTime()
		local x_w,y_w = cos(w_ang) * w_force,sin(w_ang) * w_force
		for i = 1,layers do
			local ri = (layers - i + 1)
			local x,y = offset[i][1],offset[i][2]
			offset[i] = {x + x_w * ri ,y + y_w * ri}
		end
	end)

hook.Add("StormFox2.2DSkybox.CloudLayer","StormFox2.Client.Clouds",function(eye)
	if not StormFox2 then return end
	if not StormFox2.Mixer then return end
	local cl_amd = StormFox2.Mixer.Get("clouds",0)
	--if cl_amd <= 0 then return end
		-- Update material-color
		local c = StormFox2.Mixer.Get("bottomColor") or Color(204, 255, 255)
		-- Render sideclouds
		local vec = Vector(c.r,c.g,c.b) / 255
		for k,v in ipairs(side_clouds) do
			v[1]:SetVector("$color",vec)
		end
		local cloud_speed = StormFox2.Time.GetSpeed_RAW() * 0.1
		local sideclouds = 10 * cl_amd
		for i = 1,sideclouds do
			local a = 1
			if i < sideclouds and i == math.floor(sideclouds) then
				a = sideclouds - math.floor(sideclouds)
			end
			local row = math.floor(i / 3)
			local m_id = i % #side_clouds + 1
			local y_start = (i % 3) * 120 + row * 33
			local size = (3 + i % 5)  * 24
			RenderCloud(m_id,y_start + i + SysTime() * cloud_speed, size, a, eye * eye_mult * 10 * i / 10 )
		end
		-- Render top clouds
		local up = Vector(0,0,1)
		local n = max(0,min(math.ceil(layers * cl_amd),layers))
		local thunder = 0
		if StormFox2.Thunder then
			thunder = min(255,StormFox2.Thunder.GetLight() or 0) / 25
		end
		for i = 1,n do
			local ri = n - i + layers
			local cloud_amplifier = 1 + .4 * (1 -  (i / n))
			if i == 1 then
				cloud_amplifier = cloud_amplifier + thunder
			end
			UpdateCloudMaterial(i,255)
			sky_mats[i]:SetVector("$color",Vector(min(c.r * cloud_amplifier,255),min(c.g * cloud_amplifier,255),min(c.b * cloud_amplifier,255)) / 255 )
			RenderDome(up * (z_level + 0.4 * ri) + eye * eye_mult,sky_mats[i],255)
		end
end)