From 309e39b583c0e990a3298eb3102db8b98b4e0b8d Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 11 Oct 2023 13:54:23 +0100 Subject: [PATCH] Added a custom shockwave particle when an earth pony stomps --- .../ability/EarthPonyStompAbility.java | 1 + .../unicopia/client/URenderers.java | 2 + .../client/particle/RainbowTrailParticle.java | 34 +++------- .../client/particle/ShockwaveParticle.java | 61 ++++++++++++++++++ .../unicopia/client/render/RenderUtil.java | 41 ++++++++++++ .../client/render/bezier/BezierSegment.java | 25 +++++++ .../unicopia/client/render/bezier/Ribbon.java | 49 ++++++++++++++ .../unicopia/particle/UParticles.java | 1 + .../unicopia/textures/particles/shockwave.png | Bin 0 -> 4560 bytes 9 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/ShockwaveParticle.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java create mode 100644 src/main/resources/assets/unicopia/textures/particles/shockwave.png 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 0000000000000000000000000000000000000000..6dcb96556f762c3fa5202482bb8145cac6a43ca4 GIT binary patch literal 4560 zcmeHKdr(x@8NZ7l6i`7hQ6%G?jV1wdpL-W}Z&(EuWOwQU0isAr@$P-VE&F2k0*kGW zpz(>+WQYk4qO~SIGNmd_qJ(Nx)L4tjv{73{h>#f+B1ny6jEVN#yFA;>beL)WV|UIy z-#zDde&;*C^PO+^Y|hF^pB5Yy3_;K|vne$j)M0W7nhKut?mb~Z^*Nts7R=^YCVin!4?*9l4Td_MxPtQU>OEVI!FyWCDewDAz+tzY$q!E`1m`fTAZ-p!7i{#0b?r&+sRtlFZx z5*zuulmoN!%kbmhgzTi|$DlP;C8tjP_Uxf`TbmlEH&(&ls=9i+_T5r1c#OsTY181B z$L0<68~Qs`j~BmwP*@h7rM+l+J|ZRlqgemK@D%Ly?AG=G=`gd~kR0K6^-x(VGcpw zuIvgrlJ)h9IS;JaWbGFyu_7ov-Vo(zmy&cfcuY ztE2vM;FFZ<)aguJgvu*AJQ#V(OvhLfa!Y zGN|9Lt=Yb2-u3c({y~XTmgQeBv_nn1_g%f7|4q=X&Z%hQ^_?w&1^tjQvzas1MDDqb zMXr6I`R=`F+!eC^N_$8CBi$;9{Nk0~sXCqixh=JS?jP)LxYY63@b}(N3G%C z_ktMoin)1Go@EKmI_)UKIjubEwY%gEgAx~cT?|{oOR$wM5*-Hhz{e-mu*ezI%d{5U z;xh8ZqG_d@&smv~%dRY8^_+Uq!r(+N4G8SK#K2y=&EcWF2DQRV16yv!)UX1PN(|~e z5JyI*n};csLUAO`E0&Y$g~4#5n-l2l)Wt&-;LV^emLwOAVHFh>XoUuKx{EMEuh(NZ ziIF4%5QwMJAu(RW;dxf3@NuN_9@Z_oB+==BWlqNGERzgsHRy+j{j<9)mJxb~XGjI0 z2j*p57=hxL-HweN;gQnH0m+aUKzZwF8hn3WherF`N>M#vHriFTJV3Wz-pDT%_USmS(?dz9sjbp#9_;T?w_Ub_MY zD2s(ob+To$d*)PwTArWgoUF*viph{16;JYdgkiNfLdD}eV%6($L_-OBN=t(07>Z1{2%?g)jrB^V z;s6z{(OZdloIEtZ4K~19gXiJujkpwWnpk5+6%Dkf;xuTuVkr-J`B3_?>lRB+dM`~~lo-hhp%DX)v z5@k*$cPc&dw$NZPKv+hOQ$V1!gSF5`H_u2;cdpZEGpI+h?nt)<94C&Em{dmM0qIB{ z&K=IfaX3NaKKErhi<1+D%KwFy4-cFO@Lsr*90FDM%oGv1eqVqV?_AQ(d62toX(VA#-t zF*#=puQ(C=FHI5^z?dWh#`)Sn@&dUK8%c&kngM@*%FobV{FGC`@Z=;D(sxp>Nx3GZ zz=Xh)(KRX8gcO(%crv>FZ*m2XUZ;2m_!m?GE=zUY)n9^()>P}y(^H{Y(9=-V2c`k} zdj2z)X}Jf2LconVBtix4te*`U10}O1EwCpzB$SvbTzg|5xJ}#5smZyzi+8rSIUxmI%y`M%bo*FmOwZ|yr5L2?e(RZr4Zr^9(Uc$Vq@T~b?>V|d^G<2h z^AMVoQnml)FOwTfq&#bWQrn!In>yP9%_Vj()pPjK{Lto0N$MTfZf&_||3~lT&hs_% zOU(gqZ{PFbhL4+;-~a2wZP{@z&wRU-YwAD3_gsq-7KgqT!xwzszWjc;?Zl~C@+rTY zi3`pZJS;VbRiD4H%Uy@Hw^)-p1HQ_5e_KK9ig)JhC&FLdd*;Gfec^NO6%?bZYt9L? zV#+q$a5z4F*Ol?s?AX4`!R?!04QfUI);`A*kldNxepXy^Y-?L{QB*`