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.
+ *
+ *
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..91c3b625f
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlock.java
@@ -0,0 +1,92 @@
+/*
+ * 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.BlockView;
+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.inventory.offerOrDrop(world, 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.is(Blocks.AIR)) {
+ return ActionResult.FAIL;
+ }
+
+ // Do not allow blocks that may have a block entity
+ if (handBlock.hasBlockEntity()) {
+ return ActionResult.FAIL;
+ }
+
+ if (currentBlock != null) {
+ player.inventory.offerOrDrop(world, new ItemStack(currentBlock));
+ }
+
+ ((FrameBlockEntity) blockEntity).setBlock(handBlock);
+ return ActionResult.SUCCESS;
+ }
+
+ return ActionResult.FAIL;
+ }
+
+ @Nullable
+ @Override
+ public BlockEntity createBlockEntity(BlockView world) {
+ return new FrameBlockEntity();
+ }
+}
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..e9a01cd77
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/FrameBlockEntity.java
@@ -0,0 +1,107 @@
+/*
+ * 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.CompoundTag;
+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() {
+ super(RendererTest.FRAME_BLOCK_ENTITY);
+ }
+
+ @Override
+ public void fromTag(BlockState state, CompoundTag tag) {
+ super.fromTag(state, tag);
+
+ if (tag.contains("block", NbtType.STRING)) {
+ this.block = Registry.BLOCK.get(new Identifier(tag.getString("block")));
+ }
+ }
+
+ @Override
+ public CompoundTag toTag(CompoundTag tag) {
+ if (this.block != null) {
+ tag.putString("block", Registry.BLOCK.getId(this.block).toString());
+ }
+
+ return super.toTag(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(CompoundTag 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 CompoundTag toClientTag(CompoundTag 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..36b0a4449
--- /dev/null
+++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/simple/RendererTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * 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.
+ *
+ *
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 FRAME_BLOCK_ENTITY = BlockEntityType.Builder.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 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 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 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 getModelDependencies() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection getTextureDependencies(Function unbakedModelGetter, Set> 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 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"
+ ]
+}