From f46fbf246c521b4df41973aa9dc031e99b7f9bc8 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Sun, 10 Oct 2021 15:53:26 +0300 Subject: [PATCH 01/18] Improve the controlability of the CAP traces --- lua/stargate/shared/tracelines.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 6b517ed1..239e06b4 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -23,6 +23,15 @@ StarGate.Trace = StarGate.Trace or {}; StarGate.Trace.Entities = StarGate.Trace.Entities or {}; StarGate.Trace.Classes = StarGate.Trace.Classes or {}; +StarGate.Trace.Data = { + start = Vector(), -- The start position of the trace + endpos = Vector(), -- The end position of the trace + filter = nil, -- Things the trace should not hit + mask = MASK_SOLID, -- This determines what the trace should hit + collisiongroup = COLLISION_GROUP_NONE, -- What the trace should hit collision group regards + ignoreworld = false, -- Should the trace ignore world or not + output = nil -- Result will be written here instead of returning a new table +} --################# Deephook to ents.Create serverside @aVoN if SERVER then @@ -145,7 +154,7 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) end --################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN -function StarGate.Trace:New(start,dir,ignore) +function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less reouces!) if CLIENT then for k,_ in pairs(self.Classes) do @@ -154,7 +163,15 @@ function StarGate.Trace:New(start,dir,ignore) end end end - local trace = util.QuickTrace(start,dir,ignore); + + self.Data.start:Set(start) + self.Data.endpos:Set(dir); data.endpos:Add(start) + self.Data.filter = ignore + self.Data.mask = mask + self.Data.collisiongroup = colgrp + self.Data.ignoreworld = iworld + local trace = util.TraceLine(self.Data) + --This is better and faster than using table.HasValue(ignore,e) (nested for loops) local quick_ignore = {}; if(type(ignore) == "table") then From 05a640758f69ce73c04e2c4708e0f069a35cfd37 Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Mon, 11 Oct 2021 08:52:12 +0300 Subject: [PATCH 02/18] Added: Use correct default type/values when not provided --- lua/stargate/shared/tracelines.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 239e06b4..8e074c9f 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -167,9 +167,9 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) self.Data.start:Set(start) self.Data.endpos:Set(dir); data.endpos:Add(start) self.Data.filter = ignore - self.Data.mask = mask - self.Data.collisiongroup = colgrp - self.Data.ignoreworld = iworld + self.Data.mask = tonumber(mask) or MASK_SOLID + self.Data.collisiongroup = tonumber(colgrp) or COLLISION_GROUP_NONE + self.Data.ignoreworld = tobool(iworld) local trace = util.TraceLine(self.Data) --This is better and faster than using table.HasValue(ignore,e) (nested for loops) From 2ae0eea02626285bbfec688cdd948956fc04242d Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Wed, 13 Oct 2021 09:15:45 +0300 Subject: [PATCH 03/18] Optimized: Traces minimal distance now uses integer for-loop --- lua/stargate/shared/tracelines.lua | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 8e074c9f..942138db 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -185,7 +185,7 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) local norm_world = dir:GetNormal(); -- Get Normal of the dir vector (world coordinates!) -- We need to sort all entities first according to their distance to the trace-start, or we hit a prop behind a prop instead of the one infront -- Problem noticed by Lynix here: http://img140.imageshack.us/img140/7589/gmflatgrass0017bj9.jpg - local traced_entities = {} -- Lynix modification + local trace_array = {} -- Lynix modification for e,_ in pairs(self.Entities) do if(not quick_ignore[e]) then @@ -237,7 +237,7 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) hit = self:CheckCoordinate("z",pos,norm,v.Min,v.Max,len,in_box); end - -- very ugly, but atleast works, with bugs... + -- Very ugly, but atleast works, with bugs... -- I have no idea how make function "CheckCoordinate" works with sphere @ AlexALX if (not hit and class=="shield" and not in_box and self:InBox(pos,v.Min,v.Max)) then hit = {HitPos=pos,Fraction=0.8,HitNormal=norm} @@ -261,9 +261,9 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) trace.Fraction = hit.Fraction; trace.HitNormal = e:LocalToWorld(hit.HitNormal)-e_pos; trace.Entity = e; - table.insert(traced_entities,table.Copy(trace)); -- Lynix modification - --break; + table.insert(trace_array, table.Copy(trace)); -- Lynix modification end + if(hit2) then -- Update the trace data with new and correct values, my values are already scaled so i made another if condition @Mad trace.Hit = true; @@ -280,8 +280,7 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) trace.Fraction = hit2.Fraction; trace.HitNormal = hit2.HitNormal; trace.Entity = e; - table.insert(traced_entities,table.Copy(trace)); -- Lynix modification - --break; + table.insert(trace_array, table.Copy(trace)); -- Lynix modification end end @@ -289,16 +288,16 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) end end -- START OF Lynix modification - if(#traced_entities > 0) then - local min = 10000000000; -- Random startvalue - for _,v in pairs(traced_entities) do - local dist = trace.StartPos:Distance(v.HitPos); - if(dist < min) then - min = dist; - trace = v; - end + local min; -- Minimum distance is the first entry @dvdvideo1234 + for i = 1, #trace_array do -- Interger loop for arrays @dvdvideo1234 + local v = trace_array[i] + local m = trace.StartPos:Distance(v.HitPos); + if(not min or m < min) then + min = m; + trace = v; end end + -- END OF Lynix modification return trace; end From df5a0f72ffdb8acec209189ae88cecdc74a0f312 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Fri, 22 Oct 2021 19:04:42 +0300 Subject: [PATCH 04/18] Added: Support for non-zero width traces --- lua/stargate/shared/tracelines.lua | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 942138db..904a55a0 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -24,8 +24,11 @@ StarGate.Trace = StarGate.Trace or {}; StarGate.Trace.Entities = StarGate.Trace.Entities or {}; StarGate.Trace.Classes = StarGate.Trace.Classes or {}; StarGate.Trace.Data = { + code = util.TraceLine, -- Trace code to be executed start = Vector(), -- The start position of the trace endpos = Vector(), -- The end position of the trace + mins = Vector(), -- The lowest corner of the trace + maxs = Vector(), -- The highest corner of the trace filter = nil, -- Things the trace should not hit mask = MASK_SOLID, -- This determines what the trace should hit collisiongroup = COLLISION_GROUP_NONE, -- What the trace should hit collision group regards @@ -154,7 +157,7 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) end --################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN -function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) +function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less reouces!) if CLIENT then for k,_ in pairs(self.Classes) do @@ -164,14 +167,24 @@ function StarGate.Trace:New(start,dir,ignore,mask,colgrp,iworld) end end + -- Setup trace parameters and routine code self.Data.start:Set(start) self.Data.endpos:Set(dir); data.endpos:Add(start) self.Data.filter = ignore self.Data.mask = tonumber(mask) or MASK_SOLID - self.Data.collisiongroup = tonumber(colgrp) or COLLISION_GROUP_NONE + self.Data.collisiongroup = tonumber(cogrp) or COLLISION_GROUP_NONE self.Data.ignoreworld = tobool(iworld) - local trace = util.TraceLine(self.Data) - + if(width) then local m = width / 2 + self.Data.mins:SetUnpacked(-m, -m, -m) + self.Data.maxs:SetUnpacked( m, m, m) + self.Data.code = util.TraceHull + else -- No width. Fall back to zero width trace + self.Data.code = util.TraceLine + end + + -- Run the trace when setup is ready and code is picked + local trace = self.Data.code(self.Data) + --This is better and faster than using table.HasValue(ignore,e) (nested for loops) local quick_ignore = {}; if(type(ignore) == "table") then From ad4e8110fca8f545aab111a9928e2890135da92c Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Fri, 22 Oct 2021 22:47:15 +0300 Subject: [PATCH 05/18] Fixed: Use the proper key when doing a trace Optimized: Localize trace.start for speed --- lua/stargate/shared/tracelines.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 904a55a0..4bf3156c 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -301,10 +301,10 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) end end -- START OF Lynix modification - local min; -- Minimum distance is the first entry @dvdvideo1234 + local anc, min = trace.start -- First entry minumum @dvdvideo1234 for i = 1, #trace_array do -- Interger loop for arrays @dvdvideo1234 local v = trace_array[i] - local m = trace.StartPos:Distance(v.HitPos); + local m = anc:Distance(v.HitPos) if(not min or m < min) then min = m; trace = v; From 4a6881f28458f409d81c1491db55444011e8f2dc Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Thu, 4 Nov 2021 09:36:33 +0200 Subject: [PATCH 06/18] Fixed: Move trace code outside of data Optimized: Speed of trace pick-up routine --- lua/stargate/shared/tracelines.lua | 72 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 4bf3156c..c179f0a0 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -1,4 +1,4 @@ -/* +--[[ Stargate Lib for GarrysMod10 Copyright (C) 2007 aVoN @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . -*/ +]] --######################################### -- Traclines - To stop them on lua drawn physboxes @@ -23,8 +23,8 @@ StarGate.Trace = StarGate.Trace or {}; StarGate.Trace.Entities = StarGate.Trace.Entities or {}; StarGate.Trace.Classes = StarGate.Trace.Classes or {}; +StarGate.Trace.Code = util.TraceLine; -- Trace code to be executed StarGate.Trace.Data = { - code = util.TraceLine, -- Trace code to be executed start = Vector(), -- The start position of the trace endpos = Vector(), -- The end position of the trace mins = Vector(), -- The lowest corner of the trace @@ -36,7 +36,7 @@ StarGate.Trace.Data = { output = nil -- Result will be written here instead of returning a new table } ---################# Deephook to ents.Create serverside @aVoN +-- ################# Deephook to ents.Create serverside @aVoN if SERVER then hook.Add("OnEntityCreated","StarGate.OnEntityCreated",function(e) if(not IsValid(e) or not StarGate.Trace) then return end; @@ -46,7 +46,7 @@ if SERVER then end) end ---################# Add a class to check to the tracesline calculation @aVoN +-- ################# Add a class to check to the tracesline calculation @aVoN function StarGate.Trace:Add(c,condition) self.Classes[c] = {Condition=condition}; for _,v in pairs(ents.FindByClass(c)) do @@ -54,14 +54,14 @@ function StarGate.Trace:Add(c,condition) end end ---################# Adds a condition-function to the specific entity. This function decides if the trace goes through the BoundingBox or not @aVoN +-- ################# Adds a condition-function to the specific entity. This function decides if the trace goes through the BoundingBox or not @aVoN function StarGate.Trace:AddCondition(c,condition) if(self.Classes[c]) then self.Classes[c] = {Condition=condition}; end end ---################# Remoces such a class @aVoN +-- ################# Remoces such a class @aVoN function StarGate.Trace:Remove(c) self.Classes[c] = nil; for k,_ in pairs(self.Entities) do @@ -75,7 +75,7 @@ function StarGate.Trace:Remove(c) end end ---################# Is the vector given inside the box or outside of it? @aVoN +-- ################# Is the vector given inside the box or outside of it? @aVoN function StarGate.Trace:InBox(pos,Min,Max) if( (pos.x >= Min.x and pos.x <= Max.x) and @@ -89,17 +89,17 @@ end -- Because this might also be usefull for other scripts, were defininig this global StarGate.InBox = function(a,b,c) return StarGate.Trace:InBox(a,b,c) end; ---################# Updates the entities OBB Datas @aVoN +-- ################# Updates the entities OBB Datas @aVoN function StarGate.Trace:GetEntityData(e) if(IsValid(e)) then - local time = CurTime(); - if(not self.Entities[e] or self.Entities[e].Last + 1 < time) then + local now = CurTime(); + if(not self.Entities[e] or self.Entities[e].Last + 1 < now) then local offset = e:OBBCenter(); -- We need the OBB's relatively to the Entiti's position, not to the OBBCenter self.Entities[e] = { Min = e:OBBMins()+offset, Max = e:OBBMaxs()+offset, - --Radius = e:BoudingRadius(), - Last = time, + -- Radius = e:BoudingRadius(), + Last = now, } end return true; @@ -109,7 +109,7 @@ function StarGate.Trace:GetEntityData(e) end end ---################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN +-- ################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN function StarGate.Trace:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal) local old = norm; local norm = norm*mul; -- Make the normal hit the wall @@ -128,12 +128,12 @@ function StarGate.Trace:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal) end end ---################# This checks one coordinate of the trace's normal @aVoN +-- ################# This checks one coordinate of the trace's normal @aVoN function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) -- I will not check if the trace start position is exactly on a wall, neither I will check, if the start pos is exactly in the center of this entity. -- Doing this would need me to add some more special exeptions where the probability for these cases are < 0.1% (except you are forcing it) local hit_normal = Vector(0,0,0); hit_normal[coordinate] = 1; - --################# We are inside the bounding box - Trace to one wall! + -- ################# We are inside the bounding box - Trace to one wall! if(in_box) then local mul = 0; if(norm[coordinate] > 0) then -- Norm == 0 has been avoided in StarGate.Trace:New @@ -145,7 +145,7 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) end return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal); else - --################# We are outside the bounding box. + -- ################# We are outside the bounding box. if(pos[coordinate] < Min[coordinate] and norm[coordinate] > 0) then -- We are below the Minimum and the normal goes up => We can hit local mul = math.abs((pos[coordinate] - Min[coordinate])/norm[coordinate]); -- The multiplier so the coordinate we're checking is exact on the wall's surface return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,-1*hit_normal); @@ -156,7 +156,7 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) end end ---################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN +-- ################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less reouces!) if CLIENT then @@ -177,15 +177,15 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) if(width) then local m = width / 2 self.Data.mins:SetUnpacked(-m, -m, -m) self.Data.maxs:SetUnpacked( m, m, m) - self.Data.code = util.TraceHull + self.Trace.Code = util.TraceHull else -- No width. Fall back to zero width trace - self.Data.code = util.TraceLine + self.Trace.Code = util.TraceLine end -- Run the trace when setup is ready and code is picked - local trace = self.Data.code(self.Data) + local trace = self.Trace.Code(self.Data) - --This is better and faster than using table.HasValue(ignore,e) (nested for loops) + -- This is better and faster than using table.HasValue(ignore,e) (nested for loops) local quick_ignore = {}; if(type(ignore) == "table") then for _,v in pairs(ignore) do @@ -231,14 +231,14 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) hit2 = StarGate.RayPhysicsPluckerIntersect(trace, dir2, e, a); end elseif (class == "tokra_shield") then // go ahead with my method @Mad - //if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then // check, if we intersecting bounding box - save cpu if we are not - //local a = not in_box; - ////local dir2 = dir; - //if in_box then dir2 = -1*dir end // fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) - //if (e.ShShap == 2) then a = in_box end // small fix for box shape, i fucked triangles directions - hit2 = StarGate.RayPhysicsPluckerIntersect(trace, dir, e, true); - // this code not working, need something to do @ AlexALX - //end + -- if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then // check, if we intersecting bounding box - save cpu if we are not + -- local a = not in_box; + -- local dir2 = dir; + -- if in_box then dir2 = -1*dir end // fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) + -- if (e.ShShap == 2) then a = in_box end // small fix for box shape, i fucked triangles directions + hit2 = StarGate.RayPhysicsPluckerIntersect(trace, dir, e, true); + -- this code not working, need something to do @ AlexALX + -- end else if(norm.x ~= 0) then hit = self:CheckCoordinate("x",pos,norm,v.Min,v.Max,len,in_box); @@ -300,14 +300,14 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) end end end + -- START OF Lynix modification - local anc, min = trace.start -- First entry minumum @dvdvideo1234 - for i = 1, #trace_array do -- Interger loop for arrays @dvdvideo1234 + local anc, mar = trace.start -- First entry minumum @dvdvideo1234 + for i = 1, #trace_array do -- Faster loop for arrays @dvdvideo1234 local v = trace_array[i] - local m = anc:Distance(v.HitPos) - if(not min or m < min) then - min = m; - trace = v; + local m = anc:DistToSqr(v.HitPos) -- Faster check @dvdvideo1234 + if(not mar or m < mar) then + mar, trace = m, v; end end From 18e7a803fcb000c6942fcd452c0f543e3a5ec1fe Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Fri, 5 Nov 2021 17:47:10 +0200 Subject: [PATCH 07/18] Fixed: Attempt to index a nil value `self.Trace` Added: Zero width results in running the old trace method Updated: Closest trace measured according to true start origin --- lua/stargate/shared/tracelines.lua | 56 ++++++++++++++++++------------ 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index c179f0a0..0deca3ce 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -171,19 +171,26 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) self.Data.start:Set(start) self.Data.endpos:Set(dir); data.endpos:Add(start) self.Data.filter = ignore - self.Data.mask = tonumber(mask) or MASK_SOLID - self.Data.collisiongroup = tonumber(cogrp) or COLLISION_GROUP_NONE + self.Data.mask = (tonumber(mask) or MASK_SOLID) + self.Data.collisiongroup = (tonumber(cogrp) or COLLISION_GROUP_NONE) self.Data.ignoreworld = tobool(iworld) - if(width) then local m = width / 2 - self.Data.mins:SetUnpacked(-m, -m, -m) - self.Data.maxs:SetUnpacked( m, m, m) - self.Trace.Code = util.TraceHull - else -- No width. Fall back to zero width trace - self.Trace.Code = util.TraceLine + + -- Setup trace width and routine + if(width) then -- Trace cube hull with side of width + local m = (tonumber(width) or 0) / 2 + if(m > 0) then -- Width is a valid non-zero number + self.Data.mins:SetUnpacked(-m, -m, -m) + self.Data.maxs:SetUnpacked( m, m, m) + self.Code = util.TraceHull -- Use New trace + else -- Margin must be a valid non-zero number + self.Code = util.TraceLine -- Use the old trace + end -- Otherwise falls back to using the old trace method + else -- No width so work as before. Fall back to zero width trace + self.Code = util.TraceLine end -- Run the trace when setup is ready and code is picked - local trace = self.Trace.Code(self.Data) + local trace = self.Code(self.Data) -- This is better and faster than using table.HasValue(ignore,e) (nested for loops) local quick_ignore = {}; @@ -222,22 +229,22 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) local hit; local hit2; -- We need to check to what side the start pos is the nearest and if the normal (to that side where we checking it) isn't zero - if (class == "shield_core_buble") then // go ahead with my method @Mad - if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then // check, if we intersecting bounding box - save cpu if we are not + if (class == "shield_core_buble") then -- Go ahead with my method @Mad + if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then -- Check, if we intersecting bounding box - save cpu if we are not local a = not in_box; local dir2 = dir; - if in_box then dir2 = -1*dir end // fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) - if (e.ShShap == 2) then a = in_box end // small fix for box shape, i fucked triangles directions + if in_box then dir2 = -1*dir end -- Fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) + if (e.ShShap == 2) then a = in_box end -- Small fix for box shape, i fucked triangles directions hit2 = StarGate.RayPhysicsPluckerIntersect(trace, dir2, e, a); end - elseif (class == "tokra_shield") then // go ahead with my method @Mad - -- if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then // check, if we intersecting bounding box - save cpu if we are not + elseif (class == "tokra_shield") then -- Go ahead with my method @Mad + -- if StarGate.IsRayBoxIntersect(start, trace.HitPos, e) then -- Check, if we intersecting bounding box - save cpu if we are not -- local a = not in_box; -- local dir2 = dir; - -- if in_box then dir2 = -1*dir end // fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) - -- if (e.ShShap == 2) then a = in_box end // small fix for box shape, i fucked triangles directions + -- if in_box then dir2 = -1*dir end -- Fix shoting if we are inside, and not shape - to get hitpos on right side (not opposite) + -- if (e.ShShap == 2) then a = in_box end -- Small fix for box shape, i fucked triangles directions hit2 = StarGate.RayPhysicsPluckerIntersect(trace, dir, e, true); - -- this code not working, need something to do @ AlexALX + -- This code not working, need something to do @ AlexALX -- end else if(norm.x ~= 0) then @@ -301,11 +308,16 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) end end - -- START OF Lynix modification - local anc, mar = trace.start -- First entry minumum @dvdvideo1234 - for i = 1, #trace_array do -- Faster loop for arrays @dvdvideo1234 + --[[ @Lynix @dvdvideo1234 + * First entry is considered the first minimum + * Use for-integer loop as it is way faster than pairs + * Store true trace data reference to a local and compare + * If margin is not defined will be considered as minimum + ]] + local anc, mar = self.Data.start + for i = 1, #trace_array do local v = trace_array[i] - local m = anc:DistToSqr(v.HitPos) -- Faster check @dvdvideo1234 + local m = anc:DistToSqr(v.HitPos) if(not mar or m < mar) then mar, trace = m, v; end From 863a709c9b63d04b94f3bff21979109672364b66 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Sat, 18 Dec 2021 12:20:34 +0200 Subject: [PATCH 08/18] Fixed: Some comments being too long Added: `QuickIgnore` as class method Added: Ray-sphere intersection in vector-N form `HitSphere` Removed: Unused variable `old` in `HitWall` --- lua/stargate/shared/tracelines.lua | 125 +++++++++++++++++++---------- 1 file changed, 81 insertions(+), 44 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 0deca3ce..a360ed23 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -1,5 +1,5 @@ --[[ - Stargate Lib for GarrysMod10 + Stargate Lib for Garry's Mod 10 Copyright (C) 2007 aVoN This program is free software: you can redistribute it and/or modify @@ -48,8 +48,8 @@ end -- ################# Add a class to check to the tracesline calculation @aVoN function StarGate.Trace:Add(c,condition) - self.Classes[c] = {Condition=condition}; - for _,v in pairs(ents.FindByClass(c)) do + self.Classes[c] = {Condition = condition}; + for _, v in pairs(ents.FindByClass(c)) do self:GetEntityData(v); end end @@ -57,14 +57,14 @@ end -- ################# Adds a condition-function to the specific entity. This function decides if the trace goes through the BoundingBox or not @aVoN function StarGate.Trace:AddCondition(c,condition) if(self.Classes[c]) then - self.Classes[c] = {Condition=condition}; + self.Classes[c] = {Condition = condition}; end end -- ################# Remoces such a class @aVoN function StarGate.Trace:Remove(c) self.Classes[c] = nil; - for k,_ in pairs(self.Entities) do + for k, _ in pairs(self.Entities) do if(k:IsValid()) then if(k:GetClass() == c) then self.Entities[k] = nil; @@ -94,10 +94,11 @@ function StarGate.Trace:GetEntityData(e) if(IsValid(e)) then local now = CurTime(); if(not self.Entities[e] or self.Entities[e].Last + 1 < now) then - local offset = e:OBBCenter(); -- We need the OBB's relatively to the Entiti's position, not to the OBBCenter + -- We need the OBB relatively to the Entity's position, not to the OBBCenter + local offset = e:OBBCenter(); self.Entities[e] = { - Min = e:OBBMins()+offset, - Max = e:OBBMaxs()+offset, + Min = e:OBBMins() + offset, + Max = e:OBBMaxs() + offset, -- Radius = e:BoudingRadius(), Last = now, } @@ -109,21 +110,50 @@ function StarGate.Trace:GetEntityData(e) end end +--[[ + * Checks whenever position hits sphere @dvdvideo1234 + * This can be used to check colisions for shields and spheres in general + * I've explained this here: https://math.stackexchange.com/a/2633290/266012 + * Returns the nearest circle intersection point ( when available ) + * rorg > Ray start origin position + * rdir > Ray start direction vector + * rlen > Ray length forced value ( not mandatory ) + * spos > Sphere position vector + * srad > Sphere radius value +]] +function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad) + local rlen = (tonumber(rlen) or rdir:Length()) + local rdir = rdir:GetNormalized(); rdir:Mul(rlen) + local equa = rdir:LengthSqr() -- Ray length is zero + if(equa < 0) then return nil end -- No intersection + local equr = Vector(rorg) equr:Sub(spos) -- Calculate norm + local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2) + local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots + if(equd < 0) then return nil end -- No intersection discriminant + local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua + local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) + local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) + if(ppos:DistToSqr(rorg) < mpos:DistToSqr(rorg)) then + return ppos, mpos -- Return the intersected +/- root point + end return mpos, ppos -- Return the intersected -/+ root point +end + -- ################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN function StarGate.Trace:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal) - local old = norm; - local norm = norm*mul; -- Make the normal hit the wall + local norm = norm * mul; -- Make the normal hit the wall local length = norm:Length(); -- The new normal's length! - if(length <= len) then -- The necessary normal length is shorter than the trace's length (we haven't hit anything before we hit the actual object!) + if(length <= len) then -- The necessary normal length is shorter than the trace's length. + -- We haven't hit anything before we hit the actual object! -- Check, if the remaining two coordinates are within the Min/Max range! local hit = pos + norm; - if( - -- The coordinate == "x,y,z" is because of rounding issues. The checked variable has to get skipped - It is on the wall! Sometimes we have 1.999999999 ~= 2 + if( -- The coordinate == "x,y,z" is because of rounding issues. + -- The checked variable has to get skipped - It is on the wall! + -- Sometimes we have 1.999999999 ~= 2 (coordinate == "x" or hit.x >= Min.x and hit.x <= Max.x) and (coordinate == "y" or hit.y >= Min.y and hit.y <= Max.y) and (coordinate == "z" or hit.z >= Min.z and hit.z <= Max.z) - ) then - return {HitPos=hit,Fraction=length/len,HitNormal=hit_normal}; -- The hitpos and fraction! + ) then -- The hitpos and fraction! + return {HitPos = hit, Fraction = length / len, HitNormal = hit_normal}; end end end @@ -133,49 +163,64 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) -- I will not check if the trace start position is exactly on a wall, neither I will check, if the start pos is exactly in the center of this entity. -- Doing this would need me to add some more special exeptions where the probability for these cases are < 0.1% (except you are forcing it) local hit_normal = Vector(0,0,0); hit_normal[coordinate] = 1; - -- ################# We are inside the bounding box - Trace to one wall! - if(in_box) then + if(in_box) then -- We are inside the bounding box - Trace to one wall! local mul = 0; if(norm[coordinate] > 0) then -- Norm == 0 has been avoided in StarGate.Trace:New -- The multiplier so the coordinate we're checking is exact on the wall's surface - mul = math.abs((pos[coordinate] - Max[coordinate])/norm[coordinate]); - hit_normal = -1*hit_normal; + mul = math.abs((pos[coordinate] - Max[coordinate]) / norm[coordinate]); + hit_normal = -1 * hit_normal; else - mul = math.abs((pos[coordinate] - Min[coordinate])/norm[coordinate]); + mul = math.abs((pos[coordinate] - Min[coordinate]) / norm[coordinate]); end return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal); - else - -- ################# We are outside the bounding box. + else -- We are outside the bounding box. if(pos[coordinate] < Min[coordinate] and norm[coordinate] > 0) then -- We are below the Minimum and the normal goes up => We can hit - local mul = math.abs((pos[coordinate] - Min[coordinate])/norm[coordinate]); -- The multiplier so the coordinate we're checking is exact on the wall's surface - return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,-1*hit_normal); + local mul = math.abs((pos[coordinate] - Min[coordinate]) / norm[coordinate]); -- The multiplier so the coordinate we're checking is exact on the wall's surface + return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,-1 * hit_normal); elseif(pos[coordinate] > Max[coordinate] and norm[coordinate] < 0) then --Above Max, norm down - local mul = math.abs((pos[coordinate] - Max[coordinate])/norm[coordinate]); + local mul = math.abs((pos[coordinate] - Max[coordinate]) / norm[coordinate]); return self:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal); end end end +--[[ + * Converts trace ignore angument to quick indexing @dvdvideo1234 + * Replace this with a dedicated class method in the routine + * Function filters are processed by reglar traces and CAP spcifics are skipped +]] +function StarGate.Trace:QuickIgnore(ignore) + local quick = {}; + if(type(ignore) == "table") then + for _, v in pairs(ignore) do + quick[v] = true; + end + elseif(ignore) then + quick[ignore] = true; + end + return quick +end + -- ################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less reouces!) if CLIENT then - for k,_ in pairs(self.Classes) do - for _,v in pairs(ents.FindByClass(k)) do + for k, _ in pairs(self.Classes) do + for _, v in pairs(ents.FindByClass(k)) do self:GetEntityData(v); end end end -- Setup trace parameters and routine code - self.Data.start:Set(start) - self.Data.endpos:Set(dir); data.endpos:Add(start) self.Data.filter = ignore + self.Data.start:Set(start) + self.Data.ignoreworld = tobool(iworld) self.Data.mask = (tonumber(mask) or MASK_SOLID) + self.Data.endpos:Set(dir); data.endpos:Add(start) self.Data.collisiongroup = (tonumber(cogrp) or COLLISION_GROUP_NONE) - self.Data.ignoreworld = tobool(iworld) - -- Setup trace width and routine + -- Setup trace width and routine @dvdvideo1234 if(width) then -- Trace cube hull with side of width local m = (tonumber(width) or 0) / 2 if(m > 0) then -- Width is a valid non-zero number @@ -193,21 +238,15 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) local trace = self.Code(self.Data) -- This is better and faster than using table.HasValue(ignore,e) (nested for loops) - local quick_ignore = {}; - if(type(ignore) == "table") then - for _,v in pairs(ignore) do - quick_ignore[v] = true; - end - elseif(ignore) then - quick_ignore[ignore] = true; - end - local len = dir:Length()*trace.Fraction; -- First of all: The length of the trace. + local quick_ignore = self:QuickIgnore(self.Data.filter) + + local len = dir:Length() * trace.Fraction; -- First of all: The length of the trace. local norm_world = dir:GetNormal(); -- Get Normal of the dir vector (world coordinates!) -- We need to sort all entities first according to their distance to the trace-start, or we hit a prop behind a prop instead of the one infront -- Problem noticed by Lynix here: http://img140.imageshack.us/img140/7589/gmflatgrass0017bj9.jpg local trace_array = {} -- Lynix modification - for e,_ in pairs(self.Entities) do + for e, _ in pairs(self.Entities) do if(not quick_ignore[e]) then if(self:GetEntityData(e)) then -- Update dimension data and check, if the ent is valid! local class = e:GetClass(); @@ -260,9 +299,8 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) -- Very ugly, but atleast works, with bugs... -- I have no idea how make function "CheckCoordinate" works with sphere @ AlexALX if (not hit and class=="shield" and not in_box and self:InBox(pos,v.Min,v.Max)) then - hit = {HitPos=pos,Fraction=0.8,HitNormal=norm} + hit = {HitPos = pos, Fraction = 0.8, HitNormal = norm} end - end if(hit) then @@ -302,7 +340,6 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) trace.Entity = e; table.insert(trace_array, table.Copy(trace)); -- Lynix modification end - end end end From dae4863e4cf0718ae94ad629c7566a5f67ff64b8 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Sat, 18 Dec 2021 13:54:11 +0200 Subject: [PATCH 09/18] Updated: `HitSphere` with flag forcing the API to check intersection validity --- lua/stargate/shared/tracelines.lua | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index a360ed23..14ad0bc2 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -114,14 +114,17 @@ end * Checks whenever position hits sphere @dvdvideo1234 * This can be used to check colisions for shields and spheres in general * I've explained this here: https://math.stackexchange.com/a/2633290/266012 - * Returns the nearest circle intersection point ( when available ) - * rorg > Ray start origin position - * rdir > Ray start direction vector - * rlen > Ray length forced value ( not mandatory ) - * spos > Sphere position vector - * srad > Sphere radius value + * Returns the nearest and furthest circle intersection point ( when available ) + * rorg > Ray start origin position. Where are we tracing from. + * rdir > Ray direction vector. Teace direction being checked + * rlen > Ray length forced value overrives direction ( not mandatory ) + * spos > Sphere position vector. The sphere location in 3D space + * srad > Sphere radius value. The actual sphere size in 3D space + * blen > When enabled consideres the ray lenght for intersection + * This forces the function to produce actual intersections + * that check whenever the points belong on the ray or not ]] -function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad) +function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) local rlen = (tonumber(rlen) or rdir:Length()) local rdir = rdir:GetNormalized(); rdir:Mul(rlen) local equa = rdir:LengthSqr() -- Ray length is zero @@ -133,9 +136,12 @@ function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad) local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) - if(ppos:DistToSqr(rorg) < mpos:DistToSqr(rorg)) then - return ppos, mpos -- Return the intersected +/- root point - end return mpos, ppos -- Return the intersected -/+ root point + if(blen) then equr:Set(rdir) equr:Add(rorg) -- Force-apply ray length + local vsp, vsm = (ppos - rorg), (mpos - rorg) -- According ray start + local vep, vem = (ppos - equr), (mpos - equr) -- According ray end + if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil end + if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil end + end; return mpos, ppos -- Return the intersected -/+ root point end -- ################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN From e629b6c449fb4f43a0e1c1ca67891e36cfb93cc5 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Tue, 21 Dec 2021 22:10:28 +0200 Subject: [PATCH 10/18] Fixed: Project uses tabs, but some functions use spaces --- lua/stargate/shared/tracelines.lua | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 14ad0bc2..60aed63c 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -17,7 +17,7 @@ ]] --######################################### --- Traclines - To stop them on lua drawn physboxes +-- Traclines - To stop them on lua drawn physboxes --######################################### StarGate.Trace = StarGate.Trace or {}; @@ -94,7 +94,7 @@ function StarGate.Trace:GetEntityData(e) if(IsValid(e)) then local now = CurTime(); if(not self.Entities[e] or self.Entities[e].Last + 1 < now) then - -- We need the OBB relatively to the Entity's position, not to the OBBCenter + -- We need the OBB relatively to the Entity's position, not to the OBBCenter local offset = e:OBBCenter(); self.Entities[e] = { Min = e:OBBMins() + offset, @@ -116,7 +116,7 @@ end * I've explained this here: https://math.stackexchange.com/a/2633290/266012 * Returns the nearest and furthest circle intersection point ( when available ) * rorg > Ray start origin position. Where are we tracing from. - * rdir > Ray direction vector. Teace direction being checked + * rdir > Ray direction vector. Trace direction being checked * rlen > Ray length forced value overrives direction ( not mandatory ) * spos > Sphere position vector. The sphere location in 3D space * srad > Sphere radius value. The actual sphere size in 3D space @@ -125,23 +125,23 @@ end * that check whenever the points belong on the ray or not ]] function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) - local rlen = (tonumber(rlen) or rdir:Length()) - local rdir = rdir:GetNormalized(); rdir:Mul(rlen) - local equa = rdir:LengthSqr() -- Ray length is zero - if(equa < 0) then return nil end -- No intersection - local equr = Vector(rorg) equr:Sub(spos) -- Calculate norm - local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2) - local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots - if(equd < 0) then return nil end -- No intersection discriminant - local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua - local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) - local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) - if(blen) then equr:Set(rdir) equr:Add(rorg) -- Force-apply ray length - local vsp, vsm = (ppos - rorg), (mpos - rorg) -- According ray start - local vep, vem = (ppos - equr), (mpos - equr) -- According ray end - if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil end - if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil end - end; return mpos, ppos -- Return the intersected -/+ root point + local rlen = (tonumber(rlen) or rdir:Length()) + local rdir = rdir:GetNormalized(); rdir:Mul(rlen) + local equa = rdir:LengthSqr() -- Ray length is zero + if(equa < 0) then return nil end -- No intersection + local equr = Vector(rorg) equr:Sub(spos) -- Calculate norm + local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2) + local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots + if(equd < 0) then return nil end -- No intersection discriminant + local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua + local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) + local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) + if(blen) then equr:Set(rdir) equr:Add(rorg) -- Force-apply ray length + local vsp, vsm = (ppos - rorg), (mpos - rorg) -- According ray start + local vep, vem = (ppos - equr), (mpos - equr) -- According ray end + if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil end + if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil end + end; return mpos, ppos -- Return the intersected -/+ root point end -- ################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN @@ -149,12 +149,12 @@ function StarGate.Trace:HitWall(coordinate,pos,norm,mul,Min,Max,len,hit_normal) local norm = norm * mul; -- Make the normal hit the wall local length = norm:Length(); -- The new normal's length! if(length <= len) then -- The necessary normal length is shorter than the trace's length. - -- We haven't hit anything before we hit the actual object! + -- We haven't hit anything before we hit the actual object! -- Check, if the remaining two coordinates are within the Min/Max range! local hit = pos + norm; if( -- The coordinate == "x,y,z" is because of rounding issues. - -- The checked variable has to get skipped - It is on the wall! - -- Sometimes we have 1.999999999 ~= 2 + -- The checked variable has to get skipped - It is on the wall! + -- Sometimes we have 1.999999999 ~= 2 (coordinate == "x" or hit.x >= Min.x and hit.x <= Max.x) and (coordinate == "y" or hit.y >= Min.y and hit.y <= Max.y) and (coordinate == "z" or hit.z >= Min.z and hit.z <= Max.z) @@ -204,7 +204,7 @@ function StarGate.Trace:QuickIgnore(ignore) elseif(ignore) then quick[ignore] = true; end - return quick + return quick end -- ################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN @@ -244,7 +244,7 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) local trace = self.Code(self.Data) -- This is better and faster than using table.HasValue(ignore,e) (nested for loops) - local quick_ignore = self:QuickIgnore(self.Data.filter) + local quick_ignore = self:QuickIgnore(self.Data.filter) local len = dir:Length() * trace.Fraction; -- First of all: The length of the trace. local norm_world = dir:GetNormal(); -- Get Normal of the dir vector (world coordinates!) From eab2a5a27b521eac79d492f6cffc72ed20370714 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Sun, 26 Dec 2021 13:12:11 +0200 Subject: [PATCH 11/18] Fixed: Check when ray length is zero ( never negative ) --- lua/stargate/shared/tracelines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 60aed63c..b09214ea 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -128,7 +128,7 @@ function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) local rlen = (tonumber(rlen) or rdir:Length()) local rdir = rdir:GetNormalized(); rdir:Mul(rlen) local equa = rdir:LengthSqr() -- Ray length is zero - if(equa < 0) then return nil end -- No intersection + if(equa <= 0) then return nil end -- No intersection local equr = Vector(rorg) equr:Sub(spos) -- Calculate norm local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2) local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots From 7702a96c4d37df6e4562b6188ee90b79d5d2455f Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Wed, 7 Sep 2022 09:09:06 +0300 Subject: [PATCH 12/18] Updated: Equation A coefficient from ray length --- lua/stargate/shared/tracelines.lua | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index b09214ea..b9a0cdf3 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -34,7 +34,7 @@ StarGate.Trace.Data = { collisiongroup = COLLISION_GROUP_NONE, -- What the trace should hit collision group regards ignoreworld = false, -- Should the trace ignore world or not output = nil -- Result will be written here instead of returning a new table -} +}; -- ################# Deephook to ents.Create serverside @aVoN if SERVER then @@ -120,27 +120,26 @@ end * rlen > Ray length forced value overrives direction ( not mandatory ) * spos > Sphere position vector. The sphere location in 3D space * srad > Sphere radius value. The actual sphere size in 3D space - * blen > When enabled consideres the ray lenght for intersection + * blen > When enabled consideres the ray dot for intersections * This forces the function to produce actual intersections * that check whenever the points belong on the ray or not ]] function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) - local rlen = (tonumber(rlen) or rdir:Length()) - local rdir = rdir:GetNormalized(); rdir:Mul(rlen) - local equa = rdir:LengthSqr() -- Ray length is zero + local equa = (rlen and tonumber(rlen) or rdir:Length()); if(equa <= 0) then return nil end -- No intersection - local equr = Vector(rorg) equr:Sub(spos) -- Calculate norm - local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2) + local rdir = rdir:GetNormalized(); rdir:Mul(equa); -- Read length + local equr, equa = Vector(rorg), equa^2; equr:Sub(spos); -- Sphere norm + local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2); local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots if(equd < 0) then return nil end -- No intersection discriminant - local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua - local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) - local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) - if(blen) then equr:Set(rdir) equr:Add(rorg) -- Force-apply ray length - local vsp, vsm = (ppos - rorg), (mpos - rorg) -- According ray start - local vep, vem = (ppos - equr), (mpos - equr) -- According ray end - if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil end - if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil end + local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua; + local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg); + local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg); + if(blen) then equr:Set(rdir) equr:Add(rorg); -- Force-apply ray length + local vsp, vsm = (ppos - rorg), (mpos - rorg); -- According ray start + local vep, vem = (ppos - equr), (mpos - equr); -- According ray end + if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil; end + if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil; end end; return mpos, ppos -- Return the intersected -/+ root point end @@ -357,10 +356,10 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) * Store true trace data reference to a local and compare * If margin is not defined will be considered as minimum ]] - local anc, mar = self.Data.start + local anc, mar = self.Data.start; for i = 1, #trace_array do - local v = trace_array[i] - local m = anc:DistToSqr(v.HitPos) + local v = trace_array[i]; + local m = anc:DistToSqr(v.HitPos); if(not mar or m < mar) then mar, trace = m, v; end From e73a3a5743a9213ee854133d55c8e859ef640f7e Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Wed, 7 Sep 2022 10:30:57 +0300 Subject: [PATCH 13/18] Fixed: Return `HitSphere` swapped values when origin is inside the circle Fixed: Return a proper value just like `StarGate.Trace:HitWall` --- lua/stargate/shared/tracelines.lua | 40 +++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index b9a0cdf3..e2be3071 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -110,6 +110,23 @@ function StarGate.Trace:GetEntityData(e) end end +function StarGate.Trace:InSphere(rorg, spos, srad) + local rorg = Vector(rorg); rorg:Sub(spos) + return (rorg:Length() <= srad) +end + +function StarGate.Trace:AmongRay(bpos, rorg, rdir, full) + local sray = Vector(bpos); sray:Sub(rorg); + if(sray:Cross(rdir):LengthSqr() < 0.01) then + local eray = Vector(rorg); eray:Add(rdir); eray:Sub(); + eray.x, eray.y, eray.z = -eray.x, -eray.y, -eray.z; + sdot, edot = sray:Dot(rdir), eray:Dot(rdir); + if(sdot < 0 and edot < 0) then return false; end -- Behind + if(not full and sdot > 0 and edot > 0) then return false; end + return true; -- Position is on the ray in non-full format + end; return false; +end + --[[ * Checks whenever position hits sphere @dvdvideo1234 * This can be used to check colisions for shields and spheres in general @@ -125,22 +142,27 @@ end * that check whenever the points belong on the ray or not ]] function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) - local equa = (rlen and tonumber(rlen) or rdir:Length()); - if(equa <= 0) then return nil end -- No intersection + local eque = (rlen and tonumber(rlen) or rdir:Length()); + if(eque <= 0) then return nil end -- No intersection local rdir = rdir:GetNormalized(); rdir:Mul(equa); -- Read length - local equr, equa = Vector(rorg), equa^2; equr:Sub(spos); -- Sphere norm + local equr, equa = Vector(rorg), eque^2; equr:Sub(spos); -- Sphere norm local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2); local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots if(equd < 0) then return nil end -- No intersection discriminant local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua; local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg); local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg); - if(blen) then equr:Set(rdir) equr:Add(rorg); -- Force-apply ray length - local vsp, vsm = (ppos - rorg), (mpos - rorg); -- According ray start - local vep, vem = (ppos - equr), (mpos - equr); -- According ray end - if(vsp:Dot(rdir) < 0 or vep:Dot(rdir) > 0) then ppos = nil; end - if(vsm:Dot(rdir) < 0 or vem:Dot(rdir) > 0) then mpos = nil; end - end; return mpos, ppos -- Return the intersected -/+ root point + if(self:InSphere(rorg, spos, srad)) then ppos, mpos = mpos, ppos end + if(self:AmongRay(ppos, rorg, rdir, true)) then + local frac = (ppos - rorg):Length() / eque + local norm = Vector(ppos); norm:Sub(spos); norm:Normalize() + return {HitPos = ppos, Fraction = frac, HitNormal = norm}; + end + if(self:AmongRay(mpos, rorg, rdir, true)) then + local frac = (mpos - rorg):Length() / eque + local norm = Vector(mpos); norm:Sub(spos); norm:Normalize() + return {HitPos = mpos, Fraction = frac, HitNormal = norm}; + end end -- ################# Helper Function: Makes the direction vector longer and checks if the hitpos is within a specific range (== hit wall) @aVoN From 064e1af014f5ca39f931b7b6a889ad66753d4aaf Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Thu, 28 Mar 2024 22:32:59 +0200 Subject: [PATCH 14/18] Fixed the variable typo at line:247 --- lua/stargate/shared/tracelines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index e2be3071..ab0ba047 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -244,7 +244,7 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) self.Data.start:Set(start) self.Data.ignoreworld = tobool(iworld) self.Data.mask = (tonumber(mask) or MASK_SOLID) - self.Data.endpos:Set(dir); data.endpos:Add(start) + self.Data.endpos:Set(dir); Data.endpos:Add(start) self.Data.collisiongroup = (tonumber(cogrp) or COLLISION_GROUP_NONE) -- Setup trace width and routine @dvdvideo1234 From 99624f1f6880db6af38c9d62592f74c3523c65e0 Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Thu, 28 Mar 2024 22:41:33 +0200 Subject: [PATCH 15/18] Updated: Adjust LEN^2 as SQRT is expensive operation --- lua/stargate/shared/tracelines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index ab0ba047..eedafcc7 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -112,7 +112,7 @@ end function StarGate.Trace:InSphere(rorg, spos, srad) local rorg = Vector(rorg); rorg:Sub(spos) - return (rorg:Length() <= srad) + return (rorg:LengthSqr() <= srad^2) end function StarGate.Trace:AmongRay(bpos, rorg, rdir, full) From 5945b1dd23a437ae1d114029e9deea0ffb6f9de9 Mon Sep 17 00:00:00 2001 From: Deyan Dobromirov Date: Fri, 29 Mar 2024 09:35:57 +0200 Subject: [PATCH 16/18] Fixed: Ray-circle normal being wrong when ray fired inside the sphere Fixed: Some comment typos and if-statement brackets to match convention Updated: Use squared distance instead of meridian distance when comparing --- lua/stargate/shared/tracelines.lua | 65 ++++++++++++++---------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index eedafcc7..82ce23eb 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -128,16 +128,16 @@ function StarGate.Trace:AmongRay(bpos, rorg, rdir, full) end --[[ - * Checks whenever position hits sphere @dvdvideo1234 - * This can be used to check colisions for shields and spheres in general + * Checks whenever ray hits sphere @dvdvideo1234 + * This can be used to check collisions for shields and spheres in general * I've explained this here: https://math.stackexchange.com/a/2633290/266012 * Returns the nearest and furthest circle intersection point ( when available ) * rorg > Ray start origin position. Where are we tracing from. * rdir > Ray direction vector. Trace direction being checked - * rlen > Ray length forced value overrives direction ( not mandatory ) + * rlen > Ray length forced value overdrives direction ( not mandatory ) * spos > Sphere position vector. The sphere location in 3D space * srad > Sphere radius value. The actual sphere size in 3D space - * blen > When enabled consideres the ray dot for intersections + * blen > When enabled considers the ray dot for intersections * This forces the function to produce actual intersections * that check whenever the points belong on the ray or not ]] @@ -147,20 +147,16 @@ function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) local rdir = rdir:GetNormalized(); rdir:Mul(equa); -- Read length local equr, equa = Vector(rorg), eque^2; equr:Sub(spos); -- Sphere norm local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2); - local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots + local equd, ounr = (equb ^ 2 - 4 * equa * equc), 1 -- Check imaginary roots if(equd < 0) then return nil end -- No intersection discriminant local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua; local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg); local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg); - if(self:InSphere(rorg, spos, srad)) then ppos, mpos = mpos, ppos end - if(self:AmongRay(ppos, rorg, rdir, true)) then - local frac = (ppos - rorg):Length() / eque - local norm = Vector(ppos); norm:Sub(spos); norm:Normalize() - return {HitPos = ppos, Fraction = frac, HitNormal = norm}; - end - if(self:AmongRay(mpos, rorg, rdir, true)) then - local frac = (mpos - rorg):Length() / eque + if(self:InSphere(rorg, spos, srad)) then ppos, mpos, ounr = mpos, ppos, -ounr end + -- The position that a bullet will actually hit the circle when fired + if(not self:AmongRay(mpos, rorg, rdir, true)) then return nil else local norm = Vector(mpos); norm:Sub(spos); norm:Normalize() + local frac = (mpos - rorg):Length() / eque; norm:Mul(ounr) return {HitPos = mpos, Fraction = frac, HitNormal = norm}; end end @@ -188,7 +184,7 @@ end -- ################# This checks one coordinate of the trace's normal @aVoN function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) -- I will not check if the trace start position is exactly on a wall, neither I will check, if the start pos is exactly in the center of this entity. - -- Doing this would need me to add some more special exeptions where the probability for these cases are < 0.1% (except you are forcing it) + -- Doing this would need me to add some more special exceptions where the probability for these cases are < 0.1% (except you are forcing it) local hit_normal = Vector(0,0,0); hit_normal[coordinate] = 1; if(in_box) then -- We are inside the bounding box - Trace to one wall! local mul = 0; @@ -212,9 +208,9 @@ function StarGate.Trace:CheckCoordinate(coordinate,pos,norm,Min,Max,len,in_box) end --[[ - * Converts trace ignore angument to quick indexing @dvdvideo1234 + * Converts trace ignore argument to quick indexing @dvdvideo1234 * Replace this with a dedicated class method in the routine - * Function filters are processed by reglar traces and CAP spcifics are skipped + * Function filters are processed by regular traces and CAP specifics are skipped ]] function StarGate.Trace:QuickIgnore(ignore) local quick = {}; @@ -228,9 +224,9 @@ function StarGate.Trace:QuickIgnore(ignore) return quick end --- ################# Start a traceline which can hit Lua Drawn BoundingBoxes @aVoN +-- ################# Start a trace line which can hit Lua Drawn BoundingBoxes @aVoN function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) - -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less reouces!) + -- Clients need to add new entities inside this function (Server uses "HookBased" with ents.Create which uses less resources!) if CLIENT then for k, _ in pairs(self.Classes) do for _, v in pairs(ents.FindByClass(k)) do @@ -268,8 +264,9 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) local quick_ignore = self:QuickIgnore(self.Data.filter) local len = dir:Length() * trace.Fraction; -- First of all: The length of the trace. - local norm_world = dir:GetNormal(); -- Get Normal of the dir vector (world coordinates!) - -- We need to sort all entities first according to their distance to the trace-start, or we hit a prop behind a prop instead of the one infront + local norm_world = dir:GetNormal(); -- Get Normal of the direction vector (world coordinates!) + -- We need to sort all entities first according to their distance to the + -- trace-start or we hit a prop behind a prop instead of the one in front -- Problem noticed by Lynix here: http://img140.imageshack.us/img140/7589/gmflatgrass0017bj9.jpg local trace_array = {} -- Lynix modification @@ -281,34 +278,34 @@ function StarGate.Trace:New(start,dir,ignore,mask,cogrp,iworld,width) local pos = e:WorldToLocal(start); local in_box = false; - if (class == "shield_core_buble") then + if(class == "shield_core_buble") then in_box = StarGate.IsInShieldCore(e, start); - elseif (class == "shield") then - in_box = (e:GetPos():Distance(start) Date: Sat, 30 Mar 2024 08:54:07 +0200 Subject: [PATCH 17/18] Updated: Use selection operator instead of IF statement --- lua/stargate/shared/tracelines.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 82ce23eb..01390fe0 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -147,17 +147,18 @@ function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) local rdir = rdir:GetNormalized(); rdir:Mul(equa); -- Read length local equr, equa = Vector(rorg), eque^2; equr:Sub(spos); -- Sphere norm local equb, equc = 2 * rdir:Dot(equr), (equr:LengthSqr() - srad^2); - local equd, ounr = (equb ^ 2 - 4 * equa * equc), 1 -- Check imaginary roots + local equd = (equb ^ 2 - 4 * equa * equc) -- Check imaginary roots if(equd < 0) then return nil end -- No intersection discriminant local mqua = (1 / (2 * equa)); equd, equb = mqua*math.sqrt(equd), -equb*mqua; - local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg); - local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg); - if(self:InSphere(rorg, spos, srad)) then ppos, mpos, ounr = mpos, ppos, -ounr end + local mpos = Vector(rdir); mpos:Mul(equb - equd); mpos:Add(rorg) + local ppos = Vector(rdir); ppos:Mul(equb + equd); ppos:Add(rorg) + local isin = self:InSphere(rorg, spos, srad) -- Inside check + local xpos, ounr = (isin and ppos or mpos), (isin and -1 or 1) -- The position that a bullet will actually hit the circle when fired - if(not self:AmongRay(mpos, rorg, rdir, true)) then return nil else - local norm = Vector(mpos); norm:Sub(spos); norm:Normalize() - local frac = (mpos - rorg):Length() / eque; norm:Mul(ounr) - return {HitPos = mpos, Fraction = frac, HitNormal = norm}; + if(not self:AmongRay(xpos, rorg, rdir, true)) then return nil else + local norm = Vector(xpos); norm:Sub(spos); norm:Normalize() + local frac = (xpos - rorg):Length() / eque; norm:Mul(ounr) + return {HitPos = xpos, Fraction = frac, HitNormal = norm}; end end From 41ada0c3ba3e30f054d39dac20ef545b37b8f441 Mon Sep 17 00:00:00 2001 From: dvdvideo1234 Date: Sat, 30 Mar 2024 08:57:11 +0200 Subject: [PATCH 18/18] updated: Avoid making a vector just for fraction --- lua/stargate/shared/tracelines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/stargate/shared/tracelines.lua b/lua/stargate/shared/tracelines.lua index 01390fe0..6c69a46f 100644 --- a/lua/stargate/shared/tracelines.lua +++ b/lua/stargate/shared/tracelines.lua @@ -157,7 +157,7 @@ function StarGate.Trace:HitSphere(rorg, rdir, rlen, spos, srad, blen) -- The position that a bullet will actually hit the circle when fired if(not self:AmongRay(xpos, rorg, rdir, true)) then return nil else local norm = Vector(xpos); norm:Sub(spos); norm:Normalize() - local frac = (xpos - rorg):Length() / eque; norm:Mul(ounr) + local frac = xpos:Distance(rorg) / eque; norm:Mul(ounr) return {HitPos = xpos, Fraction = frac, HitNormal = norm}; end end