@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
237 lines (182 loc) • 5.88 kB
JavaScript
import { assert } from "../../../../core/assert.js";
import { array_shift_back } from "../../../../core/collection/array/array_shift_back.js";
import {
compute_delaunay_tetrahedral_mesh
} from "../../../../core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js";
import {
tetrahedral_mesh_build_from_grid
} from "../../../../core/geom/3d/tetrahedra/delaunay/grid/tetrahedral_mesh_build_from_grid.js";
import { TetrahedralMesh } from "../../../../core/geom/3d/tetrahedra/TetrahedralMesh.js";
/**
* Irradiance field representation
* @see "Irradiance Volumes" by G.Greger, 1998
*/
export class LightProbeVolume {
#version = 0;
/**
*
* @type {number[]}
*/
#probe_positions = [];
#mesh = new TetrahedralMesh();
/**
* Probe irradiance color data
* Base-3 spherical harmonics, 9 coefficients per color channel for a total of 27 coefficients per probe
* @type {number[]}
*/
#sh3_rgb = [];
/**
* Resolution of an individual depth map for a probe
* Depth map is stored as a 2d texture of size RESOLUTION x RESOLUTION
* @type {number}
*/
#probe_depth_resolution = 12;
/**
* Octahedral-encoded depth map for each probe, 2 channels for mean and mean squared for variance recovery in the shader
* @type {number[]}
*/
#probe_depth = [];
#probe_count = 0;
/**
*
* @return {number}
*/
get version() {
return this.#version;
}
clear() {
if (this.#probe_positions.length > 0) {
this.#probe_positions = [];
}
if (this.#sh3_rgb.length > 0) {
this.#sh3_rgb = [];
}
if (this.#probe_depth.length > 0) {
this.#probe_depth = [];
}
this.#mesh.clear();
this.#probe_count = 0;
this.#version++;
}
/**
*
* @return {TetrahedralMesh}
*/
get mesh() {
return this.#mesh;
}
get points() {
return this.#probe_positions;
}
get harmonics() {
return this.#sh3_rgb;
}
get depth_map_resolution() {
return this.#probe_depth_resolution;
}
/**
*
* @param {number} v
*/
set depth_map_resolution(v) {
assert.isNonNegativeInteger(v, 'v');
this.#probe_depth_resolution = v;
}
get depth() {
return this.#probe_depth;
}
get count() {
return this.#probe_count;
}
set count(v) {
this.#probe_count = v;
this.#ensure_capacity(v);
}
/**
*
* @param {number} capacity
*/
#ensure_capacity(capacity) {
const current_capacity = this.#probe_positions.length / 3;
if (current_capacity >= capacity) {
return;
}
const new_capacity = Math.max(
current_capacity + 16,
Math.ceil(current_capacity * 1.2),
capacity
);
const new_positions = new Float32Array(new_capacity * 3);
new_positions.set(this.#probe_positions);
const new_depth = new Float32Array(new_capacity * this.#compute_depth_element_size());
new_depth.set(this.#probe_depth);
const new_color = new Float32Array(new_capacity * 27);
new_color.set(this.#sh3_rgb);
this.#probe_positions = new_positions;
this.#probe_depth = new_depth;
this.#sh3_rgb = new_color;
}
/**
*
* @param {number} x
* @param {number} y
* @param {number} z
* @returns {number}
*/
add_point(x, y, z) {
const i = this.#probe_count;
this.#ensure_capacity(i + 1);
const i3 = i * 3;
this.#probe_positions[i3] = x;
this.#probe_positions[i3 + 1] = y;
this.#probe_positions[i3 + 2] = z;
for (let j = 0; j < 9 * 3; j++) {
this.#sh3_rgb[i3 * 9 + j] = 1; // fill with white
}
this.#probe_count++;
this.#version++;
return i;
}
#compute_depth_element_size() {
return this.#probe_depth_resolution * this.#probe_depth_resolution * 2;
}
/**
*
* @param {number} index
*/
remove_point(index) {
array_shift_back(this.#probe_positions, (index + 1) * 3, 3, (this.#probe_count - index - 1) * 3);
array_shift_back(this.#sh3_rgb, (index + 1) * 27, 27, (this.#probe_count - index - 1) * 27);
const depth_element_count = this.#compute_depth_element_size();
array_shift_back(this.#probe_depth, (index + 1) * depth_element_count, depth_element_count, (this.#probe_count - index - 1) * depth_element_count);
this.#probe_count--;
this.#version++;
}
/**
*
* @param {AABB3} bounds
* @param {Vector3|{x:number, y:number, z:number}} resolution
*/
build_grid(bounds, resolution) {
this.#probe_count = tetrahedral_mesh_build_from_grid(
this.#mesh, this.#probe_positions,
bounds, resolution.x, resolution.y, resolution.z
);
}
/**
* Build tetrahedral mesh
*/
build_mesh() {
this.#mesh.clear();
const t0 = performance.now();
compute_delaunay_tetrahedral_mesh(this.#mesh, this.#probe_positions, this.#probe_count);
const t1 = performance.now();
// optional step to improve memory utilization
this.#mesh.compact();
const t2 = performance.now();
console.log(`Tetrahedral mesh (${this.#probe_count} points, ${this.#mesh.count} tets) build took ${t2 - t0}ms`);
}
incrementVersion() {
this.#version++;
}
}