From a19f8e7ad600117d405bff3e09cef92d7844bc4e Mon Sep 17 00:00:00 2001 From: Ocelot Date: Fri, 5 Apr 2024 18:41:00 -0600 Subject: [PATCH] Added tessellation example --- README.md | 4 +- build.gradle | 99 ++++++++--------- gradle.properties | 11 +- .../mixin/client/ExampleClientMixin.java | 15 --- src/client/resources/modid.client.mixins.json | 1 - src/main/java/com/example/ExampleMod.java | 22 ---- .../java/com/example/mixin/ExampleMixin.java | 15 --- .../foundry/veil/example/VeilExampleMod.java | 35 ++++++ .../veil/example/VeilExampleModClient.java | 20 ++++ .../foundry/veil/example/block/MapBlock.java | 57 ++++++++++ .../example/blockentity/MapBlockEntity.java | 18 ++++ .../client/render/MapBlockEntityRenderer.java | 101 ++++++++++++++++++ .../client/render/MapBlockItemRenderer.java | 21 ++++ .../example/editor/VeilExampleModEditor.java | 99 +++++++++++++++++ .../example/registry/VeilExampleBlocks.java | 36 +++++++ .../example/registry/VeilExampleItems.java | 30 ++++++ .../registry/VeilExampleRenderTypes.java | 53 +++++++++ src/main/resources/assets/modid/icon.png | Bin 453 -> 0 bytes .../veil-example-mod/blockstates/map.json | 7 ++ .../assets/veil-example-mod/lang/en_us.json | 4 + .../veil-example-mod/models/block/map.json | 6 ++ .../veil-example-mod/models/item/map.json | 35 ++++++ .../pinwheel/shaders/program/map.fsh | 10 ++ .../pinwheel/shaders/program/map.json | 20 ++++ .../pinwheel/shaders/program/map.tcsh | 49 +++++++++ .../pinwheel/shaders/program/map.tesh | 58 ++++++++++ .../pinwheel/shaders/program/map.vsh | 9 ++ .../pinwheel/shaders/program/map_texture.json | 4 + .../textures/misc/heightmap.png | Bin 0 -> 37173 bytes .../textures/misc/heightmap.png.mcmeta | 6 ++ src/main/resources/fabric.mod.json | 29 ++--- ...xins.json => veil-example-mod.mixins.json} | 1 - 32 files changed, 740 insertions(+), 135 deletions(-) delete mode 100644 src/client/java/com/example/mixin/client/ExampleClientMixin.java delete mode 100644 src/main/java/com/example/ExampleMod.java delete mode 100644 src/main/java/com/example/mixin/ExampleMixin.java create mode 100644 src/main/java/foundry/veil/example/VeilExampleMod.java create mode 100644 src/main/java/foundry/veil/example/VeilExampleModClient.java create mode 100644 src/main/java/foundry/veil/example/block/MapBlock.java create mode 100644 src/main/java/foundry/veil/example/blockentity/MapBlockEntity.java create mode 100644 src/main/java/foundry/veil/example/client/render/MapBlockEntityRenderer.java create mode 100644 src/main/java/foundry/veil/example/client/render/MapBlockItemRenderer.java create mode 100644 src/main/java/foundry/veil/example/editor/VeilExampleModEditor.java create mode 100644 src/main/java/foundry/veil/example/registry/VeilExampleBlocks.java create mode 100644 src/main/java/foundry/veil/example/registry/VeilExampleItems.java create mode 100644 src/main/java/foundry/veil/example/registry/VeilExampleRenderTypes.java delete mode 100644 src/main/resources/assets/modid/icon.png create mode 100644 src/main/resources/assets/veil-example-mod/blockstates/map.json create mode 100644 src/main/resources/assets/veil-example-mod/lang/en_us.json create mode 100644 src/main/resources/assets/veil-example-mod/models/block/map.json create mode 100644 src/main/resources/assets/veil-example-mod/models/item/map.json create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.fsh create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.json create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tcsh create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tesh create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.vsh create mode 100644 src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map_texture.json create mode 100644 src/main/resources/assets/veil-example-mod/textures/misc/heightmap.png create mode 100644 src/main/resources/assets/veil-example-mod/textures/misc/heightmap.png.mcmeta rename src/main/resources/{modid.mixins.json => veil-example-mod.mixins.json} (89%) diff --git a/README.md b/README.md index fd96346..1b602e6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Fabric Example Mod +# Veil Example Mod ## Setup -For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using. +For setup instructions, please see the [Veil wiki page](https://github.com/FoundryMC/Veil/wiki). ## License diff --git a/build.gradle b/build.gradle index a14d12b..d848c40 100644 --- a/build.gradle +++ b/build.gradle @@ -1,87 +1,76 @@ plugins { - id 'fabric-loom' version '1.6-SNAPSHOT' - id 'maven-publish' + id 'fabric-loom' version '1.6-SNAPSHOT' } version = project.mod_version group = project.maven_group base { - archivesName = project.archives_base_name + archivesName = project.archives_base_name } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. -} - -loom { - splitEnvironmentSourceSets() - - mods { - "modid" { - sourceSet sourceSets.main - sourceSet sourceSets.client + maven { + name = 'BlameJared Maven (CrT / Bookshelf)' + url = 'https://maven.blamejared.com' + } + exclusiveContent { + forRepository { + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + } + } + filter { + includeGroup "maven.modrinth" } } +} +loom { + mods { + "veil-example-mod" { + sourceSet sourceSets.main + } + } } dependencies { - // To change the versions see the gradle.properties file - minecraft "com.mojang:minecraft:${project.minecraft_version}" - mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" - modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings loom.officialMojangMappings() + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - // Fabric API. This is technically optional, but you probably want it anyway. - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + modImplementation("foundry.veil:Veil-fabric-${project.minecraft_version}:${project.veil_version}") { transitive = false } + modRuntimeOnly "maven.modrinth:modmenu:7.2.2" } processResources { - inputs.property "version", project.version + inputs.property "version", project.version - filesMatching("fabric.mod.json") { - expand "version": project.version - } + filesMatching("fabric.mod.json") { + expand "version": project.version + } } tasks.withType(JavaCompile).configureEach { - it.options.release = 17 + it.options.release = 17 } java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task - // if it is present. - // If you remove this line, sources will not be generated. - withSourcesJar() + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } jar { - from("LICENSE") { - rename { "${it}_${project.base.archivesName.get()}"} - } + from("LICENSE") { + rename { "${it}_${project.base.archivesName.get()}" } + } } - -// configure the maven publication -publishing { - publications { - create("mavenJava", MavenPublication) { - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - // Notice: This block does NOT have the same function as the block in the top level. - // The repositories here will be used for publishing your artifact, not for - // retrieving dependencies. - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 43f1184..4a1a2df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,14 +4,15 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 +minecraft_version=1.20.1 +yarn_mappings=1.20.1+build.10 loader_version=0.15.9 # Mod Properties mod_version=1.0.0 -maven_group=com.example -archives_base_name=modid +maven_group=foundry +archives_base_name=veil-example-mod # Dependencies -fabric_version=0.96.11+1.20.4 \ No newline at end of file +fabric_version=0.92.0+1.20.1 +veil_version=1.0.0.139 \ No newline at end of file diff --git a/src/client/java/com/example/mixin/client/ExampleClientMixin.java b/src/client/java/com/example/mixin/client/ExampleClientMixin.java deleted file mode 100644 index 061b0ef..0000000 --- a/src/client/java/com/example/mixin/client/ExampleClientMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.mixin.client; - -import net.minecraft.client.MinecraftClient; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(MinecraftClient.class) -public class ExampleClientMixin { - @Inject(at = @At("HEAD"), method = "run") - private void run(CallbackInfo info) { - // This code is injected into the start of MinecraftClient.run()V - } -} \ No newline at end of file diff --git a/src/client/resources/modid.client.mixins.json b/src/client/resources/modid.client.mixins.json index 21b0fc1..39434e9 100644 --- a/src/client/resources/modid.client.mixins.json +++ b/src/client/resources/modid.client.mixins.json @@ -3,7 +3,6 @@ "package": "com.example.mixin.client", "compatibilityLevel": "JAVA_17", "client": [ - "ExampleClientMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/java/com/example/ExampleMod.java b/src/main/java/com/example/ExampleMod.java deleted file mode 100644 index f97cce9..0000000 --- a/src/main/java/com/example/ExampleMod.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.example; - -import net.fabricmc.api.ModInitializer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ExampleMod implements ModInitializer { - // This logger is used to write text to the console and the log file. - // It is considered best practice to use your mod id as the logger's name. - // That way, it's clear which mod wrote info, warnings, and errors. - public static final Logger LOGGER = LoggerFactory.getLogger("modid"); - - @Override - public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. - - LOGGER.info("Hello Fabric world!"); - } -} \ No newline at end of file diff --git a/src/main/java/com/example/mixin/ExampleMixin.java b/src/main/java/com/example/mixin/ExampleMixin.java deleted file mode 100644 index 3c4212c..0000000 --- a/src/main/java/com/example/mixin/ExampleMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.example.mixin; - -import net.minecraft.server.MinecraftServer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(MinecraftServer.class) -public class ExampleMixin { - @Inject(at = @At("HEAD"), method = "loadWorld") - private void init(CallbackInfo info) { - // This code is injected into the start of MinecraftServer.loadWorld()V - } -} \ No newline at end of file diff --git a/src/main/java/foundry/veil/example/VeilExampleMod.java b/src/main/java/foundry/veil/example/VeilExampleMod.java new file mode 100644 index 0000000..42d1d68 --- /dev/null +++ b/src/main/java/foundry/veil/example/VeilExampleMod.java @@ -0,0 +1,35 @@ +package foundry.veil.example; + +import foundry.veil.example.registry.VeilExampleBlocks; +import foundry.veil.example.registry.VeilExampleItems; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VeilExampleMod implements ModInitializer { + + public static final String MODID = "veil-example-mod"; + public static final Logger LOGGER = LoggerFactory.getLogger(MODID); + + private static final ResourceKey ITEM_GROUP = ResourceKey.create(Registries.CREATIVE_MODE_TAB, VeilExampleMod.path("items")); + + public static ResourceLocation path(String path) { + return new ResourceLocation(MODID, path); + } + + @Override + public void onInitialize() { + VeilExampleItems.bootstrap(); + VeilExampleBlocks.bootstrap(); + Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, ITEM_GROUP, FabricItemGroup.builder().title(Component.translatable(MODID + ".items")).icon(() -> new ItemStack(VeilExampleBlocks.MAP)).displayItems(VeilExampleItems::fillTab).build()); + } +} \ No newline at end of file diff --git a/src/main/java/foundry/veil/example/VeilExampleModClient.java b/src/main/java/foundry/veil/example/VeilExampleModClient.java new file mode 100644 index 0000000..cbade8d --- /dev/null +++ b/src/main/java/foundry/veil/example/VeilExampleModClient.java @@ -0,0 +1,20 @@ +package foundry.veil.example; + +import foundry.veil.example.client.render.MapBlockEntityRenderer; +import foundry.veil.example.client.render.MapBlockItemRenderer; +import foundry.veil.example.editor.VeilExampleModEditor; +import foundry.veil.example.registry.VeilExampleBlocks; +import foundry.veil.fabric.event.FabricVeilRendererEvent; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; + +public class VeilExampleModClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + BuiltinItemRendererRegistry.INSTANCE.register(VeilExampleBlocks.MAP, new MapBlockItemRenderer()); + BlockEntityRenderers.register(VeilExampleBlocks.MAP_BE, MapBlockEntityRenderer::new); + FabricVeilRendererEvent.EVENT.register(renderer -> renderer.getEditorManager().add(new VeilExampleModEditor())); + } +} \ No newline at end of file diff --git a/src/main/java/foundry/veil/example/block/MapBlock.java b/src/main/java/foundry/veil/example/block/MapBlock.java new file mode 100644 index 0000000..ab69ddb --- /dev/null +++ b/src/main/java/foundry/veil/example/block/MapBlock.java @@ -0,0 +1,57 @@ +package foundry.veil.example.block; + +import foundry.veil.example.blockentity.MapBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.SimpleWaterloggedBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class MapBlock extends Block implements EntityBlock, SimpleWaterloggedBlock { + + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + + public MapBlock(Properties properties) { + super(properties); + this.registerDefaultState(this.stateDefinition.any().setValue(WATERLOGGED, Boolean.FALSE)); + } + + @Override + public RenderShape getRenderShape(BlockState blockState) { + return RenderShape.ENTITYBLOCK_ANIMATED; + } + + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new MapBlockEntity(pos, state); + } + + @Override + public VoxelShape getVisualShape(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext) { + return Shapes.empty(); + } + + @Override + public boolean propagatesSkylightDown(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos) { + return blockState.getFluidState().isEmpty(); + } + + @Override + public float getShadeBrightness(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos) { + return 1.0F; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(WATERLOGGED); + } +} diff --git a/src/main/java/foundry/veil/example/blockentity/MapBlockEntity.java b/src/main/java/foundry/veil/example/blockentity/MapBlockEntity.java new file mode 100644 index 0000000..ad6d3ef --- /dev/null +++ b/src/main/java/foundry/veil/example/blockentity/MapBlockEntity.java @@ -0,0 +1,18 @@ +package foundry.veil.example.blockentity; + +import foundry.veil.example.registry.VeilExampleBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class MapBlockEntity extends BlockEntity { + + public MapBlockEntity(BlockPos blockPos, BlockState blockState) { + super(VeilExampleBlocks.MAP_BE, blockPos, blockState); + } + +// @Override +// public Packet getUpdatePacket() { +// return ClientboundBlockEntityDataPacket.create(this); +// } +} diff --git a/src/main/java/foundry/veil/example/client/render/MapBlockEntityRenderer.java b/src/main/java/foundry/veil/example/client/render/MapBlockEntityRenderer.java new file mode 100644 index 0000000..bd44cc2 --- /dev/null +++ b/src/main/java/foundry/veil/example/client/render/MapBlockEntityRenderer.java @@ -0,0 +1,101 @@ +package foundry.veil.example.client.render; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import foundry.veil.api.client.render.VeilRenderSystem; +import foundry.veil.api.client.render.shader.program.ShaderProgram; +import foundry.veil.example.blockentity.MapBlockEntity; +import foundry.veil.example.editor.VeilExampleModEditor; +import foundry.veil.example.registry.VeilExampleRenderTypes; +import imgui.ImGui; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import org.lwjgl.opengl.GL11C; +import org.lwjgl.opengl.GL13C; +import org.lwjgl.opengl.GL32C; + +import static org.lwjgl.opengl.GL11C.*; +import static org.lwjgl.opengl.GL13C.GL_TEXTURE0; + +public class MapBlockEntityRenderer implements BlockEntityRenderer { + + private final VertexBuffer vbo; + + public MapBlockEntityRenderer(BlockEntityRendererProvider.Context context) { + this.vbo = new VertexBuffer(VertexBuffer.Usage.STATIC); + this.vbo.bind(); + this.vbo.upload(render(20)); + VertexBuffer.unbind(); + } + + @Override + public void render(MapBlockEntity blockEntity, float partialTicks, PoseStack poseStack, MultiBufferSource source, int light, int overlay) { + RenderType renderType = VeilExampleRenderTypes.heightmap(VeilExampleModEditor.useTessellation()); + + renderType.setupRenderState(); + ShaderProgram shader = VeilRenderSystem.getShader(); + if (shader == null) { + renderType.clearRenderState(); + return; + } + + PoseStack modelViewStack = RenderSystem.getModelViewStack(); + modelViewStack.pushPose(); + modelViewStack.mulPoseMatrix(poseStack.last().pose()); +// modelViewStack.scale(25, 2, 25); + + this.vbo.bind(); + this.vbo.upload(render(20)); + + shader.applyRenderSystem(); + shader.setMatrix("ModelViewMat", modelViewStack.last().pose()); + shader.setup(); + if (VeilExampleModEditor.tessellationWireframe()) { + glPolygonMode(GL_FRONT_AND_BACK, GL11C.GL_LINE); + } + glEnable(GL32C.GL_DEPTH_CLAMP); + RenderSystem.activeTexture(GL_TEXTURE0); + RenderSystem.bindTexture(Minecraft.getInstance().getMainRenderTarget().getColorTextureId()); + this.vbo.draw(); + glDisable(GL32C.GL_DEPTH_CLAMP); + if (VeilExampleModEditor.tessellationWireframe()) { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + renderType.clearRenderState(); + + VertexBuffer.unbind(); + + modelViewStack.popPose(); + } + + private static BufferBuilder.RenderedBuffer render(int resolution) { + Tesselator tesselator = RenderSystem.renderThreadTesselator(); + BufferBuilder builder = tesselator.getBuilder(); + builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); + + for (int z = 0; z <= resolution - 1; z++) { + for (int x = 0; x <= resolution - 1; x++) { + builder.vertex(x / (float) resolution, 0, z / (float) resolution) + .uv(x / (float) resolution, z / (float) resolution) + .endVertex(); + + builder.vertex((x + 1) / (float) resolution, 0, z / (float) resolution) + .uv((x + 1) / (float) resolution, z / (float) resolution) + .endVertex(); + + builder.vertex(x / (float) resolution, 0, (z + 1) / (float) resolution) + .uv(x / (float) resolution, (z + 1) / (float) resolution) + .endVertex(); + + builder.vertex((x + 1) / (float) resolution, 0, (z + 1) / (float) resolution) + .uv((x + 1) / (float) resolution, (z + 1) / (float) resolution) + .endVertex(); + } + } + + return builder.end(); + } +} diff --git a/src/main/java/foundry/veil/example/client/render/MapBlockItemRenderer.java b/src/main/java/foundry/veil/example/client/render/MapBlockItemRenderer.java new file mode 100644 index 0000000..43fceac --- /dev/null +++ b/src/main/java/foundry/veil/example/client/render/MapBlockItemRenderer.java @@ -0,0 +1,21 @@ +package foundry.veil.example.client.render; + +import com.mojang.blaze3d.vertex.PoseStack; +import foundry.veil.example.blockentity.MapBlockEntity; +import foundry.veil.example.registry.VeilExampleBlocks; +import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; + +public class MapBlockItemRenderer implements BuiltinItemRendererRegistry.DynamicItemRenderer { + + private static final MapBlockEntity BE = new MapBlockEntity(BlockPos.ZERO, VeilExampleBlocks.MAP.defaultBlockState()); + + @Override + public void render(ItemStack stack, ItemDisplayContext mode, PoseStack poseStack, MultiBufferSource source, int light, int overlay) { + Minecraft.getInstance().getBlockEntityRenderDispatcher().renderItem(BE, poseStack, source, light, overlay); + } +} diff --git a/src/main/java/foundry/veil/example/editor/VeilExampleModEditor.java b/src/main/java/foundry/veil/example/editor/VeilExampleModEditor.java new file mode 100644 index 0000000..7679baa --- /dev/null +++ b/src/main/java/foundry/veil/example/editor/VeilExampleModEditor.java @@ -0,0 +1,99 @@ +package foundry.veil.example.editor; + +import foundry.veil.api.client.editor.SingleWindowEditor; +import foundry.veil.api.client.render.VeilRenderSystem; +import foundry.veil.api.client.render.shader.definition.ShaderPreDefinitions; +import imgui.ImGui; +import imgui.type.ImBoolean; +import org.jetbrains.annotations.Nullable; + +public class VeilExampleModEditor extends SingleWindowEditor { + + private static final int[] minTessLevel = new int[]{4}; + private static final int[] maxTessLevel = new int[]{64}; + private static final int[] minDistance = new int[]{1}; + private static final int[] maxDistance = new int[]{6}; + private static final ImBoolean useTessellation = new ImBoolean(true); + private static final ImBoolean tessellationWireframe = new ImBoolean(false); + + @Override + protected void renderComponents() { + ShaderPreDefinitions definitions = VeilRenderSystem.renderer().getShaderDefinitions(); + + if (ImGui.beginTabBar("Examples")) { + for (Example value : Example.values()) { + if (ImGui.beginTabItem(value.name + " Example")) { + if (value == Example.TESSELLATION) { + ImGui.checkbox("Use Tessellation", useTessellation); + ImGui.sameLine(); + ImGui.checkbox("Wireframe", tessellationWireframe); + if (ImGui.dragInt("Min Tessellation Level", minTessLevel, 1, 1, Integer.MAX_VALUE)) { + definitions.define("MIN_TESS_LEVEL", String.valueOf(minTessLevel[0])); + } + if (ImGui.dragInt("Max Tessellation Level", maxTessLevel, 1, minTessLevel[0], Integer.MAX_VALUE)) { + definitions.define("MAX_TESS_LEVEL", String.valueOf(maxTessLevel[0])); + } + if (ImGui.dragInt("Min Distance", minDistance, 1, 0, Integer.MAX_VALUE)) { + definitions.define("MIN_DISTANCE", String.valueOf(minDistance[0])); + } + if (ImGui.dragInt("Max Distance", maxDistance, 1, minDistance[0], Integer.MAX_VALUE)) { + definitions.define("MAX_DISTANCE", String.valueOf(maxDistance[0])); + } + } + } + ImGui.endTabItem(); + + if (ImGui.isItemHovered()) { + ImGui.beginTooltip(); + ImGui.pushTextWrapPos(ImGui.getFontSize() * 35.0f); + ImGui.textUnformatted(value.tooltip); + ImGui.popTextWrapPos(); + ImGui.endTooltip(); + } + } + + ImGui.endTabBar(); + } + } + + @Override + public String getDisplayName() { + return "Veil Example Mod"; + } + + public static boolean useTessellation() { + return useTessellation.get(); + } + + public static boolean tessellationWireframe() { + return tessellationWireframe.get(); + } + + public static int getMinTessLevel() { + return minTessLevel[0]; + } + + public static int getMaxTessLevel() { + return maxTessLevel[0]; + } + + public static int getMinDistance() { + return minDistance[0]; + } + + public static int getMaxDistance() { + return maxDistance[0]; + } + + private enum Example { + TESSELLATION("Tessellation", ""); + + private final String name; + private final String tooltip; + + Example(String name, @Nullable String tooltip) { + this.name = name; + this.tooltip = tooltip; + } + } +} diff --git a/src/main/java/foundry/veil/example/registry/VeilExampleBlocks.java b/src/main/java/foundry/veil/example/registry/VeilExampleBlocks.java new file mode 100644 index 0000000..e574990 --- /dev/null +++ b/src/main/java/foundry/veil/example/registry/VeilExampleBlocks.java @@ -0,0 +1,36 @@ +package foundry.veil.example.registry; + +import foundry.veil.example.VeilExampleMod; +import foundry.veil.example.block.MapBlock; +import foundry.veil.example.blockentity.MapBlockEntity; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; + +public class VeilExampleBlocks { + + public static final Block MAP = register("map", new MapBlock(BlockBehaviour.Properties.copy(Blocks.BEDROCK) + .noOcclusion() + .isRedstoneConductor(Blocks::never) + .isSuffocating(Blocks::never)), + new Item.Properties()); + public static final BlockEntityType MAP_BE = Registry.register(BuiltInRegistries.BLOCK_ENTITY_TYPE, VeilExampleMod.path("map"), BlockEntityType.Builder.of(MapBlockEntity::new, MAP).build(null)); + + public static void bootstrap() { + } + + public static T register(String name, T block, Item.Properties properties) { + register(name, block); + VeilExampleItems.register(name, new BlockItem(block, properties)); + return block; + } + + public static Block register(String name, Block block) { + return Registry.register(BuiltInRegistries.BLOCK, VeilExampleMod.path(name), block); + } +} diff --git a/src/main/java/foundry/veil/example/registry/VeilExampleItems.java b/src/main/java/foundry/veil/example/registry/VeilExampleItems.java new file mode 100644 index 0000000..79116b2 --- /dev/null +++ b/src/main/java/foundry/veil/example/registry/VeilExampleItems.java @@ -0,0 +1,30 @@ +package foundry.veil.example.registry; + +import foundry.veil.example.VeilExampleMod; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +public class VeilExampleItems { + + public static final Set ITEMS = new HashSet<>(); + + public static void bootstrap() { + } + + public static T register(String name, T item) { + T value = Registry.register(BuiltInRegistries.ITEM, VeilExampleMod.path(name), item); + ITEMS.add(value); + return value; + } + + public static void fillTab(CreativeModeTab.ItemDisplayParameters context, CreativeModeTab.Output output) { + ITEMS.stream().sorted(Comparator.comparing(BuiltInRegistries.ITEM::getKey)).forEach(item -> output.accept(new ItemStack(item))); + } +} diff --git a/src/main/java/foundry/veil/example/registry/VeilExampleRenderTypes.java b/src/main/java/foundry/veil/example/registry/VeilExampleRenderTypes.java new file mode 100644 index 0000000..5e62e6c --- /dev/null +++ b/src/main/java/foundry/veil/example/registry/VeilExampleRenderTypes.java @@ -0,0 +1,53 @@ +package foundry.veil.example.registry; + +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import foundry.veil.api.client.registry.RenderTypeStageRegistry; +import foundry.veil.api.client.render.VeilRenderBridge; +import foundry.veil.example.VeilExampleMod; +import net.minecraft.client.renderer.RenderType; + +public final class VeilExampleRenderTypes extends RenderType { + + private static final ShaderStateShard MAP_SHADER = VeilRenderBridge.shaderState(VeilExampleMod.path("map")); + private static final ShaderStateShard MAP_TEXTURE_SHADER = VeilRenderBridge.shaderState(VeilExampleMod.path("map_texture")); + + private static final RenderType HEIGHTMAP_TEXTURE = create( + VeilExampleMod.MODID + ":heightmap", + DefaultVertexFormat.POSITION_TEX, + VertexFormat.Mode.QUADS, + TRANSIENT_BUFFER_SIZE, + true, + false, + RenderType.CompositeState.builder() + .setLightmapState(LIGHTMAP) + .setShaderState(MAP_TEXTURE_SHADER) + .setTextureState(new TextureStateShard(VeilExampleMod.path("textures/misc/heightmap.png"), true, false)) + .createCompositeState(true) + ); + private static final RenderType HEIGHTMAP_TESSELLATION = create( + VeilExampleMod.MODID + ":heightmap", + DefaultVertexFormat.POSITION_TEX, + VertexFormat.Mode.QUADS, + TRANSIENT_BUFFER_SIZE, + true, + false, + RenderType.CompositeState.builder() + .setLightmapState(LIGHTMAP) + .setShaderState(MAP_SHADER) + .setTextureState(new TextureStateShard(VeilExampleMod.path("textures/misc/heightmap.png"), true, false)) + .createCompositeState(true) + ); + + static { + RenderTypeStageRegistry.addStage(HEIGHTMAP_TESSELLATION, VeilRenderBridge.patchState(4)); + } + + private VeilExampleRenderTypes(String string, VertexFormat vertexFormat, VertexFormat.Mode mode, int i, boolean bl, boolean bl2, Runnable runnable, Runnable runnable2) { + super(string, vertexFormat, mode, i, bl, bl2, runnable, runnable2); + } + + public static RenderType heightmap(boolean tessellation) { + return tessellation ? HEIGHTMAP_TESSELLATION : HEIGHTMAP_TEXTURE; + } +} diff --git a/src/main/resources/assets/modid/icon.png b/src/main/resources/assets/modid/icon.png deleted file mode 100644 index 047b91f2347de5cf95f23284476fddbe21ba23fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 453 zcmV;$0XqJPP)QAFYGys`80vegN0XDFh0OXKz&i8?Le#x7{1X)R+00000NkvXXu0mjf73i~T diff --git a/src/main/resources/assets/veil-example-mod/blockstates/map.json b/src/main/resources/assets/veil-example-mod/blockstates/map.json new file mode 100644 index 0000000..d2bed70 --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/blockstates/map.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "veil-example-mod:block/map" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/lang/en_us.json b/src/main/resources/assets/veil-example-mod/lang/en_us.json new file mode 100644 index 0000000..80846b2 --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/lang/en_us.json @@ -0,0 +1,4 @@ +{ + "veil-example-mod.items": "Veil Example", + "block.veil-example-mod.map": "Tesselation Map Example" +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/models/block/map.json b/src/main/resources/assets/veil-example-mod/models/block/map.json new file mode 100644 index 0000000..c3b046c --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/models/block/map.json @@ -0,0 +1,6 @@ +{ + "parent": "builtin/entity", + "textures": { + "particle": "minecraft:block/obsidian" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/models/item/map.json b/src/main/resources/assets/veil-example-mod/models/item/map.json new file mode 100644 index 0000000..165fabd --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/models/item/map.json @@ -0,0 +1,35 @@ +{ + "parent": "veil-example-mod:block/map", + "display": { + "gui": { + "rotation": [ 30, 45, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.625, 0.625, 0.625 ] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 3, 0], + "scale":[ 0.25, 0.25, 0.25 ] + }, + "head": { + "rotation": [ 0, 180, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 1, 1, 1] + }, + "fixed": { + "rotation": [ 0, 180, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.5, 0.5, 0.5 ] + }, + "thirdperson_righthand": { + "rotation": [ 75, 315, 0 ], + "translation": [ 0, 2.5, 0], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 315, 0 ], + "translation": [ 0, 0, 0], + "scale": [ 0.4, 0.4, 0.4 ] + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.fsh b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.fsh new file mode 100644 index 0000000..397c1fa --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.fsh @@ -0,0 +1,10 @@ +uniform sampler2D Sampler0; +uniform vec4 ColorModulator; + +in vec2 fragTexCoord; + +out vec4 OutColor; + +void main() { + OutColor = texture(Sampler0, fragTexCoord) * ColorModulator; +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.json b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.json new file mode 100644 index 0000000..200d8bd --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.json @@ -0,0 +1,20 @@ +{ + "vertex": "veil-example-mod:map", + "tesselation_control": "veil-example-mod:map", + "tesselation_evaluation": "veil-example-mod:map", + "fragment": "veil-example-mod:map", + "definitions": [ + { + "MIN_TESS_LEVEL": 4 + }, + { + "MAX_TESS_LEVEL": 64 + }, + { + "MIN_DISTANCE": 1 + }, + { + "MAX_DISTANCE": 6 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tcsh b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tcsh new file mode 100644 index 0000000..5640f94 --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tcsh @@ -0,0 +1,49 @@ +#include veil:camera + +layout (vertices=4) out; + +uniform mat4 ModelViewMat; + +in vec2 texCoord[]; + +out vec2 TextureCoord[]; + +void main() { + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + TextureCoord[gl_InvocationID] = texCoord[gl_InvocationID]; + + if (gl_InvocationID == 0) { + // ---------------------------------------------------------------------- + // Step 2: transform each vertex into eye space + vec4 eyeSpacePos00 = ModelViewMat * gl_in[0].gl_Position; + vec4 eyeSpacePos01 = ModelViewMat * gl_in[1].gl_Position; + vec4 eyeSpacePos10 = ModelViewMat * gl_in[2].gl_Position; + vec4 eyeSpacePos11 = ModelViewMat * gl_in[3].gl_Position; + + // ---------------------------------------------------------------------- + // Step 3: "distance" from camera scaled between 0 and 1 + float distance00 = clamp((abs(eyeSpacePos00.z)-MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0); + float distance01 = clamp((abs(eyeSpacePos01.z)-MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0); + float distance10 = clamp((abs(eyeSpacePos10.z)-MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0); + float distance11 = clamp((abs(eyeSpacePos11.z)-MIN_DISTANCE) / (MAX_DISTANCE-MIN_DISTANCE), 0.0, 1.0); + + // ---------------------------------------------------------------------- + // Step 4: interpolate edge tessellation level based on closer vertex + float tessLevel0 = floor(mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance10, distance00))); + float tessLevel1 = floor(mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance00, distance01))); + float tessLevel2 = floor(mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance01, distance11))); + float tessLevel3 = floor(mix(MAX_TESS_LEVEL, MIN_TESS_LEVEL, min(distance11, distance10))); + + // ---------------------------------------------------------------------- + // Step 5: set the corresponding outer edge tessellation levels + gl_TessLevelOuter[0] = tessLevel0; + gl_TessLevelOuter[1] = tessLevel1; + gl_TessLevelOuter[2] = tessLevel2; + gl_TessLevelOuter[3] = tessLevel3; + + // ---------------------------------------------------------------------- + // Step 6: set the inner tessellation levels to the max of the two parallel edges + gl_TessLevelInner[0] = max(tessLevel1, tessLevel3); + gl_TessLevelInner[1] = max(tessLevel0, tessLevel2); + } +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tesh b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tesh new file mode 100644 index 0000000..3910cae --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.tesh @@ -0,0 +1,58 @@ +#include veil:camera + +layout (quads, fractional_odd_spacing, cw) in; + +uniform mat4 ModelViewMat; + +uniform sampler2D Sampler0; + +in vec2 TextureCoord[]; + +out vec2 fragTexCoord; + +void main() +{ + // get patch coordinate + float u = gl_TessCoord.x; + float v = gl_TessCoord.y; + + // ---------------------------------------------------------------------- + // retrieve control point texture coordinates + vec2 t00 = TextureCoord[0]; + vec2 t01 = TextureCoord[1]; + vec2 t10 = TextureCoord[2]; + vec2 t11 = TextureCoord[3]; + + // bilinearly interpolate texture coordinate across patch + vec2 t0 = (t01 - t00) * u + t00; + vec2 t1 = (t11 - t10) * u + t10; + vec2 texCoord = (t1 - t0) * v + t0; + + // lookup texel at patch coordinate for height and scale + shift as desired + float height = texture(Sampler0, texCoord).y; + fragTexCoord = texCoord; + + // ---------------------------------------------------------------------- + // retrieve control point position coordinates + vec4 p00 = gl_in[0].gl_Position; + vec4 p01 = gl_in[1].gl_Position; + vec4 p10 = gl_in[2].gl_Position; + vec4 p11 = gl_in[3].gl_Position; + + // compute patch surface normal + vec4 uVec = p01 - p00; + vec4 vVec = p10 - p00; + vec4 normal = normalize(vec4(cross(vVec.xyz, uVec.xyz), 0)); + + // bilinearly interpolate position coordinate across patch + vec4 p0 = (p01 - p00) * u + p00; + vec4 p1 = (p11 - p10) * u + p10; + vec4 p = (p1 - p0) * v + p0; + + // displace point along normal + p += normal * height; + + // ---------------------------------------------------------------------- + // output patch point position in clip space + gl_Position = VeilCamera.ProjMat * ModelViewMat * p; +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.vsh b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.vsh new file mode 100644 index 0000000..0414887 --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map.vsh @@ -0,0 +1,9 @@ +layout(location = 0) in vec3 Position; +layout(location = 1) in vec2 UV; + +out vec2 texCoord; + +void main() { + gl_Position = vec4(Position, 1.0); + texCoord = UV; +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map_texture.json b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map_texture.json new file mode 100644 index 0000000..d5174b7 --- /dev/null +++ b/src/main/resources/assets/veil-example-mod/pinwheel/shaders/program/map_texture.json @@ -0,0 +1,4 @@ +{ + "vertex": "veil-example-mod:map", + "fragment": "veil-example-mod:map" +} \ No newline at end of file diff --git a/src/main/resources/assets/veil-example-mod/textures/misc/heightmap.png b/src/main/resources/assets/veil-example-mod/textures/misc/heightmap.png new file mode 100644 index 0000000000000000000000000000000000000000..9991a742f69d543f55e50c6ca75eaaac2f183c6f GIT binary patch literal 37173 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.15.9", - "minecraft": "~1.20.4", + "minecraft": "~1.20.1", + "veil": ">=1.0.0", "java": ">=17", "fabric-api": "*" - }, - "suggests": { - "another-mod": "*" } } \ No newline at end of file diff --git a/src/main/resources/modid.mixins.json b/src/main/resources/veil-example-mod.mixins.json similarity index 89% rename from src/main/resources/modid.mixins.json rename to src/main/resources/veil-example-mod.mixins.json index 166e787..a34ea90 100644 --- a/src/main/resources/modid.mixins.json +++ b/src/main/resources/veil-example-mod.mixins.json @@ -3,7 +3,6 @@ "package": "com.example.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ - "ExampleMixin" ], "injectors": { "defaultRequire": 1