/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Lists * com.google.common.collect.Maps * com.google.common.collect.Queues * it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap * org.jspecify.annotations.Nullable */ package net.minecraft.client.particle; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Queues; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Queue; import net.minecraft.client.Camera; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ElderGuardianParticleGroup; import net.minecraft.client.particle.ItemPickupParticleGroup; import net.minecraft.client.particle.NoRenderParticleGroup; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleGroup; import net.minecraft.client.particle.ParticleProvider; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.ParticleResources; import net.minecraft.client.particle.QuadParticleGroup; import net.minecraft.client.particle.TrackingEmitter; import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.state.ParticlesRenderState; import net.minecraft.core.particles.ParticleLimit; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.RandomSource; import net.minecraft.util.profiling.Profiler; import net.minecraft.world.entity.Entity; import org.jspecify.annotations.Nullable; public class ParticleEngine { private static final List RENDER_ORDER = List.of(ParticleRenderType.SINGLE_QUADS, ParticleRenderType.ITEM_PICKUP, ParticleRenderType.ELDER_GUARDIANS); protected ClientLevel level; private final Map> particles = Maps.newIdentityHashMap(); private final Queue trackingEmitters = Queues.newArrayDeque(); private final Queue particlesToAdd = Queues.newArrayDeque(); private final Object2IntOpenHashMap trackedParticleCounts = new Object2IntOpenHashMap(); private final ParticleResources resourceManager; private final RandomSource random = RandomSource.create(); public ParticleEngine(ClientLevel level, ParticleResources resourceManager) { this.level = level; this.resourceManager = resourceManager; } public void createTrackingEmitter(Entity entity, ParticleOptions particle) { this.trackingEmitters.add(new TrackingEmitter(this.level, entity, particle)); } public void createTrackingEmitter(Entity entity, ParticleOptions particle, int lifeTime) { this.trackingEmitters.add(new TrackingEmitter(this.level, entity, particle, lifeTime)); } public @Nullable Particle createParticle(ParticleOptions options, double x, double y, double z, double xa, double ya, double za) { Particle particle = this.makeParticle(options, x, y, z, xa, ya, za); if (particle != null) { this.add(particle); return particle; } return null; } private @Nullable Particle makeParticle(T options, double x, double y, double z, double xa, double ya, double za) { ParticleProvider provider = (ParticleProvider)this.resourceManager.getProviders().get(BuiltInRegistries.PARTICLE_TYPE.getId(options.getType())); if (provider == null) { return null; } return provider.createParticle(options, this.level, x, y, z, xa, ya, za, this.random); } public void add(Particle p) { Optional limit = p.getParticleLimit(); if (limit.isPresent()) { if (this.hasSpaceInParticleLimit(limit.get())) { this.particlesToAdd.add(p); this.updateCount(limit.get(), 1); } } else { this.particlesToAdd.add(p); } } public void tick() { this.particles.forEach((type, group) -> { Profiler.get().push(type.name()); group.tickParticles(); Profiler.get().pop(); }); if (!this.trackingEmitters.isEmpty()) { ArrayList removed = Lists.newArrayList(); for (TrackingEmitter emitter : this.trackingEmitters) { emitter.tick(); if (emitter.isAlive()) continue; removed.add(emitter); } this.trackingEmitters.removeAll(removed); } if (!this.particlesToAdd.isEmpty()) { Particle particle; while ((particle = this.particlesToAdd.poll()) != null) { this.particles.computeIfAbsent(particle.getGroup(), this::createParticleGroup).add(particle); } } } private ParticleGroup createParticleGroup(ParticleRenderType type) { if (type == ParticleRenderType.ITEM_PICKUP) { return new ItemPickupParticleGroup(this); } if (type == ParticleRenderType.ELDER_GUARDIANS) { return new ElderGuardianParticleGroup(this); } if (type == ParticleRenderType.NO_RENDER) { return new NoRenderParticleGroup(this); } return new QuadParticleGroup(this, type); } protected void updateCount(ParticleLimit limit, int change) { this.trackedParticleCounts.addTo((Object)limit, change); } public void extract(ParticlesRenderState particlesRenderState, Frustum frustum, Camera camera, float partialTickTime) { for (ParticleRenderType particleType : RENDER_ORDER) { ParticleGroup particles = this.particles.get(particleType); if (particles == null || particles.isEmpty()) continue; particlesRenderState.add(particles.extractRenderState(frustum, camera, partialTickTime)); } } public void setLevel(@Nullable ClientLevel level) { this.level = level; this.clearParticles(); this.trackingEmitters.clear(); } public String countParticles() { return String.valueOf(this.particles.values().stream().mapToInt(ParticleGroup::size).sum()); } private boolean hasSpaceInParticleLimit(ParticleLimit limit) { return this.trackedParticleCounts.getInt((Object)limit) < limit.limit(); } public void clearParticles() { this.particles.clear(); this.particlesToAdd.clear(); this.trackingEmitters.clear(); this.trackedParticleCounts.clear(); } }