split apart modules
This commit is contained in:
235
collision.cm
Normal file
235
collision.cm
Normal file
@@ -0,0 +1,235 @@
|
||||
// Collision module for lance3d
|
||||
|
||||
// Private collider storage
|
||||
var _colliders = []
|
||||
var _collider_id = 0
|
||||
|
||||
function clear() {
|
||||
_colliders = []
|
||||
}
|
||||
|
||||
function add_sphere(transform, radius, opts) {
|
||||
opts = opts || {}
|
||||
var c = {
|
||||
id: _collider_id++,
|
||||
type: "sphere",
|
||||
transform: transform,
|
||||
radius: radius,
|
||||
layer_mask: opts.layer_mask || 1,
|
||||
user: opts.user
|
||||
}
|
||||
_colliders.push(c)
|
||||
return c
|
||||
}
|
||||
|
||||
function add_box(transform, sx, sy, sz, opts) {
|
||||
opts = opts || {}
|
||||
var c = {
|
||||
id: _collider_id++,
|
||||
type: "box",
|
||||
transform: transform,
|
||||
sx: sx, sy: sy, sz: sz,
|
||||
layer_mask: opts.layer_mask || 1,
|
||||
user: opts.user
|
||||
}
|
||||
_colliders.push(c)
|
||||
return c
|
||||
}
|
||||
|
||||
function remove(collider) {
|
||||
for (var i = 0; i < _colliders.length; i++) {
|
||||
if (_colliders[i].id == collider.id) {
|
||||
_colliders.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function overlaps(layer_mask_a, layer_mask_b) {
|
||||
var results = []
|
||||
for (var i = 0; i < _colliders.length; i++) {
|
||||
for (var j = i + 1; j < _colliders.length; j++) {
|
||||
var a = _colliders[i]
|
||||
var b = _colliders[j]
|
||||
|
||||
if (layer_mask_a != null && !(a.layer_mask & layer_mask_a)) continue
|
||||
if (layer_mask_b != null && !(b.layer_mask & layer_mask_b)) continue
|
||||
|
||||
if (_check_collision(a, b)) {
|
||||
results.push({a: a, b: b})
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
function raycast(ox, oy, oz, dx, dy, dz, opts) {
|
||||
opts = opts || {}
|
||||
var max_dist = opts.max_dist || 1000000
|
||||
var layer_mask = opts.layer_mask || 0xFFFFFFFF
|
||||
|
||||
// Normalize direction
|
||||
var len = Math.sqrt(dx*dx + dy*dy + dz*dz)
|
||||
if (len < 0.0001) return null
|
||||
dx /= len
|
||||
dy /= len
|
||||
dz /= len
|
||||
|
||||
var closest = null
|
||||
var closest_dist = max_dist
|
||||
|
||||
for (var i = 0; i < _colliders.length; i++) {
|
||||
var c = _colliders[i]
|
||||
if (!(c.layer_mask & layer_mask)) continue
|
||||
|
||||
var hit = null
|
||||
if (c.type == "sphere") {
|
||||
hit = _ray_sphere(ox, oy, oz, dx, dy, dz, c)
|
||||
} else if (c.type == "box") {
|
||||
hit = _ray_box(ox, oy, oz, dx, dy, dz, c)
|
||||
}
|
||||
|
||||
if (hit && hit.distance < closest_dist) {
|
||||
closest = hit
|
||||
closest_dist = hit.distance
|
||||
}
|
||||
}
|
||||
|
||||
return closest
|
||||
}
|
||||
|
||||
function _get_position(transform) {
|
||||
if (!transform) return {x: 0, y: 0, z: 0}
|
||||
// If transform is a matrix (array of 16), extract translation
|
||||
if (transform.length == 16) {
|
||||
return {x: transform[12], y: transform[13], z: transform[14]}
|
||||
}
|
||||
// If transform is an object with x,y,z
|
||||
if (transform.x != null) {
|
||||
return {x: transform.x, y: transform.y, z: transform.z}
|
||||
}
|
||||
return {x: 0, y: 0, z: 0}
|
||||
}
|
||||
|
||||
function _check_collision(a, b) {
|
||||
var pa = _get_position(a.transform)
|
||||
var pb = _get_position(b.transform)
|
||||
|
||||
var dx = pb.x - pa.x
|
||||
var dy = pb.y - pa.y
|
||||
var dz = pb.z - pa.z
|
||||
var dist = Math.sqrt(dx*dx + dy*dy + dz*dz)
|
||||
|
||||
// Simple sphere-sphere approximation
|
||||
var ra = a.radius || Math.max(a.sx || 0, a.sy || 0, a.sz || 0)
|
||||
var rb = b.radius || Math.max(b.sx || 0, b.sy || 0, b.sz || 0)
|
||||
return dist < ra + rb
|
||||
}
|
||||
|
||||
function _ray_sphere(ox, oy, oz, dx, dy, dz, sphere) {
|
||||
var pos = _get_position(sphere.transform)
|
||||
var r = sphere.radius
|
||||
|
||||
// Vector from ray origin to sphere center
|
||||
var lx = pos.x - ox
|
||||
var ly = pos.y - oy
|
||||
var lz = pos.z - oz
|
||||
|
||||
// Project onto ray direction
|
||||
var tca = lx*dx + ly*dy + lz*dz
|
||||
if (tca < 0) return null
|
||||
|
||||
var d2 = lx*lx + ly*ly + lz*lz - tca*tca
|
||||
var r2 = r*r
|
||||
if (d2 > r2) return null
|
||||
|
||||
var thc = Math.sqrt(r2 - d2)
|
||||
var t = tca - thc
|
||||
if (t < 0) t = tca + thc
|
||||
if (t < 0) return null
|
||||
|
||||
var hx = ox + dx*t
|
||||
var hy = oy + dy*t
|
||||
var hz = oz + dz*t
|
||||
|
||||
// Normal at hit point
|
||||
var nx = (hx - pos.x) / r
|
||||
var ny = (hy - pos.y) / r
|
||||
var nz = (hz - pos.z) / r
|
||||
|
||||
return {
|
||||
x: hx, y: hy, z: hz,
|
||||
nx: nx, ny: ny, nz: nz,
|
||||
distance: t,
|
||||
collider: sphere
|
||||
}
|
||||
}
|
||||
|
||||
function _ray_box(ox, oy, oz, dx, dy, dz, box) {
|
||||
var pos = _get_position(box.transform)
|
||||
var hx = (box.sx || 1) / 2
|
||||
var hy = (box.sy || 1) / 2
|
||||
var hz = (box.sz || 1) / 2
|
||||
|
||||
var minx = pos.x - hx, maxx = pos.x + hx
|
||||
var miny = pos.y - hy, maxy = pos.y + hy
|
||||
var minz = pos.z - hz, maxz = pos.z + hz
|
||||
|
||||
var tmin = -1000000, tmax = 1000000
|
||||
var nx = 0, ny = 0, nz = 0
|
||||
|
||||
// X slab
|
||||
if (Math.abs(dx) > 0.0001) {
|
||||
var t1 = (minx - ox) / dx
|
||||
var t2 = (maxx - ox) / dx
|
||||
if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp }
|
||||
if (t1 > tmin) { tmin = t1; nx = dx > 0 ? -1 : 1; ny = 0; nz = 0 }
|
||||
if (t2 < tmax) tmax = t2
|
||||
} else if (ox < minx || ox > maxx) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Y slab
|
||||
if (Math.abs(dy) > 0.0001) {
|
||||
var t1 = (miny - oy) / dy
|
||||
var t2 = (maxy - oy) / dy
|
||||
if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp }
|
||||
if (t1 > tmin) { tmin = t1; nx = 0; ny = dy > 0 ? -1 : 1; nz = 0 }
|
||||
if (t2 < tmax) tmax = t2
|
||||
} else if (oy < miny || oy > maxy) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Z slab
|
||||
if (Math.abs(dz) > 0.0001) {
|
||||
var t1 = (minz - oz) / dz
|
||||
var t2 = (maxz - oz) / dz
|
||||
if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp }
|
||||
if (t1 > tmin) { tmin = t1; nx = 0; ny = 0; nz = dz > 0 ? -1 : 1 }
|
||||
if (t2 < tmax) tmax = t2
|
||||
} else if (oz < minz || oz > maxz) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (tmin > tmax || tmax < 0) return null
|
||||
|
||||
var t = tmin > 0 ? tmin : tmax
|
||||
if (t < 0) return null
|
||||
|
||||
return {
|
||||
x: ox + dx*t, y: oy + dy*t, z: oz + dz*t,
|
||||
nx: nx, ny: ny, nz: nz,
|
||||
distance: t,
|
||||
collider: box
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
clear: clear,
|
||||
add_sphere: add_sphere,
|
||||
add_box: add_box,
|
||||
remove: remove,
|
||||
overlaps: overlaps,
|
||||
raycast: raycast
|
||||
}
|
||||
Reference in New Issue
Block a user