import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; import { NodeIO } from "@gltf-transform/core"; import { BufferAttribute, BufferGeometry, DoubleSide, Ray, Vector3, } from "three"; import { MeshBVH } from "three-mesh-bvh"; const here = dirname(fileURLToPath(import.meta.url)); const collisionPath = resolve( here, "../src/assets/game/dungeons/wailing-caverns/wailing-caverns-collision.glb", ); const document = await new NodeIO().read(collisionPath); const primitive = document.getRoot().listMeshes()[0]?.listPrimitives()[0]; if (!primitive) throw new Error("Collision GLB has no mesh primitive."); const positions = primitive.getAttribute("POSITION")?.getArray(); const indices = primitive.getIndices()?.getArray(); if (!positions || !indices) throw new Error("Collision GLB lacks indexed positions."); const geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(positions, 3)); geometry.setIndex(new BufferAttribute(indices, 1)); const bvh = new MeshBVH(geometry); const foot = new Vector3(163.49, -73.66, 132.9); const alternateFoot = new Vector3(-163.49, -73.66, -132.9); const body = foot.clone().add(new Vector3(0, 0.84, 0)); const yaw = -2.024; const pitch = 0.32; const horizontalDistance = 5.8 * Math.cos(pitch); const target = body.clone().add(new Vector3(0, 0.45, 0)); const camera = new Vector3( target.x - Math.sin(yaw) * horizontalDistance, target.y + 0.8 + 5.8 * Math.sin(pitch), target.z - Math.cos(yaw) * horizontalDistance, ); const lookAt = new Vector3( target.x + Math.sin(yaw) * 1.25, target.y + 0.08, target.z + Math.cos(yaw) * 1.25, ); function closest(point) { const hit = bvh.closestPointToPoint(point); return hit ? { distance: hit.distance, point: hit.point.toArray() } : null; } function raycast(origin, destination, far = Infinity) { const direction = destination.clone().sub(origin).normalize(); const hit = bvh.raycastFirst(new Ray(origin, direction), DoubleSide, 0, far); return hit ? { distance: hit.distance, point: hit.point.toArray() } : null; } console.log(JSON.stringify({ foot: foot.toArray(), footClosestSurface: closest(foot), alternateFoot: alternateFoot.toArray(), alternateClosestSurface: closest(alternateFoot), camera: camera.toArray(), cameraClosestSurface: closest(camera), target: target.toArray(), cameraToTargetDistance: camera.distanceTo(target), cameraToTargetFirstCollision: raycast(camera, target, camera.distanceTo(target)), cameraViewFirstCollision: raycast(camera, lookAt), }, null, 2));