ModelManager.java

net.minecraft.client.resources.model.ModelManager

信息

  • 全限定名:net.minecraft.client.resources.model.ModelManager
  • 类型:public class
  • 包:net.minecraft.client.resources.model
  • 源码路径:src/main/java/net/minecraft/client/resources/model/ModelManager.java
  • 起始行号:L66
  • 实现:PreparableReloadListener
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L67
    • 说明:

      TODO

  • MODEL_LISTER

    • 类型: FileToIdConverter
    • 修饰符: private static final
    • 源码定位: L68
    • 说明:

      TODO

  • bakedItemStackModels

    • 类型: Map<Identifier,ItemModel>
    • 修饰符: private
    • 源码定位: L69
    • 说明:

      TODO

  • itemProperties

    • 类型: Map<Identifier,ClientItem.Properties>
    • 修饰符: private
    • 源码定位: L70
    • 说明:

      TODO

  • atlasManager

    • 类型: AtlasManager
    • 修饰符: private final
    • 源码定位: L71
    • 说明:

      TODO

  • playerSkinRenderCache

    • 类型: PlayerSkinRenderCache
    • 修饰符: private final
    • 源码定位: L72
    • 说明:

      TODO

  • blockColors

    • 类型: BlockColors
    • 修饰符: private final
    • 源码定位: L73
    • 说明:

      TODO

  • entityModelSet

    • 类型: EntityModelSet
    • 修饰符: private
    • 源码定位: L74
    • 说明:

      TODO

  • missingModels

    • 类型: ModelBakery.MissingModels
    • 修饰符: private
    • 源码定位: L75
    • 说明:

      TODO

  • blockStateModelSet

    • 类型: BlockStateModelSet
    • 修饰符: private
    • 源码定位: L76
    • 说明:

      TODO

  • blockModelSet

    • 类型: BlockModelSet
    • 修饰符: private
    • 源码定位: L77
    • 说明:

      TODO

  • fluidStateModelSet

    • 类型: FluidStateModelSet
    • 修饰符: private
    • 源码定位: L78
    • 说明:

      TODO

  • modelGroups

    • 类型: Object2IntMap<BlockState>
    • 修饰符: private
    • 源码定位: L79
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.resources.model.ModelManager.ReloadState

    • 类型: record
    • 修饰符: private
    • 源码定位: L359
    • 说明:

      TODO

  • net.minecraft.client.resources.model.ModelManager.ResolvedModels

    • 类型: record
    • 修饰符: private
    • 源码定位: L370
    • 说明:

      TODO

构造器

public ModelManager(BlockColors blockColors, AtlasManager atlasManager, PlayerSkinRenderCache playerSkinRenderCache) @ L81

  • 构造器名:ModelManager
  • 源码定位:L81
  • 修饰符:public

参数:

  • blockColors: BlockColors
  • atlasManager: AtlasManager
  • playerSkinRenderCache: PlayerSkinRenderCache

说明:

TODO

方法

下面的方法块按源码顺序生成。

public ItemModel getItemModel(Identifier id) @ L87

  • 方法名:getItemModel
  • 源码定位:L87
  • 返回类型:ItemModel
  • 修饰符:public

参数:

  • id: Identifier

说明:

TODO

public ClientItem.Properties getItemProperties(Identifier id) @ L91

  • 方法名:getItemProperties
  • 源码定位:L91
  • 返回类型:ClientItem.Properties
  • 修饰符:public

参数:

  • id: Identifier

说明:

TODO

public BlockStateModelSet getBlockStateModelSet() @ L95

  • 方法名:getBlockStateModelSet
  • 源码定位:L95
  • 返回类型:BlockStateModelSet
  • 修饰符:public

参数:

说明:

TODO

public BlockModelSet getBlockModelSet() @ L99

  • 方法名:getBlockModelSet
  • 源码定位:L99
  • 返回类型:BlockModelSet
  • 修饰符:public

参数:

说明:

TODO

public FluidStateModelSet getFluidStateModelSet() @ L103

  • 方法名:getFluidStateModelSet
  • 源码定位:L103
  • 返回类型:FluidStateModelSet
  • 修饰符:public

参数:

说明:

TODO

public final CompletableFuture<Void> reload(PreparableReloadListener.SharedState currentReload, Executor taskExecutor, PreparableReloadListener.PreparationBarrier preparationBarrier, Executor reloadExecutor) @ L107

  • 方法名:reload
  • 源码定位:L107
  • 返回类型:CompletableFuture
  • 修饰符:public final

参数:

  • currentReload: PreparableReloadListener.SharedState
  • taskExecutor: Executor
  • preparationBarrier: PreparableReloadListener.PreparationBarrier
  • reloadExecutor: Executor

说明:

TODO

private static CompletableFuture<Map<Identifier,UnbakedModel>> loadBlockModels(ResourceManager manager, Executor executor) @ L174

  • 方法名:loadBlockModels
  • 源码定位:L174
  • 返回类型:CompletableFuture<Map<Identifier,UnbakedModel>>
  • 修饰符:private static

参数:

  • manager: ResourceManager
  • executor: Executor

说明:

TODO

private static ModelManager.ResolvedModels discoverModelDependencies(Map<Identifier,UnbakedModel> allModels, BlockStateModelLoader.LoadedModels blockStateModels, ClientItemInfoLoader.LoadedClientInfos itemInfos) @ L204

  • 方法名:discoverModelDependencies
  • 源码定位:L204
  • 返回类型:ModelManager.ResolvedModels
  • 修饰符:private static

参数:

  • allModels: Map<Identifier,UnbakedModel>
  • blockStateModels: BlockStateModelLoader.LoadedModels
  • itemInfos: ClientItemInfoLoader.LoadedClientInfos

说明:

TODO

private static CompletableFuture<ModelManager.ReloadState> loadModels(SpriteLoader.Preparations blockAtlas, SpriteLoader.Preparations itemAtlas, ModelBakery bakery, LoadedBlockModels blockModels, Object2IntMap<BlockState> modelGroups, EntityModelSet entityModelSet, Executor taskExecutor) @ L219

  • 方法名:loadModels
  • 源码定位:L219
  • 返回类型:CompletableFuture<ModelManager.ReloadState>
  • 修饰符:private static

参数:

  • blockAtlas: SpriteLoader.Preparations
  • itemAtlas: SpriteLoader.Preparations
  • bakery: ModelBakery
  • blockModels: LoadedBlockModels
  • modelGroups: Object2IntMap
  • entityModelSet: EntityModelSet
  • taskExecutor: Executor

说明:

TODO

private static Map<BlockState,BlockStateModel> createBlockStateToModelDispatch(Map<BlockState,BlockStateModel> bakedModels, BlockStateModel missingModel) @ L296

  • 方法名:createBlockStateToModelDispatch
  • 源码定位:L296
  • 返回类型:Map<BlockState,BlockStateModel>
  • 修饰符:private static

参数:

  • bakedModels: Map<BlockState,BlockStateModel>
  • missingModel: BlockStateModel

说明:

TODO

private static Object2IntMap<BlockState> buildModelGroups(BlockColors blockColors, BlockStateModelLoader.LoadedModels blockStateModels) @ L315

  • 方法名:buildModelGroups
  • 源码定位:L315
  • 返回类型:Object2IntMap
  • 修饰符:private static

参数:

  • blockColors: BlockColors
  • blockStateModels: BlockStateModelLoader.LoadedModels

说明:

TODO

private void apply(ModelManager.ReloadState preparations) @ L324

  • 方法名:apply
  • 源码定位:L324
  • 返回类型:void
  • 修饰符:private

参数:

  • preparations: ModelManager.ReloadState

说明:

TODO

public boolean requiresRender(BlockState oldState, BlockState newState) @ L336

  • 方法名:requiresRender
  • 源码定位:L336
  • 返回类型:boolean
  • 修饰符:public

参数:

  • oldState: BlockState
  • newState: BlockState

说明:

TODO

public Supplier<EntityModelSet> entityModels() @ L354

  • 方法名:entityModels
  • 源码定位:L354
  • 返回类型:Supplier
  • 修饰符:public

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class ModelManager implements PreparableReloadListener {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final FileToIdConverter MODEL_LISTER = FileToIdConverter.json("models");
    private Map<Identifier, ItemModel> bakedItemStackModels = Map.of();
    private Map<Identifier, ClientItem.Properties> itemProperties = Map.of();
    private final AtlasManager atlasManager;
    private final PlayerSkinRenderCache playerSkinRenderCache;
    private final BlockColors blockColors;
    private EntityModelSet entityModelSet = EntityModelSet.EMPTY;
    private ModelBakery.MissingModels missingModels;
    private @Nullable BlockStateModelSet blockStateModelSet;
    private @Nullable BlockModelSet blockModelSet;
    private @Nullable FluidStateModelSet fluidStateModelSet;
    private Object2IntMap<BlockState> modelGroups = Object2IntMaps.emptyMap();
 
    public ModelManager(BlockColors blockColors, AtlasManager atlasManager, PlayerSkinRenderCache playerSkinRenderCache) {
        this.blockColors = blockColors;
        this.atlasManager = atlasManager;
        this.playerSkinRenderCache = playerSkinRenderCache;
    }
 
    public ItemModel getItemModel(Identifier id) {
        return this.bakedItemStackModels.getOrDefault(id, this.missingModels.item());
    }
 
    public ClientItem.Properties getItemProperties(Identifier id) {
        return this.itemProperties.getOrDefault(id, ClientItem.Properties.DEFAULT);
    }
 
    public BlockStateModelSet getBlockStateModelSet() {
        return Objects.requireNonNull(this.blockStateModelSet, "Block models not yet initialized");
    }
 
    public BlockModelSet getBlockModelSet() {
        return Objects.requireNonNull(this.blockModelSet, "Block models not yet initialized");
    }
 
    public FluidStateModelSet getFluidStateModelSet() {
        return Objects.requireNonNull(this.fluidStateModelSet, "Fluid models not yet initialized");
    }
 
    @Override
    public final CompletableFuture<Void> reload(
        PreparableReloadListener.SharedState currentReload,
        Executor taskExecutor,
        PreparableReloadListener.PreparationBarrier preparationBarrier,
        Executor reloadExecutor
    ) {
        ResourceManager manager = currentReload.resourceManager();
        CompletableFuture<EntityModelSet> entityModelSet = CompletableFuture.supplyAsync(EntityModelSet::vanilla, taskExecutor);
        CompletableFuture<Map<Identifier, UnbakedModel>> modelCache = loadBlockModels(manager, taskExecutor);
        CompletableFuture<BlockStateModelLoader.LoadedModels> blockStateModels = BlockStateModelLoader.loadBlockStates(manager, taskExecutor);
        CompletableFuture<Map<BlockState, BlockModel.Unbaked>> blockModelContents = CompletableFuture.supplyAsync(
            () -> BuiltInBlockModels.createBlockModels(this.blockColors), taskExecutor
        );
        CompletableFuture<ClientItemInfoLoader.LoadedClientInfos> itemStackModels = ClientItemInfoLoader.scheduleLoad(manager, taskExecutor);
        CompletableFuture<ModelManager.ResolvedModels> modelDiscovery = CompletableFuture.allOf(modelCache, blockStateModels, itemStackModels)
            .thenApplyAsync(var3 -> discoverModelDependencies(modelCache.join(), blockStateModels.join(), itemStackModels.join()), taskExecutor);
        CompletableFuture<Object2IntMap<BlockState>> modelGroups = blockStateModels.thenApplyAsync(
            models -> buildModelGroups(this.blockColors, models), taskExecutor
        );
        AtlasManager.PendingStitchResults pendingStitches = currentReload.get(AtlasManager.PENDING_STITCH);
        CompletableFuture<SpriteLoader.Preparations> pendingBlockAtlasSprites = pendingStitches.get(AtlasIds.BLOCKS);
        CompletableFuture<SpriteLoader.Preparations> pendingItemAtlasSprites = pendingStitches.get(AtlasIds.ITEMS);
        CompletableFuture<LoadedBlockModels> blockModels = CompletableFuture.allOf(blockModelContents, entityModelSet)
            .thenApplyAsync(var3 -> new LoadedBlockModels(blockModelContents.join(), entityModelSet.join(), this.atlasManager, this.playerSkinRenderCache));
        return CompletableFuture.allOf(
                pendingBlockAtlasSprites,
                pendingItemAtlasSprites,
                modelDiscovery,
                modelGroups,
                blockStateModels,
                itemStackModels,
                entityModelSet,
                blockModels,
                modelCache
            )
            .thenComposeAsync(
                var11x -> {
                    SpriteLoader.Preparations blockAtlasSprites = pendingBlockAtlasSprites.join();
                    SpriteLoader.Preparations itemAtlasSprites = pendingItemAtlasSprites.join();
                    ModelManager.ResolvedModels resolvedModels = modelDiscovery.join();
                    Object2IntMap<BlockState> groups = modelGroups.join();
                    Set<Identifier> unreferencedModels = Sets.difference(modelCache.join().keySet(), resolvedModels.models.keySet());
                    if (!unreferencedModels.isEmpty()) {
                        LOGGER.debug(
                            "Unreferenced models: \n{}",
                            unreferencedModels.stream().sorted().map(modelId -> "\t" + modelId + "\n").collect(Collectors.joining())
                        );
                    }
 
                    ModelBakery bakery = new ModelBakery(
                        entityModelSet.join(),
                        this.atlasManager,
                        this.playerSkinRenderCache,
                        blockStateModels.join().models(),
                        itemStackModels.join().contents(),
                        resolvedModels.models(),
                        resolvedModels.missing()
                    );
                    return loadModels(blockAtlasSprites, itemAtlasSprites, bakery, blockModels.join(), groups, entityModelSet.join(), taskExecutor);
                },
                taskExecutor
            )
            .thenCompose(preparationBarrier::wait)
            .thenAcceptAsync(this::apply, reloadExecutor);
    }
 
    private static CompletableFuture<Map<Identifier, UnbakedModel>> loadBlockModels(ResourceManager manager, Executor executor) {
        return CompletableFuture.<Map<Identifier, Resource>>supplyAsync(() -> MODEL_LISTER.listMatchingResources(manager), executor)
            .thenCompose(
                resources -> {
                    List<CompletableFuture<Pair<Identifier, CuboidModel>>> result = new ArrayList<>(resources.size());
 
                    for (Entry<Identifier, Resource> resource : resources.entrySet()) {
                        result.add(CompletableFuture.supplyAsync(() -> {
                            Identifier modelId = MODEL_LISTER.fileToId(resource.getKey());
 
                            try {
                                Pair t$;
                                try (Reader reader = resource.getValue().openAsReader()) {
                                    t$ = Pair.of(modelId, CuboidModel.fromStream(reader));
                                }
 
                                return t$;
                            } catch (Exception var7) {
                                LOGGER.error("Failed to load model {}", resource.getKey(), var7);
                                return null;
                            }
                        }, executor));
                    }
 
                    return Util.sequence(result)
                        .thenApply(pairs -> pairs.stream().filter(Objects::nonNull).collect(Collectors.toUnmodifiableMap(Pair::getFirst, Pair::getSecond)));
                }
            );
    }
 
    private static ModelManager.ResolvedModels discoverModelDependencies(
        Map<Identifier, UnbakedModel> allModels, BlockStateModelLoader.LoadedModels blockStateModels, ClientItemInfoLoader.LoadedClientInfos itemInfos
    ) {
        ModelManager.ResolvedModels var5;
        try (Zone ignored = Profiler.get().zone("dependencies")) {
            ModelDiscovery result = new ModelDiscovery(allModels, MissingCuboidModel.missingModel());
            result.addSpecialModel(ItemModelGenerator.GENERATED_ITEM_MODEL_ID, new ItemModelGenerator());
            blockStateModels.models().values().forEach(result::addRoot);
            itemInfos.contents().values().forEach(info -> result.addRoot(info.model()));
            var5 = new ModelManager.ResolvedModels(result.missingModel(), result.resolve());
        }
 
        return var5;
    }
 
    private static CompletableFuture<ModelManager.ReloadState> loadModels(
        SpriteLoader.Preparations blockAtlas,
        SpriteLoader.Preparations itemAtlas,
        ModelBakery bakery,
        LoadedBlockModels blockModels,
        Object2IntMap<BlockState> modelGroups,
        EntityModelSet entityModelSet,
        Executor taskExecutor
    ) {
        final Multimap<String, Identifier> missingSprites = Multimaps.synchronizedMultimap(HashMultimap.create());
        final Multimap<String, String> missingReferences = Multimaps.synchronizedMultimap(HashMultimap.create());
        MaterialBaker materialBaker = new MaterialBaker() {
            private final Material.Baked blockMissing = new Material.Baked(blockAtlas.missing(), false);
            private final Map<Material, Material.@Nullable Baked> bakedMaterials = new ConcurrentHashMap<>();
            private final Function<Material, Material.@Nullable Baked> bakerFunction = this::bake;
 
            @Override
            public Material.Baked get(Material material, ModelDebugName name) {
                Material.Baked baked = this.bakedMaterials.computeIfAbsent(material, this.bakerFunction);
                if (baked == null) {
                    missingSprites.put(name.debugName(), material.sprite());
                    return this.blockMissing;
                } else {
                    return baked;
                }
            }
 
            private Material.@Nullable Baked bake(Material material) {
                Material.Baked itemMaterial = this.bakeForAtlas(material, itemAtlas);
                return itemMaterial != null ? itemMaterial : this.bakeForAtlas(material, blockAtlas);
            }
 
            private Material.@Nullable Baked bakeForAtlas(Material material, SpriteLoader.Preparations atlas) {
                TextureAtlasSprite sprite = atlas.getSprite(material.sprite());
                return sprite != null ? new Material.Baked(sprite, material.forceTranslucent()) : null;
            }
 
            @Override
            public Material.Baked reportMissingReference(String reference, ModelDebugName responsibleModel) {
                missingReferences.put(responsibleModel.debugName(), reference);
                return this.blockMissing;
            }
        };
        CompletableFuture<ModelBakery.BakingResult> bakedStateResults = bakery.bakeModels(materialBaker, taskExecutor);
        CompletableFuture<Map<BlockState, BlockModel>> bakedModelsFuture = bakedStateResults.thenCompose(
            bakingResult -> blockModels.bake(bakingResult::getBlockStateModel, bakingResult.missingModels().block(), taskExecutor)
        );
        return bakedStateResults.thenCombine(
            bakedModelsFuture,
            (bakingResult, bakedModels) -> {
                Map<Fluid, FluidModel> fluidModels = FluidStateModelSet.bake(materialBaker);
                missingSprites.asMap()
                    .forEach(
                        (location, sprites) -> LOGGER.warn(
                            "Missing textures in model {}:\n{}",
                            location,
                            sprites.stream().sorted().map(sprite -> "    " + sprite).collect(Collectors.joining("\n"))
                        )
                    );
                missingReferences.asMap()
                    .forEach(
                        (location, references) -> LOGGER.warn(
                            "Missing texture references in model {}:\n{}",
                            location,
                            references.stream().sorted().map(reference -> "    " + reference).collect(Collectors.joining("\n"))
                        )
                    );
                Map<BlockState, BlockStateModel> modelByStateCache = createBlockStateToModelDispatch(
                    bakingResult.blockStateModels(), bakingResult.missingModels().block()
                );
                return new ModelManager.ReloadState(
                    bakingResult, modelGroups, modelByStateCache, (Map<BlockState, BlockModel>)bakedModels, fluidModels, entityModelSet
                );
            }
        );
    }
 
    private static Map<BlockState, BlockStateModel> createBlockStateToModelDispatch(Map<BlockState, BlockStateModel> bakedModels, BlockStateModel missingModel) {
        Object var8;
        try (Zone ignored = Profiler.get().zone("block state dispatch")) {
            Map<BlockState, BlockStateModel> modelByStateCache = new IdentityHashMap<>(bakedModels);
 
            for (Block block : BuiltInRegistries.BLOCK) {
                block.getStateDefinition().getPossibleStates().forEach(state -> {
                    if (bakedModels.putIfAbsent(state, missingModel) == null) {
                        LOGGER.warn("Missing model for variant: '{}'", state);
                    }
                });
            }
 
            var8 = modelByStateCache;
        }
 
        return (Map<BlockState, BlockStateModel>)var8;
    }
 
    private static Object2IntMap<BlockState> buildModelGroups(BlockColors blockColors, BlockStateModelLoader.LoadedModels blockStateModels) {
        Object2IntMap var3;
        try (Zone ignored = Profiler.get().zone("block groups")) {
            var3 = ModelGroupCollector.build(blockColors, blockStateModels);
        }
 
        return var3;
    }
 
    private void apply(ModelManager.ReloadState preparations) {
        ModelBakery.BakingResult bakedModels = preparations.bakedModels;
        this.bakedItemStackModels = bakedModels.itemStackModels();
        this.itemProperties = bakedModels.itemProperties();
        this.modelGroups = preparations.modelGroups;
        this.missingModels = bakedModels.missingModels();
        this.blockStateModelSet = new BlockStateModelSet(preparations.blockStateModels, this.missingModels.block());
        this.blockModelSet = new BlockModelSet(this.blockStateModelSet, preparations.blockModels, this.blockColors);
        this.fluidStateModelSet = new FluidStateModelSet(preparations.fluidModels, this.missingModels.fluid());
        this.entityModelSet = preparations.entityModelSet;
    }
 
    public boolean requiresRender(BlockState oldState, BlockState newState) {
        if (oldState == newState) {
            return false;
        } else {
            int oldModelGroup = this.modelGroups.getInt(oldState);
            if (oldModelGroup != -1) {
                int newModelGroup = this.modelGroups.getInt(newState);
                if (oldModelGroup == newModelGroup) {
                    FluidState oldFluidState = oldState.getFluidState();
                    FluidState newFluidState = newState.getFluidState();
                    return oldFluidState != newFluidState;
                }
            }
 
            return true;
        }
    }
 
    public Supplier<EntityModelSet> entityModels() {
        return () -> this.entityModelSet;
    }
 
    @OnlyIn(Dist.CLIENT)
    private record ReloadState(
        ModelBakery.BakingResult bakedModels,
        Object2IntMap<BlockState> modelGroups,
        Map<BlockState, BlockStateModel> blockStateModels,
        Map<BlockState, BlockModel> blockModels,
        Map<Fluid, FluidModel> fluidModels,
        EntityModelSet entityModelSet
    ) {
    }
 
    @OnlyIn(Dist.CLIENT)
    private record ResolvedModels(ResolvedModel missing, Map<Identifier, ResolvedModel> models) {
    }
}

引用的其他类