@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
88 lines (68 loc) • 2.92 kB
JavaScript
import { array_copy } from "../../../../collection/array/array_copy.js";
import { min2 } from "../../../../math/min2.js";
import { ConicRay } from "../../../ConicRay.js";
import { Miniball } from "../../../packing/miniball/Miniball.js";
import { PointSet } from "../../../packing/miniball/PointSet.js";
import { v3_angle_between } from "../../../vec3/v3_angle_between.js";
/**
* Using Shirman and Abi-Ezzi method, compute bounding sphere of points, then build a cone from that
* @see L.A. Shirman and S.S. Abi-Ezzi, The cone of normals technique for fast processing of curved patches, Computer Graphics Forum, 12 (1993), 261-272.
* @param {ConicRay} result
* @param {Iterable<TopoTriangle>} faces
*/
export function computeTriangleClusterNormalBoundingCone(result, faces) {
const normal_data = [];
let normal_count = 0;
// extract face normals
for (let face of faces) {
const face_normal = face.normal;
if (face_normal[0] === 0 && face_normal[1] === 0 && face_normal[1] === 0) {
// skip
continue;
}
const address = normal_count * 3;
array_copy(face_normal, 0, normal_data, address, 3);
normal_count++;
}
const result_direction = result.direction;
if (normal_count === 0) {
// no valid normals, produce an arbitrary result
result_direction.set(1, 0, 0);
result.angle = 0;
return;
}
const point_set = new PointSet(normal_count, 3, normal_data);
const miniball = new Miniball(point_set);
const miniball_center = miniball.center();
if (Number.isNaN(miniball_center[0]) || Number.isNaN(miniball_center[1]) || Number.isNaN(miniball_center[2])) {
// something went wrong, center is undefined, fall-back to an open cone
result_direction.set(1, 0, 0);
result.angle = Math.PI;
return;
}
result_direction.fromArray(miniball_center, 0);
const center_vector_magnitude = result_direction.length();
if (center_vector_magnitude === 0) {
// degenerate, pointing in all directions
result_direction.set(1, 0, 0);
result.angle = Math.PI;
return;
}
result_direction.multiplyScalar(1 / center_vector_magnitude); // normalize direction vector
// include normals into the cone
let max_angle = 0;
for (let i = 0; i < normal_count; i++) {
const i3 = i * 3;
const normal_x = normal_data[i3];
const normal_y = normal_data[i3];
const normal_z = normal_data[i3];
const angle = v3_angle_between(
normal_x, normal_y, normal_z,
result_direction.x, result_direction.y, result_direction.z
);
if (angle > max_angle) {
max_angle = angle;
}
}
result.angle = min2(max_angle, Math.PI);
}