Clean up deprecated particle handling

This commit is contained in:
Sollace 2024-01-22 23:28:03 +00:00
parent edb1e024d6
commit eb1b767319
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
18 changed files with 292 additions and 423 deletions

View file

@ -1,11 +1,13 @@
package com.minelittlepony.unicopia;
import java.util.Map;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity;
import com.minelittlepony.unicopia.particle.ParticleSpawner;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.Entity;
@ -32,6 +34,10 @@ public class InteractionManager {
return INSTANCE;
}
public ParticleSpawner createBoundParticle(UUID id) {
return ParticleSpawner.EMPTY;
}
public Map<Identifier, ?> readChapters(PacketByteBuf buf) {
throw new RuntimeException("Method not supported");
}

View file

@ -10,10 +10,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import net.minecraft.util.math.Vec3d;
/**
* Pegasus ability to perform rainbooms
@ -72,7 +68,6 @@ public class PegasusRainboomAbility implements Ability<Hit> {
}
if (player.consumeSuperMove()) {
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE);
}
return true;

View file

@ -1,12 +1,16 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.*;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleSpawner;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import com.minelittlepony.unicopia.server.world.ModificationType;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.shape.Shape;
@ -26,7 +30,8 @@ public class RainboomAbilitySpell extends AbstractSpell {
private static final int RADIUS = 5;
private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS);
private final ParticleHandle particlEffect = new ParticleHandle();
@Nullable
private ParticleSpawner boundParticle;
private int age;
@ -35,11 +40,6 @@ public class RainboomAbilitySpell extends AbstractSpell {
setHidden(true);
}
@Override
protected void onDestroyed(Caster<?> source) {
particlEffect.destroy();
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
@ -47,14 +47,15 @@ public class RainboomAbilitySpell extends AbstractSpell {
return false;
}
particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(attachment -> {
attachment.setAttribute(Attachment.ATTR_BOUND, 1);
});
if (source.isClient()) {
//source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
if (boundParticle == null) {
boundParticle = InteractionManager.INSTANCE.createBoundParticle(getUuid());
}
boundParticle.addParticle(new TargetBoundParticleEffect(UParticles.RAINBOOM_TRAIL, source.asEntity()), source.getOriginVector(), Vec3d.ZERO);
if (age == 0) {
source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}
}
source.findAllEntitiesInRange(RADIUS).forEach(e -> {
@ -92,5 +93,6 @@ public class RainboomAbilitySpell extends AbstractSpell {
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
age = compound.getInt("age");
boundParticle = null;
}
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -14,10 +15,12 @@ import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.client.gui.DismissSpellScreen;
import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters;
import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner;
import com.minelittlepony.unicopia.client.sound.*;
import com.minelittlepony.unicopia.entity.player.PlayerPhysics;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity;
import com.minelittlepony.unicopia.particle.ParticleSpawner;
import com.mojang.authlib.GameProfile;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -47,7 +50,7 @@ public class ClientInteractionManager extends InteractionManager {
@Override
public Map<Identifier, ?> readChapters(PacketByteBuf buffer) {
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter);
}
@Override
@ -144,4 +147,9 @@ public class ClientInteractionManager extends InteractionManager {
public int getViewMode() {
return client.options.getPerspective().ordinal();
}
@Override
public ParticleSpawner createBoundParticle(UUID id) {
return new ClientBoundParticleSpawner(id);
}
}

View file

@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.client.particle;
import org.joml.Vector3f;
import com.minelittlepony.unicopia.client.render.RenderUtil;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.render.BufferBuilder;
@ -36,6 +38,16 @@ public abstract class AbstractGeometryBasedParticle extends Particle {
te.draw();
}
protected final void renderQuad(Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) {
int light = getBrightness(tickDelta);
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
for (RenderUtil.Vertex corner : corners) {
buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.u(), corner.v()).color(red, green, blue, alpha).light(light).next();
}
te.draw();
}
protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) {
int light = getBrightness(tickDelta);

View file

@ -0,0 +1,57 @@
package com.minelittlepony.unicopia.client.particle;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.BooleanSupplier;
import com.minelittlepony.unicopia.particle.ParticleSpawner;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.Particle;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
/**
* A connection class for updating and persisting an attached particle effect.
*/
public class ClientBoundParticleSpawner implements ParticleSpawner {
private static final Map<UUID, Entry> SPAWNED_PARTICLES = new HashMap<>();
private final UUID id;
private WeakReference<BooleanSupplier> attachment = new WeakReference<>(null);
private final MinecraftClient client = MinecraftClient.getInstance();
public ClientBoundParticleSpawner(UUID id) {
this.id = id;
}
@Override
public void addParticle(ParticleEffect effect, Vec3d pos, Vec3d vel) {
BooleanSupplier a = attachment.get();
if ((a == null || !a.getAsBoolean())) {
SPAWNED_PARTICLES.values().removeIf(set -> !set.getAsBoolean());
attachment = new WeakReference<>(SPAWNED_PARTICLES.computeIfAbsent(id, i -> {
return new Entry(
new WeakReference<>(client.world),
new WeakReference<>(client.particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z))
);
}));
}
}
private record Entry (WeakReference<World> world, WeakReference<Particle> particle) implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
if (world.get() == null || world.get() != MinecraftClient.getInstance().world) {
return false;
}
Particle particle = this.particle.get();
return particle != null && particle.isAlive();
}
}
}

View file

@ -1,39 +1,46 @@
package com.minelittlepony.unicopia.client.particle;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import com.minelittlepony.unicopia.EntityConvertable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
import com.minelittlepony.unicopia.client.render.bezier.Trail;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.DefaultParticleType;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
public class RainbowTrailParticle extends AbstractBillboardParticle implements Attachment {
public class RainbowTrailParticle extends AbstractBillboardParticle {
private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png");
private final List<Segment> segments = new ArrayList<>();
private final Trail trail;
private Optional<Link> link = Optional.empty();
@Nullable
private Entity target;
private boolean isAbility;
private boolean bound;
public RainbowTrailParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
segments.add(new Segment(new Vec3d(x, y, z)));
trail = new Trail(new Vec3d(x, y, z));
setMaxAge(300);
this.velocityX = velocityX;
this.velocityY = velocityY;
this.velocityZ = velocityZ;
if (effect.getTargetId() <= 0) {
this.target = world.getOtherEntities(null, Box.from(trail.pos)).get(0);
} else {
this.target = world.getEntityById(effect.getTargetId());
}
isAbility = Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isPresent();
}
@Override
@ -42,98 +49,45 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A
}
@Override
public boolean isStillAlive() {
return age < getMaxAge() && (!dead || !segments.isEmpty());
}
@Override
public void attach(Link link) {
this.link = Optional.of(link);
bound = true;
}
@Override
public void detach() {
link = Optional.empty();
}
@Override
public void setAttribute(int key, Number value) {
public boolean isAlive() {
return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty());
}
@Override
protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) {
float alpha = 1 - (float)age / maxAge;
float alpha = this.alpha * (1 - (float)age / maxAge);
List<Trail.Segment> segments = trail.getSegments();
for (int i = 0; i < segments.size() - 1; i++) {
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1));
float scale = getScale(tickDelta);
corners.forEachCorner(corner -> {
corner.mul(scale);
corner.add(x, y, z);
corner.position().mul(scale).add(x, y, z);
});
renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta);
}
}
private void follow(EntityConvertable<?> caster) {
Vec3d next = caster.asEntity().getPos();
if (segments.isEmpty()) {
segments.add(new Segment(next));
} else {
Vec3d last = segments.get(segments.size() - 1).position;
if (next.distanceTo(last) > 0.2) {
segments.add(new Segment(next));
}
}
}
@Override
public void tick() {
super.tick();
if (link.isPresent()) {
age = 0;
link.flatMap(Link::get).ifPresent(this::follow);
} else if (!dead && !bound) {
follow(Pony.of(MinecraftClient.getInstance().player));
if (target != null && target.isAlive()) {
if (isAbility) {
age = 0;
}
trail.update(target.getEyePos());
if (isAbility && Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isEmpty()) {
target = null;
}
}
if (segments.size() > 1) {
segments.removeIf(Segment::tick);
}
if (segments.isEmpty()) {
if (trail.tick()) {
markDead();
}
}
private final class Segment {
Vec3d position;
Vector3f offset;
int age;
int maxAge;
Segment(Vec3d position) {
this.position = position;
this.offset = new Vector3f((float)(position.getX() - x), (float)(position.getY() - y), (float)(position.getZ() - z));
this.maxAge = 90;
}
float getAlpha() {
return alpha * (1 - ((float)age / maxAge));
}
boolean tick() {
return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge;
}
BezierSegment getPlane(Segment to) {
return new BezierSegment(offset, to.offset, 1);
}
}
}

View file

@ -1,27 +1,20 @@
package com.minelittlepony.unicopia.client.particle;
import java.util.Optional;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.EntityConvertable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.Tessellator;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.*;
@Deprecated
public class RunesParticle extends OrientedBillboardParticle implements Attachment {
public class RunesParticle extends OrientedBillboardParticle {
private static final Identifier[] TEXTURES = new Identifier[] {
Unicopia.id("textures/particles/runes_0.png"),
@ -40,10 +33,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
private float prevRotationAngle;
private float rotationAngle;
private Optional<Link> link = Optional.empty();
private int stasisAge = -1;
public RunesParticle(OrientedBillboardParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(effect, world, x, y, z, velocityX, velocityY, velocityZ);
setMaxAge(70);
@ -53,52 +42,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
blue = world.random.nextFloat();
}
@Override
public boolean isStillAlive() {
return age < (maxAge - 1);
}
@Override
public void attach(Link link) {
this.link = Optional.of(link);
velocityX = 0;
velocityY = 0;
velocityZ = 0;
Vec3d pos = link.get().map(EntityConvertable::asEntity).map(Entity::getPos).orElse(Vec3d.ZERO);
setPos(pos.x, pos.y + 0.25, pos.z);
}
@Override
public void detach() {
link = Optional.empty();
if (targetSize > 1) {
this.targetSize = 0;
}
}
@Override
public void setAttribute(int key, Number value) {
if (key == ATTR_COLOR) {
int tint = value.intValue();
red = Color.r(tint);
green = Color.g(tint);
blue = Color.b(tint);
}
if (key == ATTR_OPACITY) {
alpha = value.floatValue();
}
if (key == ATTR_RADIUS) {
targetSize = value.floatValue();
}
if (key == ATTR_PITCH) {
rotation = new Quaternionf(0, 0, 0, 1);
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(value.floatValue()));
}
if (key == ATTR_YAW) {
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - value.floatValue()));
}
}
@Override
public float getScale(float tickDelta) {
return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta);
@ -166,15 +109,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme
public void tick() {
super.tick();
link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> {
if (getAlphaScale() >= 0.9F) {
if (stasisAge < 0) {
stasisAge = age;
}
age = stasisAge;
}
}, this::detach);
prevBaseSize = baseSize;
if (baseSize < targetSize) {
baseSize += 0.1F;

View file

@ -10,22 +10,13 @@ import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import com.minelittlepony.unicopia.EntityConvertable;
import com.minelittlepony.unicopia.client.render.RenderLayers;
import com.minelittlepony.unicopia.client.render.model.SphereModel;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.particle.ParticleHandle.Link;
import com.minelittlepony.unicopia.util.ColorHelper;
import com.mojang.blaze3d.systems.RenderSystem;
import java.util.Optional;
import com.minelittlepony.common.util.Color;
public class SphereParticle extends Particle implements Attachment {
public class SphereParticle extends Particle {
protected float prevRadius;
protected float radius;
@ -34,12 +25,6 @@ public class SphereParticle extends Particle implements Attachment {
protected float lerpIncrement;
protected float toRadius;
private Optional<Link> link = Optional.empty();
private final SphereParticleEffect parameters;
private boolean bound;
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) {
this(parameters, w, x, y, z);
@ -50,7 +35,6 @@ public class SphereParticle extends Particle implements Attachment {
public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) {
super(w, x, y, z);
this.parameters = parameters;
this.radius = parameters.radius();
this.red = parameters.color().x / 255F;
this.green = parameters.color().y / 255F;
@ -60,43 +44,6 @@ public class SphereParticle extends Particle implements Attachment {
setMaxAge(10);
}
@Override
public boolean isStillAlive() {
return age < (maxAge - 1);
}
@Override
public void attach(Link link) {
setMaxAge(50000);
this.link = Optional.of(link);
}
@Override
public void detach() {
markDead();
}
@Override
public void setAttribute(int key, Number value) {
if (key == ATTR_RADIUS) {
toRadius = value.floatValue();
steps = 20;
lerpIncrement = (toRadius - radius) / steps;
}
if (key == ATTR_COLOR) {
int tint = value.intValue();
red = Color.r(tint);
green = Color.g(tint);
blue = Color.b(tint);
}
if (key == ATTR_OPACITY) {
alpha = value.floatValue();
}
if (key == ATTR_BOUND) {
bound = value.intValue() == 1;
}
}
@Override
public ParticleTextureSheet getType() {
return ParticleTextureSheet.CUSTOM;
@ -106,24 +53,7 @@ public class SphereParticle extends Particle implements Attachment {
public void tick() {
super.tick();
if (link.isPresent()) {
link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> {
if (!bound) {
Vec3d offset = parameters.offset();
setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ());
prevPosX = e.lastRenderX + offset.getX();
prevPosY = e.lastRenderY + offset.getY();
prevPosZ = e.lastRenderZ + offset.getZ();
}
}, this::detach);
if (steps-- > 0) {
radius += lerpIncrement;
}
} else {
radius *= 0.9998281;
}
radius *= 0.9998281;
}
@Override

View file

@ -4,20 +4,22 @@ import java.util.function.Consumer;
import org.joml.Vector3f;
import com.minelittlepony.unicopia.client.render.RenderUtil;
public record BezierSegment(
Vector3f[] corners
RenderUtil.Vertex[] corners
) {
public BezierSegment(Vector3f from, Vector3f to, float height) {
this(new Vector3f[] {
new Vector3f(from.x, from.y - height/2F, from.z), // bottom left
new Vector3f(from.x, from.y + height/2F, from.z), // top left
new Vector3f(to.x, to.y + height/2F, to.z), // top right
new Vector3f(to.x, to.y - height/2F, to.z) // bottom right
this(new RenderUtil.Vertex[] {
new RenderUtil.Vertex(new Vector3f(from.x, from.y - height/2F, from.z), 0, 0), // bottom left
new RenderUtil.Vertex(new Vector3f(from.x, from.y + height/2F, from.z), 1, 0), // top left
new RenderUtil.Vertex(new Vector3f(to.x, to.y + height/2F, to.z), 1, 1), // top right
new RenderUtil.Vertex(new Vector3f(to.x, to.y - height/2F, to.z), 0, 1) // bottom right
});
}
public void forEachCorner(Consumer<Vector3f> transformer) {
public void forEachCorner(Consumer<RenderUtil.Vertex> transformer) {
for (var corner : corners) {
transformer.accept(corner);
}

View file

@ -1,49 +0,0 @@
package com.minelittlepony.unicopia.client.render.bezier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.joml.Vector3f;
import net.minecraft.util.math.Vec3d;
public class Ribbon implements Iterable<BezierSegment> {
public float width;
public float angle;
private final List<Node> nodes = new ArrayList<>();
private final List<BezierSegment> segments = new ArrayList<>();
private Node lastNode;
public Ribbon(Vec3d position, Vector3f bottom, Vector3f top, float angle) {
this.angle = angle;
lastNode = new Node(position, position.toVector3f().add(bottom), position.toVector3f().add(top));
nodes.add(lastNode);
}
public void addNode(Vec3d position, float angle) {
Vector3f directionVector = position.subtract(lastNode.position()).toVector3f();
Vector3f bottom = lastNode.bottom().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z);
Vector3f top = lastNode.top().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z);
Node oldNode = lastNode;
lastNode = new Node(position, bottom, top);
nodes.add(lastNode);
segments.add(new BezierSegment(new Vector3f[] {
oldNode.bottom(),
oldNode.top(),
lastNode.bottom(),
lastNode.top()
}));
}
@Override
public Iterator<BezierSegment> iterator() {
return segments.iterator();
}
record Node(Vec3d position, Vector3f bottom, Vector3f top) {
}
}

View file

@ -0,0 +1,67 @@
package com.minelittlepony.unicopia.client.render.bezier;
import java.util.ArrayList;
import java.util.List;
import org.joml.Vector3f;
import net.minecraft.util.math.Vec3d;
public class Trail {
private final List<Segment> segments = new ArrayList<>();
public final Vec3d pos;
public Trail(Vec3d pos) {
this.pos = pos;
segments.add(new Segment(pos));
}
public List<Segment> getSegments() {
return segments;
}
public void update(Vec3d newPosition) {
if (segments.isEmpty()) {
segments.add(new Segment(newPosition));
} else {
Vec3d last = segments.get(segments.size() - 1).position;
if (newPosition.distanceTo(last) > 0.2) {
segments.add(new Segment(newPosition));
}
}
}
public boolean tick() {
if (segments.size() > 1) {
segments.removeIf(Segment::tick);
}
return segments.isEmpty();
}
public final class Segment {
Vec3d position;
Vector3f offset;
int age;
int maxAge;
Segment(Vec3d position) {
this.position = position;
this.offset = position.subtract(pos).toVector3f();
this.maxAge = 90;
}
public float getAlpha() {
return (1 - ((float)age / maxAge));
}
boolean tick() {
return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge;
}
public BezierSegment getPlane(Segment to) {
return new BezierSegment(offset, to.offset, 1);
}
}
}

View file

@ -0,0 +1,13 @@
package com.minelittlepony.unicopia.client.render.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
public class RainboomSpellRenderer extends SpellRenderer<RainboomAbilitySpell> {
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, RainboomAbilitySpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
}
}

View file

@ -48,6 +48,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new);
register(SpellType.BUBBLE, BubbleSpellRenderer::new);
register(SpellType.PORTAL, PortalSpellRenderer::new);
register(SpellType.RAINBOOM, RainboomSpellRenderer::new);
}
@Nullable

View file

@ -1,120 +0,0 @@
package com.minelittlepony.unicopia.particle;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.function.Consumer;
import com.minelittlepony.unicopia.EntityConvertable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.Particle;
import net.minecraft.world.World;
/**
* A connection class for updating and persisting an attached particle effect.
*/
@Deprecated
public class ParticleHandle {
private final Map<String, Attachment> loadedEffects = new WeakHashMap<>();
public Optional<Attachment> update(UUID id, ParticleSource<?> source, Consumer<ParticleSpawner> constructor) {
return update(id, "prime", source, constructor);
}
public Optional<Attachment> update(UUID id, String partName, ParticleSource<?> source, Consumer<ParticleSpawner> constructor) {
return get(partName).or(() -> {
if (source.asEntity().getWorld().isClient) {
new ClientHandle().addParticle(id, partName, source, constructor);
}
return get(partName);
});
}
public void destroy() {
loadedEffects.values().forEach(Attachment::detach);
loadedEffects.clear();
}
private Optional<Attachment> get(String partName) {
return Optional.ofNullable(loadedEffects.get(partName)).filter(Attachment::isStillAlive);
}
private final class ClientHandle {
private static final Map<UUID, Map<String, Entry>> SPAWNED_PARTICLES = new HashMap<>();
private Particle pp;
@Environment(EnvType.CLIENT)
private void addParticle(UUID id, String partName, ParticleSource<?> source, Consumer<ParticleSpawner> constructor) {
SPAWNED_PARTICLES.values().removeIf(set -> {
set.values().removeIf(particle -> particle.get() == null);
return set.isEmpty();
});
Entry p = SPAWNED_PARTICLES.computeIfAbsent(id, i -> new WeakHashMap<>()).computeIfAbsent(partName, i -> {
constructor.accept((effect, pos, vel) -> {
pp = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z);
if (pp instanceof Attachment) {
((Attachment) pp).attach(new Link(id, source));
}
});
return new Entry(new WeakReference<>(MinecraftClient.getInstance().world), new WeakReference<>(pp));
});
if (p.get() instanceof Attachment) {
loadedEffects.put(partName, (Attachment)p.get());
}
}
record Entry (WeakReference<World> world, WeakReference<Particle> particle) {
public Particle get() {
if (world.get() == null || world.get() != MinecraftClient.getInstance().world) {
return null;
}
Particle particle = this.particle.get();
return particle == null || !particle.isAlive() ? null : particle;
}
}
}
public interface Attachment {
int ATTR_RADIUS = 0;
int ATTR_COLOR = 1;
int ATTR_OPACITY = 2;
int ATTR_PITCH = 3;
int ATTR_YAW = 4;
int ATTR_BOUND = 5;
boolean isStillAlive();
void attach(Link link);
void detach();
void setAttribute(int key, Number value);
}
public static final class Link {
private Optional<WeakReference<EntityConvertable<?>>> caster = Optional.empty();
private UUID effect;
private Link(UUID effect, EntityConvertable<?> caster) {
this.caster = Optional.of(new WeakReference<>(caster));
this.effect = effect;
}
public Optional<EntityConvertable<?>> get() {
caster = caster.filter(r -> r.get() != null && (!(r.get() instanceof Caster<?> c) || c.getSpellSlot().contains(effect)) && r.get().asEntity().isAlive());
return caster.map(WeakReference::get);
}
}
}

View file

@ -4,5 +4,7 @@ import net.minecraft.particle.ParticleEffect;
import net.minecraft.util.math.Vec3d;
public interface ParticleSpawner {
ParticleSpawner EMPTY = (effect, pos, vel) -> {};
void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity);
}

View file

@ -0,0 +1,55 @@
package com.minelittlepony.unicopia.particle;
import java.util.Locale;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.Entity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
import net.minecraft.registry.Registries;
public class TargetBoundParticleEffect implements ParticleEffect {
@SuppressWarnings("deprecation")
public static final Factory<TargetBoundParticleEffect> FACTORY = ParticleFactoryHelper.of(TargetBoundParticleEffect::new, TargetBoundParticleEffect::new);
private final ParticleType<TargetBoundParticleEffect> type;
private final int targetId;
protected TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, StringReader reader) throws CommandSyntaxException {
this.type = type;
this.targetId = -1;
}
protected TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, PacketByteBuf buf) {
this.type = type;
this.targetId = buf.readInt();
}
public TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, Entity target) {
this.type = type;
this.targetId = target.getId();
}
public int getTargetId() {
return targetId;
}
@Override
public ParticleType<?> getType() {
return type;
}
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(targetId);
}
@Override
public String asString() {
return String.format(Locale.ROOT, "%s", Registries.PARTICLE_TYPE.getId(getType()), targetId);
}
}

View file

@ -15,7 +15,7 @@ public interface UParticles {
DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple());
ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple());
ParticleType<TargetBoundParticleEffect> RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY));
@Deprecated
ParticleType<OrientedBillboardParticleEffect> MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));