UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

266 lines (199 loc) • 7.31 kB
import { assert } from "../../../../core/assert.js"; import { BVH } from "../../../../core/bvh2/bvh3/BVH.js"; import Vector3 from '../../../../core/geom/Vector3.js'; import { clamp } from "../../../../core/math/clamp.js"; import { max2 } from "../../../../core/math/max2.js"; import { ResourceAccessKind } from "../../../../core/model/ResourceAccessKind.js"; import { ResourceAccessSpecification } from "../../../../core/model/ResourceAccessSpecification.js"; import { System } from '../../../ecs/System.js'; import { Transform } from '../../../ecs/transform/Transform.js'; import { Reference } from "../../../reference/v2/Reference.js"; import { GraphicsEngine } from "../../GraphicsEngine.js"; import { make_bvh_visibility_builder } from "../../render/make_bvh_visibility_builder.js"; import { RIBBON_ATTRIBUTE_ADDRESS_AGE, RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET } from "../../trail/x/ribbon_attributes_spec.js"; import { RibbonX } from "../../trail/x/RibbonX.js"; import { RibbonXPlugin } from "../../trail/x/RibbonXPlugin.js"; import { RibbonXFixedPhysicsSimulator } from "../../trail/x/simulator/RibbonXFixedPhysicsSimulator.js"; import Trail2D from './Trail2D.js'; import { Trail2DFlags } from "./Trail2DFlags.js"; const v3Temp1 = new Vector3(); class Trail2DSystem extends System { dependencies = [Trail2D, Transform]; components_used = [ ResourceAccessSpecification.from(Trail2D, ResourceAccessKind.Write) ]; /** * * @type {RibbonXFixedPhysicsSimulator} */ simulator = new RibbonXFixedPhysicsSimulator(); /** * * @type {Reference<RibbonXPlugin>} * @private */ __ribbon_plugin = Reference.NULL; /** * * @type {number} * @private */ __timeDelta = 0; /** * @private * @type {BVH} */ bvh = new BVH(); /** * @param {Engine} engine * @constructor */ constructor(engine) { super(); assert.defined(engine, 'engine'); assert.notNull(engine, 'engine'); assert.equal(engine.isEngine, true, 'engine.isEngine !== true'); /** * * @type {Engine} * @private */ this.__engine = engine; /** * * @type {GraphicsEngine} */ this.graphics = engine.graphics; /** * * @type {RenderLayer|null} */ this.renderLayer = null; } async startup(entityManager) { this.entityManager = entityManager; this.renderLayer = this.graphics.layers.create('trail-2d-system'); this.renderLayer.buildVisibleSet = make_bvh_visibility_builder( this.entityManager, this.bvh, (destination, offset, entity, ecd) => { destination[offset] = ecd.getComponent(entity, Trail2D).mesh return 1; } ); this.__ribbon_plugin = await this.__engine.plugins.acquire(RibbonXPlugin); } async shutdown(entityManager) { this.graphics.layers.remove(this.renderLayer); this.__ribbon_plugin.release(); } /** * * @param {Trail2D} trail * @param {Transform} transform * @private */ __build_trail(trail, transform) { const segmentsPerSecond = 60; const maxSegments = 1024; //instantiation //make a mesh const segment_count = Math.ceil(clamp(trail.maxAge * segmentsPerSecond, 2, maxSegments)); trail.build(segment_count); const ribbon = trail.ribbon; const ribbons = this.__ribbon_plugin.getValue(); const material = ribbons.obtain_material_x(trail.material); trail.mesh.material = material; trail.time = 0; trail.trailingIndex = 0; trail.ribbon = ribbon; trail.timeSinceLastUpdate = 0; const trail_offset = trail.offset; const transform_position = transform.position; const position_x = trail_offset.x + transform_position.x; const position_y = trail_offset.y + transform_position.y; const position_z = trail_offset.z + transform_position.z; const color = trail.color; //initialize segments for (let i = 0; i < segment_count; i++) { const f = i / (segment_count - 1); ribbon.setPointColor(i, color.x * 255, color.y * 255, color.z * 255); ribbon.setPointPosition(i, position_x, position_y, position_z); ribbon.setPointThickness(i, trail.width); ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_UV_OFFSET, f); ribbon.setPointAlpha(i, color.w); ribbon.setPointAttribute_Scalar(i, RIBBON_ATTRIBUTE_ADDRESS_AGE, trail.maxAge); } trail.bvh.resize( position_x, position_y, position_z, position_x, position_y, position_z ); trail.setFlag(Trail2DFlags.Built); } /** * * @param {Transform} transform * @param {Trail2D} trail * @param {number} entityId */ link(trail, transform, entityId) { if (!trail.getFlag(Trail2DFlags.Built)) { this.__build_trail(trail, transform); } else { const material = this.__ribbon_plugin.getValue().obtain_material_x(trail.material); trail.mesh.material = material; } trail.bvh.link(this.bvh, entityId); } /** * * @param {Trail2D} component * @param {Transform} transform * @param {number} entity */ unlink(component, transform, entity) { component.bvh.unlink(); // release resources component.dispose(); } /** * * @param {Trail2D} trail * @param {Transform} transform */ updateTrailEntity(trail, transform) { const timeDelta = this.__timeDelta; /** * * @type {RibbonX|null} */ const ribbon = trail.ribbon; const newPosition = v3Temp1; newPosition.copy(trail.offset); newPosition.applyMatrix4(transform.matrix); trail.timeSinceLastUpdate += timeDelta; trail.time += timeDelta; const ageOffset = max2(0, trail.maxAge - trail.time); this.simulator.update(ribbon, trail.maxAge, timeDelta); if (trail.getFlag(Trail2DFlags.Spawning)) { trail.updateHead(newPosition.x, newPosition.y, newPosition.z, ageOffset); } if (trail.getFlag(Trail2DFlags.BoundsNeedUpdate)) { const bvh = trail.bvh; ribbon.computeBoundingBox(bvh.bounds); bvh.write_bounds(); trail.clearFlag(Trail2DFlags.BoundsNeedUpdate); } } update(timeDelta) { const em = this.entityManager; const dataset = em.dataset; this.__timeDelta = timeDelta; if (dataset !== null) { dataset.traverseEntities([Trail2D, Transform], this.updateTrailEntity, this); } } } export default Trail2DSystem;