Particle System #2728

Merged
merged 4 commits into from May 6, 2017

Conversation

@MaxBorsch
Contributor

MaxBorsch commented Jan 3, 2017

New core particle system. Currently fixing & optimizing & planning.

  • Multiple renderers
  • Particle system does not stop rendering when far away (particlesystems don't dispose)
  • Convert legacy particle stuff (block effects mostly)
  • Long-range particlesystem registry, "Chimney-problem"
  • Optimize updater to partition ParticlePool based on emissionRate of emmiters, not evenly.
  • Force-fields
  • Create documentation
  • Finish annotating code
  • Create tests

fireworks

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub Jan 3, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented Jan 3, 2017

Hooray Jenkins reported success with all tests good!

@MaxBorsch MaxBorsch closed this Jan 3, 2017

@MaxBorsch MaxBorsch reopened this Jan 3, 2017

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Jan 3, 2017

Member

Thanks!

@emanuele3d this should be the redo with the smaller base than #2632 - 21 files vs 54. Hopefully that'll make review a more smooth process :-)

Member

Cervator commented Jan 3, 2017

Thanks!

@emanuele3d this should be the redo with the smaller base than #2632 - 21 files vs 54. Hopefully that'll make review a more smooth process :-)

@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 Benjamin Glatzel <benjamin.glatzel@me.com>

This comment has been minimized.

@Cervator

Cervator Jan 3, 2017

Member

Feel free to replace these older license statements with the latest one - even can go to 2017 now!

Same thing if you come across any @author tags - we don't use them anymore.

@Cervator

Cervator Jan 3, 2017

Member

Feel free to replace these older license statements with the latest one - even can go to 2017 now!

Same thing if you come across any @author tags - we don't use them anymore.

This comment has been minimized.

@MaxBorsch

MaxBorsch Jan 4, 2017

Contributor

Fixed, will push with multiple renderers patch. I already replaced all of the @author / outdated headers. I missed the shaders since they're in the assets directory.

@MaxBorsch

MaxBorsch Jan 4, 2017

Contributor

Fixed, will push with multiple renderers patch. I already replaced all of the @author / outdated headers. I missed the shaders since they're in the assets directory.

@rzats rzats added the GCI label Jan 3, 2017

@MaxBorsch

This comment has been minimized.

Show comment
Hide comment
@MaxBorsch

MaxBorsch Jan 4, 2017

Contributor

@emanuele3d On the old PR you said that it was a problem that this doesn't use RenderSystem. It does, the manager implements RendersSystem, and on a render call loops over all of the renderers and passes them the systems that they need to render (in my local version with multiple renderers that is, will push soon.)

Also, I'm not sure how the DAG rendering system works, (a wiki page with at least a tiny explanation would be nice!) But I was wondering on what call we want particles rendered on. Currently everything is on drawAlphaBlended.

Solution 1:
Have ParticleRenderers implement RenderSystem and call every draw method on each ParticleRenderer from every draw method in the manager. (one to one) So the drawAlphaBlended method in the manager will loop over all particle renderers and call their drawAlphaBlended method.

Solution 2:
Make a particles render node / call? Particles can have varying drawing methods though, so I'm not sure if it makes sense to generalize all particles to a particles rendering node / call. I may be wrong, I'm not sure how you have the rendering nodes system organized.

Thank you in advance for any explanations.

Contributor

MaxBorsch commented Jan 4, 2017

@emanuele3d On the old PR you said that it was a problem that this doesn't use RenderSystem. It does, the manager implements RendersSystem, and on a render call loops over all of the renderers and passes them the systems that they need to render (in my local version with multiple renderers that is, will push soon.)

Also, I'm not sure how the DAG rendering system works, (a wiki page with at least a tiny explanation would be nice!) But I was wondering on what call we want particles rendered on. Currently everything is on drawAlphaBlended.

Solution 1:
Have ParticleRenderers implement RenderSystem and call every draw method on each ParticleRenderer from every draw method in the manager. (one to one) So the drawAlphaBlended method in the manager will loop over all particle renderers and call their drawAlphaBlended method.

Solution 2:
Make a particles render node / call? Particles can have varying drawing methods though, so I'm not sure if it makes sense to generalize all particles to a particles rendering node / call. I may be wrong, I'm not sure how you have the rendering nodes system organized.

Thank you in advance for any explanations.

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub Jan 5, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented Jan 5, 2017

Hooray Jenkins reported success with all tests good!

@emanuele3d

This comment has been minimized.

Show comment
Hide comment
@emanuele3d

emanuele3d Jan 5, 2017

Contributor

A couple of quick answers as I can't do a full review right now:

Documentation: high level documentation is problematic at this stage because the system is in a state of flux. It starts to make sense when the DAG-based architecture has stabilized enough to present an API, which is one of the goals of the renderer overhaul. Meanwhile, you might get some high level information in issue #2547. Furthermore, every PR I published of late has added a fair amount of low level documentation, not to mention a focus on self-documenting code - I'm trying to do this for every java file I touch. So, albeit slowly, it's all happening, but it's a long term project and in the short term you'll have to dig into the code.

Regarding the options 1-2 you mention: the "correct" approach is to go through RenderSystems. That's consistent with the current architecture: if you need something to get rendered you should have one or more classes implementing one or more methods of the RenderSystem interface. For example you'd have a set of particles needing alpha blending and those would be rendered through a class implementing the renderAlphaBlend() method. Another set of particles might be fully opaque instead and you'd render them through a system implementing the renderOpaque() method. And in some circumstances the same set of particles might need to be rendered multiple times through multiple methods, all requiring an implementation. The important thing is that particles requiring the same opengl state (i.e. the same shader, face culling enabled/disabled, etc..) should be rendered, as much as possible, within the same RenderSystem.render*() call.

For this purpose I seem to remember you can use the @RegisterSystem decorator or something similar for your instances to self-register. This way the renderer takes care of iterating through your particles calling the methods of the RenderSystem interface at the appropriate time.

New nodes are really only useful to add things that can't be rendered through the existing nodes or that must be done between existing nodes, for whatever reason.

Contributor

emanuele3d commented Jan 5, 2017

A couple of quick answers as I can't do a full review right now:

Documentation: high level documentation is problematic at this stage because the system is in a state of flux. It starts to make sense when the DAG-based architecture has stabilized enough to present an API, which is one of the goals of the renderer overhaul. Meanwhile, you might get some high level information in issue #2547. Furthermore, every PR I published of late has added a fair amount of low level documentation, not to mention a focus on self-documenting code - I'm trying to do this for every java file I touch. So, albeit slowly, it's all happening, but it's a long term project and in the short term you'll have to dig into the code.

Regarding the options 1-2 you mention: the "correct" approach is to go through RenderSystems. That's consistent with the current architecture: if you need something to get rendered you should have one or more classes implementing one or more methods of the RenderSystem interface. For example you'd have a set of particles needing alpha blending and those would be rendered through a class implementing the renderAlphaBlend() method. Another set of particles might be fully opaque instead and you'd render them through a system implementing the renderOpaque() method. And in some circumstances the same set of particles might need to be rendered multiple times through multiple methods, all requiring an implementation. The important thing is that particles requiring the same opengl state (i.e. the same shader, face culling enabled/disabled, etc..) should be rendered, as much as possible, within the same RenderSystem.render*() call.

For this purpose I seem to remember you can use the @RegisterSystem decorator or something similar for your instances to self-register. This way the renderer takes care of iterating through your particles calling the methods of the RenderSystem interface at the appropriate time.

New nodes are really only useful to add things that can't be rendered through the existing nodes or that must be done between existing nodes, for whatever reason.

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub Jan 5, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented Jan 5, 2017

Hooray Jenkins reported success with all tests good!

@MaxBorsch

This comment has been minimized.

Show comment
Hide comment
@MaxBorsch

MaxBorsch Jan 5, 2017

Contributor

@emanuele3d Thanks for the explanation. I rewrote the multiple renderers scheme to work more like you explained. Here's how you would go about making a custom particle renderer now:

  1. Create a system extending ParticleRenderer (which implements RenderSystem).
  2. Use any of the RenderSystem render...() methods in your system.
  3. ParticleRenderer provides a getParticleSystems() method that returns a filtered stream of ParticleSystemStateDatas that are supposed to be rendered by your renderer. Handle those however you want in your system.

In order for a particle system state data to be sent to a certain particle renderer, you just have to set its renderer.
ParticleSystemComponent.setRenderer (customParticleRenderer.class)

Contributor

MaxBorsch commented Jan 5, 2017

@emanuele3d Thanks for the explanation. I rewrote the multiple renderers scheme to work more like you explained. Here's how you would go about making a custom particle renderer now:

  1. Create a system extending ParticleRenderer (which implements RenderSystem).
  2. Use any of the RenderSystem render...() methods in your system.
  3. ParticleRenderer provides a getParticleSystems() method that returns a filtered stream of ParticleSystemStateDatas that are supposed to be rendered by your renderer. Handle those however you want in your system.

In order for a particle system state data to be sent to a certain particle renderer, you just have to set its renderer.
ParticleSystemComponent.setRenderer (customParticleRenderer.class)

@emanuele3d

This comment has been minimized.

Show comment
Hide comment
@emanuele3d

emanuele3d Jan 5, 2017

Contributor

I'll review everything as soon as I can.

Contributor

emanuele3d commented Jan 5, 2017

I'll review everything as soon as I can.

@flo

Overall I think it looks very promising.

What I would see as very important is that it would actually replace the existing particle system.

That way we would avoid having 2 particle systems and it would be good evaluation of this particle system.

Also please then add an explanation of how to test this PR ingame. e.g. mine a block or run the command spawnPrefab dustEffect once that effect uses the new particle system.

Also I think you could describe the highlights of the PR in the description since I think it has quite a bit to offer :)

+ /**
+ * The maximum spawn rate of this emitter
+ */
+ public float spawnRateMax = 11.0f;

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

What does a spawn rate of 11 mean? 11 particles per frame, 11 particles particles per second, 11 ms between a particles or 11 frames between particles?

@flo

flo Jan 21, 2017

Contributor

What does a spawn rate of 11 mean? 11 particles per frame, 11 particles particles per second, 11 ms between a particles or 11 frames between particles?

+ /**
+ * The maximum life time of this emitter, the emitter will auto-remove upon reaching 0 TODO: Implement emitter lifetime
+ */
+ public float maxLifeTime = Float.POSITIVE_INFINITY;

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

Life time in seconds?

I think this field should be removed till it got implemented. e.g. via animation of the enabled attribute it would already be possible to control when particles get emitted.

@flo

flo Jan 21, 2017

Contributor

Life time in seconds?

I think this field should be removed till it got implemented. e.g. via animation of the enabled attribute it would already be possible to control when particles get emitted.

+ * Also maintains a registry of generator and affector functions to be used when processing generators
+ * and affectors during a particle system update.
+ */
+public interface ParticleSystemManager {

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

I think that is pretty cool that your system allows for arbitrary emitters and generators. Maybe you could add default ones (if there are some already) to the javadoc as an excemple. Also it would be good to mention where the methods of this interface should be called and if it can be obtained via @In injection.

@flo

flo Jan 21, 2017

Contributor

I think that is pretty cool that your system allows for arbitrary emitters and generators. Maybe you could add default ones (if there are some already) to the javadoc as an excemple. Also it would be good to mention where the methods of this interface should be called and if it can be obtained via @In injection.

+ emitters.clear();
+ }
+
+ // Thanks IntelliJ ;)

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

what did it do that was so great?

@flo

flo Jan 21, 2017

Contributor

what did it do that was so great?

This comment has been minimized.

@skaldarnar

skaldarnar Mar 11, 2017

Member

probably fancy stream -> map -> collect pipeline? 👨‍🏫

@skaldarnar

skaldarnar Mar 11, 2017

Member

probably fancy stream -> map -> collect pipeline? 👨‍🏫

+ /**
+ * The lifetime of this system
+ */
+ public float maxLifeTime = Float.POSITIVE_INFINITY;

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

If we would keep this field it would be good to specifiy the unit of the duration.

However since there is a LifespanComponent I don't think we need maxLifeTime. So I would suggest we remove that field.

@flo

flo Jan 21, 2017

Contributor

If we would keep this field it would be good to specifiy the unit of the duration.

However since there is a LifespanComponent I don't think we need maxLifeTime. So I would suggest we remove that field.

+
+ updateParticles(partSys, delta); // Update lifetime and Affectors
+
+ checkCollision(partSys.particlePool, partSys.collisionUpdateIteration);

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

This should be made optional at some point as it is expensive

@flo

flo Jan 21, 2017

Contributor

This should be made optional at some point as it is expensive

+ /**
+ * Destroy this particle system when its last emitter is destroyed TODO: Unimplemented
+ */
+ public boolean destroyWhenFinished = true;

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

Isn't this field redundant to maxLifeTime. e.g. destroyWhenFinished = (maxLifeTime != Float.POSITIVE_INFINITY) ?

(When there would be a limit to the amount of emitted particles then this would make sense but I think we don't need that particle limit feature for now)

@flo

flo Jan 21, 2017

Contributor

Isn't this field redundant to maxLifeTime. e.g. destroyWhenFinished = (maxLifeTime != Float.POSITIVE_INFINITY) ?

(When there would be a limit to the amount of emitted particles then this would make sense but I think we don't need that particle limit feature for now)

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

I don't think emitters or particle systems should auto-destroy. Whatever creates them should be responsible for cleaning up if and when necessary. Auto-destroy features just complicate the code and make an assumption on the benefits of destroying things just because they are out of action. They might well be, but it might be temporary and the developer might prefer to keep a particle effects in memory even though at that stage is not doing anything or has no emitters attached to it.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

I don't think emitters or particle systems should auto-destroy. Whatever creates them should be responsible for cleaning up if and when necessary. Auto-destroy features just complicate the code and make an assumption on the benefits of destroying things just because they are out of action. They might well be, but it might be temporary and the developer might prefer to keep a particle effects in memory even though at that stage is not doing anything or has no emitters attached to it.

This comment has been minimized.

@MaxBorsch

MaxBorsch Mar 7, 2017

Contributor

@flo @emanuele3d Don't most of our current particle uses need a particle limit with auto-dispose? For example dustEffect creates a few clouds and disposes, and when you break a block it creates a few "block fragments" and disposes.

@MaxBorsch

MaxBorsch Mar 7, 2017

Contributor

@flo @emanuele3d Don't most of our current particle uses need a particle limit with auto-dispose? For example dustEffect creates a few clouds and disposes, and when you break a block it creates a few "block fragments" and disposes.

This comment has been minimized.

@emanuele3d

emanuele3d Mar 7, 2017

Contributor

I think it should be the choice of the effect's developer.

For a small effect like the one you are mentioning disposal might not impact performance and auto-disposal wouldn't probably be a problem. In the specific case I probably still wouldn't use auto-disposal because breaking a block is a fairly common operation and the memory footprint is likely to be small anyway. For less frequent effects, say a campfire, or a comet, autodisposal might make sense.

But I'm thinking about larger effects such as rain and snow. I wouldn't want to reallocate the memory for all those particles and the associated control systems every time it has to start raining or snowing. Notice that if done GPU-side these effects might realistically have tens of thousands of particles without much loss in frame rate.

@emanuele3d

emanuele3d Mar 7, 2017

Contributor

I think it should be the choice of the effect's developer.

For a small effect like the one you are mentioning disposal might not impact performance and auto-disposal wouldn't probably be a problem. In the specific case I probably still wouldn't use auto-disposal because breaking a block is a fairly common operation and the memory footprint is likely to be small anyway. For less frequent effects, say a campfire, or a comet, autodisposal might make sense.

But I'm thinking about larger effects such as rain and snow. I wouldn't want to reallocate the memory for all those particles and the associated control systems every time it has to start raining or snowing. Notice that if done GPU-side these effects might realistically have tens of thousands of particles without much loss in frame rate.

This comment has been minimized.

@MaxBorsch

MaxBorsch Mar 7, 2017

Contributor

@emanuele3d It is the choice of the developer as it is. By default particle systems need to be disposed of manually, I haven't yet added auto-disposal, that boolean is just something left over from Linus's implementation. Sorry for the confusion.

@MaxBorsch

MaxBorsch Mar 7, 2017

Contributor

@emanuele3d It is the choice of the developer as it is. By default particle systems need to be disposed of manually, I haven't yet added auto-disposal, that boolean is just something left over from Linus's implementation. Sorry for the confusion.

This comment has been minimized.

@emanuele3d

emanuele3d Mar 7, 2017

Contributor

No worries. Let's remove it then: disposal functionality is very useful because particles can involve a complex set of objects and relationships between them. Auto-disposal is not worth the effort in my opinion, or can be left for much later.

@emanuele3d

emanuele3d Mar 7, 2017

Contributor

No worries. Let's remove it then: disposal functionality is very useful because particles can involve a complex set of objects and relationships between them. Auto-disposal is not worth the effort in my opinion, or can be left for much later.

This comment has been minimized.

@flo

flo Mar 15, 2017

Contributor

The LifespanComponent can be used for now to limit the timespan of particles.

@flo

flo Mar 15, 2017

Contributor

The LifespanComponent can be used for now to limit the timespan of particles.

This comment has been minimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I agree.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I agree.

+ */
+ public void setRenderer(Class<ParticleRenderer> renderer) {
+ this.renderer = renderer;
+ requestUpdate();

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

Components should not contain logic. Instead make the system listen for a change of the component via OnChangedComponent.

@flo

flo Jan 21, 2017

Contributor

Components should not contain logic. Instead make the system listen for a change of the component via OnChangedComponent.

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And I'm not sure a ParticleSystemComponent should hold a renderer. For example the same particles might need to be rendered in more than one way - by more than one renderer.

Rather, a registered RenderSystem should iterate and render all entities having a specific ParticleDataComponent and/or a specific ParticleRenderingComponent attached, the latter providing information shared by all particles that need to be rendered in a certain way, i.e. as the flames of a torch.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And I'm not sure a ParticleSystemComponent should hold a renderer. For example the same particles might need to be rendered in more than one way - by more than one renderer.

Rather, a registered RenderSystem should iterate and render all entities having a specific ParticleDataComponent and/or a specific ParticleRenderingComponent attached, the latter providing information shared by all particles that need to be rendered in a certain way, i.e. as the flames of a torch.

+ private List<EntityRef> emitters = new LinkedList<>();
+
+ @Owns
+ private List<EntityRef> affectors = new LinkedList<>();

This comment has been minimized.

@flo

flo Jan 21, 2017

Contributor

hmm I thought emitters and affectors would each be represented by a component.

Since those fields are just implementation details I suggest you keep track of them via a map with particle system entity to value map that gets updated when the ParticleSystemComponent component gets activated or deactivated. See OnActivatedComponent and BeforeDeactivateComponent. For an example you can look at `NameTagClientSystem. That way we only get OnChangedComponent events when a actual public component field changes. Everytime a component of a persistent entity gets changed, the component gets copied for the background saving task. So we want to avoid any unnecessary component modifications events. Also it keeps the component classes simple which makes future changes of how we do components easier and helps developers that just want to get a quick overview of a component.

@flo

flo Jan 21, 2017

Contributor

hmm I thought emitters and affectors would each be represented by a component.

Since those fields are just implementation details I suggest you keep track of them via a map with particle system entity to value map that gets updated when the ParticleSystemComponent component gets activated or deactivated. See OnActivatedComponent and BeforeDeactivateComponent. For an example you can look at `NameTagClientSystem. That way we only get OnChangedComponent events when a actual public component field changes. Everytime a component of a persistent entity gets changed, the component gets copied for the background saving task. So we want to avoid any unnecessary component modifications events. Also it keeps the component classes simple which makes future changes of how we do components easier and helps developers that just want to get a quick overview of a component.

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

It does make sense that EmittersComponents are in their own entities. Emitters for example often have a location. Of course you could have the location stored in the emitter itself, but in the context of the ES it would make sense for it to be stored in a LocationComponent alongside an EmitterComponent.

Similarly, affectors can also be associated with a location component within an entity. This would allow for a turbulence field to affect particles only within some distance from the coordinates given by the LocationComponent.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

It does make sense that EmittersComponents are in their own entities. Emitters for example often have a location. Of course you could have the location stored in the emitter itself, but in the context of the ES it would make sense for it to be stored in a LocationComponent alongside an EmitterComponent.

Similarly, affectors can also be associated with a location component within an entity. This would allow for a turbulence field to affect particles only within some distance from the coordinates given by the LocationComponent.

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Feb 5, 2017

Member

Bump - @MaxBorsch have you had a chance to check this one out lately? :-)

Member

Cervator commented Feb 5, 2017

Bump - @MaxBorsch have you had a chance to check this one out lately? :-)

@emanuele3d

I need to interrupt the review here as it's already 2:22am and I better go to sleep.

I will try and finish the review tomorrow or in the next few days.

+package org.terasology.particles;
+
+/**
+ * Data mask used internally by the particle system.

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

There is no "internal" when you are browsing the particle system code.

Please explain more verbosely what the purpose of this object is.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

There is no "internal" when you are browsing the particle system code.

Please explain more verbosely what the purpose of this object is.

+ VELOCITY(0b0010000),
+ SCALE(0b0100000),
+ COLOR(0b1000000),
+ ALL(0b1111111);

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

The problem I see with this list of attributes is that they are very arbitrary.

On one hand you have typical particle attributes such as position and velocity. In the middle you have relatively useful things such as scale and previous position. But then you get into specialized territory with color, energy and texture offset.

There are particles effect that do not need textures, color, scaling (let alone energy) and I can even think of particles that do not need velocity (i.e. a static starfield).

Ideally the general architecture would keep in account that different effects will need different particle attributes, many of which you won't be able to anticipate.

Perhaps each of the attributes you deal with here should go in separate components, i.e. a ParticlePositionComponent, a ParticleVelocityComponent etc, each storing arrays that the systems can iterate over. This way if I want to make a campfire I will create an entity with position, velocity, scale, color, etc, while if I want falling snow I won't use scale and color but I will use snowflake textures.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

The problem I see with this list of attributes is that they are very arbitrary.

On one hand you have typical particle attributes such as position and velocity. In the middle you have relatively useful things such as scale and previous position. But then you get into specialized territory with color, energy and texture offset.

There are particles effect that do not need textures, color, scaling (let alone energy) and I can even think of particles that do not need velocity (i.e. a static starfield).

Ideally the general architecture would keep in account that different effects will need different particle attributes, many of which you won't be able to anticipate.

Perhaps each of the attributes you deal with here should go in separate components, i.e. a ParticlePositionComponent, a ParticleVelocityComponent etc, each storing arrays that the systems can iterate over. This way if I want to make a campfire I will create an entity with position, velocity, scale, color, etc, while if I want falling snow I won't use scale and color but I will use snowflake textures.

+ // Per particle 4d vectors
+ public final float[] color;
+
+ //== private attributes =============================

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Except the next line is a public one?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Except the next line is a public one?

+ return firstDeadParticleIndex - 1;
+ }
+
+ public void moveDeceasedParticle(final int index) {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Rather than explain it in a comment I'd write in the javadoc:

/**
 * Particles may die. This can happen when they have a finite lifespan, when they collide with something 
 * or when they exit a given volume of space - to make a few examples.
 *
 * A dead particle's data is normally useless and constitutes a hole in the arrays of particle attributes. 
 * So, when a particle die, we take the data from the last particle in the array and we move it to 
 * the index of the particle that just died. This way the particle data related to living particles remain 
 * contiguous in the data arrays.
 */

Question about this recycling strategy: did you come up with it yourself or did you read about it somewhere?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Rather than explain it in a comment I'd write in the javadoc:

/**
 * Particles may die. This can happen when they have a finite lifespan, when they collide with something 
 * or when they exit a given volume of space - to make a few examples.
 *
 * A dead particle's data is normally useless and constitutes a hole in the arrays of particle attributes. 
 * So, when a particle die, we take the data from the last particle in the array and we move it to 
 * the index of the particle that just died. This way the particle data related to living particles remain 
 * contiguous in the data arrays.
 */

Question about this recycling strategy: did you come up with it yourself or did you read about it somewhere?

+ final int index4 = 4 * index;
+
+ // scalars
+ if (ParticleDataMask.ENERGY.isEnabled(rawMask)) {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Notice how many IFs you have to use to cater for the cases in which some attributes, i.e. energy, are not in use. These would disappear if each particle effect stores its data in components and the particle system, say "CampFireSystem" simply knows what components/data it will deal with.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Notice how many IFs you have to use to cater for the cases in which some attributes, i.e. energy, are not in use. These would disappear if each particle effect stores its data in components and the particle system, say "CampFireSystem" simply knows what components/data it will deal with.

+ private List<EntityRef> emitters = new LinkedList<>();
+
+ @Owns
+ private List<EntityRef> affectors = new LinkedList<>();

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

It does make sense that EmittersComponents are in their own entities. Emitters for example often have a location. Of course you could have the location stored in the emitter itself, but in the context of the ES it would make sense for it to be stored in a LocationComponent alongside an EmitterComponent.

Similarly, affectors can also be associated with a location component within an entity. This would allow for a turbulence field to affect particles only within some distance from the coordinates given by the LocationComponent.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

It does make sense that EmittersComponents are in their own entities. Emitters for example often have a location. Of course you could have the location stored in the emitter itself, but in the context of the ES it would make sense for it to be stored in a LocationComponent alongside an EmitterComponent.

Similarly, affectors can also be associated with a location component within an entity. This would allow for a turbulence field to affect particles only within some distance from the coordinates given by the LocationComponent.

+ public Texture texture = null;
+
+ /**
+ * This system's particle texture size, in percents x: [0.0, 1.0], y: [0.0, 1.0]

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Size percentage relative to what?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Size percentage relative to what?

+ /**
+ * Notifies ParticleSystemManagerImpl that this system needs an updated ParticleSystemStateData
+ */
+ private void requestUpdate() {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with what flo says below, a component should be pretty dumb. Mostly storage and accessory. Systems and Managers do the heavy lifting. Perhaps a division of responsibility there could be that Systems do the frame-by-frame work while Managers are active on a need-to-be basis, i.e. in response to events, to make sure the systems can find everything wired correctly within entities and components.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with what flo says below, a component should be pretty dumb. Mostly storage and accessory. Systems and Managers do the heavy lifting. Perhaps a division of responsibility there could be that Systems do the frame-by-frame work while Managers are active on a need-to-be basis, i.e. in response to events, to make sure the systems can find everything wired correctly within entities and components.

+ */
+ public void setRenderer(Class<ParticleRenderer> renderer) {
+ this.renderer = renderer;
+ requestUpdate();

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And I'm not sure a ParticleSystemComponent should hold a renderer. For example the same particles might need to be rendered in more than one way - by more than one renderer.

Rather, a registered RenderSystem should iterate and render all entities having a specific ParticleDataComponent and/or a specific ParticleRenderingComponent attached, the latter providing information shared by all particles that need to be rendered in a certain way, i.e. as the flames of a torch.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And I'm not sure a ParticleSystemComponent should hold a renderer. For example the same particles might need to be rendered in more than one way - by more than one renderer.

Rather, a registered RenderSystem should iterate and render all entities having a specific ParticleDataComponent and/or a specific ParticleRenderingComponent attached, the latter providing information shared by all particles that need to be rendered in a certain way, i.e. as the flames of a torch.

+
+ public List<EntityRef> getAffectors() {
+ return affectors;
+ }

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Looking back on this class, I'm not convinced we need it.

We need ParticleDataComponents, ParticleEmitterComponents, ParticleRenderingComponents, ParticleAffectorsComponets but what glues them together should be specialized systems i.e. CampFireSystem and probably an overseeing ParticleManager more or less like you already have.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

Looking back on this class, I'm not convinced we need it.

We need ParticleDataComponents, ParticleEmitterComponents, ParticleRenderingComponents, ParticleAffectorsComponets but what glues them together should be specialized systems i.e. CampFireSystem and probably an overseeing ParticleManager more or less like you already have.

@emanuele3d

I added a number of comments to complete the review I started yesterday. In practice I didn't go too in-depth as a addressing yesterday's issues will require significant changes to the code.

+
+ public void beforeUpdates(T component, Random random, float delta) {
+ // does nothing by default
+ }

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

What's the thinking with this before/afterUpdates methods?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

What's the thinking with this before/afterUpdates methods?

+
+/**
+ * Base class for GeneratorFunction and AffectorFunction. A particle system function is called on a particle to update its fields.
+ */

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with an earlier comment both Generators and Affectors should work on particles in bulk, not on individual particles.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with an earlier comment both Generators and Affectors should work on particles in bulk, not on individual particles.

+ dispose();
+ }
+
+ private static class DisplayList {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

I don't get it, why a whole class for a simple display list? Inclusive of dispose functionality? You are not exactly dealing with complex geometry here.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

I don't get it, why a whole class for a simple display list? Inclusive of dispose functionality? You are not exactly dealing with complex geometry here.

+/**
+ * Updates the all registered particle systems every game update. Updates a particle system's state data when it has been changed.
+ */
+public interface ParticleUpdater {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with a previous comment, there shouldn't be a single updater: each ParticleSystem should be a registered UpdateSubscriberSystem.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with a previous comment, there shouldn't be a single updater: each ParticleSystem should be a registered UpdateSubscriberSystem.

@@ -130,6 +131,7 @@ public void initShaders() {
prepareAndStoreShaderProgramInstance("lightGeometryPass", new ShaderParametersLightGeometryPass());
prepareAndStoreShaderProgramInstance("simple", new ShaderParametersDefault());
prepareAndStoreShaderProgramInstance("ssaoBlur", new ShaderParametersDefault());
+ prepareAndStoreShaderProgramInstance("newParticle", new ShaderParametersNewParticle());

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with flo's suggestion, this system should replace the existing system. As such there is no need to call its shader "newParticle". Instead, call it for what it is. DefaultParticleSprites for example.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

In line with flo's suggestion, this system should replace the existing system. As such there is no need to call its shader "newParticle". Instead, call it for what it is. DefaultParticleSprites for example.

+uniform vec4 color;
+
+
+mat4 undoRotation(in mat4 matrix) {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

This is more of a rotation reset rather than undoing it. But where do you use it?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

This is more of a rotation reset rather than undoing it. But where do you use it?

+}
+
+
+vec4 applyScale(in vec4 vertex) {

This comment has been minimized.

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And where do you use this one?

@emanuele3d

emanuele3d Feb 6, 2017

Contributor

And where do you use this one?

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Feb 26, 2017

Member

Bumping @MaxBorsch for great justice :-)

Member

Cervator commented Feb 26, 2017

Bumping @MaxBorsch for great justice :-)

+ public final Vector3f scale = new Vector3f();
+
+ // 4d vectors
+ public final Vector4f color = new Vector4f();

This comment has been minimized.

@skaldarnar

skaldarnar Mar 11, 2017

Member

Should this have additional information on the color format, i.e., RGBA?

@skaldarnar

skaldarnar Mar 11, 2017

Member

Should this have additional information on the color format, i.e., RGBA?

This comment has been minimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

You mean in the comment or in a custom data structure?

In general I think RGBA is more or less the default when talking about colors, but to specify it in the comment wouldn't hurt!

I'd refrain from using a custom data structure though: particles should stay very light and it could be argued, perhaps at a later stage, they should be even lighter than Vector4f, i.e. float[4]. But at this stage I'd recommend to stick to things that are clear rather than prematurely optimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

You mean in the comment or in a custom data structure?

In general I think RGBA is more or less the default when talking about colors, but to specify it in the comment wouldn't hurt!

I'd refrain from using a custom data structure though: particles should stay very light and it could be argued, perhaps at a later stage, they should be even lighter than Vector4f, i.e. float[4]. But at this stage I'd recommend to stick to things that are clear rather than prematurely optimized.

+ }
+
+ public void update(float delta) {
+ particleUpdater.update(delta);

This comment has been minimized.

@skaldarnar

skaldarnar Mar 11, 2017

Member

Controlling/calling each particle system's update method from the manager would have the advantage of a central place to enable/disable particles altogether which in turn would not compute any updates at all. I'm not sure what the desired behavior is in case someone turns off particles for while and turns them back on again?

How would disabled particles be handled if every system was a registered system?

@skaldarnar

skaldarnar Mar 11, 2017

Member

Controlling/calling each particle system's update method from the manager would have the advantage of a central place to enable/disable particles altogether which in turn would not compute any updates at all. I'm not sure what the desired behavior is in case someone turns off particles for while and turns them back on again?

How would disabled particles be handled if every system was a registered system?

+ emitters.clear();
+ }
+
+ // Thanks IntelliJ ;)

This comment has been minimized.

@skaldarnar

skaldarnar Mar 11, 2017

Member

probably fancy stream -> map -> collect pipeline? 👨‍🏫

@skaldarnar

skaldarnar Mar 11, 2017

Member

probably fancy stream -> map -> collect pipeline? 👨‍🏫

+ emitter != null ? emitter.getComponent(ParticleEmitterComponent.class) : null,
+ emitter != null ? emitter.getComponent(LocationComponent.class) : null,
+ generatorFunctions
+ )).collect(Collectors.toList()));

This comment has been minimized.

@skaldarnar

skaldarnar Mar 11, 2017

Member

I'm not sure about the following snippet (maybe I throw out too much when the emitter is null). Having worked with Scala for some time this line is quite logical to me, but I get why it might be confusing to others.

emitters.addAll(
  particleSystemComponent.getEmitters().stream()
    .filter(Objects::nonNull)
    .map(emitter -> new EmitterData(emitter.getComponent(...), emitter.getComponent(...), generatorFncs))
    .collect(Collectors.toList()));
@skaldarnar

skaldarnar Mar 11, 2017

Member

I'm not sure about the following snippet (maybe I throw out too much when the emitter is null). Having worked with Scala for some time this line is quite logical to me, but I get why it might be confusing to others.

emitters.addAll(
  particleSystemComponent.getEmitters().stream()
    .filter(Objects::nonNull)
    .map(emitter -> new EmitterData(emitter.getComponent(...), emitter.getComponent(...), generatorFncs))
    .collect(Collectors.toList()));
@emanuele3d

Responded to some of @skaldarnar comments.

+ }
+
+ public void update(float delta) {
+ particleUpdater.update(delta);

This comment has been minimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

What's the scenario in which we'd want a particle kill-switch? Disabling all particles at once, irrelevant of what they are used for? I think it makes sense that individual effects can be toggled on/off.

Indeed it is tricky to think about what the desired behavior for particles should be when turning them on/off for a while. For some effects, say a campfire, freezing the particles and simply restarting them from where they left seems reasonable and feasible. For wider effects, say ray, there might be more nuances to that. I.e. if a player moves from an area where it was raining to a desert, reactivating the rain particles should not actually produce any rain.

Your last question is a bit broad: what do you mean?

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

What's the scenario in which we'd want a particle kill-switch? Disabling all particles at once, irrelevant of what they are used for? I think it makes sense that individual effects can be toggled on/off.

Indeed it is tricky to think about what the desired behavior for particles should be when turning them on/off for a while. For some effects, say a campfire, freezing the particles and simply restarting them from where they left seems reasonable and feasible. For wider effects, say ray, there might be more nuances to that. I.e. if a player moves from an area where it was raining to a desert, reactivating the rain particles should not actually produce any rain.

Your last question is a bit broad: what do you mean?

+ emitter != null ? emitter.getComponent(ParticleEmitterComponent.class) : null,
+ emitter != null ? emitter.getComponent(LocationComponent.class) : null,
+ generatorFunctions
+ )).collect(Collectors.toList()));

This comment has been minimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I'm not sure that improves things for me... 😄

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I'm not sure that improves things for me... 😄

+ /**
+ * Destroy this particle system when its last emitter is destroyed TODO: Unimplemented
+ */
+ public boolean destroyWhenFinished = true;

This comment has been minimized.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I agree.

@emanuele3d

emanuele3d Mar 17, 2017

Contributor

I agree.

+
+ gl_FragData[0].rgba = color;
+ gl_FragData[1].rgba = vec4(0.5, 1.0, 0.5, 1.0);
+ gl_FragData[2].rgba = vec4(0.0, 0.0, 0.0, 0.0);

This comment has been minimized.

@vampcat

vampcat Mar 22, 2017

Contributor

Better delete the gl_FragData[1] and gl_FragData[2] parts, they are useless. Maybe even consider replacing gl_FragData[0] with gl_FragColor for clarity.

@vampcat

vampcat Mar 22, 2017

Contributor

Better delete the gl_FragData[1] and gl_FragData[2] parts, they are useless. Maybe even consider replacing gl_FragData[0] with gl_FragColor for clarity.

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub Mar 27, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented Mar 27, 2017

Hooray Jenkins reported success with all tests good!

+ @Replicate
+ public Vector2f randBlockTexDisplacementScale = new Vector2f(0.25f, 0.25f);
+
+ public BlockBreakEffectComponent() {

This comment has been minimized.

@flo

flo Mar 30, 2017

Contributor

An explictly defined constructor is a bit strange in a component, I would just assign a value to the field like for the Vector.

@flo

flo Mar 30, 2017

Contributor

An explictly defined constructor is a bit strange in a component, I would just assign a value to the field like for the Vector.

This comment has been minimized.

@MaxBorsch

MaxBorsch Mar 31, 2017

Contributor

@flo This class doesn't even exist, you're looking at a removed class from a previous commit, check the latest commit :)

@MaxBorsch

MaxBorsch Mar 31, 2017

Contributor

@flo This class doesn't even exist, you're looking at a removed class from a previous commit, check the latest commit :)

@flo

This comment has been minimized.

Show comment
Hide comment
@flo

flo Mar 30, 2017

Contributor

@emanuele3d I would let it up to you to review it. If this tests out good I think we can merge it from my side as it now is replacing the existing particle system.

Contributor

flo commented Mar 30, 2017

@emanuele3d I would let it up to you to review it. If this tests out good I think we can merge it from my side as it now is replacing the existing particle system.

@flo flo dismissed their stale review Mar 30, 2017

No longer relevant

After a long discussion with Cervator I have come to the conclusion that it is better for me to get out of the way of this PR. If you submit further PRs on the subject and they are reasonably small I'll make sure to provide feedback.

@skaldarnar

This comment has been minimized.

Show comment
Hide comment
@skaldarnar

skaldarnar Apr 9, 2017

Member

Are we good to merge this soon/now or do you have any changes in the queue @MaxBorsch?

Member

skaldarnar commented Apr 9, 2017

Are we good to merge this soon/now or do you have any changes in the queue @MaxBorsch?

@MaxBorsch

This comment has been minimized.

Show comment
Hide comment
@MaxBorsch

MaxBorsch Apr 9, 2017

Contributor

@skaldarnar You're clear to merge. I'll probably move the checklist from the PR to an issue later.

Contributor

MaxBorsch commented Apr 9, 2017

@skaldarnar You're clear to merge. I'll probably move the checklist from the PR to an issue later.

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Apr 18, 2017

Member

Bump - am remembering this one is open for merge now. @MaxBorsch could you submit that follow-up issue with details on future improvements needed please, so we don't lose track? Is there any particular easy way to test it? My apologies if that has come up in the past already :-)

Member

Cervator commented Apr 18, 2017

Bump - am remembering this one is open for merge now. @MaxBorsch could you submit that follow-up issue with details on future improvements needed please, so we don't lose track? Is there any particular easy way to test it? My apologies if that has come up in the past already :-)

@MaxBorsch MaxBorsch referenced this pull request Apr 20, 2017

Open

Particle System Updates #2909

1 of 7 tasks complete
@MaxBorsch

This comment has been minimized.

Show comment
Hide comment
@MaxBorsch

MaxBorsch Apr 20, 2017

Contributor

@Cervator You can test it by making any particles, this replaced the legacy particle system. Just hit a block or spawn dustEffect for particles. Clear to merge, posted in new issue: #2909

Contributor

MaxBorsch commented Apr 20, 2017

@Cervator You can test it by making any particles, this replaced the legacy particle system. Just hit a block or spawn dustEffect for particles. Clear to merge, posted in new issue: #2909

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator Apr 21, 2017

Member

Thanks @MaxBorsch :-)

Merged this and tested it out, and it works with a few observations/fixes needed:

  • Sand particles are nearly impossibly white - that's probably an existing issue with sand being super white in some circumstances
  • The rail gun didn't explode things as it failed to find engine:smokeExplosion - it works with Core:smokeExplosion (in TunnelAction - similar fix needed in `ArrowAction)
  • Causes compilation error in GooeysQuests which still uses BlockParticleEffectComponent - oh noes! That's technically an API violation.

I wish I had my mega-workspace so I could detect any other modules with problems (anybody else?) although I suspect the affected count is minor and fixes probably straight forward (could you advise on GooeysQuests, Max?). If we can pair this PR with fixes to affected modules we can let the API violation pass.

Member

Cervator commented Apr 21, 2017

Thanks @MaxBorsch :-)

Merged this and tested it out, and it works with a few observations/fixes needed:

  • Sand particles are nearly impossibly white - that's probably an existing issue with sand being super white in some circumstances
  • The rail gun didn't explode things as it failed to find engine:smokeExplosion - it works with Core:smokeExplosion (in TunnelAction - similar fix needed in `ArrowAction)
  • Causes compilation error in GooeysQuests which still uses BlockParticleEffectComponent - oh noes! That's technically an API violation.

I wish I had my mega-workspace so I could detect any other modules with problems (anybody else?) although I suspect the affected count is minor and fixes probably straight forward (could you advise on GooeysQuests, Max?). If we can pair this PR with fixes to affected modules we can let the API violation pass.

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub May 3, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented May 3, 2017

Hooray Jenkins reported success with all tests good!

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator May 3, 2017

Member

Took it for another spin with Terasology/GooeysQuests#13 included - thanks for that :-)

Seemed like it works fine! So that's a good example for how to fix things. Digging around in both my home workspaces I have a clearer picture of what's left on the todo list (some of those I can do while merging)

  • GooeysQuests - fixed by Terasology/GooeysQuests#13
  • CopperAndBronze has an old Charcoal pit with two classes using BlockParticleEffectComponent
  • Smithing has the same as it is a renewal of TTA
  • LightAndShadow also uses BPEC in its CardSystem
  • Rails as expected also has some smoke particles in MinecartSystem
  • Sample has a single prefab
  • JoshariasSurvival has 3 prefabs
Member

Cervator commented May 3, 2017

Took it for another spin with Terasology/GooeysQuests#13 included - thanks for that :-)

Seemed like it works fine! So that's a good example for how to fix things. Digging around in both my home workspaces I have a clearer picture of what's left on the todo list (some of those I can do while merging)

  • GooeysQuests - fixed by Terasology/GooeysQuests#13
  • CopperAndBronze has an old Charcoal pit with two classes using BlockParticleEffectComponent
  • Smithing has the same as it is a renewal of TTA
  • LightAndShadow also uses BPEC in its CardSystem
  • Rails as expected also has some smoke particles in MinecartSystem
  • Sample has a single prefab
  • JoshariasSurvival has 3 prefabs
@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator May 6, 2017

Member

Ran another quick bit of testing to see if this could be merged in prior to tomorrow's play test even with known module changes to still make, but it crashed pretty hard in multiplayer with GooeysQuests. Immediately on joining player 2 (player 1 hosting) the game crashed with the following error:

01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - Task list generated in 10.304 ms
01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - 42 nodes, 38 enabled - 176 tasks (excluding marker tasks) out of 290 potential tasks.
01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - ----------------------------------------------------------
01:35:45.758 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'emptyIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UIIconBar","id":"breathBar","icon":"engine:icons#bubble","family":"breathBar","emptyIcon":"engine:icons#burstBubble","halfIconMode":"shrink","spacing":2,"maxIcons":10,"layoutInfo":{"use-content-width":true,"use-content-height":true,"position-left":{"target":"CENTER"},"position-right":{"target":"RIGHT","widget":"healthBar"},"position-bottom":{"target":"TOP","widget":"healthBar","offset":1}}}
01:35:45.759 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'crosshairIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UICrosshair","id":"crosshair","crosshairIcon":"engine:gui#crosshair","layoutInfo":{"use-content-width":true,"use-content-height":true,"position-horizontal-center":{},"position-vertical-center":{}}}
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.EnergyRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.VelocityRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.ColorRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.PositionRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.ScaleRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.TextureOffsetGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering AffectorFunction for Component class class org.terasology.particles.components.affectors.VelocityAffectorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering AffectorFunction for Component class class org.terasology.particles.components.affectors.AccelerationAffectorComponent
01:35:45.836 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'crosshairIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UICrosshair","id":"crosshair","crosshairIcon":"engine:gui#crosshair","chargeStages":["engine:gui#crosshairCharge1","engine:gui#crosshairCharge2","engine:gui#crosshairCharge3","engine:gui#crosshairCharge4","engine:gui#crosshairCharge5","engine:gui#crosshairCharge6","engine:gui#crosshairCharge7","engine:gui#crosshairCharge8"],"layoutInfo":{"use-content-width":true,"use-content-height":true,"position-horizontal-center":{},"position-vertical-center":{}}}
01:35:45.924 [main] INFO  org.terasology.work.WorkBoard - Initialize WorkBoard
01:35:45.925 [main] INFO  org.terasology.work.WorkFactory - Initialize WorkFactory
01:35:46.078 [main] WARN  o.t.l.nameTags.NameTagClientSystem - Can't create player based name tag for character as owner has no client component
01:35:46.223 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.223 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.228 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.229 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.233 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.234 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.234 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 221, netId = 217, prefab = 'Core:defaultBlockParticles'}
01:35:46.243 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 223, netId = 220, prefab = 'Core:defaultBlockParticles'}
01:35:46.244 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.244 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.246 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 228, netId = 228, prefab = 'engine:blockItemBase'}
01:35:46.248 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.248 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.249 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 225, netId = 223, prefab = 'Core:defaultBlockParticles'}
01:35:46.252 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 227, netId = 226, prefab = 'Core:defaultBlockParticles'}
01:35:46.254 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 229, netId = 229, prefab = 'Core:defaultBlockParticles'}
01:35:46.261 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 231, netId = 232, prefab = 'Core:defaultBlockParticles'}
01:35:46.263 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 234, netId = 235, prefab = 'Core:defaultBlockParticles'}
01:35:46.266 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 232, netId = 233, prefab = 'Core:dustEffect'}
01:35:46.269 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 235, netId = 236, prefab = 'Core:dustEffect'}
01:35:46.272 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 237, netId = 238, prefab = 'Core:defaultBlockParticles'}
01:35:46.273 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 233, netId = 234}
01:35:46.276 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 238, netId = 239, prefab = 'Core:dustEffect'}
01:35:46.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 240, netId = 241, prefab = 'Core:defaultBlockParticles'}
01:35:46.523 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 241, netId = 242, prefab = 'Core:dustEffect'}
01:35:47.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 242, netId = 243, prefab = 'Core:defaultBlockParticles'}
01:35:47.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 236, netId = 237}
01:35:47.879 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 243, netId = 244, prefab = 'Core:dustEffect'}
01:35:48.443 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 9543, netId = 245, prefab = 'Core:defaultBlockParticles'}
01:35:49.059 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 9544, netId = 246, prefab = 'Core:dustEffect'}
01:35:49.925 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 239, netId = 240}
01:35:49.926 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12904, netId = 248, prefab = 'Core:defaultBlockParticles'}
01:35:50.558 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12905, netId = 249, prefab = 'Core:dustEffect'}
01:35:51.069 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12907, netId = 251, prefab = 'Core:defaultBlockParticles'}
01:35:51.349 [main] ERROR o.terasology.engine.TerasologyEngine - Uncaught exception, attempting clean game shutdown
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:653)
	at java.util.ArrayList.get(ArrayList.java:429)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:36)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:27)
	at org.terasology.particles.updating.ParticleUpdaterImpl.lambda$emitParticle$2(ParticleUpdaterImpl.java:192)
	at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
	at org.terasology.particles.updating.ParticleUpdaterImpl.emitParticle(ParticleUpdaterImpl.java:190)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateEmitter(ParticleUpdaterImpl.java:219)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateParticleSystem(ParticleUpdaterImpl.java:231)
	at org.terasology.particles.updating.ParticleUpdaterImpl.update(ParticleUpdaterImpl.java:100)
	at org.terasology.particles.ParticleSystemManagerImpl.update(ParticleSystemManagerImpl.java:118)
	at org.terasology.engine.modes.StateIngame.update(StateIngame.java:169)
	at org.terasology.engine.TerasologyEngine.tick(TerasologyEngine.java:429)
	at org.terasology.engine.TerasologyEngine.mainLoop(TerasologyEngine.java:394)
	at org.terasology.engine.TerasologyEngine.run(TerasologyEngine.java:370)
	at org.terasology.engine.Terasology.main(Terasology.java:153)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
01:35:51.352 [main] INFO  o.terasology.engine.TerasologyEngine - Shutting down Terasology...
01:35:51.377 [main] INFO  o.t.n.internal.NetworkSystemImpl - Network shutdown
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:653)
	at java.util.ArrayList.get(ArrayList.java:429)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:36)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:27)
	at org.terasology.particles.updating.ParticleUpdaterImpl.lambda$emitParticle$2(ParticleUpdaterImpl.java:192)
	at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
	at org.terasology.particles.updating.ParticleUpdaterImpl.emitParticle(ParticleUpdaterImpl.java:190)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateEmitter(ParticleUpdaterImpl.java:219)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateParticleSystem(ParticleUpdaterImpl.java:231)
	at org.terasology.particles.updating.ParticleUpdaterImpl.update(ParticleUpdaterImpl.java:100)
	at org.terasology.particles.ParticleSystemManagerImpl.update(ParticleSystemManagerImpl.java:118)
	at org.terasology.engine.modes.StateIngame.update(StateIngame.java:169)
	at org.terasology.engine.TerasologyEngine.tick(TerasologyEngine.java:429)
	at org.terasology.engine.TerasologyEngine.mainLoop(TerasologyEngine.java:394)
	at org.terasology.engine.TerasologyEngine.run(TerasologyEngine.java:370)
	at org.terasology.engine.Terasology.main(Terasology.java:153)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
For more details, see the log files in C:\Dev\Terasology\Git\Terasology\terasology-2ndclient\logs\2017-05-06_01-34-51

Process finished with exit code 0

GooeysQuests has particles appear when Gooey spawns, which is usually triggered by a player spawning. Player 1 was already in place with a Gooey there, which itself respawned once every 30 seconds or so (unsure of exact timing) causing particles each time.

Also noticing some minor quirks with the particles themselves (like the Gooey special particles seem to have a black background rather than transparency, and some blocks like torches get really weird particles) but those I expect could be ironed out more easily after merging this.

Member

Cervator commented May 6, 2017

Ran another quick bit of testing to see if this could be merged in prior to tomorrow's play test even with known module changes to still make, but it crashed pretty hard in multiplayer with GooeysQuests. Immediately on joining player 2 (player 1 hosting) the game crashed with the following error:

01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - Task list generated in 10.304 ms
01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - 42 nodes, 38 enabled - 176 tasks (excluding marker tasks) out of 290 potential tasks.
01:35:45.498 [main] INFO  o.t.r.dag.RenderTaskListGenerator - ----------------------------------------------------------
01:35:45.758 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'emptyIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UIIconBar","id":"breathBar","icon":"engine:icons#bubble","family":"breathBar","emptyIcon":"engine:icons#burstBubble","halfIconMode":"shrink","spacing":2,"maxIcons":10,"layoutInfo":{"use-content-width":true,"use-content-height":true,"position-left":{"target":"CENTER"},"position-right":{"target":"RIGHT","widget":"healthBar"},"position-bottom":{"target":"TOP","widget":"healthBar","offset":1}}}
01:35:45.759 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'crosshairIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UICrosshair","id":"crosshair","crosshairIcon":"engine:gui#crosshair","layoutInfo":{"use-content-width":true,"use-content-height":true,"position-horizontal-center":{},"position-vertical-center":{}}}
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.EnergyRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.VelocityRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.ColorRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.PositionRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.ScaleRangeGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering GeneratorFunction for Component class class org.terasology.particles.components.generators.TextureOffsetGeneratorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering AffectorFunction for Component class class org.terasology.particles.components.affectors.VelocityAffectorComponent
01:35:45.775 [main] INFO  o.t.p.ParticleSystemManagerImpl - Registering AffectorFunction for Component class class org.terasology.particles.components.affectors.AccelerationAffectorComponent
01:35:45.836 [main] WARN  o.t.rendering.nui.asset.UIFormat - Field 'crosshairIcon' not recognized for interface org.terasology.rendering.nui.UIWidget in {"type":"UICrosshair","id":"crosshair","crosshairIcon":"engine:gui#crosshair","chargeStages":["engine:gui#crosshairCharge1","engine:gui#crosshairCharge2","engine:gui#crosshairCharge3","engine:gui#crosshairCharge4","engine:gui#crosshairCharge5","engine:gui#crosshairCharge6","engine:gui#crosshairCharge7","engine:gui#crosshairCharge8"],"layoutInfo":{"use-content-width":true,"use-content-height":true,"position-horizontal-center":{},"position-vertical-center":{}}}
01:35:45.924 [main] INFO  org.terasology.work.WorkBoard - Initialize WorkBoard
01:35:45.925 [main] INFO  org.terasology.work.WorkFactory - Initialize WorkFactory
01:35:46.078 [main] WARN  o.t.l.nameTags.NameTagClientSystem - Can't create player based name tag for character as owner has no client component
01:35:46.223 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.223 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.228 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.229 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.233 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.234 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.234 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 221, netId = 217, prefab = 'Core:defaultBlockParticles'}
01:35:46.243 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 223, netId = 220, prefab = 'Core:defaultBlockParticles'}
01:35:46.244 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.244 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.246 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 228, netId = 228, prefab = 'engine:blockItemBase'}
01:35:46.248 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.248 [main] INFO  o.t.network.internal.ServerImpl - Dropping event PlaySoundEvent for unavailable entity EntityRef{id=0}
01:35:46.249 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 225, netId = 223, prefab = 'Core:defaultBlockParticles'}
01:35:46.252 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 227, netId = 226, prefab = 'Core:defaultBlockParticles'}
01:35:46.254 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 229, netId = 229, prefab = 'Core:defaultBlockParticles'}
01:35:46.261 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 231, netId = 232, prefab = 'Core:defaultBlockParticles'}
01:35:46.263 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 234, netId = 235, prefab = 'Core:defaultBlockParticles'}
01:35:46.266 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 232, netId = 233, prefab = 'Core:dustEffect'}
01:35:46.269 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 235, netId = 236, prefab = 'Core:dustEffect'}
01:35:46.272 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 237, netId = 238, prefab = 'Core:defaultBlockParticles'}
01:35:46.273 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 233, netId = 234}
01:35:46.276 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 238, netId = 239, prefab = 'Core:dustEffect'}
01:35:46.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 240, netId = 241, prefab = 'Core:defaultBlockParticles'}
01:35:46.523 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 241, netId = 242, prefab = 'Core:dustEffect'}
01:35:47.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 242, netId = 243, prefab = 'Core:defaultBlockParticles'}
01:35:47.277 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 236, netId = 237}
01:35:47.879 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 243, netId = 244, prefab = 'Core:dustEffect'}
01:35:48.443 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 9543, netId = 245, prefab = 'Core:defaultBlockParticles'}
01:35:49.059 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 9544, netId = 246, prefab = 'Core:dustEffect'}
01:35:49.925 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 239, netId = 240}
01:35:49.926 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12904, netId = 248, prefab = 'Core:defaultBlockParticles'}
01:35:50.558 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12905, netId = 249, prefab = 'Core:dustEffect'}
01:35:51.069 [main] INFO  o.t.network.internal.ServerImpl - Destroying entity: EntityRef{id = 12907, netId = 251, prefab = 'Core:defaultBlockParticles'}
01:35:51.349 [main] ERROR o.terasology.engine.TerasologyEngine - Uncaught exception, attempting clean game shutdown
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:653)
	at java.util.ArrayList.get(ArrayList.java:429)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:36)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:27)
	at org.terasology.particles.updating.ParticleUpdaterImpl.lambda$emitParticle$2(ParticleUpdaterImpl.java:192)
	at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
	at org.terasology.particles.updating.ParticleUpdaterImpl.emitParticle(ParticleUpdaterImpl.java:190)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateEmitter(ParticleUpdaterImpl.java:219)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateParticleSystem(ParticleUpdaterImpl.java:231)
	at org.terasology.particles.updating.ParticleUpdaterImpl.update(ParticleUpdaterImpl.java:100)
	at org.terasology.particles.ParticleSystemManagerImpl.update(ParticleSystemManagerImpl.java:118)
	at org.terasology.engine.modes.StateIngame.update(StateIngame.java:169)
	at org.terasology.engine.TerasologyEngine.tick(TerasologyEngine.java:429)
	at org.terasology.engine.TerasologyEngine.mainLoop(TerasologyEngine.java:394)
	at org.terasology.engine.TerasologyEngine.run(TerasologyEngine.java:370)
	at org.terasology.engine.Terasology.main(Terasology.java:153)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
01:35:51.352 [main] INFO  o.terasology.engine.TerasologyEngine - Shutting down Terasology...
01:35:51.377 [main] INFO  o.t.n.internal.NetworkSystemImpl - Network shutdown
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:653)
	at java.util.ArrayList.get(ArrayList.java:429)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:36)
	at org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction.onEmission(TextureOffsetGeneratorFunction.java:27)
	at org.terasology.particles.updating.ParticleUpdaterImpl.lambda$emitParticle$2(ParticleUpdaterImpl.java:192)
	at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
	at org.terasology.particles.updating.ParticleUpdaterImpl.emitParticle(ParticleUpdaterImpl.java:190)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateEmitter(ParticleUpdaterImpl.java:219)
	at org.terasology.particles.updating.ParticleUpdaterImpl.updateParticleSystem(ParticleUpdaterImpl.java:231)
	at org.terasology.particles.updating.ParticleUpdaterImpl.update(ParticleUpdaterImpl.java:100)
	at org.terasology.particles.ParticleSystemManagerImpl.update(ParticleSystemManagerImpl.java:118)
	at org.terasology.engine.modes.StateIngame.update(StateIngame.java:169)
	at org.terasology.engine.TerasologyEngine.tick(TerasologyEngine.java:429)
	at org.terasology.engine.TerasologyEngine.mainLoop(TerasologyEngine.java:394)
	at org.terasology.engine.TerasologyEngine.run(TerasologyEngine.java:370)
	at org.terasology.engine.Terasology.main(Terasology.java:153)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
For more details, see the log files in C:\Dev\Terasology\Git\Terasology\terasology-2ndclient\logs\2017-05-06_01-34-51

Process finished with exit code 0

GooeysQuests has particles appear when Gooey spawns, which is usually triggered by a player spawning. Player 1 was already in place with a Gooey there, which itself respawned once every 30 seconds or so (unsure of exact timing) causing particles each time.

Also noticing some minor quirks with the particles themselves (like the Gooey special particles seem to have a black background rather than transparency, and some blocks like torches get really weird particles) but those I expect could be ironed out more easily after merging this.

@Cervator Cervator referenced this pull request in Terasology/GooeysQuests May 6, 2017

Closed

Update to new particle system #13

@GooeyHub

This comment has been minimized.

Show comment
Hide comment
@GooeyHub

GooeyHub May 6, 2017

Member

Hooray Jenkins reported success with all tests good!

Member

GooeyHub commented May 6, 2017

Hooray Jenkins reported success with all tests good!

@Cervator Cervator merged commit 48bf964 into MovingBlocks:develop May 6, 2017

1 check passed

default Build finished.
Details

Cervator added a commit that referenced this pull request May 6, 2017

@Cervator Cervator added this to the Alpha 8 milestone May 6, 2017

@Cervator

This comment has been minimized.

Show comment
Hide comment
@Cervator

Cervator May 6, 2017

Member

Merged this after a crash fix! @MaxBorsch aims to get to the modules soon, so long as we do that before Alpha 8 is released we should be okay. Thanks for all your hard work! :-)

JoshariasSurvival does work in the meantime but will have no particle effects. Some other modules will not until fixed.

Member

Cervator commented May 6, 2017

Merged this after a crash fix! @MaxBorsch aims to get to the modules soon, so long as we do that before Alpha 8 is released we should be okay. Thanks for all your hard work! :-)

JoshariasSurvival does work in the meantime but will have no particle effects. Some other modules will not until fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment