import {EmitterMode, EmitterShape, getValueFromEmitterMode, ShapeJSON} from './EmitterUtil'; import {Particle} from '../Particle'; import {MathUtils, Matrix4, Quaternion} from '../math'; import { ConstantValue, FunctionValueGenerator, GeneratorMemory, ValueGenerator, ValueGeneratorFromJSON, } from '../functions'; import {EmissionState, IParticleSystem} from '../IParticleSystem'; import { UP_VEC3, ZERO_VEC3 } from '../util/MathUtil'; /** * Interface representing the parameters for a hemisphere emitter. */ export interface HemisphereEmitterParameters { /** * The radius of the hemisphere. */ radius?: number; /** * The arc of the hemisphere. */ arc?: number; /** * The thickness of the hemisphere. 1 is a full hemisphere, 0 is a thin shell. */ thickness?: number; /** * The mode of the emitter. * {@link EmitterMode} */ mode?: EmitterMode; /** * The length of the segment at which the emitter point converges at the start and end, when mode is EmitterMode.Loop or EmitterMode.PingPong. * {@link EmitterMode} */ spread?: number; /** * The speed of the emitter start point when mode is EmitterMode.Loop or EmitterMode.PingPong. * {@link EmitterMode} */ speed?: ValueGenerator | FunctionValueGenerator; } export class HemisphereEmitter implements EmitterShape { type = 'hemisphere'; radius: number; arc: number; thickness: number; //[0, 1] mode: EmitterMode; spread: number; speed: ValueGenerator | FunctionValueGenerator; memory: GeneratorMemory; _m1: Matrix4; constructor(parameters: HemisphereEmitterParameters = {}) { this.radius = parameters.radius ?? 10; this.arc = parameters.arc ?? 2.0 * Math.PI; this.thickness = parameters.thickness ?? 1; this.mode = parameters.mode ?? EmitterMode.Random; this.spread = parameters.spread ?? 0; this.speed = parameters.speed ?? new ConstantValue(1); this.memory = []; this._m1 = new Matrix4(); } private currentValue = 0; update(system: IParticleSystem, delta: number): void { if (EmitterMode.Random != this.mode) { this.currentValue += this.speed.genValue(this.memory, system.emissionState.time / system.duration) * delta; } } initialize(p: Particle, emissionState: EmissionState) { const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread, emissionState); const v = Math.random(); const rand = MathUtils.lerp(1 - this.thickness, 1, Math.random()); const theta = u * this.arc; const phi = Math.acos(v); //const r = Math.cbrt(rand); const sinTheta = Math.sin(theta); const cosTheta = Math.cos(theta); const sinPhi = Math.sin(phi); const cosPhi = Math.cos(phi); p.position.x = sinPhi * cosTheta; p.position.y = sinPhi * sinTheta; p.position.z = cosPhi; p.velocity.copy(p.position).multiplyScalar(p.startSpeed); p.position.multiplyScalar(this.radius * rand); if (p.rotation instanceof Quaternion) { this._m1.lookAt(ZERO_VEC3, p.position, UP_VEC3); p.rotation.setFromRotationMatrix(this._m1); } } toJSON(): ShapeJSON { return { type: 'hemisphere', radius: this.radius, arc: this.arc, thickness: this.thickness, mode: this.mode, spread: this.spread, speed: this.speed.toJSON(), }; } static fromJSON(json: any): HemisphereEmitter { return new HemisphereEmitter({ radius: json.radius, arc: json.arc, thickness: json.thickness, mode: json.mode, speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined, spread: json.spread, }); } clone(): EmitterShape { return new HemisphereEmitter({ radius: this.radius, arc: this.arc, thickness: this.thickness, mode: this.mode, speed: this.speed.clone(), spread: this.spread, }); } }