ModelDiscovery.java

net.minecraft.client.resources.model.ModelDiscovery

信息

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

    TODO

字段/常量

  • LOGGER

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

      TODO

  • modelWrappers

    • 类型: Object2ObjectMap<Identifier,ModelDiscovery.ModelWrapper>
    • 修饰符: private final
    • 源码定位: L36
    • 说明:

      TODO

  • missingModel

    • 类型: ModelDiscovery.ModelWrapper
    • 修饰符: private final
    • 源码定位: L37
    • 说明:

      TODO

  • uncachedResolver

    • 类型: Object2ObjectFunction<Identifier,ModelDiscovery.ModelWrapper>
    • 修饰符: private final
    • 源码定位: L38
    • 说明:

      TODO

  • resolver

    • 类型: ResolvableModel.Resolver
    • 修饰符: private final
    • 源码定位: L39
    • 说明:

      TODO

  • parentDiscoveryQueue

    • 类型: Queue<ModelDiscovery.ModelWrapper>
    • 修饰符: private final
    • 源码定位: L40
    • 说明:

      TODO

内部类/嵌套类型

  • net.minecraft.client.resources.model.ModelDiscovery.ModelWrapper

    • 类型: class
    • 修饰符: private static
    • 源码定位: L143
    • 说明:

      TODO

  • net.minecraft.client.resources.model.ModelDiscovery.Slot

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

      TODO

构造器

public ModelDiscovery(Map<Identifier,UnbakedModel> unbakedModels, UnbakedModel missingUnbakedModel) @ L42

  • 构造器名:ModelDiscovery
  • 源码定位:L42
  • 修饰符:public

参数:

  • unbakedModels: Map<Identifier,UnbakedModel>
  • missingUnbakedModel: UnbakedModel

说明:

TODO

方法

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

private static boolean isRoot(UnbakedModel model) @ L58

  • 方法名:isRoot
  • 源码定位:L58
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • model: UnbakedModel

说明:

TODO

private ModelDiscovery.ModelWrapper getOrCreateModel(Identifier id) @ L62

  • 方法名:getOrCreateModel
  • 源码定位:L62
  • 返回类型:ModelDiscovery.ModelWrapper
  • 修饰符:private

参数:

  • id: Identifier

说明:

TODO

private ModelDiscovery.ModelWrapper createAndQueueWrapper(Identifier id, UnbakedModel rawModel) @ L66

  • 方法名:createAndQueueWrapper
  • 源码定位:L66
  • 返回类型:ModelDiscovery.ModelWrapper
  • 修饰符:private

参数:

  • id: Identifier
  • rawModel: UnbakedModel

说明:

TODO

public void addRoot(ResolvableModel model) @ L76

  • 方法名:addRoot
  • 源码定位:L76
  • 返回类型:void
  • 修饰符:public

参数:

  • model: ResolvableModel

说明:

TODO

public void addSpecialModel(Identifier id, UnbakedModel model) @ L80

  • 方法名:addSpecialModel
  • 源码定位:L80
  • 返回类型:void
  • 修饰符:public

参数:

  • id: Identifier
  • model: UnbakedModel

说明:

TODO

public ResolvedModel missingModel() @ L91

  • 方法名:missingModel
  • 源码定位:L91
  • 返回类型:ResolvedModel
  • 修饰符:public

参数:

说明:

TODO

public Map<Identifier,ResolvedModel> resolve() @ L95

  • 方法名:resolve
  • 源码定位:L95
  • 返回类型:Map<Identifier,ResolvedModel>
  • 修饰符:public

参数:

说明:

TODO

private void discoverDependencies(List<ModelDiscovery.ModelWrapper> toValidate) @ L110

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

参数:

  • toValidate: List<ModelDiscovery.ModelWrapper>

说明:

TODO

private static void propagateValidity(List<ModelDiscovery.ModelWrapper> toValidate) @ L124

  • 方法名:propagateValidity
  • 源码定位:L124
  • 返回类型:void
  • 修饰符:private static

参数:

  • toValidate: List<ModelDiscovery.ModelWrapper>

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class ModelDiscovery {
    private static final Logger LOGGER = LogUtils.getLogger();
    private final Object2ObjectMap<Identifier, ModelDiscovery.ModelWrapper> modelWrappers = new Object2ObjectOpenHashMap<>();
    private final ModelDiscovery.ModelWrapper missingModel;
    private final Object2ObjectFunction<Identifier, ModelDiscovery.ModelWrapper> uncachedResolver;
    private final ResolvableModel.Resolver resolver;
    private final Queue<ModelDiscovery.ModelWrapper> parentDiscoveryQueue = new ArrayDeque<>();
 
    public ModelDiscovery(Map<Identifier, UnbakedModel> unbakedModels, UnbakedModel missingUnbakedModel) {
        this.missingModel = new ModelDiscovery.ModelWrapper(MissingCuboidModel.LOCATION, missingUnbakedModel, true);
        this.modelWrappers.put(MissingCuboidModel.LOCATION, this.missingModel);
        this.uncachedResolver = rawId -> {
            Identifier id = (Identifier)rawId;
            UnbakedModel rawModel = unbakedModels.get(id);
            if (rawModel == null) {
                LOGGER.warn("Missing block model: {}", id);
                return this.missingModel;
            } else {
                return this.createAndQueueWrapper(id, rawModel);
            }
        };
        this.resolver = this::getOrCreateModel;
    }
 
    private static boolean isRoot(UnbakedModel model) {
        return model.parent() == null;
    }
 
    private ModelDiscovery.ModelWrapper getOrCreateModel(Identifier id) {
        return this.modelWrappers.computeIfAbsent(id, this.uncachedResolver);
    }
 
    private ModelDiscovery.ModelWrapper createAndQueueWrapper(Identifier id, UnbakedModel rawModel) {
        boolean isRoot = isRoot(rawModel);
        ModelDiscovery.ModelWrapper result = new ModelDiscovery.ModelWrapper(id, rawModel, isRoot);
        if (!isRoot) {
            this.parentDiscoveryQueue.add(result);
        }
 
        return result;
    }
 
    public void addRoot(ResolvableModel model) {
        model.resolveDependencies(this.resolver);
    }
 
    public void addSpecialModel(Identifier id, UnbakedModel model) {
        if (!isRoot(model)) {
            LOGGER.warn("Trying to add non-root special model {}, ignoring", id);
        } else {
            ModelDiscovery.ModelWrapper previous = this.modelWrappers.put(id, this.createAndQueueWrapper(id, model));
            if (previous != null) {
                LOGGER.warn("Duplicate special model {}", id);
            }
        }
    }
 
    public ResolvedModel missingModel() {
        return this.missingModel;
    }
 
    public Map<Identifier, ResolvedModel> resolve() {
        List<ModelDiscovery.ModelWrapper> toValidate = new ArrayList<>();
        this.discoverDependencies(toValidate);
        propagateValidity(toValidate);
        Builder<Identifier, ResolvedModel> result = ImmutableMap.builder();
        this.modelWrappers.forEach((location, model) -> {
            if (model.valid) {
                result.put(location, model);
            } else {
                LOGGER.warn("Model {} ignored due to cyclic dependency", location);
            }
        });
        return result.build();
    }
 
    private void discoverDependencies(List<ModelDiscovery.ModelWrapper> toValidate) {
        ModelDiscovery.ModelWrapper current;
        while ((current = this.parentDiscoveryQueue.poll()) != null) {
            Identifier parentLocation = Objects.requireNonNull(current.wrapped.parent());
            ModelDiscovery.ModelWrapper parent = this.getOrCreateModel(parentLocation);
            current.parent = parent;
            if (parent.valid) {
                current.valid = true;
            } else {
                toValidate.add(current);
            }
        }
    }
 
    private static void propagateValidity(List<ModelDiscovery.ModelWrapper> toValidate) {
        boolean progressed = true;
 
        while (progressed) {
            progressed = false;
            Iterator<ModelDiscovery.ModelWrapper> iterator = toValidate.iterator();
 
            while (iterator.hasNext()) {
                ModelDiscovery.ModelWrapper model = iterator.next();
                if (Objects.requireNonNull(model.parent).valid) {
                    model.valid = true;
                    iterator.remove();
                    progressed = true;
                }
            }
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class ModelWrapper implements ResolvedModel {
        private static final ModelDiscovery.Slot<Boolean> KEY_AMBIENT_OCCLUSION = slot(0);
        private static final ModelDiscovery.Slot<UnbakedModel.GuiLight> KEY_GUI_LIGHT = slot(1);
        private static final ModelDiscovery.Slot<UnbakedGeometry> KEY_GEOMETRY = slot(2);
        private static final ModelDiscovery.Slot<ItemTransforms> KEY_TRANSFORMS = slot(3);
        private static final ModelDiscovery.Slot<TextureSlots> KEY_TEXTURE_SLOTS = slot(4);
        private static final ModelDiscovery.Slot<Material.Baked> KEY_PARTICLE_SPRITE = slot(5);
        private static final ModelDiscovery.Slot<QuadCollection> KEY_DEFAULT_GEOMETRY = slot(6);
        private static final int SLOT_COUNT = 7;
        private final Identifier id;
        private boolean valid;
        private ModelDiscovery.@Nullable ModelWrapper parent;
        private final UnbakedModel wrapped;
        private final AtomicReferenceArray<@Nullable Object> fixedSlots = new AtomicReferenceArray<>(7);
        private final Map<ModelState, QuadCollection> modelBakeCache = new ConcurrentHashMap<>();
 
        private static <T> ModelDiscovery.Slot<T> slot(int index) {
            Objects.checkIndex(index, 7);
            return new ModelDiscovery.Slot<>(index);
        }
 
        private ModelWrapper(Identifier id, UnbakedModel wrapped, boolean valid) {
            this.id = id;
            this.wrapped = wrapped;
            this.valid = valid;
        }
 
        @Override
        public UnbakedModel wrapped() {
            return this.wrapped;
        }
 
        @Override
        public @Nullable ResolvedModel parent() {
            return this.parent;
        }
 
        @Override
        public String debugName() {
            return this.id.toString();
        }
 
        private <T> @Nullable T getSlot(ModelDiscovery.Slot<T> key) {
            return (T)this.fixedSlots.get(key.index);
        }
 
        private <T> T updateSlot(ModelDiscovery.Slot<T> key, T value) {
            T currentValue = (T)this.fixedSlots.compareAndExchange(key.index, null, value);
            return currentValue == null ? value : currentValue;
        }
 
        private <T> T getSimpleProperty(ModelDiscovery.Slot<T> key, Function<ResolvedModel, T> getter) {
            T result = this.getSlot(key);
            return result != null ? result : this.updateSlot(key, getter.apply(this));
        }
 
        @Override
        public boolean getTopAmbientOcclusion() {
            return this.getSimpleProperty(KEY_AMBIENT_OCCLUSION, ResolvedModel::findTopAmbientOcclusion);
        }
 
        @Override
        public UnbakedModel.GuiLight getTopGuiLight() {
            return this.getSimpleProperty(KEY_GUI_LIGHT, ResolvedModel::findTopGuiLight);
        }
 
        @Override
        public ItemTransforms getTopTransforms() {
            return this.getSimpleProperty(KEY_TRANSFORMS, ResolvedModel::findTopTransforms);
        }
 
        @Override
        public UnbakedGeometry getTopGeometry() {
            return this.getSimpleProperty(KEY_GEOMETRY, ResolvedModel::findTopGeometry);
        }
 
        @Override
        public TextureSlots getTopTextureSlots() {
            return this.getSimpleProperty(KEY_TEXTURE_SLOTS, ResolvedModel::findTopTextureSlots);
        }
 
        @Override
        public Material.Baked resolveParticleMaterial(TextureSlots textureSlots, ModelBaker baker) {
            Material.Baked result = this.getSlot(KEY_PARTICLE_SPRITE);
            return result != null ? result : this.updateSlot(KEY_PARTICLE_SPRITE, ResolvedModel.resolveParticleMaterial(textureSlots, baker, this));
        }
 
        private QuadCollection bakeDefaultState(TextureSlots textureSlots, ModelBaker baker, ModelState state) {
            QuadCollection result = this.getSlot(KEY_DEFAULT_GEOMETRY);
            return result != null ? result : this.updateSlot(KEY_DEFAULT_GEOMETRY, this.getTopGeometry().bake(textureSlots, baker, state, this));
        }
 
        @Override
        public QuadCollection bakeTopGeometry(TextureSlots textureSlots, ModelBaker baker, ModelState state) {
            return state == BlockModelRotation.IDENTITY ? this.bakeDefaultState(textureSlots, baker, state) : this.modelBakeCache.computeIfAbsent(state, s -> {
                UnbakedGeometry topGeometry = this.getTopGeometry();
                return topGeometry.bake(textureSlots, baker, s, this);
            });
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private record Slot<T>(int index) {
    }
}

引用的其他类