mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
Documentation and organization
- Add a package-info.java to many packages. - Annotate the world parameter in Backend#canUseInstancing as nullable. - New utility constructor for BlockModel - Note that IDynamicInstance#beginFrame and ITickableInstance#tick are run in parallel. - Refactor internals of InstancedRenderDispatcher to group things by InstanceWorlds. - InstanceWorlds take over most responsibility for dispatching calls. - Simplify massive private call chains in InstanceMaterial. - Reorganize methods and add some documentation in MaterialManager, MaterialGroup, InstanceMaterial, and Instancer. - Remove unused field from MaterialSpec. - Remove unused fields from Instancer and InstanceMaterial - Document RenderLayer - Add RenderLayer field to RenderLayerEvent
This commit is contained in:
parent
9c92e4d353
commit
9e066f8d41
26 changed files with 395 additions and 160 deletions
14
build.gradle
14
build.gradle
|
@ -121,6 +121,12 @@ jar {
|
|||
|
||||
jar.finalizedBy('reobfJar')
|
||||
|
||||
javadoc {
|
||||
source = [sourceSets.main.allJava]
|
||||
// prevent java 8's strict doclint for javadocs from failing builds
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from sourceSets.main.allSource
|
||||
archiveBaseName.set(project.archivesBaseName)
|
||||
|
@ -128,8 +134,13 @@ task sourcesJar(type: Jar) {
|
|||
archiveClassifier.set('sources')
|
||||
}
|
||||
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
from javadoc.destinationDir
|
||||
archiveClassifier.set('javadoc')
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar, sourcesJar
|
||||
archives jar, sourcesJar, javadocJar
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@ -138,6 +149,7 @@ publishing {
|
|||
mavenJava(MavenPublication) {
|
||||
artifact jar
|
||||
artifact sourcesJar
|
||||
artifact javadocJar
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ public class Backend {
|
|||
.enabled() && !OptifineHandler.usingShaders();
|
||||
}
|
||||
|
||||
public boolean canUseInstancing(World world) {
|
||||
public boolean canUseInstancing(@Nullable World world) {
|
||||
return canUseInstancing() && isFlywheelWorld(world);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.gl;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.gl.shader;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -14,6 +14,10 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
|
|||
public interface IDynamicInstance extends IInstance {
|
||||
/**
|
||||
* Called every frame.
|
||||
* <br>
|
||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance.
|
||||
* <br>
|
||||
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here.
|
||||
*/
|
||||
void beginFrame();
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ public interface ITickableInstance extends IInstance {
|
|||
|
||||
/**
|
||||
* Called every tick.
|
||||
* <br>
|
||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance.
|
||||
* <br>
|
||||
* {@link Instancer}/{@link InstanceData} creation/acquisition is safe here.
|
||||
*/
|
||||
void tick();
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
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.state.RenderLayer;
|
||||
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 net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
|
||||
/**
|
||||
* A manager class for a single world where instancing is supported.
|
||||
* <br>
|
||||
* The material manager is shared between the different instance managers.
|
||||
*/
|
||||
public class InstanceWorld {
|
||||
protected final MaterialManager<WorldProgram> materialManager;
|
||||
protected final InstanceManager<Entity> entityInstanceManager;
|
||||
protected final InstanceManager<TileEntity> tileEntityInstanceManager;
|
||||
|
||||
public InstanceWorld() {
|
||||
|
||||
materialManager = MaterialManager.builder(Contexts.WORLD)
|
||||
.build();
|
||||
entityInstanceManager = new EntityInstanceManager(materialManager);
|
||||
tileEntityInstanceManager = new TileInstanceManager(materialManager);
|
||||
}
|
||||
|
||||
public MaterialManager<WorldProgram> getMaterialManager() {
|
||||
return materialManager;
|
||||
}
|
||||
|
||||
public InstanceManager<Entity> getEntityInstanceManager() {
|
||||
return entityInstanceManager;
|
||||
}
|
||||
|
||||
public InstanceManager<TileEntity> getTileEntityInstanceManager() {
|
||||
return tileEntityInstanceManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ClientWorld world) {
|
||||
world.blockEntityList.forEach(tileEntityInstanceManager::add);
|
||||
world.entitiesForRendering()
|
||||
.forEach(entityInstanceManager::add);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ready to render a frame:
|
||||
* <br>
|
||||
* Check and shift the origin coordinate.
|
||||
* <br>
|
||||
* Call {@link IDynamicInstance#beginFrame()} on all instances in this world.
|
||||
*/
|
||||
public void beginFrame(BeginFrameEvent event) {
|
||||
materialManager.checkAndShiftOrigin(event.getInfo());
|
||||
|
||||
tileEntityInstanceManager.beginFrame(event.getInfo());
|
||||
entityInstanceManager.beginFrame(event.getInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick the renderers after the game has ticked:
|
||||
* <br>
|
||||
* Call {@link ITickableInstance#tick()} on all instances in this world.
|
||||
*/
|
||||
public void tick() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
Entity renderViewEntity = mc.cameraEntity != null ? mc.cameraEntity : mc.player;
|
||||
|
||||
if (renderViewEntity == null) return;
|
||||
|
||||
tileEntityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
entityInstanceManager.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the given layer.
|
||||
*/
|
||||
public void renderLayer(RenderLayerEvent event) {
|
||||
event.type.setupRenderState();
|
||||
|
||||
materialManager.render(event.layer, event.viewProjection, event.camX, event.camY, event.camZ);
|
||||
|
||||
event.type.clearRenderState();
|
||||
}
|
||||
}
|
|
@ -3,12 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
|
|||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
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.state.RenderLayer;
|
||||
import com.jozufozu.flywheel.core.Contexts;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
||||
|
@ -30,19 +25,18 @@ import net.minecraftforge.fml.common.Mod;
|
|||
@Mod.EventBusSubscriber(Dist.CLIENT)
|
||||
public class InstancedRenderDispatcher {
|
||||
|
||||
private static final WorldAttached<MaterialManager<WorldProgram>> materialManagers = new WorldAttached<>($ -> MaterialManager.builder(Contexts.WORLD).build());
|
||||
|
||||
private static final WorldAttached<InstanceManager<Entity>> entityInstanceManager = new WorldAttached<>(world -> new EntityInstanceManager(materialManagers.get(world)));
|
||||
private static final WorldAttached<InstanceManager<TileEntity>> tileInstanceManager = new WorldAttached<>(world -> new TileInstanceManager(materialManagers.get(world)));
|
||||
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld());
|
||||
|
||||
@Nonnull
|
||||
public static InstanceManager<TileEntity> getTiles(IWorld world) {
|
||||
return tileInstanceManager.get(world);
|
||||
return instanceWorlds.get(world)
|
||||
.getTileEntityInstanceManager();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static InstanceManager<Entity> getEntities(IWorld world) {
|
||||
return entityInstanceManager.get(world);
|
||||
return instanceWorlds.get(world)
|
||||
.getEntityInstanceManager();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
@ -55,12 +49,7 @@ public class InstancedRenderDispatcher {
|
|||
ClientWorld world = mc.level;
|
||||
AnimationTickHolder.tick();
|
||||
|
||||
Entity renderViewEntity = mc.cameraEntity != null ? mc.cameraEntity : mc.player;
|
||||
|
||||
if (renderViewEntity == null) return;
|
||||
|
||||
getTiles(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
getEntities(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
instanceWorlds.get(world).tick();
|
||||
}
|
||||
|
||||
public static void enqueueUpdate(TileEntity te) {
|
||||
|
@ -73,29 +62,18 @@ public class InstancedRenderDispatcher {
|
|||
|
||||
@SubscribeEvent
|
||||
public static void onBeginFrame(BeginFrameEvent event) {
|
||||
materialManagers.get(event.getWorld())
|
||||
.checkAndShiftOrigin(event.getInfo());
|
||||
|
||||
getTiles(event.getWorld()).beginFrame(event.getInfo());
|
||||
getEntities(event.getWorld()).beginFrame(event.getInfo());
|
||||
instanceWorlds.get(event.getWorld()).beginFrame(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void renderLayer(RenderLayerEvent event) {
|
||||
if (event.layer == null) return;
|
||||
|
||||
ClientWorld world = event.getWorld();
|
||||
if (!Backend.getInstance()
|
||||
.canUseInstancing(world)) return;
|
||||
|
||||
RenderLayer renderLayer = RenderLayer.fromRenderType(event.type);
|
||||
|
||||
if (renderLayer == null) return;
|
||||
|
||||
event.type.setupRenderState();
|
||||
|
||||
materialManagers.get(world)
|
||||
.render(renderLayer, event.viewProjection, event.camX, event.camY, event.camZ);
|
||||
|
||||
event.type.clearRenderState();
|
||||
instanceWorlds.get(world).renderLayer(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
@ -108,13 +86,8 @@ public class InstancedRenderDispatcher {
|
|||
}
|
||||
|
||||
public static void loadAllInWorld(ClientWorld world) {
|
||||
materialManagers.replace(world, MaterialManager::delete);
|
||||
|
||||
InstanceManager<TileEntity> tiles = tileInstanceManager.replace(world);
|
||||
world.blockEntityList.forEach(tiles::add);
|
||||
|
||||
InstanceManager<Entity> entities = entityInstanceManager.replace(world);
|
||||
world.entitiesForRendering()
|
||||
.forEach(entities::add);
|
||||
instanceWorlds.replace(world, InstanceWorld::delete)
|
||||
.loadAll(world);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -17,12 +16,26 @@ import com.jozufozu.flywheel.core.model.IModel;
|
|||
import com.jozufozu.flywheel.core.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.util.AttribUtil;
|
||||
|
||||
import net.minecraft.util.math.vector.Vector3i;
|
||||
|
||||
/**
|
||||
* An instancer is how you interact with an instanced model.
|
||||
* <p>
|
||||
* Instanced models can have many copies, and on most systems it's very fast to draw all of the copies at once.
|
||||
* There is no limit to how many copies an instanced model can have.
|
||||
* Each copy is represented by an InstanceData object.
|
||||
* </p>
|
||||
* <p>
|
||||
* When you call {@link #createInstance()} you are given an InstanceData object that you can manipulate however
|
||||
* you want. The changes you make to the InstanceData object are automatically made visible, and persistent.
|
||||
* Changing the position of your InstanceData object every frame means that that copy of the model will be in a
|
||||
* different position in the world each frame. Setting the position of your InstanceData once and not touching it
|
||||
* again means that your model will be in the same position in the world every frame. This persistence is useful
|
||||
* because it means the properties of your model don't have to be re-evaluated every frame.
|
||||
* </p>
|
||||
*
|
||||
* @param <D> the data that represents a copy of the instanced model.
|
||||
*/
|
||||
public class Instancer<D extends InstanceData> {
|
||||
|
||||
public final Supplier<Vector3i> originCoordinate;
|
||||
|
||||
protected final Supplier<IModel> gen;
|
||||
protected BufferedModel model;
|
||||
|
||||
|
@ -40,11 +53,30 @@ public class Instancer<D extends InstanceData> {
|
|||
boolean anyToRemove;
|
||||
boolean anyToUpdate;
|
||||
|
||||
public Instancer(Supplier<IModel> model, Supplier<Vector3i> originCoordinate, MaterialSpec<D> spec) {
|
||||
public Instancer(Supplier<IModel> model, MaterialSpec<D> spec) {
|
||||
this.gen = model;
|
||||
this.factory = spec.getInstanceFactory();
|
||||
this.instanceFormat = spec.getInstanceFormat();
|
||||
this.originCoordinate = originCoordinate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a handle to a new copy of this model.
|
||||
*/
|
||||
public D createInstance() {
|
||||
return _add(factory.create(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a data from another Instancer to this.
|
||||
*
|
||||
* This has the effect of swapping out one model for another.
|
||||
* @param inOther the data associated with a different model.
|
||||
*/
|
||||
public void stealInstance(D inOther) {
|
||||
if (inOther.owner == this) return;
|
||||
|
||||
inOther.owner.anyToRemove = true;
|
||||
_add(inOther);
|
||||
}
|
||||
|
||||
public void render() {
|
||||
|
@ -59,17 +91,6 @@ public class Instancer<D extends InstanceData> {
|
|||
vao.unbind();
|
||||
}
|
||||
|
||||
public D createInstance() {
|
||||
return _add(factory.create(this));
|
||||
}
|
||||
|
||||
public void stealInstance(D inOther) {
|
||||
if (inOther.owner == this) return;
|
||||
|
||||
inOther.owner.anyToRemove = true;
|
||||
_add(inOther);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
model = ModelUtil.getIndexedModel(gen.get());
|
||||
initialized = true;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.instancing.entity;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.instancing.tile;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -4,36 +4,31 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.jozufozu.flywheel.backend.RenderWork;
|
||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
||||
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.util.Pair;
|
||||
import com.jozufozu.flywheel.util.RenderUtil;
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.vector.Vector3i;
|
||||
|
||||
/**
|
||||
* A collection of Instancers that all have the same format.
|
||||
* @param <D>
|
||||
*/
|
||||
public class InstanceMaterial<D extends InstanceData> {
|
||||
|
||||
protected final Supplier<Vector3i> originCoordinate;
|
||||
protected final Cache<Object, Instancer<D>> models;
|
||||
protected final MaterialSpec<D> spec;
|
||||
private final VertexFormat modelFormat;
|
||||
|
||||
public InstanceMaterial(Supplier<Vector3i> renderer, MaterialSpec<D> spec) {
|
||||
this.originCoordinate = renderer;
|
||||
public InstanceMaterial(MaterialSpec<D> spec) {
|
||||
this.spec = spec;
|
||||
|
||||
this.models = CacheBuilder.newBuilder()
|
||||
|
@ -42,7 +37,37 @@ public class InstanceMaterial<D extends InstanceData> {
|
|||
RenderWork.enqueue(instancer::delete);
|
||||
})
|
||||
.build();
|
||||
modelFormat = this.spec.getModelFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return An instancer for the given model, capable of rendering many copies for little cost.
|
||||
*/
|
||||
public Instancer<D> model(Object key, Supplier<IModel> modelSupplier) {
|
||||
try {
|
||||
return models.get(key, () -> new Instancer<>(modelSupplier, spec));
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException("error creating instancer", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState) {
|
||||
return model(partial, () -> new BlockModel(spec.getModelFormat(), partial.get(), referenceState));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir) {
|
||||
return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
|
||||
return model(Pair.of(dir, partial), () -> new BlockModel(spec.getModelFormat(), partial.get(), referenceState, modelTransform.get()));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(BlockState toRender) {
|
||||
return model(toRender, () -> new BlockModel(spec.getModelFormat(), toRender));
|
||||
}
|
||||
|
||||
public boolean nothingToRender() {
|
||||
|
@ -72,43 +97,4 @@ public class InstanceMaterial<D extends InstanceData> {
|
|||
}
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState) {
|
||||
return model(partial, () -> buildModel(partial.get(), referenceState));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir) {
|
||||
return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
|
||||
return model(Pair.of(dir, partial), () -> buildModel(partial.get(), referenceState, modelTransform.get()));
|
||||
}
|
||||
|
||||
public Instancer<D> getModel(BlockState toRender) {
|
||||
return model(toRender, () -> buildModel(toRender));
|
||||
}
|
||||
|
||||
public Instancer<D> model(Object key, Supplier<IModel> supplier) {
|
||||
try {
|
||||
return models.get(key, () -> new Instancer<>(supplier, originCoordinate, spec));
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IModel buildModel(BlockState renderedState) {
|
||||
BlockRendererDispatcher dispatcher = Minecraft.getInstance()
|
||||
.getBlockRenderer();
|
||||
return buildModel(dispatcher.getBlockModel(renderedState), renderedState);
|
||||
}
|
||||
|
||||
private IModel buildModel(IBakedModel model, BlockState renderedState) {
|
||||
return buildModel(model, renderedState, new MatrixStack());
|
||||
}
|
||||
|
||||
private IModel buildModel(IBakedModel model, BlockState referenceState, MatrixStack ms) {
|
||||
|
||||
return new BlockModel(modelFormat, model, referenceState, ms);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|||
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
|
||||
/**
|
||||
* 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 MaterialGroup<P extends WorldProgram> {
|
||||
|
||||
protected final MaterialManager<P> owner;
|
||||
|
@ -24,6 +30,17 @@ public class MaterialGroup<P extends WorldProgram> {
|
|||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")
|
||||
public <D extends InstanceData> InstanceMaterial<D> material(MaterialSpec<D> spec) {
|
||||
return (InstanceMaterial<D>) materials.computeIfAbsent(spec, this::createInstanceMaterial);
|
||||
}
|
||||
|
||||
public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
|
||||
for (MaterialRenderer<P> renderer : renderers) {
|
||||
renderer.render(viewProjection, camX, camY, camZ);
|
||||
|
@ -34,19 +51,6 @@ public class MaterialGroup<P extends WorldProgram> {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <D extends InstanceData> InstanceMaterial<D> material(MaterialSpec<D> spec) {
|
||||
return (InstanceMaterial<D>) materials.computeIfAbsent(spec, this::createInstanceMaterial);
|
||||
}
|
||||
|
||||
private InstanceMaterial<?> createInstanceMaterial(MaterialSpec<?> type) {
|
||||
InstanceMaterial<?> material = new InstanceMaterial<>(owner::getOriginCoordinate, type);
|
||||
|
||||
this.renderers.add(new MaterialRenderer<>(owner.getProgram(type.getProgramName()), material, this::setup));
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
materials.values().forEach(InstanceMaterial::clear);
|
||||
}
|
||||
|
@ -58,4 +62,12 @@ public class MaterialGroup<P extends WorldProgram> {
|
|||
materials.clear();
|
||||
renderers.clear();
|
||||
}
|
||||
|
||||
private InstanceMaterial<?> createInstanceMaterial(MaterialSpec<?> type) {
|
||||
InstanceMaterial<?> material = new InstanceMaterial<>(type);
|
||||
|
||||
this.renderers.add(new MaterialRenderer<>(owner.getProgram(type.getProgramName()), material, this::setup));
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,9 +60,44 @@ public class MaterialManager<P extends WorldProgram> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 IRenderState} you need to draw with.
|
||||
* @return A material group whose children will
|
||||
*/
|
||||
public MaterialGroup<P> state(RenderLayer layer, IRenderState state) {
|
||||
return layers.get(layer).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> solid(IRenderState state) {
|
||||
return layers.get(RenderLayer.SOLID).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> cutout(IRenderState state) {
|
||||
return layers.get(RenderLayer.CUTOUT).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> transparent(IRenderState state) {
|
||||
return layers.get(RenderLayer.TRANSPARENT).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultSolid() {
|
||||
return solid(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultCutout() {
|
||||
return cutout(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultTransparent() {
|
||||
return transparent(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render every model for every material.
|
||||
* @param layer Which vanilla {@link RenderType} is being drawn?
|
||||
* @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) {
|
||||
|
@ -95,34 +130,6 @@ public class MaterialManager<P extends WorldProgram> {
|
|||
}
|
||||
}
|
||||
|
||||
public MaterialGroup<P> state(RenderLayer layer, IRenderState state) {
|
||||
return layers.get(layer).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> solid(IRenderState state) {
|
||||
return layers.get(RenderLayer.SOLID).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> cutout(IRenderState state) {
|
||||
return layers.get(RenderLayer.CUTOUT).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> transparent(IRenderState state) {
|
||||
return layers.get(RenderLayer.TRANSPARENT).computeIfAbsent(state, this::createGroup);
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultSolid() {
|
||||
return solid(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultCutout() {
|
||||
return cutout(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
public MaterialGroup<P> defaultTransparent() {
|
||||
return transparent(TextureRenderState.get(PlayerContainer.BLOCK_ATLAS));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public <D extends InstanceData> InstanceMaterial<D> getMaterial(MaterialSpec<D> materialType) {
|
||||
return defaultCutout().material(materialType);
|
||||
|
@ -155,6 +162,11 @@ public class MaterialManager<P extends WorldProgram> {
|
|||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions.
|
||||
*
|
||||
* This prevents floating point precision issues at high coordinates.
|
||||
*/
|
||||
public void checkAndShiftOrigin(ActiveRenderInfo info) {
|
||||
int cX = MathHelper.floor(info.getPosition().x);
|
||||
int cY = MathHelper.floor(info.getPosition().y);
|
||||
|
|
|
@ -4,7 +4,6 @@ import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|||
import com.jozufozu.flywheel.backend.instancing.IInstanceFactory;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
||||
|
||||
import net.minecraft.inventory.container.PlayerContainer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class MaterialSpec<D extends InstanceData> {
|
||||
|
@ -15,19 +14,13 @@ public class MaterialSpec<D extends InstanceData> {
|
|||
private final VertexFormat modelFormat;
|
||||
private final VertexFormat instanceFormat;
|
||||
private final IInstanceFactory<D> instanceFactory;
|
||||
private final ResourceLocation texture;
|
||||
|
||||
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, IInstanceFactory<D> instanceFactory) {
|
||||
this(name, programSpec, modelFormat, instanceFormat, PlayerContainer.BLOCK_ATLAS, instanceFactory);
|
||||
}
|
||||
|
||||
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, ResourceLocation texture, IInstanceFactory<D> instanceFactory) {
|
||||
this.name = name;
|
||||
this.programSpec = programSpec;
|
||||
this.modelFormat = modelFormat;
|
||||
this.instanceFormat = instanceFormat;
|
||||
this.instanceFactory = instanceFactory;
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
public ResourceLocation getProgramName() {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.material;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -4,9 +4,36 @@ import javax.annotation.Nullable;
|
|||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
|
||||
/**
|
||||
* The 3 discrete stages the world is rendered in.
|
||||
*/
|
||||
public enum RenderLayer {
|
||||
/**
|
||||
* Solid layer:<br>
|
||||
*
|
||||
* All polygons will entirely occlude everything behind them.
|
||||
*
|
||||
* <br><br>
|
||||
* e.g. stone, dirt, solid blocks
|
||||
*/
|
||||
SOLID,
|
||||
/**
|
||||
* Cutout layer:<br>
|
||||
*
|
||||
* <em>Fragments</em> will either occlude or not occlude depending on the texture/material.
|
||||
*
|
||||
* <br><br>
|
||||
* e.g. leaves, cobwebs, tall grass, saplings, glass
|
||||
*/
|
||||
CUTOUT,
|
||||
/**
|
||||
* Transparent layer:<br>
|
||||
*
|
||||
* Nothing is guaranteed to occlude and fragments blend their color with what's behind them.
|
||||
*
|
||||
* <br><br>
|
||||
* e.g. stained glass, water
|
||||
*/
|
||||
TRANSPARENT,
|
||||
;
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.state;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.core.crumbling;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
|
@ -30,6 +30,12 @@ public class BlockModel implements IModel {
|
|||
|
||||
private final VertexFormat modelFormat;
|
||||
|
||||
public BlockModel(VertexFormat modelFormat, BlockState state) {
|
||||
this(modelFormat, Minecraft.getInstance()
|
||||
.getBlockRenderer()
|
||||
.getBlockModel(state), state);
|
||||
}
|
||||
|
||||
public BlockModel(VertexFormat modelFormat, IBakedModel model, BlockState referenceState) {
|
||||
this(modelFormat, model, referenceState, IDENTITY);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.event;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
|
||||
|
@ -10,6 +12,7 @@ public class ReloadRenderersEvent extends Event {
|
|||
this.world = world;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClientWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package com.jozufozu.flywheel.event;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.state.RenderLayer;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
|
@ -12,6 +16,7 @@ public class RenderLayerEvent extends Event {
|
|||
public final double camX;
|
||||
public final double camY;
|
||||
public final double camZ;
|
||||
public final RenderLayer layer;
|
||||
|
||||
public RenderLayerEvent(ClientWorld world, RenderType type, Matrix4f viewProjection, double camX, double camY, double camZ) {
|
||||
this.world = world;
|
||||
|
@ -20,6 +25,13 @@ public class RenderLayerEvent extends Event {
|
|||
this.camX = camX;
|
||||
this.camY = camY;
|
||||
this.camZ = camZ;
|
||||
|
||||
this.layer = RenderLayer.fromRenderType(type);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RenderLayer getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public ClientWorld getWorld() {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.event;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
Loading…
Reference in a new issue