Multi-improvements

- Make lib instance shaders multiply color value instead of overriding
it
- Improve MultiBlockModelBuilder
  - Accept an Iterable<BlockPos> instead of a
Collection<StructureBlockInfo> and assume all block state data is
already contained within the render world
  - Accept a Function instead of a Map as the model data lookup
  - Allow enabling fluid rendering (all rendered fluids are currently
unshaded)
- Refactor VirtualEmptyBlockGetter
  - Move elementary implementations into abstract VirtualBlockGetter
class
  - Extract virtual LevelLightEngine into separate VirtualLightEngine
class with ability to lookup light values per pos
  - Turn VirtualEmptyBlockGetter into a class and make all methods final
to ensure instances of this class are actually empty even if subclassed
- Make BakedModelBufferer class package private
- Remove unused VertexTransformations class
- Remove Experimental annotation from
VisualizationContext.createEmbedding
This commit is contained in:
PepperCode1 2024-02-29 16:25:20 -08:00
parent eb22fee6ae
commit bf3b8ea89b
14 changed files with 361 additions and 245 deletions

View File

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.api.visualization;
import org.jetbrains.annotations.ApiStatus;
import com.jozufozu.flywheel.api.BackendImplemented;
import com.jozufozu.flywheel.api.instance.InstancerProvider;
@ -24,6 +22,5 @@ public interface VisualizationContext {
*/
Vec3i renderOrigin();
@ApiStatus.Experimental
VisualEmbedding createEmbedding();
}

View File

@ -11,16 +11,16 @@ import com.jozufozu.flywheel.api.model.Model;
public class SingleMeshModel implements Model {
private final Mesh mesh;
private final Material material;
private final ImmutableList<ConfiguredMesh> meshList;
public SingleMeshModel(Mesh mesh, Material material) {
this.mesh = mesh;
this.material = material;
meshList = ImmutableList.of(new ConfiguredMesh(material, mesh));
}
@Override
public List<ConfiguredMesh> meshes() {
return ImmutableList.of(new ConfiguredMesh(material, mesh));
return meshList;
}
@Override

View File

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.Collection;
import java.util.Map;
import java.util.Iterator;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
@ -11,6 +11,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexFormat;
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;
@ -21,11 +22,11 @@ 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.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.material.FluidState;
import net.minecraftforge.client.ChunkRenderTypeSet;
import net.minecraftforge.client.model.data.ModelData;
public final class BakedModelBufferer {
final class BakedModelBufferer {
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length;
@ -122,12 +123,13 @@ public final class BakedModelBufferer {
bufferSingleShadeSeparated(renderDispatcher.getModelRenderer(), renderWorld, renderDispatcher.getBlockModel(state), state, poseStack, modelData, resultConsumer);
}
public static void bufferMultiBlock(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, ModelData> modelDataMap, ResultConsumer resultConsumer) {
public static void bufferMultiBlock(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter renderWorld, @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;
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
BufferBuilder[] buffers = objects.shadedBuffers;
for (BufferBuilder buffer : buffers) {
@ -137,35 +139,51 @@ public final class BakedModelBufferer {
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
ModelBlockRenderer.enableCaching();
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
BlockState state = blockInfo.state();
while (posIterator.hasNext()) {
BlockPos pos = posIterator.next();
BlockState state = renderWorld.getBlockState(pos);
if (state.getRenderShape() != RenderShape.MODEL) {
continue;
if (renderFluids) {
FluidState fluidState = state.getFluidState();
if (!fluidState.isEmpty()) {
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
int layerIndex = layer.getChunkLayerId();
transformingWrapper.prepare(buffers[layerIndex], poseStack);
poseStack.pushPose();
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
renderDispatcher.renderLiquid(pos, renderWorld, transformingWrapper, state, fluidState);
poseStack.popPose();
}
}
BlockPos pos = blockInfo.pos();
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
ModelData modelData = modelDataMap.getOrDefault(pos, ModelData.EMPTY);
modelData = model.getModelData(renderWorld, pos, state, modelData);
random.setSeed(seed);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
if (state.getRenderShape() == RenderShape.MODEL) {
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
ModelData modelData = modelDataLookup.apply(pos);
modelData = model.getModelData(renderWorld, pos, state, modelData);
random.setSeed(seed);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
for (RenderType renderType : renderTypes) {
int layerIndex = renderType.getChunkLayerId();
for (RenderType renderType : renderTypes) {
int layerIndex = renderType.getChunkLayerId();
BufferBuilder buffer = buffers[layerIndex];
BufferBuilder buffer = buffers[layerIndex];
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, buffer, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, buffer, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
}
}
}
ModelBlockRenderer.clearCache();
transformingWrapper.clear();
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = CHUNK_LAYERS[layerIndex];
BufferBuilder buffer = buffers[layerIndex];
@ -177,13 +195,14 @@ public final class BakedModelBufferer {
}
}
public static void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, ModelData> modelDataMap, ShadeSeparatedResultConsumer resultConsumer) {
public static void bufferMultiBlockShadeSeparated(BlockRenderDispatcher renderDispatcher, Iterator<BlockPos> posIterator, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Function<BlockPos, ModelData> modelDataLookup, boolean renderFluids, ShadeSeparatedResultConsumer resultConsumer) {
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
if (poseStack == null) {
poseStack = objects.identityPoseStack;
}
RandomSource random = objects.random;
ShadeSeparatingVertexConsumer shadeSeparatingWrapper = objects.shadeSeparatingWrapper;
TransformingVertexConsumer transformingWrapper = objects.transformingWrapper;
BufferBuilder[] shadedBuffers = objects.shadedBuffers;
BufferBuilder[] unshadedBuffers = objects.unshadedBuffers;
@ -197,36 +216,51 @@ public final class BakedModelBufferer {
ModelBlockRenderer blockRenderer = renderDispatcher.getModelRenderer();
ModelBlockRenderer.enableCaching();
for (StructureTemplate.StructureBlockInfo blockInfo : blocks) {
BlockState state = blockInfo.state();
while (posIterator.hasNext()) {
BlockPos pos = posIterator.next();
BlockState state = renderWorld.getBlockState(pos);
if (state.getRenderShape() != RenderShape.MODEL) {
continue;
if (renderFluids) {
FluidState fluidState = state.getFluidState();
if (!fluidState.isEmpty()) {
RenderType layer = ItemBlockRenderTypes.getRenderLayer(fluidState);
int layerIndex = layer.getChunkLayerId();
transformingWrapper.prepare(shadedBuffers[layerIndex], poseStack);
poseStack.pushPose();
poseStack.translate(pos.getX() - (pos.getX() & 0xF), pos.getY() - (pos.getY() & 0xF), pos.getZ() - (pos.getZ() & 0xF));
renderDispatcher.renderLiquid(pos, renderWorld, transformingWrapper, state, fluidState);
poseStack.popPose();
}
}
BlockPos pos = blockInfo.pos();
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
ModelData modelData = modelDataMap.getOrDefault(pos, ModelData.EMPTY);
modelData = model.getModelData(renderWorld, pos, state, modelData);
random.setSeed(seed);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
if (state.getRenderShape() == RenderShape.MODEL) {
long seed = state.getSeed(pos);
BakedModel model = renderDispatcher.getBlockModel(state);
ModelData modelData = modelDataLookup.apply(pos);
modelData = model.getModelData(renderWorld, pos, state, modelData);
random.setSeed(seed);
ChunkRenderTypeSet renderTypes = model.getRenderTypes(state, random, modelData);
for (RenderType renderType : renderTypes) {
int layerIndex = renderType.getChunkLayerId();
for (RenderType renderType : renderTypes) {
int layerIndex = renderType.getChunkLayerId();
shadeSeparatingWrapper.prepare(shadedBuffers[layerIndex], unshadedBuffers[layerIndex]);
shadeSeparatingWrapper.prepare(shadedBuffers[layerIndex], unshadedBuffers[layerIndex]);
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, shadeSeparatingWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
poseStack.pushPose();
poseStack.translate(pos.getX(), pos.getY(), pos.getZ());
blockRenderer.tesselateBlock(renderWorld, model, state, pos, poseStack, shadeSeparatingWrapper, true, random, seed, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
}
}
}
ModelBlockRenderer.clearCache();
shadeSeparatingWrapper.clear();
transformingWrapper.clear();
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = CHUNK_LAYERS[layerIndex];
@ -258,6 +292,7 @@ public final class BakedModelBufferer {
public final RandomSource random = RandomSource.createNewThreadLocalInstance();
public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer();
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
public final BufferBuilder[] shadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];
public final BufferBuilder[] unshadedBuffers = new BufferBuilder[CHUNK_LAYER_AMOUNT];

View File

@ -23,22 +23,17 @@ import net.minecraftforge.client.model.data.ModelData;
public class BakedModelBuilder {
private final BakedModel bakedModel;
private boolean shadeSeparated = true;
private BlockAndTintGetter renderWorld;
private BlockState blockState;
private PoseStack poseStack;
private ModelData modelData;
private boolean shadeSeparated = true;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public BakedModelBuilder(BakedModel bakedModel) {
this.bakedModel = bakedModel;
}
public BakedModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BakedModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
@ -59,6 +54,11 @@ public class BakedModelBuilder {
return this;
}
public BakedModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BakedModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
this.materialFunc = materialFunc;
return this;

View File

@ -21,21 +21,16 @@ import net.minecraftforge.client.model.data.ModelData;
public class BlockModelBuilder {
private final BlockState state;
private boolean shadeSeparated = true;
private BlockAndTintGetter renderWorld;
private PoseStack poseStack;
private ModelData modelData;
private boolean shadeSeparated = true;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public BlockModelBuilder(BlockState state) {
this.state = state;
}
public BlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
this.renderWorld = renderWorld;
return this;
@ -51,6 +46,11 @@ public class BlockModelBuilder {
return this;
}
public BlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public BlockModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
this.materialFunc = materialFunc;
return this;

View File

@ -1,9 +1,7 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.material.Material;
@ -20,29 +18,20 @@ 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.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraftforge.client.model.data.ModelData;
public class MultiBlockModelBuilder {
private final Collection<StructureTemplate.StructureBlockInfo> blocks;
private boolean shadeSeparated = true;
private BlockAndTintGetter renderWorld;
private final BlockAndTintGetter renderWorld;
private final Iterable<BlockPos> positions;
private PoseStack poseStack;
private Map<BlockPos, ModelData> modelDataMap;
private Function<BlockPos, ModelData> modelDataLookup;
private boolean renderFluids = false;
private boolean shadeSeparated = true;
private BiFunction<RenderType, Boolean, Material> materialFunc;
public MultiBlockModelBuilder(Collection<StructureTemplate.StructureBlockInfo> blocks) {
this.blocks = blocks;
}
public MultiBlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
public MultiBlockModelBuilder renderWorld(BlockAndTintGetter renderWorld) {
public MultiBlockModelBuilder(BlockAndTintGetter renderWorld, Iterable<BlockPos> positions) {
this.renderWorld = renderWorld;
return this;
this.positions = positions;
}
public MultiBlockModelBuilder poseStack(PoseStack poseStack) {
@ -50,8 +39,18 @@ public class MultiBlockModelBuilder {
return this;
}
public MultiBlockModelBuilder modelDataMap(Map<BlockPos, ModelData> modelDataMap) {
this.modelDataMap = modelDataMap;
public MultiBlockModelBuilder modelDataLookup(Function<BlockPos, ModelData> modelDataLookup) {
this.modelDataLookup = modelDataLookup;
return this;
}
public MultiBlockModelBuilder enableFluidRendering() {
renderFluids = true;
return this;
}
public MultiBlockModelBuilder disableShadeSeparation() {
shadeSeparated = false;
return this;
}
@ -61,11 +60,8 @@ public class MultiBlockModelBuilder {
}
public TessellatedModel build() {
if (renderWorld == null) {
renderWorld = VirtualEmptyBlockGetter.INSTANCE;
}
if (modelDataMap == null) {
modelDataMap = Collections.emptyMap();
if (modelDataLookup == null) {
modelDataLookup = pos -> ModelData.EMPTY;
}
if (materialFunc == null) {
materialFunc = ModelUtil::getMaterial;
@ -83,7 +79,7 @@ public class MultiBlockModelBuilder {
out.add(new Model.ConfiguredMesh(material, mesh));
}
};
BakedModelBufferer.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
BakedModelBufferer.bufferMultiBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer);
} else {
ResultConsumer resultConsumer = (renderType, data) -> {
Material material = materialFunc.apply(renderType, true);
@ -94,7 +90,7 @@ public class MultiBlockModelBuilder {
out.add(new Model.ConfiguredMesh(material, mesh));
}
};
BakedModelBufferer.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
BakedModelBufferer.bufferMultiBlock(ModelUtil.VANILLA_RENDERER, positions.iterator(), renderWorld, poseStack, modelDataLookup, renderFluids, resultConsumer);
}
return new TessellatedModel(out.build(), shadeSeparated);

View File

@ -0,0 +1,88 @@
package com.jozufozu.flywheel.lib.model.baked;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
class TransformingVertexConsumer implements VertexConsumer {
private VertexConsumer delegate;
private PoseStack poseStack;
public void prepare(VertexConsumer delegate, PoseStack poseStack) {
this.delegate = delegate;
this.poseStack = poseStack;
}
public void clear() {
delegate = null;
poseStack = null;
}
@Override
public VertexConsumer vertex(double x, double y, double z) {
Matrix4f matrix = poseStack.last().pose();
float fx = (float) x;
float fy = (float) y;
float fz = (float) z;
delegate.vertex(
MatrixMath.transformPositionX(matrix, fx, fy, fz),
MatrixMath.transformPositionY(matrix, fx, fy, fz),
MatrixMath.transformPositionZ(matrix, fx, fy, fz));
return this;
}
@Override
public VertexConsumer color(int red, int green, int blue, int alpha) {
delegate.color(red, green, blue, alpha);
return this;
}
@Override
public VertexConsumer uv(float u, float v) {
delegate.uv(u, v);
return this;
}
@Override
public VertexConsumer overlayCoords(int u, int v) {
delegate.overlayCoords(u, v);
return this;
}
@Override
public VertexConsumer uv2(int u, int v) {
delegate.uv2(u, v);
return this;
}
@Override
public VertexConsumer normal(float x, float y, float z) {
Matrix3f matrix = poseStack.last().normal();
float fx = (float) x;
float fy = (float) y;
float fz = (float) z;
delegate.normal(
MatrixMath.transformNormalX(matrix, fx, fy, fz),
MatrixMath.transformNormalY(matrix, fx, fy, fz),
MatrixMath.transformNormalZ(matrix, fx, fy, fz));
return this;
}
@Override
public void endVertex() {
delegate.endVertex();
}
@Override
public void defaultColor(int red, int green, int blue, int alpha) {
delegate.defaultColor(red, green, blue, alpha);
}
@Override
public void unsetDefaultColor() {
delegate.unsetDefaultColor();
}
}

View File

@ -0,0 +1,43 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.ToIntFunction;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
public abstract class VirtualBlockGetter implements BlockAndTintGetter {
protected final VirtualLightEngine lightEngine;
public VirtualBlockGetter(ToIntFunction<BlockPos> blockLightFunc, ToIntFunction<BlockPos> skyLightFunc) {
lightEngine = new VirtualLightEngine(blockLightFunc, skyLightFunc, this);
}
@Override
public FluidState getFluidState(BlockPos pos) {
return getBlockState(pos).getFluidState();
}
@Override
public float getShade(Direction direction, boolean shaded) {
return 1f;
}
@Override
public LevelLightEngine getLightEngine() {
return lightEngine;
}
@Override
public int getBlockTint(BlockPos pos, ColorResolver resolver) {
Biome plainsBiome = Minecraft.getInstance().getConnection().registryAccess().registryOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
return resolver.getColor(plainsBiome, pos.getX(), pos.getZ());
}
}

View File

@ -1,34 +1,25 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.ToIntFunction;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.chunk.LightChunk;
import net.minecraft.world.level.chunk.LightChunkGetter;
import net.minecraft.world.level.lighting.LayerLightEventListener;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
public static final VirtualEmptyBlockGetter INSTANCE = new StaticLightImpl(0, 15);
public static final VirtualEmptyBlockGetter FULL_BRIGHT = new StaticLightImpl(15, 15);
public static final VirtualEmptyBlockGetter FULL_DARK = new StaticLightImpl(0, 0);
public class VirtualEmptyBlockGetter extends VirtualBlockGetter {
public static final VirtualEmptyBlockGetter INSTANCE = new VirtualEmptyBlockGetter(p -> 0, p -> 15);
public static final VirtualEmptyBlockGetter FULL_BRIGHT = new VirtualEmptyBlockGetter(p -> 15, p -> 15);
public static final VirtualEmptyBlockGetter FULL_DARK = new VirtualEmptyBlockGetter(p -> 0, p -> 0);
public VirtualEmptyBlockGetter(ToIntFunction<BlockPos> blockLightFunc, ToIntFunction<BlockPos> skyLightFunc) {
super(blockLightFunc, skyLightFunc);
}
public static boolean is(BlockAndTintGetter blockGetter) {
return blockGetter instanceof VirtualEmptyBlockGetter;
@ -36,110 +27,27 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
@Override
@Nullable
default BlockEntity getBlockEntity(BlockPos pos) {
public final BlockEntity getBlockEntity(BlockPos pos) {
return null;
}
@Override
default BlockState getBlockState(BlockPos pos) {
public final BlockState getBlockState(BlockPos pos) {
return Blocks.AIR.defaultBlockState();
}
@Override
default FluidState getFluidState(BlockPos pos) {
public final FluidState getFluidState(BlockPos pos) {
return Fluids.EMPTY.defaultFluidState();
}
@Override
default int getHeight() {
public final int getHeight() {
return 1;
}
@Override
default int getMinBuildHeight() {
public final int getMinBuildHeight() {
return 0;
}
@Override
default float getShade(Direction direction, boolean shaded) {
return 1f;
}
@Override
default int getBlockTint(BlockPos pos, ColorResolver resolver) {
Biome plainsBiome = Minecraft.getInstance().getConnection().registryAccess().registryOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
return resolver.getColor(plainsBiome, pos.getX(), pos.getZ());
}
public static class StaticLightImpl implements VirtualEmptyBlockGetter {
private final LevelLightEngine lightEngine;
public StaticLightImpl(int blockLight, int skyLight) {
lightEngine = new LevelLightEngine(new LightChunkGetter() {
@Override
@Nullable
public LightChunk getChunkForLighting(int x, int z) {
return null;
}
@Override
public BlockGetter getLevel() {
return StaticLightImpl.this;
}
}, false, false) {
private final LayerLightEventListener blockListener = createStaticListener(blockLight);
private final LayerLightEventListener skyListener = createStaticListener(skyLight);
@Override
public LayerLightEventListener getLayerListener(LightLayer layer) {
return layer == LightLayer.BLOCK ? blockListener : skyListener;
}
};
}
private static LayerLightEventListener createStaticListener(int light) {
return new LayerLightEventListener() {
@Override
public void checkBlock(BlockPos pos) {
}
@Override
public boolean hasLightWork() {
return false;
}
@Override
public int runLightUpdates() {
return 0;
}
@Override
public void updateSectionStatus(SectionPos pos, boolean isSectionEmpty) {
}
@Override
public void setLightEnabled(ChunkPos pos, boolean lightEnabled) {
}
@Override
public void propagateLightSources(ChunkPos pos) {
}
@Override
public DataLayer getDataLayerData(SectionPos pos) {
return null;
}
@Override
public int getLightValue(BlockPos pos) {
return light;
}
};
}
@Override
public LevelLightEngine getLightEngine() {
return lightEngine;
}
}
}

View File

@ -0,0 +1,95 @@
package com.jozufozu.flywheel.lib.model.baked;
import java.util.function.ToIntFunction;
import org.jetbrains.annotations.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.chunk.LightChunk;
import net.minecraft.world.level.chunk.LightChunkGetter;
import net.minecraft.world.level.lighting.LayerLightEventListener;
import net.minecraft.world.level.lighting.LevelLightEngine;
public final class VirtualLightEngine extends LevelLightEngine {
private final LayerLightEventListener blockListener;
private final LayerLightEventListener skyListener;
public VirtualLightEngine(ToIntFunction<BlockPos> blockLightFunc, ToIntFunction<BlockPos> skyLightFunc, BlockGetter level) {
super(new LightChunkGetter() {
@Override
@Nullable
public LightChunk getChunkForLighting(int x, int z) {
return null;
}
@Override
public BlockGetter getLevel() {
return level;
}
}, false, false);
blockListener = new VirtualLayerLightEventListener(blockLightFunc);
skyListener = new VirtualLayerLightEventListener(skyLightFunc);
}
@Override
public LayerLightEventListener getLayerListener(LightLayer layer) {
return layer == LightLayer.BLOCK ? blockListener : skyListener;
}
@Override
public int getRawBrightness(BlockPos pos, int amount) {
int i = skyListener.getLightValue(pos) - amount;
int j = blockListener.getLightValue(pos);
return Math.max(j, i);
}
private static class VirtualLayerLightEventListener implements LayerLightEventListener {
private final ToIntFunction<BlockPos> lightFunc;
public VirtualLayerLightEventListener(ToIntFunction<BlockPos> lightFunc) {
this.lightFunc = lightFunc;
}
@Override
public void checkBlock(BlockPos pos) {
}
@Override
public boolean hasLightWork() {
return false;
}
@Override
public int runLightUpdates() {
return 0;
}
@Override
public void updateSectionStatus(SectionPos pos, boolean isSectionEmpty) {
}
@Override
public void setLightEnabled(ChunkPos pos, boolean lightEnabled) {
}
@Override
public void propagateLightSources(ChunkPos pos) {
}
@Override
public DataLayer getDataLayerData(SectionPos pos) {
return null;
}
@Override
public int getLightValue(BlockPos pos) {
return lightFunc.applyAsInt(pos);
}
}
}

View File

@ -1,44 +0,0 @@
package com.jozufozu.flywheel.lib.vertex;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.lib.math.MatrixMath;
public final class VertexTransformations {
private VertexTransformations() {
}
public static void transformPos(MutableVertexList vertexList, int index, Matrix4f matrix) {
float x = vertexList.x(index);
float y = vertexList.y(index);
float z = vertexList.z(index);
vertexList.x(index, MatrixMath.transformPositionX(matrix, x, y, z));
vertexList.y(index, MatrixMath.transformPositionY(matrix, x, y, z));
vertexList.z(index, MatrixMath.transformPositionZ(matrix, x, y, z));
}
/**
* Assumes the matrix preserves scale.
*/
public static void transformNormal(MutableVertexList vertexList, int index, Matrix3f matrix) {
float nx = vertexList.normalX(index);
float ny = vertexList.normalY(index);
float nz = vertexList.normalZ(index);
float tnx = MatrixMath.transformNormalX(matrix, nx, ny, nz);
float tny = MatrixMath.transformNormalY(matrix, nx, ny, nz);
float tnz = MatrixMath.transformNormalZ(matrix, nx, ny, nz);
// seems to be the case that sqrLength is always ~1.0
// float sqrLength = fma(tnx, tnx, fma(tny, tny, tnz * tnz));
// if (sqrLength != 0) {
// float f = invsqrt(sqrLength);
// tnx *= f;
// tny *= f;
// tnz *= f;
// }
vertexList.normalX(index, tnx);
vertexList.normalY(index, tny);
vertexList.normalZ(index, tnz);
}
}

View File

@ -3,7 +3,7 @@
void flw_instanceVertex(in FlwInstance i) {
flw_vertexPos = vec4(rotateByQuaternion(flw_vertexPos.xyz - i.pivot, i.rotation) + i.pivot + i.position, 1.0);
flw_vertexNormal = rotateByQuaternion(flw_vertexNormal, i.rotation);
flw_vertexColor = i.color;
flw_vertexColor *= i.color;
flw_vertexOverlay = i.overlay;
flw_vertexLight = i.light / 15.;
}

View File

@ -7,6 +7,4 @@ void flw_instanceVertex(in FlwInstance i) {
flw_vertexTexCoord = (flw_vertexPos.xz - i.entityPosXZ) * 0.5 / i.radius + 0.5;
flw_vertexColor.a = i.alpha;
// no overlay
flw_vertexOverlay = ivec2(0, 10);
}

View File

@ -1,7 +1,7 @@
void flw_instanceVertex(in FlwInstance i) {
flw_vertexPos = i.pose * flw_vertexPos;
flw_vertexNormal = i.normal * flw_vertexNormal;
flw_vertexColor = i.color;
flw_vertexColor *= i.color;
flw_vertexOverlay = i.overlay;
flw_vertexLight = i.light / 15.;
}