-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPhysics_Lib.lua
More file actions
236 lines (198 loc) · 6.92 KB
/
Physics_Lib.lua
File metadata and controls
236 lines (198 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
-- THIS IS FOR PHYSICS FUNCTIONS AND RELATED STUFF
-- ALSO USEFUL FOR GAMES
---@section Acceleration
---@param force LBVec
---@param mass number
---@return LBVec
function Acceleration(force, mass)
return force:lbvec_scale(1 / mass)
end
---@endsection
---@section GravitationalForce
---@param mass number
---@return LBVec
function GravitationalForce(mass)
return LifeBoatAPI.LBVec:new(0, GameEngine.gravity * mass)
end
---@endsection
---@section DragForce
---@param velocity LBVec
---@param drag number
---@return LBVec
function DragForce(velocity, drag)
local dragForceMagnitude = velocity:lbvec_length()^2 * drag
return velocity:lbvec_normalize():lbvec_scale(-dragForceMagnitude)
end
---@endsection
---@section AABBCollision
---@param object1 BaseObject
---@param object2 BaseObject
---@return boolean
function AABBCollision(object1, object2)
local AABB1, AABB2, isColliding = object1:GetAABB(), object2:GetAABB(), false
isColliding = (
AABB1.maxX >= AABB2.minX and
AABB1.minX <= AABB2.maxX and
AABB1.maxY >= AABB2.minY and
AABB1.minY <= AABB2.maxY
)
return isColliding
end
---@endsection
---@section AdvancedCollision
---@param object1 BaseShape
---@param object2 BaseShape
---@return number, LBVec
function AdvancedCollision(object1, object2)
local minPenDepth, collisionNormal = math.huge, nil
if not AABBCollision(object1, object2) then return minPenDepth, collisionNormal end
local object1Shapes, object2Shapes = object1:GetWorldVertices(), object2:GetWorldVertices()
local depth, normal
for _, shapeData1 in ipairs(object1Shapes) do
local type1 = shapeData1.type
for _, shapeData2 in ipairs(object2Shapes) do
local type2 = shapeData2.type
if type1 == ShapeTypes.Polygon then
if type2 == ShapeTypes.Polygon then
depth, normal = PolygonPolygon(shapeData1, shapeData2)
else -- Circle
depth, normal = PolygonCircle(shapeData1, shapeData2)
end
else -- Circle
if type2 == ShapeTypes.Polygon then
depth, normal = PolygonCircle(shapeData2, shapeData1)
if normal then normal = normal:lbvec_scale(-1) end
else -- Circle vs Circle
depth, normal = CircleCircle(shapeData1, shapeData2)
end
end
if depth > 0 and depth < minPenDepth then
minPenDepth = depth
collisionNormal = normal
end
end
end
if minPenDepth == math.huge then
return 0, nil
else
return minPenDepth, collisionNormal
end
end
---@endsection
---@section CircleCircle
---@param c1 table
---@param c2 table
---@return number, LBVec
CircleCircle = function(c1, c2)
local delta = c2.center:lbvec_sub(c1.center)
local distSq, r = delta:lbvec_length()*delta:lbvec_length(),c1.radius + c2.radius
if distSq >= r*r then
return 0, nil -- no collision
end
local dist = math.sqrt(distSq)
local penetration = r - dist
local normal = (dist > 0) and delta:lbvec_normalize() or LifeBoatAPI.LBVec:new(1,0)
return penetration, normal
end
---@endsection
---@section projectPolygon
function projectPolygon(vertices, axis)
local min, max = math.huge, -math.huge
for _, v in ipairs(vertices) do
local p = v:lbvec_dot(axis)
if p < min then min = p end
if p > max then max = p end
end
return min, max
end
---@endsection
---@section PolygonPolygon
---@param p1 table
---@param p2 table
---@return number, LBVec
PolygonPolygon = function(p1, p2)
local axes = {}
function addPerpEdges(poly)
for i = 1, #poly.vertices do
local nextIdx = (i % #poly.vertices) + 1
local edge = poly.vertices[nextIdx]:lbvec_sub(poly.vertices[i])
local axis = LifeBoatAPI.LBVec:new(-edge.y, edge.x):lbvec_normalize()
table.insert(axes, axis)
end
end
addPerpEdges(p1)
addPerpEdges(p2)
local minOverlap, mtvAxis = math.huge, nil
for _, axis in ipairs(axes) do
local minA, maxA = projectPolygon(p1.vertices, axis)
local minB, maxB = projectPolygon(p2.vertices, axis)
local overlap = math.min(maxA, maxB) - math.max(minA, minB)
if overlap <= 0 then
return 0, nil
end
if overlap < minOverlap then
minOverlap, mtvAxis = overlap, axis
end
end
local centerDelta = LifeBoatAPI.LBVec:new(0,0)
for _, v in ipairs(p1.vertices) do centerDelta = centerDelta:lbvec_add(v) end
centerDelta = centerDelta:lbvec_scale(1 / #p1.vertices)
local centerB = LifeBoatAPI.LBVec:new(0,0)
for _, v in ipairs(p2.vertices) do centerB = centerB:lbvec_add(v) end
centerB = centerB:lbvec_scale(1 / #p2.vertices)
centerDelta = centerB:lbvec_sub(centerDelta)
if centerDelta:lbvec_dot(mtvAxis) < 0 then
mtvAxis = mtvAxis:lbvec_scale(-1)
end
return minOverlap, mtvAxis
end
---@endsection
---@section PolygonCircle
---@param polygon table
---@param circle table
---@return number, LBVec
PolygonCircle = function(polygon, circle)
local axes = {}
-- Step 1: Polygon edge normals
for i = 1, #polygon.vertices do
local nextIdx = (i % #polygon.vertices) + 1
local edge = polygon.vertices[nextIdx]:lbvec_sub(polygon.vertices[i])
local axis = LifeBoatAPI.LBVec:new(-edge.y, edge.x):lbvec_normalize()
table.insert(axes, axis)
end
-- Step 2: Axis from closest polygon vertex to circle center
local closestVertex = polygon.vertices[1]
local minDist = circle.center:lbvec_sub(closestVertex):lbvec_length()
for i = 2, #polygon.vertices do
local dist = circle.center:lbvec_sub(polygon.vertices[i]):lbvec_length()
if dist < minDist then
minDist = dist
closestVertex = polygon.vertices[i]
end
end
local axisToCircle = circle.center:lbvec_sub(closestVertex)
if axisToCircle:lbvec_length() > 0 then
table.insert(axes, axisToCircle:lbvec_normalize())
end
-- Step 4: Check overlaps
local minOverlap, mtvAxis = math.huge, nil
for _, axis in ipairs(axes) do
local minP, maxP = projectPolygon(polygon.vertices, axis)
local minC, maxC = circle.center:lbvec_dot(axis) - circle.radius, circle.center:lbvec_dot(axis) + circle.radius
local overlap = math.min(maxP, maxC) - math.max(minP, minC)
if overlap <= 0 then
-- Separating axis found → no collision
return 0, nil
end
if overlap < minOverlap then
minOverlap, mtvAxis = overlap, axis
end
end
-- Step 5: Orient axis from polygon → circle
local centerDelta = circle.center:lbvec_sub(polygon.vertices[1])
if centerDelta:lbvec_dot(mtvAxis) < 0 then
mtvAxis = mtvAxis:lbvec_scale(-1)
end
return minOverlap, mtvAxis
end
---@endsection