diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 25f72d34..1a5c72ef 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -156,6 +156,7 @@ public class EarthPonyStompAbility implements Ability { spawnEffectAround(player, center, radius, rad); ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); + ParticleUtils.spawnParticle(player.getWorld(), UParticles.SHOCKWAVE, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); iplayer.subtractEnergyCost(rad); iplayer.asEntity().addExhaustion(3); diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 58584dff..a9d76b71 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.client.particle.RainboomParticle; import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle; import com.minelittlepony.unicopia.client.particle.RaindropsParticle; import com.minelittlepony.unicopia.client.particle.RunesParticle; +import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle; import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.entity.*; @@ -54,6 +55,7 @@ public interface URenderers { ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create)); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_TRAIL, RainbowTrailParticle::new); + ParticleFactoryRegistry.getInstance().register(UParticles.SHOCKWAVE, ShockwaveParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.MAGIC_RUNES, RunesParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index aac4a49e..6093fa45 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -8,6 +8,7 @@ import org.joml.Vector3f; import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; +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; @@ -66,16 +67,15 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A float alpha = 1 - (float)age / maxAge; for (int i = 0; i < segments.size() - 1; i++) { - Vector3f[] corners = segments.get(i).getPlane(segments.get(i + 1)); + BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); float scale = getScale(tickDelta); - for (int k = 0; k < 4; ++k) { - Vector3f corner = corners[k]; - corner.mul(scale); - corner.add(x, y, z); - } + corners.forEachCorner(corner -> { + corner.mul(scale); + corner.add(x, y, z); + }); - renderQuad(te, buffer, corners, segments.get(i).getAlpha() * alpha, tickDelta); + renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); } } @@ -132,24 +132,8 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; } - Vector3f[] getPlane(Segment to) { - float fromX = offset.x; - float toX = to.offset.x; - - float fromZ = offset.z; - float toZ = to.offset.z; - float fromTopY = offset.y + 1; - float fromBottomY = offset.y; - - float toTopY = to.offset.y + 1; - float toBottomY = to.offset.y; - - return new Vector3f[]{ - new Vector3f(fromX, fromBottomY, fromZ), // bottom left - new Vector3f(fromX, fromTopY, fromZ), // top left - new Vector3f(toX, toTopY, toZ), // top right - new Vector3f(toX, toBottomY, toZ) // bottom right - }; + BezierSegment getPlane(Segment to) { + return new BezierSegment(offset, to.offset, 1); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/ShockwaveParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/ShockwaveParticle.java new file mode 100644 index 00000000..878333ba --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/ShockwaveParticle.java @@ -0,0 +1,61 @@ +package com.minelittlepony.unicopia.client.particle; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.client.render.RenderUtil; + +import net.minecraft.block.BlockState; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.DefaultParticleType; +import net.minecraft.sound.SoundCategory; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class ShockwaveParticle extends AbstractBillboardParticle { + private static final Identifier TEXTURE = Unicopia.id("textures/particles/shockwave.png"); + + public ShockwaveParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, + double velocityZ) { + super(world, x, y, z, 0, 0, 0); + maxAge = 20; + setVelocity(0, 0, 0); + } + + @Override + protected Identifier getTexture() { + return TEXTURE; + } + + @Override + protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { + if (age < 5 || age % 6 == 0) { + BlockState state = world.getBlockState(BlockPos.ofFloored(this.x, this.y - 0.5, this.z)); + if (!state.isAir()) { + world.playSound(this.x, this.y, this.z, state.getSoundGroup().getBreakSound(), SoundCategory.AMBIENT, 2.5F, 0.4F, true); + } + } + + MatrixStack matrices = new MatrixStack(); + matrices.translate(x, y, z); + + for (int ring = -1; ring < 2; ring ++) { + float scaleH = (1.5F + (age + tickDelta)) + ring; + float scaleV = (2 + MathHelper.sin(age / 5F) * 2F) - Math.abs(ring); + matrices.push(); + matrices.scale(scaleH, scaleV, scaleH); + matrices.translate(-0.5, 0, -0.5); + int sides = 5; + for (int i = 0; i < sides; i++) { + RenderUtil.renderFace(matrices, te, buffer, red, green, blue, 0.3F, LightmapTextureManager.MAX_LIGHT_COORDINATE); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(360 / sides)); + matrices.translate(-1, 0, 0); + } + matrices.pop(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java new file mode 100644 index 00000000..a126ee28 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -0,0 +1,41 @@ +package com.minelittlepony.unicopia.client.render; + +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.joml.Vector4f; + +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormat; +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 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) + }; + + 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(); + } + te.draw(); + } + + record Vertex(Vector3f position, float u, float v) {} +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java new file mode 100644 index 00000000..f38b0182 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java @@ -0,0 +1,25 @@ +package com.minelittlepony.unicopia.client.render.bezier; + +import java.util.function.Consumer; + +import org.joml.Vector3f; + +public record BezierSegment( + Vector3f[] 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 + }); + } + + public void forEachCorner(Consumer transformer) { + for (var corner : corners) { + transformer.accept(corner); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java new file mode 100644 index 00000000..361a437a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java @@ -0,0 +1,49 @@ +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 { + public float width; + public float angle; + + private final List nodes = new ArrayList<>(); + private final List 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 iterator() { + return segments.iterator(); + } + + record Node(Vec3d position, Vector3f bottom, Vector3f top) { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index ea5ce42f..7a279906 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -29,6 +29,7 @@ public interface UParticles { DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true)); DefaultParticleType LIGHTNING_BOLT = register("lightning_bolt", FabricParticleTypes.simple(true)); + DefaultParticleType SHOCKWAVE = register("shockwave", FabricParticleTypes.simple(true)); static > T register(String name, T type) { return Registry.register(Registries.PARTICLE_TYPE, Unicopia.id(name), type); diff --git a/src/main/resources/assets/unicopia/textures/particles/shockwave.png b/src/main/resources/assets/unicopia/textures/particles/shockwave.png new file mode 100644 index 00000000..6dcb9655 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/particles/shockwave.png differ