diff --git a/engine-tests/src/test/java/org/terasology/particles/updating/ParticleUpdaterImplTest.java b/engine-tests/src/test/java/org/terasology/particles/updating/ParticleUpdaterImplTest.java index 235b56f3493..ac39bd98774 100644 --- a/engine-tests/src/test/java/org/terasology/particles/updating/ParticleUpdaterImplTest.java +++ b/engine-tests/src/test/java/org/terasology/particles/updating/ParticleUpdaterImplTest.java @@ -15,26 +15,22 @@ */ package org.terasology.particles.updating; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.terasology.engine.module.ModuleManager; import org.terasology.entitySystem.Component; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.particles.components.ParticleEmitterComponent; import org.terasology.particles.components.affectors.VelocityAffectorComponent; import org.terasology.particles.components.generators.EnergyRangeGeneratorComponent; -import org.terasology.particles.functions.affectors.AffectorFunction; -import org.terasology.particles.functions.affectors.VelocityAffectorFunction; -import org.terasology.particles.functions.generators.EnergyRangeGeneratorFunction; -import org.terasology.particles.functions.generators.GeneratorFunction; import org.terasology.physics.Physics; import org.terasology.physics.engine.PhysicsEngine; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; + import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -45,30 +41,28 @@ public class ParticleUpdaterImplTest { private ParticleUpdater particleUpdater; - private BiMap, GeneratorFunction> registeredGeneratorFunctions; - private BiMap, AffectorFunction> registeredAffectorFunctions; @BeforeEach public void setUp() throws Exception { Physics physics = mock(PhysicsEngine.class); - particleUpdater = new ParticleUpdaterImpl(physics); - registeredGeneratorFunctions = HashBiMap.create(); - registeredAffectorFunctions = HashBiMap.create(); + ModuleManager moduleManager = mock(ModuleManager.class); + particleUpdater = new ParticleUpdaterImpl(physics, moduleManager); } @Test public void testNullEmitterRegistration() { - Assertions.assertThrows(IllegalArgumentException.class, - () -> particleUpdater.register(null)); + Assertions.assertThrows(IllegalArgumentException.class,() -> { + particleUpdater.addEmitter(null); + }); } @Test public void testNonEmitterRegistration() { EntityRef emitterEntity = mock(EntityRef.class); when(emitterEntity.getComponent(ParticleEmitterComponent.class)).thenReturn(null); - - Assertions.assertThrows(IllegalArgumentException.class, - ()-> particleUpdater.register(emitterEntity)); + Assertions.assertThrows(IllegalArgumentException.class,() -> { + particleUpdater.addEmitter(emitterEntity); + }); } @Test @@ -76,7 +70,7 @@ public void testEmitterRegistration() { EntityRef emitterEntity = mock(EntityRef.class); when(emitterEntity.getComponent(ParticleEmitterComponent.class)).thenReturn(new ParticleEmitterComponent()); - particleUpdater.register(emitterEntity); + particleUpdater.addEmitter(emitterEntity); } private Iterator getTestGeneratorsAndAffectors() { @@ -84,12 +78,6 @@ private Iterator getTestGeneratorsAndAffectors() { components.add(new EnergyRangeGeneratorComponent(0.5f, 1f)); components.add(new VelocityAffectorComponent()); - EnergyRangeGeneratorFunction energyRangeGeneratorFunction = new EnergyRangeGeneratorFunction(); - registeredGeneratorFunctions.put(((GeneratorFunction) energyRangeGeneratorFunction).getComponentClass(), energyRangeGeneratorFunction); - - VelocityAffectorFunction velocityAffectorFunction = new VelocityAffectorFunction(); - registeredAffectorFunctions.put(((AffectorFunction) velocityAffectorFunction).getComponentClass(), velocityAffectorFunction); - return components.iterator(); } @@ -103,14 +91,14 @@ public void testEmitterConfiguration() { particleEmitterComponent.ownerEntity = emitterEntity; when(emitterEntity.getComponent(ParticleEmitterComponent.class)).thenReturn(particleEmitterComponent); - particleUpdater.register(emitterEntity); - particleUpdater.configureEmitter(particleEmitterComponent, registeredAffectorFunctions, registeredGeneratorFunctions); + particleUpdater.addEmitter(emitterEntity); + particleUpdater.configureEmitter(particleEmitterComponent); for (Component component : (Iterable) () -> componentIterator) { - if (registeredGeneratorFunctions.containsKey(component.getClass())) { - assertTrue(particleEmitterComponent.generatorFunctionMap.containsKey(component)); - } else if (registeredGeneratorFunctions.containsKey(component.getClass())) { + if (component.getClass() == EnergyRangeGeneratorComponent.class) { assertTrue(particleEmitterComponent.generatorFunctionMap.containsKey(component)); + } else if (component.getClass() == VelocityAffectorComponent.class) { + assertTrue(particleEmitterComponent.affectorFunctionMap.containsKey(component)); } } } diff --git a/engine/src/main/java/org/terasology/particles/ParticleSystemManager.java b/engine/src/main/java/org/terasology/particles/ParticleSystemManager.java index b633fdcd375..10ce8254600 100644 --- a/engine/src/main/java/org/terasology/particles/ParticleSystemManager.java +++ b/engine/src/main/java/org/terasology/particles/ParticleSystemManager.java @@ -17,14 +17,12 @@ import org.terasology.entitySystem.Component; import org.terasology.module.sandbox.API; -import org.terasology.particles.functions.affectors.AffectorFunction; -import org.terasology.particles.functions.generators.GeneratorFunction; import org.terasology.particles.rendering.ParticleRenderingData; import java.util.stream.Stream; /** - * Component system responsible for keeping track of all ParticleSystem components and updating them. + * Component system responsible for keeping track of all {@link org.terasology.particles.components.ParticleEmitterComponent} components and updating them. * Also maintains a registry of generator and affector functions to be used when processing generators * and affectors during a particle system update. */ @@ -32,9 +30,13 @@ @API public interface ParticleSystemManager { - void registerAffectorFunction(AffectorFunction affectorFunction); - - void registerGeneratorFunction(GeneratorFunction generatorFunction); - + /** + * Gets all current emitters that have a given particle data component and returns a stream of all particle pools and their associated data for rendering. + * A particle data component stores information used to define how the particles of the emitter it is attached to are rendered. + * + * @param particleDataComponent The particle data component to select emitters by. + * + * @return A stream of {@link ParticleRenderingData} to be used by particle renderers. + */ Stream getParticleEmittersByDataComponent(Class particleDataComponent); } diff --git a/engine/src/main/java/org/terasology/particles/ParticleSystemManagerImpl.java b/engine/src/main/java/org/terasology/particles/ParticleSystemManagerImpl.java index 3939db3a625..890261cb6c7 100644 --- a/engine/src/main/java/org/terasology/particles/ParticleSystemManagerImpl.java +++ b/engine/src/main/java/org/terasology/particles/ParticleSystemManagerImpl.java @@ -15,11 +15,7 @@ */ package org.terasology.particles; -import com.google.common.base.Preconditions; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.terasology.engine.module.ModuleManager; import org.terasology.entitySystem.Component; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.entitySystem.entity.lifecycleEvents.BeforeDeactivateComponent; @@ -33,18 +29,9 @@ import org.terasology.module.sandbox.API; import org.terasology.particles.components.ParticleEmitterComponent; import org.terasology.particles.events.ParticleSystemUpdateEvent; -import org.terasology.particles.functions.affectors.AccelerationAffectorFunction; -import org.terasology.particles.functions.affectors.AffectorFunction; -import org.terasology.particles.functions.affectors.VelocityAffectorFunction; -import org.terasology.particles.functions.generators.ColorRangeGeneratorFunction; -import org.terasology.particles.functions.generators.EnergyRangeGeneratorFunction; -import org.terasology.particles.functions.generators.GeneratorFunction; -import org.terasology.particles.functions.generators.PositionRangeGeneratorFunction; -import org.terasology.particles.functions.generators.ScaleRangeGeneratorFunction; -import org.terasology.particles.functions.generators.TextureOffsetGeneratorFunction; -import org.terasology.particles.functions.generators.VelocityRangeGeneratorFunction; import org.terasology.particles.rendering.ParticleRenderingData; import org.terasology.particles.updating.ParticleUpdater; +import org.terasology.particles.updating.ParticleUpdaterImpl; import org.terasology.physics.Physics; import org.terasology.registry.In; import org.terasology.registry.Share; @@ -52,22 +39,22 @@ import java.util.stream.Stream; /** - * See ParticleSystemManager for more information. + * A particle system manager implementation using events. + *

+ * See {@link ParticleSystemManager} for more information. */ @API @Share(ParticleSystemManager.class) @RegisterSystem(RegisterMode.CLIENT) public class ParticleSystemManagerImpl extends BaseComponentSystem implements UpdateSubscriberSystem, ParticleSystemManager { - private static final Logger logger = LoggerFactory.getLogger(ParticleSystemManagerImpl.class); - @In private Physics physics; - private ParticleUpdater particleUpdater; + @In + private ModuleManager moduleManager; - private BiMap, GeneratorFunction> registeredGeneratorFunctions = HashBiMap.create(); - private BiMap, AffectorFunction> registeredAffectorFunctions = HashBiMap.create(); + private ParticleUpdater particleUpdater; @ReceiveEvent(components = {ParticleEmitterComponent.class}) @@ -77,43 +64,42 @@ public void onEmitterActivated(OnActivatedComponent event, EntityRef entity, Par if (particleEmitterComponent.particlePool == null) { particleEmitterComponent.particlePool = new ParticlePool(particleEmitterComponent.maxParticles); } - particleUpdater.register(entity); - particleUpdater.configureEmitter(particleEmitterComponent, registeredAffectorFunctions, registeredGeneratorFunctions); + particleUpdater.addEmitter(entity); + particleUpdater.configureEmitter(particleEmitterComponent); } @ReceiveEvent(components = {ParticleEmitterComponent.class}) public void onEmitterChanged(ParticleSystemUpdateEvent event, EntityRef entity, ParticleEmitterComponent emitter) { - particleUpdater.configureEmitter(emitter, registeredAffectorFunctions, registeredGeneratorFunctions); + particleUpdater.configureEmitter(emitter); } @ReceiveEvent(components = {ParticleEmitterComponent.class}) public void onEmitterDeactivated(BeforeDeactivateComponent event, EntityRef entity, ParticleEmitterComponent particleEmitterComponent) { - particleUpdater.dispose(entity); + particleUpdater.removeEmitter(entity); } - + /** + * Creates and initializes a new {@link ParticleUpdater}. + */ public void initialise() { - particleUpdater = ParticleUpdater.create(physics); - - registerGeneratorFunction(new EnergyRangeGeneratorFunction()); - registerGeneratorFunction(new VelocityRangeGeneratorFunction()); - registerGeneratorFunction(new ColorRangeGeneratorFunction()); - registerGeneratorFunction(new PositionRangeGeneratorFunction()); - registerGeneratorFunction(new ScaleRangeGeneratorFunction()); - registerGeneratorFunction(new TextureOffsetGeneratorFunction()); - - registerAffectorFunction(new VelocityAffectorFunction()); - registerAffectorFunction(new AccelerationAffectorFunction()); + particleUpdater = new ParticleUpdaterImpl(physics, moduleManager); + particleUpdater.initialize(); } + /** + * De-registers all affector and generator functions and disposes the {@link ParticleUpdater} + */ @Override public void shutdown() { - registeredAffectorFunctions.clear(); - registeredGeneratorFunctions.clear(); - + particleUpdater.dispose(); particleUpdater = null; } + /** + * Updates all particle emitters, first spawning new particles and then applying affectors. + * + * @param delta The time (in seconds) since the last engine update. + */ public void update(float delta) { particleUpdater.update(delta); } @@ -128,24 +114,4 @@ public Stream getParticleEmittersByDataComponent(Class { private final int rawDataMask; - private final Class component; - public ParticleSystemFunction(Class component, ParticleDataMask dataMask, ParticleDataMask... dataMasks) { + public ParticleSystemFunction(ParticleDataMask dataMask, ParticleDataMask... dataMasks) { this.rawDataMask = ParticleDataMask.toInt(dataMask, dataMasks); - this.component = component; - } - - @Override - public final int hashCode() { - return component.hashCode(); - } - - @Override - public final boolean equals(final Object object) { - if (object != null && object.getClass().equals(this.getClass())) { - ParticleSystemFunction other = (ParticleSystemFunction) object; - return other.getComponentClass().equals(this.getComponentClass()); - } - - return false; - } - - public final Class getComponentClass() { - return component; } public final int getDataMask() { diff --git a/engine/src/main/java/org/terasology/particles/functions/RegisterParticleSystemFunction.java b/engine/src/main/java/org/terasology/particles/functions/RegisterParticleSystemFunction.java new file mode 100644 index 00000000000..55570b6f5cb --- /dev/null +++ b/engine/src/main/java/org/terasology/particles/functions/RegisterParticleSystemFunction.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017 MovingBlocks + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.terasology.particles.functions; + +import org.terasology.entitySystem.Component; +import org.terasology.module.sandbox.API; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to mark a {@link ParticleSystemFunction} to be registered in by the {@link org.terasology.particles.updating.ParticleUpdater}. + */ +@API +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface RegisterParticleSystemFunction {} diff --git a/engine/src/main/java/org/terasology/particles/functions/affectors/AccelerationAffectorFunction.java b/engine/src/main/java/org/terasology/particles/functions/affectors/AccelerationAffectorFunction.java index 8f280575d0c..64d4117013c 100644 --- a/engine/src/main/java/org/terasology/particles/functions/affectors/AccelerationAffectorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/affectors/AccelerationAffectorFunction.java @@ -18,14 +18,16 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.affectors.AccelerationAffectorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * */ +@RegisterParticleSystemFunction() public final class AccelerationAffectorFunction extends AffectorFunction { public AccelerationAffectorFunction() { - super(AccelerationAffectorComponent.class, ParticleDataMask.VELOCITY); + super(ParticleDataMask.VELOCITY); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/affectors/AffectorFunction.java b/engine/src/main/java/org/terasology/particles/functions/affectors/AffectorFunction.java index f2b814b379d..85d62749d98 100644 --- a/engine/src/main/java/org/terasology/particles/functions/affectors/AffectorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/affectors/AffectorFunction.java @@ -28,8 +28,8 @@ @API public abstract class AffectorFunction extends ParticleSystemFunction implements Cloneable { - public AffectorFunction(Class affectorComponent, ParticleDataMask dataMask, ParticleDataMask... dataMasks) { - super(affectorComponent, dataMask, dataMasks); + public AffectorFunction(ParticleDataMask dataMask, ParticleDataMask... dataMasks) { + super(dataMask, dataMasks); } public abstract void update(T component, ParticleData particleData, Random random, float delta); diff --git a/engine/src/main/java/org/terasology/particles/functions/affectors/VelocityAffectorFunction.java b/engine/src/main/java/org/terasology/particles/functions/affectors/VelocityAffectorFunction.java index 4dba49bce19..38c9472d6d6 100644 --- a/engine/src/main/java/org/terasology/particles/functions/affectors/VelocityAffectorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/affectors/VelocityAffectorFunction.java @@ -18,14 +18,16 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.affectors.VelocityAffectorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * */ +@RegisterParticleSystemFunction() public final class VelocityAffectorFunction extends AffectorFunction { public VelocityAffectorFunction() { - super(VelocityAffectorComponent.class, ParticleDataMask.POSITION, ParticleDataMask.VELOCITY); + super(ParticleDataMask.POSITION, ParticleDataMask.VELOCITY); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/ColorRangeGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/ColorRangeGeneratorFunction.java index b3b171fb414..d27e95299b4 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/ColorRangeGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/ColorRangeGeneratorFunction.java @@ -18,15 +18,17 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.ColorRangeGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 11-3-2015. */ +@RegisterParticleSystemFunction() public final class ColorRangeGeneratorFunction extends GeneratorFunction { public ColorRangeGeneratorFunction() { - super(ColorRangeGeneratorComponent.class, ParticleDataMask.COLOR); + super(ParticleDataMask.COLOR); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/EnergyRangeGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/EnergyRangeGeneratorFunction.java index 9725d1c5a41..247461a2a23 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/EnergyRangeGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/EnergyRangeGeneratorFunction.java @@ -18,15 +18,17 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.EnergyRangeGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 11-3-2015. */ +@RegisterParticleSystemFunction() public final class EnergyRangeGeneratorFunction extends GeneratorFunction { public EnergyRangeGeneratorFunction() { - super(EnergyRangeGeneratorComponent.class, ParticleDataMask.ENERGY); + super(ParticleDataMask.ENERGY); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/GeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/GeneratorFunction.java index a29084bf829..460674f266f 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/GeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/GeneratorFunction.java @@ -27,8 +27,8 @@ */ @API public abstract class GeneratorFunction extends ParticleSystemFunction { - public GeneratorFunction(Class component, ParticleDataMask dataMask, ParticleDataMask... dataMasks) { - super(component, dataMask, dataMasks); + public GeneratorFunction(ParticleDataMask dataMask, ParticleDataMask... dataMasks) { + super(dataMask, dataMasks); } public abstract void onEmission(T component, ParticleData particleData, Random random); diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/PositionRangeGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/PositionRangeGeneratorFunction.java index 97dca873d3c..85177dd781b 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/PositionRangeGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/PositionRangeGeneratorFunction.java @@ -18,15 +18,17 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.PositionRangeGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 11-3-2015. */ +@RegisterParticleSystemFunction() public final class PositionRangeGeneratorFunction extends GeneratorFunction { public PositionRangeGeneratorFunction() { - super(PositionRangeGeneratorComponent.class, ParticleDataMask.POSITION); + super(ParticleDataMask.POSITION); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/ScaleRangeGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/ScaleRangeGeneratorFunction.java index 987e070983c..23716398286 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/ScaleRangeGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/ScaleRangeGeneratorFunction.java @@ -18,14 +18,16 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.ScaleRangeGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 11-3-2015. */ +@RegisterParticleSystemFunction() public final class ScaleRangeGeneratorFunction extends GeneratorFunction { public ScaleRangeGeneratorFunction() { - super(ScaleRangeGeneratorComponent.class, ParticleDataMask.SCALE); + super(ParticleDataMask.SCALE); } @Override diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/TextureOffsetGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/TextureOffsetGeneratorFunction.java index 31067e45825..95ecd73b8af 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/TextureOffsetGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/TextureOffsetGeneratorFunction.java @@ -19,15 +19,17 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.TextureOffsetGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 13-4-2015. */ +@RegisterParticleSystemFunction() public class TextureOffsetGeneratorFunction extends GeneratorFunction { public TextureOffsetGeneratorFunction() { - super(TextureOffsetGeneratorComponent.class, ParticleDataMask.TEXTURE_OFFSET); + super(ParticleDataMask.TEXTURE_OFFSET); } @Override @@ -35,7 +37,7 @@ public void onEmission(TextureOffsetGeneratorComponent component, ParticleData p if (component.validOffsets.size() == 0) { return; } - + final int randomOffsetIndex = random.nextInt(component.validOffsets.size()); final Vector2f randomOffset = component.validOffsets.get(randomOffsetIndex); particleData.textureOffset.set(randomOffset.getX(), randomOffset.getY()); diff --git a/engine/src/main/java/org/terasology/particles/functions/generators/VelocityRangeGeneratorFunction.java b/engine/src/main/java/org/terasology/particles/functions/generators/VelocityRangeGeneratorFunction.java index dc18be40d71..aad38f19060 100644 --- a/engine/src/main/java/org/terasology/particles/functions/generators/VelocityRangeGeneratorFunction.java +++ b/engine/src/main/java/org/terasology/particles/functions/generators/VelocityRangeGeneratorFunction.java @@ -18,14 +18,16 @@ import org.terasology.particles.ParticleData; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.components.generators.VelocityRangeGeneratorComponent; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.utilities.random.Random; /** * Created by Linus on 11-3-2015. */ +@RegisterParticleSystemFunction() public final class VelocityRangeGeneratorFunction extends GeneratorFunction { public VelocityRangeGeneratorFunction() { - super(VelocityRangeGeneratorComponent.class, ParticleDataMask.VELOCITY); + super(ParticleDataMask.VELOCITY); } @Override diff --git a/engine/src/main/java/org/terasology/particles/updating/ParticleUpdater.java b/engine/src/main/java/org/terasology/particles/updating/ParticleUpdater.java index bace1fe1cf6..b145eaa6913 100644 --- a/engine/src/main/java/org/terasology/particles/updating/ParticleUpdater.java +++ b/engine/src/main/java/org/terasology/particles/updating/ParticleUpdater.java @@ -15,13 +15,9 @@ */ package org.terasology.particles.updating; -import com.google.common.collect.BiMap; -import org.terasology.entitySystem.Component; import org.terasology.entitySystem.entity.EntityRef; +import org.terasology.particles.ParticleSystemManager; import org.terasology.particles.components.ParticleEmitterComponent; -import org.terasology.particles.functions.affectors.AffectorFunction; -import org.terasology.particles.functions.generators.GeneratorFunction; -import org.terasology.physics.Physics; import java.util.Collection; @@ -31,29 +27,48 @@ public interface ParticleUpdater { /** - * @param entity The entity with the particle system being registered. + * Registers a particle emitter entity to be updated each particle update. + * + * @param entity The entity with the {@link ParticleEmitterComponent} being registered. */ - void register(EntityRef entity); + void addEmitter(EntityRef entity); /** + * De-registers a particle emitter, stopping it from being updated. + * * @param entity The entity with the particle system being disposed of. */ - void dispose(EntityRef entity); + void removeEmitter(EntityRef entity); /** - * @param emitter The particle emitter that is being updated. - * @param registeredAffectorFunctions The list of affector functions to use when processing the given system's affectors. - * @param registeredGeneratorFunctions The list of generator functions to use when processing the given system's generators. + * Prepares an emitter to be efficiently handled by the particle updater. + * Should be called on newly-added emitters and after each configuration change to an existing emitter. + * + * @param emitter The particle emitter that is being updated. */ - void configureEmitter(ParticleEmitterComponent emitter, - BiMap, AffectorFunction> registeredAffectorFunctions, - BiMap, GeneratorFunction> registeredGeneratorFunctions); + void configureEmitter(ParticleEmitterComponent emitter); + /** + * Updates all particle emitters, first spawning new particles and then applying affectors. + * + * @param delta The time (in seconds) since the last engine update. + */ void update(float delta); + /** + * + * @return All current particle emitters. + */ Collection getParticleEmitters(); - static ParticleUpdater create(Physics physics) { - return new ParticleUpdaterImpl(physics); - } + /** + * Invoked when the updater is first created by the {@link ParticleSystemManager} + */ + void initialize(); + + /** + * Invoked when the particle engine is shutting down (when the game is unloading). + * De-registers all particle emitters. + */ + void dispose(); } diff --git a/engine/src/main/java/org/terasology/particles/updating/ParticleUpdaterImpl.java b/engine/src/main/java/org/terasology/particles/updating/ParticleUpdaterImpl.java index d6b2b47cc41..60708b092c7 100644 --- a/engine/src/main/java/org/terasology/particles/updating/ParticleUpdaterImpl.java +++ b/engine/src/main/java/org/terasology/particles/updating/ParticleUpdaterImpl.java @@ -17,76 +17,96 @@ import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.module.ModuleManager; import org.terasology.entitySystem.Component; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.math.TeraMath; import org.terasology.math.geom.Vector3f; +import org.terasology.module.ModuleEnvironment; import org.terasology.particles.ParticleDataMask; import org.terasology.particles.ParticlePool; import org.terasology.particles.components.ParticleEmitterComponent; +import org.terasology.particles.functions.ParticleSystemFunction; +import org.terasology.particles.functions.RegisterParticleSystemFunction; import org.terasology.particles.functions.affectors.AffectorFunction; import org.terasology.particles.functions.generators.GeneratorFunction; import org.terasology.physics.HitResult; import org.terasology.physics.Physics; import org.terasology.physics.StandardCollisionGroup; +import org.terasology.utilities.ReflectionUtil; import org.terasology.utilities.random.FastRandom; +import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; /** * See ParticleUpdater for more information. */ -class ParticleUpdaterImpl implements ParticleUpdater { +public class ParticleUpdaterImpl implements ParticleUpdater { + private static final Logger logger = LoggerFactory.getLogger(ParticleUpdaterImpl.class); + + /** + * Number used in determining how many particles to skip in each collision update step, as updating all particles is costly. + */ private static final int PHYSICS_SKIP_NR = 100; + private ModuleManager moduleManager; + /** - * Set of all particle emitters + * Map of Generators to the functions that process them. */ - private final Set registeredParticleSystems = new HashSet<>(); + private BiMap, GeneratorFunction> registeredGeneratorFunctions = HashBiMap.create(); /** - * Map of ParticleDataComponent type to emitters of that type. + * Map of Affectors to the functions that process them. */ - //private final Map, ParticleEmitterComponent> particleSystemsLookup = new HashMap<>(); + private BiMap, AffectorFunction> registeredAffectorFunctions = HashBiMap.create(); + + /** + * Set of all particle emitters + */ + private final Set registeredParticleSystems = new HashSet<>(); private final FastRandom random = new FastRandom(); private final Physics physics; - private float movingAvgDelta = 1.0f / 60.0f; //Starting guess average physics updateParticleSystem delta - - //== public ======================================================================================================== + private float movingAvgDelta = 1.0f / 60.0f; // Starting guess average physics updateParticleSystem delta - ParticleUpdaterImpl(final Physics physics) { + public ParticleUpdaterImpl(final Physics physics, final ModuleManager moduleManager) { this.physics = physics; + this.moduleManager = moduleManager; } @Override - public void register(final EntityRef emitter) { + public void addEmitter(final EntityRef emitter) { Preconditions.checkArgument(emitter != null, - "Argument can not be null" + "Argument can not be null" ); ParticleEmitterComponent emitterComponent = emitter.getComponent(ParticleEmitterComponent.class); Preconditions.checkArgument(emitterComponent != null, - "Entity %s does not have a ParticleEmitterComponent", emitter + "Entity %s does not have a ParticleEmitterComponent", emitter ); registeredParticleSystems.add(emitterComponent); } @Override - public void dispose(final EntityRef emitter) { + public void removeEmitter(final EntityRef emitter) { Preconditions.checkArgument(emitter != null, - "Argument can not be null" + "Argument can not be null" ); ParticleEmitterComponent emitterComponent = emitter.getComponent(ParticleEmitterComponent.class); Preconditions.checkState(registeredParticleSystems.contains(emitterComponent), - "Entity %s is not a registered entity", emitter + "Entity %s is not a registered entity", emitter ); registeredParticleSystems.remove(emitterComponent); @@ -107,9 +127,38 @@ public Set getParticleEmitters() { } @Override - public void configureEmitter(final ParticleEmitterComponent emitter, - final BiMap, AffectorFunction> registeredAffectorFunctions, - final BiMap, GeneratorFunction> registeredGeneratorFunctions) { + public void initialize() { + ModuleEnvironment environment = moduleManager.getEnvironment(); + + for (Class type : environment.getTypesAnnotatedWith(RegisterParticleSystemFunction.class)) { + RegisterParticleSystemFunction annotation = type.getAnnotation(RegisterParticleSystemFunction.class); + + if (!ParticleSystemFunction.class.isAssignableFrom(type)) { + logger.error("Cannot register particle system function {}, must be a subclass of ParticleSystemFunction", type.getSimpleName()); + } else { + try { + ParticleSystemFunction function = (ParticleSystemFunction) type.newInstance(); + if (function instanceof GeneratorFunction) { + Type componentClass = ReflectionUtil.getTypeParameterForSuper(type, GeneratorFunction.class, 0); + mapGeneratorFunction((GeneratorFunction) function, (Class) componentClass); + + } else if (function instanceof AffectorFunction) { + Type componentClass = ReflectionUtil.getTypeParameterForSuper(type, AffectorFunction.class, 0); + mapAffectorFunction((AffectorFunction) function, (Class) componentClass); + } + } catch (InstantiationException | IllegalAccessException e) { + logger.error("Failed to register particle system", e); + } + } + } + } + + @Override + public void dispose() { + } + + @Override + public void configureEmitter(final ParticleEmitterComponent emitter) { emitter.generatorFunctionMap.clear(); emitter.affectorFunctionMap.clear(); @@ -123,7 +172,35 @@ public void configureEmitter(final ParticleEmitterComponent emitter, } } - //== particles ===================================================================================================== + /** + * Maps a Generator function to the component it will be called on when new particles are emitted. + * + * @param generatorFunction The generator function to be used. + * @param componentClass The component class this function is being mapped to. + */ + private void mapGeneratorFunction(GeneratorFunction generatorFunction, Class componentClass) { + Preconditions.checkArgument(!registeredGeneratorFunctions.containsKey(componentClass), + "Tried to register an GeneratorFunction for %s twice", generatorFunction + ); + + logger.info("Registering GeneratorFunction for Component class {}", componentClass); + registeredGeneratorFunctions.put(componentClass, generatorFunction); + } + + /** + * Maps an Affector function to the component it will be called on when updating particles. + * + * @param affectorFunction The affector function to be used. + * @param componentClass The component class this function is being mapped to. + */ + private void mapAffectorFunction(AffectorFunction affectorFunction, Class componentClass) { + Preconditions.checkArgument(!registeredAffectorFunctions.containsKey(componentClass), + "Tried to register an AffectorFunction for %s twice", affectorFunction + ); + + logger.info("Registering AffectorFunction for Component class {}", componentClass); + registeredAffectorFunctions.put(componentClass, affectorFunction); + } private void checkCollision(final ParticlePool pool, final int offset) { final Vector3f vel = new Vector3f(); @@ -159,49 +236,47 @@ private void updateLifeRemaining(final ParticlePool pool, final float delta) { } /* - * Updates particle life and processes particle affectors - * */ + * Updates particle life and processes particle affectors + * */ private void updateParticles(final ParticleEmitterComponent particleSystem, final float delta) { updateLifeRemaining(particleSystem.particlePool, delta); particleSystem.affectorFunctionMap.forEach( - (component, affector) -> affector.beforeUpdates(component, random, delta) + (component, affector) -> affector.beforeUpdates(component, random, delta) ); for (int i = 0; i < particleSystem.particlePool.livingParticles(); i++) { particleSystem.particlePool.loadTemporaryDataFrom(i, ParticleDataMask.ALL.toInt()); particleSystem.affectorFunctionMap.forEach( - (component, affector) -> - affector.update(component, particleSystem.particlePool.temporaryParticleData, random, delta) + (component, affector) -> + affector.update(component, particleSystem.particlePool.temporaryParticleData, random, delta) ); particleSystem.particlePool.storeTemporaryDataAt(i, ParticleDataMask.ALL.toInt()); } } - //== emission ====================================================================================================== - private void emitParticle(final ParticleEmitterComponent particleEmitter) { int index = particleEmitter.particlePool.reviveParticle(); particleEmitter.particlePool.loadTemporaryDataFrom(index, ParticleDataMask.ALL.toInt()); particleEmitter.generatorFunctionMap.forEach( - (component, generator) -> - generator.onEmission(component, particleEmitter.particlePool.temporaryParticleData, random) + (component, generator) -> + generator.onEmission(component, particleEmitter.particlePool.temporaryParticleData, random) ); particleEmitter.particlePool.temporaryParticleData.position.add( - particleEmitter.locationComponent.getWorldPosition() + particleEmitter.locationComponent.getWorldPosition() ); particleEmitter.particlePool.storeTemporaryDataAt(index, ParticleDataMask.ALL.toInt()); } /* - * Emits particles from emitter - * */ + * Emits particles from emitter + * */ private void updateEmitter(final ParticleEmitterComponent particleEmitter, final int particleReviveLimit, final float delta) { float deltaLeft = delta; @@ -224,8 +299,6 @@ private void updateEmitter(final ParticleEmitterComponent particleEmitter, final } } - //== general ======================================================================================================= - private void updateParticleSystem(final ParticleEmitterComponent partSys, final float delta) { if (partSys.enabled && (partSys.particleSpawnsLeft == ParticleEmitterComponent.INFINITE_PARTICLE_SPAWNS || partSys.particleSpawnsLeft > 0)) { updateEmitter(partSys, 0, delta); // Emit particles @@ -238,7 +311,6 @@ private void updateParticleSystem(final ParticleEmitterComponent partSys, final partSys.collisionUpdateIteration = (partSys.collisionUpdateIteration + 1) % PHYSICS_SKIP_NR; } - // System ran out of lifetime -> stop emission -> dispose if (partSys.lifeTime != ParticleEmitterComponent.INDEFINITE_EMITTER_LIFETIME) { partSys.lifeTime = Math.max(0, partSys.lifeTime - delta);