UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

631 lines (518 loc) • 16.6 kB
import { assert } from "../../../../../assert.js"; import { array_copy } from "../../../../../collection/array/array_copy.js"; import { BinaryElementPool } from "./BinaryElementPool.js"; /** * Byte size of FLOAT_32 * @readonly * @type {number} */ const FLOAT_32_SIZE = 4; /** * Byte size of UINT_32 * @readonly * @type {number} */ const UINT_32_SIZE = 4; /** * @readonly * @type {number} */ export const NULL_POINTER = 0xFFFFFFFF; /** * Heavily influenced by blender's internal mesh structure * @see https://github.com/blender/blender/blob/master/source/blender/bmesh/bmesh_class.h */ export class BinaryTopology { /** * structure: * coordinate: float32[3] // vertex coordinates * normal: float32[3] // vertex normal * edge_pointer: uint32 // Pointer to (any) edge using this vertex (for disk cycles) * * @type {BinaryElementPool} * @private */ __vertex_pool = new BinaryElementPool(3 * FLOAT_32_SIZE + 3 * FLOAT_32_SIZE + UINT_32_SIZE); /** * structure: * v1: uint32 // Vertices (unordered) * v2: uint32 // Vertices (unordered) * l: uint32 // The list of loops around the edge points at a Loop * v1_disk_link: uint32[2] // Disk Cycle Pointers edge around vertex v1 and d2 does the same for v2. * v2_disk_link: uint32[2] // see above * @type {BinaryElementPool} * @private */ __edge_pool = new BinaryElementPool(UINT_32_SIZE * 7); /** * Loop represents a corner of the face * structure: * v: uint32 // The vertex this loop points to. This vertex must be unique within the cycle * e: uint32 // The edge this loop uses. * f: uint32 // The face this loop is part of. * radial_next: uint32 // Other loops connected to this edge. * radial_prev: uint32 * next: uint32 // Other loops that are part of this face. * prev: uint32 * @type {BinaryElementPool} * @private */ __loop_pool = new BinaryElementPool(UINT_32_SIZE * 7); /** * structure: * l_first: uint32 // first loop pointer * no: float32[3] // Face normal * @type {BinaryElementPool} * @private */ __face_pool = new BinaryElementPool(UINT_32_SIZE + FLOAT_32_SIZE * 3); /** * Total (approximate) size of this structure in memory, in bytes * @return {number} */ get byteSize() { return this.__loop_pool.byteSize + this.__vertex_pool.byteSize + this.__edge_pool.byteSize + this.__face_pool.byteSize ; } trim() { this.__loop_pool.trim(); this.__vertex_pool.trim(); this.__edge_pool.trim(); this.__face_pool.trim(); } /** * * @return {BinaryElementPool} */ get vertices() { return this.__vertex_pool; } /** * Edges are shared among faces, edges point to 2 unordered vertices * @return {BinaryElementPool} */ get edges() { return this.__edge_pool; } /** * Loops are corners of faces, a single vertex can be associated with many loops, one per connected face * @return {BinaryElementPool} */ get loops() { return this.__loop_pool; } get faces() { return this.__face_pool; } /** * Clear the topology, removed all data */ clear() { this.__vertex_pool.clear(); this.__edge_pool.clear(); this.__loop_pool.clear(); this.__face_pool.clear(); } /** * * @param {number[]|ArrayLike<number>|Float32Array} result * @param {number} result_offset * @param {number} id vertex ID */ vertex_read_coordinate(result, result_offset, id) { const pool = this.__vertex_pool; const v_address = pool.element_address(id); const v_offset = v_address >> 2; // get 4-byte boundary array_copy(pool.data_float32, v_offset, result, result_offset, 3); } /** * * @param {number} id * @param {number[]} value * @param {number} value_offset */ vertex_write_coordinate(id, value, value_offset) { const pool = this.__vertex_pool; const v_address = pool.element_address(id); const v_offset = v_address >> 2; // get 4-byte boundary array_copy(value, value_offset, pool.data_float32, v_offset, 3); } /** * * @param {number[]|ArrayLike<number>|Float32Array} result * @param {number} result_offset * @param {number} id vertex ID */ vertex_read_normal(result, result_offset, id) { const pool = this.__vertex_pool; const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE; const v_offset = v_address >> 2; // get 4-byte boundary array_copy(pool.data_float32, v_offset, result, result_offset, 3); } /** * * @param {number} id * @param {number[]} value * @param {number} value_offset */ vertex_write_normal(id, value, value_offset) { const pool = this.__vertex_pool; const v_address = pool.element_address(id) + 3 * FLOAT_32_SIZE; const v_offset = v_address >> 2; // get 4-byte boundary array_copy(value, value_offset, pool.data_float32, v_offset, 3); } /** * @param {number} id * @returns {number} */ vertex_read_edge(id) { const pool = this.__vertex_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 6 * UINT_32_SIZE); } /** * * @param {number} edge_id * @param {number} id */ vertex_write_edge(id, edge_id) { const pool = this.__vertex_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 6 * UINT_32_SIZE, edge_id); } /** * @param {number} id edge ID * @returns {number} */ edge_read_vertex1(id) { const pool = this.__edge_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_vertex1(id, value) { const pool = this.__edge_pool; const address = pool.element_address(id); pool.data_view.setUint32(address, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_vertex2(id) { const pool = this.__edge_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_vertex2(id, value) { const pool = this.__edge_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + UINT_32_SIZE, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_loop(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__edge_pool; assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); return pool.data_view.getUint32(address + 2 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_loop(id, value) { const pool = this.__edge_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 2 * UINT_32_SIZE, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_v1_disk_next(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); return pool.data_view.getUint32(address + 3 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_v1_disk_next(id, value) { const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); pool.data_view.setUint32(address + 3 * UINT_32_SIZE, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_v1_disk_prev(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); return pool.data_view.getUint32(address + 4 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_v1_disk_prev(id, value) { const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); pool.data_view.setUint32(address + 4 * UINT_32_SIZE, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_v2_disk_next(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); return pool.data_view.getUint32(address + 5 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_v2_disk_next(id, value) { const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); pool.data_view.setUint32(address + 5 * UINT_32_SIZE, value); } /** * @param {number} id edge ID * @returns {number} */ edge_read_v2_disk_prev(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); return pool.data_view.getUint32(address + 6 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id edge ID */ edge_write_v2_disk_prev(id, value) { const pool = this.__edge_pool; // assert.equal(pool.is_allocated(id), true, 'element is not allocated'); const address = pool.element_address(id); pool.data_view.setUint32(address + 6 * UINT_32_SIZE, value); } loop_create() { const id = this.__loop_pool.allocate(); this.loop_initialize(id); return id; } /** * Put loop into valid initial state, set all pointers to NULL * @param {number} id */ loop_initialize(id) { this.loop_write_radial_next(id, id); this.loop_write_radial_prev(id, id); this.loop_write_next(id, id); this.loop_write_prev(id, id); } /** * @param {number} id loop ID * @returns {number} */ loop_read_vertex(id) { const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_vertex(id, value) { const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_edge(id) { const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_edge(id, value) { const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + UINT_32_SIZE, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_face(id) { const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 2 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_face(id, value) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 2 * UINT_32_SIZE, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_radial_next(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 3 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_radial_next(id, value) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 3 * UINT_32_SIZE, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_radial_prev(id) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 4 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_radial_prev(id, value) { assert.isNonNegativeInteger(id, 'id'); const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 4 * UINT_32_SIZE, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_next(id) { const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 5 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_next(id, value) { const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 5 * UINT_32_SIZE, value); } /** * * @param {number} id loop ID * @returns {number} */ loop_read_prev(id) { const pool = this.__loop_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address + 6 * UINT_32_SIZE); } /** * * @param {number} value * @param {number} id loop ID */ loop_write_prev(id, value) { const pool = this.__loop_pool; const address = pool.element_address(id); pool.data_view.setUint32(address + 6 * UINT_32_SIZE, value); } /** * * @param {number} id face ID * @returns {number} */ face_read_loop(id) { const pool = this.__face_pool; const address = pool.element_address(id); return pool.data_view.getUint32(address); } /** * * @param {number} value * @param {number} id face ID */ face_write_loop(id, value) { const pool = this.__face_pool; const address = pool.element_address(id); pool.data_view.setUint32(address, value); } } /** * Useful for type checking * @readonly * @type {boolean} */ BinaryTopology.prototype.isBinaryTopology = true;