UNPKG

@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
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