@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
631 lines (518 loc) • 16.6 kB
JavaScript
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;