From 6a8b6a39c812a44316b416dffb7be72d7eec2d4c Mon Sep 17 00:00:00 2001
From: i509VCB <git@i509.me>
Date: Wed, 14 Apr 2021 12:40:42 -0500
Subject: [PATCH] Create a simple renderer api testmod (#1295)

* Create a simple renderer api testmod

* Update fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/RendererClientTest.java

Co-authored-by: liach <7806504+liach@users.noreply.github.com>

Co-authored-by: liach <7806504+liach@users.noreply.github.com>
(cherry picked from commit b0e4dde51b3b5ad54ae3fde8f39e843375bd2e4e)
---
 fabric-renderer-api-v1/build.gradle           |   8 +
 .../test/renderer/WorldRenderExtensions.java  |  31 ++++
 .../test/renderer/mixin/ClientWorldMixin.java |  38 +++++
 .../test/renderer/mixin/WorldMixin.java       |  32 ++++
 .../fabric/test/renderer/package-info.java    |  26 ++++
 .../test/renderer/simple/FrameBlock.java      |  91 ++++++++++++
 .../renderer/simple/FrameBlockEntity.java     | 108 ++++++++++++++
 .../test/renderer/simple/RendererTest.java    |  45 ++++++
 .../simple/client/FrameBakedModel.java        | 138 ++++++++++++++++++
 .../client/FrameModelResourceProvider.java    |  43 ++++++
 .../simple/client/FrameUnbakedModel.java      | 123 ++++++++++++++++
 .../simple/client/RendererClientTest.java     |  32 ++++
 .../blockstates/frame.json                    |   5 +
 ...fabric-renderer-api-v1-testmod.mixins.json |  14 ++
 .../src/testmod/resources/fabric.mod.json     |  23 +++
 .../src/testmod/resources/fabric.mod.json     |   3 -
 16 files changed, 757 insertions(+), 3 deletions(-)
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/WorldRenderExtensions.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/ClientWorldMixin.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/WorldMixin.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/package-info.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlock.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlockEntity.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/RendererTest.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameBakedModel.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameModelResourceProvider.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameUnbakedModel.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/RendererClientTest.java
 create mode 100644 fabric-renderer-api-v1/src/testmod/resources/assets/fabric-renderer-api-v1-testmod/blockstates/frame.json
 create mode 100644 fabric-renderer-api-v1/src/testmod/resources/fabric-renderer-api-v1-testmod.mixins.json
 create mode 100644 fabric-renderer-api-v1/src/testmod/resources/fabric.mod.json

diff --git a/fabric-renderer-api-v1/build.gradle b/fabric-renderer-api-v1/build.gradle
index bff12890a..35e957d9f 100644
--- a/fabric-renderer-api-v1/build.gradle
+++ b/fabric-renderer-api-v1/build.gradle
@@ -4,3 +4,11 @@ version = getSubprojectVersion(project, "0.4.1")
 moduleDependencies(project, [
 		'fabric-api-base'
 ])
+
+dependencies {
+	testmodCompile project(path: ':fabric-blockrenderlayer-v1', configuration: 'dev')
+	testmodCompile project(path: ':fabric-models-v0', configuration: 'dev')
+	testmodCompile project(path: ':fabric-networking-blockentity-v0', configuration: 'dev')
+	testmodCompile project(path: ':fabric-object-builder-api-v1', configuration: 'dev')
+	testmodCompile project(path: ':fabric-rendering-data-attachment-v1', configuration: 'dev')
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/WorldRenderExtensions.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/WorldRenderExtensions.java
new file mode 100644
index 000000000..a104747b7
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/WorldRenderExtensions.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+/**
+ * Extension interface for a world to notify the world that a block needs to be re-rendered.
+ */
+public interface WorldRenderExtensions {
+	static void scheduleBlockRerender(World world, BlockPos pos) {
+		((WorldRenderExtensions) world).scheduleBlockRerender(pos);
+	}
+
+	void scheduleBlockRerender(BlockPos pos);
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/ClientWorldMixin.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/ClientWorldMixin.java
new file mode 100644
index 000000000..6d8c5febf
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/ClientWorldMixin.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.mixin;
+
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+import net.minecraft.client.render.WorldRenderer;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.util.math.BlockPos;
+
+@Mixin(ClientWorld.class)
+abstract class ClientWorldMixin extends WorldMixin {
+	@Shadow
+	@Final
+	private WorldRenderer worldRenderer;
+
+	@Override
+	public void scheduleBlockRerender(BlockPos pos) {
+		// Update the block at the position to trigger chunk re-render.
+		this.worldRenderer.updateBlock(null, pos, null, null, 0);
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/WorldMixin.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/WorldMixin.java
new file mode 100644
index 000000000..03903b9fb
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/mixin/WorldMixin.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import net.fabricmc.fabric.test.renderer.WorldRenderExtensions;
+
+@Mixin(World.class)
+abstract class WorldMixin implements WorldRenderExtensions {
+	@Override
+	public void scheduleBlockRerender(BlockPos pos) {
+		// Do nothing, the client world will do things here
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/package-info.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/package-info.java
new file mode 100644
index 000000000..0af08e4ff
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The testmod for the Fabric Renderer API.
+ * Right now there is only one test here, but more tests may come to exist in the future.
+ * These tests are used to validate Indigo's implementation is correct, but these tests may also be useful for other implementations of the Fabric Renderer API.
+ *
+ * <p>Right now there is a simple test in the {@code simple} package which validates that simple meshes and quad emitters function.
+ * Future tests may look into testing things such as render materials or creating more advanced models.
+ */
+
+package net.fabricmc.fabric.test.renderer;
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlock.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlock.java
new file mode 100644
index 000000000..9c28551b7
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlock.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockEntityProvider;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+public final class FrameBlock extends Block implements BlockEntityProvider {
+	public FrameBlock(Settings settings) {
+		super(settings);
+	}
+
+	@Override
+	public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
+		if (world.isClient()) {
+			return ActionResult.PASS;
+		}
+
+		BlockEntity blockEntity = world.getBlockEntity(pos);
+
+		if (blockEntity instanceof FrameBlockEntity) {
+			ItemStack stack = player.getStackInHand(hand);
+			Block handBlock = Block.getBlockFromItem(stack.getItem());
+
+			@Nullable
+			Block currentBlock = ((FrameBlockEntity) blockEntity).getBlock();
+
+			if (stack.isEmpty()) {
+				// Try to remove if the stack in hand is empty
+				if (currentBlock != null) {
+					player.getInventory().offerOrDrop(new ItemStack(currentBlock));
+					((FrameBlockEntity) blockEntity).setBlock(null);
+					return ActionResult.SUCCESS;
+				}
+
+				return ActionResult.PASS;
+			}
+
+			// getBlockFromItem will return air if we do not have a block item in hand
+			if (handBlock == Blocks.AIR) {
+				return ActionResult.FAIL;
+			}
+
+			// Do not allow blocks that may have a block entity
+			if (handBlock instanceof BlockEntityProvider) {
+				return ActionResult.FAIL;
+			}
+
+			if (currentBlock != null) {
+				player.getInventory().offerOrDrop(new ItemStack(currentBlock));
+			}
+
+			((FrameBlockEntity) blockEntity).setBlock(handBlock);
+			return ActionResult.SUCCESS;
+		}
+
+		return ActionResult.FAIL;
+	}
+
+	@Nullable
+	@Override
+	public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
+		return new FrameBlockEntity(pos, state);
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlockEntity.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlockEntity.java
new file mode 100644
index 000000000..98f020e84
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlockEntity.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable;
+import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
+import net.fabricmc.fabric.api.util.NbtType;
+import net.fabricmc.fabric.test.renderer.WorldRenderExtensions;
+
+public final class FrameBlockEntity extends BlockEntity implements RenderAttachmentBlockEntity, BlockEntityClientSerializable {
+	@Nullable
+	private Block block = null;
+
+	public FrameBlockEntity(BlockPos blockPos, BlockState blockState) {
+		super(RendererTest.FRAME_BLOCK_ENTITY, blockPos, blockState);
+	}
+
+	@Override
+	public void readNbt(NbtCompound tag) {
+		super.readNbt(tag);
+
+		if (tag.contains("block", NbtType.STRING)) {
+			this.block = Registry.BLOCK.get(new Identifier(tag.getString("block")));
+		}
+	}
+
+	@Override
+	public NbtCompound writeNbt(NbtCompound tag) {
+		if (this.block != null) {
+			tag.putString("block", Registry.BLOCK.getId(this.block).toString());
+		}
+
+		return super.writeNbt(tag);
+	}
+
+	@Override
+	public void markDirty() {
+		super.markDirty();
+
+		if (this.hasWorld() && !this.getWorld().isClient()) {
+			this.sync();
+		}
+	}
+
+	@Nullable
+	public Block getBlock() {
+		return this.block;
+	}
+
+	public void setBlock(@Nullable Block block) {
+		this.block = block;
+		this.markDirty();
+	}
+
+	@Nullable
+	@Override
+	public Block getRenderAttachmentData() {
+		return this.block;
+	}
+
+	@Override
+	public void fromClientTag(NbtCompound tag) {
+		System.out.println("Recieved sync packet");
+
+		if (tag.contains("block", NbtType.STRING)) {
+			this.block = Registry.BLOCK.get(new Identifier(tag.getString("block")));
+		} else {
+			this.block = null;
+		}
+
+		if (this.getWorld() != null) {
+			WorldRenderExtensions.scheduleBlockRerender(this.getWorld(), this.getPos());
+		}
+	}
+
+	@Override
+	public NbtCompound toClientTag(NbtCompound tag) {
+		if (this.block != null) {
+			tag.putString("block", Registry.BLOCK.getId(this.block).toString());
+		}
+
+		return tag;
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/RendererTest.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/RendererTest.java
new file mode 100644
index 000000000..074e0ba25
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/RendererTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.entity.BlockEntityType;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
+import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
+
+/**
+ * A simple testmod that renders a simple block rendered using the fabric renderer api.
+ * The block that is rendered is a simple frame that another block is rendered in.
+ * Blocks that provide a block entity cannot be placed inside the frame.
+ *
+ * <p>There are no fancy shaders or glow that is provided by this renderer test.
+ */
+public final class RendererTest implements ModInitializer {
+	public static final Block FRAME = new FrameBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque());
+	public static final BlockEntityType<FrameBlockEntity> FRAME_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(FrameBlockEntity::new, FRAME).build(null);
+
+	@Override
+	public void onInitialize() {
+		Registry.register(Registry.BLOCK, new Identifier("fabric-renderer-api-v1-testmod", "frame"), FRAME);
+		Registry.register(Registry.BLOCK_ENTITY_TYPE, new Identifier("fabric-renderer-api-v1-testmod", "frame"), FRAME_BLOCK_ENTITY);
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameBakedModel.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameBakedModel.java
new file mode 100644
index 000000000..cd7ca0b78
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameBakedModel.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple.client;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.BakedQuad;
+import net.minecraft.client.render.model.json.ModelOverrideList;
+import net.minecraft.client.render.model.json.ModelTransformation;
+import net.minecraft.client.texture.Sprite;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.world.BlockRenderView;
+
+import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
+import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
+import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
+import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
+import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
+import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
+
+final class FrameBakedModel implements BakedModel, FabricBakedModel {
+	private final Mesh frameMesh;
+	private final Sprite frameSprite;
+
+	FrameBakedModel(Mesh frameMesh, Sprite frameSprite) {
+		this.frameMesh = frameMesh;
+		this.frameSprite = frameSprite;
+	}
+
+	@Override
+	public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction face, Random random) {
+		return Collections.emptyList(); // Renderer API makes this obsolete, so return no quads
+	}
+
+	@Override
+	public boolean useAmbientOcclusion() {
+		return true; // we want the block to have a shadow depending on the adjacent blocks
+	}
+
+	@Override
+	public boolean hasDepth() {
+		return false;
+	}
+
+	@Override
+	public boolean isSideLit() {
+		return false;
+	}
+
+	@Override
+	public boolean isBuiltin() {
+		return false;
+	}
+
+	@Override
+	public Sprite getSprite() {
+		return this.frameSprite;
+	}
+
+	@Override
+	public ModelTransformation getTransformation() {
+		return ModelTransformation.NONE;
+	}
+
+	@Override
+	public ModelOverrideList getOverrides() {
+		return ModelOverrideList.EMPTY;
+	}
+
+	@Override
+	public boolean isVanillaAdapter() {
+		return false;
+	}
+
+	@Override
+	public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
+		// Emit our frame mesh
+		context.meshConsumer().accept(this.frameMesh);
+
+		RenderAttachedBlockView renderAttachedBlockView = (RenderAttachedBlockView) blockView;
+
+		// We cannot access the block entity from here. We should instead use the immutable render attachments provided by the block entity.
+		@Nullable
+		Block data = (Block) renderAttachedBlockView.getBlockEntityRenderAttachment(pos);
+
+		if (data == null) {
+			return; // No inner block to render
+		}
+
+		Sprite sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelManager().getBlockModels().getSprite(data.getDefaultState());
+		QuadEmitter emitter = context.getEmitter();
+
+		// We can emit our quads outside of the mesh as the block being put in the frame is very much dynamic.
+		// Emit the quads for each face of the block inside the frame
+		for (Direction direction : Direction.values()) {
+			// Add a face, with an inset to give the appearance of the block being in a frame.
+			emitter.square(direction, 0.1F, 0.1F, 0.9F, 0.9F, 0.1F)
+					// Set the sprite of the fact, use whole texture via BAKE_LOCK_UV
+					.spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV)
+					// Allow textures
+					// TODO: the magic values here are not documented at all and probably should be
+					.spriteColor(0, -1, -1, -1, -1)
+					// Emit the quad
+					.emit();
+		}
+	}
+
+	@Override
+	public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
+		// TODO: Implement an item test.
+		// For now we will just leave this as I have not added a block item yet
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameModelResourceProvider.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameModelResourceProvider.java
new file mode 100644
index 000000000..6e7003b8e
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameModelResourceProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple.client;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.client.render.model.UnbakedModel;
+import net.minecraft.util.Identifier;
+
+import net.fabricmc.fabric.api.client.model.ModelProviderContext;
+import net.fabricmc.fabric.api.client.model.ModelProviderException;
+import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
+
+/**
+ * Provides the unbaked model for use with the frame block.
+ */
+final class FrameModelResourceProvider implements ModelResourceProvider {
+	private static final Identifier FRAME_MODEL_ID = new Identifier("fabric-renderer-api-v1-testmod", "block/frame");
+
+	@Nullable
+	@Override
+	public UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) throws ModelProviderException {
+		if (resourceId.equals(FRAME_MODEL_ID)) {
+			return new FrameUnbakedModel();
+		}
+
+		return null;
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameUnbakedModel.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameUnbakedModel.java
new file mode 100644
index 000000000..8656bbf64
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/FrameUnbakedModel.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple.client;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.Function;
+
+import com.mojang.datafixers.util.Pair;
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.ModelBakeSettings;
+import net.minecraft.client.render.model.ModelLoader;
+import net.minecraft.client.render.model.UnbakedModel;
+import net.minecraft.client.texture.Sprite;
+import net.minecraft.client.texture.SpriteAtlasTexture;
+import net.minecraft.client.util.SpriteIdentifier;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.Direction;
+
+import net.fabricmc.fabric.api.renderer.v1.Renderer;
+import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
+import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
+import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
+import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
+
+final class FrameUnbakedModel implements UnbakedModel {
+	FrameUnbakedModel() {
+	}
+
+	@Override
+	public Collection<Identifier> getModelDependencies() {
+		return Collections.emptySet();
+	}
+
+	@Override
+	public Collection<SpriteIdentifier> getTextureDependencies(Function<Identifier, UnbakedModel> unbakedModelGetter, Set<Pair<String, String>> unresolvedTextureReferences) {
+		return Collections.emptySet(); // TODO: Also set the return value when we set a proper texture.
+	}
+
+	/*
+	 * Bake the model.
+	 * In this case we can prebake the frame into a mesh, but will render the contained block when we draw the quads.
+	 */
+	@Nullable
+	@Override
+	public BakedModel bake(ModelLoader loader, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer, Identifier modelId) {
+		// The renderer api may not have an implementation.
+		// For this reason we will just null check the renderer impl
+		if (RendererAccess.INSTANCE.hasRenderer()) {
+			Renderer renderer = RendererAccess.INSTANCE.getRenderer();
+			MeshBuilder builder = renderer.meshBuilder();
+			QuadEmitter emitter = builder.getEmitter();
+			// TODO: Just some random texture to get a missing texture, we should get a proper texture soon
+			Sprite frameSprite = textureGetter.apply(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier("foo:foo")));
+
+			for (Direction direction : Direction.values()) {
+				// Draw outer frame
+				emitter.square(direction, 0.0F, 0.9F, 0.9F, 1.0F, 0.0F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.0F, 0.0F, 0.1F, 0.9F, 0.0F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.9F, 0.1F, 1.0F, 1.0F, 0.0F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.1F, 0.0F, 1.0F, 0.1F, 0.0F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				// Draw inner frame - inset by 0.9 so the frame looks like an actual mesh
+				emitter.square(direction, 0.0F, 0.9F, 0.9F, 1.0F, 0.9F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.0F, 0.0F, 0.1F, 0.9F, 0.9F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.9F, 0.1F, 1.0F, 1.0F, 0.9F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+
+				emitter.square(direction, 0.1F, 0.0F, 1.0F, 0.1F, 0.9F)
+						.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
+						.spriteColor(0, -1, -1, -1, -1)
+						.emit();
+			}
+
+			return new FrameBakedModel(builder.build(), frameSprite);
+		}
+
+		// No renderer implementation is present.
+		return null;
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/RendererClientTest.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/RendererClientTest.java
new file mode 100644
index 000000000..6995503fd
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/client/RendererClientTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.test.renderer.simple.client;
+
+import net.minecraft.client.render.RenderLayer;
+
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
+import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
+import net.fabricmc.fabric.test.renderer.simple.RendererTest;
+
+public final class RendererClientTest implements ClientModInitializer {
+	@Override
+	public void onInitializeClient() {
+		ModelLoadingRegistry.INSTANCE.registerResourceProvider(manager -> new FrameModelResourceProvider());
+		BlockRenderLayerMap.INSTANCE.putBlock(RendererTest.FRAME, RenderLayer.getCutoutMipped());
+	}
+}
diff --git a/fabric-renderer-api-v1/src/testmod/resources/assets/fabric-renderer-api-v1-testmod/blockstates/frame.json b/fabric-renderer-api-v1/src/testmod/resources/assets/fabric-renderer-api-v1-testmod/blockstates/frame.json
new file mode 100644
index 000000000..cb52a6f0c
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/resources/assets/fabric-renderer-api-v1-testmod/blockstates/frame.json
@@ -0,0 +1,5 @@
+{
+  "variants": {
+    "": { "model": "fabric-renderer-api-v1-testmod:block/frame" }
+  }
+}
diff --git a/fabric-renderer-api-v1/src/testmod/resources/fabric-renderer-api-v1-testmod.mixins.json b/fabric-renderer-api-v1/src/testmod/resources/fabric-renderer-api-v1-testmod.mixins.json
new file mode 100644
index 000000000..fa6f2b25c
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/resources/fabric-renderer-api-v1-testmod.mixins.json
@@ -0,0 +1,14 @@
+{
+  "required": true,
+  "package": "net.fabricmc.fabric.test.renderer.mixin",
+  "compatibilityLevel": "JAVA_8",
+  "mixins": [
+    "WorldMixin"
+  ],
+  "client": [
+    "ClientWorldMixin"
+  ],
+  "injectors": {
+    "defaultRequire": 1
+  }
+}
diff --git a/fabric-renderer-api-v1/src/testmod/resources/fabric.mod.json b/fabric-renderer-api-v1/src/testmod/resources/fabric.mod.json
new file mode 100644
index 000000000..45037a1a9
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/resources/fabric.mod.json
@@ -0,0 +1,23 @@
+{
+  "schemaVersion": 1,
+  "id": "fabric-renderer-api-v1-testmod",
+  "name": "Fabric Renderer API (v1) Test Mod",
+  "version": "1.0.0",
+  "environment": "*",
+  "license": "Apache-2.0",
+  "depends": {
+    "fabric-renderer-api-v1":"*",
+    "fabric-resource-loader-v0": "*"
+  },
+  "entrypoints": {
+    "main": [
+      "net.fabricmc.fabric.test.renderer.simple.RendererTest"
+    ],
+    "client": [
+      "net.fabricmc.fabric.test.renderer.simple.client.RendererClientTest"
+    ]
+  },
+  "mixins": [
+    "fabric-renderer-api-v1-testmod.mixins.json"
+  ]
+}
diff --git a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json
index 61ab693b4..5f8ecc40d 100644
--- a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json
+++ b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json
@@ -9,9 +9,6 @@
     "fabric-rendering-v1": "*"
   },
   "entrypoints": {
-    "main": [
-      "net.fabricmc.fabric.test.rendering.CustomArmorTests"
-    ],
     "client": [
       "net.fabricmc.fabric.test.rendering.client.WorldRenderEventsTests"
     ]