mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-06 10:14:59 +01:00
Sweeping changes
- Add VisualizationManager and VisualManager API - Simplify and fix memory leaks in model utilities - Pass partialTick directly to visuals and remove AnimationTickHolder - Fix LevelAttached and FlwTaskExecutor not being thread-safe - Reorganize and rename many things - Remove unnecessary things
This commit is contained in:
parent
fcd70cccd0
commit
66f11018fe
158 changed files with 1960 additions and 3505 deletions
|
@ -135,12 +135,12 @@ dependencies {
|
||||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
||||||
|
|
||||||
jarJar('org.joml:joml:1.10.5') {
|
jarJar('org.joml:joml:1.10.5') {
|
||||||
jarJar.ranged(it, '[1.10.0,1.11.0)')
|
jarJar.ranged(it, '[1.10.5,1.11.0)')
|
||||||
}
|
}
|
||||||
library 'org.joml:joml:1.10.5'
|
library 'org.joml:joml:1.10.5'
|
||||||
|
|
||||||
jarJar('com.dreizak:miniball:1.0.3') {
|
jarJar('com.dreizak:miniball:1.0.3') {
|
||||||
jarJar.ranged(it, '[1.0,2.0)')
|
jarJar.ranged(it, '[1.0.3,2.0.0)')
|
||||||
}
|
}
|
||||||
library 'com.dreizak:miniball:1.0.3'
|
library 'com.dreizak:miniball:1.0.3'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package com.jozufozu.flywheel;
|
package com.jozufozu.flywheel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||||
import com.jozufozu.flywheel.backend.Backends;
|
import com.jozufozu.flywheel.backend.Backends;
|
||||||
import com.jozufozu.flywheel.backend.Loader;
|
import com.jozufozu.flywheel.backend.Loader;
|
||||||
import com.jozufozu.flywheel.backend.compile.Pipelines;
|
import com.jozufozu.flywheel.backend.compile.Pipelines;
|
||||||
|
@ -11,29 +14,34 @@ import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
|
||||||
import com.jozufozu.flywheel.config.BackendArgument;
|
import com.jozufozu.flywheel.config.BackendArgument;
|
||||||
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.handler.EntityWorldHandler;
|
|
||||||
import com.jozufozu.flywheel.handler.ForgeEvents;
|
|
||||||
import com.jozufozu.flywheel.impl.BackendManagerImpl;
|
import com.jozufozu.flywheel.impl.BackendManagerImpl;
|
||||||
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
||||||
import com.jozufozu.flywheel.impl.RegistryImpl;
|
import com.jozufozu.flywheel.impl.RegistryImpl;
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.visualization.VisualizationEventHandler;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||||
|
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
import com.jozufozu.flywheel.lib.material.Materials;
|
import com.jozufozu.flywheel.lib.material.Materials;
|
||||||
|
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
||||||
import com.jozufozu.flywheel.lib.model.Models;
|
import com.jozufozu.flywheel.lib.model.Models;
|
||||||
import com.jozufozu.flywheel.lib.model.PartialModel;
|
import com.jozufozu.flywheel.lib.model.baked.PartialModel;
|
||||||
|
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||||
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||||
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
|
||||||
import com.jozufozu.flywheel.vanilla.VanillaVisuals;
|
import com.jozufozu.flywheel.vanilla.VanillaVisuals;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypes;
|
import net.minecraft.commands.synchronization.ArgumentTypes;
|
||||||
import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
|
import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
import net.minecraftforge.eventbus.api.IEventBus;
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
import net.minecraftforge.fml.DistExecutor;
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
import net.minecraftforge.fml.IExtensionPoint;
|
import net.minecraftforge.fml.IExtensionPoint;
|
||||||
|
@ -73,24 +81,24 @@ public class Flywheel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void clientInit(IEventBus forgeEventBus, IEventBus modEventBus) {
|
private static void clientInit(IEventBus forgeEventBus, IEventBus modEventBus) {
|
||||||
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
forgeEventBus.addListener(Flywheel::addDebugInfo);
|
||||||
|
|
||||||
forgeEventBus.addListener(BackendManagerImpl::onReloadRenderers);
|
forgeEventBus.addListener(BackendManagerImpl::onReloadRenderers);
|
||||||
|
|
||||||
forgeEventBus.addListener(Models::onReloadRenderers);
|
forgeEventBus.addListener(VisualizationEventHandler::onClientTick);
|
||||||
|
forgeEventBus.addListener(VisualizationEventHandler::onBeginFrame);
|
||||||
|
forgeEventBus.addListener(VisualizationEventHandler::onRenderStage);
|
||||||
|
forgeEventBus.addListener(VisualizationEventHandler::onEntityJoinWorld);
|
||||||
|
forgeEventBus.addListener(VisualizationEventHandler::onEntityLeaveWorld);
|
||||||
|
|
||||||
|
forgeEventBus.addListener(FlwCommands::registerClientCommands);
|
||||||
|
|
||||||
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
||||||
forgeEventBus.addListener(UniformBuffer::onReloadRenderers);
|
forgeEventBus.addListener(UniformBuffer::onReloadRenderers);
|
||||||
|
|
||||||
forgeEventBus.addListener(VisualizedRenderDispatcher::onRenderStage);
|
forgeEventBus.addListener(LightUpdater::onClientTick);
|
||||||
forgeEventBus.addListener(VisualizedRenderDispatcher::onBeginFrame);
|
forgeEventBus.addListener(Models::onReloadRenderers);
|
||||||
forgeEventBus.addListener(VisualizedRenderDispatcher::tick);
|
forgeEventBus.addListener((WorldEvent.Unload e) -> LevelAttached.onUnloadLevel(e));
|
||||||
|
|
||||||
forgeEventBus.addListener(EntityWorldHandler::onEntityJoinWorld);
|
|
||||||
forgeEventBus.addListener(EntityWorldHandler::onEntityLeaveWorld);
|
|
||||||
|
|
||||||
forgeEventBus.addListener(ForgeEvents::addToDebugScreen);
|
|
||||||
forgeEventBus.addListener(ForgeEvents::unloadWorld);
|
|
||||||
forgeEventBus.addListener(ForgeEvents::tickLight);
|
|
||||||
|
|
||||||
modEventBus.addListener(PartialModel::onModelRegistry);
|
modEventBus.addListener(PartialModel::onModelRegistry);
|
||||||
modEventBus.addListener(PartialModel::onModelBake);
|
modEventBus.addListener(PartialModel::onModelBake);
|
||||||
|
@ -114,13 +122,6 @@ public class Flywheel {
|
||||||
MaterialIndices.init();
|
MaterialIndices.init();
|
||||||
|
|
||||||
VanillaVisuals.init();
|
VanillaVisuals.init();
|
||||||
|
|
||||||
// https://github.com/Jozufozu/Flywheel/issues/69
|
|
||||||
// Weird issue with accessor loading.
|
|
||||||
// Only thing I've seen that's close to a fix is to force the class to load before trying to use it.
|
|
||||||
// From the SpongePowered discord:
|
|
||||||
// https://discord.com/channels/142425412096491520/626802111455297538/675007581168599041
|
|
||||||
LOGGER.debug("Successfully loaded {}", PausedPartialTickAccessor.class.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setup(final FMLCommonSetupEvent event) {
|
private static void setup(final FMLCommonSetupEvent event) {
|
||||||
|
@ -130,6 +131,30 @@ public class Flywheel {
|
||||||
ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(() -> BackendArgument.INSTANCE));
|
ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(() -> BackendArgument.INSTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addDebugInfo(RenderGameOverlayEvent.Text event) {
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (!mc.options.renderDebug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> info = event.getRight();
|
||||||
|
info.add("");
|
||||||
|
info.add("Flywheel: " + getVersion());
|
||||||
|
info.add("Backend: " + BackendManagerImpl.getBackendString());
|
||||||
|
info.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
||||||
|
|
||||||
|
VisualizationManager manager = VisualizationManager.get(mc.level);
|
||||||
|
if (manager != null) {
|
||||||
|
info.add("B: " + manager.getBlockEntities().getVisualCount()
|
||||||
|
+ ", E: " + manager.getEntities().getVisualCount()
|
||||||
|
+ ", F: " + manager.getEffects().getVisualCount());
|
||||||
|
Vec3i renderOrigin = manager.getRenderOrigin();
|
||||||
|
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
info.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
|
||||||
|
}
|
||||||
|
|
||||||
public static ArtifactVersion getVersion() {
|
public static ArtifactVersion getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.api.backend;
|
package com.jozufozu.flywheel.api.backend;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
|
@ -26,7 +24,7 @@ public interface Engine extends InstancerProvider {
|
||||||
|
|
||||||
Vec3i renderOrigin();
|
Vec3i renderOrigin();
|
||||||
|
|
||||||
void addDebugInfo(List<String> info);
|
// TODO: "delete" implies that the object cannot be used afterwards, but all current implementations
|
||||||
|
// support the "invalidate" contract as well, meaning they can be reused after this call. Rename?
|
||||||
void delete();
|
void delete();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,11 @@ import net.minecraft.client.renderer.LevelRenderer;
|
||||||
import net.minecraft.client.renderer.RenderBuffers;
|
import net.minecraft.client.renderer.RenderBuffers;
|
||||||
|
|
||||||
public record RenderContext(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack,
|
public record RenderContext(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack,
|
||||||
Matrix4f projection, Matrix4f viewProjection, Camera camera) {
|
Matrix4f projection, Matrix4f viewProjection, Camera camera, float partialTick) {
|
||||||
public static RenderContext create(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack, Matrix4f projection, Camera camera) {
|
public static RenderContext create(LevelRenderer renderer, ClientLevel level, RenderBuffers buffers, PoseStack stack, Matrix4f projection, Camera camera, float partialTick) {
|
||||||
Matrix4f viewProjection = projection.copy();
|
Matrix4f viewProjection = projection.copy();
|
||||||
viewProjection.multiply(stack.last().pose());
|
viewProjection.multiply(stack.last().pose());
|
||||||
|
|
||||||
return new RenderContext(renderer, level, buffers, stack, projection, viewProjection, camera);
|
return new RenderContext(renderer, level, buffers, stack, projection, viewProjection, camera, partialTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ public interface Material {
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
RenderType getBatchingRenderType();
|
RenderType getFallbackRenderType();
|
||||||
|
|
||||||
MaterialVertexTransformer getVertexTransformer();
|
MaterialVertexTransformer getVertexTransformer();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
|
||||||
|
// TODO: Consider adding LevelAccessor getter
|
||||||
public interface Effect {
|
public interface Effect {
|
||||||
EffectVisual<?> visualize(VisualizationContext ctx);
|
EffectVisual<?> visualize(VisualizationContext ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ import com.jozufozu.flywheel.lib.task.UnitPlan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface giving {@link Visual}s a way to define complex, parallelized update plans.
|
* An interface giving {@link Visual}s a way to define complex, parallelized update plans.
|
||||||
* <p>
|
|
||||||
* Plans allow for
|
|
||||||
*/
|
*/
|
||||||
public interface PlannedVisual extends Visual {
|
public interface PlannedVisual extends Visual {
|
||||||
default Plan<VisualFrameContext> planFrame() {
|
default Plan<VisualFrameContext> planFrame() {
|
||||||
|
|
|
@ -10,14 +10,14 @@ public interface Visual {
|
||||||
/**
|
/**
|
||||||
* Initialize instances here.
|
* Initialize instances here.
|
||||||
*/
|
*/
|
||||||
void init();
|
void init(float partialTick);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update instances here. Good for when instances don't change very often and when animations are GPU based.
|
* Update instances here. Good for when instances don't change very often and when animations are GPU based.
|
||||||
*
|
*
|
||||||
* <br><br> If your animations are complex or more CPU driven, see {@link DynamicVisual} or {@link TickableVisual}.
|
* <br><br> If your animations are complex or more CPU driven, see {@link DynamicVisual} or {@link TickableVisual}.
|
||||||
*/
|
*/
|
||||||
void update();
|
void update(float partialTick);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a visual is reset, the visual is deleted and re-created.
|
* When a visual is reset, the visual is deleted and re-created.
|
||||||
|
|
|
@ -3,5 +3,5 @@ package com.jozufozu.flywheel.api.visual;
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
public record VisualFrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum,
|
public record VisualFrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum,
|
||||||
DistanceUpdateLimiter limiter) {
|
float partialTick, DistanceUpdateLimiter limiter) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
public interface VisualManager<T> {
|
||||||
|
/**
|
||||||
|
* Get the number of game objects that are currently being visualized.
|
||||||
|
*
|
||||||
|
* @return The visual count.
|
||||||
|
*/
|
||||||
|
int getVisualCount();
|
||||||
|
|
||||||
|
void queueAdd(T obj);
|
||||||
|
|
||||||
|
void queueRemove(T obj);
|
||||||
|
|
||||||
|
void queueUpdate(T obj);
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
package com.jozufozu.flywheel.api;
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A marker interface custom levels can override to indicate
|
* A marker interface custom levels can override to indicate
|
||||||
|
@ -7,8 +9,8 @@ package com.jozufozu.flywheel.api;
|
||||||
*
|
*
|
||||||
* {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default.
|
* {@link net.minecraft.client.Minecraft#level Minecraft#level} is special cased and will support Flywheel by default.
|
||||||
*/
|
*/
|
||||||
public interface FlywheelLevel {
|
public interface VisualizationLevel extends LevelAccessor {
|
||||||
default boolean supportsFlywheel() {
|
default boolean supportsVisualization() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visual.Effect;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl;
|
||||||
|
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
|
public interface VisualizationManager {
|
||||||
|
static boolean supportsVisualization(@Nullable LevelAccessor level) {
|
||||||
|
return VisualizationManagerImpl.supportsVisualization(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static VisualizationManager get(@Nullable LevelAccessor level) {
|
||||||
|
return VisualizationManagerImpl.get(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VisualizationManager getOrThrow(@Nullable LevelAccessor level) {
|
||||||
|
return VisualizationManagerImpl.getOrThrow(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
|
* @param blockEntity The block entity whose visual you want to update.
|
||||||
|
*/
|
||||||
|
static void queueUpdate(BlockEntity blockEntity) {
|
||||||
|
Level level = blockEntity.getLevel();
|
||||||
|
VisualizationManager manager = get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getBlockEntities().queueUpdate(blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
|
* @param entity The entity whose visual you want to update.
|
||||||
|
*/
|
||||||
|
static void queueUpdate(Entity entity) {
|
||||||
|
Level level = entity.level;
|
||||||
|
VisualizationManager manager = get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getEntities().queueUpdate(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
|
* @param effect The effect whose visual you want to update.
|
||||||
|
*/
|
||||||
|
static void queueUpdate(LevelAccessor level, Effect effect) {
|
||||||
|
VisualizationManager manager = get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getEffects().queueUpdate(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3i getRenderOrigin();
|
||||||
|
|
||||||
|
VisualManager<BlockEntity> getBlockEntities();
|
||||||
|
|
||||||
|
VisualManager<Entity> getEntities();
|
||||||
|
|
||||||
|
VisualManager<Effect> getEffects();
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.impl.VisualizerRegistryImpl;
|
import com.jozufozu.flywheel.impl.visualization.VisualizerRegistryImpl;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
|
|
@ -26,6 +26,9 @@ public class Loader implements ResourceManagerReloadListener {
|
||||||
FlwPrograms.reload(manager);
|
FlwPrograms.reload(manager);
|
||||||
|
|
||||||
// TODO: Move this to the impl package
|
// TODO: Move this to the impl package
|
||||||
|
// TODO: To ensure this runs after all backends are ready, inject into Minecraft after the reload and before levelRenderer.allChanged()
|
||||||
|
// Alternatively, consider adding API
|
||||||
|
// TODO: This should reset all VisualizationManagerImpls, not just the one for the static client level
|
||||||
BackendManagerImpl.refresh(Minecraft.getInstance().level);
|
BackendManagerImpl.refresh(Minecraft.getInstance().level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.compile.core.ProgramLinker;
|
import com.jozufozu.flywheel.backend.compile.core.ProgramLinker;
|
||||||
|
@ -16,7 +18,6 @@ import com.jozufozu.flywheel.gl.shader.GlShader;
|
||||||
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
import com.jozufozu.flywheel.util.NotNullFunction;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -98,11 +99,11 @@ public class Compile {
|
||||||
return withComponent($ -> component);
|
return withComponent($ -> component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderCompilerBuilder<K> withComponent(NotNullFunction<K, SourceComponent> sourceFetcher) {
|
public ShaderCompilerBuilder<K> withComponent(Function<K, @NotNull SourceComponent> sourceFetcher) {
|
||||||
return with((key, $) -> sourceFetcher.apply(key));
|
return with((key, $) -> sourceFetcher.apply(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderCompilerBuilder<K> withResource(NotNullFunction<K, ResourceLocation> sourceFetcher) {
|
public ShaderCompilerBuilder<K> withResource(Function<K, @NotNull ResourceLocation> sourceFetcher) {
|
||||||
return with((key, loader) -> loader.find(sourceFetcher.apply(key)));
|
return with((key, loader) -> loader.find(sourceFetcher.apply(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
import com.jozufozu.flywheel.backend.compile.Pipeline.InstanceAssembler;
|
||||||
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
import com.jozufozu.flywheel.glsl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
import com.jozufozu.flywheel.glsl.SourceFile;
|
import com.jozufozu.flywheel.glsl.SourceFile;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.glsl.LoadError;
|
import com.jozufozu.flywheel.glsl.LoadError;
|
||||||
import com.jozufozu.flywheel.glsl.LoadResult;
|
import com.jozufozu.flywheel.glsl.LoadResult;
|
||||||
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public class CompilerStats {
|
public class CompilerStats {
|
||||||
private long compileStart;
|
private long compileStart;
|
||||||
|
|
|
@ -11,10 +11,10 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.glsl.SourceFile;
|
import com.jozufozu.flywheel.glsl.SourceFile;
|
||||||
import com.jozufozu.flywheel.glsl.SourceLines;
|
import com.jozufozu.flywheel.glsl.SourceLines;
|
||||||
|
import com.jozufozu.flywheel.glsl.error.ConsoleColors;
|
||||||
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
||||||
import com.jozufozu.flywheel.glsl.span.Span;
|
import com.jozufozu.flywheel.glsl.span.Span;
|
||||||
import com.jozufozu.flywheel.util.ConsoleColors;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
|
|
@ -11,15 +11,16 @@ import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
|
||||||
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
import com.jozufozu.flywheel.lib.math.MoreMath;
|
import com.jozufozu.flywheel.lib.math.MoreMath;
|
||||||
import com.jozufozu.flywheel.lib.math.RenderMath;
|
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
public class UniformBuffer {
|
public class UniformBuffer {
|
||||||
|
|
||||||
private static final int OFFSET_ALIGNMENT = GL32.glGetInteger(GL32.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
private static final int OFFSET_ALIGNMENT = GL32.glGetInteger(GL32.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
|
||||||
private static final int MAX_SIZE = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BLOCK_SIZE);
|
private static final int MAX_SIZE = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BLOCK_SIZE);
|
||||||
private static final int MAX_BINDINGS = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BUFFER_BINDINGS);
|
private static final int MAX_BINDINGS = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BUFFER_BINDINGS);
|
||||||
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
|
private static final boolean PO2_ALIGNMENT = Mth.isPowerOfTwo(OFFSET_ALIGNMENT);
|
||||||
|
|
||||||
private static UniformBuffer instance;
|
private static UniformBuffer instance;
|
||||||
private final ProviderSet providerSet;
|
private final ProviderSet providerSet;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.lib.math.MatrixUtil;
|
import com.jozufozu.flywheel.lib.math.MatrixUtil;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
|
|
@ -89,12 +89,6 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan
|
||||||
initializedInstancers.clear();
|
initializedInstancers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugInfo(List<String> info) {
|
|
||||||
info.add("Batching");
|
|
||||||
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flush() {
|
private void flush() {
|
||||||
for (var instancer : uninitializedInstancers) {
|
for (var instancer : uninitializedInstancers) {
|
||||||
add(instancer.instancer(), instancer.model(), instancer.stage());
|
add(instancer.instancer(), instancer.model(), instancer.stage());
|
||||||
|
@ -111,7 +105,7 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan
|
||||||
var meshes = model.getMeshes();
|
var meshes = model.getMeshes();
|
||||||
for (var entry : meshes.entrySet()) {
|
for (var entry : meshes.entrySet()) {
|
||||||
var material = entry.getKey();
|
var material = entry.getKey();
|
||||||
RenderType renderType = material.getBatchingRenderType();
|
RenderType renderType = material.getFallbackRenderType();
|
||||||
var transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
|
var transformCall = new TransformCall<>(instancer, material, alloc(entry.getValue(), renderType.format()));
|
||||||
stagePlan.put(renderType, transformCall);
|
stagePlan.put(renderType, transformCall);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
|
|
||||||
public class IndirectDrawManager {
|
public class IndirectDrawManager {
|
||||||
private final Map<InstancerKey<?>, IndirectInstancer<?>> instancers = new HashMap<>();
|
private final Map<InstancerKey<?>, IndirectInstancer<?>> instancers = new HashMap<>();
|
||||||
|
@ -43,7 +43,7 @@ public class IndirectDrawManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
instancers.clear();
|
instancers.clear();
|
||||||
|
|
||||||
renderLists.values()
|
renderLists.values()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.indirect;
|
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
|
@ -78,12 +76,6 @@ public class IndirectEngine extends AbstractEngine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
drawManager.delete();
|
drawManager.invalidate();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugInfo(List<String> info) {
|
|
||||||
info.add("GL46 Indirect");
|
|
||||||
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class EBOCache {
|
||||||
private final List<Entry> quads = new ArrayList<>();
|
private final List<Entry> quads = new ArrayList<>();
|
||||||
private final Object2ReferenceMap<Key, Entry> others = new Object2ReferenceOpenHashMap<>();
|
private final Object2ReferenceMap<Key, Entry> others = new Object2ReferenceOpenHashMap<>();
|
||||||
|
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
quads.forEach(Entry::delete);
|
quads.forEach(Entry::delete);
|
||||||
others.values()
|
others.values()
|
||||||
.forEach(Entry::delete);
|
.forEach(Entry::delete);
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class InstancedDrawManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void invalidate() {
|
||||||
instancers.clear();
|
instancers.clear();
|
||||||
|
|
||||||
meshPools.values()
|
meshPools.values()
|
||||||
|
@ -71,7 +71,7 @@ public class InstancedDrawManager {
|
||||||
initializedInstancers.forEach(InstancedInstancer::delete);
|
initializedInstancers.forEach(InstancedInstancer::delete);
|
||||||
initializedInstancers.clear();
|
initializedInstancers.clear();
|
||||||
|
|
||||||
eboCache.delete();
|
eboCache.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearInstancers() {
|
public void clearInstancers() {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
@ -122,12 +120,6 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
drawManager.delete();
|
drawManager.invalidate();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugInfo(List<String> info) {
|
|
||||||
info.add("GL33 Instanced Arrays");
|
|
||||||
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.task;
|
|
||||||
|
|
||||||
public class FlwTaskExecutor {
|
|
||||||
private static ParallelTaskExecutor executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a thread pool for running Flywheel related work in parallel.
|
|
||||||
* @return A global Flywheel thread pool.
|
|
||||||
*/
|
|
||||||
public static ParallelTaskExecutor get() {
|
|
||||||
if (executor == null) {
|
|
||||||
executor = new ParallelTaskExecutor("Flywheel");
|
|
||||||
executor.startWorkers();
|
|
||||||
}
|
|
||||||
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ import org.lwjgl.opengl.GL45C;
|
||||||
import org.lwjgl.system.Checks;
|
import org.lwjgl.system.Checks;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.gl.GlCompat;
|
import com.jozufozu.flywheel.gl.GlCompat;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
|
||||||
public class GlVertexArrayDSA extends GlVertexArray {
|
public class GlVertexArrayDSA extends GlVertexArray {
|
||||||
public static final boolean SUPPORTED = isSupported();
|
public static final boolean SUPPORTED = isSupported();
|
||||||
|
|
|
@ -12,7 +12,7 @@ import org.lwjgl.system.Checks;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.gl.GlCompat;
|
import com.jozufozu.flywheel.gl.GlCompat;
|
||||||
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
|
||||||
public abstract class GlVertexArrayGL3 extends GlVertexArray {
|
public abstract class GlVertexArrayGL3 extends GlVertexArray {
|
||||||
private final BitSet attributeDirty = new BitSet(MAX_ATTRIBS);
|
private final BitSet attributeDirty = new BitSet(MAX_ATTRIBS);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import org.lwjgl.system.Checks;
|
||||||
import com.jozufozu.flywheel.gl.GlCompat;
|
import com.jozufozu.flywheel.gl.GlCompat;
|
||||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
|
||||||
public class GlVertexArraySeparateAttributes extends GlVertexArray {
|
public class GlVertexArraySeparateAttributes extends GlVertexArray {
|
||||||
public static final boolean SUPPORTED = isSupported();
|
public static final boolean SUPPORTED = isSupported();
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package com.jozufozu.flywheel.gl.shader;
|
package com.jozufozu.flywheel.gl.shader;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.glDeleteProgram;
|
||||||
|
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
|
||||||
|
import static org.lwjgl.opengl.GL20.glUniform1i;
|
||||||
import static org.lwjgl.opengl.GL31.GL_INVALID_INDEX;
|
import static org.lwjgl.opengl.GL31.GL_INVALID_INDEX;
|
||||||
import static org.lwjgl.opengl.GL31.glGetUniformBlockIndex;
|
import static org.lwjgl.opengl.GL31.glGetUniformBlockIndex;
|
||||||
import static org.lwjgl.opengl.GL31.glUniformBlockBinding;
|
import static org.lwjgl.opengl.GL31.glUniformBlockBinding;
|
||||||
import static org.lwjgl.opengl.GL32.glDeleteProgram;
|
|
||||||
import static org.lwjgl.opengl.GL32.glGetUniformLocation;
|
|
||||||
import static org.lwjgl.opengl.GL32.glUniform1i;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
import com.jozufozu.flywheel.glsl.error.ErrorBuilder;
|
||||||
import com.jozufozu.flywheel.glsl.span.Span;
|
import com.jozufozu.flywheel.glsl.span.Span;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
|
|
||||||
import net.minecraft.ResourceLocationException;
|
import net.minecraft.ResourceLocationException;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.jozufozu.flywheel.glsl;
|
package com.jozufozu.flywheel.glsl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -10,10 +12,10 @@ import java.util.Map;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,11 +68,9 @@ public class ShaderSources {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected LoadResult load(ResourceLocation loc) {
|
protected LoadResult load(ResourceLocation loc) {
|
||||||
try {
|
try (Resource resource = manager.getResource(ResourceUtil.prefixed(SHADER_DIR, loc))) {
|
||||||
var resource = manager.getResource(ResourceUtil.prefixed(SHADER_DIR, loc));
|
InputStream stream = resource.getInputStream();
|
||||||
|
String sourceString = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
var sourceString = StringUtil.readToString(resource.getInputStream());
|
|
||||||
|
|
||||||
return SourceFile.parse(this, loc, sourceString);
|
return SourceFile.parse(this, loc, sourceString);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return new LoadResult.Failure(new LoadError.IOError(loc, e));
|
return new LoadResult.Failure(new LoadError.IOError(loc, e));
|
||||||
|
|
|
@ -17,8 +17,8 @@ import com.jozufozu.flywheel.glsl.parse.ShaderFunction;
|
||||||
import com.jozufozu.flywheel.glsl.parse.ShaderStruct;
|
import com.jozufozu.flywheel.glsl.parse.ShaderStruct;
|
||||||
import com.jozufozu.flywheel.glsl.span.Span;
|
import com.jozufozu.flywheel.glsl.span.Span;
|
||||||
import com.jozufozu.flywheel.glsl.span.StringSpan;
|
import com.jozufozu.flywheel.glsl.span.StringSpan;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
|
|
||||||
import net.minecraft.ResourceLocationException;
|
import net.minecraft.ResourceLocationException;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.jozufozu.flywheel.util;
|
package com.jozufozu.flywheel.glsl.error;
|
||||||
|
|
||||||
// https://stackoverflow.com/a/45444716
|
// https://stackoverflow.com/a/45444716
|
||||||
public class ConsoleColors {
|
public final class ConsoleColors {
|
||||||
// Reset
|
// Reset
|
||||||
public static final String RESET = "\033[0m"; // Text Reset
|
public static final String RESET = "\033[0m"; // Text Reset
|
||||||
|
|
||||||
|
@ -74,4 +74,7 @@ public class ConsoleColors {
|
||||||
public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
|
public static final String PURPLE_BACKGROUND_BRIGHT = "\033[0;105m"; // PURPLE
|
||||||
public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
|
public static final String CYAN_BACKGROUND_BRIGHT = "\033[0;106m"; // CYAN
|
||||||
public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
|
public static final String WHITE_BACKGROUND_BRIGHT = "\033[0;107m"; // WHITE
|
||||||
|
|
||||||
|
private ConsoleColors() {
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,8 +19,7 @@ import com.jozufozu.flywheel.glsl.error.lines.SourceLine;
|
||||||
import com.jozufozu.flywheel.glsl.error.lines.SpanHighlightLine;
|
import com.jozufozu.flywheel.glsl.error.lines.SpanHighlightLine;
|
||||||
import com.jozufozu.flywheel.glsl.error.lines.TextLine;
|
import com.jozufozu.flywheel.glsl.error.lines.TextLine;
|
||||||
import com.jozufozu.flywheel.glsl.span.Span;
|
import com.jozufozu.flywheel.glsl.span.Span;
|
||||||
import com.jozufozu.flywheel.util.ConsoleColors;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.glsl.error;
|
package com.jozufozu.flywheel.glsl.error;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.ConsoleColors;
|
|
||||||
|
|
||||||
public enum ErrorLevel {
|
public enum ErrorLevel {
|
||||||
WARN(ConsoleColors.YELLOW, "warn"),
|
WARN(ConsoleColors.YELLOW, "warn"),
|
||||||
ERROR(ConsoleColors.RED, "error"),
|
ERROR(ConsoleColors.RED, "error"),
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
|
|
||||||
public record FnSignature(String returnType, String name, ImmutableList<Pair<String, String>> args) {
|
public record FnSignature(String returnType, String name, ImmutableList<Pair<String, String>> args) {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.glsl.generate;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public class GlslFn implements GlslBuilder.Declaration {
|
public class GlslFn implements GlslBuilder.Declaration {
|
||||||
private final GlslBlock body = new GlslBlock();
|
private final GlslBlock body = new GlslBlock();
|
||||||
|
|
|
@ -4,8 +4,8 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public class GlslStruct implements GlslBuilder.Declaration {
|
public class GlslStruct implements GlslBuilder.Declaration {
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public class GlslSwitch implements GlslStmt {
|
public class GlslSwitch implements GlslStmt {
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public class GlslUniformBlock implements GlslBuilder.Declaration {
|
public class GlslUniformBlock implements GlslBuilder.Declaration {
|
||||||
private String qualifier;
|
private String qualifier;
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package com.jozufozu.flywheel.handler;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
|
||||||
import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
|
|
||||||
|
|
||||||
public class EntityWorldHandler {
|
|
||||||
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
|
||||||
Level level = event.getWorld();
|
|
||||||
if (!level.isClientSide) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FlwUtil.canUseVisualization(level)) {
|
|
||||||
VisualizedRenderDispatcher.getEntities(level)
|
|
||||||
.queueAdd(event.getEntity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
|
|
||||||
Level level = event.getWorld();
|
|
||||||
if (!level.isClientSide) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FlwUtil.canUseVisualization(level)) {
|
|
||||||
VisualizedRenderDispatcher.getEntities(level)
|
|
||||||
.queueRemove(event.getEntity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package com.jozufozu.flywheel.handler;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
|
||||||
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
|
||||||
import net.minecraftforge.event.TickEvent;
|
|
||||||
import net.minecraftforge.event.world.WorldEvent;
|
|
||||||
|
|
||||||
public class ForgeEvents {
|
|
||||||
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
|
|
||||||
if (Minecraft.getInstance().options.renderDebug) {
|
|
||||||
ArrayList<String> debug = event.getRight();
|
|
||||||
debug.add("");
|
|
||||||
debug.add("Flywheel: " + Flywheel.getVersion());
|
|
||||||
|
|
||||||
VisualizedRenderDispatcher.addDebugInfo(debug);
|
|
||||||
|
|
||||||
debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void unloadWorld(WorldEvent.Unload event) {
|
|
||||||
WorldAttached.invalidateWorld(event.getWorld());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void tickLight(TickEvent.ClientTickEvent event) {
|
|
||||||
if (event.phase == TickEvent.Phase.END && FlwUtil.isGameActive()) {
|
|
||||||
LightUpdater.get(Minecraft.getInstance().level)
|
|
||||||
.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,13 +8,14 @@ import com.jozufozu.flywheel.api.backend.Backend;
|
||||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.backend.Backends;
|
import com.jozufozu.flywheel.backend.Backends;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl;
|
||||||
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.network.chat.TextComponent;
|
import net.minecraft.network.chat.TextComponent;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraftforge.fml.CrashReportCallables;
|
import net.minecraftforge.fml.CrashReportCallables;
|
||||||
|
|
||||||
public final class BackendManagerImpl {
|
public final class BackendManagerImpl {
|
||||||
|
@ -23,7 +24,7 @@ public final class BackendManagerImpl {
|
||||||
private static final Backend OFF_BACKEND = SimpleBackend.builder()
|
private static final Backend OFF_BACKEND = SimpleBackend.builder()
|
||||||
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
||||||
.engineFactory(level -> {
|
.engineFactory(level -> {
|
||||||
throw new IllegalStateException("Cannot create engine when backend is off.");
|
throw new UnsupportedOperationException("Cannot create engine when backend is off.");
|
||||||
})
|
})
|
||||||
.supported(() -> true)
|
.supported(() -> true)
|
||||||
.register(Flywheel.rl("off"));
|
.register(Flywheel.rl("off"));
|
||||||
|
@ -32,6 +33,9 @@ public final class BackendManagerImpl {
|
||||||
|
|
||||||
private static Backend backend = OFF_BACKEND;
|
private static Backend backend = OFF_BACKEND;
|
||||||
|
|
||||||
|
private BackendManagerImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
public static Backend getBackend() {
|
public static Backend getBackend() {
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +66,7 @@ public final class BackendManagerImpl {
|
||||||
backend = chooseBackend();
|
backend = chooseBackend();
|
||||||
|
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
VisualizedRenderDispatcher.resetVisualWorld(level);
|
VisualizationManagerImpl.reset(level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,16 +81,15 @@ public final class BackendManagerImpl {
|
||||||
return actual;
|
return actual;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static String getBackendString() {
|
||||||
CrashReportCallables.registerCrashCallable("Flywheel Backend", () -> {
|
ResourceLocation backendId = Backend.REGISTRY.getId(backend);
|
||||||
var backendId = Backend.REGISTRY.getId(backend);
|
if (backendId == null) {
|
||||||
if (backendId == null) {
|
return "[unregistered]";
|
||||||
return "Unregistered";
|
}
|
||||||
}
|
return backendId.toString();
|
||||||
return backendId.toString();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BackendManagerImpl() {
|
public static void init() {
|
||||||
|
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.jozufozu.flywheel.impl.task;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.concurrent.AtomicSafeInitializer;
|
||||||
|
import org.apache.commons.lang3.concurrent.ConcurrentUtils;
|
||||||
|
|
||||||
|
public final class FlwTaskExecutor {
|
||||||
|
// TODO: system property to use SerialTaskExecutor
|
||||||
|
private static final Initializer INITIALIZER = new Initializer();
|
||||||
|
|
||||||
|
private FlwTaskExecutor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a thread pool for running Flywheel related work in parallel.
|
||||||
|
* @return A global Flywheel thread pool.
|
||||||
|
*/
|
||||||
|
public static ParallelTaskExecutor get() {
|
||||||
|
return ConcurrentUtils.initializeUnchecked(INITIALIZER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Initializer extends AtomicSafeInitializer<ParallelTaskExecutor> {
|
||||||
|
@Override
|
||||||
|
protected ParallelTaskExecutor initialize() {
|
||||||
|
ParallelTaskExecutor executor = new ParallelTaskExecutor("Flywheel");
|
||||||
|
executor.startWorkers();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.task;
|
package com.jozufozu.flywheel.impl.task;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
@ -13,8 +13,6 @@ import org.slf4j.Logger;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.lib.task.ThreadGroupNotifier;
|
|
||||||
import com.jozufozu.flywheel.lib.task.WaitGroup;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.task;
|
package com.jozufozu.flywheel.impl.task;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.lib.task;
|
package com.jozufozu.flywheel.impl.task;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thin wrapper around Java's built-in object synchronization primitives.
|
* Thin wrapper around Java's built-in object synchronization primitives.
|
|
@ -1,15 +1,10 @@
|
||||||
package com.jozufozu.flywheel.lib.task;
|
package com.jozufozu.flywheel.impl.task;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
|
|
||||||
public class WaitGroup {
|
public class WaitGroup {
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
|
||||||
|
|
||||||
private final AtomicInteger counter = new AtomicInteger(0);
|
private final AtomicInteger counter = new AtomicInteger(0);
|
||||||
|
|
||||||
public void add() {
|
public void add() {
|
|
@ -5,11 +5,9 @@ import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
public class InferredVertexListProviderImpl implements VertexListProvider {
|
public class InferredVertexListProviderImpl implements VertexListProvider {
|
||||||
private final VertexFormat format;
|
|
||||||
private final InferredVertexFormatInfo formatInfo;
|
private final InferredVertexFormatInfo formatInfo;
|
||||||
|
|
||||||
public InferredVertexListProviderImpl(VertexFormat format) {
|
public InferredVertexListProviderImpl(VertexFormat format) {
|
||||||
this.format = format;
|
|
||||||
formatInfo = new InferredVertexFormatInfo(format);
|
formatInfo = new InferredVertexFormatInfo(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@ package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
public record FrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
public record FrameContext(double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum, float partialTick) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
||||||
|
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||||
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||||
|
import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
|
||||||
|
|
||||||
|
public final class VisualizationEventHandler {
|
||||||
|
private VisualizationEventHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
||||||
|
if (event.phase != TickEvent.Phase.END || !FlwUtil.isGameActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc.isPaused()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity cameraEntity = mc.getCameraEntity() == null ? mc.player : mc.getCameraEntity();
|
||||||
|
if (cameraEntity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Level level = cameraEntity.level;
|
||||||
|
VisualizationManagerImpl manager = VisualizationManagerImpl.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double cameraX = cameraEntity.getX();
|
||||||
|
double cameraY = cameraEntity.getEyeY();
|
||||||
|
double cameraZ = cameraEntity.getZ();
|
||||||
|
|
||||||
|
manager.tick(cameraX, cameraY, cameraZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBeginFrame(BeginFrameEvent event) {
|
||||||
|
ClientLevel level = event.getContext().level();
|
||||||
|
VisualizationManagerImpl manager = VisualizationManagerImpl.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.beginFrame(event.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onRenderStage(RenderStageEvent event) {
|
||||||
|
ClientLevel level = event.getContext().level();
|
||||||
|
VisualizationManagerImpl manager = VisualizationManagerImpl.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.renderStage(event.getContext(), event.getStage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
||||||
|
Level level = event.getWorld();
|
||||||
|
VisualizationManager manager = VisualizationManager.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getEntities().queueAdd(event.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
|
||||||
|
Level level = event.getWorld();
|
||||||
|
VisualizationManager manager = VisualizationManager.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getEntities().queueRemove(event.getEntity());
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,14 +4,19 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
||||||
import com.jozufozu.flywheel.api.visualization.EntityVisualizer;
|
import com.jozufozu.flywheel.api.visualization.EntityVisualizer;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizerRegistry;
|
import com.jozufozu.flywheel.api.visualization.VisualizerRegistry;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
|
||||||
public final class VisualizationHelper {
|
public final class VisualizationHelper {
|
||||||
|
private VisualizationHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Nullable
|
@Nullable
|
||||||
public static <T extends BlockEntity> BlockEntityVisualizer<? super T> getVisualizer(T blockEntity) {
|
public static <T extends BlockEntity> BlockEntityVisualizer<? super T> getVisualizer(T blockEntity) {
|
||||||
|
@ -72,6 +77,20 @@ public final class VisualizationHelper {
|
||||||
return visualizer.shouldSkipRender(entity);
|
return visualizer.shouldSkipRender(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private VisualizationHelper() {
|
public static <T extends BlockEntity> boolean tryAddBlockEntity(T blockEntity) {
|
||||||
|
Level level = blockEntity.getLevel();
|
||||||
|
VisualizationManager manager = VisualizationManager.get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockEntityVisualizer<? super T> visualizer = getVisualizer(blockEntity);
|
||||||
|
if (visualizer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.getBlockEntities().queueAdd(blockEntity);
|
||||||
|
|
||||||
|
return visualizer.shouldSkipRender(blockEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.jozufozu.flywheel.impl.visualization;
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
import java.util.List;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||||
|
@ -13,42 +12,47 @@ import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
import com.jozufozu.flywheel.api.visual.Effect;
|
import com.jozufozu.flywheel.api.visual.Effect;
|
||||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||||
import com.jozufozu.flywheel.backend.task.FlwTaskExecutor;
|
import com.jozufozu.flywheel.api.visualization.VisualManager;
|
||||||
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
|
import com.jozufozu.flywheel.api.visualization.VisualizationLevel;
|
||||||
import com.jozufozu.flywheel.config.FlwCommands;
|
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
||||||
|
import com.jozufozu.flywheel.impl.task.FlwTaskExecutor;
|
||||||
|
import com.jozufozu.flywheel.impl.task.ParallelTaskExecutor;
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.VisualManager;
|
|
||||||
import com.jozufozu.flywheel.lib.math.MatrixUtil;
|
import com.jozufozu.flywheel.lib.math.MatrixUtil;
|
||||||
import com.jozufozu.flywheel.lib.task.NestedPlan;
|
import com.jozufozu.flywheel.lib.task.NestedPlan;
|
||||||
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
|
import com.jozufozu.flywheel.lib.task.SimplyComposedPlan;
|
||||||
import com.jozufozu.flywheel.util.Unit;
|
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A manager class for a single world where instancing is supported.
|
* A manager class for a single world where visualization is supported.
|
||||||
*/
|
*/
|
||||||
// AutoCloseable is implemented to prevent leaking this object from WorldAttached
|
public class VisualizationManagerImpl implements VisualizationManager {
|
||||||
public class VisualWorld implements AutoCloseable {
|
private static final LevelAttached<VisualizationManagerImpl> MANAGERS = new LevelAttached<>(VisualizationManagerImpl::new, VisualizationManagerImpl::delete);
|
||||||
|
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final ParallelTaskExecutor taskExecutor;
|
private final ParallelTaskExecutor taskExecutor;
|
||||||
|
|
||||||
private final VisualManager<BlockEntity> blockEntities;
|
private final BlockEntityVisualManager blockEntities;
|
||||||
private final VisualManager<Entity> entities;
|
private final EntityVisualManager entities;
|
||||||
private final VisualManager<Effect> effects;
|
private final EffectVisualManager effects;
|
||||||
|
|
||||||
private final Plan<TickContext> tickPlan;
|
private final Plan<TickContext> tickPlan;
|
||||||
private final Plan<RenderContext> framePlan;
|
private final Plan<RenderContext> framePlan;
|
||||||
|
|
||||||
public VisualWorld(LevelAccessor level) {
|
private VisualizationManagerImpl(LevelAccessor level) {
|
||||||
engine = BackendManager.getBackend()
|
engine = BackendManager.getBackend()
|
||||||
.createEngine(level);
|
.createEngine(level);
|
||||||
|
// FIXME: All VisualizationManagerImpls use the same executor so calls like syncPoint and discardAndAwait could adversely impact other active VisualizationManagerImpls
|
||||||
taskExecutor = FlwTaskExecutor.get();
|
taskExecutor = FlwTaskExecutor.get();
|
||||||
|
|
||||||
blockEntities = new BlockEntityVisualManager(engine);
|
blockEntities = new BlockEntityVisualManager(engine);
|
||||||
|
@ -62,18 +66,74 @@ public class VisualWorld implements AutoCloseable {
|
||||||
framePlan = new FramePlan();
|
framePlan = new FramePlan();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine getEngine() {
|
public static boolean supportsVisualization(@Nullable LevelAccessor level) {
|
||||||
return engine;
|
if (!BackendManager.isOn()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!level.isClientSide()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level instanceof VisualizationLevel flywheelLevel && flywheelLevel.supportsVisualization()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return level == Minecraft.getInstance().level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static VisualizationManagerImpl get(@Nullable LevelAccessor level) {
|
||||||
|
if (!supportsVisualization(level)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MANAGERS.get(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VisualizationManagerImpl getOrThrow(@Nullable LevelAccessor level) {
|
||||||
|
if (!supportsVisualization(level)) {
|
||||||
|
throw new IllegalStateException("Cannot retrieve visualization manager when visualization is not supported by level '" + level + "'!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MANAGERS.get(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider making this reset action reuse the existing added game objects instead of readding them, potentially by keeping the same VisualizationManagerImpl and not fully deleting it
|
||||||
|
// TODO: Consider changing parameter type to Level since it is also possible to get all entities from it
|
||||||
|
public static void reset(ClientLevel level) {
|
||||||
|
MANAGERS.remove(level);
|
||||||
|
VisualizationManagerImpl manager = get(level);
|
||||||
|
if (manager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block entities are loaded while chunks are baked.
|
||||||
|
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
|
||||||
|
ClientLevelExtension.getAllLoadedEntities(level)
|
||||||
|
.forEach(manager.getEntities()::queueAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3i getRenderOrigin() {
|
||||||
|
return engine.renderOrigin();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public VisualManager<BlockEntity> getBlockEntities() {
|
public VisualManager<BlockEntity> getBlockEntities() {
|
||||||
return blockEntities;
|
return blockEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public VisualManager<Entity> getEntities() {
|
public VisualManager<Entity> getEntities() {
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public VisualManager<Effect> getEffects() {
|
public VisualManager<Effect> getEffects() {
|
||||||
return effects;
|
return effects;
|
||||||
}
|
}
|
||||||
|
@ -111,16 +171,8 @@ public class VisualWorld implements AutoCloseable {
|
||||||
engine.renderStage(taskExecutor, context, stage);
|
engine.renderStage(taskExecutor, context, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDebugInfo(List<String> info) {
|
|
||||||
info.add("B: " + blockEntities.getVisualCount()
|
|
||||||
+ ", E: " + entities.getVisualCount()
|
|
||||||
+ ", F: " + effects.getVisualCount());
|
|
||||||
info.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
|
||||||
engine.addDebugInfo(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free all acquired resources and invalidate this visual world.
|
* Free all acquired resources and delete this manager.
|
||||||
*/
|
*/
|
||||||
public void delete() {
|
public void delete() {
|
||||||
taskExecutor.discardAndAwait();
|
taskExecutor.discardAndAwait();
|
||||||
|
@ -130,13 +182,8 @@ public class VisualWorld implements AutoCloseable {
|
||||||
engine.delete();
|
engine.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FramePlan implements SimplyComposedPlan<RenderContext> {
|
private class FramePlan implements SimplyComposedPlan<RenderContext> {
|
||||||
private final Plan<Unit> recreationPlan = NestedPlan.of(blockEntities.createRecreationPlan(), entities.createRecreationPlan(), effects.createRecreationPlan());
|
private final Plan<Float> recreationPlan = NestedPlan.of(blockEntities.createRecreationPlan(), entities.createRecreationPlan(), effects.createRecreationPlan());
|
||||||
private final Plan<FrameContext> normalPlan = blockEntities.createFramePlan()
|
private final Plan<FrameContext> normalPlan = blockEntities.createFramePlan()
|
||||||
.and(entities.createFramePlan())
|
.and(entities.createFramePlan())
|
||||||
.and(effects.createFramePlan());
|
.and(effects.createFramePlan());
|
||||||
|
@ -146,9 +193,10 @@ public class VisualWorld implements AutoCloseable {
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskExecutor taskExecutor, RenderContext context, Runnable onCompletion) {
|
public void execute(TaskExecutor taskExecutor, RenderContext context, Runnable onCompletion) {
|
||||||
Runnable then = () -> enginePlan.execute(taskExecutor, context, onCompletion);
|
Runnable then = () -> enginePlan.execute(taskExecutor, context, onCompletion);
|
||||||
|
float partialTick = context.partialTick();
|
||||||
|
|
||||||
if (engine.updateRenderOrigin(context.camera())) {
|
if (engine.updateRenderOrigin(context.camera())) {
|
||||||
recreationPlan.execute(taskExecutor, Unit.INSTANCE, then);
|
recreationPlan.execute(taskExecutor, partialTick, then);
|
||||||
} else {
|
} else {
|
||||||
Vec3i renderOrigin = engine.renderOrigin();
|
Vec3i renderOrigin = engine.renderOrigin();
|
||||||
var cameraPos = context.camera()
|
var cameraPos = context.camera()
|
||||||
|
@ -161,7 +209,7 @@ public class VisualWorld implements AutoCloseable {
|
||||||
proj.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
|
proj.translate((float) (renderOrigin.getX() - cameraX), (float) (renderOrigin.getY() - cameraY), (float) (renderOrigin.getZ() - cameraZ));
|
||||||
FrustumIntersection frustum = new FrustumIntersection(proj);
|
FrustumIntersection frustum = new FrustumIntersection(proj);
|
||||||
|
|
||||||
var frameContext = new FrameContext(cameraX, cameraY, cameraZ, frustum);
|
var frameContext = new FrameContext(cameraX, cameraY, cameraZ, frustum, partialTick);
|
||||||
|
|
||||||
normalPlan.execute(taskExecutor, frameContext, then);
|
normalPlan.execute(taskExecutor, frameContext, then);
|
||||||
}
|
}
|
|
@ -1,191 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.visualization;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
|
||||||
import com.jozufozu.flywheel.api.visual.Effect;
|
|
||||||
import com.jozufozu.flywheel.api.visual.Visual;
|
|
||||||
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
|
||||||
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
|
||||||
import com.jozufozu.flywheel.impl.visualization.manager.VisualManager;
|
|
||||||
import com.jozufozu.flywheel.lib.util.AnimationTickHolder;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
import net.minecraft.core.Vec3i;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraftforge.event.TickEvent;
|
|
||||||
|
|
||||||
public class VisualizedRenderDispatcher {
|
|
||||||
private static final WorldAttached<VisualWorld> VISUAL_WORLDS = new WorldAttached<>(VisualWorld::new);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this when you want to run {@link Visual#update()}.
|
|
||||||
* @param blockEntity The block entity whose visual you want to update.
|
|
||||||
*/
|
|
||||||
public static void queueUpdate(BlockEntity blockEntity) {
|
|
||||||
if (!(blockEntity.getLevel() instanceof ClientLevel level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level)
|
|
||||||
.getBlockEntities()
|
|
||||||
.queueUpdate(blockEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this when you want to run {@link Visual#update()}.
|
|
||||||
* @param entity The entity whose visual you want to update.
|
|
||||||
*/
|
|
||||||
public static void queueUpdate(Entity entity) {
|
|
||||||
Level level = entity.level;
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level)
|
|
||||||
.getEntities()
|
|
||||||
.queueUpdate(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this when you want to run {@link Visual#update()}.
|
|
||||||
* @param effect The effect whose visual you want to update.
|
|
||||||
*/
|
|
||||||
public static void queueUpdate(LevelAccessor level, Effect effect) {
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level)
|
|
||||||
.getEffects()
|
|
||||||
.queueUpdate(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create the {@link VisualWorld} for the given world.
|
|
||||||
* @throws IllegalStateException if the backend is off
|
|
||||||
*/
|
|
||||||
private static VisualWorld getVisualWorld(LevelAccessor level) {
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
throw new IllegalStateException("Cannot retrieve visual world when backend is off!");
|
|
||||||
}
|
|
||||||
return VISUAL_WORLDS.get(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VisualManager<BlockEntity> getBlockEntities(LevelAccessor level) {
|
|
||||||
return getVisualWorld(level).getBlockEntities();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VisualManager<Entity> getEntities(LevelAccessor level) {
|
|
||||||
return getVisualWorld(level).getEntities();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VisualManager<Effect> getEffects(LevelAccessor level) {
|
|
||||||
return getVisualWorld(level).getEffects();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Vec3i getRenderOrigin(LevelAccessor level) {
|
|
||||||
return getVisualWorld(level).getEngine().renderOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void tick(TickEvent.ClientTickEvent event) {
|
|
||||||
if (!FlwUtil.isGameActive() || event.phase == TickEvent.Phase.START) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimationTickHolder.tick();
|
|
||||||
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
|
||||||
if (mc.isPaused()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity cameraEntity = mc.getCameraEntity() == null ? mc.player : mc.getCameraEntity();
|
|
||||||
if (cameraEntity == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Level level = cameraEntity.level;
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double cameraX = cameraEntity.getX();
|
|
||||||
double cameraY = cameraEntity.getEyeY();
|
|
||||||
double cameraZ = cameraEntity.getZ();
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level).tick(cameraX, cameraY, cameraZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onBeginFrame(BeginFrameEvent event) {
|
|
||||||
if (!FlwUtil.isGameActive()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientLevel level = event.getContext().level();
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level).beginFrame(event.getContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onRenderStage(RenderStageEvent event) {
|
|
||||||
ClientLevel level = event.getContext().level();
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VISUAL_WORLDS.get(level).renderStage(event.getContext(), event.getStage());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void resetVisualWorld(ClientLevel level) {
|
|
||||||
VISUAL_WORLDS.remove(level, VisualWorld::delete);
|
|
||||||
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VisualWorld world = VISUAL_WORLDS.get(level);
|
|
||||||
// Block entities are loaded while chunks are baked.
|
|
||||||
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
|
|
||||||
ClientLevelExtension.getAllLoadedEntities(level)
|
|
||||||
.forEach(world.getEntities()::queueAdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends BlockEntity> boolean tryAddBlockEntity(T blockEntity) {
|
|
||||||
Level level = blockEntity.getLevel();
|
|
||||||
if (!FlwUtil.canUseVisualization(level)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockEntityVisualizer<? super T> visualizer = VisualizationHelper.getVisualizer(blockEntity);
|
|
||||||
if (visualizer == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBlockEntities(level).queueAdd(blockEntity);
|
|
||||||
|
|
||||||
return visualizer.shouldSkipRender(blockEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addDebugInfo(List<String> info) {
|
|
||||||
ClientLevel level = Minecraft.getInstance().level;
|
|
||||||
if (FlwUtil.canUseVisualization(level)) {
|
|
||||||
VISUAL_WORLDS.get(level).addDebugInfo(info);
|
|
||||||
} else {
|
|
||||||
info.add("Disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl;
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
|
||||||
//TODO: Add freezing
|
// TODO: Add freezing
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final class VisualizerRegistryImpl {
|
public final class VisualizerRegistryImpl {
|
||||||
@Nullable
|
@Nullable
|
|
@ -6,6 +6,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||||
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
import com.jozufozu.flywheel.api.visual.VisualTickContext;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualManager;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.impl.visualization.FrameContext;
|
import com.jozufozu.flywheel.impl.visualization.FrameContext;
|
||||||
import com.jozufozu.flywheel.impl.visualization.TickContext;
|
import com.jozufozu.flywheel.impl.visualization.TickContext;
|
||||||
|
@ -15,15 +16,14 @@ import com.jozufozu.flywheel.impl.visualization.ratelimit.NonLimiter;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
||||||
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
import com.jozufozu.flywheel.lib.task.SimplePlan;
|
||||||
import com.jozufozu.flywheel.util.Unit;
|
|
||||||
|
|
||||||
public abstract class VisualManager<T> {
|
public abstract class AbstractVisualManager<T> implements VisualManager<T> {
|
||||||
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
|
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
protected DistanceUpdateLimiterImpl tickLimiter;
|
protected DistanceUpdateLimiterImpl tickLimiter;
|
||||||
protected DistanceUpdateLimiterImpl frameLimiter;
|
protected DistanceUpdateLimiterImpl frameLimiter;
|
||||||
|
|
||||||
public VisualManager() {
|
public AbstractVisualManager() {
|
||||||
tickLimiter = createUpdateLimiter();
|
tickLimiter = createUpdateLimiter();
|
||||||
frameLimiter = createUpdateLimiter();
|
frameLimiter = createUpdateLimiter();
|
||||||
}
|
}
|
||||||
|
@ -39,15 +39,12 @@ public abstract class VisualManager<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Get the number of game objects that are currently being visualized.
|
|
||||||
*
|
|
||||||
* @return The object count.
|
|
||||||
*/
|
|
||||||
public int getVisualCount() {
|
public int getVisualCount() {
|
||||||
return getStorage().getAllVisuals().size();
|
return getStorage().getAllVisuals().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void queueAdd(T obj) {
|
public void queueAdd(T obj) {
|
||||||
if (!getStorage().willAccept(obj)) {
|
if (!getStorage().willAccept(obj)) {
|
||||||
return;
|
return;
|
||||||
|
@ -56,10 +53,12 @@ public abstract class VisualManager<T> {
|
||||||
queue.add(Transaction.add(obj));
|
queue.add(Transaction.add(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void queueRemove(T obj) {
|
public void queueRemove(T obj) {
|
||||||
queue.add(Transaction.remove(obj));
|
queue.add(Transaction.remove(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void queueUpdate(T obj) {
|
public void queueUpdate(T obj) {
|
||||||
if (!getStorage().willAccept(obj)) {
|
if (!getStorage().willAccept(obj)) {
|
||||||
return;
|
return;
|
||||||
|
@ -68,7 +67,7 @@ public abstract class VisualManager<T> {
|
||||||
queue.add(Transaction.update(obj));
|
queue.add(Transaction.update(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plan<Unit> createRecreationPlan() {
|
public Plan<Float> createRecreationPlan() {
|
||||||
return SimplePlan.of(getStorage()::recreateAll);
|
return SimplePlan.of(getStorage()::recreateAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,32 +75,32 @@ public abstract class VisualManager<T> {
|
||||||
getStorage().invalidate();
|
getStorage().invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processQueue() {
|
protected void processQueue(float partialTick) {
|
||||||
var storage = getStorage();
|
var storage = getStorage();
|
||||||
Transaction<T> transaction;
|
Transaction<T> transaction;
|
||||||
while ((transaction = queue.poll()) != null) {
|
while ((transaction = queue.poll()) != null) {
|
||||||
transaction.apply(storage);
|
transaction.apply(storage, partialTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plan<TickContext> createTickPlan() {
|
public Plan<TickContext> createTickPlan() {
|
||||||
return SimplePlan.<TickContext>of(() -> {
|
return SimplePlan.<TickContext>of(() -> {
|
||||||
tickLimiter.tick();
|
tickLimiter.tick();
|
||||||
processQueue();
|
processQueue(0);
|
||||||
})
|
})
|
||||||
.thenMap(this::createVisualTickContext, getStorage().getTickPlan());
|
.thenMap(this::createVisualTickContext, getStorage().getTickPlan());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plan<FrameContext> createFramePlan() {
|
public Plan<FrameContext> createFramePlan() {
|
||||||
return SimplePlan.<FrameContext>of(() -> {
|
return SimplePlan.<FrameContext>of(context -> {
|
||||||
frameLimiter.tick();
|
frameLimiter.tick();
|
||||||
processQueue();
|
processQueue(context.partialTick());
|
||||||
})
|
})
|
||||||
.thenMap(this::createVisualContext, getStorage().getFramePlan());
|
.thenMap(this::createVisualContext, getStorage().getFramePlan());
|
||||||
}
|
}
|
||||||
|
|
||||||
private VisualFrameContext createVisualContext(FrameContext ctx) {
|
private VisualFrameContext createVisualContext(FrameContext ctx) {
|
||||||
return new VisualFrameContext(ctx.cameraX(), ctx.cameraY(), ctx.cameraZ(), ctx.frustum(), frameLimiter);
|
return new VisualFrameContext(ctx.cameraX(), ctx.cameraY(), ctx.cameraZ(), ctx.frustum(), ctx.partialTick(), frameLimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private VisualTickContext createVisualTickContext(TickContext ctx) {
|
private VisualTickContext createVisualTickContext(TickContext ctx) {
|
|
@ -10,7 +10,6 @@ import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
|
|
||||||
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;
|
||||||
|
@ -19,7 +18,7 @@ import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
public class BlockEntityVisualManager extends VisualManager<BlockEntity> {
|
public class BlockEntityVisualManager extends AbstractVisualManager<BlockEntity> {
|
||||||
private final BlockEntityStorage storage;
|
private final BlockEntityStorage storage;
|
||||||
|
|
||||||
public BlockEntityVisualManager(Engine engine) {
|
public BlockEntityVisualManager(Engine engine) {
|
||||||
|
@ -56,7 +55,6 @@ public class BlockEntityVisualManager extends VisualManager<BlockEntity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Level level = blockEntity.getLevel();
|
Level level = blockEntity.getLevel();
|
||||||
|
|
||||||
if (level == null) {
|
if (level == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -65,15 +63,9 @@ public class BlockEntityVisualManager extends VisualManager<BlockEntity> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlwUtil.isFlywheelLevel(level)) {
|
BlockPos pos = blockEntity.getBlockPos();
|
||||||
BlockPos pos = blockEntity.getBlockPos();
|
BlockGetter existingChunk = level.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4);
|
||||||
|
return existingChunk != null;
|
||||||
BlockGetter existingChunk = level.getChunkForCollisions(pos.getX() >> 4, pos.getZ() >> 4);
|
|
||||||
|
|
||||||
return existingChunk != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,7 +6,7 @@ import com.jozufozu.flywheel.api.visual.EffectVisual;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
|
|
||||||
public class EffectVisualManager extends VisualManager<Effect> {
|
public class EffectVisualManager extends AbstractVisualManager<Effect> {
|
||||||
private final EffectStorage storage;
|
private final EffectStorage storage;
|
||||||
|
|
||||||
public EffectVisualManager(Engine engine) {
|
public EffectVisualManager(Engine engine) {
|
||||||
|
|
|
@ -7,12 +7,11 @@ import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
||||||
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
public class EntityVisualManager extends VisualManager<Entity> {
|
public class EntityVisualManager extends AbstractVisualManager<Entity> {
|
||||||
private final EntityStorage storage;
|
private final EntityStorage storage;
|
||||||
|
|
||||||
public EntityVisualManager(Engine engine) {
|
public EntityVisualManager(Engine engine) {
|
||||||
|
@ -51,8 +50,7 @@ public class EntityVisualManager extends VisualManager<Entity> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Level level = entity.level;
|
Level level = entity.level;
|
||||||
|
return level != null;
|
||||||
return FlwUtil.isFlywheelLevel(level);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,11 +41,11 @@ public abstract class Storage<T> {
|
||||||
return visuals.values();
|
return visuals.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(T obj) {
|
public void add(T obj, float partialTick) {
|
||||||
Visual visual = visuals.get(obj);
|
Visual visual = visuals.get(obj);
|
||||||
|
|
||||||
if (visual == null) {
|
if (visual == null) {
|
||||||
create(obj);
|
create(obj, partialTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public abstract class Storage<T> {
|
||||||
visual.delete();
|
visual.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(T obj) {
|
public void update(T obj, float partialTick) {
|
||||||
Visual visual = visuals.get(obj);
|
Visual visual = visuals.get(obj);
|
||||||
|
|
||||||
if (visual == null) {
|
if (visual == null) {
|
||||||
|
@ -76,13 +76,13 @@ public abstract class Storage<T> {
|
||||||
// delete and re-create the visual.
|
// delete and re-create the visual.
|
||||||
// resetting a visual supersedes updating it.
|
// resetting a visual supersedes updating it.
|
||||||
remove(obj);
|
remove(obj);
|
||||||
create(obj);
|
create(obj, partialTick);
|
||||||
} else {
|
} else {
|
||||||
visual.update();
|
visual.update(partialTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void recreateAll() {
|
public void recreateAll(float partialTick) {
|
||||||
tickableVisuals.clear();
|
tickableVisuals.clear();
|
||||||
dynamicVisuals.clear();
|
dynamicVisuals.clear();
|
||||||
plannedVisuals.clear();
|
plannedVisuals.clear();
|
||||||
|
@ -92,7 +92,7 @@ public abstract class Storage<T> {
|
||||||
Visual out = createRaw(obj);
|
Visual out = createRaw(obj);
|
||||||
|
|
||||||
if (out != null) {
|
if (out != null) {
|
||||||
setup(out);
|
setup(out, partialTick);
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
@ -110,11 +110,11 @@ public abstract class Storage<T> {
|
||||||
visuals.clear();
|
visuals.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create(T obj) {
|
private void create(T obj, float partialTick) {
|
||||||
Visual visual = createRaw(obj);
|
Visual visual = createRaw(obj);
|
||||||
|
|
||||||
if (visual != null) {
|
if (visual != null) {
|
||||||
setup(visual);
|
setup(visual, partialTick);
|
||||||
visuals.put(obj, visual);
|
visuals.put(obj, visual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,8 @@ public abstract class Storage<T> {
|
||||||
return tickPlan.and(ForEachPlan.of(() -> tickableVisuals, TickableVisual::tick));
|
return tickPlan.and(ForEachPlan.of(() -> tickableVisuals, TickableVisual::tick));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(Visual visual) {
|
private void setup(Visual visual, float partialTick) {
|
||||||
visual.init();
|
visual.init(partialTick);
|
||||||
|
|
||||||
if (visual instanceof TickableVisual tickable) {
|
if (visual instanceof TickableVisual tickable) {
|
||||||
tickableVisuals.add(tickable);
|
tickableVisuals.add(tickable);
|
||||||
|
|
|
@ -13,11 +13,11 @@ public record Transaction<T>(T obj, Action action) {
|
||||||
return new Transaction<>(obj, Action.UPDATE);
|
return new Transaction<>(obj, Action.UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(Storage<T> storage) {
|
public void apply(Storage<T> storage, float partialTick) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ADD -> storage.add(obj);
|
case ADD -> storage.add(obj, partialTick);
|
||||||
case REMOVE -> storage.remove(obj);
|
case REMOVE -> storage.remove(obj);
|
||||||
case UPDATE -> storage.update(obj);
|
case UPDATE -> storage.update(obj, partialTick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ public class VisualUpdatePlan<C> implements SimplyComposedPlan<C> {
|
||||||
private final Supplier<List<Plan<C>>> initializer;
|
private final Supplier<List<Plan<C>>> initializer;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Plan<C> plan;
|
private Plan<C> plan;
|
||||||
|
private boolean initialized = false;
|
||||||
private boolean needsSimplify = true;
|
private boolean needsSimplify = true;
|
||||||
|
|
||||||
public VisualUpdatePlan(Supplier<List<Plan<C>>> initializer) {
|
public VisualUpdatePlan(Supplier<List<Plan<C>>> initializer) {
|
||||||
|
@ -32,13 +33,21 @@ public class VisualUpdatePlan<C> implements SimplyComposedPlan<C> {
|
||||||
} else {
|
} else {
|
||||||
this.plan = this.plan.and(plan);
|
this.plan = this.plan.and(plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
needsSimplify = true;
|
needsSimplify = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private Plan<C> updatePlans() {
|
private Plan<C> updatePlans() {
|
||||||
if (plan == null) {
|
if (!initialized) {
|
||||||
plan = new NestedPlan<>(initializer.get()).simplify();
|
Plan<C> mainPlan = new NestedPlan<>(initializer.get());
|
||||||
|
if (plan != null) {
|
||||||
|
plan = mainPlan.and(plan);
|
||||||
|
} else {
|
||||||
|
plan = mainPlan;
|
||||||
|
}
|
||||||
|
plan = plan.simplify();
|
||||||
|
initialized = true;
|
||||||
} else if (needsSimplify) {
|
} else if (needsSimplify) {
|
||||||
plan = plan.simplify();
|
plan = plan.simplify();
|
||||||
}
|
}
|
||||||
|
@ -49,5 +58,6 @@ public class VisualUpdatePlan<C> implements SimplyComposedPlan<C> {
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
plan = null;
|
plan = null;
|
||||||
|
initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package com.jozufozu.flywheel.lib.box;
|
package com.jozufozu.flywheel.lib.box;
|
||||||
|
|
||||||
import static com.jozufozu.flywheel.lib.math.RenderMath.isPowerOf2;
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
|
||||||
public interface ImmutableBox {
|
public interface Box {
|
||||||
int getMinX();
|
int getMinX();
|
||||||
|
|
||||||
int getMinY();
|
int getMinY();
|
||||||
|
@ -33,16 +32,16 @@ public interface ImmutableBox {
|
||||||
return sizeX() * sizeY() * sizeZ();
|
return sizeX() * sizeY() * sizeZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean empty() {
|
default boolean isEmpty() {
|
||||||
// if any dimension has side length 0 this box contains no volume
|
// if any dimension has side length 0 this box contains no volume
|
||||||
return getMinX() == getMaxX() || getMinY() == getMaxY() || getMinZ() == getMaxZ();
|
return getMinX() == getMaxX() || getMinY() == getMaxY() || getMinZ() == getMaxZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean sameAs(ImmutableBox other) {
|
default boolean sameAs(Box other) {
|
||||||
return getMinX() == other.getMinX() && getMinY() == other.getMinY() && getMinZ() == other.getMinZ() && getMaxX() == other.getMaxX() && getMaxY() == other.getMaxY() && getMaxZ() == other.getMaxZ();
|
return getMinX() == other.getMinX() && getMinY() == other.getMinY() && getMinZ() == other.getMinZ() && getMaxX() == other.getMaxX() && getMaxY() == other.getMaxY() && getMaxZ() == other.getMaxZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean sameAs(ImmutableBox other, int margin) {
|
default boolean sameAs(Box other, int margin) {
|
||||||
return getMinX() == other.getMinX() - margin &&
|
return getMinX() == other.getMinX() - margin &&
|
||||||
getMinY() == other.getMinY() - margin &&
|
getMinY() == other.getMinY() - margin &&
|
||||||
getMinZ() == other.getMinZ() - margin &&
|
getMinZ() == other.getMinZ() - margin &&
|
||||||
|
@ -60,33 +59,11 @@ public interface ImmutableBox {
|
||||||
&& getMaxZ() == Math.ceil(other.maxZ);
|
&& getMaxZ() == Math.ceil(other.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean hasPowerOf2Sides() {
|
default boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
||||||
// this is only true if all individual side lengths are powers of 2
|
return this.getMinX() < maxX && this.getMaxX() > minX && this.getMinY() < maxY && this.getMaxY() > minY && this.getMinZ() < maxZ && this.getMaxZ() > minZ;
|
||||||
return isPowerOf2(volume());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default MutableBox intersect(ImmutableBox other) {
|
default boolean intersects(Box other) {
|
||||||
int minX = Math.max(this.getMinX(), other.getMinX());
|
|
||||||
int minY = Math.max(this.getMinY(), other.getMinY());
|
|
||||||
int minZ = Math.max(this.getMinZ(), other.getMinZ());
|
|
||||||
int maxX = Math.min(this.getMaxX(), other.getMaxX());
|
|
||||||
int maxY = Math.min(this.getMaxY(), other.getMaxY());
|
|
||||||
int maxZ = Math.min(this.getMaxZ(), other.getMaxZ());
|
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
default ImmutableBox union(ImmutableBox other) {
|
|
||||||
int minX = Math.min(this.getMinX(), other.getMinX());
|
|
||||||
int minY = Math.min(this.getMinY(), other.getMinY());
|
|
||||||
int minZ = Math.min(this.getMinZ(), other.getMinZ());
|
|
||||||
int maxX = Math.max(this.getMaxX(), other.getMaxX());
|
|
||||||
int maxY = Math.max(this.getMaxY(), other.getMaxY());
|
|
||||||
int maxZ = Math.max(this.getMaxZ(), other.getMaxZ());
|
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean intersects(ImmutableBox other) {
|
|
||||||
return this.intersects(other.getMinX(), other.getMinY(), other.getMinZ(), other.getMaxX(), other.getMaxY(), other.getMaxZ());
|
return this.intersects(other.getMinX(), other.getMinY(), other.getMinZ(), other.getMaxX(), other.getMaxY(), other.getMaxZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +76,7 @@ public interface ImmutableBox {
|
||||||
&& z <= getMaxZ();
|
&& z <= getMaxZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean contains(ImmutableBox other) {
|
default boolean contains(Box other) {
|
||||||
return other.getMinX() >= this.getMinX()
|
return other.getMinX() >= this.getMinX()
|
||||||
&& other.getMaxX() <= this.getMaxX()
|
&& other.getMaxX() <= this.getMaxX()
|
||||||
&& other.getMinY() >= this.getMinY()
|
&& other.getMinY() >= this.getMinY()
|
||||||
|
@ -108,26 +85,48 @@ public interface ImmutableBox {
|
||||||
&& other.getMaxZ() <= this.getMaxZ();
|
&& other.getMaxZ() <= this.getMaxZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isContainedBy(MutableBox other) {
|
|
||||||
return other.contains(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
|
||||||
return this.getMinX() < maxX && this.getMaxX() > minX && this.getMinY() < maxY && this.getMaxY() > minY && this.getMinZ() < maxZ && this.getMaxZ() > minZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
default void forEachContained(CoordinateConsumer func) {
|
default void forEachContained(CoordinateConsumer func) {
|
||||||
if (empty()) return;
|
int minX = getMinX();
|
||||||
|
int minY = getMinY();
|
||||||
|
int minZ = getMinZ();
|
||||||
|
int maxX = getMaxX();
|
||||||
|
int maxY = getMaxY();
|
||||||
|
int maxZ = getMaxZ();
|
||||||
|
|
||||||
for (int x = getMinX(); x < getMaxX(); x++) {
|
for (int x = minX; x < maxX; x++) {
|
||||||
for (int y = getMinY(); y < getMaxY(); y++) {
|
for (int y = minY; y < maxY; y++) {
|
||||||
for (int z = getMinZ(); z < getMaxZ(); z++) {
|
for (int z = minZ; z < maxZ; z++) {
|
||||||
func.consume(x, y, z);
|
func.accept(x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean hasPowerOf2Sides() {
|
||||||
|
// this is only true if all individual side lengths are powers of 2
|
||||||
|
return Mth.isPowerOfTwo(volume());
|
||||||
|
}
|
||||||
|
|
||||||
|
default MutableBox union(Box other) {
|
||||||
|
int minX = Math.min(this.getMinX(), other.getMinX());
|
||||||
|
int minY = Math.min(this.getMinY(), other.getMinY());
|
||||||
|
int minZ = Math.min(this.getMinZ(), other.getMinZ());
|
||||||
|
int maxX = Math.max(this.getMaxX(), other.getMaxX());
|
||||||
|
int maxY = Math.max(this.getMaxY(), other.getMaxY());
|
||||||
|
int maxZ = Math.max(this.getMaxZ(), other.getMaxZ());
|
||||||
|
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
default MutableBox intersect(Box other) {
|
||||||
|
int minX = Math.max(this.getMinX(), other.getMinX());
|
||||||
|
int minY = Math.max(this.getMinY(), other.getMinY());
|
||||||
|
int minZ = Math.max(this.getMinZ(), other.getMinZ());
|
||||||
|
int maxX = Math.min(this.getMaxX(), other.getMaxX());
|
||||||
|
int maxY = Math.min(this.getMaxY(), other.getMaxY());
|
||||||
|
int maxZ = Math.min(this.getMaxZ(), other.getMaxZ());
|
||||||
|
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
default AABB toAABB() {
|
default AABB toAABB() {
|
||||||
return new AABB(getMinX(), getMinY(), getMinZ(), getMaxX(), getMaxY(), getMaxZ());
|
return new AABB(getMinX(), getMinY(), getMinZ(), getMaxX(), getMaxY(), getMaxZ());
|
||||||
}
|
}
|
||||||
|
@ -138,6 +137,6 @@ public interface ImmutableBox {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface CoordinateConsumer {
|
interface CoordinateConsumer {
|
||||||
void consume(int x, int y, int z);
|
void accept(int x, int y, int z);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,13 +10,13 @@ import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
|
||||||
public class MutableBox implements ImmutableBox {
|
public class MutableBox implements Box {
|
||||||
private int minX;
|
protected int minX;
|
||||||
private int minY;
|
protected int minY;
|
||||||
private int minZ;
|
protected int minZ;
|
||||||
private int maxX;
|
protected int maxX;
|
||||||
private int maxY;
|
protected int maxY;
|
||||||
private int maxZ;
|
protected int maxZ;
|
||||||
|
|
||||||
public MutableBox() {
|
public MutableBox() {
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,6 @@ public class MutableBox implements ImmutableBox {
|
||||||
this.maxZ = maxZ;
|
this.maxZ = maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MutableBox ofRadius(int radius) {
|
|
||||||
return new MutableBox(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MutableBox from(AABB aabb) {
|
public static MutableBox from(AABB aabb) {
|
||||||
int minX = (int) Math.floor(aabb.minX);
|
int minX = (int) Math.floor(aabb.minX);
|
||||||
int minY = (int) Math.floor(aabb.minY);
|
int minY = (int) Math.floor(aabb.minY);
|
||||||
|
@ -44,25 +40,23 @@ public class MutableBox implements ImmutableBox {
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MutableBox from(Vec3i pos) {
|
||||||
|
return new MutableBox(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static MutableBox from(SectionPos pos) {
|
public static MutableBox from(SectionPos pos) {
|
||||||
return new MutableBox(pos.minBlockX(), pos.minBlockY(), pos.minBlockZ(), pos.maxBlockX() + 1, pos.maxBlockY() + 1, pos.maxBlockZ() + 1);
|
return new MutableBox(pos.minBlockX(), pos.minBlockY(), pos.minBlockZ(), pos.maxBlockX() + 1, pos.maxBlockY() + 1, pos.maxBlockZ() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MutableBox from(BlockPos start, BlockPos end) {
|
public static MutableBox from(Vec3i start, Vec3i end) {
|
||||||
return new MutableBox(start.getX(), start.getY(), start.getZ(), end.getX() + 1, end.getY() + 1, end.getZ() + 1);
|
return new MutableBox(start.getX(), start.getY(), start.getZ(), end.getX() + 1, end.getY() + 1, end.getZ() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MutableBox from(BlockPos pos) {
|
public static MutableBox ofRadius(int radius) {
|
||||||
return new MutableBox(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
return new MutableBox(-radius, -radius, -radius, radius + 1, radius + 1, radius + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MutableBox from(int sectionX, int sectionZ) {
|
public static Box containingAll(Collection<BlockPos> positions) {
|
||||||
int startX = sectionX << 4;
|
|
||||||
int startZ = sectionZ << 4;
|
|
||||||
return new MutableBox(startX, 0, startZ, startX + 16, 256, startZ + 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ImmutableBox containingAll(Collection<BlockPos> positions) {
|
|
||||||
if (positions.isEmpty()) {
|
if (positions.isEmpty()) {
|
||||||
return new MutableBox();
|
return new MutableBox();
|
||||||
}
|
}
|
||||||
|
@ -83,169 +77,6 @@ public class MutableBox implements ImmutableBox {
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fixMinMax() {
|
|
||||||
int minX = Math.min(this.minX, this.maxX);
|
|
||||||
int minY = Math.min(this.minY, this.maxY);
|
|
||||||
int minZ = Math.min(this.minZ, this.maxZ);
|
|
||||||
int maxX = Math.max(this.minX, this.maxX);
|
|
||||||
int maxY = Math.max(this.minY, this.maxY);
|
|
||||||
int maxZ = Math.max(this.minZ, this.maxZ);
|
|
||||||
|
|
||||||
this.minX = minX;
|
|
||||||
this.minY = minY;
|
|
||||||
this.minZ = minZ;
|
|
||||||
this.maxX = maxX;
|
|
||||||
this.maxY = maxY;
|
|
||||||
this.maxZ = maxZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void translate(Vec3i by) {
|
|
||||||
translate(by.getX(), by.getY(), by.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void translate(int x, int y, int z) {
|
|
||||||
minX = minX + x;
|
|
||||||
maxX = maxX + x;
|
|
||||||
minY = minY + y;
|
|
||||||
maxY = maxY + y;
|
|
||||||
minZ = minZ + z;
|
|
||||||
maxZ = maxZ + z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mirrorAbout(Direction.Axis axis) {
|
|
||||||
Vec3i axisVec = Direction.get(Direction.AxisDirection.POSITIVE, axis)
|
|
||||||
.getNormal();
|
|
||||||
int flipX = axisVec.getX() - 1;
|
|
||||||
int flipY = axisVec.getY() - 1;
|
|
||||||
int flipZ = axisVec.getZ() - 1;
|
|
||||||
|
|
||||||
int maxX = this.maxX * flipX;
|
|
||||||
int maxY = this.maxY * flipY;
|
|
||||||
int maxZ = this.maxZ * flipZ;
|
|
||||||
this.maxX = this.minX * flipX;
|
|
||||||
this.maxY = this.minY * flipY;
|
|
||||||
this.maxZ = this.minZ * flipZ;
|
|
||||||
this.minX = maxX;
|
|
||||||
this.minY = maxY;
|
|
||||||
this.minZ = maxZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grow this bounding box to have power of 2 side length, scaling from the center.
|
|
||||||
*/
|
|
||||||
public void nextPowerOf2Centered() {
|
|
||||||
int sizeX = sizeX();
|
|
||||||
int sizeY = sizeY();
|
|
||||||
int sizeZ = sizeZ();
|
|
||||||
|
|
||||||
int newSizeX = RenderMath.nextPowerOf2(sizeX);
|
|
||||||
int newSizeY = RenderMath.nextPowerOf2(sizeY);
|
|
||||||
int newSizeZ = RenderMath.nextPowerOf2(sizeZ);
|
|
||||||
|
|
||||||
int diffX = newSizeX - sizeX;
|
|
||||||
int diffY = newSizeY - sizeY;
|
|
||||||
int diffZ = newSizeZ - sizeZ;
|
|
||||||
|
|
||||||
minX = minX - diffX / 2; // floor division for the minimums
|
|
||||||
minY = minY - diffY / 2;
|
|
||||||
minZ = minZ - diffZ / 2;
|
|
||||||
maxX = maxX + (diffX + 1) / 2; // ceiling divison for the maximums
|
|
||||||
maxY = maxY + (diffY + 1) / 2;
|
|
||||||
maxZ = maxZ + (diffZ + 1) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
|
|
||||||
*/
|
|
||||||
public void nextPowerOf2() {
|
|
||||||
int sizeX = RenderMath.nextPowerOf2(sizeX());
|
|
||||||
int sizeY = RenderMath.nextPowerOf2(sizeY());
|
|
||||||
int sizeZ = RenderMath.nextPowerOf2(sizeZ());
|
|
||||||
|
|
||||||
maxX = minX + sizeX;
|
|
||||||
maxY = minY + sizeY;
|
|
||||||
maxZ = minZ + sizeZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void grow(int s) {
|
|
||||||
this.grow(s, s, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void grow(int x, int y, int z) {
|
|
||||||
minX = minX - x;
|
|
||||||
minY = minY - y;
|
|
||||||
minZ = minZ - z;
|
|
||||||
maxX = maxX + x;
|
|
||||||
maxY = maxY + y;
|
|
||||||
maxZ = maxZ + z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void intersectAssign(ImmutableBox other) {
|
|
||||||
minX = Math.max(this.minX, other.getMinX());
|
|
||||||
minY = Math.max(this.minY, other.getMinY());
|
|
||||||
minZ = Math.max(this.minZ, other.getMinZ());
|
|
||||||
maxX = Math.min(this.maxX, other.getMaxX());
|
|
||||||
maxY = Math.min(this.maxY, other.getMaxY());
|
|
||||||
maxZ = Math.min(this.maxZ, other.getMaxZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unionAssign(ImmutableBox other) {
|
|
||||||
minX = Math.min(this.minX, other.getMinX());
|
|
||||||
minY = Math.min(this.minY, other.getMinY());
|
|
||||||
minZ = Math.min(this.minZ, other.getMinZ());
|
|
||||||
maxX = Math.max(this.maxX, other.getMaxX());
|
|
||||||
maxY = Math.max(this.maxY, other.getMaxY());
|
|
||||||
maxZ = Math.max(this.maxZ, other.getMaxZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unionAssign(AABB other) {
|
|
||||||
minX = Math.min(this.minX, (int) Math.floor(other.minX));
|
|
||||||
minY = Math.min(this.minY, (int) Math.floor(other.minY));
|
|
||||||
minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
|
|
||||||
maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
|
|
||||||
maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
|
|
||||||
maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assign(AABB other) {
|
|
||||||
minX = (int) Math.floor(other.minX);
|
|
||||||
minY = (int) Math.floor(other.minY);
|
|
||||||
minZ = (int) Math.floor(other.minZ);
|
|
||||||
maxX = (int) Math.ceil(other.maxX);
|
|
||||||
maxY = (int) Math.ceil(other.maxY);
|
|
||||||
maxZ = (int) Math.ceil(other.maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assign(ImmutableBox other) {
|
|
||||||
minX = other.getMinX();
|
|
||||||
minY = other.getMinY();
|
|
||||||
minZ = other.getMinZ();
|
|
||||||
maxX = other.getMaxX();
|
|
||||||
maxY = other.getMaxY();
|
|
||||||
maxZ = other.getMaxZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
ImmutableBox that = (ImmutableBox) o;
|
|
||||||
|
|
||||||
return this.sameAs(that);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = getMinX();
|
|
||||||
result = 31 * result + getMinY();
|
|
||||||
result = 31 * result + getMinZ();
|
|
||||||
result = 31 * result + getMaxX();
|
|
||||||
result = 31 * result + getMaxY();
|
|
||||||
result = 31 * result + getMaxZ();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMinX() {
|
public int getMinX() {
|
||||||
return minX;
|
return minX;
|
||||||
|
@ -276,14 +107,12 @@ public class MutableBox implements ImmutableBox {
|
||||||
return maxZ;
|
return maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMinX(int minX) {
|
public void setMinX(int minX) {
|
||||||
this.minX = minX;
|
this.minX = minX;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMinY(int minY) {
|
public void setMinY(int minY) {
|
||||||
this.minY = minY;
|
this.minY = minY;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMinZ(int minZ) {
|
public MutableBox setMinZ(int minZ) {
|
||||||
|
@ -291,142 +120,207 @@ public class MutableBox implements ImmutableBox {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMaxX(int maxX) {
|
public void setMaxX(int maxX) {
|
||||||
this.maxX = maxX;
|
this.maxX = maxX;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMaxY(int maxY) {
|
public void setMaxY(int maxY) {
|
||||||
this.maxY = maxY;
|
this.maxY = maxY;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMaxZ(int maxZ) {
|
public void setMaxZ(int maxZ) {
|
||||||
this.maxZ = maxZ;
|
this.maxZ = maxZ;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox assign(BlockPos start, BlockPos end) {
|
public void setMin(int x, int y, int z) {
|
||||||
|
minX = x;
|
||||||
|
minY = y;
|
||||||
|
minZ = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(int x, int y, int z) {
|
||||||
|
maxX = x;
|
||||||
|
maxY = y;
|
||||||
|
maxZ = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMin(Vec3i v) {
|
||||||
|
setMin(v.getX(), v.getY(), v.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(Vec3i v) {
|
||||||
|
setMax(v.getX(), v.getY(), v.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(Box other) {
|
||||||
|
minX = other.getMinX();
|
||||||
|
minY = other.getMinY();
|
||||||
|
minZ = other.getMinZ();
|
||||||
|
maxX = other.getMaxX();
|
||||||
|
maxY = other.getMaxY();
|
||||||
|
maxZ = other.getMaxZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(AABB other) {
|
||||||
|
minX = (int) Math.floor(other.minX);
|
||||||
|
minY = (int) Math.floor(other.minY);
|
||||||
|
minZ = (int) Math.floor(other.minZ);
|
||||||
|
maxX = (int) Math.ceil(other.maxX);
|
||||||
|
maxY = (int) Math.ceil(other.maxY);
|
||||||
|
maxZ = (int) Math.ceil(other.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assign(Vec3i start, Vec3i end) {
|
||||||
minX = start.getX();
|
minX = start.getX();
|
||||||
minY = start.getY();
|
minY = start.getY();
|
||||||
minZ = start.getZ();
|
minZ = start.getZ();
|
||||||
maxX = end.getX() + 1;
|
maxX = end.getX() + 1;
|
||||||
maxY = end.getY() + 1;
|
maxY = end.getY() + 1;
|
||||||
maxZ = end.getZ() + 1;
|
maxZ = end.getZ() + 1;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMax(Vec3i v) {
|
public void unionAssign(Box other) {
|
||||||
return setMax(v.getX(), v.getY(), v.getZ());
|
minX = Math.min(this.minX, other.getMinX());
|
||||||
|
minY = Math.min(this.minY, other.getMinY());
|
||||||
|
minZ = Math.min(this.minZ, other.getMinZ());
|
||||||
|
maxX = Math.max(this.maxX, other.getMaxX());
|
||||||
|
maxY = Math.max(this.maxY, other.getMaxY());
|
||||||
|
maxZ = Math.max(this.maxZ, other.getMaxZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMin(Vec3i v) {
|
public void unionAssign(AABB other) {
|
||||||
return setMin(v.getX(), v.getY(), v.getZ());
|
minX = Math.min(this.minX, (int) Math.floor(other.minX));
|
||||||
|
minY = Math.min(this.minY, (int) Math.floor(other.minY));
|
||||||
|
minZ = Math.min(this.minZ, (int) Math.floor(other.minZ));
|
||||||
|
maxX = Math.max(this.maxX, (int) Math.ceil(other.maxX));
|
||||||
|
maxY = Math.max(this.maxY, (int) Math.ceil(other.maxY));
|
||||||
|
maxZ = Math.max(this.maxZ, (int) Math.ceil(other.maxZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMax(int x, int y, int z) {
|
public void intersectAssign(Box other) {
|
||||||
maxX = x;
|
minX = Math.max(this.minX, other.getMinX());
|
||||||
maxY = y;
|
minY = Math.max(this.minY, other.getMinY());
|
||||||
maxZ = z;
|
minZ = Math.max(this.minZ, other.getMinZ());
|
||||||
return this;
|
maxX = Math.min(this.maxX, other.getMaxX());
|
||||||
|
maxY = Math.min(this.maxY, other.getMaxY());
|
||||||
|
maxZ = Math.min(this.maxZ, other.getMaxZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MutableBox setMin(int x, int y, int z) {
|
public void fixMinMax() {
|
||||||
minX = x;
|
int minX = Math.min(this.minX, this.maxX);
|
||||||
minY = y;
|
int minY = Math.min(this.minY, this.maxY);
|
||||||
minZ = z;
|
int minZ = Math.min(this.minZ, this.maxZ);
|
||||||
return this;
|
int maxX = Math.max(this.minX, this.maxX);
|
||||||
|
int maxY = Math.max(this.minY, this.maxY);
|
||||||
|
int maxZ = Math.max(this.minZ, this.maxZ);
|
||||||
|
|
||||||
|
this.minX = minX;
|
||||||
|
this.minY = minY;
|
||||||
|
this.minZ = minZ;
|
||||||
|
this.maxX = maxX;
|
||||||
|
this.maxY = maxY;
|
||||||
|
this.maxZ = maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void translate(int x, int y, int z) {
|
||||||
|
minX = minX + x;
|
||||||
|
maxX = maxX + x;
|
||||||
|
minY = minY + y;
|
||||||
|
maxY = maxY + y;
|
||||||
|
minZ = minZ + z;
|
||||||
|
maxZ = maxZ + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void translate(Vec3i by) {
|
||||||
|
translate(by.getX(), by.getY(), by.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void grow(int x, int y, int z) {
|
||||||
|
minX = minX - x;
|
||||||
|
minY = minY - y;
|
||||||
|
minZ = minZ - z;
|
||||||
|
maxX = maxX + x;
|
||||||
|
maxY = maxY + y;
|
||||||
|
maxZ = maxZ + z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void grow(int s) {
|
||||||
|
this.grow(s, s, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grow this box to have power of 2 side lengths, scaling from the minimum coords.
|
||||||
|
*/
|
||||||
|
public void nextPowerOf2() {
|
||||||
|
int sizeX = RenderMath.nextPowerOf2(sizeX());
|
||||||
|
int sizeY = RenderMath.nextPowerOf2(sizeY());
|
||||||
|
int sizeZ = RenderMath.nextPowerOf2(sizeZ());
|
||||||
|
|
||||||
|
maxX = minX + sizeX;
|
||||||
|
maxY = minY + sizeY;
|
||||||
|
maxZ = minZ + sizeZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grow this box to have power of 2 side length, scaling from the center.
|
||||||
|
*/
|
||||||
|
public void nextPowerOf2Centered() {
|
||||||
|
int sizeX = sizeX();
|
||||||
|
int sizeY = sizeY();
|
||||||
|
int sizeZ = sizeZ();
|
||||||
|
|
||||||
|
int newSizeX = RenderMath.nextPowerOf2(sizeX);
|
||||||
|
int newSizeY = RenderMath.nextPowerOf2(sizeY);
|
||||||
|
int newSizeZ = RenderMath.nextPowerOf2(sizeZ);
|
||||||
|
|
||||||
|
int diffX = newSizeX - sizeX;
|
||||||
|
int diffY = newSizeY - sizeY;
|
||||||
|
int diffZ = newSizeZ - sizeZ;
|
||||||
|
|
||||||
|
minX = minX - diffX / 2; // floor division for the minimums
|
||||||
|
minY = minY - diffY / 2;
|
||||||
|
minZ = minZ - diffZ / 2;
|
||||||
|
maxX = maxX + (diffX + 1) / 2; // ceiling divison for the maximums
|
||||||
|
maxY = maxY + (diffY + 1) / 2;
|
||||||
|
maxZ = maxZ + (diffZ + 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mirrorAbout(Direction.Axis axis) {
|
||||||
|
Vec3i axisVec = Direction.get(Direction.AxisDirection.POSITIVE, axis)
|
||||||
|
.getNormal();
|
||||||
|
int flipX = axisVec.getX() - 1;
|
||||||
|
int flipY = axisVec.getY() - 1;
|
||||||
|
int flipZ = axisVec.getZ() - 1;
|
||||||
|
|
||||||
|
int maxX = this.maxX * flipX;
|
||||||
|
int maxY = this.maxY * flipY;
|
||||||
|
int maxZ = this.maxZ * flipZ;
|
||||||
|
this.maxX = this.minX * flipX;
|
||||||
|
this.maxY = this.minY * flipY;
|
||||||
|
this.maxZ = this.minZ * flipZ;
|
||||||
|
this.minX = maxX;
|
||||||
|
this.minY = maxY;
|
||||||
|
this.minZ = maxZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int sizeX() {
|
public boolean equals(Object o) {
|
||||||
return maxX - minX;
|
if (this == o) return true;
|
||||||
|
if (o == null) return false;
|
||||||
|
if (!(o instanceof Box that)) return false;
|
||||||
|
|
||||||
|
return this.sameAs(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int sizeY() {
|
public int hashCode() {
|
||||||
return maxY - minY;
|
int result = minX;
|
||||||
}
|
result = 31 * result + minY;
|
||||||
|
result = 31 * result + minZ;
|
||||||
@Override
|
result = 31 * result + maxX;
|
||||||
public int sizeZ() {
|
result = 31 * result + maxY;
|
||||||
return maxZ - minZ;
|
result = 31 * result + maxZ;
|
||||||
}
|
return result;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean empty() {
|
|
||||||
// if any dimension has side length 0 this box contains no volume
|
|
||||||
return minX == maxX || minY == maxY || minZ == maxZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean sameAs(ImmutableBox other) {
|
|
||||||
return minX == other.getMinX() && minY == other.getMinY() && minZ == other.getMinZ() && maxX == other.getMaxX() && maxY == other.getMaxY() && maxZ == other.getMaxZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean sameAs(AABB other) {
|
|
||||||
return minX == Math.floor(other.minX)
|
|
||||||
&& minY == Math.floor(other.minY)
|
|
||||||
&& minZ == Math.floor(other.minZ)
|
|
||||||
&& maxX == Math.ceil(other.maxX)
|
|
||||||
&& maxY == Math.ceil(other.maxY)
|
|
||||||
&& maxZ == Math.ceil(other.maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MutableBox intersect(ImmutableBox other) {
|
|
||||||
int minX = Math.max(this.minX, other.getMinX());
|
|
||||||
int minY = Math.max(this.minY, other.getMinY());
|
|
||||||
int minZ = Math.max(this.minZ, other.getMinZ());
|
|
||||||
int maxX = Math.min(this.maxX, other.getMaxX());
|
|
||||||
int maxY = Math.min(this.maxY, other.getMaxY());
|
|
||||||
int maxZ = Math.min(this.maxZ, other.getMaxZ());
|
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImmutableBox union(ImmutableBox other) {
|
|
||||||
int minX = Math.min(this.minX, other.getMinX());
|
|
||||||
int minY = Math.min(this.minY, other.getMinY());
|
|
||||||
int minZ = Math.min(this.minZ, other.getMinZ());
|
|
||||||
int maxX = Math.max(this.maxX, other.getMaxX());
|
|
||||||
int maxY = Math.max(this.maxY, other.getMaxY());
|
|
||||||
int maxZ = Math.max(this.maxZ, other.getMaxZ());
|
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(ImmutableBox other) {
|
|
||||||
return other.getMinX() >= this.minX && other.getMaxX() <= this.maxX && other.getMinY() >= this.minY && other.getMaxY() <= this.maxY && other.getMinZ() >= this.minZ && other.getMaxZ() <= this.maxZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean intersects(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
|
||||||
return this.minX < maxX && this.maxX > minX && this.minY < maxY && this.maxY > minY && this.minZ < maxZ && this.maxZ > minZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forEachContained(CoordinateConsumer func) {
|
|
||||||
if (empty()) return;
|
|
||||||
|
|
||||||
for (int x = minX; x < maxX; x++) {
|
|
||||||
for (int y = minY; y < maxY; y++) {
|
|
||||||
for (int z = minZ; z < maxZ; z++) {
|
|
||||||
func.consume(x, y, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AABB toAABB() {
|
|
||||||
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MutableBox copy() {
|
|
||||||
return new MutableBox(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@ public final class Contexts {
|
||||||
public static final SimpleContext WORLD = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
|
public static final SimpleContext WORLD = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
|
||||||
public static final SimpleContext CRUMBLING = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
|
public static final SimpleContext CRUMBLING = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
|
||||||
|
|
||||||
|
private Contexts() {
|
||||||
|
}
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public static void init() {
|
public static void init() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@ public final class InstanceTypes {
|
||||||
public static final InstanceType<TransformedInstance> TRANSFORMED = InstanceType.REGISTRY.registerAndGet(new TransformedType());
|
public static final InstanceType<TransformedInstance> TRANSFORMED = InstanceType.REGISTRY.registerAndGet(new TransformedType());
|
||||||
public static final InstanceType<OrientedInstance> ORIENTED = InstanceType.REGISTRY.registerAndGet(new OrientedType());
|
public static final InstanceType<OrientedInstance> ORIENTED = InstanceType.REGISTRY.registerAndGet(new OrientedType());
|
||||||
|
|
||||||
|
private InstanceTypes() {
|
||||||
|
}
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public static void init() {
|
public static void init() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,17 @@ import com.mojang.math.Quaternion;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
||||||
public class TransformedInstance extends ColoredLitInstance implements Transform<TransformedInstance> {
|
public class TransformedInstance extends ColoredLitInstance implements Transform<TransformedInstance> {
|
||||||
private static final Matrix4f EMPTY_MATRIX_4f = new Matrix4f();
|
private static final Matrix4f ZERO_MATRIX_4f = new Matrix4f();
|
||||||
private static final Matrix3f EMPTY_MATRIX_3f = new Matrix3f();
|
private static final Matrix3f ZERO_MATRIX_3f = new Matrix3f();
|
||||||
|
|
||||||
public final Matrix4f model = new Matrix4f();
|
public final Matrix4f model = new Matrix4f();
|
||||||
public final Matrix3f normal = new Matrix3f();
|
public final Matrix3f normal = new Matrix3f();
|
||||||
|
|
||||||
|
{
|
||||||
|
model.setIdentity();
|
||||||
|
normal.setIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
public TransformedInstance(InstanceType<? extends TransformedInstance> type, InstanceHandle handle) {
|
public TransformedInstance(InstanceType<? extends TransformedInstance> type, InstanceHandle handle) {
|
||||||
super(type, handle);
|
super(type, handle);
|
||||||
}
|
}
|
||||||
|
@ -41,16 +46,16 @@ public class TransformedInstance extends ColoredLitInstance implements Transform
|
||||||
public TransformedInstance setEmptyTransform() {
|
public TransformedInstance setEmptyTransform() {
|
||||||
setChanged();
|
setChanged();
|
||||||
|
|
||||||
this.model.load(EMPTY_MATRIX_4f);
|
model.load(ZERO_MATRIX_4f);
|
||||||
this.normal.load(EMPTY_MATRIX_3f);
|
normal.load(ZERO_MATRIX_3f);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransformedInstance loadIdentity() {
|
public TransformedInstance loadIdentity() {
|
||||||
setChanged();
|
setChanged();
|
||||||
|
|
||||||
this.model.setIdentity();
|
model.setIdentity();
|
||||||
this.normal.setIdentity();
|
normal.setIdentity();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,13 +100,17 @@ public class TransformedInstance extends ColoredLitInstance implements Transform
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformedInstance mulPose(Matrix4f pose) {
|
public TransformedInstance mulPose(Matrix4f pose) {
|
||||||
this.model.multiply(pose);
|
setChanged();
|
||||||
|
|
||||||
|
model.multiply(pose);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransformedInstance mulNormal(Matrix3f normal) {
|
public TransformedInstance mulNormal(Matrix3f normal) {
|
||||||
this.normal.mul(normal);
|
setChanged();
|
||||||
|
|
||||||
|
normal.mul(normal);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.jozufozu.flywheel.gl.array.VertexAttribute;
|
||||||
import com.jozufozu.flywheel.glsl.generate.FnSignature;
|
import com.jozufozu.flywheel.glsl.generate.FnSignature;
|
||||||
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
|
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
|
||||||
|
|
||||||
public class CommonItems {
|
public final class CommonItems {
|
||||||
private static final String VEC2_TYPE = "vec2";
|
private static final String VEC2_TYPE = "vec2";
|
||||||
private static final String VEC3_TYPE = "vec3";
|
private static final String VEC3_TYPE = "vec3";
|
||||||
private static final String VEC4_TYPE = "vec4";
|
private static final String VEC4_TYPE = "vec4";
|
||||||
|
@ -128,4 +128,7 @@ public class CommonItems {
|
||||||
|
|
||||||
public static final MatInput MAT3 = new MatInput(3, 3, "mat3", "Mat3F", "unpackMat3F");
|
public static final MatInput MAT3 = new MatInput(3, 3, "mat3", "Mat3F", "unpackMat3F");
|
||||||
public static final MatInput MAT4 = new MatInput(4, 4, "mat4", "Mat4F", "unpackMat4F");
|
public static final MatInput MAT4 = new MatInput(4, 4, "mat4", "Mat4F", "unpackMat4F");
|
||||||
|
|
||||||
|
private CommonItems() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,15 @@ package com.jozufozu.flywheel.lib.light;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.Box;
|
||||||
|
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
|
||||||
public class DummyLightUpdater extends LightUpdater {
|
public final class DummyLightUpdater extends LightUpdater {
|
||||||
public static final DummyLightUpdater INSTANCE = new DummyLightUpdater();
|
public static final DummyLightUpdater INSTANCE = new DummyLightUpdater();
|
||||||
|
|
||||||
private DummyLightUpdater() {
|
private DummyLightUpdater() {
|
||||||
super(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +34,7 @@ public class DummyLightUpdater extends LightUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<ImmutableBox> getAllBoxes() {
|
public Stream<Box> getAllBoxes() {
|
||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.lwjgl.opengl.GL30;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.gl.GlTexture;
|
import com.jozufozu.flywheel.gl.GlTexture;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.Box;
|
||||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||||
|
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
@ -36,7 +36,7 @@ public class GPULightVolume extends LightVolume {
|
||||||
private final GlTextureUnit textureUnit = GlTextureUnit.T4;
|
private final GlTextureUnit textureUnit = GlTextureUnit.T4;
|
||||||
protected boolean bufferDirty;
|
protected boolean bufferDirty;
|
||||||
|
|
||||||
public GPULightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) {
|
public GPULightVolume(BlockAndTintGetter level, Box sampleVolume) {
|
||||||
super(level, sampleVolume);
|
super(level, sampleVolume);
|
||||||
this.sampleVolume.assign(sampleVolume);
|
this.sampleVolume.assign(sampleVolume);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class GPULightVolume extends LightVolume {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setBox(ImmutableBox box) {
|
protected void setBox(Box box) {
|
||||||
this.box.assign(box);
|
this.box.assign(box);
|
||||||
this.box.nextPowerOf2Centered();
|
this.box.nextPowerOf2Centered();
|
||||||
// called during super ctor
|
// called during super ctor
|
||||||
|
@ -110,7 +110,7 @@ public class GPULightVolume extends LightVolume {
|
||||||
glTexture.delete();
|
glTexture.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(ImmutableBox newSampleVolume) {
|
public void move(Box newSampleVolume) {
|
||||||
if (lightData == null) return;
|
if (lightData == null) return;
|
||||||
|
|
||||||
if (box.contains(newSampleVolume)) {
|
if (box.contains(newSampleVolume)) {
|
||||||
|
@ -122,7 +122,7 @@ public class GPULightVolume extends LightVolume {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableBox getVolume() {
|
public Box getVolume() {
|
||||||
return sampleVolume;
|
return sampleVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.jozufozu.flywheel.lib.light;
|
package com.jozufozu.flywheel.lib.light;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.Box;
|
||||||
|
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
@ -12,8 +12,7 @@ import net.minecraft.world.level.LightLayer;
|
||||||
* It is the responsibility of the implementor to keep a reference to the level an object is contained in.
|
* It is the responsibility of the implementor to keep a reference to the level an object is contained in.
|
||||||
*/
|
*/
|
||||||
public interface LightListener {
|
public interface LightListener {
|
||||||
|
Box getVolume();
|
||||||
ImmutableBox getVolume();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the status of the light listener.
|
* Check the status of the light listener.
|
||||||
|
@ -23,7 +22,7 @@ public interface LightListener {
|
||||||
boolean isInvalid();
|
boolean isInvalid();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a light updates in a chunk the implementor cares about.
|
* Called when light updates in a section the implementor cares about.
|
||||||
*/
|
*/
|
||||||
void onLightUpdate(LightLayer type, SectionPos pos);
|
void onLightUpdate(LightLayer type, SectionPos pos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package com.jozufozu.flywheel.lib.light;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marker interface for custom/fake levels to indicate that LightUpdater should bother interacting with it.<p>
|
|
||||||
*
|
|
||||||
* Implement this if your custom level has light updates at all. If so, be sure to call
|
|
||||||
* {@link com.jozufozu.flywheel.util.WorldAttached#invalidateWorld} when your level in unloaded.
|
|
||||||
*/
|
|
||||||
public interface LightUpdated extends LevelAccessor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@code true} if this level is passing light updates into LightUpdater.
|
|
||||||
*/
|
|
||||||
default boolean receivesLightUpdates() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean receivesLightUpdates(LevelAccessor level) {
|
|
||||||
// The client level is guaranteed to receive updates.
|
|
||||||
if (Minecraft.getInstance().level == level) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Custom/fake levels need to indicate that LightUpdater has meaning.
|
|
||||||
if (level instanceof LightUpdated c) {
|
|
||||||
return c.receivesLightUpdates();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.jozufozu.flywheel.lib.light;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker interface for custom/fake levels to indicate that LightUpdater should interact with it.<p>
|
||||||
|
*
|
||||||
|
* Implement this if your custom level has light updates at all. If so, be sure to call
|
||||||
|
* {@link com.jozufozu.flywheel.lib.util.LevelAttached#invalidateLevel} when your level is unloaded.
|
||||||
|
*/
|
||||||
|
public interface LightUpdatedLevel extends LevelAccessor {
|
||||||
|
/**
|
||||||
|
* @return {@code true} if this level is passing light updates into LightUpdater.
|
||||||
|
*/
|
||||||
|
default boolean receivesLightUpdates() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,107 +5,71 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
import com.jozufozu.flywheel.lib.box.Box;
|
||||||
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated.
|
* Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated.
|
||||||
*
|
*
|
||||||
* @apiNote Custom/fake levels (that are {@code != Minecraft.getInstance.level}) need to implement
|
* @apiNote Custom/fake levels (that are {@code != Minecraft.getInstance.level}) need to implement
|
||||||
* {@link LightUpdated} for LightUpdater to work with them.
|
* {@link LightUpdatedLevel} for LightUpdater to work with them.
|
||||||
*/
|
*/
|
||||||
public class LightUpdater {
|
public class LightUpdater {
|
||||||
private static final WorldAttached<LightUpdater> LEVELS = new WorldAttached<>(LightUpdater::new);
|
private static final LevelAttached<LightUpdater> UPDATERS = new LevelAttached<>(level -> new LightUpdater());
|
||||||
|
|
||||||
private final LevelAccessor level;
|
|
||||||
|
|
||||||
private final WeakContainmentMultiMap<LightListener> listenersBySection = new WeakContainmentMultiMap<>();
|
private final WeakContainmentMultiMap<LightListener> listenersBySection = new WeakContainmentMultiMap<>();
|
||||||
private final Set<TickingLightListener> tickingListeners = FlwUtil.createWeakHashSet();
|
private final Set<TickingLightListener> tickingListeners = FlwUtil.createWeakHashSet();
|
||||||
|
|
||||||
private final Queue<LightListener> queue = new ConcurrentLinkedQueue<>();
|
private final Queue<LightListener> additionQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
public static boolean supports(LevelAccessor level) {
|
||||||
|
// The client level is guaranteed to receive updates.
|
||||||
|
if (Minecraft.getInstance().level == level) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Custom/fake levels need to indicate that LightUpdater has meaning.
|
||||||
|
if (level instanceof LightUpdatedLevel c) {
|
||||||
|
return c.receivesLightUpdates();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static LightUpdater get(LevelAccessor level) {
|
public static LightUpdater get(LevelAccessor level) {
|
||||||
if (LightUpdated.receivesLightUpdates(level)) {
|
if (supports(level)) {
|
||||||
// The level is valid, add it to the map.
|
// The level is valid, so add it to the map.
|
||||||
return LEVELS.get(level);
|
return UPDATERS.get(level);
|
||||||
} else {
|
} else {
|
||||||
// Fake light updater for a fake level.
|
// Fake light updater for a fake level.
|
||||||
return DummyLightUpdater.INSTANCE;
|
return DummyLightUpdater.INSTANCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LightUpdater(LevelAccessor level) {
|
|
||||||
this.level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick() {
|
|
||||||
processQueue();
|
|
||||||
tickSerial();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickSerial() {
|
|
||||||
for (TickingLightListener tickingLightListener : tickingListeners) {
|
|
||||||
if (tickingLightListener.tickLightListener()) {
|
|
||||||
addListener(tickingLightListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener.
|
* Add a listener.
|
||||||
*
|
*
|
||||||
* @param listener The object that wants to receive light update notifications.
|
* @param listener The object that wants to receive light update notifications.
|
||||||
*/
|
*/
|
||||||
public void addListener(LightListener listener) {
|
public void addListener(LightListener listener) {
|
||||||
queue.add(listener);
|
additionQueue.add(listener);
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void processQueue() {
|
|
||||||
LightListener listener;
|
|
||||||
while ((listener = queue.poll()) != null) {
|
|
||||||
doAdd(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doAdd(LightListener listener) {
|
|
||||||
if (listener instanceof TickingLightListener) {
|
|
||||||
tickingListeners.add(((TickingLightListener) listener));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImmutableBox box = listener.getVolume();
|
|
||||||
|
|
||||||
LongSet sections = this.listenersBySection.getAndResetContainment(listener);
|
|
||||||
|
|
||||||
int minX = SectionPos.blockToSectionCoord(box.getMinX());
|
|
||||||
int minY = SectionPos.blockToSectionCoord(box.getMinY());
|
|
||||||
int minZ = SectionPos.blockToSectionCoord(box.getMinZ());
|
|
||||||
int maxX = SectionPos.blockToSectionCoord(box.getMaxX());
|
|
||||||
int maxY = SectionPos.blockToSectionCoord(box.getMaxY());
|
|
||||||
int maxZ = SectionPos.blockToSectionCoord(box.getMaxZ());
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++) {
|
|
||||||
for (int y = minY; y <= maxY; y++) {
|
|
||||||
for (int z = minZ; z <= maxZ; z++) {
|
|
||||||
long sectionPos = SectionPos.asLong(x, y, z);
|
|
||||||
this.listenersBySection.put(sectionPos, listener);
|
|
||||||
sections.add(sectionPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(LightListener listener) {
|
public void removeListener(LightListener listener) {
|
||||||
this.listenersBySection.remove(listener);
|
listenersBySection.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch light updates to all registered {@link LightListener}s.
|
* Dispatch light updates to all registered {@link LightListener}s.
|
||||||
|
*
|
||||||
* @param type The type of light that changed.
|
* @param type The type of light that changed.
|
||||||
* @param pos The section position where light changed.
|
* @param pos The section position where light changed.
|
||||||
*/
|
*/
|
||||||
|
@ -125,11 +89,63 @@ public class LightUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<ImmutableBox> getAllBoxes() {
|
public Stream<Box> getAllBoxes() {
|
||||||
return listenersBySection.stream().map(LightListener::getVolume);
|
return listenersBySection.stream().map(LightListener::getVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return listenersBySection.isEmpty();
|
return listenersBySection.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
||||||
|
if (event.phase == TickEvent.Phase.END && FlwUtil.isGameActive()) {
|
||||||
|
get(Minecraft.getInstance().level)
|
||||||
|
.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() {
|
||||||
|
processQueue();
|
||||||
|
|
||||||
|
for (TickingLightListener tickingListener : tickingListeners) {
|
||||||
|
if (tickingListener.tickLightListener()) {
|
||||||
|
addListener(tickingListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void processQueue() {
|
||||||
|
LightListener listener;
|
||||||
|
while ((listener = additionQueue.poll()) != null) {
|
||||||
|
doAdd(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doAdd(LightListener listener) {
|
||||||
|
if (listener instanceof TickingLightListener ticking) {
|
||||||
|
tickingListeners.add(ticking);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box box = listener.getVolume();
|
||||||
|
|
||||||
|
LongSet sections = listenersBySection.getAndResetContainment(listener);
|
||||||
|
|
||||||
|
int minX = SectionPos.blockToSectionCoord(box.getMinX());
|
||||||
|
int minY = SectionPos.blockToSectionCoord(box.getMinY());
|
||||||
|
int minZ = SectionPos.blockToSectionCoord(box.getMinZ());
|
||||||
|
int maxX = SectionPos.blockToSectionCoord(box.getMaxX());
|
||||||
|
int maxY = SectionPos.blockToSectionCoord(box.getMaxY());
|
||||||
|
int maxZ = SectionPos.blockToSectionCoord(box.getMaxZ());
|
||||||
|
|
||||||
|
for (int x = minX; x <= maxX; x++) {
|
||||||
|
for (int y = minY; y <= maxY; y++) {
|
||||||
|
for (int z = minZ; z <= maxZ; z++) {
|
||||||
|
long sectionPos = SectionPos.asLong(x, y, z);
|
||||||
|
listenersBySection.put(sectionPos, listener);
|
||||||
|
sections.add(sectionPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.lib.light;
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.Box;
|
||||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
|
||||||
|
@ -11,13 +11,13 @@ import net.minecraft.core.SectionPos;
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
|
||||||
public class LightVolume implements ImmutableBox, LightListener {
|
public class LightVolume implements Box, LightListener {
|
||||||
|
|
||||||
protected final BlockAndTintGetter level;
|
protected final BlockAndTintGetter level;
|
||||||
protected final MutableBox box = new MutableBox();
|
protected final MutableBox box = new MutableBox();
|
||||||
protected MemoryBlock lightData;
|
protected MemoryBlock lightData;
|
||||||
|
|
||||||
public LightVolume(BlockAndTintGetter level, ImmutableBox sampleVolume) {
|
public LightVolume(BlockAndTintGetter level, Box sampleVolume) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
this.setBox(sampleVolume);
|
this.setBox(sampleVolume);
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableBox getVolume() {
|
public Box getVolume() {
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
return lightData == null;
|
return lightData == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setBox(ImmutableBox box) {
|
protected void setBox(Box box) {
|
||||||
this.box.assign(box);
|
this.box.assign(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(ImmutableBox newSampleVolume) {
|
public void move(Box newSampleVolume) {
|
||||||
if (lightData == null) return;
|
if (lightData == null) return;
|
||||||
|
|
||||||
setBox(newSampleVolume);
|
setBox(newSampleVolume);
|
||||||
|
@ -112,7 +112,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
*
|
*
|
||||||
* @param worldVolume the region in the world to copy data from.
|
* @param worldVolume the region in the world to copy data from.
|
||||||
*/
|
*/
|
||||||
public void copyLight(ImmutableBox worldVolume) {
|
public void copyLight(Box worldVolume) {
|
||||||
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
int xShift = box.getMinX();
|
int xShift = box.getMinX();
|
||||||
|
@ -143,7 +143,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
*
|
*
|
||||||
* @param worldVolume the region in the world to copy data from.
|
* @param worldVolume the region in the world to copy data from.
|
||||||
*/
|
*/
|
||||||
public void copyBlock(ImmutableBox worldVolume) {
|
public void copyBlock(Box worldVolume) {
|
||||||
var pos = new BlockPos.MutableBlockPos();
|
var pos = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
int xShift = box.getMinX();
|
int xShift = box.getMinX();
|
||||||
|
@ -168,7 +168,7 @@ public class LightVolume implements ImmutableBox, LightListener {
|
||||||
*
|
*
|
||||||
* @param worldVolume the region in the world to copy data from.
|
* @param worldVolume the region in the world to copy data from.
|
||||||
*/
|
*/
|
||||||
public void copySky(ImmutableBox worldVolume) {
|
public void copySky(Box worldVolume) {
|
||||||
var pos = new BlockPos.MutableBlockPos();
|
var pos = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
int xShift = box.getMinX();
|
int xShift = box.getMinX();
|
||||||
|
|
|
@ -6,15 +6,14 @@ import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.function.LongConsumer;
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.lib.util.FlwUtil;
|
||||||
|
|
||||||
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;
|
||||||
import it.unimi.dsi.fastutil.longs.LongRBTreeSet;
|
import it.unimi.dsi.fastutil.longs.LongRBTreeSet;
|
||||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
|
|
||||||
public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
||||||
|
|
||||||
private final Long2ObjectMap<Set<T>> forward;
|
private final Long2ObjectMap<Set<T>> forward;
|
||||||
private final WeakHashMap<T, LongSet> reverse;
|
private final WeakHashMap<T, LongSet> reverse;
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import com.jozufozu.flywheel.api.material.MaterialVertexTransformer;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.lib.material.SimpleMaterial.GlStateShard;
|
import com.jozufozu.flywheel.lib.material.SimpleMaterial.GlStateShard;
|
||||||
import com.jozufozu.flywheel.lib.math.DiffuseLightCalculator;
|
import com.jozufozu.flywheel.lib.math.DiffuseLightCalculator;
|
||||||
|
import com.jozufozu.flywheel.lib.util.ResourceUtil;
|
||||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
import com.mojang.blaze3d.platform.GlStateManager;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
|
@ -134,6 +134,9 @@ public final class Materials {
|
||||||
.batchingRenderType(RenderType.entitySolid(MINECART_LOCATION))
|
.batchingRenderType(RenderType.entitySolid(MINECART_LOCATION))
|
||||||
.register();
|
.register();
|
||||||
|
|
||||||
|
private Materials() {
|
||||||
|
}
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public static void init() {
|
public static void init() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class SimpleMaterial implements Material {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RenderType getBatchingRenderType() {
|
public RenderType getFallbackRenderType() {
|
||||||
return batchingRenderType;
|
return batchingRenderType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,19 +36,6 @@ public final class RenderMath {
|
||||||
return (h == a) ? h : (h << 1);
|
return (h == a) ? h : (h << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPowerOf2(int n) {
|
|
||||||
int b = n & (n - 1);
|
|
||||||
return b == 0 && n != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double lengthSqr(double x, double y, double z) {
|
|
||||||
return x * x + y * y + z * z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double length(double x, double y, double z) {
|
|
||||||
return Math.sqrt(lengthSqr(x, y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float diffuseLight(float x, float y, float z, boolean shaded) {
|
public static float diffuseLight(float x, float y, float z, boolean shaded) {
|
||||||
if (!shaded) {
|
if (!shaded) {
|
||||||
return 1f;
|
return 1f;
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.jozufozu.flywheel.lib.memory;
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
class DebugMemoryBlockImpl extends MemoryBlockImpl {
|
class DebugMemoryBlockImpl extends MemoryBlockImpl {
|
||||||
final CleaningAction cleaningAction;
|
final CleaningAction cleaningAction;
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
package com.jozufozu.flywheel.lib.memory;
|
package com.jozufozu.flywheel.lib.memory;
|
||||||
|
|
||||||
import java.lang.ref.Cleaner;
|
import java.lang.ref.Cleaner;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
|
|
||||||
public final class FlwMemoryTracker {
|
public final class FlwMemoryTracker {
|
||||||
public static final boolean DEBUG_MEMORY_SAFETY = System.getProperty("flw.debugMemorySafety") != null;
|
public static final boolean DEBUG_MEMORY_SAFETY = System.getProperty("flw.debugMemorySafety") != null;
|
||||||
|
|
||||||
static final Cleaner CLEANER = Cleaner.create();
|
static final Cleaner CLEANER = Cleaner.create();
|
||||||
|
|
||||||
|
// TODO: Should these be volatile?
|
||||||
private static long cpuMemory = 0;
|
private static long cpuMemory = 0;
|
||||||
private static long gpuMemory = 0;
|
private static long gpuMemory = 0;
|
||||||
|
|
||||||
|
private FlwMemoryTracker() {
|
||||||
|
}
|
||||||
|
|
||||||
public static long malloc(long size) {
|
public static long malloc(long size) {
|
||||||
long ptr = MemoryUtil.nmemAlloc(size);
|
long ptr = MemoryUtil.nmemAlloc(size);
|
||||||
if (ptr == MemoryUtil.NULL) {
|
if (ptr == MemoryUtil.NULL) {
|
||||||
|
@ -23,18 +26,6 @@ public final class FlwMemoryTracker {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link MemoryBlock#malloc(long)} or {@link MemoryBlock#mallocTracked(long)} and
|
|
||||||
* {@link MemoryBlock#asBuffer()} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and it is
|
|
||||||
* short-lived.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static ByteBuffer mallocBuffer(int size) {
|
|
||||||
ByteBuffer buffer = MemoryUtil.memByteBuffer(malloc(size), size);
|
|
||||||
_allocCPUMemory(buffer.capacity());
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long calloc(long num, long size) {
|
public static long calloc(long num, long size) {
|
||||||
long ptr = MemoryUtil.nmemCalloc(num, size);
|
long ptr = MemoryUtil.nmemCalloc(num, size);
|
||||||
if (ptr == MemoryUtil.NULL) {
|
if (ptr == MemoryUtil.NULL) {
|
||||||
|
@ -43,18 +34,6 @@ public final class FlwMemoryTracker {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link MemoryBlock#calloc(long, long)} or {@link MemoryBlock#callocTracked(long, long)} and
|
|
||||||
* {@link MemoryBlock#asBuffer()} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and it is
|
|
||||||
* short-lived.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static ByteBuffer callocBuffer(int num, int size) {
|
|
||||||
ByteBuffer buffer = MemoryUtil.memByteBuffer(calloc(num, size), num * size);
|
|
||||||
_allocCPUMemory(buffer.capacity());
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long realloc(long ptr, long size) {
|
public static long realloc(long ptr, long size) {
|
||||||
ptr = MemoryUtil.nmemRealloc(ptr, size);
|
ptr = MemoryUtil.nmemRealloc(ptr, size);
|
||||||
if (ptr == MemoryUtil.NULL) {
|
if (ptr == MemoryUtil.NULL) {
|
||||||
|
@ -63,32 +42,10 @@ public final class FlwMemoryTracker {
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link MemoryBlock#realloc(long)} or {@link MemoryBlock#reallocTracked(long)} instead. This method
|
|
||||||
* should only be used if specifically a {@linkplain ByteBuffer} is needed and it is short-lived.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static ByteBuffer reallocBuffer(ByteBuffer buffer, int size) {
|
|
||||||
ByteBuffer newBuffer = MemoryUtil.memByteBuffer(realloc(MemoryUtil.memAddress(buffer), size), size);
|
|
||||||
_freeCPUMemory(buffer.capacity());
|
|
||||||
_allocCPUMemory(newBuffer.capacity());
|
|
||||||
return newBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void free(long ptr) {
|
public static void free(long ptr) {
|
||||||
MemoryUtil.nmemFree(ptr);
|
MemoryUtil.nmemFree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link MemoryBlock#free} instead. This method should only be used if specifically a {@linkplain ByteBuffer} is needed and
|
|
||||||
* it is short-lived.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static void freeBuffer(ByteBuffer buffer) {
|
|
||||||
free(MemoryUtil.memAddress(buffer));
|
|
||||||
_freeCPUMemory(buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void _allocCPUMemory(long size) {
|
public static void _allocCPUMemory(long size) {
|
||||||
cpuMemory += size;
|
cpuMemory += size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,9 @@ public final class ModelUtil {
|
||||||
*/
|
*/
|
||||||
public static final BlockRenderDispatcher VANILLA_RENDERER = createVanillaRenderer();
|
public static final BlockRenderDispatcher VANILLA_RENDERER = createVanillaRenderer();
|
||||||
|
|
||||||
|
private ModelUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
private static BlockRenderDispatcher createVanillaRenderer() {
|
private static BlockRenderDispatcher createVanillaRenderer() {
|
||||||
BlockRenderDispatcher defaultDispatcher = Minecraft.getInstance().getBlockRenderer();
|
BlockRenderDispatcher defaultDispatcher = Minecraft.getInstance().getBlockRenderer();
|
||||||
BlockRenderDispatcher dispatcher = new BlockRenderDispatcher(null, null, null);
|
BlockRenderDispatcher dispatcher = new BlockRenderDispatcher(null, null, null);
|
||||||
|
@ -53,6 +56,10 @@ public final class ModelUtil {
|
||||||
return dispatcher;
|
return dispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isVanillaBufferEmpty(Pair<DrawState, ByteBuffer> pair) {
|
||||||
|
return pair.getFirst().vertexCount() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static MemoryBlock convertVanillaBuffer(Pair<DrawState, ByteBuffer> pair, VertexType vertexType) {
|
public static MemoryBlock convertVanillaBuffer(Pair<DrawState, ByteBuffer> pair, VertexType vertexType) {
|
||||||
DrawState drawState = pair.getFirst();
|
DrawState drawState = pair.getFirst();
|
||||||
int vertexCount = drawState.vertexCount();
|
int vertexCount = drawState.vertexCount();
|
||||||
|
@ -108,12 +115,12 @@ public final class ModelUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double coord(int i, int j) {
|
public double coord(int i, int dim) {
|
||||||
return switch (j) {
|
return switch (dim) {
|
||||||
case 0 -> vertexList.x(i);
|
case 0 -> vertexList.x(i);
|
||||||
case 1 -> vertexList.y(i);
|
case 1 -> vertexList.y(i);
|
||||||
case 2 -> vertexList.z(i);
|
case 2 -> vertexList.z(i);
|
||||||
default -> throw new IllegalArgumentException("Invalid dimension: " + j);
|
default -> throw new IllegalArgumentException("Invalid dimension: " + dim);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,12 +4,15 @@ import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.BakedModelBuilder;
|
import com.jozufozu.flywheel.lib.model.baked.BakedModelBuilder;
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.BlockModelBuilder;
|
import com.jozufozu.flywheel.lib.model.baked.BlockModelBuilder;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.PartialModel;
|
||||||
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.lib.util.Pair;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
@ -20,6 +23,9 @@ public final class Models {
|
||||||
private static final Map<PartialModel, Model> PARTIAL = new ConcurrentHashMap<>();
|
private static final Map<PartialModel, Model> PARTIAL = new ConcurrentHashMap<>();
|
||||||
private static final Map<Pair<PartialModel, Direction>, Model> PARTIAL_DIR = new ConcurrentHashMap<>();
|
private static final Map<Pair<PartialModel, Direction>, Model> PARTIAL_DIR = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Models() {
|
||||||
|
}
|
||||||
|
|
||||||
public static Model block(BlockState state) {
|
public static Model block(BlockState state) {
|
||||||
return BLOCK_STATE.computeIfAbsent(state, it -> new BlockModelBuilder(it).build());
|
return BLOCK_STATE.computeIfAbsent(state, it -> new BlockModelBuilder(it).build());
|
||||||
}
|
}
|
||||||
|
@ -32,7 +38,7 @@ public final class Models {
|
||||||
return PARTIAL_DIR.computeIfAbsent(Pair.of(partial, dir), it -> new BakedModelBuilder(it.first().get()).poseStack(createRotation(it.second())).build());
|
return PARTIAL_DIR.computeIfAbsent(Pair.of(partial, dir), it -> new BakedModelBuilder(it.first().get()).poseStack(createRotation(it.second())).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PoseStack createRotation(Direction facing) {
|
private static PoseStack createRotation(Direction facing) {
|
||||||
PoseStack stack = new PoseStack();
|
PoseStack stack = new PoseStack();
|
||||||
TransformStack.cast(stack)
|
TransformStack.cast(stack)
|
||||||
.centre()
|
.centre()
|
||||||
|
@ -41,6 +47,7 @@ public final class Models {
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||||
deleteAll(BLOCK_STATE.values());
|
deleteAll(BLOCK_STATE.values());
|
||||||
deleteAll(PARTIAL.values());
|
deleteAll(PARTIAL.values());
|
||||||
|
|
|
@ -1,41 +1,50 @@
|
||||||
package com.jozufozu.flywheel.lib.model;
|
package com.jozufozu.flywheel.lib.model;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.util.Lazy;
|
|
||||||
import com.jozufozu.flywheel.util.NonNullSupplier;
|
|
||||||
|
|
||||||
public class SimpleLazyModel implements Model {
|
public class SimpleLazyModel implements Model {
|
||||||
private final Lazy<Mesh> supplier;
|
private final Supplier<@NotNull Mesh> meshSupplier;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
|
|
||||||
public SimpleLazyModel(NonNullSupplier<Mesh> supplier, Material material) {
|
@Nullable
|
||||||
this.supplier = Lazy.of(supplier);
|
private Mesh mesh;
|
||||||
|
@Nullable
|
||||||
|
private Map<Material, Mesh> meshMap;
|
||||||
|
|
||||||
|
public SimpleLazyModel(Supplier<@NotNull Mesh> meshSupplier, Material material) {
|
||||||
|
this.meshSupplier = meshSupplier;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Material, Mesh> getMeshes() {
|
public Map<Material, Mesh> getMeshes() {
|
||||||
return ImmutableMap.of(material, supplier.get());
|
if (mesh == null) {
|
||||||
|
mesh = meshSupplier.get();
|
||||||
|
meshMap = ImmutableMap.of(material, mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meshMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete() {
|
public void delete() {
|
||||||
supplier.ifPresent(Mesh::delete);
|
if (mesh != null) {
|
||||||
}
|
mesh.delete();
|
||||||
|
}
|
||||||
public int getVertexCount() {
|
|
||||||
return supplier.map(Mesh::vertexCount)
|
|
||||||
.orElse(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SimpleLazyModel{" + supplier.map(Mesh::name)
|
String name = mesh != null ? mesh.name() : "Uninitialized";
|
||||||
.orElse("Uninitialized") + '}';
|
return "SimpleLazyModel{" + name + '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.lib.model.buffering;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
@ -8,18 +8,10 @@ import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||||
import com.jozufozu.flywheel.lib.model.TessellatedModel;
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ResultConsumer;
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.BufferFactory;
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||||
import com.jozufozu.flywheel.lib.virtualworld.VirtualEmptyBlockGetter;
|
|
||||||
import com.jozufozu.flywheel.lib.virtualworld.VirtualEmptyModelData;
|
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
|
||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
@ -29,8 +21,6 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraftforge.client.model.data.IModelData;
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
|
||||||
public class BakedModelBuilder {
|
public class BakedModelBuilder {
|
||||||
private static final int STARTING_CAPACITY = 64;
|
|
||||||
|
|
||||||
private final BakedModel bakedModel;
|
private final BakedModel bakedModel;
|
||||||
private boolean shadeSeparated = true;
|
private boolean shadeSeparated = true;
|
||||||
private BlockAndTintGetter renderWorld;
|
private BlockAndTintGetter renderWorld;
|
||||||
|
@ -73,19 +63,13 @@ public class BakedModelBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public TessellatedModel build() {
|
public TessellatedModel build() {
|
||||||
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
|
|
||||||
|
|
||||||
if (renderWorld == null) {
|
if (renderWorld == null) {
|
||||||
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
||||||
}
|
}
|
||||||
if (blockState == null) {
|
if (blockState == null) {
|
||||||
blockState = Blocks.AIR.defaultBlockState();
|
blockState = Blocks.AIR.defaultBlockState();
|
||||||
}
|
}
|
||||||
if (poseStack == null) {
|
|
||||||
poseStack = objects.identityPoseStack;
|
|
||||||
}
|
|
||||||
if (modelData == null) {
|
if (modelData == null) {
|
||||||
modelData = VirtualEmptyModelData.INSTANCE;
|
modelData = VirtualEmptyModelData.INSTANCE;
|
||||||
}
|
}
|
||||||
|
@ -96,35 +80,27 @@ public class BakedModelBuilder {
|
||||||
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
|
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
|
||||||
|
|
||||||
if (shadeSeparated) {
|
if (shadeSeparated) {
|
||||||
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
|
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
return buffer;
|
if (material != null) {
|
||||||
};
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||||
buffer.end();
|
}
|
||||||
Material material = materialFunc.apply(renderType, shaded);
|
|
||||||
if (material != null) {
|
|
||||||
MemoryBlock data = ModelUtil.convertVanillaBuffer(buffer.popNextBuffer(), VertexTypes.BLOCK);
|
|
||||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, data, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
|
ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||||
} else {
|
} else {
|
||||||
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
|
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||||
BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
Material material = materialFunc.apply(renderType, true);
|
||||||
return buffer;
|
if (material != null) {
|
||||||
};
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString()));
|
||||||
buffer.end();
|
}
|
||||||
Material material = materialFunc.apply(renderType, false);
|
|
||||||
if (material != null) {
|
|
||||||
MemoryBlock data = ModelUtil.convertVanillaBuffer(buffer.popNextBuffer(), VertexTypes.BLOCK);
|
|
||||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, data, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString()));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);
|
ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ResultConsumer;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
|
||||||
|
public class BlockModelBuilder {
|
||||||
|
private final BlockState state;
|
||||||
|
private boolean shadeSeparated = true;
|
||||||
|
private BlockAndTintGetter renderWorld;
|
||||||
|
private PoseStack poseStack;
|
||||||
|
private IModelData modelData;
|
||||||
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
|
public BlockModelBuilder(BlockState state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModelBuilder disableShadeSeparation() {
|
||||||
|
shadeSeparated = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
|
||||||
|
this.renderWorld = renderWorld;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModelBuilder poseStack(PoseStack poseStack) {
|
||||||
|
this.poseStack = poseStack;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModelBuilder modelData(IModelData modelData) {
|
||||||
|
this.modelData = modelData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
|
this.materialFunc = materialFunc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TessellatedModel build() {
|
||||||
|
if (renderWorld == null) {
|
||||||
|
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
||||||
|
}
|
||||||
|
if (modelData == null) {
|
||||||
|
modelData = VirtualEmptyModelData.INSTANCE;
|
||||||
|
}
|
||||||
|
if (materialFunc == null) {
|
||||||
|
materialFunc = ModelUtil::getMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
|
||||||
|
|
||||||
|
if (shadeSeparated) {
|
||||||
|
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
|
if (material != null) {
|
||||||
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||||
|
} else {
|
||||||
|
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||||
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
|
Material material = materialFunc.apply(renderType, true);
|
||||||
|
if (material != null) {
|
||||||
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "state=" + state.toString() + ",renderType=" + renderType.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder.DrawState;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||||
|
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||||
|
import net.minecraftforge.client.ForgeHooksClient;
|
||||||
|
import net.minecraftforge.client.model.data.EmptyModelData;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
|
||||||
|
public final class ModelBufferingUtil {
|
||||||
|
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
|
||||||
|
private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length;
|
||||||
|
|
||||||
|
private static final ThreadLocal<ModelBufferingObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ModelBufferingObjects::new);
|
||||||
|
|
||||||
|
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ResultConsumer resultConsumer) {
|
||||||
|
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||||
|
if (poseStack == null) {
|
||||||
|
poseStack = objects.identityPoseStack;
|
||||||
|
}
|
||||||
|
Random random = objects.random;
|
||||||
|
BufferBuilder[] buffers = objects.shadedBuffers;
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
|
||||||
|
if (!ItemBlockRenderTypes.canRenderInLayer(state, renderType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder buffer = buffers[layerIndex];
|
||||||
|
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(renderType);
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, buffer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData);
|
||||||
|
poseStack.popPose();
|
||||||
|
|
||||||
|
buffer.end();
|
||||||
|
Pair<DrawState, ByteBuffer> data = buffer.popNextBuffer();
|
||||||
|
resultConsumer.accept(renderType, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ShadeSeparatedResultConsumer resultConsumer) {
|
||||||
|
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||||
|
if (poseStack == null) {
|
||||||
|
poseStack = objects.identityPoseStack;
|
||||||
|
}
|
||||||
|
Random random = objects.random;
|
||||||
|
ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper;
|
||||||
|
BufferBuilder[] shadedBuffers = objects.shadedBuffers;
|
||||||
|
BufferBuilder[] unshadedBuffers = objects.unshadedBuffers;
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
|
||||||
|
if (!ItemBlockRenderTypes.canRenderInLayer(state, renderType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder shadedBuffer = shadedBuffers[layerIndex];
|
||||||
|
BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex];
|
||||||
|
shadeSeparatingWrapper.prepare(shadedBuffer, unshadedBuffer);
|
||||||
|
shadedBuffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
unshadedBuffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(renderType);
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, shadeSeparatingWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData);
|
||||||
|
poseStack.popPose();
|
||||||
|
|
||||||
|
shadedBuffer.end();
|
||||||
|
unshadedBuffer.end();
|
||||||
|
Pair<DrawState, ByteBuffer> shadedData = shadedBuffer.popNextBuffer();
|
||||||
|
Pair<DrawState, ByteBuffer> unshadedData = unshadedBuffer.popNextBuffer();
|
||||||
|
resultConsumer.accept(renderType, true, shadedData);
|
||||||
|
resultConsumer.accept(renderType, false, unshadedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(null);
|
||||||
|
|
||||||
|
shadeSeparatingWrapper.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ResultConsumer resultConsumer) {
|
||||||
|
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferSingle(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bufferBlockShadeSeparated(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ShadeSeparatedResultConsumer resultConsumer) {
|
||||||
|
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferSingleShadeSeparated(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bufferMultiBlock(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, IModelData> modelDataMap, ResultConsumer resultConsumer) {
|
||||||
|
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||||
|
if (poseStack == null) {
|
||||||
|
poseStack = objects.identityPoseStack;
|
||||||
|
}
|
||||||
|
Random random = objects.random;
|
||||||
|
|
||||||
|
BufferBuilder[] buffers = objects.shadedBuffers;
|
||||||
|
for (BufferBuilder buffer : buffers) {
|
||||||
|
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
||||||
|
ModelBlockRenderer.enableCaching();
|
||||||
|
|
||||||
|
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
|
||||||
|
BlockState state = blockInfo.state;
|
||||||
|
|
||||||
|
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BakedModel model = renderDispatcher.getBlockModel(state);
|
||||||
|
BlockPos pos = blockInfo.pos;
|
||||||
|
long seed = state.getSeed(pos);
|
||||||
|
IModelData modelData = modelDataMap.getOrDefault(pos, EmptyModelData.INSTANCE);
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
|
||||||
|
if (!ItemBlockRenderTypes.canRenderInLayer(state, renderType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferBuilder buffer = buffers[layerIndex];
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(renderType);
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, buffer, true, random, seed, OverlayTexture.NO_OVERLAY, modelData);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(null);
|
||||||
|
ModelBlockRenderer.clearCache();
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
BufferBuilder buffer = buffers[layerIndex];
|
||||||
|
buffer.end();
|
||||||
|
Pair<DrawState, ByteBuffer> data = buffer.popNextBuffer();
|
||||||
|
resultConsumer.accept(renderType, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, IModelData> modelDataMap, ShadeSeparatedResultConsumer resultConsumer) {
|
||||||
|
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||||
|
if (poseStack == null) {
|
||||||
|
poseStack = objects.identityPoseStack;
|
||||||
|
}
|
||||||
|
Random random = objects.random;
|
||||||
|
ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper;
|
||||||
|
|
||||||
|
BufferBuilder[] shadedBuffers = objects.shadedBuffers;
|
||||||
|
BufferBuilder[] unshadedBuffers = objects.unshadedBuffers;
|
||||||
|
for (BufferBuilder buffer : shadedBuffers) {
|
||||||
|
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
}
|
||||||
|
for (BufferBuilder buffer : unshadedBuffers) {
|
||||||
|
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
||||||
|
ModelBlockRenderer.enableCaching();
|
||||||
|
|
||||||
|
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
|
||||||
|
BlockState state = blockInfo.state;
|
||||||
|
|
||||||
|
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BakedModel model = renderDispatcher.getBlockModel(state);
|
||||||
|
BlockPos pos = blockInfo.pos;
|
||||||
|
long seed = state.getSeed(pos);
|
||||||
|
IModelData modelData = modelDataMap.getOrDefault(pos, EmptyModelData.INSTANCE);
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
|
||||||
|
if (!ItemBlockRenderTypes.canRenderInLayer(state, renderType)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
shadeSeparatingWrapper.prepare(shadedBuffers[layerIndex], unshadedBuffers[layerIndex]);
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(renderType);
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||||
|
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, shadeSeparatingWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgeHooksClient.setRenderType(null);
|
||||||
|
ModelBlockRenderer.clearCache();
|
||||||
|
|
||||||
|
shadeSeparatingWrapper.clear();
|
||||||
|
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||||
|
BufferBuilder shadedBuffer = shadedBuffers[layerIndex];
|
||||||
|
BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex];
|
||||||
|
shadedBuffer.end();
|
||||||
|
unshadedBuffer.end();
|
||||||
|
Pair<DrawState, ByteBuffer> shadedData = shadedBuffer.popNextBuffer();
|
||||||
|
Pair<DrawState, ByteBuffer> unshadedData = unshadedBuffer.popNextBuffer();
|
||||||
|
resultConsumer.accept(renderType, true, shadedData);
|
||||||
|
resultConsumer.accept(renderType, false, unshadedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ResultConsumer {
|
||||||
|
void accept(RenderType renderType, Pair<DrawState, ByteBuffer> data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ShadeSeparatedResultConsumer {
|
||||||
|
void accept(RenderType renderType, boolean shaded, Pair<DrawState, ByteBuffer> data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModelBufferingObjects {
|
||||||
|
public final PoseStack identityPoseStack = new PoseStack();
|
||||||
|
public final Random random = new Random();
|
||||||
|
|
||||||
|
public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer();
|
||||||
|
|
||||||
|
public final BufferBuilder[] shadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];
|
||||||
|
public final BufferBuilder[] unshadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];
|
||||||
|
|
||||||
|
{
|
||||||
|
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||||
|
int initialSize = CHUNK_LAYERS[layerIndex].bufferSize();
|
||||||
|
shadedBuffers[layerIndex] = new BufferBuilder(initialSize);
|
||||||
|
unshadedBuffers[layerIndex] = new BufferBuilder(initialSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ResultConsumer;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||||
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
|
||||||
|
public class MultiBlockModelBuilder {
|
||||||
|
private final Collection<StructureTemplate.StructureBlockInfo> blocks;
|
||||||
|
private boolean shadeSeparated = true;
|
||||||
|
private BlockAndTintGetter renderWorld;
|
||||||
|
private PoseStack poseStack;
|
||||||
|
private Map<BlockPos, IModelData> modelDataMap;
|
||||||
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder(Collection<StructureTemplate.StructureBlockInfo> blocks) {
|
||||||
|
this.blocks = blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder disableShadeSeparation() {
|
||||||
|
shadeSeparated = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
|
||||||
|
this.renderWorld = renderWorld;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||||
|
this.poseStack = poseStack;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder modelDataMap(Map<BlockPos, IModelData> modelDataMap) {
|
||||||
|
this.modelDataMap = modelDataMap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
|
this.materialFunc = materialFunc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TessellatedModel build() {
|
||||||
|
if (renderWorld == null) {
|
||||||
|
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
||||||
|
}
|
||||||
|
if (modelDataMap == null) {
|
||||||
|
modelDataMap = Collections.emptyMap();
|
||||||
|
}
|
||||||
|
if (materialFunc == null) {
|
||||||
|
materialFunc = ModelUtil::getMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
|
||||||
|
|
||||||
|
if (shadeSeparated) {
|
||||||
|
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
|
if (material != null) {
|
||||||
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ModelBufferingUtil.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||||
|
} else {
|
||||||
|
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||||
|
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||||
|
Material material = materialFunc.apply(renderType, true);
|
||||||
|
if (material != null) {
|
||||||
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||||
|
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "renderType=" + renderType.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ModelBufferingUtil.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.lib.model;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -24,7 +24,6 @@ import net.minecraftforge.client.model.ForgeModelBakery;
|
||||||
* Attempting to create a PartialModel after {@link ModelRegistryEvent} will cause an error.
|
* Attempting to create a PartialModel after {@link ModelRegistryEvent} will cause an error.
|
||||||
*/
|
*/
|
||||||
public class PartialModel {
|
public class PartialModel {
|
||||||
|
|
||||||
private static final List<PartialModel> ALL = new ArrayList<>();
|
private static final List<PartialModel> ALL = new ArrayList<>();
|
||||||
private static boolean tooLate = false;
|
private static boolean tooLate = false;
|
||||||
|
|
||||||
|
@ -68,5 +67,4 @@ public class PartialModel {
|
||||||
public BakedModel get() {
|
public BakedModel get() {
|
||||||
return bakedModel;
|
return bakedModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
|
||||||
|
class ShadeSeparatingVertexConsumer implements VertexConsumer {
|
||||||
|
private VertexConsumer shadedConsumer;
|
||||||
|
private VertexConsumer unshadedConsumer;
|
||||||
|
|
||||||
|
public void prepare(VertexConsumer shadedConsumer, VertexConsumer unshadedConsumer) {
|
||||||
|
this.shadedConsumer = shadedConsumer;
|
||||||
|
this.unshadedConsumer = unshadedConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
shadedConsumer = null;
|
||||||
|
unshadedConsumer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
|
||||||
|
if (quad.isShade()) {
|
||||||
|
shadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
|
||||||
|
} else {
|
||||||
|
unshadedConsumer.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
|
||||||
|
if (bakedQuad.isShade()) {
|
||||||
|
shadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
|
||||||
|
} else {
|
||||||
|
unshadedConsumer.putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer vertex(double x, double y, double z) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer color(int red, int green, int blue, int alpha) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer uv(float u, float v) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer overlayCoords(int u, int v) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer uv2(int u, int v) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer normal(float x, float y, float z) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endVertex() {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void defaultColor(int red, int green, int blue, int alpha) {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsetDefaultColor() {
|
||||||
|
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.lib.model;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.lib.virtualworld;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
@ -99,7 +99,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlockEmissionIncrease(BlockPos pos, int p_164456_) {
|
public void onBlockEmissionIncrease(BlockPos pos, int emissionLevel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -108,16 +108,16 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int runUpdates(int p_164449_, boolean p_164450_, boolean p_164451_) {
|
public int runUpdates(int pos, boolean isQueueEmpty, boolean updateBlockLight) {
|
||||||
return p_164449_;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSectionStatus(SectionPos pos, boolean p_75838_) {
|
public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableLightSources(ChunkPos pos, boolean p_164453_) {
|
public void enableLightSources(ChunkPos pos, boolean isQueueEmpty) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,15 +1,16 @@
|
||||||
package com.jozufozu.flywheel.lib.virtualworld;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import net.minecraftforge.client.model.data.IModelData;
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
import net.minecraftforge.client.model.data.ModelProperty;
|
import net.minecraftforge.client.model.data.ModelProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This model data instance is passed whenever a model is rendered without
|
* This model data instance is passed whenever a model is rendered without
|
||||||
* available in-world context. IBakedModel#getModelData can react accordingly
|
* available in-world context. BakedModel#getModelData can react accordingly
|
||||||
* and avoid looking for model data itself
|
* and avoid looking for model data itself.
|
||||||
**/
|
**/
|
||||||
public enum VirtualEmptyModelData implements IModelData {
|
public enum VirtualEmptyModelData implements IModelData {
|
||||||
|
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
public static boolean is(IModelData data) {
|
public static boolean is(IModelData data) {
|
||||||
|
@ -22,13 +23,14 @@ public enum VirtualEmptyModelData implements IModelData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public <T> T getData(ModelProperty<T> prop) {
|
public <T> T getData(ModelProperty<T> prop) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public <T> T setData(ModelProperty<T> prop, T data) {
|
public <T> T setData(ModelProperty<T> prop, T data) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,121 +0,0 @@
|
||||||
package com.jozufozu.flywheel.lib.model.buffering;
|
|
||||||
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|
||||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
|
||||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
|
||||||
import com.jozufozu.flywheel.lib.model.TessellatedModel;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.BufferFactory;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ShadeSeparatedBufferFactory;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
|
||||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
|
||||||
import com.jozufozu.flywheel.lib.virtualworld.VirtualEmptyBlockGetter;
|
|
||||||
import com.jozufozu.flywheel.lib.virtualworld.VirtualEmptyModelData;
|
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
|
||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraftforge.client.model.data.IModelData;
|
|
||||||
|
|
||||||
public class BlockModelBuilder {
|
|
||||||
private static final int STARTING_CAPACITY = 64;
|
|
||||||
|
|
||||||
private final BlockState state;
|
|
||||||
private boolean shadeSeparated = true;
|
|
||||||
private BlockAndTintGetter renderWorld;
|
|
||||||
private PoseStack poseStack;
|
|
||||||
private IModelData modelData;
|
|
||||||
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
|
||||||
|
|
||||||
public BlockModelBuilder(BlockState state) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder disableShadeSeparation() {
|
|
||||||
shadeSeparated = false;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
|
|
||||||
this.renderWorld = renderWorld;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder poseStack(PoseStack poseStack) {
|
|
||||||
this.poseStack = poseStack;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder modelData(IModelData modelData) {
|
|
||||||
this.modelData = modelData;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
|
||||||
this.materialFunc = materialFunc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public TessellatedModel build() {
|
|
||||||
ModelBufferingObjects objects = ModelBufferingObjects.THREAD_LOCAL.get();
|
|
||||||
|
|
||||||
if (renderWorld == null) {
|
|
||||||
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
|
|
||||||
}
|
|
||||||
if (poseStack == null) {
|
|
||||||
poseStack = objects.identityPoseStack;
|
|
||||||
}
|
|
||||||
if (modelData == null) {
|
|
||||||
modelData = VirtualEmptyModelData.INSTANCE;
|
|
||||||
}
|
|
||||||
if (materialFunc == null) {
|
|
||||||
materialFunc = ModelUtil::getMaterial;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImmutableMap.Builder<Material, Mesh> meshMapBuilder = ImmutableMap.builder();
|
|
||||||
|
|
||||||
if (shadeSeparated) {
|
|
||||||
ShadeSeparatedBufferFactory<BufferBuilder> bufferFactory = (renderType, shaded) -> {
|
|
||||||
BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
|
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
ShadeSeparatedResultConsumer<BufferBuilder> resultConsumer = (renderType, shaded, buffer) -> {
|
|
||||||
buffer.end();
|
|
||||||
Material material = materialFunc.apply(renderType, shaded);
|
|
||||||
if (material != null) {
|
|
||||||
MemoryBlock data = ModelUtil.convertVanillaBuffer(buffer.popNextBuffer(), VertexTypes.BLOCK);
|
|
||||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, data, "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.shadeSeparatingBufferWrapper, objects.random, modelData, resultConsumer);
|
|
||||||
} else {
|
|
||||||
BufferFactory<BufferBuilder> bufferFactory = (renderType) -> {
|
|
||||||
BufferBuilder buffer = new BufferBuilder(STARTING_CAPACITY);
|
|
||||||
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
|
||||||
return buffer;
|
|
||||||
};
|
|
||||||
ResultConsumer<BufferBuilder> resultConsumer = (renderType, buffer) -> {
|
|
||||||
buffer.end();
|
|
||||||
Material material = materialFunc.apply(renderType, false);
|
|
||||||
if (material != null) {
|
|
||||||
MemoryBlock data = ModelUtil.convertVanillaBuffer(buffer.popNextBuffer(), VertexTypes.BLOCK);
|
|
||||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, data, "state=" + state.toString() + ",renderType=" + renderType.toString()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, bufferFactory, objects.bufferWrapper, objects.random, modelData, resultConsumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
package com.jozufozu.flywheel.lib.model.buffering;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
||||||
|
|
||||||
public interface LazyDelegatingVertexConsumer<T extends VertexConsumer> extends VertexConsumer {
|
|
||||||
T getDelegate();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer vertex(double x, double y, double z) {
|
|
||||||
return getDelegate().vertex(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer color(int red, int green, int blue, int alpha) {
|
|
||||||
return getDelegate().color(red, green, blue, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer uv(float u, float v) {
|
|
||||||
return getDelegate().uv(u, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer overlayCoords(int u, int v) {
|
|
||||||
return getDelegate().overlayCoords(u, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer uv2(int u, int v) {
|
|
||||||
return getDelegate().uv2(u, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default VertexConsumer normal(float x, float y, float z) {
|
|
||||||
return getDelegate().normal(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void endVertex() {
|
|
||||||
getDelegate().endVertex();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void defaultColor(int red, int green, int blue, int alpha) {
|
|
||||||
getDelegate().defaultColor(red, green, blue, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void unsetDefaultColor() {
|
|
||||||
getDelegate().unsetDefaultColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
|
|
||||||
getDelegate().putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void putBulkData(PoseStack.Pose matrixEntry, BakedQuad bakedQuad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
|
|
||||||
getDelegate().putBulkData(matrixEntry, bakedQuad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package com.jozufozu.flywheel.lib.model.buffering;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.BufferWrapper;
|
|
||||||
import com.jozufozu.flywheel.lib.model.buffering.ModelBufferingUtil.ShadeSeparatingBufferWrapper;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
|
|
||||||
public class ModelBufferingObjects {
|
|
||||||
public static final ThreadLocal<ModelBufferingObjects> THREAD_LOCAL = ThreadLocal.withInitial(ModelBufferingObjects::new);
|
|
||||||
|
|
||||||
public final PoseStack identityPoseStack = new PoseStack();
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public final BufferWrapper bufferWrapper = new BufferWrapper<>();
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public final ShadeSeparatingBufferWrapper shadeSeparatingBufferWrapper = new ShadeSeparatingBufferWrapper<>();
|
|
||||||
public final Random random = new Random();
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue