started support for weighted models to support texture randomness. still W.I.P. (textures are normalized)
This commit is contained in:
parent
7ae4858bb4
commit
4315314593
@ -108,9 +108,6 @@ public class TemplatesClient implements ClientModInitializer {
|
|||||||
api.assignItemModel(Templates.id("wall_inventory_special") , Templates.WALL);
|
api.assignItemModel(Templates.id("wall_inventory_special") , Templates.WALL);
|
||||||
api.assignItemModel(Templates.id("slope_special") , Templates.SLOPE);
|
api.assignItemModel(Templates.id("slope_special") , Templates.SLOPE);
|
||||||
api.assignItemModel(Templates.id("tiny_slope_special") , Templates.TINY_SLOPE);
|
api.assignItemModel(Templates.id("tiny_slope_special") , Templates.TINY_SLOPE);
|
||||||
|
|
||||||
//TODO: i could stick some kind of entrypoint here for signalling other mods that it's ok to register now?
|
|
||||||
// Dont think it rly matters though, everything's all kept in nice hash maps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void privateInit() {
|
private void privateInit() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package fr.adrien1106.reframedtemplates;
|
package fr.adrien1106.reframedtemplates;
|
||||||
|
|
||||||
import fr.adrien1106.reframedtemplates.api.TemplatesClientApi;
|
import fr.adrien1106.reframedtemplates.api.TemplatesClientApi;
|
||||||
import fr.adrien1106.reframedtemplates.model.TemplateAppearanceManager;
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
|
||||||
import fr.adrien1106.reframedtemplates.model.UnbakedAutoRetexturedModel;
|
import fr.adrien1106.reframedtemplates.model.UnbakedAutoRetexturedModel;
|
||||||
import fr.adrien1106.reframedtemplates.model.UnbakedJsonRetexturedModel;
|
import fr.adrien1106.reframedtemplates.model.UnbakedJsonRetexturedModel;
|
||||||
import fr.adrien1106.reframedtemplates.model.UnbakedMeshRetexturedModel;
|
import fr.adrien1106.reframedtemplates.model.UnbakedMeshRetexturedModel;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package fr.adrien1106.reframedtemplates;
|
package fr.adrien1106.reframedtemplates;
|
||||||
|
|
||||||
import fr.adrien1106.reframedtemplates.model.TemplateAppearanceManager;
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
|
||||||
import net.fabricmc.fabric.api.client.model.ModelProviderContext;
|
import net.fabricmc.fabric.api.client.model.ModelProviderContext;
|
||||||
import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
|
import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
|
||||||
import net.fabricmc.fabric.api.client.model.ModelVariantProvider;
|
import net.fabricmc.fabric.api.client.model.ModelVariantProvider;
|
||||||
|
@ -113,7 +113,7 @@ public class TemplateInteractionUtil {
|
|||||||
if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) {
|
if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) {
|
||||||
Block block = bi.getBlock();
|
Block block = bi.getBlock();
|
||||||
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
|
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
|
||||||
BlockState placementState = block.getPlacementState(ctx); // TODO Smart
|
BlockState placementState = block.getPlacementState(ctx);
|
||||||
if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
|
if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
|
||||||
if(!world.isClient) be.setRenderedState(placementState);
|
if(!world.isClient) be.setRenderedState(placementState);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package fr.adrien1106.reframedtemplates.api;
|
package fr.adrien1106.reframedtemplates.api;
|
||||||
|
|
||||||
import fr.adrien1106.reframedtemplates.model.TemplateAppearanceManager;
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
|
||||||
import fr.adrien1106.reframedtemplates.TemplatesClient;
|
import fr.adrien1106.reframedtemplates.TemplatesClient;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.mixin.model;
|
||||||
|
|
||||||
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
|
import net.minecraft.client.render.model.WeightedBakedModel;
|
||||||
|
import net.minecraft.util.collection.Weighted;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(WeightedBakedModel.class)
|
||||||
|
public interface WeightedBakedModelAccessor {
|
||||||
|
|
||||||
|
@Accessor("models") List<Weighted.Present<BakedModel>> getModels();
|
||||||
|
|
||||||
|
}
|
@ -21,11 +21,11 @@ record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
|
|||||||
return sprite.getMinU() <= minU && sprite.getMaxU() >= maxU && sprite.getMinV() <= minV && sprite.getMaxV() >= maxV;
|
return sprite.getMinU() <= minU && sprite.getMaxU() >= maxU && sprite.getMinV() <= minV && sprite.getMaxV() >= maxV;
|
||||||
}
|
}
|
||||||
|
|
||||||
void normalizeUv(MutableQuadView quad, Sprite specialSprite) {
|
void normalizeUv(MutableQuadView quad, Sprite sprite) {
|
||||||
float remappedMinU = norm(minU, specialSprite.getMinU(), specialSprite.getMaxU());
|
float remappedMinU = norm(minU, sprite.getMinU(), sprite.getMaxU());
|
||||||
float remappedMaxU = norm(maxU, specialSprite.getMinU(), specialSprite.getMaxU());
|
float remappedMaxU = norm(maxU, sprite.getMinU(), sprite.getMaxU());
|
||||||
float remappedMinV = norm(minV, specialSprite.getMinV(), specialSprite.getMaxV());
|
float remappedMinV = norm(minV, sprite.getMinV(), sprite.getMaxV());
|
||||||
float remappedMaxV = norm(maxV, specialSprite.getMinV(), specialSprite.getMaxV());
|
float remappedMaxV = norm(maxV, sprite.getMinV(), sprite.getMaxV());
|
||||||
quad.uv(0, MathHelper.approximatelyEquals(quad.u(0), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(0), minV) ? remappedMinV : remappedMaxV);
|
quad.uv(0, MathHelper.approximatelyEquals(quad.u(0), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(0), minV) ? remappedMinV : remappedMaxV);
|
||||||
quad.uv(1, MathHelper.approximatelyEquals(quad.u(1), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(1), minV) ? remappedMinV : remappedMaxV);
|
quad.uv(1, MathHelper.approximatelyEquals(quad.u(1), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(1), minV) ? remappedMinV : remappedMaxV);
|
||||||
quad.uv(2, MathHelper.approximatelyEquals(quad.u(2), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(2), minV) ? remappedMinV : remappedMaxV);
|
quad.uv(2, MathHelper.approximatelyEquals(quad.u(2), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(2), minV) ? remappedMinV : remappedMaxV);
|
||||||
|
@ -2,11 +2,14 @@ package fr.adrien1106.reframedtemplates.model;
|
|||||||
|
|
||||||
import fr.adrien1106.reframedtemplates.block.TemplateEntity;
|
import fr.adrien1106.reframedtemplates.block.TemplateEntity;
|
||||||
import fr.adrien1106.reframedtemplates.mixin.MinecraftAccessor;
|
import fr.adrien1106.reframedtemplates.mixin.MinecraftAccessor;
|
||||||
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearance;
|
||||||
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
|
||||||
|
import fr.adrien1106.reframedtemplates.model.apperance.WeightedComputedAppearance;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
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.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
@ -41,8 +44,10 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
protected final BlockState itemModelState;
|
protected final BlockState itemModelState;
|
||||||
protected final boolean ao;
|
protected final boolean ao;
|
||||||
|
|
||||||
protected record CacheKey(BlockState state, TemplateAppearance appearance) {}
|
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {}
|
||||||
protected final ConcurrentMap<CacheKey, Mesh> retexturedMeshes = new ConcurrentHashMap<>(); //mutable, append-only cache
|
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache
|
||||||
|
protected record TransformCacheKey(TemplateAppearance appearance, int model_id) {}
|
||||||
|
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
protected static final Direction[] DIRECTIONS = Direction.values();
|
protected static final Direction[] DIRECTIONS = Direction.values();
|
||||||
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
||||||
@ -57,32 +62,34 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sprite getParticleSprite() {
|
public Sprite getParticleSprite() {
|
||||||
return tam.getDefaultAppearance().getSprite(Direction.UP);
|
return tam.getDefaultAppearance().getSprite(Direction.UP, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
BlockState theme = (((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos) instanceof BlockState s) ? s : null;
|
BlockState theme = (blockView.getBlockEntityRenderData(pos) instanceof BlockState s) ? s : null;
|
||||||
|
QuadEmitter quad_emitter = context.getEmitter();
|
||||||
if(theme == null || theme.isAir()) {
|
if(theme == null || theme.isAir()) {
|
||||||
context.meshConsumer().accept(getUntintedRetexturedMesh(new CacheKey(state, tam.getDefaultAppearance())));
|
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter);
|
||||||
return;
|
|
||||||
} else if(theme.getBlock() == Blocks.BARRIER) {
|
|
||||||
//TODO i don't love putting this rare specialcase smack in the middle of the hot code path
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(theme.getBlock() == Blocks.BARRIER) return;
|
||||||
|
|
||||||
TemplateAppearance ta = tam.getAppearance(theme);
|
TemplateAppearance ta = tam.getTemplateAppearance(theme);
|
||||||
|
long seed = theme.getRenderingSeed(pos);
|
||||||
|
int model_id = 0;
|
||||||
|
if (ta instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
|
||||||
|
|
||||||
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, blockView, pos, 0);
|
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, blockView, pos, 0);
|
||||||
Mesh untintedMesh = getUntintedRetexturedMesh(new CacheKey(state, ta));
|
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(ta, model_id)), seed);
|
||||||
|
|
||||||
//The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into
|
//The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into
|
||||||
//the cached mesh will pollute it with a ton of single-use meshes with only slighly different colors.
|
//the cached mesh will pollute it with a ton of single-use meshes with only slightly different colors.
|
||||||
if(tint == 0xFFFFFFFF) {
|
if(tint == 0xFFFFFFFF) {
|
||||||
context.meshConsumer().accept(untintedMesh);
|
untintedMesh.outputTo(quad_emitter);
|
||||||
} else {
|
} else {
|
||||||
context.pushTransform(new TintingTransformer(ta, tint));
|
context.pushTransform(new TintingTransformer(ta, tint, seed));
|
||||||
context.meshConsumer().accept(untintedMesh);
|
untintedMesh.outputTo(quad_emitter);
|
||||||
context.popTransform();
|
context.popTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,40 +102,39 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
int tint;
|
int tint;
|
||||||
BlockState theme = TemplateEntity.readStateFromItem(stack);
|
BlockState theme = TemplateEntity.readStateFromItem(stack);
|
||||||
if(!theme.isAir()) {
|
if(!theme.isAir()) {
|
||||||
nbtAppearance = tam.getAppearance(theme);
|
nbtAppearance = tam.getTemplateAppearance(theme);
|
||||||
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).templates$getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).templates$getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
||||||
} else {
|
} else {
|
||||||
nbtAppearance = tam.getDefaultAppearance();
|
nbtAppearance = tam.getDefaultAppearance();
|
||||||
tint = 0xFFFFFFFF;
|
tint = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh untintedMesh = getUntintedRetexturedMesh(new CacheKey(itemModelState, nbtAppearance));
|
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(itemModelState, new TransformCacheKey(nbtAppearance, 0)), 0);
|
||||||
|
|
||||||
|
QuadEmitter quad_emitter = context.getEmitter();
|
||||||
if(tint == 0xFFFFFFFF) {
|
if(tint == 0xFFFFFFFF) {
|
||||||
context.meshConsumer().accept(untintedMesh);
|
untintedMesh.outputTo(quad_emitter);
|
||||||
} else {
|
} else {
|
||||||
context.pushTransform(new TintingTransformer(nbtAppearance, tint));
|
context.pushTransform(new TintingTransformer(nbtAppearance, tint, 0));
|
||||||
context.meshConsumer().accept(untintedMesh);
|
untintedMesh.outputTo(quad_emitter);
|
||||||
context.popTransform();
|
context.popTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Mesh getUntintedRetexturedMesh(CacheKey key) {
|
protected Mesh getUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
||||||
return retexturedMeshes.computeIfAbsent(key, this::createUntintedRetexturedMesh);
|
return retextured_meshes.computeIfAbsent(key, (k) -> createUntintedRetexturedMesh(k, seed));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Mesh createUntintedRetexturedMesh(CacheKey key) {
|
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
||||||
return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), new RetexturingTransformer(key.appearance));
|
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
|
||||||
|
return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), transformer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class RetexturingTransformer implements RenderContext.QuadTransform {
|
protected class RetexturingTransformer implements RenderContext.QuadTransform {
|
||||||
protected RetexturingTransformer(TemplateAppearance ta) {
|
private final long seed;
|
||||||
|
protected RetexturingTransformer(TemplateAppearance ta, long seed) {
|
||||||
this.ta = ta;
|
this.ta = ta;
|
||||||
}
|
this.seed = seed;
|
||||||
|
|
||||||
@Deprecated(forRemoval = true) //TODO ABI: Deprecated in 2.2. Use TintingTransformer for retinting, it works better anyway
|
|
||||||
protected RetexturingTransformer(TemplateAppearance ta, int ignoredTint) {
|
|
||||||
this(ta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final TemplateAppearance ta;
|
protected final TemplateAppearance ta;
|
||||||
@ -141,17 +147,18 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
if(tag == 0) return true; //Pass the quad through unmodified.
|
if(tag == 0) return true; //Pass the quad through unmodified.
|
||||||
|
|
||||||
//The quad tag numbers were selected so this magic trick works:
|
//The quad tag numbers were selected so this magic trick works:
|
||||||
Direction dir = facePermutation.get(DIRECTIONS[quad.tag() - 1]);
|
Direction direction = facePermutation.get(DIRECTIONS[quad.tag() - 1]);
|
||||||
quad.spriteBake(ta.getSprite(dir), MutableQuadView.BAKE_NORMALIZED | ta.getBakeFlags(dir) | (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0));
|
quad.spriteBake(ta.getSprite(direction, seed), MutableQuadView.BAKE_NORMALIZED | ta.getBakeFlags(direction, seed) | (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class TintingTransformer implements RenderContext.QuadTransform {
|
protected class TintingTransformer implements RenderContext.QuadTransform {
|
||||||
protected TintingTransformer(TemplateAppearance ta, int tint) {
|
private final long seed;
|
||||||
|
protected TintingTransformer(TemplateAppearance ta, int tint, long seed) {
|
||||||
this.ta = ta;
|
this.ta = ta;
|
||||||
this.tint = tint;
|
this.tint = tint;
|
||||||
|
this.seed = seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final TemplateAppearance ta;
|
protected final TemplateAppearance ta;
|
||||||
@ -163,15 +170,9 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
if(tag == 0) return true;
|
if(tag == 0) return true;
|
||||||
|
|
||||||
Direction dir = facePermutation.get(DIRECTIONS[quad.tag() - 1]);
|
Direction dir = facePermutation.get(DIRECTIONS[quad.tag() - 1]);
|
||||||
if(ta.hasColor(dir)) quad.color(tint, tint, tint, tint);
|
if(ta.hasColor(dir, seed)) quad.color(tint, tint, tint, tint);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO ABI: From before there was an AO boolean, <2.1.1.
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public RetexturingBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState) {
|
|
||||||
this(baseModel, tam, settings, itemModelState, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model;
|
||||||
|
|
||||||
|
import fr.adrien1106.reframedtemplates.api.TemplatesClientApi;
|
||||||
|
import fr.adrien1106.reframedtemplates.model.apperance.TemplateAppearanceManager;
|
||||||
|
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.Mesh;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
|
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||||
|
import net.minecraft.util.collection.Weighted;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
|
public class RetexturingWeightedBakedModel extends RetexturingBakedModel {
|
||||||
|
|
||||||
|
final ConcurrentMap<BlockState, List<Weighted.Present<Mesh>>> jsonToMesh = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public RetexturingWeightedBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
|
||||||
|
super(baseModel, tam, settings, itemModelState, ao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Mesh getBaseMesh(BlockState state) {
|
||||||
|
return null;
|
||||||
|
// random.setSeed(seed);
|
||||||
|
// List<Weighted.Present<Mesh>> models = jsonToMesh.computeIfAbsent(state, this::convertModel);
|
||||||
|
// return Weighting
|
||||||
|
// .getAt(models, Math.abs((int)random.nextLong()) % Weighting.getWeightSum(models))
|
||||||
|
// .map(Weighted.Present::getData).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Weighted.Present<Mesh>> convertModel(BlockState state) {
|
||||||
|
Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
|
||||||
|
MeshBuilder builder = r.meshBuilder();
|
||||||
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
RenderMaterial mat = tam.getCachedMaterial(state, false);
|
||||||
|
|
||||||
|
Random rand = Random.create(42);
|
||||||
|
|
||||||
|
for(Direction cullFace : DIRECTIONS_AND_NULL) {
|
||||||
|
for(BakedQuad quad : wrapped.getQuads(state, cullFace, rand)) {
|
||||||
|
emitter.fromVanilla(quad, mat, cullFace);
|
||||||
|
|
||||||
|
QuadUvBounds bounds = QuadUvBounds.read(emitter);
|
||||||
|
// for(int i = 0; i < specialSprites.length; i++) {
|
||||||
|
// if(bounds.displaysSprite(specialSprites[i])) {
|
||||||
|
// bounds.normalizeUv(emitter, specialSprites[i]);
|
||||||
|
// emitter.tag(i + 1);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
emitter.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// builder.build();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
package fr.adrien1106.reframedtemplates.model;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.util.math.Direction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
//TODO ABI: move to the api package
|
|
||||||
public interface TemplateAppearance {
|
|
||||||
@NotNull RenderMaterial getRenderMaterial(boolean ao);
|
|
||||||
@NotNull Sprite getSprite(Direction dir);
|
|
||||||
int getBakeFlags(Direction dir);
|
|
||||||
boolean hasColor(Direction dir);
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ import net.minecraft.client.render.model.UnbakedModel;
|
|||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
|
||||||
|
record Appearance(Sprite[] sprites, int[] flags, byte color_mask) {
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ComputedAppearance implements TemplateAppearance {
|
||||||
|
private final Appearance appearance;
|
||||||
|
private final int id;
|
||||||
|
private final RenderMaterial matWithAo;
|
||||||
|
private final RenderMaterial matWithoutAo;
|
||||||
|
|
||||||
|
public ComputedAppearance(@NotNull Appearance appearance, RenderMaterial withAo, RenderMaterial withoutAo, int id) {
|
||||||
|
this.appearance = appearance;
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
this.matWithAo = withAo;
|
||||||
|
this.matWithoutAo = withoutAo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||||
|
return ao ? matWithAo : matWithoutAo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||||
|
return appearance.sprites()[dir.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBakeFlags(Direction dir, long seed) {
|
||||||
|
return appearance.flags()[dir.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasColor(Direction dir, long seed) {
|
||||||
|
return (appearance.color_mask() & (1 << dir.ordinal())) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if(this == o) return true;
|
||||||
|
if(o == null || getClass() != o.getClass()) return false;
|
||||||
|
ComputedAppearance that = (ComputedAppearance) o;
|
||||||
|
return id == that.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, matWithoutAo=%s, matWithAo=%s, id=%d}"
|
||||||
|
.formatted(Arrays.toString(appearance.sprites()), Arrays.toString(appearance.flags()), appearance.color_mask(), matWithoutAo, matWithAo, id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class SingleSpriteAppearance implements TemplateAppearance {
|
||||||
|
private final @NotNull Sprite defaultSprite;
|
||||||
|
private final RenderMaterial mat;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) {
|
||||||
|
this.defaultSprite = defaultSprite;
|
||||||
|
this.mat = mat;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||||
|
return defaultSprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBakeFlags(Direction dir, long seed) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasColor(Direction dir, long seed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if(this == o) return true;
|
||||||
|
if(o == null || getClass() != o.getClass()) return false;
|
||||||
|
SingleSpriteAppearance that = (SingleSpriteAppearance) o;
|
||||||
|
return id == that.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, mat, id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface TemplateAppearance {
|
||||||
|
@NotNull RenderMaterial getRenderMaterial(boolean ao);
|
||||||
|
@NotNull Sprite getSprite(Direction dir, @NotNull long seed);
|
||||||
|
int getBakeFlags(Direction dir, @NotNull long seed);
|
||||||
|
boolean hasColor(Direction dir, @NotNull long seed);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package fr.adrien1106.reframedtemplates.model;
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
import fr.adrien1106.reframedtemplates.api.TemplatesClientApi;
|
import fr.adrien1106.reframedtemplates.api.TemplatesClientApi;
|
||||||
|
import fr.adrien1106.reframedtemplates.mixin.model.WeightedBakedModelAccessor;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||||
@ -14,25 +15,22 @@ import net.minecraft.client.MinecraftClient;
|
|||||||
import net.minecraft.client.render.RenderLayers;
|
import net.minecraft.client.render.RenderLayers;
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
import net.minecraft.client.render.model.BakedQuad;
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
|
import net.minecraft.client.render.model.json.ModelTransformation;
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.screen.PlayerScreenHandler;
|
import net.minecraft.screen.PlayerScreenHandler;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.collection.Weighted;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
//TODO: extract an API for the api package
|
|
||||||
public class TemplateAppearanceManager {
|
public class TemplateAppearanceManager {
|
||||||
|
|
||||||
public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
public TemplateAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||||
MaterialFinder finder = TemplatesClientApi.getInstance().getFabricRenderer().materialFinder();
|
MaterialFinder finder = TemplatesClientApi.getInstance().getFabricRenderer().materialFinder();
|
||||||
for(BlendMode blend : BlendMode.values()) {
|
for(BlendMode blend : BlendMode.values()) {
|
||||||
@ -51,9 +49,7 @@ public class TemplateAppearanceManager {
|
|||||||
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
|
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO ABI: Shouldn't have been made public. Noticed this in 2.2.
|
protected static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("reframedtemplates:block/framed_block"));
|
||||||
@ApiStatus.Internal
|
|
||||||
public static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("reframedtemplates:block/framed_block"));
|
|
||||||
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
||||||
|
|
||||||
private final TemplateAppearance defaultAppearance;
|
private final TemplateAppearance defaultAppearance;
|
||||||
@ -69,7 +65,7 @@ public class TemplateAppearanceManager {
|
|||||||
return defaultAppearance;
|
return defaultAppearance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TemplateAppearance getAppearance(BlockState state) {
|
public TemplateAppearance getTemplateAppearance(BlockState state) {
|
||||||
return appearanceCache.computeIfAbsent(state, this::computeAppearance);
|
return appearanceCache.computeIfAbsent(state, this::computeAppearance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,31 +92,52 @@ public class TemplateAppearanceManager {
|
|||||||
private TemplateAppearance computeAppearance(BlockState state) {
|
private TemplateAppearance computeAppearance(BlockState state) {
|
||||||
if(state.getBlock() == Blocks.BARRIER) return barrierItemAppearance;
|
if(state.getBlock() == Blocks.BARRIER) return barrierItemAppearance;
|
||||||
|
|
||||||
Random rand = Random.create(42);
|
|
||||||
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
||||||
|
if (!(model instanceof WeightedBakedModelAccessor weighted_model)) {
|
||||||
|
return new ComputedAppearance(
|
||||||
|
getAppearance(model),
|
||||||
|
getCachedMaterial(state, true),
|
||||||
|
getCachedMaterial(state, false),
|
||||||
|
serialNumber.getAndIncrement()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
|
||||||
|
.map(baked_model -> Weighted.of(getAppearance(baked_model.getData()), baked_model.getWeight().getValue()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return new WeightedComputedAppearance(
|
||||||
|
appearances,
|
||||||
|
getCachedMaterial(state, true),
|
||||||
|
getCachedMaterial(state, false),
|
||||||
|
serialNumber.getAndIncrement()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Appearance getAppearance(BakedModel model) {
|
||||||
//Only for parsing vanilla quads:
|
//Only for parsing vanilla quads:
|
||||||
Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
|
Renderer r = TemplatesClientApi.getInstance().getFabricRenderer();
|
||||||
QuadEmitter emitterAsParser = r.meshBuilder().getEmitter();
|
QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
|
||||||
RenderMaterial defaultMat = r.materialFinder().clear().find();
|
RenderMaterial material = r.materialFinder().clear().find();
|
||||||
|
Random random = Random.create();
|
||||||
|
|
||||||
Sprite[] sprites = new Sprite[6];
|
Sprite[] sprites = new Sprite[6];
|
||||||
int[] bakeFlags = new int[6];
|
int[] flags = new int[6];
|
||||||
byte hasColorMask = 0b000000;
|
byte[] color_mask = {0b000000};
|
||||||
|
|
||||||
//Read quads off the model by their `cullface`
|
//Read quads off the model by their `cullface`
|
||||||
for(Direction dir : Direction.values()) {
|
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||||
List<BakedQuad> sideQuads = model.getQuads(null, dir, rand);
|
List<BakedQuad> quads = model.getQuads(null, direction, random);
|
||||||
if(sideQuads.isEmpty()) continue;
|
if(quads.isEmpty() || quads.get(0) == null) {
|
||||||
|
sprites[direction.ordinal()] = defaultAppearance.getSprite(direction, 0); // make sure direction has a sprite
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BakedQuad arbitraryQuad = sideQuads.get(0); //TODO: maybe pick a largest quad instead?
|
BakedQuad quad = quads.get(0);
|
||||||
if(arbitraryQuad == null) continue;
|
if(quad.hasColor()) color_mask[0] |= (byte) (1 << direction.ordinal());
|
||||||
|
|
||||||
if(arbitraryQuad.hasColor()) hasColorMask |= (byte) (1 << dir.ordinal());
|
Sprite sprite = quad.getSprite();
|
||||||
|
if(sprite == null) return;
|
||||||
Sprite sprite = arbitraryQuad.getSprite();
|
sprites[direction.ordinal()] = sprite;
|
||||||
if(sprite == null) continue;
|
|
||||||
sprites[dir.ordinal()] = sprite;
|
|
||||||
|
|
||||||
//Problem: Some models (eg. pistons, stone, glazed terracotta) have their UV coordinates permuted in
|
//Problem: Some models (eg. pistons, stone, glazed terracotta) have their UV coordinates permuted in
|
||||||
//non-standard ways. The actual png image appears sideways, but the original model's UVs rotate it right way up again.
|
//non-standard ways. The actual png image appears sideways, but the original model's UVs rotate it right way up again.
|
||||||
@ -133,139 +150,19 @@ public class TemplateAppearanceManager {
|
|||||||
//of the other two (since UV coordinates are unique and shouldn't cross). There are 16 possibilities so this information
|
//of the other two (since UV coordinates are unique and shouldn't cross). There are 16 possibilities so this information
|
||||||
//is easily summarized in a bitfield, and the correct fabric rendering API "bake flags" to un-transform the sprite
|
//is easily summarized in a bitfield, and the correct fabric rendering API "bake flags" to un-transform the sprite
|
||||||
//are looked up with a simple table.
|
//are looked up with a simple table.
|
||||||
emitterAsParser.fromVanilla(arbitraryQuad, defaultMat, dir);
|
quad_emitter.fromVanilla(quad, material, direction);
|
||||||
|
|
||||||
float spriteUAvg = (sprite.getMinU() + sprite.getMaxU()) / 2;
|
float spriteUAvg = (sprite.getMinU() + sprite.getMaxU()) / 2;
|
||||||
float spriteVAvg = (sprite.getMinV() + sprite.getMaxV()) / 2;
|
float spriteVAvg = (sprite.getMinV() + sprite.getMaxV()) / 2;
|
||||||
|
|
||||||
bakeFlags[dir.ordinal()] = MAGIC_BAKEFLAGS_SBOX[
|
flags[direction.ordinal()] = MAGIC_BAKEFLAGS_SBOX[
|
||||||
(emitterAsParser.u(0) < spriteUAvg ? 8 : 0) |
|
(quad_emitter.u(0) < spriteUAvg ? 8 : 0) |
|
||||||
(emitterAsParser.v(0) < spriteVAvg ? 4 : 0) |
|
(quad_emitter.v(0) < spriteVAvg ? 4 : 0) |
|
||||||
(emitterAsParser.u(1) < spriteUAvg ? 2 : 0) |
|
(quad_emitter.u(1) < spriteUAvg ? 2 : 0) |
|
||||||
(emitterAsParser.v(1) < spriteVAvg ? 1 : 0)
|
(quad_emitter.v(1) < spriteVAvg ? 1 : 0)
|
||||||
];
|
];
|
||||||
}
|
});
|
||||||
|
|
||||||
//Fill out any missing values in the sprites array, since failure to pick textures shouldn't lead to NPEs later on
|
return new Appearance(sprites, flags, color_mask[0]);
|
||||||
for(int i = 0; i < sprites.length; i++) {
|
|
||||||
if(sprites[i] == null) sprites[i] = defaultAppearance.getSprite(Direction.byId(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ComputedApperance(
|
|
||||||
sprites,
|
|
||||||
bakeFlags,
|
|
||||||
hasColorMask,
|
|
||||||
getCachedMaterial(state, true),
|
|
||||||
getCachedMaterial(state, false),
|
|
||||||
serialNumber.getAndIncrement()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ComputedApperance implements TemplateAppearance {
|
|
||||||
private final Sprite @NotNull[] sprites;
|
|
||||||
private final int @NotNull[] bakeFlags;
|
|
||||||
private final byte hasColorMask;
|
|
||||||
private final int id;
|
|
||||||
private final RenderMaterial matWithAo;
|
|
||||||
private final RenderMaterial matWithoutAo;
|
|
||||||
|
|
||||||
private ComputedApperance(@NotNull Sprite @NotNull[] sprites, int @NotNull[] bakeFlags, byte hasColorMask, RenderMaterial withAo, RenderMaterial withoutAo, int id) {
|
|
||||||
this.sprites = sprites;
|
|
||||||
this.bakeFlags = bakeFlags;
|
|
||||||
this.hasColorMask = hasColorMask;
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
this.matWithAo = withAo;
|
|
||||||
this.matWithoutAo = withoutAo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
|
||||||
return ao ? matWithAo : matWithoutAo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Sprite getSprite(Direction dir) {
|
|
||||||
return sprites[dir.ordinal()];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBakeFlags(Direction dir) {
|
|
||||||
return bakeFlags[dir.ordinal()];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasColor(Direction dir) {
|
|
||||||
return (hasColorMask & (1 << dir.ordinal())) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if(this == o) return true;
|
|
||||||
if(o == null || getClass() != o.getClass()) return false;
|
|
||||||
ComputedApperance that = (ComputedApperance) o;
|
|
||||||
return id == that.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, matWithoutAo=%s, matWithAo=%s, id=%d}".formatted(Arrays.toString(sprites), Arrays.toString(bakeFlags), hasColorMask, matWithoutAo, matWithAo, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ClassCanBeRecord")
|
|
||||||
private static final class SingleSpriteAppearance implements TemplateAppearance {
|
|
||||||
private final @NotNull Sprite defaultSprite;
|
|
||||||
private final RenderMaterial mat;
|
|
||||||
private final int id;
|
|
||||||
|
|
||||||
private SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) {
|
|
||||||
this.defaultSprite = defaultSprite;
|
|
||||||
this.mat = mat;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
|
||||||
return mat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Sprite getSprite(Direction dir) {
|
|
||||||
return defaultSprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBakeFlags(Direction dir) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasColor(Direction dir) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if(this == o) return true;
|
|
||||||
if(o == null || getClass() != o.getClass()) return false;
|
|
||||||
SingleSpriteAppearance that = (SingleSpriteAppearance) o;
|
|
||||||
return id == that.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, mat, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package fr.adrien1106.reframedtemplates.model.apperance;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.util.collection.Weighted;
|
||||||
|
import net.minecraft.util.collection.Weighting;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WeightedComputedAppearance implements TemplateAppearance {
|
||||||
|
private final List<Weighted.Present<Appearance>> appearances;
|
||||||
|
private final int total_weight;
|
||||||
|
private final int id;
|
||||||
|
private final RenderMaterial matWithAo;
|
||||||
|
private final RenderMaterial matWithoutAo;
|
||||||
|
|
||||||
|
public WeightedComputedAppearance(@NotNull List<Weighted.Present<Appearance>> appearances, RenderMaterial withAo, RenderMaterial withoutAo, int id) {
|
||||||
|
this.appearances = appearances;
|
||||||
|
this.total_weight = Weighting.getWeightSum(appearances);
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
|
this.matWithAo = withAo;
|
||||||
|
this.matWithoutAo = withoutAo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||||
|
return ao ? matWithAo : matWithoutAo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getAppearanceIndex(long seed) {
|
||||||
|
Random random = Random.create(seed);
|
||||||
|
return Weighting.getAt(appearances, Math.abs((int)random.nextLong()) % total_weight)
|
||||||
|
.map(appearances::indexOf).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Appearance getAppearance(long seed) {
|
||||||
|
Random random = Random.create(seed);
|
||||||
|
return Weighting.getAt(appearances, Math.abs((int)random.nextLong()) % total_weight)
|
||||||
|
.map(Weighted.Present::getData).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||||
|
return getAppearance(seed).sprites()[dir.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBakeFlags(Direction dir, long seed) {
|
||||||
|
return getAppearance(seed).flags()[dir.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasColor(Direction dir, long seed) {
|
||||||
|
return (getAppearance(seed).color_mask() & (1 << dir.ordinal())) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if(this == o) return true;
|
||||||
|
if(o == null || getClass() != o.getClass()) return false;
|
||||||
|
WeightedComputedAppearance that = (WeightedComputedAppearance) o;
|
||||||
|
return id == that.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
Appearance appearance = appearances.get(0).getData();
|
||||||
|
return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, matWithoutAo=%s, matWithAo=%s, id=%d}"
|
||||||
|
.formatted(Arrays.toString(appearance.sprites()), Arrays.toString(appearance.flags()), appearance.color_mask(), matWithoutAo, matWithAo, id);
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,9 @@
|
|||||||
"client": [
|
"client": [
|
||||||
"particles.AccessorParticle",
|
"particles.AccessorParticle",
|
||||||
"particles.AccessorSpriteBillboardParticle",
|
"particles.AccessorSpriteBillboardParticle",
|
||||||
"particles.MixinBlockDustParticle"
|
"particles.MixinBlockDustParticle",
|
||||||
|
"particles.MixinBlockDustParticle",
|
||||||
|
"model.WeightedBakedModelAccessor"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
Loading…
Reference in New Issue
Block a user