World building exercise

- Yoink the WorldModelBuilder and associated changes from 1.18/next
This commit is contained in:
Jozufozu 2022-07-10 19:04:30 -04:00
parent 273c99619f
commit faa5652c4c
7 changed files with 216 additions and 85 deletions

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.core.model;
import java.util.Random;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
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.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public final class BakedModelBuilder implements Bufferable {
private final BakedModel model;
private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE;
private BlockState referenceState = Blocks.AIR.defaultBlockState();
private PoseStack poseStack = new PoseStack();
public BakedModelBuilder(BakedModel model) {
this.model = model;
}
public BakedModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public BakedModelBuilder withReferenceState(BlockState referenceState) {
this.referenceState = referenceState;
return this;
}
public BakedModelBuilder withPoseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
@Override
public void bufferInto(ModelBlockRenderer blockRenderer, VertexConsumer consumer, Random random) {
blockRenderer.tesselateBlock(renderWorld, model, referenceState, BlockPos.ZERO, poseStack, consumer, false, random, 42, OverlayTexture.NO_OVERLAY, VirtualEmptyModelData.INSTANCE);
}
}

View file

@ -0,0 +1,19 @@
package com.jozufozu.flywheel.core.model;
import java.util.Random;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
/**
* An interface for objects that can "rendered" into a BufferBuilder.
*/
public interface Bufferable {
void bufferInto(ModelBlockRenderer renderer, VertexConsumer consumer, Random random);
default ShadeSeparatedBufferBuilder build() {
return ModelUtil.getBufferBuilder(this);
}
}

View file

@ -6,8 +6,6 @@ import java.util.Random;
import java.util.function.Supplier;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyModelData;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
@ -15,20 +13,14 @@ 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.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.core.Direction;
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.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
public class ModelUtil {
@ -56,75 +48,43 @@ public class ModelUtil {
return dispatcher;
}
public static ShadeSeparatedBufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack poseStack) {
return getBufferBuilder(VirtualEmptyBlockGetter.INSTANCE, model, referenceState, poseStack);
}
public static ShadeSeparatedBufferBuilder getBufferBuilder(BlockAndTintGetter renderWorld, BakedModel model, BlockState referenceState, PoseStack poseStack) {
public static ShadeSeparatedBufferBuilder getBufferBuilder(Bufferable bufferable) {
ModelBlockRenderer blockRenderer = VANILLA_RENDERER.getModelRenderer();
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper;
ShadeSeparatedBufferBuilder builder = new ShadeSeparatedBufferBuilder(512);
BufferBuilder unshadedBuilder = objects.unshadedBuilder;
objects.begin();
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
shadeSeparatingWrapper.prepare(builder, unshadedBuilder);
blockRenderer.tesselateBlock(renderWorld, model, referenceState, BlockPos.ZERO, poseStack, shadeSeparatingWrapper,
false, objects.random, 42, OverlayTexture.NO_OVERLAY, VirtualEmptyModelData.INSTANCE);
shadeSeparatingWrapper.clear();
unshadedBuilder.end();
builder.appendUnshadedVertices(unshadedBuilder);
builder.end();
bufferable.bufferInto(blockRenderer, objects.shadeSeparatingWrapper, objects.random);
return builder;
objects.end();
return objects.separatedBufferBuilder;
}
public static ShadeSeparatedBufferBuilder getBufferBuilder(BakedModel model, BlockState referenceState, PoseStack poseStack) {
return new BakedModelBuilder(model).withReferenceState(referenceState)
.withPoseStack(poseStack)
.build();
}
public static ShadeSeparatedBufferBuilder getBufferBuilder(BlockAndTintGetter renderWorld, BakedModel model, BlockState referenceState, PoseStack poseStack) {
return new BakedModelBuilder(model).withReferenceState(referenceState)
.withPoseStack(poseStack)
.withRenderWorld(renderWorld)
.build();
}
public static ShadeSeparatedBufferBuilder getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks) {
return getBufferBuilderFromTemplate(renderWorld, layer, blocks, new PoseStack());
return new WorldModelBuilder(layer).withRenderWorld(renderWorld)
.withBlocks(blocks)
.build();
}
public static ShadeSeparatedBufferBuilder getBufferBuilderFromTemplate(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, PoseStack poseStack) {
ModelBlockRenderer modelRenderer = VANILLA_RENDERER.getModelRenderer();
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
Random random = objects.random;
ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper;
ShadeSeparatedBufferBuilder builder = new ShadeSeparatedBufferBuilder(512);
BufferBuilder unshadedBuilder = objects.unshadedBuilder;
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
shadeSeparatingWrapper.prepare(builder, unshadedBuilder);
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))
continue;
BlockPos pos = info.pos;
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
modelRenderer.tesselateBlock(renderWorld, VANILLA_RENDERER.getBlockModel(state), state, pos, poseStack, shadeSeparatingWrapper,
true, random, 42, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE);
poseStack.popPose();
}
ModelBlockRenderer.clearCache();
ForgeHooksClient.setRenderType(null);
shadeSeparatingWrapper.clear();
unshadedBuilder.end();
builder.appendUnshadedVertices(unshadedBuilder);
builder.end();
return builder;
return new WorldModelBuilder(layer).withRenderWorld(renderWorld)
.withBlocks(blocks)
.withPoseStack(poseStack)
.build();
}
public static Supplier<PoseStack> rotateToFace(Direction facing) {
@ -141,6 +101,20 @@ public class ModelUtil {
private static class ThreadLocalObjects {
public final Random random = new Random();
public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer();
public final ShadeSeparatedBufferBuilder separatedBufferBuilder = new ShadeSeparatedBufferBuilder(512);
public final BufferBuilder unshadedBuilder = new BufferBuilder(512);
private void begin() {
this.separatedBufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
this.unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK);
this.shadeSeparatingWrapper.prepare(this.separatedBufferBuilder, this.unshadedBuilder);
}
private void end() {
this.shadeSeparatingWrapper.clear();
this.unshadedBuilder.end();
this.separatedBufferBuilder.appendUnshadedVertices(this.unshadedBuilder);
this.separatedBufferBuilder.end();
}
}
}

View file

@ -1,25 +1,17 @@
package com.jozufozu.flywheel.core.model;
import java.util.Collection;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import com.mojang.blaze3d.vertex.BufferBuilder;
public class WorldModel implements Model {
private final VertexList reader;
private final String name;
/**
* It is expected that {@code renderWorld.getShade(...)} returns a constant.
*/
public WorldModel(BlockAndTintGetter renderWorld, RenderType layer, Collection<StructureTemplate.StructureBlockInfo> blocks, String name) {
reader = Formats.BLOCK.createReader(ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, blocks));
public WorldModel(BufferBuilder bufferBuilder, String name) {
this.reader = Formats.BLOCK.createReader(bufferBuilder);
this.name = name;
}

View file

@ -0,0 +1,86 @@
package com.jozufozu.flywheel.core.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
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.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.model.data.EmptyModelData;
import net.minecraftforge.client.model.data.IModelData;
public final class WorldModelBuilder implements Bufferable {
private final RenderType layer;
private PoseStack poseStack = new PoseStack();
private Map<BlockPos, IModelData> modelData = Collections.emptyMap();
private BlockAndTintGetter renderWorld = VirtualEmptyBlockGetter.INSTANCE;
private Collection<StructureTemplate.StructureBlockInfo> blocks = Collections.emptyList();
public WorldModelBuilder(RenderType layer) {
this.layer = layer;
}
@Override
public void bufferInto(ModelBlockRenderer modelRenderer, VertexConsumer consumer, Random random) {
ForgeHooksClient.setRenderType(this.layer);
ModelBlockRenderer.enableCaching();
for (StructureTemplate.StructureBlockInfo info : this.blocks) {
BlockState state = info.state;
if (state.getRenderShape() != RenderShape.MODEL) continue;
if (!ItemBlockRenderTypes.canRenderInLayer(state, this.layer)) continue;
BlockPos pos = info.pos;
IModelData data = this.modelData.getOrDefault(pos, EmptyModelData.INSTANCE);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
modelRenderer.tesselateBlock(this.renderWorld, ModelUtil.VANILLA_RENDERER.getBlockModel(state), state, pos, poseStack, consumer, true, random, 42, OverlayTexture.NO_OVERLAY, data);
poseStack.popPose();
}
ModelBlockRenderer.clearCache();
ForgeHooksClient.setRenderType(null);
}
/**
* It is expected that {@code renderWorld.getShade(...)} returns a constant.
*/
public WorldModelBuilder withRenderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
}
public WorldModelBuilder withBlocks(Collection<StructureTemplate.StructureBlockInfo> blocks) {
this.blocks = blocks;
return this;
}
public WorldModelBuilder withModelData(Map<BlockPos, IModelData> modelData) {
this.modelData = modelData;
return this;
}
public WorldModelBuilder withPoseStack(PoseStack poseStack) {
this.poseStack = poseStack;
return this;
}
public WorldModel intoMesh(String name) {
return new WorldModel(ModelUtil.getBufferBuilder(this), name);
}
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
@ -7,16 +8,21 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.platform.MemoryTracker;
public class BlockVertexListUnsafe implements VertexList {
private final ByteBuffer buffer;
private final ByteBuffer contents;
private final int vertexCount;
private final long base;
public BlockVertexListUnsafe(ByteBuffer buffer, int vertexCount) {
this.buffer = buffer;
this.base = MemoryUtil.memAddress(buffer);
public BlockVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
this.contents = MemoryTracker.create(copyFrom.capacity());
this.contents.order(copyFrom.order());
this.contents.put(copyFrom);
((Buffer) this.contents).flip();
this.base = MemoryUtil.memAddress(this.contents);
this.vertexCount = vertexCount;
}

View file

@ -1,22 +1,28 @@
package com.jozufozu.flywheel.core.vertex;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.platform.MemoryTracker;
public class PosTexNormalVertexListUnsafe implements VertexList {
private final ByteBuffer buffer;
private final ByteBuffer contents;
private final int vertexCount;
private final long base;
public PosTexNormalVertexListUnsafe(ByteBuffer buffer, int vertexCount) {
this.buffer = buffer;
public PosTexNormalVertexListUnsafe(ByteBuffer copyFrom, int vertexCount) {
this.contents = MemoryTracker.create(copyFrom.capacity());
this.contents.order(copyFrom.order());
this.contents.put(copyFrom);
((Buffer) this.contents).flip();
this.base = MemoryUtil.memAddress(this.contents);
this.vertexCount = vertexCount;
this.base = MemoryUtil.memAddress(buffer);
}
private long ptr(long idx) {