@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
252 lines (195 loc) • 9.16 kB
JavaScript
import { assert } from "../../../../../core/assert.js";
import {
typed_array_value_denormalize
} from "../../../../../core/collection/array/typed/typed_array_value_denormalize.js";
import { SCRATCH_UINT32_TRAVERSAL_STACK } from "../../../../../core/collection/SCRATCH_UINT32_TRAVERSAL_STACK.js";
import {
aabb3_unsigned_distance_sqr_to_point
} from "../../../../../core/geom/3d/aabb/aabb3_unsigned_distance_sqr_to_point.js";
import {
computeTriangleClosestPointToPointBarycentric
} from "../../../../../core/geom/3d/triangle/computeTriangleClosestPointToPointBarycentric.js";
import { v3_compute_triangle_normal } from "../../../../../core/geom/3d/triangle/v3_compute_triangle_normal.js";
import { v3_distance_sqr } from "../../../../../core/geom/vec3/v3_distance_sqr.js";
const stack = SCRATCH_UINT32_TRAVERSAL_STACK;
/**
*
* @type {number[]}
*/
const v3_scratch_0 = [];
/**
* Get the nearest point on the geometry, relative to query point
*
* @param {SurfacePoint3} result
* @param {BinaryUint32BVH} bvh
* @param {number[]|ArrayLike<number>} vertices
* @param {number} vertex_offset Unless you're using an interleaved buffer of some kind, this will be 0
* @param {number} vertex_stride Unless you're using an interleaved buffer, this should be 3
* @param {boolean} vertex_data_normalized do we need to denormalize vertex data?
* @param {number[]|ArrayLike<number>|undefined} indices if this is set to undefined - implicit indexing will be used
* @param {number} x Query point X
* @param {number} y Query point Y
* @param {number} z Query point Z
* @param {number} [max_distance] limit search distance, useful for optimization
* @returns {boolean} true iff a point was found, false otherwise
*/
export function bvh32_geometry_nearest(
result,
bvh,
vertices, vertex_offset, vertex_stride,
vertex_data_normalized,
indices,
x, y, z,
max_distance = Infinity,
) {
let nearest_distance_sqr = max_distance * max_distance;
let got_a_hit = false;
const binary_node_count = bvh.binary_node_count;
if (binary_node_count <= 0) {
// this should not happen
return false;
}
/**
*
* @type {number}
*/
const stack_top = stack.pointer++;
/**
* After performing empirical tests, stack-based depth-first traversal turns out faster than using a queue
* @type {number}
*/
stack[stack_top] = 0;
const last_valid_index = binary_node_count + bvh.leaf_node_count;
const float32 = bvh.float32;
const uint32 = bvh.uint32;
do {
stack.pointer--;
// query_bvh_frustum_from_objects.iteration_count++;
const node_index = stack[stack.pointer];
const node_address = bvh.getNodeAddress(node_index);
const is_intermediate_node = node_index < binary_node_count;
if (is_intermediate_node) {
// is intermediate node
const left_index = (node_index << 1) + 1;
const right_index = left_index + 1;
if (right_index < last_valid_index) {
const child_1_address = bvh.getNodeAddress(left_index);
const distance_sqr_to_child1 = aabb3_unsigned_distance_sqr_to_point(
float32[child_1_address], float32[child_1_address + 1], float32[child_1_address + 2],
float32[child_1_address + 3], float32[child_1_address + 4], float32[child_1_address + 5],
x, y, z
);
const child_2_address = bvh.getNodeAddress(right_index);
const distance_sqr_to_child2 = aabb3_unsigned_distance_sqr_to_point(
float32[child_2_address], float32[child_2_address + 1], float32[child_2_address + 2],
float32[child_2_address + 3], float32[child_2_address + 4], float32[child_2_address + 5],
x, y, z
);
if (distance_sqr_to_child1 < distance_sqr_to_child2) {
if (distance_sqr_to_child2 < nearest_distance_sqr) {
stack[stack.pointer++] = right_index;
}
if (distance_sqr_to_child1 < nearest_distance_sqr) {
stack[stack.pointer++] = left_index;
}
} else {
if (distance_sqr_to_child1 < nearest_distance_sqr) {
stack[stack.pointer++] = left_index;
}
if (distance_sqr_to_child2 < nearest_distance_sqr) {
stack[stack.pointer++] = right_index;
}
}
} else if (left_index < last_valid_index) {
stack[stack.pointer++] = left_index;
}
} else {
// leaf node
// read triangle data
const triangle_index = uint32[node_address + 6];
const index3 = triangle_index * 3;
let a, b, c;
if (indices !== undefined) {
assert.lessThan(index3 + 2, indices.length, 'triangle index overflow, possibly geometry changed but tree was not rebuilt?');
a = indices[index3];
b = indices[index3 + 1];
c = indices[index3 + 2];
} else {
// implicit indices
a = index3;
b = index3 + 1;
c = index3 + 2;
}
const a_address = a * vertex_stride + vertex_offset;
const b_address = b * vertex_stride + vertex_offset;
const c_address = c * vertex_stride + vertex_offset;
assert.lessThan(a_address + 2, vertices.length, 'a-vertex overflow');
assert.lessThan(b_address + 2, vertices.length, 'b-vertex overflow');
assert.lessThan(c_address + 2, vertices.length, 'c-vertex overflow');
let ax = vertices[a_address];
let ay = vertices[a_address + 1];
let az = vertices[a_address + 2];
let bx = vertices[b_address];
let by = vertices[b_address + 1];
let bz = vertices[b_address + 2];
let cx = vertices[c_address];
let cy = vertices[c_address + 1];
let cz = vertices[c_address + 2];
// denormalize if necessary
if (vertex_data_normalized) {
ax = typed_array_value_denormalize(ax, vertices);
ay = typed_array_value_denormalize(ay, vertices);
az = typed_array_value_denormalize(az, vertices);
bx = typed_array_value_denormalize(bx, vertices);
by = typed_array_value_denormalize(by, vertices);
bz = typed_array_value_denormalize(bz, vertices);
cx = typed_array_value_denormalize(cx, vertices);
cy = typed_array_value_denormalize(cy, vertices);
cz = typed_array_value_denormalize(cz, vertices);
}
// denormalize if necessary
if (vertex_data_normalized) {
ax = typed_array_value_denormalize(ax, vertices);
ay = typed_array_value_denormalize(ay, vertices);
az = typed_array_value_denormalize(az, vertices);
bx = typed_array_value_denormalize(bx, vertices);
by = typed_array_value_denormalize(by, vertices);
bz = typed_array_value_denormalize(bz, vertices);
cx = typed_array_value_denormalize(cx, vertices);
cy = typed_array_value_denormalize(cy, vertices);
cz = typed_array_value_denormalize(cz, vertices);
}
computeTriangleClosestPointToPointBarycentric(
v3_scratch_0, 0,
x, x, x,
ax, ay, az,
bx, by, bz,
cx, cy, cz
);
const u = v3_scratch_0[0];
const v = v3_scratch_0[1];
const w = 1 - u - v;
// construct edge
const contact_x = ax * u + bx * v + cx * w;
const contact_y = ay * u + by * v + cy * w;
const contact_z = az * u + bz * v + cz * w;
const distance_to_triangle_sqr = v3_distance_sqr(
contact_x, contact_y, contact_z,
x, y, z
);
if (distance_to_triangle_sqr >= nearest_distance_sqr) {
continue;
}
nearest_distance_sqr = distance_to_triangle_sqr;
result.position.set(contact_x, contact_y, contact_z);
v3_compute_triangle_normal(result.normal, 0,
ax, ay, az,
bx, by, bz,
cx, cy, cz
);
result.index = triangle_index;
got_a_hit = true;
}
} while (stack.pointer > stack_top);
return got_a_hit;
}