UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

237 lines (182 loc) • 5.88 kB
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++; } }