Spaces:
Build error
Build error
package net.minecraft.client.resources.model; | |
import com.google.common.collect.HashMultimap; | |
import com.google.common.collect.Multimap; | |
import com.mojang.datafixers.util.Pair; | |
import com.mojang.logging.LogUtils; | |
import it.unimi.dsi.fastutil.objects.Object2IntMap; | |
import it.unimi.dsi.fastutil.objects.Object2IntMaps; | |
import java.io.Reader; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.IdentityHashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Objects; | |
import java.util.Set; | |
import java.util.Map.Entry; | |
import java.util.concurrent.CompletableFuture; | |
import java.util.concurrent.Executor; | |
import java.util.function.Supplier; | |
import java.util.stream.Collectors; | |
import java.util.stream.Stream; | |
import net.minecraft.Util; | |
import net.minecraft.client.color.block.BlockColors; | |
import net.minecraft.client.model.geom.EntityModelSet; | |
import net.minecraft.client.renderer.Sheets; | |
import net.minecraft.client.renderer.SpecialBlockModelRenderer; | |
import net.minecraft.client.renderer.block.BlockModelShaper; | |
import net.minecraft.client.renderer.block.model.BlockModel; | |
import net.minecraft.client.renderer.item.ClientItem; | |
import net.minecraft.client.renderer.item.ItemModel; | |
import net.minecraft.client.renderer.texture.TextureAtlas; | |
import net.minecraft.client.renderer.texture.TextureAtlasSprite; | |
import net.minecraft.client.renderer.texture.TextureManager; | |
import net.minecraft.core.registries.BuiltInRegistries; | |
import net.minecraft.resources.FileToIdConverter; | |
import net.minecraft.resources.ResourceLocation; | |
import net.minecraft.server.packs.resources.PreparableReloadListener; | |
import net.minecraft.server.packs.resources.Resource; | |
import net.minecraft.server.packs.resources.ResourceManager; | |
import net.minecraft.util.profiling.Profiler; | |
import net.minecraft.util.profiling.ProfilerFiller; | |
import net.minecraft.world.level.block.Block; | |
import net.minecraft.world.level.block.state.BlockState; | |
import net.minecraft.world.level.material.FluidState; | |
import net.minecraftforge.api.distmarker.Dist; | |
import net.minecraftforge.api.distmarker.OnlyIn; | |
import org.slf4j.Logger; | |
public class ModelManager implements PreparableReloadListener, AutoCloseable { | |
private static final Logger LOGGER = LogUtils.getLogger(); | |
private static final FileToIdConverter MODEL_LISTER = FileToIdConverter.json("models"); | |
private static final Map<ResourceLocation, ResourceLocation> VANILLA_ATLASES = Map.of( | |
Sheets.BANNER_SHEET, | |
ResourceLocation.withDefaultNamespace("banner_patterns"), | |
Sheets.BED_SHEET, | |
ResourceLocation.withDefaultNamespace("beds"), | |
Sheets.CHEST_SHEET, | |
ResourceLocation.withDefaultNamespace("chests"), | |
Sheets.SHIELD_SHEET, | |
ResourceLocation.withDefaultNamespace("shield_patterns"), | |
Sheets.SIGN_SHEET, | |
ResourceLocation.withDefaultNamespace("signs"), | |
Sheets.SHULKER_SHEET, | |
ResourceLocation.withDefaultNamespace("shulker_boxes"), | |
Sheets.ARMOR_TRIMS_SHEET, | |
ResourceLocation.withDefaultNamespace("armor_trims"), | |
Sheets.DECORATED_POT_SHEET, | |
ResourceLocation.withDefaultNamespace("decorated_pot"), | |
TextureAtlas.LOCATION_BLOCKS, | |
ResourceLocation.withDefaultNamespace("blocks") | |
); | |
private Map<ModelResourceLocation, BakedModel> bakedBlockStateModels = Map.of(); | |
private Map<ResourceLocation, ItemModel> bakedItemStackModels = Map.of(); | |
private Map<ResourceLocation, ClientItem.Properties> itemProperties = Map.of(); | |
private final AtlasSet atlases; | |
private final BlockModelShaper blockModelShaper; | |
private final BlockColors blockColors; | |
private EntityModelSet entityModelSet = EntityModelSet.EMPTY; | |
private SpecialBlockModelRenderer specialBlockModelRenderer = SpecialBlockModelRenderer.EMPTY; | |
private int maxMipmapLevels; | |
private BakedModel missingModel; | |
private ItemModel missingItemModel; | |
private Object2IntMap<BlockState> modelGroups = Object2IntMaps.emptyMap(); | |
public ModelManager(TextureManager p_119406_, BlockColors p_119407_, int p_119408_) { | |
this.blockColors = p_119407_; | |
this.maxMipmapLevels = p_119408_; | |
this.blockModelShaper = new BlockModelShaper(this); | |
this.atlases = new AtlasSet(VANILLA_ATLASES, p_119406_); | |
} | |
public BakedModel getModel(ModelResourceLocation p_119423_) { | |
return this.bakedBlockStateModels.getOrDefault(p_119423_, this.missingModel); | |
} | |
public BakedModel getMissingModel() { | |
return this.missingModel; | |
} | |
public ItemModel getItemModel(ResourceLocation p_376816_) { | |
return this.bakedItemStackModels.getOrDefault(p_376816_, this.missingItemModel); | |
} | |
public ClientItem.Properties getItemProperties(ResourceLocation p_378319_) { | |
return this.itemProperties.getOrDefault(p_378319_, ClientItem.Properties.DEFAULT); | |
} | |
public BlockModelShaper getBlockModelShaper() { | |
return this.blockModelShaper; | |
} | |
public final CompletableFuture<Void> reload( | |
PreparableReloadListener.PreparationBarrier p_249079_, ResourceManager p_251134_, Executor p_250550_, Executor p_249221_ | |
) { | |
UnbakedModel unbakedmodel = MissingBlockModel.missingModel(); | |
CompletableFuture<EntityModelSet> completablefuture = CompletableFuture.supplyAsync(EntityModelSet::vanilla, p_250550_); | |
CompletableFuture<SpecialBlockModelRenderer> completablefuture1 = completablefuture.thenApplyAsync(SpecialBlockModelRenderer::vanilla, p_250550_); | |
CompletableFuture<Map<ResourceLocation, UnbakedModel>> completablefuture2 = loadBlockModels(p_251134_, p_250550_); | |
CompletableFuture<BlockStateModelLoader.LoadedModels> completablefuture3 = BlockStateModelLoader.loadBlockStates(unbakedmodel, p_251134_, p_250550_); | |
CompletableFuture<ClientItemInfoLoader.LoadedClientInfos> completablefuture4 = ClientItemInfoLoader.scheduleLoad(p_251134_, p_250550_); | |
CompletableFuture<ModelDiscovery> completablefuture5 = CompletableFuture.allOf(completablefuture2, completablefuture3, completablefuture4) | |
.thenApplyAsync(p_374722_ -> discoverModelDependencies(unbakedmodel, completablefuture2.join(), completablefuture3.join(), completablefuture4.join()), p_250550_); | |
CompletableFuture<Object2IntMap<BlockState>> completablefuture6 = completablefuture3.thenApplyAsync( | |
p_358038_ -> buildModelGroups(this.blockColors, p_358038_), p_250550_ | |
); | |
Map<ResourceLocation, CompletableFuture<AtlasSet.StitchResult>> map = this.atlases.scheduleLoad(p_251134_, this.maxMipmapLevels, p_250550_); | |
return CompletableFuture.allOf( | |
Stream.concat( | |
map.values().stream(), | |
Stream.of(completablefuture5, completablefuture6, completablefuture3, completablefuture4, completablefuture, completablefuture1) | |
) | |
.toArray(CompletableFuture[]::new) | |
) | |
.thenApplyAsync( | |
p_374732_ -> { | |
Map<ResourceLocation, AtlasSet.StitchResult> map1 = map.entrySet() | |
.stream() | |
.collect(Collectors.toMap(Entry::getKey, p_248988_ -> p_248988_.getValue().join())); | |
ModelDiscovery modeldiscovery = completablefuture5.join(); | |
Object2IntMap<BlockState> object2intmap = completablefuture6.join(); | |
Set<ResourceLocation> set = modeldiscovery.getUnreferencedModels(); | |
if (!set.isEmpty()) { | |
LOGGER.debug( | |
"Unreferenced models: \n{}", set.stream().sorted().map(p_374723_ -> "\t" + p_374723_ + "\n").collect(Collectors.joining()) | |
); | |
} | |
ModelBakery modelbakery = new ModelBakery( | |
completablefuture.join(), | |
completablefuture3.join().plainModels(), | |
completablefuture4.join().contents(), | |
modeldiscovery.getReferencedModels(), | |
unbakedmodel | |
); | |
return loadModels(Profiler.get(), map1, modelbakery, object2intmap, completablefuture.join(), completablefuture1.join()); | |
}, | |
p_250550_ | |
) | |
.thenCompose(p_252255_ -> p_252255_.readyForUpload.thenApply(p_251581_ -> (ModelManager.ReloadState)p_252255_)) | |
.thenCompose(p_249079_::wait) | |
.thenAcceptAsync(p_358039_ -> this.apply(p_358039_, Profiler.get()), p_249221_); | |
} | |
private static CompletableFuture<Map<ResourceLocation, UnbakedModel>> loadBlockModels(ResourceManager p_251361_, Executor p_252189_) { | |
return CompletableFuture.<Map<ResourceLocation, Resource>>supplyAsync(() -> MODEL_LISTER.listMatchingResources(p_251361_), p_252189_) | |
.thenCompose( | |
p_250597_ -> { | |
List<CompletableFuture<Pair<ResourceLocation, BlockModel>>> list = new ArrayList<>(p_250597_.size()); | |
for (Entry<ResourceLocation, Resource> entry : p_250597_.entrySet()) { | |
list.add(CompletableFuture.supplyAsync(() -> { | |
ResourceLocation resourcelocation = MODEL_LISTER.fileToId(entry.getKey()); | |
try { | |
Pair pair; | |
try (Reader reader = entry.getValue().openAsReader()) { | |
pair = Pair.of(resourcelocation, BlockModel.fromStream(reader)); | |
} | |
return pair; | |
} catch (Exception exception) { | |
LOGGER.error("Failed to load model {}", entry.getKey(), exception); | |
return null; | |
} | |
}, p_252189_)); | |
} | |
return Util.sequence(list) | |
.thenApply( | |
p_250813_ -> p_250813_.stream().filter(Objects::nonNull).collect(Collectors.toUnmodifiableMap(Pair::getFirst, Pair::getSecond)) | |
); | |
} | |
); | |
} | |
private static ModelDiscovery discoverModelDependencies( | |
UnbakedModel p_364387_, | |
Map<ResourceLocation, UnbakedModel> p_360749_, | |
BlockStateModelLoader.LoadedModels p_366446_, | |
ClientItemInfoLoader.LoadedClientInfos p_378505_ | |
) { | |
ModelDiscovery modeldiscovery = new ModelDiscovery(p_360749_, p_364387_); | |
p_366446_.forResolving().forEach(modeldiscovery::addRoot); | |
p_378505_.contents().values().forEach(p_374734_ -> modeldiscovery.addRoot(p_374734_.model())); | |
modeldiscovery.registerSpecialModels(); | |
modeldiscovery.discoverDependencies(); | |
return modeldiscovery; | |
} | |
private static ModelManager.ReloadState loadModels( | |
ProfilerFiller p_252136_, | |
final Map<ResourceLocation, AtlasSet.StitchResult> p_250646_, | |
ModelBakery p_248945_, | |
Object2IntMap<BlockState> p_361513_, | |
EntityModelSet p_378097_, | |
SpecialBlockModelRenderer p_377275_ | |
) { | |
p_252136_.push("baking"); | |
final Multimap<String, Material> multimap = HashMultimap.create(); | |
final Multimap<String, String> multimap1 = HashMultimap.create(); | |
final TextureAtlasSprite textureatlassprite = p_250646_.get(TextureAtlas.LOCATION_BLOCKS).missing(); | |
ModelBakery.BakingResult modelbakery$bakingresult = p_248945_.bakeModels(new ModelBakery.TextureGetter() { | |
public TextureAtlasSprite get(ModelDebugName p_375833_, Material p_375858_) { | |
AtlasSet.StitchResult atlasset$stitchresult = p_250646_.get(p_375858_.atlasLocation()); | |
TextureAtlasSprite textureatlassprite1 = atlasset$stitchresult.getSprite(p_375858_.texture()); | |
if (textureatlassprite1 != null) { | |
return textureatlassprite1; | |
} else { | |
multimap.put(p_375833_.get(), p_375858_); | |
return atlasset$stitchresult.missing(); | |
} | |
} | |
public TextureAtlasSprite reportMissingReference(ModelDebugName p_377684_, String p_378821_) { | |
multimap1.put(p_377684_.get(), p_378821_); | |
return textureatlassprite; | |
} | |
}); | |
multimap.asMap() | |
.forEach( | |
(p_376688_, p_252017_) -> LOGGER.warn( | |
"Missing textures in model {}:\n{}", | |
p_376688_, | |
p_252017_.stream() | |
.sorted(Material.COMPARATOR) | |
.map(p_325574_ -> " " + p_325574_.atlasLocation() + ":" + p_325574_.texture()) | |
.collect(Collectors.joining("\n")) | |
) | |
); | |
multimap1.asMap() | |
.forEach( | |
(p_374739_, p_374740_) -> LOGGER.warn( | |
"Missing texture references in model {}:\n{}", | |
p_374739_, | |
p_374740_.stream().sorted().map(p_374742_ -> " " + p_374742_).collect(Collectors.joining("\n")) | |
) | |
); | |
p_252136_.popPush("dispatch"); | |
Map<BlockState, BakedModel> map = createBlockStateToModelDispatch(modelbakery$bakingresult.blockStateModels(), modelbakery$bakingresult.missingModel()); | |
CompletableFuture<Void> completablefuture = CompletableFuture.allOf( | |
p_250646_.values().stream().map(AtlasSet.StitchResult::readyForUpload).toArray(CompletableFuture[]::new) | |
); | |
p_252136_.pop(); | |
return new ModelManager.ReloadState(modelbakery$bakingresult, p_361513_, map, p_250646_, p_378097_, p_377275_, completablefuture); | |
} | |
private static Map<BlockState, BakedModel> createBlockStateToModelDispatch(Map<ModelResourceLocation, BakedModel> p_377857_, BakedModel p_378390_) { | |
Map<BlockState, BakedModel> map = new IdentityHashMap<>(); | |
for (Block block : BuiltInRegistries.BLOCK) { | |
block.getStateDefinition().getPossibleStates().forEach(p_374738_ -> { | |
ResourceLocation resourcelocation = p_374738_.getBlock().builtInRegistryHolder().key().location(); | |
ModelResourceLocation modelresourcelocation = BlockModelShaper.stateToModelLocation(resourcelocation, p_374738_); | |
BakedModel bakedmodel = p_377857_.get(modelresourcelocation); | |
if (bakedmodel == null) { | |
LOGGER.warn("Missing model for variant: '{}'", modelresourcelocation); | |
map.putIfAbsent(p_374738_, p_378390_); | |
} else { | |
map.put(p_374738_, bakedmodel); | |
} | |
}); | |
} | |
return map; | |
} | |
private static Object2IntMap<BlockState> buildModelGroups(BlockColors p_369941_, BlockStateModelLoader.LoadedModels p_360724_) { | |
return ModelGroupCollector.build(p_369941_, p_360724_); | |
} | |
private void apply(ModelManager.ReloadState p_248996_, ProfilerFiller p_251960_) { | |
p_251960_.push("upload"); | |
p_248996_.atlasPreparations.values().forEach(AtlasSet.StitchResult::upload); | |
ModelBakery.BakingResult modelbakery$bakingresult = p_248996_.bakedModels; | |
this.bakedBlockStateModels = modelbakery$bakingresult.blockStateModels(); | |
this.bakedItemStackModels = modelbakery$bakingresult.itemStackModels(); | |
this.itemProperties = modelbakery$bakingresult.itemProperties(); | |
this.modelGroups = p_248996_.modelGroups; | |
this.missingModel = modelbakery$bakingresult.missingModel(); | |
this.missingItemModel = modelbakery$bakingresult.missingItemModel(); | |
p_251960_.popPush("cache"); | |
this.blockModelShaper.replaceCache(p_248996_.modelCache); | |
this.specialBlockModelRenderer = p_248996_.specialBlockModelRenderer; | |
this.entityModelSet = p_248996_.entityModelSet; | |
p_251960_.pop(); | |
} | |
public boolean requiresRender(BlockState p_119416_, BlockState p_119417_) { | |
if (p_119416_ == p_119417_) { | |
return false; | |
} else { | |
int i = this.modelGroups.getInt(p_119416_); | |
if (i != -1) { | |
int j = this.modelGroups.getInt(p_119417_); | |
if (i == j) { | |
FluidState fluidstate = p_119416_.getFluidState(); | |
FluidState fluidstate1 = p_119417_.getFluidState(); | |
return fluidstate != fluidstate1; | |
} | |
} | |
return true; | |
} | |
} | |
public TextureAtlas getAtlas(ResourceLocation p_119429_) { | |
return this.atlases.getAtlas(p_119429_); | |
} | |
public void close() { | |
this.atlases.close(); | |
} | |
public void updateMaxMipLevel(int p_119411_) { | |
this.maxMipmapLevels = p_119411_; | |
} | |
public Supplier<SpecialBlockModelRenderer> specialBlockModelRenderer() { | |
return () -> this.specialBlockModelRenderer; | |
} | |
public Supplier<EntityModelSet> entityModels() { | |
return () -> this.entityModelSet; | |
} | |
static record ReloadState( | |
ModelBakery.BakingResult bakedModels, | |
Object2IntMap<BlockState> modelGroups, | |
Map<BlockState, BakedModel> modelCache, | |
Map<ResourceLocation, AtlasSet.StitchResult> atlasPreparations, | |
EntityModelSet entityModelSet, | |
SpecialBlockModelRenderer specialBlockModelRenderer, | |
CompletableFuture<Void> readyForUpload | |
) { | |
} | |
} |