mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 23:47:09 +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 {
|
||||
url 'https://maven.parchmentmc.org/'
|
||||
}
|
||||
maven {
|
||||
name 'Modrinth'
|
||||
url 'https://api.modrinth.com/maven'
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.vram.io'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -44,6 +51,8 @@ dependencies {
|
|||
modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}"
|
||||
|
||||
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'
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public class PartialModel {
|
|||
|
||||
public static void onModelRegistry(ResourceManager manager, Consumer<ResourceLocation> out) {
|
||||
for (PartialModel partial : ALL)
|
||||
out.accept(partial.modelLocation);
|
||||
out.accept(partial.getLocation());
|
||||
|
||||
tooLate = true;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import net.minecraft.client.resources.model.BakedModel;
|
|||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
|
||||
// TODO Fabric
|
||||
public class BakedModelModel implements Model {
|
||||
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
|
||||
private static final Direction[] dirs;
|
||||
|
@ -44,7 +45,6 @@ public class BakedModelModel implements Model {
|
|||
|
||||
for (Direction dir : dirs) {
|
||||
random.setSeed(42);
|
||||
// TODO
|
||||
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
||||
|
||||
numQuads += quads.size();
|
||||
|
@ -69,7 +69,6 @@ public class BakedModelModel implements Model {
|
|||
|
||||
for (Direction dir : dirs) {
|
||||
random.setSeed(42);
|
||||
// TODO
|
||||
List<BakedQuad> quads = model.getQuads(null, dir, random/*, VirtualEmptyModelData.INSTANCE*/);
|
||||
|
||||
for (BakedQuad bakedQuad : quads) {
|
||||
|
|
|
@ -4,6 +4,9 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
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.VirtualEmptyBlockGetter;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
|
@ -12,7 +15,6 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.BlockModelShaper;
|
||||
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.levelgen.structure.templatesystem.StructureTemplate;
|
||||
|
||||
// TODO
|
||||
public class ModelUtil {
|
||||
private static final Lazy<ModelBlockRenderer> MODEL_RENDERER = Lazy.of(() -> new ModelBlockRenderer(Minecraft.getInstance().getBlockColors()));
|
||||
|
||||
|
@ -50,8 +51,9 @@ public class ModelUtil {
|
|||
// .collect(Collectors.toList());
|
||||
|
||||
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||
model = DefaultLayerFilteringBakedModel.wrap(model);
|
||||
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();
|
||||
return builder;
|
||||
}
|
||||
|
@ -65,27 +67,25 @@ public class ModelUtil {
|
|||
BufferBuilder builder = new BufferBuilder(512);
|
||||
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||
|
||||
// ForgeHooksClient.setRenderType(layer);
|
||||
ModelBlockRenderer.enableCaching();
|
||||
for (StructureTemplate.StructureBlockInfo info : blocks) {
|
||||
BlockState state = info.state;
|
||||
|
||||
if (state.getRenderShape() != RenderShape.MODEL)
|
||||
continue;
|
||||
// if (!ItemBlockRenderTypes.canRenderInLayer(state, layer))
|
||||
if (ItemBlockRenderTypes.getChunkRenderType(state) != layer)
|
||||
continue;
|
||||
|
||||
BlockPos pos = info.pos;
|
||||
|
||||
ms.pushPose();
|
||||
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);
|
||||
ms.popPose();
|
||||
}
|
||||
ModelBlockRenderer.clearCache();
|
||||
// ForgeHooksClient.setRenderType(null);
|
||||
|
||||
builder.end();
|
||||
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