From ceb09d3dfc0b0f24e6a699f442b69d209a03f92c Mon Sep 17 00:00:00 2001
From: Jozufozu <jozsefaug@gmail.com>
Date: Wed, 22 Dec 2021 00:22:41 -0800
Subject: [PATCH] util and core cleanup

 - Consolidate/audit utility classes
 - Move more towards sane vertex types
---
 .../com/jozufozu/flywheel/api/Material.java   |   4 +-
 .../com/jozufozu/flywheel/backend/Loader.java |   4 +-
 .../backend/instancing/AbstractInstance.java  |   2 +-
 .../instancing/entity/EntityInstance.java     |   2 +-
 .../instancing/InstancedMaterial.java         |   4 +-
 .../instancing/InstancedMaterialGroup.java    |   4 +-
 .../instancing/tile/TileEntityInstance.java   |   4 +-
 .../flywheel/backend/model/BufferedModel.java |   1 -
 .../backend/source/ShaderSources.java         |   4 +-
 .../com/jozufozu/flywheel/core/Formats.java   |  28 +----
 .../flywheel/core/atlas/AtlasInfo.java        |  48 -------
 .../flywheel/core/atlas/SheetData.java        |  11 --
 .../flywheel/core/crumbling/AtlasInfo.java    |  48 +++++++
 .../core/crumbling/CrumblingGroup.java        |  15 +--
 .../core/crumbling/CrumblingProgram.java      |   1 -
 .../core/crumbling/CrumblingRenderer.java     |   4 +-
 .../core/{model => hardcoded}/ModelPart.java  |  17 ++-
 .../{model => hardcoded}/PartBuilder.java     |   2 +-
 .../core/materials/model/ModelType.java       |  10 +-
 .../core/materials/oriented/OrientedData.java |  10 --
 .../core/materials/oriented/OrientedType.java |   9 +-
 .../flywheel/core/model/BlockModel.java       |  10 +-
 .../flywheel/core/model/BlockType.java        |  71 -----------
 .../jozufozu/flywheel/core/model/Model.java   |   8 +-
 .../flywheel/core/model/ModelTransformer.java |   4 +-
 .../flywheel/core/model/ModelUtil.java        |  13 ++
 .../flywheel/core/model/Readable.java         | 119 ------------------
 .../flywheel/core/model/VecBufferWriter.java  |  71 -----------
 .../flywheel/core/model/WorldModel.java       |  13 +-
 .../core/shader/GameStateProgram.java         |   6 +-
 .../flywheel/core/vertex/BlockVertex.java     |  71 +++++++++++
 .../vertex/BlockVertexList.java}              |   7 +-
 .../vertex/BlockVertexListUnsafe.java}        |   7 +-
 ...alTexType.java => PosTexNormalVertex.java} |  13 +-
 ...java => PosTexNormalVertexListUnsafe.java} |   7 +-
 .../core/vertex/PosTexNormalWriter.java       |   6 +
 .../vertex/VertexList.java}                   |   4 +-
 .../flywheel/core/vertex/VertexType.java      |   3 +-
 .../flywheel/light/GPULightVolume.java        |   2 +
 .../flywheel/light/LightListener.java         |   3 +
 .../jozufozu/flywheel/light/LightUpdater.java |   2 +
 .../jozufozu/flywheel/light/LightVolume.java  |   3 +
 .../flywheel/mixin/RenderTexturesMixin.java   |   4 +-
 .../flywheel/mixin/atlas/AtlasDataMixin.java  |   8 +-
 .../jozufozu/flywheel/util/AngleHelper.java   |  42 -------
 .../flywheel/util/AnimationTickHolder.java    |  20 +--
 .../jozufozu/flywheel/util/AttribUtil.java    |   1 +
 .../java/com/jozufozu/flywheel/util/Pair.java |  37 +-----
 .../jozufozu/flywheel/util/RenderMath.java    |  38 ++++++
 .../jozufozu/flywheel/util/RenderUtil.java    |  61 ---------
 .../jozufozu/flywheel/util/StreamUtil.java    |  57 ---------
 .../jozufozu/flywheel/util/StringUtil.java    |  58 +++++++++
 .../jozufozu/flywheel/util/TextureBinder.java |  14 ---
 .../{RenderTextures.java => Textures.java}    |  18 ++-
 .../box}/CoordinateConsumer.java              |   2 +-
 .../{light => util/box}/GridAlignedBB.java    |  16 +--
 .../{light => util/box}/ImmutableBox.java     |   4 +-
 .../com/jozufozu/flywheel/util/vec/Vec3.java  |  80 ------------
 .../com/jozufozu/flywheel/util/vec/Vec4.java  |  66 ----------
 .../flywheel/vanilla/BellInstance.java        |   2 +-
 .../flywheel/vanilla/ChestInstance.java       |   2 +-
 .../flywheel/vanilla/MinecartInstance.java    |   2 +-
 .../flywheel/vanilla/ShulkerBoxInstance.java  |   2 +-
 63 files changed, 391 insertions(+), 818 deletions(-)
 delete mode 100644 src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java
 create mode 100644 src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java
 rename src/main/java/com/jozufozu/flywheel/core/{model => hardcoded}/ModelPart.java (66%)
 rename src/main/java/com/jozufozu/flywheel/core/{model => hardcoded}/PartBuilder.java (99%)
 delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/BlockType.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/Readable.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java
 create mode 100644 src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java
 rename src/main/java/com/jozufozu/flywheel/{util/BlockFormatReader.java => core/vertex/BlockVertexList.java} (90%)
 rename src/main/java/com/jozufozu/flywheel/{util/UnsafeBlockFormatReader.java => core/vertex/BlockVertexListUnsafe.java} (91%)
 rename src/main/java/com/jozufozu/flywheel/core/vertex/{PosNormalTexType.java => PosTexNormalVertex.java} (63%)
 rename src/main/java/com/jozufozu/flywheel/core/vertex/{PosNormalTexReader.java => PosTexNormalVertexListUnsafe.java} (88%)
 rename src/main/java/com/jozufozu/flywheel/{util/ModelReader.java => core/vertex/VertexList.java} (85%)
 delete mode 100644 src/main/java/com/jozufozu/flywheel/util/AngleHelper.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/util/RenderUtil.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/util/StreamUtil.java
 rename src/main/java/com/jozufozu/flywheel/util/{RenderTextures.java => Textures.java} (58%)
 rename src/main/java/com/jozufozu/flywheel/{light => util/box}/CoordinateConsumer.java (71%)
 rename src/main/java/com/jozufozu/flywheel/{light => util/box}/GridAlignedBB.java (96%)
 rename src/main/java/com/jozufozu/flywheel/{light => util/box}/ImmutableBox.java (97%)
 delete mode 100644 src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java
 delete mode 100644 src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java

diff --git a/src/main/java/com/jozufozu/flywheel/api/Material.java b/src/main/java/com/jozufozu/flywheel/api/Material.java
index edcc489d4..252c9fb33 100644
--- a/src/main/java/com/jozufozu/flywheel/api/Material.java
+++ b/src/main/java/com/jozufozu/flywheel/api/Material.java
@@ -5,8 +5,8 @@ import java.util.function.Supplier;
 import com.jozufozu.flywheel.core.PartialModel;
 import com.jozufozu.flywheel.core.model.BlockModel;
 import com.jozufozu.flywheel.core.model.Model;
+import com.jozufozu.flywheel.core.model.ModelUtil;
 import com.jozufozu.flywheel.util.Pair;
-import com.jozufozu.flywheel.util.RenderUtil;
 import com.mojang.blaze3d.vertex.PoseStack;
 
 import net.minecraft.core.Direction;
@@ -27,7 +27,7 @@ public interface Material<D extends InstanceData> {
 	}
 
 	default Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir) {
-		return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir));
+		return getModel(partial, referenceState, dir, ModelUtil.rotateToFace(dir));
 	}
 
 	default Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<PoseStack> modelTransform) {
diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java
index 3d9d6b369..92b675943 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java
@@ -13,7 +13,7 @@ import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
 import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
 import com.jozufozu.flywheel.event.GatherContextEvent;
 import com.jozufozu.flywheel.util.ResourceUtil;
-import com.jozufozu.flywheel.util.StreamUtil;
+import com.jozufozu.flywheel.util.StringUtil;
 import com.mojang.datafixers.util.Pair;
 import com.mojang.serialization.DataResult;
 import com.mojang.serialization.JsonOps;
@@ -106,7 +106,7 @@ public class Loader implements ResourceManagerReloadListener {
 			try {
 				Resource file = manager.getResource(location);
 
-				String s = StreamUtil.readToString(file.getInputStream());
+				String s = StringUtil.readToString(file.getInputStream());
 
 				ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json");
 
diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java
index 3bf0c14b2..a194a265c 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java
@@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.instance.IInstance;
 import com.jozufozu.flywheel.api.instance.ITickableInstance;
 import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
 import com.jozufozu.flywheel.core.materials.FlatLit;
-import com.jozufozu.flywheel.light.ImmutableBox;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
 import com.jozufozu.flywheel.light.LightListener;
 import com.jozufozu.flywheel.light.LightProvider;
 import com.jozufozu.flywheel.light.ListenerStatus;
diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java
index ac6443bf0..e1f6f3830 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/entity/EntityInstance.java
@@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance;
 import com.jozufozu.flywheel.api.instance.ITickableInstance;
 import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
 import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
-import com.jozufozu.flywheel.light.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
 import com.jozufozu.flywheel.light.LightListener;
 import com.jozufozu.flywheel.light.LightProvider;
 import com.jozufozu.flywheel.light.MovingListener;
diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java
index d60a18926..a72dcf415 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java
@@ -14,8 +14,8 @@ import com.jozufozu.flywheel.backend.RenderWork;
 import com.jozufozu.flywheel.backend.model.ImmediateAllocator;
 import com.jozufozu.flywheel.backend.model.ModelAllocator;
 import com.jozufozu.flywheel.backend.model.ModelPool;
+import com.jozufozu.flywheel.core.Formats;
 import com.jozufozu.flywheel.core.model.Model;
-import com.jozufozu.flywheel.core.vertex.PosNormalTexType;
 
 /**
  * A collection of Instancers that all have the same format.
@@ -33,7 +33,7 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
 		if (Backend.getInstance().compat.onAMDWindows()) {
 			allocator = ImmediateAllocator.INSTANCE;
 		} else {
-			allocator = new ModelPool(PosNormalTexType.INSTANCE, 64);
+			allocator = new ModelPool(Formats.POS_TEX_NORMAL, 64);
 		}
 		this.models = CacheBuilder.newBuilder()
 				.removalListener(notification -> {
diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java
index 184aa3be4..f0a432b2d 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java
@@ -10,7 +10,7 @@ import com.jozufozu.flywheel.api.struct.Instanced;
 import com.jozufozu.flywheel.api.struct.StructType;
 import com.jozufozu.flywheel.backend.model.ModelPool;
 import com.jozufozu.flywheel.core.shader.WorldProgram;
-import com.jozufozu.flywheel.util.TextureBinder;
+import com.jozufozu.flywheel.util.Textures;
 import com.mojang.math.Matrix4f;
 
 import net.minecraft.client.renderer.RenderType;
@@ -45,7 +45,7 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
 
 	public void render(Matrix4f viewProjection, double camX, double camY, double camZ) {
 		type.setupRenderState();
-		TextureBinder.bindActiveTextures();
+		Textures.bindActiveTextures();
 		renderAll(viewProjection, camX, camY, camZ);
 		type.clearRenderState();
 	}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java
index b064d214f..59e9b71f1 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/tile/TileEntityInstance.java
@@ -8,8 +8,8 @@ import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
 import com.jozufozu.flywheel.core.Materials;
 import com.jozufozu.flywheel.core.materials.model.ModelData;
 import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
-import com.jozufozu.flywheel.light.GridAlignedBB;
-import com.jozufozu.flywheel.light.ImmutableBox;
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
 
 import net.minecraft.core.BlockPos;
 import net.minecraft.world.level.block.entity.BlockEntity;
diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java
index 9ec3effcd..e73844a0f 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java
@@ -12,7 +12,6 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
 import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
 import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer;
 import com.jozufozu.flywheel.core.model.Model;
-import com.jozufozu.flywheel.core.model.VecBufferWriter;
 import com.jozufozu.flywheel.util.AttribUtil;
 
 public class BufferedModel implements IBufferedModel {
diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java
index 88db37083..00898a238 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java
@@ -8,7 +8,7 @@ import java.util.Map;
 
 import com.google.common.collect.Lists;
 import com.jozufozu.flywheel.util.ResourceUtil;
-import com.jozufozu.flywheel.util.StreamUtil;
+import com.jozufozu.flywheel.util.StringUtil;
 
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.server.packs.resources.Resource;
@@ -37,7 +37,7 @@ public class ShaderSources implements ISourceHolder {
 			try {
 				Resource resource = manager.getResource(location);
 
-				String source = StreamUtil.readToString(resource.getInputStream());
+				String source = StringUtil.readToString(resource.getInputStream());
 
 				ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
 
diff --git a/src/main/java/com/jozufozu/flywheel/core/Formats.java b/src/main/java/com/jozufozu/flywheel/core/Formats.java
index fcd25a452..21f45dd76 100644
--- a/src/main/java/com/jozufozu/flywheel/core/Formats.java
+++ b/src/main/java/com/jozufozu/flywheel/core/Formats.java
@@ -1,30 +1,10 @@
 package com.jozufozu.flywheel.core;
 
-import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
-import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes;
-import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
+import com.jozufozu.flywheel.core.vertex.BlockVertex;
+import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
 
 public class Formats {
-	public static final VertexFormat UNLIT_MODEL = VertexFormat.builder()
-			.addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL)
-			.build();
 
-	public static final VertexFormat COLORED_LIT_MODEL = VertexFormat.builder()
-			.addAttributes(CommonAttributes.VEC3,
-					CommonAttributes.NORMAL,
-					CommonAttributes.UV,
-					CommonAttributes.RGBA,
-					CommonAttributes.LIGHT)
-			.build();
-
-	public static final VertexFormat TRANSFORMED = litInstance().addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3)
-			.build();
-
-	public static final VertexFormat ORIENTED = litInstance().addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION)
-			.build();
-
-    public static VertexFormat.Builder litInstance() {
-		return VertexFormat.builder()
-				.addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA);
-	}
+	public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex();
+	public static final BlockVertex BLOCK = new BlockVertex();
 }
diff --git a/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java b/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java
deleted file mode 100644
index 6b915c3a9..000000000
--- a/src/main/java/com/jozufozu/flywheel/core/atlas/AtlasInfo.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.jozufozu.flywheel.core.atlas;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Nullable;
-
-import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor;
-
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.renderer.texture.AbstractTexture;
-import net.minecraft.client.renderer.texture.TextureAtlas;
-import net.minecraft.client.renderer.texture.TextureAtlasSprite;
-import net.minecraft.resources.ResourceLocation;
-
-public class AtlasInfo {
-
-	private static final Map<ResourceLocation, SheetData> sheetData = new HashMap<>();
-
-	public static TextureAtlas getAtlas(ResourceLocation name) {
-		AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name);
-
-		if (texture instanceof TextureAtlas)
-			return (TextureAtlas) texture;
-		else
-			return null;
-	}
-
-	@Nullable
-	public static SheetData getAtlasData(TextureAtlasSprite texture) {
-		return getAtlasData(texture.atlas());
-	}
-
-	@Nullable
-	public static SheetData getAtlasData(TextureAtlas atlas) {
-		return getAtlasData(atlas.location());
-	}
-
-	@Nullable
-	public static SheetData getAtlasData(@Nullable ResourceLocation loc) {
-		return sheetData.get(loc);
-	}
-
-	public static void setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) {
-		sheetData.put(atlas, new SheetData(accessor.getWidth(), accessor.getHeight()));
-	}
-
-}
diff --git a/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java b/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java
deleted file mode 100644
index e015ab5cf..000000000
--- a/src/main/java/com/jozufozu/flywheel/core/atlas/SheetData.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.jozufozu.flywheel.core.atlas;
-
-public class SheetData {
-	public final int width;
-	public final int height;
-
-	public SheetData(int width, int height) {
-		this.width = width;
-		this.height = height;
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java
new file mode 100644
index 000000000..a21ba81b8
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/AtlasInfo.java
@@ -0,0 +1,48 @@
+package com.jozufozu.flywheel.core.crumbling;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import com.jozufozu.flywheel.mixin.atlas.SheetDataAccessor;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.AbstractTexture;
+import net.minecraft.client.renderer.texture.TextureAtlas;
+import net.minecraft.resources.ResourceLocation;
+
+/**
+ * Track width and height of all created texture atlases.
+ *
+ * @see com.jozufozu.flywheel.mixin.atlas.AtlasDataMixin
+ */
+public class AtlasInfo {
+
+	private static final Map<ResourceLocation, SheetSize> sheetData = new HashMap<>();
+
+	@Nullable
+	public static TextureAtlas getAtlas(ResourceLocation name) {
+		AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(name);
+
+		if (texture instanceof TextureAtlas atlas)
+			return atlas;
+		else
+			return null;
+	}
+
+	@Nullable
+	public static SheetSize getSheetSize(@Nullable ResourceLocation loc) {
+		return sheetData.get(loc);
+	}
+
+	/**
+	 * FOR USE IN MIXIN
+	 */
+	public static void _setAtlasData(ResourceLocation atlas, SheetDataAccessor accessor) {
+		sheetData.put(atlas, new SheetSize(accessor.getWidth(), accessor.getHeight()));
+	}
+
+	public record SheetSize(int width, int height) {
+	}
+}
diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java
index c0eff27ae..19385b5a3 100644
--- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java
+++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java
@@ -2,10 +2,7 @@ package com.jozufozu.flywheel.core.crumbling;
 
 import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup;
 import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
-import com.jozufozu.flywheel.core.atlas.AtlasInfo;
-import com.jozufozu.flywheel.core.atlas.SheetData;
-import com.jozufozu.flywheel.util.RenderTextures;
-import com.jozufozu.flywheel.util.TextureBinder;
+import com.jozufozu.flywheel.util.Textures;
 import com.mojang.blaze3d.systems.RenderSystem;
 import com.mojang.math.Matrix4f;
 
@@ -37,7 +34,7 @@ public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMateria
 		RenderSystem.setShaderTexture(0, renderTex);
 		RenderSystem.setShaderTexture(4, breakingTex);
 
-		TextureBinder.bindActiveTextures();
+		Textures.bindActiveTextures();
 		renderAll(viewProjection, camX, camY, camZ);
 
 		CrumblingRenderer._currentLayer.clearRenderState();
@@ -45,11 +42,11 @@ public class CrumblingGroup<P extends CrumblingProgram> extends InstancedMateria
 
 	private void updateAtlasSize() {
 
-		SheetData atlasData = AtlasInfo.getAtlasData(RenderTextures.getShaderTexture(0));
+		AtlasInfo.SheetSize sheetSize = AtlasInfo.getSheetSize(Textures.getShaderTexture(0));
 
-		if (atlasData != null) {
-			width = atlasData.width;
-			height = atlasData.height;
+		if (sheetSize != null) {
+			width = sheetSize.width();
+			height = sheetSize.height();
 		} else {
 			width = height = 256;
 		}
diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java
index 8669ccef1..fddee3b91 100644
--- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java
+++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingProgram.java
@@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.crumbling;
 
 import static org.lwjgl.opengl.GL20.glUniform2f;
 
-import com.jozufozu.flywheel.core.atlas.AtlasInfo;
 import com.jozufozu.flywheel.core.shader.WorldProgram;
 
 import net.minecraft.client.renderer.texture.TextureAtlas;
diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java
index 57f75d84f..29efdc84d 100644
--- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java
+++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java
@@ -49,8 +49,8 @@ public class CrumblingRenderer {
 	static {
 		Pair<Lazy<State>, Lazy.KillSwitch<State>> state = Lazy.ofKillable(State::new, State::kill);
 
-		STATE = state.getFirst();
-		INVALIDATOR = state.getSecond();
+        STATE = state.first();
+		INVALIDATOR = state.second();
 	}
 
 	public static void renderBreaking(RenderLayerEvent event) {
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java
similarity index 66%
rename from src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java
rename to src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java
index 854779e69..6dadbd2f3 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/ModelPart.java
+++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java
@@ -1,18 +1,18 @@
-package com.jozufozu.flywheel.core.model;
+package com.jozufozu.flywheel.core.hardcoded;
 
-import java.nio.ByteBuffer;
 import java.util.List;
 
-import com.jozufozu.flywheel.core.vertex.PosNormalTexReader;
+import com.jozufozu.flywheel.core.model.Model;
+import com.jozufozu.flywheel.core.vertex.VertexList;
+import com.jozufozu.flywheel.core.vertex.PosTexNormalVertexListUnsafe;
 import com.jozufozu.flywheel.core.vertex.PosTexNormalWriter;
-import com.jozufozu.flywheel.util.ModelReader;
 import com.mojang.blaze3d.platform.MemoryTracker;
 
 public class ModelPart implements Model {
 
 	private final int vertices;
 	private final String name;
-	private final PosNormalTexReader reader;
+	private final PosTexNormalVertexListUnsafe reader;
 
 	public ModelPart(List<PartBuilder.CuboidBuilder> cuboids, String name) {
 		this.name = name;
@@ -25,13 +25,12 @@ public class ModelPart implements Model {
 			this.vertices = vertices;
 		}
 
-		ByteBuffer buffer = MemoryTracker.create(size());
-		PosTexNormalWriter writer = new PosTexNormalWriter(buffer);
+		PosTexNormalWriter writer = new PosTexNormalWriter(MemoryTracker.create(size()));
 		for (PartBuilder.CuboidBuilder cuboid : cuboids) {
 			cuboid.buffer(writer);
 		}
 
-		reader = new PosNormalTexReader(buffer, vertices);
+		reader = writer.intoReader();
 	}
 
 	public static PartBuilder builder(String name, int sizeU, int sizeV) {
@@ -49,7 +48,7 @@ public class ModelPart implements Model {
 	}
 
 	@Override
-	public ModelReader getReader() {
+	public VertexList getReader() {
 		return reader;
 	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java
similarity index 99%
rename from src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java
rename to src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java
index f96ba4cab..b5d7a503a 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/PartBuilder.java
+++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/PartBuilder.java
@@ -1,4 +1,4 @@
-package com.jozufozu.flywheel.core.model;
+package com.jozufozu.flywheel.core.hardcoded;
 
 import java.util.ArrayList;
 import java.util.EnumSet;
diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java
index 55bef78a8..b2f7090fd 100644
--- a/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java
+++ b/src/main/java/com/jozufozu/flywheel/core/materials/model/ModelType.java
@@ -3,9 +3,10 @@ package com.jozufozu.flywheel.core.materials.model;
 import com.jozufozu.flywheel.api.struct.Batched;
 import com.jozufozu.flywheel.api.struct.Instanced;
 import com.jozufozu.flywheel.api.struct.StructWriter;
+import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
+import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes;
 import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
 import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
-import com.jozufozu.flywheel.core.Formats;
 import com.jozufozu.flywheel.core.Programs;
 import com.jozufozu.flywheel.core.model.ModelTransformer;
 
@@ -13,6 +14,11 @@ import net.minecraft.resources.ResourceLocation;
 
 public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
 
+	public static final VertexFormat FORMAT = VertexFormat.builder()
+			.addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA)
+			.addAttributes(MatrixAttributes.MAT4, MatrixAttributes.MAT3)
+			.build();
+
 	@Override
 	public ModelData create() {
 		return new ModelData();
@@ -20,7 +26,7 @@ public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
 
 	@Override
 	public VertexFormat format() {
-		return Formats.TRANSFORMED;
+		return FORMAT;
 	}
 
 	@Override
diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java
index d819870f2..1971a3a05 100644
--- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java
+++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedData.java
@@ -1,7 +1,6 @@
 package com.jozufozu.flywheel.core.materials.oriented;
 
 import com.jozufozu.flywheel.core.materials.BasicData;
-import com.jozufozu.flywheel.util.vec.Vec3;
 import com.mojang.math.Quaternion;
 import com.mojang.math.Vector3f;
 
@@ -36,11 +35,6 @@ public class OrientedData extends BasicData {
 		return this;
 	}
 
-
-	public OrientedData nudge(Vec3 pos) {
-		return nudge(pos.getX(), pos.getY(), pos.getZ());
-	}
-
 	public OrientedData nudge(float x, float y, float z) {
 		this.posX += x;
 		this.posY += y;
@@ -57,10 +51,6 @@ public class OrientedData extends BasicData {
 		return setPosition((float) pos.x(), (float) pos.y(), (float) pos.z());
 	}
 
-	public OrientedData setPivot(Vec3 pos) {
-		return setPivot(pos.getX(), pos.getY(), pos.getZ());
-	}
-
 	public OrientedData setPivot(float x, float y, float z) {
 		this.pivotX = x;
 		this.pivotY = y;
diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java
index 2bd2195ae..4bd6d9c52 100644
--- a/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java
+++ b/src/main/java/com/jozufozu/flywheel/core/materials/oriented/OrientedType.java
@@ -3,9 +3,9 @@ package com.jozufozu.flywheel.core.materials.oriented;
 import com.jozufozu.flywheel.api.struct.Batched;
 import com.jozufozu.flywheel.api.struct.Instanced;
 import com.jozufozu.flywheel.api.struct.StructWriter;
+import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
 import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
 import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
-import com.jozufozu.flywheel.core.Formats;
 import com.jozufozu.flywheel.core.Programs;
 import com.jozufozu.flywheel.core.model.ModelTransformer;
 import com.mojang.math.Quaternion;
@@ -14,6 +14,11 @@ import net.minecraft.resources.ResourceLocation;
 
 public class OrientedType implements Instanced<OrientedData>, Batched<OrientedData> {
 
+	public static final VertexFormat FORMAT = VertexFormat.builder()
+			.addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA)
+			.addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION)
+			.build();
+
 	@Override
 	public OrientedData create() {
 		return new OrientedData();
@@ -21,7 +26,7 @@ public class OrientedType implements Instanced<OrientedData>, Batched<OrientedDa
 
 	@Override
 	public VertexFormat format() {
-		return Formats.ORIENTED;
+		return FORMAT;
 	}
 
 	@Override
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java
index c33013ed3..93a52f897 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java
+++ b/src/main/java/com/jozufozu/flywheel/core/model/BlockModel.java
@@ -1,7 +1,7 @@
 package com.jozufozu.flywheel.core.model;
 
-import com.jozufozu.flywheel.util.UnsafeBlockFormatReader;
-import com.jozufozu.flywheel.util.ModelReader;
+import com.jozufozu.flywheel.core.vertex.VertexList;
+import com.jozufozu.flywheel.core.vertex.BlockVertexListUnsafe;
 import com.mojang.blaze3d.vertex.PoseStack;
 
 import net.minecraft.client.Minecraft;
@@ -14,7 +14,7 @@ import net.minecraft.world.level.block.state.BlockState;
 public class BlockModel implements Model {
 	private static final PoseStack IDENTITY = new PoseStack();
 
-	private final ModelReader reader;
+	private final VertexList reader;
 
 	private final String name;
 
@@ -29,7 +29,7 @@ public class BlockModel implements Model {
 	}
 
 	public BlockModel(BakedModel model, BlockState referenceState, PoseStack ms) {
-		reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilder(model, referenceState, ms));
+		reader = new BlockVertexListUnsafe(ModelUtil.getBufferBuilder(model, referenceState, ms));
 		name = referenceState.toString();
 	}
 
@@ -49,7 +49,7 @@ public class BlockModel implements Model {
 	}
 
 	@Override
-	public ModelReader getReader() {
+	public VertexList getReader() {
 		return reader;
 	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java b/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java
deleted file mode 100644
index c766c89d3..000000000
--- a/src/main/java/com/jozufozu/flywheel/core/model/BlockType.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.jozufozu.flywheel.core.model;
-
-import java.nio.ByteBuffer;
-
-import org.lwjgl.system.MemoryUtil;
-
-import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
-import com.jozufozu.flywheel.core.Formats;
-import com.jozufozu.flywheel.core.vertex.VertexType;
-import com.jozufozu.flywheel.util.ModelReader;
-import com.jozufozu.flywheel.util.RenderMath;
-
-import net.minecraft.client.renderer.LightTexture;
-
-public class BlockType implements VertexType {
-
-	public static final BlockType INSTANCE = new BlockType();
-
-	@Override
-	public VertexFormat getFormat() {
-		return Formats.COLORED_LIT_MODEL;
-	}
-
-	@Override
-	public void copyInto(ByteBuffer buffer, ModelReader reader) {
-		int stride = getStride();
-		long addr = MemoryUtil.memAddress(buffer);
-
-		int vertexCount = reader.getVertexCount();
-		for (int i = 0; i < vertexCount; i++) {
-			float x = reader.getX(i);
-			float y = reader.getY(i);
-			float z = reader.getZ(i);
-
-			float xN = reader.getNX(i);
-			float yN = reader.getNY(i);
-			float zN = reader.getNZ(i);
-
-			float u = reader.getU(i);
-			float v = reader.getV(i);
-
-			byte r = reader.getR(i);
-			byte g = reader.getG(i);
-			byte b = reader.getB(i);
-			byte a = reader.getA(i);
-
-			int light = reader.getLight(i);
-
-			MemoryUtil.memPutFloat(addr, x);
-			MemoryUtil.memPutFloat(addr + 4, y);
-			MemoryUtil.memPutFloat(addr + 8, z);
-			MemoryUtil.memPutByte(addr + 12, RenderMath.nb(xN));
-			MemoryUtil.memPutByte(addr + 13, RenderMath.nb(yN));
-			MemoryUtil.memPutByte(addr + 14, RenderMath.nb(zN));
-			MemoryUtil.memPutFloat(addr + 15, u);
-			MemoryUtil.memPutFloat(addr + 19, v);
-			MemoryUtil.memPutByte(addr + 23, r);
-			MemoryUtil.memPutByte(addr + 24, g);
-			MemoryUtil.memPutByte(addr + 25, b);
-			MemoryUtil.memPutByte(addr + 26, a);
-
-			byte block = (byte) (LightTexture.block(light) << 4);
-			byte sky = (byte) (LightTexture.sky(light) << 4);
-
-			MemoryUtil.memPutByte(addr + 27, block);
-			MemoryUtil.memPutByte(addr + 28, sky);
-
-			addr += stride;
-		}
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Model.java b/src/main/java/com/jozufozu/flywheel/core/model/Model.java
index 631cb2362..6ad820da1 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/Model.java
+++ b/src/main/java/com/jozufozu/flywheel/core/model/Model.java
@@ -2,10 +2,10 @@ package com.jozufozu.flywheel.core.model;
 
 import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
 import com.jozufozu.flywheel.backend.model.ElementBuffer;
+import com.jozufozu.flywheel.core.Formats;
 import com.jozufozu.flywheel.core.QuadConverter;
-import com.jozufozu.flywheel.core.vertex.PosNormalTexType;
+import com.jozufozu.flywheel.core.vertex.VertexList;
 import com.jozufozu.flywheel.core.vertex.VertexType;
-import com.jozufozu.flywheel.util.ModelReader;
 
 /**
  * A model that can be rendered by flywheel.
@@ -34,7 +34,7 @@ public interface Model {
 	 */
 	String name();
 
-	ModelReader getReader();
+	VertexList getReader();
 
 	/**
 	 * @return The number of vertices the model has.
@@ -46,7 +46,7 @@ public interface Model {
 	}
 
 	default VertexType getType() {
-		return PosNormalTexType.INSTANCE;
+		return Formats.POS_TEX_NORMAL;
 	}
 
 	/**
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java
index d5e1dbcab..336dccae5 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java
+++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelTransformer.java
@@ -1,6 +1,6 @@
 package com.jozufozu.flywheel.core.model;
 
-import com.jozufozu.flywheel.util.ModelReader;
+import com.jozufozu.flywheel.core.vertex.VertexList;
 import com.jozufozu.flywheel.util.RenderMath;
 import com.jozufozu.flywheel.util.transform.Transform;
 import com.mojang.blaze3d.vertex.PoseStack;
@@ -15,7 +15,7 @@ import net.minecraftforge.client.model.pipeline.LightUtil;
 public class ModelTransformer {
 
 	private final Model model;
-	private final ModelReader reader;
+	private final VertexList reader;
 
 	public final Context context = new Context();
 
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
index 7e5dca986..365751f62 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
+++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java
@@ -3,10 +3,12 @@ package com.jozufozu.flywheel.core.model;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Random;
+import java.util.function.Supplier;
 
 import com.jozufozu.flywheel.util.Lazy;
 import com.jozufozu.flywheel.util.VirtualEmptyBlockGetter;
 import com.jozufozu.flywheel.util.VirtualEmptyModelData;
+import com.jozufozu.flywheel.util.transform.TransformStack;
 import com.mojang.blaze3d.vertex.BufferBuilder;
 import com.mojang.blaze3d.vertex.DefaultVertexFormat;
 import com.mojang.blaze3d.vertex.PoseStack;
@@ -91,4 +93,15 @@ public class ModelUtil {
 		builder.end();
 		return builder;
 	}
+
+	public static Supplier<PoseStack> rotateToFace(Direction facing) {
+		return () -> {
+			PoseStack stack = new PoseStack();
+			TransformStack.cast(stack)
+					.centre()
+					.rotateToFace(facing)
+					.unCentre();
+			return stack;
+		};
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/Readable.java b/src/main/java/com/jozufozu/flywheel/core/model/Readable.java
deleted file mode 100644
index 1108ce02d..000000000
--- a/src/main/java/com/jozufozu/flywheel/core/model/Readable.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package com.jozufozu.flywheel.core.model;
-
-import com.mojang.math.Vector3f;
-
-import net.minecraft.core.Direction;
-
-public class Readable {
-	public static class ModelBox {
-		private final TexturedQuad[] quads;
-		public final float posX1;
-		public final float posY1;
-		public final float posZ1;
-		public final float posX2;
-		public final float posY2;
-		public final float posZ2;
-
-		public ModelBox(int texOffU, int texOffV, float posX1, float posY1, float posZ1, float sizeX, float sizeY, float sizeZ, float growX, float growY, float growZ, boolean mirror, float texWidth, float texHeight) {
-			this.posX1 = posX1;
-			this.posY1 = posY1;
-			this.posZ1 = posZ1;
-			this.posX2 = posX1 + sizeX;
-			this.posY2 = posY1 + sizeY;
-			this.posZ2 = posZ1 + sizeZ;
-			this.quads = new TexturedQuad[6];
-			float posX2 = posX1 + sizeX;
-			float posY2 = posY1 + sizeY;
-			float posZ2 = posZ1 + sizeZ;
-			posX1 = posX1 - growX;
-			posY1 = posY1 - growY;
-			posZ1 = posZ1 - growZ;
-			posX2 = posX2 + growX;
-			posY2 = posY2 + growY;
-			posZ2 = posZ2 + growZ;
-			if (mirror) {
-				float tmp = posX2;
-				posX2 = posX1;
-				posX1 = tmp;
-			}
-
-			PositionTextureVertex lll = new PositionTextureVertex(posX1, posY1, posZ1, 0.0F, 0.0F);
-			PositionTextureVertex hll = new PositionTextureVertex(posX2, posY1, posZ1, 0.0F, 8.0F);
-			PositionTextureVertex hhl = new PositionTextureVertex(posX2, posY2, posZ1, 8.0F, 8.0F);
-			PositionTextureVertex lhl = new PositionTextureVertex(posX1, posY2, posZ1, 8.0F, 0.0F);
-			PositionTextureVertex llh = new PositionTextureVertex(posX1, posY1, posZ2, 0.0F, 0.0F);
-			PositionTextureVertex hlh = new PositionTextureVertex(posX2, posY1, posZ2, 0.0F, 8.0F);
-			PositionTextureVertex hhh = new PositionTextureVertex(posX2, posY2, posZ2, 8.0F, 8.0F);
-			PositionTextureVertex lhh = new PositionTextureVertex(posX1, posY2, posZ2, 8.0F, 0.0F);
-			float f4 = (float)texOffU;
-			float f5 = (float)texOffU + sizeZ;
-			float f6 = (float)texOffU + sizeZ + sizeX;
-			float f7 = (float)texOffU + sizeZ + sizeX + sizeX;
-			float f8 = (float)texOffU + sizeZ + sizeX + sizeZ;
-			float f9 = (float)texOffU + sizeZ + sizeX + sizeZ + sizeX;
-			float f10 = (float)texOffV;
-			float f11 = (float)texOffV + sizeZ;
-			float f12 = (float)texOffV + sizeZ + sizeY;
-			this.quads[2] = new TexturedQuad(new PositionTextureVertex[]{hlh, llh, lll, hll}, f5, f10, f6, f11, texWidth, texHeight, mirror, Direction.DOWN);
-			this.quads[3] = new TexturedQuad(new PositionTextureVertex[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, texWidth, texHeight, mirror, Direction.UP);
-			this.quads[1] = new TexturedQuad(new PositionTextureVertex[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, texWidth, texHeight, mirror, Direction.WEST);
-			this.quads[4] = new TexturedQuad(new PositionTextureVertex[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, texWidth, texHeight, mirror, Direction.NORTH);
-			this.quads[0] = new TexturedQuad(new PositionTextureVertex[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, texWidth, texHeight, mirror, Direction.EAST);
-			this.quads[5] = new TexturedQuad(new PositionTextureVertex[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, texWidth, texHeight, mirror, Direction.SOUTH);
-		}
-	}
-
-	public static class PositionTextureVertex {
-		public final float x;
-		public final float y;
-		public final float z;
-		public final float u;
-		public final float v;
-
-		public PositionTextureVertex(float x, float y, float z) {
-			this(x, y, z, 0, 0);
-		}
-
-		public PositionTextureVertex(float x, float y, float z, float u, float v) {
-			this.x = x;
-			this.y = y;
-			this.z = z;
-			this.u = u;
-			this.v = v;
-		}
-
-		public PositionTextureVertex setTexturePosition(float u, float v) {
-			return new PositionTextureVertex(x, y, z, u, v);
-		}
-	}
-
-	public static class TexturedQuad {
-		public final PositionTextureVertex[] vertices;
-		public final Vector3f normal;
-
-		public TexturedQuad(PositionTextureVertex[] vertices, float minU, float minV, float maxU, float maxV, float texWidth, float texHeight, boolean p_i225951_8_, Direction p_i225951_9_) {
-			this.vertices = vertices;
-			float w = 0.0F / texWidth;
-			float h = 0.0F / texHeight;
-			vertices[0] = vertices[0].setTexturePosition(maxU / texWidth - w, minV / texHeight + h);
-			vertices[1] = vertices[1].setTexturePosition(minU / texWidth + w, minV / texHeight + h);
-			vertices[2] = vertices[2].setTexturePosition(minU / texWidth + w, maxV / texHeight - h);
-			vertices[3] = vertices[3].setTexturePosition(maxU / texWidth - w, maxV / texHeight - h);
-			if (p_i225951_8_) {
-				int i = vertices.length;
-
-				for(int j = 0; j < i / 2; ++j) {
-					PositionTextureVertex modelrenderer$positiontexturevertex = vertices[j];
-					vertices[j] = vertices[i - 1 - j];
-					vertices[i - 1 - j] = modelrenderer$positiontexturevertex;
-				}
-			}
-
-			this.normal = p_i225951_9_.step();
-			if (p_i225951_8_) {
-				this.normal.mul(-1.0F, 1.0F, 1.0F);
-			}
-
-		}
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java b/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java
deleted file mode 100644
index c1ea4957c..000000000
--- a/src/main/java/com/jozufozu/flywheel/core/model/VecBufferWriter.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.jozufozu.flywheel.core.model;
-
-import static com.jozufozu.flywheel.util.RenderMath.nb;
-
-import java.nio.ByteBuffer;
-
-import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
-import com.mojang.blaze3d.vertex.VertexConsumer;
-
-public class VecBufferWriter implements VertexConsumer {
-
-	private final VecBuffer buffer;
-
-	public VecBufferWriter(ByteBuffer buffer) {
-		this.buffer = new VecBuffer(buffer);
-	}
-
-	public VecBufferWriter(VecBuffer buffer) {
-		this.buffer = buffer;
-	}
-
-	@Override
-	public VertexConsumer vertex(double v, double v1, double v2) {
-		buffer.putVec3((float) v, (float) v1, (float) v2);
-		return this;
-	}
-
-	@Override
-	public VertexConsumer color(int i, int i1, int i2, int i3) {
-		buffer.putColor(i, i1, i2, i3);
-		return this;
-	}
-
-	@Override
-	public VertexConsumer uv(float v, float v1) {
-		buffer.putVec2(v, v1);
-		return this;
-	}
-
-	@Override
-	public VertexConsumer overlayCoords(int i, int i1) {
-		return this;
-	}
-
-	@Override
-	public VertexConsumer uv2(int i, int i1) {
-		buffer.putVec2((byte) i, (byte) i1);
-		return this;
-	}
-
-	@Override
-	public VertexConsumer normal(float v, float v1, float v2) {
-		buffer.putVec3(nb(v), nb(v1), nb(v2));
-		return this;
-	}
-
-	@Override
-	public void endVertex() {
-
-	}
-
-	@Override
-	public void defaultColor(int i, int i1, int i2, int i3) {
-
-	}
-
-	@Override
-	public void unsetDefaultColor() {
-
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java
index 4d61f4c80..e390bdbc5 100644
--- a/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java
+++ b/src/main/java/com/jozufozu/flywheel/core/model/WorldModel.java
@@ -2,9 +2,10 @@ package com.jozufozu.flywheel.core.model;
 
 import java.util.Collection;
 
+import com.jozufozu.flywheel.core.Formats;
+import com.jozufozu.flywheel.core.vertex.VertexList;
 import com.jozufozu.flywheel.core.vertex.VertexType;
-import com.jozufozu.flywheel.util.UnsafeBlockFormatReader;
-import com.jozufozu.flywheel.util.ModelReader;
+import com.jozufozu.flywheel.core.vertex.BlockVertexListUnsafe;
 
 import net.minecraft.client.renderer.RenderType;
 import net.minecraft.world.level.BlockAndTintGetter;
@@ -12,11 +13,11 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
 
 public class WorldModel implements Model {
 
-	private final ModelReader reader;
+	private final VertexList reader;
 	private final String name;
 
 	public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) {
-		reader = new UnsafeBlockFormatReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));
+		reader = new BlockVertexListUnsafe(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));
 		this.name = name;
 	}
 
@@ -27,7 +28,7 @@ public class WorldModel implements Model {
 
 	@Override
 	public VertexType getType() {
-		return BlockType.INSTANCE;
+		return Formats.BLOCK;
 	}
 
 	@Override
@@ -36,7 +37,7 @@ public class WorldModel implements Model {
 	}
 
 	@Override
-	public ModelReader getReader() {
+	public VertexList getReader() {
 		return reader;
 	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java
index d0591c29d..12bc659ad 100644
--- a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java
+++ b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java
@@ -21,8 +21,8 @@ public class GameStateProgram<P extends GlProgram> implements ContextAwareProgra
 	@Override
 	public P get() {
 		for (Pair<IGameStateCondition, P> variant : variants) {
-			if (variant.getFirst()
-					.isMet()) return variant.getSecond();
+            if (variant.first()
+					.isMet()) return variant.second();
 		}
 
 		return fallback;
@@ -31,7 +31,7 @@ public class GameStateProgram<P extends GlProgram> implements ContextAwareProgra
 	@Override
 	public void delete() {
 		for (Pair<IGameStateCondition, P> variant : variants) {
-			variant.getSecond()
+			variant.second()
 					.delete();
 		}
 
diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java
new file mode 100644
index 000000000..2264443e9
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java
@@ -0,0 +1,71 @@
+package com.jozufozu.flywheel.core.vertex;
+
+import java.nio.ByteBuffer;
+
+import org.lwjgl.system.MemoryUtil;
+
+import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
+import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
+import com.jozufozu.flywheel.util.RenderMath;
+
+import net.minecraft.client.renderer.LightTexture;
+
+public class BlockVertex implements VertexType {
+
+	public static final VertexFormat FORMAT = VertexFormat.builder()
+			.addAttributes(CommonAttributes.VEC3,
+					CommonAttributes.UV,
+					CommonAttributes.RGBA,
+					CommonAttributes.LIGHT,
+					CommonAttributes.NORMAL)
+			.build();
+
+	@Override
+	public VertexFormat getFormat() {
+		return FORMAT;
+	}
+
+	@Override
+	public void copyInto(ByteBuffer buffer, VertexList reader) {
+		int stride = getStride();
+		long addr = MemoryUtil.memAddress(buffer);
+
+		int vertexCount = reader.getVertexCount();
+		for (int i = 0; i < vertexCount; i++) {
+			float x = reader.getX(i);
+			float y = reader.getY(i);
+			float z = reader.getZ(i);
+
+			float xN = reader.getNX(i);
+			float yN = reader.getNY(i);
+			float zN = reader.getNZ(i);
+
+			float u = reader.getU(i);
+			float v = reader.getV(i);
+
+			byte r = reader.getR(i);
+			byte g = reader.getG(i);
+			byte b = reader.getB(i);
+			byte a = reader.getA(i);
+
+			int light = reader.getLight(i);
+
+			MemoryUtil.memPutFloat(addr, x);
+			MemoryUtil.memPutFloat(addr + 4, y);
+			MemoryUtil.memPutFloat(addr + 8, z);
+			MemoryUtil.memPutFloat(addr + 12, u);
+			MemoryUtil.memPutFloat(addr + 16, v);
+			MemoryUtil.memPutByte(addr + 20, r);
+			MemoryUtil.memPutByte(addr + 21, g);
+			MemoryUtil.memPutByte(addr + 22, b);
+			MemoryUtil.memPutByte(addr + 23, a);
+			MemoryUtil.memPutByte(addr + 24, (byte) (LightTexture.block(light) << 4));
+			MemoryUtil.memPutByte(addr + 25, (byte) (LightTexture.sky(light) << 4));
+			MemoryUtil.memPutByte(addr + 26, RenderMath.nb(xN));
+			MemoryUtil.memPutByte(addr + 27, RenderMath.nb(yN));
+			MemoryUtil.memPutByte(addr + 28, RenderMath.nb(zN));
+
+			addr += stride;
+		}
+	}
+}
diff --git a/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java
similarity index 90%
rename from src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java
rename to src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java
index 2372d0036..a316d26e6 100644
--- a/src/main/java/com/jozufozu/flywheel/util/BlockFormatReader.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexList.java
@@ -1,17 +1,18 @@
-package com.jozufozu.flywheel.util;
+package com.jozufozu.flywheel.core.vertex;
 
 import java.nio.ByteBuffer;
 
+import com.jozufozu.flywheel.util.RenderMath;
 import com.mojang.blaze3d.vertex.BufferBuilder;
 import com.mojang.datafixers.util.Pair;
 
-public class BlockFormatReader implements ModelReader {
+public class BlockVertexList implements VertexList {
 
 	private final ByteBuffer buffer;
 	private final int vertexCount;
 	private final int stride;
 
-	public BlockFormatReader(BufferBuilder builder) {
+	public BlockVertexList(BufferBuilder builder) {
 		Pair<BufferBuilder.DrawState, ByteBuffer> data = builder.popNextBuffer();
 		buffer = data.getSecond();
 
diff --git a/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java
similarity index 91%
rename from src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java
rename to src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java
index a81ca4205..d47cf6839 100644
--- a/src/main/java/com/jozufozu/flywheel/util/UnsafeBlockFormatReader.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java
@@ -1,20 +1,21 @@
-package com.jozufozu.flywheel.util;
+package com.jozufozu.flywheel.core.vertex;
 
 import java.nio.ByteBuffer;
 
 import org.lwjgl.system.MemoryUtil;
 
+import com.jozufozu.flywheel.util.RenderMath;
 import com.mojang.blaze3d.vertex.BufferBuilder;
 import com.mojang.blaze3d.vertex.VertexFormat;
 import com.mojang.datafixers.util.Pair;
 
-public class UnsafeBlockFormatReader implements ModelReader {
+public class BlockVertexListUnsafe implements VertexList {
 
 	private final int vertexCount;
 	private final int stride;
 	private final long base;
 
-	public UnsafeBlockFormatReader(BufferBuilder builder) {
+	public BlockVertexListUnsafe(BufferBuilder builder) {
 		VertexFormat vertexFormat = builder.getVertexFormat();
 		Pair<BufferBuilder.DrawState, ByteBuffer> data = builder.popNextBuffer();
 		this.base = MemoryUtil.memAddress(data.getSecond());
diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java
similarity index 63%
rename from src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java
rename to src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java
index eb2396848..590c7d82e 100644
--- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexType.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java
@@ -2,21 +2,22 @@ package com.jozufozu.flywheel.core.vertex;
 
 import java.nio.ByteBuffer;
 
+import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
 import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
-import com.jozufozu.flywheel.core.Formats;
-import com.jozufozu.flywheel.util.ModelReader;
 
-public class PosNormalTexType implements VertexType {
+public class PosTexNormalVertex implements VertexType {
 
-	public static final PosNormalTexType INSTANCE = new PosNormalTexType();
+	public static final VertexFormat FORMAT = VertexFormat.builder()
+			.addAttributes(CommonAttributes.VEC3, CommonAttributes.UV, CommonAttributes.NORMAL)
+			.build();
 
 	@Override
 	public VertexFormat getFormat() {
-		return Formats.UNLIT_MODEL;
+		return FORMAT;
 	}
 
 	@Override
-	public void copyInto(ByteBuffer buffer, ModelReader reader) {
+	public void copyInto(ByteBuffer buffer, VertexList reader) {
 		PosTexNormalWriter writer = new PosTexNormalWriter(buffer);
 
 		int vertexCount = reader.getVertexCount();
diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java
similarity index 88%
rename from src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java
rename to src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java
index f5c745148..801c0e56b 100644
--- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosNormalTexReader.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java
@@ -4,18 +4,15 @@ import java.nio.ByteBuffer;
 
 import org.lwjgl.system.MemoryUtil;
 
-import com.jozufozu.flywheel.util.ModelReader;
 import com.jozufozu.flywheel.util.RenderMath;
 
-import net.minecraft.client.renderer.LightTexture;
-
-public class PosNormalTexReader implements ModelReader {
+public class PosTexNormalVertexListUnsafe implements VertexList {
 
 	private final ByteBuffer buffer;
 	private final int vertexCount;
 	private final long base;
 
-	public PosNormalTexReader(ByteBuffer buffer, int vertexCount) {
+	public PosTexNormalVertexListUnsafe(ByteBuffer buffer, int vertexCount) {
 		this.buffer = buffer;
 		this.vertexCount = vertexCount;
 		this.base = MemoryUtil.memAddress(buffer);
diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java
index a0a7aa1f7..c04bdd651 100644
--- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalWriter.java
@@ -8,11 +8,13 @@ import com.jozufozu.flywheel.util.RenderMath;
 
 public class PosTexNormalWriter {
 
+	private final ByteBuffer buffer;
 	private long addr;
 
 	private int vertexCount;
 
 	public PosTexNormalWriter(ByteBuffer buffer) {
+		this.buffer = buffer;
 		addr = MemoryUtil.memAddress(buffer);
 	}
 
@@ -33,4 +35,8 @@ public class PosTexNormalWriter {
 	public int getVertexCount() {
 		return vertexCount;
 	}
+
+	public PosTexNormalVertexListUnsafe intoReader() {
+		return new PosTexNormalVertexListUnsafe(buffer, vertexCount);
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/ModelReader.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java
similarity index 85%
rename from src/main/java/com/jozufozu/flywheel/util/ModelReader.java
rename to src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java
index c1dda5f8b..a87df6315 100644
--- a/src/main/java/com/jozufozu/flywheel/util/ModelReader.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexList.java
@@ -1,6 +1,6 @@
-package com.jozufozu.flywheel.util;
+package com.jozufozu.flywheel.core.vertex;
 
-public interface ModelReader {
+public interface VertexList {
 	float getX(int index);
 
 	float getY(int index);
diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java
index 24fd80ba1..d58f1b076 100644
--- a/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java
+++ b/src/main/java/com/jozufozu/flywheel/core/vertex/VertexType.java
@@ -3,13 +3,12 @@ package com.jozufozu.flywheel.core.vertex;
 import java.nio.ByteBuffer;
 
 import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
-import com.jozufozu.flywheel.util.ModelReader;
 
 public interface VertexType {
 
 	VertexFormat getFormat();
 
-    void copyInto(ByteBuffer buffer, ModelReader reader);
+    void copyInto(ByteBuffer buffer, VertexList reader);
 
 	default int getStride() {
 		return getFormat().getStride();
diff --git a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java
index 68985220b..43d42d90a 100644
--- a/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java
+++ b/src/main/java/com/jozufozu/flywheel/light/GPULightVolume.java
@@ -23,6 +23,8 @@ import org.lwjgl.opengl.GL30;
 
 import com.jozufozu.flywheel.backend.gl.GlTexture;
 import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
 
 import net.minecraft.world.level.LightLayer;
 
diff --git a/src/main/java/com/jozufozu/flywheel/light/LightListener.java b/src/main/java/com/jozufozu/flywheel/light/LightListener.java
index 9037d3b1f..396ae6bbd 100644
--- a/src/main/java/com/jozufozu/flywheel/light/LightListener.java
+++ b/src/main/java/com/jozufozu/flywheel/light/LightListener.java
@@ -1,5 +1,8 @@
 package com.jozufozu.flywheel.light;
 
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
+
 import net.minecraft.world.level.LightLayer;
 
 public interface LightListener {
diff --git a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java
index 26404ab2a..cf6a8d491 100644
--- a/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java
+++ b/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java
@@ -6,6 +6,8 @@ import java.util.Set;
 import java.util.stream.Stream;
 
 import com.jozufozu.flywheel.util.WeakHashSet;
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
 
 import it.unimi.dsi.fastutil.longs.LongSet;
 import net.minecraft.core.BlockPos;
diff --git a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java
index 2924277b3..b5a830f86 100644
--- a/src/main/java/com/jozufozu/flywheel/light/LightVolume.java
+++ b/src/main/java/com/jozufozu/flywheel/light/LightVolume.java
@@ -4,6 +4,9 @@ import java.nio.ByteBuffer;
 
 import org.lwjgl.system.MemoryUtil;
 
+import com.jozufozu.flywheel.util.box.GridAlignedBB;
+import com.jozufozu.flywheel.util.box.ImmutableBox;
+
 import net.minecraft.core.BlockPos;
 import net.minecraft.world.level.LightLayer;
 
diff --git a/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java
index 5e38c5ea1..083d2050f 100644
--- a/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java
+++ b/src/main/java/com/jozufozu/flywheel/mixin/RenderTexturesMixin.java
@@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
-import com.jozufozu.flywheel.util.RenderTextures;
+import com.jozufozu.flywheel.util.Textures;
 import com.mojang.blaze3d.systems.RenderSystem;
 
 import net.minecraft.resources.ResourceLocation;
@@ -15,6 +15,6 @@ public class RenderTexturesMixin {
 
 	@Inject(method = "_setShaderTexture(ILnet/minecraft/resources/ResourceLocation;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/AbstractTexture;getId()I"))
 	private static void storeTextureLoc(int pShaderTexture, ResourceLocation pTextureId, CallbackInfo ci) {
-		RenderTextures._setShaderTexture(pShaderTexture, pTextureId);
+		Textures._setShaderTexture(pShaderTexture, pTextureId);
 	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java
index 9a9cd1dc2..0df35f618 100644
--- a/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java
+++ b/src/main/java/com/jozufozu/flywheel/mixin/atlas/AtlasDataMixin.java
@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
-import com.jozufozu.flywheel.core.atlas.AtlasInfo;
+import com.jozufozu.flywheel.core.crumbling.AtlasInfo;
 
 import net.minecraft.client.renderer.texture.TextureAtlas;
 import net.minecraft.resources.ResourceLocation;
@@ -23,10 +23,6 @@ public abstract class AtlasDataMixin {
 
 	@Inject(method = "prepareToStitch", at = @At("RETURN"))
 	public void stealAtlasData(ResourceManager resourceManager, Stream<ResourceLocation> locationStream, ProfilerFiller profiler, int mipMapLevels, CallbackInfoReturnable<TextureAtlas.Preparations> cir) {
-		TextureAtlas.Preparations value = cir.getReturnValue();
-
-		SheetDataAccessor dataAccessor = (SheetDataAccessor) value;
-
-		AtlasInfo.setAtlasData(location(), dataAccessor);
+		AtlasInfo._setAtlasData(location(), (SheetDataAccessor) cir.getReturnValue());
 	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java b/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java
deleted file mode 100644
index a89f8bf87..000000000
--- a/src/main/java/com/jozufozu/flywheel/util/AngleHelper.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.jozufozu.flywheel.util;
-
-import net.minecraft.core.Direction;
-import net.minecraft.core.Direction.Axis;
-
-public class AngleHelper {
-
-	public static float horizontalAngle(Direction facing) {
-		if (facing.getAxis()
-				.isVertical()) {
-			return 0;
-		}
-		float angle = facing.toYRot();
-		if (facing.getAxis() == Axis.X) angle = -angle;
-		return angle;
-	}
-
-	public static float verticalAngle(Direction facing) {
-		return facing == Direction.UP ? -90 : facing == Direction.DOWN ? 90 : 0;
-	}
-
-	public static float rad(double angle) {
-		if (angle == 0) return 0;
-		return (float) (angle / 180 * Math.PI);
-	}
-
-	public static float deg(double angle) {
-		if (angle == 0) return 0;
-		return (float) (angle * 180 / Math.PI);
-	}
-
-	public static float angleLerp(double pct, double current, double target) {
-		return (float) (current + getShortestAngleDiff(current, target) * pct);
-	}
-
-	public static float getShortestAngleDiff(double current, double target) {
-		current = current % 360;
-		target = target % 360;
-		return (float) (((((target - current) % 360) + 540) % 360) - 180);
-	}
-
-}
diff --git a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java
index c16da14d8..20ed918df 100644
--- a/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java
+++ b/src/main/java/com/jozufozu/flywheel/util/AnimationTickHolder.java
@@ -4,22 +4,22 @@ import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
 
 import net.minecraft.client.Minecraft;
 
+/**
+ * Static access to tick-count and partialTick time, accounting for pausing.
+ */
 public class AnimationTickHolder {
 
+	// Wrap around every 24 hours to maintain floating point accuracy.
+	private static final int wrappingInterval = 1_728_000;
 	private static int ticks;
 	private static int paused_ticks;
 
-	public static void reset() {
-		ticks = 0;
-		paused_ticks = 0;
-	}
-
 	public static void tick() {
 		if (!Minecraft.getInstance()
 				.isPaused()) {
-			ticks = (ticks + 1) % 1_728_000; // wrap around every 24 hours so we maintain enough floating point precision
+			ticks = (ticks + 1) % wrappingInterval;
 		} else {
-			paused_ticks = (paused_ticks + 1) % 1_728_000;
+			paused_ticks = (paused_ticks + 1) % wrappingInterval;
 		}
 	}
 
@@ -39,4 +39,10 @@ public class AnimationTickHolder {
 		Minecraft mc = Minecraft.getInstance();
 		return (mc.isPaused() ? ((PausedPartialTickAccessor) mc).flywheel$getPartialTicksPaused() : mc.getFrameTime());
 	}
+
+	// Unused but might be useful for debugging.
+	public static void _reset() {
+		ticks = 0;
+		paused_ticks = 0;
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java
index eb1c7aa89..b50fe47a3 100644
--- a/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java
+++ b/src/main/java/com/jozufozu/flywheel/util/AttribUtil.java
@@ -2,6 +2,7 @@ package com.jozufozu.flywheel.util;
 
 import org.lwjgl.opengl.GL20;
 
+// TODO: move this functionality into GlVertexArray and track it
 public class AttribUtil {
 
 	public static void enableArrays(int count) {
diff --git a/src/main/java/com/jozufozu/flywheel/util/Pair.java b/src/main/java/com/jozufozu/flywheel/util/Pair.java
index ac3a10d83..6b3a48d7d 100644
--- a/src/main/java/com/jozufozu/flywheel/util/Pair.java
+++ b/src/main/java/com/jozufozu/flywheel/util/Pair.java
@@ -2,34 +2,14 @@ package com.jozufozu.flywheel.util;
 
 import java.util.Objects;
 
-public class Pair<F, S> {
-
-	F first;
-	S second;
-
-	protected Pair(F first, S second) {
-		this.first = first;
-		this.second = second;
-	}
+public record Pair<F, S>(F first, S second) {
 
 	public static <F, S> Pair<F, S> of(F first, S second) {
 		return new Pair<>(first, second);
 	}
 
-	public F getFirst() {
-		return first;
-	}
-
-	public S getSecond() {
-		return second;
-	}
-
-	public void setFirst(F first) {
-		this.first = first;
-	}
-
-	public void setSecond(S second) {
-		this.second = second;
+	public Pair<S, F> swap() {
+		return Pair.of(second, first);
 	}
 
 	public Pair<F, S> copy() {
@@ -39,8 +19,7 @@ public class Pair<F, S> {
 	@Override
 	public boolean equals(final Object obj) {
 		if (obj == this) return true;
-		if (obj instanceof Pair) {
-			final Pair<?, ?> other = (Pair<?, ?>) obj;
+		if (obj instanceof final Pair<?, ?> other) {
 			return Objects.equals(first, other.first) && Objects.equals(second, other.second);
 		}
 		return false;
@@ -51,17 +30,13 @@ public class Pair<F, S> {
 		return (nullHash(first) * 31) ^ nullHash(second);
 	}
 
-	int nullHash(Object o) {
-		return o == null ? 0 : o.hashCode();
-	}
-
 	@Override
 	public String toString() {
 		return "(" + first + ", " + second + ")";
 	}
 
-	public Pair<S, F> swap() {
-		return Pair.of(second, first);
+	static int nullHash(Object o) {
+		return o == null ? 0 : o.hashCode();
 	}
 
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java
index 3b891d683..77321641b 100644
--- a/src/main/java/com/jozufozu/flywheel/util/RenderMath.java
+++ b/src/main/java/com/jozufozu/flywheel/util/RenderMath.java
@@ -29,4 +29,42 @@ public class RenderMath {
 	public static byte unb(float f) {
 		return (byte) Math.floor(f * 255);
 	}
+
+	public static int nextPowerOf2(int a) {
+		int h = Integer.highestOneBit(a);
+		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 rad(double angle) {
+		if (angle == 0) return 0;
+		return (float) (angle / 180 * Math.PI);
+	}
+
+	public static float deg(double angle) {
+		if (angle == 0) return 0;
+		return (float) (angle * 180 / Math.PI);
+	}
+
+	public static float angleLerp(double pct, double current, double target) {
+		return (float) (current + getShortestAngleDiff(current, target) * pct);
+	}
+
+	public static float getShortestAngleDiff(double current, double target) {
+		current = current % 360;
+		target = target % 360;
+		return (float) (((((target - current) % 360) + 540) % 360) - 180);
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java b/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java
deleted file mode 100644
index 4897d7888..000000000
--- a/src/main/java/com/jozufozu/flywheel/util/RenderUtil.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.jozufozu.flywheel.util;
-
-import java.util.function.Supplier;
-
-import com.mojang.blaze3d.vertex.PoseStack;
-import com.mojang.math.Matrix4f;
-import com.mojang.math.Vector3f;
-
-import net.minecraft.core.Direction;
-
-public class RenderUtil {
-
-	private static final Matrix4f IDENTITY = new Matrix4f();
-	static {
-		IDENTITY.setIdentity();
-	}
-
-	public static Matrix4f getIdentity() {
-		return IDENTITY;
-	}
-
-	public static Matrix4f copyIdentity() {
-		return IDENTITY.copy();
-	}
-
-	public static int nextPowerOf2(int a) {
-		int h = Integer.highestOneBit(a);
-		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 Supplier<PoseStack> rotateToFace(Direction facing) {
-		return () -> {
-			PoseStack stack = new PoseStack();
-			//			MatrixStacker.of(stack)
-			//					.centre()
-			//					.rotateY(AngleHelper.horizontalAngle(facing))
-			//					.rotateX(AngleHelper.verticalAngle(facing))
-			//					.unCentre();
-			stack.last()
-					.pose()
-					.setTranslation(0.5f, 0.5f, 0.5f);
-			stack.mulPose(Vector3f.YP.rotationDegrees(AngleHelper.horizontalAngle(facing)));
-			stack.mulPose(Vector3f.XP.rotationDegrees(AngleHelper.verticalAngle(facing)));
-			stack.translate(-0.5f, -0.5f, -0.5f);
-			return stack;
-		};
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java b/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java
deleted file mode 100644
index dd1b4d70a..000000000
--- a/src/main/java/com/jozufozu/flywheel/util/StreamUtil.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.jozufozu.flywheel.util;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-
-import org.lwjgl.system.MemoryUtil;
-
-public class StreamUtil {
-	public static String readToString(InputStream is) {
-		ByteBuffer bytebuffer = null;
-
-		try {
-			bytebuffer = readToBuffer(is);
-			int i = bytebuffer.position();
-			((Buffer) bytebuffer).rewind();
-			return MemoryUtil.memASCII(bytebuffer, i);
-		} catch (IOException e) {
-
-		} finally {
-			if (bytebuffer != null) {
-				MemoryUtil.memFree(bytebuffer);
-			}
-
-		}
-
-		return null;
-	}
-
-	public static ByteBuffer readToBuffer(InputStream is) throws IOException {
-		ByteBuffer bytebuffer;
-		if (is instanceof FileInputStream) {
-			FileInputStream fileinputstream = (FileInputStream) is;
-			FileChannel filechannel = fileinputstream.getChannel();
-			bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1);
-
-			while (filechannel.read(bytebuffer) != -1) {
-			}
-		} else {
-			bytebuffer = MemoryUtil.memAlloc(8192);
-			ReadableByteChannel readablebytechannel = Channels.newChannel(is);
-
-			while (readablebytechannel.read(bytebuffer) != -1) {
-				if (bytebuffer.remaining() == 0) {
-					bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2);
-				}
-			}
-		}
-
-		return bytebuffer;
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java
index b4ee5a638..9a1d5d2e9 100644
--- a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java
+++ b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java
@@ -1,8 +1,18 @@
 package com.jozufozu.flywheel.util;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
 import java.util.Arrays;
 import java.util.stream.Collectors;
 
+import org.lwjgl.system.MemoryUtil;
+
 public class StringUtil {
 
 	public static String args(String functionName, Object... args) {
@@ -20,4 +30,52 @@ public class StringUtil {
 		}
 		return value.substring(0, len);
 	}
+
+	public static String readToString(InputStream is) {
+		ByteBuffer bytebuffer = null;
+
+		try {
+			bytebuffer = readToBuffer(is);
+			int i = bytebuffer.position();
+			((Buffer) bytebuffer).rewind();
+			return MemoryUtil.memASCII(bytebuffer, i);
+		} catch (IOException ignored) {
+		} finally {
+			if (bytebuffer != null) {
+				MemoryUtil.memFree(bytebuffer);
+			}
+
+		}
+
+		return null;
+	}
+
+	public static ByteBuffer readToBuffer(InputStream is) throws IOException {
+		if (is instanceof FileInputStream fileinputstream) {
+			return readFileInputStream(fileinputstream);
+		} else {
+			return readInputStream(is);
+		}
+	}
+
+	private static ByteBuffer readInputStream(InputStream is) throws IOException {
+		ByteBuffer bytebuffer = MemoryUtil.memAlloc(8192);
+		ReadableByteChannel readablebytechannel = Channels.newChannel(is);
+
+		while (readablebytechannel.read(bytebuffer) != -1) {
+			if (bytebuffer.remaining() == 0) {
+				bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2);
+			}
+		}
+		return bytebuffer;
+	}
+
+	private static ByteBuffer readFileInputStream(FileInputStream fileinputstream) throws IOException {
+		FileChannel filechannel = fileinputstream.getChannel();
+		ByteBuffer bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1);
+
+		while (filechannel.read(bytebuffer) != -1) {
+		}
+		return bytebuffer;
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java b/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java
index ba9c4f64e..e90435078 100644
--- a/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java
+++ b/src/main/java/com/jozufozu/flywheel/util/TextureBinder.java
@@ -1,9 +1,5 @@
 package com.jozufozu.flywheel.util;
 
-import org.lwjgl.opengl.GL32;
-
-import com.mojang.blaze3d.systems.RenderSystem;
-
 import net.minecraft.client.renderer.RenderType;
 
 /**
@@ -16,14 +12,4 @@ import net.minecraft.client.renderer.RenderType;
  */
 public class TextureBinder {
 
-	/**
-	 * Call this after calling {@link RenderType#setupRenderState()}.
-	 */
-	public static void bindActiveTextures() {
-		for (int i = 0; i < 12; i++) {
-			int shaderTexture = RenderSystem.getShaderTexture(i);
-			RenderSystem.activeTexture(GL32.GL_TEXTURE0 + i);
-			RenderSystem.bindTexture(shaderTexture);
-		}
-	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/util/RenderTextures.java b/src/main/java/com/jozufozu/flywheel/util/Textures.java
similarity index 58%
rename from src/main/java/com/jozufozu/flywheel/util/RenderTextures.java
rename to src/main/java/com/jozufozu/flywheel/util/Textures.java
index 8ea3f12dc..f648e7cde 100644
--- a/src/main/java/com/jozufozu/flywheel/util/RenderTextures.java
+++ b/src/main/java/com/jozufozu/flywheel/util/Textures.java
@@ -2,6 +2,11 @@ package com.jozufozu.flywheel.util;
 
 import javax.annotation.Nullable;
 
+import org.lwjgl.opengl.GL32;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import net.minecraft.client.renderer.RenderType;
 import net.minecraft.resources.ResourceLocation;
 
 /**
@@ -11,7 +16,7 @@ import net.minecraft.resources.ResourceLocation;
  *     Works with {@link com.jozufozu.flywheel.mixin.RenderTexturesMixin}.
  * </p>
  */
-public class RenderTextures {
+public class Textures {
 
 	private static final ResourceLocation[] shaderTextures = new ResourceLocation[12];
 
@@ -23,4 +28,15 @@ public class RenderTextures {
 	public static void _setShaderTexture(int pShaderTexture, ResourceLocation pTextureId) {
 		shaderTextures[pShaderTexture] = pTextureId;
 	}
+
+	/**
+	 * Call this after calling {@link RenderType#setupRenderState()}.
+	 */
+	public static void bindActiveTextures() {
+		for (int i = 0; i < 12; i++) {
+			int shaderTexture = RenderSystem.getShaderTexture(i);
+			RenderSystem.activeTexture(GL32.GL_TEXTURE0 + i);
+			RenderSystem.bindTexture(shaderTexture);
+		}
+	}
 }
diff --git a/src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java b/src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java
similarity index 71%
rename from src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java
rename to src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java
index b6a64eb0d..f42c5a00b 100644
--- a/src/main/java/com/jozufozu/flywheel/light/CoordinateConsumer.java
+++ b/src/main/java/com/jozufozu/flywheel/util/box/CoordinateConsumer.java
@@ -1,4 +1,4 @@
-package com.jozufozu.flywheel.light;
+package com.jozufozu.flywheel.util.box;
 
 @FunctionalInterface
 public interface CoordinateConsumer {
diff --git a/src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java b/src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java
similarity index 96%
rename from src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java
rename to src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java
index e96f9fbc7..fa6925a36 100644
--- a/src/main/java/com/jozufozu/flywheel/light/GridAlignedBB.java
+++ b/src/main/java/com/jozufozu/flywheel/util/box/GridAlignedBB.java
@@ -1,6 +1,6 @@
-package com.jozufozu.flywheel.light;
+package com.jozufozu.flywheel.util.box;
 
-import com.jozufozu.flywheel.util.RenderUtil;
+import com.jozufozu.flywheel.util.RenderMath;
 
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
@@ -116,9 +116,9 @@ public class GridAlignedBB implements ImmutableBox {
 		int sizeY = sizeY();
 		int sizeZ = sizeZ();
 
-		int newSizeX = RenderUtil.nextPowerOf2(sizeX);
-		int newSizeY = RenderUtil.nextPowerOf2(sizeY);
-		int newSizeZ = RenderUtil.nextPowerOf2(sizeZ);
+		int newSizeX = RenderMath.nextPowerOf2(sizeX);
+		int newSizeY = RenderMath.nextPowerOf2(sizeY);
+		int newSizeZ = RenderMath.nextPowerOf2(sizeZ);
 
 		int diffX = newSizeX - sizeX;
 		int diffY = newSizeY - sizeY;
@@ -136,9 +136,9 @@ public class GridAlignedBB implements ImmutableBox {
 	 * Grow this bounding box to have power of 2 side lengths, scaling from the minimum coords.
 	 */
 	public void nextPowerOf2() {
-		int sizeX = RenderUtil.nextPowerOf2(sizeX());
-		int sizeY = RenderUtil.nextPowerOf2(sizeY());
-		int sizeZ = RenderUtil.nextPowerOf2(sizeZ());
+		int sizeX = RenderMath.nextPowerOf2(sizeX());
+		int sizeY = RenderMath.nextPowerOf2(sizeY());
+		int sizeZ = RenderMath.nextPowerOf2(sizeZ());
 
 		maxX = minX + sizeX;
 		maxY = minY + sizeY;
diff --git a/src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java b/src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java
similarity index 97%
rename from src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java
rename to src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java
index 719966424..10a5c7a91 100644
--- a/src/main/java/com/jozufozu/flywheel/light/ImmutableBox.java
+++ b/src/main/java/com/jozufozu/flywheel/util/box/ImmutableBox.java
@@ -1,6 +1,6 @@
-package com.jozufozu.flywheel.light;
+package com.jozufozu.flywheel.util.box;
 
-import static com.jozufozu.flywheel.util.RenderUtil.isPowerOf2;
+import static com.jozufozu.flywheel.util.RenderMath.isPowerOf2;
 
 import net.minecraft.world.phys.AABB;
 
diff --git a/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java b/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java
deleted file mode 100644
index 4bd8e309f..000000000
--- a/src/main/java/com/jozufozu/flywheel/util/vec/Vec3.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.jozufozu.flywheel.util.vec;
-
-import com.mojang.math.Quaternion;
-import com.mojang.math.Vector3f;
-
-public class Vec3 {
-	public static final Vec3 NEGATIVE_X = new Vec3(-1.0F, 0.0F, 0.0F);
-	public static final Vec3 POSITIVE_X = new Vec3(1.0F, 0.0F, 0.0F);
-	public static final Vec3 NEGATIVE_Y = new Vec3(0.0F, -1.0F, 0.0F);
-	public static final Vec3 POSITIVE_Y = new Vec3(0.0F, 1.0F, 0.0F);
-	public static final Vec3 NEGATIVE_Z = new Vec3(0.0F, 0.0F, -1.0F);
-	public static final Vec3 POSITIVE_Z = new Vec3(0.0F, 0.0F, 1.0F);
-
-	private float x;
-	private float y;
-	private float z;
-
-	public Vec3(float x, float y, float z) {
-		this.x = x;
-		this.y = y;
-		this.z = z;
-	}
-
-	public float getX() {
-		return x;
-	}
-
-	public float getY() {
-		return y;
-	}
-
-	public float getZ() {
-		return z;
-	}
-
-	public Vec3 multiply(Quaternion quat) {
-		Vec4 vec4 = new Vec4(this, 1f);
-
-		vec4.multiply(quat);
-
-		return set(vec4.getX(), vec4.getY(), vec4.getZ());
-	}
-
-	public Vec3 copy() {
-		return new Vec3(x, y, z);
-	}
-
-	public Vector3f convert() {
-		return new Vector3f(x, y, z);
-	}
-
-	public Vec3 set(float x, float y, float z) {
-		this.x = x;
-		this.y = y;
-		this.z = z;
-		return this;
-	}
-
-	public Vec3 add(Vec3 v) {
-		return add(v.x, v.y, v.z);
-	}
-
-	public Vec3 add(float x, float y, float z) {
-		this.x += x;
-		this.y += y;
-		this.z += z;
-		return this;
-	}
-
-	public Vec3 sub(Vec3 v) {
-		return sub(v.x, v.y, v.z);
-	}
-
-	public Vec3 sub(float x, float y, float z) {
-		this.x -= x;
-		this.y -= y;
-		this.z -= z;
-		return this;
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java b/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java
deleted file mode 100644
index b4f5cb663..000000000
--- a/src/main/java/com/jozufozu/flywheel/util/vec/Vec4.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.jozufozu.flywheel.util.vec;
-
-import com.mojang.math.Quaternion;
-
-public class Vec4 {
-
-	private float x;
-	private float y;
-	private float z;
-	private float w;
-
-	public Vec4(float x, float y, float z, float w) {
-		this.x = x;
-		this.y = y;
-		this.z = z;
-		this.w = w;
-	}
-
-	public Vec4(Vec3 vec3) {
-		this(vec3, 0);
-	}
-
-	public Vec4(Vec3 vec3, float w) {
-		this.x = vec3.getX();
-		this.y = vec3.getY();
-		this.z = vec3.getZ();
-		this.w = w;
-	}
-
-	public Vec4 multiply(Quaternion quat) {
-		Quaternion quaternion = new Quaternion(quat);
-		quaternion.mul(new Quaternion(this.getX(), this.getY(), this.getZ(), 0.0F));
-		Quaternion quaternion1 = new Quaternion(quat);
-		quaternion1.conj();
-		quaternion.mul(quaternion1);
-		return set(quaternion.i(), quaternion.j(), quaternion.k(), this.getW());
-	}
-
-	public Vec3 xyz() {
-		return new Vec3(x, y, z);
-	}
-
-	public float getX() {
-		return x;
-	}
-
-	public float getY() {
-		return y;
-	}
-
-	public float getZ() {
-		return z;
-	}
-
-	public float getW() {
-		return w;
-	}
-
-	public Vec4 set(float x, float y, float z, float w) {
-		this.x = x;
-		this.y = y;
-		this.z = z;
-		this.w = w;
-		return this;
-	}
-}
diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java
index 5cfd8eb60..c66e41b63 100644
--- a/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/vanilla/BellInstance.java
@@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance;
 import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
 import com.jozufozu.flywheel.core.Materials;
 import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
-import com.jozufozu.flywheel.core.model.ModelPart;
+import com.jozufozu.flywheel.core.hardcoded.ModelPart;
 import com.jozufozu.flywheel.util.AnimationTickHolder;
 import com.mojang.math.Quaternion;
 import com.mojang.math.Vector3f;
diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java
index 0dddeefb7..ab1acdebf 100644
--- a/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/vanilla/ChestInstance.java
@@ -10,7 +10,7 @@ import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
 import com.jozufozu.flywheel.core.Materials;
 import com.jozufozu.flywheel.core.materials.model.ModelData;
 import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
-import com.jozufozu.flywheel.core.model.ModelPart;
+import com.jozufozu.flywheel.core.hardcoded.ModelPart;
 import com.jozufozu.flywheel.util.AnimationTickHolder;
 import com.mojang.math.Quaternion;
 import com.mojang.math.Vector3f;
diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java
index bf53ae248..4fe3116df 100644
--- a/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/vanilla/MinecartInstance.java
@@ -7,7 +7,7 @@ import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
 import com.jozufozu.flywheel.core.Materials;
 import com.jozufozu.flywheel.core.materials.model.ModelData;
 import com.jozufozu.flywheel.core.model.Model;
-import com.jozufozu.flywheel.core.model.ModelPart;
+import com.jozufozu.flywheel.core.hardcoded.ModelPart;
 import com.jozufozu.flywheel.util.AnimationTickHolder;
 import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
 import com.mojang.math.Vector3f;
diff --git a/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java b/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java
index 1f3ecbfe1..a64e5b0ec 100644
--- a/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java
+++ b/src/main/java/com/jozufozu/flywheel/vanilla/ShulkerBoxInstance.java
@@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.instance.IDynamicInstance;
 import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
 import com.jozufozu.flywheel.core.Materials;
 import com.jozufozu.flywheel.core.materials.model.ModelData;
-import com.jozufozu.flywheel.core.model.ModelPart;
+import com.jozufozu.flywheel.core.hardcoded.ModelPart;
 import com.jozufozu.flywheel.util.AnimationTickHolder;
 import com.jozufozu.flywheel.util.transform.MatrixTransformStack;
 import com.mojang.math.Quaternion;