Giter VIP home page Giter VIP logo

three.quarks's Introduction

three.quarks

three.quarks is a high-performance javascript particle system based visual effect library for threejs written in modern TypeScript.

Join our discord for discussion.

landing image

Links

Background

three.quarks is a high-performance general-purpose particle system library with a WYSIWYG visual editor three.quarks-editor for it. It runs on top of the well-known WebGL library called three.js.

Roadmap

  • Port Simulation Compiler code to Rust
  • WebAssembly Particle Simulation on CPU. (WIP)
  • GPU simulation on GPU (WIP)
  • Node based / scriptable particle system (WIP)
  • Simulation Frequency - Performance
  • Unity / Standalone Cross-platform Native plugin to run VFX

Features

  • Semi-compatible Unity (shuriken) Particle system
  • Support Mesh Standard Material and Mesh Basic Material
  • Batch Render Multiple Particle System (reduce draw calls) - BatchedParticleRenderer
  • Emission Shape and Control
    • Mesh Surface Emitter
  • Plugin System - Plugin
    • Customizable Behaviors
    • Customizable Emitter Shape
  • 4 Type of renderer
    • Billboard
    • Stretched Billboard
    • Mesh Renderer
    • Trail Renderer TrailBatch
  • Spawn Particle on Mesh Surface
  • Configuable RenderMode and BlendMode
  • 1D Bézier curve function for adjusting
  • Texture Atlas Animation
  • User Extension and Customization
  • A realtime editor to test and create visual effects three.quarks-editor
  • VFX json load and save

three.quarks computes most particle information on CPU, and it uses customized shader , instancing, batch techniques to render those particles with as few draw calls as possible. three.quarks supports one dimension piecewise Bézier curves for the customizable transform visual effect. Most importantly, developers can customize how the particle system works by adding their own Behavior.

Install

Package install

npm install three.quarks

Check examples folder

Add particle system to the scene

const clock = new THREE.Clock();
const batchSystem = new BatchedRenderer();
const texture = new TextureLoader().load("atlas.png");
// Particle system configuration
const muzzle = {
    duration: 1,
    looping: false,
    startLife: new IntervalValue(0.1, 0.2),
    startSpeed: new ConstantValue(0),
    startSize: new IntervalValue(1, 5),
    startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),
    worldSpace: false,

    maxParticle: 5,
    emissionOverTime: new ConstantValue(0),
    emissionBursts: [{
        time: 0,
        count: new ConstantValue(1),
        cycle: 1,
        interval: 0.01,
        probability: 1,
    }],

    shape: new PointEmitter(),
    material: new MeshBasicMaterial({map: texture, blending: AdditiveBlending, transparent: true}),
    startTileIndex: new ConstantValue(91),
    uTileCount: 10,
    vTileCount: 10,
    renderOrder: 2,
    renderMode: RenderMode.Mesh
};

// Create particle system based on your configuration
muzzle1 = new ParticleSystem(muzzle);
// developers can customize how the particle system works by 
// using existing behavior or adding their own Behavior.
muzzle1.addBehavior(new ColorOverLife(new ColorRange(new THREE.Vector4(1, 0.3882312, 0.125, 1), new THREE.Vector4(1, 0.826827, 0.3014706, 1))));
muzzle1.addBehavior(new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 0.95, 0.75, 0), 0]])));
// texture atlas animation
muzzle1.addBehavior(new FrameOverLife(new PiecewiseBezier([[new Bezier(91, 94, 97, 100), 0]])));
muzzle1.emitter.name = 'muzzle1';
muzzle1.emitter.position.x = 1;

batchSystem.addSystem(muzzle1);

// Add emitter to your Object3D
scene.add(muzzle1.emitter);
scene.add(batchSystem);

Add batch renderer update in your main loop

// update batched renderer
batchSystem.update(clock.getDelta());

Import VFX JSON

const batchSystem = new BatchedRenderer();
const loader = new QuarksLoader();
loader.setCrossOrigin("");
loader.load(jsonURL, (obj) => {
    // the API uses manuel loading because users may need to 
    // store the VFX somewhere to reuse it later.
    obj.traverse((child) => {
        if (child.type === "ParticleEmitter") {
            // only if want to display the VFX
            batchRenderer.addSystem(child.system);
        }
    });
    if (obj.type === "ParticleEmitter") {
        batchRenderer.addSystem(obj.system);
    }
    scene.add(obj);
}, () => {
}, () => {
});
scene.add(batchSystem);

Note: the texture url reference is defined by the texture's name field. you may need to modify the texture url in json as needed.

Export VFX JSON

JSON.stringify(muzzle1.emitter.toJSON())
JSON.stringify(muzzle1.emitter.parent.toJSON())

Examples

Launch Examples

npm install             # install dependencies
npm run build           # build three.quarks
npm run example         # start an HTTP server to serve example particle effects

three.quarks-editor

three.quarks-editor can help you preview a set of particle system at once. and you can also adjust all the particle system at real time and export those system as a JSON file. Your app or game can load those JSON file later. It even includes a Javascript scripting system to test those effect in a similar environment to your application.

Tests

Check test folder

More examples will come up later.

Notes

If you want the best performance please consider `yarn link` [https://github.com/Alchemist0823/three.js](https://github.com/Alchemist0823/three.js). This version of three.js performs much better than official release, because it avoids unnecessary `getProgramCachedKey()` calls and material updates.

three.quarks's People

Contributors

alchemist0823 avatar benferns avatar cygnusfear avatar d8h avatar danielbelmes avatar dependabot[bot] avatar hybridherbst avatar marwie avatar tracyhenry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

three.quarks's Issues

Particle opacity behavior

I could not find a way to control the opacity of particles by analogy with ColorOverLife.
Are you planning to add this functionality?

Trail Particle Performance on Quest

Hello,

I recently noticed a huge performance difference on Quest using trail particles - trail particles are currently very slow on Quest to the point where I can't really use them easily in my scenario.

Could that be related to the default maxParticles size being 10x the size of a normal sprite batch? I'm also wondering why the max size is hardcoded for both of these cases? Would it be possible to expose this with the VFXBatchSettings interface?
E.g. in my case i want to render a single particle trail following an arrow so I surely don't need that many particles. Also in most cases I'm dealing with particle counts < 100 or < 200

this.maxParticles = 10000;

this.maxParticles = 1000;

Horizontal- & Vertical billboards

Hello,

prior to 0.10.6 (in version 0.7.3) we could create horizontal and vertical billboarding by modifying the mesh geometry. This doesn't work anymore. Is there a way to add this behaviour again?

Old code:

if (renderMode === ParticleSystemRenderMode.HorizontalBillboard) {
            geo = new THREE.BoxGeometry(1, 1, 0);
        }
        else if (renderMode === ParticleSystemRenderMode.VerticalBillboard) {
            geo = new THREE.BoxGeometry(1, 0, 1);
        }

Creating an emitter that emits particles with a velocity

I've noticed that it seems possible to add a gravityForce behaviour to a particle system, but I did not find anything regarding a velocity or acceleration behaviour.

My particular intended use case is I would like to create an emitter can emit particles at a specified vector3 velocity (eg shooting rays from a gun towards a target). If there is another way I can achieve this please let me know! Thank you :)

Improvement: Offload Physics to Thread

The system will reach a CPU bottleneck pretty fast when using complex effects, way sooner than any GPU bottleneck.

Implement a way to offload the physics calculations to a worker. Ideally, when a particle is first created the first couple frames are calculated on the main thread while calculations of multiple frames are requested to the worker which can generate the simulation in advance.

Obviously requesting the calculations every frame to the worker would not be possible since you would be bottlenecked by the transfer between threads so the only way i see this working is if the worker calculates multiple frames in advance per request.

Issue forcing particle to render always on top of all scene objects

I want my particle to render on top over all other objects in the scene. however setting depthTest false on the material is not making it render on top as i'd expect, is there another way I should be approaching this? Thanks again, this framework is amazing!

 new ParticleSystem({ 
 ...
  material: new MeshBasicMaterial({
                map: this.texture,
                blending: AdditiveBlending,
                transparent: true,
                side: DoubleSide,
                
                //Here
                depthTest: false, 
             
            }),
            }
            ```

[Question] Multiple objects with the same particle system

Hey @Alchemist0823!

First of all, neat project you have here, really like it!

I'm trying to build a tab-target game, and in need of a particle system. First thing I tried is to add damage effect to the entities when they get hit. I used the one in the marketplace with a little modification to be white: https://quarks.art/asset/cartoon-energy-explosion

When an entity get hit, I load the effect with the QuarksLoader().load method. But the more I load, the more the FPS drops, and memory usage goes up. I guess it always loads it from scratch.
So I wanted to cache the loaded object in some Map, reuse that when I need to add another particle system, and also set the autoDestroy to true for systems, to clean up every effect.
This works only partly, because only the "Glow" emitter visible after the first system is emitted.

In the future the entities would have the same effect on them at the same time, like healing or shield effect, so I must sort this out somehow.

My question is how would you go about this?

This is the class that handles particles:
import { Object3D, Object3DEventMap, Vector3 } from "three"
import { BatchedRenderer, ParticleEmitter, ParticleSystem, QuarksLoader } from "three.quarks"
import { generateUUID } from "three/src/math/MathUtils"
import Game from "../Game"

type EffectOptions = {
  name: string
  parent?: Object3D
  position?: Vector3
  scale?: number
}

type Effect = {
  key: string
  object: Object3D<Object3DEventMap>
  systems: ParticleSystem[]
}

export default class ParticleRenderer {
  private static readonly renderer = new BatchedRenderer()
  private static readonly loadedEffects = new Map<string, Object3D<Object3DEventMap>>()

  static init(): void {
    Game.scene.add(this.renderer)
  }

  static async addEffect(options: EffectOptions): Promise<Effect> {
    return new Promise(async (resolve) => {
      let object: Object3D<Object3DEventMap>

      if (!this.loadedEffects.has(options.name)) {
        object = await this.loadEffect(options.name)
      } else {
        object = (this.loadedEffects.get(options.name) as Object3D<Object3DEventMap>).clone()
      }

      const key = generateUUID()
      const systems = new Array<ParticleSystem>()
      const parent = options.parent || Game.scene

      object.traverse((child: Object3D<Object3DEventMap>) => {
        if (child.type === 'ParticleEmitter') {
          systems.push(this.addSystem(child as ParticleEmitter))
        }
      })

      if (object.type === 'ParticleEmitter') {
        systems.push(this.addSystem(object as ParticleEmitter))
      }

      if (options.position) {
        object.position.copy(options.position)
      }

      if (options.scale) {
        object.scale.set(options.scale, options.scale, options.scale)
      }

      parent.add(object)

      resolve({ key, object, systems })
    })
  }

  static removeEffect(effect: Effect): void {
    effect.systems.forEach((system: ParticleSystem) => this.renderer.deleteSystem(system))
    Game.scene.remove(effect.object)
  }

  static update(): void {
    this.renderer.update(Game.deltaTime)
  }

  private static async loadEffect(name: string): Promise<Object3D<Object3DEventMap>> {
    return new Promise((resolve) => {
      new QuarksLoader().load(`/assets/particle-fx/${name}.json`, (object: Object3D<Object3DEventMap>) => {
        this.loadedEffects.set(name, object.clone())
        resolve(object)
      })
    })
  }

  private static addSystem(object: ParticleEmitter): ParticleSystem {
    const system = object.system as ParticleSystem
    system.renderOrder = 1
    system.autoDestroy = true
    this.renderer.addSystem(system)

    return system
  }
}

Particle died event

Hello,

is there a good way to get the died state of a particle from a behaviour? How should events be implemented that happen on particle death?

My workaround for now is adding the current delta time and checking if it is above the life.
But that is quite hacky and unstable I would say :)

const willDie = particle.age + delta >= particle.life

Json import and world space scale not working

Importing a json that has world space effects, then applying a scale to the container of the particle systems does not reflect on the object. This can also be verified in the visual editor by changing the scale of the “plasma projectile” example and observing it does nothing

Change the map doesn't work

createParticleSystem = () => {
		this.batchSystem = new BatchedParticleRenderer();
		const texture = new TextureLoader().load("textures/star.png");
		texture.encoding = sRGBEncoding;
		const param = {
			shape: new DonutEmitter({
				radius: 2,
				angle: Math.PI / 4,
				arc: Math.PI,
			}),
			renderMode: RenderMode.BillBoard,
			worldSpace: false,
			renderOrder: 2,
			looping: true,
			duration: 5,
			emissionOverTime: new ConstantValue(100),
			startLife: new ConstantValue(5),
			startSize: new IntervalValue(10, 15),
			startSpeed: new ConstantValue(5),
			material: new MeshBasicMaterial({ transparent: true, color: 0xff0000, map: texture, blending: AdditiveBlending, depthTest: false }),
			onlyUsedByOther: false,
		};

		setTimeout(() => {
			const texture = new TextureLoader().load("textures/1.jpg");
			this.muzzle.texture = texture;
		}, 5000);

		this.muzzle = new ParticleSystem(param);

		this.muzzle.addBehavior(new SizeOverLife(new PiecewiseBezier([[new Bezier(.3, .5, .6, .3), 0]])));
		this.muzzle.addBehavior(new ColorOverLife(new ColorRange(new Vector4(0, 0, 0, 1), new Vector4(1, 1, 1, .3))));
		this.muzzle.emitter.name = 'muzzle1';
		this.batchSystem.addSystem(this.muzzle);

		this.muzzle.emitter.rotateX(-Math.PI / 2);
		this.scene.add(this.muzzle.emitter);
		this.scene.add(this.batchSystem);
	};

The above dynamic modify texture is not effective, Version is 0.10.2 ,Where I do wrong?

ReadMe example code seems outdated

In the code example from README.md, should the curly brackets be removed?

Existing code:

muzzle1 = new ParticleSystem({muzzle});

Proposition:

muzzle1 = new ParticleSystem(muzzle);

how to start particle more than 1 time without looping

I would like to create an effect such as we currently have on the three.quark website - i.e. pressing a button triggers an effect, the next press triggers another, etc.

image

Currently, using addSystem will simply add an effect to me that executes once (if it is not looped)

this.quarksRenderer.addSystem(child.system);

but calling the function again and creating a new object will not trigger the effect a second time - only loading it and downloading the asset again will allow this.

So, how do you trigger effect after effect depending on the will of the user? (effect read from json file)

looping property in particle system configuration doesn't work (?)

hello,

does looping property in particle system configuration work well?
In example muzzleFlashDemo animation, effect is constantly looping on the demo, even though each instance of the System has looping on false.

*but if we read effect from .json file, then looping work well

also you should remove console.log(this.subEmissions.length, this.subParticleSystem.system.particleNum);

Three.js is used as dependency, not peer dependency

I was investigating why we always get double versions of three in some applications. Turns out that three.quarks is the only package in the chain that is pulling in an actual version of three and not a peerDependency, which would correctly resolve to the actual intended three version.

Are you open to changing this? Should just be moving three from "dependency" to "peerDependency".
Thanks!

image

JSM file

Hello! My project does not use TS nor compiling, it would be very convinient to have a jsm version where, similar to threejs, you can just drop the file in and import what you need, would it be possible to have such release downloadable for convenience ?

RenderMode ?

Hello,

could you clarify what you mean by localspace in the RenderMode enum?

export declare enum RenderMode {
    BillBoard = 0,
    StretchedBillBoard = 1,
    LocalSpace = 2,
    Trail = 3
}

was expecting something like this

export enum ParticleSystemRenderMode {
    Billboard = 0,
    //   Stretch = 1,
    //   HorizontalBillboard = 2,
    //   VerticalBillboard = 3,
    Mesh = 4,
    //   None = 5,
}

Adjust log messaging

Follow-up to #63

We've had a few people voice confusion as to why their entire website is "powered by three.quarks" according to the logs.
Would it be possible to adjust the messaging to read "Particle Systems powered by three.quarks" or similar?

Thank you!

Intended to be compatible with logarithmic depth?

I get the following error in BatchedParticleRenderer materials when switching the threejs renderer to logarithmic depth:

THREE.WebGLProgram: Shader Error 0 - VALIDATE_STATUS false Program Info Log: Vertex shader is not compiled. VERTEX ERROR: 0:123: 'isPerspectiveMatrix' : no matching overloaded function found

Usage of ``ParticleSystem.emit``

Hello,

I have a case where I have a one-shot effect with a duration of 1 and not looping. I still want to be able to call emit on this effect and have the system emit the provided amount of particles that frame. Currently the internal emitEnded flag prevents that.

What would you suggest on how to make this still work and force it to emit particles?

Here's a short video where I set emitEnded to false before calling emit in a 1 sec interval

20221111-124820_Made_with_Needle_-_Google_Chrome.mp4

the system is setup like this:
image

Support for cutout / alpha clipping

Right now it seems either "transparent" or "opaque" are supported. In some cases "opaque + alphaClip" would be desirable, e.g. to have circular or bokeh particles that properly render depth.

Changelog out of date and no release tags

Hello,

do you think you could update the changelog and push release tags to github? It would help with knowing when to update to which version / when new features are available 😊

Last release: 13 days ago, version 0.10.0
Changelog: almost a year ago, version 0.7.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.