63 lines
1.6 KiB
TypeScript
63 lines
1.6 KiB
TypeScript
|
|
import type { Vec3 } from "../math/Vec3"
|
||
|
|
import { Collider, type Collider as ColliderShape } from "./Collider"
|
||
|
|
import type { Terrain } from "./Terrain"
|
||
|
|
|
||
|
|
export type CollisionWorld = {
|
||
|
|
terrain: Terrain
|
||
|
|
staticColliders: ColliderShape[]
|
||
|
|
dynamicColliders: ColliderShape[]
|
||
|
|
}
|
||
|
|
|
||
|
|
export namespace CollisionWorld {
|
||
|
|
export function create(
|
||
|
|
terrain: Terrain,
|
||
|
|
staticColliders: ColliderShape[],
|
||
|
|
): CollisionWorld {
|
||
|
|
return { terrain, staticColliders, dynamicColliders: [] }
|
||
|
|
}
|
||
|
|
|
||
|
|
export function pushOut(
|
||
|
|
world: CollisionWorld,
|
||
|
|
position: Vec3,
|
||
|
|
radius: number,
|
||
|
|
): void {
|
||
|
|
pushFrom(world.staticColliders, position, radius)
|
||
|
|
pushFrom(world.dynamicColliders, position, radius)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function groundHeight(world: CollisionWorld, position: Vec3): number {
|
||
|
|
let ground = world.terrain.heightAt(position.x, position.z)
|
||
|
|
ground = standingHeight(world.staticColliders, position, ground)
|
||
|
|
return standingHeight(world.dynamicColliders, position, ground)
|
||
|
|
}
|
||
|
|
|
||
|
|
function pushFrom(
|
||
|
|
colliders: ColliderShape[],
|
||
|
|
position: Vec3,
|
||
|
|
radius: number,
|
||
|
|
): void {
|
||
|
|
for (const collider of colliders) {
|
||
|
|
if (position.y < collider.top - 0.01) {
|
||
|
|
Collider.pushOut(collider, position, radius)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function standingHeight(
|
||
|
|
colliders: ColliderShape[],
|
||
|
|
position: Vec3,
|
||
|
|
initial: number,
|
||
|
|
): number {
|
||
|
|
let ground = initial
|
||
|
|
for (const collider of colliders) {
|
||
|
|
if (
|
||
|
|
collider.standable &&
|
||
|
|
Collider.contains(collider, position.x, position.z)
|
||
|
|
) {
|
||
|
|
ground = Math.max(ground, collider.top)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ground
|
||
|
|
}
|
||
|
|
}
|