@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
148 lines (113 loc) • 3.8 kB
JavaScript
import { mat4 } from "gl-matrix";
import { array_copy } from "../../../../core/collection/array/array_copy.js";
import { AABB3 } from "../../../../core/geom/3d/aabb/AABB3.js";
import { aabb3_from_v3_array_transformed } from "../../../../core/geom/3d/aabb/aabb3_from_v3_array_transformed.js";
import { allocate_m4 } from "../../../../core/geom/3d/mat4/allocate_m4.js";
import { decompose_matrix_4_array } from "../../../../core/geom/3d/mat4/decompose_matrix_4_array.js";
import { Ray3 } from "../../../../core/geom/3d/ray/Ray3.js";
import Quaternion from "../../../../core/geom/Quaternion.js";
import { v3_distance } from "../../../../core/geom/vec3/v3_distance.js";
import Vector3 from "../../../../core/geom/Vector3.js";
import { ray_hit_apply_transform } from "./ray_hit_apply_transform.js";
const local_ray = new Ray3();
let mesh_id_counter = 0;
export class PathTracedMesh {
/**
*
* @type {number}
*/
id = mesh_id_counter++;
/**
*
* @type {AABB3}
*/
aabb = new AABB3();
/**
*
* @type {BufferedGeometryBVH|null}
*/
bvh = null;
/**
*
* @type {THREE.BufferGeometry|null}
*/
geometry = null;
/**
*
* @type {StandardMaterial|null}
*/
material = null;
#transform = allocate_m4();
#transform_inverse = allocate_m4();
get transform_inverse() {
return this.#transform_inverse;
}
build_tight_bounds() {
const position = new Vector3();
const rotation = new Quaternion();
const scale = new Vector3();
decompose_matrix_4_array(this.#transform, position, rotation, scale);
if (rotation.roughlyEquals(Quaternion.identity)) {
// no rotation component, can safely scale/translate geometry bounds to get tight fit
this.update_bounds();
} else {
const position_attribute = this.geometry.getAttribute('position');
const position_array = position_attribute.array;
aabb3_from_v3_array_transformed(
this.aabb,
position_array, position_array.length,
this.#transform
);
}
}
update_bounds() {
this.bvh.getBounds(this.aabb);
this.aabb.applyMatrix4(this.#transform);
}
set transform(m) {
array_copy(m, 0, this.#transform, 0, 16);
mat4.invert(this.#transform_inverse, m);
if (this.bvh !== null) {
this.update_bounds();
}
}
get transform() {
return this.#transform;
}
/**
*
* @param {Ray3} ray
* @returns {boolean}
*/
occluded(ray) {
const m4 = this.#transform_inverse;
local_ray.copy(ray);
local_ray.applyMatrix4(m4);
return this.bvh.occluded(local_ray);
}
/**
*
* @param {number[]} out
* @param {number[]|Ray3} ray
* @param {number} ray_limit overrides Ray.tMax
* @returns {number} distance along the ray to contact
*/
hit(out, ray, ray_limit) {
//transform ray
const m4 = this.#transform_inverse;
local_ray.copy(ray);
local_ray.tMax = ray_limit;
local_ray.applyMatrix4(m4);
let distance_to_hit = this.bvh.raycast2(out, local_ray);
if (distance_to_hit >= 0) {
// transform output
ray_hit_apply_transform(out, out, this.#transform);
out[10] = this.id;
distance_to_hit = v3_distance(
ray[0], ray[1], ray[2],
out[0], out[1], out[2]
);
}
return distance_to_hit;
}
}