diff --git a/e2e/case/particleRenderer-dream.ts b/e2e/case/particleRenderer-dream.ts deleted file mode 100644 index e86dbbfe4..000000000 --- a/e2e/case/particleRenderer-dream.ts +++ /dev/null @@ -1,343 +0,0 @@ -/** - * @title Particle Dream - * @category Particle - */ -import { - AssetType, - BlendMode, - BoxShape, - Camera, - Color, - Engine, - Entity, - Logger, - ParticleCurveMode, - ParticleGradientMode, - ParticleMaterial, - ParticleRenderMode, - ParticleRenderer, - Texture2D, - Vector3, - WebGLEngine, - WebGLMode -} from "@galacean/engine"; -import { initScreenshot, updateForE2E } from "./.mockForE2E"; - -// Create engine -WebGLEngine.create({ - canvas: "canvas", - graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 } -}).then((engine) => { - Logger.enable(); - engine.canvas.resizeByClientSize(); - - const scene = engine.sceneManager.activeScene; - const rootEntity = scene.createRootEntity(); - scene.background.solidColor = new Color(15 / 255, 38 / 255, 18 / 255, 1); - - // Create camera - const cameraEntity = rootEntity.createChild("camera_entity"); - cameraEntity.transform.position = new Vector3(0, 1, 3); - const camera = cameraEntity.addComponent(Camera); - camera.fieldOfView = 60; - - engine.run(); - - engine.resourceManager - .load([ - { - url: "https://mdn.alipayobjects.com/huamei_b4l2if/afts/img/A*JPsCSK5LtYkAAAAAAAAAAAAADil6AQ/original", - type: AssetType.Texture2D - }, - { - url: "https://mdn.alipayobjects.com/huamei_b4l2if/afts/img/A*eWTFRZPqfDMAAAAAAAAAAAAADil6AQ/original", - type: AssetType.Texture2D - }, - { - url: "https://mdn.alipayobjects.com/huamei_b4l2if/afts/img/A*J8uhRoxJtYgAAAAAAAAAAAAADil6AQ/original", - type: AssetType.Texture2D - }, - { - url: "https://mdn.alipayobjects.com/huamei_b4l2if/afts/img/A*Ea3qRb1yCQMAAAAAAAAAAAAADil6AQ/original", - type: AssetType.Texture2D - } - ]) - .then((textures) => { - const fireEntity = createDebrisParticle(engine, textures[0]); - createGlowParticle(fireEntity, textures[1]); - createSparksParticle(fireEntity, textures[2]); - createHighlightsParticle(fireEntity, textures[3]); - - cameraEntity.addChild(fireEntity); - - setTimeout(() => { - updateForE2E(engine); - initScreenshot(engine, camera); - }, 2000); - }); -}); - -function createDebrisParticle(engine: Engine, texture: Texture2D): Entity { - const particleEntity = new Entity(engine, "Debris"); - particleEntity.transform.position.set(0, -7.5, -8); - - const particleRenderer = particleEntity.addComponent(ParticleRenderer); - - const material = new ParticleMaterial(engine); - material.baseColor = new Color(1.0, 1.0, 1.0, 1.0); - material.blendMode = BlendMode.Additive; - material.baseTexture = texture; - particleRenderer.setMaterial(material); - particleRenderer.priority = 2; - - particleRenderer.generator.useAutoRandomSeed = false; - - const { main, emission, sizeOverLifetime, colorOverLifetime, velocityOverLifetime } = particleRenderer.generator; - - // Main module - main.startSpeed.constant = 0; - - main.startSize.constantMin = 0.1; - main.startSize.constantMax = 1; - main.startSize.mode = ParticleCurveMode.TwoConstants; - - main.startRotationZ.constantMin = 0; - main.startRotationZ.constantMax = 360; - main.startRotationZ.mode = ParticleCurveMode.TwoConstants; - - main.startColor.constantMin.set(255 / 255, 255 / 255, 255 / 255, 1.0); - main.startColor.constantMax.set(13 / 255, 255 / 255, 0 / 255, 1.0); - main.startColor.mode = ParticleGradientMode.TwoConstants; - - // Emission module - emission.rateOverTime.constant = 5; - - const boxShape = new BoxShape(); - boxShape.size.set(22, 1, 0); - emission.shape = boxShape; - - // Color over lifetime module - colorOverLifetime.enabled = true; - colorOverLifetime.color.mode = ParticleGradientMode.Gradient; - - const gradient = colorOverLifetime.color.gradient; - gradient.alphaKeys[0].alpha = 0; - gradient.alphaKeys[1].alpha = 0; - gradient.addAlphaKey(0.2, 1.0); - gradient.addAlphaKey(0.8, 1.0); - - // Size over lifetime module - sizeOverLifetime.enabled = true; - const keys = sizeOverLifetime.size.curve.keys; - keys[0].value = 1; - keys[1].value = 0; - - // Velocity over lifetime module - velocityOverLifetime.enabled = true; - velocityOverLifetime.velocityX.constantMin = 2; - velocityOverLifetime.velocityX.constantMax = 1; - velocityOverLifetime.velocityX.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityY.constantMin = 4; - velocityOverLifetime.velocityY.constantMax = 2; - velocityOverLifetime.velocityY.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityZ.constantMin = 0; - velocityOverLifetime.velocityZ.constantMax = 0; - velocityOverLifetime.velocityZ.mode = ParticleCurveMode.TwoConstants; - - return particleEntity; -} - -function createGlowParticle(fireEntity: Entity, texture: Texture2D): void { - const particleEntity = fireEntity.createChild("Glow"); - particleEntity.transform.position.set(-1.88, 0, 0); - - const particleRenderer = particleEntity.addComponent(ParticleRenderer); - particleRenderer.renderMode = ParticleRenderMode.StretchBillboard; - particleRenderer.lengthScale = 2; - - const material = new ParticleMaterial(fireEntity.engine); - material.blendMode = BlendMode.Additive; - material.baseTexture = texture; - particleRenderer.setMaterial(material); - particleRenderer.priority = 1; - - const generator = particleRenderer.generator; - generator.useAutoRandomSeed = false; - const { main, emission, velocityOverLifetime, colorOverLifetime } = generator; - - // Main module - main.startSpeed.constant = 0.0; - - main.startSize.constantMin = 5; - main.startSize.constantMax = 9; - main.startSize.mode = ParticleCurveMode.TwoConstants; - - main.startRotationZ.constantMin = 0; - main.startRotationZ.constantMax = 360; - main.startRotationZ.mode = ParticleCurveMode.TwoConstants; - - main.startColor.constantMin = new Color(0 / 255, 157 / 255, 255 / 255, 64 / 255); - main.startColor.constantMax = new Color(13 / 255, 255 / 255, 0 / 255, 128 / 255); - main.startColor.mode = ParticleGradientMode.TwoConstants; - - // Emission module - emission.rateOverTime.constant = 10; - - const boxShape = new BoxShape(); - boxShape.size.set(22, 1, 0); - emission.shape = boxShape; - - // Velocity over lifetime module - velocityOverLifetime.enabled = true; - velocityOverLifetime.velocityX.constantMin = 2; - velocityOverLifetime.velocityX.constantMax = 1; - velocityOverLifetime.velocityX.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityY.constantMin = 4; - velocityOverLifetime.velocityY.constantMax = 2; - velocityOverLifetime.velocityY.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityZ.constantMin = 0; - velocityOverLifetime.velocityZ.constantMax = 0; - velocityOverLifetime.velocityZ.mode = ParticleCurveMode.TwoConstants; - - // Color over lifetime module - colorOverLifetime.enabled = true; - colorOverLifetime.color.mode = ParticleGradientMode.Gradient; - - const gradient = colorOverLifetime.color.gradient; - gradient.alphaKeys[0].alpha = 0; - gradient.alphaKeys[1].alpha = 0; - gradient.addAlphaKey(0.2, 1.0); -} - -function createSparksParticle(fireEntity: Entity, texture: Texture2D): void { - const particleEntity = fireEntity.createChild("Sparks"); - particleEntity.transform.position.set(-1.54, 0, 0); - - const particleRenderer = particleEntity.addComponent(ParticleRenderer); - const material = new ParticleMaterial(fireEntity.engine); - material.baseTexture = texture; - particleRenderer.setMaterial(material); - particleRenderer.priority = 0; - - const { main, emission, colorOverLifetime, velocityOverLifetime } = particleRenderer.generator; - particleRenderer.generator.useAutoRandomSeed = false; - - // Main module - main.startLifetime.constant = 5; - main.startSpeed.constant = 0; - - main.startSize.constantMin = 0.05; - main.startSize.constantMax = 0.2; - main.startSize.mode = ParticleCurveMode.TwoConstants; - - main.startRotationZ.constantMin = 0; - main.startRotationZ.constantMax = 360; - main.startRotationZ.mode = ParticleCurveMode.TwoConstants; - - main.startColor.constant = new Color(37 / 255, 133 / 255, 255 / 255, 255 / 255); - - // Emission module - emission.rateOverTime.constant = 30; - - const boxShape = new BoxShape(); - boxShape.size.set(22, 1, 0); - emission.shape = boxShape; - - // Velocity over lifetime module - velocityOverLifetime.enabled = true; - velocityOverLifetime.velocityX.constantMin = 2; - velocityOverLifetime.velocityX.constantMax = 1; - velocityOverLifetime.velocityX.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityY.constantMin = 4; - velocityOverLifetime.velocityY.constantMax = 2; - velocityOverLifetime.velocityY.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityZ.constantMin = 0; - velocityOverLifetime.velocityZ.constantMax = 0; - velocityOverLifetime.velocityZ.mode = ParticleCurveMode.TwoConstants; - - // Color over lifetime module - colorOverLifetime.enabled = true; - colorOverLifetime.color.mode = ParticleGradientMode.Gradient; - - const gradient = colorOverLifetime.color.gradient; - gradient.alphaKeys[0].alpha = 0; - gradient.alphaKeys[1].alpha = 0; - gradient.addAlphaKey(0.2, 1.0); - gradient.addAlphaKey(0.8, 1.0); -} - -function createHighlightsParticle(fireEntity: Entity, texture: Texture2D): void { - const particleEntity = fireEntity.createChild("Highlights"); - particleEntity.transform.position.set(-5.31, 0, 0); - - const particleRenderer = particleEntity.addComponent(ParticleRenderer); - - const material = new ParticleMaterial(fireEntity.engine); - material.blendMode = BlendMode.Additive; - material.baseTexture = texture; - particleRenderer.setMaterial(material); - particleRenderer.priority = 3; - - const generator = particleRenderer.generator; - const { main, emission, sizeOverLifetime, colorOverLifetime, velocityOverLifetime } = generator; - generator.useAutoRandomSeed = false; - - // Main module - main.startSpeed.constant = 0; - - main.startSize.constantMin = 0.1; - main.startSize.constantMax = 7; - main.startSize.mode = ParticleCurveMode.TwoConstants; - - main.startRotationZ.constantMin = 0; - main.startRotationZ.constantMax = 360; - main.startRotationZ.mode = ParticleCurveMode.TwoConstants; - - main.startColor.constantMin.set(105 / 255, 198 / 255, 255 / 255, 64 / 255); - main.startColor.constantMax.set(13 / 255, 255 / 255, 0 / 255, 32 / 255); - main.startColor.mode = ParticleGradientMode.TwoConstants; - - // Emission module - emission.rateOverTime.constant = 40; - - const boxShape = new BoxShape(); - boxShape.size.set(22, 1, 0); - emission.shape = boxShape; - - // Velocity over lifetime module - velocityOverLifetime.enabled = true; - velocityOverLifetime.velocityX.constantMin = 3; - velocityOverLifetime.velocityX.constantMax = 2; - velocityOverLifetime.velocityX.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityY.constantMin = 4; - velocityOverLifetime.velocityY.constantMax = 2; - velocityOverLifetime.velocityY.mode = ParticleCurveMode.TwoConstants; - - velocityOverLifetime.velocityZ.constantMin = 0; - velocityOverLifetime.velocityZ.constantMax = 0; - velocityOverLifetime.velocityZ.mode = ParticleCurveMode.TwoConstants; - - // Color over lifetime module - colorOverLifetime.enabled = true; - colorOverLifetime.color.mode = ParticleGradientMode.Gradient; - - const gradient = colorOverLifetime.color.gradient; - gradient.alphaKeys[0].alpha = 0; - gradient.alphaKeys[1].alpha = 0; - gradient.addAlphaKey(0.2, 1.0); - gradient.addAlphaKey(0.8, 1.0); - - // Size over lifetime module - sizeOverLifetime.enabled = true; - const curve = sizeOverLifetime.size.curve; - sizeOverLifetime.size.mode = ParticleCurveMode.Curve; - curve.keys[0].value = 1; - curve.keys[1].value = 0; -} diff --git a/e2e/config.ts b/e2e/config.ts index 4b1f105a8..f0ce5ec80 100644 --- a/e2e/config.ts +++ b/e2e/config.ts @@ -163,12 +163,5 @@ export const E2E_CONFIG = { caseFileName: "physx-collision", threshold: 0.1 } - }, - Particle: { - meshopt: { - category: "Particle", - caseFileName: "particleRenderer-dream", - threshold: 0.3 - } } }; diff --git a/e2e/fixtures/originImage/Particle_particleRenderer-dream.jpg b/e2e/fixtures/originImage/Particle_particleRenderer-dream.jpg deleted file mode 100644 index ca9bcf21c..000000000 Binary files a/e2e/fixtures/originImage/Particle_particleRenderer-dream.jpg and /dev/null differ diff --git a/packages/core/src/particle/ParticleBufferUtils.ts b/packages/core/src/particle/ParticleBufferUtils.ts index f4b7af1a3..735119fd5 100644 --- a/packages/core/src/particle/ParticleBufferUtils.ts +++ b/packages/core/src/particle/ParticleBufferUtils.ts @@ -15,19 +15,6 @@ import { ParticleInstanceVertexAttribute } from "./enums/attributes/ParticleInst * @internal */ export class ParticleBufferUtils { - static readonly instanceVertexStride = 152; - static readonly instanceVertexFloatStride = ParticleBufferUtils.instanceVertexStride / 4; - - static readonly startLifeTimeOffset = 3; - static readonly timeOffset = 7; - static readonly simulationUVOffset = 34; - - static readonly billboardIndexCount = 6; - - static readonly boundsFloatStride = 8; - static readonly boundsTimeOffset = 6; - static readonly boundsMaxLifetimeOffset = 7; - readonly billboardVertexElement = new VertexElement( ParticleBillboardVertexAttribute.cornerTextureCoordinate, 0, @@ -49,6 +36,15 @@ export class ParticleBufferUtils { new VertexElement(ParticleInstanceVertexAttribute.SimulationUV, 136, VertexElementFormat.Vector4, 1, 1) ]; + readonly instanceVertexStride = 152; + readonly instanceVertexFloatStride = this.instanceVertexStride / 4; + + readonly startLifeTimeOffset = 3; + readonly timeOffset = 7; + readonly simulationUVOffset = 34; + + readonly billboardIndexCount = 6; + readonly billboardVertexBufferBinding: VertexBufferBinding; readonly billboardIndexBufferBinding: IndexBufferBinding; @@ -67,7 +63,7 @@ export class ParticleBufferUtils { const indexBuffer = new Buffer( engine, BufferBindFlag.IndexBuffer, - ParticleBufferUtils.billboardIndexCount, + this.billboardIndexCount, BufferUsage.Static, false ); diff --git a/packages/core/src/particle/ParticleGenerator.ts b/packages/core/src/particle/ParticleGenerator.ts index da3a70bfb..678b4f42c 100644 --- a/packages/core/src/particle/ParticleGenerator.ts +++ b/packages/core/src/particle/ParticleGenerator.ts @@ -1,4 +1,4 @@ -import { BoundingBox, Color, MathUtil, Matrix, Quaternion, Vector2, Vector3 } from "@galacean/engine-math"; +import { Color, MathUtil, Quaternion, Vector3 } from "@galacean/engine-math"; import { Transform } from "../Transform"; import { deepClone, ignoreClone } from "../clone/CloneManager"; import { ColorSpace } from "../enums/ColorSpace"; @@ -14,7 +14,7 @@ import { SetDataOptions } from "../graphic/enums/SetDataOptions"; import { VertexAttribute } from "../mesh"; import { ShaderData } from "../shader"; import { Buffer } from "./../graphic/Buffer"; -import { ParticleRenderer, ParticleUpdateFlags } from "./ParticleRenderer"; +import { ParticleRenderer } from "./ParticleRenderer"; import { ParticleCurveMode } from "./enums/ParticleCurveMode"; import { ParticleGradientMode } from "./enums/ParticleGradientMode"; import { ParticleRenderMode } from "./enums/ParticleRenderMode"; @@ -27,40 +27,36 @@ import { RotationOverLifetimeModule } from "./modules/RotationOverLifetimeModule import { SizeOverLifetimeModule } from "./modules/SizeOverLifetimeModule"; import { TextureSheetAnimationModule } from "./modules/TextureSheetAnimationModule"; import { VelocityOverLifetimeModule } from "./modules/VelocityOverLifetimeModule"; -import { ParticleBufferUtils } from "./ParticleBufferUtils"; -import { ParticleCompositeCurve } from "./modules/ParticleCompositeCurve"; /** * Particle Generator. */ export class ParticleGenerator { - private static _tempVector20 = new Vector2(); - private static _tempVector21 = new Vector2(); - private static _tempVector22 = new Vector2(); + /** @internal */ private static _tempVector30 = new Vector3(); + /** @internal */ private static _tempVector31 = new Vector3(); - private static _tempMat = new Matrix(); + /** @internal */ private static _tempColor0 = new Color(); + /** @internal */ private static _tempParticleRenderers = new Array(); - private static readonly _particleIncreaseCount = 128; - private static readonly _transformedBoundsIncreaseCount = 16; /** Use auto random seed. */ useAutoRandomSeed = true; /** Main module. */ @deepClone - readonly main: MainModule; + readonly main = new MainModule(this); /** Emission module. */ @deepClone readonly emission = new EmissionModule(this); /** Velocity over lifetime module. */ @deepClone - readonly velocityOverLifetime: VelocityOverLifetimeModule; + readonly velocityOverLifetime = new VelocityOverLifetimeModule(this); /** Size over lifetime module. */ @deepClone - readonly sizeOverLifetime: SizeOverLifetimeModule; + readonly sizeOverLifetime = new SizeOverLifetimeModule(this); /** Rotation over lifetime module. */ @deepClone readonly rotationOverLifetime = new RotationOverLifetimeModule(this); @@ -113,14 +109,6 @@ export class ParticleGenerator { @ignoreClone private _instanceVertices: Float32Array; private _randomSeed = 0; - @ignoreClone - private _transformedBoundsArray: Float32Array; - @ignoreClone - private _transformedBoundsCount = 0; - @ignoreClone - private _firstActiveTransformedBoundingBox = 0; - @ignoreClone - private _firstFreeTransformedBoundingBox = 0; /** * Whether the particle generator is contain alive or is still creating particles. @@ -161,10 +149,6 @@ export class ParticleGenerator { this._reorganizeGeometryBuffers(); this._resizeInstanceBuffer(true, ParticleGenerator._particleIncreaseCount); - this.main = new MainModule(this); - this.velocityOverLifetime = new VelocityOverLifetimeModule(this); - this.sizeOverLifetime = new SizeOverLifetimeModule(this); - this.emission.enabled = true; } @@ -215,8 +199,6 @@ export class ParticleGenerator { this._firstNewElement = firstFreeElement; this._playTime = 0; - this._firstActiveTransformedBoundingBox = this._firstFreeTransformedBoundingBox; - this.emission._reset(); } } @@ -262,7 +244,6 @@ export class ParticleGenerator { * @internal */ _update(elapsedTime: number): void { - const lastAlive = this.isAlive; const { main, emission } = this; const duration = main.duration; const lastPlayTime = this._playTime; @@ -272,10 +253,6 @@ export class ParticleGenerator { this._retireActiveParticles(); this._freeRetiredParticles(); - if (main.simulationSpace === ParticleSimulationSpace.World) { - this._retireTransformedBounds(); - } - if (emission.enabled && this._isPlaying) { // If maxParticles is changed dynamically, currentParticleCount may be greater than maxParticles if (this._currentParticleCount > main._maxParticleBuffer) { @@ -284,27 +261,21 @@ export class ParticleGenerator { this._resizeInstanceBuffer(false); } } + emission._emit(lastPlayTime, this._playTime); + if (!main.isLoop && this._playTime > duration) { this._isPlaying = false; } } - if (this.isAlive) { - if (main.simulationSpace === ParticleSimulationSpace.World) { - this._generateTransformedBounds(); - } - } else { - // Reset play time when is not playing and no active particles to avoid potential precision problems in GPU + // Reset play time when is not playing and no active particles to avoid potential precision problems in GPU + if (!this.isAlive) { const discardTime = Math.min(emission._frameRateTime, Math.floor(this._playTime / duration) * duration); this._playTime -= discardTime; emission._frameRateTime -= discardTime; } - if (this.isAlive !== lastAlive) { - this._renderer._onWorldVolumeChanged(); - } - // Add new particles to vertex buffer when has wait process retired element or new particle // // Another choice is just add new particles to vertex buffer and render all particles ignore the retired particle in shader, especially billboards @@ -371,7 +342,7 @@ export class ParticleGenerator { primitive.addVertexElement(particleUtils.billboardVertexElement); vertexBufferBindings.push(particleUtils.billboardVertexBufferBinding); primitive.setIndexBufferBinding(particleUtils.billboardIndexBufferBinding); - this._subPrimitive.count = ParticleBufferUtils.billboardIndexCount; + this._subPrimitive.count = particleUtils.billboardIndexCount; } primitive.setVertexBufferBindings(vertexBufferBindings); @@ -391,7 +362,8 @@ export class ParticleGenerator { _resizeInstanceBuffer(isIncrease: boolean, increaseCount?: number): void { this._instanceVertexBufferBinding?.buffer.destroy(); - const stride = ParticleBufferUtils.instanceVertexStride; + const particleUtils = this._renderer.engine._particleBufferUtils; + const stride = particleUtils.instanceVertexStride; const newParticleCount = isIncrease ? this._currentParticleCount + increaseCount : this.main._maxParticleBuffer; const newByteLength = stride * newParticleCount; const engine = this._renderer.engine; @@ -408,22 +380,17 @@ export class ParticleGenerator { const vertexBufferBinding = new VertexBufferBinding(vertexInstanceBuffer, stride); const instanceVertices = new Float32Array(newByteLength / 4); - const lastInstanceVertices = this._instanceVertices; if (lastInstanceVertices) { - const floatStride = ParticleBufferUtils.instanceVertexFloatStride; + const floatStride = particleUtils.instanceVertexFloatStride; const firstFreeElement = this._firstFreeElement; const firstRetiredElement = this._firstRetiredElement; if (isIncrease) { - instanceVertices.set(new Float32Array(lastInstanceVertices.buffer, 0, firstFreeElement * floatStride)); - - const nextFreeElement = firstFreeElement + 1; - const freeEndOffset = (nextFreeElement + increaseCount) * floatStride; - instanceVertices.set( - new Float32Array(lastInstanceVertices.buffer, nextFreeElement * floatStride * 4), - freeEndOffset - ); + const freeOffset = this._firstFreeElement * floatStride; + instanceVertices.set(new Float32Array(lastInstanceVertices.buffer, 0, freeOffset)); + const freeEndOffset = (this._firstFreeElement + increaseCount) * floatStride; + instanceVertices.set(new Float32Array(lastInstanceVertices.buffer, freeOffset * 4), freeEndOffset); // Maintain expanded pointers this._firstNewElement > firstFreeElement && (this._firstNewElement += increaseCount); @@ -534,122 +501,6 @@ export class ParticleGenerator { _destroy(): void { this._instanceVertexBufferBinding.buffer.destroy(); this._primitive.destroy(); - this.emission._destroy(); - } - - /** - * @internal - */ - _updateBoundsSimulationLocal(bounds: BoundingBox): void { - const renderer = this._renderer; - // Get longest Lifetime - const maxLifetime = this.main.startLifetime._getMax(); - - const { _generatorBounds: generatorBounds, _transformedBounds: transformedBounds } = renderer; - if (renderer._isContainDirtyFlag(ParticleUpdateFlags.GeneratorVolume)) { - this._calculateGeneratorBounds(maxLifetime, generatorBounds); - renderer._setDirtyFlagFalse(ParticleUpdateFlags.GeneratorVolume); - } - - if (renderer._isContainDirtyFlag(ParticleUpdateFlags.TransformVolume)) { - this._calculateTransformedBounds(maxLifetime, generatorBounds, transformedBounds); - renderer._setDirtyFlagFalse(ParticleUpdateFlags.TransformVolume); - } - - this._addGravityToBounds(maxLifetime, transformedBounds, bounds); - } - - /** - * @internal - */ - _updateBoundsSimulationWorld(bounds: BoundingBox): void { - const boundsArray = this._transformedBoundsArray; - const firstActiveElement = this._firstActiveTransformedBoundingBox; - const firstFreeElement = this._firstFreeTransformedBoundingBox; - - const index = firstActiveElement * ParticleBufferUtils.boundsFloatStride; - bounds.min.copyFromArray(boundsArray, index); - bounds.max.copyFromArray(boundsArray, index + 3); - - if (firstActiveElement < firstFreeElement) { - for (let i = firstActiveElement + 1; i < firstFreeElement; i++) { - this._mergeTransformedBounds(i, bounds); - } - } else { - for (let i = firstActiveElement + 1, n = this._transformedBoundsCount; i < n; i++) { - this._mergeTransformedBounds(i, bounds); - } - if (firstFreeElement > 0) { - for (let i = 0; i < firstFreeElement; i++) { - this._mergeTransformedBounds(i, bounds); - } - } - } - - const maxLifetime = this.main.startLifetime._getMax(); - this._addGravityToBounds(maxLifetime, bounds, bounds); - } - - /** - * @internal - */ - _freeBoundsArray(): void { - this._transformedBoundsArray = null; - - this._transformedBoundsCount = 0; - this._firstActiveTransformedBoundingBox = 0; - this._firstFreeTransformedBoundingBox = 0; - } - - /** - * @internal - */ - _generateTransformedBounds(): void { - const renderer = this._renderer; - // Get longest Lifetime - const maxLifetime = this.main.startLifetime._getMax(); - - const generatorBounds = renderer._generatorBounds; - if (renderer._isContainDirtyFlag(ParticleUpdateFlags.GeneratorVolume)) { - this._calculateGeneratorBounds(maxLifetime, generatorBounds); - renderer._setDirtyFlagFalse(ParticleUpdateFlags.GeneratorVolume); - } - - const { boundsFloatStride, boundsTimeOffset, boundsMaxLifetimeOffset } = ParticleBufferUtils; - const firstFreeElement = this._firstFreeTransformedBoundingBox; - if (renderer._isContainDirtyFlag(ParticleUpdateFlags.TransformVolume)) { - // Resize transformed bounds if needed - let nextFreeElement = firstFreeElement + 1; - if (nextFreeElement >= this._transformedBoundsCount) { - nextFreeElement = 0; - } - if (nextFreeElement === this._firstActiveTransformedBoundingBox) { - this._resizeTransformedBoundsArray(); - nextFreeElement = firstFreeElement + 1; - } - - // Generate transformed bounds - const transformedBounds = renderer._transformedBounds; - this._calculateTransformedBounds(maxLifetime, generatorBounds, transformedBounds); - - const boundsOffset = firstFreeElement * boundsFloatStride; - const boundsArray = this._transformedBoundsArray; - transformedBounds.min.copyToArray(boundsArray, boundsOffset); - transformedBounds.max.copyToArray(boundsArray, boundsOffset + 3); - - boundsArray[boundsOffset + boundsTimeOffset] = this._playTime; - boundsArray[boundsOffset + boundsMaxLifetimeOffset] = maxLifetime; - - this._firstFreeTransformedBoundingBox = nextFreeElement; - renderer._setDirtyFlagFalse(ParticleUpdateFlags.TransformVolume); - } else { - let previousFreeElement = this._firstFreeTransformedBoundingBox - 1; - if (previousFreeElement < 0) { - previousFreeElement = this._transformedBoundsCount; - } - this._transformedBoundsArray[previousFreeElement * ParticleBufferUtils.boundsFloatStride + boundsTimeOffset] = - this._playTime; - } } private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, time: number): void { @@ -661,12 +512,6 @@ export class ParticleGenerator { const main = this.main; // Check if can be expanded - - // Using 'nextFreeElement' instead of 'freeElement' when comparing with '_firstRetiredElement' - // aids in definitively identifying the head and tail of the circular queue. - - // Failure to adopt this approach may impede growth initiation - // due to the initial alignment of 'freeElement' and 'firstRetiredElement'. if (nextFreeElement === this._firstRetiredElement) { const increaseCount = Math.min( ParticleGenerator._particleIncreaseCount, @@ -688,10 +533,11 @@ export class ParticleGenerator { rot = transform.worldRotationQuaternion; } + const particleUtils = this._renderer.engine._particleBufferUtils; const startSpeed = main.startSpeed.evaluate(undefined, main._startSpeedRand.random()); const instanceVertices = this._instanceVertices; - const offset = firstFreeElement * ParticleBufferUtils.instanceVertexFloatStride; + const offset = firstFreeElement * particleUtils.instanceVertexFloatStride; // Position instanceVertices[offset] = position.x; @@ -699,7 +545,7 @@ export class ParticleGenerator { instanceVertices[offset + 2] = position.z; // Start life time - instanceVertices[offset + ParticleBufferUtils.startLifeTimeOffset] = main.startLifetime.evaluate( + instanceVertices[offset + particleUtils.startLifeTimeOffset] = main.startLifetime.evaluate( undefined, main._startLifeTimeRand.random() ); @@ -710,7 +556,7 @@ export class ParticleGenerator { instanceVertices[offset + 6] = direction.z; // Time - instanceVertices[offset + ParticleBufferUtils.timeOffset] = time; + instanceVertices[offset + particleUtils.timeOffset] = time; // Color const startColor = ParticleGenerator._tempColor0; @@ -808,12 +654,12 @@ export class ParticleGenerator { // Simulation UV if (this.textureSheetAnimation.enabled) { const tillingInfo = this.textureSheetAnimation._tillingInfo; - instanceVertices[offset + ParticleBufferUtils.simulationUVOffset] = tillingInfo.x; + instanceVertices[offset + particleUtils.simulationUVOffset] = tillingInfo.x; instanceVertices[offset + 35] = tillingInfo.y; instanceVertices[offset + 36] = 0; instanceVertices[offset + 37] = 0; } else { - instanceVertices[offset + ParticleBufferUtils.simulationUVOffset] = 1; + instanceVertices[offset + particleUtils.simulationUVOffset] = 1; instanceVertices[offset + 35] = 1; instanceVertices[offset + 36] = 0; instanceVertices[offset + 37] = 0; @@ -824,17 +670,18 @@ export class ParticleGenerator { private _retireActiveParticles(): void { const engine = this._renderer.engine; + const particleUtils = engine._particleBufferUtils; const frameCount = engine.time.frameCount; const instanceVertices = this._instanceVertices; while (this._firstActiveElement !== this._firstNewElement) { - const activeParticleOffset = this._firstActiveElement * ParticleBufferUtils.instanceVertexFloatStride; - const activeParticleTimeOffset = activeParticleOffset + ParticleBufferUtils.timeOffset; + const activeParticleOffset = this._firstActiveElement * particleUtils.instanceVertexFloatStride; + const activeParticleTimeOffset = activeParticleOffset + particleUtils.timeOffset; const particleAge = this._playTime - instanceVertices[activeParticleTimeOffset]; // Use `Math.fround` to ensure the precision of comparison is same - if (Math.fround(particleAge) < instanceVertices[activeParticleOffset + ParticleBufferUtils.startLifeTimeOffset]) { + if (Math.fround(particleAge) < instanceVertices[activeParticleOffset + particleUtils.startLifeTimeOffset]) { break; } @@ -850,12 +697,12 @@ export class ParticleGenerator { } private _freeRetiredParticles(): void { + const particleUtils = this._renderer.engine._particleBufferUtils; const frameCount = this._renderer.engine.time.frameCount; while (this._firstRetiredElement !== this._firstActiveElement) { const offset = - this._firstRetiredElement * ParticleBufferUtils.instanceVertexFloatStride + - ParticleBufferUtils.startLifeTimeOffset; + this._firstRetiredElement * particleUtils.instanceVertexFloatStride + particleUtils.startLifeTimeOffset; const age = frameCount - this._instanceVertices[offset]; // WebGL don't support map buffer range, so off this optimization @@ -878,7 +725,7 @@ export class ParticleGenerator { return; } - const byteStride = ParticleBufferUtils.instanceVertexStride; + const byteStride = this._renderer.engine._particleBufferUtils.instanceVertexStride; const start = firstActiveElement * byteStride; const instanceBuffer = this._instanceVertexBufferBinding.buffer; const dataBuffer = this._instanceVertices.buffer; @@ -917,234 +764,4 @@ export class ParticleGenerator { out.push(vertexBufferBinding); return index; } - - private _resizeTransformedBoundsArray(): void { - const floatStride = ParticleBufferUtils.boundsFloatStride; - const increaseCount = ParticleGenerator._transformedBoundsIncreaseCount; - - this._transformedBoundsCount += increaseCount; - const lastBoundsArray = this._transformedBoundsArray; - const boundsArray = new Float32Array(this._transformedBoundsCount * floatStride); - - if (lastBoundsArray) { - const firstFreeElement = this._firstFreeTransformedBoundingBox; - boundsArray.set(new Float32Array(lastBoundsArray.buffer, 0, firstFreeElement * floatStride)); - - const nextFreeElement = firstFreeElement + 1; - const freeEndOffset = (nextFreeElement + increaseCount) * floatStride; - boundsArray.set(new Float32Array(lastBoundsArray.buffer, nextFreeElement * floatStride * 4), freeEndOffset); - - const firstActiveElement = this._firstActiveTransformedBoundingBox; - if (firstActiveElement > firstFreeElement) { - this._firstActiveTransformedBoundingBox += increaseCount; - } - } - - this._transformedBoundsArray = boundsArray; - } - - private _retireTransformedBounds(): void { - const { boundsFloatStride, boundsTimeOffset, boundsMaxLifetimeOffset } = ParticleBufferUtils; - const boundsArray = this._transformedBoundsArray; - const firstFreeElement = this._firstFreeTransformedBoundingBox; - const count = this._transformedBoundsCount; - - while (this._firstActiveTransformedBoundingBox !== firstFreeElement) { - const index = this._firstActiveTransformedBoundingBox * boundsFloatStride; - const age = this._playTime - boundsArray[index + boundsTimeOffset]; - if (age <= boundsArray[index + boundsMaxLifetimeOffset]) { - break; - } - - if (++this._firstActiveTransformedBoundingBox >= count) { - this._firstActiveTransformedBoundingBox = 0; - } - this._renderer._onWorldVolumeChanged(); - } - } - - private _calculateGeneratorBounds(maxLifetime: number, bounds: BoundingBox): void { - const { _tempVector30: directionMax, _tempVector31: directionMin, _tempVector20: speedMinMax } = ParticleGenerator; - const { min, max } = bounds; - const { main } = this; - - // StartSpeed's impact - const { shape } = this.emission; - if (shape?.enabled) { - shape._getPositionRange(min, max); - shape._getDirectionRange(directionMin, directionMax); - } else { - min.set(0, 0, 0); - max.set(0, 0, 0); - directionMin.set(0, 0, -1); - directionMax.set(0, 0, 0); - } - this._getExtremeValueFromZero(main.startSpeed, speedMinMax); - - const { x: speedMin, y: speedMax } = speedMinMax; - const { x: dirMinX, y: dirMinY, z: dirMinZ } = directionMin; - const { x: dirMaxX, y: dirMaxY, z: dirMaxZ } = directionMax; - - min.set( - min.x + Math.min(dirMinX * speedMax, dirMaxX * speedMin) * maxLifetime, - min.y + Math.min(dirMinY * speedMax, dirMaxY * speedMin) * maxLifetime, - min.z + Math.min(dirMinZ * speedMax, dirMaxZ * speedMin) * maxLifetime - ); - - max.set( - max.x + Math.max(dirMinX * speedMin, dirMaxX * speedMax) * maxLifetime, - max.y + Math.max(dirMinY * speedMin, dirMaxY * speedMax) * maxLifetime, - max.z + Math.max(dirMinZ * speedMin, dirMaxZ * speedMax) * maxLifetime - ); - - // StartSize's impact - let maxSize = main.startSize._getMax(); - - if (main.startSize3D) { - const startSizeYMax = main.startSizeY._getMax(); - if ( - this._renderer.renderMode === ParticleRenderMode.Billboard || - ParticleRenderMode.StretchBillboard || - ParticleRenderMode.HorizontalBillboard - ) { - maxSize = Math.max(maxSize, startSizeYMax); - } else { - const startSizeZMax = main.startSizeZ._getMax(); - maxSize = Math.max(maxSize, startSizeYMax, startSizeZMax); - } - } - - // Use diagonal for potential rotation - maxSize *= 1.414; - - // SizeOverLifetime impact - const { sizeOverLifetime } = this; - if (sizeOverLifetime.enabled) { - let maxSizeOverLifetime = sizeOverLifetime.size._getMax(); - if (sizeOverLifetime.separateAxes) { - const maxSizeOverLifetimeY = sizeOverLifetime.sizeY._getMax(); - const maxSizeOverLifetimeZ = sizeOverLifetime.sizeZ._getMax(); - maxSizeOverLifetime = Math.max(maxSizeOverLifetime, maxSizeOverLifetimeY, maxSizeOverLifetimeZ); - } - - maxSize *= maxSizeOverLifetime; - } - - min.set(min.x - maxSize, min.y - maxSize, min.z - maxSize); - max.set(max.x + maxSize, max.y + maxSize, max.z + maxSize); - } - - private _mergeTransformedBounds(index: number, bounds: BoundingBox): void { - const { min, max } = bounds; - const boundsArray = this._transformedBoundsArray; - - const offset = index * ParticleBufferUtils.boundsFloatStride; - - min.set( - Math.min(min.x, boundsArray[offset]), - Math.min(min.y, boundsArray[offset + 1]), - Math.min(min.z, boundsArray[offset + 2]) - ); - - max.set( - Math.max(max.x, boundsArray[offset + 3]), - Math.max(max.y, boundsArray[offset + 4]), - Math.max(max.z, boundsArray[offset + 5]) - ); - } - - private _calculateTransformedBounds(maxLifetime: number, origin: BoundingBox, out: BoundingBox): void { - const { - _tempVector20: velMinMaxX, - _tempVector21: velMinMaxY, - _tempVector22: velMinMaxZ, - _tempMat: rotateMat - } = ParticleGenerator; - const { transform } = this._renderer.entity; - const worldPosition = transform.worldPosition; - Matrix.rotationQuaternion(transform.worldRotationQuaternion, rotateMat); - - const { min: originMin, max: originMax } = origin; - const { min, max } = out; - - const { velocityOverLifetime } = this; - if (velocityOverLifetime.enabled) { - this._getExtremeValueFromZero(velocityOverLifetime.velocityX, velMinMaxX); - this._getExtremeValueFromZero(velocityOverLifetime.velocityY, velMinMaxY); - this._getExtremeValueFromZero(velocityOverLifetime.velocityZ, velMinMaxZ); - - if (velocityOverLifetime.space === ParticleSimulationSpace.Local) { - min.set( - originMin.x + velMinMaxX.x * maxLifetime, - originMin.y + velMinMaxY.x * maxLifetime, - originMin.z + velMinMaxZ.x * maxLifetime - ); - max.set( - originMax.x + velMinMaxX.y * maxLifetime, - originMax.y + velMinMaxY.y * maxLifetime, - originMax.z + velMinMaxZ.y * maxLifetime - ); - - out.transform(rotateMat); - } else { - out.transform(rotateMat); - - min.set( - originMin.x + velMinMaxX.x * maxLifetime, - originMin.y + velMinMaxY.x * maxLifetime, - originMin.z + velMinMaxZ.x * maxLifetime - ); - max.set( - originMax.x + velMinMaxX.y * maxLifetime, - originMax.y + velMinMaxY.y * maxLifetime, - originMax.z + velMinMaxZ.y * maxLifetime - ); - } - } else { - BoundingBox.transform(origin, rotateMat, out); - } - - min.add(worldPosition); - max.add(worldPosition); - } - - private _addGravityToBounds(maxLifetime: number, origin: BoundingBox, out: BoundingBox): void { - const { min: originMin, max: originMax } = origin; - const modifierMinMax = ParticleGenerator._tempVector20; - - // Gravity modifier impact - this._getExtremeValueFromZero(this.main.gravityModifier, modifierMinMax); - const { x, y, z } = this._renderer.scene.physics.gravity; - - const coefficient = 0.5 * maxLifetime * maxLifetime; - const minGravityEffect = modifierMinMax.x * coefficient; - const maxGravityEffect = modifierMinMax.y * coefficient; - - const gravityEffectMinX = x * minGravityEffect; - const gravityEffectMaxX = x * maxGravityEffect; - - const gravityEffectMinY = y * minGravityEffect; - const gravityEffectMaxY = y * maxGravityEffect; - - const gravityEffectMinZ = z * minGravityEffect; - const gravityEffectMaxZ = z * maxGravityEffect; - - out.min.set( - Math.min(gravityEffectMinX, gravityEffectMaxX) + originMin.x, - Math.min(gravityEffectMinY, gravityEffectMaxY) + originMin.y, - Math.min(gravityEffectMinZ, gravityEffectMaxZ) + originMin.z - ); - - out.max.set( - Math.max(gravityEffectMinX, gravityEffectMaxX) + originMax.x, - Math.max(gravityEffectMinY, gravityEffectMaxY) + originMax.y, - Math.max(gravityEffectMinZ, gravityEffectMaxZ) + originMax.z - ); - } - - private _getExtremeValueFromZero(curve: ParticleCompositeCurve, out: Vector2): void { - curve._getMinMax(out); - out.x = Math.min(0, out.x); - out.y = Math.max(0, out.y); - } } diff --git a/packages/core/src/particle/ParticleRenderer.ts b/packages/core/src/particle/ParticleRenderer.ts index 3bab170ac..ba3971a54 100644 --- a/packages/core/src/particle/ParticleRenderer.ts +++ b/packages/core/src/particle/ParticleRenderer.ts @@ -1,17 +1,15 @@ -import { Vector3, BoundingBox } from "@galacean/engine-math"; +import { BoundingBox, Vector3 } from "@galacean/engine-math"; import { Entity } from "../Entity"; import { RenderContext } from "../RenderPipeline/RenderContext"; import { Renderer, RendererUpdateFlags } from "../Renderer"; import { GLCapabilityType } from "../base/Constant"; -import { deepClone, ignoreClone, shallowClone } from "../clone/CloneManager"; +import { deepClone, shallowClone } from "../clone/CloneManager"; import { ModelMesh } from "../mesh/ModelMesh"; import { ShaderMacro } from "../shader/ShaderMacro"; import { ShaderProperty } from "../shader/ShaderProperty"; +import { ParticleGenerator } from "./ParticleGenerator"; import { ParticleRenderMode } from "./enums/ParticleRenderMode"; import { ParticleStopMode } from "./enums/ParticleStopMode"; -import { ParticleGenerator } from "./ParticleGenerator"; -import { ParticleSimulationSpace } from "./enums/ParticleSimulationSpace"; -import { TransformModifyFlags } from "../Transform"; /** * Particle Renderer Component. @@ -30,7 +28,7 @@ export class ParticleRenderer extends Renderer { /** Particle generator. */ @deepClone - readonly generator: ParticleGenerator; + readonly generator = new ParticleGenerator(this); /** Specifies how much particles stretch depending on their velocity. */ velocityScale = 0; /** How much are the particles stretched in their direction of motion, defined as the length of the particle compared to its width. */ @@ -39,13 +37,6 @@ export class ParticleRenderer extends Renderer { @shallowClone pivot = new Vector3(); - /** @internal */ - @ignoreClone - _generatorBounds = new BoundingBox(); - /** @internal */ - @ignoreClone - _transformedBounds = new BoundingBox(); - private _renderMode: ParticleRenderMode; private _currentRenderModeMacro: ShaderMacro; private _mesh: ModelMesh; @@ -124,15 +115,12 @@ export class ParticleRenderer extends Renderer { */ constructor(entity: Entity) { super(entity); - this._onGeneratorParamsChanged = this._onGeneratorParamsChanged.bind(this); - this.generator = new ParticleGenerator(this); this._currentRenderModeMacro = ParticleRenderer._billboardModeMacro; this.shaderData.enableMacro(ParticleRenderer._billboardModeMacro); this._supportInstancedArrays = this.engine._hardwareRenderer.canIUse(GLCapabilityType.instancedArrays); - - this._onGeneratorParamsChanged(); + this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume; } /** @@ -174,26 +162,8 @@ export class ParticleRenderer extends Renderer { * @internal */ protected override _updateBounds(worldBounds: BoundingBox): void { - const { generator } = this; - - // Using `isAlive` instead of `firstActiveElement !== firstFreeElement` - // Because `firstActiveElement !== firstFreeElement` will cause bounds is merely a point, and cannot be culled forever - // Must generate bounds even when there is no particle but in play state - if (!generator.isAlive) { - const worldPosition = this.entity.transform.worldPosition; - worldBounds.min.copyFrom(worldPosition); - worldBounds.max.copyFrom(worldPosition); - return; - } - if (generator.main.simulationSpace === ParticleSimulationSpace.Local) { - generator._updateBoundsSimulationLocal(worldBounds); - } else { - if (this._isContainDirtyFlag(ParticleUpdateFlags.TransformVolume)) { - generator._generateTransformedBounds(); - this._setDirtyFlagFalse(ParticleUpdateFlags.TransformVolume); - } - generator._updateBoundsSimulationWorld(worldBounds); - } + worldBounds.min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + worldBounds.max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); } /** @@ -235,51 +205,4 @@ export class ParticleRenderer extends Renderer { } this.generator._destroy(); } - - /** - * @internal - */ - _isContainDirtyFlag(type: number): boolean { - return (this._dirtyUpdateFlag & type) != 0; - } - - /** - * @internal - */ - _setDirtyFlagFalse(type: number): void { - this._dirtyUpdateFlag &= ~type; - } - - /** - * @internal - */ - _onWorldVolumeChanged(): void { - this._dirtyUpdateFlag |= RendererUpdateFlags.WorldVolume; - } - - /** - * @internal - */ - @ignoreClone - _onGeneratorParamsChanged(): void { - this._dirtyUpdateFlag |= - ParticleUpdateFlags.GeneratorVolume | ParticleUpdateFlags.TransformVolume | RendererUpdateFlags.WorldVolume; - } - - /** - * @internal - */ - override _onTransformChanged(type: TransformModifyFlags): void { - this._dirtyUpdateFlag |= ParticleUpdateFlags.TransformVolume | RendererUpdateFlags.WorldVolume; - } -} - -/** - * @internal - */ -export enum ParticleUpdateFlags { - /** On World Transform Changed */ - TransformVolume = 0x2, - /** On Generator Bounds Related Params Changed */ - GeneratorVolume = 0x4 } diff --git a/packages/core/src/particle/modules/EmissionModule.ts b/packages/core/src/particle/modules/EmissionModule.ts index d90722b08..559052709 100644 --- a/packages/core/src/particle/modules/EmissionModule.ts +++ b/packages/core/src/particle/modules/EmissionModule.ts @@ -16,9 +16,10 @@ export class EmissionModule extends ParticleGeneratorModule { /** The rate at which the emitter spawns new particles over distance. */ @deepClone rateOverDistance: ParticleCompositeCurve = new ParticleCompositeCurve(0); - + /** The shape of the emitter. */ @deepClone - _shape: BaseShape; + shape: BaseShape; + /** @internal */ @ignoreClone _shapeRand = new Rand(0, ParticleRandomSubSeeds.Shape); @@ -33,26 +34,6 @@ export class EmissionModule extends ParticleGeneratorModule { @ignoreClone private _burstRand: Rand = new Rand(0, ParticleRandomSubSeeds.Burst); - /** - * The shape of the emitter. - */ - get shape() { - return this._shape; - } - - set shape(value: BaseShape) { - const lastShape = this._shape; - if (value !== lastShape) { - this._shape = value; - - const renderer = this._generator._renderer; - lastShape?._unRegisterOnValueChanged(renderer._onGeneratorParamsChanged); - value?._registerOnValueChanged(renderer._onGeneratorParamsChanged); - - renderer._onGeneratorParamsChanged(); - } - } - /** * Gets the burst array. */ @@ -121,13 +102,6 @@ export class EmissionModule extends ParticleGeneratorModule { this._currentBurstIndex = 0; } - /** - * @internal - */ - _destroy(): void { - this._shape?._unRegisterOnValueChanged(this._generator._renderer._onGeneratorParamsChanged); - } - private _emitByRateOverTime(playTime: number): void { const ratePerSeconds = this.rateOverTime.evaluate(undefined, undefined); if (ratePerSeconds > 0) { diff --git a/packages/core/src/particle/modules/MainModule.ts b/packages/core/src/particle/modules/MainModule.ts index 0349aec31..65a69019d 100644 --- a/packages/core/src/particle/modules/MainModule.ts +++ b/packages/core/src/particle/modules/MainModule.ts @@ -9,7 +9,6 @@ import { ParticleScaleMode } from "../enums/ParticleScaleMode"; import { ParticleSimulationSpace } from "../enums/ParticleSimulationSpace"; import { ParticleCompositeCurve } from "./ParticleCompositeCurve"; import { ParticleCompositeGradient } from "./ParticleCompositeGradient"; -import { TransformModifyFlags } from "../../Transform"; export class MainModule implements ICustomClone { private static _tempVector40 = new Vector4(); @@ -32,6 +31,24 @@ export class MainModule implements ICustomClone { /** Start delay in seconds. */ @deepClone startDelay = new ParticleCompositeCurve(0); + /** The initial lifetime of particles when emitted. */ + @deepClone + startLifetime = new ParticleCompositeCurve(5); + /** The initial speed of particles when the Particle Generator first spawns them. */ + @deepClone + startSpeed = new ParticleCompositeCurve(5); + + /** A flag to enable specifying particle size individually for each axis. */ + startSize3D = false; + /** The initial size of particles along the x-axis when the Particle Generator first spawns them. */ + @deepClone + startSizeX = new ParticleCompositeCurve(1); + /** The initial size of particles along the y-axis when the Particle Generator first spawns them. */ + @deepClone + startSizeY = new ParticleCompositeCurve(1); + /** The initial size of particles along the z-axis when the Particle Generator first spawns them. */ + @deepClone + startSizeZ = new ParticleCompositeCurve(1); /** A flag to enable 3D particle rotation, when disabled, only `startRotationZ` is used. */ startRotation3D = false; @@ -51,6 +68,10 @@ export class MainModule implements ICustomClone { @deepClone startColor = new ParticleCompositeGradient(new Color(1, 1, 1, 1)); /** A scale that this Particle Generator applies to gravity, defined by Physics.gravity. */ + @deepClone + gravityModifier = new ParticleCompositeCurve(0); + /** This selects the space in which to simulate particles. It can be either world or local space. */ + simulationSpace = ParticleSimulationSpace.Local; /** Override the default playback speed of the Particle Generator. */ simulationSpeed = 1.0; /** Control how the Particle Generator applies its Transform component to the particles it emits. */ @@ -80,149 +101,11 @@ export class MainModule implements ICustomClone { @ignoreClone readonly _gravityModifierRand = new Rand(0, ParticleRandomSubSeeds.GravityModifier); - @deepClone - private _startLifetime: ParticleCompositeCurve; - @deepClone - private _startSpeed: ParticleCompositeCurve; - private _startSize3D = false; - @deepClone - private _startSizeX: ParticleCompositeCurve; - @deepClone - private _startSizeY: ParticleCompositeCurve; - @deepClone - private _startSizeZ: ParticleCompositeCurve; - @deepClone - private _gravityModifier: ParticleCompositeCurve; - private _simulationSpace = ParticleSimulationSpace.Local; @ignoreClone private _generator: ParticleGenerator; @ignoreClone private _gravity = new Vector3(); - /** - * The initial lifetime of particles when emitted. - */ - get startLifetime(): ParticleCompositeCurve { - return this._startLifetime; - } - - set startLifetime(value: ParticleCompositeCurve) { - const lastValue = this._startLifetime; - if (value !== lastValue) { - this._startLifetime = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * The initial speed of particles when the Particle Generator first spawns them. - */ - get startSpeed(): ParticleCompositeCurve { - return this._startSpeed; - } - - set startSpeed(value: ParticleCompositeCurve) { - const lastValue = this._startSpeed; - if (value !== lastValue) { - this._startSpeed = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * A flag to enable specifying particle size individually for each axis. - */ - get startSize3D(): boolean { - return this._startSize3D; - } - - set startSize3D(value: boolean) { - if (value !== this._startSize3D) { - this._startSize3D = value; - this._generator._renderer._onGeneratorParamsChanged(); - } - } - - /** - * The initial size of particles along the x-axis when the Particle Generator first spawns them. - */ - get startSizeX(): ParticleCompositeCurve { - return this._startSizeX; - } - - set startSizeX(value: ParticleCompositeCurve) { - const lastValue = this._startSizeX; - if (value !== lastValue) { - this._startSizeX = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * The initial size of particles along the y-axis when the Particle Generator first spawns them. - */ - get startSizeY(): ParticleCompositeCurve { - return this._startSizeY; - } - - set startSizeY(value: ParticleCompositeCurve) { - const lastValue = this._startSizeY; - if (value !== lastValue) { - this._startSizeY = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * The initial size of particles along the z-axis when the Particle Generator first spawns them. - */ - get startSizeZ(): ParticleCompositeCurve { - return this._startSizeZ; - } - - set startSizeZ(value: ParticleCompositeCurve) { - const lastValue = this._startSizeZ; - if (value !== lastValue) { - this._startSizeZ = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * A scale that this Particle Generator applies to gravity, defined by Physics.gravity. - */ - get gravityModifier(): ParticleCompositeCurve { - return this._gravityModifier; - } - - set gravityModifier(value: ParticleCompositeCurve) { - const lastValue = this._gravityModifier; - if (value !== lastValue) { - this._gravityModifier = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * This selects the space in which to simulate particles. It can be either world or local space. - */ - get simulationSpace(): ParticleSimulationSpace { - return this._simulationSpace; - } - - set simulationSpace(value: ParticleSimulationSpace) { - if (value !== this._simulationSpace) { - this._simulationSpace = value; - - const generator = this._generator; - generator._renderer._onTransformChanged(TransformModifyFlags.WorldMatrix); - - if (value === ParticleSimulationSpace.Local) { - generator._freeBoundsArray(); - } - } - } - /** * Max particles count. */ @@ -250,13 +133,6 @@ export class MainModule implements ICustomClone { */ constructor(generator: ParticleGenerator) { this._generator = generator; - - this.startLifetime = new ParticleCompositeCurve(5); - this.startSpeed = new ParticleCompositeCurve(5); - this.startSizeX = new ParticleCompositeCurve(1); - this.startSizeY = new ParticleCompositeCurve(1); - this.startSizeZ = new ParticleCompositeCurve(1); - this.gravityModifier = new ParticleCompositeCurve(0); } /** @@ -337,16 +213,5 @@ export class MainModule implements ICustomClone { */ _cloneTo(target: MainModule): void { target.maxParticles = this.maxParticles; - - if (target._simulationSpace === ParticleSimulationSpace.World) { - target._generator._generateTransformedBounds(); - } - } - - private _onCompositeCurveChange(lastValue: ParticleCompositeCurve, value: ParticleCompositeCurve): void { - const renderer = this._generator._renderer; - lastValue?._unRegisterOnValueChanged(renderer._onGeneratorParamsChanged); - value?._registerOnValueChanged(renderer._onGeneratorParamsChanged); - renderer._onGeneratorParamsChanged(); } } diff --git a/packages/core/src/particle/modules/ParticleCompositeCurve.ts b/packages/core/src/particle/modules/ParticleCompositeCurve.ts index a4ad00215..0fd65f9c1 100644 --- a/packages/core/src/particle/modules/ParticleCompositeCurve.ts +++ b/packages/core/src/particle/modules/ParticleCompositeCurve.ts @@ -1,95 +1,23 @@ -import { Vector2 } from "@galacean/engine-math"; -import { deepClone, ignoreClone } from "../../clone/CloneManager"; +import { deepClone } from "../../clone/CloneManager"; import { ParticleCurveMode } from "../enums/ParticleCurveMode"; -import { CurveKey, ParticleCurve } from "./ParticleCurve"; -import { UpdateFlagManager } from "../../UpdateFlagManager"; +import { ParticleCurve } from "./ParticleCurve"; /** * Particle composite curve. */ export class ParticleCompositeCurve { - @ignoreClone - private _updateManager = new UpdateFlagManager(); - private _mode = ParticleCurveMode.Constant; - private _constantMin = 0; - private _constantMax = 0; + /** The curve mode. */ + mode: ParticleCurveMode = ParticleCurveMode.Constant; + /** The min constant value used by the curve if mode is set to `TwoConstants`.*/ + constantMin: number = 0; + /** The max constant value used by the curve if mode is set to `TwoConstants`.*/ + constantMax: number = 0; + /** The min curve used by the curve if mode is set to `TwoCurves`. */ @deepClone - private _curveMin: ParticleCurve; + curveMin: ParticleCurve; + /** The max curve used by the curve if mode is set to `TwoCurves`. */ @deepClone - private _curveMax: ParticleCurve; - @ignoreClone - private _updateDispatch: () => void; - - /** - * The curve mode. - */ - get mode(): ParticleCurveMode { - return this._mode; - } - set mode(value: ParticleCurveMode) { - if (value !== this._mode) { - this._mode = value; - this._updateDispatch(); - } - } - - /** - * The min constant value used by the curve if mode is set to `TwoConstants`. - */ - get constantMin(): number { - return this._constantMin; - } - - set constantMin(value: number) { - if (value !== this._constantMin) { - this._constantMin = value; - this._updateDispatch(); - } - } - - /** - * The max constant value used by the curve if mode is set to `TwoConstants`. - */ - get constantMax(): number { - return this._constantMax; - } - - set constantMax(value: number) { - if (value !== this._constantMax) { - this._constantMax = value; - this._updateDispatch(); - } - } - - /** - * The min curve used by the curve if mode is set to `TwoCurves`. - */ - get curveMin(): ParticleCurve { - return this._curveMin; - } - - set curveMin(value: ParticleCurve) { - const lastCurve = this._curveMin; - if (value !== lastCurve) { - this._curveMin = value; - this._onCurveChange(lastCurve, value); - } - } - - /** - * The max curve used by the curve if mode is set to `TwoCurves`. - */ - get curveMax(): ParticleCurve { - return this._curveMax; - } - - set curveMax(value: ParticleCurve) { - const lastCurve = this._curveMax; - if (value !== lastCurve) { - this._curveMax = value; - this._onCurveChange(lastCurve, value); - } - } + curveMax: ParticleCurve; /** * The constant value used by the curve if mode is set to `Constant`. @@ -140,7 +68,6 @@ export class ParticleCompositeCurve { constructor(curveMin: ParticleCurve, curveMax: ParticleCurve); constructor(constantOrCurve: number | ParticleCurve, constantMaxOrCurveMax?: number | ParticleCurve) { - this._updateDispatch = this._updateManager.dispatch.bind(this._updateManager); if (typeof constantOrCurve === "number") { if (constantMaxOrCurveMax) { this.constantMin = constantOrCurve; @@ -179,99 +106,4 @@ export class ParticleCompositeCurve { break; } } - - /** - * @internal - */ - _getMax(): number { - switch (this.mode) { - case ParticleCurveMode.Constant: - return this.constantMax; - case ParticleCurveMode.TwoConstants: - return Math.max(this.constantMin, this.constantMax); - case ParticleCurveMode.Curve: - return this._getMaxKeyValue(this.curveMax?.keys); - case ParticleCurveMode.TwoCurves: - const min = this._getMaxKeyValue(this.curveMin?.keys); - const max = this._getMaxKeyValue(this.curveMax?.keys); - return min > max ? min : max; - } - } - - /** - * @internal - - */ - _getMinMax(out: Vector2): void { - switch (this.mode) { - case ParticleCurveMode.Constant: - out.x = out.y = this.constantMax; - break; - case ParticleCurveMode.TwoConstants: - out.set(Math.min(this.constantMin, this.constantMax), Math.max(this.constantMin, this.constantMax)); - break; - case ParticleCurveMode.Curve: - out.set(this._getMinKeyValue(this.curveMax?.keys), this._getMaxKeyValue(this.curveMax?.keys)); - break; - case ParticleCurveMode.TwoCurves: - const minCurveMax = this._getMinKeyValue(this.curveMax?.keys); - const minCurveMin = this._getMinKeyValue(this.curveMin?.keys); - - const maxCurveMax = this._getMaxKeyValue(this.curveMax?.keys); - const maxCurveMin = this._getMaxKeyValue(this.curveMin?.keys); - - const min = minCurveMax < minCurveMin ? minCurveMax : minCurveMin; - const max = maxCurveMax > maxCurveMin ? maxCurveMax : maxCurveMin; - - out.set(min, max); - break; - } - } - - /** - * @internal - */ - _registerOnValueChanged(listener: () => void): void { - this._updateManager.addListener(listener); - } - - /** - * @internal - */ - _unRegisterOnValueChanged(listener: () => void): void { - this._updateManager.removeListener(listener); - } - - private _getMaxKeyValue(keys: ReadonlyArray): number { - let max = undefined; - const count = keys?.length ?? 0; - if (count > 0) { - max = keys[0].value; - for (let i = 1; i < count; i++) { - const value = keys[i].value; - max = Math.max(max, value); - } - } - return max; - } - - private _getMinKeyValue(keys: ReadonlyArray): number { - let min = undefined; - const count = keys?.length ?? 0; - if (count > 0) { - min = keys[0].value; - for (let i = 1; i < count; i++) { - const value = keys[i].value; - min = Math.min(min, value); - } - } - return min; - } - - private _onCurveChange(lastValue: ParticleCurve, value: ParticleCurve) { - const dispatch = this._updateDispatch; - lastValue?._unRegisterOnValueChanged(dispatch); - value?._registerOnValueChanged(dispatch); - dispatch(); - } } diff --git a/packages/core/src/particle/modules/ParticleCurve.ts b/packages/core/src/particle/modules/ParticleCurve.ts index 7a2989b85..2a4066a20 100644 --- a/packages/core/src/particle/modules/ParticleCurve.ts +++ b/packages/core/src/particle/modules/ParticleCurve.ts @@ -1,19 +1,14 @@ -import { UpdateFlagManager } from "../../UpdateFlagManager"; import { deepClone, ignoreClone } from "../../clone/CloneManager"; /** * Particle curve. */ export class ParticleCurve { - @ignoreClone - private _updateManager = new UpdateFlagManager(); @deepClone - private _keys = new Array(); + private _keys: CurveKey[] = []; @ignoreClone private _typeArray: Float32Array; - private _typeArrayDirty = false; - @ignoreClone - private _updateDispatch: () => void; + private _typeArrayDirty: boolean = false; /** * The keys of the curve. @@ -27,8 +22,6 @@ export class ParticleCurve { * @param keys - The keys of the curve */ constructor(...keys: CurveKey[]) { - this._updateDispatch = this._updateManager.dispatch.bind(this._updateManager); - for (let i = 0, n = keys.length; i < n; i++) { const key = keys[i]; this.addKey(key); @@ -57,8 +50,6 @@ export class ParticleCurve { const key = typeof timeOrKey === "number" ? new CurveKey(timeOrKey, value) : timeOrKey; this._addKey(keys, key); - key._registerOnValueChanged(this._updateDispatch); - this._updateDispatch(); this._typeArrayDirty = true; } @@ -69,9 +60,6 @@ export class ParticleCurve { removeKey(index: number): void { this._keys.splice(index, 1); this._typeArrayDirty = true; - const removeKey = this._keys[index]; - removeKey._unRegisterOnValueChanged(this._updateDispatch); - this._updateDispatch(); } /** @@ -104,20 +92,6 @@ export class ParticleCurve { return typeArray; } - /** - * @internal - */ - _registerOnValueChanged(listener: () => void): void { - this._updateManager.addListener(listener); - } - - /** - * @internal - */ - _unRegisterOnValueChanged(listener: () => void): void { - this._updateManager.removeListener(listener); - } - private _addKey(keys: CurveKey[], key: CurveKey): void { const count = keys.length; const time = key.time; @@ -136,58 +110,13 @@ export class ParticleCurve { * The key of the curve. */ export class CurveKey { - @ignoreClone - private _updateManager = new UpdateFlagManager(); - private _time: number; - private _value: number; - - /** - * The key time. - */ - get time(): number { - return this._time; - } - - set time(value: number) { - if (value !== this._time) { - this._time = value; - this._updateManager.dispatch(); - } - } - - /** - * The key value. - */ - get value(): number { - return this._value; - } - - set value(value: number) { - if (value !== this._value) { - this._value = value; - this._updateManager.dispatch(); - } - } - /** * Create a new key. */ - constructor(time: number, value: number) { - this._time = time; - this._value = value; - } - - /** - * @internal - */ - _registerOnValueChanged(listener: () => void): void { - this._updateManager.addListener(listener); - } - - /** - * @internal - */ - _unRegisterOnValueChanged(listener: () => void): void { - this._updateManager.removeListener(listener); - } + constructor( + /** The key time. */ + public time: number, + /** The key value. */ + public value: number + ) {} } diff --git a/packages/core/src/particle/modules/ParticleGeneratorModule.ts b/packages/core/src/particle/modules/ParticleGeneratorModule.ts index bf2b5b8e7..bc82764e1 100644 --- a/packages/core/src/particle/modules/ParticleGeneratorModule.ts +++ b/packages/core/src/particle/modules/ParticleGeneratorModule.ts @@ -6,22 +6,12 @@ import { ParticleGenerator } from "../ParticleGenerator"; * Particle generator module. */ export abstract class ParticleGeneratorModule { + /** Specifies whether the module is enabled or not. */ + enabled: boolean = false; + @ignoreClone protected _generator: ParticleGenerator; - protected _enabled: boolean = false; - - /** - * Specifies whether the module is enabled or not. - */ - get enabled(): boolean { - return this._enabled; - } - - set enabled(value: boolean) { - this._enabled = value; - } - /** * @internal */ diff --git a/packages/core/src/particle/modules/ParticleGradient.ts b/packages/core/src/particle/modules/ParticleGradient.ts index 03c055c3d..db53b87b7 100644 --- a/packages/core/src/particle/modules/ParticleGradient.ts +++ b/packages/core/src/particle/modules/ParticleGradient.ts @@ -215,12 +215,10 @@ export class ParticleGradient { keys.splice(index, 1); } - @ignoreClone private _setColorTypeArrayDirty(): void { this._colorTypeArrayDirty = true; } - @ignoreClone private _setAlphaTypeArrayDirty(): void { this._alphaTypeArrayDirty = true; } diff --git a/packages/core/src/particle/modules/SizeOverLifetimeModule.ts b/packages/core/src/particle/modules/SizeOverLifetimeModule.ts index 426bb3a30..3c80e938c 100644 --- a/packages/core/src/particle/modules/SizeOverLifetimeModule.ts +++ b/packages/core/src/particle/modules/SizeOverLifetimeModule.ts @@ -2,7 +2,6 @@ import { deepClone, ignoreClone } from "../../clone/CloneManager"; import { ShaderData } from "../../shader/ShaderData"; import { ShaderMacro } from "../../shader/ShaderMacro"; import { ShaderProperty } from "../../shader/ShaderProperty"; -import { ParticleGenerator } from "../ParticleGenerator"; import { ParticleCurveMode } from "../enums/ParticleCurveMode"; import { ParticleCompositeCurve } from "./ParticleCompositeCurve"; import { CurveKey, ParticleCurve } from "./ParticleCurve"; @@ -23,13 +22,17 @@ export class SizeOverLifetimeModule extends ParticleGeneratorModule { static readonly _maxCurveYProperty = ShaderProperty.getByName("renderer_SOLMaxCurveY"); static readonly _maxCurveZProperty = ShaderProperty.getByName("renderer_SOLMaxCurveZ"); - private _separateAxes = false; + /** Specifies whether the Size is separate on each axis. */ + separateAxes = false; + /** Size curve over lifetime for x axis. */ @deepClone - private _sizeX: ParticleCompositeCurve; + sizeX = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); + /** Size curve over lifetime for y axis. */ @deepClone - private _sizeY: ParticleCompositeCurve; + sizeY = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); + /** Size curve over lifetime for z axis. */ @deepClone - private _sizeZ: ParticleCompositeCurve; + sizeZ = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); @ignoreClone private _enableSeparateMacro: ShaderMacro; @@ -38,65 +41,6 @@ export class SizeOverLifetimeModule extends ParticleGeneratorModule { @ignoreClone private _isRandomTwoMacro: ShaderMacro; - /** - * Specifies whether the Size is separate on each axis. - */ - set separateAxes(value: boolean) { - if (value !== this._separateAxes) { - this._separateAxes = value; - this._generator._renderer._onGeneratorParamsChanged(); - } - } - - get separateAxes(): boolean { - return this._separateAxes; - } - - /** - * Size curve over lifetime for x axis. - */ - get sizeX(): ParticleCompositeCurve { - return this._sizeX; - } - - set sizeX(value: ParticleCompositeCurve) { - const lastValue = this._sizeX; - if (value !== lastValue) { - this._sizeX = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * Size curve over lifetime for y axis. - */ - get sizeY(): ParticleCompositeCurve { - return this._sizeY; - } - - set sizeY(value: ParticleCompositeCurve) { - const lastValue = this._sizeY; - if (value !== lastValue) { - this._sizeY = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * Size curve over lifetime for z axis. - */ - get sizeZ(): ParticleCompositeCurve { - return this._sizeZ; - } - - set sizeZ(value: ParticleCompositeCurve) { - const lastValue = this._sizeZ; - if (value !== lastValue) { - this._sizeZ = value; - this._onCompositeCurveChange(lastValue, value); - } - } - /** * Size curve over lifetime. */ @@ -108,14 +52,6 @@ export class SizeOverLifetimeModule extends ParticleGeneratorModule { this.sizeX = value; } - constructor(generator: ParticleGenerator) { - super(generator); - - this.sizeX = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); - this.sizeY = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); - this.sizeZ = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0), new CurveKey(1, 1))); - } - /** * @internal */ @@ -169,11 +105,4 @@ export class SizeOverLifetimeModule extends ParticleGeneratorModule { this._isCurveMacro = this._enableMacro(shaderData, this._isCurveMacro, isCurveMacro); this._isRandomTwoMacro = this._enableMacro(shaderData, this._isRandomTwoMacro, isRandomTwoMacro); } - - private _onCompositeCurveChange(lastValue: ParticleCompositeCurve, value: ParticleCompositeCurve): void { - const renderer = this._generator._renderer; - lastValue?._unRegisterOnValueChanged(renderer._onGeneratorParamsChanged); - value?._registerOnValueChanged(renderer._onGeneratorParamsChanged); - renderer._onGeneratorParamsChanged(); - } } diff --git a/packages/core/src/particle/modules/VelocityOverLifetimeModule.ts b/packages/core/src/particle/modules/VelocityOverLifetimeModule.ts index 2ea958351..7e986b7dc 100644 --- a/packages/core/src/particle/modules/VelocityOverLifetimeModule.ts +++ b/packages/core/src/particle/modules/VelocityOverLifetimeModule.ts @@ -8,7 +8,6 @@ import { ParticleRandomSubSeeds } from "../enums/ParticleRandomSubSeeds"; import { ParticleSimulationSpace } from "../enums/ParticleSimulationSpace"; import { ParticleCompositeCurve } from "./ParticleCompositeCurve"; import { ParticleGeneratorModule } from "./ParticleGeneratorModule"; -import { ParticleGenerator } from "../ParticleGenerator"; /** * Velocity over lifetime module. @@ -29,6 +28,19 @@ export class VelocityOverLifetimeModule extends ParticleGeneratorModule { static readonly _maxGradientZProperty = ShaderProperty.getByName("renderer_VOLMaxGradientZ"); static readonly _spaceProperty = ShaderProperty.getByName("renderer_VOLSpace"); + /** Velocity over lifetime for x axis. */ + @deepClone + velocityX = new ParticleCompositeCurve(0); + /** Velocity over lifetime for z axis. */ + @deepClone + velocityY = new ParticleCompositeCurve(0); + /** Velocity over lifetime for z axis. */ + @deepClone + velocityZ = new ParticleCompositeCurve(0); + + /** Velocity space. */ + space = ParticleSimulationSpace.Local; + /** @internal */ @ignoreClone _velocityRand = new Rand(0, ParticleRandomSubSeeds.VelocityOverLifetime); @@ -40,92 +52,6 @@ export class VelocityOverLifetimeModule extends ParticleGeneratorModule { @ignoreClone private _velocityMacro: ShaderMacro; - @deepClone - private _velocityX: ParticleCompositeCurve; - @deepClone - private _velocityY: ParticleCompositeCurve; - @deepClone - private _velocityZ: ParticleCompositeCurve; - private _space = ParticleSimulationSpace.Local; - - /** - * Velocity over lifetime for x axis. - */ - get velocityX(): ParticleCompositeCurve { - return this._velocityX; - } - - set velocityX(value: ParticleCompositeCurve) { - const lastValue = this._velocityX; - if (value !== lastValue) { - this._velocityX = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * Velocity over lifetime for y axis. - */ - get velocityY(): ParticleCompositeCurve { - return this._velocityY; - } - - set velocityY(value: ParticleCompositeCurve) { - const lastValue = this._velocityY; - if (value !== lastValue) { - this._velocityY = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * Velocity over lifetime for z axis. - */ - get velocityZ(): ParticleCompositeCurve { - return this._velocityZ; - } - - set velocityZ(value: ParticleCompositeCurve) { - const lastValue = this._velocityZ; - if (value !== lastValue) { - this._velocityZ = value; - this._onCompositeCurveChange(lastValue, value); - } - } - - /** - * Velocity space. - */ - get space(): ParticleSimulationSpace { - return this._space; - } - - set space(value: ParticleSimulationSpace) { - if (value !== this._space) { - this._space = value; - this._generator._renderer._onGeneratorParamsChanged(); - } - } - - override get enabled(): boolean { - return this._enabled; - } - - override set enabled(value: boolean) { - if (value !== this._enabled) { - this._enabled = value; - this._generator._renderer._onGeneratorParamsChanged(); - } - } - - constructor(generator: ParticleGenerator) { - super(generator); - - this.velocityX = new ParticleCompositeCurve(0); - this.velocityY = new ParticleCompositeCurve(0); - this.velocityZ = new ParticleCompositeCurve(0); - } - /** * @internal */ @@ -196,11 +122,4 @@ export class VelocityOverLifetimeModule extends ParticleGeneratorModule { _resetRandomSeed(seed: number): void { this._velocityRand.reset(seed, ParticleRandomSubSeeds.VelocityOverLifetime); } - - private _onCompositeCurveChange(lastValue: ParticleCompositeCurve, value: ParticleCompositeCurve): void { - const renderer = this._generator._renderer; - lastValue?._unRegisterOnValueChanged(renderer._onGeneratorParamsChanged); - value?._registerOnValueChanged(renderer._onGeneratorParamsChanged); - renderer._onGeneratorParamsChanged(); - } } diff --git a/packages/core/src/particle/modules/shape/BaseShape.ts b/packages/core/src/particle/modules/shape/BaseShape.ts index 87347b3e0..abfb542fb 100644 --- a/packages/core/src/particle/modules/shape/BaseShape.ts +++ b/packages/core/src/particle/modules/shape/BaseShape.ts @@ -1,7 +1,5 @@ import { Rand, Vector3 } from "@galacean/engine-math"; import { ParticleShapeType } from "./enums/ParticleShapeType"; -import { UpdateFlagManager } from "../../../UpdateFlagManager"; -import { ignoreClone } from "../../../clone/CloneManager"; /** * Base class for all particle shapes. @@ -9,67 +7,15 @@ import { ignoreClone } from "../../../clone/CloneManager"; export abstract class BaseShape { /** The type of shape to emit particles from. */ abstract readonly shapeType: ParticleShapeType; - - @ignoreClone - protected _updateManager = new UpdateFlagManager(); - - private _enabled = true; - private _randomDirectionAmount = 0; - - /** - * Specifies whether the ShapeModule is enabled or disabled. - */ - get enabled(): boolean { - return this._enabled; - } - - set enabled(value: boolean) { - if (value !== this._enabled) { - this._enabled = value; - this._updateManager.dispatch(); - } - } - - /** - * Randomizes the starting direction of particles. - */ - get randomDirectionAmount(): number { - return this._randomDirectionAmount; - } - - set randomDirectionAmount(value: number) { - if (value !== this._randomDirectionAmount) { - this._randomDirectionAmount = value; - this._updateManager.dispatch(); - } - } + /** Specifies whether the ShapeModule is enabled or disabled. */ + enabled: boolean = true; + /** Randomizes the starting direction of particles. */ + randomDirectionAmount: number = 0; /** * @internal */ - _registerOnValueChanged(listener: () => void): void { - this._updateManager.addListener(listener); + _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + throw new Error("BaseShape: must override it."); } - - /** - * @internal - */ - _unRegisterOnValueChanged(listener: () => void): void { - this._updateManager.removeListener(listener); - } - - /** - * @internal - */ - abstract _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void; - - /** - * @internal - */ - abstract _getDirectionRange(outMin: Vector3, outMax: Vector3): void; - - /** - * @internal - */ - abstract _getPositionRange(outMin: Vector3, outMax: Vector3): void; } diff --git a/packages/core/src/particle/modules/shape/BoxShape.ts b/packages/core/src/particle/modules/shape/BoxShape.ts index 5d23f54c8..c55a0ace7 100644 --- a/packages/core/src/particle/modules/shape/BoxShape.ts +++ b/packages/core/src/particle/modules/shape/BoxShape.ts @@ -12,32 +12,14 @@ export class BoxShape extends BaseShape { readonly shapeType = ParticleShapeType.Box; + /** The size of the box. */ @deepClone - private _size = new Vector3(1, 1, 1); - - /** - * The size of the box. - */ - get size() { - return this._size; - } - - set size(value: Vector3) { - if (value !== this._size) { - this._size.copyFrom(value); - } - } - - constructor() { - super(); - // @ts-ignore - this._size._onValueChanged = this._updateManager.dispatch.bind(this._updateManager); - } + size = new Vector3(1, 1, 1); /** * @internal */ - _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + override _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { ShapeUtils._randomPointInsideHalfUnitBox(position, rand); position.multiply(this.size); @@ -46,30 +28,4 @@ export class BoxShape extends BaseShape { ShapeUtils._randomPointUnitSphere(direction, rand); Vector3.lerp(defaultDirection, direction, this.randomDirectionAmount, direction); } - - /** - * @internal - */ - _getDirectionRange(outMin: Vector3, outMax: Vector3): void { - const radian = Math.PI * this.randomDirectionAmount; - - if (this.randomDirectionAmount < 0.5) { - const dirSin = Math.sin(radian); - outMin.set(-dirSin, -dirSin, -1); - outMax.set(dirSin, dirSin, 0); - } else { - const dirCos = Math.cos(radian); - outMin.set(-1, -1, -1); - outMax.set(1, 1, -dirCos); - } - } - - /** - * @internal - */ - _getPositionRange(outMin: Vector3, outMax: Vector3): void { - const { x, y, z } = this._size; - outMin.set(-x * 0.5, -y * 0.5, -z * 0.5); - outMax.set(x * 0.5, y * 0.5, z * 0.5); - } } diff --git a/packages/core/src/particle/modules/shape/CircleShape.ts b/packages/core/src/particle/modules/shape/CircleShape.ts index 75e18e850..6210f7dc9 100644 --- a/packages/core/src/particle/modules/shape/CircleShape.ts +++ b/packages/core/src/particle/modules/shape/CircleShape.ts @@ -8,75 +8,23 @@ import { ParticleShapeType } from "./enums/ParticleShapeType"; * Particle shape that emits particles from a circle. */ export class CircleShape extends BaseShape { - private static _tempPositionPoint = new Vector2(); + private static _tempPositionPoint: Vector2 = new Vector2(); readonly shapeType = ParticleShapeType.Circle; - private _radius = 1.0; - private _arc = 360.0; - private _arcMode = ParticleShapeArcMode.Random; - private _arcSpeed = 1.0; - - /** - * Radius of the shape to emit particles from. - */ - get radius(): number { - return this._radius; - } - - set radius(value: number) { - if (value !== this._radius) { - this._radius = value; - this._updateManager.dispatch(); - } - } - - /** - * Angle of the circle arc to emit particles from. - */ - get arc(): number { - return this._arc; - } - - set arc(value: number) { - if (value !== this._arc) { - this._arc = value; - this._updateManager.dispatch(); - } - } - - /** - * The mode to generate particles around the arc. - */ - get arcMode(): ParticleShapeArcMode { - return this._arcMode; - } - - set arcMode(value: ParticleShapeArcMode) { - if (value !== this._arcMode) { - this._arcMode = value; - this._updateManager.dispatch(); - } - } - - /** - * The speed of complete 360 degree rotation. - */ - get arcSpeed(): number { - return this._arcSpeed; - } - - set arcSpeed(value: number) { - if (value !== this._arcSpeed) { - this._arcSpeed = value; - this._updateManager.dispatch(); - } - } + /** Radius of the shape to emit particles from. */ + radius = 1.0; + /** Angle of the circle arc to emit particles from. */ + arc = 360.0; + /** The mode to generate particles around the arc. */ + arcMode = ParticleShapeArcMode.Random; + /** The speed of complete 360 degree rotation. */ + arcSpeed = 1.0; /** * @internal */ - _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + override _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { const positionPoint = CircleShape._tempPositionPoint; switch (this.arcMode) { @@ -97,49 +45,4 @@ export class CircleShape extends BaseShape { ShapeUtils._randomPointUnitSphere(direction, rand); Vector3.lerp(position, direction, this.randomDirectionAmount, direction); } - - /** - * @internal - */ - _getDirectionRange(outMin: Vector3, outMax: Vector3): void { - const randomDirZ = this.randomDirectionAmount > 0.5 ? 1 : Math.sin(this.randomDirectionAmount * Math.PI); - const randomDegreeOnXY = 0.5 * (360 - this._arc) * this.randomDirectionAmount; - const randomDirY = randomDegreeOnXY > 90 ? -1 : -Math.sin(randomDegreeOnXY); - this._getUnitArcRange(this._arc + randomDegreeOnXY, outMin, outMax, randomDirY, randomDirZ); - } - - /** - * @internal - */ - _getPositionRange(outMin: Vector3, outMax: Vector3): void { - this._getUnitArcRange(this._arc, outMin, outMax, 0, 0); - outMin.scale(this._radius); - outMax.scale(this._radius); - } - - private _getUnitArcRange( - arc: number, - outMin: Vector3, - outMax: Vector3, - randomDirY: number, - randomDirZ: number - ): void { - const radian = MathUtil.degreeToRadian(arc); - const dirSin = Math.sin(radian); - const dirCos = Math.cos(radian); - - if (arc < 90) { - outMin.set(0, randomDirY, -randomDirZ); - outMax.set(1, dirSin, randomDirZ); - } else if (arc < 180) { - outMin.set(dirCos, randomDirY, -randomDirZ); - outMax.set(1, 1, randomDirZ); - } else if (arc < 270) { - outMin.set(-1, Math.min(dirSin, randomDirY), -randomDirZ); - outMax.set(1, 1, randomDirZ); - } else { - outMin.set(-1, -1, -randomDirZ); - outMax.set(1, 1, randomDirZ); - } - } } diff --git a/packages/core/src/particle/modules/shape/ConeShape.ts b/packages/core/src/particle/modules/shape/ConeShape.ts index a18558826..cb24dedf3 100644 --- a/packages/core/src/particle/modules/shape/ConeShape.ts +++ b/packages/core/src/particle/modules/shape/ConeShape.ts @@ -14,71 +14,19 @@ export class ConeShape extends BaseShape { readonly shapeType = ParticleShapeType.Cone; - private _angle = 25.0; - private _radius = 1.0; - private _length = 5.0; - private _emitType = ConeEmitType.Base; - - /** - * Angle of the cone to emit particles from. - */ - get angle(): number { - return this._angle; - } - - set angle(value: number) { - if (value !== this._angle) { - this._angle = value; - this._updateManager.dispatch(); - } - } - - /** - * Radius of the shape to emit particles from. - */ - get radius(): number { - return this._radius; - } - - set radius(value: number) { - if (value !== this._radius) { - this._radius = value; - this._updateManager.dispatch(); - } - } - - /** - * Length of the cone to emit particles from. - */ - get length(): number { - return this._length; - } - - set length(value: number) { - if (value !== this._length) { - this._length = value; - this._updateManager.dispatch(); - } - } - - /** - * Cone emitter type. - */ - get emitType(): ConeEmitType { - return this._emitType; - } - - set emitType(value: ConeEmitType) { - if (value !== this._emitType) { - this._emitType = value; - this._updateManager.dispatch(); - } - } + /** Angle of the cone to emit particles from. */ + angle = 25.0; + /** Radius of the shape to emit particles from. */ + radius = 1.0; + /** Length of the cone to emit particles from. */ + length = 5.0; + /** Cone emitter type. */ + emitType = ConeEmitType.Base; /** * @internal */ - _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + override _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { const unitPosition = ConeShape._tempVector20; const radian = MathUtil.degreeToRadian(this.angle); const dirSinA = Math.sin(radian); @@ -111,47 +59,6 @@ export class ConeShape extends BaseShape { break; } } - - /** - * @internal - */ - _getDirectionRange(outMin: Vector3, outMax: Vector3): void { - let radian = 0; - switch (this.emitType) { - case ConeEmitType.Base: - radian = MathUtil.degreeToRadian(this._angle); - - break; - case ConeEmitType.Volume: - const randomRadian = MathUtil.degreeToRadian((180 - this._angle) * this.randomDirectionAmount + this._angle); - radian = Math.sin(randomRadian); - break; - } - - const dirSin = Math.sin(radian); - outMin.set(-dirSin, -dirSin, -1); - outMax.set(dirSin, dirSin, 0); - } - - /** - * @internal - */ - _getPositionRange(outMin: Vector3, outMax: Vector3): void { - const { radius } = this; - - switch (this.emitType) { - case ConeEmitType.Base: - outMin.set(-radius, -radius, 0); - outMax.set(radius, radius, 0); - break; - case ConeEmitType.Volume: - const { length } = this; - const dirSin = Math.sin(MathUtil.degreeToRadian(this._angle)); - outMin.set(-radius - dirSin * length, -radius - dirSin * length, -length); - outMax.set(radius + dirSin * length, radius + dirSin * length, 0); - break; - } - } } /** diff --git a/packages/core/src/particle/modules/shape/HemisphereShape.ts b/packages/core/src/particle/modules/shape/HemisphereShape.ts index 6001460b5..e350de298 100644 --- a/packages/core/src/particle/modules/shape/HemisphereShape.ts +++ b/packages/core/src/particle/modules/shape/HemisphereShape.ts @@ -9,26 +9,13 @@ import { ParticleShapeType } from "./enums/ParticleShapeType"; export class HemisphereShape extends BaseShape { readonly shapeType = ParticleShapeType.Hemisphere; - private _radius = 1.0; - - /** - * Radius of the shape to emit particles from. - */ - get radius(): number { - return this._radius; - } - - set radius(value: number) { - if (value !== this._radius) { - this._radius = value; - this._updateManager.dispatch(); - } - } + /** Radius of the shape to emit particles from. */ + radius = 1.0; /** * @internal */ - _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + override _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { ShapeUtils._randomPointInsideUnitSphere(position, rand); position.scale(this.radius); @@ -38,22 +25,4 @@ export class HemisphereShape extends BaseShape { ShapeUtils._randomPointUnitSphere(direction, rand); Vector3.lerp(position, direction, this.randomDirectionAmount, direction); } - - /** - * @internal - */ - _getDirectionRange(outMin: Vector3, outMax: Vector3): void { - const randomDir = Math.sin(0.5 * this.randomDirectionAmount * Math.PI); - outMin.set(-1, -1, -1); - outMax.set(1, 1, randomDir); - } - - /** - * @internal - */ - _getPositionRange(outMin: Vector3, outMax: Vector3): void { - const radius = this._radius; - outMin.set(-radius, -radius, -radius); - outMax.set(radius, radius, 0); - } } diff --git a/packages/core/src/particle/modules/shape/SphereShape.ts b/packages/core/src/particle/modules/shape/SphereShape.ts index 269ff20d4..4972de275 100644 --- a/packages/core/src/particle/modules/shape/SphereShape.ts +++ b/packages/core/src/particle/modules/shape/SphereShape.ts @@ -9,47 +9,17 @@ import { ParticleShapeType } from "./enums/ParticleShapeType"; export class SphereShape extends BaseShape { readonly shapeType = ParticleShapeType.Sphere; - private _radius = 1.0; - - /** - * Radius of the shape to emit particles from. - */ - get radius(): number { - return this._radius; - } - - set radius(value: number) { - if (value !== this._radius) { - this._radius = value; - this._updateManager.dispatch(); - } - } + /** Radius of the shape to emit particles from. */ + radius = 1.0; /** * @internal */ - _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { + override _generatePositionAndDirection(rand: Rand, emitTime: number, position: Vector3, direction: Vector3): void { ShapeUtils._randomPointInsideUnitSphere(position, rand); position.scale(this.radius); ShapeUtils._randomPointUnitSphere(direction, rand); Vector3.lerp(position, direction, this.randomDirectionAmount, direction); } - - /** - * @internal - */ - _getDirectionRange(outMin: Vector3, outMax: Vector3): void { - outMin.set(-1, -1, -1); - outMax.set(1, 1, 1); - } - - /** - * @internal - */ - _getPositionRange(outMin: Vector3, outMax: Vector3): void { - const radius = this._radius; - outMin.set(-radius, -radius, -radius); - outMax.set(radius, radius, radius); - } } diff --git a/tests/src/core/particle/ParticleBoundingBox.test.ts b/tests/src/core/particle/ParticleBoundingBox.test.ts deleted file mode 100644 index 3a2acfa68..000000000 --- a/tests/src/core/particle/ParticleBoundingBox.test.ts +++ /dev/null @@ -1,428 +0,0 @@ -import { - ParticleRenderer, - BoxShape, - ParticleMaterial, - Camera, - SphereShape, - HemisphereShape, - CircleShape, - ConeShape, - ParticleShapeArcMode, - ConeEmitType, - Entity, - ParticleCurveMode, - Engine, - ParticleStopMode -} from "@galacean/engine-core"; -import { Color, Vector3 } from "@galacean/engine-math"; -import { WebGLEngine } from "@galacean/engine-rhi-webgl"; -import { LitePhysics } from "@galacean/engine-physics-lite"; -import { expect } from "chai"; - -const delta = 0.2; - -function expectObjectToBeCloseTo(actual, expected, delta) { - Object.keys(expected).forEach((key) => { - expect(actual[key]).to.be.closeTo(expected[key], delta); - }); -} - -export const updateEngine = (engine: Engine, deltaTime = 100) => { - //@ts-ignore - engine._vSyncCount = Infinity; - //@ts-ignore - engine._time._lastSystemTime = 0; - let times = 0; - performance.now = function () { - times++; - return times * deltaTime; - }; - for (let i = 0; i < 50; ++i) { - engine.update(); - } -}; - -function testParticleRendererBounds( - engine: Engine, - render: ParticleRenderer, - expectedMinBounds: { x: number; y: number; z: number }, - expectedMaxBounds: { x: number; y: number; z: number }, - delta: number -) { - render.generator.stop(true, ParticleStopMode.StopEmittingAndClear); - render.generator.play(); - updateEngine(engine); - expectObjectToBeCloseTo(render.bounds.min, expectedMinBounds, delta); - expectObjectToBeCloseTo(render.bounds.max, expectedMaxBounds, delta); -} - -describe("ParticleBoundingBox", function () { - let engine: Engine; - let particleRenderer: ParticleRenderer; - let entity: Entity; - - before(async function () { - engine = await WebGLEngine.create({ canvas: document.createElement("canvas"), physics: new LitePhysics() }); - const scene = engine.sceneManager.activeScene; - const rootEntity = scene.createRootEntity("root"); - - const cameraEntity = rootEntity.createChild("camera"); - cameraEntity.addComponent(Camera); - cameraEntity.transform.setPosition(0, 0, -10); - cameraEntity.transform.lookAt(new Vector3()); - - entity = rootEntity.createChild("particle"); - particleRenderer = entity.addComponent(ParticleRenderer); - const material = new ParticleMaterial(engine); - material.baseColor = new Color(1.0, 1.0, 1.0, 1.0); - particleRenderer.setMaterial(material); - - engine.run(); - }); - - beforeEach(function () { - particleRenderer.generator.stop(true, ParticleStopMode.StopEmittingAndClear); - entity.transform.position.set(0, 0, 0); - entity.transform.rotation.set(0, 0, 0); - entity.transform.scale.set(1, 1, 1); - - particleRenderer.generator.main.startSpeed.mode = ParticleCurveMode.Constant; - particleRenderer.generator.main.startSpeed.constant = 5; - - particleRenderer.generator.main.gravityModifier.mode = ParticleCurveMode.Constant; - particleRenderer.generator.main.gravityModifier.constant = 0; - - particleRenderer.generator.velocityOverLifetime.enabled = false; - - particleRenderer.generator.emission.shape = null; - }); - - it("EmptyShape", function () { - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.414, y: -1.414, z: -26.414 }, - { x: 1.414, y: 1.414, z: 1.414 }, - delta - ); - }); - - it("BoxShape", function () { - const shape = new BoxShape(); - particleRenderer.generator.emission.shape = shape; - - // Test that box shape works correctly on boundingBox - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.914, y: -1.914, z: -26.914 }, - { x: 1.914, y: 1.914, z: 1.914 }, - delta - ); - - // Test that size works correctly on boundingBox - shape.size.set(1, 2, 4); - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.914, y: -2.414, z: -28.414 }, - { x: 1.914, y: 2.414, z: 3.414 }, - delta - ); - - // Test that randomDirectionAmount works correctly on boundingBox - shape.randomDirectionAmount = 0.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -26.914, y: -27.414, z: -28.414 }, - { x: 26.914, y: 27.414, z: 3.414 }, - delta - ); - }); - - it("SphereShape", function () { - const shape = new SphereShape(); - particleRenderer.generator.emission.shape = shape; - - // Test that sphere shape works correctly on boundingBox - testParticleRendererBounds( - engine, - particleRenderer, - { x: -27.414, y: -27.414, z: -27.414 }, - { x: 27.414, y: 27.414, z: 27.414 }, - delta - ); - - // Test that radius works correctly on boundingBox - shape.radius = 2.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -28.914 }, - { x: 28.914, y: 28.914, z: 28.914 }, - delta - ); - - // Test that randomDirectionAmount works correctly on boundingBox - shape.randomDirectionAmount = 0.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -28.914 }, - { x: 28.914, y: 28.914, z: 28.914 }, - delta - ); - }); - - it("HemisphereShape", function () { - const shape = new HemisphereShape(); - particleRenderer.generator.emission.shape = shape; - - // Test that hemisphere shape works correctly on boundingBox - testParticleRendererBounds( - engine, - particleRenderer, - { x: -27.414, y: -27.414, z: -27.414 }, - { x: 27.414, y: 27.414, z: 1.414 }, - delta - ); - - // Test that radius works correctly on boundingBox - shape.radius = 2.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -28.914 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - // Test that randomDirectionAmount works correctly on boundingBox - shape.randomDirectionAmount = 0.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -28.914 }, - { x: 28.914, y: 28.914, z: 19.092 }, - delta - ); - }); - - it("CircleShape", function () { - const shape = new CircleShape(); - particleRenderer.generator.emission.shape = shape; - - // Test that circle shape works correctly on boundingBox - testParticleRendererBounds( - engine, - particleRenderer, - { x: -27.414, y: -27.414, z: -1.414 }, - { x: 27.414, y: 27.414, z: 1.414 }, - delta - ); - - // Test that radius works correctly on boundingBox - shape.radius = 2.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -1.414 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - // Test that arc works correctly on boundingBox - shape.arc = 45; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.414, y: -1.414, z: -1.414 }, - { x: 28.914, y: 20.859, z: 1.414 }, - delta - ); - - shape.arc = 135; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -20.859, y: -1.414, z: -1.414 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - shape.arc = 225; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -20.859, z: -1.414 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - shape.arc = 315; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -1.414 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - // Test that arc mode loop works correctly on boundingBox - shape.arcMode = ParticleShapeArcMode.Loop; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -1.414 }, - { x: 28.914, y: 28.914, z: 1.414 }, - delta - ); - - // Test that randomDirectionAmount works correctly on boundingBox - shape.randomDirectionAmount = 0.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -28.914, y: -28.914, z: -26.414 }, - { x: 28.914, y: 28.914, z: 26.414 }, - delta - ); - }); - - it("ConeShape", function () { - const shape = new ConeShape(); - particleRenderer.generator.emission.shape = shape; - - // Test that cone shape works correctly on boundingBox - testParticleRendererBounds( - engine, - particleRenderer, - { x: -12.979, y: -12.979, z: -26.414 }, - { x: 12.979, y: 12.979, z: 1.414 }, - delta - ); - - // Test that radius works correctly on boundingBox - shape.radius = 2.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -14.479, y: -14.479, z: -26.414 }, - { x: 14.479, y: 14.479, z: 1.414 }, - delta - ); - - // Test that angle works correctly on boundingBox - shape.angle = 30; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -16.414, y: -16.414, z: -26.414 }, - { x: 16.414, y: 16.414, z: 1.414 }, - delta - ); - - // Test that arc mode loop works correctly on boundingBox - shape.emitType = ConeEmitType.Volume; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -18.4, y: -18.4, z: -31.414 }, - { x: 18.4, y: 18.4, z: 1.414 }, - delta - ); - - // Test that arc mode loop works correctly on boundingBox - shape.length = 10; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -20.9, y: -20.9, z: -36.414 }, - { x: 20.9, y: 20.9, z: 1.414 }, - delta - ); - - // Test that randomDirectionAmount works correctly on boundingBox - shape.randomDirectionAmount = 0.5; - testParticleRendererBounds( - engine, - particleRenderer, - { x: -29.478, y: -29.478, z: -36.414 }, - { x: 29.478, y: 29.478, z: 1.414 }, - delta - ); - }); - - it("StartSpeed", function () { - particleRenderer.generator.main.startSpeed.mode = ParticleCurveMode.TwoConstants; - particleRenderer.generator.main.startSpeed.constantMin = -10; - particleRenderer.generator.main.startSpeed.constantMax = 2; - - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.414, y: -1.414, z: -11.414 }, - { x: 1.414, y: 1.414, z: 51.414 }, - delta - ); - }); - - it("Gravity", function () { - particleRenderer.generator.main.gravityModifier.mode = ParticleCurveMode.TwoConstants; - particleRenderer.generator.main.gravityModifier.constantMin = -1; - particleRenderer.generator.main.gravityModifier.constantMax = 0.2; - - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.414, y: -25.939, z: -26.414 }, - { x: 1.414, y: 124.039, z: 1.414 }, - delta - ); - }); - - it("VelocityOverLifetime", function () { - this.timeout(10000); - - particleRenderer.generator.main.startSpeed.mode = ParticleCurveMode.Constant; - particleRenderer.generator.main.startSpeed.constant = 0; - - const velocityOverLifetime = particleRenderer.generator.velocityOverLifetime; - const { velocityX, velocityY, velocityZ } = velocityOverLifetime; - velocityOverLifetime.enabled = true; - velocityX.constant = 1; - velocityY.constant = 1; - velocityZ.mode = ParticleCurveMode.TwoConstants; - velocityZ.constantMin = -1; - velocityZ.constantMax = 0.5; - - testParticleRendererBounds( - engine, - particleRenderer, - { x: -1.414, y: -1.414, z: -6.414 }, - { x: 6.414, y: 6.414, z: 3.914 }, - delta - ); - }); - - it("Transform", function () { - entity.transform.position.set(1, 2, 3); - testParticleRendererBounds( - engine, - particleRenderer, - { x: -0.414, y: 0.586, z: -23.414 }, - { x: 2.414, y: 3.414, z: 4.414 }, - delta - ); - - entity.transform.rotation.set(30, 60, 120); - testParticleRendererBounds( - engine, - particleRenderer, - { x: -19.906, y: -0.3798, z: -10.239 }, - { x: 3.156, y: 16.88, z: 5.414 }, - delta - ); - }); -});