@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
274 lines (216 loc) • 6.21 kB
JavaScript
import { BvhClient } from "../../../../core/bvh2/bvh3/BvhClient.js";
import Vector3 from "../../../../core/geom/Vector3.js";
import Vector4 from "../../../../core/geom/Vector4.js";
import { clamp01 } from "../../../../core/math/clamp01.js";
import ThreeFactory from "../../three/ThreeFactory.js";
import { RIBBON_ATTRIBUTE_ADDRESS_AGE } from "../../trail/x/ribbon_attributes_spec.js";
import { RibbonX } from "../../trail/x/RibbonX.js";
import { RibbonXMaterialSpec } from "../../trail/x/RibbonXMaterialSpec.js";
import { makeGradientTrail } from "./makeGradientTrail.js";
import { Trail2DFlags } from "./Trail2DFlags.js";
const DEFAULT_FLAGS = Trail2DFlags.Spawning | Trail2DFlags.Aging | Trail2DFlags.BoundsNeedUpdate;
const DEFAULT_MAX_AGE = 5;
const DEFAULT_WIDTH = 1;
const v3_array = new Float32Array(3);
class Trail2D {
/**
* Age at which trail segment disappears, in seconds
* @type {number}
*/
maxAge = DEFAULT_MAX_AGE;
/**
* Trail width
* @type {number}
*/
width = DEFAULT_WIDTH;
/**
* Current simulated time since trail birth
* @type {number}
*/
time = 0;
/**
*
* @type {number}
*/
trailingIndex = 0;
/**
* Time elapsed since last update
* @type {number}
*/
timeSinceLastUpdate = 0;
/**
* @readonly
* @type {Vector4}
*/
color = new Vector4(1, 1, 1, 1);
/**
* World offset
* @readonly
* @type {Vector3}
*/
offset = new Vector3();
/**
* transient
* @type {RibbonX|null}
*/
ribbon = null;
/**
* transient
* @type {RibbonXMaterialSpec}
*/
material = new RibbonXMaterialSpec();
/**
* @readonly
* @type {BvhClient}
*/
bvh = new BvhClient();
/**
* @private
* @type {Trail2DFlags|number}
*/
flags = DEFAULT_FLAGS;
dispose() {
if (this.ribbon === null) {
// nothing to dispose
return;
}
this.ribbon.dispose();
this.ribbon = null;
}
get textureURL() {
return this.material.diffuse;
}
set textureURL(v) {
this.material.diffuse = v;
}
/**
*
* @param {number|Trail2DFlags} flag
* @returns {void}
*/
setFlag(flag) {
this.flags |= flag;
}
/**
*
* @param {number|Trail2DFlags} flag
* @returns {void}
*/
clearFlag(flag) {
this.flags &= ~flag;
}
/**
*
* @param {number|Trail2DFlags} flag
* @param {boolean} value
*/
writeFlag(flag, value) {
if (value) {
this.setFlag(flag);
} else {
this.clearFlag(flag);
}
}
/**
*
* @param {number|Trail2DFlags} flag
* @returns {boolean}
*/
getFlag(flag) {
return (this.flags & flag) === flag;
}
/**
*
* @param {number} segment_count
*/
build(segment_count) {
const ribbon = new RibbonX();
ribbon.buildGeometry();
ribbon.setCount(segment_count);
const geometry = ribbon.getGeometry();
const mesh = ThreeFactory.createMesh(geometry, null);
this.ribbon = ribbon;
this.mesh = mesh;
}
static fromJSON(json) {
const r = new Trail2D();
r.fromJSON(json);
return r;
}
fromJSON({
maxAge = DEFAULT_MAX_AGE,
width = DEFAULT_WIDTH,
textureURL,
offset = Vector3.zero,
color = { x: 1, y: 1, z: 1, w: 1 }
}) {
this.maxAge = maxAge;
this.width = width;
this.textureURL = textureURL;
this.offset.fromJSON(offset);
this.color.fromJSON(color);
}
toJSON() {
return {
maxAge: this.maxAge,
width: this.width,
color: this.color.toJSON(),
textureURL: this.textureURL,
offset: this.offset.toJSON()
};
}
/**
*
* @param {Trail2D} other
*/
equals(other) {
return this.textureURL === other.textureURL
&& this.width === other.width
&& this.maxAge === other.maxAge
&& this.color.equals(other.color)
&& this.offset.equals(other.offset)
;
}
/**
*
* @param {number} x
* @param {number} y
* @param {number} z
* @param {number} head_age
* @returns {boolean}
*/
updateHead(x, y, z, head_age) {
const ribbon = this.ribbon;
const maxAge = this.maxAge;
const refitTimeDelta = maxAge / ribbon.getCount();
let head_index = ribbon.getHeadIndex();
ribbon.getPointPosition(v3_array, head_index);
const head_position_changed = x !== v3_array[0]
|| y !== v3_array[1]
|| z !== v3_array[2];
if (this.timeSinceLastUpdate >= refitTimeDelta) {
if (
head_position_changed
) {
// make sure that this is a new position before rotating new segment
this.timeSinceLastUpdate = 0;
// rotating segment
ribbon.rotate();
head_index = ribbon.getHeadIndex();
}
}
if (head_position_changed) {
ribbon.setPointPosition(head_index, x, y, z);
this.setFlag(Trail2DFlags.BoundsNeedUpdate);
}
// set head to fresh value
ribbon.setPointAttribute_Scalar(head_index, RIBBON_ATTRIBUTE_ADDRESS_AGE, head_age);
ribbon.setPointAlpha(head_index, clamp01(head_age / maxAge));
ribbon.setPointThickness(head_index, this.width);
return head_position_changed;
}
}
Trail2D.typeName = "Trail2D";
Trail2D.serializable = false;
Trail2D.makeGradientTrail = makeGradientTrail;
export default Trail2D;