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:
PepperCode1 2023-11-18 11:46:04 -08:00
parent fcd70cccd0
commit 66f11018fe
158 changed files with 1960 additions and 3505 deletions

View file

@ -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'

View file

@ -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;
} }

View file

@ -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();
} }

View file

@ -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);
} }
} }

View file

@ -17,7 +17,7 @@ public interface Material {
void clear(); void clear();
RenderType getBatchingRenderType(); RenderType getFallbackRenderType();
MaterialVertexTransformer getVertexTransformer(); MaterialVertexTransformer getVertexTransformer();
} }

View file

@ -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);
} }

View file

@ -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() {

View file

@ -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.

View file

@ -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) {
} }

View file

@ -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);
}

View file

@ -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;
} }
} }

View file

@ -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();
}

View file

@ -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;

View file

@ -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);
} }

View file

@ -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)));
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);
} }

View file

@ -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()

View file

@ -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());
} }
} }

View file

@ -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);

View file

@ -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() {

View file

@ -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());
} }
} }

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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));

View file

@ -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;

View file

@ -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() {
}
} }

View file

@ -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;

View file

@ -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"),

View file

@ -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) {

View file

@ -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();

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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());
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
} }
} }

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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.

View file

@ -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() {

View file

@ -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);
} }

View file

@ -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) {
} }

View file

@ -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());
}
}

View file

@ -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);
} }
} }

View file

@ -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);
} }

View file

@ -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");
}
}
}

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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) {

View file

@ -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);
} }
} }
} }

View file

@ -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);

View file

@ -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);
} }
} }
} }

View file

@ -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;
} }
} }

View file

@ -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);
} }
} }

View file

@ -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

View file

@ -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() {
} }

View file

@ -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() {
} }

View file

@ -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;
} }

View file

@ -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() {
}
} }

View file

@ -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();
} }

View file

@ -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;
} }

View file

@ -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);
} }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}
}
}
} }

View file

@ -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();

View file

@ -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;

View file

@ -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() {
} }

View file

@ -49,7 +49,7 @@ public class SimpleMaterial implements Material {
} }
@Override @Override
public RenderType getBatchingRenderType() { public RenderType getFallbackRenderType() {
return batchingRenderType; return batchingRenderType;
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;
} }

View file

@ -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);
}; };
} }
}); });

View file

@ -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());

View file

@ -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 + '}';
} }
} }

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}
}
}

View file

@ -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);
}
}

View file

@ -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;
} }
} }

View file

@ -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!");
}
}

View file

@ -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;

View file

@ -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

View file

@ -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;
} }
} }

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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