mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
339 lines
10 KiB
Lua
339 lines
10 KiB
Lua
--[[
|
|
| 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/
|
|
--]]
|
|
|
|
local INVALID_VERTS = 0
|
|
local WATER_VERTS = 1
|
|
|
|
StormFox2.Setting.AddSV("enable_ice",not game.IsDedicated())
|
|
StormFox2.Setting.AddSV("enable_wateroverlay",true, nil, "Effects")
|
|
|
|
--[[-------------------------------------------------------------------------
|
|
Localize
|
|
---------------------------------------------------------------------------]]
|
|
local round = math.Round
|
|
local util_TraceLine = util.TraceLine
|
|
local table_sort = table.sort
|
|
local LocalPlayer = LocalPlayer
|
|
--[[-------------------------------------------------------------------------
|
|
Make a few SurfaceInfo functions.
|
|
---------------------------------------------------------------------------]]
|
|
local meta = FindMetaTable("SurfaceInfo")
|
|
-- Support caching stuff
|
|
local surf_caching = {}
|
|
meta.__index = function(a,b)
|
|
return meta[b] or surf_caching[a] and surf_caching[a][b]
|
|
end
|
|
meta.__newindex = function(a,b,c)
|
|
if not surf_caching[a] then surf_caching[a] = {} end
|
|
surf_caching[a][b] = c
|
|
end
|
|
hook.Add("EntityRemoved", "ClearSurfaceInfo", function(ent)
|
|
for _,surf in ipairs(ent:GetBrushSurfaces() or {}) do
|
|
surf_caching[surf] = nil
|
|
end
|
|
end)
|
|
function meta:IsValid( )
|
|
if self.b_invalid ~= nil then return self.b_invalid end
|
|
local b = #(self:GetVertices()) > 0
|
|
self.b_invalid = b
|
|
return self.b_invalid
|
|
end
|
|
function meta:GetVerticesNoParallel( )
|
|
if not self:IsValid() then return {} end
|
|
if self.v_vertNP then return table.Copy(self.v_vertNP) end
|
|
self.v_vertNP = {}
|
|
local verts = self:GetVertices()
|
|
for i,cv in ipairs(verts) do
|
|
local pv,nv = verts[i - 1] or verts[#verts], verts[i + 1] or verts[1]
|
|
local cP = ( cv - pv ):Cross( nv - pv )
|
|
if cP.x == 0 and cP.y == 0 and cP.z == 0 then continue end -- parallel vector.
|
|
table.insert(self.v_vertNP, cv)
|
|
end
|
|
return table.Copy(self.v_vertNP)
|
|
end
|
|
function meta:GetCenter( )
|
|
if not self:IsValid() then return end
|
|
if self.v_cent then return self.v_cent end
|
|
local verts = self:GetVertices()
|
|
if #verts < 2 then
|
|
self.v_cent = verts[1]
|
|
return self.v_cent
|
|
end
|
|
local vmax,vmin = verts[1],verts[1]
|
|
for i = 2,#verts do
|
|
vmax[1] = math.max(vmax[1],verts[i][1])
|
|
vmax[2] = math.max(vmax[2],verts[i][2])
|
|
vmax[3] = math.max(vmax[3],verts[i][3])
|
|
vmin[1] = math.min(vmin[1],verts[i][1])
|
|
vmin[2] = math.min(vmin[2],verts[i][2])
|
|
vmin[3] = math.min(vmin[3],verts[i][3])
|
|
end
|
|
self.v_cent = vmin + (vmax - vmin) / 2
|
|
return self.v_cent
|
|
end
|
|
function meta:GetNormal( )
|
|
if not self:IsValid() then return end
|
|
if self.v_norm then return self.v_norm end
|
|
local p = self:GetVertices()
|
|
if #p < 3 then return end -- Invalid brush. (Yes this happens)
|
|
local c = p[1]
|
|
local s = Vector(0,0,0)
|
|
for i = 2,#p do
|
|
s = s + ( p[i] - c ):Cross( (p[i + 1] or p[1]) - c )
|
|
if s.x ~= 0 and s.y ~= 0 and s.z ~= 0 then -- Check if this isn't a parallel vector.
|
|
break -- Got a valid norm
|
|
end
|
|
end
|
|
self.v_norm = s:GetNormalized()
|
|
return self.v_norm
|
|
end
|
|
function meta:GetAngles( )
|
|
if not self:IsValid() then return end
|
|
if self.a_ang then return self.a_ang end
|
|
self.a_ang = self:GetNormal():Angle()
|
|
return self.a_ang
|
|
end
|
|
function meta:GetPerimeter( )
|
|
if not self:IsValid() then return end
|
|
if self.n_peri then return self.n_peri end
|
|
local p = self:GetVertices()
|
|
local n = 0
|
|
for i = 1,#p do
|
|
n = n + p[i]:Distance(p[i + 1] or p[1])
|
|
end
|
|
self.n_peri = n
|
|
return self.n_peri
|
|
end
|
|
function meta:GetArea( )
|
|
if not self:IsValid() then return end
|
|
if self.n_area then return self.n_area end
|
|
local p = self:GetVertices()
|
|
local n = #p
|
|
if n < 3 then -- Invalid shape
|
|
self.n_area = 0
|
|
return 0
|
|
--elseif n == 3 then -- Triangle, but cost more?
|
|
-- local a,b,c = p[1]:Distance(p[2]),p[2]:Distance(p[3]),p[3]:Distance(p[1])
|
|
-- local s = (a + b + c) / 2
|
|
-- t_t[self] = sqrt( s * (s - a) * (s - b) * (s - c) )
|
|
-- return t_t[self]
|
|
else -- Any shape
|
|
local a = Vector(0,0,0)
|
|
for i,pc in ipairs(p) do
|
|
local pn = p[i + 1] or p[1]
|
|
a = a + pc:Cross(pn)
|
|
end
|
|
a = a / 2
|
|
self.n_area = a:Distance(Vector(0,0,0))
|
|
return self.n_area
|
|
end
|
|
end
|
|
--[[-------------------------------------------------------------------------
|
|
Make some adv SurfaceInfo functions.
|
|
---------------------------------------------------------------------------]]
|
|
function meta:GetUVVerts() -- Creates UV-data out from the shape.
|
|
if not self:IsValid() then return end
|
|
if self.t_uv then return table.Copy(self.t_uv) end
|
|
local t = self:GetVerticesNoParallel()
|
|
local a = self:GetNormal():Angle()
|
|
local c = self:GetCenter()
|
|
local vmin,vmax
|
|
for i,v in ipairs(t) do
|
|
t[i] = (t[i] - c)
|
|
t[i]:Rotate(a)
|
|
if not vmin then
|
|
vmin = Vector(t[i].x,t[i].y,t[i].z)
|
|
vmax = Vector(t[i].x,t[i].y,t[i].z)
|
|
else
|
|
for ii = 1,3 do
|
|
vmin[ii] = math.min(vmin[ii],t[i][ii])
|
|
vmax[ii] = math.max(vmax[ii],t[i][ii])
|
|
end
|
|
end
|
|
end
|
|
local y_r = vmax.z - vmin.z
|
|
local x_r,x_r2 = vmax.x - vmin.x,vmax.y - vmin.y
|
|
local min_x = vmin.x
|
|
local i2 = 1
|
|
if x_r2 > x_r then
|
|
x_r = x_r2
|
|
i2 = 2
|
|
min_x = vmin.y
|
|
end
|
|
local new_t = {}
|
|
for i = 1,#t do
|
|
table.insert(new_t, {u = (t[i][i2] - min_x) / x_r,v = (t[i].z - vmin.z) / y_r})
|
|
end
|
|
self.t_uv = new_t
|
|
return table.Copy(self.t_uv)
|
|
end
|
|
function meta:GetMesh() -- Generates a mesh-table for the surfaceinfo.
|
|
if not self:IsValid() then return end
|
|
if self.t_mesh then return table.Copy(self.t_mesh) end
|
|
local verts = self:GetVerticesNoParallel()
|
|
local n = self:GetNormal()
|
|
-- Calc the height and width
|
|
local h_max,h_min = verts[1].z,verts[1].z
|
|
for i = 2,#verts do
|
|
local h = verts[i].z
|
|
h_max = math.max(h_max,h)
|
|
h_min = math.min(h_min,h)
|
|
end
|
|
local uvt = self:GetUVVerts()
|
|
local t = {}
|
|
for i = 1,3 do
|
|
table.insert(t, {pos = verts[i], u = uvt[i].u,v = uvt[i].v, normal = n})
|
|
end
|
|
for i = 4,#verts do
|
|
table.insert(t, {pos = verts[1], u = uvt[1].u,v = uvt[1].v, normal = n})
|
|
table.insert(t, {pos = verts[i - 1], u = uvt[i - 1].u,v = uvt[i - 1].v, normal = n})
|
|
table.insert(t, {pos = verts[i], u = uvt[i].u,v = uvt[i].v, normal = n})
|
|
end
|
|
self.t_mesh = t
|
|
return table.Copy(self.t_mesh)
|
|
end
|
|
function meta:GetMinSide()
|
|
if not self:IsValid() then return end
|
|
if self.n_midi then return self.n_midi end
|
|
local mi,ma
|
|
local p = self:GetVertices()
|
|
for i = 1,#p do
|
|
if not mi then
|
|
mi = p[i]:Distance(p[i + 1] or p[1])
|
|
ma = mi
|
|
else
|
|
mi = math.min(mi,p[i]:Distance(p[i + 1] or p[1]))
|
|
ma = math.max(ma,p[i]:Distance(p[i + 1] or p[1]))
|
|
end
|
|
end
|
|
self.n_midi = mi
|
|
self.n_madi = ma
|
|
return mi
|
|
end
|
|
function meta:GetMaxSide()
|
|
if not self:IsValid() then return end
|
|
if self.n_madi then return self.n_madi end
|
|
self:GetMinSide()
|
|
return self.n_madi
|
|
end
|
|
--[[-------------------------------------------------------------------------
|
|
Generate meshes and env-points out from the map-data.
|
|
---------------------------------------------------------------------------]]
|
|
-- New surface functions
|
|
local function SurfaceInfo_GetType( eEnt, SurfaceInfo )
|
|
if #SurfaceInfo:GetVertices() < 3 then return INVALID_VERTS end
|
|
if SurfaceInfo:IsWater() then -- Water
|
|
return WATER_VERTS
|
|
end
|
|
return INVALID_VERTS
|
|
end
|
|
|
|
|
|
local ice = Material("stormfox2/effects/ice_water")
|
|
local ice_size = 500
|
|
local vec_ex = Vector(0,0,1)
|
|
|
|
STORMFOX_WATERMESHCOLLISON = {}
|
|
|
|
local scan = function() -- Locates all surfaceinfos we need.
|
|
StormFox2.Msg("Scanning surfaces ..")
|
|
surfaceinfos = {}
|
|
-- Scan all brushsurfaces and grab the glass/windows, water and metal. Put them in a table with matching normal.
|
|
for i,v in ipairs( game.GetWorld():GetBrushSurfaces() ) do
|
|
if not v then continue end
|
|
if not v:IsValid() then continue end
|
|
if not v:IsWater() then continue end
|
|
local v_type = SurfaceInfo_GetType( game.GetWorld(), v )
|
|
if v_type == INVALID_VERTS then continue end -- Invalid or doesn't have a type.
|
|
if not surfaceinfos[v_type] then surfaceinfos[v_type] = {} end
|
|
table.insert(surfaceinfos[v_type], {v, v:GetCenter()} )
|
|
end
|
|
coroutine.yield()
|
|
-- Generate water mesh
|
|
if surfaceinfos[WATER_VERTS] then
|
|
StormFox2.Msg("Generating ice-mesh [" .. #surfaceinfos[WATER_VERTS] .. "] ")
|
|
local mesh = {}
|
|
for i,v in ipairs(surfaceinfos[WATER_VERTS]) do
|
|
if StormFox2.Map.IsInside( v[2] ) then
|
|
local t = v[1]:GetVertices()
|
|
if #t >= 3 then
|
|
local t2 = {}
|
|
if #t == 3 then
|
|
for i = 1,3 do
|
|
table.insert(t2, t[i] + vec_ex)
|
|
table.insert(t2, t[i] - vec_ex)
|
|
end
|
|
table.insert(t2, t[3] + vec_ex)
|
|
table.insert(t2, t[3] - vec_ex)
|
|
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
|
elseif #t == 4 then
|
|
for i = 1,4 do
|
|
table.insert(t2, t[i] + vec_ex)
|
|
table.insert(t2, t[i] - vec_ex)
|
|
end
|
|
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
|
else
|
|
for i = 1,#t do
|
|
table.insert(t2, t[i] + vec_ex)
|
|
table.insert(t2, t[i] - vec_ex)
|
|
end
|
|
table.insert(STORMFOX_WATERMESHCOLLISON, t2)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
coroutine.yield(true)
|
|
end
|
|
|
|
local cor_scan = coroutine.wrap(scan)
|
|
local function StartGenerating()
|
|
timer.Create("SF_ENV_SCAN", 0.2, 0, function()
|
|
if cor_scan() then
|
|
cor_scan = nil
|
|
timer.Remove("SF_ENV_SCAN")
|
|
StormFox2.Msg("Meshes completed.")
|
|
end
|
|
end)
|
|
hook.Remove("StormFox2.InitPostEntity", "StormFox_ENV_SCAN")
|
|
end
|
|
hook.Add("StormFox2.InitPostEntity", "StormFox_ENV_SCAN", StartGenerating)
|
|
|
|
local bIce = false
|
|
local function SpawnIce()
|
|
for k,v in ipairs(ents.FindByClass("stormfox_mapice")) do
|
|
v:Remove()
|
|
end
|
|
local e = ents.Create("stormfox_mapice")
|
|
e:SetPos(Vector(0,0,0))
|
|
e:Spawn()
|
|
bIce = true
|
|
end
|
|
|
|
local function RemoveIce()
|
|
bIce = false
|
|
for k,v in ipairs(ents.FindByClass("stormfox_mapice")) do
|
|
v:Remove()
|
|
end
|
|
end
|
|
|
|
timer.Create("stormfox2.spawnice", 8, 0, function()
|
|
if not StormFox2.Setting.GetCache("enable_ice") then
|
|
if bIce then
|
|
RemoveIce()
|
|
end
|
|
return
|
|
end
|
|
if bIce and StormFox2.Temperature.Get() > -1 then
|
|
RemoveIce()
|
|
elseif not bIce and StormFox2.Temperature.Get() <= -8 then
|
|
SpawnIce()
|
|
end
|
|
end) |