net.minecraftforge.client.model.MultiLayerModel.java Source code

Java tutorial

Introduction

Here is the source code for net.minecraftforge.client.model.MultiLayerModel.java

Source

/*
 * Minecraft Forge
 * Copyright (c) 2016.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version 2.1
 * of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.minecraftforge.client.model;

import java.util.Collection;
import java.util.List;

import javax.vecmath.Matrix4f;

import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import net.minecraftforge.fml.common.FMLLog;

import org.apache.commons.lang3.tuple.Pair;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

public final class MultiLayerModel implements IModelCustomData {
    public static final MultiLayerModel INSTANCE = new MultiLayerModel(
            ImmutableMap.<Optional<BlockRenderLayer>, ModelResourceLocation>of());

    private final ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models;

    public MultiLayerModel(ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models) {
        this.models = models;
    }

    @Override
    public Collection<ResourceLocation> getDependencies() {
        return ImmutableList.<ResourceLocation>copyOf(models.values());
    }

    @Override
    public Collection<ResourceLocation> getTextures() {
        return ImmutableList.of();
    }

    private static ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> buildModels(
            ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models, IModelState state,
            VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) {
        ImmutableMap.Builder<Optional<BlockRenderLayer>, IBakedModel> builder = ImmutableMap.builder();
        for (Optional<BlockRenderLayer> key : models.keySet()) {
            IModel model = ModelLoaderRegistry.getModelOrLogError(models.get(key),
                    "Couldn't load MultiLayerModel dependency: " + models.get(key));
            builder.put(key, model.bake(new ModelStateComposition(state, model.getDefaultState()), format,
                    bakedTextureGetter));
        }
        return builder.build();
    }

    @Override
    public IBakedModel bake(IModelState state, VertexFormat format,
            Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) {
        IModel missing = ModelLoaderRegistry.getMissingModel();
        return new MultiLayerBakedModel(buildModels(models, state, format, bakedTextureGetter),
                missing.bake(missing.getDefaultState(), format, bakedTextureGetter),
                IPerspectiveAwareModel.MapWrapper.getTransforms(state));
    }

    @Override
    public IModelState getDefaultState() {
        return TRSRTransformation.identity();
    }

    @Override
    public MultiLayerModel process(ImmutableMap<String, String> customData) {
        ImmutableMap.Builder<Optional<BlockRenderLayer>, ModelResourceLocation> builder = ImmutableMap.builder();
        for (String key : customData.keySet()) {
            if ("base".equals(key)) {
                builder.put(Optional.<BlockRenderLayer>absent(), getLocation(customData.get(key)));
            }
            for (BlockRenderLayer layer : BlockRenderLayer.values()) {
                if (layer.toString().equals(key)) {
                    builder.put(Optional.of(layer), getLocation(customData.get(key)));
                }
            }
        }
        ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models = builder.build();
        if (models.isEmpty())
            return INSTANCE;
        return new MultiLayerModel(models);
    }

    private ModelResourceLocation getLocation(String json) {
        JsonElement e = new JsonParser().parse(json);
        if (e.isJsonPrimitive() && e.getAsJsonPrimitive().isString()) {
            return new ModelResourceLocation(e.getAsString());
        }
        FMLLog.severe("Expect ModelResourceLocation, got: ", json);
        return new ModelResourceLocation("builtin/missing", "missing");
    }

    private static final class MultiLayerBakedModel implements IPerspectiveAwareModel {
        private final ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> models;
        private final ImmutableMap<TransformType, TRSRTransformation> cameraTransforms;;
        private final IBakedModel base;
        private final IBakedModel missing;
        private final ImmutableMap<Optional<EnumFacing>, ImmutableList<BakedQuad>> quads;

        public MultiLayerBakedModel(ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> models,
                IBakedModel missing, ImmutableMap<TransformType, TRSRTransformation> cameraTransforms) {
            this.models = models;
            this.cameraTransforms = cameraTransforms;
            this.missing = missing;
            if (models.containsKey(Optional.absent())) {
                base = models.get(Optional.absent());
            } else {
                base = missing;
            }
            ImmutableMap.Builder<Optional<EnumFacing>, ImmutableList<BakedQuad>> quadBuilder = ImmutableMap
                    .builder();
            quadBuilder.put(Optional.<EnumFacing>absent(), buildQuads(models, Optional.<EnumFacing>absent()));
            for (EnumFacing side : EnumFacing.values()) {
                quadBuilder.put(Optional.of(side), buildQuads(models, Optional.of(side)));
            }
            quads = quadBuilder.build();
        }

        private static ImmutableList<BakedQuad> buildQuads(
                ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> models, Optional<EnumFacing> side) {
            ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
            for (IBakedModel model : models.values()) {
                builder.addAll(model.getQuads(null, side.orNull(), 0));
            }
            return builder.build();
        }

        @Override
        public List<BakedQuad> getQuads(IBlockState state, EnumFacing side, long rand) {
            IBakedModel model;
            BlockRenderLayer layer = MinecraftForgeClient.getRenderLayer();
            if (layer == null) {
                return quads.get(Optional.fromNullable(side));
            } else if (!models.containsKey(Optional.of(layer))) {
                model = missing;
            } else {
                model = models.get(Optional.of(layer));
            }
            // assumes that child model will handle this state properly. FIXME?
            return model.getQuads(state, side, rand);
        }

        @Override
        public boolean isAmbientOcclusion() {
            return base.isAmbientOcclusion();
        }

        @Override
        public boolean isGui3d() {
            return base.isGui3d();
        }

        @Override
        public boolean isBuiltInRenderer() {
            return base.isBuiltInRenderer();
        }

        @Override
        public TextureAtlasSprite getParticleTexture() {
            return base.getParticleTexture();
        }

        @Override
        public ItemCameraTransforms getItemCameraTransforms() {
            return ItemCameraTransforms.DEFAULT;
        }

        @Override
        public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType) {
            return IPerspectiveAwareModel.MapWrapper.handlePerspective(this, cameraTransforms, cameraTransformType);
        }

        @Override
        public ItemOverrideList getOverrides() {
            return ItemOverrideList.NONE;
        }
    }

    public static enum Loader implements ICustomModelLoader {
        INSTANCE;

        public void onResourceManagerReload(IResourceManager resourceManager) {
        }

        public boolean accepts(ResourceLocation modelLocation) {
            return modelLocation.getResourceDomain().equals("forge")
                    && (modelLocation.getResourcePath().equals("multi-layer")
                            || modelLocation.getResourcePath().equals("models/block/multi-layer")
                            || modelLocation.getResourcePath().equals("models/item/multi-layer"));
        }

        public IModel loadModel(ResourceLocation modelLocation) {
            return MultiLayerModel.INSTANCE;
        }
    }
}