Influencer-based ParticleEmitter candidate (mesh-based animated particles)

@nehon said: @t0neg0d I'm sorry I'll be short on time this week end, I won't be able to work a lot on this. I peeked at the code and I agree with @zarch in every point.

On the API side, I would add that I’m not fond of the fact that you have to add the control to the rootNode and attach some node to it. This is not intuitive, and it denotes from the rest of JME API.
Intuitively, if I wanted to make a model emit particle, I would add the control to this model. The control would use its mesh as emitter shape.
This is mostly blender’s approach.
But maybe I’m overseeing things.

About the read/write things, don’t worry I’ll help you on this. Everything has to be savable if you want it to be saved in a j3o. It’s kind of vital because we want to be able to tweak those emitter in the SDK.

For now, let’s keep this in your repo, would you mind giving me commit access? I have to take full grasp on the system to be able to know how it will fit into JME.
Great work so far!

I can do this, just give me the email addy you want associated with it.

As for the separation between the control and where it is added. This is confusing as hell and definitely needs to change. I’m not sure how to handle this… the thought behind it was this (and how I handled it was wrong but I’m not sure how to best fix this):

  1. The mesh you use as a shape may or may not exist in your scene.
  2. For animated shapes to work, the mesh MUST exist in your scene.
  3. The setup process for following an animated mesh (i.e. getting the mesh from the scene you would like to use as an animated emitter shape) will have to be well documented. I tried to figure out a way to automate this. It was unsuccessful.

Now that what the emitter does is nailed down, the API can change to a more final version, it will just require a bit of hand-holding on your part, as I’m not totally sure what this should be to stay consistent with JME.

1 Like
@abies said: Regarding enums - it is bit of personal preference. I never put any 'business logic' into enums. Same example as zarch was giving - what if you need _different_ implementation of this code depending on type of mesh? Are you going to put processTriangle, processPoint, processQuad, processXYZ all in some enum? From one side, if you are sure that logic will be exactly the same, you have a saving (but same you can achieve by sharing this bit of code outside of enum). On the other hand, if implementations are different, you are coupling enum to every use of it, which sometimes might require putting embarrasing private details into the code inside enum.
@pspeed said: For what it's worth, I 100% agree with this.

Custom methods on enums are good for providing additional information about the type, ie: immutable concepts. Display names, bitmasks, whatever. Not so good for business logic.

a) switch/case blocks are not that expensive.
b) if it really feels like a good idea to put business logic into an enum then it isn’t an enum, it’s a strategy pattern.

I guess it depends on how you view and use it. As I said, it is not appropriate in all places. The example processTriangle, processPoint, processQuad, processXYZ is a good case of where not to do that.

However what you should do is identify what the common behaviour from inside those 4 is. The enum provides the generic behaviour and the specific implementation provides the rest.

Take this example - the enum is a description of an orientation of a particle. That’s what it does. Encapsulating the actual processing of orientation within the enum is not breaking encapsulation - it’s improving it. Now other things do not need to know anything about the orientation. They just trust the enum to know and let the magic happen.

It’s not really a performance thing to avoid the switch (although there is a small gain) - it’s a code stability thing. In the future if you add a new orientation then all you do is make sure the abstract methods are implemented and everything using those orientations picks them up with no need to hunt down switch statements. Yes, this is implementing a strategy pattern - and doing so using an enum which provides a nice and clean way to do so. (The only real disadvantage being that you can’t add new strategies without modifying the enum which may be either an advantage or a disadvantage depending on what you are doing)

1 Like

Hey all… I have a question concerning read/write when saving an emitter.

As it is right now, you can share influencers as updates are sequential and the influencers are built to not care where the particles are coming from.

For my benefit, I’ll break down what I just said to LD speak…

I can create a single RotationInfluencer and add it to 6 different emitters and it will handle each individually without corruption of data.

How would this work when saving and reloading an emitter?

1 Like
@zarch said: I guess it depends on how you view and use it. As I said, it is not appropriate in all places. The example processTriangle, processPoint, processQuad, processXYZ is a good case of where not to do that.

However what you should do is identify what the common behaviour from inside those 4 is. The enum provides the generic behaviour and the specific implementation provides the rest.

Take this example - the enum is a description of an orientation of a particle. That’s what it does. Encapsulating the actual processing of orientation within the enum is not breaking encapsulation - it’s improving it. Now other things do not need to know anything about the orientation. They just trust the enum to know and let the magic happen.

It’s not really a performance thing to avoid the switch (although there is a small gain) - it’s a code stability thing. In the future if you add a new orientation then all you do is make sure the abstract methods are implemented and everything using those orientations picks them up with no need to hunt down switch statements. Yes, this is implementing a strategy pattern - and doing so using an enum which provides a nice and clean way to do so. (The only real disadvantage being that you can’t add new strategies without modifying the enum which may be either an advantage or a disadvantage depending on what you are doing)

It’s not an enum anymore, then. It’s a strategy pattern. Why not let other things implement their own strategy also then. Enum was not meant to be abused this way.

1 Like
@zarch said: Yes, this is implementing a strategy pattern - and doing so using an enum which provides a nice and clean way to do so.

More to the point, what is the advantage over a more conventional strategy pattern? I’m trying to rack my brain on this and so far all I see is that it has a shorter keyword. If you aren’t going to use the abilities of an enumerated type then it’s all disadvantages and no advantages (other than “enum” being a shorter word than either “class” or “interface”)

1 Like

The semantics on the construction are simpler as well (as in creating all the sub types). Unless you are going to define all of them as inner classes within one file but even then you end up with a lot of boiler plate.

The main advantage is control, you have defined these strategies and those are the only ones you need to deal with. Also a weakness in some cases though. Enums also have the advantage of being nicely handled for you as singletons.

But yeah, whether you do it as an enum or as a strategy pattern doesn’t make a big difference. I guess its a stylistic thing. So maybe the solution here is to take out the enum and make it a strategy pattern :slight_smile:

Either way I don’t like the “switch on an enum” anti pattern :wink:

1 Like
@zarch said: Either way I don't like the "switch on an enum" anti pattern ;)

Then you don’t like enums because that is nearly the entire point of enums. It’s the only object you can switch/case on for a reason. It should be thought of as an int with benefits.

I would argue that it’s bad even in this use case. You’ve taken the concept of a “general type of thing” and made it a “specific type of thing for this one use case”. Where you might have been able to generally abstract the idea of a “billboard type”, now it is specific to particle systems and/or being passed three vectors. That’s only one of about half a dozen ways I could conceive of to implement billboarding in general. Which is why business logic doesn’t belong in an enum. It is entirely against the point of an enumerated type.

And to be pedantic, there is almost no extra boiler plate to doing an abstract class with inner class implementations. The extra code over enum amounts to a dozen characters or so.

Generally, any decision train today that goes like:
“It doesn’t matter much either way.”
“But this way limits what we can do in the future.”
“Oh, we’ll never need to do that.”

…makes me shudder a little. If it didn’t matter much either way then why pretend we can predict the future. :wink:

1 Like

Um…does anyone disagree with the following?

– Quads only require billboarding. Nothing else ever will because a point is always facing the camera and a 3d particle would take care of itself.

Or am I missing something fundamental here?

If the above is true… then billboarding could be wrapped in a box of Fruit Loops and it wouldn’t matter because only the ParticleDataTriMesh class will ever use it.

Or has this conversation grown beyond this?

1 Like

I was thinking that billboarding may need to change to handle static particles, but the more I consider this, the more I am thinking that is not true.

Example:

A leaf would be a static particle billboarded Velocity_Z_up. To make the leaf blow in the wind, you simply need to adjust it’s velocity vector to point in different directions. This would keep the stem of the leaf anchored where it originally was and appear to be effected by wind.

Yeah, I’m rambling… but it is applicable if this conversation is still concerning what the particle emitter can/will do.

1 Like

Before I go any further I just want to re-iterate. I don’t think they are right for all cases. I think they are right for some cases, but they are a perfectly valid and useful tool and should not be dismissed out of hand.

Switching on enums only works if the enum and the thing doing the switching are tightly coupled. Which entirely removes your point about being generic anyway.
i.e. lets say this particle system adds its own list of billboard types - now we update the BillboardControl to use these types since they are generic and shared. All well and good.

Now the particle system adds a new entry to the enum and updates itself…but what about the entirely unrelated billboard control? Does that need to be updated too? How does someone know to update it? What happens if you don’t? In the best case you are going to get an undocumented NotYetImplemented exception thrown (assuming the control implements remembered to add a default case and throw the exception). Now imagine the thing using the enum was in a plugin, not even part of the main jme core! Re-using enums is bad.

Having said that doing it as a strategy using abstract inner classes would make a lot of sense. The only advantage it actually offers is that people can implement their own strategies without needing to modify the library but I guess that’s not a bad thing to offer.

Advantages of enums vs abstract classes:
You know that every behaviour is defined in that location (could also be done using restricted access constructors and/or factories but if you are going to do that might as well just use the enum). If you are making changes you know that they cannot break anything else and that you cannot forget some random implementation somewhere else entirely.
This means you know that the behaviour being passed to you is one you have defined.
Less boilerplate code (not a huge amount less but it is noticably less. In cases where the logic is quite small it could actually be a quarter file length).
Java takes care of all your initialization, creating singletons, etc.

Disadvantages:
They cannot be extended without modifying the enum itself (but see above - that can also be an advantage).

Enums and the strategy patten vs switch on enum:
Much better future modification proofing and stability
Marginally faster performance
In general better control for you the library developer over how it is used and what it does.

For example lets say you make the business logic method package private and you define the enum. Now someone outside using it cannot even see the logic exists, they just pass in an enum. Internally the method is called but since that API is not exposed you can make any future changes you like (for example changing the method signature) and know that you are not going to break the code for anyone using the library.

1 Like
@t0neg0d said: I was thinking that billboarding may need to change to handle static particles, but the more I consider this, the more I am thinking that is not true.

Example:

A leaf would be a static particle billboarded Velocity_Z_up. To make the leaf blow in the wind, you simply need to adjust it’s velocity vector to point in different directions. This would keep the stem of the leaf anchored where it originally was and appear to be effected by wind.

Yeah, I’m rambling… but it is applicable if this conversation is still concerning what the particle emitter can/will do.

Yeah, we are debating a general point of java style/usage now - not specific to this case. :slight_smile:

Are the meshes triangles at the moment? Is there any demand for quad meshes? Have you considered doing the billboarding in the geometry shader rather than in CPU at all?

1 Like
@zarch said: ** snipped long post describing why implementing a strategy pattern with enums is sometimes a good idea **

You can do it. You should never do it, though. If you want to implement a strategy pattern then implement one. If you want to implement an enumerated type then implement one. The fact that you can use an enumerated type to do it doesn’t mean that it’s right. You are using a screwdriver like a hammer.

Every “nice thing” you talked about why using an enum is “right sometimes” can be done with regular classes. Every one of them. The difference is that you aren’t handing the caller a screwdriver and pointing it to a bunch of nails. You’re giving them a hammer. They don’t have to guess about what other magic might exist.

You have accomplished one thing, though. I went from thinking you should rarely include business logic in an enum to believing you should never do it. :slight_smile:

1 Like

Should without reasons is faith not science. So far you haven’t actually refuted my points, and while you raised some very valid points as to why some cases should not have business logic in enums I’ve never denied that.

Yes you can implement everything I describe with regular classes, in fact I said so in my post, but enum already implements it for you so why re-invent the wheel? You are right on one thing, your hammer will get everything you want into the wood. Unfortunately some of the things you are hammering in are screws…so you may find using a screwdriver makes it a little easier and produces a neater job :wink:

Anyhow, both sides have made their case so I’ll drop it here. @t0neg0d - leave it as the switch on a case if you like, alternatively implement it as a strategy pattern not an enum at all.

1 Like
@zarch said: Should without reasons is faith not science. So far you haven't actually refuted my points, and while you raised some very valid points as to why some cases should not have business logic in enums I've never denied that.

Yes you can implement everything I describe with regular classes, in fact I said so in my post, but enum already implements it for you so why re-invent the wheel? You are right on one thing, your hammer will get everything you want into the wood. Unfortunately some of the things you are hammering in are screws…so you may find using a screwdriver makes it a little easier and produces a neater job :wink:

Anyhow, both sides have made their case so I’ll drop it here. @t0neg0d - leave it as the switch on a case if you like, alternatively implement it as a strategy pattern not an enum at all.

Yes, but the funny part is… to continue the metaphor, you’ve argued for using a screw driver as a hammer but never using it for screws (switch/case in this case). You’ve mixed the metaphor anyway. In this case, strategy pattern was the nails and switch/case was the screw.

It is funny that the only thing that you can’t implement with regular classes that you can with enums is the switch/case. Food for thought.

1 Like
@zarch said: Yeah, we are debating a general point of java style/usage now - not specific to this case. :)

Are the meshes triangles at the moment? Is there any demand for quad meshes? Have you considered doing the billboarding in the geometry shader rather than in CPU at all?

The Triangle mesh is a quad. It’s named as such, because that’s what the original was named. I think it was to slap you up side the head with THIS IS A TRIANGLE BASED MESH, NOT A POINT!!!

As for billboarding on the GPU, two reasons this has not been considered.

  1. We have no access to the geometry shader… and all particles are one big mesh.
  2. It would require altering one of the core shaders (Particle) and it’s not necessary, so I didn’t go there.

As for current updates:

  1. There is only a single constructor now that requires you to pass in the influencers.
  2. setSpatial now works like it should… no need to add the emitter node manually. The spatial you add the control to is the spatial it ends up in.
  3. Mesh is no longer a param of the constructor, you now select from a shape type enum (currently this consists of:
  • Simple - creates a single triangle emitter mesh (you can override this by calling setShape(Mesh mesh))
  • Custom - requires you to specify the shape by calling setMesh(Mesh mesh)

The default shapes will be expanded to include Sphere & Hemisphere at a minimum.

EDIT: You don’t have to pass in any influencers… you can use addInfluencer() after the fact.

1 Like

Um… I need some thought power behind the shape thing. This (once again, could be excluded from the constructor and may need to be considered, because of the following:

For every default shape, at a minimum a size will need to be specified.

Reason?

Currently, particles are emitted from the direct center of the randomly selected face… however… this needs to be configurable. You should be able to specifiy that the emitter selects a random point on the face to emit from. Or… always use the center.

Why?

Because static partcles will require dispersal based on the size and coverage of said face. Otherwise you would have an ass-load of fur growing out of detailed areas and one hair growing out of larger faces.

I hope this is making sense to people.

1 Like

Hmm, I’d rename it to Quad mesh then.

Would there be any use for a real triangle mesh? It would have 1 less vertex and half as many triangles per particle. I can see how it would work in some cases but not others and I’m not sure if the gains are actually worthwhile so I’m just throwing it out there.

1 Like
@t0neg0d said: 3. Mesh is no longer a param of the constructor, you now select from a shape type enum (currently this consists of: * Simple - creates a single triangle emitter mesh (you can override this by calling setShape(Mesh mesh)) * Custom - requires you to specify the shape by calling setMesh(Mesh mesh)

Trying to get this straight: you have a two value enum to decide if the user has set a custom mesh or not? Isn’t the mesh enough?

What’s the difference between setShape(mesh) and setMesh(mesh)?

1 Like
@pspeed said: Trying to get this straight: you have a two value enum to decide if the user has set a custom mesh or not? Isn't the mesh enough?

What’s the difference between setShape(mesh) and setMesh(mesh)?

Actually… no… I have an enum that will consist of:

Simple,
Sphere,
Hemisphere,
HugeAssShapedEmitter (etc, etc),
Custom

=) It just doesn’t atm.

And the setMesh setShape thing was a typo… sorry!!

1 Like
@zarch said: Hmm, I'd rename it to Quad mesh then.

Would there be any use for a real triangle mesh? It would have 1 less vertex and half as many triangles per particle. I can see how it would work in some cases but not others and I’m not sure if the gains are actually worthwhile so I’m just throwing it out there.

Honestly, I don’t know!

It would be hard to map a texture to a single triangle though.

1 Like