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

Conflicts:
	README.md
	build.gradle
	gradle.properties
	src/main/java/com/jozufozu/flywheel/backend/RenderWork.java
	src/main/java/com/jozufozu/flywheel/backend/material/MaterialManagerImpl.java
	src/main/java/com/jozufozu/flywheel/config/BooleanConfigCommand.java
	src/main/java/com/jozufozu/flywheel/config/FlwCommands.java
	src/main/java/com/jozufozu/flywheel/config/FlwPackets.java
	src/main/java/com/jozufozu/flywheel/config/SConfigureBooleanPacket.java
	src/main/java/com/jozufozu/flywheel/core/PartialModel.java
	src/main/java/com/jozufozu/flywheel/core/StitchedSprite.java
	src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
	src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java
	src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java
	src/main/java/com/jozufozu/flywheel/mixin/LeakChunkStorageArrayMixin.java
	src/main/java/com/jozufozu/flywheel/mixin/RenderHooksMixin.java
	src/main/java/com/jozufozu/flywheel/util/ChunkIter.java
	src/main/resources/META-INF/mods.toml
	src/main/resources/flywheel.mixins.json
This commit is contained in:
PepperBell 2021-12-12 17:24:43 -08:00
commit 6fede0851e
152 changed files with 1376 additions and 1572 deletions

89
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View file

@ -0,0 +1,89 @@
name: Bug Report
description: Create a bug report to help us improve Flywheel
labels: [ "bug" ]
body:
- type: textarea
attributes:
label: Describe the Bug
description: A clear and concise description of what the bug is
validations:
required: true
- type: textarea
attributes:
label: Reproduction Steps
description: Tell us about the steps to reproduce the bug
value: |
1.
2.
3.
...
validations:
required: true
- type: textarea
attributes:
label: Expected Result
description: A clear and concise description of what you expected to happen
validations:
required: true
- type: textarea
attributes:
label: Screenshots and Videos
description: If applicable, add screenshots or videos to help explain your problem
validations:
required: false
- type: input
attributes:
label: Crash Report or Log
description: If applicable, please add a link to the crash report or log that was created when this issue occured
validations:
required: false
- type: markdown
attributes:
value: "*[Pastebin](https://pastebin.com/) is standard.*"
- type: input
attributes:
label: Operating System
description: The operating system you were using when the bug occured
placeholder: Windows 11
validations:
required: true
- type: dropdown
attributes:
label: Mod Version
description: The version of the mod you were using when the bug occured
options:
- "0.4.1"
- "0.3.0"
validations:
required: true
- type: dropdown
attributes:
label: Minecraft Version
description: The version of Minecraft you were using when the bug occured
options:
- "1.18.1"
- "1.17.1"
validations:
required: true
- type: input
attributes:
label: Fabric API Version
description: The version of Fabric API you were using when the bug occured
placeholder: 0.44.0+1.18
validations:
required: true
- type: textarea
attributes:
label: Other Mods
description: Please list any other mods that were running when the crash happened
validations:
required: false
- type: textarea
attributes:
label: Additional Context
description: Add any extra context about the bug here
validations:
required: false
- type: markdown
attributes:
value: "Thank you for taking the time to make this bug report and to help improve Flywheel"

View file

@ -2,7 +2,7 @@
<img src="https://i.imgur.com/yVFgPpr.png" alt="Logo by @voxel_dani on Twitter" width="250">
<h1>Flywheel</h1>
<h6>A modern engine for modded Minecraft.</h6>
<a href='https://ci.tterrag.com/job/Flywheel/job/Fabric/job/1.17/'><img src='https://ci.tterrag.com/job/Flywheel/job/Fabric/job/1.17/badge/icon' alt="Jenkins"></a>
<a href='https://ci.tterrag.com/job/Flywheel/job/Fabric/job/1.18/'><img src='https://ci.tterrag.com/job/Flywheel/job/Fabric/job/1.18/badge/icon' alt="Jenkins"></a>
<a href="https://discord.gg/xjD59ThnXy"><img src="https://img.shields.io/discord/841464837406195712?color=5865f2&label=Discord&style=flat" alt="Discord"></a>
<a href="https://www.curseforge.com/minecraft/mc-mods/flywheel"><img src="http://cf.way2muchnoise.eu/486392.svg" alt="Curseforge Downloads"></a>
<br>
@ -49,6 +49,6 @@ dependencies {
implementation fg.deobf("com.jozufozu.flywheel:Flywheel-Fabric:${flywheel_version}")
}
```
`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `0.3.0.5`
`${flywheel_version}` gets replaced by the version of Flywheel you want to use, eg. `1.18-0.3.0.3`
For a list of available Flywheel versions, you can check [the maven](https://maven.tterrag.com/com/jozufozu/flywheel/Flywheel-Fabric/).

View file

@ -13,10 +13,10 @@ version = "${mc_update_version}-${mod_version}" + (dev ? ".${buildnumber}" : '')
group = 'com.jozufozu.flywheel'
archivesBaseName = 'flywheel-fabric'
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
java.toolchain.languageVersion = JavaLanguageVersion.of(16)
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
@ -33,10 +33,11 @@ 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.layered() {
// officialMojangMappings()
// parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
// }
mappings loom.officialMojangMappings() // TODO: waiting for parchment 1.18
modImplementation "net.fabricmc:fabric-loader:${loader_version}"
// Fabric API
@ -56,14 +57,8 @@ processResources {
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = 'UTF-8'
// Minecraft 1.17 (21w19a) upwards uses Java 16.
it.options.release = 16
// Minecraft 1.18 (1.18-pre2) upwards uses Java 17.
it.options.release = 17
}
java {
@ -141,6 +136,7 @@ curseforge {
changelog = file('changelog.txt')
releaseType = project.curse_type
mainArtifact jar
addGameVersion '1.17.1'
addGameVersion '1.18'
addGameVersion '1.18.1'
}
}

View file

@ -1,4 +1,25 @@
0.3.0:
Update to 1.17
New
0.4.1:
Update to 1.18.1
Changes
- Use cylindrical fog to match vanilla
Fixes
- Fix crash affecting systems that don't support persistent mapping
- Fix crash when Create contraptions extend below Y=0
- Fix Create contraptions having misaligned light when launched after world load
0.4.0:
Update to 1.18
Fixes
- Fix potential nullpointer rendering breaking overlay
- Fix inconsistency in minecart model
- Fix memory leak when instance worlds get reset
Technical/API
- No more MaterialSpec, everything is StructType
- Move most user facing interfaces to flywheel.api package
- Refactor InstanceData to have no package private fields
- Rename many interfaces
- All materials use the same vertex format: UNLIT_MODEL
- call #tick and #beginFrame on instance creation
- Fixes weird delay in object appearance when reloading chunks
- Add instances when chunks are built for rendering
- Server worlds are not flywheel worlds

View file

@ -2,11 +2,11 @@ org.gradle.jvmargs = -Xmx3G
org.gradle.daemon = false
# mod version info
mod_version = 0.3.0
mc_update_version = 1.17
minecraft_version = 1.17.1
loader_version = 0.12.8
fabric_version = 0.44.0+1.17
mod_version = 0.4.1
mc_update_version = 1.18
minecraft_version = 1.18.1
loader_version = 0.12.11
fabric_version = 0.44.0+1.18
# build dependency versions
loom_version = 0.10-SNAPSHOT

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -1,11 +1,9 @@
package com.jozufozu.flywheel.backend.instancing;
import net.minecraft.world.level.Level;
package com.jozufozu.flywheel.api;
/**
* Something (a BlockEntity or Entity) that can be rendered using the instancing API.
*/
public interface IInstanceRendered {
public interface FlywheelRendered {
/**
* @return true if there are parts of the renderer that cannot be implemented with Flywheel.
@ -13,6 +11,4 @@ public interface IInstanceRendered {
default boolean shouldRenderNormally() {
return false;
}
Level getWorld();
}

View file

@ -1,12 +1,12 @@
package com.jozufozu.flywheel.backend;
package com.jozufozu.flywheel.api;
/**
* A marker interface custom worlds can override to indicate
* that tiles inside the world should render with Flywheel.
*
* <code>Minecraft.getInstance().world</code> is special cased and will support Flywheel by default.
* {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default.
*/
public interface IFlywheelWorld {
public interface FlywheelWorld {
default boolean supportsFlywheel() {
return true;
}

View file

@ -0,0 +1,41 @@
package com.jozufozu.flywheel.api;
public abstract class InstanceData {
private Instancer<?> owner;
private boolean dirty;
private boolean removed;
public final void markDirty() {
dirty = true;
owner.notifyDirty();
}
public final void delete() {
removed = true;
owner.notifyRemoval();
}
public final boolean checkDirtyAndClear() {
if (dirty) {
dirty = false;
return true;
} else {
return false;
}
}
public final boolean isRemoved() {
return removed;
}
public Instancer<?> getOwner() {
return owner;
}
public InstanceData setOwner(Instancer<?> owner) {
this.owner = owner;
return this;
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.api;
/**
* An instancer is how you interact with an instanced model.
@ -32,7 +32,22 @@ public interface Instancer<D extends InstanceData> {
*/
void stealInstance(D inOther);
void markDirty(InstanceData instanceData);
/**
* Notify the Instancer that some of its data needs updating.
*
* <p>
* This might be ignored, depending on the implementation. For the GPUInstancer, this triggers a scan of all
* instances.
* </p>
*/
void notifyDirty();
void markRemoval(InstanceData instanceData);
/**
* Notify the Instances that some of its data should be removed.
*
* <p>
* By the time the next frame is drawn, the instanceData passed will no longer be considered for rendering.
* </p>
*/
void notifyRemoval();
}

View file

@ -1,12 +1,10 @@
package com.jozufozu.flywheel.backend.material;
package com.jozufozu.flywheel.api;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.backend.instancing.Instancer;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.model.BlockModel;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.Pair;
import com.jozufozu.flywheel.util.RenderUtil;
import com.mojang.blaze3d.vertex.PoseStack;
@ -19,10 +17,10 @@ public interface Material<D extends InstanceData> {
* Get an instancer for the given model. Calling this method twice with the same key will return the same instancer.
*
* @param key An object that uniquely identifies the model.
* @param modelSupplier A factory that creates the IModel that you want to render.
* @param modelSupplier A factory that creates the Model that you want to render.
* @return An instancer for the given model, capable of rendering many copies for little cost.
*/
Instancer<D> model(Object key, Supplier<IModel> modelSupplier);
Instancer<D> model(Object key, Supplier<Model> modelSupplier);
default Instancer<D> getModel(PartialModel partial, BlockState referenceState) {
return model(partial, () -> new BlockModel(partial.get(), referenceState));

View file

@ -0,0 +1,14 @@
package com.jozufozu.flywheel.api;
import com.jozufozu.flywheel.api.struct.StructType;
public interface MaterialGroup {
/**
* Get the material as defined by the given {@link StructType type}.
*
* @param spec The material you want to create instances with.
* @param <D> The type representing the per instance data.
* @return A material you can use to render models.
*/
<D extends InstanceData> Material<D> material(StructType<D> spec);
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.material;
package com.jozufozu.flywheel.api;
import com.jozufozu.flywheel.backend.state.RenderLayer;
import com.jozufozu.flywheel.backend.RenderLayer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Vec3i;
@ -11,7 +11,7 @@ public interface MaterialManager {
* Get a material group that will render in the given layer with the given state.
*
* @param layer The {@link RenderLayer} you want to draw in.
* @param state The {@link net.minecraft.client.renderer.RenderType} you need to draw with.
* @param state The {@link RenderType} you need to draw with.
* @return A material group whose children will
*/
MaterialGroup state(RenderLayer layer, RenderType state);

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
/**
@ -13,9 +15,9 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
*/
public interface IDynamicInstance extends IInstance {
/**
* Called every frame.
* Called every frame, and after initialization.
* <br>
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance.
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this instance.
* <br>
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here.
*/

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.api.instance;
import net.minecraft.core.BlockPos;

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
/**
@ -21,7 +23,7 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
public interface ITickableInstance extends IInstance {
/**
* Called every tick.
* Called every tick, and after initialization.
* <br>
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance.
* <br>

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.state;
package com.jozufozu.flywheel.api.instance;
import javax.annotation.ParametersAreNonnullByDefault;

View file

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

View file

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

View file

@ -0,0 +1,11 @@
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

@ -1,8 +1,10 @@
package com.jozufozu.flywheel.backend.struct;
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
public interface Writeable<S> extends StructType<S> {
import net.minecraft.resources.ResourceLocation;
public interface Instanced<S> extends StructType<S> {
/**
* Create a {@link StructWriter} that will consume instances of S and write them to the given buffer.
*
@ -10,8 +12,10 @@ public interface Writeable<S> extends StructType<S> {
*/
StructWriter<S> getWriter(VecBuffer backing);
ResourceLocation getProgramSpec();
@Override
default Writeable<S> asWriteable() {
default Instanced<S> asInstanced() {
return this;
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.struct;
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
@ -18,5 +18,7 @@ public interface StructType<S> {
*/
VertexFormat format();
Writeable<S> asWriteable();
Instanced<S> asInstanced();
Batched<S> asBatched();
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.struct;
package com.jozufozu.flywheel.api.struct;
/**
* StructWriters can quickly consume many instances of S and write them to some backing buffer.

View file

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

View file

@ -13,9 +13,10 @@ import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.api.FlywheelWorld;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.backend.material.MaterialSpec;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
@ -41,8 +42,8 @@ public class Backend {
private boolean instancedArrays;
private boolean enabled;
private final List<IShaderContext<?>> contexts = new ArrayList<>();
private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>();
private final List<ShaderContext<?>> contexts = new ArrayList<>();
private final Map<ResourceLocation, StructType<?>> materialRegistry = new HashMap<>();
private final Map<ResourceLocation, ProgramSpec> programSpecRegistry = new HashMap<>();
protected Backend() {
@ -82,7 +83,7 @@ public class Backend {
/**
* Register a shader context.
*/
public <C extends IShaderContext<?>> C register(C spec) {
public <C extends ShaderContext<?>> C register(C spec) {
contexts.add(spec);
return spec;
}
@ -90,15 +91,13 @@ public class Backend {
/**
* Register an instancing material.
*/
public <D extends InstanceData> MaterialSpec<D> register(MaterialSpec<D> spec) {
ResourceLocation name = spec.name;
public <D extends InstanceData> StructType<D> register(ResourceLocation name, StructType<D> spec) {
if (materialRegistry.containsKey(name)) {
throw new IllegalStateException("Material spec '" + name + "' already registered.");
}
materialRegistry.put(name, spec);
log.debug("registered material '" + name + "' with vertex size " + spec.getModelFormat()
.getStride() + " and instance size " + spec.getInstanceType().format().getStride());
log.debug("registered material '" + name + "' with instance size " + spec.format().getStride());
return spec;
}
@ -133,7 +132,7 @@ public class Backend {
compat = new GlCompat(capabilities);
instancedArrays = compat.vertexArrayObjectsSupported() && compat.drawInstancedSupported() && compat.instancedArraysSupported();
instancedArrays = compat.instancedArraysSupported();
enabled = FlwConfig.get()
.enabled() && !OptifineHandler.usingShaders();
@ -143,7 +142,7 @@ public class Backend {
return canUseInstancing() && isFlywheelWorld(world);
}
public Collection<MaterialSpec<?>> allMaterials() {
public Collection<StructType<?>> allMaterials() {
return materialRegistry.values();
}
@ -151,7 +150,7 @@ public class Backend {
return programSpecRegistry.values();
}
public Collection<IShaderContext<?>> allContexts() {
public Collection<ShaderContext<?>> allContexts() {
return contexts;
}
@ -161,7 +160,9 @@ public class Backend {
public static boolean isFlywheelWorld(@Nullable LevelAccessor world) {
if (world == null) return false;
if (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel()) return true;
if (!world.isClientSide()) return false;
if (world instanceof FlywheelWorld && ((FlywheelWorld) world).supportsFlywheel()) return true;
return world == Minecraft.getInstance().level;
}
@ -180,7 +181,7 @@ public class Backend {
public void _clearContexts() {
GameStateRegistry.clear();
programSpecRegistry.clear();
contexts.forEach(IShaderContext::delete);
contexts.forEach(ShaderContext::delete);
contexts.clear();
materialRegistry.clear();
}

View file

@ -75,7 +75,7 @@ public class Loader {
Resolver.INSTANCE.resolve(sources);
for (IShaderContext<?> context : backend.allContexts()) {
for (ShaderContext<?> context : backend.allContexts()) {
context.load();
}
@ -88,7 +88,7 @@ public class Loader {
ClientLevel world = Minecraft.getInstance().level;
if (Backend.isFlywheelWorld(world)) {
// TODO: looks like it might be good to have another event here
InstancedRenderDispatcher.loadAllInWorld(world);
InstancedRenderDispatcher.resetInstanceWorld(world);
CrumblingRenderer.reset();
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.state;
package com.jozufozu.flywheel.backend;
import javax.annotation.Nullable;

View file

@ -6,7 +6,7 @@ import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.resources.ResourceLocation;
public interface IShaderContext<P extends GlProgram> {
public interface ShaderContext<P extends GlProgram> {
default P getProgram(ResourceLocation loc) {
return this.getProgramSupplier(loc)

View file

@ -22,4 +22,8 @@ public class GlTexture extends GlObject {
public void unbind() {
GL20.glBindTexture(textureType, 0);
}
public void setParameteri(int parameter, int value) {
GL20.glTexParameteri(textureType, parameter, value);
}
}

View file

@ -1,21 +1,21 @@
package com.jozufozu.flywheel.backend.gl;
import com.jozufozu.flywheel.backend.Backend;
import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject {
public GlVertexArray() {
setHandle(Backend.getInstance().compat.vao.genVertexArrays());
setHandle(GlStateManager._glGenVertexArrays());
}
public void bind() {
Backend.getInstance().compat.vao.bindVertexArray(handle());
GlStateManager._glBindVertexArray(handle());
}
public void unbind() {
Backend.getInstance().compat.vao.bindVertexArray(0);
public static void unbind() {
GlStateManager._glBindVertexArray(0);
}
protected void deleteInternal(int handle) {
Backend.getInstance().compat.vao.deleteVertexArrays(handle);
GlStateManager._glDeleteVertexArrays(handle);
}
}

View file

@ -40,11 +40,11 @@ public abstract class GlBuffer extends GlObject {
}
public void bind() {
GL20.glBindBuffer(type.glEnum, handle());
type.bind(handle());
}
public void unbind() {
GL20.glBindBuffer(type.glEnum, 0);
type.unbind();
}
public void doneForThisFrame() {

View file

@ -8,6 +8,8 @@ import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43;
import com.mojang.blaze3d.platform.GlStateManager;
public enum GlBufferType {
ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER),
ELEMENT_ARRAY_BUFFER(GL15C.GL_ELEMENT_ARRAY_BUFFER),
@ -29,4 +31,12 @@ public enum GlBufferType {
GlBufferType(int glEnum) {
this.glEnum = glEnum;
}
public void bind(int buffer) {
GlStateManager._glBindBuffer(glEnum, buffer);
}
public void unbind() {
GlStateManager._glBindBuffer(glEnum, 0);
}
}

View file

@ -0,0 +1,14 @@
package com.jozufozu.flywheel.backend.gl.buffer;
/**
* Interface for generically dealing with mapped buffers.
*/
public interface Mappable {
GlBufferType getType();
/**
* Indicates that this buffer need not be #flush()'d for its contents to sync.
* @return true if this buffer is persistently mapped.
*/
boolean isPersistent();
}

View file

@ -1,133 +1,45 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryUtil;
public abstract class MappedBuffer extends VecBuffer implements AutoCloseable {
public class MappedBuffer extends VecBuffer implements AutoCloseable {
protected boolean mapped;
protected final GlBuffer owner;
protected final long offset;
protected final long length;
protected final Mappable owner;
public MappedBuffer(GlBuffer owner) {
public MappedBuffer(Mappable owner, ByteBuffer internal, long offset, long length) {
this.internal = internal;
this.owner = owner;
this.offset = offset;
this.length = length;
}
public long addr() {
return MemoryUtil.memAddress(this.internal, internal.position());
}
protected abstract void checkAndMap();
/**
* Make the changes in client memory available to the GPU.
*/
public void flush() {
if (mapped) {
GL15.glUnmapBuffer(owner.type.glEnum);
mapped = false;
setInternal(null);
if (owner.isPersistent()) return;
if (internal == null) return;
GL15.glUnmapBuffer(owner.getType().glEnum);
internal = null;
}
@Override
public MappedBuffer position(int p) {
if (p < offset || p >= offset + length) {
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
}
super.position(p - (int) offset);
return this;
}
@Override
public void close() throws Exception {
flush();
}
public MappedBuffer putFloatArray(float[] floats) {
checkAndMap();
super.putFloatArray(floats);
return this;
}
public MappedBuffer putByteArray(byte[] bytes) {
checkAndMap();
super.putByteArray(bytes);
return this;
}
public MappedBuffer put(FloatBuffer floats) {
checkAndMap();
super.put(floats);
return this;
}
public int position() {
checkAndMap();
return super.position();
}
/**
* Position this buffer relative to the 0-index in GPU memory.
*
* @return This buffer.
*/
public MappedBuffer position(int p) {
checkAndMap();
super.position(p);
return this;
}
public MappedBuffer putFloat(float f) {
checkAndMap();
super.putFloat(f);
return this;
}
public MappedBuffer putInt(int i) {
checkAndMap();
super.putInt(i);
return this;
}
public MappedBuffer putShort(short s) {
checkAndMap();
super.putShort(s);
return this;
}
public MappedBuffer put(byte b) {
checkAndMap();
super.put(b);
return this;
}
public MappedBuffer put(ByteBuffer b) {
checkAndMap();
super.put(b);
return this;
}
public MappedBuffer putVec4(float x, float y, float z, float w) {
checkAndMap();
super.putVec4(x, y, z, w);
return this;
}
public MappedBuffer putVec3(float x, float y, float z) {
checkAndMap();
super.putVec3(x, y, z);
return this;
}
public MappedBuffer putVec2(float x, float y) {
checkAndMap();
super.putVec2(x, y);
return this;
}
public MappedBuffer putVec3(byte x, byte y, byte z) {
checkAndMap();
super.putVec3(x, y, z);
return this;
}
public MappedBuffer putVec2(byte x, byte y) {
checkAndMap();
super.putVec2(x, y);
return this;
}
}

View file

@ -1,41 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.util.StringUtil;
public class MappedBufferRange extends MappedBuffer {
long offset, length;
int access;
public MappedBufferRange(GlBuffer buffer, long offset, long length, int access) {
super(buffer);
this.offset = offset;
this.length = length;
this.access = access;
}
@Override
public MappedBuffer position(int p) {
if (p < offset || p >= offset + length) {
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
}
return super.position(p - (int) offset);
}
@Override
protected void checkAndMap() {
if (!mapped) {
setInternal(Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, access));
GlError error = GlError.poll();
if (error != null) {
throw new GlException(error, StringUtil.args("mapBufferRange", owner.type, offset, length, access));
}
mapped = true;
}
}
}

View file

@ -1,32 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import org.lwjgl.opengl.GL15;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.util.StringUtil;
public class MappedFullBuffer extends MappedBuffer {
MappedBufferUsage usage;
public MappedFullBuffer(GlBuffer buffer, MappedBufferUsage usage) {
super(buffer);
this.usage = usage;
}
@Override
protected void checkAndMap() {
if (!mapped) {
setInternal(GL15.glMapBuffer(owner.type.glEnum, usage.glEnum));
GlError error = GlError.poll();
if (error != null) {
throw new GlException(error, StringUtil.args("mapBuffer", owner.type, usage));
}
mapped = true;
}
}
}

View file

@ -5,10 +5,11 @@ import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.versioned.MapBufferRange;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.util.StringUtil;
public class MappedGlBuffer extends GlBuffer {
public class MappedGlBuffer extends GlBuffer implements Mappable {
protected final GlBufferUsage usage;
@ -30,12 +31,22 @@ public class MappedGlBuffer extends GlBuffer {
}
public MappedBuffer getBuffer(int offset, int length) {
if (Backend.getInstance().compat.mapBufferRange != MapBufferRange.UNSUPPORTED) {
return new MappedBufferRange(this, offset, length, GL30.GL_MAP_WRITE_BIT);
} else {
MappedFullBuffer fullBuffer = new MappedFullBuffer(this, MappedBufferUsage.WRITE_ONLY);
fullBuffer.position(offset);
return fullBuffer;
ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, offset, length, GL30.GL_MAP_WRITE_BIT);
if (byteBuffer == null) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
return new MappedBuffer(this, byteBuffer, offset, length);
}
@Override
public GlBufferType getType() {
return type;
}
@Override
public boolean isPersistent() {
return false;
}
}

View file

@ -6,16 +6,17 @@ import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL30;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlFence;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.util.StringUtil;
public class PersistentGlBuffer extends GlBuffer implements Mappable {
public class PersistentGlBuffer extends GlBuffer {
private PersistentMappedBuffer buffer;
private MappedBuffer buffer;
int flags;
long size;
@ -49,17 +50,24 @@ public class PersistentGlBuffer extends GlBuffer {
Backend.getInstance().compat.bufferStorage.bufferStorage(type, size, flags);
GlError error = GlError.poll();
if (error != null) {
// If this error is being thrown but everything seems fine,
// GlError.poll() might be returning an error from something earlier.
throw new GlException(error, StringUtil.args("bufferStorage", type, size, flags));
}
buffer = new PersistentMappedBuffer(this);
ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, flags);
if (byteBuffer == null) {
throw new GlException(GlError.poll(), "Could not map buffer");
}
buffer = new MappedBuffer(this, byteBuffer, 0, size);
}
@Override
public void upload(ByteBuffer directBuffer) {
throw new UnsupportedOperationException("FIXME: Nothing calls #upload on a persistent buffer as of 12/10/2021.");
}
@Override
@ -71,4 +79,14 @@ public class PersistentGlBuffer extends GlBuffer {
return buffer;
}
@Override
public GlBufferType getType() {
return type;
}
@Override
public boolean isPersistent() {
return true;
}
}

View file

@ -1,50 +0,0 @@
package com.jozufozu.flywheel.backend.gl.buffer;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.gl.error.GlException;
import com.jozufozu.flywheel.util.StringUtil;
public class PersistentMappedBuffer extends MappedBuffer {
private final long offset;
private final long length;
PersistentGlBuffer owner;
public PersistentMappedBuffer(PersistentGlBuffer buffer) {
super(buffer);
owner = buffer;
offset = 0;
length = owner.size;
ByteBuffer byteBuffer = Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, owner.flags);
GlError error = GlError.poll();
if (error != null) {
throw new GlException(error, StringUtil.args("mapBuffer", owner.type, offset, length, owner.flags));
}
setInternal(byteBuffer);
}
@Override
public MappedBuffer position(int p) {
if (p < offset || p >= offset + length) {
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
}
return super.position(p - (int) offset);
}
@Override
public void flush() {
}
@Override
protected void checkAndMap() {
}
}

View file

@ -5,6 +5,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.annotation.Nullable;
public class VecBuffer {
protected ByteBuffer internal;
@ -22,10 +24,6 @@ public class VecBuffer {
return new VecBuffer(buffer);
}
protected void setInternal(ByteBuffer internal) {
this.internal = internal;
}
public ByteBuffer unwrap() {
return internal;
}

View file

@ -35,16 +35,16 @@ public enum GlError {
this.glEnum = glEnum;
}
// Great for use in your debugger's expression evaluator
public static GlError poll() {
return errorLookup.get(GL20.glGetError());
}
public static void pollAndThrow(Supplier<String> context) {
// TODO: build flag? to enable or disable this function
GlError poll = GlError.poll();
if (poll != null) {
Flywheel.log.error("{}: {}", poll.name(), context.get());
GlError err = GlError.poll();
if (err != null) {
Flywheel.log.error("{}: {}", err.name(), context.get());
}
}
}

View file

@ -4,35 +4,12 @@ public class GlException extends RuntimeException {
final GlError errorCode;
@Override
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
String withCode = s + ": " + errorCode;
return (message != null) ? (withCode + ": " + message) : withCode;
}
public GlException(GlError errorCode) {
this.errorCode = errorCode;
}
public GlException(GlError errorCode, String message) {
super(message);
super(updateMessage(errorCode, message));
this.errorCode = errorCode;
}
public GlException(GlError errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public GlException(GlError errorCode, Throwable cause) {
super(cause);
this.errorCode = errorCode;
}
public GlException(GlError errorCode, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
this.errorCode = errorCode;
private static String updateMessage(GlError error, String message) {
return String.format("%s: %s", error, message);
}
}

View file

@ -9,13 +9,6 @@ import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Blit;
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Framebuffer;
import com.jozufozu.flywheel.backend.gl.versioned.instancing.BaseVertex;
import com.jozufozu.flywheel.backend.gl.versioned.instancing.DrawInstanced;
import com.jozufozu.flywheel.backend.gl.versioned.instancing.InstancedArrays;
import com.jozufozu.flywheel.backend.gl.versioned.instancing.VertexArrayObject;
/**
* An instance of this class stores information about what OpenGL features are available.
* <br>
@ -23,52 +16,19 @@ import com.jozufozu.flywheel.backend.gl.versioned.instancing.VertexArrayObject;
* system.
*/
public class GlCompat {
public final MapBufferRange mapBufferRange;
public final VertexArrayObject vao;
public final InstancedArrays instancedArrays;
public final DrawInstanced drawInstanced;
public final Blit blit;
public final Framebuffer fbo;
public final BufferStorage bufferStorage;
public final RGPixelFormat pixelFormat;
public final BaseVertex baseVertex;
public GlCompat(GLCapabilities caps) {
mapBufferRange = getLatest(MapBufferRange.class, caps);
vao = getLatest(VertexArrayObject.class, caps);
instancedArrays = getLatest(InstancedArrays.class, caps);
drawInstanced = getLatest(DrawInstanced.class, caps);
baseVertex = getLatest(BaseVertex.class, caps);
blit = getLatest(Blit.class, caps);
fbo = getLatest(Framebuffer.class, caps);
bufferStorage = getLatest(BufferStorage.class, caps);
pixelFormat = getLatest(RGPixelFormat.class, caps);
}
public boolean vertexArrayObjectsSupported() {
return vao != VertexArrayObject.UNSUPPORTED;
}
public boolean instancedArraysSupported() {
return instancedArrays != InstancedArrays.UNSUPPORTED;
}
public boolean drawInstancedSupported() {
return drawInstanced != DrawInstanced.UNSUPPORTED;
}
public boolean fbosSupported() {
return fbo != Framebuffer.UNSUPPORTED;
}
public boolean blitSupported() {
return blit != Blit.UNSUPPORTED;
}
public boolean bufferStorageSupported() {
return bufferStorage != BufferStorage.UNSUPPORTED;
}

View file

@ -1,11 +1,9 @@
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
package com.jozufozu.flywheel.backend.gl.versioned;
import org.lwjgl.opengl.ARBInstancedArrays;
import org.lwjgl.opengl.GL33;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum InstancedArrays implements GlVersioned {
GL33_INSTANCED_ARRAYS {
@Override

View file

@ -1,49 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.ARBMapBufferRange;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
public enum MapBufferRange implements GlVersioned {
GL30_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
return GL30.glMapBufferRange(target.glEnum, offset, length, access);
}
},
ARB_RANGE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_map_buffer_range;
}
@Override
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
return ARBMapBufferRange.glMapBufferRange(target.glEnum, offset, length, access);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
throw new UnsupportedOperationException("glMapBuffer not supported");
}
};
public abstract ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access);
}

View file

@ -1,77 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
public enum RGPixelFormat implements GlVersioned {
GL30_RG {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public int internalFormat() {
return GL30.GL_RG8;
}
@Override
public int format() {
return GL30.GL_RG;
}
@Override
public int byteCount() {
return 2;
}
},
GL11_RGB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL11;
}
@Override
public int internalFormat() {
return GL11.GL_RGB8;
}
@Override
public int format() {
return GL11.GL_RGB;
}
@Override
public int byteCount() {
return 3;
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int internalFormat() {
throw new UnsupportedOperationException();
}
@Override
public int format() {
throw new UnsupportedOperationException();
}
@Override
public int byteCount() {
throw new UnsupportedOperationException();
}
};
public abstract int internalFormat();
public abstract int format();
public abstract int byteCount();
}

View file

@ -1,45 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned.framebuffer;
import org.lwjgl.opengl.EXTFramebufferBlit;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum Blit implements GlVersioned {
CORE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
},
EXT {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_framebuffer_blit;
}
@Override
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
throw new UnsupportedOperationException("Framebuffer blitting not supported.");
}
};
public abstract void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
}

View file

@ -1,57 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned.framebuffer;
import org.lwjgl.opengl.ARBFramebufferObject;
import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL30C;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum Framebuffer implements GlVersioned {
CORE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public void bindFramebuffer(int target, int framebuffer) {
GL30C.glBindFramebuffer(target, framebuffer);
}
},
ARB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_framebuffer_object;
}
@Override
public void bindFramebuffer(int target, int framebuffer) {
ARBFramebufferObject.glBindFramebuffer(target, framebuffer);
}
},
EXT {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_framebuffer_object;
}
@Override
public void bindFramebuffer(int target, int framebuffer) {
EXTFramebufferObject.glBindFramebufferEXT(target, framebuffer);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public void bindFramebuffer(int target, int framebuffer) {
throw new UnsupportedOperationException("Framebuffers not supported");
}
};
public abstract void bindFramebuffer(int target, int framebuffer);
}

View file

@ -1,60 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
import org.lwjgl.opengl.ARBDrawElementsBaseVertex;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum BaseVertex implements GlVersioned {
GL31_CORE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL31;
}
@Override
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
GL32.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex);
}
@Override
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
GL32.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex);
}
},
ARB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_draw_elements_base_vertex;
}
@Override
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
ARBDrawElementsBaseVertex.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex);
}
@Override
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
ARBDrawElementsBaseVertex.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
};
public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) {
throw new UnsupportedOperationException();
}
public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) {
throw new UnsupportedOperationException();
}
}

View file

@ -1,76 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
import org.lwjgl.opengl.ARBDrawInstanced;
import org.lwjgl.opengl.EXTDrawInstanced;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum DrawInstanced implements GlVersioned {
GL31_CORE {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL31;
}
@Override
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
GL31.glDrawArraysInstanced(mode.glEnum, first, count, primcount);
}
@Override
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
GL31.glDrawElementsInstanced(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
}
},
ARB {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_draw_instanced;
}
@Override
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
ARBDrawInstanced.glDrawArraysInstancedARB(mode.glEnum, first, count, primcount);
}
@Override
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
ARBDrawInstanced.glDrawElementsInstancedARB(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
}
},
EXT {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_EXT_draw_instanced;
}
@Override
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
EXTDrawInstanced.glDrawArraysInstancedEXT(mode.glEnum, first, count, primcount);
}
@Override
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
EXTDrawInstanced.glDrawElementsInstancedEXT(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
};
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
throw new UnsupportedOperationException();
}
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
throw new UnsupportedOperationException();
}
}

View file

@ -1,79 +0,0 @@
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
import org.lwjgl.opengl.ARBVertexArrayObject;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
public enum VertexArrayObject implements GlVersioned {
GL30_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.OpenGL30;
}
@Override
public int genVertexArrays() {
return GL30.glGenVertexArrays();
}
@Override
public void bindVertexArray(int array) {
GL30.glBindVertexArray(array);
}
@Override
public void deleteVertexArrays(int array) {
GL30.glDeleteVertexArrays(array);
}
},
ARB_VAO {
@Override
public boolean supported(GLCapabilities caps) {
return caps.GL_ARB_vertex_array_object;
}
@Override
public int genVertexArrays() {
return ARBVertexArrayObject.glGenVertexArrays();
}
@Override
public void bindVertexArray(int array) {
ARBVertexArrayObject.glBindVertexArray(array);
}
@Override
public void deleteVertexArrays(int array) {
ARBVertexArrayObject.glDeleteVertexArrays(array);
}
},
UNSUPPORTED {
@Override
public boolean supported(GLCapabilities caps) {
return true;
}
@Override
public int genVertexArrays() {
throw new UnsupportedOperationException();
}
@Override
public void bindVertexArray(int array) {
throw new UnsupportedOperationException();
}
@Override
public void deleteVertexArrays(int array) {
throw new UnsupportedOperationException();
}
};
public abstract int genVertexArrays();
public abstract void bindVertexArray(int array);
public abstract void deleteVertexArrays(int array);
}

View file

@ -3,10 +3,13 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.Arrays;
import java.util.stream.Stream;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
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.backend.material.MaterialManager;
import com.jozufozu.flywheel.core.materials.IFlatLight;
import com.jozufozu.flywheel.light.ILightUpdateListener;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.materials.FlatLit;
import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.ImmutableBox;
import com.jozufozu.flywheel.light.LightProvider;
import com.jozufozu.flywheel.light.ListenerStatus;
@ -19,7 +22,7 @@ import net.minecraft.world.level.LightLayer;
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
* Right now, that's only {@link TileInstanceManager}, but there could be an entity equivalent in the future.
*/
public abstract class AbstractInstance implements IInstance, ILightUpdateListener {
public abstract class AbstractInstance implements IInstance, LightListener {
protected final MaterialManager materialManager;
public final Level world;
@ -84,19 +87,19 @@ public abstract class AbstractInstance implements IInstance, ILightUpdateListene
updateLight();
}
protected void relight(BlockPos pos, IFlatLight<?>... models) {
protected void relight(BlockPos pos, FlatLit<?>... models) {
relight(world.getBrightness(LightLayer.BLOCK, pos), world.getBrightness(LightLayer.SKY, pos), models);
}
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> models) {
relight(world.getBrightness(LightLayer.BLOCK, pos), world.getBrightness(LightLayer.SKY, pos), models);
}
protected void relight(int block, int sky, IFlatLight<?>... models) {
protected void relight(int block, int sky, FlatLit<?>... models) {
relight(block, sky, Arrays.stream(models));
}
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> models) {
models.forEach(model -> model.setBlockLight(block)
.setSkyLight(sky));
}

View file

@ -3,19 +3,20 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.BitSet;
import com.jozufozu.flywheel.backend.struct.StructType;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.Model;
public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
public abstract class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
protected final StructType<D> type;
protected final IModel modelData;
protected final Model modelData;
protected final ArrayList<D> data = new ArrayList<>();
boolean anyToRemove;
boolean anyToUpdate;
protected boolean anyToRemove;
public AbstractInstancer(StructType<D> type, IModel modelData) {
protected AbstractInstancer(StructType<D> type, Model modelData) {
this.type = type;
this.modelData = modelData;
}
@ -25,9 +26,7 @@ public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
*/
@Override
public D createInstance() {
D data = type.create();
data.owner = this;
return _add(data);
return _add(type.create());
}
/**
@ -38,24 +37,17 @@ public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
*/
@Override
public void stealInstance(D inOther) {
if (inOther.owner == this) return;
if (inOther.getOwner() == this) return;
inOther.delete();
// sike, we want to keep it, changing the owner reference will still delete it in the other
inOther.removed = false;
// Changing the owner reference will delete it in the other instancer
inOther.getOwner()
.notifyRemoval();
_add(inOther);
}
@Override
public void markDirty(InstanceData instanceData) {
anyToUpdate = true;
instanceData.dirty = true;
}
@Override
public void markRemoval(InstanceData instanceData) {
public void notifyRemoval() {
anyToRemove = true;
instanceData.removed = true;
}
/**
@ -72,10 +64,8 @@ public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
for (int i = 0; i < size; i++) {
D element = data.get(i);
if (element.dirty) {
if (element.checkDirtyAndClear()) {
dirtySet.set(i);
element.dirty = false;
}
}
return dirtySet;
@ -88,7 +78,7 @@ public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
final BitSet removeSet = new BitSet(oldSize);
for (int i = 0; i < oldSize; i++) {
final D element = this.data.get(i);
if (element.removed || element.owner != this) {
if (element.isRemoved() || element.getOwner() != this) {
removeSet.set(i);
removeCount++;
}
@ -103,22 +93,22 @@ public class AbstractInstancer<D extends InstanceData> implements Instancer<D> {
if (i != j) {
D element = data.get(i);
data.set(j, element);
element.dirty = true;
// Marking the data dirty marks us dirty too.
// Perhaps there will be some wasted cycles, but the JVM should be able to
// generate code that moves the repeated segment out of the loop.
element.markDirty();
}
}
anyToUpdate = true;
data.subList(newSize, oldSize)
.clear();
}
private D _add(D instanceData) {
instanceData.owner = this;
instanceData.setOwner(this);
instanceData.dirty = true;
anyToUpdate = true;
instanceData.markDirty();
synchronized (data) {
data.add(instanceData);
}

View file

@ -0,0 +1,6 @@
package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.MaterialManager;
public interface Engine extends RenderDispatcher, MaterialManager {
}

View file

@ -1,18 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
public abstract class InstanceData {
Instancer<?> owner;
boolean dirty;
boolean removed;
public void markDirty() {
owner.markDirty(this);
}
public void delete() {
owner.markRemoval(this);
}
}

View file

@ -9,8 +9,10 @@ import java.util.Set;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.light.LightUpdater;
import com.mojang.math.Vector3f;
@ -19,7 +21,7 @@ import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginShiftListener {
public abstract class InstanceManager<T> implements InstancingEngine.OriginShiftListener {
public final MaterialManager materialManager;
@ -33,7 +35,7 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
protected int frame;
protected int tick;
public InstanceManager(MaterialManagerImpl<?> materialManager) {
public InstanceManager(MaterialManager materialManager) {
this.materialManager = materialManager;
this.queuedUpdates = new HashSet<>(64);
this.queuedAdditions = new HashSet<>(64);
@ -41,8 +43,6 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
this.tickableInstances = new Object2ObjectOpenHashMap<>();
materialManager.addListener(this);
}
/**
@ -213,6 +213,10 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
}
protected void processQueuedAdditions() {
if (queuedAdditions.isEmpty()) {
return;
}
ArrayList<T> queued;
synchronized (queuedAdditions) {
@ -220,7 +224,7 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
queuedAdditions.clear();
}
if (queued.size() > 0) {
if (!queued.isEmpty()) {
queued.forEach(this::addInternal);
}
}
@ -292,9 +296,15 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
.addListener(renderer);
instances.put(obj, renderer);
if (renderer instanceof IDynamicInstance) dynamicInstances.put(obj, (IDynamicInstance) renderer);
if (renderer instanceof ITickableInstance r) {
tickableInstances.put(obj, r);
r.tick();
}
if (renderer instanceof ITickableInstance) tickableInstances.put(obj, ((ITickableInstance) renderer));
if (renderer instanceof IDynamicInstance r) {
dynamicInstances.put(obj, r);
r.beginFrame();
}
}
return renderer;
@ -306,4 +316,10 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
invalidate();
instancedTiles.forEach(this::add);
}
public void detachLightListeners() {
for (AbstractInstance value : instances.values()) {
LightUpdater.get(value.world).removeListener(value);
}
}
}

View file

@ -1,17 +1,17 @@
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.instancing.entity.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.util.ChunkIter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -22,20 +22,28 @@ import net.minecraft.world.level.block.entity.BlockEntity;
* </p>
*/
public class InstanceWorld {
protected final MaterialManagerImpl<WorldProgram> materialManager;
protected final Engine engine;
protected final InstanceManager<Entity> entityInstanceManager;
protected final InstanceManager<BlockEntity> tileEntityInstanceManager;
public InstanceWorld() {
materialManager = MaterialManagerImpl.builder(Contexts.WORLD)
.build();
entityInstanceManager = new EntityInstanceManager(materialManager);
tileEntityInstanceManager = new TileInstanceManager(materialManager);
}
// TODO: finish impl
if (false) {
engine = new BatchingEngine();
entityInstanceManager = new EntityInstanceManager(engine);
tileEntityInstanceManager = new TileInstanceManager(engine);
} else {
InstancingEngine<WorldProgram> manager = InstancingEngine.builder(Contexts.WORLD)
.build();
public MaterialManager getMaterialManager() {
return materialManager;
entityInstanceManager = new EntityInstanceManager(manager);
tileEntityInstanceManager = new TileInstanceManager(manager);
manager.addListener(entityInstanceManager);
manager.addListener(tileEntityInstanceManager);
engine = manager;
}
}
public InstanceManager<Entity> getEntityInstanceManager() {
@ -50,18 +58,9 @@ public class InstanceWorld {
* Free all acquired resources and invalidate this instance world.
*/
public void delete() {
materialManager.delete();
}
/**
* Instantiate all the necessary instances to render the given world.
*/
public void loadAll(ClientLevel world) {
ChunkIter.forEachChunk(world, chunk -> {
chunk.getBlockEntities().values().forEach(tileEntityInstanceManager::add);
});
world.entitiesForRendering()
.forEach(entityInstanceManager::add);
engine.delete();
entityInstanceManager.detachLightListeners();
tileEntityInstanceManager.detachLightListeners();
}
/**
@ -73,7 +72,7 @@ public class InstanceWorld {
* </p>
*/
public void beginFrame(BeginFrameEvent event) {
materialManager.beginFrame(event.getInfo());
engine.beginFrame(event.getInfo());
tileEntityInstanceManager.beginFrame(event.getInfo());
entityInstanceManager.beginFrame(event.getInfo());
@ -99,6 +98,6 @@ public class InstanceWorld {
* Draw the given layer.
*/
public void renderLayer(RenderLayerEvent event) {
materialManager.render(event.layer, event.viewProjection, event.camX, event.camY, event.camZ);
engine.render(event, event.buffers.bufferSource());
}
}

View file

@ -76,13 +76,12 @@ public class InstancedRenderDispatcher {
ClientLevel world = event.getWorld();
if (Backend.getInstance()
.canUseInstancing() && world != null) {
loadAllInWorld(world);
resetInstanceWorld(world);
}
}
public static void loadAllInWorld(ClientLevel world) {
instanceWorlds.replace(world, InstanceWorld::delete)
.loadAll(world);
public static void resetInstanceWorld(ClientLevel world) {
instanceWorlds.replace(world, InstanceWorld::delete);
}
}

View file

@ -5,11 +5,12 @@ import java.util.Map;
import javax.annotation.Nullable;
import com.google.common.collect.Maps;
import com.jozufozu.flywheel.api.FlywheelRendered;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.backend.instancing.entity.IEntityInstanceFactory;
import com.jozufozu.flywheel.backend.instancing.tile.ITileInstanceFactory;
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.api.MaterialManager;
import it.unimi.dsi.fastutil.objects.Object2BooleanLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@ -34,11 +35,11 @@ public class InstancedRenderRegistry {
}
public <T extends BlockEntity> boolean shouldSkipRender(T type) {
return _skipRender(type.getType()) || ((type instanceof IInstanceRendered) && !((IInstanceRendered) type).shouldRenderNormally());
return _skipRender(type.getType()) || ((type instanceof FlywheelRendered) && !((FlywheelRendered) type).shouldRenderNormally());
}
public <T extends Entity> boolean shouldSkipRender(T type) {
return _skipRender(type.getType()) || ((type instanceof IInstanceRendered) && !((IInstanceRendered) type).shouldRenderNormally());
return _skipRender(type.getType()) || ((type instanceof FlywheelRendered) && !((FlywheelRendered) type).shouldRenderNormally());
}
public <T extends BlockEntity> boolean canInstance(BlockEntityType<? extends T> type) {

View file

@ -0,0 +1,27 @@
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.
*/
void render(RenderLayerEvent event, MultiBufferSource buffers);
/**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.
* <p>
* This prevents floating point precision issues at high coordinates.
*/
void beginFrame(Camera info);
default void delete() {
}
}

View file

@ -0,0 +1,44 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashMap;
import java.util.Map;
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.core.model.Model;
import com.mojang.blaze3d.vertex.PoseStack;
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;
public BatchedMaterial(StructType<D> type) {
this.type = type;
this.models = new HashMap<>();
}
@Override
public Instancer<D> model(Object key, Supplier<Model> modelSupplier) {
return models.computeIfAbsent(key, $ -> new CPUInstancer<>(type, modelSupplier.get()));
}
public void render(PoseStack stack, VertexConsumer buffer) {
for (CPUInstancer<D> instancer : models.values()) {
instancer.drawAll(stack, buffer);
}
}
/**
* Clear all instance data without freeing resources.
*/
public void clear() {
models.values()
.forEach(CPUInstancer::clear);
}
}

View file

@ -0,0 +1,52 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.StructType;
import com.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<>();
public BatchedMaterialGroup(RenderType state) {
this.state = state;
}
/**
* Get the material as defined by the given {@link StructType type}.
* @param spec The material you want to create instances with.
* @param <D> The type representing the per instance data.
* @return A
*/
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> BatchedMaterial<D> material(StructType<D> spec) {
return (BatchedMaterial<D>) materials.computeIfAbsent(spec, BatchedMaterial::new);
}
public void render(PoseStack stack, MultiBufferSource source) {
VertexConsumer buffer = source.getBuffer(state);
for (BatchedMaterial<?> value : materials.values()) {
value.render(stack, buffer);
}
}
public void clear() {
materials.values().forEach(BatchedMaterial::clear);
}
public void delete() {
materials.clear();
}
}

View file

@ -0,0 +1,54 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.event.RenderLayerEvent;
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;
public class BatchingEngine implements Engine {
protected BlockPos originCoordinate = BlockPos.ZERO;
protected final Map<RenderLayer, Map<RenderType, BatchedMaterialGroup>> layers;
public BatchingEngine() {
this.layers = new EnumMap<>(RenderLayer.class);
for (RenderLayer value : RenderLayer.values()) {
layers.put(value, new HashMap<>());
}
}
@Override
public MaterialGroup state(RenderLayer layer, RenderType state) {
return layers.get(layer).computeIfAbsent(state, BatchedMaterialGroup::new);
}
@Override
public Vec3i getOriginCoordinate() {
return originCoordinate;
}
@Override
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
for (Map.Entry<RenderType, BatchedMaterialGroup> entry : layers.get(event.getLayer()).entrySet()) {
BatchedMaterialGroup group = entry.getValue();
group.render(event.stack, buffers);
}
}
@Override
public void beginFrame(Camera info) {
}
}

View file

@ -0,0 +1,46 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import com.jozufozu.flywheel.backend.instancing.AbstractInstancer;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.struct.BatchingTransformer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.model.Model;
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;
public CPUInstancer(StructType<D> type, Model modelData) {
super(type, modelData);
renderer = type.asBatched()
.getTransformer(modelData);
}
@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,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.instancing.batching;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.light.GridAlignedBB;
import com.jozufozu.flywheel.light.ILightUpdateListener;
import com.jozufozu.flywheel.light.IMovingListener;
import com.jozufozu.flywheel.light.LightListener;
import com.jozufozu.flywheel.light.MovingListener;
import com.jozufozu.flywheel.light.LightProvider;
import com.mojang.math.Vector3f;
@ -34,7 +34,7 @@ import net.minecraft.world.phys.Vec3;
*
* @param <E> The type of {@link Entity} your class is an instance of.
*/
public abstract class EntityInstance<E extends Entity> extends AbstractInstance implements ILightUpdateListener, IMovingListener {
public abstract class EntityInstance<E extends Entity> extends AbstractInstance implements LightListener, MovingListener {
protected final E entity;
protected final GridAlignedBB bounds;

View file

@ -4,7 +4,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.material.MaterialManagerImpl;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
@ -13,7 +13,7 @@ import net.minecraft.world.level.Level;
public class EntityInstanceManager extends InstanceManager<Entity> {
public EntityInstanceManager(MaterialManagerImpl<?> materialManager) {
public EntityInstanceManager(MaterialManager materialManager) {
super(materialManager);
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.entity;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.entity.Entity;

View file

@ -1,7 +1,8 @@
package com.jozufozu.flywheel.backend.instancing;
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.BitSet;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
@ -9,11 +10,13 @@ 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.api.InstanceData;
import com.jozufozu.flywheel.backend.model.IBufferedModel;
import com.jozufozu.flywheel.backend.model.ModelAllocator;
import com.jozufozu.flywheel.backend.struct.StructType;
import com.jozufozu.flywheel.backend.struct.StructWriter;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.AttribUtil;
public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
@ -29,12 +32,19 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
private boolean deleted;
private boolean initialized;
public GPUInstancer(StructType<D> type, IModel model, ModelAllocator modelAllocator) {
protected boolean anyToUpdate;
public GPUInstancer(StructType<D> type, Model model, ModelAllocator modelAllocator) {
super(type, model);
this.modelAllocator = modelAllocator;
this.instanceFormat = type.format();
}
@Override
public void notifyDirty() {
anyToUpdate = true;
}
public void render() {
if (invalid()) return;
@ -51,10 +61,6 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
// persistent mapping sync point
instanceVBO.doneForThisFrame();
vao.unbind();
GlError.pollAndThrow(() -> modelData.name() + "_unbind");
}
private boolean invalid() {
@ -72,16 +78,12 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
vao.bind();
model.setupState();
vao.unbind();
});
vao.bind();
instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER);
AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
vao.unbind();
}
public boolean isInitialized() {
@ -135,9 +137,11 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
final int offset = size * instanceFormat.getStride();
final int length = glBufferSize - offset;
if (length > 0) {
instanceVBO.getBuffer(offset, length)
.putByteArray(new byte[length])
.flush();
try (MappedBuffer buf = instanceVBO.getBuffer(offset, length)) {
buf.putByteArray(new byte[length]);
} catch (Exception e) {
Flywheel.log.error("Error clearing buffer tail:", e);
}
}
}
@ -158,16 +162,19 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
final int length = (1 + lastDirty - firstDirty) * stride;
if (length > 0) {
MappedBuffer mapped = instanceVBO.getBuffer(offset, length);
try (MappedBuffer mapped = instanceVBO.getBuffer(offset, length)) {
StructWriter<D> writer = type.asWriteable().getWriter(mapped);
StructWriter<D> writer = type.asInstanced()
.getWriter(mapped);
dirtySet.stream()
.forEach(i -> {
writer.seek(i);
writer.write(data.get(i));
});
mapped.flush();
dirtySet.stream()
.forEach(i -> {
writer.seek(i);
writer.write(data.get(i));
});
} catch (Exception e) {
Flywheel.log.error("Error updating GPUInstancer:", e);
}
}
}
@ -179,12 +186,15 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
glBufferSize = requiredSize + stride * 16;
instanceVBO.alloc(glBufferSize);
MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize);
StructWriter<D> writer = type.asWriteable().getWriter(buffer);
for (D datum : data) {
writer.write(datum);
try (MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize)) {
StructWriter<D> writer = type.asInstanced()
.getWriter(buffer);
for (D datum : data) {
writer.write(datum);
}
} catch (Exception e) {
Flywheel.log.error("Error reallocating GPUInstancer:", e);
}
buffer.flush();
glInstanceCount = size;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.material;
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
@ -6,27 +6,28 @@ import java.util.function.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.jozufozu.flywheel.backend.RenderWork;
import com.jozufozu.flywheel.backend.instancing.GPUInstancer;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.backend.instancing.Instancer;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.backend.model.ModelPool;
import com.jozufozu.flywheel.backend.struct.StructType;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Model;
/**
* A collection of Instancers that all have the same format.
* @param <D>
*/
public class MaterialImpl<D extends InstanceData> implements Material<D> {
public class InstancedMaterial<D extends InstanceData> implements Material<D> {
final ModelPool modelPool;
protected final Cache<Object, GPUInstancer<D>> models;
protected final StructType<D> type;
public MaterialImpl(MaterialSpec<D> spec) {
this.type = spec.getInstanceType();
public InstancedMaterial(StructType<D> spec) {
this.type = spec;
modelPool = new ModelPool(spec.getModelFormat(), 64);
modelPool = new ModelPool(Formats.UNLIT_MODEL, 64);
this.models = CacheBuilder.newBuilder()
.removalListener(notification -> {
GPUInstancer<?> instancer = (GPUInstancer<?>) notification.getValue();
@ -43,7 +44,7 @@ public class MaterialImpl<D extends InstanceData> implements Material<D> {
* @return An instancer for the given model, capable of rendering many copies for little cost.
*/
@Override
public Instancer<D> model(Object key, Supplier<IModel> modelSupplier) {
public Instancer<D> model(Object key, Supplier<Model> modelSupplier) {
try {
return models.get(key, () -> new GPUInstancer<>(type, modelSupplier.get(), modelPool));
} catch (ExecutionException e) {

View file

@ -0,0 +1,81 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.api.InstanceData;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.util.TextureBinder;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
/**
* A group of materials all rendered with the same GL state.
*
* The children of a material group will all be rendered at the same time.
* No guarantees are made about the order of draw calls.
*/
public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialGroup {
protected final InstancingEngine<P> owner;
protected final RenderType type;
protected final ArrayList<InstancedMaterialRenderer<P>> renderers = new ArrayList<>();
private final Map<StructType<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
public InstancedMaterialGroup(InstancingEngine<P> owner, RenderType type) {
this.owner = owner;
this.type = type;
}
/**
* Get the material as defined by the given {@link MaterialSpec spec}.
* @param spec The material you want to create instances with.
* @param <D> The type representing the per instance data.
* @return A
*/
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> InstancedMaterial<D> material(StructType<D> spec) {
return (InstancedMaterial<D>) materials.computeIfAbsent(spec, this::createInstanceMaterial);
}
public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
type.setupRenderState();
TextureBinder.bindActiveTextures();
for (InstancedMaterialRenderer<P> renderer : renderers) {
renderer.render(viewProjection, camX, camY, camZ);
}
type.clearRenderState();
}
public void setup(P program) {
}
public void clear() {
materials.values().forEach(InstancedMaterial::clear);
}
public void delete() {
materials.values()
.forEach(InstancedMaterial::delete);
materials.clear();
renderers.clear();
}
private InstancedMaterial<?> createInstanceMaterial(StructType<? extends InstanceData> type) {
InstancedMaterial<?> material = new InstancedMaterial<>(type);
this.renderers.add(new InstancedMaterialRenderer<>(owner.getProgram(type.asInstanced()
.getProgramSpec()), material, this::setup));
return material;
}
}

View file

@ -1,21 +1,20 @@
package com.jozufozu.flywheel.backend.material;
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.instancing.GPUInstancer;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.mojang.math.Matrix4f;
public class MaterialRenderer<P extends WorldProgram> {
public class InstancedMaterialRenderer<P extends WorldProgram> {
protected final Supplier<P> program;
protected final MaterialImpl<?> material;
protected final InstancedMaterial<?> material;
protected final Consumer<P> setupFunc;
public MaterialRenderer(Supplier<P> programSupplier, MaterialImpl<?> material, Consumer<P> setupFunc) {
public InstancedMaterialRenderer(Supplier<P> programSupplier, InstancedMaterial<?> material, Consumer<P> setupFunc) {
this.program = programSupplier;
this.material = material;
this.setupFunc = setupFunc;

View file

@ -1,25 +1,34 @@
package com.jozufozu.flywheel.backend.material;
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.jozufozu.flywheel.backend.state.RenderLayer;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.backend.RenderLayer;
import com.jozufozu.flywheel.core.WorldContext;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.fabric.helper.Matrix4fHelper;
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;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
public class MaterialManagerImpl<P extends WorldProgram> implements MaterialManager {
public class InstancingEngine<P extends WorldProgram> implements Engine {
public static int MAX_ORIGIN_DISTANCE = 100;
@ -29,19 +38,19 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
protected final GroupFactory<P> groupFactory;
protected final boolean ignoreOriginCoordinate;
protected final Map<RenderLayer, Map<RenderType, MaterialGroupImpl<P>>> layers;
protected final Map<RenderLayer, Map<RenderType, InstancedMaterialGroup<P>>> layers;
private final WeakHashSet<OriginShiftListener> listeners;
public MaterialManagerImpl(WorldContext<P> context) {
this(context, MaterialGroupImpl::new, false);
public InstancingEngine(WorldContext<P> context) {
this(context, InstancedMaterialGroup::new, false);
}
public static <P extends WorldProgram> Builder<P> builder(WorldContext<P> context) {
return new Builder<>(context);
}
public MaterialManagerImpl(WorldContext<P> context, GroupFactory<P> groupFactory, boolean ignoreOriginCoordinate) {
public InstancingEngine(WorldContext<P> context, GroupFactory<P> groupFactory, boolean ignoreOriginCoordinate) {
this.context = context;
this.ignoreOriginCoordinate = ignoreOriginCoordinate;
@ -55,47 +64,65 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
}
/**
* Get a material group that will render in the given layer with the given state.
* Get a material group that will render in the given layer with the given type.
*
* @param layer The {@link RenderLayer} you want to draw in.
* @param state The {@link RenderType} you need to draw with.
* @param type The {@link RenderType} you need to draw with.
* @return A material group whose children will
*/
@Override
public MaterialGroup state(RenderLayer layer, RenderType state) {
return layers.get(layer).computeIfAbsent(state, $ -> groupFactory.create(this));
public MaterialGroup state(RenderLayer layer, RenderType type) {
return layers.get(layer).computeIfAbsent(type, t -> groupFactory.create(this, t));
}
/**
* Render every model for every material.
* @param layer Which of the 3 {@link RenderLayer render layers} is being drawn?
* @param viewProjection How do we get from camera space to clip space?
*/
public void render(RenderLayer layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
@Override
public void render(RenderLayerEvent event, MultiBufferSource buffers) {
double camX;
double camY;
double camZ;
Matrix4f viewProjection;
if (!ignoreOriginCoordinate) {
camX -= originCoordinate.getX();
camY -= originCoordinate.getY();
camZ -= originCoordinate.getZ();
camX = event.camX - originCoordinate.getX();
camY = event.camY - originCoordinate.getY();
camZ = event.camZ - originCoordinate.getZ();
Matrix4f translate = Matrix4f.createTranslateMatrix((float) -camX, (float) -camY, (float) -camZ);
Matrix4fHelper.multiplyBackward(translate, viewProjection);
viewProjection = translate;
viewProjection = Matrix4f.createTranslateMatrix((float) -camX, (float) -camY, (float) -camZ);
Matrix4fHelper.multiplyBackward(viewProjection, event.viewProjection);
} else {
camX = event.camX;
camY = event.camY;
camZ = event.camZ;
viewProjection = event.viewProjection;
}
for (Map.Entry<RenderType, MaterialGroupImpl<P>> entry : layers.get(layer).entrySet()) {
RenderType state = entry.getKey();
MaterialGroupImpl<P> group = entry.getValue();
getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ));
group.render(state, viewProjection, camX, camY, camZ);
GlBufferType.ELEMENT_ARRAY_BUFFER.unbind();
GlBufferType.ARRAY_BUFFER.unbind();
GlVertexArray.unbind();
}
private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) {
if (layer != null) {
return layers.get(layer)
.values()
.stream();
} else {
return layers.values()
.stream()
.flatMap(it -> it.values()
.stream());
}
}
@Override
public void delete() {
for (Map<RenderType, MaterialGroupImpl<P>> groups : layers.values()) {
for (Map<RenderType, InstancedMaterialGroup<P>> groups : layers.values()) {
groups.values().forEach(MaterialGroupImpl::delete);
groups.values().forEach(InstancedMaterialGroup::delete);
}
}
@ -117,6 +144,7 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
*
* This prevents floating point precision issues at high coordinates.
*/
@Override
public void beginFrame(Camera info) {
int cX = Mth.floor(info.getPosition().x);
int cY = Mth.floor(info.getPosition().y);
@ -130,8 +158,8 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
originCoordinate = new BlockPos(cX, cY, cZ);
for (Map<RenderType, MaterialGroupImpl<P>> groups : layers.values()) {
groups.values().forEach(MaterialGroupImpl::clear);
for (Map<RenderType, InstancedMaterialGroup<P>> groups : layers.values()) {
groups.values().forEach(InstancedMaterialGroup::clear);
}
listeners.forEach(OriginShiftListener::onOriginShift);
@ -145,12 +173,12 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
@FunctionalInterface
public interface GroupFactory<P extends WorldProgram> {
MaterialGroupImpl<P> create(MaterialManagerImpl<P> materialManager);
InstancedMaterialGroup<P> create(InstancingEngine<P> engine, RenderType type);
}
public static class Builder<P extends WorldProgram> {
protected final WorldContext<P> context;
protected GroupFactory<P> groupFactory = MaterialGroupImpl::new;
protected GroupFactory<P> groupFactory = InstancedMaterialGroup::new;
protected boolean ignoreOriginCoordinate;
public Builder(WorldContext<P> context) {
@ -167,8 +195,8 @@ public class MaterialManagerImpl<P extends WorldProgram> implements MaterialMana
return this;
}
public MaterialManagerImpl<P> build() {
return new MaterialManagerImpl<>(context, groupFactory, ignoreOriginCoordinate);
public InstancingEngine<P> build() {
return new InstancingEngine<>(context, groupFactory, ignoreOriginCoordinate);
}
}
}

View file

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

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.tile;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.world.level.block.entity.BlockEntity;

View file

@ -1,10 +1,10 @@
package com.jozufozu.flywheel.backend.instancing.tile;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
import com.jozufozu.flywheel.backend.material.Material;
import com.jozufozu.flywheel.backend.material.MaterialManager;
import com.jozufozu.flywheel.api.instance.IDynamicInstance;
import com.jozufozu.flywheel.api.instance.ITickableInstance;
import com.jozufozu.flywheel.api.Material;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;

View file

@ -4,7 +4,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.material.MaterialManagerImpl;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
@ -13,7 +13,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
public class TileInstanceManager extends InstanceManager<BlockEntity> {
public TileInstanceManager(MaterialManagerImpl<?> materialManager) {
public TileInstanceManager(MaterialManager materialManager) {
super(materialManager);
}

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.backend.material;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
public interface MaterialGroup {
/**
* Get the material as defined by the given {@link MaterialSpec spec}.
*
* @param spec The material you want to create instances with.
* @param <D> The type representing the per instance data.
* @return A material you can use to render models.
*/
<D extends InstanceData> Material<D> material(MaterialSpec<D> spec);
}

View file

@ -1,76 +0,0 @@
package com.jozufozu.flywheel.backend.material;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.util.TextureBinder;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
/**
* A group of materials all rendered with the same GL state.
*
* The children of a material group will all be rendered at the same time.
* No guarantees are made about the order of draw calls.
*/
public class MaterialGroupImpl<P extends WorldProgram> implements MaterialGroup {
protected final MaterialManagerImpl<P> owner;
protected final ArrayList<MaterialRenderer<P>> renderers = new ArrayList<>();
private final Map<MaterialSpec<?>, MaterialImpl<?>> materials = new HashMap<>();
public MaterialGroupImpl(MaterialManagerImpl<P> owner) {
this.owner = owner;
}
/**
* Get the material as defined by the given {@link MaterialSpec spec}.
* @param spec The material you want to create instances with.
* @param <D> The type representing the per instance data.
* @return A
*/
@SuppressWarnings("unchecked")
@Override
public <D extends InstanceData> MaterialImpl<D> material(MaterialSpec<D> spec) {
return (MaterialImpl<D>) materials.computeIfAbsent(spec, this::createInstanceMaterial);
}
public void render(RenderType type, Matrix4f viewProjection, double camX, double camY, double camZ) {
type.setupRenderState();
TextureBinder.bindActiveTextures();
for (MaterialRenderer<P> renderer : renderers) {
renderer.render(viewProjection, camX, camY, camZ);
}
type.clearRenderState();
}
public void setup(P program) {
}
public void clear() {
materials.values().forEach(MaterialImpl::clear);
}
public void delete() {
materials.values()
.forEach(MaterialImpl::delete);
materials.clear();
renderers.clear();
}
private MaterialImpl<?> createInstanceMaterial(MaterialSpec<?> type) {
MaterialImpl<?> material = new MaterialImpl<>(type);
this.renderers.add(new MaterialRenderer<>(owner.getProgram(type.getProgramName()), material, this::setup));
return material;
}
}

View file

@ -1,36 +0,0 @@
package com.jozufozu.flywheel.backend.material;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.instancing.InstanceData;
import com.jozufozu.flywheel.backend.struct.StructType;
import net.minecraft.resources.ResourceLocation;
public class MaterialSpec<D extends InstanceData> {
public final ResourceLocation name;
private final ResourceLocation programSpec;
private final VertexFormat modelFormat;
private final StructType<D> instanceType;
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, StructType<D> type) {
this.name = name;
this.programSpec = programSpec;
this.modelFormat = modelFormat;
this.instanceType = type;
}
public ResourceLocation getProgramName() {
return programSpec;
}
public VertexFormat getModelFormat() {
return modelFormat;
}
public StructType<D> getInstanceType() {
return instanceType;
}
}

View file

@ -3,14 +3,14 @@ package com.jozufozu.flywheel.backend.model;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.AttribUtil;
public class ArrayModelRenderer extends ModelRenderer {
protected GlVertexArray vao;
public ArrayModelRenderer(Supplier<IModel> model) {
public ArrayModelRenderer(Supplier<Model> model) {
super(model);
}
@ -22,14 +22,12 @@ public class ArrayModelRenderer extends ModelRenderer {
vao.bind();
model.drawCall();
vao.unbind();
}
@Override
protected void init() {
initialized = true;
IModel model = modelSupplier.get();
Model model = modelSupplier.get();
if (model.empty()) return;
@ -44,7 +42,7 @@ public class ArrayModelRenderer extends ModelRenderer {
AttribUtil.enableArrays(this.model.getAttributeCount());
vao.unbind();
GlVertexArray.unbind();
this.model.clearState();
}

View file

@ -2,25 +2,27 @@ package com.jozufozu.flywheel.backend.model;
import static org.lwjgl.opengl.GL11.glDrawArrays;
import com.jozufozu.flywheel.backend.Backend;
import org.lwjgl.opengl.GL31;
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.IModel;
import com.jozufozu.flywheel.core.model.VecBufferConsumer;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.VecBufferWriter;
import com.jozufozu.flywheel.util.AttribUtil;
public class BufferedModel implements IBufferedModel {
protected final IModel model;
protected final Model model;
protected final GlPrimitive primitiveMode;
protected GlBuffer vbo;
protected boolean deleted;
public BufferedModel(GlPrimitive primitiveMode, IModel model) {
public BufferedModel(GlPrimitive primitiveMode, Model model) {
this.model = model;
this.primitiveMode = primitiveMode;
@ -31,9 +33,11 @@ public class BufferedModel implements IBufferedModel {
vbo.alloc(model.size());
// mirror it in system memory so we can write to it, and upload our model.
MappedBuffer buffer = vbo.getBuffer(0, model.size());
model.buffer(new VecBufferConsumer(buffer, model.format()));
buffer.flush();
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();
}
@ -74,7 +78,7 @@ public class BufferedModel implements IBufferedModel {
public void drawInstances(int instanceCount) {
if (!valid()) return;
Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, getVertexCount(), instanceCount);
GL31.glDrawArraysInstanced(primitiveMode.glEnum, 0, getVertexCount(), instanceCount);
}
public void delete() {

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
public class ImmediateAllocator implements ModelAllocator {
public static final ImmediateAllocator INSTANCE = new ImmediateAllocator();
@Override
public IBufferedModel alloc(IModel model, Callback allocationCallback) {
public IBufferedModel alloc(Model model, Callback allocationCallback) {
IndexedModel out = new IndexedModel(model);
allocationCallback.onAlloc(out);
return out;

View file

@ -1,10 +1,10 @@
package com.jozufozu.flywheel.backend.model;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
/**
* An indexed triangle model. Just what the driver ordered.
@ -15,7 +15,7 @@ public class IndexedModel extends BufferedModel {
protected ElementBuffer ebo;
public IndexedModel(IModel model) {
public IndexedModel(Model model) {
super(GlPrimitive.TRIANGLES, model);
this.ebo = model.createEBO();
@ -35,6 +35,7 @@ public class IndexedModel extends BufferedModel {
@Override
public void drawCall() {
ebo.bind();
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
}
@ -42,6 +43,6 @@ public class IndexedModel extends BufferedModel {
public void drawInstances(int instanceCount) {
if (!valid()) return;
Backend.getInstance().compat.drawInstanced.drawElementsInstanced(primitiveMode, ebo.elementCount, ebo.eboIndexType, 0, instanceCount);
GL31.glDrawElementsInstanced(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount);
}
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.model;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
public interface ModelAllocator {
/**
@ -9,7 +9,7 @@ public interface ModelAllocator {
* @param model The model to allocate.
* @return A handle to the allocated model.
*/
IBufferedModel alloc(IModel model, Callback allocationCallback);
IBufferedModel alloc(Model model, Callback allocationCallback);
@FunctionalInterface
interface Callback {

View file

@ -3,15 +3,17 @@ package com.jozufozu.flywheel.backend.model;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.backend.Backend;
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.IModel;
import com.jozufozu.flywheel.core.model.VecBufferConsumer;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.VecBufferWriter;
import com.jozufozu.flywheel.util.AttribUtil;
public class ModelPool implements ModelAllocator {
@ -48,7 +50,7 @@ public class ModelPool implements ModelAllocator {
* @return A handle to the allocated model.
*/
@Override
public PooledModel alloc(IModel model, Callback callback) {
public PooledModel alloc(Model model, Callback callback) {
PooledModel bufferedModel = new PooledModel(model, vertices);
bufferedModel.callback = callback;
vertices += model.vertexCount();
@ -114,34 +116,35 @@ public class ModelPool implements ModelAllocator {
}
private void uploadAll() {
MappedBuffer buffer = vbo.getBuffer(0, bufferSize);
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
VecBufferConsumer consumer = new VecBufferConsumer(buffer, format);
VecBufferWriter consumer = new VecBufferWriter(buffer);
for (PooledModel model : models) {
model.model.buffer(consumer);
if (model.callback != null)
model.callback.onAlloc(model);
for (PooledModel model : models) {
model.model.buffer(consumer);
if (model.callback != null) model.callback.onAlloc(model);
}
} catch (Exception e) {
Flywheel.log.error("Error uploading pooled models:", e);
}
buffer.flush();
}
private void uploadPending() {
MappedBuffer buffer = vbo.getBuffer(0, bufferSize);
VecBufferConsumer consumer = new VecBufferConsumer(buffer, format);
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
VecBufferWriter consumer = new VecBufferWriter(buffer);
int stride = format.getStride();
for (PooledModel model : pendingUpload) {
int pos = model.first * stride;
buffer.position(pos);
model.model.buffer(consumer);
if (model.callback != null)
model.callback.onAlloc(model);
int stride = format.getStride();
for (PooledModel model : pendingUpload) {
int pos = model.first * stride;
buffer.position(pos);
model.model.buffer(consumer);
if (model.callback != null) model.callback.onAlloc(model);
}
pendingUpload.clear();
} catch (Exception e) {
Flywheel.log.error("Error uploading pooled models:", e);
}
pendingUpload.clear();
buffer.flush();
}
private void setDirty() {
@ -157,12 +160,12 @@ public class ModelPool implements ModelAllocator {
private final ElementBuffer ebo;
private Callback callback;
private final IModel model;
private final Model model;
private int first;
private boolean remove;
public PooledModel(IModel model, int first) {
public PooledModel(Model model, int first) {
this.model = model;
this.first = first;
ebo = model.createEBO();
@ -195,7 +198,7 @@ public class ModelPool implements ModelAllocator {
@Override
public void drawCall() {
Backend.getInstance().compat.baseVertex.drawElementsBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, first);
GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first);
}
@Override
@ -206,7 +209,7 @@ public class ModelPool implements ModelAllocator {
//Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first));
Backend.getInstance().compat.baseVertex.drawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first);
GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first);
}
@Override

View file

@ -2,16 +2,16 @@ package com.jozufozu.flywheel.backend.model;
import java.util.function.Supplier;
import com.jozufozu.flywheel.core.model.IModel;
import com.jozufozu.flywheel.core.model.Model;
public class ModelRenderer {
protected Supplier<IModel> modelSupplier;
protected Supplier<Model> modelSupplier;
protected IBufferedModel model;
protected boolean initialized;
public ModelRenderer(Supplier<IModel> modelSupplier) {
public ModelRenderer(Supplier<Model> modelSupplier) {
this.modelSupplier = modelSupplier;
}
@ -34,7 +34,7 @@ public class ModelRenderer {
protected void init() {
initialized = true;
IModel model = modelSupplier.get();
Model model = modelSupplier.get();
if (model.empty()) return;

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.backend.pipeline;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
@ -8,8 +8,8 @@ import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
* The main interface for compiling usable shaders from program specs.
* @param <P> the type of the program that this pipeline compiles.
*/
public interface IShaderPipeline<P extends WorldProgram> {
public interface ShaderPipeline<P extends WorldProgram> {
IMultiProgram<P> compile(ProgramSpec spec);
ContextAwareProgram<P> compile(ProgramSpec spec);
}

View file

@ -9,14 +9,14 @@ import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
import com.jozufozu.flywheel.core.shader.GameStateProgram;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
import net.minecraft.resources.ResourceLocation;
public class WorldShaderPipeline<P extends WorldProgram> implements IShaderPipeline<P> {
public class WorldShaderPipeline<P extends WorldProgram> implements ShaderPipeline<P> {
private final ExtensibleGlProgram.Factory<P> factory;
@ -29,14 +29,14 @@ public class WorldShaderPipeline<P extends WorldProgram> implements IShaderPipel
this.header = header;
}
public IMultiProgram<P> compile(ProgramSpec spec) {
public ContextAwareProgram<P> compile(ProgramSpec spec) {
SourceFile file = spec.getSource().getFile();
return compile(spec.name, file, spec.getStates());
}
public IMultiProgram<P> compile(ResourceLocation name, SourceFile file, List<ProgramState> variants) {
public ContextAwareProgram<P> compile(ResourceLocation name, SourceFile file, List<ProgramState> variants) {
WorldShader shader = new WorldShader(name, template, header)
.setMainSource(file);

View file

@ -1,5 +1,7 @@
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;

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.struct;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
/**

View file

@ -25,7 +25,6 @@ import net.fabricmc.loader.api.FabricLoader;
public class FlwConfig {
protected static final Logger LOGGER = LogManager.getLogger("Flywheel Config");
protected static final JsonParser PARSER = new JsonParser();
protected static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final FlwConfig INSTANCE = new FlwConfig(FabricLoader.getInstance().getConfigDir().resolve("flywheel.json").toFile());
@ -63,7 +62,7 @@ public class FlwConfig {
public void load() {
if (file.exists()) {
try (FileReader reader = new FileReader(file)) {
fromJson(PARSER.parse(reader));
fromJson(JsonParser.parseReader(reader));
} catch (Exception e) {
LOGGER.error("Could not load config from file '" + file.getAbsolutePath() + "'", e);
}

View file

@ -3,7 +3,7 @@ package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.GameStateRegistry;
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
import com.jozufozu.flywheel.backend.pipeline.InstancingTemplate;
import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline;
import com.jozufozu.flywheel.backend.source.FileResolution;
@ -29,8 +29,8 @@ public class Contexts {
FileResolution crumblingBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
FileResolution worldBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.WORLD, ".glsl"));
IShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
IShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
ShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
ShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline));
WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline));

View file

@ -1,11 +1,15 @@
package com.jozufozu.flywheel.core;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.GlNumericType;
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
import com.jozufozu.flywheel.util.Lazy;
@ -28,25 +32,27 @@ public class FullscreenQuad {
vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER);
vbo.bind();
vbo.alloc(bufferSize);
vbo.getBuffer(0, bufferSize)
.putFloatArray(vertices)
.flush();
try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) {
buffer.putFloatArray(vertices);
} catch (Exception e) {
Flywheel.log.error("Could not create fullscreen quad.", e);
}
vao = new GlVertexArray();
vao.bind();
GL20.glEnableVertexAttribArray(0);
glEnableVertexAttribArray(0);
GL20.glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0);
glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0);
vao.unbind();
GlVertexArray.unbind();
vbo.unbind();
}
public void draw() {
vao.bind();
GL20.glDrawArrays(GL20.GL_TRIANGLES, 0, 6);
vao.unbind();
glDrawArrays(GL_TRIANGLES, 0, 6);
GlVertexArray.unbind();
}
public void delete() {

View file

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.backend.material.MaterialSpec;
import com.jozufozu.flywheel.backend.struct.StructType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.model.ModelType;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
@ -11,17 +11,14 @@ import com.jozufozu.flywheel.event.GatherContextEvent;
import net.minecraft.resources.ResourceLocation;
public class Materials {
public static final StructType<OrientedData> ORIENTED_TYPE = new OrientedType();
public static final StructType<ModelData> TRANSFORMED_TYPE = new ModelType();
public static final MaterialSpec<OrientedData> ORIENTED = new MaterialSpec<>(Names.ORIENTED, Programs.ORIENTED, Formats.UNLIT_MODEL, ORIENTED_TYPE);
public static final MaterialSpec<ModelData> TRANSFORMED = new MaterialSpec<>(Names.MODEL, Programs.TRANSFORMED, Formats.UNLIT_MODEL, TRANSFORMED_TYPE);
public static final StructType<OrientedData> ORIENTED = new OrientedType();
public static final StructType<ModelData> TRANSFORMED = new ModelType();
public static void flwInit(GatherContextEvent event) {
event.getBackend()
.register(ORIENTED);
event.getBackend()
.register(TRANSFORMED);
Backend backend = event.getBackend();
backend.register(Names.ORIENTED, ORIENTED);
backend.register(Names.MODEL, TRANSFORMED);
}
public static class Names {

View file

@ -6,24 +6,23 @@ import java.util.function.Supplier;
import java.util.stream.Stream;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.IShaderContext;
import com.jozufozu.flywheel.backend.material.MaterialSpec;
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
import com.jozufozu.flywheel.core.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
import net.minecraft.resources.ResourceLocation;
public class WorldContext<P extends WorldProgram> implements IShaderContext<P> {
public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
public final Backend backend;
protected final Map<ResourceLocation, IMultiProgram<P>> programs = new HashMap<>();
protected final Map<ResourceLocation, ContextAwareProgram<P>> programs = new HashMap<>();
protected final ResourceLocation name;
protected final Supplier<Stream<ResourceLocation>> specStream;
public final IShaderPipeline<P> pipeline;
public final ShaderPipeline<P> pipeline;
public WorldContext(Backend backend, ResourceLocation name, Supplier<Stream<ResourceLocation>> specStream, IShaderPipeline<P> pipeline) {
public WorldContext(Backend backend, ResourceLocation name, Supplier<Stream<ResourceLocation>> specStream, ShaderPipeline<P> pipeline) {
this.backend = backend;
this.name = name;
this.specStream = specStream;
@ -61,7 +60,7 @@ public class WorldContext<P extends WorldProgram> implements IShaderContext<P> {
@Override
public void delete() {
programs.values()
.forEach(IMultiProgram::delete);
.forEach(ContextAwareProgram::delete);
programs.clear();
}
@ -84,11 +83,12 @@ public class WorldContext<P extends WorldProgram> implements IShaderContext<P> {
return this;
}
public <P extends WorldProgram> WorldContext<P> build(IShaderPipeline<P> pipeline) {
public <P extends WorldProgram> WorldContext<P> build(ShaderPipeline<P> pipeline) {
if (specStream == null) {
specStream = () -> backend.allMaterials()
.stream()
.map(MaterialSpec::getProgramName);
.map(type -> type.asInstanced()
.getProgramSpec());
}
return new WorldContext<>(backend, name, specStream, pipeline);
}

View file

@ -3,6 +3,8 @@ 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;
@ -24,15 +26,18 @@ public class AtlasInfo {
return null;
}
@Nullable
public static SheetData getAtlasData(TextureAtlasSprite texture) {
return getAtlasData(texture.atlas());
}
@Nullable
public static SheetData getAtlasData(TextureAtlas atlas) {
return getAtlasData(atlas.location());
}
public static SheetData getAtlasData(ResourceLocation loc) {
@Nullable
public static SheetData getAtlasData(@Nullable ResourceLocation loc) {
return sheetData.get(loc);
}

View file

@ -1,8 +1,8 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.backend.material.MaterialGroupImpl;
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
import com.jozufozu.flywheel.backend.material.MaterialRenderer;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialRenderer;
import com.jozufozu.flywheel.core.atlas.AtlasInfo;
import com.jozufozu.flywheel.core.atlas.SheetData;
import com.jozufozu.flywheel.util.RenderTextures;
@ -11,33 +11,23 @@ import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public class CrumblingGroup<P extends CrumblingProgram> extends MaterialGroupImpl<P> {
public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMaterialGroup<P> {
private int width;
private int height;
public CrumblingGroup(MaterialManagerImpl<P> owner) {
super(owner);
public CrumblingGroup(InstancingEngine<P> owner, RenderType type) {
super(owner, type);
}
@Override
public void render(RenderType type, Matrix4f viewProjection, double camX, double camY, double camZ) {
public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
type.setupRenderState();
int renderTex = RenderSystem.getShaderTexture(0);
ResourceLocation texture = RenderTextures.getShaderTexture(0);
if (texture != null) {
SheetData atlasData = AtlasInfo.getAtlasData(texture);
width = atlasData.width;
height = atlasData.height;
} else {
width = height = 256;
}
updateAtlasSize();
type.clearRenderState();
@ -49,13 +39,25 @@ public class CrumblingGroup<P extends CrumblingProgram> extends MaterialGroupImp
RenderSystem.setShaderTexture(4, breakingTex);
TextureBinder.bindActiveTextures();
for (MaterialRenderer<P> renderer : renderers) {
for (InstancedMaterialRenderer<P> renderer : renderers) {
renderer.render(viewProjection, camX, camY, camZ);
}
CrumblingRenderer._currentLayer.clearRenderState();
}
private void updateAtlasSize() {
SheetData atlasData = AtlasInfo.getAtlasData(RenderTextures.getShaderTexture(0));
if (atlasData != null) {
width = atlasData.width;
height = atlasData.height;
} else {
width = height = 256;
}
}
@Override
public void setup(P p) {
p.setAtlasSize(width, height);

View file

@ -1,13 +1,13 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
import com.jozufozu.flywheel.api.MaterialManager;
import net.minecraft.core.BlockPos;
public class CrumblingInstanceManager extends TileInstanceManager {
public CrumblingInstanceManager(MaterialManagerImpl<?> materialManager) {
public CrumblingInstanceManager(MaterialManager materialManager) {
super(materialManager);
}

View file

@ -6,17 +6,15 @@ import java.util.SortedSet;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.gl.error.GlError;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
import com.jozufozu.flywheel.backend.state.RenderLayer;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Lazy;
import com.jozufozu.flywheel.util.Pair;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Matrix4f;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -49,17 +47,17 @@ public class CrumblingRenderer {
INVALIDATOR = state.getSecond();
}
public static void renderBreaking(ClientLevel world, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
public static void renderBreaking(RenderLayerEvent event) {
if (!Backend.getInstance()
.canUseInstancing(world)) return;
.canUseInstancing(event.getWorld())) return;
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageTiles(world);
Int2ObjectMap<List<BlockEntity>> activeStages = getActiveStageTiles(event.getWorld());
if (activeStages.isEmpty()) return;
State state = STATE.get();
InstanceManager<BlockEntity> instanceManager = state.instanceManager;
MaterialManagerImpl<CrumblingProgram> materials = state.materialManager;
InstancingEngine<CrumblingProgram> materials = state.materialManager;
TextureManager textureManager = Minecraft.getInstance().getTextureManager();
Camera info = Minecraft.getInstance().gameRenderer.getMainCamera();
@ -73,7 +71,7 @@ public class CrumblingRenderer {
instanceManager.beginFrame(info);
materials.render(RenderLayer.SOLID, viewProjection, cameraX, cameraY, cameraZ);
materials.render(event, null);
instanceManager.invalidate();
}
@ -126,14 +124,15 @@ public class CrumblingRenderer {
}
private static class State {
private final MaterialManagerImpl<CrumblingProgram> materialManager;
private final InstancingEngine<CrumblingProgram> materialManager;
private final InstanceManager<BlockEntity> instanceManager;
private State() {
materialManager = MaterialManagerImpl.builder(Contexts.CRUMBLING)
materialManager = InstancingEngine.builder(Contexts.CRUMBLING)
.setGroupFactory(CrumblingGroup::new)
.build();
instanceManager = new CrumblingInstanceManager(materialManager);
materialManager.addListener(instanceManager);
}
private void kill() {

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