mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-03 08:44:57 +01:00
Best-effort untested Fabric model builders
- Also fix crash buffering fluids in BakedModelBufferer#bufferMultiBlock (Forge) - The mesh order in models created by model builders is currently incorrect and will be fixed later
This commit is contained in:
parent
9ae4065c1c
commit
b9490fe11a
19 changed files with 853 additions and 219 deletions
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
|
|||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
|
@ -14,25 +15,26 @@ import net.minecraft.client.resources.model.BakedModel;
|
|||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public abstract class BakedModelBuilder {
|
||||
protected final BakedModel bakedModel;
|
||||
final BakedModel bakedModel;
|
||||
@Nullable
|
||||
protected BlockAndTintGetter level;
|
||||
BlockAndTintGetter level;
|
||||
@Nullable
|
||||
protected BlockState blockState;
|
||||
BlockState blockState;
|
||||
@Nullable
|
||||
protected PoseStack poseStack;
|
||||
PoseStack poseStack;
|
||||
@Nullable
|
||||
protected BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
|
||||
BakedModelBuilder(BakedModel bakedModel) {
|
||||
this.bakedModel = bakedModel;
|
||||
}
|
||||
|
||||
public static BakedModelBuilder create(BakedModel bakedModel) {
|
||||
return FlwLibXplat.INSTANCE.createBakedModelBuilder(bakedModel);
|
||||
}
|
||||
|
||||
protected BakedModelBuilder(BakedModel bakedModel) {
|
||||
this.bakedModel = bakedModel;
|
||||
}
|
||||
|
||||
public BakedModelBuilder level(BlockAndTintGetter level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
|
|||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
|
@ -13,23 +14,24 @@ import net.minecraft.client.renderer.RenderType;
|
|||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public abstract class BlockModelBuilder {
|
||||
protected final BlockState state;
|
||||
final BlockState state;
|
||||
@Nullable
|
||||
protected BlockAndTintGetter level;
|
||||
BlockAndTintGetter level;
|
||||
@Nullable
|
||||
protected PoseStack poseStack;
|
||||
PoseStack poseStack;
|
||||
@Nullable
|
||||
protected BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
|
||||
BlockModelBuilder(BlockState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public static BlockModelBuilder create(BlockState state) {
|
||||
return FlwLibXplat.INSTANCE.createBlockModelBuilder(state);
|
||||
}
|
||||
|
||||
protected BlockModelBuilder(BlockState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public BlockModelBuilder level(BlockAndTintGetter level) {
|
||||
this.level = level;
|
||||
return this;
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
|
||||
class MeshEmitter implements VertexConsumer {
|
||||
protected final BufferBuilder bufferBuilder;
|
||||
private final RenderType renderType;
|
||||
private boolean lastQuadWasShaded;
|
||||
private boolean seenFirstQuad;
|
||||
@Nullable
|
||||
private MeshEmitter.ResultConsumer resultConsumer;
|
||||
|
||||
MeshEmitter(BufferBuilder bufferBuilder, RenderType renderType) {
|
||||
this.bufferBuilder = bufferBuilder;
|
||||
this.renderType = renderType;
|
||||
}
|
||||
|
||||
public void begin(ResultConsumer resultConsumer) {
|
||||
this.resultConsumer = resultConsumer;
|
||||
|
||||
begin();
|
||||
}
|
||||
|
||||
public void end() {
|
||||
emit();
|
||||
seenFirstQuad = false;
|
||||
resultConsumer = null;
|
||||
}
|
||||
|
||||
private void begin() {
|
||||
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||
}
|
||||
|
||||
private void emit() {
|
||||
var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty();
|
||||
|
||||
if (renderedBuffer != null) {
|
||||
if (resultConsumer != null) {
|
||||
resultConsumer.accept(renderType, lastQuadWasShaded, renderedBuffer);
|
||||
}
|
||||
renderedBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
protected void observeQuadAndEmitIfNecessary(BakedQuad quad) {
|
||||
if (seenFirstQuad && lastQuadWasShaded != quad.isShade()) {
|
||||
emit();
|
||||
begin();
|
||||
}
|
||||
|
||||
seenFirstQuad = true;
|
||||
lastQuadWasShaded = quad.isShade();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose poseEntry, BakedQuad quad, float[] colorMuls, float red, float green, float blue, int[] combinedLights, int combinedOverlay, boolean mulColor) {
|
||||
observeQuadAndEmitIfNecessary(quad);
|
||||
|
||||
bufferBuilder.putBulkData(poseEntry, quad, colorMuls, red, green, blue, combinedLights, combinedOverlay, mulColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer vertex(double x, double y, double z) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer color(int red, int green, int blue, int alpha) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv(float u, float v) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer overlayCoords(int u, int v) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv2(int u, int v) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer normal(float x, float y, float z) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVertex() {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultColor(int red, int green, int blue, int alpha) {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetDefaultColor() {
|
||||
throw new UnsupportedOperationException("ShadeSeparatingVertexConsumer only supports putBulkData!");
|
||||
}
|
||||
|
||||
public interface ResultConsumer {
|
||||
void accept(RenderType renderType, boolean shaded, BufferBuilder.RenderedBuffer data);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.lib.model.baked;
|
|||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
|
@ -13,24 +14,25 @@ import net.minecraft.client.renderer.RenderType;
|
|||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public abstract class MultiBlockModelBuilder {
|
||||
protected final BlockAndTintGetter level;
|
||||
protected final Iterable<BlockPos> positions;
|
||||
final BlockAndTintGetter level;
|
||||
final Iterable<BlockPos> positions;
|
||||
@Nullable
|
||||
protected PoseStack poseStack;
|
||||
protected boolean renderFluids = false;
|
||||
PoseStack poseStack;
|
||||
boolean renderFluids = false;
|
||||
@Nullable
|
||||
protected BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
|
||||
MultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
|
||||
this.level = level;
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public static MultiBlockModelBuilder create(BlockAndTintGetter level, Iterable<BlockPos> positions) {
|
||||
return FlwLibXplat.INSTANCE.createMultiBlockModelBuilder(level, positions);
|
||||
}
|
||||
|
||||
protected MultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
|
||||
this.level = level;
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public MultiBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||
this.poseStack = poseStack;
|
||||
return this;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
|
@ -8,7 +9,9 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
class TransformingVertexConsumer implements VertexConsumer {
|
||||
@UnknownNullability
|
||||
private VertexConsumer delegate;
|
||||
@UnknownNullability
|
||||
private PoseStack poseStack;
|
||||
|
||||
public void prepare(VertexConsumer delegate, PoseStack poseStack) {
|
||||
|
|
|
@ -15,7 +15,7 @@ import net.minecraft.core.SectionPos;
|
|||
import net.minecraft.world.level.LightLayer;
|
||||
|
||||
@Mixin(ClientChunkCache.class)
|
||||
public class ClientChunkCacheMixin {
|
||||
abstract class ClientChunkCacheMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
ClientLevel level;
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
|
||||
final class BakedModelBufferer {
|
||||
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
|
||||
private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length;
|
||||
|
||||
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
||||
|
||||
private BakedModelBufferer() {
|
||||
}
|
||||
|
||||
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ResultConsumer resultConsumer) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
RandomSource random = objects.random;
|
||||
MeshEmitter[] emitters = objects.emitters;
|
||||
UniversalMeshEmitter universalEmitter = objects.universalEmitter;
|
||||
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.prepare(resultConsumer);
|
||||
}
|
||||
|
||||
RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state);
|
||||
universalEmitter.prepare(defaultLayer);
|
||||
model = universalEmitter.wrapModel(model);
|
||||
|
||||
poseStack.pushPose();
|
||||
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, universalEmitter, false, random, 42L, OverlayTexture.NO_OVERLAY);
|
||||
poseStack.popPose();
|
||||
|
||||
universalEmitter.clear();
|
||||
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.end();
|
||||
}
|
||||
}
|
||||
|
||||
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ResultConsumer resultConsumer) {
|
||||
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
bufferSingle(renderDispatcher.getModelRenderer(), level, renderDispatcher.getBlockModel(state), state, poseStack, resultConsumer);
|
||||
}
|
||||
|
||||
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, boolean renderFluids, ResultConsumer resultConsumer) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
RandomSource random = objects.random;
|
||||
MeshEmitter[] emitters = objects.emitters;
|
||||
Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap = objects.emitterMap;
|
||||
UniversalMeshEmitter universalEmitter = objects.universalEmitter;
|
||||
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
|
||||
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.prepare(resultConsumer);
|
||||
}
|
||||
|
||||
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
||||
ModelBlockRenderer.enableCaching();
|
||||
|
||||
while (posIterator.hasNext()) {
|
||||
BlockPos pos = posIterator.next();
|
||||
BlockState state = level.getBlockState(pos);
|
||||
|
||||
if (renderFluids) {
|
||||
FluidState fluidState = state.getFluidState();
|
||||
|
||||
if (!fluidState.isEmpty()) {
|
||||
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||
|
||||
transformingWrapper.prepare(emitterMap.get(renderType).getBuffer(true), poseStack);
|
||||
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
|
||||
renderDispatcher.renderLiquid(pos, level, transformingWrapper, state, fluidState);
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
if (state.getRenderShape() == RenderShape.MODEL) {
|
||||
long seed = state.getSeed(pos);
|
||||
BakedModel model = renderDispatcher.getBlockModel(state);
|
||||
|
||||
RenderType defaultLayer = ItemBlockRenderTypes.getChunkRenderType(state);
|
||||
universalEmitter.prepare(defaultLayer);
|
||||
model = universalEmitter.wrapModel(model);
|
||||
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
|
||||
blockRenderer.tesselateBlock(level, model, state, pos, poseStack, universalEmitter, true, random, seed, OverlayTexture.NO_OVERLAY);
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
ModelBlockRenderer.clearCache();
|
||||
transformingWrapper.clear();
|
||||
universalEmitter.clear();
|
||||
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.end();
|
||||
}
|
||||
}
|
||||
|
||||
public interface ResultConsumer {
|
||||
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
||||
}
|
||||
|
||||
private static class ThreadLocalObjects {
|
||||
public final PoseStack identityPoseStack = new PoseStack();
|
||||
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
|
||||
|
||||
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||
public final Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap = new Reference2ReferenceOpenHashMap<>();
|
||||
public final UniversalMeshEmitter universalEmitter;
|
||||
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
||||
|
||||
{
|
||||
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||
MeshEmitter emitter = new MeshEmitter(renderType);
|
||||
emitters[layerIndex] = emitter;
|
||||
emitterMap.put(renderType, emitter);
|
||||
}
|
||||
universalEmitter = new UniversalMeshEmitter(emitterMap);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexView;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public final class FabricBakedModelBuilder extends BakedModelBuilder {
|
||||
public FabricBakedModelBuilder(BakedModel bakedModel) {
|
||||
super(bakedModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBakedModelBuilder level(BlockAndTintGetter level) {
|
||||
super.level(level);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBakedModelBuilder blockState(BlockState blockState) {
|
||||
super.blockState(blockState);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBakedModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (level == null) {
|
||||
level = VirtualEmptyBlockGetter.INSTANCE;
|
||||
}
|
||||
if (blockState == null) {
|
||||
blockState = Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView);
|
||||
var mesh = new SimpleMesh(vertexView, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexView;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public final class FabricBlockModelBuilder extends BlockModelBuilder {
|
||||
public FabricBlockModelBuilder(BlockState state) {
|
||||
super(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBlockModelBuilder level(BlockAndTintGetter level) {
|
||||
super.level(level);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (level == null) {
|
||||
level = VirtualEmptyBlockGetter.INSTANCE;
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView);
|
||||
var mesh = new SimpleMesh(vertexView, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexView;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
|
||||
public final class FabricMultiBlockModelBuilder extends MultiBlockModelBuilder {
|
||||
public FabricMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
|
||||
super(level, positions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricMultiBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricMultiBlockModelBuilder enableFluidRendering() {
|
||||
super.enableFluidRendering();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricMultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, renderFluids, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView);
|
||||
var mesh = new SimpleMesh(vertexView, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
|
||||
class MeshEmitter {
|
||||
private final RenderType renderType;
|
||||
private final BufferBuilder bufferBuilder;
|
||||
|
||||
private BakedModelBufferer.@UnknownNullability ResultConsumer resultConsumer;
|
||||
private boolean currentShade;
|
||||
|
||||
MeshEmitter(RenderType renderType) {
|
||||
this.renderType = renderType;
|
||||
this.bufferBuilder = new BufferBuilder(renderType.bufferSize());
|
||||
}
|
||||
|
||||
public void prepare(BakedModelBufferer.ResultConsumer resultConsumer) {
|
||||
this.resultConsumer = resultConsumer;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
if (bufferBuilder.building()) {
|
||||
emit();
|
||||
}
|
||||
resultConsumer = null;
|
||||
}
|
||||
|
||||
public BufferBuilder getBuffer(boolean shade) {
|
||||
prepareForGeometry(shade);
|
||||
return bufferBuilder;
|
||||
}
|
||||
|
||||
void prepareForGeometry(boolean shade) {
|
||||
if (!bufferBuilder.building()) {
|
||||
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||
} else if (shade != currentShade) {
|
||||
emit();
|
||||
}
|
||||
|
||||
currentShade = shade;
|
||||
}
|
||||
|
||||
void emit() {
|
||||
var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty();
|
||||
|
||||
if (renderedBuffer != null) {
|
||||
resultConsumer.accept(renderType, currentShade, renderedBuffer);
|
||||
renderedBuffer.release();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
class UniversalMeshEmitter implements VertexConsumer {
|
||||
private final Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap;
|
||||
private final WrapperModel wrapperModel = new WrapperModel();
|
||||
|
||||
@UnknownNullability
|
||||
private RenderType defaultLayer;
|
||||
@UnknownNullability
|
||||
private BufferBuilder currentDelegate;
|
||||
|
||||
UniversalMeshEmitter(Reference2ReferenceMap<RenderType, MeshEmitter> emitterMap) {
|
||||
this.emitterMap = emitterMap;
|
||||
}
|
||||
|
||||
public void prepare(RenderType defaultLayer) {
|
||||
this.defaultLayer = defaultLayer;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
wrapperModel.setWrapped(null);
|
||||
}
|
||||
|
||||
public BakedModel wrapModel(BakedModel model) {
|
||||
wrapperModel.setWrapped(model);
|
||||
return wrapperModel;
|
||||
}
|
||||
|
||||
private void prepareForGeometry(RenderMaterial material) {
|
||||
BlendMode blendMode = material.blendMode();
|
||||
RenderType layer = blendMode == BlendMode.DEFAULT ? defaultLayer : blendMode.blockRenderLayer;
|
||||
boolean shade = !material.disableDiffuse();
|
||||
currentDelegate = emitterMap.get(layer).getBuffer(shade);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer vertex(double x, double y, double z) {
|
||||
currentDelegate.vertex(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer color(int red, int green, int blue, int alpha) {
|
||||
currentDelegate.color(red, green, blue, alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv(float u, float v) {
|
||||
currentDelegate.uv(u, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer overlayCoords(int u, int v) {
|
||||
currentDelegate.overlayCoords(u, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv2(int u, int v) {
|
||||
currentDelegate.uv2(u, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer normal(float x, float y, float z) {
|
||||
currentDelegate.normal(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVertex() {
|
||||
currentDelegate.endVertex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultColor(int red, int green, int blue, int alpha) {
|
||||
currentDelegate.defaultColor(red, green, blue, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetDefaultColor() {
|
||||
currentDelegate.unsetDefaultColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void vertex(float x, float y, float z, float red, float green, float blue, float alpha, float u, float v, int overlay, int light, float normalX, float normalY, float normalZ) {
|
||||
currentDelegate.vertex(x, y, z, red, green, blue, alpha, u, v, overlay, light, normalX, normalY, normalZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int light, int overlay) {
|
||||
currentDelegate.putBulkData(pose, quad, red, green, blue, light, overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, int[] lights, int overlay, boolean readExistingColor) {
|
||||
currentDelegate.putBulkData(pose, quad, brightnesses, red, green, blue, lights, overlay, readExistingColor);
|
||||
}
|
||||
|
||||
private class WrapperModel extends ForwardingBakedModel {
|
||||
private final RenderContext.QuadTransform quadTransform = quad -> {
|
||||
UniversalMeshEmitter.this.prepareForGeometry(quad.material());
|
||||
return true;
|
||||
};
|
||||
|
||||
public void setWrapped(@Nullable BakedModel wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockAndTintGetter level, BlockState state, BlockPos pos, Supplier<RandomSource> randomSupplier, RenderContext context) {
|
||||
context.pushTransform(quadTransform);
|
||||
super.emitBlockQuads(level, state, pos, randomSupplier, context);
|
||||
context.popTransform();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ import org.jetbrains.annotations.Nullable;
|
|||
import com.jozufozu.flywheel.lib.internal.FlwLibXplat;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BlockModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.FabricBakedModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.FabricBlockModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.FabricMultiBlockModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.MultiBlockModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||
|
||||
|
@ -25,17 +28,17 @@ public class FlwLibXplatImpl implements FlwLibXplat {
|
|||
|
||||
@Override
|
||||
public BakedModelBuilder createBakedModelBuilder(BakedModel bakedModel) {
|
||||
return null;
|
||||
return new FabricBakedModelBuilder(bakedModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockModelBuilder createBlockModelBuilder(BlockState state) {
|
||||
return null;
|
||||
return new FabricBlockModelBuilder(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
|
||||
return null;
|
||||
return new FabricMultiBlockModelBuilder(level, positions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,7 +5,6 @@ import java.util.function.Function;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
|
@ -33,13 +32,13 @@ final class BakedModelBufferer {
|
|||
private BakedModelBufferer() {
|
||||
}
|
||||
|
||||
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, MeshEmitter.ResultConsumer resultConsumer) {
|
||||
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BakedModel model, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
RandomSource random = objects.random;
|
||||
var consumers = objects.emitters;
|
||||
MeshEmitter[] emitters = objects.emitters;
|
||||
|
||||
modelData = model.getModelData(level, BlockPos.ZERO, state, modelData);
|
||||
random.setSeed(42L);
|
||||
|
@ -47,19 +46,19 @@ final class BakedModelBufferer {
|
|||
|
||||
for (RenderType renderType : renderTypes) {
|
||||
int layerIndex = renderType.getChunkLayerId();
|
||||
var consumer = consumers[layerIndex];
|
||||
MeshEmitter emitter = emitters[layerIndex];
|
||||
|
||||
consumer.begin(resultConsumer);
|
||||
emitter.prepare(resultConsumer);
|
||||
|
||||
poseStack.pushPose();
|
||||
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, consumer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
|
||||
blockRenderer.tesselateBlock(level, model, state, BlockPos.ZERO, poseStack, emitter, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
|
||||
poseStack.popPose();
|
||||
|
||||
consumer.end();
|
||||
emitter.end();
|
||||
}
|
||||
}
|
||||
|
||||
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, MeshEmitter.ResultConsumer resultConsumer) {
|
||||
public static void bufferBlock(BlockRenderDispatcher renderDispatcher, BlockAndTintGetter level, BlockState state, @Nullable PoseStack poseStack, ModelData modelData, ResultConsumer resultConsumer) {
|
||||
if (state.getRenderShape() != RenderShape.MODEL) {
|
||||
return;
|
||||
}
|
||||
|
@ -67,18 +66,17 @@ final class BakedModelBufferer {
|
|||
bufferSingle(renderDispatcher.getModelRenderer(), level, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
|
||||
}
|
||||
|
||||
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, MeshEmitter.ResultConsumer resultConsumer) {
|
||||
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter level, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ResultConsumer resultConsumer) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
RandomSource random = objects.random;
|
||||
MeshEmitter[] emitters = objects.emitters;
|
||||
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
|
||||
|
||||
var emitters = objects.emitters;
|
||||
|
||||
for (var emitter : emitters) {
|
||||
emitter.begin(resultConsumer);
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.prepare(resultConsumer);
|
||||
}
|
||||
|
||||
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
|
||||
|
@ -92,10 +90,10 @@ final class BakedModelBufferer {
|
|||
FluidState fluidState = state.getFluidState();
|
||||
|
||||
if (!fluidState.isEmpty()) {
|
||||
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||
int layerIndex = layer.getChunkLayerId();
|
||||
RenderType renderType = ItemBlockRenderTypes.getRenderLayer(fluidState);
|
||||
int layerIndex = renderType.getChunkLayerId();
|
||||
|
||||
transformingWrapper.prepare(emitters[layerIndex], poseStack);
|
||||
transformingWrapper.prepare(emitters[layerIndex].unwrap(true), poseStack);
|
||||
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
|
||||
|
@ -124,12 +122,11 @@ final class BakedModelBufferer {
|
|||
}
|
||||
|
||||
ModelBlockRenderer.clearCache();
|
||||
transformingWrapper.clear();
|
||||
|
||||
for (var emitter : emitters) {
|
||||
for (MeshEmitter emitter : emitters) {
|
||||
emitter.end();
|
||||
}
|
||||
|
||||
transformingWrapper.clear();
|
||||
}
|
||||
|
||||
public interface ResultConsumer {
|
||||
|
@ -140,15 +137,13 @@ final class BakedModelBufferer {
|
|||
public final PoseStack identityPoseStack = new PoseStack();
|
||||
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
|
||||
|
||||
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
||||
|
||||
public final ForgeMeshEmitter[] emitters = new ForgeMeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||
|
||||
{
|
||||
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||
var renderType = CHUNK_LAYERS[layerIndex];
|
||||
var buffer = new BufferBuilder(renderType.bufferSize());
|
||||
emitters[layerIndex] = new ForgeMeshEmitter(buffer, renderType);
|
||||
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||
emitters[layerIndex] = new MeshEmitter(renderType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -10,14 +12,17 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.baked.MeshEmitter.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
|
||||
public class ForgeBakedModelBuilder extends BakedModelBuilder {
|
||||
public final class ForgeBakedModelBuilder extends BakedModelBuilder {
|
||||
@Nullable
|
||||
private ModelData modelData;
|
||||
|
||||
|
@ -25,11 +30,36 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
|
|||
super(bakedModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBakedModelBuilder level(BlockAndTintGetter level) {
|
||||
super.level(level);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBakedModelBuilder blockState(BlockState blockState) {
|
||||
super.blockState(blockState);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBakedModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ForgeBakedModelBuilder modelData(ModelData modelData) {
|
||||
this.modelData = modelData;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (level == null) {
|
||||
level = VirtualEmptyBlockGetter.INSTANCE;
|
||||
|
@ -37,16 +67,16 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
|
|||
if (blockState == null) {
|
||||
blockState = Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
if (modelData == null) {
|
||||
modelData = ModelData.EMPTY;
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
if (modelData == null) {
|
||||
modelData = ModelData.EMPTY;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, modelData, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
|
@ -54,8 +84,7 @@ public class ForgeBakedModelBuilder extends BakedModelBuilder {
|
|||
var mesh = new SimpleMesh(vertexView, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
};
|
||||
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), level, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -10,13 +12,15 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.baked.MeshEmitter.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
|
||||
public class ForgeBlockModelBuilder extends BlockModelBuilder {
|
||||
public final class ForgeBlockModelBuilder extends BlockModelBuilder {
|
||||
@Nullable
|
||||
private ModelData modelData;
|
||||
|
||||
|
@ -24,25 +28,44 @@ public class ForgeBlockModelBuilder extends BlockModelBuilder {
|
|||
super(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBlockModelBuilder level(BlockAndTintGetter level) {
|
||||
super.level(level);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ForgeBlockModelBuilder modelData(ModelData modelData) {
|
||||
this.modelData = modelData;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (level == null) {
|
||||
level = VirtualEmptyBlockGetter.INSTANCE;
|
||||
}
|
||||
if (modelData == null) {
|
||||
modelData = ModelData.EMPTY;
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
if (modelData == null) {
|
||||
modelData = ModelData.EMPTY;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, modelData, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
|
@ -50,8 +73,7 @@ public class ForgeBlockModelBuilder extends BlockModelBuilder {
|
|||
var mesh = new SimpleMesh(vertexView, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
};
|
||||
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, level, state, poseStack, modelData, resultConsumer);
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
|
||||
class ForgeMeshEmitter extends MeshEmitter {
|
||||
ForgeMeshEmitter(BufferBuilder bufferBuilder, RenderType renderType) {
|
||||
super(bufferBuilder, renderType);
|
||||
}
|
||||
|
||||
// Forge has another putBulkData that we need to override
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose matrixEntry, BakedQuad quad, float[] baseBrightness, float red, float green, float blue, float alpha, int[] lightmapCoords, int overlayCoords, boolean readExistingColor) {
|
||||
observeQuadAndEmitIfNecessary(quad);
|
||||
|
||||
bufferBuilder.putBulkData(matrixEntry, quad, baseBrightness, red, green, blue, alpha, lightmapCoords, overlayCoords, readExistingColor);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -12,14 +13,15 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|||
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.baked.MeshEmitter.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
|
||||
public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
|
||||
public final class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
|
||||
@Nullable
|
||||
private Function<BlockPos, ModelData> modelDataLookup;
|
||||
|
||||
|
@ -27,22 +29,41 @@ public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
|
|||
super(level, positions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeMultiBlockModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeMultiBlockModelBuilder enableFluidRendering() {
|
||||
super.enableFluidRendering();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeMultiBlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ForgeMultiBlockModelBuilder modelDataLookup(Function<BlockPos, ModelData> modelDataLookup) {
|
||||
this.modelDataLookup = modelDataLookup;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (modelDataLookup == null) {
|
||||
modelDataLookup = pos -> ModelData.EMPTY;
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getMaterial;
|
||||
}
|
||||
if (modelDataLookup == null) {
|
||||
modelDataLookup = pos -> ModelData.EMPTY;
|
||||
}
|
||||
|
||||
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||
|
||||
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, modelDataLookup, renderFluids, (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
VertexView vertexView = new NoOverlayVertexView();
|
||||
|
@ -50,8 +71,7 @@ public class ForgeMultiBlockModelBuilder extends MultiBlockModelBuilder {
|
|||
var mesh = new SimpleMesh(vertexView, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded);
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
};
|
||||
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), level, poseStack, modelDataLookup, renderFluids, resultConsumer);
|
||||
});
|
||||
|
||||
return new SimpleModel(out.build());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package com.jozufozu.flywheel.lib.model.baked;
|
||||
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
|
||||
class MeshEmitter implements VertexConsumer {
|
||||
private final RenderType renderType;
|
||||
private final BufferBuilder bufferBuilder;
|
||||
|
||||
private BakedModelBufferer.@UnknownNullability ResultConsumer resultConsumer;
|
||||
private boolean currentShade;
|
||||
|
||||
MeshEmitter(RenderType renderType) {
|
||||
this.renderType = renderType;
|
||||
this.bufferBuilder = new BufferBuilder(renderType.bufferSize());
|
||||
}
|
||||
|
||||
public void prepare(BakedModelBufferer.ResultConsumer resultConsumer) {
|
||||
this.resultConsumer = resultConsumer;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
if (bufferBuilder.building()) {
|
||||
emit();
|
||||
}
|
||||
resultConsumer = null;
|
||||
}
|
||||
|
||||
public BufferBuilder unwrap(boolean shade) {
|
||||
prepareForGeometry(shade);
|
||||
return bufferBuilder;
|
||||
}
|
||||
|
||||
private void prepareForGeometry(boolean shade) {
|
||||
if (!bufferBuilder.building()) {
|
||||
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
|
||||
} else if (shade != currentShade) {
|
||||
emit();
|
||||
}
|
||||
|
||||
currentShade = shade;
|
||||
}
|
||||
|
||||
private void prepareForGeometry(BakedQuad quad) {
|
||||
prepareForGeometry(quad.isShade());
|
||||
}
|
||||
|
||||
private void emit() {
|
||||
var renderedBuffer = bufferBuilder.endOrDiscardIfEmpty();
|
||||
|
||||
if (renderedBuffer != null) {
|
||||
resultConsumer.accept(renderType, currentShade, renderedBuffer);
|
||||
renderedBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int light, int overlay) {
|
||||
prepareForGeometry(quad);
|
||||
bufferBuilder.putBulkData(pose, quad, red, green, blue, light, overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, float alpha, int light, int overlay, boolean readExistingColor) {
|
||||
prepareForGeometry(quad);
|
||||
bufferBuilder.putBulkData(pose, quad, red, green, blue, alpha, light, overlay, readExistingColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, int[] lights, int overlay, boolean readExistingColor) {
|
||||
prepareForGeometry(quad);
|
||||
bufferBuilder.putBulkData(pose, quad, brightnesses, red, green, blue, lights, overlay, readExistingColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad quad, float[] brightnesses, float red, float green, float blue, float alpha, int[] lights, int overlay, boolean readExistingColor) {
|
||||
prepareForGeometry(quad);
|
||||
bufferBuilder.putBulkData(pose, quad, brightnesses, red, green, blue, alpha, lights, overlay, readExistingColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer vertex(double x, double y, double z) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer color(int red, int green, int blue, int alpha) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv(float u, float v) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer overlayCoords(int u, int v) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv2(int u, int v) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer normal(float x, float y, float z) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVertex() {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultColor(int red, int green, int blue, int alpha) {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetDefaultColor() {
|
||||
throw new UnsupportedOperationException("MeshEmitter only supports putBulkData!");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue