@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
1,245 lines • 74 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { createNoise4D } from 'simplex-noise';
import { Euler, Matrix4, Quaternion, Triangle, Vector2, Vector3 } from "three";
import { isDevEnvironment } from '../../engine/debug/index.js';
import { Gizmos } from "../../engine/engine_gizmos.js";
import { Mathf } from "../../engine/engine_math.js";
import { serializable } from "../../engine/engine_serialization.js";
import { getTempVector, getWorldQuaternion } from '../../engine/engine_three_utils.js';
import { getParam } from "../../engine/engine_utils.js";
import { RGBAColor } from "../../engine/js-extensions/index.js";
import { AnimationCurve } from "../AnimationCurve.js";
import { MeshRenderer } from '../Renderer.js';
const debug = getParam("debugparticles");
export var ParticleSystemRenderMode;
(function (ParticleSystemRenderMode) {
ParticleSystemRenderMode[ParticleSystemRenderMode["Billboard"] = 0] = "Billboard";
ParticleSystemRenderMode[ParticleSystemRenderMode["Stretch"] = 1] = "Stretch";
ParticleSystemRenderMode[ParticleSystemRenderMode["HorizontalBillboard"] = 2] = "HorizontalBillboard";
ParticleSystemRenderMode[ParticleSystemRenderMode["VerticalBillboard"] = 3] = "VerticalBillboard";
ParticleSystemRenderMode[ParticleSystemRenderMode["Mesh"] = 4] = "Mesh";
// None = 5,
})(ParticleSystemRenderMode || (ParticleSystemRenderMode = {}));
export class Gradient {
alphaKeys = [];
colorKeys = [];
get duration() {
return 1;
}
evaluate(time, target) {
// target.r = this.colorKeys[0].color.r;
// target.g = this.colorKeys[0].color.g;
// target.b = this.colorKeys[0].color.b;
// target.alpha = this.alphaKeys[0].alpha;
// return;
let closestAlpha = undefined;
let closestAlphaIndex = 0;
let closestColor = null;
let closestColorIndex = 0;
for (let i = 0; i < this.alphaKeys.length; i++) {
const key = this.alphaKeys[i];
if (key.time < time || !closestAlpha) {
closestAlpha = key;
closestAlphaIndex = i;
}
}
for (let i = 0; i < this.colorKeys.length; i++) {
const key = this.colorKeys[i];
if (key.time < time || !closestColor) {
closestColor = key;
closestColorIndex = i;
}
}
if (closestColor) {
const hasNextColor = closestColorIndex + 1 < this.colorKeys.length;
if (hasNextColor) {
const nextColor = this.colorKeys[closestColorIndex + 1];
const t = Mathf.remap(time, closestColor.time, nextColor.time, 0, 1);
target.r = Mathf.lerp(closestColor.color.r, nextColor.color.r, t);
target.g = Mathf.lerp(closestColor.color.g, nextColor.color.g, t);
target.b = Mathf.lerp(closestColor.color.b, nextColor.color.b, t);
}
else {
target.r = closestColor.color.r;
target.g = closestColor.color.g;
target.b = closestColor.color.b;
}
}
if (closestAlpha) {
const hasNextAlpha = closestAlphaIndex + 1 < this.alphaKeys.length;
if (hasNextAlpha) {
const nextAlpha = this.alphaKeys[closestAlphaIndex + 1];
const t = Mathf.remap(time, closestAlpha.time, nextAlpha.time, 0, 1);
target.alpha = Mathf.lerp(closestAlpha.alpha, nextAlpha.alpha, t);
}
else {
target.alpha = closestAlpha.alpha;
}
}
return target;
}
}
__decorate([
serializable()
], Gradient.prototype, "alphaKeys", void 0);
__decorate([
serializable()
], Gradient.prototype, "colorKeys", void 0);
export var ParticleSystemCurveMode;
(function (ParticleSystemCurveMode) {
ParticleSystemCurveMode[ParticleSystemCurveMode["Constant"] = 0] = "Constant";
ParticleSystemCurveMode[ParticleSystemCurveMode["Curve"] = 1] = "Curve";
ParticleSystemCurveMode[ParticleSystemCurveMode["TwoCurves"] = 2] = "TwoCurves";
ParticleSystemCurveMode[ParticleSystemCurveMode["TwoConstants"] = 3] = "TwoConstants";
})(ParticleSystemCurveMode || (ParticleSystemCurveMode = {}));
export var ParticleSystemGradientMode;
(function (ParticleSystemGradientMode) {
ParticleSystemGradientMode[ParticleSystemGradientMode["Color"] = 0] = "Color";
ParticleSystemGradientMode[ParticleSystemGradientMode["Gradient"] = 1] = "Gradient";
ParticleSystemGradientMode[ParticleSystemGradientMode["TwoColors"] = 2] = "TwoColors";
ParticleSystemGradientMode[ParticleSystemGradientMode["TwoGradients"] = 3] = "TwoGradients";
ParticleSystemGradientMode[ParticleSystemGradientMode["RandomColor"] = 4] = "RandomColor";
})(ParticleSystemGradientMode || (ParticleSystemGradientMode = {}));
export var ParticleSystemSimulationSpace;
(function (ParticleSystemSimulationSpace) {
ParticleSystemSimulationSpace[ParticleSystemSimulationSpace["Local"] = 0] = "Local";
ParticleSystemSimulationSpace[ParticleSystemSimulationSpace["World"] = 1] = "World";
ParticleSystemSimulationSpace[ParticleSystemSimulationSpace["Custom"] = 2] = "Custom";
})(ParticleSystemSimulationSpace || (ParticleSystemSimulationSpace = {}));
export var ParticleSystemShapeType;
(function (ParticleSystemShapeType) {
ParticleSystemShapeType[ParticleSystemShapeType["Sphere"] = 0] = "Sphere";
ParticleSystemShapeType[ParticleSystemShapeType["SphereShell"] = 1] = "SphereShell";
ParticleSystemShapeType[ParticleSystemShapeType["Hemisphere"] = 2] = "Hemisphere";
ParticleSystemShapeType[ParticleSystemShapeType["HemisphereShell"] = 3] = "HemisphereShell";
ParticleSystemShapeType[ParticleSystemShapeType["Cone"] = 4] = "Cone";
ParticleSystemShapeType[ParticleSystemShapeType["Box"] = 5] = "Box";
ParticleSystemShapeType[ParticleSystemShapeType["Mesh"] = 6] = "Mesh";
ParticleSystemShapeType[ParticleSystemShapeType["ConeShell"] = 7] = "ConeShell";
ParticleSystemShapeType[ParticleSystemShapeType["ConeVolume"] = 8] = "ConeVolume";
ParticleSystemShapeType[ParticleSystemShapeType["ConeVolumeShell"] = 9] = "ConeVolumeShell";
ParticleSystemShapeType[ParticleSystemShapeType["Circle"] = 10] = "Circle";
ParticleSystemShapeType[ParticleSystemShapeType["CircleEdge"] = 11] = "CircleEdge";
ParticleSystemShapeType[ParticleSystemShapeType["SingleSidedEdge"] = 12] = "SingleSidedEdge";
ParticleSystemShapeType[ParticleSystemShapeType["MeshRenderer"] = 13] = "MeshRenderer";
ParticleSystemShapeType[ParticleSystemShapeType["SkinnedMeshRenderer"] = 14] = "SkinnedMeshRenderer";
ParticleSystemShapeType[ParticleSystemShapeType["BoxShell"] = 15] = "BoxShell";
ParticleSystemShapeType[ParticleSystemShapeType["BoxEdge"] = 16] = "BoxEdge";
ParticleSystemShapeType[ParticleSystemShapeType["Donut"] = 17] = "Donut";
ParticleSystemShapeType[ParticleSystemShapeType["Rectangle"] = 18] = "Rectangle";
ParticleSystemShapeType[ParticleSystemShapeType["Sprite"] = 19] = "Sprite";
ParticleSystemShapeType[ParticleSystemShapeType["SpriteRenderer"] = 20] = "SpriteRenderer";
})(ParticleSystemShapeType || (ParticleSystemShapeType = {}));
export var ParticleSystemShapeMultiModeValue;
(function (ParticleSystemShapeMultiModeValue) {
ParticleSystemShapeMultiModeValue[ParticleSystemShapeMultiModeValue["Random"] = 0] = "Random";
ParticleSystemShapeMultiModeValue[ParticleSystemShapeMultiModeValue["Loop"] = 1] = "Loop";
ParticleSystemShapeMultiModeValue[ParticleSystemShapeMultiModeValue["PingPong"] = 2] = "PingPong";
ParticleSystemShapeMultiModeValue[ParticleSystemShapeMultiModeValue["BurstSpread"] = 3] = "BurstSpread";
})(ParticleSystemShapeMultiModeValue || (ParticleSystemShapeMultiModeValue = {}));
export class MinMaxCurve {
static constant(val) {
const obj = new MinMaxCurve();
obj.setConstant(val);
return obj;
}
static betweenTwoConstants(min, max) {
const obj = new MinMaxCurve();
obj.setMinMaxConstant(min, max);
return obj;
}
static curve(curve, multiplier = 1) {
const obj = new MinMaxCurve();
obj.setCurve(curve, multiplier);
return obj;
}
setConstant(val) {
this.mode = ParticleSystemCurveMode.Constant;
this.constant = val;
}
setMinMaxConstant(min, max) {
this.mode = ParticleSystemCurveMode.TwoConstants;
this.constantMin = min;
this.constantMax = max;
}
setCurve(curve, multiplier = 1) {
this.mode = ParticleSystemCurveMode.Curve;
this.curve = curve;
this.curveMultiplier = multiplier;
}
mode = "Constant";
constant;
constantMin;
constantMax;
curve;
curveMin;
curveMax;
curveMultiplier;
clone() {
const clone = new MinMaxCurve();
clone.mode = this.mode;
clone.constant = this.constant;
clone.constantMin = this.constantMin;
clone.constantMax = this.constantMax;
clone.curve = this.curve?.clone();
clone.curveMin = this.curveMin?.clone();
clone.curveMax = this.curveMax?.clone();
clone.curveMultiplier = this.curveMultiplier;
return clone;
}
evaluate(t01, lerpFactor) {
const t = lerpFactor === undefined ? Math.random() : lerpFactor;
switch (this.mode) {
case ParticleSystemCurveMode.Constant:
case "Constant":
return this.constant;
case ParticleSystemCurveMode.Curve:
case "Curve":
t01 = Mathf.clamp01(t01);
return this.curve.evaluate(t01) * this.curveMultiplier;
case ParticleSystemCurveMode.TwoCurves:
case "TwoCurves":
const t1 = t01 * this.curveMin.duration;
const t2 = t01 * this.curveMax.duration;
return Mathf.lerp(this.curveMin.evaluate(t1), this.curveMax.evaluate(t2), t % 1) * this.curveMultiplier;
case ParticleSystemCurveMode.TwoConstants:
case "TwoConstants":
return Mathf.lerp(this.constantMin, this.constantMax, t % 1);
default:
this.curveMax.evaluate(t01) * this.curveMultiplier;
break;
}
return 0;
}
getMax() {
switch (this.mode) {
case ParticleSystemCurveMode.Constant:
case "Constant":
return this.constant;
case ParticleSystemCurveMode.Curve:
case "Curve":
return this.getMaxFromCurve(this.curve) * this.curveMultiplier;
case ParticleSystemCurveMode.TwoCurves:
case "TwoCurves":
return Math.max(this.getMaxFromCurve(this.curveMin), this.getMaxFromCurve(this.curveMax)) * this.curveMultiplier;
case ParticleSystemCurveMode.TwoConstants:
case "TwoConstants":
return Math.max(this.constantMin, this.constantMax);
default:
return 0;
}
}
getMaxFromCurve(curve) {
if (!curve)
return 0;
let maxNumber = Number.MIN_VALUE;
for (let i = 0; i < curve.keys.length; i++) {
const key = curve.keys[i];
if (key.value > maxNumber) {
maxNumber = key.value;
}
}
return maxNumber;
}
}
__decorate([
serializable()
], MinMaxCurve.prototype, "mode", void 0);
__decorate([
serializable()
], MinMaxCurve.prototype, "constant", void 0);
__decorate([
serializable()
], MinMaxCurve.prototype, "constantMin", void 0);
__decorate([
serializable()
], MinMaxCurve.prototype, "constantMax", void 0);
__decorate([
serializable(AnimationCurve)
], MinMaxCurve.prototype, "curve", void 0);
__decorate([
serializable(AnimationCurve)
], MinMaxCurve.prototype, "curveMin", void 0);
__decorate([
serializable(AnimationCurve)
], MinMaxCurve.prototype, "curveMax", void 0);
__decorate([
serializable()
], MinMaxCurve.prototype, "curveMultiplier", void 0);
export class MinMaxGradient {
static constant(color) {
const obj = new MinMaxGradient();
obj.constant(color);
return obj;
}
static betweenTwoColors(color1, color2) {
const obj = new MinMaxGradient();
obj.betweenTwoColors(color1, color2);
return obj;
}
constant(color) {
this.mode = ParticleSystemGradientMode.Color;
this.color = color;
return this;
}
betweenTwoColors(color1, color2) {
this.mode = ParticleSystemGradientMode.TwoColors;
this.colorMin = color1;
this.colorMax = color2;
return this;
}
/**
* The mode of the gradient, which can be Color, Gradient, TwoColors or TwoGradients.
*/
mode = ParticleSystemGradientMode.Color;
color;
colorMin;
colorMax;
gradient;
gradientMin;
gradientMax;
static _temp = new RGBAColor(0, 0, 0, 1);
static _temp2 = new RGBAColor(0, 0, 0, 1);
evaluate(t01, lerpFactor) {
const t = lerpFactor === undefined ? Math.random() : lerpFactor;
switch (this.mode) {
case ParticleSystemGradientMode.Color:
case "Color":
return this.color;
case ParticleSystemGradientMode.Gradient:
case "Gradient":
this.gradient.evaluate(t01, MinMaxGradient._temp);
return MinMaxGradient._temp;
case ParticleSystemGradientMode.TwoColors:
case "TwoColors":
const col1 = MinMaxGradient._temp.lerpColors(this.colorMin, this.colorMax, t);
return col1;
case ParticleSystemGradientMode.TwoGradients:
case "TwoGradients":
this.gradientMin.evaluate(t01, MinMaxGradient._temp);
this.gradientMax.evaluate(t01, MinMaxGradient._temp2);
return MinMaxGradient._temp.lerp(MinMaxGradient._temp2, t);
case ParticleSystemGradientMode.RandomColor:
case "RandomColor":
const random_t = Math.random();
this.gradientMin.evaluate(t01, MinMaxGradient._temp);
this.gradientMax.evaluate(t01, MinMaxGradient._temp2);
return MinMaxGradient._temp.lerp(MinMaxGradient._temp2, random_t);
}
// console.warn("Not implemented", ParticleSystemGradientMode[this.mode]);
MinMaxGradient._temp.set(0xffffff);
MinMaxGradient._temp.alpha = 1;
return MinMaxGradient._temp;
}
}
__decorate([
serializable()
], MinMaxGradient.prototype, "mode", void 0);
__decorate([
serializable(RGBAColor)
], MinMaxGradient.prototype, "color", void 0);
__decorate([
serializable(RGBAColor)
], MinMaxGradient.prototype, "colorMin", void 0);
__decorate([
serializable(RGBAColor)
], MinMaxGradient.prototype, "colorMax", void 0);
__decorate([
serializable(Gradient)
], MinMaxGradient.prototype, "gradient", void 0);
__decorate([
serializable(Gradient)
], MinMaxGradient.prototype, "gradientMin", void 0);
__decorate([
serializable(Gradient)
], MinMaxGradient.prototype, "gradientMax", void 0);
export var ParticleSystemScalingMode;
(function (ParticleSystemScalingMode) {
ParticleSystemScalingMode[ParticleSystemScalingMode["Hierarchy"] = 0] = "Hierarchy";
ParticleSystemScalingMode[ParticleSystemScalingMode["Local"] = 1] = "Local";
ParticleSystemScalingMode[ParticleSystemScalingMode["Shape"] = 2] = "Shape";
})(ParticleSystemScalingMode || (ParticleSystemScalingMode = {}));
export class MainModule {
cullingMode;
duration;
emitterVelocityMode;
flipRotation;
gravityModifier;
gravityModifierMultiplier;
loop;
maxParticles;
playOnAwake;
prewarm;
ringBufferLoopRange;
ringBufferMode;
scalingMode;
simulationSpace;
simulationSpeed;
startColor;
startDelay;
startDelayMultiplier;
startLifetime;
startLifetimeMultiplier;
startRotation;
startRotationMultiplier;
startRotation3D;
startRotationX;
startRotationXMultiplier;
startRotationY;
startRotationYMultiplier;
startRotationZ;
startRotationZMultiplier;
startSize;
startSize3D;
startSizeMultiplier;
startSizeX;
startSizeXMultiplier;
startSizeY;
startSizeYMultiplier;
startSizeZ;
startSizeZMultiplier;
startSpeed;
startSpeedMultiplier;
stopAction;
useUnscaledTime;
}
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "gravityModifier", void 0);
__decorate([
serializable(MinMaxGradient)
], MainModule.prototype, "startColor", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startDelay", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startLifetime", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startRotation", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startRotationX", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startRotationY", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startRotationZ", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startSize", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startSizeX", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startSizeY", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startSizeZ", void 0);
__decorate([
serializable(MinMaxCurve)
], MainModule.prototype, "startSpeed", void 0);
export class ParticleBurst {
cycleCount;
maxCount;
minCount;
probability;
repeatInterval;
time;
count;
_performed = 0;
reset() {
this._performed = 0;
}
run(time) {
if (time <= this.time) {
return 0;
}
let amount = 0;
if (this.cycleCount === 0 || this._performed < this.cycleCount) {
const nextTime = this.time + this.repeatInterval * this._performed;
if (time >= nextTime) {
this._performed += 1;
if (Math.random() < this.probability) {
switch (this.count.mode) {
case ParticleSystemCurveMode.Constant:
amount = this.count.constant;
break;
case ParticleSystemCurveMode.TwoConstants:
amount = Mathf.lerp(this.count.constantMin, this.count.constantMax, Math.random());
break;
case ParticleSystemCurveMode.Curve:
amount = this.count.curve.evaluate(Math.random());
break;
case ParticleSystemCurveMode.TwoCurves:
const t = Math.random();
amount = Mathf.lerp(this.count.curveMin.evaluate(t), this.count.curveMax.evaluate(t), Math.random());
break;
}
}
}
}
return amount;
}
}
export class EmissionModule {
enabled;
get burstCount() {
return this.bursts?.length ?? 0;
}
bursts;
rateOverTime;
rateOverTimeMultiplier;
rateOverDistance;
rateOverDistanceMultiplier;
/** set from system */
system;
reset() {
this.bursts?.forEach(b => b.reset());
}
getBurst() {
let amount = 0;
if (this.burstCount > 0) {
for (let i = 0; i < this.burstCount; i++) {
const burst = this.bursts[i];
if (this.system.main.loop && burst.time >= this.system.time) {
burst.reset();
}
amount += Math.round(burst.run(this.system.time));
}
}
return amount;
}
}
__decorate([
serializable()
], EmissionModule.prototype, "enabled", void 0);
__decorate([
serializable()
], EmissionModule.prototype, "bursts", void 0);
__decorate([
serializable(MinMaxCurve)
], EmissionModule.prototype, "rateOverTime", void 0);
__decorate([
serializable()
], EmissionModule.prototype, "rateOverTimeMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], EmissionModule.prototype, "rateOverDistance", void 0);
__decorate([
serializable()
], EmissionModule.prototype, "rateOverDistanceMultiplier", void 0);
export class ColorOverLifetimeModule {
enabled;
color;
}
__decorate([
serializable(MinMaxGradient)
], ColorOverLifetimeModule.prototype, "color", void 0);
export class SizeOverLifetimeModule {
enabled;
separateAxes;
size;
sizeMultiplier;
x;
xMultiplier;
y;
yMultiplier;
z;
zMultiplier;
_time = 0;
_temp = new Vector3();
evaluate(t01, target, lerpFactor) {
if (!target)
target = this._temp;
if (!this.enabled) {
target.x = target.y = target.z = 1;
return target;
}
if (!this.separateAxes) {
const scale = this.size.evaluate(t01, lerpFactor) * this.sizeMultiplier;
target.x = scale;
// target.y = scale;
// target.z = scale;
}
else {
target.x = this.x.evaluate(t01, lerpFactor) * this.xMultiplier;
target.y = this.y.evaluate(t01, lerpFactor) * this.yMultiplier;
target.z = this.z.evaluate(t01, lerpFactor) * this.zMultiplier;
}
return target;
}
}
__decorate([
serializable(MinMaxCurve)
], SizeOverLifetimeModule.prototype, "size", void 0);
__decorate([
serializable(MinMaxCurve)
], SizeOverLifetimeModule.prototype, "x", void 0);
__decorate([
serializable(MinMaxCurve)
], SizeOverLifetimeModule.prototype, "y", void 0);
__decorate([
serializable(MinMaxCurve)
], SizeOverLifetimeModule.prototype, "z", void 0);
export var ParticleSystemMeshShapeType;
(function (ParticleSystemMeshShapeType) {
ParticleSystemMeshShapeType[ParticleSystemMeshShapeType["Vertex"] = 0] = "Vertex";
ParticleSystemMeshShapeType[ParticleSystemMeshShapeType["Edge"] = 1] = "Edge";
ParticleSystemMeshShapeType[ParticleSystemMeshShapeType["Triangle"] = 2] = "Triangle";
})(ParticleSystemMeshShapeType || (ParticleSystemMeshShapeType = {}));
export class ShapeModule {
// Emittershape start
get type() {
return ParticleSystemShapeType[this.shapeType];
}
initialize(particle) {
this.onInitialize(particle);
particle.position.x = this._vector.x;
particle.position.y = this._vector.y;
particle.position.z = this._vector.z;
}
toJSON() {
return this;
}
clone() {
return new ShapeModule();
}
// EmitterShape end
shapeType = ParticleSystemShapeType.Box;
enabled = true;
alignToDirection = false;
angle = 0;
arc = 360;
arcSpread;
arcSpeedMultiplier;
arcMode;
boxThickness;
position;
rotation;
_rotation = new Euler();
scale;
radius;
radiusThickness;
sphericalDirectionAmount;
randomDirectionAmount;
randomPositionAmount;
/** Controls if particles should spawn off vertices, faces or edges. `shapeType` must be set to `MeshRenderer` */
meshShapeType;
/** When assigned and `shapeType` is set to `MeshRenderer` particles will spawn using a mesh in the scene.
* Use the `meshShapeType` to choose if particles should be spawned from vertices, faces or edges
* To re-assign use the `setMesh` function to cache the mesh and geometry
* */
meshRenderer;
_meshObj;
_meshGeometry;
setMesh(mesh) {
this.meshRenderer = mesh;
if (mesh) {
this._meshObj = mesh.sharedMeshes[Math.floor(Math.random() * mesh.sharedMeshes.length)];
this._meshGeometry = this._meshObj.geometry;
}
else {
this._meshObj = undefined;
this._meshGeometry = undefined;
}
}
system;
_space;
_worldSpaceMatrix = new Matrix4();
_worldSpaceMatrixInverse = new Matrix4();
constructor() {
if (debug)
console.log(this);
}
update(_system, _delta) {
/* this is called by quarks */
}
onUpdate(system, _context, simulationSpace, obj) {
this.system = system;
this._space = simulationSpace;
if (simulationSpace === ParticleSystemSimulationSpace.World) {
this._worldSpaceMatrix.copy(obj.matrixWorld);
// set scale to 1
this._worldSpaceMatrix.elements[0] = 1;
this._worldSpaceMatrix.elements[5] = 1;
this._worldSpaceMatrix.elements[10] = 1;
this._worldSpaceMatrixInverse.copy(this._worldSpaceMatrix).invert();
}
}
applyRotation(vector) {
const isRotated = this.rotation.x !== 0 || this.rotation.y !== 0 || this.rotation.z !== 0;
if (isRotated) {
// console.log(this._rotation);
// TODO: we need to convert this to threejs euler
this._rotation.x = Mathf.toRadians(this.rotation.x);
this._rotation.y = Mathf.toRadians(this.rotation.y);
this._rotation.z = Mathf.toRadians(this.rotation.z);
this._rotation.order = 'ZYX';
vector.applyEuler(this._rotation);
// this._quat.setFromEuler(this._rotation);
// // this._quat.invert();
// this._quat.x *= -1;
// // this._quat.y *= -1;
// // this._quat.z *= -1;
// this._quat.w *= -1;
// vector.applyQuaternion(this._quat);
}
return isRotated;
}
/** nebula implementations: */
/** initializer implementation */
_vector = new Vector3(0, 0, 0);
_temp = new Vector3(0, 0, 0);
_triangle = new Triangle();
onInitialize(particle) {
this._vector.set(0, 0, 0);
// remove mesh from particle in case it got destroyed (we dont want to keep a reference to a destroyed mesh in the particle system)
particle["mesh"] = undefined;
particle["mesh_geometry"] = undefined;
const pos = this._temp.copy(this.position);
const isWorldSpace = this._space === ParticleSystemSimulationSpace.World;
if (isWorldSpace) {
pos.applyQuaternion(this.system.worldQuaternion);
}
let radius = this.radius;
if (isWorldSpace)
radius *= this.system.worldScale.x;
if (this.enabled) {
switch (this.shapeType) {
case ParticleSystemShapeType.Box:
if (debug)
Gizmos.DrawWireBox(this.position, this.scale, 0xdddddd, 1);
this._vector.x = Math.random() * this.scale.x - this.scale.x / 2;
this._vector.y = Math.random() * this.scale.y - this.scale.y / 2;
this._vector.z = Math.random() * this.scale.z - this.scale.z / 2;
this._vector.add(pos);
break;
case ParticleSystemShapeType.Cone:
this.randomConePoint(this.position, this.angle, radius, this.radiusThickness, this.arc, this.arcMode, this._vector);
break;
case ParticleSystemShapeType.Sphere:
this.randomSpherePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
break;
case ParticleSystemShapeType.Circle:
this.randomCirclePoint(this.position, radius, this.radiusThickness, this.arc, this._vector);
break;
case ParticleSystemShapeType.MeshRenderer:
const renderer = this.meshRenderer;
if (renderer?.destroyed == false)
this.setMesh(renderer);
const mesh = particle["mesh"] = this._meshObj;
const geometry = particle["mesh_geometry"] = this._meshGeometry;
if (mesh && geometry) {
switch (this.meshShapeType) {
case ParticleSystemMeshShapeType.Vertex:
{
const vertices = geometry.getAttribute("position");
const index = Math.floor(Math.random() * vertices.count);
this._vector.fromBufferAttribute(vertices, index);
this._vector.applyMatrix4(mesh.matrixWorld);
particle["mesh_normal"] = index;
}
break;
case ParticleSystemMeshShapeType.Edge:
break;
case ParticleSystemMeshShapeType.Triangle:
{
const faces = geometry.index;
if (faces) {
let u = Math.random();
let v = Math.random();
if (u + v > 1) {
u = 1 - u;
v = 1 - v;
}
const faceIndex = Math.floor(Math.random() * (faces.count / 3));
let i0 = faceIndex * 3;
let i1 = faceIndex * 3 + 1;
let i2 = faceIndex * 3 + 2;
i0 = faces.getX(i0);
i1 = faces.getX(i1);
i2 = faces.getX(i2);
const positionAttribute = geometry.getAttribute("position");
this._triangle.a.fromBufferAttribute(positionAttribute, i0);
this._triangle.b.fromBufferAttribute(positionAttribute, i1);
this._triangle.c.fromBufferAttribute(positionAttribute, i2);
this._vector
.set(0, 0, 0)
.addScaledVector(this._triangle.a, u)
.addScaledVector(this._triangle.b, v)
.addScaledVector(this._triangle.c, 1 - (u + v));
this._vector.applyMatrix4(mesh.matrixWorld);
particle["mesh_normal"] = faceIndex;
}
}
break;
}
}
break;
default:
this._vector.set(0, 0, 0);
if (isDevEnvironment() && !globalThis["__particlesystem_shapetype_unsupported"]) {
console.warn("ParticleSystem ShapeType is not supported:", ParticleSystemShapeType[this.shapeType]);
globalThis["__particlesystem_shapetype_unsupported"] = true;
}
break;
// case ParticleSystemShapeType.Hemisphere:
// randomSpherePoint(this.position.x, this.position.y, this.position.z, this.radius, this.radiusThickness, 180, this._vector);
// break;
}
this.randomizePosition(this._vector, this.randomPositionAmount);
}
this.applyRotation(this._vector);
if (isWorldSpace) {
this._vector.applyQuaternion(this.system.worldQuaternion);
this._vector.add(this.system.worldPos);
}
if (debug) {
Gizmos.DrawSphere(this._vector, .03, 0xff0000, .5, true);
}
}
_dir = new Vector3();
getDirection(particle, pos) {
if (!this.enabled) {
this._dir.set(0, 0, 1);
return this._dir;
}
switch (this.shapeType) {
case ParticleSystemShapeType.Box:
this._dir.set(0, 0, 1);
break;
case ParticleSystemShapeType.Cone:
this._dir.set(0, 0, 1);
// apply cone angle
// this._dir.applyAxisAngle(new Vector3(0, 1, 0), Mathf.toRadians(this.angle));
break;
case ParticleSystemShapeType.Circle:
case ParticleSystemShapeType.Sphere:
const rx = pos.x;
const ry = pos.y;
const rz = pos.z;
this._dir.set(rx, ry, rz);
if (this.system?.worldspace)
this._dir.sub(this.system.worldPos);
else
this._dir.sub(this.position);
break;
case ParticleSystemShapeType.MeshRenderer:
const mesh = particle["mesh"];
const geometry = particle["mesh_geometry"];
if (mesh && geometry) {
switch (this.meshShapeType) {
case ParticleSystemMeshShapeType.Vertex:
{
const normal = geometry.getAttribute("normal");
const index = particle["mesh_normal"];
this._dir.fromBufferAttribute(normal, index);
}
break;
case ParticleSystemMeshShapeType.Edge:
break;
case ParticleSystemMeshShapeType.Triangle:
{
const faces = geometry.index;
if (faces) {
const index = particle["mesh_normal"];
const i0 = faces.getX(index * 3);
const i1 = faces.getX(index * 3 + 1);
const i2 = faces.getX(index * 3 + 2);
const positionAttribute = geometry.getAttribute("position");
const a = getTempVector();
const b = getTempVector();
const c = getTempVector();
a.fromBufferAttribute(positionAttribute, i0);
b.fromBufferAttribute(positionAttribute, i1);
c.fromBufferAttribute(positionAttribute, i2);
a.sub(b);
c.sub(b);
a.cross(c);
this._dir.copy(a).multiplyScalar(-1);
const rot = getWorldQuaternion(mesh);
this._dir.applyQuaternion(rot);
}
}
break;
}
}
break;
default:
this._dir.set(0, 0, 1);
break;
}
if (this._space === ParticleSystemSimulationSpace.World) {
this._dir.applyQuaternion(this.system.worldQuaternion);
}
this.applyRotation(this._dir);
this._dir.normalize();
this.spherizeDirection(this._dir, this.sphericalDirectionAmount);
this.randomizeDirection(this._dir, this.randomDirectionAmount);
if (debug) {
Gizmos.DrawSphere(pos, .01, 0x883300, .5, true);
Gizmos.DrawDirection(pos, this._dir, 0x883300, .5, true);
}
return this._dir;
}
static _randomQuat = new Quaternion();
static _tempVec = new Vector3();
randomizePosition(pos, amount) {
if (amount <= 0)
return;
const rp = ShapeModule._tempVec;
rp.set(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
rp.x *= amount * this.scale.x;
rp.y *= amount * this.scale.y;
rp.z *= amount * this.scale.z;
pos.add(rp);
}
randomizeDirection(direction, amount) {
if (amount === 0)
return;
const randomQuat = ShapeModule._randomQuat;
const tempVec = ShapeModule._tempVec;
tempVec.set(Math.random() - .5, Math.random() - .5, Math.random() - .5).normalize();
randomQuat.setFromAxisAngle(tempVec, amount * Math.random() * Math.PI);
direction.applyQuaternion(randomQuat);
}
spherizeDirection(dir, amount) {
if (amount === 0)
return;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(1 - Math.random() * 2);
const x = Math.sin(phi) * Math.cos(theta);
const y = Math.sin(phi) * Math.sin(theta);
const z = Math.cos(phi);
const v = new Vector3(x, y, z);
dir.lerp(v, amount);
}
randomSpherePoint(pos, radius, thickness, arc, vec) {
const u = Math.random();
const v = Math.random();
const theta = 2 * Math.PI * u * (arc / 360);
const phi = Math.acos(2 * v - 1);
const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
const x = pos.x + this.scale.x * (-r * Math.sin(phi) * Math.cos(theta));
const y = pos.y + this.scale.y * (r * Math.sin(phi) * Math.sin(theta));
const z = pos.z + this.scale.z * (r * Math.cos(phi));
vec.x = x;
vec.y = y;
vec.z = z;
}
randomCirclePoint(pos, radius, thickness, arg, vec) {
const u = Math.random();
const theta = 2 * Math.PI * u * (arg / 360);
const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * (radius);
const x = pos.x + this.scale.x * r * Math.cos(theta);
const y = pos.y + this.scale.y * r * Math.sin(theta);
const z = pos.z;
vec.x = x;
vec.y = y;
vec.z = z;
}
_loopTime = 0;
_loopDirection = 1;
randomConePoint(pos, _angle, radius, thickness, arc, arcMode, vec) {
let u = 0;
let v = 0;
switch (arcMode) {
case ParticleSystemShapeMultiModeValue.Random:
u = Math.random();
v = Math.random();
break;
case ParticleSystemShapeMultiModeValue.PingPong:
if (this._loopTime > 1)
this._loopDirection = -1;
if (this._loopTime < 0)
this._loopDirection = 1;
// continue with loop
case ParticleSystemShapeMultiModeValue.Loop:
u = .5;
v = Math.random();
this._loopTime += this.system.deltaTime * this._loopDirection;
break;
}
let theta = 2 * Math.PI * u * (arc / 360);
switch (arcMode) {
case ParticleSystemShapeMultiModeValue.PingPong:
case ParticleSystemShapeMultiModeValue.Loop:
theta += Math.PI + .5;
theta += this._loopTime * Math.PI * 2;
theta %= Mathf.toRadians(arc);
break;
}
const phi = Math.acos(2 * v - 1);
const r = Mathf.lerp(1, 1 - (Math.pow(1 - Math.random(), Math.PI)), thickness) * radius;
const x = pos.x + (-r * Math.sin(phi) * Math.cos(theta));
const y = pos.y + (r * Math.sin(phi) * Math.sin(theta));
const z = pos.z;
vec.x = x * this.scale.x;
vec.y = y * this.scale.y;
vec.z = z * this.scale.z;
}
}
__decorate([
serializable()
], ShapeModule.prototype, "shapeType", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "enabled", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "alignToDirection", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "angle", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "arc", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "arcSpread", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "arcSpeedMultiplier", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "arcMode", void 0);
__decorate([
serializable(Vector3)
], ShapeModule.prototype, "boxThickness", void 0);
__decorate([
serializable(Vector3)
], ShapeModule.prototype, "position", void 0);
__decorate([
serializable(Vector3)
], ShapeModule.prototype, "rotation", void 0);
__decorate([
serializable(Vector3)
], ShapeModule.prototype, "scale", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "radius", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "radiusThickness", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "sphericalDirectionAmount", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "randomDirectionAmount", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "randomPositionAmount", void 0);
__decorate([
serializable()
], ShapeModule.prototype, "meshShapeType", void 0);
__decorate([
serializable(MeshRenderer)
], ShapeModule.prototype, "meshRenderer", void 0);
export class NoiseModule {
damping;
enabled;
frequency;
octaveCount;
octaveMultiplier;
octaveScale;
positionAmount;
quality;
remap;
remapEnabled;
remapMultiplier;
remapX;
remapXMultiplier;
remapY;
remapYMultiplier;
remapZ;
remapZMultiplier;
scrollSpeedMultiplier;
separateAxes;
strengthMultiplier;
strengthX;
strengthXMultiplier;
strengthY;
strengthYMultiplier;
strengthZ;
strengthZMultiplier;
_noise;
_time = 0;
update(context) {
this._time += context.time.deltaTime * this.scrollSpeedMultiplier;
}
/** nebula implementations: */
_temp = new Vector3();
apply(_index, pos, vel, _deltaTime, age, life) {
if (!this.enabled)
return;
if (!this._noise) {
this._noise = createNoise4D(() => 0);
}
const temp = this._temp.set(pos.x, pos.y, pos.z).multiplyScalar(this.frequency);
const nx = this._noise(temp.x, temp.y, temp.z, this._time);
const ny = this._noise(temp.x, temp.y, temp.z, this._time + 1000 * this.frequency);
const nz = this._noise(temp.x, temp.y, temp.z, this._time + 2000 * this.frequency);
this._temp.set(nx, ny, nz).normalize();
const t = age / life;
let strengthFactor = this.positionAmount.evaluate(t);
if (!this.separateAxes) {
if (this.strengthX) {
strengthFactor *= this.strengthX.evaluate(t) * 1.5;
}
// strengthFactor *= this.strengthMultiplier;
// strengthFactor *= deltaTime;
this._temp.multiplyScalar(strengthFactor);
}
else {
this._temp.x *= strengthFactor * this.strengthXMultiplier;
this._temp.y *= strengthFactor * this.strengthYMultiplier;
this._temp.z *= strengthFactor * this.strengthZMultiplier;
}
// this._temp.setLength(strengthFactor * deltaTime);
vel.x += this._temp.x;
vel.y += this._temp.y;
vel.z += this._temp.z;
}
}
__decorate([
serializable()
], NoiseModule.prototype, "damping", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "enabled", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "frequency", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "octaveCount", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "octaveMultiplier", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "octaveScale", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "positionAmount", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "quality", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "remap", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "remapEnabled", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "remapMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "remapX", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "remapXMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "remapY", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "remapYMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "remapZ", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "remapZMultiplier", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "scrollSpeedMultiplier", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "separateAxes", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "strengthMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "strengthX", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "strengthXMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "strengthY", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "strengthYMultiplier", void 0);
__decorate([
serializable(MinMaxCurve)
], NoiseModule.prototype, "strengthZ", void 0);
__decorate([
serializable()
], NoiseModule.prototype, "strengthZMultiplier", void 0);
export var ParticleSystemTrailMode;
(function (ParticleSystemTrailMode) {
ParticleSystemTrailMode[ParticleSystemTrailMode["PerParticle"] = 0] = "PerParticle";
ParticleSystemTrailMode[ParticleSystemTrailMode["Ribbon"] = 1] = "Ribbon";
})(ParticleSystemTrailMode || (ParticleSystemTrailMode = {}));
export var ParticleSystemTrailTextureMode;
(function (ParticleSystemTrailTextureMode) {
ParticleSystemTrailTextureMode[ParticleSystemTrailTextureMode["Stretch"] = 0] = "Stretch";
ParticleSystemTrailTextureMode[ParticleSystemTrailTextureMode["Tile"] = 1] = "Tile";
ParticleSystemTrailTextureMode[ParticleSystemTrailTextureMode["DistributePerSegment"] = 2] = "DistributePerSegment";
ParticleSystemTrailTextureMode[ParticleSystemTrailTextureMode["RepeatPerSegment"] = 3] = "RepeatPerSegment";
})(ParticleSystemTrailTextureMode || (ParticleSystemTrailTextureMode = {}));
export class TrailModule {
enabled;
attachRibbonToTransform = false;
colorOverLifetime;
colorOverTrail;
dieWithParticles = true;
inheritParticleColor = true;
lifetime;
lifetimeMultiplier;
minVertexDistance = .2;
mode = ParticleSystemTrailMode.PerParticle;
ratio = 1;
ribbonCount = 1;
shadowBias = 0;
sizeAffectsLifetime = false;
sizeAffectsWidth = false;
splitSubEmitterRibbons = false;
textureMode = ParticleSystemTrailTextureMode.Stretch;
widthOverTrail;
widthOverTrailMultiplier;
worldSpace = false;
getWidth(size, _life01, pos01, t) {
const res = this.widthOverTrail.evaluate(pos01, t);
size *= res;
return size;
}
getColor(color, life01, pos01) {
const overTrail = this.colorOverTrail.evaluate(pos01);
const overLife = this.colorOverLifetime.evaluate(life01);
color.x *= overTrail.r * overLife.r;
color.y *= overTrail.g * overLife.g;
color.z *= overTrail.b * overLife.b;
if ("alpha" in overTrail && "alpha" in overLife)
color.w *= overTrail.alpha * overLife.alpha;
}
}
__decorate([
serializable()
], TrailModule.prototype, "enabled", void 0);
__decorate([
serializable()
], TrailModule.prototype