v1.5 new shapes + self culling + caching + coding tools/cleanup #10
@ -76,4 +76,15 @@ public record QuadPosBounds(float min_x, float max_x, float min_y, float max_y,
|
|||||||
quad.pos(i, pos);
|
quad.pos(i, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof QuadPosBounds other)) return false;
|
||||||
|
return MathHelper.approximatelyEquals(min_x, other.min_x)
|
||||||
|
&& MathHelper.approximatelyEquals(min_y, other.min_y)
|
||||||
|
&& MathHelper.approximatelyEquals(min_z, other.min_z)
|
||||||
|
&& MathHelper.approximatelyEquals(max_x, other.max_x)
|
||||||
|
&& MathHelper.approximatelyEquals(max_y, other.max_y)
|
||||||
|
&& MathHelper.approximatelyEquals(max_z, other.max_z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,7 @@ import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
|||||||
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
|
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
|
||||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.*;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@ -27,6 +24,7 @@ import net.minecraft.util.math.Direction;
|
|||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||||
@ -167,12 +165,15 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
|
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
|
||||||
QuadEmitter emitter = builder.getEmitter();
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
|
||||||
|
AtomicInteger quad_index = new AtomicInteger();
|
||||||
getBaseMesh(key.state_key, state).forEach(quad -> {
|
getBaseMesh(key.state_key, state).forEach(quad -> {
|
||||||
int i = -1;
|
int i = -1;
|
||||||
do {
|
do {
|
||||||
emitter.copyFrom(quad);
|
emitter.copyFrom(quad);
|
||||||
i = key.appearance.transformQuad(emitter, i, key.model_id, ao, uv_lock);
|
i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, ao, uv_lock);
|
||||||
} while (i > 0);
|
} while (i > 0);
|
||||||
|
// kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter
|
||||||
|
if (quad.cullFace() == null) quad_index.getAndIncrement();
|
||||||
});
|
});
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
@ -191,9 +192,10 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean transform(MutableQuadView quad) {
|
public boolean transform(MutableQuadView quad) {
|
||||||
if(quad.tag() == 0) return true;
|
int camo_quad_index = quad.tag() - ((quad.tag() >>> 8) << 8);
|
||||||
|
if(camo_quad_index == 0) return true;
|
||||||
|
|
||||||
if(appearance.hasColor(quad.nominalFace(), model_id, quad.tag())) quad.color(tint, tint, tint, tint);
|
if(appearance.hasColor(quad.nominalFace(), model_id, camo_quad_index)) quad.color(tint, tint, tint, tint);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public abstract class CamoAppearance {
|
|||||||
return ao && ao_material != null? ao_material : material;
|
return ao && ao_material != null? ao_material : material;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int transformQuad(QuadEmitter quad, int i, int model_id, boolean ao, boolean uv_lock) {
|
public int transformQuad(QuadEmitter quad, int i, int quad_index, int model_id, boolean ao, boolean uv_lock) {
|
||||||
if(quad.tag() == 0) return 0; // Pass the quad through unmodified.
|
if(quad.tag() == 0) return 0; // Pass the quad through unmodified.
|
||||||
|
|
||||||
Direction direction = quad.nominalFace();
|
Direction direction = quad.nominalFace();
|
||||||
@ -36,6 +36,7 @@ public abstract class CamoAppearance {
|
|||||||
if (i == -1) i = sprites.size();
|
if (i == -1) i = sprites.size();
|
||||||
|
|
||||||
SpriteProperties properties = sprites.get(sprites.size() - i);
|
SpriteProperties properties = sprites.get(sprites.size() - i);
|
||||||
|
int tag = i + (quad_index << 8);
|
||||||
i--;
|
i--;
|
||||||
QuadPosBounds bounds = properties.bounds();
|
QuadPosBounds bounds = properties.bounds();
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ public abstract class CamoAppearance {
|
|||||||
| properties.flags()
|
| properties.flags()
|
||||||
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
|
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
|
||||||
);
|
);
|
||||||
quad.tag(i+1);
|
quad.tag(tag);
|
||||||
quad.emit();
|
quad.emit();
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -64,7 +65,7 @@ public abstract class CamoAppearance {
|
|||||||
MutableQuadView.BAKE_NORMALIZED
|
MutableQuadView.BAKE_NORMALIZED
|
||||||
| MutableQuadView.BAKE_LOCK_UV
|
| MutableQuadView.BAKE_LOCK_UV
|
||||||
);
|
);
|
||||||
quad.tag(i+1);
|
quad.tag(tag);
|
||||||
quad.emit();
|
quad.emit();
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,7 @@ import net.minecraft.nbt.NbtCompound;
|
|||||||
import net.minecraft.nbt.NbtHelper;
|
import net.minecraft.nbt.NbtHelper;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
@ -21,9 +21,11 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
|
|||||||
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
||||||
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
|
||||||
"fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
"fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
|
||||||
"fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
"fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
|
"fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
|
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
|
"fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
|
||||||
"fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))
|
"fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package fr.adrien1106.reframed.mixin.compat;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||||
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
|
import link.infra.indium.renderer.mesh.MutableQuadViewImpl;
|
||||||
|
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
||||||
|
import link.infra.indium.renderer.render.BlockRenderInfo;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(AbstractBlockRenderContext.class)
|
||||||
|
public abstract class IndiumAbstractBlockRenderContextMixin {
|
||||||
|
|
||||||
|
@Shadow(remap = false) protected BlockRenderInfo blockInfo;
|
||||||
|
|
||||||
|
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
|
||||||
|
|
||||||
|
@Redirect(method = "renderQuad", at = @At(value = "INVOKE", target = "Llink/infra/indium/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"))
|
||||||
|
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
|
||||||
|
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face);
|
||||||
|
|
||||||
|
return !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
|||||||
@Mixin(TerrainBlockRenderInfo.class)
|
@Mixin(TerrainBlockRenderInfo.class)
|
||||||
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
|
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
|
||||||
|
|
||||||
@Unique private int theme_index = 1;
|
@Unique private int theme_index = 0;
|
||||||
|
|
||||||
@Redirect(
|
@Redirect(
|
||||||
method = "shouldDrawFaceInner",
|
method = "shouldDrawFaceInner",
|
||||||
@ -40,4 +40,9 @@ public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo
|
|||||||
this.theme_index = theme_index;
|
this.theme_index = theme_index;
|
||||||
prepareForBlock(blockState, blockPos, seed, modelAo);
|
prepareForBlock(blockState, blockPos, seed, modelAo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getThemeIndex() {
|
||||||
|
return theme_index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.adrien1106.reframed.mixin.compat;
|
package fr.adrien1106.reframed.mixin.compat;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
|
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
|
||||||
|
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
||||||
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
||||||
@ -31,6 +32,7 @@ public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRende
|
|||||||
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
|
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
|
||||||
|
|
||||||
List<ForwardingBakedModel> models = retexturing_model.models();
|
List<ForwardingBakedModel> models = retexturing_model.models();
|
||||||
|
BlockHelper.computeInnerCull(ctx.state(), models);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (BakedModel model : models) {
|
for (BakedModel model : models) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package fr.adrien1106.reframed.mixin.render;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||||
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
|
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||||
|
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
|
||||||
|
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(AbstractBlockRenderContext.class)
|
||||||
|
public abstract class AbstractBlockRenderContextMixin {
|
||||||
|
|
||||||
|
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
|
||||||
|
|
||||||
|
@Shadow(remap = false) @Final protected BlockRenderInfo blockInfo;
|
||||||
|
|
||||||
|
@Redirect(
|
||||||
|
method = "renderQuad",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
|
||||||
|
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face);
|
||||||
|
|
||||||
|
return !BlockHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
|
||||||
|
}
|
||||||
|
}
|
@ -24,8 +24,7 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
|
|||||||
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
|
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
|
||||||
|
|
||||||
@Shadow public BlockRenderView blockView;
|
@Shadow public BlockRenderView blockView;
|
||||||
@Unique
|
@Unique private int theme_index = 0;
|
||||||
private int theme_index = 1;
|
|
||||||
|
|
||||||
@ModifyArg(method = "prepareForBlock",
|
@ModifyArg(method = "prepareForBlock",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
||||||
@ -47,4 +46,9 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
|
|||||||
this.theme_index = theme_index;
|
this.theme_index = theme_index;
|
||||||
prepareForBlock(state, pos, ao);
|
prepareForBlock(state, pos, ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getThemeIndex() {
|
||||||
|
return theme_index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.adrien1106.reframed.mixin.render;
|
package fr.adrien1106.reframed.mixin.render;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
|
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
|
||||||
|
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||||
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
@ -30,10 +31,7 @@ public abstract class TerrainRenderContextMixin extends AbstractBlockRenderConte
|
|||||||
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
|
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
|
||||||
|
|
||||||
List<ForwardingBakedModel> models = retexturing_model.models();
|
List<ForwardingBakedModel> models = retexturing_model.models();
|
||||||
// models.forEach(model -> // TODO self culling here
|
BlockHelper.computeInnerCull(state, models);
|
||||||
// model.getWrappedModel().getQuads(state_key, null, blockInfo.randomSupplier.get())
|
|
||||||
// .forEach(quad -> QuadPosBounds.read(getEmitter().fromVanilla(quad.getVertexData(), 0)).min_x())
|
|
||||||
// );
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (BakedModel model : models) {
|
for (BakedModel model : models) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package fr.adrien1106.reframed.util.blocks;
|
package fr.adrien1106.reframed.util.blocks;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap;
|
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||||
|
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockEntityProvider;
|
import net.minecraft.block.BlockEntityProvider;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@ -18,14 +25,17 @@ import net.minecraft.util.hit.BlockHitResult;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
import net.minecraft.util.shape.VoxelShapes;
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
import net.minecraft.world.BlockView;
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -36,17 +46,10 @@ import static net.minecraft.util.shape.VoxelShapes.combine;
|
|||||||
|
|
||||||
public class BlockHelper {
|
public class BlockHelper {
|
||||||
|
|
||||||
// self culling cache TODO move
|
// self culling cache of the models not made thread local so that it is only computed once
|
||||||
private static final ThreadLocal<Object2ByteLinkedOpenHashMap<CullElement>> INNER_FACE_CULL_MAP = ThreadLocal.withInitial(() -> {
|
private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).concurrencyLevel().build();
|
||||||
Object2ByteLinkedOpenHashMap<CullElement> object2ByteLinkedOpenHashMap = new Object2ByteLinkedOpenHashMap<>(2048, 0.25F) {
|
|
||||||
protected void rehash(int newN) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
object2ByteLinkedOpenHashMap.defaultReturnValue((byte)0);
|
|
||||||
return object2ByteLinkedOpenHashMap;
|
|
||||||
});
|
|
||||||
|
|
||||||
private record CullElement(BlockState state, int model, Direction side) {}
|
private record CullElement(Object state_key, int model) {}
|
||||||
|
|
||||||
public static Corner getPlacementCorner(ItemPlacementContext ctx) {
|
public static Corner getPlacementCorner(ItemPlacementContext ctx) {
|
||||||
Direction side = ctx.getSide().getOpposite();
|
Direction side = ctx.getSide().getOpposite();
|
||||||
@ -69,7 +72,7 @@ public class BlockHelper {
|
|||||||
Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z))
|
Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z))
|
||||||
? axis_1
|
? axis_1
|
||||||
: axis_2
|
: axis_2
|
||||||
).get();
|
).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vec3d getRelativePos(Vec3d pos, BlockPos block_pos) {
|
public static Vec3d getRelativePos(Vec3d pos, BlockPos block_pos) {
|
||||||
@ -216,6 +219,66 @@ public class BlockHelper {
|
|||||||
return ActionResult.PASS;
|
return ActionResult.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compute which quad might cull with another model quad
|
||||||
|
* @param state - the state of the model
|
||||||
|
* @param models - list of models on the same block
|
||||||
|
*/
|
||||||
|
public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> models) {
|
||||||
|
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
|
||||||
|
Object key = frame_block.getModelCacheKey(state);
|
||||||
|
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(key, 1))) return;
|
||||||
|
|
||||||
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
|
QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
|
||||||
|
RenderMaterial material = r.materialFinder().clear().find();
|
||||||
|
Random random = Random.create();
|
||||||
|
|
||||||
|
List<List<QuadPosBounds>> model_bounds = models.stream()
|
||||||
|
.map(ForwardingBakedModel::getWrappedModel)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(wrapped -> wrapped.getQuads(state, null, random))
|
||||||
|
.map(quads -> quads.stream().map(quad -> {
|
||||||
|
quad_emitter.fromVanilla(quad, material, null);
|
||||||
|
return QuadPosBounds.read(quad_emitter, false);
|
||||||
|
}).toList()).toList();
|
||||||
|
|
||||||
|
Integer[] cull_array;
|
||||||
|
for(int self_id = 1; self_id <= model_bounds.size(); self_id++) {
|
||||||
|
List<QuadPosBounds> self_bounds = model_bounds.get(self_id - 1);
|
||||||
|
cull_array = new Integer[self_bounds.size()];
|
||||||
|
for (int self_quad = 0; self_quad < cull_array.length; self_quad++) {
|
||||||
|
QuadPosBounds self_bound = self_bounds.get(self_quad);
|
||||||
|
for(int other_id = 1; other_id <= model_bounds.size(); other_id++) {
|
||||||
|
if (other_id == self_id) continue;
|
||||||
|
if (model_bounds.get(other_id - 1).stream().anyMatch(other_bound -> other_bound.equals(self_bound))) {
|
||||||
|
cull_array[self_quad] = other_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INNER_CULL_MAP.put(new CullElement(key, self_id), cull_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldDrawInnerFace(BlockState state, BlockRenderView view, BlockPos pos, int quad_index, int theme_index) {
|
||||||
|
if ( !(state.getBlock() instanceof ReFramedBlock frame_block)
|
||||||
|
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
|
||||||
|
) return true;
|
||||||
|
CullElement key = new CullElement(frame_block.getModelCacheKey(state), theme_index);
|
||||||
|
if (!INNER_CULL_MAP.asMap().containsKey(key)) return true;
|
||||||
|
|
||||||
|
// needs to be Integer object because array is initialized with null not 0
|
||||||
|
Integer cull_theme = Objects.requireNonNull(INNER_CULL_MAP.getIfPresent(key))[quad_index];
|
||||||
|
if (cull_theme == null) return true; // no culling possible
|
||||||
|
|
||||||
|
BlockState self_theme = frame_entity.getTheme(theme_index);
|
||||||
|
BlockState other_theme = frame_entity.getTheme(cull_theme);
|
||||||
|
|
||||||
|
if (self_theme.isSideInvisible(other_theme, null)) return false;
|
||||||
|
return !self_theme.isOpaque() || !other_theme.isOpaque();
|
||||||
|
}
|
||||||
|
|
||||||
// Doing this method from scratch as it is simpler to do than injecting everywhere
|
// Doing this method from scratch as it is simpler to do than injecting everywhere
|
||||||
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
|
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
|
||||||
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
|
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
|
||||||
|
@ -8,4 +8,6 @@ public interface IBlockRenderInfoMixin {
|
|||||||
void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index);
|
void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index);
|
||||||
|
|
||||||
void prepareForBlock(BlockState state, BlockPos pos, long seed, boolean ao, int theme_index);
|
void prepareForBlock(BlockState state, BlockPos pos, long seed, boolean ao, int theme_index);
|
||||||
|
|
||||||
|
int getThemeIndex();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"fabricloader": "^${loader_version}",
|
"fabricloader": "^${loader_version}",
|
||||||
"fabric-api": "*"
|
"fabric-api": "*"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggests": {
|
||||||
"athena": "^${athena_version}",
|
"athena": "^${athena_version}",
|
||||||
"sodium": "^${sodium_version}",
|
"sodium": "^${sodium_version}",
|
||||||
"indium": "^${indium_version}"
|
"indium": "^${indium_version}"
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"compat.AthenaBakedModelMixin",
|
"compat.AthenaBakedModelMixin",
|
||||||
"compat.AthenaConnectedBlockModelMixin",
|
"compat.AthenaConnectedBlockModelMixin",
|
||||||
"compat.AthenaWrappedGetterMixin",
|
"compat.AthenaWrappedGetterMixin",
|
||||||
|
"compat.IndiumAbstractBlockRenderContextMixin",
|
||||||
"compat.IndiumTerrainBlockRenderInfoMixin",
|
"compat.IndiumTerrainBlockRenderInfoMixin",
|
||||||
"compat.IndiumTerrainRenderContextMixin",
|
"compat.IndiumTerrainRenderContextMixin",
|
||||||
"compat.SodiumBlockOcclusionCacheMixin",
|
"compat.SodiumBlockOcclusionCacheMixin",
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"particles.AccessorParticle",
|
"particles.AccessorParticle",
|
||||||
"particles.AccessorSpriteBillboardParticle",
|
"particles.AccessorSpriteBillboardParticle",
|
||||||
"particles.MixinBlockDustParticle",
|
"particles.MixinBlockDustParticle",
|
||||||
|
"render.AbstractBlockRenderContextMixin",
|
||||||
"render.BlockRenderInfoMixin",
|
"render.BlockRenderInfoMixin",
|
||||||
"render.MultipartBakedModelMixin",
|
"render.MultipartBakedModelMixin",
|
||||||
"render.TerrainRenderContextMixin",
|
"render.TerrainRenderContextMixin",
|
||||||
|
Loading…
Reference in New Issue
Block a user