Refactor GPUInstancer to separate behavior

- Instancer VAO logic moved to InstancedModel
 - Refactor GlVertexArray for easier debugging
 - Sort of clean up BufferLayout
 - Padding items are broken, needs fix
This commit is contained in:
Jozufozu 2022-04-30 19:42:57 -07:00
parent a336abe4f4
commit d5e9e044ec
43 changed files with 579 additions and 458 deletions

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.api;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.model.ModelSupplier;
public interface Material<D extends InstanceData> {

View file

@ -38,6 +38,8 @@ public interface VertexList {
int getVertexCount();
VertexType getVertexType();
default boolean isEmpty() {
return getVertexCount() == 0;
}

View file

@ -3,66 +3,123 @@ package com.jozufozu.flywheel.backend.gl;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.mojang.blaze3d.platform.GlStateManager;
@SuppressWarnings("MismatchedReadAndWriteOfArray")
public class GlVertexArray extends GlObject {
private final boolean[] arrays = new boolean[GL32.glGetInteger(GL32.GL_MAX_VERTEX_ATTRIBS)];
private static final int MAX_ATTRIBS = GL32.glGetInteger(GL32.GL_MAX_VERTEX_ATTRIBS);
/**
* Whether each attribute is enabled.
*/
private final boolean[] enabled = new boolean[MAX_ATTRIBS];
/**
* Each attribute's divisor.
*/
private final int[] divisors = new int[MAX_ATTRIBS];
/**
* The VBO to which each attribute is bound.
*/
private final int[] targets = new int[MAX_ATTRIBS];
/**
* Each attribute's data type.
*/
private final VertexAttribute[] attributes = new VertexAttribute[MAX_ATTRIBS];
/**
* Each attribute's offset.
*/
private final int[] offsets = new int[MAX_ATTRIBS];
/**
* Each attribute's stride.
*/
private final int[] strides = new int[MAX_ATTRIBS];
public GlVertexArray() {
setHandle(GlStateManager._glGenVertexArrays());
}
public static void bind(int vao) {
GlStateManager._glBindVertexArray(vao);
public void bind() {
if (!isBound()) {
GlStateManager._glBindVertexArray(handle());
}
}
public void bind() {
bind(handle());
public boolean isBound() {
return handle() == GlStateTracker.getVertexArray();
}
public static void unbind() {
GlStateManager._glBindVertexArray(0);
}
public void bindAttributes(int startIndex, BufferLayout type) {
int boundBuffer = GlStateTracker.getBuffer(GlBufferType.ARRAY_BUFFER);
bind();
int i = startIndex;
int offset = 0;
final int stride = type.getStride();
for (VertexAttribute attribute : type.getAttributes()) {
targets[i] = boundBuffer;
attributes[i] = attribute;
offsets[i] = offset;
strides[i] = stride;
GL20.glVertexAttribPointer(i++, attribute.size(), attribute.type().getGlEnum(), attribute.normalized(), stride, offset);
offset += attribute.getByteWidth();
}
}
public void enableArrays(int count) {
bind();
for (int i = 0; i < count; i++) {
enable(i);
}
}
public void disableArrays(int count) {
bind();
for (int i = 0; i < count; i++) {
disable(i);
}
}
private void enable(int i) {
if (!arrays[i]) {
if (!enabled[i]) {
GL20.glEnableVertexAttribArray(i);
arrays[i] = true;
enabled[i] = true;
}
}
private void disable(int i) {
if (arrays[i]) {
if (enabled[i]) {
GL20.glDisableVertexAttribArray(i);
arrays[i] = false;
}
}
public void bindAttributes(int startIndex, BufferLayout type) {
int offset = 0;
for (LayoutItem spec : type.getLayoutItems()) {
spec.vertexAttribPointer(type.getStride(), startIndex, offset);
startIndex += spec.attributeCount();
offset += spec.size();
enabled[i] = false;
}
}
protected void deleteInternal(int handle) {
GlStateManager._glDeleteVertexArrays(handle);
}
public void setAttributeDivisor(int index, int divisor) {
if (divisors[index] != divisor) {
bind();
GlCompat.getInstance().instancedArrays.vertexAttribDivisor(index, divisor);
divisors[index] = divisor;
}
}
}

View file

@ -0,0 +1,15 @@
package com.jozufozu.flywheel.backend.gl;
/**
* A bindable attribute in a vertex array.
*
* @param size The number of components in the attribute, e.g. 3 for a vec3.
* @param type The type of the attribute, e.g. GL_FLOAT.
* @param normalized Whether the data is normalized.
*/
public record VertexAttribute(int size, GlNumericType type, boolean normalized) {
public int getByteWidth() {
return size * type.getByteWidth();
}
}

View file

@ -6,19 +6,17 @@ import java.util.function.Supplier;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.model.ModelSupplier;
public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
protected final Supplier<D> factory;
protected final ModelSupplier modelData;
protected final ArrayList<D> data = new ArrayList<>();
protected boolean anyToRemove;
protected AbstractInstancer(Supplier<D> factory, ModelSupplier modelData) {
protected AbstractInstancer(Supplier<D> factory) {
this.factory = factory;
this.modelData = modelData;
}
/**
@ -58,18 +56,10 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
anyToRemove = true;
}
public int getModelVertexCount() {
return modelData.getVertexCount();
}
public int getInstanceCount() {
return data.size();
}
public int getVertexCount() {
return getModelVertexCount() * getInstanceCount();
}
protected void removeDeletedInstances() {
// Figure out which elements are to be removed.
final int oldSize = this.data.size();
@ -117,6 +107,6 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
@Override
public String toString() {
return "Instancer[" + modelData + ']';
return "Instancer[" + getInstanceCount() + ']';
}
}

View file

@ -7,7 +7,8 @@ import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -24,7 +25,7 @@ public class BatchedMaterial<D extends InstanceData> implements Material<D> {
@Override
public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, k -> new CPUInstancer<>(type, k));
return models.computeIfAbsent(modelKey, k -> new CPUInstancer<>(type));
}
public void setupAndRenderInto(PoseStack stack, VertexConsumer buffer) {

View file

@ -39,27 +39,27 @@ public class BatchedMaterialGroup implements MaterialGroup {
public void render(PoseStack stack, BatchDrawingTracker source, TaskEngine pool) {
vertexCount = 0;
instanceCount = 0;
for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) {
instancer.setup();
vertexCount += instancer.getVertexCount();
instanceCount += instancer.getInstanceCount();
}
}
DirectVertexConsumer consumer = source.getDirectConsumer(state, vertexCount);
// avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
consumer.memSetZero();
for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) {
instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders();
instancer.submitTasks(stack, pool, consumer);
}
}
// vertexCount = 0;
// instanceCount = 0;
// for (BatchedMaterial<?> material : materials.values()) {
// for (CPUInstancer<?> instancer : material.models.values()) {
// instancer.setup();
// vertexCount += instancer.getVertexCount();
// instanceCount += instancer.getInstanceCount();
// }
// }
//
// DirectVertexConsumer consumer = source.getDirectConsumer(state, vertexCount);
//
// // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered
// consumer.memSetZero();
//
// for (BatchedMaterial<?> material : materials.values()) {
// for (CPUInstancer<?> instancer : material.models.values()) {
// instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders();
// instancer.submitTasks(stack, pool, consumer);
// }
// }
}
public void clear() {

View file

@ -5,68 +5,67 @@ import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private final Batched<D> batchingType;
// private final Batched<D> batchingType;
//
// final ModelTransformer sbb;
final ModelTransformer sbb;
public CPUInstancer(Batched<D> type, ModelSupplier modelData) {
super(type::create, modelData);
batchingType = type;
sbb = new ModelTransformer(modelData.get());
public CPUInstancer(Batched<D> type) {
super(type::create);
// batchingType = type;
//
// sbb = new ModelTransformer(modelData.get());
}
void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) {
int instances = getInstanceCount();
while (instances > 0) {
int end = instances;
instances -= 512;
int start = Math.max(instances, 0);
int verts = getModelVertexCount() * (end - start);
DirectVertexConsumer sub = consumer.split(verts);
pool.submit(() -> drawRange(stack, sub, start, end));
}
// int instances = getInstanceCount();
//
// while (instances > 0) {
// int end = instances;
// instances -= 512;
// int start = Math.max(instances, 0);
//
// int verts = getModelVertexCount() * (end - start);
//
// DirectVertexConsumer sub = consumer.split(verts);
//
// pool.submit(() -> drawRange(stack, sub, start, end));
// }
}
private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) {
ModelTransformer.Params params = new ModelTransformer.Params();
for (D d : data.subList(from, to)) {
params.loadDefault();
batchingType.transform(d, params);
sbb.renderInto(params, stack, buffer);
}
// ModelTransformer.Params params = new ModelTransformer.Params();
//
// for (D d : data.subList(from, to)) {
// params.loadDefault();
//
// batchingType.transform(d, params);
//
// sbb.renderInto(params, stack, buffer);
// }
}
void drawAll(PoseStack stack, VertexConsumer buffer) {
ModelTransformer.Params params = new ModelTransformer.Params();
for (D d : data) {
params.loadDefault();
batchingType.transform(d, params);
sbb.renderInto(params, stack, buffer);
}
// ModelTransformer.Params params = new ModelTransformer.Params();
// for (D d : data) {
// params.loadDefault();
//
// batchingType.transform(d, params);
//
// sbb.renderInto(params, stack, buffer);
// }
}
void setup() {
if (anyToRemove) {
data.removeIf(InstanceData::isRemoved);
anyToRemove = false;
}
// if (anyToRemove) {
// data.removeIf(InstanceData::isRemoved);
// anyToRemove = false;
// }
}
@Override

View file

@ -1,45 +1,31 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Collections;
import java.util.Map;
import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import net.minecraft.client.renderer.RenderType;
public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private final BufferLayout instanceFormat;
private final Instanced<D> instancedType;
final BufferLayout instanceFormat;
final Instanced<D> instancedType;
private BufferedModel model;
private GlVertexArray vao;
private GlBuffer instanceVBO;
private int glInstanceCount = 0;
private boolean deleted;
private boolean initialized;
GlBuffer vbo;
int attributeBaseIndex;
int glInstanceCount = 0;
protected boolean anyToUpdate;
boolean anyToUpdate;
public GPUInstancer(Instanced<D> type, ModelSupplier model) {
super(type::create, model);
public GPUInstancer(Instanced<D> type) {
super(type::create);
this.instanceFormat = type.getLayout();
instancedType = type;
}
@ -49,72 +35,24 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
anyToUpdate = true;
}
public void render() {
if (invalid()) return;
public void init() {
if (vbo != null) return;
vao.bind();
renderSetup();
if (glInstanceCount > 0) {
model.drawInstances(glInstanceCount);
}
// persistent mapping sync point
instanceVBO.doneForThisFrame();
}
private boolean invalid() {
return deleted || model == null;
}
public Map<RenderType, Renderable> init(ModelAllocator modelAllocator) {
if (isInitialized()) return Collections.emptyMap();
initialized = true;
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
instanceVBO.setGrowthMargin(instanceFormat.getStride() * 16);
vao = new GlVertexArray();
model = modelAllocator.alloc(modelData.get(), vao);
vao.bind();
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
return ImmutableMap.of(modelData.getRenderType(), this::render);
}
public boolean isInitialized() {
return initialized;
vbo = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
vbo.setGrowthMargin(instanceFormat.getStride() * 16);
}
public boolean isEmpty() {
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
}
/**
* Free acquired resources. All other Instancer methods are undefined behavior after calling delete.
*/
public void delete() {
if (invalid()) return;
deleted = true;
model.delete();
instanceVBO.delete();
vao.delete();
}
protected void renderSetup() {
void renderSetup(GlVertexArray vao) {
if (anyToRemove) {
removeDeletedInstances();
}
instanceVBO.bind();
if (!realloc()) {
vbo.bind();
if (!realloc(vao)) {
if (anyToRemove) {
clearBufferTail();
@ -127,7 +65,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
glInstanceCount = data.size();
}
instanceVBO.unbind();
vbo.unbind();
anyToRemove = anyToUpdate = false;
}
@ -135,9 +73,9 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private void clearBufferTail() {
int size = data.size();
final int offset = size * instanceFormat.getStride();
final long length = instanceVBO.getCapacity() - offset;
final long length = vbo.getCapacity() - offset;
if (length > 0) {
try (MappedBuffer buf = instanceVBO.getBuffer(offset, length)) {
try (MappedBuffer buf = vbo.getBuffer(offset, length)) {
MemoryUtil.memSet(MemoryUtil.memAddress(buf.unwrap()), 0, length);
} catch (Exception e) {
Flywheel.LOGGER.error("Error clearing buffer tail:", e);
@ -150,7 +88,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
if (size <= 0) return;
try (MappedBuffer mapped = instanceVBO.getBuffer()) {
try (MappedBuffer mapped = vbo.getBuffer()) {
final StructWriter<D> writer = instancedType.getWriter(mapped);
@ -172,13 +110,13 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
}
}
private boolean realloc() {
private boolean realloc(GlVertexArray vao) {
int size = this.data.size();
int stride = instanceFormat.getStride();
int requiredSize = size * stride;
if (instanceVBO.ensureCapacity(requiredSize)) {
if (vbo.ensureCapacity(requiredSize)) {
try (MappedBuffer buffer = instanceVBO.getBuffer()) {
try (MappedBuffer buffer = vbo.getBuffer()) {
StructWriter<D> writer = instancedType.getWriter(buffer);
for (D datum : data) {
writer.write(datum);
@ -189,19 +127,18 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
glInstanceCount = size;
bindInstanceAttributes();
bindInstanceAttributes(vao);
return true;
}
return false;
}
private void bindInstanceAttributes() {
int attributeBaseIndex = model.getAttributeCount();
private void bindInstanceAttributes(GlVertexArray vao) {
vao.bindAttributes(attributeBaseIndex, instanceFormat);
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
GlCompat.getInstance().instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1);
vao.setAttributeDivisor(attributeBaseIndex + i, 1);
}
}
}

View file

@ -12,8 +12,8 @@ import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.backend.model.MeshAllocator;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import net.minecraft.client.renderer.RenderType;
@ -23,10 +23,10 @@ import net.minecraft.client.renderer.RenderType;
*/
public class InstancedMaterial<D extends InstanceData> implements Material<D> {
protected final Map<ModelSupplier, GPUInstancer<D>> models = new HashMap<>();
protected final Map<ModelSupplier, InstancedModel<D>> models = new HashMap<>();
protected final Instanced<D> type;
protected final List<GPUInstancer<D>> uninitialized = new ArrayList<>();
protected final List<InstancedModel<D>> uninitialized = new ArrayList<>();
protected final Multimap<RenderType, Renderable> renderables = ArrayListMultimap.create();
@ -36,29 +36,32 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
@Override
public Instancer<D> model(ModelSupplier modelKey) {
return models.computeIfAbsent(modelKey, k -> {
GPUInstancer<D> instancer = new GPUInstancer<>(type, modelKey);
uninitialized.add(instancer);
return instancer;
});
return models.computeIfAbsent(modelKey, this::createInstancer).instancer;
}
public int getInstanceCount() {
return models.values().stream().mapToInt(GPUInstancer::getInstanceCount).sum();
return models.values()
.stream()
.map(InstancedModel::getInstancer)
.mapToInt(GPUInstancer::getInstanceCount)
.sum();
}
public int getVertexCount() {
return models.values().stream().mapToInt(GPUInstancer::getVertexCount).sum();
return models.values()
.stream()
.mapToInt(InstancedModel::getVertexCount)
.sum();
}
public boolean nothingToRender() {
return models.size() > 0 && models.values()
.stream()
.allMatch(GPUInstancer::isEmpty);
.allMatch(InstancedModel::isEmpty);
}
public void delete() {
models.values().forEach(GPUInstancer::delete);
models.values().forEach(InstancedModel::delete);
models.clear();
renderables.clear();
}
@ -68,16 +71,15 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
*/
public void clear() {
models.values()
.stream()
.map(InstancedModel::getInstancer)
.forEach(GPUInstancer::clear);
}
public Collection<GPUInstancer<D>> getAllInstancers() {
return models.values();
}
public void init(MeshAllocator allocator) {
for (var instanced : uninitialized) {
public void init(ModelAllocator allocator) {
for (GPUInstancer<?> instancer : uninitialized) {
var map = instancer.init(allocator);
var map = instanced.init(allocator);
map.forEach((type, renderable) -> renderables.get(type).add(renderable));
}
@ -91,4 +93,10 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
public boolean anythingToRender(RenderType type) {
return renderables.get(type).size() > 0;
}
private InstancedModel<D> createInstancer(ModelSupplier model) {
var instancer = new InstancedModel<>(new GPUInstancer<>(type), model);
uninitialized.add(instancer);
return instancer;
}
}

View file

@ -0,0 +1,89 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Map;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.MeshAllocator;
import com.jozufozu.flywheel.core.model.ModelSupplier;
import net.minecraft.client.renderer.RenderType;
public class InstancedModel<D extends InstanceData> {
GPUInstancer<D> instancer;
ModelSupplier model;
@Nullable
private BufferedModel bufferedMesh;
@Nullable
private GlVertexArray vao;
public InstancedModel(GPUInstancer<D> instancer, ModelSupplier model) {
this.instancer = instancer;
this.model = model;
}
public Map<RenderType, Renderable> init(MeshAllocator allocator) {
instancer.init();
vao = new GlVertexArray();
bufferedMesh = allocator.alloc(model.get(), vao);
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
return ImmutableMap.of(RenderType.solid(), this::render);
}
public void render() {
if (invalid()) return;
vao.bind();
instancer.renderSetup(vao);
if (instancer.glInstanceCount > 0) {
bufferedMesh.drawInstances(instancer.glInstanceCount);
}
// persistent mapping sync point
instancer.vbo.doneForThisFrame();
}
private boolean invalid() {
return instancer.vbo == null || bufferedMesh == null || vao == null;
}
public GPUInstancer<D> getInstancer() {
return instancer;
}
public ModelSupplier getModel() {
return model;
}
public int getVertexCount() {
return model.getVertexCount() * instancer.glInstanceCount;
}
public boolean isEmpty() {
return instancer.isEmpty();
}
public void delete() {
if (invalid()) return;
vao.delete();
bufferedMesh.delete();
instancer.vbo.delete();
vao = null;
bufferedMesh = null;
instancer.vbo = null;
}
}

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -9,9 +8,6 @@ import java.util.Set;
import javax.annotation.Nonnull;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructType;
@ -19,8 +15,8 @@ import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.FallbackAllocator;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.backend.model.ModelPool;
import com.jozufozu.flywheel.backend.model.MeshAllocator;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.RenderTypeRegistry;
@ -45,7 +41,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final ProgramCompiler<P> context;
private ModelAllocator allocator;
private MeshAllocator allocator;
protected final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
@ -169,7 +165,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
public void beginFrame(Camera info) {
checkOriginDistance(info);
ModelAllocator allocator = getModelAllocator();
MeshAllocator allocator = getModelAllocator();
for (InstancedMaterial<?> material : materials.values()) {
material.init(allocator);
@ -177,7 +173,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
toRender.addAll(material.renderables.keySet());
}
if (allocator instanceof ModelPool pool) {
if (allocator instanceof MeshPool pool) {
// ...and then flush the model arena in case anything was marked for upload
pool.flush();
}
@ -215,19 +211,19 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
}
private ModelAllocator getModelAllocator() {
private MeshAllocator getModelAllocator() {
if (allocator == null) {
allocator = createAllocator();
}
return this.allocator;
}
private static ModelAllocator createAllocator() {
private static MeshAllocator createAllocator() {
if (GlCompat.getInstance()
.onAMDWindows()) {
return FallbackAllocator.INSTANCE;
} else {
return new ModelPool(Formats.POS_TEX_NORMAL);
return new MeshPool(Formats.POS_TEX_NORMAL);
}
}

View file

@ -1,17 +1,17 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
public class ArrayModelRenderer {
private final Model model;
private final Mesh mesh;
protected GlVertexArray vao;
protected BufferedModel vbo;
protected boolean initialized;
public ArrayModelRenderer(Model model) {
this.model = model;
public ArrayModelRenderer(Mesh mesh) {
this.mesh = mesh;
}
/**
@ -29,14 +29,12 @@ public class ArrayModelRenderer {
protected void init() {
initialized = true;
if (model.empty()) return;
if (mesh.empty()) return;
this.vbo = new IndexedModel(model);
this.vbo = new IndexedModel(mesh);
vao = new GlVertexArray();
vao.bind();
// bind the model's vbo to our vao
this.vbo.setupState(vao);

View file

@ -1,15 +1,14 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
public enum FallbackAllocator implements ModelAllocator {
public enum FallbackAllocator implements MeshAllocator {
INSTANCE;
@Override
public BufferedModel alloc(Model model, GlVertexArray vao) {
IndexedModel out = new IndexedModel(model);
vao.bind();
public BufferedModel alloc(Mesh mesh, GlVertexArray vao) {
IndexedModel out = new IndexedModel(mesh);
out.setupState(vao);
return out;
}

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
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.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
/**
* An indexed triangle model. Just what the driver ordered.
@ -20,32 +20,32 @@ import com.jozufozu.flywheel.core.model.Model;
*/
public class IndexedModel implements BufferedModel {
protected final Model model;
protected final Mesh mesh;
protected final GlPrimitive primitiveMode;
protected ElementBuffer ebo;
protected GlBuffer vbo;
protected boolean deleted;
public IndexedModel(Model model) {
this.model = model;
public IndexedModel(Mesh mesh) {
this.mesh = mesh;
this.primitiveMode = GlPrimitive.TRIANGLES;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
// allocate the buffer on the gpu
vbo.ensureCapacity(model.size());
vbo.ensureCapacity(mesh.size());
// mirror it in system memory, so we can write to it, and upload our model.
try (MappedBuffer buffer = vbo.getBuffer()) {
model.writeInto(buffer.unwrap());
mesh.writeInto(buffer.unwrap());
} catch (Exception e) {
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", model.name()), e);
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e);
}
vbo.unbind();
this.ebo = model.createEBO();
this.ebo = mesh.createEBO();
}
/**
@ -81,11 +81,11 @@ public class IndexedModel implements BufferedModel {
@Override
public VertexType getType() {
return model.getType();
return mesh.getType();
}
public int getVertexCount() {
return model.vertexCount();
return mesh.vertexCount();
}
public void delete() {

View file

@ -1,17 +1,17 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
public interface ModelAllocator {
public interface MeshAllocator {
/**
* Allocate a model.
*
* @param model The model to allocate.
* @param mesh The model to allocate.
* @param vao The vertex array object to attach the model to.
* @return A handle to the allocated model.
*/
BufferedModel alloc(Model model, GlVertexArray vao);
BufferedModel alloc(Mesh mesh, GlVertexArray vao);
@FunctionalInterface
interface Callback {

View file

@ -14,9 +14,9 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
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.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
public class ModelPool implements ModelAllocator {
public class MeshPool implements MeshAllocator {
protected final VertexType vertexType;
@ -36,7 +36,7 @@ public class ModelPool implements ModelAllocator {
*
* @param vertexType The vertex type of the models that will be stored in the pool.
*/
public ModelPool(VertexType vertexType) {
public MeshPool(VertexType vertexType) {
this.vertexType = vertexType;
int stride = vertexType.getStride();
@ -49,14 +49,14 @@ public class ModelPool implements ModelAllocator {
/**
* Allocate a model in the arena.
*
* @param model The model to allocate.
* @param mesh The model to allocate.
* @param vao The vertex array object to attach the model to.
* @return A handle to the allocated model.
*/
@Override
public PooledModel alloc(Model model, GlVertexArray vao) {
PooledModel bufferedModel = new PooledModel(vao, model, vertices);
vertices += model.vertexCount();
public PooledModel alloc(Mesh mesh, GlVertexArray vao) {
PooledModel bufferedModel = new PooledModel(vao, mesh, vertices);
vertices += mesh.vertexCount();
models.add(bufferedModel);
pendingUpload.add(bufferedModel);
@ -94,7 +94,7 @@ public class ModelPool implements ModelAllocator {
model.first = vertices;
vertices += model.getVertexCount();
vertices += model.mesh.vertexCount();
}
this.vertices = vertices;
@ -120,7 +120,7 @@ public class ModelPool implements ModelAllocator {
model.buffer(writer);
vertices += model.getVertexCount();
vertices += model.mesh.vertexCount();
}
} catch (Exception e) {
@ -153,31 +153,31 @@ public class ModelPool implements ModelAllocator {
private final ElementBuffer ebo;
private final GlVertexArray vao;
private final Model model;
private final Mesh mesh;
private int first;
private boolean deleted;
public PooledModel(GlVertexArray vao, Model model, int first) {
public PooledModel(GlVertexArray vao, Mesh mesh, int first) {
this.vao = vao;
this.model = model;
this.mesh = mesh;
this.first = first;
ebo = model.createEBO();
ebo = mesh.createEBO();
}
@Override
public VertexType getType() {
return ModelPool.this.vertexType;
return MeshPool.this.vertexType;
}
@Override
public int getVertexCount() {
return model.vertexCount();
return mesh.vertexCount();
}
@Override
public void setupState(GlVertexArray vao) {
vao.bind();
vbo.bind();
vao.enableArrays(getAttributeCount());
vao.bindAttributes(0, vertexType.getLayout());
}
@ -212,9 +212,8 @@ public class ModelPool implements ModelAllocator {
private void buffer(VertexWriter writer) {
writer.seekToVertex(first);
writer.writeVertexList(model.getReader());
writer.writeVertexList(mesh.getReader());
vao.bind();
setupState(vao);
}
}

View file

@ -12,30 +12,30 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
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.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
public class VBOModel implements BufferedModel {
protected final Model model;
protected final Mesh mesh;
protected final GlPrimitive primitiveMode;
protected GlBuffer vbo;
protected boolean deleted;
public VBOModel(GlPrimitive primitiveMode, Model model) {
this.model = model;
public VBOModel(GlPrimitive primitiveMode, Mesh mesh) {
this.mesh = mesh;
this.primitiveMode = primitiveMode;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
// allocate the buffer on the gpu
vbo.ensureCapacity(model.size());
vbo.ensureCapacity(mesh.size());
// mirror it in system memory, so we can write to it, and upload our model.
try (MappedBuffer buffer = vbo.getBuffer()) {
model.writeInto(buffer.unwrap());
mesh.writeInto(buffer.unwrap());
} catch (Exception e) {
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", model.name()), e);
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e);
}
vbo.unbind();
@ -47,11 +47,11 @@ public class VBOModel implements BufferedModel {
@Override
public VertexType getType() {
return model.getType();
return mesh.getType();
}
public int getVertexCount() {
return model.vertexCount();
return mesh.vertexCount();
}
/**
@ -64,7 +64,7 @@ public class VBOModel implements BufferedModel {
}
public void drawCall() {
glDrawArrays(primitiveMode.glEnum, 0, getVertexCount());
glDrawArrays(primitiveMode.glEnum, 0, mesh.vertexCount());
}
/**
@ -73,7 +73,7 @@ public class VBOModel implements BufferedModel {
public void drawInstances(int instanceCount) {
if (!valid()) return;
GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, getVertexCount(), instanceCount);
GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, mesh.vertexCount(), instanceCount);
}
public void delete() {

View file

@ -0,0 +1,55 @@
package com.jozufozu.flywheel.core;
import javax.annotation.Nonnull;
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;
import net.minecraft.client.renderer.RenderType;
public class BasicModelSupplier implements ModelSupplier {
private RenderType renderType;
private final Lazy<Mesh> supplier;
public BasicModelSupplier(NonNullSupplier<Mesh> supplier) {
this(supplier, RenderType.solid());
}
public BasicModelSupplier(NonNullSupplier<Mesh> supplier, RenderType renderType) {
this.supplier = Lazy.of(supplier);
this.renderType = renderType;
}
public BasicModelSupplier setCutout() {
return setRenderType(RenderType.cutoutMipped());
}
public BasicModelSupplier setRenderType(@Nonnull RenderType renderType) {
this.renderType = renderType;
return this;
}
@Override
public Mesh get() {
return supplier.get();
}
@Nonnull
public RenderType getRenderType() {
return renderType;
}
public int getVertexCount() {
return supplier.map(Mesh::vertexCount)
.orElse(0);
}
@Override
public String toString() {
return "ModelSupplier{" + supplier.map(Mesh::name)
.orElse("Uninitialized") + '}';
}
}

View file

@ -42,7 +42,7 @@ public class FullscreenQuad {
vao = new GlVertexArray();
vao.bind();
glEnableVertexAttribArray(0);
vao.enableArrays(1);
glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0);

View file

@ -1,46 +0,0 @@
package com.jozufozu.flywheel.core;
import javax.annotation.Nonnull;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.NonNullSupplier;
import net.minecraft.client.renderer.RenderType;
public class ModelSupplier extends Lazy<Model> {
private RenderType renderType;
public ModelSupplier(NonNullSupplier<Model> supplier) {
this(supplier, RenderType.solid());
}
public ModelSupplier(NonNullSupplier<Model> supplier, RenderType renderType) {
super(supplier);
this.renderType = renderType;
}
public ModelSupplier setCutout() {
return setRenderType(RenderType.cutoutMipped());
}
public ModelSupplier setRenderType(@Nonnull RenderType renderType) {
this.renderType = renderType;
return this;
}
@Nonnull
public RenderType getRenderType() {
return renderType;
}
public int getVertexCount() {
return map(Model::vertexCount).orElse(0);
}
@Override
public String toString() {
return "ModelSupplier{" + map(Model::name).orElse("Uninitialized") + '}';
}
}

View file

@ -2,36 +2,33 @@ package com.jozufozu.flywheel.core;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import com.jozufozu.flywheel.core.model.BlockModel;
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.Util;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public class Models {
public static ModelSupplier block(BlockState state) {
return BLOCK_STATE.computeIfAbsent(state, it -> new ModelSupplier(() -> new BlockModel(it)));
public static BasicModelSupplier block(BlockState state) {
return BLOCK_STATE.computeIfAbsent(state, it -> new BasicModelSupplier(() -> new BlockMesh(it)));
}
public static ModelSupplier partial(PartialModel partial) {
return PARTIAL.computeIfAbsent(partial, it -> new ModelSupplier(() -> new BlockModel(it)));
public static BasicModelSupplier partial(PartialModel partial) {
return PARTIAL.computeIfAbsent(partial, it -> new BasicModelSupplier(() -> new BlockMesh(it)));
}
public static ModelSupplier partial(PartialModel partial, Direction dir) {
public static BasicModelSupplier partial(PartialModel partial, Direction dir) {
return partial(partial, dir, () -> ModelUtil.rotateToFace(dir));
}
public static ModelSupplier partial(PartialModel partial, Direction dir, Supplier<PoseStack> modelTransform) {
return PARTIAL_DIR.computeIfAbsent(Pair.of(dir, partial), $ -> new ModelSupplier(() -> new BlockModel(partial, modelTransform.get())));
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())));
}
public static void onReload(ReloadRenderersEvent ignored) {
@ -40,8 +37,8 @@ public class Models {
PARTIAL_DIR.clear();
}
private static final Map<BlockState, ModelSupplier> BLOCK_STATE = new HashMap<>();
private static final Map<PartialModel, ModelSupplier> PARTIAL = new HashMap<>();
private static final Map<Pair<Direction, PartialModel>, ModelSupplier> PARTIAL_DIR = new HashMap<>();
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

@ -3,12 +3,14 @@ package com.jozufozu.flywheel.core.hardcoded;
import java.util.List;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker;
public class ModelPart implements Model {
public class ModelPart implements Mesh {
private final int vertices;
private final String name;
@ -25,7 +27,7 @@ public class ModelPart implements Model {
this.vertices = vertices;
}
PosTexNormalWriterUnsafe writer = Formats.POS_TEX_NORMAL.createWriter(MemoryTracker.create(size()));
PosTexNormalWriterUnsafe writer = getType().createWriter(MemoryTracker.create(size()));
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
cuboid.buffer(writer);
}
@ -51,4 +53,9 @@ public class ModelPart implements Model {
public VertexList getReader() {
return reader;
}
@Override
public PosTexNormalVertex getType() {
return Formats.POS_TEX_NORMAL;
}
}

View file

@ -1,9 +1,11 @@
package com.jozufozu.flywheel.core.layout;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.VertexAttribute;
/**
* Classic Vertex Format struct with a clever name.
@ -17,29 +19,29 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
*/
public class BufferLayout {
private final List<LayoutItem> allAttributes;
private final List<VertexAttribute> attributes;
private final int numAttributes;
private final int stride;
public BufferLayout(List<LayoutItem> allAttributes) {
this.allAttributes = allAttributes;
public BufferLayout(List<LayoutItem> layoutItems) {
int numAttributes = 0, stride = 0;
for (LayoutItem spec : allAttributes) {
numAttributes += spec.attributeCount();
stride += spec.size();
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
stride = calculateStride(layoutItems);
for (LayoutItem item : layoutItems) {
item.provideAttributes(attributes::add);
}
this.numAttributes = numAttributes;
this.stride = stride;
this.attributes = attributes.build();
}
public List<LayoutItem> getLayoutItems() {
return allAttributes;
public Collection<VertexAttribute> getAttributes() {
return attributes;
}
public int getAttributeCount() {
return numAttributes;
return attributes.size();
}
public int getStride() {
@ -50,6 +52,14 @@ public class BufferLayout {
return new Builder();
}
private static int calculateStride(List<LayoutItem> layoutItems) {
int stride = 0;
for (LayoutItem spec : layoutItems) {
stride += spec.getByteWidth();
}
return stride;
}
public static class Builder {
private final ImmutableList.Builder<LayoutItem> allItems;
@ -66,4 +76,5 @@ public class BufferLayout {
return new BufferLayout(allItems.build());
}
}
}

View file

@ -21,4 +21,6 @@ public class CommonItems {
public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true);
public static final LayoutItem PADDING_BYTE = new Padding(1);
public static final MatrixItem MAT3 = new MatrixItem(3, 3);
public static final MatrixItem MAT4 = new MatrixItem(4, 4);
}

View file

@ -1,10 +1,13 @@
package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.VertexAttribute;
public interface LayoutItem {
void vertexAttribPointer(int stride, int index, int offset);
void provideAttributes(Consumer<VertexAttribute> consumer);
int size();
int getByteWidth();
int attributeCount();
}

View file

@ -0,0 +1,22 @@
package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.VertexAttribute;
public record MatrixItem(int rows, int cols) implements LayoutItem {
@Override
public void provideAttributes(Consumer<VertexAttribute> consumer) {
for (int i = 0; i < rows; i++) {
consumer.accept(new VertexAttribute(cols, GlNumericType.FLOAT, false));
}
}
@Override
public int getByteWidth() {
return GlNumericType.FLOAT.getByteWidth() * rows * cols;
}
}

View file

@ -1,38 +0,0 @@
package com.jozufozu.flywheel.core.layout;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
public enum MatrixItems implements LayoutItem {
MAT3(3, 3),
MAT4(4, 4),
;
private final int rows;
private final int cols;
MatrixItems(int rows, int cols) {
this.rows = rows;
this.cols = cols;
}
@Override
public void vertexAttribPointer(int stride, int index, int offset) {
for (int i = 0; i < rows; i++) {
long attribPointer = offset + (long) i * cols * GlNumericType.FLOAT.getByteWidth();
GL20.glVertexAttribPointer(index + i, cols, GlNumericType.FLOAT.getGlEnum(), false, stride, attribPointer);
}
}
@Override
public int size() {
return GlNumericType.FLOAT.getByteWidth() * rows * cols;
}
@Override
public int attributeCount() {
return rows;
}
}

View file

@ -1,20 +1,19 @@
package com.jozufozu.flywheel.core.layout;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.VertexAttribute;
record Padding(int bytes) implements LayoutItem {
@Override
public void vertexAttribPointer(int stride, int index, int offset) {
public void provideAttributes(Consumer<VertexAttribute> consumer) {
}
@Override
public int size() {
public int getByteWidth() {
return bytes;
}
@Override
public int attributeCount() {
return 0;
}
}

View file

@ -1,42 +1,30 @@
package com.jozufozu.flywheel.core.layout;
import org.lwjgl.opengl.GL20;
import java.util.function.Consumer;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.VertexAttribute;
public class PrimitiveItem implements LayoutItem {
private final GlNumericType type;
private final int count;
private final int size;
private final int attributeCount;
private final boolean normalized;
private final VertexAttribute attribute;
public PrimitiveItem(GlNumericType type, int count) {
this(type, count, false);
}
public PrimitiveItem(GlNumericType type, int count, boolean normalized) {
this.type = type;
this.count = count;
this.size = type.getByteWidth() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
this.normalized = normalized;
attribute = new VertexAttribute(count, type, normalized);
}
@Override
public void vertexAttribPointer(int stride, int index, int offset) {
GL20.glVertexAttribPointer(index, count, type.getGlEnum(), normalized, stride, offset);
public void provideAttributes(Consumer<VertexAttribute> consumer) {
consumer.accept(attribute);
}
@Override
public int size() {
return size;
}
@Override
public int attributeCount() {
return attributeCount;
public int getByteWidth() {
return attribute.getByteWidth();
}
}

View file

@ -7,7 +7,6 @@ import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.MatrixItems;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import net.minecraft.resources.ResourceLocation;
@ -16,7 +15,7 @@ public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
.addItems(MatrixItems.MAT4, MatrixItems.MAT3)
.addItems(CommonItems.MAT4, CommonItems.MAT3)
.build();
@Override

View file

@ -12,43 +12,43 @@ import net.minecraft.world.level.block.state.BlockState;
/**
* A model of a single block.
*/
public class BlockModel implements Model {
public class BlockMesh implements Mesh {
private static final PoseStack IDENTITY = new PoseStack();
private final VertexList reader;
private final String name;
public BlockModel(BlockState state) {
public BlockMesh(BlockState state) {
this(Minecraft.getInstance()
.getBlockRenderer()
.getBlockModel(state), state);
}
public BlockModel(BakedModel model, BlockState referenceState) {
public BlockMesh(BakedModel model, BlockState referenceState) {
this(model, referenceState, IDENTITY);
}
public BlockModel(PartialModel model) {
public BlockMesh(PartialModel model) {
this(model, IDENTITY);
}
public BlockModel(PartialModel model, PoseStack ms) {
public BlockMesh(PartialModel model, PoseStack ms) {
this(ModelUtil.bakedModel(model.get())
.withPoseStack(ms), model.getName());
}
public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) {
public BlockMesh(BakedModel model, BlockState referenceState, PoseStack ms) {
this(ModelUtil.bakedModel(model)
.withReferenceState(referenceState)
.withPoseStack(ms), referenceState.toString());
}
public BlockModel(Bufferable builder, String name) {
public BlockMesh(Bufferable builder, String name) {
this(Formats.BLOCK.createReader(builder.build()), name);
}
public BlockModel(VertexList reader, String name) {
public BlockMesh(VertexList reader, String name) {
this.reader = reader;
this.name = name;
}
@ -58,13 +58,13 @@ public class BlockModel implements Model {
return name;
}
@Override
public int vertexCount() {
return reader.getVertexCount();
}
@Override
public VertexList getReader() {
return reader;
}
@Override
public String toString() {
return "BlockMesh{" + "name='" + name + "',type='" + reader.getVertexType() + "}";
}
}

View file

@ -5,7 +5,6 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.model.ElementBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.QuadConverter;
/**
@ -28,7 +27,7 @@ import com.jozufozu.flywheel.core.QuadConverter;
* assert model.size() == final - initial;
* }</pre>
*/
public interface Model {
public interface Mesh {
/**
* A name uniquely identifying this model.
@ -40,10 +39,12 @@ public interface Model {
/**
* @return The number of vertices the model has.
*/
int vertexCount();
default int vertexCount() {
return getReader().getVertexCount();
}
default VertexType getType() {
return Formats.POS_TEX_NORMAL;
return getReader().getVertexType();
}
/**

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.core.model;
public interface ModelSupplier {
Mesh get();
int getVertexCount();
}

View file

@ -20,15 +20,15 @@ import net.minecraft.util.Mth;
public class ModelTransformer {
private final Model model;
private final Mesh mesh;
private final VertexList reader;
private final IntPredicate shadedPredicate;
public final Context context = new Context();
public ModelTransformer(Model model) {
this.model = model;
reader = model.getReader();
public ModelTransformer(Mesh mesh) {
this.mesh = mesh;
reader = mesh.getReader();
if (reader instanceof ShadedVertexList shaded) {
shadedPredicate = shaded::isShaded;
} else {
@ -128,7 +128,7 @@ public class ModelTransformer {
@Override
public String toString() {
return "ModelTransformer[" + model + ']';
return "ModelTransformer[" + mesh + ']';
}
public static int transformColor(byte component, float scale) {

View file

@ -76,4 +76,8 @@ public final class WorldModelBuilder implements Bufferable {
this.poseStack = poseStack;
return this;
}
public BlockMesh finish() {
return new BlockMesh(this, "name");
}
}

View file

@ -4,6 +4,8 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.BufferBuilder;
@ -106,6 +108,11 @@ public class BlockVertexList implements VertexList {
return vertexCount;
}
@Override
public VertexType getVertexType() {
return Formats.BLOCK;
}
public static class Shaded extends BlockVertexList implements ShadedVertexList {
private final int unshadedStartVertex;

View file

@ -6,6 +6,8 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.RenderMath;
public class BlockVertexListUnsafe implements VertexList {
@ -99,6 +101,11 @@ public class BlockVertexListUnsafe implements VertexList {
return vertexCount;
}
@Override
public VertexType getVertexType() {
return Formats.BLOCK;
}
public static class Shaded extends BlockVertexListUnsafe implements ShadedVertexList {
private final int unshadedStartVertex;

View file

@ -5,6 +5,8 @@ import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.RenderMath;
public class PosTexNormalVertexListUnsafe implements VertexList {
@ -92,4 +94,9 @@ public class PosTexNormalVertexListUnsafe implements VertexList {
public int getVertexCount() {
return vertexCount;
}
@Override
public VertexType getVertexType() {
return Formats.POS_TEX_NORMAL;
}
}

View file

@ -6,7 +6,7 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -20,7 +20,7 @@ import net.minecraft.world.level.block.entity.BellBlockEntity;
public class BellInstance extends BlockEntityInstance<BellBlockEntity> implements DynamicInstance {
private static final ModelSupplier MODEL = new ModelSupplier(BellInstance::createBellModel, RenderType.cutoutMipped());
private static final BasicModelSupplier MODEL = new BasicModelSupplier(BellInstance::createBellModel, RenderType.cutoutMipped());
private final OrientedData bell;

View file

@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
@ -19,7 +19,6 @@ import com.mojang.math.Vector3f;
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
import net.minecraft.Util;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.Material;
@ -34,8 +33,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, Material, ModelSupplier> LID = Util.memoize((type, mat) -> new ModelSupplier(() -> createLidModel(type, mat.sprite()), Sheets.chestSheet()));
private static final BiFunction<ChestType, Material, ModelSupplier> BASE = Util.memoize((type, mat) -> new ModelSupplier(() -> createBaseModel(type, mat.sprite()), Sheets.chestSheet()));
private static final BiFunction<ChestType, Material, BasicModelSupplier> LID = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createLidModel(type, mat.sprite()), Sheets.chestSheet()));
private static final BiFunction<ChestType, Material, BasicModelSupplier> BASE = Util.memoize((type, mat) -> new BasicModelSupplier(() -> createBaseModel(type, mat.sprite()), Sheets.chestSheet()));
private final OrientedData body;
private final ModelData lid;

View file

@ -2,17 +2,16 @@ package com.jozufozu.flywheel.vanilla;
import javax.annotation.Nonnull;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
@ -30,7 +29,7 @@ import net.minecraft.world.phys.Vec3;
public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance<T> implements DynamicInstance, TickableInstance {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
private static final ModelSupplier MODEL = new ModelSupplier(MinecartInstance::getBodyModel, RenderType.entitySolid(MINECART_LOCATION));
private static final BasicModelSupplier MODEL = new BasicModelSupplier(MinecartInstance::getBodyModel, RenderType.entitySolid(MINECART_LOCATION));
private final PoseStack stack = new PoseStack();
@ -160,7 +159,7 @@ public class MinecartInstance<T extends AbstractMinecart> extends EntityInstance
}
@Nonnull
private static Model getBodyModel() {
private static Mesh getBodyModel() {
int y = -3;
return ModelPart.builder("minecart", 64, 32)
.cuboid().invertYZ().start(-10, -8, -y).size(20, 16, 2).textureOffset(0, 10).rotateZ((float) Math.PI).rotateX(((float)Math.PI / 2F)).endCuboid()

View file

@ -6,7 +6,7 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.ModelSupplier;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
@ -26,8 +26,8 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
public class ShulkerBoxInstance extends BlockEntityInstance<ShulkerBoxBlockEntity> implements DynamicInstance {
private static final Function<TextureAtlasSprite, ModelSupplier> BASE = Util.memoize(it -> new ModelSupplier(() -> makeBaseModel(it), RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET)));
private static final Function<TextureAtlasSprite, ModelSupplier> LID = Util.memoize(it -> new ModelSupplier(() -> makeLidModel(it), RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET)));
private static final Function<TextureAtlasSprite, BasicModelSupplier> BASE = Util.memoize(it -> new BasicModelSupplier(() -> makeBaseModel(it), RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET)));
private static final Function<TextureAtlasSprite, BasicModelSupplier> LID = Util.memoize(it -> new BasicModelSupplier(() -> makeLidModel(it), RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET)));
private final TextureAtlasSprite texture;