mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-07 12:56:31 +01:00
Freestyle instances
- Add Effect instancing - Allows mods to create visual effects without (block) entities - One effect object maps to many AbstractInstances - Effect objects are expected to exist in isolation - Refactor InstanceManager to be composable about how its instances are stored - EffectInstanceManager uses this to provide a one to many topology - This is in need of more iteration, and more aspects of InstanceManager should be made composable in the future
This commit is contained in:
parent
5657e8669e
commit
1fe8297e72
22 changed files with 683 additions and 242 deletions
|
@ -28,6 +28,7 @@ import com.jozufozu.flywheel.event.ForgeEvents;
|
|||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
||||
import com.jozufozu.flywheel.vanilla.VanillaInstances;
|
||||
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraft.commands.synchronization.ArgumentTypes;
|
||||
|
@ -108,6 +109,8 @@ public class Flywheel {
|
|||
modEventBus.addListener(StitchedSprite::onTextureStitchPre);
|
||||
modEventBus.addListener(StitchedSprite::onTextureStitchPost);
|
||||
|
||||
// forgeEventBus.addListener(ExampleEffect::spawn);
|
||||
|
||||
LayoutShaders.init();
|
||||
InstanceShaders.init();
|
||||
Contexts.init();
|
||||
|
|
|
@ -5,5 +5,7 @@ import java.util.List;
|
|||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
|
||||
public interface Engine extends RenderDispatcher, InstancerManager {
|
||||
void attachManagers(InstanceManager<?>... listener);
|
||||
|
||||
void addDebugInfo(List<String> info);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
|
@ -20,37 +14,38 @@ import com.jozufozu.flywheel.config.FlwConfig;
|
|||
import com.jozufozu.flywheel.light.LightUpdater;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
public abstract class InstanceManager<T> {
|
||||
|
||||
public final InstancerManager instancerManager;
|
||||
|
||||
private final Set<T> queuedAdditions;
|
||||
private final Set<T> queuedUpdates;
|
||||
|
||||
protected final Map<T, AbstractInstance> instances;
|
||||
protected final Object2ObjectOpenHashMap<T, TickableInstance> tickableInstances;
|
||||
protected final Object2ObjectOpenHashMap<T, DynamicInstance> dynamicInstances;
|
||||
|
||||
protected DistanceUpdateLimiter frame;
|
||||
protected DistanceUpdateLimiter tick;
|
||||
|
||||
public InstanceManager(InstancerManager instancerManager) {
|
||||
this.instancerManager = instancerManager;
|
||||
public InstanceManager() {
|
||||
this.queuedUpdates = new HashSet<>(64);
|
||||
this.queuedAdditions = new HashSet<>(64);
|
||||
this.instances = new HashMap<>();
|
||||
|
||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
frame = createUpdateLimiter();
|
||||
tick = createUpdateLimiter();
|
||||
}
|
||||
|
||||
public abstract Storage<T> getStorage();
|
||||
|
||||
/**
|
||||
* Is the given object currently capable of being instanced?
|
||||
*
|
||||
* <p>
|
||||
* This won't be the case for TEs or entities that are outside of loaded chunks.
|
||||
* </p>
|
||||
*
|
||||
* @return true if the object is currently capable of being instanced.
|
||||
*/
|
||||
protected abstract boolean canCreateInstance(T obj);
|
||||
|
||||
protected DistanceUpdateLimiter createUpdateLimiter() {
|
||||
if (FlwConfig.get().limitUpdates()) {
|
||||
return new BandedPrimeLimiter();
|
||||
|
@ -65,30 +60,9 @@ public abstract class InstanceManager<T> {
|
|||
* @return The object count.
|
||||
*/
|
||||
public int getObjectCount() {
|
||||
return instances.size();
|
||||
return getStorage().getObjectCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given object capable of being instanced at all?
|
||||
*
|
||||
* @return false if on object cannot be instanced.
|
||||
*/
|
||||
protected abstract boolean canInstance(T obj);
|
||||
|
||||
/**
|
||||
* Is the given object currently capable of being instanced?
|
||||
*
|
||||
* <p>
|
||||
* This won't be the case for TEs or entities that are outside of loaded chunks.
|
||||
* </p>
|
||||
*
|
||||
* @return true if the object is currently capable of being instanced.
|
||||
*/
|
||||
protected abstract boolean canCreateInstance(T obj);
|
||||
|
||||
@Nullable
|
||||
protected abstract AbstractInstance createRaw(T obj);
|
||||
|
||||
/**
|
||||
* Ticks the InstanceManager.
|
||||
*
|
||||
|
@ -107,14 +81,14 @@ public abstract class InstanceManager<T> {
|
|||
int cY = (int) cameraY;
|
||||
int cZ = (int) cameraZ;
|
||||
|
||||
ArrayList<TickableInstance> instances = new ArrayList<>(tickableInstances.values());
|
||||
var instances = getStorage().getInstancesForTicking();
|
||||
int incr = 500;
|
||||
int size = instances.size();
|
||||
int start = 0;
|
||||
while (start < size) {
|
||||
int end = Math.min(start + incr, size);
|
||||
|
||||
List<TickableInstance> sub = instances.subList(start, end);
|
||||
var sub = instances.subList(start, end);
|
||||
taskEngine.submit(() -> {
|
||||
for (TickableInstance instance : sub) {
|
||||
tickInstance(cX, cY, cZ, instance);
|
||||
|
@ -154,14 +128,14 @@ public abstract class InstanceManager<T> {
|
|||
int cY = (int) camera.getPosition().y;
|
||||
int cZ = (int) camera.getPosition().z;
|
||||
|
||||
ArrayList<DynamicInstance> instances = new ArrayList<>(dynamicInstances.values());
|
||||
var instances = getStorage().getInstancesForUpdate();
|
||||
int incr = 500;
|
||||
int size = instances.size();
|
||||
int start = 0;
|
||||
while (start < size) {
|
||||
int end = Math.min(start + incr, size);
|
||||
|
||||
List<DynamicInstance> sub = instances.subList(start, end);
|
||||
var sub = instances.subList(start, end);
|
||||
taskEngine.submit(() -> {
|
||||
for (DynamicInstance dyn : sub) {
|
||||
updateInstance(dyn, lookX, lookY, lookZ, cX, cY, cZ);
|
||||
|
@ -197,13 +171,19 @@ public abstract class InstanceManager<T> {
|
|||
public void add(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
addInternal(obj);
|
||||
if (canCreateInstance(obj)) {
|
||||
getStorage().add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void queueAdd(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
if (!Backend.isOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canCreateInstance(obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (queuedAdditions) {
|
||||
queuedAdditions.add(obj);
|
||||
|
@ -212,6 +192,11 @@ public abstract class InstanceManager<T> {
|
|||
|
||||
public void queueUpdate(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
|
||||
if (!canCreateInstance(obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (queuedUpdates) {
|
||||
queuedUpdates.add(obj);
|
||||
}
|
||||
|
@ -231,45 +216,21 @@ public abstract class InstanceManager<T> {
|
|||
public void update(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
AbstractInstance instance = getInstance(obj);
|
||||
|
||||
if (instance != null) {
|
||||
|
||||
// resetting instances is by default used to handle block state changes.
|
||||
if (instance.shouldReset()) {
|
||||
// delete and re-create the instance.
|
||||
// resetting an instance supersedes updating it.
|
||||
removeInternal(obj, instance);
|
||||
createInternal(obj);
|
||||
} else {
|
||||
instance.update();
|
||||
}
|
||||
}
|
||||
if (canCreateInstance(obj)) {
|
||||
getStorage().update(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
|
||||
if (canInstance(obj)) {
|
||||
AbstractInstance instance = getInstance(obj);
|
||||
if (instance != null) removeInternal(obj, instance);
|
||||
if (canCreateInstance(obj)) {
|
||||
getStorage().remove(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
instances.values().forEach(AbstractInstance::remove);
|
||||
instances.clear();
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected <I extends T> AbstractInstance getInstance(I obj) {
|
||||
if (!Backend.isOn()) return null;
|
||||
|
||||
return instances.get(obj);
|
||||
getStorage().invalidate();
|
||||
}
|
||||
|
||||
protected void processQueuedAdditions() {
|
||||
|
@ -285,7 +246,7 @@ public abstract class InstanceManager<T> {
|
|||
}
|
||||
|
||||
if (!queued.isEmpty()) {
|
||||
queued.forEach(this::addInternal);
|
||||
queued.forEach(getStorage()::add);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,75 +259,16 @@ public abstract class InstanceManager<T> {
|
|||
}
|
||||
|
||||
if (queued.size() > 0) {
|
||||
queued.forEach(this::update);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addInternal(T obj) {
|
||||
if (!Backend.isOn()) return;
|
||||
|
||||
AbstractInstance instance = instances.get(obj);
|
||||
|
||||
if (instance == null && canCreateInstance(obj)) {
|
||||
createInternal(obj);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeInternal(T obj, AbstractInstance instance) {
|
||||
instance.remove();
|
||||
instances.remove(obj);
|
||||
dynamicInstances.remove(obj);
|
||||
tickableInstances.remove(obj);
|
||||
LightUpdater.get(instance.level)
|
||||
.removeListener(instance);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected AbstractInstance createInternal(T obj) {
|
||||
AbstractInstance renderer = createRaw(obj);
|
||||
|
||||
if (renderer != null) {
|
||||
setup(obj, renderer);
|
||||
instances.put(obj, renderer);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private void setup(T obj, AbstractInstance renderer) {
|
||||
renderer.init();
|
||||
renderer.updateLight();
|
||||
LightUpdater.get(renderer.level)
|
||||
.addListener(renderer);
|
||||
if (renderer instanceof TickableInstance r) {
|
||||
tickableInstances.put(obj, r);
|
||||
r.tick();
|
||||
}
|
||||
|
||||
if (renderer instanceof DynamicInstance r) {
|
||||
dynamicInstances.put(obj, r);
|
||||
r.beginFrame();
|
||||
queued.forEach(getStorage()::update);
|
||||
}
|
||||
}
|
||||
|
||||
public void onOriginShift() {
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
instances.replaceAll((obj, instance) -> {
|
||||
instance.remove();
|
||||
|
||||
AbstractInstance out = createRaw(obj);
|
||||
|
||||
if (out != null) {
|
||||
setup(obj, out);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
getStorage().recreateAll();
|
||||
}
|
||||
|
||||
public void detachLightListeners() {
|
||||
for (AbstractInstance value : instances.values()) {
|
||||
public void delete() {
|
||||
for (AbstractInstance value : getStorage().allInstances()) {
|
||||
LightUpdater.get(value.level).removeListener(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,15 @@ import com.jozufozu.flywheel.api.instance.TickableInstance;
|
|||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.Effect;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.EffectInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||
import com.jozufozu.flywheel.core.Contexts;
|
||||
import com.jozufozu.flywheel.core.RenderContext;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||
import com.jozufozu.flywheel.util.ClientLevelExtension;
|
||||
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
@ -29,47 +31,47 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||
*/
|
||||
public class InstanceWorld {
|
||||
protected final Engine engine;
|
||||
protected final InstanceManager<Entity> entityInstanceManager;
|
||||
protected final InstanceManager<BlockEntity> blockEntityInstanceManager;
|
||||
protected final InstanceManager<Entity> entities;
|
||||
protected final InstanceManager<BlockEntity> blockEntities;
|
||||
|
||||
public final ParallelTaskEngine taskEngine;
|
||||
private final InstanceManager<Effect> effects;
|
||||
|
||||
public static InstanceWorld create(LevelAccessor level) {
|
||||
return switch (Backend.getBackendType()) {
|
||||
case INSTANCING -> {
|
||||
InstancingEngine<WorldProgram> engine = new InstancingEngine<>(Contexts.WORLD);
|
||||
|
||||
var entityInstanceManager = new EntityInstanceManager(engine);
|
||||
var blockEntityInstanceManager = new BlockEntityInstanceManager(engine);
|
||||
|
||||
engine.attachManager(entityInstanceManager);
|
||||
engine.attachManager(blockEntityInstanceManager);
|
||||
yield new InstanceWorld(engine, entityInstanceManager, blockEntityInstanceManager);
|
||||
}
|
||||
case BATCHING -> {
|
||||
var engine = new BatchingEngine();
|
||||
var entityInstanceManager = new EntityInstanceManager(engine);
|
||||
var blockEntityInstanceManager = new BlockEntityInstanceManager(engine);
|
||||
|
||||
yield new InstanceWorld(engine, entityInstanceManager, blockEntityInstanceManager);
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown engine type");
|
||||
var engine = switch (Backend.getBackendType()) {
|
||||
case INSTANCING -> new InstancingEngine<>(Contexts.WORLD);
|
||||
case BATCHING -> new BatchingEngine();
|
||||
case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off.");
|
||||
};
|
||||
|
||||
var entities = new EntityInstanceManager(engine);
|
||||
var blockEntities = new BlockEntityInstanceManager(engine);
|
||||
var effects = new EffectInstanceManager(engine);
|
||||
|
||||
engine.attachManagers(entities, blockEntities, effects);
|
||||
|
||||
return new InstanceWorld(engine, entities, blockEntities, effects);
|
||||
}
|
||||
|
||||
public InstanceWorld(Engine engine, InstanceManager<Entity> entityInstanceManager, InstanceManager<BlockEntity> blockEntityInstanceManager) {
|
||||
public InstanceWorld(Engine engine, InstanceManager<Entity> entities, InstanceManager<BlockEntity> blockEntities,
|
||||
InstanceManager<Effect> effects) {
|
||||
this.engine = engine;
|
||||
this.entityInstanceManager = entityInstanceManager;
|
||||
this.blockEntityInstanceManager = blockEntityInstanceManager;
|
||||
this.entities = entities;
|
||||
this.blockEntities = blockEntities;
|
||||
this.effects = effects;
|
||||
this.taskEngine = Backend.getTaskEngine();
|
||||
}
|
||||
|
||||
public InstanceManager<Entity> getEntityInstanceManager() {
|
||||
return entityInstanceManager;
|
||||
public InstanceManager<Entity> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
public InstanceManager<BlockEntity> getBlockEntityInstanceManager() {
|
||||
return blockEntityInstanceManager;
|
||||
public InstanceManager<Effect> getEffects() {
|
||||
return effects;
|
||||
}
|
||||
|
||||
public InstanceManager<BlockEntity> getBlockEntities() {
|
||||
return blockEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,8 +79,8 @@ public class InstanceWorld {
|
|||
*/
|
||||
public void delete() {
|
||||
engine.delete();
|
||||
entityInstanceManager.detachLightListeners();
|
||||
blockEntityInstanceManager.detachLightListeners();
|
||||
entities.delete();
|
||||
blockEntities.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,8 +98,9 @@ public class InstanceWorld {
|
|||
taskEngine.syncPoint();
|
||||
|
||||
if (!shifted) {
|
||||
blockEntityInstanceManager.beginFrame(taskEngine, camera);
|
||||
entityInstanceManager.beginFrame(taskEngine, camera);
|
||||
blockEntities.beginFrame(taskEngine, camera);
|
||||
entities.beginFrame(taskEngine, camera);
|
||||
effects.beginFrame(taskEngine, camera);
|
||||
}
|
||||
|
||||
engine.beginFrame(taskEngine, camera);
|
||||
|
@ -115,8 +118,13 @@ public class InstanceWorld {
|
|||
|
||||
if (renderViewEntity == null) return;
|
||||
|
||||
blockEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
||||
double x = renderViewEntity.getX();
|
||||
double y = renderViewEntity.getY();
|
||||
double z = renderViewEntity.getZ();
|
||||
|
||||
blockEntities.tick(taskEngine, x, y, z);
|
||||
entities.tick(taskEngine, x, y, z);
|
||||
effects.tick(taskEngine, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -148,7 +156,7 @@ public class InstanceWorld {
|
|||
// Block entities are loaded while chunks are baked.
|
||||
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added.
|
||||
ClientLevelExtension.getAllLoadedEntities(world)
|
||||
.forEach(entityInstanceManager::add);
|
||||
.forEach(entities::add);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
|
|||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.Effect;
|
||||
import com.jozufozu.flywheel.config.FlwCommands;
|
||||
import com.jozufozu.flywheel.config.FlwConfig;
|
||||
import com.jozufozu.flywheel.core.RenderContext;
|
||||
|
@ -10,6 +11,7 @@ import com.jozufozu.flywheel.event.BeginFrameEvent;
|
|||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.util.AnimationTickHolder;
|
||||
import com.jozufozu.flywheel.util.WorldAttached;
|
||||
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
@ -30,7 +32,7 @@ public class InstancedRenderDispatcher {
|
|||
public static void enqueueUpdate(BlockEntity blockEntity) {
|
||||
if (Backend.isOn() && blockEntity.hasLevel() && blockEntity.getLevel() instanceof ClientLevel) {
|
||||
instanceWorlds.get(blockEntity.getLevel())
|
||||
.getBlockEntityInstanceManager()
|
||||
.getBlockEntities()
|
||||
.queueUpdate(blockEntity);
|
||||
}
|
||||
}
|
||||
|
@ -42,17 +44,21 @@ public class InstancedRenderDispatcher {
|
|||
public static void enqueueUpdate(Entity entity) {
|
||||
if (Backend.isOn()) {
|
||||
instanceWorlds.get(entity.level)
|
||||
.getEntityInstanceManager()
|
||||
.getEntities()
|
||||
.queueUpdate(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor world) {
|
||||
return getInstanceWorld(world).getBlockEntityInstanceManager();
|
||||
return getInstanceWorld(world).getBlockEntities();
|
||||
}
|
||||
|
||||
public static InstanceManager<Entity> getEntities(LevelAccessor world) {
|
||||
return getInstanceWorld(world).getEntityInstanceManager();
|
||||
return getInstanceWorld(world).getEntities();
|
||||
}
|
||||
|
||||
public static InstanceManager<Effect> getEffects(LevelAccessor world) {
|
||||
return getInstanceWorld(world).getEffects();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,7 +125,7 @@ public class InstancedRenderDispatcher {
|
|||
InstanceWorld instanceWorld = instanceWorlds.get(Minecraft.getInstance().level);
|
||||
|
||||
debug.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
||||
debug.add("B: " + instanceWorld.blockEntityInstanceManager.getObjectCount() + ", E: " + instanceWorld.entityInstanceManager.getObjectCount());
|
||||
debug.add("B: " + instanceWorld.blockEntities.getObjectCount() + ", E: " + instanceWorld.entities.getObjectCount());
|
||||
instanceWorld.engine.addDebugInfo(debug);
|
||||
} else {
|
||||
debug.add("Disabled");
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
import com.jozufozu.flywheel.light.LightUpdater;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
public abstract class One2OneStorage<T> implements Storage<T> {
|
||||
private final Map<T, AbstractInstance> instances;
|
||||
private final Object2ObjectOpenHashMap<T, TickableInstance> tickableInstances;
|
||||
private final Object2ObjectOpenHashMap<T, DynamicInstance> dynamicInstances;
|
||||
protected final InstancerManager instancerManager;
|
||||
|
||||
public One2OneStorage(InstancerManager instancerManager) {
|
||||
this.instancerManager = instancerManager;
|
||||
this.instances = new HashMap<>();
|
||||
|
||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObjectCount() {
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<AbstractInstance> allInstances() {
|
||||
return instances.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TickableInstance> getInstancesForTicking() {
|
||||
return new ArrayList<>(tickableInstances.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DynamicInstance> getInstancesForUpdate() {
|
||||
return new ArrayList<>(dynamicInstances.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
instances.values().forEach(AbstractInstance::remove);
|
||||
instances.clear();
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T obj) {
|
||||
AbstractInstance instance = instances.get(obj);
|
||||
|
||||
if (instance == null) {
|
||||
create(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(T obj) {
|
||||
var instance = instances.remove(obj);
|
||||
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
instance.remove();
|
||||
dynamicInstances.remove(obj);
|
||||
tickableInstances.remove(obj);
|
||||
LightUpdater.get(instance.level)
|
||||
.removeListener(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T obj) {
|
||||
AbstractInstance instance = instances.get(obj);
|
||||
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// resetting instances is by default used to handle block state changes.
|
||||
if (instance.shouldReset()) {
|
||||
// delete and re-create the instance.
|
||||
// resetting an instance supersedes updating it.
|
||||
remove(obj);
|
||||
create(obj);
|
||||
} else {
|
||||
instance.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateAll() {
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
instances.replaceAll((obj, instance) -> {
|
||||
instance.remove();
|
||||
|
||||
AbstractInstance out = createRaw(obj);
|
||||
|
||||
if (out != null) {
|
||||
setup(obj, out);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
}
|
||||
|
||||
private void create(T obj) {
|
||||
AbstractInstance renderer = createRaw(obj);
|
||||
|
||||
if (renderer != null) {
|
||||
setup(obj, renderer);
|
||||
instances.put(obj, renderer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract AbstractInstance createRaw(T obj);
|
||||
|
||||
private void setup(T obj, AbstractInstance renderer) {
|
||||
renderer.init();
|
||||
renderer.updateLight();
|
||||
LightUpdater.get(renderer.level)
|
||||
.addListener(renderer);
|
||||
if (renderer instanceof TickableInstance r) {
|
||||
tickableInstances.put(obj, r);
|
||||
r.tick();
|
||||
}
|
||||
|
||||
if (renderer instanceof DynamicInstance r) {
|
||||
dynamicInstances.put(obj, r);
|
||||
r.beginFrame();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.jozufozu.flywheel.backend.instancing;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
|
||||
public interface Storage<T> {
|
||||
int getObjectCount();
|
||||
|
||||
Iterable<AbstractInstance> allInstances();
|
||||
|
||||
List<TickableInstance> getInstancesForTicking();
|
||||
|
||||
List<DynamicInstance> getInstancesForUpdate();
|
||||
|
||||
void invalidate();
|
||||
|
||||
void add(T obj);
|
||||
|
||||
void remove(T obj);
|
||||
|
||||
void update(T obj);
|
||||
|
||||
void recreateAll();
|
||||
}
|
|
@ -8,9 +8,7 @@ import java.util.Map;
|
|||
import com.jozufozu.flywheel.api.InstancedPart;
|
||||
import com.jozufozu.flywheel.api.struct.StructType;
|
||||
import com.jozufozu.flywheel.backend.OptifineHandler;
|
||||
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.*;
|
||||
import com.jozufozu.flywheel.core.RenderContext;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
import com.mojang.blaze3d.platform.Lighting;
|
||||
|
@ -112,6 +110,11 @@ public class BatchingEngine implements Engine {
|
|||
submitTasks(stack, taskEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachManagers(InstanceManager<?>... listener) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugInfo(List<String> info) {
|
||||
info.add("Batching");
|
||||
|
|
|
@ -5,8 +5,10 @@ import java.util.List;
|
|||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.One2OneStorage;
|
||||
import com.jozufozu.flywheel.backend.instancing.Storage;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
@ -17,51 +19,43 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
|||
|
||||
public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||
|
||||
private final Long2ObjectMap<BlockEntityInstance<?>> posLookup = new Long2ObjectOpenHashMap<>();
|
||||
private final BlockEntityStorage storage;
|
||||
|
||||
public BlockEntityInstanceManager(InstancerManager instancerManager) {
|
||||
super(instancerManager);
|
||||
storage = new BlockEntityStorage(instancerManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage<BlockEntity> getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public void getCrumblingInstances(long pos, List<BlockEntityInstance<?>> data) {
|
||||
BlockEntityInstance<?> instance = posLookup.get(pos);
|
||||
BlockEntityInstance<?> instance = storage.posLookup.get(pos);
|
||||
if (instance != null) {
|
||||
data.add(instance);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canInstance(BlockEntity obj) {
|
||||
return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractInstance createRaw(BlockEntity obj) {
|
||||
var instance = InstancedRenderRegistry.createInstance(instancerManager, obj);
|
||||
|
||||
if (instance != null) {
|
||||
BlockPos blockPos = obj.getBlockPos();
|
||||
posLookup.put(blockPos.asLong(), instance);
|
||||
protected boolean canCreateInstance(BlockEntity blockEntity) {
|
||||
if (blockEntity.isRemoved()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeInternal(BlockEntity obj, AbstractInstance instance) {
|
||||
super.removeInternal(obj, instance);
|
||||
posLookup.remove(obj.getBlockPos().asLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCreateInstance(BlockEntity blockEntity) {
|
||||
if (blockEntity.isRemoved()) return false;
|
||||
if (!InstancedRenderRegistry.canInstance(blockEntity.getType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Level world = blockEntity.getLevel();
|
||||
|
||||
if (world == null) return false;
|
||||
if (world == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (world.isEmptyBlock(blockEntity.getBlockPos())) return false;
|
||||
if (world.isEmptyBlock(blockEntity.getBlockPos())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Backend.isFlywheelWorld(world)) {
|
||||
BlockPos pos = blockEntity.getBlockPos();
|
||||
|
@ -73,4 +67,32 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class BlockEntityStorage extends One2OneStorage<BlockEntity> {
|
||||
|
||||
final Long2ObjectMap<BlockEntityInstance<?>> posLookup = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
|
||||
public BlockEntityStorage(InstancerManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractInstance createRaw(BlockEntity obj) {
|
||||
var instance = InstancedRenderRegistry.createInstance(instancerManager, obj);
|
||||
|
||||
if (instance != null) {
|
||||
BlockPos blockPos = obj.getBlockPos();
|
||||
posLookup.put(blockPos.asLong(), instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(BlockEntity obj) {
|
||||
super.remove(obj);
|
||||
posLookup.remove(obj.getBlockPos().asLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.effect;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
|
||||
public interface Effect {
|
||||
|
||||
Collection<? extends AbstractInstance> createInstances(InstancerManager instancerManager);
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.effect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.Storage;
|
||||
import com.jozufozu.flywheel.light.LightUpdater;
|
||||
|
||||
public class EffectInstanceManager extends InstanceManager<Effect> {
|
||||
|
||||
private final EffectStorage<Effect> storage;
|
||||
|
||||
public EffectInstanceManager(InstancerManager instancerManager) {
|
||||
storage = new EffectStorage<>(instancerManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage<Effect> getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCreateInstance(Effect obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class EffectStorage<T extends Effect> implements Storage<T> {
|
||||
|
||||
private final Multimap<T, AbstractInstance> instances;
|
||||
private final Set<DynamicInstance> dynamicInstances;
|
||||
private final Set<TickableInstance> tickableInstances;
|
||||
private final InstancerManager manager;
|
||||
|
||||
public EffectStorage(InstancerManager manager) {
|
||||
this.instances = HashMultimap.create();
|
||||
this.dynamicInstances = new HashSet<>();
|
||||
this.tickableInstances = new HashSet<>();
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObjectCount() {
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<AbstractInstance> allInstances() {
|
||||
return instances.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TickableInstance> getInstancesForTicking() {
|
||||
return new ArrayList<>(tickableInstances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DynamicInstance> getInstancesForUpdate() {
|
||||
return new ArrayList<>(dynamicInstances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
instances.values().forEach(AbstractInstance::remove);
|
||||
instances.clear();
|
||||
tickableInstances.clear();
|
||||
dynamicInstances.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T obj) {
|
||||
var instances = this.instances.get(obj);
|
||||
|
||||
if (instances.isEmpty()) {
|
||||
create(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(T obj) {
|
||||
var instances = this.instances.removeAll(obj);
|
||||
|
||||
if (instances.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tickableInstances.removeAll(instances);
|
||||
this.dynamicInstances.removeAll(instances);
|
||||
for (AbstractInstance instance : instances) {
|
||||
LightUpdater.get(instance.level)
|
||||
.removeListener(instance);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T obj) {
|
||||
var instances = this.instances.get(obj);
|
||||
|
||||
if (instances.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
instances.forEach(AbstractInstance::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateAll() {
|
||||
this.dynamicInstances.clear();
|
||||
this.tickableInstances.clear();
|
||||
this.instances.asMap()
|
||||
.forEach((obj, instances) -> {
|
||||
instances.forEach(AbstractInstance::remove);
|
||||
instances.clear();
|
||||
|
||||
var newInstances = obj.createInstances(manager);
|
||||
|
||||
newInstances.forEach(this::setup);
|
||||
|
||||
instances.addAll(newInstances);
|
||||
});
|
||||
}
|
||||
|
||||
private void create(T obj) {
|
||||
var instances = obj.createInstances(manager);
|
||||
|
||||
this.instances.putAll(obj, instances);
|
||||
|
||||
instances.forEach(this::setup);
|
||||
}
|
||||
|
||||
private void setup(AbstractInstance renderer) {
|
||||
renderer.init();
|
||||
renderer.updateLight();
|
||||
LightUpdater.get(renderer.level)
|
||||
.addListener(renderer);
|
||||
if (renderer instanceof TickableInstance r) {
|
||||
tickableInstances.add(r);
|
||||
r.tick();
|
||||
}
|
||||
|
||||
if (renderer instanceof DynamicInstance r) {
|
||||
dynamicInstances.add(r);
|
||||
r.beginFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.backend.instancing.effect;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
|
@ -1,10 +1,13 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.entity;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.One2OneStorage;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
@ -13,23 +16,31 @@ import net.minecraft.world.level.Level;
|
|||
|
||||
public class EntityInstanceManager extends InstanceManager<Entity> {
|
||||
|
||||
private final One2OneStorage<Entity> storage;
|
||||
|
||||
public EntityInstanceManager(InstancerManager instancerManager) {
|
||||
super(instancerManager);
|
||||
storage = new One2OneStorage<>(instancerManager) {
|
||||
@Override
|
||||
protected @Nullable AbstractInstance createRaw(Entity obj) {
|
||||
return InstancedRenderRegistry.createInstance(this.instancerManager, obj);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canInstance(Entity obj) {
|
||||
return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractInstance createRaw(Entity obj) {
|
||||
return InstancedRenderRegistry.createInstance(instancerManager, obj);
|
||||
public One2OneStorage<Entity> getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canCreateInstance(Entity entity) {
|
||||
if (!entity.isAlive()) return false;
|
||||
if (!entity.isAlive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InstancedRenderRegistry.canInstance(entity.getType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Level world = entity.level;
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
|
|||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
|
||||
|
@ -202,8 +202,9 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
|||
return originCoordinate;
|
||||
}
|
||||
|
||||
public void attachManager(InstanceManager<?> listener) {
|
||||
instanceManagers.add(listener);
|
||||
@Override
|
||||
public void attachManagers(InstanceManager<?>... listener) {
|
||||
instanceManagers.addAll(List.of(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -350,7 +351,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
|||
}
|
||||
|
||||
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
|
||||
.getBlockEntityInstanceManager() instanceof BlockEntityInstanceManager beim)) {
|
||||
.getBlockEntities() instanceof BlockEntityInstanceManager beim)) {
|
||||
return Int2ObjectMaps.emptyMap();
|
||||
}
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ public class CrumblingRenderer {
|
|||
private State() {
|
||||
instancerManager = new CrumblingEngine();
|
||||
instanceManager = new CrumblingInstanceManager(instancerManager);
|
||||
instancerManager.attachManager(instanceManager);
|
||||
instancerManager.attachManagers(instanceManager);
|
||||
}
|
||||
|
||||
private void kill() {
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.source.parse.AbstractShaderElement;
|
|||
import com.jozufozu.flywheel.core.source.span.Span;
|
||||
|
||||
public class ShaderField extends AbstractShaderElement {
|
||||
public static final Pattern PATTERN = Pattern.compile("layout\\s+\\(location\\s+=\\s+(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)");
|
||||
public static final Pattern PATTERN = Pattern.compile("layout\\s*\\(location\\s*=\\s*(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)");
|
||||
|
||||
public final Span location;
|
||||
public final @Nullable Decoration decoration;
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
|||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
|
@ -24,9 +25,16 @@ public class LevelRendererInstanceUpdateMixin {
|
|||
*/
|
||||
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
|
||||
private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
|
||||
if (Backend.isOn()) {
|
||||
InstancedRenderDispatcher.getBlockEntities(level)
|
||||
.update(level.getBlockEntity(pos));
|
||||
if (!Backend.isOn()) {
|
||||
return;
|
||||
}
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
|
||||
if (blockEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InstancedRenderDispatcher.getBlockEntities(level)
|
||||
.update(blockEntity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package com.jozufozu.flywheel.vanilla.effect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.api.InstancerManager;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.instancing.effect.Effect;
|
||||
import com.jozufozu.flywheel.core.Models;
|
||||
import com.jozufozu.flywheel.core.structs.StructTypes;
|
||||
import com.jozufozu.flywheel.core.structs.model.TransformedPart;
|
||||
import com.jozufozu.flywheel.util.box.GridAlignedBB;
|
||||
import com.jozufozu.flywheel.util.box.ImmutableBox;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
|
||||
public class ExampleEffect implements Effect {
|
||||
|
||||
private static final int INSTANCE_COUNT = 50;
|
||||
|
||||
private final Level level;
|
||||
private final Vec3 targetPoint;
|
||||
private final BlockPos blockPos;
|
||||
private final ImmutableBox volume;
|
||||
|
||||
private final List<Instance> effects;
|
||||
|
||||
public ExampleEffect(Level level, Vec3 targetPoint) {
|
||||
this.level = level;
|
||||
this.targetPoint = targetPoint;
|
||||
this.blockPos = new BlockPos(targetPoint);
|
||||
this.effects = new ArrayList<>();
|
||||
this.volume = GridAlignedBB.from(this.blockPos);
|
||||
}
|
||||
|
||||
public static void spawn(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side == LogicalSide.SERVER || event.phase == TickEvent.Phase.START) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.player;
|
||||
Level level = player.level;
|
||||
|
||||
if (level.random.nextFloat() > 0.01) {
|
||||
return;
|
||||
}
|
||||
|
||||
var effects = InstancedRenderDispatcher.getEffects(level);
|
||||
|
||||
effects.add(new ExampleEffect(level, player.position()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends AbstractInstance> createInstances(InstancerManager instancerManager) {
|
||||
effects.clear();
|
||||
for (int i = 0; i < INSTANCE_COUNT; i++) {
|
||||
effects.add(new Instance(instancerManager, level));
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
||||
public class Instance extends AbstractInstance implements DynamicInstance {
|
||||
|
||||
TransformedPart firefly;
|
||||
|
||||
public Instance(InstancerManager instancerManager, Level level) {
|
||||
super(instancerManager, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
firefly = instancerManager.factory(StructTypes.TRANSFORMED)
|
||||
.model(Models.block(Blocks.SHROOMLIGHT.defaultBlockState()))
|
||||
.createInstance();
|
||||
|
||||
firefly.setBlockLight(15)
|
||||
.setSkyLight(15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getWorldPosition() {
|
||||
return blockPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
firefly.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame() {
|
||||
var x = level.random.nextFloat() * 3 - 1.5;
|
||||
var y = level.random.nextFloat() * 3 - 1.5;
|
||||
var z = level.random.nextFloat() * 3 - 1.5;
|
||||
|
||||
firefly.loadIdentity()
|
||||
.translate(instancerManager.getOriginCoordinate())
|
||||
.translate(targetPoint)
|
||||
.translate(x, y, z)
|
||||
.scale(1 / 16f);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.vanilla.effect;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
|
@ -0,0 +1,6 @@
|
|||
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
|
||||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
|
@ -2,11 +2,11 @@
|
|||
#use "flywheel:util/light.glsl"
|
||||
#use "flywheel:util/quaternion.glsl"
|
||||
|
||||
layout (location = 0) in vec2 oriented_light;
|
||||
layout (location = 1) in vec4 oriented_color;
|
||||
layout (location = 2) in vec3 oriented_pos;
|
||||
layout (location = 3) in vec3 oriented_pivot;
|
||||
layout (location = 4) in vec4 oriented_rotation;
|
||||
layout(location = 0) in vec2 oriented_light;
|
||||
layout(location = 1) in vec4 oriented_color;
|
||||
layout(location = 2) in vec3 oriented_pos;
|
||||
layout(location = 3) in vec3 oriented_pivot;
|
||||
layout(location = 4) in vec4 oriented_rotation;
|
||||
|
||||
void flw_instanceVertex() {
|
||||
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#use "flywheel:api/vertex.glsl"
|
||||
#use "flywheel:util/light.glsl"
|
||||
|
||||
layout (location = 0) in vec2 transformed_light;
|
||||
layout (location = 1) in vec4 transformed_color;
|
||||
layout (location = 2) in mat4 transformed_pose;
|
||||
layout (location = 6) in mat3 transformed_normal;
|
||||
layout(location = 0) in vec2 transformed_light;
|
||||
layout(location = 1) in vec4 transformed_color;
|
||||
layout(location = 2) in mat4 transformed_pose;
|
||||
layout(location = 6) in mat3 transformed_normal;
|
||||
|
||||
void flw_instanceVertex() {
|
||||
flw_vertexPos = transformed_pose * flw_vertexPos;
|
||||
|
|
Loading…
Reference in a new issue