@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
204 lines (166 loc) • 6.24 kB
JavaScript
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;
}