mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-08 13:26:39 +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.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
||||||
import com.jozufozu.flywheel.vanilla.VanillaInstances;
|
import com.jozufozu.flywheel.vanilla.VanillaInstances;
|
||||||
|
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypes;
|
import net.minecraft.commands.synchronization.ArgumentTypes;
|
||||||
|
@ -108,6 +109,8 @@ public class Flywheel {
|
||||||
modEventBus.addListener(StitchedSprite::onTextureStitchPre);
|
modEventBus.addListener(StitchedSprite::onTextureStitchPre);
|
||||||
modEventBus.addListener(StitchedSprite::onTextureStitchPost);
|
modEventBus.addListener(StitchedSprite::onTextureStitchPost);
|
||||||
|
|
||||||
|
// forgeEventBus.addListener(ExampleEffect::spawn);
|
||||||
|
|
||||||
LayoutShaders.init();
|
LayoutShaders.init();
|
||||||
InstanceShaders.init();
|
InstanceShaders.init();
|
||||||
Contexts.init();
|
Contexts.init();
|
||||||
|
|
|
@ -5,5 +5,7 @@ import java.util.List;
|
||||||
import com.jozufozu.flywheel.api.InstancerManager;
|
import com.jozufozu.flywheel.api.InstancerManager;
|
||||||
|
|
||||||
public interface Engine extends RenderDispatcher, InstancerManager {
|
public interface Engine extends RenderDispatcher, InstancerManager {
|
||||||
|
void attachManagers(InstanceManager<?>... listener);
|
||||||
|
|
||||||
void addDebugInfo(List<String> info);
|
void addDebugInfo(List<String> info);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
package com.jozufozu.flywheel.backend.instancing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
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.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
@ -20,37 +14,38 @@ import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.light.LightUpdater;
|
import com.jozufozu.flywheel.light.LightUpdater;
|
||||||
import com.mojang.math.Vector3f;
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
|
||||||
public abstract class InstanceManager<T> {
|
public abstract class InstanceManager<T> {
|
||||||
|
|
||||||
public final InstancerManager instancerManager;
|
|
||||||
|
|
||||||
private final Set<T> queuedAdditions;
|
private final Set<T> queuedAdditions;
|
||||||
private final Set<T> queuedUpdates;
|
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 frame;
|
||||||
protected DistanceUpdateLimiter tick;
|
protected DistanceUpdateLimiter tick;
|
||||||
|
|
||||||
public InstanceManager(InstancerManager instancerManager) {
|
public InstanceManager() {
|
||||||
this.instancerManager = instancerManager;
|
|
||||||
this.queuedUpdates = new HashSet<>(64);
|
this.queuedUpdates = new HashSet<>(64);
|
||||||
this.queuedAdditions = new HashSet<>(64);
|
this.queuedAdditions = new HashSet<>(64);
|
||||||
this.instances = new HashMap<>();
|
|
||||||
|
|
||||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
frame = createUpdateLimiter();
|
frame = createUpdateLimiter();
|
||||||
tick = 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() {
|
protected DistanceUpdateLimiter createUpdateLimiter() {
|
||||||
if (FlwConfig.get().limitUpdates()) {
|
if (FlwConfig.get().limitUpdates()) {
|
||||||
return new BandedPrimeLimiter();
|
return new BandedPrimeLimiter();
|
||||||
|
@ -65,30 +60,9 @@ public abstract class InstanceManager<T> {
|
||||||
* @return The object count.
|
* @return The object count.
|
||||||
*/
|
*/
|
||||||
public int getObjectCount() {
|
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.
|
* Ticks the InstanceManager.
|
||||||
*
|
*
|
||||||
|
@ -107,14 +81,14 @@ public abstract class InstanceManager<T> {
|
||||||
int cY = (int) cameraY;
|
int cY = (int) cameraY;
|
||||||
int cZ = (int) cameraZ;
|
int cZ = (int) cameraZ;
|
||||||
|
|
||||||
ArrayList<TickableInstance> instances = new ArrayList<>(tickableInstances.values());
|
var instances = getStorage().getInstancesForTicking();
|
||||||
int incr = 500;
|
int incr = 500;
|
||||||
int size = instances.size();
|
int size = instances.size();
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (start < size) {
|
while (start < size) {
|
||||||
int end = Math.min(start + incr, size);
|
int end = Math.min(start + incr, size);
|
||||||
|
|
||||||
List<TickableInstance> sub = instances.subList(start, end);
|
var sub = instances.subList(start, end);
|
||||||
taskEngine.submit(() -> {
|
taskEngine.submit(() -> {
|
||||||
for (TickableInstance instance : sub) {
|
for (TickableInstance instance : sub) {
|
||||||
tickInstance(cX, cY, cZ, instance);
|
tickInstance(cX, cY, cZ, instance);
|
||||||
|
@ -154,14 +128,14 @@ public abstract class InstanceManager<T> {
|
||||||
int cY = (int) camera.getPosition().y;
|
int cY = (int) camera.getPosition().y;
|
||||||
int cZ = (int) camera.getPosition().z;
|
int cZ = (int) camera.getPosition().z;
|
||||||
|
|
||||||
ArrayList<DynamicInstance> instances = new ArrayList<>(dynamicInstances.values());
|
var instances = getStorage().getInstancesForUpdate();
|
||||||
int incr = 500;
|
int incr = 500;
|
||||||
int size = instances.size();
|
int size = instances.size();
|
||||||
int start = 0;
|
int start = 0;
|
||||||
while (start < size) {
|
while (start < size) {
|
||||||
int end = Math.min(start + incr, size);
|
int end = Math.min(start + incr, size);
|
||||||
|
|
||||||
List<DynamicInstance> sub = instances.subList(start, end);
|
var sub = instances.subList(start, end);
|
||||||
taskEngine.submit(() -> {
|
taskEngine.submit(() -> {
|
||||||
for (DynamicInstance dyn : sub) {
|
for (DynamicInstance dyn : sub) {
|
||||||
updateInstance(dyn, lookX, lookY, lookZ, cX, cY, cZ);
|
updateInstance(dyn, lookX, lookY, lookZ, cX, cY, cZ);
|
||||||
|
@ -197,13 +171,19 @@ public abstract class InstanceManager<T> {
|
||||||
public void add(T obj) {
|
public void add(T obj) {
|
||||||
if (!Backend.isOn()) return;
|
if (!Backend.isOn()) return;
|
||||||
|
|
||||||
if (canInstance(obj)) {
|
if (canCreateInstance(obj)) {
|
||||||
addInternal(obj);
|
getStorage().add(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueAdd(T obj) {
|
public void queueAdd(T obj) {
|
||||||
if (!Backend.isOn()) return;
|
if (!Backend.isOn()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canCreateInstance(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (queuedAdditions) {
|
synchronized (queuedAdditions) {
|
||||||
queuedAdditions.add(obj);
|
queuedAdditions.add(obj);
|
||||||
|
@ -212,6 +192,11 @@ public abstract class InstanceManager<T> {
|
||||||
|
|
||||||
public void queueUpdate(T obj) {
|
public void queueUpdate(T obj) {
|
||||||
if (!Backend.isOn()) return;
|
if (!Backend.isOn()) return;
|
||||||
|
|
||||||
|
if (!canCreateInstance(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (queuedUpdates) {
|
synchronized (queuedUpdates) {
|
||||||
queuedUpdates.add(obj);
|
queuedUpdates.add(obj);
|
||||||
}
|
}
|
||||||
|
@ -231,45 +216,21 @@ public abstract class InstanceManager<T> {
|
||||||
public void update(T obj) {
|
public void update(T obj) {
|
||||||
if (!Backend.isOn()) return;
|
if (!Backend.isOn()) return;
|
||||||
|
|
||||||
if (canInstance(obj)) {
|
if (canCreateInstance(obj)) {
|
||||||
AbstractInstance instance = getInstance(obj);
|
getStorage().update(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(T obj) {
|
public void remove(T obj) {
|
||||||
if (!Backend.isOn()) return;
|
if (!Backend.isOn()) return;
|
||||||
|
|
||||||
if (canInstance(obj)) {
|
if (canCreateInstance(obj)) {
|
||||||
AbstractInstance instance = getInstance(obj);
|
getStorage().remove(obj);
|
||||||
if (instance != null) removeInternal(obj, instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
instances.values().forEach(AbstractInstance::remove);
|
getStorage().invalidate();
|
||||||
instances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
tickableInstances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected <I extends T> AbstractInstance getInstance(I obj) {
|
|
||||||
if (!Backend.isOn()) return null;
|
|
||||||
|
|
||||||
return instances.get(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processQueuedAdditions() {
|
protected void processQueuedAdditions() {
|
||||||
|
@ -285,7 +246,7 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!queued.isEmpty()) {
|
if (!queued.isEmpty()) {
|
||||||
queued.forEach(this::addInternal);
|
queued.forEach(getStorage()::add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,75 +259,16 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queued.size() > 0) {
|
if (queued.size() > 0) {
|
||||||
queued.forEach(this::update);
|
queued.forEach(getStorage()::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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOriginShift() {
|
public void onOriginShift() {
|
||||||
dynamicInstances.clear();
|
getStorage().recreateAll();
|
||||||
tickableInstances.clear();
|
|
||||||
instances.replaceAll((obj, instance) -> {
|
|
||||||
instance.remove();
|
|
||||||
|
|
||||||
AbstractInstance out = createRaw(obj);
|
|
||||||
|
|
||||||
if (out != null) {
|
|
||||||
setup(obj, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void detachLightListeners() {
|
public void delete() {
|
||||||
for (AbstractInstance value : instances.values()) {
|
for (AbstractInstance value : getStorage().allInstances()) {
|
||||||
LightUpdater.get(value.level).removeListener(value);
|
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.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
|
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager;
|
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.entity.EntityInstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
|
||||||
import com.jozufozu.flywheel.core.Contexts;
|
import com.jozufozu.flywheel.core.Contexts;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.util.ClientLevelExtension;
|
import com.jozufozu.flywheel.util.ClientLevelExtension;
|
||||||
|
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
@ -29,47 +31,47 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
*/
|
*/
|
||||||
public class InstanceWorld {
|
public class InstanceWorld {
|
||||||
protected final Engine engine;
|
protected final Engine engine;
|
||||||
protected final InstanceManager<Entity> entityInstanceManager;
|
protected final InstanceManager<Entity> entities;
|
||||||
protected final InstanceManager<BlockEntity> blockEntityInstanceManager;
|
protected final InstanceManager<BlockEntity> blockEntities;
|
||||||
|
|
||||||
public final ParallelTaskEngine taskEngine;
|
public final ParallelTaskEngine taskEngine;
|
||||||
|
private final InstanceManager<Effect> effects;
|
||||||
|
|
||||||
public static InstanceWorld create(LevelAccessor level) {
|
public static InstanceWorld create(LevelAccessor level) {
|
||||||
return switch (Backend.getBackendType()) {
|
var engine = switch (Backend.getBackendType()) {
|
||||||
case INSTANCING -> {
|
case INSTANCING -> new InstancingEngine<>(Contexts.WORLD);
|
||||||
InstancingEngine<WorldProgram> engine = new InstancingEngine<>(Contexts.WORLD);
|
case BATCHING -> new BatchingEngine();
|
||||||
|
case OFF -> throw new IllegalStateException("Cannot create instance world when backend is off.");
|
||||||
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 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.engine = engine;
|
||||||
this.entityInstanceManager = entityInstanceManager;
|
this.entities = entities;
|
||||||
this.blockEntityInstanceManager = blockEntityInstanceManager;
|
this.blockEntities = blockEntities;
|
||||||
|
this.effects = effects;
|
||||||
this.taskEngine = Backend.getTaskEngine();
|
this.taskEngine = Backend.getTaskEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<Entity> getEntityInstanceManager() {
|
public InstanceManager<Entity> getEntities() {
|
||||||
return entityInstanceManager;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<BlockEntity> getBlockEntityInstanceManager() {
|
public InstanceManager<Effect> getEffects() {
|
||||||
return blockEntityInstanceManager;
|
return effects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceManager<BlockEntity> getBlockEntities() {
|
||||||
|
return blockEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,8 +79,8 @@ public class InstanceWorld {
|
||||||
*/
|
*/
|
||||||
public void delete() {
|
public void delete() {
|
||||||
engine.delete();
|
engine.delete();
|
||||||
entityInstanceManager.detachLightListeners();
|
entities.delete();
|
||||||
blockEntityInstanceManager.detachLightListeners();
|
blockEntities.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,8 +98,9 @@ public class InstanceWorld {
|
||||||
taskEngine.syncPoint();
|
taskEngine.syncPoint();
|
||||||
|
|
||||||
if (!shifted) {
|
if (!shifted) {
|
||||||
blockEntityInstanceManager.beginFrame(taskEngine, camera);
|
blockEntities.beginFrame(taskEngine, camera);
|
||||||
entityInstanceManager.beginFrame(taskEngine, camera);
|
entities.beginFrame(taskEngine, camera);
|
||||||
|
effects.beginFrame(taskEngine, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.beginFrame(taskEngine, camera);
|
engine.beginFrame(taskEngine, camera);
|
||||||
|
@ -115,8 +118,13 @@ public class InstanceWorld {
|
||||||
|
|
||||||
if (renderViewEntity == null) return;
|
if (renderViewEntity == null) return;
|
||||||
|
|
||||||
blockEntityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
double x = renderViewEntity.getX();
|
||||||
entityInstanceManager.tick(taskEngine, renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
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.
|
// 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.
|
// Entities are loaded with the world, so when chunks are reloaded they need to be re-added.
|
||||||
ClientLevelExtension.getAllLoadedEntities(world)
|
ClientLevelExtension.getAllLoadedEntities(world)
|
||||||
.forEach(entityInstanceManager::add);
|
.forEach(entities::add);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
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.FlwCommands;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
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.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.util.AnimationTickHolder;
|
import com.jozufozu.flywheel.util.AnimationTickHolder;
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
import com.jozufozu.flywheel.util.WorldAttached;
|
||||||
|
import com.jozufozu.flywheel.vanilla.effect.ExampleEffect;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
@ -30,7 +32,7 @@ public class InstancedRenderDispatcher {
|
||||||
public static void enqueueUpdate(BlockEntity blockEntity) {
|
public static void enqueueUpdate(BlockEntity blockEntity) {
|
||||||
if (Backend.isOn() && blockEntity.hasLevel() && blockEntity.getLevel() instanceof ClientLevel) {
|
if (Backend.isOn() && blockEntity.hasLevel() && blockEntity.getLevel() instanceof ClientLevel) {
|
||||||
instanceWorlds.get(blockEntity.getLevel())
|
instanceWorlds.get(blockEntity.getLevel())
|
||||||
.getBlockEntityInstanceManager()
|
.getBlockEntities()
|
||||||
.queueUpdate(blockEntity);
|
.queueUpdate(blockEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,17 +44,21 @@ public class InstancedRenderDispatcher {
|
||||||
public static void enqueueUpdate(Entity entity) {
|
public static void enqueueUpdate(Entity entity) {
|
||||||
if (Backend.isOn()) {
|
if (Backend.isOn()) {
|
||||||
instanceWorlds.get(entity.level)
|
instanceWorlds.get(entity.level)
|
||||||
.getEntityInstanceManager()
|
.getEntities()
|
||||||
.queueUpdate(entity);
|
.queueUpdate(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor world) {
|
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor world) {
|
||||||
return getInstanceWorld(world).getBlockEntityInstanceManager();
|
return getInstanceWorld(world).getBlockEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceManager<Entity> getEntities(LevelAccessor world) {
|
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);
|
InstanceWorld instanceWorld = instanceWorlds.get(Minecraft.getInstance().level);
|
||||||
|
|
||||||
debug.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
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);
|
instanceWorld.engine.addDebugInfo(debug);
|
||||||
} else {
|
} else {
|
||||||
debug.add("Disabled");
|
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.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.OptifineHandler;
|
import com.jozufozu.flywheel.backend.OptifineHandler;
|
||||||
import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker;
|
import com.jozufozu.flywheel.backend.instancing.*;
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.mojang.blaze3d.platform.Lighting;
|
import com.mojang.blaze3d.platform.Lighting;
|
||||||
|
@ -112,6 +110,11 @@ public class BatchingEngine implements Engine {
|
||||||
submitTasks(stack, taskEngine);
|
submitTasks(stack, taskEngine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachManagers(InstanceManager<?>... listener) {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDebugInfo(List<String> info) {
|
public void addDebugInfo(List<String> info) {
|
||||||
info.add("Batching");
|
info.add("Batching");
|
||||||
|
|
|
@ -5,8 +5,10 @@ import java.util.List;
|
||||||
import com.jozufozu.flywheel.api.InstancerManager;
|
import com.jozufozu.flywheel.api.InstancerManager;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
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.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.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
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> {
|
public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
|
|
||||||
private final Long2ObjectMap<BlockEntityInstance<?>> posLookup = new Long2ObjectOpenHashMap<>();
|
private final BlockEntityStorage storage;
|
||||||
|
|
||||||
public BlockEntityInstanceManager(InstancerManager instancerManager) {
|
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) {
|
public void getCrumblingInstances(long pos, List<BlockEntityInstance<?>> data) {
|
||||||
BlockEntityInstance<?> instance = posLookup.get(pos);
|
BlockEntityInstance<?> instance = storage.posLookup.get(pos);
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
data.add(instance);
|
data.add(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canInstance(BlockEntity obj) {
|
protected boolean canCreateInstance(BlockEntity blockEntity) {
|
||||||
return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
|
if (blockEntity.isRemoved()) {
|
||||||
}
|
return false;
|
||||||
|
|
||||||
@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;
|
if (!InstancedRenderRegistry.canInstance(blockEntity.getType())) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
@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;
|
|
||||||
|
|
||||||
Level world = blockEntity.getLevel();
|
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)) {
|
if (Backend.isFlywheelWorld(world)) {
|
||||||
BlockPos pos = blockEntity.getBlockPos();
|
BlockPos pos = blockEntity.getBlockPos();
|
||||||
|
@ -73,4 +67,32 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
|
|
||||||
return false;
|
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;
|
package com.jozufozu.flywheel.backend.instancing.entity;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.InstancerManager;
|
import com.jozufozu.flywheel.api.InstancerManager;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
|
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.InstancedRenderRegistry;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.One2OneStorage;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
@ -13,23 +16,31 @@ import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
public class EntityInstanceManager extends InstanceManager<Entity> {
|
public class EntityInstanceManager extends InstanceManager<Entity> {
|
||||||
|
|
||||||
|
private final One2OneStorage<Entity> storage;
|
||||||
|
|
||||||
public EntityInstanceManager(InstancerManager instancerManager) {
|
public EntityInstanceManager(InstancerManager instancerManager) {
|
||||||
super(instancerManager);
|
storage = new One2OneStorage<>(instancerManager) {
|
||||||
|
@Override
|
||||||
|
protected @Nullable AbstractInstance createRaw(Entity obj) {
|
||||||
|
return InstancedRenderRegistry.createInstance(this.instancerManager, obj);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canInstance(Entity obj) {
|
public One2OneStorage<Entity> getStorage() {
|
||||||
return obj != null && InstancedRenderRegistry.canInstance(obj.getType());
|
return storage;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AbstractInstance createRaw(Entity obj) {
|
|
||||||
return InstancedRenderRegistry.createInstance(instancerManager, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canCreateInstance(Entity entity) {
|
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;
|
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.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
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.InstanceManager;
|
||||||
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
|
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
|
||||||
|
@ -202,8 +202,9 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
return originCoordinate;
|
return originCoordinate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attachManager(InstanceManager<?> listener) {
|
@Override
|
||||||
instanceManagers.add(listener);
|
public void attachManagers(InstanceManager<?>... listener) {
|
||||||
|
instanceManagers.addAll(List.of(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -350,7 +351,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
|
if (!(InstancedRenderDispatcher.getInstanceWorld(level)
|
||||||
.getBlockEntityInstanceManager() instanceof BlockEntityInstanceManager beim)) {
|
.getBlockEntities() instanceof BlockEntityInstanceManager beim)) {
|
||||||
return Int2ObjectMaps.emptyMap();
|
return Int2ObjectMaps.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ public class CrumblingRenderer {
|
||||||
private State() {
|
private State() {
|
||||||
instancerManager = new CrumblingEngine();
|
instancerManager = new CrumblingEngine();
|
||||||
instanceManager = new CrumblingInstanceManager(instancerManager);
|
instanceManager = new CrumblingInstanceManager(instancerManager);
|
||||||
instancerManager.attachManager(instanceManager);
|
instancerManager.attachManagers(instanceManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void kill() {
|
private void kill() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.source.parse.AbstractShaderElement;
|
||||||
import com.jozufozu.flywheel.core.source.span.Span;
|
import com.jozufozu.flywheel.core.source.span.Span;
|
||||||
|
|
||||||
public class ShaderField extends AbstractShaderElement {
|
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 Span location;
|
||||||
public final @Nullable Decoration decoration;
|
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.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
import net.minecraft.client.renderer.LevelRenderer;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
@Mixin(LevelRenderer.class)
|
@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")
|
@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) {
|
private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
|
||||||
if (Backend.isOn()) {
|
if (!Backend.isOn()) {
|
||||||
InstancedRenderDispatcher.getBlockEntities(level)
|
return;
|
||||||
.update(level.getBlockEntity(pos));
|
|
||||||
}
|
}
|
||||||
|
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/light.glsl"
|
||||||
#use "flywheel:util/quaternion.glsl"
|
#use "flywheel:util/quaternion.glsl"
|
||||||
|
|
||||||
layout (location = 0) in vec2 oriented_light;
|
layout(location = 0) in vec2 oriented_light;
|
||||||
layout (location = 1) in vec4 oriented_color;
|
layout(location = 1) in vec4 oriented_color;
|
||||||
layout (location = 2) in vec3 oriented_pos;
|
layout(location = 2) in vec3 oriented_pos;
|
||||||
layout (location = 3) in vec3 oriented_pivot;
|
layout(location = 3) in vec3 oriented_pivot;
|
||||||
layout (location = 4) in vec4 oriented_rotation;
|
layout(location = 4) in vec4 oriented_rotation;
|
||||||
|
|
||||||
void flw_instanceVertex() {
|
void flw_instanceVertex() {
|
||||||
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0);
|
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:api/vertex.glsl"
|
||||||
#use "flywheel:util/light.glsl"
|
#use "flywheel:util/light.glsl"
|
||||||
|
|
||||||
layout (location = 0) in vec2 transformed_light;
|
layout(location = 0) in vec2 transformed_light;
|
||||||
layout (location = 1) in vec4 transformed_color;
|
layout(location = 1) in vec4 transformed_color;
|
||||||
layout (location = 2) in mat4 transformed_pose;
|
layout(location = 2) in mat4 transformed_pose;
|
||||||
layout (location = 6) in mat3 transformed_normal;
|
layout(location = 6) in mat3 transformed_normal;
|
||||||
|
|
||||||
void flw_instanceVertex() {
|
void flw_instanceVertex() {
|
||||||
flw_vertexPos = transformed_pose * flw_vertexPos;
|
flw_vertexPos = transformed_pose * flw_vertexPos;
|
||||||
|
|
Loading…
Reference in a new issue