Improve efficiency and fix some texture glitches when rendering wind and rainbow trail particles

This commit is contained in:
Sollace 2024-12-20 13:59:03 +01:00
parent 14685d80fa
commit ed15923f28
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
6 changed files with 82 additions and 19 deletions

View file

@ -3,11 +3,13 @@ package com.minelittlepony.unicopia.client.particle;
import org.joml.Vector3f; import org.joml.Vector3f;
import com.minelittlepony.unicopia.client.render.RenderUtil; import com.minelittlepony.unicopia.client.render.RenderUtil;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.ParticleTextureSheet; import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer; import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormat;
@ -42,6 +44,7 @@ public abstract class AbstractGeometryBasedParticle extends Particle {
protected final void renderQuad(MatrixStack matrices, Tessellator te, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { protected final void renderQuad(MatrixStack matrices, Tessellator te, RenderUtil.Vertex[] corners, float alpha, float tickDelta) {
int light = getBrightness(tickDelta); int light = getBrightness(tickDelta);
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram);
BufferBuilder buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); BufferBuilder buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
for (RenderUtil.Vertex corner : corners) { for (RenderUtil.Vertex corner : corners) {
var position = corner.position(matrices.peek().getPositionMatrix()); var position = corner.position(matrices.peek().getPositionMatrix());
@ -52,11 +55,16 @@ public abstract class AbstractGeometryBasedParticle extends Particle {
protected final void renderQuad(Tessellator te, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { protected final void renderQuad(Tessellator te, RenderUtil.Vertex[] corners, float alpha, float tickDelta) {
int light = getBrightness(tickDelta); int light = getBrightness(tickDelta);
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram);
BufferBuilder buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); BufferBuilder buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
quad(buffer, corners, alpha, tickDelta, light);
BufferRenderer.drawWithGlobalProgram(buffer.end());
}
protected final void quad(BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta, int light) {
for (RenderUtil.Vertex corner : corners) { for (RenderUtil.Vertex corner : corners) {
buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light); buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light);
} }
BufferRenderer.drawWithGlobalProgram(buffer.end());
} }
protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) { protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) {

View file

@ -9,8 +9,14 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment; import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
import com.minelittlepony.unicopia.client.render.bezier.Trail; import com.minelittlepony.unicopia.client.render.bezier.Trail;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -20,6 +26,8 @@ import net.minecraft.util.math.Vec3d;
public class RainbowTrailParticle extends AbstractBillboardParticle { public class RainbowTrailParticle extends AbstractBillboardParticle {
private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png"); private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png");
private final BezierSegment bezier = new BezierSegment();
private final Trail trail; private final Trail trail;
@Nullable @Nullable
@ -55,18 +63,31 @@ public class RainbowTrailParticle extends AbstractBillboardParticle {
@Override @Override
protected void renderQuads(Tessellator te, float x, float y, float z, float tickDelta) { protected void renderQuads(Tessellator te, float x, float y, float z, float tickDelta) {
float alpha = this.alpha * (1 - (float)age / maxAge); float alpha = this.alpha * (1 - (float)age / maxAge);
int light = getBrightness(tickDelta);
float scale = getScale(tickDelta);
List<Trail.Segment> segments = trail.getSegments(); List<Trail.Segment> segments = trail.getSegments();
@Nullable
BufferBuilder buffer = null;
for (int i = 0; i < segments.size() - 1; i++) { for (int i = 0; i < segments.size() - 1; i++) {
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); segments.get(i).getPlane(segments.get(i + 1), bezier);
float scale = getScale(tickDelta);
corners.forEachCorner(corner -> { for (var corner : bezier.corners()) {
corner.position().mul(scale).add(x, y, z); corner.position().mul(scale).add(x, y, z);
}); }
renderQuad(te, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); if (buffer == null) {
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram);
buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
}
quad(buffer, bezier.corners(), segments.get(i).getAlpha() * alpha, tickDelta, light);
}
if (buffer != null) {
BufferRenderer.drawWithGlobalProgram(buffer.end());
} }
} }

View file

@ -7,9 +7,15 @@ import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.client.render.bezier.BezierSegment; import com.minelittlepony.unicopia.client.render.bezier.BezierSegment;
import com.minelittlepony.unicopia.client.render.bezier.Trail; import com.minelittlepony.unicopia.client.render.bezier.Trail;
import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.BufferRenderer;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@ -19,6 +25,8 @@ import net.minecraft.util.math.Vec3d;
public class WindParticle extends AbstractBillboardParticle { public class WindParticle extends AbstractBillboardParticle {
private static final Identifier TEXTURE = Unicopia.id("textures/particle/wind.png"); private static final Identifier TEXTURE = Unicopia.id("textures/particle/wind.png");
private final BezierSegment bezier = new BezierSegment();
private final Trail trail; private final Trail trail;
@Nullable @Nullable
@ -60,18 +68,31 @@ public class WindParticle extends AbstractBillboardParticle {
@Override @Override
protected void renderQuads(Tessellator te, float x, float y, float z, float tickDelta) { protected void renderQuads(Tessellator te, float x, float y, float z, float tickDelta) {
float alpha = this.alpha * (1 - (float)age / maxAge); float alpha = this.alpha * (1 - (float)age / maxAge);
int light = getBrightness(tickDelta);
float scale = getScale(tickDelta);
List<Trail.Segment> segments = trail.getSegments(); List<Trail.Segment> segments = trail.getSegments();
@Nullable
BufferBuilder buffer = null;
for (int i = 0; i < segments.size() - 1; i++) { for (int i = 0; i < segments.size() - 1; i++) {
BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); segments.get(i).getPlane(segments.get(i + 1), bezier);
float scale = getScale(tickDelta);
corners.forEachCorner(corner -> { for (var corner : bezier.corners()) {
corner.position().mul(scale).add(x, y, z); corner.position().mul(scale).add(x, y, z);
}); }
renderQuad(te, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); if (buffer == null) {
RenderSystem.setShader(GameRenderer::getPositionTexColorProgram);
buffer = te.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT);
}
quad(buffer, bezier.corners(), segments.get(i).getAlpha() * alpha, tickDelta, light);
}
if (buffer != null) {
BufferRenderer.drawWithGlobalProgram(buffer.end());
} }
} }

View file

@ -47,6 +47,15 @@ public class RenderUtil {
this(new Vector3f(x, y, z), new Vector3f(u, v, 1)); this(new Vector3f(x, y, z), new Vector3f(u, v, 1));
} }
public Vertex() {
this(new Vector3f(), new Vector3f());
}
public void set(float x, float y, float z, float u, float v) {
position.set(x, y, z);
texture.set(u, v, 1);
}
public Vector4f position(Matrix4f mat) { public Vector4f position(Matrix4f mat) {
return mat.transform(TEMP_VECTOR.set(position, 1)); return mat.transform(TEMP_VECTOR.set(position, 1));
} }

View file

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

View file

@ -63,8 +63,8 @@ public class Trail {
return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge;
} }
public BezierSegment getPlane(Segment to) { public void getPlane(Segment to, BezierSegment segment) {
return new BezierSegment(offset, to.offset, height); segment.set(offset, to.offset, height);
} }
} }
} }