Reimplement placed spell rendering to no longer require a particle

This commit is contained in:
Sollace 2024-01-20 19:48:43 +00:00
parent 81070be4b3
commit b3a7d338d2
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
12 changed files with 125 additions and 76 deletions

View file

@ -13,10 +13,6 @@ import com.minelittlepony.unicopia.entity.mob.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgCasterLookRequest;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.NbtSerialisable;
@ -24,6 +20,7 @@ import net.minecraft.nbt.*;
import net.minecraft.registry.*;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@ -40,11 +37,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Nullable
private RegistryKey<World> dimension;
/**
* The visual effect
*/
private final ParticleHandle particlEffect = new ParticleHandle();
/**
* ID of the placed counterpart of this spell.
*/
@ -64,6 +56,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
public float pitch;
public float yaw;
private int prevAge;
private int age;
private Optional<Vec3d> position = Optional.empty();
public PlaceableSpell(CustomisedSpellType<?> type) {
@ -75,15 +70,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return this;
}
@Override
public Collection<Spell> getDelegates() {
return List.of(spell);
public float getAge(float tickDelta) {
return MathHelper.lerp(tickDelta, prevAge, age);
}
@Override
public void setDead() {
super.setDead();
particlEffect.destroy();
public Collection<Spell> getDelegates() {
return List.of(spell);
}
@Override
@ -113,16 +106,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
setDead();
return false;
}
} else {
prevAge = age;
if (age < 25) {
age++;
}
}
if (spell instanceof PlacementDelegate delegate) {
delegate.updatePlacement(source, this);
}
getParticleEffectAttachment(source).ifPresent(p -> {
p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor());
});
return super.tick(source, Situation.GROUND);
}
@ -200,12 +190,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return castEntity.getTarget().map(EntityValues::pos);
}
public Optional<Attachment> getParticleEffectAttachment(Caster<?> source) {
return particlEffect.update(getUuid(), source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, pitch + 90, yaw), Vec3d.ZERO, Vec3d.ZERO);
});
}
protected Optional<World> getWorld(Caster<?> source) {
return Optional.ofNullable(dimension)
.map(dim -> source.asWorld().getServer().getWorld(dim));
@ -214,6 +198,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("age", age);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
position.ifPresent(pos -> {
@ -232,6 +217,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
age = compound.getInt("age");
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");
position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty();
@ -264,9 +250,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
}
public interface PlacementDelegate {
void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity);
void updatePlacement(Caster<?> source, PlaceableSpell parent);
}
}

View file

@ -54,7 +54,7 @@ public class RainboomAbilitySpell extends AbstractSpell {
});
if (source.isClient()) {
// source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
//source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}
source.findAllEntitiesInRange(RADIUS).forEach(e -> {

View file

@ -6,8 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
@ -16,7 +14,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d;
public class DisplacementSpell extends AbstractSpell implements HomingSpell, PlaceableSpell.PlacementDelegate, ProjectileDelegate.EntityHitListener {
public class DisplacementSpell extends AbstractSpell implements HomingSpell, ProjectileDelegate.EntityHitListener {
private final EntityReference<Entity> target = new EntityReference<>();
@ -67,19 +65,6 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla
originator.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F);
}
@Override
public void onPlaced(Caster<?> source, PlaceableSpell parent, CastSpellEntity entity) {
}
@Override
public void updatePlacement(Caster<?> caster, PlaceableSpell parent) {
parent.getParticleEffectAttachment(caster).ifPresent(attachment -> {
float r = 3 - (1 - ((ticks + 10) / 20F)) * 3;
attachment.setAttribute(Attachment.ATTR_RADIUS, r);
});
}
private void teleport(Caster<?> source, Entity entity, Vec3d pos, Vec3d vel) {
entity.teleport(pos.x, pos.y, pos.z);
entity.setVelocity(vel);

View file

@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.mob.CastSpellEntity;
import com.minelittlepony.unicopia.particle.*;
import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment;
import com.minelittlepony.unicopia.server.world.Ether;
import com.minelittlepony.unicopia.util.shape.*;
@ -177,14 +176,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z);
}
@Override
public void updatePlacement(Caster<?> source, PlaceableSpell parent) {
parent.getParticleEffectAttachment(source).ifPresent(attachment -> {
attachment.setAttribute(Attachment.ATTR_RADIUS, 2);
attachment.setAttribute(Attachment.ATTR_OPACITY, 0.92F);
});
}
@Override
protected void onDestroyed(Caster<?> caster) {
particleEffect.destroy();

View file

@ -22,7 +22,6 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl
fixed = effect.fixed();
if (fixed) {
// Was hamiltonianProduct (CHECK THIS!!)
rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch()));
rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw()));
}

View file

@ -20,6 +20,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.*;
@Deprecated
public class RunesParticle extends OrientedBillboardParticle implements Attachment {
private static final Identifier[] TEXTURES = new Identifier[] {

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.client.render;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.joml.Vector4f;
@ -11,7 +10,7 @@ import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.util.math.MatrixStack;
public class RenderUtil {
private static final Vector4f TEMP_VECTOR = new Vector4f();
public static final Vector4f TEMP_VECTOR = new Vector4f();
public static final Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(new Vector3f(0, 0, 0), 1, 1),
new Vertex(new Vector3f(0, 1, 0), 1, 0),
@ -21,21 +20,18 @@ public class RenderUtil {
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(new Vector3f(0, 0, 0), 1, 1),
new Vertex(new Vector3f(0, 1, 0), 1, 0),
new Vertex(new Vector3f(1, 1, 0), 0, 0),
new Vertex(new Vector3f(1, 0, 0), 0, 1)
};
Matrix4f transformation = matrices.peek().getPositionMatrix();
for (Vertex vertex : UNIT_FACE) {
transformation.transform(TEMP_VECTOR.set(vertex.position(), 1));
buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
Vector4f position = vertex.position(matrices);
buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next();
}
te.draw();
}
record Vertex(Vector3f position, float u, float v) {}
public record Vertex(Vector3f position, float u, float v) {
public Vector4f position(MatrixStack matrices) {
matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1));
return TEMP_VECTOR;
}
}
}

View file

@ -11,8 +11,6 @@ import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.util.Identifier;
public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher();
public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) {
super(ctx);
}
@ -24,7 +22,7 @@ public class CastSpellEntityRenderer extends EntityRenderer<CastSpellEntity> {
@Override
public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) {
spellRenderDispatcher.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0);
SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0);
}
protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) {

View file

@ -0,0 +1,88 @@
package com.minelittlepony.unicopia.client.render.spell;
import org.joml.Vector3f;
import org.joml.Vector4f;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.client.render.RenderUtil;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.RotationAxis;
public class PlacedSpellRenderer implements SpellRenderer<PlaceableSpell> {
private static final Identifier[] TEXTURES = new Identifier[] {
Unicopia.id("textures/particles/runes_0.png"),
Unicopia.id("textures/particles/runes_1.png"),
Unicopia.id("textures/particles/runes_2.png"),
Unicopia.id("textures/particles/runes_3.png"),
Unicopia.id("textures/particles/runes_4.png"),
Unicopia.id("textures/particles/runes_5.png")
};
private static final RenderUtil.Vertex[] CORNERS = new RenderUtil.Vertex[]{
new RenderUtil.Vertex(new Vector3f(-1, -1, 0), 0, 0),
new RenderUtil.Vertex(new Vector3f(-1, 1, 0), 1, 0),
new RenderUtil.Vertex(new Vector3f( 1, 1, 0), 1, 1),
new RenderUtil.Vertex(new Vector3f( 1, -1, 0), 0, 1)
};
@Override
public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster<?> caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) {
for (Spell delegate : spell.getDelegates()) {
matrices.push();
matrices.translate(0, 0.001, 0);
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.pitch));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 - spell.yaw));
float scale = (spell.getAge(tickDelta) / 25F) * 3;
matrices.scale(scale, scale, scale);
float alpha = scale;
float angle = (animationProgress / 9F) % 360;
int color = delegate.getType().getColor();
float red = Color.r(color);
float green = Color.g(color);
float blue = Color.b(color);
for (int i = 0; i < TEXTURES.length; i++) {
VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i]));
for (int dim = 0; dim < 3; dim++) {
float ringSpeed = (i % 2 == 0 ? i : -1) * i;
matrices.push();
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim));
renderQuad(buffer, matrices, red, green, blue, alpha / ((float)(dim * 3) + 1), light);
matrices.pop();
}
}
matrices.pop();
var renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate);
if (renderer != null) {
renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch);
}
}
}
protected final void renderQuad(VertexConsumer buffer, MatrixStack matrices, float red, float green, float blue, float alpha, int light) {
for (var corner : CORNERS) {
Vector4f pos = corner.position(matrices);
buffer.vertex(pos.x, pos.y, pos.z, red, green, blue, alpha, corner.u(), corner.v(), 0, light, 1, 1, 1);
}
}
}

View file

@ -43,10 +43,16 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader
REGISTRY.put(type, rendererFactory);
}
static {
register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new);
}
@Nullable
private Map<SpellType<?>, SpellRenderer<?>> renderers = Map.of();
private final MinecraftClient client = MinecraftClient.getInstance();
private SpellEffectsRenderDispatcher() {}
@Override
public Identifier getFabricId() {
return ID;

View file

@ -20,6 +20,7 @@ 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<>();

View file

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