mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-08 13:26:39 +01:00
Simplifications
- Inline a handful of interfaces that had limited use - Removed IndexedModel/VBOModel as a first step in refactoring model uploads - InstancedModels/GPUInstancers support multiple VAOs - Fix padding issue
This commit is contained in:
parent
d5e9e044ec
commit
be60eae9af
25 changed files with 271 additions and 461 deletions
|
@ -1,5 +1,8 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
@ -46,13 +49,18 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
||||||
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
|
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
|
|
||||||
void renderSetup(GlVertexArray vao) {
|
void renderSetup(GlVertexArray vao) {
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
removeDeletedInstances();
|
removeDeletedInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
vbo.bind();
|
vbo.bind();
|
||||||
if (!realloc(vao)) {
|
|
||||||
|
if (!realloc()) {
|
||||||
|
|
||||||
|
boundTo.clear();
|
||||||
|
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
clearBufferTail();
|
clearBufferTail();
|
||||||
|
@ -65,6 +73,10 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
||||||
glInstanceCount = data.size();
|
glInstanceCount = data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (boundTo.add(vao)) {
|
||||||
|
bindInstanceAttributes(vao);
|
||||||
|
}
|
||||||
|
|
||||||
vbo.unbind();
|
vbo.unbind();
|
||||||
|
|
||||||
anyToRemove = anyToUpdate = false;
|
anyToRemove = anyToUpdate = false;
|
||||||
|
@ -110,7 +122,7 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean realloc(GlVertexArray vao) {
|
private boolean realloc() {
|
||||||
int size = this.data.size();
|
int size = this.data.size();
|
||||||
int stride = instanceFormat.getStride();
|
int stride = instanceFormat.getStride();
|
||||||
int requiredSize = size * stride;
|
int requiredSize = size * stride;
|
||||||
|
@ -127,8 +139,6 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
|
||||||
|
|
||||||
glInstanceCount = size;
|
glInstanceCount = size;
|
||||||
|
|
||||||
bindInstanceAttributes(vao);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -12,7 +11,7 @@ import com.jozufozu.flywheel.api.InstanceData;
|
||||||
import com.jozufozu.flywheel.api.Instancer;
|
import com.jozufozu.flywheel.api.Instancer;
|
||||||
import com.jozufozu.flywheel.api.Material;
|
import com.jozufozu.flywheel.api.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||||
import com.jozufozu.flywheel.backend.model.MeshAllocator;
|
import com.jozufozu.flywheel.backend.model.MeshPool;
|
||||||
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
@ -54,12 +53,6 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
||||||
.sum();
|
.sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean nothingToRender() {
|
|
||||||
return models.size() > 0 && models.values()
|
|
||||||
.stream()
|
|
||||||
.allMatch(InstancedModel::isEmpty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
models.values().forEach(InstancedModel::delete);
|
models.values().forEach(InstancedModel::delete);
|
||||||
models.clear();
|
models.clear();
|
||||||
|
@ -76,7 +69,7 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
||||||
.forEach(GPUInstancer::clear);
|
.forEach(GPUInstancer::clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(MeshAllocator allocator) {
|
public void init(MeshPool allocator) {
|
||||||
for (var instanced : uninitialized) {
|
for (var instanced : uninitialized) {
|
||||||
|
|
||||||
var map = instanced.init(allocator);
|
var map = instanced.init(allocator);
|
||||||
|
@ -86,14 +79,6 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
||||||
uninitialized.clear();
|
uninitialized.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renderIn(RenderType layer) {
|
|
||||||
renderables.get(layer).forEach(Renderable::render);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean anythingToRender(RenderType type) {
|
|
||||||
return renderables.get(type).size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancedModel<D> createInstancer(ModelSupplier model) {
|
private InstancedModel<D> createInstancer(ModelSupplier model) {
|
||||||
var instancer = new InstancedModel<>(new GPUInstancer<>(type), model);
|
var instancer = new InstancedModel<>(new GPUInstancer<>(type), model);
|
||||||
uninitialized.add(instancer);
|
uninitialized.add(instancer);
|
||||||
|
|
|
@ -2,61 +2,88 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.api.InstanceData;
|
import com.jozufozu.flywheel.api.InstanceData;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.model.BufferedModel;
|
import com.jozufozu.flywheel.backend.model.MeshPool;
|
||||||
import com.jozufozu.flywheel.backend.model.MeshAllocator;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
||||||
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public class InstancedModel<D extends InstanceData> {
|
public class InstancedModel<D extends InstanceData> {
|
||||||
|
|
||||||
GPUInstancer<D> instancer;
|
final GPUInstancer<D> instancer;
|
||||||
ModelSupplier model;
|
final ModelSupplier model;
|
||||||
|
private Map<RenderType, Layer> layers;
|
||||||
@Nullable
|
|
||||||
private BufferedModel bufferedMesh;
|
|
||||||
@Nullable
|
|
||||||
private GlVertexArray vao;
|
|
||||||
|
|
||||||
public InstancedModel(GPUInstancer<D> instancer, ModelSupplier model) {
|
public InstancedModel(GPUInstancer<D> instancer, ModelSupplier model) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<RenderType, Renderable> init(MeshAllocator allocator) {
|
public Map<RenderType, ? extends Renderable> init(MeshPool allocator) {
|
||||||
instancer.init();
|
instancer.init();
|
||||||
|
|
||||||
vao = new GlVertexArray();
|
layers = model.get()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> Pair.of(entry.getKey(), new Layer(allocator, entry.getKey(), entry.getValue())))
|
||||||
|
.collect(ImmutableMap.toImmutableMap(Pair::first, Pair::second));
|
||||||
|
|
||||||
bufferedMesh = allocator.alloc(model.get(), vao);
|
|
||||||
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
|
|
||||||
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
|
|
||||||
|
|
||||||
return ImmutableMap.of(RenderType.solid(), this::render);
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
private class Layer implements Renderable {
|
||||||
if (invalid()) return;
|
|
||||||
|
|
||||||
vao.bind();
|
final RenderType type;
|
||||||
|
MeshPool.BufferedMesh bufferedMesh;
|
||||||
|
GlVertexArray vao;
|
||||||
|
|
||||||
instancer.renderSetup(vao);
|
private Layer(MeshPool allocator, RenderType type, Mesh mesh) {
|
||||||
|
this.type = type;
|
||||||
if (instancer.glInstanceCount > 0) {
|
vao = new GlVertexArray();
|
||||||
bufferedMesh.drawInstances(instancer.glInstanceCount);
|
bufferedMesh = allocator.alloc(mesh, vao);
|
||||||
|
instancer.attributeBaseIndex = bufferedMesh.getAttributeCount();
|
||||||
|
vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
// persistent mapping sync point
|
@Override
|
||||||
instancer.vbo.doneForThisFrame();
|
public void render() {
|
||||||
}
|
if (invalid()) return;
|
||||||
|
|
||||||
private boolean invalid() {
|
vao.bind();
|
||||||
return instancer.vbo == null || bufferedMesh == null || vao == null;
|
|
||||||
|
instancer.renderSetup(vao);
|
||||||
|
|
||||||
|
if (instancer.glInstanceCount > 0) {
|
||||||
|
bufferedMesh.drawInstances(instancer.glInstanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// persistent mapping sync point
|
||||||
|
instancer.vbo.doneForThisFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRemove() {
|
||||||
|
return invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean invalid() {
|
||||||
|
return instancer.vbo == null || bufferedMesh == null || vao == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
if (invalid()) return;
|
||||||
|
|
||||||
|
vao.delete();
|
||||||
|
bufferedMesh.delete();
|
||||||
|
|
||||||
|
vao = null;
|
||||||
|
bufferedMesh = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GPUInstancer<D> getInstancer() {
|
public GPUInstancer<D> getInstancer() {
|
||||||
|
@ -71,19 +98,14 @@ public class InstancedModel<D extends InstanceData> {
|
||||||
return model.getVertexCount() * instancer.glInstanceCount;
|
return model.getVertexCount() * instancer.glInstanceCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return instancer.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (invalid()) return;
|
if (instancer.vbo == null) return;
|
||||||
|
|
||||||
vao.delete();
|
|
||||||
bufferedMesh.delete();
|
|
||||||
instancer.vbo.delete();
|
instancer.vbo.delete();
|
||||||
|
|
||||||
vao = null;
|
|
||||||
bufferedMesh = null;
|
|
||||||
instancer.vbo = null;
|
instancer.vbo = null;
|
||||||
|
|
||||||
|
for (var layer : layers.values()) {
|
||||||
|
layer.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,8 @@ import javax.annotation.Nonnull;
|
||||||
import com.jozufozu.flywheel.api.InstanceData;
|
import com.jozufozu.flywheel.api.InstanceData;
|
||||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||||
import com.jozufozu.flywheel.backend.model.FallbackAllocator;
|
|
||||||
import com.jozufozu.flywheel.backend.model.MeshAllocator;
|
|
||||||
import com.jozufozu.flywheel.backend.model.MeshPool;
|
import com.jozufozu.flywheel.backend.model.MeshPool;
|
||||||
import com.jozufozu.flywheel.core.Formats;
|
import com.jozufozu.flywheel.core.Formats;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
|
@ -41,7 +38,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
protected BlockPos originCoordinate = BlockPos.ZERO;
|
||||||
|
|
||||||
protected final ProgramCompiler<P> context;
|
protected final ProgramCompiler<P> context;
|
||||||
private MeshAllocator allocator;
|
private MeshPool allocator;
|
||||||
|
|
||||||
protected final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
|
protected final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
|
||||||
|
|
||||||
|
@ -110,7 +107,10 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
|
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
|
||||||
InstancedMaterial<?> material = entry.getValue();
|
InstancedMaterial<?> material = entry.getValue();
|
||||||
|
|
||||||
if (material.anythingToRender(type)) {
|
var toRender = material.renderables.get(type);
|
||||||
|
toRender.removeIf(Renderable::shouldRemove);
|
||||||
|
|
||||||
|
if (!toRender.isEmpty()) {
|
||||||
Instanced<? extends InstanceData> instanceType = entry.getKey();
|
Instanced<? extends InstanceData> instanceType = entry.getKey();
|
||||||
|
|
||||||
setup(type, camX, camY, camZ, viewProjection, instanceType.getProgramSpec());
|
setup(type, camX, camY, camZ, viewProjection, instanceType.getProgramSpec());
|
||||||
|
@ -118,7 +118,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
instanceCount += material.getInstanceCount();
|
instanceCount += material.getInstanceCount();
|
||||||
vertexCount += material.getVertexCount();
|
vertexCount += material.getVertexCount();
|
||||||
|
|
||||||
material.renderIn(type);
|
toRender.forEach(Renderable::render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
public void beginFrame(Camera info) {
|
public void beginFrame(Camera info) {
|
||||||
checkOriginDistance(info);
|
checkOriginDistance(info);
|
||||||
|
|
||||||
MeshAllocator allocator = getModelAllocator();
|
MeshPool allocator = getModelAllocator();
|
||||||
|
|
||||||
for (InstancedMaterial<?> material : materials.values()) {
|
for (InstancedMaterial<?> material : materials.values()) {
|
||||||
material.init(allocator);
|
material.init(allocator);
|
||||||
|
@ -173,10 +173,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
toRender.addAll(material.renderables.keySet());
|
toRender.addAll(material.renderables.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allocator instanceof MeshPool pool) {
|
allocator.flush();
|
||||||
// ...and then flush the model arena in case anything was marked for upload
|
|
||||||
pool.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,20 +208,17 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
private MeshAllocator getModelAllocator() {
|
private MeshPool getModelAllocator() {
|
||||||
if (allocator == null) {
|
if (allocator == null) {
|
||||||
allocator = createAllocator();
|
allocator = createAllocator();
|
||||||
}
|
}
|
||||||
return this.allocator;
|
return this.allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MeshAllocator createAllocator() {
|
private static MeshPool createAllocator() {
|
||||||
if (GlCompat.getInstance()
|
|
||||||
.onAMDWindows()) {
|
// FIXME: Windows AMD Drivers don't like ..BaseVertex
|
||||||
return FallbackAllocator.INSTANCE;
|
return new MeshPool(Formats.POS_TEX_NORMAL);
|
||||||
} else {
|
|
||||||
return new MeshPool(Formats.POS_TEX_NORMAL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
|
|
@ -3,4 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
public interface Renderable {
|
public interface Renderable {
|
||||||
|
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
|
boolean shouldRemove();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,31 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
package com.jozufozu.flywheel.backend.model;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.BlockMesh;
|
||||||
|
|
||||||
public class ArrayModelRenderer {
|
public class ArrayModelRenderer {
|
||||||
|
|
||||||
private final Mesh mesh;
|
protected final GlVertexArray vao;
|
||||||
protected GlVertexArray vao;
|
protected final MeshPool.BufferedMesh mesh;
|
||||||
protected BufferedModel vbo;
|
|
||||||
protected boolean initialized;
|
|
||||||
|
|
||||||
public ArrayModelRenderer(Mesh mesh) {
|
public ArrayModelRenderer(BlockMesh mesh, MeshPool meshPool) {
|
||||||
this.mesh = mesh;
|
this.vao = new GlVertexArray();
|
||||||
|
this.mesh = meshPool.alloc(mesh, this.vao);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders this model, checking first if there is anything to render.
|
* Renders this model, checking first if there is anything to render.
|
||||||
*/
|
*/
|
||||||
public void draw() {
|
public void draw() {
|
||||||
if (!initialized) init();
|
if (mesh.isDeleted()) return;
|
||||||
if (!isValid()) return;
|
|
||||||
|
|
||||||
vao.bind();
|
vao.bind();
|
||||||
|
|
||||||
vbo.drawCall();
|
mesh.drawCall();
|
||||||
}
|
|
||||||
|
|
||||||
protected void init() {
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
if (mesh.empty()) return;
|
|
||||||
|
|
||||||
this.vbo = new IndexedModel(mesh);
|
|
||||||
|
|
||||||
vao = new GlVertexArray();
|
|
||||||
|
|
||||||
// bind the model's vbo to our vao
|
|
||||||
this.vbo.setupState(vao);
|
|
||||||
|
|
||||||
GlVertexArray.unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
if (vbo != null)
|
mesh.delete();
|
||||||
vbo.delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isValid() {
|
|
||||||
return vbo != null && vbo.valid();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
|
||||||
|
|
||||||
public interface BufferedModel {
|
|
||||||
|
|
||||||
VertexType getType();
|
|
||||||
|
|
||||||
int getVertexCount();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VAO must be bound externally.
|
|
||||||
*/
|
|
||||||
void setupState(GlVertexArray vao);
|
|
||||||
|
|
||||||
void drawCall();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws many instances of this model, assuming the appropriate state is already bound.
|
|
||||||
*/
|
|
||||||
void drawInstances(int instanceCount);
|
|
||||||
|
|
||||||
boolean isDeleted();
|
|
||||||
|
|
||||||
void delete();
|
|
||||||
|
|
||||||
default BufferLayout getLayout() {
|
|
||||||
return getType().getLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean valid() {
|
|
||||||
return getVertexCount() > 0 && !isDeleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getAttributeCount() {
|
|
||||||
return getType().getLayout().getAttributeCount();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
|
||||||
|
|
||||||
public enum FallbackAllocator implements MeshAllocator {
|
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BufferedModel alloc(Mesh mesh, GlVertexArray vao) {
|
|
||||||
IndexedModel out = new IndexedModel(mesh);
|
|
||||||
out.setupState(vao);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
import org.lwjgl.opengl.GL31;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|
||||||
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.buffer.MappedGlBuffer;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An indexed triangle model. Just what the driver ordered.
|
|
||||||
*
|
|
||||||
* <br><em>This should be favored over a normal BufferedModel.</em>
|
|
||||||
*/
|
|
||||||
public class IndexedModel implements BufferedModel {
|
|
||||||
|
|
||||||
protected final Mesh mesh;
|
|
||||||
protected final GlPrimitive primitiveMode;
|
|
||||||
protected ElementBuffer ebo;
|
|
||||||
protected GlBuffer vbo;
|
|
||||||
protected boolean deleted;
|
|
||||||
|
|
||||||
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(mesh.size());
|
|
||||||
|
|
||||||
// mirror it in system memory, so we can write to it, and upload our model.
|
|
||||||
try (MappedBuffer buffer = vbo.getBuffer()) {
|
|
||||||
mesh.writeInto(buffer.unwrap());
|
|
||||||
} catch (Exception e) {
|
|
||||||
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
vbo.unbind();
|
|
||||||
|
|
||||||
this.ebo = mesh.createEBO();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VBO/VAO should be bound externally.
|
|
||||||
*/
|
|
||||||
public void setupState(GlVertexArray vao) {
|
|
||||||
vbo.bind();
|
|
||||||
vao.enableArrays(getAttributeCount());
|
|
||||||
vao.bindAttributes(0, getType().getLayout());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawCall() {
|
|
||||||
ebo.bind();
|
|
||||||
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws many instances of this model, assuming the appropriate state is already bound.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void drawInstances(int instanceCount) {
|
|
||||||
if (!valid()) return;
|
|
||||||
|
|
||||||
ebo.bind();
|
|
||||||
|
|
||||||
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VertexType getType() {
|
|
||||||
return mesh.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVertexCount() {
|
|
||||||
return mesh.vertexCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (deleted) return;
|
|
||||||
|
|
||||||
deleted = true;
|
|
||||||
vbo.delete();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
|
||||||
|
|
||||||
public interface MeshAllocator {
|
|
||||||
/**
|
|
||||||
* Allocate a model.
|
|
||||||
*
|
|
||||||
* @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(Mesh mesh, GlVertexArray vao);
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface Callback {
|
|
||||||
void onAlloc(BufferedModel arenaModel);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,13 +16,13 @@ import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
|
|
||||||
public class MeshPool implements MeshAllocator {
|
public class MeshPool {
|
||||||
|
|
||||||
protected final VertexType vertexType;
|
protected final VertexType vertexType;
|
||||||
|
|
||||||
private final List<PooledModel> models = new ArrayList<>();
|
private final List<BufferedMesh> models = new ArrayList<>();
|
||||||
|
|
||||||
private final List<PooledModel> pendingUpload = new ArrayList<>();
|
private final List<BufferedMesh> pendingUpload = new ArrayList<>();
|
||||||
|
|
||||||
private final GlBuffer vbo;
|
private final GlBuffer vbo;
|
||||||
|
|
||||||
|
@ -53,10 +53,9 @@ public class MeshPool implements MeshAllocator {
|
||||||
* @param vao The vertex array object to attach the model to.
|
* @param vao The vertex array object to attach the model to.
|
||||||
* @return A handle to the allocated model.
|
* @return A handle to the allocated model.
|
||||||
*/
|
*/
|
||||||
@Override
|
public BufferedMesh alloc(Mesh mesh, GlVertexArray vao) {
|
||||||
public PooledModel alloc(Mesh mesh, GlVertexArray vao) {
|
BufferedMesh bufferedModel = new BufferedMesh(vao, mesh, vertices);
|
||||||
PooledModel bufferedModel = new PooledModel(vao, mesh, vertices);
|
vertices += mesh.getVertexCount();
|
||||||
vertices += mesh.vertexCount();
|
|
||||||
models.add(bufferedModel);
|
models.add(bufferedModel);
|
||||||
pendingUpload.add(bufferedModel);
|
pendingUpload.add(bufferedModel);
|
||||||
|
|
||||||
|
@ -84,17 +83,17 @@ public class MeshPool implements MeshAllocator {
|
||||||
private void processDeletions() {
|
private void processDeletions() {
|
||||||
|
|
||||||
// remove deleted models
|
// remove deleted models
|
||||||
models.removeIf(PooledModel::isDeleted);
|
models.removeIf(BufferedMesh::isDeleted);
|
||||||
|
|
||||||
// re-evaluate first vertex for each model
|
// re-evaluate first vertex for each model
|
||||||
int vertices = 0;
|
int vertices = 0;
|
||||||
for (PooledModel model : models) {
|
for (BufferedMesh model : models) {
|
||||||
if (model.first != vertices)
|
if (model.first != vertices)
|
||||||
pendingUpload.add(model);
|
pendingUpload.add(model);
|
||||||
|
|
||||||
model.first = vertices;
|
model.first = vertices;
|
||||||
|
|
||||||
vertices += model.mesh.vertexCount();
|
vertices += model.mesh.getVertexCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.vertices = vertices;
|
this.vertices = vertices;
|
||||||
|
@ -115,12 +114,12 @@ public class MeshPool implements MeshAllocator {
|
||||||
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
|
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
|
||||||
|
|
||||||
int vertices = 0;
|
int vertices = 0;
|
||||||
for (PooledModel model : models) {
|
for (BufferedMesh model : models) {
|
||||||
model.first = vertices;
|
model.first = vertices;
|
||||||
|
|
||||||
model.buffer(writer);
|
model.buffer(writer);
|
||||||
|
|
||||||
vertices += model.mesh.vertexCount();
|
vertices += model.mesh.getVertexCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -131,7 +130,7 @@ public class MeshPool implements MeshAllocator {
|
||||||
private void uploadPending() {
|
private void uploadPending() {
|
||||||
try (MappedBuffer buffer = vbo.getBuffer()) {
|
try (MappedBuffer buffer = vbo.getBuffer()) {
|
||||||
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
|
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
|
||||||
for (PooledModel model : pendingUpload) {
|
for (BufferedMesh model : pendingUpload) {
|
||||||
model.buffer(writer);
|
model.buffer(writer);
|
||||||
}
|
}
|
||||||
pendingUpload.clear();
|
pendingUpload.clear();
|
||||||
|
@ -148,7 +147,7 @@ public class MeshPool implements MeshAllocator {
|
||||||
vbo.delete();
|
vbo.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PooledModel implements BufferedModel {
|
public class BufferedMesh {
|
||||||
|
|
||||||
private final ElementBuffer ebo;
|
private final ElementBuffer ebo;
|
||||||
private final GlVertexArray vao;
|
private final GlVertexArray vao;
|
||||||
|
@ -158,38 +157,20 @@ public class MeshPool implements MeshAllocator {
|
||||||
|
|
||||||
private boolean deleted;
|
private boolean deleted;
|
||||||
|
|
||||||
public PooledModel(GlVertexArray vao, Mesh mesh, int first) {
|
public BufferedMesh(GlVertexArray vao, Mesh mesh, int first) {
|
||||||
this.vao = vao;
|
this.vao = vao;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
this.first = first;
|
this.first = first;
|
||||||
ebo = mesh.createEBO();
|
ebo = mesh.createEBO();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public VertexType getType() {
|
|
||||||
return MeshPool.this.vertexType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getVertexCount() {
|
|
||||||
return mesh.vertexCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupState(GlVertexArray vao) {
|
|
||||||
vbo.bind();
|
|
||||||
vao.enableArrays(getAttributeCount());
|
|
||||||
vao.bindAttributes(0, vertexType.getLayout());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawCall() {
|
public void drawCall() {
|
||||||
|
ebo.bind();
|
||||||
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first);
|
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawInstances(int instanceCount) {
|
public void drawInstances(int instanceCount) {
|
||||||
if (!valid()) return;
|
if (mesh.getVertexCount() <= 0 || isDeleted()) return;
|
||||||
|
|
||||||
ebo.bind();
|
ebo.bind();
|
||||||
|
|
||||||
|
@ -198,12 +179,10 @@ public class MeshPool implements MeshAllocator {
|
||||||
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first);
|
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDeleted() {
|
public boolean isDeleted() {
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
setDirty();
|
setDirty();
|
||||||
anyToRemove = true;
|
anyToRemove = true;
|
||||||
|
@ -214,7 +193,12 @@ public class MeshPool implements MeshAllocator {
|
||||||
writer.seekToVertex(first);
|
writer.seekToVertex(first);
|
||||||
writer.writeVertexList(mesh.getReader());
|
writer.writeVertexList(mesh.getReader());
|
||||||
|
|
||||||
setupState(vao);
|
vao.enableArrays(getAttributeCount());
|
||||||
|
vao.bindAttributes(0, vertexType.getLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAttributeCount() {
|
||||||
|
return MeshPool.this.vertexType.getLayout().getAttributeCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.glDrawArrays;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL31;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|
||||||
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.buffer.MappedGlBuffer;
|
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
|
||||||
|
|
||||||
public class VBOModel implements BufferedModel {
|
|
||||||
|
|
||||||
protected final Mesh mesh;
|
|
||||||
protected final GlPrimitive primitiveMode;
|
|
||||||
protected GlBuffer vbo;
|
|
||||||
protected boolean deleted;
|
|
||||||
|
|
||||||
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(mesh.size());
|
|
||||||
|
|
||||||
// mirror it in system memory, so we can write to it, and upload our model.
|
|
||||||
try (MappedBuffer buffer = vbo.getBuffer()) {
|
|
||||||
mesh.writeInto(buffer.unwrap());
|
|
||||||
} catch (Exception e) {
|
|
||||||
Flywheel.LOGGER.error(String.format("Error uploading model '%s':", mesh.name()), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
vbo.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VertexType getType() {
|
|
||||||
return mesh.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVertexCount() {
|
|
||||||
return mesh.vertexCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VBO/VAO should be bound externally.
|
|
||||||
*/
|
|
||||||
public void setupState(GlVertexArray vao) {
|
|
||||||
vbo.bind();
|
|
||||||
vao.enableArrays(getAttributeCount());
|
|
||||||
vao.bindAttributes(0, getLayout());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawCall() {
|
|
||||||
glDrawArrays(primitiveMode.glEnum, 0, mesh.vertexCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws many instances of this model, assuming the appropriate state is already bound.
|
|
||||||
*/
|
|
||||||
public void drawInstances(int instanceCount) {
|
|
||||||
if (!valid()) return;
|
|
||||||
|
|
||||||
GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, mesh.vertexCount(), instanceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (deleted) return;
|
|
||||||
|
|
||||||
deleted = true;
|
|
||||||
vbo.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package com.jozufozu.flywheel.core;
|
package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.core.model.Mesh;
|
import com.jozufozu.flywheel.core.model.Mesh;
|
||||||
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
import com.jozufozu.flywheel.core.model.ModelSupplier;
|
||||||
import com.jozufozu.flywheel.util.Lazy;
|
import com.jozufozu.flywheel.util.Lazy;
|
||||||
|
@ -33,17 +36,12 @@ public class BasicModelSupplier implements ModelSupplier {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mesh get() {
|
public Map<RenderType, Mesh> get() {
|
||||||
return supplier.get();
|
return ImmutableMap.of(renderType, supplier.get());
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public RenderType getRenderType() {
|
|
||||||
return renderType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getVertexCount() {
|
public int getVertexCount() {
|
||||||
return supplier.map(Mesh::vertexCount)
|
return supplier.map(Mesh::getVertexCount)
|
||||||
.orElse(0);
|
.orElse(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class ModelPart implements Mesh {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int vertexCount() {
|
public int getVertexCount() {
|
||||||
return vertices;
|
return vertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,17 +23,16 @@ public class BufferLayout {
|
||||||
|
|
||||||
private final int stride;
|
private final int stride;
|
||||||
|
|
||||||
public BufferLayout(List<LayoutItem> layoutItems) {
|
public BufferLayout(List<LayoutItem> layoutItems, int padding) {
|
||||||
|
|
||||||
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
|
ImmutableList.Builder<VertexAttribute> attributes = ImmutableList.builder();
|
||||||
|
|
||||||
stride = calculateStride(layoutItems);
|
|
||||||
|
|
||||||
for (LayoutItem item : layoutItems) {
|
for (LayoutItem item : layoutItems) {
|
||||||
item.provideAttributes(attributes::add);
|
item.provideAttributes(attributes::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attributes = attributes.build();
|
this.attributes = attributes.build();
|
||||||
|
this.stride = calculateStride(this.attributes) + padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<VertexAttribute> getAttributes() {
|
public Collection<VertexAttribute> getAttributes() {
|
||||||
|
@ -52,9 +51,9 @@ public class BufferLayout {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int calculateStride(List<LayoutItem> layoutItems) {
|
private static int calculateStride(List<VertexAttribute> layoutItems) {
|
||||||
int stride = 0;
|
int stride = 0;
|
||||||
for (LayoutItem spec : layoutItems) {
|
for (VertexAttribute spec : layoutItems) {
|
||||||
stride += spec.getByteWidth();
|
stride += spec.getByteWidth();
|
||||||
}
|
}
|
||||||
return stride;
|
return stride;
|
||||||
|
@ -62,6 +61,7 @@ public class BufferLayout {
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private final ImmutableList.Builder<LayoutItem> allItems;
|
private final ImmutableList.Builder<LayoutItem> allItems;
|
||||||
|
private int padding;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
allItems = ImmutableList.builder();
|
allItems = ImmutableList.builder();
|
||||||
|
@ -72,8 +72,13 @@ public class BufferLayout {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder withPadding(int padding) {
|
||||||
|
this.padding = padding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public BufferLayout build() {
|
public BufferLayout build() {
|
||||||
return new BufferLayout(allItems.build());
|
return new BufferLayout(allItems.build(), padding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ public class CommonItems {
|
||||||
public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true);
|
public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true);
|
||||||
|
|
||||||
public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true);
|
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 MAT3 = new MatrixItem(3, 3);
|
||||||
public static final MatrixItem MAT4 = new MatrixItem(4, 4);
|
public static final MatrixItem MAT4 = new MatrixItem(4, 4);
|
||||||
|
|
|
@ -8,6 +8,4 @@ public interface LayoutItem {
|
||||||
|
|
||||||
void provideAttributes(Consumer<VertexAttribute> consumer);
|
void provideAttributes(Consumer<VertexAttribute> consumer);
|
||||||
|
|
||||||
int getByteWidth();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,4 @@ public record MatrixItem(int rows, int cols) implements LayoutItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getByteWidth() {
|
|
||||||
return GlNumericType.FLOAT.getByteWidth() * rows * cols;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
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 provideAttributes(Consumer<VertexAttribute> consumer) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getByteWidth() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -22,9 +22,4 @@ public class PrimitiveItem implements LayoutItem {
|
||||||
consumer.accept(attribute);
|
consumer.accept(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getByteWidth() {
|
|
||||||
return attribute.getByteWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public interface Mesh {
|
||||||
/**
|
/**
|
||||||
* @return The number of vertices the model has.
|
* @return The number of vertices the model has.
|
||||||
*/
|
*/
|
||||||
default int vertexCount() {
|
default int getVertexCount() {
|
||||||
return getReader().getVertexCount();
|
return getReader().getVertexCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,14 +59,14 @@ public interface Mesh {
|
||||||
*/
|
*/
|
||||||
default ElementBuffer createEBO() {
|
default ElementBuffer createEBO() {
|
||||||
return QuadConverter.getInstance()
|
return QuadConverter.getInstance()
|
||||||
.quads2Tris(vertexCount() / 4);
|
.quads2Tris(getVertexCount() / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size in bytes that this model's data takes up.
|
* The size in bytes that this model's data takes up.
|
||||||
*/
|
*/
|
||||||
default int size() {
|
default int size() {
|
||||||
return getType().byteOffset(vertexCount());
|
return getType().byteOffset(getVertexCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,7 +74,7 @@ public interface Mesh {
|
||||||
* @return true if there are no vertices.
|
* @return true if there are no vertices.
|
||||||
*/
|
*/
|
||||||
default boolean empty() {
|
default boolean empty() {
|
||||||
return vertexCount() == 0;
|
return getVertexCount() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void writeInto(ByteBuffer buffer) {
|
default void writeInto(ByteBuffer buffer) {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.jozufozu.flywheel.core.model;
|
package com.jozufozu.flywheel.core.model;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public interface ModelSupplier {
|
public interface ModelSupplier {
|
||||||
|
|
||||||
Mesh get();
|
Map<RenderType, Mesh> get();
|
||||||
|
|
||||||
int getVertexCount();
|
int getVertexCount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
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.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -77,7 +77,7 @@ public final class WorldModelBuilder implements Bufferable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockMesh finish() {
|
public BlockMesh intoMesh(String name) {
|
||||||
return new BlockMesh(this, "name");
|
return new BlockMesh(this, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ public class BlockVertex implements VertexType {
|
||||||
CommonItems.RGBA,
|
CommonItems.RGBA,
|
||||||
CommonItems.UV,
|
CommonItems.UV,
|
||||||
CommonItems.LIGHT_SHORT,
|
CommonItems.LIGHT_SHORT,
|
||||||
CommonItems.NORMAL,
|
CommonItems.NORMAL)
|
||||||
CommonItems.PADDING_BYTE)
|
.withPadding(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue