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:
Jozufozu 2021-07-27 17:31:58 -07:00
parent 9c92e4d353
commit 9e066f8d41
26 changed files with 395 additions and 160 deletions

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.gl.attrib;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.gl.buffer;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.gl;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.gl.shader;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -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();

View file

@ -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();

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

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

View file

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

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.loading;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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() {

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.material;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -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,
;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.backend.state;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.core.crumbling;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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() {

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.jozufozu.flywheel.event;
import javax.annotation.ParametersAreNonnullByDefault;
import mcp.MethodsReturnNonnullByDefault;