mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-28 16:06:48 +01:00
Instance managers are abstract
This commit is contained in:
parent
b4a1fbf2c7
commit
c1dadf3860
9 changed files with 329 additions and 487 deletions
|
@ -26,7 +26,7 @@ import net.minecraft.world.World;
|
||||||
public class Backend {
|
public class Backend {
|
||||||
public static final Logger log = LogManager.getLogger(Backend.class);
|
public static final Logger log = LogManager.getLogger(Backend.class);
|
||||||
|
|
||||||
public static Backend INSTANCE;
|
protected static final Backend INSTANCE = new Backend();
|
||||||
|
|
||||||
public static Backend getInstance() {
|
public static Backend getInstance() {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
|
@ -46,7 +46,7 @@ public class Backend {
|
||||||
private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>();
|
private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>();
|
||||||
private final Map<ResourceLocation, ProgramSpec> programSpecRegistry = new HashMap<>();
|
private final Map<ResourceLocation, ProgramSpec> programSpecRegistry = new HashMap<>();
|
||||||
|
|
||||||
public Backend() {
|
protected Backend() {
|
||||||
// Can be null when running datagenerators due to the unfortunate time we call this
|
// Can be null when running datagenerators due to the unfortunate time we call this
|
||||||
minecraft = Minecraft.getInstance();
|
minecraft = Minecraft.getInstance();
|
||||||
if (minecraft == null) return;
|
if (minecraft == null) return;
|
||||||
|
|
|
@ -11,4 +11,12 @@ import net.minecraft.util.math.BlockPos;
|
||||||
public interface IInstance {
|
public interface IInstance {
|
||||||
|
|
||||||
BlockPos getWorldPosition();
|
BlockPos getWorldPosition();
|
||||||
|
|
||||||
|
void updateLight();
|
||||||
|
|
||||||
|
void remove();
|
||||||
|
|
||||||
|
boolean shouldReset();
|
||||||
|
|
||||||
|
void update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
package com.jozufozu.flywheel.backend.instancing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.vector.Vector3f;
|
||||||
|
|
||||||
|
public abstract class InstanceManager<T> implements MaterialManager.OriginShiftListener {
|
||||||
|
|
||||||
|
public final MaterialManager<?> materialManager;
|
||||||
|
|
||||||
|
protected final ArrayList<T> queuedAdditions;
|
||||||
|
protected final ConcurrentHashMap.KeySetView<T, Boolean> queuedUpdates;
|
||||||
|
|
||||||
|
protected final Map<T, IInstance> instances;
|
||||||
|
protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances;
|
||||||
|
protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances;
|
||||||
|
|
||||||
|
protected int frame;
|
||||||
|
protected int tick;
|
||||||
|
|
||||||
|
public InstanceManager(MaterialManager<?> materialManager) {
|
||||||
|
this.materialManager = materialManager;
|
||||||
|
this.queuedUpdates = ConcurrentHashMap.newKeySet(64);
|
||||||
|
this.queuedAdditions = new ArrayList<>(64);
|
||||||
|
this.instances = new HashMap<>();
|
||||||
|
|
||||||
|
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
||||||
|
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
materialManager.onOriginShift(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick(double cameraX, double cameraY, double cameraZ) {
|
||||||
|
tick++;
|
||||||
|
|
||||||
|
// integer camera pos
|
||||||
|
int cX = (int) cameraX;
|
||||||
|
int cY = (int) cameraY;
|
||||||
|
int cZ = (int) cameraZ;
|
||||||
|
|
||||||
|
if (tickableInstances.size() > 0) {
|
||||||
|
for (ITickableInstance instance : tickableInstances.values()) {
|
||||||
|
if (!instance.decreaseTickRateWithDistance()) {
|
||||||
|
instance.tick();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos pos = instance.getWorldPosition();
|
||||||
|
|
||||||
|
int dX = pos.getX() - cX;
|
||||||
|
int dY = pos.getY() - cY;
|
||||||
|
int dZ = pos.getZ() - cZ;
|
||||||
|
|
||||||
|
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
||||||
|
instance.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedUpdates.forEach(te -> {
|
||||||
|
queuedUpdates.remove(te);
|
||||||
|
|
||||||
|
update(te);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginFrame(ActiveRenderInfo info) {
|
||||||
|
frame++;
|
||||||
|
processQueuedAdditions();
|
||||||
|
|
||||||
|
Vector3f look = info.getHorizontalPlane();
|
||||||
|
float lookX = look.getX();
|
||||||
|
float lookY = look.getY();
|
||||||
|
float lookZ = look.getZ();
|
||||||
|
|
||||||
|
// integer camera pos
|
||||||
|
int cX = (int) info.getProjectedView().x;
|
||||||
|
int cY = (int) info.getProjectedView().y;
|
||||||
|
int cZ = (int) info.getProjectedView().z;
|
||||||
|
|
||||||
|
if (dynamicInstances.size() > 0) {
|
||||||
|
dynamicInstances.object2ObjectEntrySet().fastForEach(e -> {
|
||||||
|
IDynamicInstance dyn = e.getValue();
|
||||||
|
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
||||||
|
dyn.beginFrame();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
if (obj instanceof IInstanceRendered) {
|
||||||
|
addInternal(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void queueAdd(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
queuedAdditions.add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
if (obj instanceof IInstanceRendered) {
|
||||||
|
IInstance instance = getInstance(obj, false);
|
||||||
|
|
||||||
|
if (instance != null) {
|
||||||
|
|
||||||
|
if (instance.shouldReset()) {
|
||||||
|
removeInternal(obj, instance);
|
||||||
|
|
||||||
|
createInternal(obj);
|
||||||
|
} else {
|
||||||
|
instance.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void queueUpdate(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
queuedUpdates.add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLightUpdate(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
if (obj instanceof IInstanceRendered) {
|
||||||
|
IInstance instance = getInstance(obj, false);
|
||||||
|
|
||||||
|
if (instance != null)
|
||||||
|
instance.updateLight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(T obj) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return;
|
||||||
|
|
||||||
|
if (obj instanceof IInstanceRendered) {
|
||||||
|
IInstance instance = getInstance(obj, false);
|
||||||
|
if (instance != null)
|
||||||
|
removeInternal(obj, instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
instances.clear();
|
||||||
|
dynamicInstances.clear();
|
||||||
|
tickableInstances.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
protected <I extends T> IInstance getInstance(I obj, boolean create) {
|
||||||
|
if (!Backend.getInstance().canUseInstancing()) return null;
|
||||||
|
|
||||||
|
IInstance instance = instances.get(obj);
|
||||||
|
|
||||||
|
if (instance != null) {
|
||||||
|
return instance;
|
||||||
|
} else if (create && canCreateInstance(obj)) {
|
||||||
|
return createInternal(obj);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void processQueuedAdditions() {
|
||||||
|
if (queuedAdditions.size() > 0) {
|
||||||
|
queuedAdditions.forEach(this::addInternal);
|
||||||
|
queuedAdditions.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
||||||
|
int dX = worldPos.getX() - cX;
|
||||||
|
int dY = worldPos.getY() - cY;
|
||||||
|
int dZ = worldPos.getZ() - cZ;
|
||||||
|
|
||||||
|
// is it more than 2 blocks behind the camera?
|
||||||
|
int dist = 2;
|
||||||
|
float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ;
|
||||||
|
if (dot < 0) return false;
|
||||||
|
|
||||||
|
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
||||||
|
int dSq = dX * dX + dY * dY + dZ * dZ;
|
||||||
|
|
||||||
|
return (dSq / 1024) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addInternal(T tile) {
|
||||||
|
getInstance(tile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeInternal(T obj, IInstance instance) {
|
||||||
|
instance.remove();
|
||||||
|
instances.remove(obj);
|
||||||
|
dynamicInstances.remove(obj);
|
||||||
|
tickableInstances.remove(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IInstance createInternal(T obj) {
|
||||||
|
IInstance renderer = createRaw(obj);
|
||||||
|
|
||||||
|
if (renderer != null) {
|
||||||
|
renderer.updateLight();
|
||||||
|
instances.put(obj, renderer);
|
||||||
|
|
||||||
|
if (renderer instanceof IDynamicInstance)
|
||||||
|
dynamicInstances.put(obj, (IDynamicInstance) renderer);
|
||||||
|
|
||||||
|
if (renderer instanceof ITickableInstance)
|
||||||
|
tickableInstances.put(obj, ((ITickableInstance) renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOriginShift() {
|
||||||
|
ArrayList<T> instancedTiles = new ArrayList<>(instances.keySet());
|
||||||
|
invalidate();
|
||||||
|
instancedTiles.forEach(this::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract IInstance createRaw(T obj);
|
||||||
|
|
||||||
|
protected abstract boolean canCreateInstance(T entity);
|
||||||
|
}
|
|
@ -35,7 +35,6 @@ public class InstancedRenderRegistry {
|
||||||
this.entities.put(type, rendererFactory);
|
this.entities.put(type, rendererFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
INSTANCE.register(AllEntityTypes.SUPER_GLUE.get(), GlueInstance::new);
|
INSTANCE.register(AllEntityTypes.SUPER_GLUE.get(), GlueInstance::new);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,259 +1,29 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.entity;
|
package com.jozufozu.flywheel.backend.instancing.entity;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstanceRendered;
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
import net.minecraft.world.IBlockReader;
|
import net.minecraft.world.IBlockReader;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class EntityInstanceManager implements MaterialManager.OriginShiftListener {
|
public class EntityInstanceManager extends InstanceManager<Entity> {
|
||||||
|
|
||||||
public final MaterialManager<?> materialManager;
|
|
||||||
|
|
||||||
protected final ArrayList<Entity> queuedAdditions;
|
|
||||||
protected final ConcurrentHashMap.KeySetView<Entity, Boolean> queuedUpdates;
|
|
||||||
|
|
||||||
protected final Map<Entity, EntityInstance<?>> instances;
|
|
||||||
protected final Object2ObjectOpenHashMap<Entity, ITickableInstance> tickableInstances;
|
|
||||||
protected final Object2ObjectOpenHashMap<Entity, IDynamicInstance> dynamicInstances;
|
|
||||||
|
|
||||||
protected int frame;
|
|
||||||
protected int tick;
|
|
||||||
|
|
||||||
public EntityInstanceManager(MaterialManager<?> materialManager) {
|
public EntityInstanceManager(MaterialManager<?> materialManager) {
|
||||||
this.materialManager = materialManager;
|
super(materialManager);
|
||||||
this.queuedUpdates = ConcurrentHashMap.newKeySet(64);
|
|
||||||
this.queuedAdditions = new ArrayList<>(64);
|
|
||||||
this.instances = new HashMap<>();
|
|
||||||
|
|
||||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
materialManager.onOriginShift(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
|
||||||
tick++;
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) cameraX;
|
|
||||||
int cY = (int) cameraY;
|
|
||||||
int cZ = (int) cameraZ;
|
|
||||||
|
|
||||||
if (tickableInstances.size() > 0) {
|
|
||||||
for (ITickableInstance instance : tickableInstances.values()) {
|
|
||||||
if (!instance.decreaseTickRateWithDistance()) {
|
|
||||||
instance.tick();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos pos = instance.getWorldPosition();
|
|
||||||
|
|
||||||
int dX = pos.getX() - cX;
|
|
||||||
int dY = pos.getY() - cY;
|
|
||||||
int dZ = pos.getZ() - cZ;
|
|
||||||
|
|
||||||
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
|
||||||
instance.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queuedUpdates.forEach(te -> {
|
|
||||||
queuedUpdates.remove(te);
|
|
||||||
|
|
||||||
update(te);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(ActiveRenderInfo info) {
|
|
||||||
frame++;
|
|
||||||
processQueuedAdditions();
|
|
||||||
|
|
||||||
Vector3f look = info.getHorizontalPlane();
|
|
||||||
float lookX = look.getX();
|
|
||||||
float lookY = look.getY();
|
|
||||||
float lookZ = look.getZ();
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) info.getProjectedView().x;
|
|
||||||
int cY = (int) info.getProjectedView().y;
|
|
||||||
int cZ = (int) info.getProjectedView().z;
|
|
||||||
|
|
||||||
if (dynamicInstances.size() > 0) {
|
|
||||||
dynamicInstances.object2ObjectEntrySet().fastForEach(e -> {
|
|
||||||
IDynamicInstance dyn = e.getValue();
|
|
||||||
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
|
||||||
dyn.beginFrame();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOriginShift() {
|
protected IInstance createRaw(Entity obj) {
|
||||||
ArrayList<Entity> instancedTiles = new ArrayList<>(instances.keySet());
|
return InstancedRenderRegistry.getInstance().create(materialManager, obj);
|
||||||
invalidate();
|
|
||||||
instancedTiles.forEach(this::add);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@Override
|
||||||
@Nullable
|
protected boolean canCreateInstance(Entity entity) {
|
||||||
public <T extends Entity> EntityInstance<? super T> getInstance(T entity, boolean create) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return null;
|
|
||||||
|
|
||||||
EntityInstance<?> instance = instances.get(entity);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
return (EntityInstance<? super T>) instance;
|
|
||||||
} else if (create && canCreateInstance(entity)) {
|
|
||||||
return createInternal(entity);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Entity> void onLightUpdate(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
EntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null)
|
|
||||||
instance.updateLight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Entity> void add(T entity) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (entity instanceof IInstanceRendered) {
|
|
||||||
addInternal(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Entity> void update(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
EntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
|
|
||||||
if (instance.shouldReset()) {
|
|
||||||
removeInternal(tile, instance);
|
|
||||||
|
|
||||||
createInternal(tile);
|
|
||||||
} else {
|
|
||||||
instance.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Entity> void remove(T entity) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (entity instanceof IInstanceRendered) {
|
|
||||||
removeInternal(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized <T extends Entity> void queueAdd(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedAdditions.add(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized <T extends Entity> void queueUpdate(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedUpdates.add(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void processQueuedAdditions() {
|
|
||||||
if (queuedAdditions.size() > 0) {
|
|
||||||
queuedAdditions.forEach(this::addInternal);
|
|
||||||
queuedAdditions.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
|
||||||
int dX = worldPos.getX() - cX;
|
|
||||||
int dY = worldPos.getY() - cY;
|
|
||||||
int dZ = worldPos.getZ() - cZ;
|
|
||||||
|
|
||||||
// is it more than 2 blocks behind the camera?
|
|
||||||
int dist = 2;
|
|
||||||
float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ;
|
|
||||||
if (dot < 0) return false;
|
|
||||||
|
|
||||||
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
|
||||||
int dSq = dX * dX + dY * dY + dZ * dZ;
|
|
||||||
|
|
||||||
return (dSq / 1024) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInternal(Entity tile) {
|
|
||||||
getInstance(tile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Entity> void removeInternal(T tile) {
|
|
||||||
EntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
removeInternal(tile, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeInternal(Entity tile, EntityInstance<?> instance) {
|
|
||||||
instance.remove();
|
|
||||||
instances.remove(tile);
|
|
||||||
dynamicInstances.remove(tile);
|
|
||||||
tickableInstances.remove(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Entity> EntityInstance<? super T> createInternal(T tile) {
|
|
||||||
EntityInstance<? super T> renderer = InstancedRenderRegistry.getInstance().create(materialManager, tile);
|
|
||||||
|
|
||||||
if (renderer != null) {
|
|
||||||
renderer.updateLight();
|
|
||||||
instances.put(tile, renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof IDynamicInstance)
|
|
||||||
dynamicInstances.put(tile, (IDynamicInstance) renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof ITickableInstance)
|
|
||||||
tickableInstances.put(tile, ((ITickableInstance) renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
instances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
tickableInstances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canCreateInstance(Entity entity) {
|
|
||||||
if (!entity.isAlive()) return false;
|
if (!entity.isAlive()) return false;
|
||||||
|
|
||||||
World world = entity.world;
|
World world = entity.world;
|
||||||
|
|
|
@ -1,259 +1,29 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.tile;
|
package com.jozufozu.flywheel.backend.instancing.tile;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstanceRendered;
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
import net.minecraft.world.IBlockReader;
|
import net.minecraft.world.IBlockReader;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class TileInstanceManager implements MaterialManager.OriginShiftListener {
|
public class TileInstanceManager extends InstanceManager<TileEntity> {
|
||||||
|
|
||||||
public final MaterialManager<?> materialManager;
|
|
||||||
|
|
||||||
protected final ArrayList<TileEntity> queuedAdditions;
|
|
||||||
protected final ConcurrentHashMap.KeySetView<TileEntity, Boolean> queuedUpdates;
|
|
||||||
|
|
||||||
protected final Map<TileEntity, TileEntityInstance<?>> instances;
|
|
||||||
protected final Object2ObjectOpenHashMap<TileEntity, ITickableInstance> tickableInstances;
|
|
||||||
protected final Object2ObjectOpenHashMap<TileEntity, IDynamicInstance> dynamicInstances;
|
|
||||||
|
|
||||||
protected int frame;
|
|
||||||
protected int tick;
|
|
||||||
|
|
||||||
public TileInstanceManager(MaterialManager<?> materialManager) {
|
public TileInstanceManager(MaterialManager<?> materialManager) {
|
||||||
this.materialManager = materialManager;
|
super(materialManager);
|
||||||
this.queuedUpdates = ConcurrentHashMap.newKeySet(64);
|
|
||||||
this.queuedAdditions = new ArrayList<>(64);
|
|
||||||
this.instances = new HashMap<>();
|
|
||||||
|
|
||||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
materialManager.onOriginShift(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
|
||||||
tick++;
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) cameraX;
|
|
||||||
int cY = (int) cameraY;
|
|
||||||
int cZ = (int) cameraZ;
|
|
||||||
|
|
||||||
if (tickableInstances.size() > 0) {
|
|
||||||
for (ITickableInstance instance : tickableInstances.values()) {
|
|
||||||
if (!instance.decreaseTickRateWithDistance()) {
|
|
||||||
instance.tick();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos pos = instance.getWorldPosition();
|
|
||||||
|
|
||||||
int dX = pos.getX() - cX;
|
|
||||||
int dY = pos.getY() - cY;
|
|
||||||
int dZ = pos.getZ() - cZ;
|
|
||||||
|
|
||||||
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
|
||||||
instance.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queuedUpdates.forEach(te -> {
|
|
||||||
queuedUpdates.remove(te);
|
|
||||||
|
|
||||||
update(te);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(ActiveRenderInfo info) {
|
|
||||||
frame++;
|
|
||||||
processQueuedAdditions();
|
|
||||||
|
|
||||||
Vector3f look = info.getHorizontalPlane();
|
|
||||||
float lookX = look.getX();
|
|
||||||
float lookY = look.getY();
|
|
||||||
float lookZ = look.getZ();
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) info.getProjectedView().x;
|
|
||||||
int cY = (int) info.getProjectedView().y;
|
|
||||||
int cZ = (int) info.getProjectedView().z;
|
|
||||||
|
|
||||||
if (dynamicInstances.size() > 0) {
|
|
||||||
dynamicInstances.object2ObjectEntrySet().fastForEach(e -> {
|
|
||||||
IDynamicInstance dyn = e.getValue();
|
|
||||||
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
|
||||||
dyn.beginFrame();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOriginShift() {
|
protected IInstance createRaw(TileEntity obj) {
|
||||||
ArrayList<TileEntity> instancedTiles = new ArrayList<>(instances.keySet());
|
return InstancedRenderRegistry.getInstance().create(materialManager, obj);
|
||||||
invalidate();
|
|
||||||
instancedTiles.forEach(this::add);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@Override
|
||||||
@Nullable
|
protected boolean canCreateInstance(TileEntity tile) {
|
||||||
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return null;
|
|
||||||
|
|
||||||
TileEntityInstance<?> instance = instances.get(tile);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
return (TileEntityInstance<? super T>) instance;
|
|
||||||
} else if (create && canCreateInstance(tile)) {
|
|
||||||
return createInternal(tile);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends TileEntity> void onLightUpdate(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null)
|
|
||||||
instance.updateLight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends TileEntity> void add(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
addInternal(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends TileEntity> void update(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
|
|
||||||
if (instance.shouldReset()) {
|
|
||||||
removeInternal(tile, instance);
|
|
||||||
|
|
||||||
createInternal(tile);
|
|
||||||
} else {
|
|
||||||
instance.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends TileEntity> void remove(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (tile instanceof IInstanceRendered) {
|
|
||||||
removeInternal(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized <T extends TileEntity> void queueAdd(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedAdditions.add(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized <T extends TileEntity> void queueUpdate(T tile) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedUpdates.add(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void processQueuedAdditions() {
|
|
||||||
if (queuedAdditions.size() > 0) {
|
|
||||||
queuedAdditions.forEach(this::addInternal);
|
|
||||||
queuedAdditions.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
|
||||||
int dX = worldPos.getX() - cX;
|
|
||||||
int dY = worldPos.getY() - cY;
|
|
||||||
int dZ = worldPos.getZ() - cZ;
|
|
||||||
|
|
||||||
// is it more than 2 blocks behind the camera?
|
|
||||||
int dist = 2;
|
|
||||||
float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ;
|
|
||||||
if (dot < 0) return false;
|
|
||||||
|
|
||||||
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
|
||||||
int dSq = dX * dX + dY * dY + dZ * dZ;
|
|
||||||
|
|
||||||
return (dSq / 1024) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addInternal(TileEntity tile) {
|
|
||||||
getInstance(tile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends TileEntity> void removeInternal(T tile) {
|
|
||||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
removeInternal(tile, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeInternal(TileEntity tile, TileEntityInstance<?> instance) {
|
|
||||||
instance.remove();
|
|
||||||
instances.remove(tile);
|
|
||||||
dynamicInstances.remove(tile);
|
|
||||||
tickableInstances.remove(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends TileEntity> TileEntityInstance<? super T> createInternal(T tile) {
|
|
||||||
TileEntityInstance<? super T> renderer = InstancedRenderRegistry.getInstance().create(materialManager, tile);
|
|
||||||
|
|
||||||
if (renderer != null) {
|
|
||||||
renderer.updateLight();
|
|
||||||
instances.put(tile, renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof IDynamicInstance)
|
|
||||||
dynamicInstances.put(tile, (IDynamicInstance) renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof ITickableInstance)
|
|
||||||
tickableInstances.put(tile, ((ITickableInstance) renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
instances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
tickableInstances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canCreateInstance(TileEntity tile) {
|
|
||||||
if (tile.isRemoved()) return false;
|
if (tile.isRemoved()) return false;
|
||||||
|
|
||||||
World world = tile.getWorld();
|
World world = tile.getWorld();
|
||||||
|
|
|
@ -6,22 +6,17 @@ import java.util.List;
|
||||||
import net.minecraft.inventory.container.PlayerContainer;
|
import net.minecraft.inventory.container.PlayerContainer;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||||
import net.minecraftforge.eventbus.api.IEventBus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is primarily for hacking entity textures into the block atlas.
|
* This is primarily for hacking entity textures into the block atlas.
|
||||||
*/
|
*/
|
||||||
public class AtlasStitcher {
|
public class AtlasStitcher {
|
||||||
public static AtlasStitcher INSTANCE;
|
protected static final AtlasStitcher INSTANCE = new AtlasStitcher();
|
||||||
|
|
||||||
public static AtlasStitcher getInstance() {
|
public static AtlasStitcher getInstance() {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AtlasStitcher(IEventBus modEventBus) {
|
|
||||||
modEventBus.addListener(this::onTextureStitch);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<StitchedSprite> sprites = new ArrayList<>();
|
private final List<StitchedSprite> sprites = new ArrayList<>();
|
||||||
|
|
||||||
public StitchedSprite get(ResourceLocation loc) {
|
public StitchedSprite get(ResourceLocation loc) {
|
||||||
|
|
|
@ -78,8 +78,8 @@ public class CreateClient {
|
||||||
modEventBus.addListener(AllParticleTypes::registerFactories);
|
modEventBus.addListener(AllParticleTypes::registerFactories);
|
||||||
modEventBus.addListener(ClientEvents::loadCompleted);
|
modEventBus.addListener(ClientEvents::loadCompleted);
|
||||||
|
|
||||||
Backend.INSTANCE = new Backend();
|
Backend.getInstance();
|
||||||
AtlasStitcher.INSTANCE = new AtlasStitcher(modEventBus);
|
modEventBus.addListener(AtlasStitcher.getInstance()::onTextureStitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clientInit(FMLClientSetupEvent event) {
|
public static void clientInit(FMLClientSetupEvent event) {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#flwbuiltins
|
||||||
|
#flwinclude <"flywheel:core/matutils.glsl">
|
||||||
|
#flwinclude <"flywheel:core/quaternion.glsl">
|
||||||
|
#flwinclude <"flywheel:core/diffuse.glsl">
|
||||||
|
|
||||||
|
#[InstanceData]
|
||||||
|
struct Oriented {
|
||||||
|
// each vec 4 is 2 light coords packed <lo y, hi y>
|
||||||
|
// x z
|
||||||
|
vec4 lightA;// lo, lo
|
||||||
|
vec4 lightB;// hi, lo
|
||||||
|
vec4 lightC;// hi, hi
|
||||||
|
vec4 lightD;// lo, hi
|
||||||
|
|
||||||
|
vec3 loCorner;
|
||||||
|
vec3 size;
|
||||||
|
|
||||||
|
vec4 color;
|
||||||
|
vec3 pos;
|
||||||
|
vec3 pivot;
|
||||||
|
vec4 rotation;
|
||||||
|
};
|
||||||
|
|
||||||
|
#flwinclude <"flywheel:data/modelvertex.glsl">
|
||||||
|
#flwinclude <"flywheel:data/blockfragment.glsl">
|
||||||
|
|
||||||
|
BlockFrag FLWMain(Vertex v, Oriented o) {
|
||||||
|
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||||
|
|
||||||
|
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||||
|
|
||||||
|
FLWFinalizeWorldPos(worldPos);
|
||||||
|
FLWFinalizeNormal(norm);
|
||||||
|
|
||||||
|
// manual trilinear interpolation
|
||||||
|
vec3 lightPos = (worldPos.xyz - o.loCorner) / o.size;
|
||||||
|
|
||||||
|
vec4 lightLoZ = mix(lightA, lightB, lightPos.x);// lo z
|
||||||
|
vec4 lightHiZ = mix(lightD, lightC, lightPos.x);// hi z
|
||||||
|
|
||||||
|
vec4 lightE = mix(lightLoZ, lightHiZ, lightPos.z);// <lo y, hi y>
|
||||||
|
|
||||||
|
vec2 lightCoord = mix(lightE.xy, lightE.zw, lightPos.y);
|
||||||
|
|
||||||
|
BlockFrag b;
|
||||||
|
b.diffuse = diffuse(norm);
|
||||||
|
b.texCoords = v.texCoords;
|
||||||
|
b.light = lightCoord;
|
||||||
|
#if defined(DEBUG_NORMAL)
|
||||||
|
b.color = vec4(norm, 1.);
|
||||||
|
#else
|
||||||
|
b.color = o.color;
|
||||||
|
#endif
|
||||||
|
return b;
|
||||||
|
}
|
Loading…
Reference in a new issue