A fresh batch of refactoring

- Replace ModelTransformer with modular pipeline that closely resembles
the instancing pipeline
  - StructTypes and Materials now provide a VertexTransformer that
mutates a MutableVertexList
- Rewrite all model building to make it cleaner, more flexible, and
easier to use
- Add SimpleMaterial.GlStateShard for easier control over GL state
- Rename and move some classes
This commit is contained in:
PepperCode1 2022-07-29 15:58:34 -07:00
parent 69de15d4de
commit 83f4aaa4db
76 changed files with 1974 additions and 1549 deletions

View file

@ -7,17 +7,17 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool;
import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.api.instancer;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
public interface InstancerFactory<D extends InstancedPart> {
@ -10,6 +10,6 @@ public interface InstancerFactory<D extends InstancedPart> {
* @param modelKey An object that uniquely identifies and provides the model.
* @return An instancer for the given model, capable of rendering many copies for little cost.
*/
Instancer<D> model(ModelSupplier modelKey);
Instancer<D> model(Model modelKey);
}

View file

@ -1,16 +1,15 @@
package com.jozufozu.flywheel.api.material;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.core.source.FileResolution;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public interface Material {
RenderStage getRenderStage();
RenderType getBatchingRenderType();
FileResolution getVertexShader();
FileResolution getFragmentShader();
@ -18,4 +17,12 @@ public interface Material {
void setup();
void clear();
RenderType getBatchingRenderType();
VertexTransformer getVertexTransformer();
public interface VertexTransformer {
void transform(MutableVertexList vertexList, ClientLevel level);
}
}

View file

@ -3,10 +3,12 @@ package com.jozufozu.flywheel.api.struct;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import net.minecraft.client.multiplayer.ClientLevel;
/**
* A StructType contains metadata for a specific instance struct that Flywheel can interface with.
* @param <S> The java representation of the instance struct.
@ -32,6 +34,10 @@ public interface StructType<S extends InstancedPart> {
FileResolution getInstanceShader();
void transform(S d, ModelTransformer.Params b);
VertexTransformer<? extends S> getVertexTransformer();
public interface VertexTransformer<S extends InstancedPart> {
void transform(MutableVertexList vertexList, S struct, ClientLevel level);
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.api.vertex;
public interface MutableVertexList extends VertexList {
void x(int index, float x);
void y(int index, float y);
void z(int index, float z);
void r(int index, byte r);
void g(int index, byte g);
void b(int index, byte b);
void a(int index, byte a);
default void color(int index, int color) {
a(index, (byte) (color >> 24 & 0xFF));
r(index, (byte) (color >> 16 & 0xFF));
g(index, (byte) (color >> 8 & 0xFF));
b(index, (byte) (color & 0xFF));
}
void u(int index, float u);
void v(int index, float v);
void overlay(int index, int overlay);
void light(int index, int light);
void normalX(int index, float normalX);
void normalY(int index, float normalY);
void normalZ(int index, float normalZ);
}

View file

@ -1,5 +0,0 @@
package com.jozufozu.flywheel.api.vertex;
public interface ShadedVertexList extends VertexList {
boolean isShaded(int index);
}

View file

@ -10,36 +10,40 @@ package com.jozufozu.flywheel.api.vertex;
* TODO: more flexible elements?
*/
public interface VertexList {
float getX(int index);
float x(int index);
float getY(int index);
float y(int index);
float getZ(int index);
float z(int index);
byte getR(int index);
byte r(int index);
byte getG(int index);
byte g(int index);
byte getB(int index);
byte b(int index);
byte getA(int index);
byte a(int index);
float getU(int index);
default int color(int index) {
return a(index) << 24 | r(index) << 16 | g(index) << 8 | b(index);
}
float getV(int index);
float u(int index);
int getLight(int index);
float v(int index);
float getNX(int index);
int overlay(int index);
float getNY(int index);
int light(int index);
float getNZ(int index);
float normalX(int index);
float normalY(int index);
float normalZ(int index);
int getVertexCount();
VertexType getVertexType();
default boolean isEmpty() {
return getVertexCount() == 0;
}

View file

@ -1,15 +1,10 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.light.LightUpdater;

View file

@ -14,7 +14,7 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.GPUInstancer;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
@ -94,8 +94,8 @@ public class SadCrumbling {
// }
@NotNull
private Map<ModelSupplier, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
var map = new HashMap<ModelSupplier, List<InstancedPart>>();
private Map<Model, List<InstancedPart>> modelsToParts(Int2ObjectMap<List<BlockEntityInstance<?>>> dataByStage) {
var map = new HashMap<Model, List<InstancedPart>>();
for (var entry : dataByStage.int2ObjectEntrySet()) {
RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey());

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashSet;
import java.util.Set;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.util.RenderTypeExtension;
import com.mojang.blaze3d.vertex.BufferBuilder;
@ -21,16 +19,9 @@ public class BatchDrawingTracker {
((BufferBuilderExtension) scratch).flywheel$freeBuffer();
}
/**
* Get a direct vertex consumer for drawing the given number of vertices to the given RenderType.
* @param renderType The RenderType to draw to.
* @param vertexCount The number of vertices that will be drawn.
* @return A direct vertex consumer.
*/
public DirectVertexConsumer getDirectConsumer(RenderType renderType, int vertexCount) {
public DrawBuffer getBuffer(RenderType renderType) {
activeTypes.add(renderType);
return RenderTypeExtension.getDrawBuffer(renderType)
.begin(vertexCount);
return RenderTypeExtension.getDrawBuffer(renderType);
}
/**

View file

@ -1,18 +1,14 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.client.renderer.RenderType;
public class BatchLists {
public final Map<RenderType, List<TransformSet<?>>> renderLists = new HashMap<>();
public final Multimap<RenderType, TransformSet<?>> renderLists = ArrayListMultimap.create();
public void add(TransformSet<?> set) {
renderLists.computeIfAbsent(set.material.getBatchingRenderType(), k -> new ArrayList<>())
.add(set);
renderLists.put(set.getMaterial().getBatchingRenderType(), set);
}
}

View file

@ -4,23 +4,23 @@ import java.util.List;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
public class BatchedModel<D extends InstancedPart> {
CPUInstancer<D> instancer;
ModelSupplier model;
StructType<D> type;
private final StructType<D> type;
private final Model model;
private final CPUInstancer<D> instancer;
private List<TransformSet<D>> layers;
public BatchedModel(StructType<D> type, ModelSupplier model) {
public BatchedModel(StructType<D> type, Model model) {
this.type = type;
this.model = model;
this.instancer = new CPUInstancer<>(type);
}
public void init(BatchLists batchLists) {
layers = model.get()
layers = model.getMeshes()
.entrySet()
.stream()
.map(entry -> new TransformSet<>(instancer, entry.getKey(), entry.getValue()))
@ -31,6 +31,14 @@ public class BatchedModel<D extends InstancedPart> {
}
}
public Model getModel() {
return model;
}
public CPUInstancer<D> getInstancer() {
return instancer;
}
public void clear() {
instancer.clear();
}

View file

@ -8,8 +8,6 @@ import java.util.Map;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
@ -20,6 +18,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
@ -48,21 +47,20 @@ public class BatchingEngine implements Engine {
return BlockPos.ZERO;
}
public void submitTasks(PoseStack stack, TaskEngine taskEngine) {
batchLists.renderLists.forEach((renderType, renderList) -> {
public void submitTasks(TaskEngine taskEngine, PoseStack stack, ClientLevel level) {
batchLists.renderLists.asMap().forEach((renderType, renderList) -> {
int vertices = 0;
for (var transformSet : renderList) {
vertices += transformSet.getTotalVertexCount();
}
var consumer = batchTracker.getDirectConsumer(renderType, vertices);
consumer.memSetZero();
var outputColorDiffuse = !consumer.hasOverlay() && !ShadersModHandler.isShaderPackInUse();
DrawBuffer buffer = batchTracker.getBuffer(renderType);
buffer.prepare(vertices);
int startVertex = 0;
for (var transformSet : renderList) {
transformSet.setOutputColorDiffuse(outputColorDiffuse);
transformSet.submitTasks(stack, taskEngine, consumer);
transformSet.submitTasks(taskEngine, buffer, startVertex, stack, level);
startVertex += transformSet.getTotalVertexCount();
}
});
}
@ -83,7 +81,6 @@ public class BatchingEngine implements Engine {
Lighting.setupLevel(mat);
}
taskEngine.syncPoint();
batchTracker.endBatch();
}
@ -109,7 +106,7 @@ public class BatchingEngine implements Engine {
var stack = FlwUtil.copyPoseStack(context.stack());
stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
submitTasks(stack, taskEngine);
submitTasks(taskEngine, stack, context.level());
}
@Override

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.model;
package com.jozufozu.flywheel.backend.instancing.batching;
import java.nio.ByteBuffer;
@ -12,8 +12,6 @@ import com.mojang.blaze3d.vertex.VertexFormat;
*/
public interface BufferBuilderExtension {
int flywheel$getVertices();
/**
* Frees the internal ByteBuffer, if it exists.
*/
@ -26,12 +24,4 @@ public interface BufferBuilderExtension {
* @param vertexCount The number of vertices in the buffer.
*/
void flywheel$injectForRender(ByteBuffer buffer, VertexFormat format, int vertexCount);
/**
* Appends the remaining bytes from the given buffer to this BufferBuilder.
* @param buffer The buffer from which to copy bytes.
* @throws IllegalStateException If this BufferBuilder is not started or is the process of writing a vertex
* @throws IllegalArgumentException If the given buffer does not contain a whole number of vertices
*/
void flywheel$appendBufferUnsafe(ByteBuffer buffer);
}

View file

@ -8,11 +8,11 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
public class CPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final Map<ModelSupplier, BatchedModel<D>> models;
protected final Map<Model, BatchedModel<D>> models;
private final StructType<D> type;
private final Consumer<BatchedModel<D>> creationListener;
@ -25,8 +25,8 @@ public class CPUInstancerFactory<D extends InstancedPart> implements InstancerFa
}
@Override
public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, this::createModel).instancer;
public Instancer<D> model(Model modelKey) {
return models.computeIfAbsent(modelKey, this::createModel).getInstancer();
}
/**
@ -37,7 +37,7 @@ public class CPUInstancerFactory<D extends InstancedPart> implements InstancerFa
.forEach(BatchedModel::clear);
}
private BatchedModel<D> createModel(ModelSupplier k) {
private BatchedModel<D> createModel(Model k) {
var out = new BatchedModel<>(type, k);
creationListener.accept(out);
return out;

View file

@ -1,45 +1,47 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.backend.instancing.batching;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import org.lwjgl.system.MemoryUtil;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
/**
* A byte buffer that can be used to draw vertices through a {@link DirectVertexConsumer}.
* A byte buffer that can be used to draw vertices through multiple {@link MutableVertexListImpl}s.
*
* The number of vertices needs to be known ahead of time.
*/
public class DrawBuffer {
private final RenderType parent;
private final VertexFormatInfo formatInfo;
private ByteBuffer backingBuffer;
private int expectedVertices;
private long ptr;
public DrawBuffer(RenderType parent) {
this.parent = parent;
formatInfo = new VertexFormatInfo(parent.format());
}
/**
* Creates a direct vertex consumer that can be used to write vertices into this buffer.
* Prepares this buffer by initializing a block of memory.
* @param vertexCount The number of vertices to reserve memory for.
* @return A direct vertex consumer.
* @throws IllegalStateException If the buffer is already in use.
*/
public DirectVertexConsumer begin(int vertexCount) {
public void prepare(int vertexCount) {
if (expectedVertices != 0) {
throw new IllegalStateException("Already drawing");
}
this.expectedVertices = vertexCount;
VertexFormat format = parent.format();
int byteSize = format.getVertexSize() * vertexCount;
// Add one extra vertex to uphold the vanilla assumption that BufferBuilders have at least
// enough buffer space for one more vertex. Sodium checks for this extra space when popNextBuffer
// is called and reallocates the buffer if there is not space for one more vertex.
int byteSize = formatInfo.stride * (vertexCount + 1);
if (backingBuffer == null) {
backingBuffer = MemoryTracker.create(byteSize);
@ -47,7 +49,13 @@ public class DrawBuffer {
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize);
}
return new DirectVertexConsumer(backingBuffer, format, vertexCount);
backingBuffer.clear();
ptr = MemoryUtil.memAddress(backingBuffer);
MemoryUtil.memSet(ptr, 0, byteSize);
}
public MutableVertexListImpl slice(int startVertex, int vertexCount) {
return new MutableVertexListImpl(ptr + startVertex * formatInfo.stride, formatInfo, vertexCount);
}
/**
@ -55,7 +63,11 @@ public class DrawBuffer {
* @param bufferBuilder The buffer builder to inject into.
*/
public void inject(BufferBuilderExtension bufferBuilder) {
bufferBuilder.flywheel$injectForRender(backingBuffer, parent.format(), expectedVertices);
bufferBuilder.flywheel$injectForRender(backingBuffer, formatInfo.format, expectedVertices);
}
public int getVertexCount() {
return expectedVertices;
}
/**

View file

@ -0,0 +1,206 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.util.RenderMath;
public class MutableVertexListImpl extends VertexFormatInfo implements MutableVertexList {
private final long anchorPtr;
private final int totalVertexCount;
private long ptr;
private int vertexCount;
public MutableVertexListImpl(long ptr, VertexFormatInfo formatInfo, int vertexCount) {
super(formatInfo);
anchorPtr = ptr;
totalVertexCount = vertexCount;
setFullRange();
}
public void setRange(int startVertex, int vertexCount) {
ptr = anchorPtr + startVertex * stride;
this.vertexCount = vertexCount;
}
public void setFullRange() {
ptr = anchorPtr;
vertexCount = totalVertexCount;
}
@Override
public float x(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset);
}
@Override
public float y(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset + 4);
}
@Override
public float z(int index) {
if (positionOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + positionOffset + 8);
}
@Override
public byte r(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset);
}
@Override
public byte g(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 1);
}
@Override
public byte b(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 2);
}
@Override
public byte a(int index) {
if (colorOffset < 0) return 0;
return MemoryUtil.memGetByte(ptr + index * stride + colorOffset + 3);
}
@Override
public float u(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + textureOffset);
}
@Override
public float v(int index) {
if (textureOffset < 0) return 0;
return MemoryUtil.memGetFloat(ptr + index * stride + textureOffset + 4);
}
@Override
public int overlay(int index) {
if (overlayOffset < 0) return 0;
return MemoryUtil.memGetInt(ptr + index * stride + overlayOffset);
}
@Override
public int light(int index) {
if (lightOffset < 0) return 0;
return MemoryUtil.memGetInt(ptr + index * stride + lightOffset);
}
@Override
public float normalX(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset));
}
@Override
public float normalY(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset + 1));
}
@Override
public float normalZ(int index) {
if (normalOffset < 0) return 0;
return RenderMath.f(MemoryUtil.memGetByte(ptr + index * stride + normalOffset + 2));
}
@Override
public int getVertexCount() {
return vertexCount;
}
@Override
public void x(int index, float x) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset, x);
}
@Override
public void y(int index, float y) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset + 4, y);
}
@Override
public void z(int index, float z) {
if (positionOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + positionOffset + 8, z);
}
@Override
public void r(int index, byte r) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset, r);
}
@Override
public void g(int index, byte g) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 1, g);
}
@Override
public void b(int index, byte b) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 2, b);
}
@Override
public void a(int index, byte a) {
if (colorOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + colorOffset + 3, a);
}
@Override
public void u(int index, float u) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + textureOffset, u);
}
@Override
public void v(int index, float v) {
if (textureOffset < 0) return;
MemoryUtil.memPutFloat(ptr + index * stride + textureOffset + 4, v);
}
@Override
public void overlay(int index, int overlay) {
if (overlayOffset < 0) return;
MemoryUtil.memPutInt(ptr + index * stride + overlayOffset, overlay);
}
@Override
public void light(int index, int light) {
if (lightOffset < 0) return;
MemoryUtil.memPutInt(ptr + index * stride + lightOffset, light);
}
@Override
public void normalX(int index, float normalX) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset, RenderMath.nb(normalX));
}
@Override
public void normalY(int index, float normalY) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset + 1, RenderMath.nb(normalY));
}
@Override
public void normalZ(int index, float normalZ) {
if (normalOffset < 0) return;
MemoryUtil.memPutByte(ptr + index * stride + normalOffset + 2, RenderMath.nb(normalZ));
}
}

View file

@ -4,29 +4,41 @@ import java.util.List;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructType.VertexTransformer;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
import net.minecraft.client.multiplayer.ClientLevel;
public class TransformSet<D extends InstancedPart> {
public final Material material;
public final Mesh mesh;
private final CPUInstancer<D> instancer;
private final ModelTransformer modelTransformer;
private final Material material;
private final Mesh mesh;
public TransformSet(CPUInstancer<D> instancer, Material material, Mesh mesh) {
this.instancer = instancer;
this.material = material;
this.mesh = mesh;
modelTransformer = new ModelTransformer(mesh);
}
void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) {
public Material getMaterial() {
return material;
}
public Mesh getMesh() {
return mesh;
}
void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack stack, ClientLevel level) {
instancer.setup();
int instances = instancer.getInstanceCount();
@ -36,39 +48,110 @@ public class TransformSet<D extends InstancedPart> {
instances -= 512;
int start = Math.max(instances, 0);
int verts = mesh.getVertexCount() * (end - start);
int vertexCount = mesh.getVertexCount() * (end - start);
MutableVertexListImpl sub = buffer.slice(startVertex, vertexCount);
startVertex += vertexCount;
DirectVertexConsumer sub = consumer.split(verts);
pool.submit(() -> drawRange(stack, sub, start, end));
pool.submit(() -> drawRange(sub, start, end, stack, level));
}
}
private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) {
drawList(stack, buffer, instancer.getRange(from, to));
private void drawRange(MutableVertexListImpl vertexList, int from, int to, PoseStack stack, ClientLevel level) {
drawList(vertexList, instancer.getRange(from, to), stack, level);
}
void drawAll(PoseStack stack, VertexConsumer buffer) {
drawList(stack, buffer, instancer.getAll());
void drawAll(MutableVertexListImpl vertexList, PoseStack stack, ClientLevel level) {
drawList(vertexList, instancer.getAll(), stack, level);
}
private void drawList(PoseStack stack, VertexConsumer buffer, List<D> list) {
ModelTransformer.Params params = new ModelTransformer.Params();
private void drawList(MutableVertexListImpl vertexList, List<D> list, PoseStack stack, ClientLevel level) {
int startVertex = 0;
int meshVertexCount = mesh.getVertexCount();
VertexList meshReader = mesh.getReader();
@SuppressWarnings("unchecked")
StructType.VertexTransformer<D> structVertexTransformer = (VertexTransformer<D>) instancer.type.getVertexTransformer();
for (D d : list) {
params.loadDefault();
vertexList.setRange(startVertex, meshVertexCount);
instancer.type.transform(d, params);
writeMesh(vertexList, meshReader);
modelTransformer.renderInto(params, stack, buffer);
structVertexTransformer.transform(vertexList, d, level);
startVertex += meshVertexCount;
}
vertexList.setFullRange();
material.getVertexTransformer().transform(vertexList, level);
applyPoseStack(vertexList, stack, false);
}
// TODO: remove this
// The VertexWriter API and VertexFormat conversion needs to be rewritten to make this unnecessary
private static void writeMesh(MutableVertexList vertexList, VertexList meshReader) {
for (int i = 0; i < meshReader.getVertexCount(); i++) {
vertexList.x(i, meshReader.x(i));
vertexList.y(i, meshReader.y(i));
vertexList.z(i, meshReader.z(i));
vertexList.r(i, meshReader.r(i));
vertexList.g(i, meshReader.g(i));
vertexList.b(i, meshReader.b(i));
vertexList.a(i, meshReader.a(i));
vertexList.u(i, meshReader.u(i));
vertexList.v(i, meshReader.v(i));
vertexList.overlay(i, meshReader.overlay(i));
vertexList.light(i, meshReader.light(i));
vertexList.normalX(i, meshReader.normalX(i));
vertexList.normalY(i, meshReader.normalY(i));
vertexList.normalZ(i, meshReader.normalZ(i));
}
}
private static void applyPoseStack(MutableVertexList vertexList, PoseStack stack, boolean applyNormalMatrix) {
Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f();
Matrix4f modelMatrix = stack.last().pose();
Matrix3f normalMatrix;
if (applyNormalMatrix) {
normalMatrix = stack.last().normal();
} else {
normalMatrix = null;
}
for (int i = 0; i < vertexList.getVertexCount(); i++) {
pos.set(
vertexList.x(i),
vertexList.y(i),
vertexList.z(i),
1f
);
pos.transform(modelMatrix);
vertexList.x(i, pos.x());
vertexList.y(i, pos.y());
vertexList.z(i, pos.z());
if (applyNormalMatrix) {
normal.set(
vertexList.normalX(i),
vertexList.normalY(i),
vertexList.normalZ(i)
);
normal.transform(normalMatrix);
normal.normalize();
vertexList.normalX(i, normal.x());
vertexList.normalY(i, normal.y());
vertexList.normalZ(i, normal.z());
}
}
}
public int getTotalVertexCount() {
return mesh.getVertexCount() * instancer.getInstanceCount();
}
public void setOutputColorDiffuse(boolean outputColorDiffuse) {
modelTransformer.context.outputColorDiffuse = outputColorDiffuse;
}
}

View file

@ -0,0 +1,64 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
public class VertexFormatInfo {
public final VertexFormat format;
public final int stride;
public final int positionOffset;
public final int colorOffset;
public final int textureOffset;
public final int overlayOffset;
public final int lightOffset;
public final int normalOffset;
public VertexFormatInfo(VertexFormat format) {
this.format = format;
stride = format.getVertexSize();
int positionOffset = -1;
int colorOffset = -1;
int textureOffset = -1;
int overlayOffset = -1;
int lightOffset = -1;
int normalOffset = -1;
int offset = 0;
for (VertexFormatElement element : format.getElements()) {
switch (element.getUsage()) {
case POSITION -> positionOffset = offset;
case NORMAL -> normalOffset = offset;
case COLOR -> colorOffset = offset;
case UV -> {
switch (element.getIndex()) {
case 0 -> textureOffset = offset;
case 1 -> overlayOffset = offset;
case 2 -> lightOffset = offset;
}
}
}
offset += element.getByteSize();
}
this.positionOffset = positionOffset;
this.colorOffset = colorOffset;
this.textureOffset = textureOffset;
this.overlayOffset = overlayOffset;
this.lightOffset = lightOffset;
this.normalOffset = normalOffset;
}
protected VertexFormatInfo(VertexFormatInfo formatInfo) {
format = formatInfo.format;
stride = formatInfo.stride;
positionOffset = formatInfo.positionOffset;
colorOffset = formatInfo.colorOffset;
textureOffset = formatInfo.textureOffset;
overlayOffset = formatInfo.overlayOffset;
lightOffset = formatInfo.lightOffset;
normalOffset = formatInfo.normalOffset;
}
}

View file

@ -1,14 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.effect;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractStorage;

View file

@ -4,13 +4,12 @@ import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.model.Mesh;
public class DrawCall {
final Material material;
private final GPUInstancer<?> instancer;
private final Material material;
MeshPool.BufferedMesh bufferedMesh;
GlVertexArray vao;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.model;
package com.jozufozu.flywheel.backend.instancing.instancing;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;

View file

@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.instancer.InstancerFactory;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
/**
* A collection of Instancers that all have the same format.
@ -17,7 +17,7 @@ import com.jozufozu.flywheel.core.model.ModelSupplier;
*/
public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFactory<D> {
protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>();
protected final Map<Model, InstancedModel<D>> models = new HashMap<>();
protected final StructType<D> type;
private final Consumer<InstancedModel<D>> creationListener;
@ -27,7 +27,7 @@ public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFa
}
@Override
public Instancer<D> model(ModelSupplier modelKey) {
public Instancer<D> model(Model modelKey) {
return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer();
}
@ -61,7 +61,7 @@ public class GPUInstancerFactory<D extends InstancedPart> implements InstancerFa
.forEach(AbstractInstancer::clear);
}
private InstancedModel<D> createInstancer(ModelSupplier model) {
private InstancedModel<D> createInstancer(Model model) {
var instancer = new InstancedModel<>(type, model);
this.creationListener.accept(instancer);
return instancer;

View file

@ -4,43 +4,43 @@ import java.util.List;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.core.model.Model;
public class InstancedModel<D extends InstancedPart> {
private final ModelSupplier model;
private final StructType<D> type;
private final Model model;
private final GPUInstancer<D> instancer;
private List<DrawCall> layers;
public InstancedModel(StructType<D> type, ModelSupplier model) {
public InstancedModel(StructType<D> type, Model model) {
this.type = type;
this.model = model;
this.instancer = new GPUInstancer<>(this, type);
this.type = type;
}
public void init(RenderLists renderLists) {
instancer.init();
layers = model.get()
layers = model.getMeshes()
.entrySet()
.stream()
.map(entry -> new DrawCall(instancer, entry.getKey(), entry.getValue()))
.toList();
for (DrawCall layer : layers) {
renderLists.add(new ShaderState(layer.material, layer.getVertexType(), type), layer);
renderLists.add(new ShaderState(layer.getMaterial(), layer.getVertexType(), type), layer);
}
}
public Model getModel() {
return model;
}
public GPUInstancer<D> getInstancer() {
return instancer;
}
public ModelSupplier getModel() {
return model;
}
public int getVertexCount() {
return model.getVertexCount() * instancer.glInstanceCount;
}

View file

@ -18,7 +18,6 @@ import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.model;
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@ -192,7 +192,7 @@ public class MeshPool {
this.mesh = mesh;
this.byteIndex = byteIndex;
this.ebo = mesh.createEBO();
this.layout = mesh.getType()
this.layout = mesh.getVertexType()
.getLayout();
}
@ -255,7 +255,7 @@ public class MeshPool {
}
public VertexType getVertexType() {
return this.mesh.getType();
return this.mesh.getVertexType();
}
}

View file

@ -1,29 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.core.model.BlockMesh;
public class ArrayModelRenderer {
protected final GlVertexArray vao;
protected final MeshPool.BufferedMesh mesh;
public ArrayModelRenderer(BlockMesh mesh, MeshPool meshPool) {
this.vao = new GlVertexArray();
this.mesh = meshPool.alloc(mesh);
}
/**
* Renders this model, checking first if there is anything to render.
*/
public void draw() {
if (mesh.isDeleted()) return;
mesh.drawCall(vao);
}
public void delete() {
mesh.delete();
}
}

View file

@ -1,180 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.BufferOverflowException;
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;
/**
* An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer.
*
* @see BufferBuilderExtension
*/
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 final long end;
public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format, int maxVertices) {
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, startPos);
this.end = vertexBase + (long) maxVertices * stride;
}
private DirectVertexConsumer(DirectVertexConsumer parent, int maxVertices) {
this.format = parent.format;
this.stride = parent.stride;
this.startPos = parent.startPos;
this.position = parent.position;
this.normal = parent.normal;
this.color = parent.color;
this.uv = parent.uv;
this.uv1 = parent.uv1;
this.uv2 = parent.uv2;
this.vertexBase = parent.vertexBase;
this.end = parent.vertexBase + (long) maxVertices * this.stride;
}
public void memSetZero() {
MemoryUtil.memSet(vertexBase, 0, end - vertexBase);
}
public boolean hasOverlay() {
return uv1 >= 0;
}
/**
* Split off the head of this consumer into a new object and advance this object's write-pointer.
* @param vertexCount The number of vertices that must be written to the head.
* @return The head of this consumer.
*/
public DirectVertexConsumer split(int vertexCount) {
int bytes = vertexCount * stride;
DirectVertexConsumer head = new DirectVertexConsumer(this, vertexCount);
this.vertexBase += bytes;
return head;
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
checkOverflow();
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;
// int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24);
// MemoryUtil.memPutInt(base, 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;
}
@Override
public void defaultColor(int r, int g, int b, int a) {
}
@Override
public void unsetDefaultColor() {
}
private void checkOverflow() {
if (vertexBase >= end) {
throw new BufferOverflowException();
}
}
}

View file

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

View file

@ -2,44 +2,189 @@ package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.core.material.SimpleMaterial;
import com.jozufozu.flywheel.core.material.SimpleMaterial.GlStateShard;
import com.jozufozu.flywheel.util.DiffuseLightCalculator;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.texture.AbstractTexture;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.inventory.InventoryMenu;
public final class Materials {
public static final Material.VertexTransformer SHADING_TRANSFORMER = (vertexList, level) -> {
if (ShadersModHandler.isShaderPackInUse()) {
return;
}
DiffuseLightCalculator diffuseCalc = DiffuseLightCalculator.forLevel(level);
for (int i = 0; i < vertexList.getVertexCount(); i++) {
float diffuse = diffuseCalc.getDiffuse(vertexList.normalX(i), vertexList.normalY(i), vertexList.normalZ(i), true);
vertexList.r(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.r(i)) * diffuse), 0, 255));
vertexList.g(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.g(i)) * diffuse), 0, 255));
vertexList.b(i, (byte) Mth.clamp((int) (Byte.toUnsignedInt(vertexList.b(i)) * diffuse), 0, 255));
}
};
public class Materials {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
public static final Material DEFAULT = SimpleMaterial.builder()
public static final Material CHUNK_SOLID_SHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.renderType(RenderType.cutout())
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.DEFAULT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.batchingRenderType(RenderType.solid())
.vertexTransformer(SHADING_TRANSFORMER)
.register();
public static final Material CHUNK_SOLID_UNSHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.vertexShader(Components.Files.DEFAULT_VERTEX)
.fragmentShader(Components.Files.DEFAULT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.batchingRenderType(RenderType.solid())
.register();
public static final Material CHUNK_CUTOUT_MIPPED_SHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.batchingRenderType(RenderType.cutoutMipped())
.vertexTransformer(SHADING_TRANSFORMER)
.register();
public static final Material CHUNK_CUTOUT_MIPPED_UNSHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.vertexShader(Components.Files.DEFAULT_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.batchingRenderType(RenderType.cutoutMipped())
.register();
public static final Material CHUNK_CUTOUT_SHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, false))
.batchingRenderType(RenderType.cutout())
.vertexTransformer(SHADING_TRANSFORMER)
.register();
public static final Material CHUNK_CUTOUT_UNSHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.vertexShader(Components.Files.DEFAULT_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, false))
.batchingRenderType(RenderType.cutout())
.register();
public static final Material CHUNK_TRANSLUCENT_SHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_TRANSLUCENT_TERRAIN)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.DEFAULT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.addShard(Shards.TRANSLUCENT_TRANSPARENCY)
.batchingRenderType(RenderType.translucent())
.vertexTransformer(SHADING_TRANSFORMER)
.register();
public static final Material CHUNK_TRANSLUCENT_UNSHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_TRANSLUCENT_TERRAIN)
.vertexShader(Components.Files.DEFAULT_VERTEX)
.fragmentShader(Components.Files.DEFAULT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.addShard(Shards.TRANSLUCENT_TRANSPARENCY)
.batchingRenderType(RenderType.translucent())
.register();
public static final Material CHUNK_TRIPWIRE_SHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_TRANSLUCENT_TERRAIN)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.addShard(Shards.TRANSLUCENT_TRANSPARENCY)
.batchingRenderType(RenderType.tripwire())
.vertexTransformer(SHADING_TRANSFORMER)
.register();
public static final Material CHUNK_TRIPWIRE_UNSHADED = SimpleMaterial.builder()
.stage(RenderStage.AFTER_TRANSLUCENT_TERRAIN)
.vertexShader(Components.Files.DEFAULT_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, true))
.addShard(Shards.TRANSLUCENT_TRANSPARENCY)
.batchingRenderType(RenderType.tripwire())
.register();
public static final Material CHEST = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.chestSheet())
.diffuseTex(Sheets.CHEST_SHEET)
.vertexShader(Components.Files.SHADED_VERTEX)
.addShard(Shards.diffuseTex(Sheets.CHEST_SHEET, false, false))
.batchingRenderType(Sheets.chestSheet())
.register();
public static final Material SHULKER = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.shulkerBoxSheet())
.diffuseTex(Sheets.SHULKER_SHEET)
.vertexShader(Components.Files.SHADED_VERTEX)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.alsoSetup(RenderSystem::disableCull)
.alsoClear(RenderSystem::enableCull)
.addShard(Shards.diffuseTex(Sheets.SHULKER_SHEET, false, false))
.addShard(Shards.DISABLE_CULL)
.batchingRenderType(Sheets.shulkerBoxSheet())
.register();
public static final Material BELL = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.solidBlockSheet())
.vertexShader(Components.Files.SHADED_VERTEX)
.addShard(Shards.diffuseTex(InventoryMenu.BLOCK_ATLAS, false, false))
.batchingRenderType(Sheets.solidBlockSheet())
.register();
public static final Material MINECART = SimpleMaterial.builder()
.stage(RenderStage.AFTER_ENTITIES)
.renderType(RenderType.entitySolid(MINECART_LOCATION))
.diffuseTex(MINECART_LOCATION)
.vertexShader(Components.Files.SHADED_VERTEX)
.addShard(Shards.diffuseTex(MINECART_LOCATION, false, false))
.batchingRenderType(RenderType.entitySolid(MINECART_LOCATION))
.register();
public static void init() {
// noop
}
public static final class Shards {
public static final GlStateShard DISABLE_CULL = new GlStateShard(
() -> {
RenderSystem.disableCull();
},
() -> {
RenderSystem.enableCull();
}
);
public static final GlStateShard TRANSLUCENT_TRANSPARENCY = new GlStateShard(
() -> {
RenderSystem.enableBlend();
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
},
() -> {
RenderSystem.disableBlend();
RenderSystem.defaultBlendFunc();
}
);
public static GlStateShard diffuseTex(ResourceLocation loc, boolean blur, boolean mipmap) {
return new GlStateShard(
() -> {
GlTextureUnit.T0.makeActive();
RenderSystem.enableTexture();
AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(loc);
texture.setFilter(blur, mipmap);
RenderSystem.setShaderTexture(0, texture.getId());
},
() -> {
GlTextureUnit.T0.makeActive();
RenderSystem.setShaderTexture(0, 0);
}
);
}
}
}

View file

@ -1,43 +0,0 @@
package com.jozufozu.flywheel.core;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import com.jozufozu.flywheel.core.model.BlockMesh;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.Pair;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
public class Models {
public static BasicModelSupplier block(BlockState state) {
return BLOCK_STATE.computeIfAbsent(state, it -> new BasicModelSupplier(() -> new BlockMesh(it), Materials.DEFAULT));
}
public static BasicModelSupplier partial(PartialModel partial) {
return PARTIAL.computeIfAbsent(partial, it -> new BasicModelSupplier(() -> new BlockMesh(it), Materials.DEFAULT));
}
public static BasicModelSupplier partial(PartialModel partial, Direction dir) {
return partial(partial, dir, () -> ModelUtil.rotateToFace(dir));
}
public static BasicModelSupplier partial(PartialModel partial, Direction dir, Supplier<PoseStack> modelTransform) {
return PARTIAL_DIR.computeIfAbsent(Pair.of(dir, partial), $ -> new BasicModelSupplier(() -> new BlockMesh(partial, modelTransform.get()), Materials.DEFAULT));
}
public static void onReload(ReloadRenderersEvent ignored) {
BLOCK_STATE.clear();
PARTIAL.clear();
PARTIAL_DIR.clear();
}
private static final Map<BlockState, BasicModelSupplier> BLOCK_STATE = new HashMap<>();
private static final Map<PartialModel, BasicModelSupplier> PARTIAL = new HashMap<>();
private static final Map<Pair<Direction, PartialModel>, BasicModelSupplier> PARTIAL_DIR = new HashMap<>();
}

View file

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
/**

View file

@ -4,14 +4,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import com.google.common.collect.ListMultimap;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.DrawCall;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.ShaderState;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;

View file

@ -28,7 +28,7 @@ public class ModelPart implements Mesh {
}
try (var stack = MemoryStack.stackPush()) {
PosTexNormalWriterUnsafe writer = getType().createWriter(stack.malloc(size()));
PosTexNormalWriterUnsafe writer = getVertexType().createWriter(stack.malloc(size()));
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
cuboid.buffer(writer);
}
@ -57,7 +57,7 @@ public class ModelPart implements Mesh {
}
@Override
public PosTexNormalVertex getType() {
public PosTexNormalVertex getVertexType() {
return Formats.POS_TEX_NORMAL;
}
}

View file

@ -6,7 +6,6 @@ import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.array.VertexAttribute;
import com.jozufozu.flywheel.backend.gl.array.VertexAttributeF;
/**
* Classic Vertex Format struct with a clever name.

View file

@ -1,39 +1,31 @@
package com.jozufozu.flywheel.core.material;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
public class SimpleMaterial implements Material {
protected final RenderStage stage;
protected final RenderType type;
protected final FileResolution vertexShader;
protected final FileResolution fragmentShader;
protected final ResourceLocation diffuseTex;
@Nullable
protected final Runnable setup;
@Nullable
protected final Runnable clear;
protected final RenderType batchingRenderType;
protected final VertexTransformer vertexTransformer;
public SimpleMaterial(RenderStage stage, RenderType type, FileResolution vertexShader, FileResolution fragmentShader, ResourceLocation diffuseTex, @Nullable Runnable setup, @Nullable Runnable clear) {
public SimpleMaterial(RenderStage stage, FileResolution vertexShader, FileResolution fragmentShader, Runnable setup, Runnable clear, RenderType batchingRenderType, VertexTransformer vertexTransformer) {
this.stage = stage;
this.type = type;
this.vertexShader = vertexShader;
this.fragmentShader = fragmentShader;
this.diffuseTex = diffuseTex;
this.setup = setup;
this.clear = clear;
this.batchingRenderType = batchingRenderType;
this.vertexTransformer = vertexTransformer;
}
public static Builder builder() {
@ -45,11 +37,6 @@ public class SimpleMaterial implements Material {
return stage;
}
@Override
public RenderType getBatchingRenderType() {
return type;
}
@Override
public FileResolution getVertexShader() {
return vertexShader;
@ -62,34 +49,32 @@ public class SimpleMaterial implements Material {
@Override
public void setup() {
GlTextureUnit.T0.makeActive();
RenderSystem.setShaderTexture(0, diffuseTex);
Minecraft.getInstance().textureManager.bindForSetup(diffuseTex);
if (setup != null) {
setup.run();
}
setup.run();
}
@Override
public void clear() {
GlTextureUnit.T0.makeActive();
RenderSystem.setShaderTexture(0, 0);
clear.run();
}
if (clear != null) {
clear.run();
}
@Override
public RenderType getBatchingRenderType() {
return batchingRenderType;
}
@Override
public VertexTransformer getVertexTransformer() {
return vertexTransformer;
}
public static class Builder {
protected RenderStage stage = RenderStage.AFTER_SOLID_TERRAIN;
protected RenderType type = RenderType.solid();
protected FileResolution vertexShader = Components.Files.SHADED_VERTEX;
protected FileResolution vertexShader = Components.Files.DEFAULT_VERTEX;
protected FileResolution fragmentShader = Components.Files.DEFAULT_FRAGMENT;
protected ResourceLocation diffuseTex = InventoryMenu.BLOCK_ATLAS;
protected Runnable setup = null;
protected Runnable clear = null;
protected Runnable setup = () -> {};
protected Runnable clear = () -> {};
protected RenderType batchingRenderType = RenderType.solid();
protected VertexTransformer vertexTransformer = (vertexList, level) -> {};
public Builder() {
}
@ -99,11 +84,6 @@ public class SimpleMaterial implements Material {
return this;
}
public Builder renderType(RenderType type) {
this.type = type;
return this;
}
public Builder vertexShader(FileResolution vertexShader) {
this.vertexShader = vertexShader;
return this;
@ -114,33 +94,63 @@ public class SimpleMaterial implements Material {
return this;
}
public Builder shaded() {
this.vertexShader = Components.Files.SHADED_VERTEX;
public Builder addSetup(Runnable setup) {
this.setup = chain(this.setup, setup);
return this;
}
public Builder unShaded() {
this.vertexShader = Components.Files.DEFAULT_VERTEX;
public Builder addClear(Runnable clear) {
this.clear = chain(this.clear, clear);
return this;
}
public Builder diffuseTex(ResourceLocation diffuseTex) {
this.diffuseTex = diffuseTex;
public Builder addShard(GlStateShard shard) {
addSetup(shard.getSetup());
addClear(shard.getClear());
return this;
}
public Builder alsoSetup(Runnable runnable) {
this.setup = runnable;
public Builder batchingRenderType(RenderType type) {
this.batchingRenderType = type;
return this;
}
public Builder alsoClear(Runnable clear) {
this.clear = clear;
public Builder vertexTransformer(VertexTransformer vertexTransformer) {
this.vertexTransformer = vertexTransformer;
return this;
}
public SimpleMaterial register() {
return ComponentRegistry.register(new SimpleMaterial(stage, type, vertexShader, fragmentShader, diffuseTex, setup, clear));
return ComponentRegistry.register(new SimpleMaterial(stage, vertexShader, fragmentShader, setup, clear, batchingRenderType, vertexTransformer));
}
private static Runnable chain(Runnable runnable1, Runnable runnable2) {
return () -> {
runnable1.run();
runnable2.run();
};
}
}
public static class GlStateShard {
protected final Runnable setup;
protected final Runnable clear;
public GlStateShard(Runnable setup, Runnable clear) {
this.setup = setup;
this.clear = clear;
}
public static GlStateShard fromVanilla(RenderStateShard vanillaShard) {
return new GlStateShard(() -> vanillaShard.setupRenderState(), () -> vanillaShard.clearRenderState());
}
public Runnable getSetup() {
return setup;
}
public Runnable getClear() {
return clear;
}
}
}

View file

@ -1,47 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.Random;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public final class BakedModelBuilder implements Bufferable {
private final BakedModel model;
private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE;
private BlockState referenceState = Blocks.AIR.defaultBlockState();
private PoseStack poseStack = new PoseStack();
public BakedModelBuilder(BakedModel model) {
this.model = model;
}
public BakedModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public BakedModelBuilder withReferenceState(BlockState referenceState) {
this.referenceState = referenceState;
return this;
}
public BakedModelBuilder withPoseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
@Override
public void bufferInto(ModelBlockRenderer blockRenderer, VertexConsumer consumer, Random random) {
blockRenderer.tesselateBlock(renderWorld, model, referenceState, BlockPos.ZERO, poseStack, consumer, false, random, 42, OverlayTexture.NO_OVERLAY, VirtualEmptyModelData.INSTANCE);
}
}

View file

@ -1,70 +0,0 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.block.state.BlockState;
/**
* A model of a single block.
*/
public class BlockMesh implements Mesh {
private static final PoseStack IDENTITY = new PoseStack();
private final VertexList reader;
private final String name;
public BlockMesh(BlockState state) {
this(Minecraft.getInstance()
.getBlockRenderer()
.getBlockModel(state), state);
}
public BlockMesh(BakedModel model, BlockState referenceState) {
this(model, referenceState, IDENTITY);
}
public BlockMesh(PartialModel model) {
this(model, IDENTITY);
}
public BlockMesh(PartialModel model, PoseStack ms) {
this(ModelUtil.bakedModel(model.get())
.withPoseStack(ms), model.getName());
}
public BlockMesh(BakedModel model, BlockState referenceState, PoseStack ms) {
this(ModelUtil.bakedModel(model)
.withReferenceState(referenceState)
.withPoseStack(ms), referenceState.toString());
}
public BlockMesh(Bufferable builder, String name) {
this(Formats.BLOCK.createReader(builder.build()), name);
}
public BlockMesh(VertexList reader, String name) {
this.reader = reader;
this.name = name;
}
@Override
public String name() {
return name;
}
@Override
public VertexList getReader() {
return reader;
}
@Override
public String toString() {
return "BlockMesh{" + "name='" + name + "',type='" + reader.getVertexType() + "}";
}
}

View file

@ -1,18 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.Random;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
/**
* An interface for objects that can "rendered" into a BufferBuilder.
*/
public interface Bufferable {
void bufferInto(ModelBlockRenderer renderer, VertexConsumer consumer, Random random);
default ShadeSeparatedBufferBuilder build() {
return ModelUtil.getBufferBuilder(this);
}
}

View file

@ -5,27 +5,27 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.backend.instancing.instancing.ElementBuffer;
import com.jozufozu.flywheel.core.QuadConverter;
/**
* A model that can be rendered by flywheel.
* A mesh that can be rendered by flywheel.
*
* <p>
* It is expected that the following assertion will not fail:
* </p>
*
* <pre>{@code
* Model model = ...;
* Mesh mesh = ...;
* VecBuffer into = ...;
*
* int initial = VecBuffer.unwrap().position();
*
* model.buffer(into);
* mesh.buffer(into);
*
* int final = VecBuffer.unwrap().position();
*
* assert model.size() == final - initial;
* assert mesh.size() == final - initial;
* }</pre>
*/
public interface Mesh {
@ -35,6 +35,8 @@ public interface Mesh {
*/
String name();
VertexType getVertexType();
VertexList getReader();
/**
@ -44,8 +46,19 @@ public interface Mesh {
return getReader().getVertexCount();
}
default VertexType getType() {
return getReader().getVertexType();
/**
* Is there nothing to render?
* @return true if there are no vertices.
*/
default boolean isEmpty() {
return getReader().isEmpty();
}
/**
* The size in bytes that this model's data takes up.
*/
default int size() {
return getVertexType().byteOffset(getVertexCount());
}
/**
@ -63,23 +76,8 @@ public interface Mesh {
.quads2Tris(getVertexCount() / 4);
}
/**
* The size in bytes that this model's data takes up.
*/
default int size() {
return getType().byteOffset(getVertexCount());
}
/**
* Is there nothing to render?
* @return true if there are no vertices.
*/
default boolean empty() {
return getVertexCount() == 0;
}
default void writeInto(ByteBuffer buffer, long byteIndex) {
VertexWriter writer = getType().createWriter(buffer);
VertexWriter writer = getVertexType().createWriter(buffer);
writer.seek(byteIndex);
writer.writeVertexList(getReader());
}

View file

@ -0,0 +1,17 @@
package com.jozufozu.flywheel.core.model;
import java.util.Map;
import com.jozufozu.flywheel.api.material.Material;
public interface Model {
Map<Material, Mesh> getMeshes();
default int getVertexCount() {
int size = 0;
for (Mesh mesh : getMeshes().values()) {
size += mesh.getVertexCount();
}
return size;
}
}

View file

@ -1,11 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.Map;
import com.jozufozu.flywheel.api.material.Material;
public interface ModelSupplier {
Map<Material, Mesh> get();
int getVertexCount();
}

View file

@ -1,305 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.function.IntPredicate;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.DiffuseLightCalculator;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.util.Mth;
public class ModelTransformer {
private final Mesh mesh;
private final VertexList reader;
private final IntPredicate shadedPredicate;
public final Context context = new Context();
public ModelTransformer(Mesh mesh) {
this.mesh = mesh;
reader = mesh.getReader();
if (reader instanceof ShadedVertexList shaded) {
shadedPredicate = shaded::isShaded;
} else {
shadedPredicate = index -> true;
}
}
public void renderInto(Params params, PoseStack input, VertexConsumer builder) {
if (isEmpty())
return;
Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f();
Matrix4f modelMat = input.last()
.pose()
.copy();
modelMat.multiply(params.model);
Matrix3f normalMat;
if (context.fullNormalTransform) {
normalMat = input.last().normal().copy();
normalMat.mul(params.normal);
} else {
normalMat = params.normal.copy();
}
final DiffuseLightCalculator diffuseCalculator = DiffuseLightCalculator.forCurrentLevel();
final int vertexCount = reader.getVertexCount();
for (int i = 0; i < vertexCount; i++) {
float x = reader.getX(i);
float y = reader.getY(i);
float z = reader.getZ(i);
pos.set(x, y, z, 1F);
pos.transform(modelMat);
builder.vertex(pos.x(), pos.y(), pos.z());
float normalX = reader.getNX(i);
float normalY = reader.getNY(i);
float normalZ = reader.getNZ(i);
normal.set(normalX, normalY, normalZ);
normal.transform(normalMat);
normal.normalize();
float nx = normal.x();
float ny = normal.y();
float nz = normal.z();
byte r, g, b, a;
if (params.useParamColor) {
r = (byte) params.r;
g = (byte) params.g;
b = (byte) params.b;
a = (byte) params.a;
} else {
r = reader.getR(i);
g = reader.getG(i);
b = reader.getB(i);
a = reader.getA(i);
}
if (context.outputColorDiffuse) {
float instanceDiffuse = diffuseCalculator.getDiffuse(nx, ny, nz, shadedPredicate.test(i));
int colorR = transformColor(r, instanceDiffuse);
int colorG = transformColor(g, instanceDiffuse);
int colorB = transformColor(b, instanceDiffuse);
builder.color(colorR, colorG, colorB, a);
} else {
builder.color(r, g, b, 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 = reader.getU(i);
float v = reader.getV(i);
if (params.spriteShiftFunc != null) {
params.spriteShiftFunc.shift(builder, u, v);
} else {
builder.uv(u, v);
}
// not always used, but will be ignored by formats that don't use it
builder.overlayCoords(params.overlay);
builder.uv2(params.useParamLight ? params.packedLightCoords : reader.getLight(i));
builder.normal(nx, ny, nz);
builder.endVertex();
}
}
public boolean isEmpty() {
return reader.isEmpty();
}
@Override
public String toString() {
return "ModelTransformer[" + mesh + ']';
}
public static int transformColor(byte component, float scale) {
return Mth.clamp((int) (Byte.toUnsignedInt(component) * scale), 0, 255);
}
public static int transformColor(int component, float scale) {
return Mth.clamp((int) (component * scale), 0, 255);
}
@FunctionalInterface
public interface SpriteShiftFunc {
void shift(VertexConsumer builder, float u, float v);
}
public static class Context {
/**
* Do we need to include the PoseStack transforms in our transformation of the normal?
*/
public boolean fullNormalTransform = false;
/**
* Do we need to bake diffuse lighting into the output colors?
*/
public boolean outputColorDiffuse = true;
}
public static class Params implements Transform<Params> {
// Transform
public final Matrix4f model;
public final Matrix3f normal;
// Vertex Coloring
public boolean useParamColor;
public int r;
public int g;
public int b;
public int a;
// Vertex Texture Coords
public SpriteShiftFunc spriteShiftFunc;
// Vertex Overlay Color
public int overlay;
// Vertex Lighting
public boolean useParamLight;
public int packedLightCoords;
public Params() {
model = new Matrix4f();
normal = new Matrix3f();
}
public void loadDefault() {
model.setIdentity();
normal.setIdentity();
useParamColor = true;
r = 0xFF;
g = 0xFF;
b = 0xFF;
a = 0xFF;
spriteShiftFunc = null;
overlay = OverlayTexture.NO_OVERLAY;
useParamLight = false;
packedLightCoords = LightTexture.FULL_BRIGHT;
}
public void load(Params from) {
model.load(from.model);
normal.load(from.normal);
useParamColor = from.useParamColor;
r = from.r;
g = from.g;
b = from.b;
a = from.a;
spriteShiftFunc = from.spriteShiftFunc;
overlay = from.overlay;
useParamLight = from.useParamLight;
packedLightCoords = from.packedLightCoords;
}
public Params color(int r, int g, int b, int a) {
this.useParamColor = true;
this.r = r;
this.g = g;
this.b = b;
this.a = a;
return this;
}
public Params color(byte r, byte g, byte b, byte a) {
this.useParamColor = true;
this.r = Byte.toUnsignedInt(r);
this.g = Byte.toUnsignedInt(g);
this.b = Byte.toUnsignedInt(b);
this.a = Byte.toUnsignedInt(a);
return this;
}
public Params color(int color) {
this.useParamColor = true;
this.r = ((color >> 16) & 0xFF);
this.g = ((color >> 8) & 0xFF);
this.b = (color & 0xFF);
this.a = 255;
return this;
}
public Params shiftUV(SpriteShiftFunc entry) {
this.spriteShiftFunc = entry;
return this;
}
public Params overlay(int overlay) {
this.overlay = overlay;
return this;
}
public Params light(int packedLightCoords) {
this.useParamLight = true;
this.packedLightCoords = packedLightCoords;
return this;
}
@Override
public Params multiply(Quaternion quaternion) {
model.multiply(quaternion);
normal.mul(quaternion);
return this;
}
@Override
public Params scale(float pX, float pY, float pZ) {
model.multiply(Matrix4f.createScaleMatrix(pX, pY, pZ));
if (pX == pY && pY == pZ) {
if (pX > 0.0F) {
return this;
}
normal.mul(-1.0F);
return this;
}
float f = 1.0F / pX;
float f1 = 1.0F / pY;
float f2 = 1.0F / pZ;
float f3 = Mth.fastInvCubeRoot(Math.abs(f * f1 * f2));
normal.mul(Matrix3f.createScaleMatrix(f3 * f, f3 * f1, f3 * f2));
return this;
}
@Override
public Params translate(double x, double y, double z) {
model.multiplyWithTranslation((float) x, (float) y, (float) z);
return this;
}
@Override
public Params mulPose(Matrix4f pose) {
this.model.multiply(pose);
return this;
}
@Override
public Params mulNormal(Matrix3f normal) {
this.normal.mul(normal);
return this;
}
}
}

View file

@ -1,22 +1,23 @@
package com.jozufozu.flywheel.core.model;
import java.lang.reflect.Field;
import java.util.EnumMap;
import java.util.Random;
import java.nio.ByteBuffer;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
public class ModelUtil {
@ -26,8 +27,6 @@ public class ModelUtil {
*/
public static final BlockRenderDispatcher VANILLA_RENDERER = createVanillaRenderer();
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
private static BlockRenderDispatcher createVanillaRenderer() {
BlockRenderDispatcher defaultDispatcher = Minecraft.getInstance().getBlockRenderer();
BlockRenderDispatcher dispatcher = new BlockRenderDispatcher(null, null, null);
@ -44,66 +43,34 @@ public class ModelUtil {
return dispatcher;
}
public static BakedModelBuilder bakedModel(BakedModel model) {
return new BakedModelBuilder(model);
}
public static VertexList createVertexList(BufferBuilder bufferBuilder) {
Pair<BufferBuilder.DrawState, ByteBuffer> pair = bufferBuilder.popNextBuffer();
BufferBuilder.DrawState drawState = pair.getFirst();
public static WorldModelBuilder worldLayer(RenderType layer) {
return new WorldModelBuilder(layer);
}
public static ShadeSeparatedBufferBuilder getBufferBuilder(Bufferable bufferable) {
ModelBlockRenderer blockRenderer = VANILLA_RENDERER.getModelRenderer();
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
objects.begin();
bufferable.bufferInto(blockRenderer, objects.shadeSeparatingWrapper, objects.random);
objects.end();
return objects.separatedBufferBuilder;
}
private static PoseStack createRotation(Direction facing) {
PoseStack stack = new PoseStack();
TransformStack.cast(stack)
.centre()
.rotateToFace(facing.getOpposite())
.unCentre();
return stack;
}
public static PoseStack rotateToFace(Direction facing) {
return TRANSFORMS.get(facing);
}
private static final EnumMap<Direction, PoseStack> TRANSFORMS = new EnumMap<>(Direction.class);
static {
for (Direction value : Direction.values()) {
TRANSFORMS.put(value, createRotation(value));
}
}
private static class ThreadLocalObjects {
public final Random random = new Random();
public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer();
public final ShadeSeparatedBufferBuilder separatedBufferBuilder = new ShadeSeparatedBufferBuilder(512);
public final BufferBuilder unshadedBuilder = new BufferBuilder(512);
private void begin() {
this.separatedBufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
this.unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
this.shadeSeparatingWrapper.prepare(this.separatedBufferBuilder, this.unshadedBuilder);
if (drawState.format() != DefaultVertexFormat.BLOCK) {
throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format());
}
private void end() {
this.shadeSeparatingWrapper.clear();
this.unshadedBuilder.end();
this.separatedBufferBuilder.appendUnshadedVertices(this.unshadedBuilder);
this.separatedBufferBuilder.end();
}
return Formats.BLOCK.createReader(pair.getSecond(), drawState.vertexCount());
}
@Nullable
public static Material getMaterial(RenderType chunkRenderType, boolean shaded) {
if (chunkRenderType == RenderType.solid()) {
return shaded ? Materials.CHUNK_SOLID_SHADED : Materials.CHUNK_SOLID_UNSHADED;
}
if (chunkRenderType == RenderType.cutoutMipped()) {
return shaded ? Materials.CHUNK_CUTOUT_MIPPED_SHADED : Materials.CHUNK_CUTOUT_MIPPED_UNSHADED;
}
if (chunkRenderType == RenderType.cutout()) {
return shaded ? Materials.CHUNK_CUTOUT_SHADED : Materials.CHUNK_CUTOUT_UNSHADED;
}
if (chunkRenderType == RenderType.translucent()) {
return shaded ? Materials.CHUNK_TRANSLUCENT_SHADED : Materials.CHUNK_TRANSLUCENT_UNSHADED;
}
if (chunkRenderType == RenderType.tripwire()) {
return shaded ? Materials.CHUNK_TRIPWIRE_SHADED : Materials.CHUNK_TRIPWIRE_UNSHADED;
}
return null;
}
}

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.core.model;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.model.buffering.BakedModelBuilder;
import com.jozufozu.flywheel.core.model.buffering.BlockModelBuilder;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.util.Pair;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;
public class Models {
private static final Map<BlockState, Model> BLOCK_STATE = new HashMap<>();
private static final Map<PartialModel, Model> PARTIAL = new HashMap<>();
private static final Map<Pair<PartialModel, Direction>, Model> PARTIAL_DIR = new HashMap<>();
public static Model block(BlockState state) {
return BLOCK_STATE.computeIfAbsent(state, it -> new BlockModelBuilder(it).build());
}
public static Model partial(PartialModel partial) {
return PARTIAL.computeIfAbsent(partial, it -> new BakedModelBuilder(it.get()).build());
}
public static Model partial(PartialModel partial, Direction dir) {
return PARTIAL_DIR.computeIfAbsent(Pair.of(partial, dir), it -> new BakedModelBuilder(it.first().get()).poseStack(createRotation(it.second())).build());
}
public static PoseStack createRotation(Direction facing) {
PoseStack stack = new PoseStack();
TransformStack.cast(stack)
.centre()
.rotateToFace(facing.getOpposite())
.unCentre();
return stack;
}
public static void onReload(ReloadRenderersEvent event) {
BLOCK_STATE.clear();
PARTIAL.clear();
PARTIAL_DIR.clear();
}
}

View file

@ -1,117 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
public class SeparatedWorldModelBuilder {
private PoseStack poseStack = new PoseStack();
private Map<BlockPos, IModelData> modelData = Collections.emptyMap();
private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE;
private Collection<StructureTemplate.StructureBlockInfo> blocks = Collections.emptyList();
public Map<RenderType, Mesh> getMeshes() {
Map<RenderType, BufferBuilder> builders = new HashMap<>();
ModelBlockRenderer modelRenderer = ModelUtil.VANILLA_RENDERER.getModelRenderer();
buffer(modelRenderer, new Random(), type -> builders.computeIfAbsent(type, $ -> {
var out = new BufferBuilder(512);
out.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
return out;
}));
return builders.entrySet()
.stream()
.collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, e -> {
var b = e.getValue();
b.end();
return new BlockMesh(Formats.BLOCK.createReader(b), "");
}));
}
public void buffer(ModelBlockRenderer modelRenderer, Random random, Function<RenderType, VertexConsumer> consumer) {
ModelBlockRenderer.enableCaching();
for (StructureTemplate.StructureBlockInfo info : this.blocks) {
var state = info.state;
if (state.getRenderShape() != RenderShape.MODEL) continue;
var pos = info.pos;
var seed = state.getSeed(pos);
var data = this.modelData.getOrDefault(pos, EmptyModelData.INSTANCE);
var blockModel = ModelUtil.VANILLA_RENDERER.getBlockModel(state);
this.poseStack.pushPose();
this.poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
for (RenderType type : RenderType.chunkBufferLayers()) {
if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) {
continue;
}
var vertexConsumer = consumer.apply(type);
if (vertexConsumer == null) {
continue;
}
ForgeHooksClient.setRenderType(type);
modelRenderer.tesselateBlock(this.renderWorld, blockModel, state, pos, poseStack, vertexConsumer, true, random, seed, OverlayTexture.NO_OVERLAY, data);
}
this.poseStack.popPose();
}
ForgeHooksClient.setRenderType(null);
ModelBlockRenderer.clearCache();
}
public SeparatedWorldModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public SeparatedWorldModelBuilder withBlocks(Collection<StructureTemplate.StructureBlockInfo> blocks) {
this.blocks = blocks;
return this;
}
public SeparatedWorldModelBuilder withModelData(Map<BlockPos, IModelData> modelData) {
this.modelData = modelData;
return this;
}
public SeparatedWorldModelBuilder withPoseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
}

View file

@ -1,25 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.datafixers.util.Pair;
public class ShadeSeparatedBufferBuilder extends BufferBuilder {
protected int unshadedStartVertex;
public ShadeSeparatedBufferBuilder(int capacity) {
super(capacity);
}
public void appendUnshadedVertices(BufferBuilder unshadedBuilder) {
Pair<DrawState, ByteBuffer> data = unshadedBuilder.popNextBuffer();
unshadedStartVertex = ((BufferBuilderExtension) this).flywheel$getVertices();
((BufferBuilderExtension) this).flywheel$appendBufferUnsafe(data.getSecond());
}
public int getUnshadedStartVertex() {
return unshadedStartVertex;
}
}

View file

@ -1,84 +0,0 @@
package com.jozufozu.flywheel.core.model;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.model.BakedQuad;
public class ShadeSeparatingVertexConsumer implements VertexConsumer {
protected VertexConsumer shadedConsumer;
protected VertexConsumer unshadedConsumer;
public void prepare(VertexConsumer shadedConsumer, VertexConsumer unshadedConsumer) {
this.shadedConsumer = shadedConsumer;
this.unshadedConsumer = unshadedConsumer;
}
public void clear() {
shadedConsumer = null;
unshadedConsumer = null;
}
@Override
public void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
if (quad.isShade()) {
shadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
} else {
unshadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
}
}
@Override
public void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
if (bakedQuad.isShade()) {
shadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
} else {
unshadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
}
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public VertexConsumer color(int red, int green, int blue, int alpha) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public VertexConsumer uv(float u, float v) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public VertexConsumer uv2(int u, int v) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public VertexConsumer normal(float x, float y, float z) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public void endVertex() {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public void defaultColor(int red, int green, int blue, int alpha) {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
@Override
public void unsetDefaultColor() {
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core;
package com.jozufozu.flywheel.core.model;
import java.util.Map;
@ -6,27 +6,25 @@ import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.NonNullSupplier;
public class BasicModelSupplier implements ModelSupplier {
public class SimpleLazyModel implements Model {
private final Lazy<Mesh> supplier;
private Material material;
public BasicModelSupplier(NonNullSupplier<Mesh> supplier, Material material) {
public SimpleLazyModel(NonNullSupplier<Mesh> supplier, Material material) {
this.supplier = Lazy.of(supplier);
this.material = material;
}
public BasicModelSupplier setMaterial(@NotNull Material material) {
public SimpleLazyModel setMaterial(@NotNull Material material) {
this.material = material;
return this;
}
@Override
public Map<Material, Mesh> get() {
public Map<Material, Mesh> getMeshes() {
return ImmutableMap.of(material, supplier.get());
}

View file

@ -0,0 +1,36 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
public class SimpleMesh implements Mesh {
private final VertexList reader;
private final VertexType vertexType;
private final String name;
public SimpleMesh(VertexList reader, VertexType vertexType, String name) {
this.reader = reader;
this.vertexType = vertexType;
this.name = name;
}
@Override
public String name() {
return name;
}
@Override
public VertexType getVertexType() {
return vertexType;
}
@Override
public VertexList getReader() {
return reader;
}
@Override
public String toString() {
return "SimpleMesh{" + "name='" + name + "',vertexType='" + vertexType + "}";
}
}

View file

@ -0,0 +1,25 @@
package com.jozufozu.flywheel.core.model;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material;
public class TessellatedModel implements Model {
private final ImmutableMap<Material, Mesh> meshes;
private final boolean shadeSeparated;
public TessellatedModel(ImmutableMap<Material, Mesh> meshes, boolean shadeSeparated) {
this.meshes = meshes;
this.shadeSeparated = shadeSeparated;
}
@Override
public Map<Material, Mesh> getMeshes() {
return meshes;
}
public boolean isShadeSeparated() {
return shadeSeparated;
}
}

View file

@ -1,86 +0,0 @@
package com.jozufozu.flywheel.core.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
public final class WorldModelBuilder implements Bufferable {
private final RenderType layer;
private PoseStack poseStack = new PoseStack();
private Map<BlockPos, IModelData> modelData = Collections.emptyMap();
private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE;
private Collection<StructureTemplate.StructureBlockInfo> blocks = Collections.emptyList();
public WorldModelBuilder(RenderType layer) {
this.layer = layer;
}
@Override
public void bufferInto(ModelBlockRenderer modelRenderer, VertexConsumer consumer, Random random) {
ForgeHooksClient.setRenderType(this.layer);
ModelBlockRenderer.enableCaching();
for (StructureTemplate.StructureBlockInfo info : this.blocks) {
BlockState state = info.state;
if (state.getRenderShape() != RenderShape.MODEL) continue;
if (!ItemBlockRenderTypes.canRenderInLayer(state, this.layer)) continue;
BlockPos pos = info.pos;
IModelData data = this.modelData.getOrDefault(pos, EmptyModelData.INSTANCE);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
modelRenderer.tesselateBlock(this.renderWorld, ModelUtil.VANILLA_RENDERER.getBlockModel(state), state, pos, poseStack, consumer, true, random, 42, OverlayTexture.NO_OVERLAY, data);
poseStack.popPose();
}
ModelBlockRenderer.clearCache();
ForgeHooksClient.setRenderType(null);
}
/**
* It is expected that {@code renderWorld.getShade(...)} returns a constant.
*/
public WorldModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public WorldModelBuilder withBlocks(Collection<StructureTemplate.StructureBlockInfo> blocks) {
this.blocks = blocks;
return this;
}
public WorldModelBuilder withModelData(Map<BlockPos, IModelData> modelData) {
this.modelData = modelData;
return this;
}
public WorldModelBuilder withPoseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
public BlockMesh intoMesh(String name) {
return new BlockMesh(this, name);
}
}

View file

@ -0,0 +1,136 @@
package com.jozufozu.flywheel.core.model.buffering;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh;
import com.jozufozu.flywheel.core.model.TessellatedModel;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.IModelData;
public class BakedModelBuilder {
private final BakedModel bakedModel;
private boolean shadeSeparated = true;
private VertexFormat vertexFormat;
private BlockAndTintGetter renderWorld;
private BlockState blockState;
private PoseStack poseStack;
private IModelData modelData;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public BakedModelBuilder(BakedModel bakedModel) {
this.bakedModel = bakedModel;
}
public BakedModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BakedModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public BakedModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public BakedModelBuilder blockState(BlockState blockState) {
this.blockState = blockState;
return this;
}
public BakedModelBuilder poseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
public BakedModelBuilder modelData(IModelData modelData) {
this.modelData = modelData;
return this;
}
public BakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
this.materialFunc = materialFunc;
return this;
}
@SuppressWarnings("unchecked")
public TessellatedModel build() {
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
if (vertexFormat == null) {
vertexFormat = DefaultVertexFormat.BLOCK;
}
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
}
if (blockState == null) {
blockState = Blocks.AIR.defaultBlockState();
}
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
if (modelData == null) {
modelData = VirtualEmptyModelData.INSTANCE;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(64);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
}
};
ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
} else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(64);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, false);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString()));
}
};
ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);
}
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
}
}

View file

@ -0,0 +1,125 @@
package com.jozufozu.flywheel.core.model.buffering;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh;
import com.jozufozu.flywheel.core.model.TessellatedModel;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.client.model.data.IModelData;
public class BlockModelBuilder {
private final BlockState state;
private boolean shadeSeparated = true;
private VertexFormat vertexFormat;
private BlockAndTintGetter renderWorld;
private PoseStack poseStack;
private IModelData modelData;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public BlockModelBuilder(BlockState state) {
this.state = state;
}
public BlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BlockModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public BlockModelBuilder poseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
public BlockModelBuilder modelData(IModelData modelData) {
this.modelData = modelData;
return this;
}
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
this.materialFunc = materialFunc;
return this;
}
@SuppressWarnings("unchecked")
public TessellatedModel build() {
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
if (vertexFormat == null) {
vertexFormat = DefaultVertexFormat.BLOCK;
}
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
}
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
if (modelData == null) {
modelData = VirtualEmptyModelData.INSTANCE;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(64);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
}
};
ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
} else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(64);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, false);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "state=" + state.toString() + ",renderType=" + renderType.toString()));
}
};
ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);
}
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
}
}

View file

@ -0,0 +1,65 @@
package com.jozufozu.flywheel.core.model.buffering;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.model.BakedQuad;
public interface LazyDelegatingVertexConsumer<T extends VertexConsumer> extends VertexConsumer {
T getDelegate();
@Override
default VertexConsumer vertex(double x, double y, double z) {
return getDelegate().vertex(x, y, z);
}
@Override
default VertexConsumer color(int red, int green, int blue, int alpha) {
return getDelegate().color(red, green, blue, alpha);
}
@Override
default VertexConsumer uv(float u, float v) {
return getDelegate().uv(u, v);
}
@Override
default VertexConsumer overlayCoords(int u, int v) {
return getDelegate().overlayCoords(u, v);
}
@Override
default VertexConsumer uv2(int u, int v) {
return getDelegate().uv2(u, v);
}
@Override
default VertexConsumer normal(float x, float y, float z) {
return getDelegate().normal(x, y, z);
}
@Override
default void endVertex() {
getDelegate().endVertex();
}
@Override
default void defaultColor(int red, int green, int blue, int alpha) {
getDelegate().defaultColor(red, green, blue, alpha);
}
@Override
default void unsetDefaultColor() {
getDelegate().unsetDefaultColor();
}
@Override
default void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
getDelegate().putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
}
@Override
default void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
getDelegate().putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
}
}

View file

@ -0,0 +1,18 @@
package com.jozufozu.flywheel.core.model.buffering;
import java.util.Random;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferWrapper;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatingBufferWrapper;
import com.mojang.blaze3d.vertex.PoseStack;
public class ModelBufferingObjects {
public static final ThreadLocal<ModelBufferingObjects> THREAD_LOCAL = ThreadLocal.withInitial(ModelBufferingObjects::new);
public final PoseStack identityPoseStack = new PoseStack();
@SuppressWarnings("rawtypes")
public final BufferWrapper bufferWrapper = new BufferWrapper<>();
@SuppressWarnings("rawtypes")
public final ShadeSeparatingBufferWrapper shadeSeparatingBufferWrapper = new ShadeSeparatingBufferWrapper<>();
public final Random random = new Random();
}

View file

@ -0,0 +1,283 @@
package com.jozufozu.flywheel.core.model.buffering;
import java.util.Collection;
import java.util.Map;
import java.util.Random;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
public final class ModelBufferingUtil {
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
private static final int CHUNK_LAYERS_AMOUNT = CHUNK_LAYERS.length;
public static <T extends VertexConsumer> void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, PoseStack poseStack, BufferFactory<T> bufferFactory, BufferWrapper<T> bufferWrapper, Random random, IModelData modelData, ResultConsumer<? super T> resultConsumer) {
bufferWrapper.bufferFactory = bufferFactory;
for (RenderType type : CHUNK_LAYERS) {
if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) {
continue;
}
bufferWrapper.currentRenderType = type;
bufferWrapper.delegate = null;
ForgeHooksClient.setRenderType(type);
poseStack.pushPose();
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, bufferWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData);
poseStack.popPose();
T buffer = bufferWrapper.delegate;
if (buffer != null) {
resultConsumer.accept(type, buffer);
}
}
ForgeHooksClient.setRenderType(null);
bufferWrapper.bufferFactory = null;
bufferWrapper.currentRenderType = null;
bufferWrapper.delegate = null;
}
public static <T extends VertexConsumer> void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, PoseStack poseStack, ShadeSeparatedBufferFactory<T> bufferFactory, ShadeSeparatingBufferWrapper<T> bufferWrapper, Random random, IModelData modelData, ShadeSeparatedResultConsumer<? super T> resultConsumer) {
bufferWrapper.bufferFactory = bufferFactory;
for (RenderType type : CHUNK_LAYERS) {
if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) {
continue;
}
bufferWrapper.currentRenderType = type;
bufferWrapper.shadedConsumer = null;
bufferWrapper.unshadedConsumer = null;
ForgeHooksClient.setRenderType(type);
poseStack.pushPose();
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, bufferWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData);
poseStack.popPose();
T shadedConsumer = bufferWrapper.shadedConsumer;
T unshadedConsumer = bufferWrapper.unshadedConsumer;
if (shadedConsumer != null) {
resultConsumer.accept(type, true, shadedConsumer);
}
if (unshadedConsumer != null) {
resultConsumer.accept(type, false, unshadedConsumer);
}
}
ForgeHooksClient.setRenderType(null);
bufferWrapper.bufferFactory = null;
bufferWrapper.currentRenderType = null;
bufferWrapper.shadedConsumer = null;
bufferWrapper.unshadedConsumer = null;
}
public static <T extends VertexConsumer> void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, PoseStack poseStack, BufferFactory<T> bufferFactory, BufferWrapper<T> bufferWrapper, Random random, IModelData modelData, ResultConsumer<? super T> resultConsumer) {
if (state.getRenderShape() != RenderShape.MODEL) {
return;
}
bufferSingle(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, bufferFactory, bufferWrapper, random, modelData, resultConsumer);
}
public static <T extends VertexConsumer> void bufferBlockShadeSeparated(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, PoseStack poseStack, ShadeSeparatedBufferFactory<T> bufferFactory, ShadeSeparatingBufferWrapper<T> bufferWrapper, Random random, IModelData modelData, ShadeSeparatedResultConsumer<? super T> resultConsumer) {
if (state.getRenderShape() != RenderShape.MODEL) {
return;
}
bufferSingleShadeSeparated(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, bufferFactory, bufferWrapper, random, modelData, resultConsumer);
}
public static <T extends VertexConsumer> void bufferMultiBlock(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, PoseStack poseStack, BufferFactory<T> bufferFactory, BufferWrapper<T> bufferWrapper, Random random, Map<BlockPos, IModelData> modelDataMap, ResultConsumer<? super T> resultConsumer) {
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
ModelBlockRenderer.enableCaching();
bufferWrapper.bufferFactory = bufferFactory;
@SuppressWarnings("unchecked")
T[] bufferCache = (T[]) new VertexConsumer[CHUNK_LAYERS_AMOUNT];
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
BlockState state = blockInfo.state;
if (state.getRenderShape() != RenderShape.MODEL) {
continue;
}
BakedModel model = renderDispatcher.getBlockModel(state);
BlockPos pos = blockInfo.pos;
long seed = state.getSeed(pos);
IModelData modelData = modelDataMap.getOrDefault(pos, EmptyModelData.INSTANCE);
for (int layerIndex = 0; layerIndex < CHUNK_LAYERS_AMOUNT; layerIndex++) {
RenderType type = CHUNK_LAYERS[layerIndex];
if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) {
continue;
}
bufferWrapper.currentRenderType = type;
bufferWrapper.delegate = bufferCache[layerIndex];
ForgeHooksClient.setRenderType(type);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, bufferWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData);
poseStack.popPose();
bufferCache[layerIndex] = bufferWrapper.delegate;
}
}
ForgeHooksClient.setRenderType(null);
ModelBlockRenderer.clearCache();
bufferWrapper.bufferFactory = null;
bufferWrapper.currentRenderType = null;
bufferWrapper.delegate = null;
for (int layerIndex = 0; layerIndex < CHUNK_LAYERS_AMOUNT; layerIndex++) {
T buffer = bufferCache[layerIndex];
if (buffer != null) {
resultConsumer.accept(CHUNK_LAYERS[layerIndex], buffer);
}
}
}
public static <T extends VertexConsumer> void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, PoseStack poseStack, ShadeSeparatedBufferFactory<T> bufferFactory, ShadeSeparatingBufferWrapper<T> bufferWrapper, Random random, Map<BlockPos, IModelData> modelDataMap, ShadeSeparatedResultConsumer<? super T> resultConsumer) {
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
ModelBlockRenderer.enableCaching();
bufferWrapper.bufferFactory = bufferFactory;
@SuppressWarnings("unchecked")
T[] bufferCache = (T[]) new VertexConsumer[CHUNK_LAYERS_AMOUNT * 2];
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
BlockState state = blockInfo.state;
if (state.getRenderShape() != RenderShape.MODEL) {
continue;
}
BakedModel model = renderDispatcher.getBlockModel(state);
BlockPos pos = blockInfo.pos;
long seed = state.getSeed(pos);
IModelData modelData = modelDataMap.getOrDefault(pos, EmptyModelData.INSTANCE);
for (int layerIndex = 0; layerIndex < CHUNK_LAYERS_AMOUNT; layerIndex++) {
RenderType type = CHUNK_LAYERS[layerIndex];
if (!ItemBlockRenderTypes.canRenderInLayer(state, type)) {
continue;
}
bufferWrapper.currentRenderType = type;
bufferWrapper.shadedConsumer = bufferCache[layerIndex];
bufferWrapper.unshadedConsumer = bufferCache[layerIndex + CHUNK_LAYERS_AMOUNT];
ForgeHooksClient.setRenderType(type);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, bufferWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData);
poseStack.popPose();
bufferCache[layerIndex] = bufferWrapper.shadedConsumer;
bufferCache[layerIndex + CHUNK_LAYERS_AMOUNT] = bufferWrapper.unshadedConsumer;
}
}
ForgeHooksClient.setRenderType(null);
ModelBlockRenderer.clearCache();
bufferWrapper.bufferFactory = null;
bufferWrapper.currentRenderType = null;
bufferWrapper.shadedConsumer = null;
bufferWrapper.unshadedConsumer = null;
for (int layerIndex = 0; layerIndex < CHUNK_LAYERS_AMOUNT; layerIndex++) {
RenderType type = CHUNK_LAYERS[layerIndex];
T shadedConsumer = bufferCache[layerIndex];
T unshadedConsumer = bufferCache[layerIndex + CHUNK_LAYERS_AMOUNT];
if (shadedConsumer != null) {
resultConsumer.accept(type, true, shadedConsumer);
}
if (unshadedConsumer != null) {
resultConsumer.accept(type, false, unshadedConsumer);
}
}
}
public interface BufferFactory<T extends VertexConsumer> {
T get(RenderType renderType);
}
public interface ShadeSeparatedBufferFactory<T extends VertexConsumer> {
T get(RenderType renderType, boolean shaded);
}
public interface ResultConsumer<T extends VertexConsumer> {
void accept(RenderType renderType, T vertexConsumer);
}
public interface ShadeSeparatedResultConsumer<T extends VertexConsumer> {
void accept(RenderType renderType, boolean shaded, T vertexConsumer);
}
public static class BufferWrapper<T extends VertexConsumer> implements LazyDelegatingVertexConsumer<T> {
protected BufferFactory<T> bufferFactory;
protected RenderType currentRenderType;
protected T delegate;
@Override
public T getDelegate() {
if (delegate == null) {
delegate = bufferFactory.get(currentRenderType);
}
return delegate;
}
}
public static class ShadeSeparatingBufferWrapper<T extends VertexConsumer> implements ShadeSeparatingVertexConsumer<T> {
protected ShadeSeparatedBufferFactory<T> bufferFactory;
protected RenderType currentRenderType;
protected T shadedConsumer;
protected T unshadedConsumer;
@Override
public T getShadedConsumer() {
if (shadedConsumer == null) {
shadedConsumer = bufferFactory.get(currentRenderType, true);
}
return shadedConsumer;
}
@Override
public T getUnshadedConsumer() {
if (unshadedConsumer == null) {
unshadedConsumer = bufferFactory.get(currentRenderType, false);
}
return unshadedConsumer;
}
}
}

View file

@ -0,0 +1,128 @@
package com.jozufozu.flywheel.core.model.buffering;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.BiFunction;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.core.model.SimpleMesh;
import com.jozufozu.flywheel.core.model.TessellatedModel;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.BufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ResultConsumer;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
import com.jozufozu.flywheel.core.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.model.data.IModelData;
public class MultiBlockModelBuilder {
private final Collection<StructureTemplate.StructureBlockInfo> blocks;
private boolean shadeSeparated = true;
private VertexFormat vertexFormat;
private BlockAndTintGetter renderWorld;
private PoseStack poseStack;
private Map<BlockPos, IModelData> modelDataMap;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public MultiBlockModelBuilder(Collection<StructureTemplate.StructureBlockInfo> blocks) {
this.blocks = blocks;
}
public MultiBlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public MultiBlockModelBuilder vertexFormat(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat;
return this;
}
public MultiBlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public MultiBlockModelBuilder poseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
public MultiBlockModelBuilder modelDataMap(Map<BlockPos, IModelData> modelDataMap) {
this.modelDataMap = modelDataMap;
return this;
}
public MultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
this.materialFunc = materialFunc;
return this;
}
@SuppressWarnings("unchecked")
public TessellatedModel build() {
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
if (vertexFormat == null) {
vertexFormat = DefaultVertexFormat.BLOCK;
}
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
}
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
if (modelDataMap == null) {
modelDataMap = Collections.emptyMap();
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
}
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
if (shadeSeparated) {
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
BufferBuilder buffer = new BufferBuilder(1024);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "renderType=" + renderType.toString() + ",shaded=" + shaded));
}
};
ModelBufferingUtil.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelDataMap, resultConsumer);
} else {
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
BufferBuilder buffer = new BufferBuilder(1024);
buffer.begin(VertexFormat.Mode.QUADS, vertexFormat);
return buffer;
};
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
buffer.end();
Material material = materialFunc.apply(renderType, false);
if (material != null) {
meshMapBuilder.put(material, new SimpleMesh(ModelUtil.createVertexList(buffer), Formats.BLOCK, "renderType=" + renderType.toString()));
}
};
ModelBufferingUtil.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelDataMap, resultConsumer);
}
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
}
}

View file

@ -0,0 +1,75 @@
package com.jozufozu.flywheel.core.model.buffering;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.model.BakedQuad;
public interface ShadeSeparatingVertexConsumer<T extends VertexConsumer> extends VertexConsumer {
T getShadedConsumer();
T getUnshadedConsumer();
@Override
default void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
if (quad.isShade()) {
getShadedConsumer().putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
} else {
getUnshadedConsumer().putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
}
}
@Override
default void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
if (bakedQuad.isShade()) {
getShadedConsumer().putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
} else {
getUnshadedConsumer().putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
}
}
@Override
default VertexConsumer vertex(double x, double y, double z) {
return getUnshadedConsumer().vertex(x, y, z);
}
@Override
default VertexConsumer color(int red, int green, int blue, int alpha) {
return getUnshadedConsumer().color(red, green, blue, alpha);
}
@Override
default VertexConsumer uv(float u, float v) {
return getUnshadedConsumer().uv(u, v);
}
@Override
default VertexConsumer overlayCoords(int u, int v) {
return getUnshadedConsumer().overlayCoords(u, v);
}
@Override
default VertexConsumer uv2(int u, int v) {
return getUnshadedConsumer().uv2(u, v);
}
@Override
default VertexConsumer normal(float x, float y, float z) {
return getUnshadedConsumer().normal(x, y, z);
}
@Override
default void endVertex() {
getUnshadedConsumer().endVertex();
}
@Override
default void defaultColor(int red, int green, int blue, int alpha) {
getUnshadedConsumer().defaultColor(red, green, blue, alpha);
}
@Override
default void unsetDefaultColor() {
getUnshadedConsumer().unsetDefaultColor();
}
}

View file

@ -7,8 +7,9 @@ import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
public class TransformedType implements StructType<TransformedPart> {
@ -38,9 +39,41 @@ public class TransformedType implements StructType<TransformedPart> {
}
@Override
public void transform(TransformedPart d, ModelTransformer.Params b) {
b.transform(d.model, d.normal)
.color(d.r, d.g, d.b, d.a)
.light(d.getPackedLight());
public VertexTransformer<? extends TransformedPart> getVertexTransformer() {
return (vertexList, struct, level) -> {
Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f();
int light = struct.getPackedLight();
for (int i = 0; i < vertexList.getVertexCount(); i++) {
pos.set(
vertexList.x(i),
vertexList.y(i),
vertexList.z(i),
1F
);
pos.transform(struct.model);
vertexList.x(i, pos.x());
vertexList.y(i, pos.y());
vertexList.z(i, pos.z());
normal.set(
vertexList.normalX(i),
vertexList.normalY(i),
vertexList.normalZ(i)
);
normal.transform(struct.normal);
normal.normalize();
vertexList.normalX(i, normal.x());
vertexList.normalY(i, normal.y());
vertexList.normalZ(i, normal.z());
vertexList.r(i, struct.r);
vertexList.g(i, struct.g);
vertexList.b(i, struct.b);
vertexList.a(i, struct.a);
vertexList.light(i, light);
}
};
}
}

View file

@ -7,9 +7,12 @@ import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
public class OrientedType implements StructType<OrientedPart> {
@ -39,11 +42,51 @@ public class OrientedType implements StructType<OrientedPart> {
}
@Override
public void transform(OrientedPart d, ModelTransformer.Params b) {
b.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);
public VertexTransformer<? extends OrientedPart> getVertexTransformer() {
return (vertexList, struct, level) -> {
Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f();
Quaternion q = new Quaternion(struct.qX, struct.qY, struct.qZ, struct.qW);
Matrix4f modelMatrix = new Matrix4f();
modelMatrix.setIdentity();
modelMatrix.multiplyWithTranslation(struct.posX + struct.pivotX, struct.posY + struct.pivotY, struct.posZ + struct.pivotZ);
modelMatrix.multiply(q);
modelMatrix.multiplyWithTranslation(-struct.pivotX, -struct.pivotY, -struct.pivotZ);
Matrix3f normalMatrix = new Matrix3f(q);
int light = struct.getPackedLight();
for (int i = 0; i < vertexList.getVertexCount(); i++) {
pos.set(
vertexList.x(i),
vertexList.y(i),
vertexList.z(i),
1f
);
pos.transform(modelMatrix);
vertexList.x(i, pos.x());
vertexList.y(i, pos.y());
vertexList.z(i, pos.z());
normal.set(
vertexList.normalX(i),
vertexList.normalY(i),
vertexList.normalZ(i)
);
normal.transform(normalMatrix);
normal.normalize();
vertexList.normalX(i, normal.x());
vertexList.normalY(i, normal.y());
vertexList.normalZ(i, normal.z());
vertexList.r(i, struct.r);
vertexList.g(i, struct.g);
vertexList.b(i, struct.b);
vertexList.a(i, struct.a);
vertexList.light(i, light);
}
};
}
}

View file

@ -2,16 +2,11 @@ package com.jozufozu.flywheel.core.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.datafixers.util.Pair;
public class BlockVertex implements VertexType {
@ -43,24 +38,4 @@ public class BlockVertex implements VertexType {
public FileResolution getLayoutShader() {
return Components.Files.BLOCK_LAYOUT;
}
public BlockVertexListUnsafe.Shaded createReader(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) {
return new BlockVertexListUnsafe.Shaded(buffer, vertexCount, unshadedStartVertex);
}
public VertexList createReader(BufferBuilder bufferBuilder) {
// TODO: try to avoid virtual model rendering
Pair<BufferBuilder.DrawState, ByteBuffer> pair = bufferBuilder.popNextBuffer();
BufferBuilder.DrawState drawState = pair.getFirst();
if (drawState.format() != DefaultVertexFormat.BLOCK) {
throw new RuntimeException("Cannot use BufferBuilder with " + drawState.format());
}
if (bufferBuilder instanceof ShadeSeparatedBufferBuilder separated) {
return createReader(pair.getSecond(), drawState.vertexCount(), separated.getUnshadedStartVertex());
} else {
return createReader(pair.getSecond(), drawState.vertexCount());
}
}
}

View file

@ -1,11 +1,10 @@
package com.jozufozu.flywheel.core.vertex;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.BufferBuilder;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class BlockVertexList extends AbstractVertexList {
private final int stride;
@ -26,89 +25,73 @@ public class BlockVertexList extends AbstractVertexList {
}
@Override
public float getX(int index) {
public float x(int index) {
return contents.getFloat(vertIdx(index));
}
@Override
public float getY(int index) {
public float y(int index) {
return contents.getFloat(vertIdx(index) + 4);
}
@Override
public float getZ(int index) {
public float z(int index) {
return contents.getFloat(vertIdx(index) + 8);
}
@Override
public byte getR(int index) {
public byte r(int index) {
return contents.get(vertIdx(index) + 12);
}
@Override
public byte getG(int index) {
public byte g(int index) {
return contents.get(vertIdx(index) + 13);
}
@Override
public byte getB(int index) {
public byte b(int index) {
return contents.get(vertIdx(index) + 14);
}
@Override
public byte getA(int index) {
public byte a(int index) {
return contents.get(vertIdx(index) + 15);
}
@Override
public float getU(int index) {
public float u(int index) {
return contents.getFloat(vertIdx(index) + 16);
}
@Override
public float getV(int index) {
public float v(int index) {
return contents.getFloat(vertIdx(index) + 20);
}
@Override
public int getLight(int index) {
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return contents.getInt(vertIdx(index) + 24);
}
@Override
public float getNX(int index) {
public float normalX(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 28));
}
@Override
public float getNY(int index) {
public float normalY(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 29));
}
@Override
public float getNZ(int index) {
public float normalZ(int index) {
return RenderMath.f(contents.get(vertIdx(index) + 30));
}
@Override
public VertexType getVertexType() {
return Formats.BLOCK;
}
public static class Shaded extends BlockVertexList implements ShadedVertexList {
private final int unshadedStartVertex;
public Shaded(ShadeSeparatedBufferBuilder builder) {
super(builder);
unshadedStartVertex = builder.getUnshadedStartVertex();
}
@Override
public boolean isShaded(int index) {
return index < unshadedStartVertex;
}
}
}

View file

@ -4,10 +4,10 @@ import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class BlockVertexListUnsafe extends AbstractVertexList {
public BlockVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
@ -19,89 +19,73 @@ public class BlockVertexListUnsafe extends AbstractVertexList {
}
@Override
public float getX(int index) {
public float x(int index) {
return MemoryUtil.memGetFloat(ptr(index));
}
@Override
public float getY(int index) {
public float y(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 4);
}
@Override
public float getZ(int index) {
public float z(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 8);
}
@Override
public byte getR(int index) {
public byte r(int index) {
return MemoryUtil.memGetByte(ptr(index) + 12);
}
@Override
public byte getG(int index) {
public byte g(int index) {
return MemoryUtil.memGetByte(ptr(index) + 13);
}
@Override
public byte getB(int index) {
public byte b(int index) {
return MemoryUtil.memGetByte(ptr(index) + 14);
}
@Override
public byte getA(int index) {
public byte a(int index) {
return MemoryUtil.memGetByte(ptr(index) + 15);
}
@Override
public float getU(int index) {
public float u(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 16);
}
@Override
public float getV(int index) {
public float v(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 20);
}
@Override
public int getLight(int index) {
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return MemoryUtil.memGetInt(ptr(index) + 24);
}
@Override
public float getNX(int index) {
public float normalX(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 28));
}
@Override
public float getNY(int index) {
public float normalY(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 29));
}
@Override
public float getNZ(int index) {
public float normalZ(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 30));
}
@Override
public VertexType getVertexType() {
return Formats.BLOCK;
}
public static class Shaded extends BlockVertexListUnsafe implements ShadedVertexList {
private final int unshadedStartVertex;
public Shaded(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) {
super(buffer, vertexCount);
this.unshadedStartVertex = unshadedStartVertex;
}
@Override
public boolean isShaded(int index) {
return index < unshadedStartVertex;
}
}
}

View file

@ -15,23 +15,23 @@ public class BlockWriterUnsafe extends VertexWriterUnsafe<BlockVertex> {
@Override
public void writeVertex(VertexList list, int i) {
float x = list.getX(i);
float y = list.getY(i);
float z = list.getZ(i);
float x = list.x(i);
float y = list.y(i);
float z = list.z(i);
float xN = list.getNX(i);
float yN = list.getNY(i);
float zN = list.getNZ(i);
float xN = list.normalX(i);
float yN = list.normalY(i);
float zN = list.normalZ(i);
float u = list.getU(i);
float v = list.getV(i);
float u = list.u(i);
float v = list.v(i);
byte r = list.getR(i);
byte g = list.getG(i);
byte b = list.getB(i);
byte a = list.getA(i);
byte r = list.r(i);
byte g = list.g(i);
byte b = list.b(i);
byte a = list.a(i);
int light = list.getLight(i);
int light = list.light(i);
putVertex(x, y, z, u, v, r, g, b, a, light, xN, yN, zN);
}

View file

@ -4,9 +4,10 @@ import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.util.RenderMath;
import net.minecraft.client.renderer.texture.OverlayTexture;
public class PosTexNormalVertexListUnsafe extends AbstractVertexList {
public PosTexNormalVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
@ -18,72 +19,72 @@ public class PosTexNormalVertexListUnsafe extends AbstractVertexList {
}
@Override
public float getX(int index) {
public float x(int index) {
return MemoryUtil.memGetFloat(ptr(index));
}
@Override
public float getY(int index) {
public float y(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 4);
}
@Override
public float getZ(int index) {
public float z(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 8);
}
@Override
public byte getR(int index) {
public byte r(int index) {
return (byte) 0xFF;
}
@Override
public byte getG(int index) {
public byte g(int index) {
return (byte) 0xFF;
}
@Override
public byte getB(int index) {
public byte b(int index) {
return (byte) 0xFF;
}
@Override
public byte getA(int index) {
public byte a(int index) {
return (byte) 0xFF;
}
@Override
public float getU(int index) {
public float u(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 12);
}
@Override
public float getV(int index) {
public float v(int index) {
return MemoryUtil.memGetFloat(ptr(index) + 16);
}
@Override
public int getLight(int index) {
public int overlay(int index) {
return OverlayTexture.NO_OVERLAY;
}
@Override
public int light(int index) {
return 0;
}
@Override
public float getNX(int index) {
public float normalX(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 20));
}
@Override
public float getNY(int index) {
public float normalY(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 21));
}
@Override
public float getNZ(int index) {
public float normalZ(int index) {
return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 22));
}
@Override
public VertexType getVertexType() {
return Formats.POS_TEX_NORMAL;
}
}

View file

@ -15,16 +15,16 @@ public class PosTexNormalWriterUnsafe extends VertexWriterUnsafe<PosTexNormalVer
@Override
public void writeVertex(VertexList list, int i) {
float x = list.getX(i);
float y = list.getY(i);
float z = list.getZ(i);
float x = list.x(i);
float y = list.y(i);
float z = list.z(i);
float u = list.getU(i);
float v = list.getV(i);
float u = list.u(i);
float v = list.v(i);
float xN = list.getNX(i);
float yN = list.getNY(i);
float zN = list.getNZ(i);
float xN = list.normalX(i);
float yN = list.normalY(i);
float zN = list.normalZ(i);
putVertex(x, y, z, xN, yN, zN, u, v);
}

View file

@ -8,7 +8,7 @@ import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.backend.model.BufferBuilderExtension;
import com.jozufozu.flywheel.backend.instancing.batching.BufferBuilderExtension;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
@ -44,11 +44,6 @@ public abstract class BufferBuilderMixin implements BufferBuilderExtension {
private void ensureCapacity(int increaseAmount) {
}
@Override
public int flywheel$getVertices() {
return vertices;
}
@Override
public void flywheel$freeBuffer() {
if (this.buffer != null) {
@ -69,29 +64,4 @@ public abstract class BufferBuilderMixin implements BufferBuilderExtension {
this.currentElement = this.format.getElements().get(0);
this.elementIndex = 0;
}
@Override
public void flywheel$appendBufferUnsafe(ByteBuffer buffer) {
if (!building) {
throw new IllegalStateException("BufferBuilder not started");
}
if (elementIndex != 0) {
throw new IllegalStateException("Cannot append buffer while building vertex");
}
int numBytes = buffer.remaining();
if (numBytes % format.getVertexSize() != 0) {
throw new IllegalArgumentException("Cannot append buffer with non-whole number of vertices");
}
int numVertices = numBytes / format.getVertexSize();
ensureCapacity(numBytes + format.getVertexSize());
int originalPosition = this.buffer.position();
this.buffer.position(nextElementByte);
MemoryUtil.memCopy(buffer, this.buffer);
this.buffer.position(originalPosition);
nextElementByte += numBytes;
vertices += numVertices;
}
}

View file

@ -3,17 +3,24 @@ package com.jozufozu.flywheel.mixin;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.instancing.DrawBuffer;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;
import com.jozufozu.flywheel.util.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType;
@Mixin(RenderType.class)
public class RenderTypeMixin implements RenderTypeExtension {
@Unique
private final DrawBuffer flywheel$drawBuffer = new DrawBuffer((RenderType) (Object) this);
private DrawBuffer flywheel$drawBuffer;
@Inject(method = "<init>", at = @At("RETURN"))
private void onReturnInit(CallbackInfo ci) {
flywheel$drawBuffer = new DrawBuffer((RenderType) (Object) this);
}
@Override
@NotNull

View file

@ -1,16 +1,11 @@
package com.jozufozu.flywheel.util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
public interface DiffuseLightCalculator {
DiffuseLightCalculator DEFAULT = RenderMath::diffuseLight;
DiffuseLightCalculator NETHER = RenderMath::diffuseLightNether;
static DiffuseLightCalculator forCurrentLevel() {
return forLevel(Minecraft.getInstance().level);
}
static DiffuseLightCalculator forLevel(ClientLevel level) {
return level.effects().constantAmbientLight() ? NETHER : DEFAULT;
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.util;
import com.jozufozu.flywheel.backend.instancing.DrawBuffer;
import com.jozufozu.flywheel.backend.instancing.batching.DrawBuffer;
import net.minecraft.client.renderer.RenderType;

View file

@ -9,9 +9,9 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -24,7 +24,7 @@ import net.minecraft.world.level.block.entity.BellBlockEntity;
public class BellInstance extends BlockEntityInstance<BellBlockEntity> implements DynamicInstance {
private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, Materials.BELL);
private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL);
private final OrientedPart bell;

View file

@ -11,9 +11,9 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.core.structs.oriented.OrientedPart;
@ -36,8 +36,8 @@ import net.minecraft.world.level.block.state.properties.ChestType;
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends BlockEntityInstance<T> implements DynamicInstance {
private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST));
private final OrientedPart body;
private final TransformedPart lid;

View file

@ -6,11 +6,11 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -27,7 +27,7 @@ import net.minecraft.world.phys.Vec3;
public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements DynamicInstance, TickableInstance {
private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, Materials.MINECART);
private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART);
private final PoseStack stack = new PoseStack();

View file

@ -8,9 +8,9 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.model.SimpleLazyModel;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -29,8 +29,8 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntity> implements DynamicInstance {
private static final Function<TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize(it -> new BasicModelSupplier(() -> makeBaseModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize(it -> new SimpleLazyModel(() -> makeBaseModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER));
private final TextureAtlasSprite texture;

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.backend.instancing.effect.Effect;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.model.Models;
import com.jozufozu.flywheel.core.structs.StructTypes;
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;