Managing Point Cloud Particles
Particle Properties
Once the PCS mesh is built, unless immutable, it can respond to changes in the properties of each particle. Existing properties are shown in the table below.
| Property | Type | Default |
|---|---|---|
| position | Vector3 | (0, 0, 0) |
| rotation | Vector3 | (0, 0, 0) |
| rotationQuaternion | Vector3 | undefined |
| velocity | Vector3 | (0, 0, 0) |
| color | Vector4 | (1, 1, 1, 1) |
| pivot | Vector3 | (0, 0, 0) |
| uvs | Vector2 | (0,0) |
| translateFromPivot | boolean | false |
| parentId | integer | null |
| idx | integer (read only) | index of particle |
| groupId | integer (read only) | group number for a particle |
If you set a particle rotation quaternion, its rotation property will then be ignored.
New properties can be initialized.
Note: Since point particles always face the camera, setting the rotation of a particle only has any effect in two cases:
- The rotation of a particle parent will rotate its children about itself;
- A pivot is set for the particle.
Initialising Particles
When using addPoints, particle properties can be set in the passed function. The addSurfacePoints and addVolumePoints methods obviously set the position and color properties of the particles, but you may still want to set the initial values of other particle properties.
This can be done using initParticles. To do this, iterate over all the particles by using the nbParticles property, and follow the call to this function with a call to setParticles(). For example:
import { Vector3 } from "@babylonjs/core/Maths/math.vector";import { PointColor } from "@babylonjs/core/Particles/pointsCloudSystem";pcs.initParticles = function () { for (let p = 0; p < pcs.nbParticles; p++) { pcs.particles[p].velocity = Vector3.Zero(); pcs.particles[p].acceleration = pcs.particles[p].position.scale(0.01); }};pcs.addSurfacePoints(model, 10000, PointColor.Color);pcs.buildMeshAsync().then(() => { model.dispose(); pcs.initParticles(); pcs.setParticles();});Updating Particles
Once the appropriate particle properties are initialized, the updateParticles method can be used. Unlike initParticles, this function is called by setParticles and already receives a particle as an argument. The setParticles method will execute only after the PCS mesh has been built, so it can safely be placed inside a render loop to produce an animation. For example:
pcs.updateParticle = function(particle) { particle.velocity.addInPlace(particle.acceleration); particle.position.addInPlace(particle.velocity);}
scene.registerBeforeRender(() => { pcs.setParticles();});Note: All particle positions are expressed in the local space of the PCS mesh.
The particle pivot vector is also in the local space of the PCS mesh. By default, rotations around a pivot are calculated by translating the particle to the pivot point, then rotating it, and then applying the inverse translation. By setting the particle property translateFromPivot to true (default false), rotations are calculated using only the initial translation followed by the rotation, leaving the particle at the translated location.
In the following playground, the particle pivots in the top PCS are set relative to the particle position, and in the lower one they are set in the same place.
Pivot Animation Example 1In this playground, the only difference is that the lower PCS has translateFromPivot set to true.
This playground animates the mesh, not the particles.
Immutable AnimationThis playground loads meshes from a file, converts them to particles, and animates them.
Loaded Mesh AnimationUVs
While setting and changing particle colors is straightforward, doing the same for UVs is a little more complex. Using BABYLON.PointColor.UV as a parameter within addSurfacepoints or addVolumePoints will set the UVs automatically based on the passed mesh.
You can also set the UV value for each particle using the function passed to addPoints. To make the texture map correctly, the UV values should relate in some way to the positional values for each particle. To apply the texture with these UVs, both the emissiveColor and emissiveTexture for the PCS mesh material must be set after the mesh is built.
Note: Only use emissive.
For example
import { Color3 } from "@babylonjs/core/Maths/math.color";import { Vector3 } from "@babylonjs/core/Maths/math.vector";var myfunc = function (particle) { var x = Math.random(); var y = Math.random(); var z = 0; particle.position = new Vector3(x, y, z); //Relate uv values to positional values particle.uv.x = x; particle.uv.y = y;};pcs.addPoints(5000, myfunc);pcs.buildMeshAsync().then(() => { pcs.mesh.material.emissiveColor = new Color3(0, 0, 0); pcs.mesh.material.emissiveTexture = myTexture;});It is possible to use a texture atlas, but you need to customize and calculate the more complex relationship between positional and UV values.
UV With Added Points UV With Texture AtlasRecycling Particles
You can write your own code to recycle particles using recycleParticle, which can be called from within updateParticle. For example:
import { Vector3 } from "@babylonjs/core/Maths/math.vector";pcs.recycleParticle = function (particle) { particle.position = Vector3.Zero(); particle.velocity = Vector3.Zero(); particle.heightLim = 4 + 0.5 * Math.random();};pcs.updateParticle = function (particle) { if (particle.position.y > particle.heightLim) { this.recycleParticle(particle); } particle.velocity.addInPlace(particle.acceleration); particle.position.addInPlace(particle.velocity);};Particle Parenting
Each particle can be given another particle as a parent.
The parent must be created before the child particle. This means the parent must have a lower index ID (particle.idx) than the current particle. So the first particle in the pool (idx = 0) cannot have a parent. To give a parent to a particle, just set its property parentId to the parent index ID value.
if (particle.idx > 0) { particle.parentId = particle.idx - 1; // the previous particle becomes the parent of the current one}To un-parent a particle, just set .parentId back to null which is the default value.
When a particle has a parent, its position and rotation are then expressed in its parent's local space.
if (particle.idx > 0) { particle.parentId = particle.idx - 1; // the previous particle becomes the parent of the current one // the particle position and rotation are expressed in the previous particle space, this one being already // rotated and translated from the yet previous particle. Etc. particle.rotation.z = 0.01; particle.position.x = 1.0;}Particle Intersections
The PCS provides a simple way to deal with intersections between a particle and other meshes. As this feature consumes more memory and CPU, do not include it unless necessary.
To use it, call the method intersectsMesh(target) (target is a mesh) for any particle to check whether it intersects the target.
It will simply return true or false depending on whether the particle intersects the target.
if (particle.intersectsMesh(anyMesh)) { // change properties of particle}By default, the check is carried out on the mesh's (AABB) bounding box. If you want to use the mesh's bounding sphere, add the parameter true.
if (particle.intersectsMesh(mesh, true) { // change properties of particle};