mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-28 16:06:28 +01:00
Fix virtual model rendering
- Add various utility classes to allow FRAPI-compatible virtual rendering - Fix PartialModel using field instead of getter method
This commit is contained in:
parent
1ee11c0af9
commit
09f3c495e9
8 changed files with 252 additions and 12 deletions
|
@ -28,6 +28,13 @@ repositories {
|
||||||
maven {
|
maven {
|
||||||
url 'https://maven.parchmentmc.org/'
|
url 'https://maven.parchmentmc.org/'
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name 'Modrinth'
|
||||||
|
url 'https://api.modrinth.com/maven'
|
||||||
|
}
|
||||||
|
maven {
|
||||||
|
url 'https://maven.vram.io'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -44,6 +51,8 @@ dependencies {
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"
|
||||||
|
|
||||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
modCompileOnly 'maven.modrinth:indium:1.0.2-alpha1+mc1.18'
|
||||||
|
modCompileOnly 'io.vram:frex-fabric-mc118:6.0.229'
|
||||||
|
|
||||||
//implementation 'org.joml:joml:1.10.1'
|
//implementation 'org.joml:joml:1.10.1'
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class PartialModel {
|
||||||
|
|
||||||
public static void onModelRegistry(ResourceManager manager, Consumer<ResourceLocation> out) {
|
public static void onModelRegistry(ResourceManager manager, Consumer<ResourceLocation> out) {
|
||||||
for (PartialModel partial : ALL)
|
for (PartialModel partial : ALL)
|
||||||
out.accept(partial.modelLocation);
|
out.accept(partial.getLocation());
|
||||||
|
|
||||||
tooLate = true;
|
tooLate = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
|
|
||||||
|
// TODO Fabric
|
||||||
public class BakedModelModel implements Model {
|
public class BakedModelModel implements Model {
|
||||||
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
|
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
|
||||||
private static final Direction[] dirs;
|
private static final Direction[] dirs;
|
||||||
|
@ -44,7 +45,6 @@ public class BakedModelModel implements Model {
|
||||||
|
|
||||||
for (Direction dir : dirs) {
|
for (Direction dir : dirs) {
|
||||||
random.setSeed(42);
|
random.setSeed(42);
|
||||||
// TODO
|
|
||||||
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
||||||
|
|
||||||
numQuads += quads.size();
|
numQuads += quads.size();
|
||||||
|
@ -69,7 +69,6 @@ public class BakedModelModel implements Model {
|
||||||
|
|
||||||
for (Direction dir : dirs) {
|
for (Direction dir : dirs) {
|
||||||
random.setSeed(42);
|
random.setSeed(42);
|
||||||
// TODO
|
|
||||||
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
||||||
|
|
||||||
for (BakedQuad bakedQuad : quads) {
|
for (BakedQuad bakedQuad : quads) {
|
||||||
|
|
|
@ -4,6 +4,9 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.fabric.model.CullingBakedModel;
|
||||||
|
import com.jozufozu.flywheel.fabric.model.DefaultLayerFilteringBakedModel;
|
||||||
|
import com.jozufozu.flywheel.fabric.model.LayerFilteringBakedModel;
|
||||||
import com.jozufozu.flywheel.util.Lazy;
|
import com.jozufozu.flywheel.util.Lazy;
|
||||||
import com.jozufozu.flywheel.util.VirtualEmptyBlockGetter;
|
import com.jozufozu.flywheel.util.VirtualEmptyBlockGetter;
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
@ -12,7 +15,6 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||||
|
@ -25,7 +27,6 @@ import net.minecraft.world.level.block.RenderShape;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||||
|
|
||||||
// TODO
|
|
||||||
public class ModelUtil {
|
public class ModelUtil {
|
||||||
private static final Lazy<ModelBlockRenderer> MODEL_RENDERER = Lazy.of(() -> new ModelBlockRenderer(Minecraft.getInstance().getBlockColors()));
|
private static final Lazy<ModelBlockRenderer> MODEL_RENDERER = Lazy.of(() -> new ModelBlockRenderer(Minecraft.getInstance().getBlockColors()));
|
||||||
|
|
||||||
|
@ -50,8 +51,9 @@ public class ModelUtil {
|
||||||
// .collect(Collectors.toList());
|
// .collect(Collectors.toList());
|
||||||
|
|
||||||
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
model = DefaultLayerFilteringBakedModel.wrap(model);
|
||||||
blockRenderer.tesselateBlock(VirtualEmptyBlockGetter.INSTANCE, model, referenceState, BlockPos.ZERO, ms, builder,
|
blockRenderer.tesselateBlock(VirtualEmptyBlockGetter.INSTANCE, model, referenceState, BlockPos.ZERO, ms, builder,
|
||||||
true, new Random(), 42, OverlayTexture.NO_OVERLAY);
|
false, new Random(), 42, OverlayTexture.NO_OVERLAY);
|
||||||
builder.end();
|
builder.end();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
@ -65,27 +67,25 @@ public class ModelUtil {
|
||||||
BufferBuilder builder = new BufferBuilder(512);
|
BufferBuilder builder = new BufferBuilder(512);
|
||||||
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||||
|
|
||||||
// ForgeHooksClient.setRenderType(layer);
|
|
||||||
ModelBlockRenderer.enableCaching();
|
ModelBlockRenderer.enableCaching();
|
||||||
for (StructureTemplate.StructureBlockInfo info : blocks) {
|
for (StructureTemplate.StructureBlockInfo info : blocks) {
|
||||||
BlockState state = info.state;
|
BlockState state = info.state;
|
||||||
|
|
||||||
if (state.getRenderShape() != RenderShape.MODEL)
|
if (state.getRenderShape() != RenderShape.MODEL)
|
||||||
continue;
|
continue;
|
||||||
// if (!ItemBlockRenderTypes.canRenderInLayer(state, layer))
|
|
||||||
if (ItemBlockRenderTypes.getChunkRenderType(state) != layer)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
BlockPos pos = info.pos;
|
BlockPos pos = info.pos;
|
||||||
|
|
||||||
ms.pushPose();
|
ms.pushPose();
|
||||||
ms.translate(pos.getX(), pos.getY(), pos.getZ());
|
ms.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||||
modelRenderer.tesselateBlock(renderWorld, blockModels.getBlockModel(state), state, pos, ms, builder,
|
BakedModel model = blockModels.getBlockModel(state);
|
||||||
|
model = CullingBakedModel.wrap(model);
|
||||||
|
model = LayerFilteringBakedModel.wrap(model, layer);
|
||||||
|
modelRenderer.tesselateBlock(renderWorld, model, state, pos, ms, builder,
|
||||||
true, random, 42, OverlayTexture.NO_OVERLAY);
|
true, random, 42, OverlayTexture.NO_OVERLAY);
|
||||||
ms.popPose();
|
ms.popPose();
|
||||||
}
|
}
|
||||||
ModelBlockRenderer.clearCache();
|
ModelBlockRenderer.clearCache();
|
||||||
// ForgeHooksClient.setRenderType(null);
|
|
||||||
|
|
||||||
builder.end();
|
builder.end();
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.jozufozu.flywheel.fabric.model;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class CullingBakedModel extends ForwardingBakedModel {
|
||||||
|
private static final ThreadLocal<CullingBakedModel> THREAD_LOCAL = ThreadLocal.withInitial(CullingBakedModel::new);
|
||||||
|
|
||||||
|
protected int completionFlags = 0;
|
||||||
|
protected int resultFlags = 0;
|
||||||
|
|
||||||
|
protected final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
||||||
|
|
||||||
|
public static BakedModel wrap(BakedModel model) {
|
||||||
|
if (!FabricModelUtil.FREX_LOADED && !((FabricBakedModel) model).isVanillaAdapter()) {
|
||||||
|
CullingBakedModel wrapper = THREAD_LOCAL.get();
|
||||||
|
wrapper.wrapped = model;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CullingBakedModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
completionFlags = 0;
|
||||||
|
resultFlags = 0;
|
||||||
|
context.pushTransform(quad -> {
|
||||||
|
Direction cullFace = quad.cullFace();
|
||||||
|
if (cullFace != null) {
|
||||||
|
int mask = 1 << cullFace.ordinal();
|
||||||
|
if ((completionFlags & mask) == 0) {
|
||||||
|
completionFlags |= mask;
|
||||||
|
if (Block.shouldRenderFace(state, blockView, pos, cullFace, mutablePos.setWithOffset(pos, cullFace))) {
|
||||||
|
resultFlags |= mask;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (resultFlags & mask) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
super.emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||||
|
context.popTransform();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.jozufozu.flywheel.fabric.model;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class DefaultLayerFilteringBakedModel extends ForwardingBakedModel {
|
||||||
|
private static final ThreadLocal<DefaultLayerFilteringBakedModel> THREAD_LOCAL = ThreadLocal.withInitial(DefaultLayerFilteringBakedModel::new);
|
||||||
|
|
||||||
|
public static BakedModel wrap(BakedModel model) {
|
||||||
|
if (!((FabricBakedModel) model).isVanillaAdapter()) {
|
||||||
|
DefaultLayerFilteringBakedModel wrapper = THREAD_LOCAL.get();
|
||||||
|
wrapper.wrapped = model;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DefaultLayerFilteringBakedModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
context.pushTransform(DefaultLayerFilteringBakedModel::hasDefaultBlendMode);
|
||||||
|
super.emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||||
|
context.popTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
context.pushTransform(DefaultLayerFilteringBakedModel::hasDefaultBlendMode);
|
||||||
|
super.emitItemQuads(stack, randomSupplier, context);
|
||||||
|
context.popTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasDefaultBlendMode(QuadView quad) {
|
||||||
|
return FabricModelUtil.getBlendMode(quad.material()) == BlendMode.DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.jozufozu.flywheel.fabric.model;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
|
||||||
|
import io.vram.frex.api.material.MaterialConstants;
|
||||||
|
import io.vram.frex.fabric.compat.FabricMaterial;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
|
||||||
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
|
|
||||||
|
public class FabricModelUtil {
|
||||||
|
public static final boolean INDIUM_LOADED = FabricLoader.getInstance().isModLoaded("indium");
|
||||||
|
public static final boolean FREX_LOADED = FabricLoader.getInstance().isModLoaded("frex");
|
||||||
|
|
||||||
|
private static final BlendModeGetter BLEND_MODE_GETTER = createBlendModeGetter();
|
||||||
|
|
||||||
|
private static BlendModeGetter createBlendModeGetter() {
|
||||||
|
if (FREX_LOADED) {
|
||||||
|
try {
|
||||||
|
Field frexMaterialField = FabricMaterial.class.getDeclaredField("wrapped");
|
||||||
|
frexMaterialField.setAccessible(true);
|
||||||
|
return material -> {
|
||||||
|
try {
|
||||||
|
io.vram.frex.api.material.RenderMaterial frexMaterial = (io.vram.frex.api.material.RenderMaterial) frexMaterialField.get(material);
|
||||||
|
return switch (frexMaterial.preset()) {
|
||||||
|
case MaterialConstants.PRESET_DEFAULT -> BlendMode.DEFAULT;
|
||||||
|
case MaterialConstants.PRESET_SOLID -> BlendMode.SOLID;
|
||||||
|
case MaterialConstants.PRESET_CUTOUT_MIPPED -> BlendMode.CUTOUT_MIPPED;
|
||||||
|
case MaterialConstants.PRESET_CUTOUT -> BlendMode.CUTOUT;
|
||||||
|
case MaterialConstants.PRESET_TRANSLUCENT -> BlendMode.TRANSLUCENT;
|
||||||
|
case MaterialConstants.PRESET_NONE -> {
|
||||||
|
if (frexMaterial.transparency() != MaterialConstants.TRANSPARENCY_NONE) {
|
||||||
|
yield BlendMode.TRANSLUCENT;
|
||||||
|
} else if (frexMaterial.cutout() == MaterialConstants.CUTOUT_NONE) {
|
||||||
|
yield BlendMode.SOLID;
|
||||||
|
} else {
|
||||||
|
yield frexMaterial.unmipped() ? BlendMode.CUTOUT : BlendMode.CUTOUT_MIPPED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> BlendMode.DEFAULT;
|
||||||
|
};
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
return BlendMode.DEFAULT;
|
||||||
|
};
|
||||||
|
} catch (Exception e) {
|
||||||
|
Flywheel.log.error("Detected FREX but failed to load wrapper field.", e);
|
||||||
|
return material -> BlendMode.DEFAULT;
|
||||||
|
}
|
||||||
|
} else if (INDIUM_LOADED) {
|
||||||
|
return material -> ((link.infra.indium.renderer.RenderMaterialImpl) material).blendMode(0);
|
||||||
|
} else {
|
||||||
|
return material -> ((RenderMaterialImpl) material).blendMode(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlendMode getBlendMode(RenderMaterial material) {
|
||||||
|
return BLEND_MODE_GETTER.getBlendMode(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface BlendModeGetter {
|
||||||
|
BlendMode getBlendMode(RenderMaterial material);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.jozufozu.flywheel.fabric.model;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
|
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public class LayerFilteringBakedModel extends ForwardingBakedModel {
|
||||||
|
private static final ThreadLocal<LayerFilteringBakedModel> THREAD_LOCAL = ThreadLocal.withInitial(LayerFilteringBakedModel::new);
|
||||||
|
|
||||||
|
protected RenderType targetLayer;
|
||||||
|
|
||||||
|
public static BakedModel wrap(BakedModel model, RenderType layer) {
|
||||||
|
LayerFilteringBakedModel wrapper = THREAD_LOCAL.get();
|
||||||
|
wrapper.wrapped = model;
|
||||||
|
wrapper.targetLayer = layer;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LayerFilteringBakedModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVanillaAdapter() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state);
|
||||||
|
if (((FabricBakedModel) wrapped).isVanillaAdapter()) {
|
||||||
|
if (defaultLayer == targetLayer) {
|
||||||
|
super.emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.pushTransform(quad -> {
|
||||||
|
RenderType quadLayer = FabricModelUtil.getBlendMode(quad.material()).blockRenderLayer;
|
||||||
|
if (quadLayer == null) {
|
||||||
|
quadLayer = defaultLayer;
|
||||||
|
}
|
||||||
|
return quadLayer == targetLayer;
|
||||||
|
});
|
||||||
|
super.emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||||
|
context.popTransform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue