Batching Engine

- Implement alternate backend using SBBs
This commit is contained in:
Jozufozu 2021-12-15 00:35:48 -08:00
parent 26c35e31b6
commit f1e63106fc
47 changed files with 1215 additions and 356 deletions

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.core.model.Model;
public interface Batched<S> extends StructType<S> {
BatchingTransformer<S> getTransformer(Model model);
BatchingTransformer<S> getTransformer();
@Override
default Batched<S> asBatched() {

View file

@ -1,11 +1,9 @@
package com.jozufozu.flywheel.api.struct;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.jozufozu.flywheel.core.model.SuperByteBuffer;
public abstract class BatchingTransformer<S> {
@FunctionalInterface
public interface BatchingTransformer<S> {
public void draw(S s, PoseStack stack, VertexConsumer consumer) {
}
void transform(S s, SuperByteBuffer b);
}

View file

@ -17,10 +17,13 @@ import com.jozufozu.flywheel.api.FlywheelWorld;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.config.FlwEngine;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
@ -29,6 +32,7 @@ public class Backend {
public static final Logger log = LogManager.getLogger(Backend.class);
protected static final Backend INSTANCE = new Backend();
private FlwEngine engine;
public static Backend getInstance() {
return INSTANCE;
@ -57,12 +61,13 @@ public class Backend {
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
*/
public String getBackendDescriptor() {
if (canUseInstancing()) {
return "GL33 Instanced Arrays";
}
if (enabled) {
ClientLevel level = Minecraft.getInstance().level;
if (canUseVBOs()) {
return "VBOs";
if (level == null) {
return "Invalid";
}
return InstancedRenderDispatcher.getEngineName(level);
}
return "Disabled";
@ -134,8 +139,9 @@ public class Backend {
instancedArrays = compat.instancedArraysSupported();
enabled = FlwConfig.get()
.enabled() && !OptifineHandler.usingShaders();
FlwConfig config = FlwConfig.get();
enabled = config.enabled() && !OptifineHandler.usingShaders();
engine = config.client.engine.get();
}
public boolean canUseInstancing(@Nullable Level world) {
@ -188,4 +194,8 @@ public class Backend {
public static void init() {
}
public FlwEngine getEngine() {
return engine;
}
}

View file

@ -58,6 +58,14 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
anyToRemove = true;
}
public int getModelVertexCount() {
return modelData.vertexCount();
}
public int numInstances() {
return data.size();
}
protected BitSet getDirtyBitSet() {
final int size = data.size();
final BitSet dirtySet = new BitSet(size);

View file

@ -3,4 +3,5 @@ package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.MaterialManager;
public interface Engine extends RenderDispatcher, MaterialManager {
String getName();
}

View file

@ -2,10 +2,12 @@ package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.config.FlwEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.BeginFrameEvent;
@ -29,12 +31,11 @@ public class InstanceWorld {
public InstanceWorld() {
// TODO: finish impl
if (false) {
engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(engine);
tileEntityInstanceManager = new TileInstanceManager(engine);
} else {
FlwEngine engine = Backend.getInstance()
.getEngine();
switch (engine) {
case GL33 -> {
InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD)
.build();
@ -43,7 +44,14 @@ public class InstanceWorld {
manager.addListener(entityInstanceManager);
manager.addListener(tileEntityInstanceManager);
engine = manager;
this.engine = manager;
}
case BATCHING -> {
this.engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(this.engine);
tileEntityInstanceManager = new TileInstanceManager(this.engine);
}
default -> throw new IllegalArgumentException("Unknown engine type");
}
}

View file

@ -41,6 +41,10 @@ public class InstancedRenderDispatcher {
getEntities(entity.level).queueUpdate(entity);
}
public static String getEngineName(LevelAccessor world) {
return instanceWorlds.get(world).engine.getName();
}
public static InstanceManager<BlockEntity> getTiles(LevelAccessor world) {
return instanceWorlds.get(world)
.getTileEntityInstanceManager();

View file

@ -28,9 +28,9 @@ public class BatchedMaterial<D extends InstanceData> implements Material<D> {
return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get()));
}
public void render(PoseStack stack, VertexConsumer buffer) {
public void render(PoseStack stack, VertexConsumer buffer, FormatContext context) {
for (CPUInstancer<D> instancer : models.values()) {
instancer.drawAll(stack, buffer);
instancer.drawAll(stack, buffer, context);
}
}

View file

@ -6,6 +6,8 @@ import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.model.DirectBufferBuilder;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -31,8 +33,31 @@ public class BatchedMaterialGroup implements MaterialGroup {
public void render(PoseStack stack, MultiBufferSource source) {
VertexConsumer buffer = source.getBuffer(state);
if (buffer instanceof DirectBufferBuilder direct) {
DirectVertexConsumer consumer = direct.intoDirectConsumer(calculateNeededVertices());
renderInto(stack, consumer, new FormatContext(consumer.hasOverlay()));
direct.updateAfterWriting(consumer);
} else {
renderInto(stack, buffer, FormatContext.defaultContext());
}
}
private int calculateNeededVertices() {
int total = 0;
for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) {
total += instancer.getModelVertexCount() * instancer.numInstances();
}
}
return total;
}
private void renderInto(PoseStack stack, VertexConsumer consumer, FormatContext context) {
for (BatchedMaterial<?> value : materials.values()) {
value.render(stack, buffer);
value.render(stack, consumer, context);
}
}

View file

@ -8,6 +8,7 @@ import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
@ -40,15 +41,28 @@ public class BatchingEngine implements Engine {
@Override
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
PoseStack stack = event.stack;
stack.pushPose();
stack.translate(-event.camX, -event.camY, -event.camZ);
for (Map.Entry<RenderType, BatchedMaterialGroup> entry : layers.get(event.getLayer()).entrySet()) {
BatchedMaterialGroup group = entry.getValue();
group.render(event.stack, buffers);
group.render(stack, buffers);
}
stack.popPose();
}
@Override
public void beginFrame(Camera info) {
}
@Override
public String getName() {
return "Batching";
}
}

View file

@ -5,18 +5,23 @@ import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.SuperByteBuffer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private final BatchingTransformer<D> renderer;
private final BatchingTransformer<D> transform;
private final SuperByteBuffer sbb;
public CPUInstancer(StructType<D> type, Model modelData) {
super(type, modelData);
renderer = type.asBatched()
.getTransformer(modelData);
sbb = new SuperByteBuffer(modelData);
transform = type.asBatched()
.getTransformer();
}
@Override
@ -24,15 +29,19 @@ public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
// noop
}
public void drawAll(PoseStack stack, VertexConsumer buffer) {
if (renderer == null) {
public void drawAll(PoseStack stack, VertexConsumer buffer, FormatContext context) {
if (transform == null) {
return;
}
renderSetup();
for (D d : data) {
renderer.draw(d, stack, buffer);
if (context.usesOverlay()) sbb.entityMode();
transform.transform(d, sbb);
sbb.renderInto(stack, buffer);
}
}

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.backend.instancing.batching;
public record FormatContext(boolean usesOverlay) {
public static FormatContext defaultContext() {
return new FormatContext(false);
}
}

View file

@ -165,6 +165,11 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
}
}
@Override
public String getName() {
return "GL33 Instanced Arrays";
}
@FunctionalInterface
public interface OriginShiftListener {
void onOriginShift();

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.backend.model;
public interface DirectBufferBuilder {
DirectVertexConsumer intoDirectConsumer(int neededVerts);
void updateAfterWriting(DirectVertexConsumer complete);
}

View file

@ -0,0 +1,136 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
public class DirectVertexConsumer implements VertexConsumer {
public final VertexFormat format;
private final int stride;
public final int startPos;
private int position = -1;
private int normal = -1;
private int color = -1;
private int uv = -1;
private int uv1 = -1;
private int uv2 = -1;
private long vertexBase;
private int vertexCount;
public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format) {
this.format = format;
startPos = buffer.position();
stride = format.getVertexSize();
int offset = 0;
for (VertexFormatElement element : format.getElements()) {
switch (element.getUsage()) {
case POSITION -> this.position = offset;
case NORMAL -> this.normal = offset;
case COLOR -> this.color = offset;
case UV -> {
switch (element.getIndex()) {
case 0 -> this.uv = offset;
case 1 -> this.uv1 = offset;
case 2 -> this.uv2 = offset;
}
}
}
offset += element.getByteSize();
}
this.vertexBase = MemoryUtil.memAddress(buffer);
}
public boolean hasOverlay() {
return uv1 >= 0;
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
if (position < 0) return this;
long base = vertexBase + position;
MemoryUtil.memPutFloat(base, (float) x);
MemoryUtil.memPutFloat(base + 4, (float) y);
MemoryUtil.memPutFloat(base + 8, (float) z);
return this;
}
@Override
public VertexConsumer color(int r, int g, int b, int a) {
if (color < 0) return this;
long base = vertexBase + color;
MemoryUtil.memPutByte(base, (byte) r);
MemoryUtil.memPutByte(base + 1, (byte) g);
MemoryUtil.memPutByte(base + 2, (byte) b);
MemoryUtil.memPutByte(base + 3, (byte) a);
return this;
}
@Override
public VertexConsumer uv(float u, float v) {
if (uv < 0) return this;
long base = vertexBase + uv;
MemoryUtil.memPutFloat(base, u);
MemoryUtil.memPutFloat(base + 4, v);
return this;
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
if (uv1 < 0) return this;
long base = vertexBase + uv1;
MemoryUtil.memPutShort(base, (short) u);
MemoryUtil.memPutShort(base + 2, (short) v);
return this;
}
@Override
public VertexConsumer uv2(int u, int v) {
if (uv2 < 0) return this;
long base = vertexBase + uv2;
MemoryUtil.memPutShort(base, (short) u);
MemoryUtil.memPutShort(base + 2, (short) v);
return this;
}
@Override
public VertexConsumer normal(float x, float y, float z) {
if (normal < 0) return this;
long base = vertexBase + normal;
MemoryUtil.memPutByte(base, RenderMath.nb(x));
MemoryUtil.memPutByte(base + 1, RenderMath.nb(y));
MemoryUtil.memPutByte(base + 2, RenderMath.nb(z));
return this;
}
@Override
public void endVertex() {
vertexBase += stride;
vertexCount++;
}
@Override
public void defaultColor(int r, int g, int b, int a) {
}
@Override
public void unsetDefaultColor() {
}
public int getVertexCount() {
return vertexCount;
}
}

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.model;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -18,14 +18,22 @@ public abstract class BufferWriter<S> implements StructWriter<S> {
this.stride = this.format.getStride();
}
/**
* Advances the write pointer forward by the stride of one vertex. This should always be called after a
* vertex is written. Implementations which override this should always call invoke the super implementation.
*/
protected void advance() {
@Override
public final void write(S struct) {
writeInternal(struct);
advance();
}
/**
* Advances the write pointer forward by the stride of one vertex.
* This will always be called after a struct is written, implementors need not call it themselves.
*
* @see #write
*/
protected abstract void advance();
protected abstract void writeInternal(S s);
@Override
public void seek(int pos) {
backingBuffer.position(pos * stride);

View file

@ -15,7 +15,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
*/
public abstract class UnsafeBufferWriter<S> extends BufferWriter<S> {
/**
* The write pointer into the buffer storage. This is advanced by the vertex stride every time
* The write pointer into the buffer storage. This is advanced by the stride every time
* {@link UnsafeBufferWriter#advance()} is called.
*/
protected long writePointer;
@ -35,8 +35,6 @@ public abstract class UnsafeBufferWriter<S> extends BufferWriter<S> {
@Override
protected void advance() {
this.writePointer += this.stride;
super.advance();
}
private void acquireWritePointer() {

View file

@ -0,0 +1,32 @@
package com.jozufozu.flywheel.config;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.ArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.server.command.EnumArgument;
public class EngineConfigCommand {
public ArgumentBuilder<CommandSourceStack, ?> register() {
return Commands.literal("engine")
.executes(context -> {
ServerPlayer player = context.getSource()
.getPlayerOrException();
FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket());
return Command.SINGLE_SUCCESS;
})
.then(Commands.argument("type", EnumArgument.enumArgument(FlwEngine.class))
.executes(context -> {
FlwEngine type = context.getArgument("type", FlwEngine.class);
ServerPlayer player = context.getSource()
.getPlayerOrException();
FlwPackets.channel.send(PacketDistributor.PLAYER.with(() -> player), new SConfigureEnginePacket(type));
return Command.SINGLE_SUCCESS;
}));
}
}

View file

@ -17,6 +17,7 @@ public class FlwCommands {
dispatcher.register(Commands.literal("flywheel")
.then(new BooleanConfigCommand("backend", BooleanConfig.ENGINE).register())
.then(new BooleanConfigCommand("debugNormals", BooleanConfig.NORMAL_OVERLAY).register())
.then(new EngineConfigCommand().register())
);
}
}

View file

@ -39,6 +39,7 @@ public class FlwConfig {
public static class ClientConfig {
public final BooleanValue enabled;
public final ForgeConfigSpec.EnumValue<FlwEngine> engine;
public final BooleanValue debugNormals;
public ClientConfig(ForgeConfigSpec.Builder builder) {
@ -46,6 +47,9 @@ public class FlwConfig {
enabled = builder.comment("Enable or disable the entire engine")
.define("enabled", true);
engine = builder.comment("Enable or disable the entire engine")
.defineEnum("backend", FlwEngine.GL33);
debugNormals = builder.comment("Enable or disable a debug overlay that colors pixels by their normal")
.define("debugNormals", false);
}

View file

@ -0,0 +1,57 @@
package com.jozufozu.flywheel.config;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.OptifineHandler;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextComponent;
public enum FlwEngine {
BATCHING(new TextComponent("Batching").withStyle(ChatFormatting.BLUE)),
GL33(new TextComponent("GL 3.3 Instanced Arrays").withStyle(ChatFormatting.GREEN)),
;
private final Component name;
FlwEngine(Component name) {
this.name = name;
}
@Nullable
public static FlwEngine decode(FriendlyByteBuf buffer) {
byte b = buffer.readByte();
if (b == -1) return null;
return values()[b];
}
public void encode(FriendlyByteBuf buffer) {
buffer.writeByte(this.ordinal());
}
public void switchTo() {
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return;
// if (state == BooleanDirective.DISPLAY) {
// Component text = new TextComponent("Flywheel renderer is currently: ").append(boolToText(FlwConfig.get().enabled()));
// player.displayClientMessage(text, false);
// return;
// }
FlwConfig.get().client.engine.set(this);
Component text = new TextComponent("Using ").withStyle(ChatFormatting.WHITE).append(name);
player.displayClientMessage(text, false);
Backend.reloadWorldRenderers();
}
}

View file

@ -24,5 +24,11 @@ public class FlwPackets {
.encoder(SConfigureBooleanPacket::encode)
.consumer(SConfigureBooleanPacket::execute)
.add();
channel.messageBuilder(SConfigureEnginePacket.class, 1, NetworkDirection.PLAY_TO_CLIENT)
.decoder(SConfigureEnginePacket::new)
.encoder(SConfigureEnginePacket::encode)
.consumer(SConfigureEnginePacket::execute)
.add();
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.config;
import java.util.function.Supplier;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent;
public class SConfigureEnginePacket {
private final FlwEngine type;
public SConfigureEnginePacket() {
type = null;
}
public SConfigureEnginePacket(FlwEngine type) {
this.type = type;
}
public SConfigureEnginePacket(FriendlyByteBuf buffer) {
type = FlwEngine.decode(buffer);
}
public void encode(FriendlyByteBuf buffer) {
if (type != null)
type.encode(buffer);
else
buffer.writeByte(-1);
}
public void execute(Supplier<NetworkEvent.Context> ctx) {
if (type != null) {
type.switchTo();
}
ctx.get()
.setPacketHandled(true);
}
}

View file

@ -2,6 +2,8 @@ package com.jozufozu.flywheel.core.materials;
import com.jozufozu.flywheel.api.InstanceData;
import net.minecraft.client.renderer.LightTexture;
public abstract class BasicData extends InstanceData implements FlatLit<BasicData> {
public byte blockLight;
@ -14,18 +16,23 @@ public abstract class BasicData extends InstanceData implements FlatLit<BasicDat
@Override
public BasicData setBlockLight(int blockLight) {
this.blockLight = (byte) (blockLight << 4);
this.blockLight = (byte) blockLight;
markDirty();
return this;
}
@Override
public BasicData setSkyLight(int skyLight) {
this.skyLight = (byte) (skyLight << 4);
this.skyLight = (byte) skyLight;
markDirty();
return this;
}
@Override
public int getPackedLight() {
return LightTexture.pack(this.blockLight, this.skyLight);
}
public BasicData setColor(int color) {
return setColor(color, false);
}

View file

@ -24,4 +24,6 @@ public interface FlatLit<D extends InstanceData & FlatLit<D>> {
* @return <code>this</code>
*/
D setSkyLight(int skyLight);
int getPackedLight();
}

View file

@ -0,0 +1,25 @@
package com.jozufozu.flywheel.core.materials;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter;
public abstract class UnsafeBasicWriter<D extends BasicData> extends UnsafeBufferWriter<D> {
public UnsafeBasicWriter(VecBuffer backingBuffer, StructType<D> vertexType) {
super(backingBuffer, vertexType);
}
@Override
protected void writeInternal(D d) {
long addr = writePointer;
MemoryUtil.memPutByte(addr, (byte) (d.blockLight << 4));
MemoryUtil.memPutByte(addr + 1, (byte) (d.skyLight << 4));
MemoryUtil.memPutByte(addr + 2, d.r);
MemoryUtil.memPutByte(addr + 3, d.g);
MemoryUtil.memPutByte(addr + 4, d.b);
MemoryUtil.memPutByte(addr + 5, d.a);
}
}

View file

@ -1,12 +0,0 @@
package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.core.model.Model;
public class ModelTransformer extends BatchingTransformer<ModelData> {
public ModelTransformer(Model model) {
//model.buffer();
}
}

View file

@ -8,8 +8,6 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.materials.model.writer.UnsafeModelWriter;
import com.jozufozu.flywheel.core.model.Model;
import net.minecraft.resources.ResourceLocation;
@ -36,7 +34,11 @@ public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
}
@Override
public BatchingTransformer<ModelData> getTransformer(Model model) {
return null;
public BatchingTransformer<ModelData> getTransformer() {
return (d, b) -> {
b.transform(d.model, d.normal)
.color(d.r, d.g, d.b, d.a)
.light(d.getPackedLight());
};
}
}

View file

@ -0,0 +1,23 @@
package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter;
import com.jozufozu.flywheel.util.WriteUnsafe;
public class UnsafeModelWriter extends UnsafeBasicWriter<ModelData> {
public UnsafeModelWriter(VecBuffer backingBuffer, StructType<ModelData> vertexType) {
super(backingBuffer, vertexType);
}
@Override
protected void writeInternal(ModelData d) {
super.writeInternal(d);
long addr = writePointer + 6;
((WriteUnsafe) (Object) d.model).writeUnsafe(addr);
addr += 4 * 16;
((WriteUnsafe) (Object) d.normal).writeUnsafe(addr);
}
}

View file

@ -1,35 +0,0 @@
package com.jozufozu.flywheel.core.materials.model.writer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.WriteUnsafe;
public class UnsafeModelWriter extends UnsafeBufferWriter<ModelData> {
public UnsafeModelWriter(VecBuffer backingBuffer, StructType<ModelData> vertexType) {
super(backingBuffer, vertexType);
}
@Override
public void write(ModelData d) {
long addr = writePointer;
MemoryUtil.memPutByte(addr, d.blockLight);
MemoryUtil.memPutByte(addr + 1, d.skyLight);
MemoryUtil.memPutByte(addr + 2, d.r);
MemoryUtil.memPutByte(addr + 3, d.g);
MemoryUtil.memPutByte(addr + 4, d.b);
MemoryUtil.memPutByte(addr + 5, d.a);
addr += 6;
((WriteUnsafe) (Object) d.model).writeUnsafe(addr);
addr += 4 * 16;
((WriteUnsafe) (Object) d.normal).writeUnsafe(addr);
advance();
}
}

View file

@ -8,8 +8,7 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.materials.oriented.writer.UnsafeOrientedWriter;
import com.jozufozu.flywheel.core.model.Model;
import com.mojang.math.Quaternion;
import net.minecraft.resources.ResourceLocation;
@ -36,7 +35,13 @@ public class OrientedType implements Instanced<OrientedData>, Batched<OrientedDa
}
@Override
public BatchingTransformer<OrientedData> getTransformer(Model model) {
return null;
public BatchingTransformer<OrientedData> getTransformer() {
return (d, sbb) -> {
sbb.light(d.getPackedLight())
.color(d.r, d.g, d.b, d.a)
.translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ)
.multiply(new Quaternion(d.qX, d.qY, d.qZ, d.qW))
.translate(-d.pivotX, -d.pivotY, -d.pivotZ);
};
}
}

View file

@ -1,26 +1,20 @@
package com.jozufozu.flywheel.core.materials.oriented.writer;
package com.jozufozu.flywheel.core.materials.oriented;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.materials.UnsafeBasicWriter;
public class UnsafeOrientedWriter extends UnsafeBufferWriter<OrientedData> {
public class UnsafeOrientedWriter extends UnsafeBasicWriter<OrientedData> {
public UnsafeOrientedWriter(VecBuffer backingBuffer, StructType<OrientedData> vertexType) {
super(backingBuffer, vertexType);
}
@Override
public void write(OrientedData d) {
protected void writeInternal(OrientedData d) {
long addr = writePointer;
MemoryUtil.memPutByte(addr, d.blockLight);
MemoryUtil.memPutByte(addr + 1, d.skyLight);
MemoryUtil.memPutByte(addr + 2, d.r);
MemoryUtil.memPutByte(addr + 3, d.g);
MemoryUtil.memPutByte(addr + 4, d.b);
MemoryUtil.memPutByte(addr + 5, d.a);
super.writeInternal(d);
MemoryUtil.memPutFloat(addr + 6, d.posX);
MemoryUtil.memPutFloat(addr + 10, d.posY);
@ -32,7 +26,5 @@ public class UnsafeOrientedWriter extends UnsafeBufferWriter<OrientedData> {
MemoryUtil.memPutFloat(addr + 34, d.qY);
MemoryUtil.memPutFloat(addr + 38, d.qZ);
MemoryUtil.memPutFloat(addr + 42, d.qW);
advance();
}
}

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.core.materials;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -1,138 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
public class BakedModelModel implements Model {
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
private static final Direction[] dirs;
static {
Direction[] directions = Direction.values();
dirs = Arrays.copyOf(directions, directions.length + 1);
}
public final BakedModel model;
private final int numQuads;
public BakedModelModel(BakedModel model) {
this.model = model;
Random random = new Random();
int numQuads = 0;
for (Direction dir : dirs) {
random.setSeed(42);
List<BakedQuad> quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE);
numQuads += quads.size();
}
this.numQuads = numQuads;
}
@Override
public String name() {
return model.toString();
}
@Override
public void buffer(VertexConsumer buffer) {
Minecraft mc = Minecraft.getInstance();
ItemColors itemColors = mc.getItemColors();
Random random = new Random();
for (Direction dir : dirs) {
random.setSeed(42);
List<BakedQuad> quads = model.getQuads(null, dir, random, VirtualEmptyModelData.INSTANCE);
for (BakedQuad bakedQuad : quads) {
// int i = -1;
// if (!itemStack.isEmpty() && bakedQuad.isTinted()) {
// i = itemColors.getColor(itemStack, bakedQuad.getTintIndex());
// }
//
// byte red = (byte)(i >> 16 & 255);
// byte green = (byte)(i >> 8 & 255);
// byte blue = (byte)(i & 255);
int[] aint = bakedQuad.getVertices();
Vec3i faceNormal = bakedQuad.getDirection().getNormal();
Vector3f normal = new Vector3f((float)faceNormal.getX(), (float)faceNormal.getY(), (float)faceNormal.getZ());
int intSize = DefaultVertexFormat.BLOCK.getIntegerSize();
int vertexCount = aint.length / intSize;
try (MemoryStack memorystack = MemoryStack.stackPush()) {
ByteBuffer bytebuffer = memorystack.malloc(DefaultVertexFormat.BLOCK.getVertexSize());
IntBuffer intbuffer = bytebuffer.asIntBuffer();
for(int j = 0; j < vertexCount; ++j) {
((Buffer)intbuffer).clear();
intbuffer.put(aint, j * 8, 8);
float f = bytebuffer.getFloat(0);
float f1 = bytebuffer.getFloat(4);
float f2 = bytebuffer.getFloat(8);
// float cr;
// float cg;
// float cb;
// float ca;
// {
// float r = (float)(bytebuffer.get(12) & 255) / 255.0F;
// float g = (float)(bytebuffer.get(13) & 255) / 255.0F;
// float b = (float)(bytebuffer.get(14) & 255) / 255.0F;
// float a = (float)(bytebuffer.get(15) & 255) / 255.0F;
// cr = r * red;
// cg = g * green;
// cb = b * blue;
// ca = a;
// }
float u = bytebuffer.getFloat(16);
float v = bytebuffer.getFloat(20);
buffer.vertex(f, f1, f2);
buffer.normal(normal.x(), normal.y(), normal.z());
buffer.uv(u, v);
buffer.endVertex();
}
}
}
}
}
@Override
public int vertexCount() {
return numQuads * 4;
}
@Override
public VertexFormat format() {
return Formats.UNLIT_MODEL;
}
}

View file

@ -5,6 +5,7 @@ import java.util.Arrays;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.BufferBuilderReader;
import com.jozufozu.flywheel.util.ModelReader;
import com.jozufozu.flywheel.util.RenderMath;
import com.jozufozu.flywheel.util.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.BufferBuilder;
@ -27,7 +28,7 @@ import net.minecraft.world.level.block.state.BlockState;
public class BlockModel implements Model {
private static final PoseStack IDENTITY = new PoseStack();
private final BufferBuilderReader reader;
private final ModelReader reader;
private final String name;
@ -69,7 +70,7 @@ public class BlockModel implements Model {
for (int i = 0; i < vertexCount; i++) {
buffer.vertex(reader.getX(i), reader.getY(i), reader.getZ(i));
buffer.normal(RenderMath.f(reader.getNX(i)), RenderMath.f(reader.getNY(i)), RenderMath.f(reader.getNZ(i)));
buffer.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i));
buffer.uv(reader.getU(i), reader.getV(i));
@ -77,6 +78,11 @@ public class BlockModel implements Model {
}
}
@Override
public ModelReader getReader() {
return reader;
}
public static BufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack ms) {
Minecraft mc = Minecraft.getInstance();
BlockRenderDispatcher dispatcher = mc.getBlockRenderer();
@ -96,12 +102,4 @@ public class BlockModel implements Model {
return builder;
}
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
private static final Direction[] dirs;
static {
Direction[] directions = Direction.values();
dirs = Arrays.copyOf(directions, directions.length + 1);
}
}

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.util.ModelReader;
import com.mojang.blaze3d.vertex.VertexConsumer;
/**
@ -76,4 +77,6 @@ public interface Model {
default boolean empty() {
return vertexCount() == 0;
}
ModelReader getReader();
}

View file

@ -1,10 +1,15 @@
package com.jozufozu.flywheel.core.model;
import java.nio.ByteBuffer;
import java.util.List;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.ModelReader;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Vector3f;
public class ModelPart implements Model {
@ -48,4 +53,95 @@ public class ModelPart implements Model {
public VertexFormat format() {
return Formats.UNLIT_MODEL;
}
@Override
public ModelReader getReader() {
return new PartReader(this);
}
private class PartReader implements ModelReader {
private final ByteBuffer buffer;
private PartReader(ModelPart part) {
this.buffer = ByteBuffer.allocate(part.size());
VecBufferWriter writer = new VecBufferWriter(this.buffer);
buffer(writer);
}
private int vertIdx(int vertexIndex) {
return vertexIndex * format().getStride();
}
@Override
public float getX(int index) {
return buffer.getFloat(vertIdx(index));
}
@Override
public float getY(int index) {
return buffer.getFloat(vertIdx(index) + 4);
}
@Override
public float getZ(int index) {
return buffer.getFloat(vertIdx(index) + 8);
}
@Override
public byte getR(int index) {
return (byte) 0xFF;
}
@Override
public byte getG(int index) {
return (byte) 0xFF;
}
@Override
public byte getB(int index) {
return (byte) 0xFF;
}
@Override
public byte getA(int index) {
return (byte) 0xFF;
}
@Override
public float getU(int index) {
return buffer.getFloat(vertIdx(index) + 15);
}
@Override
public float getV(int index) {
return buffer.getFloat(vertIdx(index) + 19);
}
@Override
public int getLight(int index) {
return 0;
}
@Override
public float getNX(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 12));
}
@Override
public float getNY(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 13));
}
@Override
public float getNZ(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 14));
}
@Override
public int getVertexCount() {
return vertices;
}
}
}

View file

@ -1,70 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.VertexConsumer;
public class RecordingVertexConsumer implements VertexConsumer {
List<Consumer<VertexConsumer>> replay = new ArrayList<>();
@Override
public VertexConsumer vertex(double x, double y, double z) {
replay.add(v -> v.vertex(x, y, z));
return this;
}
@Override
public VertexConsumer color(int r, int g, int b, int a) {
replay.add(v -> v.color(r, g, b, a));
return this;
}
@Override
public VertexConsumer uv(float u, float v) {
replay.add(vc -> vc.uv(u, v));
return this;
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
replay.add(vc -> vc.overlayCoords(u, v));
return this;
}
@Override
public VertexConsumer uv2(int u, int v) {
replay.add(vc -> vc.uv2(u, v));
return this;
}
@Override
public VertexConsumer normal(float x, float y, float z) {
replay.add(v -> v.normal(x, y, z));
return this;
}
@Override
public void endVertex() {
replay.add(VertexConsumer::endVertex);
}
@Override
public void defaultColor(int r, int g, int b, int a) {
replay.add(vc -> vc.defaultColor(r, g, b, a));
}
@Override
public void unsetDefaultColor() {
replay.add(VertexConsumer::unsetDefaultColor);
}
public VertexRecording saveRecording() {
VertexRecording out = new VertexRecording(ImmutableList.copyOf(replay));
replay.clear();
return out;
}
}

View file

@ -0,0 +1,468 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.util.ModelReader;
import com.jozufozu.flywheel.util.transform.Rotate;
import com.jozufozu.flywheel.util.transform.Scale;
import com.jozufozu.flywheel.util.transform.TStack;
import com.jozufozu.flywheel.util.transform.Translate;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.*;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraftforge.client.model.pipeline.LightUtil;
public class SuperByteBuffer implements Scale<SuperByteBuffer>, Translate<SuperByteBuffer>, Rotate<SuperByteBuffer>, TStack<SuperByteBuffer> {
private final Model model;
private final ModelReader template;
// Vertex Position
private final PoseStack transforms;
private final Params defaultParams = Params.defaultParams();
private final Params params = defaultParams.copy();
// Temporary
private static final Long2IntMap WORLD_LIGHT_CACHE = new Long2IntOpenHashMap();
private final Vector4f pos = new Vector4f();
private final Vector3f normal = new Vector3f();
private final Vector4f lightPos = new Vector4f();
public SuperByteBuffer(Model model) {
this.model = model;
template = model.getReader();
transforms = new PoseStack();
transforms.pushPose();
}
public void renderInto(PoseStack input, VertexConsumer builder) {
if (isEmpty())
return;
Matrix4f modelMat = input.last()
.pose()
.copy();
Matrix4f localTransforms = transforms.last()
.pose();
modelMat.multiply(localTransforms);
Matrix3f normalMat;
if (params.fullNormalTransform) {
normalMat = input.last().normal().copy();
normalMat.mul(transforms.last().normal());
} else {
normalMat = transforms.last().normal().copy();
}
if (params.useWorldLight) {
WORLD_LIGHT_CACHE.clear();
}
float f = .5f;
int vertexCount = template.getVertexCount();
for (int i = 0; i < vertexCount; i++) {
float x = template.getX(i);
float y = template.getY(i);
float z = template.getZ(i);
pos.set(x, y, z, 1F);
pos.transform(modelMat);
builder.vertex(pos.x(), pos.y(), pos.z());
float normalX = template.getNX(i);
float normalY = template.getNY(i);
float normalZ = template.getNZ(i);
normal.set(normalX, normalY, normalZ);
normal.transform(normalMat);
normal.normalize();
float nx = normal.x();
float ny = normal.y();
float nz = normal.z();
float instanceDiffuse = LightUtil.diffuseLight(nx, ny, nz);
switch (params.colorMode) {
case MODEL_ONLY -> builder.color(template.getR(i), template.getG(i), template.getB(i), template.getA(i));
case DIFFUSE_ONLY -> builder.color(instanceDiffuse, instanceDiffuse, instanceDiffuse, 1f);
case MODEL_DIFFUSE -> {
int r = Byte.toUnsignedInt(template.getR(i));
int g = Byte.toUnsignedInt(template.getG(i));
int b = Byte.toUnsignedInt(template.getB(i));
int a = Byte.toUnsignedInt(template.getA(i));
float diffuse = switch (params.diffuseMode) {
case NONE -> 1f;
case INSTANCE -> instanceDiffuse;
case ONE_OVER_STATIC -> 1 / LightUtil.diffuseLight(normalX, normalY, normalZ);
case INSTANCE_OVER_STATIC -> instanceDiffuse / LightUtil.diffuseLight(normalX, normalY, normalZ);
};
if (diffuse != 1) {
r = transformColor(r, diffuse);
g = transformColor(g, diffuse);
b = transformColor(b, diffuse);
}
builder.color(r, g, b, a);
}
case RECOLOR -> {
if (params.diffuseMode == DiffuseMode.NONE) {
builder.color(params.r, params.g, params.b, params.a);
} else {
int colorR = transformColor(params.r, instanceDiffuse);
int colorG = transformColor(params.g, instanceDiffuse);
int colorB = transformColor(params.b, instanceDiffuse);
builder.color(colorR, colorG, colorB, params.a);
}
}
}
//builder.color(Math.max(0, (int) (nx * 255)), Math.max(0, (int) (ny * 255)), Math.max(0, (int) (nz * 255)), 0xFF);
//builder.color(Math.max(0, (int) (normalX * 255)), Math.max(0, (int) (normalY * 255)), Math.max(0, (int) (normalZ * 255)), 0xFF);
float u = template.getU(i);
float v = template.getV(i);
if (params.spriteShiftFunc != null) {
params.spriteShiftFunc.shift(builder, u, v);
} else {
builder.uv(u, v);
}
if (params.hasOverlay) {
builder.overlayCoords(params.overlay);
}
int light;
if (params.useWorldLight) {
lightPos.set(((x - f) * 15 / 16f) + f, (y - f) * 15 / 16f + f, (z - f) * 15 / 16f + f, 1F);
lightPos.transform(localTransforms);
if (params.lightTransform != null) {
lightPos.transform(params.lightTransform);
}
light = getLight(Minecraft.getInstance().level, lightPos);
if (params.hasCustomLight) {
light = maxLight(light, params.packedLightCoords);
}
} else if (params.hasCustomLight) {
light = params.packedLightCoords;
} else {
light = template.getLight(i);
}
if (params.hybridLight) {
builder.uv2(maxLight(light, template.getLight(i)));
} else {
builder.uv2(light);
}
builder.normal(nx, ny, nz);
builder.endVertex();
}
reset();
}
public SuperByteBuffer reset() {
while (!transforms.clear())
transforms.popPose();
transforms.pushPose();
params.load(defaultParams);
return this;
}
@Override
public SuperByteBuffer translate(double x, double y, double z) {
transforms.translate(x, y, z);
return this;
}
@Override
public SuperByteBuffer multiply(Quaternion quaternion) {
transforms.mulPose(quaternion);
return this;
}
public SuperByteBuffer transform(PoseStack stack) {
PoseStack.Pose last = stack.last();
return transform(last.pose(), last.normal());
}
public SuperByteBuffer transform(Matrix4f pose, Matrix3f normal) {
transforms.last()
.pose()
.multiply(pose);
transforms.last()
.normal()
.mul(normal);
return this;
}
public SuperByteBuffer rotateCentered(Direction axis, float radians) {
translate(.5f, .5f, .5f).rotate(axis, radians)
.translate(-.5f, -.5f, -.5f);
return this;
}
public SuperByteBuffer rotateCentered(Quaternion q) {
translate(.5f, .5f, .5f).multiply(q)
.translate(-.5f, -.5f, -.5f);
return this;
}
public SuperByteBuffer color(int r, int g, int b, int a) {
params.colorMode = ColorMode.RECOLOR;
params.r = r;
params.g = g;
params.b = b;
params.a = a;
return this;
}
public SuperByteBuffer color(byte r, byte g, byte b, byte a) {
params.colorMode = ColorMode.RECOLOR;
params.r = Byte.toUnsignedInt(r);
params.g = Byte.toUnsignedInt(g);
params.b = Byte.toUnsignedInt(b);
params.a = Byte.toUnsignedInt(a);
return this;
}
public SuperByteBuffer color(int color) {
params.colorMode = ColorMode.RECOLOR;
params.r = ((color >> 16) & 0xFF);
params.g = ((color >> 8) & 0xFF);
params.b = (color & 0xFF);
params.a = 255;
return this;
}
public SuperByteBuffer shiftUV(SpriteShiftFunc entry) {
params.spriteShiftFunc = entry;
return this;
}
public SuperByteBuffer overlay() {
params.hasOverlay = true;
return this;
}
public SuperByteBuffer overlay(int overlay) {
params.hasOverlay = true;
params.overlay = overlay;
return this;
}
/**
* Transforms normals not only by the local matrix stack, but also by the passed matrix stack.
*/
public SuperByteBuffer entityMode() {
params.hasOverlay = true;
params.fullNormalTransform = true;
params.diffuseMode = DiffuseMode.NONE;
params.colorMode = ColorMode.RECOLOR;
return this;
}
public SuperByteBuffer light() {
params.useWorldLight = true;
return this;
}
public SuperByteBuffer light(Matrix4f lightTransform) {
params.useWorldLight = true;
params.lightTransform = lightTransform;
return this;
}
public SuperByteBuffer light(int packedLightCoords) {
params.hasCustomLight = true;
params.packedLightCoords = packedLightCoords;
return this;
}
public SuperByteBuffer light(Matrix4f lightTransform, int packedLightCoords) {
light(lightTransform);
light(packedLightCoords);
return this;
}
/**
* Uses max light from calculated light (world light or custom light) and vertex light for the final light value.
* Ineffective if any other light method was not called.
*/
public SuperByteBuffer hybridLight() {
params.hybridLight = true;
return this;
}
public boolean isEmpty() {
return template.isEmpty();
}
@Override
public SuperByteBuffer scale(float factorX, float factorY, float factorZ) {
transforms.scale(factorX, factorY, factorZ);
return this;
}
@Override
public SuperByteBuffer pushPose() {
transforms.pushPose();
return this;
}
@Override
public SuperByteBuffer popPose() {
transforms.popPose();
return this;
}
@Override
public String toString() {
return "SuperByteBuffer[" + model + ']';
}
public static int transformColor(int component, float scale) {
return Mth.clamp((int) (component * scale), 0, 255);
}
public static int maxLight(int packedLight1, int packedLight2) {
int blockLight1 = LightTexture.block(packedLight1);
int skyLight1 = LightTexture.sky(packedLight1);
int blockLight2 = LightTexture.block(packedLight2);
int skyLight2 = LightTexture.sky(packedLight2);
return LightTexture.pack(Math.max(blockLight1, blockLight2), Math.max(skyLight1, skyLight2));
}
private static int getLight(Level world, Vector4f lightPos) {
BlockPos pos = new BlockPos(lightPos.x(), lightPos.y(), lightPos.z());
return WORLD_LIGHT_CACHE.computeIfAbsent(pos.asLong(), $ -> LevelRenderer.getLightColor(world, pos));
}
@FunctionalInterface
public interface SpriteShiftFunc {
void shift(VertexConsumer builder, float u, float v);
}
public enum ColorMode {
MODEL_ONLY,
MODEL_DIFFUSE,
DIFFUSE_ONLY,
RECOLOR
}
public enum DiffuseMode {
NONE,
INSTANCE,
ONE_OVER_STATIC,
INSTANCE_OVER_STATIC,
}
public static class Params {
// Vertex Coloring
public ColorMode colorMode = ColorMode.DIFFUSE_ONLY;
public DiffuseMode diffuseMode = DiffuseMode.INSTANCE;
public int r;
public int g;
public int b;
public int a;
// Vertex Texture Coords
public SpriteShiftFunc spriteShiftFunc;
// Vertex Overlay Color
public boolean hasOverlay;
public int overlay = OverlayTexture.NO_OVERLAY;
// Vertex Lighting
public boolean useWorldLight;
public Matrix4f lightTransform;
public boolean hasCustomLight;
public int packedLightCoords;
public boolean hybridLight;
// Vertex Normals
public boolean fullNormalTransform;
public void load(Params from) {
colorMode = from.colorMode;
diffuseMode = from.diffuseMode;
r = from.r;
g = from.g;
b = from.b;
a = from.a;
spriteShiftFunc = from.spriteShiftFunc;
hasOverlay = from.hasOverlay;
overlay = from.overlay;
useWorldLight = from.useWorldLight;
lightTransform = from.lightTransform;
hasCustomLight = from.hasCustomLight;
packedLightCoords = from.packedLightCoords;
hybridLight = from.hybridLight;
fullNormalTransform = from.fullNormalTransform;
}
public Params copy() {
Params params = new Params();
params.load(this);
return params;
}
public static Params defaultParams() {
Params out = new Params();
out.colorMode = ColorMode.DIFFUSE_ONLY;
out.diffuseMode = DiffuseMode.INSTANCE;
out.r = 0xFF;
out.g = 0xFF;
out.b = 0xFF;
out.a = 0xFF;
out.spriteShiftFunc = null;
out.hasOverlay = false;
out.overlay = OverlayTexture.NO_OVERLAY;
out.useWorldLight = false;
out.lightTransform = null;
out.hasCustomLight = false;
out.packedLightCoords = 0;
out.hybridLight = false;
out.fullNormalTransform = false;
return out;
}
public static Params newEntityParams() {
Params out = new Params();
out.colorMode = ColorMode.RECOLOR;
out.diffuseMode = DiffuseMode.NONE;
out.r = 0xFF;
out.g = 0xFF;
out.b = 0xFF;
out.a = 0xFF;
out.spriteShiftFunc = null;
out.hasOverlay = true;
out.overlay = OverlayTexture.NO_OVERLAY;
out.useWorldLight = false;
out.lightTransform = null;
out.hasCustomLight = false;
out.packedLightCoords = 0;
out.hybridLight = false;
out.fullNormalTransform = true;
return out;
}
}
}

View file

@ -2,6 +2,8 @@ package com.jozufozu.flywheel.core.model;
import static com.jozufozu.flywheel.util.RenderMath.nb;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -9,6 +11,10 @@ public class VecBufferWriter implements VertexConsumer {
private final VecBuffer buffer;
public VecBufferWriter(ByteBuffer buffer) {
this.buffer = new VecBuffer(buffer);
}
public VecBufferWriter(VecBuffer buffer) {
this.buffer = buffer;
}

View file

@ -1,15 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.VertexConsumer;
public record VertexRecording(ImmutableList<Consumer<VertexConsumer>> recording) {
public void replay(VertexConsumer vc) {
for (Consumer<VertexConsumer> consumer : recording) {
consumer.accept(vc);
}
}
}

View file

@ -5,6 +5,7 @@ import java.util.Collection;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.BufferBuilderReader;
import com.jozufozu.flywheel.util.ModelReader;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -15,7 +16,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
public class WorldModel implements Model {
private final BufferBuilderReader reader;
private final ModelReader reader;
private final String name;
public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) {
@ -33,7 +34,7 @@ public class WorldModel implements Model {
for (int i = 0; i < vertexCount(); i++) {
vertices.vertex(reader.getX(i), reader.getY(i), reader.getZ(i));
vertices.normal(RenderMath.f(reader.getNX(i)), RenderMath.f(reader.getNY(i)), RenderMath.f(reader.getNZ(i)));
vertices.normal(reader.getNX(i), reader.getNY(i), reader.getNZ(i));
vertices.uv(reader.getU(i), reader.getV(i));
@ -60,4 +61,8 @@ public class WorldModel implements Model {
return Formats.COLORED_LIT_MODEL;
}
@Override
public ModelReader getReader() {
return reader;
}
}

View file

@ -0,0 +1,58 @@
package com.jozufozu.flywheel.mixin;
import java.nio.ByteBuffer;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.backend.model.DirectBufferBuilder;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
@Mixin(BufferBuilder.class)
public abstract class BufferBuilderMixin implements DirectBufferBuilder {
@Shadow
private ByteBuffer buffer;
@Shadow
private VertexFormat format;
@Shadow
protected abstract void ensureCapacity(int p_85723_);
@Shadow
private int vertices;
@Shadow
@Nullable
private VertexFormatElement currentElement;
@Shadow
private int elementIndex;
@Shadow
private int nextElementByte;
@Override
public DirectVertexConsumer intoDirectConsumer(int neededVerts) {
ensureCapacity(neededVerts * this.format.getVertexSize());
return new DirectVertexConsumer(this.buffer, this.format);
}
@Override
public void updateAfterWriting(DirectVertexConsumer complete) {
int vertexCount = complete.getVertexCount();
int totalWrittenBytes = vertexCount * format.getVertexSize();
this.vertices += vertexCount;
this.currentElement = format.getElements()
.get(0);
this.elementIndex = 0;
this.nextElementByte += totalWrittenBytes;
this.buffer.position(complete.startPos + totalWrittenBytes);
}
}

View file

@ -6,7 +6,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair;
public class BufferBuilderReader {
public class BufferBuilderReader implements ModelReader {
private final ByteBuffer buffer;
private final int vertexCount;
@ -35,66 +35,81 @@ public class BufferBuilderReader {
// }
}
@Override
public boolean isEmpty() {
return vertexCount == 0;
}
public int vertIdx(int vertexIndex) {
private int vertIdx(int vertexIndex) {
return vertexIndex * formatSize;
}
@Override
public float getX(int index) {
return buffer.getFloat(vertIdx(index));
}
@Override
public float getY(int index) {
return buffer.getFloat(vertIdx(index) + 4);
}
@Override
public float getZ(int index) {
return buffer.getFloat(vertIdx(index) + 8);
}
@Override
public byte getR(int index) {
return buffer.get(vertIdx(index) + 12);
}
@Override
public byte getG(int index) {
return buffer.get(vertIdx(index) + 13);
}
@Override
public byte getB(int index) {
return buffer.get(vertIdx(index) + 14);
}
@Override
public byte getA(int index) {
return buffer.get(vertIdx(index) + 15);
}
@Override
public float getU(int index) {
return buffer.getFloat(vertIdx(index) + 16);
}
@Override
public float getV(int index) {
return buffer.getFloat(vertIdx(index) + 20);
}
@Override
public int getLight(int index) {
return buffer.getInt(vertIdx(index) + 24);
}
public byte getNX(int index) {
return buffer.get(vertIdx(index) + 28);
@Override
public float getNX(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 28));
}
public byte getNY(int index) {
return buffer.get(vertIdx(index) + 29);
@Override
public float getNY(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 29));
}
public byte getNZ(int index) {
return buffer.get(vertIdx(index) + 30);
@Override
public float getNZ(int index) {
return RenderMath.f(buffer.get(vertIdx(index) + 30));
}
@Override
public int getVertexCount() {
return vertexCount;
}

View file

@ -0,0 +1,35 @@
package com.jozufozu.flywheel.util;
public interface ModelReader {
float getX(int index);
float getY(int index);
float getZ(int index);
byte getR(int index);
byte getG(int index);
byte getB(int index);
byte getA(int index);
float getU(int index);
float getV(int index);
int getLight(int index);
float getNX(int index);
float getNY(int index);
float getNZ(int index);
int getVertexCount();
default boolean isEmpty() {
return getVertexCount() == 0;
}
}

View file

@ -5,6 +5,7 @@
"compatibilityLevel": "JAVA_17",
"refmap": "flywheel.refmap.json",
"client": [
"BufferBuilderMixin",
"CancelEntityRenderMixin",
"ChunkRebuildHooksMixin",
"FixFabulousDepthMixin",