UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

312 lines (265 loc) • 7.97 kB
import { mat4 } from "gl-matrix"; import { BvhClient } from "../../../../core/bvh2/bvh3/BvhClient.js"; import { AABB3 } from "../../../../core/geom/3d/aabb/AABB3.js"; import { aabb3_from_threejs_geometry } from "../../../../core/geom/3d/aabb/aabb3_from_threejs_geometry.js"; import { aabb3_matrix4_project } from "../../../../core/geom/3d/aabb/aabb3_matrix4_project.js"; import { ray3_array_apply_matrix4 } from "../../../../core/geom/3d/ray/ray3_array_apply_matrix4.js"; import { GeometrySpatialQueryAccelerator } from "../../geometry/buffered/query/GeometrySpatialQueryAccelerator.js"; import { DrawMode } from "./DrawMode.js"; import { ShadedGeometryFlags } from "./ShadedGeometryFlags.js"; /** * @readonly * @type {Float32Array} */ const scratch_aabb3_array = new Float32Array(6); const DEFAULT_FLAGS = ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow | ShadedGeometryFlags.Visible ; const FLAG_SET_EQUALITY = ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow | ShadedGeometryFlags.DrawMethodLocked | ShadedGeometryFlags.Visible ; /** * * @type {Float32Array|mat4} */ const scratch_m4 = new Float32Array(16); /** * @readonly * @type {Float32Array} */ const scratch_ray_0 = new Float32Array(6); /** * Represents a primitive 3d object, a combination of a material and geometry * This is roughly equivalent to single draw call, note that his is not a hierarchical data structure, if you want that - you will need to combine multiple entities, each with a `ShadedGeometry` component */ export class ShadedGeometry { constructor() { /** * * @type {THREE.BufferGeometry|null} */ this.geometry = null; /** * * @type {THREE.Material|null} */ this.material = null; /** * Optional extra material used for shadowmaps * @type {THREE.Material|null} */ this.depth_material = null; this.__bvh_leaf = new BvhClient(); /** * Transient, assigned in the system * @type {number} * @private */ this.__entity = -1; /** * Transient, cached for convenience. Associated transform component from the same entity * @type {Transform|null} * @private */ this.__c_transform = null; /** * * @type {DrawMode|number} */ this.mode = DrawMode.Triangles; /** * Transient property signifying how to render object, such as using INSTANCED draw * @type {number} */ this.draw_method = 0; /** * * @type {number} */ this.flags = DEFAULT_FLAGS; } /** * * @param {number|ShadedGeometryFlags} flag * @returns {void} */ setFlag(flag) { this.flags |= flag; } /** * * @param {number|ShadedGeometryFlags} flag * @returns {void} */ clearFlag(flag) { this.flags &= ~flag; } /** * * @param {number|ShadedGeometryFlags} flag * @param {boolean} value */ writeFlag(flag, value) { if (value) { this.setFlag(flag); } else { this.clearFlag(flag); } } /** * * @param {number|ShadedGeometryFlags} flag * @returns {boolean} */ getFlag(flag) { return (this.flags & flag) === flag; } /** * * @return {number} */ hash() { return this.geometry.id ^ this.material.id ^ (this.flags & FLAG_SET_EQUALITY) ^ (this.draw_method << 16) ^ (this.mode << 12) ; } /** * * @param {ShadedGeometry} other * @returns {boolean} */ equals(other) { return this.geometry.id === other.geometry.id && this.material.id === other.material.id && this.draw_method === other.draw_method && this.mode === other.mode && (this.flags & FLAG_SET_EQUALITY) === (other.flags & FLAG_SET_EQUALITY) && this.depth_material === other.depth_material ; } /** * * @param {ShadedGeometry} other */ copy(other) { this.geometry = other.geometry; this.material = other.material; this.depth_material = other.depth_material; this.draw_method = other.draw_method; this.mode = other.mode; this.flags = other.flags; } /** * * @returns {ShadedGeometry} */ clone() { const r = new ShadedGeometry(); r.copy(this); return r; } /** * Current cached entity that this component is attached to * @return {number} */ get entity() { return this.__entity; } /** * Global transform matrix * 4x4 matrix * @return {null|number[]} */ get transform() { return this.__c_transform.matrix; } /** * * @param {AABB3} destination */ getBoundingBox(destination) { const aabb = this.__bvh_leaf.bounds; destination.readFromArray(aabb); } /** * * @param {THREE.BufferGeometry} geometry * @param {THREE.Material} material * @param {DrawMode} [draw_mode] */ from(geometry, material, draw_mode = DrawMode.Triangles) { this.geometry = geometry; this.material = material; this.mode = draw_mode; // ensure that geometry has a bounding box if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } } /** * * @param {THREE.BufferGeometry} geometry * @param {THREE.Material} material * @param {DrawMode} [draw_mode] * @return {ShadedGeometry} */ static from(geometry, material, draw_mode) { const r = new ShadedGeometry(); r.from(geometry, material, draw_mode); return r; } updateTransform() { this.update_bounds(); this.__bvh_leaf.write_bounds(); } update_bounds() { // update bounds aabb3_from_threejs_geometry(scratch_aabb3_array, this.geometry); // transform aabb3_matrix4_project(this.__bvh_leaf.bounds, scratch_aabb3_array, this.transform); } /** * * @param {SurfacePoint3} contact if found, contact is written here * @param {ArrayLike<number>|number[]|Float32Array} ray 6-tuple: [origin_x, origin_y, origin_z, direction_x, direction_y, direction_z] * @param {ArrayLike<number>|number[]|Float32Array} transform_matrix4 transform applied to the geometry * @returns {boolean} */ query_raycast_nearest(contact, ray, transform_matrix4) { // get transform in local space mat4.invert(scratch_m4, transform_matrix4); // transform ray to local space if (!ray3_array_apply_matrix4(scratch_ray_0, 0, ray, 0, scratch_m4)) { // invalid transform matrix return false; } const hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_array( contact, this.geometry, scratch_ray_0 ); if (!hit_found) { return false; } // transform hit contact into global space contact.applyMatrix4(transform_matrix4); return true; } } /** * @readonly * @type {string} */ ShadedGeometry.typeName = 'ShadedGeometry'; /** * TODO add serialization support * @readonly * @type {boolean} */ ShadedGeometry.serializable = false;