minecraft-src/net/minecraft/client/renderer/block/model/multipart/MultiPartModel.java
2025-07-04 03:45:38 +03:00

140 lines
4.7 KiB
Java

package net.minecraft.client.renderer.block.model.multipart;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.client.renderer.block.model.BlockStateModel;
import net.minecraft.client.renderer.block.model.multipart.MultiPartModel.Unbaked.1;
import net.minecraft.client.renderer.block.model.multipart.MultiPartModel.Unbaked.1Key;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ResolvableModel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class MultiPartModel implements BlockStateModel {
private final MultiPartModel.SharedBakedState shared;
private final BlockState blockState;
@Nullable
private List<BlockStateModel> models;
MultiPartModel(MultiPartModel.SharedBakedState shared, BlockState blockState) {
this.shared = shared;
this.blockState = blockState;
}
@Override
public TextureAtlasSprite particleIcon() {
return this.shared.particleIcon;
}
@Override
public void collectParts(RandomSource random, List<BlockModelPart> output) {
if (this.models == null) {
this.models = this.shared.selectModels(this.blockState);
}
long l = random.nextLong();
for (BlockStateModel blockStateModel : this.models) {
random.setSeed(l);
blockStateModel.collectParts(random, output);
}
}
@Environment(EnvType.CLIENT)
public record Selector<T>(Predicate<BlockState> condition, T model) {
public <S> MultiPartModel.Selector<S> with(S model) {
return new MultiPartModel.Selector<>(this.condition, model);
}
}
@Environment(EnvType.CLIENT)
static final class SharedBakedState {
private final List<MultiPartModel.Selector<BlockStateModel>> selectors;
final TextureAtlasSprite particleIcon;
private final Map<BitSet, List<BlockStateModel>> subsets = new ConcurrentHashMap();
private static BlockStateModel getFirstModel(List<MultiPartModel.Selector<BlockStateModel>> selectors) {
if (selectors.isEmpty()) {
throw new IllegalArgumentException("Model must have at least one selector");
} else {
return (BlockStateModel)((MultiPartModel.Selector)selectors.getFirst()).model();
}
}
public SharedBakedState(List<MultiPartModel.Selector<BlockStateModel>> selectors) {
this.selectors = selectors;
BlockStateModel blockStateModel = getFirstModel(selectors);
this.particleIcon = blockStateModel.particleIcon();
}
public List<BlockStateModel> selectModels(BlockState state) {
BitSet bitSet = new BitSet();
for (int i = 0; i < this.selectors.size(); i++) {
if (((MultiPartModel.Selector)this.selectors.get(i)).condition.test(state)) {
bitSet.set(i);
}
}
return (List<BlockStateModel>)this.subsets.computeIfAbsent(bitSet, bitSetx -> {
Builder<BlockStateModel> builder = ImmutableList.builder();
for (int ix = 0; ix < this.selectors.size(); ix++) {
if (bitSetx.get(ix)) {
builder.add((BlockStateModel)((MultiPartModel.Selector)this.selectors.get(ix)).model);
}
}
return builder.build();
});
}
}
@Environment(EnvType.CLIENT)
public static class Unbaked implements BlockStateModel.UnbakedRoot {
final List<MultiPartModel.Selector<BlockStateModel.Unbaked>> selectors;
private final ModelBaker.SharedOperationKey<MultiPartModel.SharedBakedState> sharedStateKey = new 1(this);
public Unbaked(List<MultiPartModel.Selector<BlockStateModel.Unbaked>> selectors) {
this.selectors = selectors;
}
@Override
public Object visualEqualityGroup(BlockState state) {
IntList intList = new IntArrayList();
for (int i = 0; i < this.selectors.size(); i++) {
if (((MultiPartModel.Selector)this.selectors.get(i)).condition.test(state)) {
intList.add(i);
}
}
return new 1Key(this, intList);
}
@Override
public void resolveDependencies(ResolvableModel.Resolver resolver) {
this.selectors.forEach(selector -> ((BlockStateModel.Unbaked)selector.model).resolveDependencies(resolver));
}
@Override
public BlockStateModel bake(BlockState state, ModelBaker baker) {
MultiPartModel.SharedBakedState sharedBakedState = baker.compute(this.sharedStateKey);
return new MultiPartModel(sharedBakedState, state);
}
}
}