@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
126 lines (108 loc) • 4.26 kB
JavaScript
import { query_bvh_frustum_from_texture } from "./query/query_bvh_frustum_from_texture.js";
import { compute_light_data_hash_0 } from "./cluster/compute_light_data_hash_0.js";
import { testClusterEquality } from "./testClusterEquality.js";
const LOOKUP_CACHE_SIZE = 1024; // must be power of two
/**
* Using continuous buffer for related attributes to get better cache utilization
* @type {ArrayBuffer}
* @private
*/
const _scratch_array_buffer = new ArrayBuffer(
24 * 4
+ 24 * 4
+ LOOKUP_CACHE_SIZE * 4
);
/**
*
* @type {number[]|Float32Array|Float64Array}
*/
export const scratch_corners = new Float32Array(_scratch_array_buffer, 0, 24);
/**
* Linear access data structure with parameters of each plane of the frustum
* 6 planes, 4 values per plane
* Layout: [normal.x, normal.y, normal.z, constant]
* @type {number[]|Float32Array|Float64Array}
*/
export const scratch_frustum_planes = new Float32Array(_scratch_array_buffer, 24 * 4, 24);
/**
* Cache used to identify duplicate cells. Helps reduce memory usage and just point to equivalent existing cells instead of allocating new ones.
* @type {Uint16Array|Uint32Array}
*/
export const LOOKUP_CACHE = new Uint32Array(_scratch_array_buffer, (24 + 24) * 4, LOOKUP_CACHE_SIZE);
/**
*
* @type {number[]}
*/
const scratch_light_nodes = [];
/**
*
* @param {number} tile_texture_data_offset
* @param {BinaryUint32BVH} bvh
* @param {number} lookup_address_offset
* @param {number[]|ArrayLike<number>|Uint8ClampedArray|Uint8Array|Uint16Array|Uint32Array} lookup_data
* @param {number[]|ArrayLike<number>|Uint8Array|Uint8ClampedArray|Uint16Array|Uint32Array} cluster_data
* @param {number[]|ArrayLike<number>|Float32Array} source_data Light/Decal source information is stored in here, such as position, color etc.
* @return {number}
*/
export function assign_cluster(
tile_texture_data_offset,
bvh,
lookup_address_offset,
lookup_data,
cluster_data,
source_data
) {
// at this point we have a fully constructed cluster, we can query lights within the cluster
const match_count = query_bvh_frustum_from_texture(
scratch_light_nodes,
0,
bvh,
source_data,
scratch_frustum_planes,
scratch_corners
);
// construct tile data
// write number of light entries
cluster_data[tile_texture_data_offset + 1] = match_count;
if (match_count === 0) {
// don't waste time on assignment, there are no lights in the cluster
return 0;
}
/*
We can sort lights by index to get higher cache utilization, but it turns out quite costly.
Through experimentation, sorting was taking around 20% of the whole assignment time
while improving number of cache hits by around 40% (8000 without sorting -> 11000 with sorting)
*/
const cluster_data_hash = compute_light_data_hash_0(scratch_light_nodes, match_count) & (LOOKUP_CACHE_SIZE - 1);
const existing_lookup_address = LOOKUP_CACHE[cluster_data_hash];
if (existing_lookup_address !== 0xFFFFFFFF) {
// found a hash match, validate equality
const existing_lookup_entry_match = testClusterEquality(
scratch_light_nodes,
match_count,
lookup_data,
existing_lookup_address
);
if (existing_lookup_entry_match) {
cluster_data[tile_texture_data_offset] = existing_lookup_address;
//hash_reuse_count++;
return 0;
}
} else {
// no hash entry exists, create one
LOOKUP_CACHE[cluster_data_hash] = lookup_address_offset;
}
// if we get to this point, that means no hash match was found, or equality check was failed
// write address of the lookup table
cluster_data[tile_texture_data_offset] = lookup_address_offset;
for (let i = 0; i < match_count; i++) {
/**
*
* @type {number}
*/
const light_descriptor = scratch_light_nodes[i];
// construct lookup table
lookup_data[lookup_address_offset + i] = light_descriptor;
}
return match_count;
}