Merge remote-tracking branch 'origin/1.18/dev' into 1.18/fabric/dev

Conflicts:
	build.gradle
	gradle.properties
	src/main/java/com/jozufozu/flywheel/Flywheel.java
	src/main/java/com/jozufozu/flywheel/backend/Loader.java
	src/main/java/com/jozufozu/flywheel/config/BooleanConfig.java
	src/main/java/com/jozufozu/flywheel/config/FlwCommands.java
	src/main/java/com/jozufozu/flywheel/config/FlwConfig.java
	src/main/java/com/jozufozu/flywheel/config/FlwPackets.java
	src/main/java/com/jozufozu/flywheel/core/model/BakedModelModel.java
	src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
	src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java
	src/main/java/com/jozufozu/flywheel/util/BakedQuadWrapper.java
	src/main/java/com/jozufozu/flywheel/util/BufferBuilderReader.java
	src/main/java/com/jozufozu/flywheel/util/RenderUtil.java
This commit is contained in:
PepperCode1 2021-12-24 22:55:44 -08:00
commit 0d79ee8638
249 changed files with 159131 additions and 2076 deletions

View file

@ -59,6 +59,7 @@ body:
label: Mod Version
description: The version of the mod you were using when the bug occured
options:
- "0.5.0"
- "0.4.2-rc"
- "0.4.1"
- "0.3.0"

View file

@ -40,11 +40,10 @@ repositories {
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${minecraft_version}"
// mappings loom.layered() {
// officialMojangMappings()
// parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
// }
mappings loom.officialMojangMappings() // TODO: waiting for parchment 1.18
mappings loom.layered() {
officialMojangMappings()
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
}
modImplementation "net.fabricmc:fabric-loader:${loader_version}"
// Fabric API
@ -53,8 +52,6 @@ dependencies {
implementation 'com.google.code.findbugs:jsr305:3.0.2'
modCompileOnly 'maven.modrinth:indium:1.0.2-alpha1+mc1.18'
modCompileOnly 'io.vram:frex-fabric-mc118:6.0.229'
//implementation 'org.joml:joml:1.10.1'
}
processResources {

View file

@ -2,16 +2,16 @@ org.gradle.jvmargs = -Xmx3G
org.gradle.daemon = false
# mod version info
mod_version = 0.4.2-rc
mod_version = 0.5.0
mc_update_version = 1.18
minecraft_version = 1.18.1
loader_version = 0.12.12
fabric_version = 0.44.0+1.18
fabric_version = 0.45.0+1.18
# build dependency versions
loom_version = 0.10-SNAPSHOT
cursegradle_version = 1.4.0
parchment_version = 2021.10.31
parchment_version = 2021.12.19
# curseforge info
projectId = 486392

View file

@ -5,8 +5,8 @@ import java.util.function.Supplier;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.model.BlockModel;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.ModelUtil;
import com.jozufozu.flywheel.util.Pair;
import com.jozufozu.flywheel.util.RenderUtil;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.core.Direction;
@ -27,7 +27,7 @@ public interface Material<D extends InstanceData> {
}
default Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir) {
return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir));
return getModel(partial, referenceState, dir, ModelUtil.rotateToFace(dir));
}
default Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<PoseStack> modelTransform) {

View file

@ -1,13 +1,8 @@
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.ModelTransformer;
public interface Batched<S> extends StructType<S> {
BatchingTransformer<S> getTransformer(Model model);
@Override
default Batched<S> asBatched() {
return this;
}
void transform(S d, ModelTransformer.Params b);
}

View file

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

View file

@ -14,8 +14,4 @@ public interface Instanced<S> extends StructType<S> {
ResourceLocation getProgramSpec();
@Override
default Instanced<S> asInstanced() {
return this;
}
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.layout.BufferLayout;
/**
* A StructType contains metadata for a specific instance struct that Flywheel can interface with.
@ -9,16 +9,13 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
public interface StructType<S> {
/**
* @return A new, zeroed instance of the struct.
* @return A new, zeroed instance of S.
*/
S create();
/**
* @return The format descriptor of the struct type.
* @return The layout of S when buffered.
*/
VertexFormat format();
BufferLayout getLayout();
Instanced<S> asInstanced();
Batched<S> asBatched();
}

View file

@ -0,0 +1,44 @@
package com.jozufozu.flywheel.api.vertex;
/**
* A read only view of a vertex buffer.
*
* <p>
* VertexList assumes nothing about the layout of the vertices. Implementations should feel free to return constants
* for values that are unused in their layout.
* </p>
* TODO: more flexible elements?
*/
public interface VertexList {
float getX(int index);
float getY(int index);
float getZ(int index);
byte getR(int index);
byte getG(int index);
byte getB(int index);
byte getA(int index);
float getU(int index);
float getV(int index);
int getLight(int index);
float getNX(int index);
float getNY(int index);
float getNZ(int index);
int getVertexCount();
default boolean isEmpty() {
return getVertexCount() == 0;
}
}

View file

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.api.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout;
/**
* A vertex type containing metadata about a specific vertex layout.
*/
public interface VertexType {
/**
* The layout of this type of vertex when buffered.
*/
BufferLayout getLayout();
/**
* Create a writer backed by the given ByteBuffer.
*
* <p>
* Implementors are encouraged to override the return type for ergonomics.
* </p>
*/
VertexWriter createWriter(ByteBuffer buffer);
/**
* Create a view of the given ByteBuffer as if it were already filled with vertices.
*
* <p>
* Implementors are encouraged to override the return type for ergonomics.
* </p>
*/
VertexList createReader(ByteBuffer buffer, int vertexCount);
default int getStride() {
return getLayout().getStride();
}
default int byteOffset(int vertexIndex) {
return getStride() * vertexIndex;
}
}

View file

@ -0,0 +1,15 @@
package com.jozufozu.flywheel.api.vertex;
public interface VertexWriter {
void writeVertex(VertexList list, int index);
void seekToVertex(int vertex);
VertexList intoReader();
default void writeVertexList(VertexList list) {
for (int i = 0; i < list.getVertexCount(); i++) {
this.writeVertex(list, i);
}
}
}

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.gl.attrib;
package com.jozufozu.flywheel.api.vertex;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -18,6 +18,7 @@ import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.config.FlwEngine;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import net.minecraft.client.Minecraft;
@ -29,19 +30,18 @@ public class Backend {
public static final Logger log = LogManager.getLogger(Backend.class);
protected static final Backend INSTANCE = new Backend();
public static Backend getInstance() {
return INSTANCE;
}
public final Loader loader;
private FlwEngine engine;
public GLCapabilities capabilities;
public GlCompat compat;
private boolean instancedArrays;
private boolean enabled;
public final Loader loader;
private final List<ShaderContext<?>> contexts = new ArrayList<>();
private final Map<ResourceLocation, StructType<?>> materialRegistry = new HashMap<>();
private final Map<ResourceLocation, ProgramSpec> programSpecRegistry = new HashMap<>();
@ -57,15 +57,11 @@ public class Backend {
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
*/
public String getBackendDescriptor() {
if (canUseInstancing()) {
return "GL33 Instanced Arrays";
}
return engine.getProperName();
}
if (canUseVBOs()) {
return "VBOs";
}
return "Disabled";
public FlwEngine getEngine() {
return engine;
}
/**
@ -97,7 +93,7 @@ public class Backend {
}
materialRegistry.put(name, spec);
log.debug("registered material '" + name + "' with instance size " + spec.format().getStride());
log.debug("registered material '" + name + "' with instance size " + spec.getLayout().getStride());
return spec;
}
@ -106,40 +102,22 @@ public class Backend {
return programSpecRegistry.get(name);
}
public boolean available() {
return canUseVBOs();
}
public boolean canUseInstancing() {
return enabled && instancedArrays;
}
public boolean canUseVBOs() {
return enabled && gl20();
}
public boolean gl33() {
return capabilities.OpenGL33;
}
public boolean gl20() {
return capabilities.OpenGL20;
}
public void refresh() {
OptifineHandler.refresh();
boolean usingShaders = OptifineHandler.usingShaders();
capabilities = GL.createCapabilities();
compat = new GlCompat(capabilities);
instancedArrays = compat.instancedArraysSupported();
engine = FlwConfig.get()
.getEngine();
enabled = FlwConfig.get()
.enabled() && !OptifineHandler.usingShaders();
}
public boolean canUseInstancing(@Nullable Level world) {
return canUseInstancing() && isFlywheelWorld(world);
enabled = switch (engine) {
case OFF -> false;
case BATCHING -> true;
case INSTANCING -> !usingShaders && compat.instancedArraysSupported();
};
}
public Collection<StructType<?>> allMaterials() {
@ -154,6 +132,14 @@ public class Backend {
return contexts;
}
public static boolean isOn() {
return getInstance().enabled;
}
public static boolean canUseInstancing(@Nullable Level world) {
return isOn() && isFlywheelWorld(world);
}
/**
* Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it.
*/
@ -178,7 +164,7 @@ public class Backend {
/**
* INTERNAL USE ONLY
*/
public void _clearContexts() {
void _clearContexts() {
GameStateRegistry.clear();
programSpecRegistry.clear();
contexts.forEach(ShaderContext::delete);

View file

@ -18,7 +18,7 @@ import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.event.GatherContextEvent;
import com.jozufozu.flywheel.fabric.event.FlywheelEvents;
import com.jozufozu.flywheel.util.ResourceUtil;
import com.jozufozu.flywheel.util.StreamUtil;
import com.jozufozu.flywheel.util.StringUtil;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
@ -61,36 +61,34 @@ public class Loader {
public void onResourceManagerReload(ResourceManager manager) {
backend.refresh();
if (backend.gl20()) {
shouldCrash = false;
backend._clearContexts();
shouldCrash = false;
backend._clearContexts();
Resolver.INSTANCE.invalidate();
FlywheelEvents.GATHER_CONTEXT.invoker()
.handleEvent(new GatherContextEvent(backend, firstLoad));
Resolver.INSTANCE.invalidate();
FlywheelEvents.GATHER_CONTEXT.invoker()
.handleEvent(new GatherContextEvent(backend, firstLoad));
ShaderSources sources = new ShaderSources(manager);
ShaderSources sources = new ShaderSources(manager);
loadProgramSpecs(manager);
loadProgramSpecs(manager);
Resolver.INSTANCE.resolve(sources);
Resolver.INSTANCE.resolve(sources);
for (ShaderContext<?> context : backend.allContexts()) {
context.load();
}
for (ShaderContext<?> context : backend.allContexts()) {
context.load();
}
if (shouldCrash) {
throw new ShaderLoadingException("Could not load all shaders, see log for details");
}
if (shouldCrash) {
throw new ShaderLoadingException("Could not load all shaders, see log for details");
}
Backend.log.info("Loaded all shader programs.");
Backend.log.info("Loaded all shader programs.");
ClientLevel world = Minecraft.getInstance().level;
if (Backend.isFlywheelWorld(world)) {
// TODO: looks like it might be good to have another event here
InstancedRenderDispatcher.resetInstanceWorld(world);
CrumblingRenderer.reset();
}
ClientLevel world = Minecraft.getInstance().level;
if (Backend.isFlywheelWorld(world)) {
// TODO: looks like it might be good to have another event here
InstancedRenderDispatcher.resetInstanceWorld(world);
CrumblingRenderer.reset();
}
firstLoad = false;
@ -103,7 +101,7 @@ public class Loader {
try {
Resource file = manager.getResource(location);
String s = StreamUtil.readToString(file.getInputStream());
String s = StringUtil.readToString(file.getInputStream());
ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json");

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.backend.gl;
import com.jozufozu.flywheel.mixin.BufferUploaderAccessor;
import com.jozufozu.flywheel.util.AttribUtil;
import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject {
@ -8,14 +10,21 @@ public class GlVertexArray extends GlObject {
}
public void bind() {
GlStateManager._glBindVertexArray(handle());
int handle = handle();
GlStateManager._glBindVertexArray(handle);
BufferUploaderAccessor.flywheel$setLastVAO(handle);
}
public static void unbind() {
GlStateManager._glBindVertexArray(0);
BufferUploaderAccessor.flywheel$setLastVAO(0);
}
protected void deleteInternal(int handle) {
GlStateManager._glDeleteVertexArrays(handle);
}
public void enableArrays(int count) {
AttribUtil.enableArrays(count);
}
}

View file

@ -1,21 +0,0 @@
package com.jozufozu.flywheel.backend.gl.attrib;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
public class CommonAttributes {
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlNumericType.FLOAT, 4);
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlNumericType.FLOAT, 3);
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlNumericType.FLOAT, 2);
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlNumericType.FLOAT, 1);
public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlNumericType.FLOAT, 4);
public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlNumericType.BYTE, 3, true);
public static final VertexAttribSpec UV = new VertexAttribSpec(GlNumericType.FLOAT, 2);
public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlNumericType.UBYTE, 4, true);
public static final VertexAttribSpec RGB = new VertexAttribSpec(GlNumericType.UBYTE, 3, true);
public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlNumericType.UBYTE, 2, true);
public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlNumericType.BYTE, 1, true);
}

View file

@ -1,61 +0,0 @@
package com.jozufozu.flywheel.backend.gl.attrib;
import java.util.ArrayList;
import java.util.Collections;
public class VertexFormat {
private final ArrayList<IAttribSpec> allAttributes;
private final int numAttributes;
private final int stride;
public VertexFormat(ArrayList<IAttribSpec> allAttributes) {
this.allAttributes = allAttributes;
int numAttributes = 0, stride = 0;
for (IAttribSpec spec : allAttributes) {
numAttributes += spec.getAttributeCount();
stride += spec.getSize();
}
this.numAttributes = numAttributes;
this.stride = stride;
}
public int getAttributeCount() {
return numAttributes;
}
public int getStride() {
return stride;
}
public void vertexAttribPointers(int index) {
int offset = 0;
for (IAttribSpec spec : this.allAttributes) {
spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount();
offset += spec.getSize();
}
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final ArrayList<IAttribSpec> allAttributes = new ArrayList<>();
public Builder() {
}
public Builder addAttributes(IAttribSpec... attributes) {
Collections.addAll(allAttributes, attributes);
return this;
}
public VertexFormat build() {
return new VertexFormat(allAttributes);
}
}
}

View file

@ -9,13 +9,6 @@ import com.jozufozu.flywheel.backend.gl.GlObject;
public abstract class GlBuffer extends GlObject {
protected final GlBufferType type;
public GlBuffer(GlBufferType type) {
_create();
this.type = type;
}
/**
* Request a Persistent mapped buffer.
*
@ -35,10 +28,63 @@ public abstract class GlBuffer extends GlObject {
}
}
public GlBufferType getBufferTarget() {
return type;
protected final GlBufferType type;
/**
* The size (in bytes) of the buffer on the GPU.
*/
protected long capacity;
/**
* How much extra room to give the buffer when we reallocate.
*/
protected int growthMargin;
public GlBuffer(GlBufferType type) {
_create();
this.type = type;
}
public void setGrowthMargin(int growthMargin) {
this.growthMargin = growthMargin;
}
public long getCapacity() {
return capacity;
}
public MappedBuffer getBuffer() {
return getBuffer(0, capacity);
}
public abstract MappedBuffer getBuffer(long offset, long length);
/**
* Ensure that the buffer has at least enough room to store size bytes.
*
* @return true if the buffer grew.
*/
public boolean ensureCapacity(long size) {
if (size > capacity) {
capacity = size + growthMargin;
alloc(capacity);
return true;
}
return false;
}
/**
* Call this after all draw calls using this buffer are complete.
*/
public void doneForThisFrame() {
}
protected abstract void alloc(long size);
public abstract void upload(ByteBuffer directBuffer);
public void bind() {
type.bind(handle());
}
@ -47,16 +93,6 @@ public abstract class GlBuffer extends GlObject {
type.unbind();
}
public void doneForThisFrame() {
}
public abstract void alloc(long size);
public abstract void upload(ByteBuffer directBuffer);
public abstract MappedBuffer getBuffer(int offset, int length);
protected void _create() {
setHandle(GL20.glGenBuffers());
}

View file

@ -8,6 +8,7 @@ import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import com.jozufozu.flywheel.mixin.BufferUploaderAccessor;
import com.mojang.blaze3d.platform.GlStateManager;
public enum GlBufferType {
@ -34,9 +35,19 @@ public enum GlBufferType {
public void bind(int buffer) {
GlStateManager._glBindBuffer(glEnum, buffer);
switch (this.glEnum) {
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(buffer);
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(buffer);
}
}
public void unbind() {
GlStateManager._glBindBuffer(glEnum, 0);
switch (this.glEnum) {
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(0);
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(0);
}
}
}

View file

@ -21,7 +21,7 @@ public class MappedGlBuffer extends GlBuffer implements Mappable {
this.usage = usage;
}
public void alloc(long size) {
protected void alloc(long size) {
GL15.glBufferData(type.glEnum, size, usage.glEnum);
}
@ -29,7 +29,7 @@ public class MappedGlBuffer extends GlBuffer implements Mappable {
GL15.glBufferData(type.glEnum, directBuffer, usage.glEnum);
}
public MappedBuffer getBuffer(int offset, int length) {
public MappedBuffer getBuffer(long offset, long length) {
ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, offset, length, GL30.GL_MAP_WRITE_BIT);
if (byteBuffer == null) {

View file

@ -35,7 +35,7 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable {
}
@Override
public void alloc(long size) {
protected void alloc(long size) {
this.size = size;
if (buffer != null) {
@ -71,11 +71,11 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable {
}
@Override
public MappedBuffer getBuffer(int offset, int length) {
public MappedBuffer getBuffer(long offset, long length) {
fence.waitSync();
buffer.position(offset);
buffer.position((int) offset);
return buffer;
}

View file

@ -4,7 +4,6 @@ import static org.lwjgl.opengl.GL20.glDeleteProgram;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL20.glUseProgram;
import java.nio.FloatBuffer;
@ -12,6 +11,8 @@ import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.mixin.ShaderInstanceAccessor;
import com.mojang.blaze3d.shaders.ProgramManager;
import com.mojang.math.Matrix4f;
import net.minecraft.resources.ResourceLocation;
@ -28,11 +29,14 @@ public abstract class GlProgram extends GlObject {
}
public void bind() {
glUseProgram(handle());
int handle = handle();
ProgramManager.glUseProgram(handle);
ShaderInstanceAccessor.flywheel$setLastProgramId(handle);
}
public void unbind() {
glUseProgram(0);
ProgramManager.glUseProgram(0);
ShaderInstanceAccessor.flywheel$setLastProgramId(0);
}
/**

View file

@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.instance.IInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.core.materials.FlatLit;
import com.jozufozu.flywheel.light.ImmutableBox;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.ListenerStatus;

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.function.Supplier;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
@ -10,14 +11,14 @@ import com.jozufozu.flywheel.core.model.Model;
public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
protected final StructType<D> type;
protected final Supplier<D> factory;
protected final Model modelData;
protected final ArrayList<D> data = new ArrayList<>();
protected boolean anyToRemove;
protected AbstractInstancer(StructType<D> type, Model modelData) {
this.type = type;
protected AbstractInstancer(Supplier<D> factory, Model modelData) {
this.factory = factory;
this.modelData = modelData;
}
@ -26,7 +27,7 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
*/
@Override
public D createInstance() {
return _add(type.create());
return _add(factory.get());
}
/**
@ -58,17 +59,16 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
anyToRemove = true;
}
protected BitSet getDirtyBitSet() {
final int size = data.size();
final BitSet dirtySet = new BitSet(size);
public int getModelVertexCount() {
return modelData.vertexCount();
}
for (int i = 0; i < size; i++) {
D element = data.get(i);
if (element.checkDirtyAndClear()) {
dirtySet.set(i);
}
}
return dirtySet;
public int numInstances() {
return data.size();
}
public int getTotalVertexCount() {
return getModelVertexCount() * numInstances();
}
protected void removeDeletedInstances() {
@ -115,4 +115,9 @@ public abstract class AbstractInstancer<D extends InstanceData> implements Insta
return instanceData;
}
@Override
public String toString() {
return "Instancer[" + modelData + ']';
}
}

View file

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

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -75,7 +76,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
* Queued updates are processed.
* </p>
*/
public void tick(double cameraX, double cameraY, double cameraZ) {
public void tick(TaskEngine taskEngine, double cameraX, double cameraY, double cameraZ) {
tick++;
processQueuedUpdates();
@ -84,26 +85,40 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
int cY = (int) cameraY;
int cZ = (int) cameraZ;
if (tickableInstances.size() > 0) {
tickableInstances.object2ObjectEntrySet().parallelStream().forEach(e -> {
ITickableInstance instance = e.getValue();
if (!instance.decreaseTickRateWithDistance()) {
instance.tick();
return;
ArrayList<ITickableInstance> instances = new ArrayList<>(tickableInstances.values());
int incr = 500;
int size = instances.size();
int start = 0;
while (start < size) {
int end = Math.min(start + incr, size);
List<ITickableInstance> sub = instances.subList(start, end);
taskEngine.submit(() -> {
for (ITickableInstance instance : sub) {
tickInstance(cX, cY, cZ, instance);
}
BlockPos pos = instance.getWorldPosition();
int dX = pos.getX() - cX;
int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ;
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick();
});
start += incr;
}
}
public void beginFrame(Camera info) {
private void tickInstance(int cX, int cY, int cZ, ITickableInstance instance) {
if (!instance.decreaseTickRateWithDistance()) {
instance.tick();
return;
}
BlockPos pos = instance.getWorldPosition();
int dX = pos.getX() - cX;
int dY = pos.getY() - cY;
int dZ = pos.getZ() - cZ;
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick();
}
public void beginFrame(TaskEngine taskEngine, Camera info) {
frame++;
processQueuedAdditions();
@ -117,20 +132,27 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
int cY = (int) info.getPosition().y;
int cZ = (int) info.getPosition().z;
if (dynamicInstances.size() > 0) {
dynamicInstances.object2ObjectEntrySet()
.parallelStream()
.forEach(e -> {
IDynamicInstance dyn = e.getValue();
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame();
});
ArrayList<IDynamicInstance> instances = new ArrayList<>(dynamicInstances.values());
int incr = 500;
int size = instances.size();
int start = 0;
while (start < size) {
int end = Math.min(start + incr, size);
List<IDynamicInstance> sub = instances.subList(start, end);
taskEngine.submit(() -> {
for (IDynamicInstance dyn : sub) {
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
dyn.beginFrame();
}
});
start += incr;
}
}
public void add(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
if (canInstance(obj)) {
addInternal(obj);
@ -138,8 +160,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
}
public void queueAdd(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
synchronized (queuedAdditions) {
queuedAdditions.add(obj);
@ -147,8 +168,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
}
public void queueUpdate(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
synchronized (queuedUpdates) {
queuedUpdates.add(obj);
}
@ -166,8 +186,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
* @param obj the object to update.
*/
public void update(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
if (canInstance(obj)) {
AbstractInstance instance = getInstance(obj);
@ -188,8 +207,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
}
public void remove(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
if (canInstance(obj)) {
AbstractInstance instance = getInstance(obj);
@ -206,8 +224,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
@Nullable
protected <I extends T> AbstractInstance getInstance(I obj) {
if (!Backend.getInstance()
.canUseInstancing()) return null;
if (!Backend.isOn()) return null;
return instances.get(obj);
}
@ -266,8 +283,7 @@ public abstract class InstanceManager<T> implements InstancingEngine.OriginShift
}
protected void addInternal(T obj) {
if (!Backend.getInstance()
.canUseInstancing()) return;
if (!Backend.isOn()) return;
AbstractInstance instance = instances.get(obj);

View file

@ -2,10 +2,12 @@ package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.config.FlwEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.BeginFrameEvent;
@ -14,6 +16,8 @@ import com.jozufozu.flywheel.event.RenderLayerEvent;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
@ -27,14 +31,19 @@ public class InstanceWorld {
protected final InstanceManager<Entity> entityInstanceManager;
protected final InstanceManager<BlockEntity> tileEntityInstanceManager;
public InstanceWorld() {
protected final ParallelTaskEngine taskEngine;
// TODO: finish impl
if (false) {
engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(engine);
tileEntityInstanceManager = new TileInstanceManager(engine);
} else {
public InstanceWorld(LevelAccessor levelAccessor) {
Level world = (Level) levelAccessor;
this.taskEngine = new ParallelTaskEngine("Flywheel " + world.dimension().location());
this.taskEngine.startWorkers();
FlwEngine engine = Backend.getInstance()
.getEngine();
switch (engine) {
case INSTANCING -> {
InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD)
.build();
@ -43,7 +52,14 @@ public class InstanceWorld {
manager.addListener(entityInstanceManager);
manager.addListener(tileEntityInstanceManager);
engine = manager;
this.engine = manager;
}
case BATCHING -> {
this.engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(this.engine);
tileEntityInstanceManager = new TileInstanceManager(this.engine);
}
default -> throw new IllegalArgumentException("Unknown engine type");
}
}
@ -59,6 +75,7 @@ public class InstanceWorld {
* Free all acquired resources and invalidate this instance world.
*/
public void delete() {
this.taskEngine.stopWorkers();
engine.delete();
entityInstanceManager.detachLightListeners();
tileEntityInstanceManager.detachLightListeners();
@ -75,8 +92,10 @@ public class InstanceWorld {
public void beginFrame(BeginFrameEvent event) {
engine.beginFrame(event.getInfo());
tileEntityInstanceManager.beginFrame(event.getInfo());
entityInstanceManager.beginFrame(event.getInfo());
taskEngine.syncPoint();
tileEntityInstanceManager.beginFrame(taskEngine, event.getInfo());
entityInstanceManager.beginFrame(taskEngine, event.getInfo());
}
/**
@ -91,15 +110,19 @@ public class InstanceWorld {
if (renderViewEntity == null) return;
tileEntityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
entityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
tileEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
}
/**
* Draw the given layer.
*/
public void renderLayer(RenderLayerEvent event) {
engine.render(event, event.buffers.bufferSource());
taskEngine.syncPoint();
event.stack.pushPose();
event.stack.translate(-event.camX, -event.camY, -event.camZ);
engine.render(taskEngine, event);
event.stack.popPose();
}
/**

View file

@ -15,7 +15,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld());
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>(InstanceWorld::new);
/**
* Call this when you want to manually run {@link AbstractInstance#update()}.
@ -66,16 +66,14 @@ public class InstancedRenderDispatcher {
if (event.layer == null) return;
ClientLevel world = event.getWorld();
if (!Backend.getInstance()
.canUseInstancing(world)) return;
if (!Backend.canUseInstancing(world)) return;
instanceWorlds.get(world).renderLayer(event);
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld();
if (Backend.getInstance()
.canUseInstancing() && world != null) {
if (Backend.isOn() && world != null) {
resetInstanceWorld(world);
}
}

View file

@ -0,0 +1,178 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup;
import net.minecraft.util.Mth;
// https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java
public class ParallelTaskEngine implements TaskEngine {
private static final Logger LOGGER = LogManager.getLogger("BatchExecutor");
private final AtomicBoolean running = new AtomicBoolean(false);
private final WaitGroup wg = new WaitGroup();
private final Deque<Runnable> jobQueue = new ConcurrentLinkedDeque<>();
private final List<Thread> threads = new ArrayList<>();
private final Object jobNotifier = new Object();
private final int threadCount;
private final String name;
public ParallelTaskEngine(String name) {
this.name = name;
threadCount = getOptimalThreadCount();
}
/**
* Spawns a number of work-stealing threads to process results in the build queue. If the builder is already
* running, this method does nothing and exits.
*/
public void startWorkers() {
if (this.running.getAndSet(true)) {
return;
}
if (!this.threads.isEmpty()) {
throw new IllegalStateException("Threads are still alive while in the STOPPED state");
}
for (int i = 0; i < this.threadCount; i++) {
Thread thread = new Thread(new WorkerRunnable(), name + " " + i);
thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2));
thread.start();
this.threads.add(thread);
}
LOGGER.info("Started {} worker threads", this.threads.size());
}
public void stopWorkers() {
if (!this.running.getAndSet(false)) {
return;
}
if (this.threads.isEmpty()) {
throw new IllegalStateException("No threads are alive but the executor is in the RUNNING state");
}
synchronized (this.jobNotifier) {
this.jobNotifier.notifyAll();
}
try {
for (Thread thread : this.threads) {
thread.join();
}
} catch (InterruptedException ignored) {
}
this.threads.clear();
this.jobQueue.clear();
}
/**
* Submit a task to the pool.
*/
@Override
public void submit(@NotNull Runnable command) {
this.jobQueue.add(command);
this.wg.add(1);
synchronized (this.jobNotifier) {
this.jobNotifier.notify();
}
}
/**
* Wait for all running jobs to finish.
*/
@Override
public void syncPoint() {
Runnable job;
// Finish everyone else's work...
while ((job = this.jobQueue.pollLast()) != null) {
processTask(job);
}
// and wait for any stragglers.
try {
this.wg.await();
} catch (InterruptedException ignored) {
}
}
@Nullable
private Runnable getNextTask() {
Runnable job = this.jobQueue.pollFirst();
if (job == null) {
synchronized (ParallelTaskEngine.this.jobNotifier) {
try {
ParallelTaskEngine.this.jobNotifier.wait();
} catch (InterruptedException ignored) {
}
}
}
return job;
}
private void processTask(Runnable job) {
try {
job.run();
} catch (Exception e) {
Flywheel.log.error(e);
} finally {
ParallelTaskEngine.this.wg.done();
}
}
/**
* Returns the "optimal" number of threads to be used for chunk build tasks. This will always return at least one
* thread.
*/
private static int getOptimalThreadCount() {
return Mth.clamp(Math.max(getMaxThreadCount() / 3, getMaxThreadCount() - 6), 1, 10);
}
private static int getMaxThreadCount() {
return Runtime.getRuntime().availableProcessors();
}
private class WorkerRunnable implements Runnable {
private final AtomicBoolean running = ParallelTaskEngine.this.running;
@Override
public void run() {
// Run until the chunk builder shuts down
while (this.running.get()) {
Runnable job = ParallelTaskEngine.this.getNextTask();
if (job == null) {
continue;
}
ParallelTaskEngine.this.processTask(job);
}
}
}
}

View file

@ -3,16 +3,15 @@ package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
public interface RenderDispatcher {
/**
* Render every model for every material.
*
* @param event Context for rendering.
* @param buffers The buffer source for which batched rendering should happen.
* @param taskEngine
* @param event Context for rendering.
*/
void render(RenderLayerEvent event, MultiBufferSource buffers);
void render(TaskEngine taskEngine, RenderLayerEvent event);
/**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.
@ -21,7 +20,5 @@ public interface RenderDispatcher {
*/
void beginFrame(Camera info);
default void delete() {
}
void delete();
}

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.backend.instancing;
import org.jetbrains.annotations.NotNull;
public class SerialTaskEngine implements TaskEngine {
public static final SerialTaskEngine INSTANCE = new SerialTaskEngine();
private SerialTaskEngine() {
}
@Override
public void submit(@NotNull Runnable command) {
command.run();
}
@Override
public void syncPoint() {
// noop
}
}

View file

@ -0,0 +1,79 @@
package com.jozufozu.flywheel.backend.instancing;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.model.BufferBuilderHack;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
public class SuperBufferSource {
protected final Map<RenderType, DrawBuffer> buffers = new HashMap<>();
private final BufferBuilder scratch;
public SuperBufferSource() {
scratch = new BufferBuilder(8);
((BufferBuilderHack) scratch).flywheel$freeBuffer();
}
public DirectVertexConsumer getBuffer(RenderType renderType, int vertexCount) {
return buffers.computeIfAbsent(renderType, DrawBuffer::new)
.begin(vertexCount);
}
public void endBatch() {
// TODO: when/if this causes trouble with shaders, try to inject our BufferBuilders
// into the RenderBuffers from context.
BufferBuilderHack hack = (BufferBuilderHack) scratch;
for (Map.Entry<RenderType, DrawBuffer> entry : buffers.entrySet()) {
DrawBuffer builder = entry.getValue();
if (builder.expectedVertices > 0) {
RenderType type = entry.getKey();
hack.flywheel$hackBegin(builder.backingBuffer, type.format(), builder.expectedVertices);
type.end(scratch, 0, 0, 0);
builder.expectedVertices = 0;
}
}
}
private static class DrawBuffer {
private final RenderType type;
private ByteBuffer backingBuffer;
private int expectedVertices;
public DrawBuffer(RenderType type) {
this.type = type;
}
public DirectVertexConsumer begin(int vertexCount) {
this.expectedVertices = vertexCount;
VertexFormat format = type.format();
int byteSize = format.getVertexSize() * vertexCount;
if (backingBuffer == null) {
backingBuffer = MemoryTracker.create(byteSize);
} if (byteSize > backingBuffer.capacity()) {
backingBuffer = MemoryTracker.resize(backingBuffer, byteSize);
}
return new DirectVertexConsumer(backingBuffer, format, vertexCount);
}
}
}

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.backend.instancing;
import org.jetbrains.annotations.NotNull;
public interface TaskEngine {
void submit(@NotNull Runnable command);
/**
* Wait for all running jobs to finish.
*/
void syncPoint();
}

View file

@ -7,7 +7,7 @@ import java.util.function.Supplier;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.core.model.Model;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
@ -15,9 +15,9 @@ import com.mojang.blaze3d.vertex.VertexConsumer;
public class BatchedMaterial<D extends InstanceData> implements Material<D> {
protected final Map<Object, CPUInstancer<D>> models;
private final StructType<D> type;
private final Batched<D> type;
public BatchedMaterial(StructType<D> type) {
public BatchedMaterial(Batched<D> type) {
this.type = type;
this.models = new HashMap<>();
@ -28,8 +28,9 @@ public class BatchedMaterial<D extends InstanceData> implements Material<D> {
return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get()));
}
public void render(PoseStack stack, VertexConsumer buffer) {
public void setupAndRenderInto(PoseStack stack, VertexConsumer buffer) {
for (CPUInstancer<D> instancer : models.values()) {
instancer.setup();
instancer.drawAll(stack, buffer);
}
}

View file

@ -5,18 +5,20 @@ import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.DirectVertexConsumer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
public class BatchedMaterialGroup implements MaterialGroup {
protected final RenderType state;
private final Map<StructType<? extends InstanceData>, BatchedMaterial<?>> materials = new HashMap<>();
private final Map<Batched<? extends InstanceData>, BatchedMaterial<?>> materials = new HashMap<>();
public BatchedMaterialGroup(RenderType state) {
this.state = state;
@ -24,15 +26,40 @@ public class BatchedMaterialGroup implements MaterialGroup {
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> BatchedMaterial<D> material(StructType<D> spec) {
return (BatchedMaterial<D>) materials.computeIfAbsent(spec, BatchedMaterial::new);
public <D extends InstanceData> BatchedMaterial<D> material(StructType<D> type) {
if (type instanceof Batched<D> batched) {
return (BatchedMaterial<D>) materials.computeIfAbsent(batched, BatchedMaterial::new);
} else {
throw new ClassCastException("Cannot use type '" + type + "' with CPU instancing.");
}
}
public void render(PoseStack stack, MultiBufferSource source) {
VertexConsumer buffer = source.getBuffer(state);
public void render(PoseStack stack, SuperBufferSource source, TaskEngine pool) {
for (BatchedMaterial<?> value : materials.values()) {
value.render(stack, buffer);
int vertexCount = 0;
for (BatchedMaterial<?> material : materials.values()) {
for (CPUInstancer<?> instancer : material.models.values()) {
instancer.setup();
vertexCount += instancer.getTotalVertexCount();
}
}
DirectVertexConsumer consumer = source.getBuffer(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()) {
if (consumer.hasOverlay()) {
instancer.sbb.context.fullNormalTransform = false;
instancer.sbb.context.outputColorDiffuse = false;
} else {
instancer.sbb.context.fullNormalTransform = false;
instancer.sbb.context.outputColorDiffuse = true;
}
instancer.submitTasks(stack, pool, consumer);
}
}
}

View file

@ -6,8 +6,15 @@ import java.util.Map;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.SuperBufferSource;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
@ -17,15 +24,15 @@ import net.minecraft.core.Vec3i;
public class BatchingEngine implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
private final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
private final SuperBufferSource superBufferSource = new SuperBufferSource();
public BatchingEngine() {
this.layers = new EnumMap<>(RenderLayer.class);
for (RenderLayer value : RenderLayer.values()) {
layers.put(value, new HashMap<>());
}
}
@Override
@ -35,20 +42,42 @@ public class BatchingEngine implements Engine {
@Override
public Vec3i getOriginCoordinate() {
return originCoordinate;
return BlockPos.ZERO;
}
@Override
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
for (Map.Entry<RenderType, BatchedMaterialGroup> entry : layers.get(event.getLayer()).entrySet()) {
BatchedMaterialGroup group = entry.getValue();
public void render(TaskEngine taskEngine, RenderLayerEvent event) {
group.render(event.stack, buffers);
Map<RenderType, BatchedMaterialGroup> groups = layers.get(event.getLayer());
for (BatchedMaterialGroup group : groups.values()) {
group.render(event.stack, superBufferSource, taskEngine);
}
taskEngine.syncPoint();
// FIXME: this probably breaks some vanilla stuff but it works much better for flywheel
Matrix4f mat = new Matrix4f();
mat.setIdentity();
if (event.getWorld().effects().constantAmbientLight()) {
Lighting.setupNetherLevel(mat);
} else {
Lighting.setupLevel(mat);
}
superBufferSource.endBatch();
}
@Override
public void delete() {
}
@Override
public void beginFrame(Camera info) {
}
@Override
public String getName() {
return "Batching";
}
}

View file

@ -1,46 +1,87 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.api.struct.StructType;
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.model.Model;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.util.Color;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
public class CPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private final BatchingTransformer<D> renderer;
private final Batched<D> batchingType;
public CPUInstancer(StructType<D> type, Model modelData) {
super(type, modelData);
final ModelTransformer sbb;
renderer = type.asBatched()
.getTransformer(modelData);
public CPUInstancer(Batched<D> type, Model modelData) {
super(type::create, modelData);
batchingType = type;
sbb = new ModelTransformer(modelData);
modelData.configure(sbb.context);
}
void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) {
int instances = numInstances();
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();
// Color color = Color.generateFromLong(from);
for (D d : data.subList(from, to)) {
params.loadDefault();
batchingType.transform(d, params);
//params.color(color.getRGB());
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);
}
}
void setup() {
if (anyToRemove) {
data.removeIf(InstanceData::isRemoved);
anyToRemove = false;
}
if (false) {
this.sbb.context.outputColorDiffuse = false;
this.sbb.context.fullNormalTransform = false;
}
}
@Override
public void notifyDirty() {
// noop
}
public void drawAll(PoseStack stack, VertexConsumer buffer) {
if (renderer == null) {
return;
}
renderSetup();
for (D d : data) {
renderer.draw(d, stack, buffer);
}
}
protected void renderSetup() {
if (anyToRemove) {
removeDeletedInstances();
}
anyToRemove = false;
}
}

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.backend.instancing.batching;
// https://stackoverflow.com/questions/29655531
public class WaitGroup {
private int jobs = 0;
public synchronized void add(int i) {
jobs += i;
}
public synchronized void done() {
if (--jobs == 0) {
notifyAll();
}
}
public synchronized void await() throws InterruptedException {
while (jobs > 0) {
wait();
}
}
}

View file

@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.MovingListener;

View file

@ -5,6 +5,7 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;

View file

@ -1,43 +1,42 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.BitSet;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.layout.BufferLayout;
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.error.GlError;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.backend.model.IBufferedModel;
import com.jozufozu.flywheel.backend.model.BufferedModel;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.AttribUtil;
public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private final ModelAllocator modelAllocator;
private final VertexFormat instanceFormat;
private final BufferLayout instanceFormat;
private final Instanced<D> instancedType;
private IBufferedModel model;
private BufferedModel model;
private GlVertexArray vao;
private GlBuffer instanceVBO;
private int glBufferSize = -1;
private int glInstanceCount = 0;
private boolean deleted;
private boolean initialized;
protected boolean anyToUpdate;
public GPUInstancer(StructType<D> type, Model model, ModelAllocator modelAllocator) {
super(type, model);
public GPUInstancer(Instanced<D> type, Model model, ModelAllocator modelAllocator) {
super(type::create, model);
this.modelAllocator = modelAllocator;
this.instanceFormat = type.format();
this.instanceFormat = type.getLayout();
instancedType = type;
}
@Override
@ -49,14 +48,11 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
if (invalid()) return;
vao.bind();
GlError.pollAndThrow(() -> modelData.name() + "_bind");
renderSetup();
GlError.pollAndThrow(() -> modelData.name() + "_setup");
if (glInstanceCount > 0) {
model.drawInstances(glInstanceCount);
GlError.pollAndThrow(() -> modelData.name() + "_draw");
}
// persistent mapping sync point
@ -83,7 +79,8 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
vao.bind();
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
instanceVBO.setGrowthMargin(instanceFormat.getStride() * 16);
vao.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
}
public boolean isInitialized() {
@ -135,10 +132,10 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private void clearBufferTail() {
int size = data.size();
final int offset = size * instanceFormat.getStride();
final int length = glBufferSize - offset;
final long length = instanceVBO.getCapacity() - offset;
if (length > 0) {
try (MappedBuffer buf = instanceVBO.getBuffer(offset, length)) {
buf.putByteArray(new byte[length]);
MemoryUtil.memSet(MemoryUtil.memAddress(buf.unwrap()), 0, length);
} catch (Exception e) {
Flywheel.log.error("Error clearing buffer tail:", e);
}
@ -150,31 +147,19 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
if (size <= 0) return;
final int stride = instanceFormat.getStride();
final BitSet dirtySet = getDirtyBitSet();
try (MappedBuffer mapped = instanceVBO.getBuffer()) {
if (dirtySet.isEmpty()) return;
final StructWriter<D> writer = instancedType.getWriter(mapped);
final int firstDirty = dirtySet.nextSetBit(0);
final int lastDirty = dirtySet.previousSetBit(size);
final int offset = firstDirty * stride;
final int length = (1 + lastDirty - firstDirty) * stride;
if (length > 0) {
try (MappedBuffer mapped = instanceVBO.getBuffer(offset, length)) {
StructWriter<D> writer = type.asInstanced()
.getWriter(mapped);
dirtySet.stream()
.forEach(i -> {
writer.seek(i);
writer.write(data.get(i));
});
} catch (Exception e) {
Flywheel.log.error("Error updating GPUInstancer:", e);
for (int i = 0; i < size; i++) {
final D element = data.get(i);
if (element.checkDirtyAndClear()) {
writer.seek(i);
writer.write(element);
}
}
} catch (Exception e) {
Flywheel.log.error("Error updating GPUInstancer:", e);
}
}
@ -182,13 +167,10 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
int size = this.data.size();
int stride = instanceFormat.getStride();
int requiredSize = size * stride;
if (requiredSize > glBufferSize) {
glBufferSize = requiredSize + stride * 16;
instanceVBO.alloc(glBufferSize);
if (instanceVBO.ensureCapacity(requiredSize)) {
try (MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize)) {
StructWriter<D> writer = type.asInstanced()
.getWriter(buffer);
try (MappedBuffer buffer = instanceVBO.getBuffer()) {
StructWriter<D> writer = instancedType.getWriter(buffer);
for (D datum : data) {
writer.write(datum);
}

View file

@ -8,7 +8,7 @@ import com.google.common.cache.CacheBuilder;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.backend.model.ImmediateAllocator;
@ -25,15 +25,15 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
final ModelAllocator allocator;
protected final Cache<Object, GPUInstancer<D>> models;
protected final StructType<D> type;
protected final Instanced<D> type;
public InstancedMaterial(StructType<D> spec) {
this.type = spec;
public InstancedMaterial(Instanced<D> type) {
this.type = type;
if (Backend.getInstance().compat.onAMDWindows()) {
allocator = ImmediateAllocator.INSTANCE;
} else {
allocator = new ModelPool(Formats.UNLIT_MODEL, 64);
allocator = new ModelPool(Formats.POS_TEX_NORMAL, 64);
}
this.models = CacheBuilder.newBuilder()
.removalListener(notification -> {

View file

@ -6,10 +6,11 @@ import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.model.ModelPool;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.util.TextureBinder;
import com.jozufozu.flywheel.util.Textures;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
@ -25,7 +26,7 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
protected final InstancingEngine<P> owner;
protected final RenderType type;
private final Map<StructType<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
private final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
public InstancedMaterialGroup(InstancingEngine<P> owner, RenderType type) {
this.owner = owner;
@ -34,19 +35,23 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> InstancedMaterial<D> material(StructType<D> spec) {
return (InstancedMaterial<D>) materials.computeIfAbsent(spec, InstancedMaterial::new);
public <D extends InstanceData> InstancedMaterial<D> material(StructType<D> type) {
if (type instanceof Instanced<D> instanced) {
return (InstancedMaterial<D>) materials.computeIfAbsent(instanced, InstancedMaterial::new);
} else {
throw new ClassCastException("Cannot use type '" + type + "' with GPU instancing.");
}
}
public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
type.setupRenderState();
TextureBinder.bindActiveTextures();
Textures.bindActiveTextures();
renderAll(viewProjection, camX, camY, camZ);
type.clearRenderState();
}
protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ) {
for (Map.Entry<StructType<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
InstancedMaterial<?> material = entry.getValue();
if (material.nothingToRender()) continue;
@ -54,14 +59,16 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
.values();
// initialize all uninitialized instancers...
instancers.forEach(GPUInstancer::init);
for (GPUInstancer<?> gpuInstancer : instancers) {
gpuInstancer.init();
}
if (material.allocator instanceof ModelPool pool) {
// ...and then flush the model arena in case anything was marked for upload
pool.flush();
}
P program = owner.getProgram(entry.getKey()
.asInstanced()
.getProgramSpec()).get();
program.bind();
@ -70,7 +77,9 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
setup(program);
instancers.forEach(GPUInstancer::render);
for (GPUInstancer<?> instancer : instancers) {
instancer.render();
}
}
}

View file

@ -9,6 +9,7 @@ import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
@ -21,7 +22,6 @@ import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
@ -42,7 +42,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
private final WeakHashSet<OriginShiftListener> listeners;
public InstancingEngine(WorldContext<P> context) {
public InstancingEngine(WorldContext<P> context, TaskEngine taskEngine) {
this(context, InstancedMaterialGroup::new, false);
}
@ -79,7 +79,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
* Render every model for every material.
*/
@Override
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
public void render(TaskEngine taskEngine, RenderLayerEvent event) {
double camX;
double camY;
double camZ;
@ -106,6 +106,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
}
private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) {
// layer is null when this is called from CrumblingRenderer
if (layer != null) {
return layers.get(layer)
.values()
@ -166,6 +167,11 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
}
}
@Override
public String getName() {
return "GL33 Instanced Arrays";
}
@FunctionalInterface
public interface OriginShiftListener {
void onOriginShift();

View file

@ -8,8 +8,8 @@ import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.light.ImmutableBox;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;

View file

@ -5,6 +5,7 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;

View file

@ -36,12 +36,11 @@ public class ArrayModelRenderer extends ModelRenderer {
vao = new GlVertexArray();
vao.bind();
vao.enableArrays(this.model.getAttributeCount());
// bind the model's vbo to our vao
this.model.setupState();
AttribUtil.enableArrays(this.model.getAttributeCount());
GlVertexArray.unbind();
this.model.clearState();

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.ByteBuffer;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.VertexFormat;
/**
* Duck interface used on {@link BufferBuilder} to provide lower level access to the backing memory.
*/
public interface BufferBuilderHack {
void flywheel$freeBuffer();
void flywheel$hackBegin(ByteBuffer buffer, VertexFormat format, int vertexCount);
}

View file

@ -1,91 +1,41 @@
package com.jozufozu.flywheel.backend.model;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.api.vertex.VertexType;
import org.lwjgl.opengl.GL31;
public interface BufferedModel {
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
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.VecBufferWriter;
import com.jozufozu.flywheel.util.AttribUtil;
VertexType getType();
public class BufferedModel implements IBufferedModel {
protected final Model model;
protected final GlPrimitive primitiveMode;
protected GlBuffer vbo;
protected boolean deleted;
public BufferedModel(GlPrimitive primitiveMode, Model model) {
this.model = model;
this.primitiveMode = primitiveMode;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
// allocate the buffer on the gpu
vbo.alloc(model.size());
// mirror it in system memory so we can write to it, and upload our model.
try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) {
model.buffer(new VecBufferWriter(buffer));
} catch (Exception e) {
Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e);
}
vbo.unbind();
}
public boolean isDeleted() {
return deleted;
}
public VertexFormat getFormat() {
return model.format();
}
public int getVertexCount() {
return model.vertexCount();
}
int getVertexCount();
/**
* The VBO/VAO should be bound externally.
*/
public void setupState() {
vbo.bind();
AttribUtil.enableArrays(getAttributeCount());
getFormat().vertexAttribPointers(0);
}
void setupState();
public void clearState() {
AttribUtil.disableArrays(getAttributeCount());
vbo.unbind();
}
void clearState();
public void drawCall() {
glDrawArrays(primitiveMode.glEnum, 0, getVertexCount());
}
void drawCall();
/**
* Draws many instances of this model, assuming the appropriate state is already bound.
*/
public void drawInstances(int instanceCount) {
if (!valid()) return;
void drawInstances(int instanceCount);
GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, getVertexCount(), instanceCount);
boolean isDeleted();
void delete();
default BufferLayout getFormat() {
return getType().getLayout();
}
public void delete() {
if (deleted) return;
default boolean valid() {
return getVertexCount() > 0 && !isDeleted();
}
deleted = true;
vbo.delete();
default int getAttributeCount() {
return getFormat().getAttributeCount();
}
}

View file

@ -0,0 +1,180 @@
package com.jozufozu.flywheel.backend.model;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
/**
* An unsafe vertex consumer allowing for unchecked writes into a ByteBuffer.
*
* @see BufferBuilderHack
*/
public class DirectVertexConsumer implements VertexConsumer {
public final VertexFormat format;
private final int stride;
public final int startPos;
private int position = -1;
private int normal = -1;
private int color = -1;
private int uv = -1;
private int uv1 = -1;
private int uv2 = -1;
private long vertexBase;
private final long end;
public DirectVertexConsumer(ByteBuffer buffer, VertexFormat format, int maxVertices) {
this.format = format;
startPos = buffer.position();
stride = format.getVertexSize();
int offset = 0;
for (VertexFormatElement element : format.getElements()) {
switch (element.getUsage()) {
case POSITION -> this.position = offset;
case NORMAL -> this.normal = offset;
case COLOR -> this.color = offset;
case UV -> {
switch (element.getIndex()) {
case 0 -> this.uv = offset;
case 1 -> this.uv1 = offset;
case 2 -> this.uv2 = offset;
}
}
}
offset += element.getByteSize();
}
this.vertexBase = MemoryUtil.memAddress(buffer, startPos);
this.end = vertexBase + (long) maxVertices * stride;
}
private DirectVertexConsumer(DirectVertexConsumer parent, int maxVertices) {
this.format = parent.format;
this.stride = parent.stride;
this.startPos = parent.startPos;
this.position = parent.position;
this.normal = parent.normal;
this.color = parent.color;
this.uv = parent.uv;
this.uv1 = parent.uv1;
this.uv2 = parent.uv2;
this.vertexBase = parent.vertexBase;
this.end = parent.vertexBase + (long) maxVertices * this.stride;
}
public void memSetZero() {
MemoryUtil.memSet(vertexBase, 0, end - vertexBase);
}
public boolean hasOverlay() {
return uv1 >= 0;
}
/**
* Split off the head of this consumer into a new object and advance this object's write-pointer.
* @param vertexCount The number of vertices that must be written to the head.
* @return The head of this consumer.
*/
public DirectVertexConsumer split(int vertexCount) {
int bytes = vertexCount * stride;
DirectVertexConsumer head = new DirectVertexConsumer(this, vertexCount);
this.vertexBase += bytes;
return head;
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
checkOverflow();
if (position < 0) return this;
long base = vertexBase + position;
MemoryUtil.memPutFloat(base, (float) x);
MemoryUtil.memPutFloat(base + 4, (float) y);
MemoryUtil.memPutFloat(base + 8, (float) z);
return this;
}
@Override
public VertexConsumer color(int r, int g, int b, int a) {
if (color < 0) return this;
long base = vertexBase + color;
// int color = ((r & 0xFF)) | ((g & 0xFF) << 8) | ((b & 0xFF) << 16) | ((a & 0xFF) << 24);
// MemoryUtil.memPutInt(base, color);
MemoryUtil.memPutByte(base, (byte) r);
MemoryUtil.memPutByte(base + 1, (byte) g);
MemoryUtil.memPutByte(base + 2, (byte) b);
MemoryUtil.memPutByte(base + 3, (byte) a);
return this;
}
@Override
public VertexConsumer uv(float u, float v) {
if (uv < 0) return this;
long base = vertexBase + uv;
MemoryUtil.memPutFloat(base, u);
MemoryUtil.memPutFloat(base + 4, v);
return this;
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
if (uv1 < 0) return this;
long base = vertexBase + uv1;
MemoryUtil.memPutShort(base, (short) u);
MemoryUtil.memPutShort(base + 2, (short) v);
return this;
}
@Override
public VertexConsumer uv2(int u, int v) {
if (uv2 < 0) return this;
long base = vertexBase + uv2;
MemoryUtil.memPutShort(base, (short) u);
MemoryUtil.memPutShort(base + 2, (short) v);
return this;
}
@Override
public VertexConsumer normal(float x, float y, float z) {
if (normal < 0) return this;
long base = vertexBase + normal;
MemoryUtil.memPutByte(base, RenderMath.nb(x));
MemoryUtil.memPutByte(base + 1, RenderMath.nb(y));
MemoryUtil.memPutByte(base + 2, RenderMath.nb(z));
return this;
}
@Override
public void endVertex() {
vertexBase += stride;
}
@Override
public void defaultColor(int r, int g, int b, int a) {
}
@Override
public void unsetDefaultColor() {
}
private void checkOverflow() {
if (vertexBase >= end) {
throw new BufferOverflowException();
}
}
}

View file

@ -1,36 +0,0 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
public interface IBufferedModel {
VertexFormat getFormat();
int getVertexCount();
/**
* The VBO/VAO should be bound externally.
*/
void setupState();
void clearState();
void drawCall();
/**
* Draws many instances of this model, assuming the appropriate state is already bound.
*/
void drawInstances(int instanceCount);
boolean isDeleted();
void delete();
default boolean valid() {
return getVertexCount() > 0 && !isDeleted();
}
default int getAttributeCount() {
return getFormat().getAttributeCount();
}
}

View file

@ -7,7 +7,7 @@ public class ImmediateAllocator implements ModelAllocator {
public static final ImmediateAllocator INSTANCE = new ImmediateAllocator();
@Override
public IBufferedModel alloc(Model model, Callback allocationCallback) {
public BufferedModel alloc(Model model, Callback allocationCallback) {
IndexedModel out = new IndexedModel(model);
allocationCallback.onAlloc(out);
return out;

View file

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.core.model.Model;
*
* <br><em>This should be favored over a normal BufferedModel.</em>
*/
public class IndexedModel extends BufferedModel {
public class IndexedModel extends VBOModel {
protected ElementBuffer ebo;

View file

@ -9,10 +9,10 @@ public interface ModelAllocator {
* @param model The model to allocate.
* @return A handle to the allocated model.
*/
IBufferedModel alloc(Model model, Callback allocationCallback);
BufferedModel alloc(Model model, Callback allocationCallback);
@FunctionalInterface
interface Callback {
void onAlloc(IBufferedModel arenaModel);
void onAlloc(BufferedModel arenaModel);
}
}

View file

@ -7,39 +7,39 @@ import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
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.VecBufferWriter;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.api.vertex.VertexWriter;
import com.jozufozu.flywheel.util.AttribUtil;
public class ModelPool implements ModelAllocator {
protected final VertexFormat format;
protected final VertexType vertexType;
private final List<PooledModel> models = new ArrayList<>();
private final List<PooledModel> pendingUpload = new ArrayList<>();
private final GlBuffer vbo;
private int bufferSize;
private int vertices;
private boolean dirty;
private boolean anyToRemove;
public ModelPool(VertexFormat format, int initialSize) {
this.format = format;
bufferSize = format.getStride() * initialSize;
public ModelPool(VertexType vertexType, int initialSize) {
this.vertexType = vertexType;
int stride = vertexType.getStride();
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
vbo.alloc(bufferSize);
vbo.ensureCapacity((long) stride * initialSize);
vbo.setGrowthMargin(stride * 64);
vbo.unbind();
}
@ -104,27 +104,19 @@ public class ModelPool implements ModelAllocator {
* @return true if the buffer was reallocated
*/
private boolean realloc() {
int neededSize = vertices * format.getStride();
if (neededSize > bufferSize) {
bufferSize = neededSize + 128;
vbo.alloc(bufferSize);
return true;
}
return false;
return vbo.ensureCapacity((long) vertices * vertexType.getStride());
}
private void uploadAll() {
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
VecBufferWriter consumer = new VecBufferWriter(buffer);
try (MappedBuffer buffer = vbo.getBuffer()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
int vertices = 0;
for (PooledModel model : models) {
model.first = vertices;
model.model.buffer(consumer);
if (model.callback != null) model.callback.onAlloc(model);
buffer(writer, model);
vertices += model.getVertexCount();
}
@ -134,15 +126,10 @@ public class ModelPool implements ModelAllocator {
}
private void uploadPending() {
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
VecBufferWriter consumer = new VecBufferWriter(buffer);
int stride = format.getStride();
try (MappedBuffer buffer = vbo.getBuffer()) {
VertexWriter writer = vertexType.createWriter(buffer.unwrap());
for (PooledModel model : pendingUpload) {
int pos = model.first * stride;
buffer.position(pos);
model.model.buffer(consumer);
if (model.callback != null) model.callback.onAlloc(model);
buffer(writer, model);
}
pendingUpload.clear();
} catch (Exception e) {
@ -150,6 +137,12 @@ public class ModelPool implements ModelAllocator {
}
}
private void buffer(VertexWriter writer, PooledModel model) {
writer.seekToVertex(model.first);
writer.writeVertexList(model.model.getReader());
if (model.callback != null) model.callback.onAlloc(model);
}
private void setDirty() {
dirty = true;
}
@ -158,7 +151,7 @@ public class ModelPool implements ModelAllocator {
vbo.delete();
}
public class PooledModel implements IBufferedModel {
public class PooledModel implements BufferedModel {
private final ElementBuffer ebo;
private Callback callback;
@ -175,8 +168,8 @@ public class ModelPool implements ModelAllocator {
}
@Override
public VertexFormat getFormat() {
return model.format();
public VertexType getType() {
return ModelPool.this.vertexType;
}
@Override
@ -189,7 +182,7 @@ public class ModelPool implements ModelAllocator {
vbo.bind();
ebo.bind();
AttribUtil.enableArrays(getAttributeCount());
getFormat().vertexAttribPointers(0);
ModelPool.this.vertexType.getLayout().vertexAttribPointers(0);
}
@Override

View file

@ -7,7 +7,7 @@ import com.jozufozu.flywheel.core.model.Model;
public class ModelRenderer {
protected Supplier<Model> modelSupplier;
protected IBufferedModel model;
protected BufferedModel model;
protected boolean initialized;

View file

@ -0,0 +1,91 @@
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.backend.gl.GlPrimitive;
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.api.vertex.VertexType;
import com.jozufozu.flywheel.util.AttribUtil;
public class VBOModel implements BufferedModel {
protected final Model model;
protected final GlPrimitive primitiveMode;
protected GlBuffer vbo;
protected boolean deleted;
public VBOModel(GlPrimitive primitiveMode, Model model) {
this.model = model;
this.primitiveMode = primitiveMode;
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
// allocate the buffer on the gpu
vbo.ensureCapacity(model.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());
} catch (Exception e) {
Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e);
}
vbo.unbind();
}
public boolean isDeleted() {
return deleted;
}
@Override
public VertexType getType() {
return model.getType();
}
public int getVertexCount() {
return model.vertexCount();
}
/**
* The VBO/VAO should be bound externally.
*/
public void setupState() {
vbo.bind();
AttribUtil.enableArrays(getAttributeCount());
getFormat().vertexAttribPointers(0);
}
public void clearState() {
AttribUtil.disableArrays(getAttributeCount());
vbo.unbind();
}
public void drawCall() {
glDrawArrays(primitiveMode.glEnum, 0, getVertexCount());
}
/**
* 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, getVertexCount(), instanceCount);
}
public void delete() {
if (deleted) return;
deleted = true;
vbo.delete();
}
}

View file

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

View file

@ -8,7 +8,7 @@ import java.util.Map;
import com.google.common.collect.Lists;
import com.jozufozu.flywheel.util.ResourceUtil;
import com.jozufozu.flywheel.util.StreamUtil;
import com.jozufozu.flywheel.util.StringUtil;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
@ -37,7 +37,7 @@ public class ShaderSources implements ISourceHolder {
try {
Resource resource = manager.getResource(location);
String source = StreamUtil.readToString(resource.getInputStream());
String source = StringUtil.readToString(resource.getInputStream());
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);

View file

@ -2,32 +2,37 @@ package com.jozufozu.flywheel.backend.struct;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
public abstract class BufferWriter<S> implements StructWriter<S> {
protected final VecBuffer backingBuffer;
protected final VertexFormat format;
protected final int stride;
protected BufferWriter(VecBuffer backingBuffer, StructType<S> vertexType) {
this.backingBuffer = backingBuffer;
this.format = vertexType.format();
this.stride = this.format.getStride();
}
/**
* Advances the write pointer forward by the stride of one vertex. This should always be called after a
* vertex is written. Implementations which override this should always call invoke the super implementation.
*/
protected void advance() {
this.stride = vertexType.getLayout().getStride();
}
@Override
public final void write(S struct) {
writeInternal(struct);
advance();
}
/**
* Advances the write pointer forward by the stride of one vertex.
* This will always be called after a struct is written, implementors need not call it themselves.
*
* @see #write
*/
protected abstract void advance();
protected abstract void writeInternal(S s);
@Override
public void seek(int pos) {
backingBuffer.position(pos * stride);
backingBuffer.position(pos * stride);
}
}

View file

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

View file

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

View file

@ -2,13 +2,16 @@ package com.jozufozu.flywheel.config;
import java.util.function.BiConsumer;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.OptifineHandler;
import com.jozufozu.flywheel.config.Option.EnumOption;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
@ -18,19 +21,12 @@ public final class ConfigCommands {
public static void init(FlwConfig config) {
ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel");
commandBuilder.addOption(config.enabled, "backend", (builder, option) -> booleanOptionCommand(builder, config, option,
commandBuilder.addOption(config.engine, "backend", (builder, option) -> enumOptionCommand(builder, config, option,
(source, value) -> {
Component text = new TextComponent("Flywheel renderer is currently: ").append(boolToText(value));
source.sendFeedback(text);
source.sendFeedback(getEngineMessage(value));
},
(source, value) -> {
Component text;
if (OptifineHandler.usingShaders() && value) {
text = new TextComponent("Flywheel renderer does not support Optifine Shaders").withStyle(ChatFormatting.RED);
} else {
text = boolToText(value).append(new TextComponent(" Flywheel renderer").withStyle(ChatFormatting.WHITE));
}
source.sendFeedback(text);
source.sendFeedback(getEngineMessage(value));
Backend.reloadWorldRenderers();
}
));
@ -71,10 +67,50 @@ public final class ConfigCommands {
}));
}
public static <E extends Enum<E>> void enumOptionCommand(LiteralArgumentBuilder<FabricClientCommandSource> builder, FlwConfig config, EnumOption<E> option, BiConsumer<FabricClientCommandSource, E> displayAction, BiConsumer<FabricClientCommandSource, E> setAction) {
builder.executes(context -> {
displayAction.accept(context.getSource(), option.get());
return Command.SINGLE_SUCCESS;
});
for (E constant : option.getEnumType().getEnumConstants()) {
String name;
if (constant instanceof CommandNameProviderEnum nameProvider) {
name = nameProvider.getCommandName();
} else {
name = constant.name();
}
builder.then(ClientCommandManager.literal(name)
.executes(context -> {
option.set(constant);
setAction.accept(context.getSource(), option.get());
config.save();
return Command.SINGLE_SUCCESS;
}));
}
}
public static MutableComponent boolToText(boolean b) {
return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED);
}
public static Component getEngineMessage(@NotNull FlwEngine type) {
return switch (type) {
case OFF -> new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED);
case INSTANCING -> new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN);
case BATCHING -> {
MutableComponent msg = new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN);
if (FabricLoader.getInstance()
.isModLoaded("create")) {
// FIXME: batching engine contraption lighting issues
msg.append(new TextComponent("\nWARNING: May cause issues with Create Contraptions").withStyle(ChatFormatting.RED));
}
yield msg;
}
};
}
public static class ConfigCommandBuilder {
protected LiteralArgumentBuilder<FabricClientCommandSource> command;
@ -96,4 +132,8 @@ public final class ConfigCommands {
ClientCommandManager.DISPATCHER.register(command);
}
}
public interface CommandNameProviderEnum {
String getCommandName();
}
}

View file

@ -17,6 +17,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.jozufozu.flywheel.config.Option.BooleanOption;
import com.jozufozu.flywheel.config.Option.EnumOption;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
@ -34,7 +35,7 @@ public class FlwConfig {
protected final Map<String, Option<?>> optionMapView = Collections.unmodifiableMap(optionMap);
/** Enable or disable the entire engine */
public final BooleanOption enabled = addOption(new BooleanOption("enabled", true));
public final EnumOption<FlwEngine> engine = addOption(new EnumOption<>("engine", FlwEngine.INSTANCING));
/** Enable or disable a debug overlay that colors pixels by their normal */
public final BooleanOption debugNormals = addOption(new BooleanOption("debugNormals", false));
@ -51,8 +52,8 @@ public class FlwConfig {
ConfigCommands.init(INSTANCE);
}
public boolean enabled() {
return enabled.get();
public FlwEngine getEngine() {
return engine.get();
}
public boolean debugNormals() {

View file

@ -0,0 +1,49 @@
package com.jozufozu.flywheel.config;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
public enum FlwEngine implements ConfigCommands.CommandNameProviderEnum {
OFF("off", "Off"),
BATCHING("batching", "Parallel Batching"),
INSTANCING("instancing", "GL33 Instanced Arrays"),
;
private static final Map<String, FlwEngine> lookup;
static {
lookup = new HashMap<>();
for (FlwEngine value : values()) {
lookup.put(value.shortName, value);
}
}
private final String shortName;
private final String properName;
FlwEngine(String shortName, String properName) {
this.shortName = shortName;
this.properName = properName;
}
@Override
public String getCommandName() {
return shortName;
}
public String getProperName() {
return properName;
}
@Nullable
public static FlwEngine byName(String name) {
return lookup.get(name);
}
public static Collection<String> validNames() {
return lookup.keySet();
}
}

View file

@ -55,4 +55,34 @@ public interface Option<T> {
set(json.getAsBoolean());
}
}
public class EnumOption<E extends Enum<E>> extends BaseOption<E> {
protected final Class<E> enumType;
@SuppressWarnings("unchecked")
public EnumOption(String id, E defaultValue) {
super(id, defaultValue);
enumType = (Class<E>) defaultValue.getClass();
}
@Override
public JsonElement toJson() {
return new JsonPrimitive(get().name());
}
@Override
public void fromJson(JsonElement json) throws JsonParseException {
String constantName = json.getAsString();
for (E constant : enumType.getEnumConstants()) {
if (constant.name().equals(constantName)) {
set(constant);
break;
}
}
}
public Class<E> getEnumType() {
return enumType;
}
}
}

View file

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

View file

@ -1,30 +1,10 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.vertex.BlockVertex;
import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
public class Formats {
public static final VertexFormat UNLIT_MODEL = VertexFormat.builder()
.addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV)
.build();
public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder()
.addAttributes(CommonAttributes.VEC3,
CommonAttributes.NORMAL,
CommonAttributes.UV,
CommonAttributes.RGBA,
CommonAttributes.LIGHT)
.build();
public static final VertexFormat TRANSFORMED = litInstance().addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3)
.build();
public static final VertexFormat ORIENTED = litInstance().addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION)
.build();
public static VertexFormat.Builder litInstance() {
return VertexFormat.builder()
.addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA);
}
public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex();
public static final BlockVertex BLOCK = new BlockVertex();
}

View file

@ -32,8 +32,8 @@ public class FullscreenQuad {
private FullscreenQuad() {
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
vbo.alloc(bufferSize);
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
vbo.ensureCapacity(bufferSize);
try (MappedBuffer buffer = vbo.getBuffer()) {
buffer.putFloatArray(vertices);
} catch (Exception e) {
Flywheel.log.error("Could not create fullscreen quad.", e);

View file

@ -2,9 +2,11 @@ package com.jozufozu.flywheel.core;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
@ -87,8 +89,9 @@ public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
if (specStream == null) {
specStream = () -> backend.allMaterials()
.stream()
.map(type -> type.asInstanced()
.getProgramSpec());
.map(t -> t instanceof Instanced<?> i ? i : null)
.filter(Objects::nonNull)
.map(Instanced::getProgramSpec);
}
return new WorldContext<>(backend, name, specStream, pipeline);
}

View file

@ -1,48 +0,0 @@
package com.jozufozu.flywheel.core.atlas;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.AbstractTexture;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
public class AtlasInfo {
private static final Map<ResourceLocation, SheetData> sheetData = new HashMap<>();
public static TextureAtlas getAtlas(ResourceLocation name) {
AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name);
if (texture instanceof TextureAtlas)
return (TextureAtlas) texture;
else
return null;
}
@Nullable
public static SheetData getAtlasData(TextureAtlasSprite texture) {
return getAtlasData(texture.atlas());
}
@Nullable
public static SheetData getAtlasData(TextureAtlas atlas) {
return getAtlasData(atlas.location());
}
@Nullable
public static SheetData getAtlasData(@Nullable ResourceLocation loc) {
return sheetData.get(loc);
}
public static void setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) {
sheetData.put(atlas, new SheetData(accessor.getWidth(), accessor.getHeight()));
}
}

View file

@ -1,11 +0,0 @@
package com.jozufozu.flywheel.core.atlas;
public class SheetData {
public final int width;
public final int height;
public SheetData(int width, int height) {
this.width = width;
this.height = height;
}
}

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.core.crumbling;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.AbstractTexture;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.resources.ResourceLocation;
/**
* Track width and height of all created texture atlases.
*
* @see com.jozufozu.flywheel.mixin.atlas.AtlasDataMixin
*/
public class AtlasInfo {
private static final Map<ResourceLocation, SheetSize> sheetData = new HashMap<>();
@Nullable
public static TextureAtlas getAtlas(ResourceLocation name) {
AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name);
if (texture instanceof TextureAtlas atlas)
return atlas;
else
return null;
}
@Nullable
public static SheetSize getSheetSize(@Nullable ResourceLocation loc) {
return sheetData.get(loc);
}
/**
* FOR USE IN MIXIN
*/
public static void _setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) {
sheetData.put(atlas, new SheetSize(accessor.flywheel$getWidth(), accessor.flywheel$getHeight()));
}
public record SheetSize(int width, int height) {
}
}

View file

@ -2,10 +2,7 @@ package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.atlas.AtlasInfo;
import com.jozufozu.flywheel.core.atlas.SheetData;
import com.jozufozu.flywheel.util.RenderTextures;
import com.jozufozu.flywheel.util.TextureBinder;
import com.jozufozu.flywheel.util.Textures;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Matrix4f;
@ -37,7 +34,7 @@ public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMateria
RenderSystem.setShaderTexture(0, renderTex);
RenderSystem.setShaderTexture(4, breakingTex);
TextureBinder.bindActiveTextures();
Textures.bindActiveTextures();
renderAll(viewProjection, camX, camY, camZ);
CrumblingRenderer._currentLayer.clearRenderState();
@ -45,11 +42,11 @@ public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMateria
private void updateAtlasSize() {
SheetData atlasData = AtlasInfo.getAtlasData(RenderTextures.getShaderTexture(0));
AtlasInfo.SheetSize sheetSize = AtlasInfo.getSheetSize(Textures.getShaderTexture(0));
if (atlasData != null) {
width = atlasData.width;
height = atlasData.height;
if (sheetSize != null) {
width = sheetSize.width();
height = sheetSize.height();
} else {
width = height = 256;
}

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.crumbling;
import static org.lwjgl.opengl.GL20.glUniform2f;
import com.jozufozu.flywheel.core.atlas.AtlasInfo;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import net.minecraft.client.renderer.texture.TextureAtlas;

View file

@ -6,6 +6,7 @@ import java.util.SortedSet;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
@ -43,13 +44,12 @@ public class CrumblingRenderer {
static {
Pair<Lazy<State>, Lazy.KillSwitch<State>> state = Lazy.ofKillable(State::new, State::kill);
STATE = state.getFirst();
INVALIDATOR = state.getSecond();
STATE = state.first();
INVALIDATOR = state.second();
}
public static void renderBreaking(RenderLayerEvent event) {
if (!Backend.getInstance()
.canUseInstancing(event.getWorld())) return;
if (!Backend.canUseInstancing(event.getWorld())) return;
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageTiles(event.getWorld());
@ -69,9 +69,9 @@ public class CrumblingRenderer {
if (_currentLayer != null) {
stage.getValue().forEach(instanceManager::add);
instanceManager.beginFrame(info);
instanceManager.beginFrame(SerialTaskEngine.INSTANCE, info);
materials.render(event, null);
materials.render(SerialTaskEngine.INSTANCE, event);
instanceManager.invalidate();
}
@ -90,7 +90,7 @@ public class CrumblingRenderer {
Int2ObjectMap<List<BlockEntity>> breakingEntities = new Int2ObjectArrayMap<>();
for (Long2ObjectMap.Entry<SortedSet<BlockDestructionProgress>> entry : ((LevelRendererAccessor) Minecraft.getInstance().levelRenderer).getDestructionProgress()
for (Long2ObjectMap.Entry<SortedSet<BlockDestructionProgress>> entry : ((LevelRendererAccessor) Minecraft.getInstance().levelRenderer).flywheel$getDestructionProgress()
.long2ObjectEntrySet()) {
BlockPos breakingPos = BlockPos.of(entry.getLongKey());
@ -113,8 +113,7 @@ public class CrumblingRenderer {
public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel world = event.getWorld();
if (Backend.getInstance()
.canUseInstancing() && world != null) {
if (Backend.isOn() && world != null) {
reset();
}
}

View file

@ -0,0 +1,54 @@
package com.jozufozu.flywheel.core.hardcoded;
import java.util.List;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker;
public class ModelPart implements Model {
private final int vertices;
private final String name;
private final VertexList reader;
public ModelPart(List<PartBuilder.CuboidBuilder> cuboids, String name) {
this.name = name;
{
int vertices = 0;
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
vertices += cuboid.vertices();
}
this.vertices = vertices;
}
PosTexNormalWriterUnsafe writer = Formats.POS_TEX_NORMAL.createWriter(MemoryTracker.create(size()));
for (PartBuilder.CuboidBuilder cuboid : cuboids) {
cuboid.buffer(writer);
}
reader = writer.intoReader();
}
public static PartBuilder builder(String name, int sizeU, int sizeV) {
return new PartBuilder(name, sizeU, sizeV);
}
@Override
public String name() {
return name;
}
@Override
public int vertexCount() {
return vertices;
}
@Override
public VertexList getReader() {
return reader;
}
}

View file

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.core.model;
package com.jozufozu.flywheel.core.hardcoded;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.math.Matrix3f;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
@ -160,7 +160,7 @@ public class PartBuilder {
return visibleFaces.size() * 4;
}
public void buffer(VertexConsumer buffer) {
public void buffer(PosTexNormalWriterUnsafe buffer) {
float sizeX = posX2 - posX1;
float sizeY = posY2 - posY1;
@ -235,11 +235,11 @@ public class PartBuilder {
}
}
public void quad(VertexConsumer buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
buffer.vertex(vertices[0].x(), vertices[0].y(), vertices[0].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, minV).endVertex();
buffer.vertex(vertices[1].x(), vertices[1].y(), vertices[1].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, minV).endVertex();
buffer.vertex(vertices[2].x(), vertices[2].y(), vertices[2].z()).normal(normal.x(), normal.y(), normal.z()).uv(minU, maxV).endVertex();
buffer.vertex(vertices[3].x(), vertices[3].y(), vertices[3].z()).normal(normal.x(), normal.y(), normal.z()).uv(maxU, maxV).endVertex();
public void quad(PosTexNormalWriterUnsafe buffer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
buffer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), normal.x(), normal.y(), normal.z(), maxU, minV);
buffer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), normal.x(), normal.y(), normal.z(), minU, minV);
buffer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), normal.x(), normal.y(), normal.z(), minU, maxV);
buffer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), normal.x(), normal.y(), normal.z(), maxU, maxV);
}

View file

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

View file

@ -0,0 +1,74 @@
package com.jozufozu.flywheel.core.layout;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.vertex.VertexType;
/**
* Classic Vertex Format struct with a clever name.
*
* <p>
* Used for vertices and instance. Describes the layout of a datatype in a buffer object.
* </p>
*
* @see com.jozufozu.flywheel.api.struct.StructType
* @see VertexType
*/
public class BufferLayout {
private final List<LayoutItem> allAttributes;
private final int numAttributes;
private final int stride;
public BufferLayout(List<LayoutItem> allAttributes) {
this.allAttributes = allAttributes;
int numAttributes = 0, stride = 0;
for (LayoutItem spec : allAttributes) {
numAttributes += spec.getAttributeCount();
stride += spec.getSize();
}
this.numAttributes = numAttributes;
this.stride = stride;
}
public int getAttributeCount() {
return numAttributes;
}
public int getStride() {
return stride;
}
public void vertexAttribPointers(int index) {
int offset = 0;
for (LayoutItem spec : this.allAttributes) {
spec.vertexAttribPointer(stride, index, offset);
index += spec.getAttributeCount();
offset += spec.getSize();
}
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final ImmutableList.Builder<LayoutItem> allItems;
public Builder() {
allItems = ImmutableList.builder();
}
public Builder addItems(LayoutItem... attributes) {
allItems.add(attributes);
return this;
}
public BufferLayout build() {
return new BufferLayout(allItems.build());
}
}
}

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.core.layout;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
public class CommonItems {
public static final PrimitiveItem VEC4 = new PrimitiveItem(GlNumericType.FLOAT, 4);
public static final PrimitiveItem VEC3 = new PrimitiveItem(GlNumericType.FLOAT, 3);
public static final PrimitiveItem VEC2 = new PrimitiveItem(GlNumericType.FLOAT, 2);
public static final PrimitiveItem FLOAT = new PrimitiveItem(GlNumericType.FLOAT, 1);
public static final PrimitiveItem QUATERNION = new PrimitiveItem(GlNumericType.FLOAT, 4);
public static final PrimitiveItem NORMAL = new PrimitiveItem(GlNumericType.BYTE, 3, true);
public static final PrimitiveItem UV = new PrimitiveItem(GlNumericType.FLOAT, 2);
public static final PrimitiveItem RGBA = new PrimitiveItem(GlNumericType.UBYTE, 4, true);
public static final PrimitiveItem RGB = new PrimitiveItem(GlNumericType.UBYTE, 3, true);
public static final PrimitiveItem LIGHT = new PrimitiveItem(GlNumericType.UBYTE, 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 LayoutItem PADDING_BYTE = new PaddingItem(1);
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.gl.attrib;
package com.jozufozu.flywheel.core.layout;
public interface IAttribSpec {
public interface LayoutItem {
void vertexAttribPointer(int stride, int index, int offset);

View file

@ -1,10 +1,10 @@
package com.jozufozu.flywheel.backend.gl.attrib;
package com.jozufozu.flywheel.core.layout;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
public enum MatrixAttributes implements IAttribSpec {
public enum MatrixItems implements LayoutItem {
MAT3(3, 3),
MAT4(4, 4),
;
@ -12,7 +12,7 @@ public enum MatrixAttributes implements IAttribSpec {
private final int rows;
private final int cols;
MatrixAttributes(int rows, int cols) {
MatrixItems(int rows, int cols) {
this.rows = rows;
this.cols = cols;
}

View file

@ -0,0 +1,19 @@
package com.jozufozu.flywheel.core.layout;
record PaddingItem(int bytes) implements LayoutItem {
@Override
public void vertexAttribPointer(int stride, int index, int offset) {
}
@Override
public int getSize() {
return bytes;
}
@Override
public int getAttributeCount() {
return 0;
}
}

View file

@ -1,10 +1,10 @@
package com.jozufozu.flywheel.backend.gl.attrib;
package com.jozufozu.flywheel.core.layout;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
public class VertexAttribSpec implements IAttribSpec {
public class PrimitiveItem implements LayoutItem {
private final GlNumericType type;
private final int count;
@ -12,11 +12,11 @@ public class VertexAttribSpec implements IAttribSpec {
private final int attributeCount;
private final boolean normalized;
public VertexAttribSpec(GlNumericType type, int count) {
public PrimitiveItem(GlNumericType type, int count) {
this(type, count, false);
}
public VertexAttribSpec(GlNumericType type, int count, boolean normalized) {
public PrimitiveItem(GlNumericType type, int count, boolean normalized) {
this.type = type;
this.count = count;
this.size = type.getByteWidth() * count;

View file

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

View file

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

View file

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

View file

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

View file

@ -1,9 +1,7 @@
package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.core.materials.BasicData;
import com.jozufozu.flywheel.util.transform.Rotate;
import com.jozufozu.flywheel.util.transform.Scale;
import com.jozufozu.flywheel.util.transform.Translate;
import com.jozufozu.flywheel.util.transform.Transform;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
@ -11,7 +9,7 @@ import com.mojang.math.Quaternion;
import net.minecraft.util.Mth;
public class ModelData extends BasicData implements Translate<ModelData>, Rotate<ModelData>, Scale<ModelData> {
public class ModelData extends BasicData implements Transform<ModelData> {
public final Matrix4f model = new Matrix4f();
public final Matrix3f normal = new Matrix3f();
@ -83,4 +81,16 @@ public class ModelData extends BasicData implements Translate<ModelData>, Rotate
model.multiplyWithTranslation((float) x, (float) y, (float) z);
return this;
}
@Override
public ModelData mulPose(Matrix4f pose) {
this.model.multiply(pose);
return this;
}
@Override
public ModelData mulNormal(Matrix3f normal) {
this.normal.mul(normal);
return this;
}
}

View file

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

View file

@ -1,33 +1,37 @@
package com.jozufozu.flywheel.core.materials.model;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.MatrixItems;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.materials.model.writer.UnsafeModelWriter;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import net.minecraft.resources.ResourceLocation;
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)
.build();
@Override
public ModelData create() {
return new ModelData();
}
@Override
public VertexFormat format() {
return Formats.TRANSFORMED;
public BufferLayout getLayout() {
return FORMAT;
}
@Override
public StructWriter<ModelData> getWriter(VecBuffer backing) {
return new UnsafeModelWriter(backing, this);
return new ModelWriterUnsafe(backing, this);
}
@Override
@ -36,7 +40,9 @@ public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
}
@Override
public BatchingTransformer<ModelData> getTransformer(Model model) {
return null;
public void transform(ModelData d, ModelTransformer.Params b) {
b.transform(d.model, d.normal)
.color(d.r, d.g, d.b, d.a)
.light(d.getPackedLight());
}
}

View file

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

View file

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

View file

@ -1,7 +1,6 @@
package com.jozufozu.flywheel.core.materials.oriented;
import com.jozufozu.flywheel.core.materials.BasicData;
import com.jozufozu.flywheel.util.vec.Vec3;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
@ -36,11 +35,6 @@ public class OrientedData extends BasicData {
return this;
}
public OrientedData nudge(Vec3 pos) {
return nudge(pos.getX(), pos.getY(), pos.getZ());
}
public OrientedData nudge(float x, float y, float z) {
this.posX += x;
this.posY += y;
@ -57,10 +51,6 @@ public class OrientedData extends BasicData {
return setPosition((float) pos.x(), (float) pos.y(), (float) pos.z());
}
public OrientedData setPivot(Vec3 pos) {
return setPivot(pos.getX(), pos.getY(), pos.getZ());
}
public OrientedData setPivot(float x, float y, float z) {
this.pivotX = x;
this.pivotY = y;

View file

@ -1,33 +1,37 @@
package com.jozufozu.flywheel.core.materials.oriented;
import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.materials.oriented.writer.UnsafeOrientedWriter;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.mojang.math.Quaternion;
import net.minecraft.resources.ResourceLocation;
public class OrientedType implements Instanced<OrientedData>, Batched<OrientedData> {
public static final BufferLayout FORMAT = BufferLayout.builder()
.addItems(CommonItems.LIGHT, CommonItems.RGBA)
.addItems(CommonItems.VEC3, CommonItems.VEC3, CommonItems.QUATERNION)
.build();
@Override
public OrientedData create() {
return new OrientedData();
}
@Override
public VertexFormat format() {
return Formats.ORIENTED;
public BufferLayout getLayout() {
return FORMAT;
}
@Override
public StructWriter<OrientedData> getWriter(VecBuffer backing) {
return new UnsafeOrientedWriter(backing, this);
return new OrientedWriterUnsafe(backing, this);
}
@Override
@ -36,7 +40,11 @@ public class OrientedType implements Instanced<OrientedData>, Batched<OrientedDa
}
@Override
public BatchingTransformer<OrientedData> getTransformer(Model model) {
return null;
public void transform(OrientedData d, ModelTransformer.Params b) {
b.light(d.getPackedLight())
.color(d.r, d.g, d.b, d.a)
.translate(d.posX + d.pivotX, d.posY + d.pivotY, d.posZ + d.pivotZ)
.multiply(new Quaternion(d.qX, d.qY, d.qZ, d.qW))
.translate(-d.pivotX, -d.pivotY, -d.pivotZ);
}
}

View file

@ -0,0 +1,30 @@
package com.jozufozu.flywheel.core.materials.oriented;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.materials.BasicWriterUnsafe;
public class OrientedWriterUnsafe extends BasicWriterUnsafe<OrientedData> {
public OrientedWriterUnsafe(VecBuffer backingBuffer, StructType<OrientedData> vertexType) {
super(backingBuffer, vertexType);
}
@Override
protected void writeInternal(OrientedData d) {
long ptr = writePointer;
super.writeInternal(d);
MemoryUtil.memPutFloat(ptr + 6, d.posX);
MemoryUtil.memPutFloat(ptr + 10, d.posY);
MemoryUtil.memPutFloat(ptr + 14, d.posZ);
MemoryUtil.memPutFloat(ptr + 18, d.pivotX);
MemoryUtil.memPutFloat(ptr + 22, d.pivotY);
MemoryUtil.memPutFloat(ptr + 26, d.pivotZ);
MemoryUtil.memPutFloat(ptr + 30, d.qX);
MemoryUtil.memPutFloat(ptr + 34, d.qY);
MemoryUtil.memPutFloat(ptr + 38, d.qZ);
MemoryUtil.memPutFloat(ptr + 42, d.qW);
}
}

View file

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

View file

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

View file

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

Some files were not shown because too many files have changed in this diff Show more