Skip to content

Commit

Permalink
greedy mesher
Browse files Browse the repository at this point in the history
but still some index errors
  • Loading branch information
Goby56 committed Jul 24, 2024
1 parent e28bb97 commit c3f71d7
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 303 deletions.
4 changes: 2 additions & 2 deletions src/main/java/com/goby56/wakes/WakesClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.goby56.wakes.event.WakeTicker;
import com.goby56.wakes.particle.ModParticles;
import com.goby56.wakes.render.SplashPlaneRenderer;
import com.goby56.wakes.render.WakeTextureRenderer;
import com.goby56.wakes.render.WakeRenderer;
import com.goby56.wakes.render.debug.WakeDebugRenderer;
import ladysnake.satin.api.managed.ManagedCoreShader;
import ladysnake.satin.api.managed.ShaderEffectManager;
Expand Down Expand Up @@ -54,7 +54,7 @@ public void onInitializeClient() {
ClientPickBlockGatherCallback.EVENT.register(new PickBoat());

// Rendering events
WorldRenderEvents.AFTER_TRANSLUCENT.register(new WakeTextureRenderer());
WorldRenderEvents.AFTER_TRANSLUCENT.register(new WakeRenderer());
WorldRenderEvents.BEFORE_DEBUG_RENDER.register(new WakeDebugRenderer());

ClientLifecycleEvents.CLIENT_STARTED.register(new SplashPlaneRenderer());
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/goby56/wakes/event/WakeTicker.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.goby56.wakes.event;

import com.goby56.wakes.render.SplashPlaneRenderer;
import com.goby56.wakes.render.WakeTextureRenderer;
import com.goby56.wakes.simulation.WakeHandler;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.world.ClientWorld;
Expand Down
6 changes: 2 additions & 4 deletions src/main/java/com/goby56/wakes/mixin/DebugHudMixin.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.goby56.wakes.mixin;

import com.goby56.wakes.WakesClient;
import com.goby56.wakes.render.WakeTextureRenderer;
import com.goby56.wakes.render.WakeRenderer;
import com.goby56.wakes.simulation.WakeHandler;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.minecraft.client.gui.hud.DebugHud;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -18,8 +17,7 @@ public class DebugHudMixin {
@Inject(at = @At("RETURN"), method = "getLeftText")
protected void getLeftText(CallbackInfoReturnable<List<String>> info) {
if (WakesClient.CONFIG_INSTANCE.drawDebugBoxes) {
info.getReturnValue().add(String.format("[Wakes] Rendering %d/%d wake nodes", WakeTextureRenderer.nodesRendered, WakeHandler.getInstance().getTotal()));
info.getReturnValue().add(String.format("[Wakes] Max tree depth: %d", WakeHandler.getInstance().getMaxDepth()));
info.getReturnValue().add(String.format("[Wakes] Rendering %d/%d wake quads", WakeRenderer.nodesRendered, WakeHandler.getInstance().getTotal()));
}
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/goby56/wakes/render/BrickMesher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.goby56.wakes.render;

import com.goby56.wakes.simulation.Brick;
import com.goby56.wakes.simulation.WakeNode;

import java.util.ArrayList;
import java.util.List;

public class BrickMesher {
public static List<WakeRenderer.WakeQuad> generateMesh(Brick brick) {
ArrayList<WakeRenderer.WakeQuad> quads = new ArrayList<>();
var ints = brick.bitMask;
for (int i = 0; i < brick.dim; i++) {
int j = 0;
while (j < brick.dim) {
j += Integer.numberOfTrailingZeros(ints[i] >> j);
if (j >= brick.dim) continue;

int h = Integer.numberOfTrailingZeros(~(ints[i] >> j));
int hm = (h == 32) ? -1 : (1 << h) - 1;
int mask = hm << j;

int w = 1;
while (i + w < brick.dim) {
int nextH = (ints[i + w] >> j) & hm;
if (nextH != hm) {
break;
}
ints[i + w] &= ~mask;
w++;
}
quads.add(new WakeRenderer.WakeQuad(i, j, w, h, getFromArea(i, j, w, h, brick)));
j += h;
}
}
return quads;
}

private static WakeNode[][] getFromArea(int x, int z, int w, int h, Brick brick) {
WakeNode[][] nodes = new WakeNode[w][h];
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
System.out.printf("%d+%d=%d, %d+%d=%d\n", x, i, x+i, z, j, z+j);
nodes[i][j] = brick.get(x + i, z + j);
}
}
return nodes;
}
}
75 changes: 0 additions & 75 deletions src/main/java/com/goby56/wakes/render/DynamicWakeTexture.java

This file was deleted.

164 changes: 164 additions & 0 deletions src/main/java/com/goby56/wakes/render/WakeRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package com.goby56.wakes.render;

import com.goby56.wakes.WakesClient;
import com.goby56.wakes.render.enums.RenderType;
import com.goby56.wakes.render.enums.WakeColor;
import com.goby56.wakes.simulation.Brick;
import com.goby56.wakes.simulation.WakeHandler;
import com.goby56.wakes.simulation.WakeNode;
import com.mojang.blaze3d.platform.GlConst;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.TextureUtil;
import com.mojang.blaze3d.systems.RenderSystem;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.color.world.BiomeColors;
import net.minecraft.client.render.*;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
import org.lwjgl.system.MemoryUtil;

import java.util.ArrayList;

public class WakeRenderer implements WorldRenderEvents.AfterTranslucent {
public static int nodesRendered = 0;
public static int res;
public static int glTexId;
public static long imgPtr;

public record WakeQuad(int x, int z, int w, int h, WakeNode[][] affectedNodes) {
}

@Override
public void afterTranslucent(WorldRenderContext context) {
if (WakesClient.CONFIG_INSTANCE.disableMod) {
return;
}
WakeHandler wakeHandler = WakeHandler.getInstance();
if (wakeHandler == null || wakeHandler.resolutionResetScheduled) return;

ArrayList<Brick> bricks = wakeHandler.getVisible(context.frustum());
Matrix4f matrix = context.matrixStack().peek().getPositionMatrix();
RenderSystem.enableBlend();
RenderSystem.disableCull();
context.lightmapTextureManager().enable();
prepTextures();

float x, z;

int n = 0;
for (var brick : bricks) {
if (WakesClient.CONFIG_INSTANCE.wakeResolution.res != WakeNode.res) continue;
Vec3d screenSpace = brick.getPos().add(context.camera().getPos().negate());
x = (float) screenSpace.x;
z = (float) screenSpace.z;

for (var quad : BrickMesher.generateMesh(brick)) {
render(matrix, quad, x, z, wakeHandler.world);
}
n++;
}
RenderSystem.enableCull();

nodesRendered = n;

}

public static void prepTextures() {
GlStateManager._bindTexture(glTexId);
GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0);
GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL12.GL_TEXTURE_MIN_LOD, 0);
GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LOD, 0);
GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0f);

GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL12.GL_TEXTURE_MIN_FILTER, GL12.GL_NEAREST);
GlStateManager._texParameter(GlConst.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAG_FILTER, GL12.GL_NEAREST);
}

public static void render(Matrix4f matrix, WakeQuad quad, float gx, float gz, World world) {
int x = quad.x();
int z = quad.z();
int w = quad.w();
int h = quad.h();
WakeNode[][] nodes = quad.affectedNodes();

res = WakesClient.CONFIG_INSTANCE.wakeResolution.res;
glTexId = TextureUtil.generateTextureId();
imgPtr = MemoryUtil.nmemAlloc((long) w * res * h * res * 4);

populatePixels(x, z, w, h, nodes, world);
draw(matrix, x + gx, z + gz, w, h, nodes, world);
}

private static void populatePixels(int x, int z, int w, int h, WakeNode[][] nodes, World world) {
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
WakeNode node = nodes[i][j];
long nodeOffset = (((i*(long) w)+j)*h);
int waterCol = BiomeColors.getWaterColor(world, node.blockPos());
float opacity = (float) ((-Math.pow(node.t, 2) + 1) * WakesClient.CONFIG_INSTANCE.wakeOpacity);
for (int r = 0; r < res; r++) {
for (int c = 0; c < res; c++) {
float avg = 0;
avg += (node.u[0][r + 1][c + 1] + node.u[1][r + 1][c + 1] + node.u[2][r + 1][c + 1]) / 3;
int color = WakeColor.getColor(avg, waterCol, opacity);
long pixelOffset = (((r*(long) res)+c)*4);
MemoryUtil.memPutInt(imgPtr + nodeOffset + pixelOffset, color);
}
}
}
}
}

private static void draw(Matrix4f matrix, float x, float z, int w, int h, WakeNode[][] nodes, World world) {
GlStateManager._bindTexture(glTexId);
GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0);
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0);
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0);
GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4);
GlStateManager._texSubImage2D(GlConst.GL_TEXTURE_2D, 0, 0, 0, w * res, h * res, GlConst.GL_RGBA, GlConst.GL_UNSIGNED_BYTE, imgPtr);

RenderSystem.setShaderTexture(0, glTexId);
RenderSystem.setShader(RenderType.getProgram());
RenderSystem.enableDepthTest(); // Is it THIS simple? https://github.com/Goby56/wakes/issues/46

BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL);

int X = nodes.length - 1;
int Z = nodes[0].length - 1;
buffer.vertex(matrix, x, nodes[0][0].height, z)
.color(1f, 1f, 1f, 1f)
.texture(0, 0)
.overlay(OverlayTexture.DEFAULT_UV)
.light(light(nodes[0][0], world))
.normal(0f, 1f, 0f).next();
buffer.vertex(matrix, x, nodes[0][Z].height, z + 1)
.color(1f, 1f, 1f, 1f)
.texture(0, 1)
.overlay(OverlayTexture.DEFAULT_UV)
.light(light(nodes[0][Z], world))
.normal(0f, 1f, 0f).next();
buffer.vertex(matrix, x + 1, nodes[X][Z].height, z + 1)
.color(1f, 1f, 1f, 1f)
.texture(1, 1)
.overlay(OverlayTexture.DEFAULT_UV)
.light(light(nodes[X][Z], world))
.normal(0f, 1f, 0f).next();
buffer.vertex(matrix, x + 1, nodes[X][0].height, z)
.color(1f, 1f, 1f, 1f)
.texture(1, 0)
.overlay(OverlayTexture.DEFAULT_UV)
.light(light(nodes[X][0], world))
.normal(0f, 1f, 0f).next();

Tessellator.getInstance().draw();
}

private static int light(WakeNode node, World world) {
return WorldRenderer.getLightmapCoordinates(world, node.blockPos());
}
}
Loading

0 comments on commit c3f71d7

Please sign in to comment.