diff --git a/CHANGELOG.md b/CHANGELOG.md index 315859d..af3f0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change Log +## version 0.10.8 +- Support horizontal and vertical billboard in shader + ## version 0.10.6 - fix render setting reference bug - fix shaders can not loaded in some loader because of shader chunk reference diff --git a/examples/billboardDemo.js b/examples/billboardDemo.js new file mode 100644 index 0000000..72d9b87 --- /dev/null +++ b/examples/billboardDemo.js @@ -0,0 +1,99 @@ +import { + BatchedParticleRenderer, + ConstantColor, + PointEmitter, + IntervalValue, + ConstantValue, + ParticleSystem, + SizeOverLife, + PiecewiseBezier, + Bezier, + RenderMode, + RandomQuatGenerator, + AxisAngleGenerator, + Rotation3DOverLife, + RotationOverLife, + SpeedOverLife, + EulerGenerator, +} from './js/three.quarks.esm.js'; +import { + MeshBasicMaterial, + NormalBlending, + AdditiveBlending, + TextureLoader, + Vector4, + Vector3, + PlaneGeometry, + DoubleSide, +} from './js/three.module.js'; +import {Demo} from './demo.js'; + +export class BillboardDemo extends Demo { + name = 'Horizon & Vertical BillboardDemo'; + + initScene() { + super.initScene(); + + this.batchRenderer = new BatchedParticleRenderer(); + this.scene.add(this.batchRenderer); + + const texture = new TextureLoader().load('textures/logo_texture.png'); + const config = { + duration: 5, + looping: true, + //instancingGeometry: new PlaneGeometry(1, 1),//.rotateX((-90 / 180) * Math.PI), + startLife: new IntervalValue(4, 5), + startSpeed: new ConstantValue(2), + startSize: new IntervalValue(0.4, 0.5), + //startRotation: new EulerGenerator(new ConstantValue(0), new ConstantValue(0), new ConstantValue(0)), + startColor: new ConstantColor(new Vector4(1, 1, 1, 1)), + worldSpace: false, + + maxParticle: 100, + emissionOverTime: new ConstantValue(0), + emissionBursts: [ + { + time: 0, + count: 100, + cycle: 1, + interval: 0.01, + probability: 1, + }, + ], + + shape: new PointEmitter(), + material: new MeshBasicMaterial({ + blending: NormalBlending, + transparent: true, + map: texture, + //side: DoubleSide, + }), + startTileIndex: new ConstantValue(0), + uTileCount: 1, + vTileCount: 1, + renderOrder: 2, + renderMode: RenderMode.VerticalBillBoard, + }; + + // Create particle system based on your configuration + let billboard1 = new ParticleSystem(config); + billboard1.addBehavior(new SpeedOverLife(new PiecewiseBezier([[new Bezier(1, 0.75, 0.5, 0), 0]]))); + billboard1.emitter.name = 'billboard'; + billboard1.emitter.position.x = 5; + + config.renderMode = RenderMode.HorizontalBillBoard; + let billboard2 = new ParticleSystem(config); + billboard2.addBehavior(new SpeedOverLife(new PiecewiseBezier([[new Bezier(1, 0.75, 0.5, 0), 0]]))); + billboard2.emitter.name = 'billboard'; + billboard2.emitter.position.x = -5; + + this.batchRenderer.addSystem(billboard1); + this.batchRenderer.addSystem(billboard2); + + this.scene.add(billboard1.emitter); + this.scene.add(billboard2.emitter); + this.scene.add(this.batchRenderer); + + return this.scene; + } +} diff --git a/examples/index.html b/examples/index.html index 83f0b6d..eb23738 100644 --- a/examples/index.html +++ b/examples/index.html @@ -79,6 +79,7 @@ import {SequencerDemo} from "./sequencerDemo.js"; import {MeshMaterialDemo} from "./meshMaterialDemo.js"; import {AlphaTestDemo} from "./alphaTestDemo.js"; + import {BillboardDemo} from "./billboardDemo.js"; const WEBGL = { isWebGLAvailable: function () { @@ -142,7 +143,7 @@ let scene; let demo; - let demos = [MuzzleFlashDemo, TornadoDemo, TrailDemo, SequencerDemo, MeshMaterialDemo, TurbulenceDemo, AlphaTestDemo, CustomPluginDemo]; + let demos = [MuzzleFlashDemo, TornadoDemo, TrailDemo, SequencerDemo, MeshMaterialDemo, TurbulenceDemo, AlphaTestDemo, CustomPluginDemo, BillboardDemo]; let demoIndex = 0; function init() { diff --git a/examples/textures/particle_default.png b/examples/textures/particle_default.png new file mode 100644 index 0000000..5fa079f Binary files /dev/null and b/examples/textures/particle_default.png differ diff --git a/package.json b/package.json index cd46160..b92055f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "three.quarks", - "version": "0.10.7", + "version": "0.10.8", "description": "A General-Purpose Particle System for three.js", "type": "module", "types": "./dist/types/index.d.ts", diff --git a/src/BatchedRenderer.ts b/src/BatchedRenderer.ts index c6cfd6b..44eb624 100644 --- a/src/BatchedRenderer.ts +++ b/src/BatchedRenderer.ts @@ -52,6 +52,8 @@ export class BatchedRenderer extends Object3D { break; case RenderMode.Mesh: case RenderMode.BillBoard: + case RenderMode.VerticalBillBoard: + case RenderMode.HorizontalBillBoard: case RenderMode.StretchedBillBoard: batch = new SpriteBatch(settings); break; diff --git a/src/ParticleSystem.ts b/src/ParticleSystem.ts index aec5861..62f06ca 100644 --- a/src/ParticleSystem.ts +++ b/src/ParticleSystem.ts @@ -454,6 +454,8 @@ export class ParticleSystem { this.startRotation = new AxisAngleGenerator(new Vector3(0, 1, 0), new ConstantValue(0)); break; case RenderMode.BillBoard: + case RenderMode.VerticalBillBoard: + case RenderMode.HorizontalBillBoard: case RenderMode.StretchedBillBoard: this.rendererEmitterSettings = {}; if (this.rendererSettings.renderMode === RenderMode.Mesh) { @@ -572,6 +574,8 @@ export class ParticleSystem { if ( this.rendererSettings.renderMode === RenderMode.Mesh || this.rendererSettings.renderMode === RenderMode.BillBoard || + this.rendererSettings.renderMode === RenderMode.VerticalBillBoard || + this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard || this.rendererSettings.renderMode === RenderMode.StretchedBillBoard ) { const sprite = particle as SpriteParticle; @@ -612,7 +616,8 @@ export class ParticleSystem { } if (this.worldSpace) { particle.position.applyMatrix4(matrix); - particle.startSize = (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3; + particle.startSize = + (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3; particle.size = particle.startSize; particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix); if (particle.rotation && particle.rotation instanceof Quaternion) { @@ -685,10 +690,15 @@ export class ParticleSystem { if (this.looping && this.prewarm && !this.prewarmed) { this.prewarmed = true; for (let i = 0; i < this.duration * PREWARM_FPS; i++) { + // stack overflow? this.update(1.0 / PREWARM_FPS); } } + if (delta > 0.1) { + delta = 0.1; + } + if (this.neededToUpdateRender) { if (this._renderer) this._renderer.updateSystem(this); this.neededToUpdateRender = false; diff --git a/src/SpriteBatch.ts b/src/SpriteBatch.ts index fe15919..f364529 100644 --- a/src/SpriteBatch.ts +++ b/src/SpriteBatch.ts @@ -59,6 +59,8 @@ export class SpriteBatch extends VFXBatch { this.geometry.setAttribute('rotation', this.rotationBuffer); } else if ( this.settings.renderMode === RenderMode.BillBoard || + this.settings.renderMode === RenderMode.HorizontalBillBoard || + this.settings.renderMode === RenderMode.VerticalBillBoard || this.settings.renderMode === RenderMode.StretchedBillBoard ) { this.rotationBuffer = new InstancedBufferAttribute(new Float32Array(this.maxParticles), 1); @@ -186,7 +188,12 @@ export class SpriteBatch extends VFXBatch { defines['USE_COLOR_ALPHA'] = ''; let needLights = false; - if (this.settings.renderMode === RenderMode.BillBoard || this.settings.renderMode === RenderMode.Mesh) { + if ( + this.settings.renderMode === RenderMode.BillBoard || + this.settings.renderMode === RenderMode.VerticalBillBoard || + this.settings.renderMode === RenderMode.HorizontalBillBoard || + this.settings.renderMode === RenderMode.Mesh + ) { let vertexShader; let fragmentShader; if (this.settings.renderMode === RenderMode.Mesh) { @@ -206,6 +213,11 @@ export class SpriteBatch extends VFXBatch { vertexShader = particle_vert; fragmentShader = particle_frag; } + if (this.settings.renderMode === RenderMode.VerticalBillBoard) { + defines['VERTICAL'] = ''; + } else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) { + defines['HORIZONTAL'] = ''; + } this.material = new ShaderMaterial({ uniforms: uniforms, defines: defines, @@ -236,11 +248,6 @@ export class SpriteBatch extends VFXBatch { } } - /* - clone() { - let system = this.system.clone(); - return system.emitter as any; - }*/ vector_: Vector3 = new Vector3(); vector2_: Vector3 = new Vector3(); vector3_: Vector3 = new Vector3(); @@ -291,6 +298,8 @@ export class SpriteBatch extends VFXBatch { this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w); } else if ( this.settings.renderMode === RenderMode.StretchedBillBoard || + this.settings.renderMode === RenderMode.VerticalBillBoard || + this.settings.renderMode === RenderMode.HorizontalBillBoard || this.settings.renderMode === RenderMode.BillBoard ) { this.rotationBuffer.setX(index, particle.rotation as number); @@ -316,7 +325,10 @@ export class SpriteBatch extends VFXBatch { if (particle.parentMatrix) { this.sizeBuffer.setX(index, particle.size); } else { - this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3); + this.sizeBuffer.setX( + index, + (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3 + ); } } this.uvTileBuffer.setX(index, particle.uvTile); @@ -364,6 +376,8 @@ export class SpriteBatch extends VFXBatch { this.rotationBuffer.needsUpdate = true; } else if ( this.settings.renderMode === RenderMode.StretchedBillBoard || + this.settings.renderMode === RenderMode.HorizontalBillBoard || + this.settings.renderMode === RenderMode.VerticalBillBoard || this.settings.renderMode === RenderMode.BillBoard ) { this.rotationBuffer.updateRange.count = index; diff --git a/src/VFXBatch.ts b/src/VFXBatch.ts index 230e2ed..c7c7cce 100644 --- a/src/VFXBatch.ts +++ b/src/VFXBatch.ts @@ -17,6 +17,8 @@ export enum RenderMode { StretchedBillBoard = 1, Mesh = 2, Trail = 3, + HorizontalBillBoard = 4, + VerticalBillBoard = 5, } export abstract class VFXBatch extends Mesh { diff --git a/src/shaders/local_particle_physics_vert.glsl.ts b/src/shaders/local_particle_physics_vert.glsl.ts index d4098b4..a41b0a7 100644 --- a/src/shaders/local_particle_physics_vert.glsl.ts +++ b/src/shaders/local_particle_physics_vert.glsl.ts @@ -30,25 +30,25 @@ uniform vec2 tileCount; void main() { ${uv_vertex_tile} - + float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z; float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2; float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2; float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2; float sx = size, sy = size, sz = size; - + mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0, // 1. column ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0, // 2. column ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0, // 3. column offset.x, offset.y, offset.z, 1.0); - + #include #include #include #include #include #include - + // replace defaultnormal_vertex vec3 transformedNormal = objectNormal; mat3 m = mat3( particleMatrix ); @@ -64,16 +64,16 @@ void main() { transformedTangent = - transformedTangent; #endif #endif - + #include #include #include #include #include - + // replace include - vec4 mvPosition = vec4( transformed, 1.0 ); - mvPosition = modelViewMatrix * (particleMatrix * mvPosition); + vec4 mvPosition = vec4( transformed, 1.0 ); + mvPosition = modelViewMatrix * (particleMatrix * mvPosition); gl_Position = projectionMatrix * mvPosition; #include diff --git a/src/shaders/particle_vert.glsl.ts b/src/shaders/particle_vert.glsl.ts index 1af21ce..0f3a40d 100644 --- a/src/shaders/particle_vert.glsl.ts +++ b/src/shaders/particle_vert.glsl.ts @@ -19,15 +19,25 @@ void main() { ${uv_vertex_tile} - vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 ); - vec2 alignedPosition = ( position.xy ) * size; vec2 rotatedPosition; rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; - +#ifdef HORIZONTAL + vec4 mvPosition = modelMatrix * vec4( offset, 1.0 ); + mvPosition.x += rotatedPosition.x; + mvPosition.z -= rotatedPosition.y; + mvPosition = viewMatrix * mvPosition; +#elif defined(VERTICAL) + vec4 mvPosition = modelMatrix * vec4( offset, 1.0 ); + mvPosition.y += rotatedPosition.y; + mvPosition = viewMatrix * mvPosition; + mvPosition.x += rotatedPosition.x; +#else + vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 ); mvPosition.xy += rotatedPosition; +#endif vColor = color;