UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

204 lines (166 loc) • 6.24 kB
import { assert } from "../../../../core/assert.js"; import { Ray3 } from "../../../../core/geom/3d/ray/Ray3.js"; import { compute_triangle_area_3d } from "../../../../core/geom/3d/triangle/compute_triangle_area_3d.js"; import { v3_compute_triangle_normal } from "../../../../core/geom/3d/triangle/v3_compute_triangle_normal.js"; import { clamp } from "../../../../core/math/clamp.js"; import { roundFair } from "../../../../core/math/random/roundFair.js"; function generate_hole_shapes_barycentric( holes_positions, hole_size, ax, ay, az, bx, by, bz, cx, cy, cz, ) { throw new Error('Not implemented'); } /** * * @param {number[]} output where to write new triangles * @param {number} output_offset * @param {number[][]} hole_shapes * @param {number} ax * @param {number} ay * @param {number} az * @param {number} bx * @param {number} by * @param {number} bz * @param {number} cx * @param {number} cy * @param {number} cz * @returns {number} new triangles added to the output */ function triangle_punch_holes( output, output_offset, hole_shapes, ax, ay, az, bx, by, bz, cx, cy, cz, ) { throw new Error('Not implemented'); } /** * * @param {BVH} bvh * @param {number} agent_height * @param {number} agent_radius * @param {number} triangle_count * @param {number[]} triangles * @param {function():number} random * @returns {number} new triangle count */ export function enforce_agent_height_clearance({ bvh, agent_height, agent_radius, triangle_count, triangles, random }) { console.warn('enforce_agent_height_clearance() is not implemented'); return triangle_count; /** * Spatial resolution for performing clearance checks * @type {number} */ const sampling_resolution = Math.min( agent_height / 4, Math.max(agent_radius / 2, 0.001) ); assert.notNaN(sampling_resolution, 'sampling_resolution'); assert.isFinite(sampling_resolution, 'sampling_resolution'); const sample_area = sampling_resolution * sampling_resolution; const triangle_normal = new Float32Array(3); const ray = new Ray3(); ray.tMax = agent_height; let current_triangle_count = triangle_count; for (let i = 0; i < current_triangle_count; i++) { const triangle_address = i * 9; const ax = triangles[triangle_address]; const ay = triangles[triangle_address + 1]; const az = triangles[triangle_address + 2]; const bx = triangles[triangle_address + 3]; const by = triangles[triangle_address + 4]; const bz = triangles[triangle_address + 5]; const cx = triangles[triangle_address + 6]; const cy = triangles[triangle_address + 7]; const cz = triangles[triangle_address + 8]; // TODO we can accelerate this by performing a clipping volume query first instead // majority of the triangles are expected to fully pass clearance, and a single clipping volume test will be able to give us "Not obstructed" answer. const triangle_area = compute_triangle_area_3d( ax, ay, az, bx, by, bz, cx, cy, cz ); if (triangle_area === 0) { // a degenerate triangle, should not happen but no point in sampling continue; } // construct normal v3_compute_triangle_normal( triangle_normal, 0, ax, ay, az, bx, by, bz, cx, cy, cz ); const sample_count_desired = triangle_area / sample_area; const sample_count_rounded = roundFair(sample_count_desired, random); // Guard against degenerate cases const SAMPLE_COUNT_MAX = 32000; const sample_count = clamp(sample_count_rounded, 1, SAMPLE_COUNT_MAX); // perform sampling const normal_x = triangle_normal[0]; const normal_y = triangle_normal[1]; const normal_z = triangle_normal[2]; ray.setDirection(normal_x, normal_y, normal_z); // construct edges for sampling const e0x = bx - ax; const e0y = by - ay; const e0z = bz - az; const e1x = cx - bx; const e1y = cy - by; const e1z = cz - bz; /** * Barycentric coordinates of where the holes are * @type {number[]} */ const holes = []; for (let j = 0; j < sample_count; j++) { // perform initial sample const r0 = random(); const r1 = random() * (1 - r0); const x = ax + r0 * e0x + r1 * e1x; const y = ay + r0 * e0y + r1 * e1y; const z = az + r0 * e0z + r1 * e1z; ray.setOrigin(x, y, z); // tiny offset to avoid self-occlusion ray.shiftForward(1e-7); // TODO actual raycast // if we got a hit - create a hole triangle, if no hit - continue holes.push(r0, r1); } if (holes.length === 0) { // clearance OK continue; } const hole_shapes = generate_hole_shapes_barycentric( holes, sampling_resolution, ax, ay, az, bx, by, bz, cx, cy, cz ); const added_triangle_count = triangle_punch_holes( triangles, triangle_count, hole_shapes, ax, ay, az, bx, by, bz, cx, cy, cz ); triangle_count += added_triangle_count; // TODO remove the original triangle, cut out the holes and add new triangles back } // return current_triangle_count; }