Nodragem

Solid Particle System UVs: particle[p].setVerticesData( )

Recommended Posts

Hello,

I am playing around with the Solid Particle System (SPS) and the performance looks great!

However I would like to show a different letter on the front face of each of my cubes, such as illustrated here:

image.thumb.png.793d12bd0e8e0afb97f961aa4c3f8caa.png

 

However, it seems that the property uvs (SPS.particles[p].uvs) does not allow me to do that. It is expecting a Vector4, while I was expecting to give an array such as the one given for Mesh.setVerticesData(BABYLON.VertexBuffer.UVKind, array).

Is it in theory possible to change the VerticesData of each particle as if they were meshes?

 

Share this post


Link to post
Share on other sites

https://playground.babylonjs.com/ts.html#9GFDZN#3

I just set some faceUVs on the model box before so only one face has UVs(0, 0, 1, 1)

then each particle can be given a part of the texture in the same way. You may have some error in your row, col computations. In my example, I just give (0, 0, 1, 1) to each particle

here's another example with some fixed, but computed, values with your initial texture so you can understand how to set the particle UVs : https://playground.babylonjs.com/ts.html#9GFDZN#4  

 

 

Share this post


Link to post
Share on other sites

Here is a version that gets close. Its in Javascript rather than typescript but might help you spot any issue with your PG. Doesn't deal with non-letters and letters are applied to all sides. Also I made the cuboids immovable but this can be removed.

https://www.babylonjs-playground.com/#UIIK1W

And here is one using an second SPS of labels in front of the cuboid

https://www.babylonjs-playground.com/#UIIK1W#1

Share this post


Link to post
Share on other sites

hey hey, this is working! Thank you very much to both of you!

I needed the trick that Jerome gave to project only on 1 face; then I also realised that my function getEventAtIndex(i) was not returning a number 😕 

Here I modified Jerome's PG: https://playground.babylonjs.com/ts.html#9GFDZN#6 

Ha! I see that you also did it meantime @JohnK :D I was too slow to answer!

 

So now, I have an other question. Would it be possible (and efficient) to add an update function to each particles, like this one:

Class RuneLetter{

player:Player;
particule:BABYLON.SolidParticle;

update(){
   if(this.player && BABYLON.Vector3.Distance(this.player.position, this.particle.position) < 1 && 
   this.particle.isVisible && !this.wasPickedUp){
            this.onPickedUp(this.data);
        }
        if(this.isRotating){
            this.mesh.rotate(BABYLON.Vector3.Up(), Math.PI/2000 * this.scene.getEngine().getDeltaTime())
        }
}

}

 

I need to check if there is an optimised way to detect collision with a particle and send a signal to the class RuneLetter.

Share this post


Link to post
Share on other sites

great, you found the easiest way by inversing the texture in the model :D just what I was about to suggest to you

The SPS can handle particle intersection directly : https://doc.babylonjs.com/how_to/solid_particle_system#particle-intersections

unless you just want pickability : https://doc.babylonjs.com/how_to/solid_particle_system#pickable-particles

not sure to understand what you want to achieve

Share this post


Link to post
Share on other sites

Yeah 😕 so I will try to clarify:

Imagine that you have the player character (for instance Mario) and you have collectables (like the coins in Mario).

I want to use SPS to manage the animation of my coins (i.e. they just rotate constantly), while detecting if Mario collided with a coin.

Hence, I think for now I want to use something like that:

var SPS = new SolidParticleSystem("CoinManager", scene, {particleIntersection: true, boundingSphereOnly: true});

SPS.particle[p].intersectsMesh(Mario);

I could also use this for the rotation animation:

// animation
var updateParticle = function(particle) {
    particle.rotation.y += 0.015;
};

SPS.updateParticle = updateParticle;
scene.registerBeforeRender(function() {
    SPS.setParticles();
});

And if I did not want to use the intersectMesh() function, I could check for the distance between mario and the collectables in updateParticle:

// animation
var updateParticle = function(particle) {
    particle.rotation.y += 0.015;
    if(BABYLON.Vector3.distance(Mario.position, particle.position) < 1){
        doSomething()
    }
};

SPS.updateParticle = updateParticle;
scene.registerBeforeRender(function() {
    SPS.setParticles();
});

 

I am going to try that last one...

Share this post


Link to post
Share on other sites

yep, that's the right way to go :)

The BoundingSphere intersection test actually also does a distance computation (just a little more accurate than the position/position one), it should be quite fast then too.

If your particle are quite immobile in the space, you could create some space partitioning and store where the particle are located, so you could check the intersection only against the particles in the same space section than the player character. Or use a quadtree (don't know you to use the BJS provided one)

Imagine something like this :

partX is an array, some equal sections of the X world axis, say, from 0 to 99 units per element. So partX[0] depicts all the x coordinates between 0 and 99.

And so on, partX[1] is from 100 to 199, etc. 

partX[0] = [idP1, idp25, idP56, idP67];   //  means that particle id1 has its position x in the range 0..99, idem for id25, etc

then do the same with partX[n]

and the same with partY[], then partZ[]

now, each frame, you know mario.position.x, y and z. If you divide x, y and z by 100 and get the Floor() of this, you know the particles in the same world section than Mario, right ?

indexX = Math.floor(mario.position.x / 100);

indexY = Math.floor(mario.position.y / 100);

indexZ = Math.floor(mario.position.z / 100);

=> particles near Mario : those thtat are in partX[indexX] and partY[indexY] and partZ[indexZ] , this is really a very fast search.

You can mark them directly when found :

 sps.particles[id].toCheck = true;

You can then just test the distance between Mario and the particle only for the ones (if any) fullfilling the former condition, not for all the particles.

just reset finaly each particle .toCheck to false in updateParticle()

 

 

Of course, this space partitioning is worth it if you have a big number of particles and if they don't move in the space.

Share this post


Link to post
Share on other sites

I tried to add some optimization by freezing some properties:

https://www.babylonjs-playground.com/#UIIK1W#6

Here what I added:

SPS.computeParticleRotation = true;       // prevents from computing particle.rotation
SPS.computeParticleTexture = false;        // prevents from computing particle.uvs
SPS.computeParticleColor = false;          // prevents from computing particle.color
SPS.computeParticleVertex = false;         // prevents from calling the custom updateParticleVertex() function
//SPS.mesh.freezeNormals();

The freezeNormals() makes the back side of the letter runes black.

Any other suggestions of optimisation?

Share this post


Link to post
Share on other sites

Yep, when the particles rotate (even just once), the normals are internally rotated, so they can't be frozen in this case.

You set the right optimizers in your case. Are you encountering some performance issues with less than 1000 boxes ? weird ...

If yes, what can be done then would be to act on your logic part :

- for instance, set only the particles needing to be updated in the current frame with setParticles(start, end)

if you manage more particles than the ones that are actually visible in the screen, then reduce the particle pool and recycle them. If recycling is too complex to implement, then set the not visible ones to invisible (invisible particles aren't processed to make the system faster, only their function updateParticles() is called ... just in case it would reset them to visible)

- if not necessary, don't call at all setParticle() each frame

etc

this chaotic example runs at 60 fps on my laptop : https://www.babylonjs-playground.com/#UIIK1W#7

Share this post


Link to post
Share on other sites

no I have no issues; I am just making more optimisation than necessary cause I target mobiles and tablets :)

by the way, when my player collects a coin, the coin should be killed. I did not find a way to dispose of a particle, so for now I do:

p.isVisible = false;

 

However, I think that the invisible particles should not be updated by setParticles(), so I tried:

p.isVisible = false;
p.alive = false;

However, it seems that p.alive = false is cancelling the effect of p.isVisible. Is that normal?

Share this post


Link to post
Share on other sites

alive false means that the particle is frozen in its current state (visible or not) and no longer computed until reset to true. It can used to define particles within a pool that won't evolve for a while and that don't need to be computed then.

isVisible false means the particle is  set in a first pass (first call to setParticles() when just set to invisible) in the same location than the camera, is scaled to zero (so no more in the frustum, not mathematically intersectable within anything else although its logical settings can be set and are saved (position, rotation, scaling, uvs, etc), then is no longer computed in the next passes (meaning next calls to setParticles() ) to improve the perfs.

Therefore isVisible = false has more effect than alive = false.

isVisible = false is the right way to go to fake a particle disposing : no garbage collector activity, no un-necessary computation, etc.

If you want to get a bit more speed, just exit updateParticles(), that is called anyway, immediatly for invisible particles :

SPS.updateParticle = function(p) {
    if (!p.isVisible && someCondition ) { return; }
    // your active particle logic then here
    ...
};

I added "someCondition" more because you need to keep a way to reset the particle to visible at some moment :)

 

[EDIT] you could of course reset the particle to visible also outside the function updateParticle() :

// somewhere in the code, nothing to do with SPS management

var p = sps.particles[thatOne];

if (SomeOtherCondition) { p.isVisible = true; }

Share this post


Link to post
Share on other sites

Thank you very much for your explanation, that is indeed very useful to know! :) I am using isVisible = false now.

Is it possible to prevent particles to be updated/visible based on whether they are behind a wall? My players are in a maze, so in fact I don't need to update what's behind walls.

 

Note that, it could still be useful to be able to definitely destroy a particle as they could have no need to be visible again. 

Also, it could be useful to have a data property on each particle, or to have observables to make it easier to link the particles to other objects in the game. For instance, here my particles represent letter runes that the player can collect. Hence, I need to know which letter my player have collected. For now, I organised my code so that I can use the particle's idx property to retrieve the letter that the particle represents. But alternatively, I could have store the letter in a data property. Or alternatively, I could have observed the collision of each particle with an observer storing the letter.

I mean, I am just giving ideas :D I think SPS is definitely a great tool to manage collectables in game level! I gonna use it all the time now.

Share this post


Link to post
Share on other sites

The SPS is designed to be agnostic about everything else : it doesn't know how the user wants to emit or not the particles, how they must move (physics or not), what they must depict or act for. This is a design choice in order to make it as versatile as possible. This choice, although it forces the user to make his own behavior implementation, has shown it was a good option until now to achieve this versatility.

So, if you want to make particles behind walls invisible, you will have to implement this as the SPS doesn't know anything about its environment. "Behind walls" implies a point a view, it is to say a camera position. If the camera is fixed, the walls are fixed, maybe it's easy to know if a particle is visible or not only by knowing its position in the space and without having to compute anything. In this case, it's probably worth it to set them as invisible when behind walls.

Else, if you have to check if a particle is in the camera frustum and if it's occulted or not by a wall, it will be probably far too much computation to do compared to the SPS computation dedicated to the hidden particles that wouldn't be set invisible. It's just a matter a choice depending on your very special case.

The need for storing extra properties for particles is obvious (what letter it represents in your case).

As you discovered it, it can be achieved by two different approaches :

- as you did, you store this extra property values in some structure besides the SPS, say an array storing objects (logicalParticles ?) of your own and indexed, by example, by the particleIdx. Yo can then easily access from a particle to a collection of data related to your own logic and vice versa.

- you could also extend the class SolidParticle (well, it's javascript) before creating your SPS and it will be compiled with the same performance then :

SolidParticle.prototype.letter = "";

SolidParticle.prototype.myOwnFunction = function() {

  this.letter = doSomething();

};

 

then create your SPS as usual and your added particle properties will be available 🙂

 

Share this post


Link to post
Share on other sites

Thank you for your detailed explanation!

Yep, so I guess it is not worth making my particles invisibles when behind walls.

Concerning the data storage; I am using Typescript, I think it is a bit more complicated to add a custom property on a object.

Would it impede the default performance if you added a property 'data:any' on your side, ready-to-use for the users?

 

Share this post


Link to post
Share on other sites

I don't think so because it's just some JS that is generated finally. This will rather impact the RAM consumption.

But ... as people can manage dozen thousands particles in their SPS, I wouldn't add a default property that could never be used because people don't need it.

I'm not a TypeScript expert, but I think there must be a way to re-open a class in order to add a property as JS allows it with prototypes (never forget that the final code is simply JS. Maybe some TS experts here could answer better than me.

Anyway, the notation with SolidParticle.prototype.property should still work, because TS is still JS.

If you can't modify the SolidParticle class and you don't want to manage a side array, maybe you could create your own objects (LogicalSolidParticles ?) either by extending the SolidParticle class, either by embedding the reference to the SolidParticle  instance (the SPS will treat only SolidParticles, not other objects) in your own object type holding its dedicated properties. Then you could process your logic on the LogicalSolidParticles and just set the linked SolidParticles according to what they are supposed to do. It's a bit the same approach than the side array one, but you manage a pool of logical objects, each refering its related SolidParticle instead.

There are plenty ways of doing.

 

It would nice anyway to have an elegant way to  reopen a class directly in TS.

Share this post


Link to post
Share on other sites

The three common ways would be :

- inheritance so that you can modify the base call functions

- creating a proxy which encapsulates the sps and drives the rest of your code

or in Typescript, you can extend any classes with class Augmentation: Look at Module augmentation: https://www.typescriptlang.org/docs/handbook/declaration-merging.html

 

Share this post


Link to post
Share on other sites

Cool! I did not know we could do that with TS :D I will totally use this next time.

Thanks for the ideas; for now I am happy with my array of data.

5 hours ago, jerome said:

This will rather impact the RAM consumption.

But ... as people can manage dozen thousands particles in their SPS, I wouldn't add a default property that could never be used because people don't need it.

Ha 😕 yeah, that is indeed not a good idea if that impedes the performance :) 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.