mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +01:00
Light update convergence
- Move light update logic for all instances to use LightUpdater - Begin refactor of LightUpdater to account for moving listeners
This commit is contained in:
parent
cb10e4e7d1
commit
7ca4ea5c3e
19 changed files with 358 additions and 340 deletions
|
@ -0,0 +1,97 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.GridAlignedBB;
|
||||
import com.jozufozu.flywheel.light.ILightUpdateListener;
|
||||
import com.jozufozu.flywheel.light.LightUpdater;
|
||||
import com.jozufozu.flywheel.light.ListenerStatus;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
protected final MaterialManager materialManager;
|
||||
protected final World world;
|
||||
|
||||
public AbstractInstance(MaterialManager materialManager, World world) {
|
||||
this.materialManager = materialManager;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free any acquired resources.
|
||||
*/
|
||||
public abstract void remove();
|
||||
|
||||
/**
|
||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
||||
*
|
||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
||||
*/
|
||||
public void update() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after construction and when a light update occurs in the world.
|
||||
*
|
||||
* <br> If your model needs it, update light here.
|
||||
*/
|
||||
public void updateLight() {
|
||||
}
|
||||
|
||||
/**
|
||||
* When an instance is reset, the instance is deleted and re-created.
|
||||
*
|
||||
* <p>
|
||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
||||
* If this function returns <code>true</code>, then this instance will be {@link #remove removed},
|
||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
||||
* </p>
|
||||
*
|
||||
* @return <code>true</code> if this instance should be discarded and refreshed.
|
||||
*/
|
||||
public boolean shouldReset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerStatus status() {
|
||||
return ListenerStatus.OKAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changed) {
|
||||
updateLight();
|
||||
}
|
||||
|
||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
||||
relight(block, sky, Arrays.stream(models));
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
||||
models.forEach(model -> model.setBlockLight(block)
|
||||
.setSkyLight(sky));
|
||||
}
|
||||
}
|
|
@ -1,31 +1,7 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
* 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 interface IInstance {
|
||||
|
||||
BlockPos getWorldPosition();
|
||||
|
||||
void updateLight();
|
||||
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* When an instance is reset, the instance is deleted and re-created.
|
||||
*
|
||||
* <p>
|
||||
* This is used to handle things like block state changes.
|
||||
* </p>
|
||||
*
|
||||
* @return true if this instance should be reset
|
||||
*/
|
||||
boolean shouldReset();
|
||||
|
||||
void update();
|
||||
BlockPos getWorldPosition();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
private final Set<T> queuedAdditions;
|
||||
private final Set<T> queuedUpdates;
|
||||
|
||||
protected final Map<T, IInstance> instances;
|
||||
protected final Map<T, AbstractInstance> instances;
|
||||
protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances;
|
||||
protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances;
|
||||
|
||||
|
@ -63,7 +63,7 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
protected abstract boolean canCreateInstance(T obj);
|
||||
|
||||
@Nullable
|
||||
protected abstract IInstance createRaw(T obj);
|
||||
protected abstract AbstractInstance createRaw(T obj);
|
||||
|
||||
/**
|
||||
* Ticks the InstanceManager.
|
||||
|
@ -169,7 +169,7 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
.canUseInstancing()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
IInstance instance = getInstance(obj, false);
|
||||
AbstractInstance instance = getInstance(obj, false);
|
||||
|
||||
if (instance != null) {
|
||||
|
||||
|
@ -186,40 +186,29 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
}
|
||||
}
|
||||
|
||||
public void onLightUpdate(T obj) {
|
||||
if (!Backend.getInstance()
|
||||
.canUseInstancing()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
IInstance instance = getInstance(obj, false);
|
||||
|
||||
if (instance != null) instance.updateLight();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(T obj) {
|
||||
if (!Backend.getInstance()
|
||||
.canUseInstancing()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
IInstance instance = getInstance(obj, false);
|
||||
AbstractInstance instance = getInstance(obj, false);
|
||||
if (instance != null) removeInternal(obj, instance);
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
instances.values().forEach(IInstance::remove);
|
||||
instances.values().forEach(AbstractInstance::remove);
|
||||
instances.clear();
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected <I extends T> IInstance getInstance(I obj, boolean create) {
|
||||
protected <I extends T> AbstractInstance getInstance(I obj, boolean create) {
|
||||
if (!Backend.getInstance()
|
||||
.canUseInstancing()) return null;
|
||||
|
||||
IInstance instance = instances.get(obj);
|
||||
AbstractInstance instance = instances.get(obj);
|
||||
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
|
@ -283,18 +272,20 @@ public abstract class InstanceManager<T> implements MaterialManagerImpl.OriginSh
|
|||
getInstance(tile, true);
|
||||
}
|
||||
|
||||
protected void removeInternal(T obj, IInstance instance) {
|
||||
protected void removeInternal(T obj, AbstractInstance instance) {
|
||||
instance.remove();
|
||||
instances.remove(obj);
|
||||
dynamicInstances.remove(obj);
|
||||
tickableInstances.remove(obj);
|
||||
}
|
||||
|
||||
protected IInstance createInternal(T obj) {
|
||||
IInstance renderer = createRaw(obj);
|
||||
@Nullable
|
||||
protected AbstractInstance createInternal(T obj) {
|
||||
AbstractInstance renderer = createRaw(obj);
|
||||
|
||||
if (renderer != null) {
|
||||
renderer.updateLight();
|
||||
renderer.startListening();
|
||||
instances.put(obj, renderer);
|
||||
|
||||
if (renderer instanceof IDynamicInstance) dynamicInstances.put(obj, (IDynamicInstance) renderer);
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing;
|
|||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.state.RenderLayer;
|
||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
||||
|
@ -28,7 +27,7 @@ public class InstancedRenderDispatcher {
|
|||
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>($ -> new InstanceWorld());
|
||||
|
||||
/**
|
||||
* Call this when you want to manually run {@link IInstance#update()}.
|
||||
* Call this when you want to manually run {@link AbstractInstance#update()}.
|
||||
* @param te The tile whose instance you want to update.
|
||||
*/
|
||||
public static void enqueueUpdate(TileEntity te) {
|
||||
|
@ -36,7 +35,7 @@ public class InstancedRenderDispatcher {
|
|||
}
|
||||
|
||||
/**
|
||||
* Call this when you want to manually run {@link IInstance#update()}.
|
||||
* Call this when you want to manually run {@link AbstractInstance#update()}.
|
||||
* @param entity The entity whose instance you want to update.
|
||||
*/
|
||||
public static void enqueueUpdate(Entity entity) {
|
||||
|
|
|
@ -4,15 +4,13 @@ import java.util.Arrays;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.material.InstanceMaterial;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialManager;
|
||||
import com.jozufozu.flywheel.core.Materials;
|
||||
import com.jozufozu.flywheel.core.materials.IFlatLight;
|
||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
||||
import com.jozufozu.flywheel.light.ILightUpdateListener;
|
||||
import com.jozufozu.flywheel.light.ListenerStatus;
|
||||
import com.jozufozu.flywheel.light.Volume;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
@ -21,8 +19,6 @@ import net.minecraft.util.math.MathHelper;
|
|||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraft.util.math.vector.Vector3i;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* The layer between a {@link TileEntity} and the Flywheel backend.
|
||||
|
@ -39,50 +35,25 @@ import net.minecraft.world.World;
|
|||
*
|
||||
* @param <E> The type of {@link Entity} your class is an instance of.
|
||||
*/
|
||||
public abstract class EntityInstance<E extends Entity> implements IInstance {
|
||||
public abstract class EntityInstance<E extends Entity> extends AbstractInstance implements ILightUpdateListener {
|
||||
|
||||
protected final MaterialManager materialManager;
|
||||
protected final E entity;
|
||||
protected final World world;
|
||||
|
||||
public EntityInstance(MaterialManager materialManager, E entity) {
|
||||
this.materialManager = materialManager;
|
||||
super(materialManager, entity.level);
|
||||
this.entity = entity;
|
||||
this.world = entity.level;
|
||||
|
||||
startListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Free any acquired resources.
|
||||
*/
|
||||
public abstract void remove();
|
||||
|
||||
/**
|
||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
||||
*
|
||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
||||
*/
|
||||
public void update() {
|
||||
@Override
|
||||
public Volume.Box getVolume() {
|
||||
return Volume.box(entity.getBoundingBox());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after construction and when a light update occurs in the world.
|
||||
*
|
||||
* <br> If your model needs it, update light here.
|
||||
*/
|
||||
public void updateLight() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
||||
* If this function returns <code>true</code>, then this instance will be {@link #remove removed},
|
||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
||||
*
|
||||
* @return <code>true</code> if this instance should be discarded and refreshed.
|
||||
*/
|
||||
public boolean shouldReset() {
|
||||
return false;
|
||||
@Override
|
||||
public ListenerStatus status() {
|
||||
return ListenerStatus.UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,30 +90,4 @@ public abstract class EntityInstance<E extends Entity> implements IInstance {
|
|||
public BlockPos getWorldPosition() {
|
||||
return entity.blockPosition();
|
||||
}
|
||||
|
||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
||||
relight(block, sky, Arrays.stream(models));
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
||||
models.forEach(model -> model.setBlockLight(block)
|
||||
.setSkyLight(sky));
|
||||
}
|
||||
|
||||
protected InstanceMaterial<ModelData> getTransformMaterial() {
|
||||
return materialManager.defaultSolid().material(Materials.TRANSFORMED);
|
||||
}
|
||||
|
||||
protected InstanceMaterial<OrientedData> getOrientedMaterial() {
|
||||
return materialManager.defaultSolid().material(Materials.ORIENTED);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.entity;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||
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;
|
||||
|
@ -23,7 +23,7 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected IInstance createRaw(Entity obj) {
|
||||
protected AbstractInstance createRaw(Entity obj) {
|
||||
return InstancedRenderRegistry.getInstance()
|
||||
.create(materialManager, obj);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.tile;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
||||
import com.jozufozu.flywheel.backend.material.InstanceMaterial;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialManager;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialManagerImpl;
|
||||
import com.jozufozu.flywheel.core.Materials;
|
||||
import com.jozufozu.flywheel.core.materials.IFlatLight;
|
||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
||||
import com.jozufozu.flywheel.light.GridAlignedBB;
|
||||
import com.jozufozu.flywheel.light.Volume;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* The layer between a {@link TileEntity} and the Flywheel backend.
|
||||
|
@ -37,46 +32,21 @@ import net.minecraft.world.World;
|
|||
*
|
||||
* @param <T> The type of {@link TileEntity} your class is an instance of.
|
||||
*/
|
||||
public abstract class TileEntityInstance<T extends TileEntity> implements IInstance {
|
||||
public abstract class TileEntityInstance<T extends TileEntity> extends AbstractInstance {
|
||||
|
||||
protected final MaterialManager materialManager;
|
||||
protected final T tile;
|
||||
protected final World world;
|
||||
protected final BlockPos pos;
|
||||
protected final BlockPos instancePos;
|
||||
protected final BlockState blockState;
|
||||
|
||||
public TileEntityInstance(MaterialManager materialManager, T tile) {
|
||||
this.materialManager = materialManager;
|
||||
super(materialManager, tile.getLevel());
|
||||
this.tile = tile;
|
||||
this.world = tile.getLevel();
|
||||
this.pos = tile.getBlockPos();
|
||||
this.blockState = tile.getBlockState();
|
||||
this.instancePos = pos.subtract(materialManager.getOriginCoordinate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
||||
*
|
||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
||||
*/
|
||||
public void update() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after construction and when a light update occurs in the world.
|
||||
*
|
||||
* <br> If your model needs it, update light here.
|
||||
*/
|
||||
public void updateLight() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Free any acquired resources.
|
||||
*/
|
||||
public abstract void remove();
|
||||
|
||||
/**
|
||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
||||
* If this function returns <code>true</code>, then this instance will be {@link #remove removed},
|
||||
|
@ -106,23 +76,6 @@ public abstract class TileEntityInstance<T extends TileEntity> implements IInsta
|
|||
return pos;
|
||||
}
|
||||
|
||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
||||
relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
|
||||
}
|
||||
|
||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
||||
relight(block, sky, Arrays.stream(models));
|
||||
}
|
||||
|
||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
||||
models.forEach(model -> model.setBlockLight(block)
|
||||
.setSkyLight(sky));
|
||||
}
|
||||
|
||||
protected InstanceMaterial<ModelData> getTransformMaterial() {
|
||||
return materialManager.defaultCutout().material(Materials.TRANSFORMED);
|
||||
}
|
||||
|
@ -130,4 +83,9 @@ public abstract class TileEntityInstance<T extends TileEntity> implements IInsta
|
|||
protected InstanceMaterial<OrientedData> getOrientedMaterial() {
|
||||
return materialManager.defaultCutout().material(Materials.ORIENTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Volume.Block getVolume() {
|
||||
return Volume.block(pos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.tile;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||
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;
|
||||
|
@ -23,7 +23,7 @@ public class TileInstanceManager extends InstanceManager<TileEntity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected IInstance createRaw(TileEntity obj) {
|
||||
protected AbstractInstance createRaw(TileEntity obj) {
|
||||
return InstancedRenderRegistry.getInstance()
|
||||
.create(materialManager, obj);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,15 @@ import java.util.ArrayList;
|
|||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.light.LightUpdater;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
@ -45,4 +48,10 @@ public class ForgeEvents {
|
|||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void rwle(TickEvent.ClientTickEvent e) {
|
||||
if (e.phase == TickEvent.Phase.END && Backend.isGameActive())
|
||||
LightUpdater.getInstance().tick();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,10 @@ public class GridAlignedBB {
|
|||
return new GridAlignedBB(start.getX(), start.getY(), start.getZ(), end.getX() + 1, end.getY() + 1, end.getZ() + 1);
|
||||
}
|
||||
|
||||
public static GridAlignedBB from(BlockPos pos) {
|
||||
return new GridAlignedBB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
||||
}
|
||||
|
||||
public static GridAlignedBB from(int sectionX, int sectionZ) {
|
||||
int startX = sectionX << 4;
|
||||
int startZ = sectionZ << 4;
|
||||
|
|
|
@ -4,28 +4,33 @@ import net.minecraft.world.IBlockDisplayReader;
|
|||
import net.minecraft.world.LightType;
|
||||
|
||||
/**
|
||||
* Anything can implement this, implementors should call {@link LightUpdater#startListening}
|
||||
* Anything can implement this, implementors should call {@link #startListening}
|
||||
* appropriately to make sure they get the updates they want.
|
||||
*/
|
||||
public interface ILightUpdateListener {
|
||||
|
||||
Volume getVolume();
|
||||
|
||||
ListenerStatus status();
|
||||
|
||||
default void startListening() {
|
||||
LightUpdater.getInstance().addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a light updates in a chunk the implementor cares about.
|
||||
*
|
||||
* @return true if this object is no longer valid and should not receive any more updates.
|
||||
*/
|
||||
boolean onLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changed);
|
||||
void onLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changed);
|
||||
|
||||
/**
|
||||
* Called when the server sends light data to the client.
|
||||
*
|
||||
* @return true if this object is no longer valid and should not receive any more updates.
|
||||
*/
|
||||
default boolean onLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
|
||||
default void onLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
|
||||
GridAlignedBB changedVolume = GridAlignedBB.from(chunkX, chunkZ);
|
||||
|
||||
if (onLightUpdate(world, LightType.BLOCK, changedVolume)) return true;
|
||||
onLightUpdate(world, LightType.BLOCK, changedVolume);
|
||||
|
||||
return onLightUpdate(world, LightType.SKY, changedVolume);
|
||||
onLightUpdate(world, LightType.SKY, changedVolume);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.Set;
|
||||
|
||||
import com.jozufozu.flywheel.util.WeakHashSet;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongRBTreeSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.SectionPos;
|
||||
import net.minecraft.world.IBlockDisplayReader;
|
||||
import net.minecraft.world.LightType;
|
||||
|
||||
/**
|
||||
* By using WeakReferences we can automatically remove listeners when they are garbage collected.
|
||||
* This allows us to easily be more clever about how we store the listeners. Each listener is associated
|
||||
* with 2 sets of longs indicating what chunks and sections each listener is in. Additionally, a reverse
|
||||
* mapping is created to allow for fast lookups when light updates. The reverse mapping is more interesting,
|
||||
* but {@link #listenersToSections}, and {@link #listenersToChunks} are used to know what sections and
|
||||
* chunks we need to remove the listeners from if they re-subscribe. Otherwise, listeners could get updates
|
||||
* they no longer care about. This is done in {@link #clearSections} and {@link #clearChunks}
|
||||
*/
|
||||
public class LightUpdater {
|
||||
|
||||
private static LightUpdater instance;
|
||||
|
@ -32,72 +21,69 @@ public class LightUpdater {
|
|||
return instance;
|
||||
}
|
||||
|
||||
private final Long2ObjectMap<WeakHashSet<ILightUpdateListener>> sections;
|
||||
private final WeakHashMap<ILightUpdateListener, LongRBTreeSet> listenersToSections;
|
||||
|
||||
private final Long2ObjectMap<WeakHashSet<ILightUpdateListener>> chunks;
|
||||
private final WeakHashMap<ILightUpdateListener, LongRBTreeSet> listenersToChunks;
|
||||
private final WeakHashSet<ILightUpdateListener> allListeners;
|
||||
private final WeakContainmentMultiMap<ILightUpdateListener> sections;
|
||||
private final WeakContainmentMultiMap<ILightUpdateListener> chunks;
|
||||
|
||||
public LightUpdater() {
|
||||
sections = new Long2ObjectOpenHashMap<>();
|
||||
listenersToSections = new WeakHashMap<>();
|
||||
allListeners = new WeakHashSet<>();
|
||||
sections = new WeakContainmentMultiMap<>();
|
||||
chunks = new WeakContainmentMultiMap<>();
|
||||
}
|
||||
|
||||
chunks = new Long2ObjectOpenHashMap<>();
|
||||
listenersToChunks = new WeakHashMap<>();
|
||||
public void tick() {
|
||||
for (ILightUpdateListener listener : allListeners) {
|
||||
if (listener.status() == ListenerStatus.UPDATE) {
|
||||
addListener(listener);
|
||||
|
||||
listener.onLightUpdate(Minecraft.getInstance().level, LightType.BLOCK, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener associated with the given {@link BlockPos}.
|
||||
* <p>
|
||||
* When a light update occurs in the chunk the position is contained in,
|
||||
* {@link ILightUpdateListener#onLightUpdate} will be called.
|
||||
*
|
||||
* @param pos The position in the world that the listener cares about.
|
||||
* Add a listener.
|
||||
|
||||
* @param listener The object that wants to receive light update notifications.
|
||||
*/
|
||||
public void startListening(BlockPos pos, ILightUpdateListener listener) {
|
||||
LongRBTreeSet sections = clearSections(listener);
|
||||
LongRBTreeSet chunks = clearChunks(listener);
|
||||
public void addListener(ILightUpdateListener listener) {
|
||||
allListeners.add(listener);
|
||||
|
||||
long sectionPos = worldToSection(pos);
|
||||
addToSection(sectionPos, listener);
|
||||
sections.add(sectionPos);
|
||||
Volume volume = listener.getVolume();
|
||||
|
||||
long chunkPos = sectionToChunk(sectionPos);
|
||||
addToChunk(chunkPos, listener);
|
||||
chunks.add(chunkPos);
|
||||
}
|
||||
LongSet sections = this.sections.getAndResetContainment(listener);
|
||||
LongSet chunks = this.chunks.getAndResetContainment(listener);
|
||||
|
||||
/**
|
||||
* Add a listener associated with the given {@link GridAlignedBB}.
|
||||
* <p>
|
||||
* When a light update occurs in any chunk spanning the given volume,
|
||||
* {@link ILightUpdateListener#onLightUpdate} will be called.
|
||||
*
|
||||
* @param volume The volume in the world that the listener cares about.
|
||||
* @param listener The object that wants to receive light update notifications.
|
||||
*/
|
||||
public void startListening(GridAlignedBB volume, ILightUpdateListener listener) {
|
||||
LongRBTreeSet sections = clearSections(listener);
|
||||
LongRBTreeSet chunks = clearSections(listener);
|
||||
if (volume instanceof Volume.Block) {
|
||||
BlockPos pos = ((Volume.Block) volume).pos;
|
||||
long sectionPos = blockToSection(pos);
|
||||
this.sections.put(sectionPos, listener);
|
||||
sections.add(sectionPos);
|
||||
|
||||
int minX = SectionPos.blockToSectionCoord(volume.minX);
|
||||
int minY = SectionPos.blockToSectionCoord(volume.minY);
|
||||
int minZ = SectionPos.blockToSectionCoord(volume.minZ);
|
||||
int maxX = SectionPos.blockToSectionCoord(volume.maxX);
|
||||
int maxY = SectionPos.blockToSectionCoord(volume.maxY);
|
||||
int maxZ = SectionPos.blockToSectionCoord(volume.maxZ);
|
||||
long chunkPos = sectionToChunk(sectionPos);
|
||||
this.chunks.put(chunkPos, listener);
|
||||
chunks.add(chunkPos);
|
||||
} else if (volume instanceof Volume.Box) {
|
||||
GridAlignedBB box = ((Volume.Box) volume).box;
|
||||
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
long sectionPos = SectionPos.asLong(x, y, z);
|
||||
addToSection(sectionPos, listener);
|
||||
sections.add(sectionPos);
|
||||
int minX = SectionPos.blockToSectionCoord(box.minX);
|
||||
int minY = SectionPos.blockToSectionCoord(box.minY);
|
||||
int minZ = SectionPos.blockToSectionCoord(box.minZ);
|
||||
int maxX = SectionPos.blockToSectionCoord(box.maxX);
|
||||
int maxY = SectionPos.blockToSectionCoord(box.maxY);
|
||||
int maxZ = SectionPos.blockToSectionCoord(box.maxZ);
|
||||
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
long sectionPos = SectionPos.asLong(x, y, z);
|
||||
this.sections.put(sectionPos, listener);
|
||||
sections.add(sectionPos);
|
||||
}
|
||||
long chunkPos = SectionPos.asLong(x, 0, z);
|
||||
this.chunks.put(chunkPos, listener);
|
||||
chunks.add(chunkPos);
|
||||
}
|
||||
long chunkPos = SectionPos.asLong(x, 0, z);
|
||||
addToChunk(chunkPos, listener);
|
||||
chunks.add(chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,13 +96,17 @@ public class LightUpdater {
|
|||
* @param sectionPos A long representing the section position where light changed.
|
||||
*/
|
||||
public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) {
|
||||
WeakHashSet<ILightUpdateListener> set = sections.get(sectionPos);
|
||||
Set<ILightUpdateListener> set = sections.get(sectionPos);
|
||||
|
||||
if (set == null || set.isEmpty()) return;
|
||||
|
||||
set.removeIf(l -> l.status().shouldRemove());
|
||||
|
||||
GridAlignedBB chunkBox = GridAlignedBB.from(SectionPos.of(sectionPos));
|
||||
|
||||
set.removeIf(listener -> listener.onLightUpdate(world, type, chunkBox.copy()));
|
||||
for (ILightUpdateListener listener : set) {
|
||||
listener.onLightUpdate(world, type, chunkBox.copy());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,63 +116,20 @@ public class LightUpdater {
|
|||
* @param world The world in which light was updated.
|
||||
*/
|
||||
public void onLightPacket(IBlockDisplayReader world, int chunkX, int chunkZ) {
|
||||
|
||||
long chunkPos = SectionPos.asLong(chunkX, 0, chunkZ);
|
||||
|
||||
WeakHashSet<ILightUpdateListener> set = chunks.get(chunkPos);
|
||||
Set<ILightUpdateListener> set = chunks.get(chunkPos);
|
||||
|
||||
if (set == null || set.isEmpty()) return;
|
||||
|
||||
set.removeIf(listener -> listener.onLightPacket(world, chunkX, chunkZ));
|
||||
}
|
||||
set.removeIf(l -> l.status().shouldRemove());
|
||||
|
||||
private LongRBTreeSet clearChunks(ILightUpdateListener listener) {
|
||||
return clear(listener, listenersToChunks, chunks);
|
||||
}
|
||||
|
||||
private LongRBTreeSet clearSections(ILightUpdateListener listener) {
|
||||
return clear(listener, listenersToSections, sections);
|
||||
}
|
||||
|
||||
private LongRBTreeSet clear(ILightUpdateListener listener, WeakHashMap<ILightUpdateListener, LongRBTreeSet> listeners, Long2ObjectMap<WeakHashSet<ILightUpdateListener>> lookup) {
|
||||
LongRBTreeSet set = listeners.get(listener);
|
||||
|
||||
if (set == null) {
|
||||
set = new LongRBTreeSet();
|
||||
listeners.put(listener, set);
|
||||
} else {
|
||||
set.forEach((LongConsumer) l -> {
|
||||
WeakHashSet<ILightUpdateListener> listeningSections = lookup.get(l);
|
||||
|
||||
if (listeningSections != null) listeningSections.remove(listener);
|
||||
});
|
||||
|
||||
set.clear();
|
||||
for (ILightUpdateListener listener : set) {
|
||||
listener.onLightPacket(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private void addToSection(long sectionPos, ILightUpdateListener listener) {
|
||||
getOrCreate(sections, sectionPos).add(listener);
|
||||
}
|
||||
|
||||
private void addToChunk(long chunkPos, ILightUpdateListener listener) {
|
||||
getOrCreate(chunks, chunkPos).add(listener);
|
||||
}
|
||||
|
||||
private WeakHashSet<ILightUpdateListener> getOrCreate(Long2ObjectMap<WeakHashSet<ILightUpdateListener>> sections, long chunkPos) {
|
||||
WeakHashSet<ILightUpdateListener> set = sections.get(chunkPos);
|
||||
|
||||
if (set == null) {
|
||||
set = new WeakHashSet<>();
|
||||
sections.put(chunkPos, set);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
public static long worldToSection(BlockPos pos) {
|
||||
public static long blockToSection(BlockPos pos) {
|
||||
return SectionPos.asLong(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
public enum ListenerStatus {
|
||||
OKAY,
|
||||
REMOVE,
|
||||
UPDATE,
|
||||
;
|
||||
|
||||
public boolean isOk() {
|
||||
return this == OKAY;
|
||||
}
|
||||
|
||||
public boolean shouldRemove() {
|
||||
return this == REMOVE;
|
||||
}
|
||||
}
|
35
src/main/java/com/jozufozu/flywheel/light/Volume.java
Normal file
35
src/main/java/com/jozufozu/flywheel/light/Volume.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class Volume {
|
||||
|
||||
public static Volume.Block block(BlockPos pos) {
|
||||
return new Block(pos);
|
||||
}
|
||||
|
||||
public static Volume.Box box(GridAlignedBB box) {
|
||||
return new Box(box);
|
||||
}
|
||||
|
||||
public static Volume.Box box(AxisAlignedBB box) {
|
||||
return new Box(GridAlignedBB.from(box));
|
||||
}
|
||||
|
||||
public static class Block extends Volume {
|
||||
public final BlockPos pos;
|
||||
|
||||
public Block(BlockPos pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Box extends Volume {
|
||||
public final GridAlignedBB box;
|
||||
|
||||
public Box(GridAlignedBB box) {
|
||||
this.box = box;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.jozufozu.flywheel.light;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
import com.jozufozu.flywheel.util.WeakHashSet;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongRBTreeSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
public class WeakContainmentMultiMap<T> {
|
||||
|
||||
private final Long2ObjectMap<WeakHashSet<T>> forward;
|
||||
private final WeakHashMap<T, LongSet> reverse;
|
||||
|
||||
public WeakContainmentMultiMap() {
|
||||
forward = new Long2ObjectOpenHashMap<>();
|
||||
reverse = new WeakHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a confusing function, but it maintains the internal state of the chunk/section maps.
|
||||
*
|
||||
* <p>
|
||||
* First, uses the reverse lookup map to remove listener from all sets in the lookup map.<br>
|
||||
* Then, clears the listeners containment set.
|
||||
* </p>
|
||||
*
|
||||
* @param listener The listener to clean up.
|
||||
* @return An empty set that should be populated with the chunks/sections the listener is contained in.
|
||||
*/
|
||||
public LongSet getAndResetContainment(T listener) {
|
||||
LongSet containmentSet = reverse.computeIfAbsent(listener, $ -> new LongRBTreeSet());
|
||||
|
||||
containmentSet.forEach((LongConsumer) l -> {
|
||||
WeakHashSet<T> listeners = forward.get(l);
|
||||
|
||||
if (listeners != null) listeners.remove(listener);
|
||||
});
|
||||
|
||||
containmentSet.clear();
|
||||
|
||||
return containmentSet;
|
||||
}
|
||||
|
||||
public Set<T> get(long l) {
|
||||
return forward.get(l);
|
||||
}
|
||||
|
||||
public void put(long sectionPos, T listener) {
|
||||
forward.computeIfAbsent(sectionPos, $ -> new WeakHashSet<>()).add(listener);
|
||||
}
|
||||
}
|
|
@ -38,25 +38,6 @@ public abstract class LightUpdateMixin extends AbstractChunkProvider {
|
|||
ClientChunkProvider thi = ((ClientChunkProvider) (Object) this);
|
||||
ClientWorld world = (ClientWorld) thi.getLevel();
|
||||
|
||||
Chunk chunk = thi.getChunk(pos.x(), pos.z(), false);
|
||||
|
||||
int sectionY = pos.y();
|
||||
|
||||
if (ChunkUtil.isValidSection(chunk, sectionY)) {
|
||||
InstanceManager<TileEntity> tiles = InstancedRenderDispatcher.getTiles(world);
|
||||
InstanceManager<Entity> entities = InstancedRenderDispatcher.getEntities(world);
|
||||
|
||||
chunk.getBlockEntities()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> SectionPos.blockToSectionCoord(entry.getKey()
|
||||
.getY()) == sectionY)
|
||||
.map(Map.Entry::getValue)
|
||||
.forEach(tiles::onLightUpdate);
|
||||
|
||||
chunk.getEntitySections()[sectionY].forEach(entities::onLightUpdate);
|
||||
}
|
||||
|
||||
LightUpdater.getInstance()
|
||||
.onLightUpdate(world, type, pos.asLong());
|
||||
}
|
||||
|
|
|
@ -34,22 +34,6 @@ public class NetworkLightUpdateMixin {
|
|||
int chunkX = packet.getX();
|
||||
int chunkZ = packet.getZ();
|
||||
|
||||
Chunk chunk = world.getChunkSource()
|
||||
.getChunk(chunkX, chunkZ, false);
|
||||
|
||||
if (chunk != null) {
|
||||
InstanceManager<TileEntity> tiles = InstancedRenderDispatcher.getTiles(world);
|
||||
InstanceManager<Entity> entities = InstancedRenderDispatcher.getEntities(world);
|
||||
|
||||
chunk.getBlockEntities()
|
||||
.values()
|
||||
.forEach(tiles::onLightUpdate);
|
||||
|
||||
Arrays.stream(chunk.getEntitySections())
|
||||
.flatMap(ClassInheritanceMultiMap::stream)
|
||||
.forEach(entities::onLightUpdate);
|
||||
}
|
||||
|
||||
LightUpdater.getInstance()
|
||||
.onLightPacket(world, chunkX, chunkZ);
|
||||
});
|
||||
|
|
|
@ -98,7 +98,11 @@ public class WeakHashSet<T> extends AbstractSet<T> {
|
|||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c) {
|
||||
return false;
|
||||
boolean out = false;
|
||||
for (T t : c) {
|
||||
out |= add(t);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialManager;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.backend.state.TextureRenderState;
|
||||
import com.jozufozu.flywheel.core.Materials;
|
||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
||||
import com.jozufozu.flywheel.core.model.BlockModel;
|
||||
import com.jozufozu.flywheel.core.model.IModel;
|
||||
import com.jozufozu.flywheel.core.model.ModelPart;
|
||||
import com.jozufozu.flywheel.util.AnimationTickHolder;
|
||||
|
@ -21,22 +20,36 @@ import net.minecraft.util.math.MathHelper;
|
|||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
|
||||
public class MinecartInstance<T extends AbstractMinecartEntity> extends EntityInstance<T> implements IDynamicInstance {
|
||||
public class MinecartInstance<T extends AbstractMinecartEntity> extends EntityInstance<T> implements IDynamicInstance, ITickableInstance {
|
||||
|
||||
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
|
||||
|
||||
MatrixTransformStack stack = new MatrixTransformStack();
|
||||
|
||||
private final ModelData body;
|
||||
private final ModelData contents;
|
||||
private ModelData contents;
|
||||
private BlockState blockstate;
|
||||
|
||||
public MinecartInstance(MaterialManager materialManager, T entity) {
|
||||
super(materialManager, entity);
|
||||
|
||||
blockstate = entity.getDisplayBlockState();
|
||||
contents = getContents();
|
||||
body = getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
BlockState displayBlockState = entity.getDisplayBlockState();
|
||||
|
||||
if (displayBlockState != blockstate) {
|
||||
blockstate = displayBlockState;
|
||||
contents.delete();
|
||||
contents = getContents();
|
||||
updateLight();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
stack.setIdentity();
|
||||
|
@ -124,8 +137,6 @@ public class MinecartInstance<T extends AbstractMinecartEntity> extends EntityIn
|
|||
}
|
||||
|
||||
private ModelData getContents() {
|
||||
BlockState blockstate = entity.getDisplayBlockState();
|
||||
|
||||
if (blockstate.getRenderShape() == BlockRenderType.INVISIBLE)
|
||||
return null;
|
||||
|
||||
|
|
Loading…
Reference in a new issue