@kunosyn/shatterbox 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,403 @@
1
+ --!native
2
+ --!optimize 2
3
+
4
+ --#selene: allow(unused_variable)
5
+
6
+ local EPSILON = 1e-3
7
+ local UNEPS = 1 - EPSILON
8
+
9
+ local localAxisVectors = { X = "RightVector", Y = "UpVector", Z = "ZVector" }
10
+
11
+ local axisID = {"X", "Y", "Z"}
12
+
13
+
14
+
15
+ local PartTypeContainsPoint = {}
16
+
17
+ function PartTypeContainsPoint.Ball(cframe : CFrame, size : Vector3, p : Vector3)
18
+
19
+ local dp = (p - cframe.Position) * UNEPS
20
+ local dx, dy, dz = dp.X, dp.Y, dp.Z
21
+ local minSize = math.min(size.X, size.Y, size.Z) * 0.5
22
+
23
+ return dx*dx + dy*dy + dz*dz <= minSize*minSize
24
+ end
25
+
26
+ function PartTypeContainsPoint.Cylinder(cframe : CFrame, size : Vector3, p : Vector3)
27
+
28
+ local localPoint = cframe:PointToObjectSpace(p):Abs() * UNEPS
29
+
30
+ if localPoint.X > size.X * 0.5 then return false end
31
+
32
+ local dy, dz = localPoint.Y, localPoint.Z
33
+ local minSize = math.min(size.Y, size.Z) * 0.5
34
+
35
+ return dy*dy + dz*dz <= minSize*minSize
36
+ end
37
+
38
+ function PartTypeContainsPoint.Block(cframe : CFrame, size : Vector3, p : Vector3)
39
+
40
+ local localPoint = cframe:PointToObjectSpace(p):Abs() * UNEPS
41
+
42
+ return (localPoint.X <= size.X * 0.5) and (localPoint.Y <= size.Y * 0.5) and (localPoint.Z <= size.Z * 0.5)
43
+ end
44
+
45
+ function PartTypeContainsPoint.Wedge(cframe : CFrame, size : Vector3, p : Vector3)
46
+
47
+ local localPoint = cframe:PointToObjectSpace(p) * UNEPS
48
+ local absPoint = localPoint:Abs()
49
+ local wsy, wsz = size.Y, size.Z
50
+
51
+ return (absPoint.X <= size.X * 0.5) and (absPoint.Y <= wsy * 0.5) and (absPoint.Z <= wsz * 0.5) and (localPoint.Y * wsz <= localPoint.Z * wsy)
52
+ end
53
+
54
+ function PartTypeContainsPoint.CornerWedge(cframe : CFrame, size : Vector3, p : Vector3)
55
+
56
+ local localPoint = cframe:PointToObjectSpace(p) * UNEPS
57
+ local absPoint = localPoint:Abs()
58
+ local wsx, wsy, wsz = size.X, size.Y, size.Z
59
+
60
+ return (absPoint.X <= wsx * 0.5) and (absPoint.Y <= wsy * 0.5) and (absPoint.Z <= wsz * 0.5) and (localPoint.Y * wsz <= localPoint.Z * -wsy) and (localPoint.Y * wsx <= localPoint.X * wsy)
61
+ end
62
+
63
+
64
+
65
+ local GetVerts = {}
66
+
67
+ function GetVerts.Block(cframe : CFrame, size : Vector3)
68
+ size = size * 0.5
69
+ local pos = cframe.Position
70
+ local lx, ly, lz = cframe.XVector*size.X, cframe.YVector*size.Y, cframe.ZVector*size.Z
71
+ return {
72
+ pos - lx - ly - lz,
73
+ pos - lx - ly + lz,
74
+ pos - lx + ly - lz,
75
+ pos - lx + ly + lz,
76
+ pos + lx - ly - lz,
77
+ pos + lx - ly + lz,
78
+ pos + lx + ly - lz,
79
+ pos + lx + ly + lz
80
+ }
81
+ end
82
+ local block_edges = { {1,2}, {1,3}, {1,5}, {2,4}, {2,6}, {3,4}, {3,7}, {4,8}, {5,6}, {5,7}, {6,8}, {7,8} }
83
+
84
+ function GetVerts.Wedge(cframe : CFrame, size : Vector3)
85
+ size = size * 0.5
86
+ local pos = cframe.Position
87
+ local lx, ly, lz = cframe.XVector*size.X, cframe.YVector*size.Y, cframe.ZVector*size.Z
88
+ return {
89
+ pos - lx - ly - lz,
90
+ pos - lx - ly + lz,
91
+ pos + lx - ly - lz,
92
+ pos + lx - ly + lz,
93
+ pos - lx + ly + lz,
94
+ pos + lx + ly + lz
95
+ }
96
+ end
97
+ local wedge_edges = { {1,2}, {2,4}, {4,3}, {3,1}, {2,5}, {4,6}, {5,6}, {1,3}, {1,5}, {3,6} }
98
+
99
+ function GetVerts.CornerWedge(cframe : CFrame, size : Vector3)
100
+ size = size * 0.5
101
+ local pos = cframe.Position
102
+ local lx, ly, lz = cframe.XVector*size.X, cframe.YVector*size.Y, cframe.ZVector*size.Z
103
+ return {
104
+ pos - lx - ly - lz,
105
+ pos - lx - ly + lz,
106
+ pos + lx - ly - lz,
107
+ pos + lx - ly + lz,
108
+ pos + lx + ly - lz
109
+ }
110
+ end
111
+ local corner_wedge_edges = { {1,2}, {2,4}, {4,3}, {3,1}, {3,5}, {1,5}, {4,5} }
112
+
113
+
114
+
115
+ local GetNormals = {}
116
+
117
+ function GetNormals.Block(cframe : CFrame)
118
+ local lx, ly, lz = cframe.XVector, cframe.YVector, cframe.ZVector
119
+ return {
120
+ -lx, lx,
121
+ -ly, ly,
122
+ -lz, lz
123
+ }
124
+ end
125
+
126
+ function GetNormals.Wedge(cframe : CFrame, size : Vector3)
127
+ local lx, ly, lz = cframe.XVector, cframe.YVector, cframe.ZVector
128
+ return {
129
+ -cframe.YVector,
130
+ (lz*size.Y - ly*size.Z).Unit,
131
+ -lz,
132
+ -lx, lx
133
+ }
134
+ end
135
+
136
+ function GetNormals.CornerWedge(cframe : CFrame, size : Vector3)
137
+ local lx, ly, lz = cframe.XVector, cframe.YVector, cframe.ZVector
138
+ return {
139
+ -ly,
140
+ (lz*size.Y - lx*size.X).Unit,
141
+ (ly*size.Z + lz*size.Y).Unit,
142
+ -lz,
143
+ lx
144
+ }
145
+ end
146
+
147
+
148
+
149
+ local function ProjectExtent(verts: {Vector3}, axis: Vector3)
150
+ local min, max = nil, nil
151
+ local ax, ay, az = axis.X, axis.Y, axis.Z
152
+ for _, v in verts do
153
+ local d = v.X*ax + v.Y*ay + v.Z*az
154
+ if min then
155
+ min, max = math.min(min, d), math.max(max, d)
156
+ else
157
+ min, max = d, d
158
+ end
159
+ end
160
+ return min, max
161
+ end
162
+
163
+ local function SAT(VA, NA, VB, NB)
164
+ -- Create candidate axis array and indexer
165
+ local candidates, n = table.create(#NA + #NB + #NA*#NB), 0
166
+
167
+ -- Populate candidate axis
168
+ for _, a in ipairs(NA) do
169
+ n += 1
170
+ candidates[n] = a
171
+
172
+ local ax, ay, az = a.X, a.Y, a.Z
173
+ for _, b in ipairs(NB) do
174
+ n += 1
175
+ candidates[n] = b
176
+
177
+ local bx, by, bz = b.X, b.Y, b.Z
178
+ local cross = Vector3.new(
179
+ ay*bz - az*by,
180
+ az*bx - ax*bz,
181
+ ax*by - ay*bx
182
+ )
183
+ if cross.Magnitude > EPSILON then
184
+ n += 1
185
+ candidates[n] = cross.Unit
186
+ end
187
+ end
188
+ end
189
+
190
+ -- SAT test on all candidate axes
191
+ for _, axis in candidates do
192
+ local min1, max1 = ProjectExtent(VA, axis)
193
+ local min2, max2 = ProjectExtent(VB, axis)
194
+
195
+ if max1 < min2 or max2 < min1 then
196
+ return false -- Separating axis found
197
+ end
198
+ end
199
+
200
+ return true -- No separating axis found
201
+ end
202
+
203
+ local function PartContainsAllVerts(part : Part, verts : {Vector3})
204
+
205
+ local PartContainsPoint = PartTypeContainsPoint[part.Shape.Name]
206
+ local partSize = part.Size
207
+ local partCFrame = part.CFrame
208
+
209
+ for _, vert in ipairs(verts) do
210
+ if PartContainsPoint(partCFrame, partSize, vert) then continue end
211
+ return false
212
+ end
213
+
214
+ return true
215
+ end
216
+
217
+ local function PartEncapsulatesBlockPart(part : Part, blockCFrame : CFrame, blockSize : Vector3)
218
+
219
+ return PartContainsAllVerts(part, GetVerts.Block(blockCFrame, blockSize))
220
+ end
221
+
222
+ local function PartContainsAVert(part : Part, verts : {Vector3})
223
+
224
+ local PartContainsPoint = PartTypeContainsPoint[part.Shape.Name]
225
+ local partSize = part.Size
226
+ local partCFrame = part.CFrame
227
+
228
+ for i, vert in ipairs(verts) do
229
+
230
+ if PartContainsPoint(partCFrame, partSize, vert) then return i end
231
+ end
232
+
233
+ return false
234
+ end
235
+
236
+ local function BallIntersectsBlock(sphereCFrame, sphereSize, boxCFrame : CFrame, boxSize : Vector3)
237
+ local radius = math.min(sphereSize.X, sphereSize.Y, sphereSize.Z) * 0.5
238
+ local localCenter = boxCFrame:Inverse() * sphereCFrame.Position
239
+ local halfSize = boxSize * 0.5
240
+
241
+ local clamped = Vector3.new(
242
+ math.clamp(localCenter.X, -halfSize.X, halfSize.X),
243
+ math.clamp(localCenter.Y, -halfSize.Y, halfSize.Y),
244
+ math.clamp(localCenter.Z, -halfSize.Z, halfSize.Z)
245
+ )
246
+
247
+ return (localCenter - clamped).Magnitude <= radius
248
+ end
249
+
250
+ local function SegmentIntersectsCylinder(L1, L2, C1, C2, radius)
251
+
252
+ local Ca = C2 - C1
253
+ if Ca.Magnitude < EPSILON then return false end
254
+ local n = Ca.Unit
255
+
256
+ local d = L2 - L1
257
+ local m = L1 - C1
258
+
259
+ local md = m - n * m:Dot(n)
260
+ local dd = d - n * d:Dot(n)
261
+
262
+ local a = dd:Dot(dd)
263
+ local b = 2 * dd:Dot(md)
264
+ local c = md:Dot(md) - radius * radius
265
+
266
+ if math.abs(a) < EPSILON then
267
+ -- d parallel to cylinder axis or nearly so
268
+ local projL1 = (L1 - C1):Dot(n)
269
+ local radialL1 = (L1 - C1 - n * projL1).Magnitude
270
+ local projL2 = (L2 - C1):Dot(n)
271
+ local radialL2 = (L2 - C1 - n * projL2).Magnitude
272
+
273
+ local axisLen = Ca.Magnitude
274
+
275
+ if projL1 >= -EPSILON and projL1 <= axisLen + EPSILON and radialL1 <= radius + EPSILON then return true end
276
+ if projL2 >= -EPSILON and projL2 <= axisLen + EPSILON and radialL2 <= radius + EPSILON then return true end
277
+
278
+ return false
279
+ end
280
+
281
+ local discriminant = b * b - 4 * a * c
282
+ if discriminant < 0 then return false end
283
+
284
+ local sqrtDisc = math.sqrt(discriminant)
285
+ local t1 = (-b - sqrtDisc) / (2 * a)
286
+ local t2 = (-b + sqrtDisc) / (2 * a)
287
+
288
+ if t1 >= 0 - EPSILON and t1 <= 1 + EPSILON then
289
+ local point = L1 + d * t1
290
+ local projLength = (point - C1):Dot(n)
291
+ local axisLength = Ca.Magnitude
292
+ if projLength >= 0 - EPSILON and projLength <= axisLength + EPSILON then
293
+ return true
294
+ end
295
+ end
296
+
297
+ if t2 >= 0 - EPSILON and t2 <= 1 + EPSILON then
298
+ local point = L1 + d * t2
299
+ local projLength = (point - C1):Dot(n)
300
+ local axisLength = Ca.Magnitude
301
+ if projLength >= 0 - EPSILON and projLength <= axisLength + EPSILON then
302
+ return true
303
+ end
304
+ end
305
+
306
+ return false
307
+ end
308
+
309
+ local function PlaneIntersectsCylinder(P, S, U, V, C0, C1, radius)
310
+ local N = U:Cross(V).Unit
311
+ local Ca = C1 - C0
312
+ local axisLen = Ca.Magnitude
313
+ if axisLen < 1e-6 then return false end
314
+
315
+ local CaUnit = Ca / axisLen
316
+ local d0 = (C0 - P):Dot(N)
317
+ local d1 = (C1 - P):Dot(N)
318
+
319
+ if d0 > radius and d1 > radius then return false end
320
+ if d0 < -radius and d1 < -radius then return false end
321
+
322
+ local function ProjectOntoPlane(Q)
323
+ local toQ = Q - P
324
+ return Vector2.new(toQ:Dot(U), toQ:Dot(V))
325
+ end
326
+
327
+ local q0 = ProjectOntoPlane(C0)
328
+ local q1 = ProjectOntoPlane(C1)
329
+ local axis2D = q1 - q0
330
+ local center = (q0 + q1) * 0.5
331
+
332
+ local dir = axis2D.Unit
333
+ local halfLen = (axis2D.Magnitude * 0.5)
334
+
335
+ -- radius projects orthogonal to Ca axis in the plane only
336
+ local orthogonalU = (U - CaUnit * U:Dot(CaUnit)).Unit
337
+ local orthogonalV = (V - CaUnit * V:Dot(CaUnit)).Unit
338
+
339
+ local radiusU = radius * orthogonalU.Magnitude
340
+ local radiusV = radius * orthogonalV.Magnitude
341
+
342
+ local px = math.abs(center.X)
343
+ local py = math.abs(center.Y)
344
+ local dx = math.abs(dir.X) * halfLen + radiusU
345
+ local dy = math.abs(dir.Y) * halfLen + radiusV
346
+
347
+ return px <= S.X + dx and py <= S.Y + dy
348
+ end
349
+
350
+ local function CylinderIntersectsBlock(cylinderCFrame, cylinderSize, cylinderBoxVerts, cylinderBoxNormals, boxCFrame, boxSize, boxVerts, boxNormals)
351
+
352
+ local halfAxis = cylinderCFrame.RightVector * (cylinderSize.X * 0.5)
353
+ local p = cylinderCFrame.Position
354
+ local c0, c1 = p - halfAxis, p + halfAxis
355
+ local radius = 0.5 * math.min(cylinderSize.Y, cylinderSize.Z)
356
+
357
+ for _, seg in ipairs(block_edges) do
358
+ if SegmentIntersectsCylinder(boxVerts[seg[1]], boxVerts[seg[2]], c0, c1, radius) then return true end
359
+ end
360
+
361
+ if not SAT(cylinderBoxVerts, cylinderBoxNormals, boxVerts, boxNormals) then return false end
362
+
363
+ for i, a in ipairs(axisID) do
364
+ local la = localAxisVectors[a]
365
+ local lcf = boxCFrame[la] * boxSize[a] * 0.5
366
+
367
+ local sf = table.clone(axisID)
368
+ table.remove(sf, i)
369
+
370
+ local UA, VA = sf[1], sf[2]
371
+ local S = Vector2.new(boxSize[UA], boxSize[VA]) * 0.5
372
+
373
+ local U, V = boxCFrame[localAxisVectors[UA]], boxCFrame[localAxisVectors[VA]]
374
+
375
+ if PlaneIntersectsCylinder(boxCFrame.Position + lcf, S, U, V, c0, c1, radius) then return true end
376
+ if PlaneIntersectsCylinder(boxCFrame.Position - lcf, S, U, V, c0, c1, radius) then return true end
377
+ end
378
+
379
+ return false
380
+ end
381
+
382
+
383
+
384
+
385
+ local VertexMath = {}
386
+
387
+ VertexMath.GetVerts = GetVerts
388
+
389
+ VertexMath.GetNormals = GetNormals
390
+
391
+ VertexMath.SAT = SAT
392
+
393
+ VertexMath.PartEncapsulatesBlockPart = PartEncapsulatesBlockPart
394
+
395
+ VertexMath.PartContainsAllVerts = PartContainsAllVerts
396
+
397
+ VertexMath.PartContainsAVert = PartContainsAVert
398
+
399
+ VertexMath.BallIntersectsBlock = BallIntersectsBlock
400
+
401
+ VertexMath.CylinderIntersectsBlock = CylinderIntersectsBlock
402
+
403
+ return VertexMath