mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Backport implementation of the new model system from 1.15: (#6180)
- Extended block model format to support a "loader" value, as an alternative to "elements.
- Extended block model format to support "gui_3d":false as a way to mark a model as being intended to be flat and lit from the front.
(No this is not equivalent to the "gui_light" flag in 1.15.2. It changes more than just the light direction)
- Added a new OBJ loader designed from the ground up to work with the new model system.
- Added stock models to serve as parent models when using custom loaders (forge:item/default, forge:item/default-tool, and forge:block/default).
- Added stock white texture (forge:white) for use in loaders that want to draw untextured geometry.
- Deprecated the old loader components, since they are gone in 1.15 already.
This commit is contained in:
1 parent
4e45da3cc3
commit
dc145a3dfc
82 files changed
+5576
-305
No files matched your search
@@ -181,7 +181,6 @@ public class ForgeHooksClient
|
||||
{
|
||||
ModLoader.get().postEvent(new TextureStitchEvent.Pre(map, resourceLocations));
|
||||
// ModelLoader.White.INSTANCE.register(map); // TODO Custom TAS
|
||||
ModelDynBucket.LoaderDynBucket.INSTANCE.register(map);
|
||||
}
|
||||
|
||||
public static void onTextureStitchedPost(AtlasTexture map)
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Random;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
@@ -34,6 +35,7 @@ import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IEnviromentBlockReader;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public interface IForgeBakedModel
|
||||
{
|
||||
@@ -50,11 +52,16 @@ public interface IForgeBakedModel
|
||||
|
||||
default boolean isAmbientOcclusion(BlockState state) { return getBakedModel().isAmbientOcclusion(); }
|
||||
|
||||
/**
|
||||
* Override to tell the new model loader that it shouldn't wrap this model
|
||||
*/
|
||||
default boolean doesHandlePerspectives() { return false; }
|
||||
|
||||
/*
|
||||
* Returns the pair of the model for the given perspective, and the matrix that
|
||||
* should be applied to the GL state before rendering it (matrix may be null).
|
||||
*/
|
||||
default org.apache.commons.lang3.tuple.Pair<? extends IBakedModel, javax.vecmath.Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
default Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
return net.minecraftforge.client.ForgeHooksClient.handlePerspective(getBakedModel(), cameraTransformType);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,12 @@ public class BakedItemModel implements IBakedModel
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,6 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IEnviromentBlockReader;
|
||||
import net.minecraft.world.IWorldReader;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -95,6 +94,12 @@ public abstract class BakedModelWrapper<T extends IBakedModel> implements IBaked
|
||||
return originalModel.getOverrides();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return originalModel.doesHandlePerspectives();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class BlockModelConfiguration implements IModelConfiguration
|
||||
{
|
||||
public final BlockModel owner;
|
||||
public final VisibilityData visibilityData = new VisibilityData();
|
||||
@Nullable
|
||||
private IModelGeometry<?> customGeometry;
|
||||
@Nullable
|
||||
private IModelState customModelState;
|
||||
|
||||
public BlockModelConfiguration(BlockModel owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IUnbakedModel getOwnerModel()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelName()
|
||||
{
|
||||
return owner.name;
|
||||
}
|
||||
|
||||
public boolean hasCustomGeometry()
|
||||
{
|
||||
return getCustomGeometry() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IModelGeometry<?> getCustomGeometry()
|
||||
{
|
||||
return owner.parent != null && customGeometry == null ? owner.parent.customData.getCustomGeometry() : customGeometry;
|
||||
}
|
||||
|
||||
public void setCustomGeometry(IModelGeometry<?> geometry)
|
||||
{
|
||||
this.customGeometry = geometry;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IModelState getCustomModelState()
|
||||
{
|
||||
return owner.parent != null && customModelState == null ? owner.parent.customData.getCustomModelState() : customModelState;
|
||||
}
|
||||
|
||||
public void setCustomModelState(IModelState modelState)
|
||||
{
|
||||
this.customModelState = modelState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPartVisibility(IModelGeometryPart part, boolean fallback)
|
||||
{
|
||||
return owner.parent != null && !visibilityData.hasCustomVisibility(part) ?
|
||||
owner.parent.customData.getPartVisibility(part, fallback):
|
||||
visibilityData.isVisible(part, fallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTexturePresent(String name)
|
||||
{
|
||||
return owner.isTexturePresent(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveTexture(String name)
|
||||
{
|
||||
return owner.resolveTextureName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShadedInGui() {
|
||||
return owner.isGui3d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSmoothLighting()
|
||||
{
|
||||
return owner.isAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemCameraTransforms getCameraTransforms()
|
||||
{
|
||||
return owner.getAllTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModelState getCombinedState()
|
||||
{
|
||||
IModelState state = getCustomModelState();
|
||||
|
||||
return state != null
|
||||
? new SimpleModelState(PerspectiveMapWrapper.getTransformsWithFallback(state, getCameraTransforms()), state.apply(Optional.empty()))
|
||||
: new SimpleModelState(PerspectiveMapWrapper.getTransforms(getCameraTransforms()));
|
||||
}
|
||||
|
||||
public void copyFrom(BlockModelConfiguration other)
|
||||
{
|
||||
this.customGeometry = other.customGeometry;
|
||||
this.customModelState = other.customModelState;
|
||||
this.visibilityData.copyFrom(other.visibilityData);
|
||||
}
|
||||
|
||||
public Collection<ResourceLocation> getTextureDependencies(Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
IModelGeometry<?> geometry = getCustomGeometry();
|
||||
return geometry == null ? Collections.emptySet() :
|
||||
geometry.getTextureDependencies(this, modelGetter, missingTextureErrors);
|
||||
}
|
||||
|
||||
public IBakedModel bake(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides)
|
||||
{
|
||||
IModelGeometry<?> geometry = getCustomGeometry();
|
||||
if (geometry == null)
|
||||
throw new IllegalStateException("Can not use custom baking without custom geometry");
|
||||
return geometry.bake(this, bakery, bakedTextureGetter, sprite, format, overrides);
|
||||
}
|
||||
|
||||
public static class VisibilityData
|
||||
{
|
||||
private final Map<String, Boolean> data = new HashMap<>();
|
||||
|
||||
public boolean hasCustomVisibility(IModelGeometryPart part)
|
||||
{
|
||||
return data.containsKey(part.name());
|
||||
}
|
||||
|
||||
public boolean isVisible(IModelGeometryPart part, boolean fallback)
|
||||
{
|
||||
return data.getOrDefault(part.name(), fallback);
|
||||
}
|
||||
|
||||
public void setVisibilityState(String partName, boolean type)
|
||||
{
|
||||
data.put(partName, type);
|
||||
}
|
||||
|
||||
public void copyFrom(VisibilityData visibilityData)
|
||||
{
|
||||
data.clear();
|
||||
data.putAll(visibilityData.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import com.google.gson.GsonBuilder;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public class BlockStateLoader
|
||||
{
|
||||
private static final Gson GSON = (new GsonBuilder())
|
||||
@@ -103,7 +104,7 @@ public class BlockStateLoader
|
||||
entry.getKey().replace("=","_").replace(",","_"))
|
||||
);
|
||||
|
||||
IUnbakedModel model = ForgeVariantHelper.prepareInjectedModel((ModelLoader)bakery, modelLocation, var.getModel(), var.getSmooth(), var.getGui3d(), var.getTextures(), var.getOnlyPartsVariant(), var.getCustomData());
|
||||
IUnbakedModel model = ForgeVariantHelper.prepareInjectedModel(modelLocation, var.getModel(), var.getSmooth(), var.getGui3d(), var.getTextures(), var.getOnlyPartsVariant(), var.getCustomData());
|
||||
|
||||
modelConsumer.accept(modelLocation, model);
|
||||
}
|
||||
@@ -185,7 +186,7 @@ public class BlockStateLoader
|
||||
|
||||
private static class ForgeVariantHelper
|
||||
{
|
||||
public static IUnbakedModel prepareInjectedModel(ModelLoader bakery, ResourceLocation blockstateLocation, @Nullable ResourceLocation modelLocation, Optional<Boolean> smooth, Optional<Boolean> gui3d, ImmutableMap<String, String> textures, ImmutableMap<String, SubModel> parts, ImmutableMap<String, String> customData)
|
||||
public static IUnbakedModel prepareInjectedModel(ResourceLocation blockstateLocation, @Nullable ResourceLocation modelLocation, Optional<Boolean> smooth, Optional<Boolean> gui3d, ImmutableMap<String, String> textures, ImmutableMap<String, SubModel> parts, ImmutableMap<String, String> customData)
|
||||
{
|
||||
int size = parts.size();
|
||||
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IEnviromentBlockReader;
|
||||
import net.minecraftforge.client.model.data.*;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
|
||||
import net.minecraftforge.client.model.geometry.IMultipartModelGeometry;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CompositeModel implements IDynamicBakedModel
|
||||
{
|
||||
private final ImmutableMap<String, IBakedModel> bakedParts;
|
||||
private final boolean isAmbientOcclusion;
|
||||
private final boolean isGui3d;
|
||||
private final TextureAtlasSprite particle;
|
||||
private final ItemOverrideList overrides;
|
||||
private final IModelState transforms;
|
||||
|
||||
public CompositeModel(boolean isGui3d, boolean isAmbientOcclusion, TextureAtlasSprite particle, ImmutableMap<String, IBakedModel> bakedParts, IModelState combinedState, ItemOverrideList overrides)
|
||||
{
|
||||
this.bakedParts = bakedParts;
|
||||
this.isAmbientOcclusion = isAmbientOcclusion;
|
||||
this.isGui3d = isGui3d;
|
||||
this.particle = particle;
|
||||
this.overrides = overrides;
|
||||
this.transforms = combinedState;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData)
|
||||
{
|
||||
List<BakedQuad> quads = new ArrayList<>();
|
||||
for(Map.Entry<String, IBakedModel> entry : bakedParts.entrySet())
|
||||
{
|
||||
quads.addAll(entry.getValue().getQuads(state, side, rand, CompositeModelData.get(extraData, entry.getKey())));
|
||||
}
|
||||
return quads;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IModelData getModelData(@Nonnull IEnviromentBlockReader world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull IModelData tileData)
|
||||
{
|
||||
CompositeModelData composite = new CompositeModelData();
|
||||
for(Map.Entry<String, IBakedModel> entry : bakedParts.entrySet())
|
||||
{
|
||||
composite.putSubmodelData(entry.getKey(), entry.getValue().getModelData(world, pos, state, new ModelDataWrapper(tileData)));
|
||||
}
|
||||
return composite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion()
|
||||
{
|
||||
return isAmbientOcclusion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d()
|
||||
{
|
||||
return isGui3d;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltInRenderer()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleTexture()
|
||||
{
|
||||
return particle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides()
|
||||
{
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
return PerspectiveMapWrapper.handlePerspective(this, transforms, cameraTransformType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBakedModel getPart(String name)
|
||||
{
|
||||
return bakedParts.get(name);
|
||||
}
|
||||
|
||||
private static class Submodel implements IModelGeometryPart
|
||||
{
|
||||
private final String name;
|
||||
private final BlockModel model;
|
||||
private final ISprite sprite;
|
||||
|
||||
private Submodel(String name, BlockModel model, ISprite sprite)
|
||||
{
|
||||
this.name = name;
|
||||
this.model = model;
|
||||
this.sprite = sprite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
throw new UnsupportedOperationException("Attempted to call adQuads on a Submodel instance. Please don't.");
|
||||
}
|
||||
|
||||
public IBakedModel bake(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
return model.bake(bakery, spriteGetter, new ModelStateComposition(this.sprite.getState(), sprite.getState(),
|
||||
this.sprite.isUvLock() || sprite.isUvLock()), format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
return model.getTextures(modelGetter, missingTextureErrors);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Geometry implements IMultipartModelGeometry<Geometry>
|
||||
{
|
||||
private final ImmutableMap<String, Submodel> parts;
|
||||
|
||||
Geometry(ImmutableMap<String, Submodel> parts)
|
||||
{
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends IModelGeometryPart> getParts()
|
||||
{
|
||||
return parts.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<? extends IModelGeometryPart> getPart(String name)
|
||||
{
|
||||
return Optional.ofNullable(parts.get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides)
|
||||
{
|
||||
String particleLocation = owner.resolveTexture("particle");
|
||||
TextureAtlasSprite particle = spriteGetter.apply(new ResourceLocation(particleLocation));
|
||||
|
||||
ImmutableMap.Builder<String, IBakedModel> bakedParts = ImmutableMap.builder();
|
||||
for(Map.Entry<String, Submodel> part : parts.entrySet())
|
||||
{
|
||||
Submodel submodel = part.getValue();
|
||||
if (!owner.getPartVisibility(submodel))
|
||||
continue;
|
||||
bakedParts.put(part.getKey(), submodel.bake(bakery, spriteGetter, sprite, format));
|
||||
}
|
||||
return new CompositeModel(owner.isShadedInGui(), owner.useSmoothLighting(), particle, bakedParts.build(), owner.getCombinedState(), overrides);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
Set<ResourceLocation> textures = new HashSet<>();
|
||||
for(Submodel part : parts.values())
|
||||
{
|
||||
textures.addAll(part.getTextureDependencies(owner, modelGetter, missingTextureErrors));
|
||||
}
|
||||
return textures;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Loader implements IModelLoader<Geometry>
|
||||
{
|
||||
public static final Loader INSTANCE = new Loader();
|
||||
|
||||
private Loader() {}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Geometry read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
|
||||
{
|
||||
if (!modelContents.has("parts"))
|
||||
throw new RuntimeException("Composite model requires a \"parts\" element.");
|
||||
ImmutableMap.Builder<String, Submodel> parts = ImmutableMap.builder();
|
||||
for(Map.Entry<String, JsonElement> part : modelContents.get("parts").getAsJsonObject().entrySet())
|
||||
{
|
||||
ISprite sprite = ModelRotation.X0_Y0;
|
||||
parts.put(part.getKey(), new Submodel(
|
||||
part.getKey(),
|
||||
deserializationContext.deserialize(part.getValue(), BlockModel.class),
|
||||
sprite));
|
||||
}
|
||||
return new Geometry(parts.build());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompositeModelData implements IModelData
|
||||
{
|
||||
public static final ModelProperty<CompositeModelData> SUBMODEL_DATA = new ModelProperty<>();
|
||||
|
||||
public static Optional<CompositeModelData> get(IModelData modelData)
|
||||
{
|
||||
return Optional.ofNullable(modelData.getData(SUBMODEL_DATA));
|
||||
}
|
||||
|
||||
public static IModelData get(IModelData modelData, String name)
|
||||
{
|
||||
return get(modelData).map(data -> data.getSubmodelData(name))
|
||||
.orElse(EmptyModelData.INSTANCE);
|
||||
}
|
||||
|
||||
private final Map<String, IModelData> parts = new HashMap<>();
|
||||
|
||||
public IModelData getSubmodelData(String name)
|
||||
{
|
||||
if (parts.containsKey(name))
|
||||
return parts.get(name);
|
||||
return EmptyModelData.INSTANCE;
|
||||
}
|
||||
|
||||
public void putSubmodelData(String name, IModelData data)
|
||||
{
|
||||
parts.put(name, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(ModelProperty<?> prop)
|
||||
{
|
||||
return prop == SUBMODEL_DATA;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getData(ModelProperty<T> prop)
|
||||
{
|
||||
if (prop == SUBMODEL_DATA)
|
||||
return (T)this;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T setData(ModelProperty<T> prop, T data)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModelDataWrapper extends ModelDataMap
|
||||
{
|
||||
private final IModelData parent;
|
||||
|
||||
public ModelDataWrapper(IModelData parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(ModelProperty<?> prop)
|
||||
{
|
||||
return super.hasProperty(prop) || parent.hasProperty(prop);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getData(ModelProperty<T> prop)
|
||||
{
|
||||
return super.hasProperty(prop) ? super.getData(prop) : parent.getData(prop);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T setData(ModelProperty<T> prop, T data)
|
||||
{
|
||||
// We do not want to delegate setting to the parent
|
||||
return super.setData(prop, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,6 +187,12 @@ final class FancyMissingModel implements IUnbakedModel
|
||||
@Override
|
||||
public ItemOverrideList getOverrides() { return ItemOverrideList.EMPTY; }
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Quat4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -62,6 +63,7 @@ import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public class ForgeBlockStateV1 extends Marker
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -390,7 +392,11 @@ public class ForgeBlockStateV1 extends Marker
|
||||
|
||||
boolean isVanillaCompatible()
|
||||
{
|
||||
return model != null && submodels.isEmpty() && textures.isEmpty() && customData.isEmpty() && !smooth.isPresent() && !gui3d.isPresent() && state.orElse(ModelRotation.X0_Y0) instanceof ModelRotation;
|
||||
return model != null && !ModelLoaderRegistry.isCustomModel(model)
|
||||
&& submodels.isEmpty()
|
||||
&& textures.isEmpty() && customData.isEmpty()
|
||||
&& !smooth.isPresent() && !gui3d.isPresent()
|
||||
&& state.orElse(ModelRotation.X0_Y0) instanceof ModelRotation;
|
||||
}
|
||||
|
||||
protected SubModel asGenericSubModel()
|
||||
@@ -495,118 +501,8 @@ public class ForgeBlockStateV1 extends Marker
|
||||
|
||||
if (json.has("transform"))
|
||||
{
|
||||
if (json.get("transform").isJsonPrimitive() && json.get("transform").getAsJsonPrimitive().isString())
|
||||
{
|
||||
String transform = json.get("transform").getAsString();
|
||||
ret.state = Transforms.get(transform);
|
||||
if (!ret.state.isPresent())
|
||||
{
|
||||
throw new JsonParseException("transform: unknown default string: " + transform);
|
||||
}
|
||||
}
|
||||
else if (!json.get("transform").isJsonObject())
|
||||
{
|
||||
try
|
||||
{
|
||||
TRSRTransformation base = context.deserialize(json.get("transform"), TRSRTransformation.class);
|
||||
ret.state = Optional.of(TRSRTransformation.blockCenterToCorner(base));
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
throw new JsonParseException("transform: expected a string, object or valid base transformation, got: " + json.get("transform"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonObject transform = json.get("transform").getAsJsonObject();
|
||||
EnumMap<TransformType, TRSRTransformation> transforms = Maps.newEnumMap(TransformType.class);
|
||||
if(transform.has("thirdperson"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("thirdperson"), TRSRTransformation.class);
|
||||
transform.remove("thirdperson");
|
||||
transforms.put(TransformType.THIRD_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("thirdperson_righthand"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("thirdperson_righthand"), TRSRTransformation.class);
|
||||
transform.remove("thirdperson_righthand");
|
||||
transforms.put(TransformType.THIRD_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("thirdperson_lefthand"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("thirdperson_lefthand"), TRSRTransformation.class);
|
||||
transform.remove("thirdperson_lefthand");
|
||||
transforms.put(TransformType.THIRD_PERSON_LEFT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("firstperson"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("firstperson"), TRSRTransformation.class);
|
||||
transform.remove("firstperson");
|
||||
transforms.put(TransformType.FIRST_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("firstperson_righthand"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("firstperson_righthand"), TRSRTransformation.class);
|
||||
transform.remove("firstperson_righthand");
|
||||
transforms.put(TransformType.FIRST_PERSON_RIGHT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("firstperson_lefthand"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("firstperson_lefthand"), TRSRTransformation.class);
|
||||
transform.remove("firstperson_lefthand");
|
||||
transforms.put(TransformType.FIRST_PERSON_LEFT_HAND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("head"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("head"), TRSRTransformation.class);
|
||||
transform.remove("head");
|
||||
transforms.put(TransformType.HEAD, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("gui"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("gui"), TRSRTransformation.class);
|
||||
transform.remove("gui");
|
||||
transforms.put(TransformType.GUI, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("ground"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("ground"), TRSRTransformation.class);
|
||||
transform.remove("ground");
|
||||
transforms.put(TransformType.GROUND, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
if(transform.has("fixed"))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.get("fixed"), TRSRTransformation.class);
|
||||
transform.remove("fixed");
|
||||
transforms.put(TransformType.FIXED, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
int k = transform.entrySet().size();
|
||||
if(transform.has("matrix")) k--;
|
||||
if(transform.has("translation")) k--;
|
||||
if(transform.has("rotation")) k--;
|
||||
if(transform.has("scale")) k--;
|
||||
if(transform.has("post-rotation")) k--;
|
||||
if(k > 0)
|
||||
{
|
||||
throw new JsonParseException("transform: allowed keys: 'thirdperson', 'firstperson', 'gui', 'head', 'matrix', 'translation', 'rotation', 'scale', 'post-rotation'");
|
||||
}
|
||||
TRSRTransformation base = TRSRTransformation.identity();
|
||||
if(!transform.entrySet().isEmpty())
|
||||
{
|
||||
base = context.deserialize(transform, TRSRTransformation.class);
|
||||
base = TRSRTransformation.blockCenterToCorner(base);
|
||||
}
|
||||
IModelState state;
|
||||
if(transforms.isEmpty())
|
||||
{
|
||||
state = base;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = new SimpleModelState(Maps.immutableEnumMap(transforms), Optional.of(base));
|
||||
}
|
||||
ret.state = Optional.of(state);
|
||||
}
|
||||
JsonElement transformData = json.get("transform");
|
||||
ret.state = ModelLoaderRegistry2.deserializeTransform(context, transformData);
|
||||
}
|
||||
|
||||
if (json.has("uvlock"))
|
||||
@@ -923,6 +819,7 @@ public class ForgeBlockStateV1 extends Marker
|
||||
return TRSRTransformation.blockCenterToCorner(flipX.compose(TRSRTransformation.blockCornerToCenter(transform)).compose(flipX));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Optional<IModelState> get(String name)
|
||||
{
|
||||
return Optional.ofNullable(transforms.get(name));
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.minecraftforge.resource.IResourceType;
|
||||
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
|
||||
import net.minecraftforge.resource.VanillaResourceType;
|
||||
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public interface ICustomModelLoader extends ISelectiveResourceReloadListener
|
||||
{
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.animation.IClip;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface IForgeUnbakedModel
|
||||
{
|
||||
/**
|
||||
* @param spriteGetter Where textures will be looked up when baking
|
||||
* @param sprite Transforms to apply while baking. Usually will be an instance of {@link IModelState}.
|
||||
*/
|
||||
@Nullable
|
||||
IBakedModel bake(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format);
|
||||
|
||||
/**
|
||||
* Retrieves information about an animation clip in the model.
|
||||
* @param name The clip name
|
||||
* @return
|
||||
*/
|
||||
default Optional<? extends IClip> getClip(String name) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -41,15 +41,9 @@ import net.minecraftforge.common.model.animation.IClip;
|
||||
* Models can be baked to different vertex formats and with different state.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface IModel<T extends IModel<T>>
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public interface IModel<T extends IModel<T>> extends IForgeUnbakedModel
|
||||
{
|
||||
/**
|
||||
* @param spriteGetter Where textures will be looked up when baking
|
||||
* @param sprite Transforms to apply while baking. Usually will be an instance of {@link IModelState}.
|
||||
*/
|
||||
@Nullable
|
||||
IBakedModel bake(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format);
|
||||
|
||||
/**
|
||||
* Default state this model will be baked with.
|
||||
* @see IModelState
|
||||
@@ -58,10 +52,6 @@ public interface IModel<T extends IModel<T>>
|
||||
return TRSRTransformation.identity();
|
||||
}
|
||||
|
||||
default Optional<? extends IClip> getClip(String name) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the model to process custom data from the variant definition.
|
||||
* If unknown data is encountered it should be skipped.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 net.minecraft.client.renderer.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.model.SimpleBakedModel;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.util.Direction;
|
||||
|
||||
public interface IModelBuilder<T extends IModelBuilder<T>>
|
||||
{
|
||||
static IModelBuilder<?> of(IModelConfiguration owner, ItemOverrideList overrides, TextureAtlasSprite particle)
|
||||
{
|
||||
return new Simple(new SimpleBakedModel.Builder(owner, overrides).setTexture(particle));
|
||||
}
|
||||
|
||||
T addFaceQuad(Direction facing, BakedQuad quad);
|
||||
T addGeneralQuad(BakedQuad quad);
|
||||
|
||||
IBakedModel build();
|
||||
|
||||
class Simple implements IModelBuilder<Simple> {
|
||||
final SimpleBakedModel.Builder builder;
|
||||
|
||||
Simple(SimpleBakedModel.Builder builder)
|
||||
{
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Simple addFaceQuad(Direction facing, BakedQuad quad)
|
||||
{
|
||||
builder.addFaceQuad(facing, quad);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Simple addGeneralQuad(BakedQuad quad)
|
||||
{
|
||||
builder.addGeneralQuad(quad);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel build()
|
||||
{
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 net.minecraft.client.renderer.model.IUnbakedModel;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/*
|
||||
* Interface that provides generic access to the data within BlockModel,
|
||||
* while allowing non-blockmodel usage of models
|
||||
*/
|
||||
public interface IModelConfiguration {
|
||||
|
||||
/**
|
||||
* If available, gets the owning model (usually BlockModel) of this configuration
|
||||
*/
|
||||
@Nullable
|
||||
IUnbakedModel getOwnerModel();
|
||||
|
||||
/**
|
||||
* @return The name of the model being baked, for logging and cache purposes.
|
||||
*/
|
||||
String getModelName();
|
||||
|
||||
/**
|
||||
* Checks if a texture is present in the model.
|
||||
* @param name The name of a texture channel.
|
||||
*/
|
||||
boolean isTexturePresent(String name);
|
||||
|
||||
/**
|
||||
* Resolves the final texture name, taking into account texture aliases and replacements.
|
||||
* @param name The name of a texture channel.
|
||||
* @return The location of the texture, or the missing texture if not found.
|
||||
*/
|
||||
String resolveTexture(String name);
|
||||
|
||||
/**
|
||||
* @return True if the item uses 3D lighting.
|
||||
*/
|
||||
boolean isShadedInGui();
|
||||
|
||||
/**
|
||||
* @return True if the item requires per-vertex lighting.
|
||||
*/
|
||||
boolean useSmoothLighting();
|
||||
|
||||
/**
|
||||
* Gets the vanilla camera transforms data.
|
||||
* Do not use for non-vanilla code. For general usage, prefer getCombinedState.
|
||||
*/
|
||||
@Deprecated
|
||||
ItemCameraTransforms getCameraTransforms();
|
||||
|
||||
/**
|
||||
* @return The combined transformation state including vanilla and forge transforms data.
|
||||
*/
|
||||
IModelState getCombinedState();
|
||||
|
||||
/**
|
||||
* Queries the visibility information for the model parts.
|
||||
* @param part A part for which to query visibility.
|
||||
* @param fallback A boolean specifying the default visibility if an override isn't found in the model data.
|
||||
* @return The final computed visibility.
|
||||
*/
|
||||
default boolean getPartVisibility(IModelGeometryPart part, boolean fallback) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the visibility information for the model parts. Same as above, but defaulting to visible.
|
||||
* @param part A part for which to query visibility.
|
||||
* @return The final computed visibility.
|
||||
*/
|
||||
default boolean getPartVisibility(IModelGeometryPart part) {
|
||||
return getPartVisibility(part, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||
import net.minecraftforge.resource.IResourceType;
|
||||
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
|
||||
import net.minecraftforge.resource.VanillaResourceType;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface IModelLoader<T extends IModelGeometry<T>> extends ISelectiveResourceReloadListener
|
||||
{
|
||||
@Override
|
||||
default IResourceType getResourceType()
|
||||
{
|
||||
return VanillaResourceType.MODELS;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onResourceManagerReload(IResourceManager resourceManager);
|
||||
|
||||
@Override
|
||||
default void onResourceManagerReload(IResourceManager resourceManager, Predicate<IResourceType> resourcePredicate)
|
||||
{
|
||||
if (resourcePredicate.test(getResourceType()))
|
||||
{
|
||||
onResourceManagerReload(resourceManager);
|
||||
}
|
||||
}
|
||||
|
||||
T read(JsonDeserializationContext deserializationContext, JsonObject modelContents);
|
||||
}
|
||||
@@ -20,49 +20,46 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Quat4f;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.PngSizeInfo;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture;
|
||||
import net.minecraft.client.renderer.texture.*;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.resources.data.AnimationMetadataSection;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.resource.IResourceType;
|
||||
import net.minecraftforge.resource.VanillaResourceType;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import net.minecraftforge.fluids.FluidUtil;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public final class ModelDynBucket implements IUnbakedModel
|
||||
public final class ModelDynBucket implements IUnbakedModel, IModelGeometry<ModelDynBucket>
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final ModelResourceLocation LOCATION = new ModelResourceLocation(new ResourceLocation(ForgeVersion.MOD_ID, "dynbucket"), "inventory");
|
||||
@@ -73,111 +70,88 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
private static final float NORTH_Z_FLUID = 7.498f / 16f;
|
||||
private static final float SOUTH_Z_FLUID = 8.502f / 16f;
|
||||
|
||||
public static final IUnbakedModel MODEL = new ModelDynBucket();
|
||||
@Deprecated
|
||||
public static final ModelDynBucket MODEL = new ModelDynBucket();
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
private final ResourceLocation baseLocation;
|
||||
private ResourceLocation particleLocation;
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
private final ResourceLocation liquidLocation;
|
||||
private ResourceLocation baseLocation;
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
private final ResourceLocation coverLocation;
|
||||
private ResourceLocation fluidMaskLocation;
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
private ResourceLocation coverLocation;
|
||||
|
||||
@Nonnull
|
||||
private final Fluid fluid;
|
||||
|
||||
private final boolean flipGas;
|
||||
private final boolean tint;
|
||||
private final boolean coverIsMask;
|
||||
|
||||
@Deprecated
|
||||
public ModelDynBucket()
|
||||
{
|
||||
this(null, null, null, null, false, true);
|
||||
this(Fluids.EMPTY, false, true, false);
|
||||
}
|
||||
|
||||
public ModelDynBucket(@Nullable ResourceLocation baseLocation, @Nullable ResourceLocation liquidLocation, @Nullable ResourceLocation coverLocation, @Nullable Fluid fluid, boolean flipGas, boolean tint)
|
||||
@Deprecated
|
||||
public ModelDynBucket(@Nullable ResourceLocation baseLocation, @Nullable ResourceLocation fluidMaskLocation, @Nullable ResourceLocation coverLocation, @Nullable Fluid fluid, boolean flipGas, boolean tint)
|
||||
{
|
||||
this(fluid != null ? fluid : Fluids.EMPTY, flipGas, tint, false);
|
||||
this.baseLocation = baseLocation;
|
||||
this.liquidLocation = liquidLocation;
|
||||
this.fluidMaskLocation = fluidMaskLocation;
|
||||
this.coverLocation = coverLocation;
|
||||
}
|
||||
|
||||
public ModelDynBucket(Fluid fluid, boolean flipGas, boolean tint, boolean coverIsMask)
|
||||
{
|
||||
this.fluid = fluid;
|
||||
this.flipGas = flipGas;
|
||||
this.tint = tint;
|
||||
this.coverIsMask = coverIsMask;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextures(Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
ImmutableSet.Builder<ResourceLocation> builder = ImmutableSet.builder();
|
||||
|
||||
if (particleLocation != null)
|
||||
builder.add(particleLocation);
|
||||
if (baseLocation != null)
|
||||
builder.add(baseLocation);
|
||||
if (liquidLocation != null)
|
||||
builder.add(liquidLocation);
|
||||
if (fluidMaskLocation != null)
|
||||
builder.add(fluidMaskLocation);
|
||||
if (coverLocation != null)
|
||||
builder.add(coverLocation);
|
||||
if (fluid != null)
|
||||
if (fluid != Fluids.EMPTY)
|
||||
builder.add(fluid.getAttributes().getStillTexture());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies()
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
@Override
|
||||
public IBakedModel bake(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
IModelState state = sprite.getState();
|
||||
ImmutableMap<TransformType, TRSRTransformation> transformMap = PerspectiveMapWrapper.getTransforms(state);
|
||||
|
||||
// if the fluid is lighter than air, will manipulate the initial state to be rotated 180° to turn it upside down
|
||||
if (flipGas && fluid != null && fluid.getAttributes().isLighterThanAir())
|
||||
{
|
||||
sprite = new ModelStateComposition(state, TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, new Quat4f(0, 0, 1, 0), null, null)));
|
||||
state = sprite.getState();
|
||||
}
|
||||
|
||||
TRSRTransformation transform = state.apply(Optional.empty()).orElse(TRSRTransformation.identity());
|
||||
TextureAtlasSprite fluidSprite = null;
|
||||
TextureAtlasSprite particleSprite = null;
|
||||
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
|
||||
|
||||
if(fluid != null) {
|
||||
fluidSprite = spriteGetter.apply(fluid.getAttributes().getStillTexture());
|
||||
}
|
||||
|
||||
Random random = new Random();
|
||||
if (baseLocation != null)
|
||||
{
|
||||
// build base (insidest)
|
||||
IBakedModel model = (new ItemLayerModel(ImmutableList.of(baseLocation))).bake(bakery, spriteGetter, sprite, format);
|
||||
random.setSeed(42);
|
||||
builder.addAll(model.getQuads(null, null, random));
|
||||
particleSprite = model.getParticleTexture();
|
||||
}
|
||||
if (liquidLocation != null && fluidSprite != null)
|
||||
{
|
||||
TextureAtlasSprite liquid = spriteGetter.apply(liquidLocation);
|
||||
// build liquid layer (inside)
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, NORTH_Z_FLUID, Direction.NORTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, liquid, fluidSprite, SOUTH_Z_FLUID, Direction.SOUTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
|
||||
particleSprite = fluidSprite;
|
||||
}
|
||||
if (coverLocation != null)
|
||||
{
|
||||
// cover (the actual item around the other two)
|
||||
TextureAtlasSprite cover = spriteGetter.apply(coverLocation);
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_COVER, cover, Direction.NORTH, 0xFFFFFFFF, 2));
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_COVER, cover, Direction.SOUTH, 0xFFFFFFFF, 2));
|
||||
if (particleSprite == null)
|
||||
{
|
||||
particleSprite = cover;
|
||||
}
|
||||
}
|
||||
|
||||
return new BakedDynBucket(bakery, this, builder.build(), particleSprite, format, Maps.immutableEnumMap(transformMap), Maps.newHashMap(), transform.isIdentity());
|
||||
return bakeInternal(bakery, spriteGetter, sprite, format, null, particleLocation, baseLocation, fluidMaskLocation, coverLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,6 +162,7 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
* <p/>
|
||||
* If the fluid can't be found, water is used.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public ModelDynBucket process(ImmutableMap<String, String> customData)
|
||||
{
|
||||
@@ -219,7 +194,7 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
}
|
||||
|
||||
// create new model with correct liquid
|
||||
return new ModelDynBucket(baseLocation, liquidLocation, coverLocation, fluid, flip, tint);
|
||||
return new ModelDynBucket(baseLocation, fluidMaskLocation, coverLocation, fluid, flip, tint);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,12 +206,12 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
* <p/>
|
||||
* If no liquid is given a hardcoded variant for the bucket is used.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public ModelDynBucket retexture(ImmutableMap<String, String> textures)
|
||||
{
|
||||
|
||||
ResourceLocation base = baseLocation;
|
||||
ResourceLocation liquid = liquidLocation;
|
||||
ResourceLocation liquid = fluidMaskLocation;
|
||||
ResourceLocation cover = coverLocation;
|
||||
|
||||
if (textures.containsKey("base"))
|
||||
@@ -248,7 +223,8 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
|
||||
return new ModelDynBucket(base, liquid, cover, fluid, flipGas, tint);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
protected static IResource getResource(ResourceLocation resourceLocation)
|
||||
{
|
||||
@@ -261,7 +237,8 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
@Nullable
|
||||
protected static PngSizeInfo getSizeInfo(IResource resource)
|
||||
{
|
||||
@@ -274,7 +251,128 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides)
|
||||
{
|
||||
String particle = owner.resolveTexture("particle");
|
||||
if (!MissingTextureSprite.getLocation().toString().equals(particle))
|
||||
{
|
||||
particleLocation = new ResourceLocation(particle);
|
||||
}
|
||||
|
||||
String base = owner.resolveTexture("base");
|
||||
if (!MissingTextureSprite.getLocation().toString().equals(base))
|
||||
{
|
||||
baseLocation = new ResourceLocation(base);
|
||||
}
|
||||
|
||||
String template = owner.resolveTexture("fluid");
|
||||
if (!MissingTextureSprite.getLocation().toString().equals(template))
|
||||
{
|
||||
fluidMaskLocation = new ResourceLocation(template);
|
||||
}
|
||||
|
||||
String cover = owner.resolveTexture("cover");
|
||||
if (!MissingTextureSprite.getLocation().toString().equals(cover))
|
||||
{
|
||||
// cover (the actual item around the other two)
|
||||
coverLocation = new ResourceLocation(cover);
|
||||
}
|
||||
|
||||
return bakeInternal(bakery, spriteGetter, sprite, format, owner.getCombinedState(), particleLocation, baseLocation, fluidMaskLocation, coverLocation);
|
||||
}
|
||||
|
||||
private IBakedModel bakeInternal(
|
||||
ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format,
|
||||
@Nullable IModelState transformsFromModel,
|
||||
@Nullable ResourceLocation particleLocation, @Nullable ResourceLocation baseLocation,
|
||||
@Nullable ResourceLocation fluidMaskLocation, @Nullable ResourceLocation coverLocation)
|
||||
{
|
||||
IModelState state = sprite.getState();
|
||||
ImmutableMap<TransformType, TRSRTransformation> transformMap = transformsFromModel != null ?
|
||||
PerspectiveMapWrapper.getTransforms(new ModelStateComposition(transformsFromModel, state)) :
|
||||
PerspectiveMapWrapper.getTransforms(state);
|
||||
|
||||
TextureAtlasSprite particle = particleLocation != null ? spriteGetter.apply(particleLocation) : null;
|
||||
|
||||
// if the fluid is lighter than air, will manipulate the initial state to be rotated 180° to turn it upside down
|
||||
if (flipGas && fluid != Fluids.EMPTY && fluid.getAttributes().isLighterThanAir())
|
||||
{
|
||||
sprite = new ModelStateComposition(state, TRSRTransformation.blockCenterToCorner(new TRSRTransformation(null, new Quat4f(0, 0, 1, 0), null, null)));
|
||||
state = sprite.getState();
|
||||
}
|
||||
|
||||
TRSRTransformation transform = state.apply(Optional.empty()).orElse(TRSRTransformation.identity());
|
||||
|
||||
ResourceLocation fluidLocation = fluid != Fluids.EMPTY ? fluid.getAttributes().getStillTexture() : null;
|
||||
TextureAtlasSprite fluidSprite = fluidLocation != null ? spriteGetter.apply(fluidLocation) : null;
|
||||
|
||||
if (particle == null) particle = fluidSprite;
|
||||
|
||||
Random random = new Random();
|
||||
ImmutableList.Builder<BakedQuad> builder = ImmutableList.builder();
|
||||
|
||||
if (baseLocation != null)
|
||||
{
|
||||
// build base (insidest)
|
||||
IBakedModel model = new ItemLayerModel(ImmutableList.of(baseLocation)).bake(bakery, spriteGetter, sprite, format);
|
||||
random.setSeed(42);
|
||||
builder.addAll(model.getQuads(null, null, random));
|
||||
}
|
||||
|
||||
if (fluidMaskLocation != null && fluidSprite != null)
|
||||
{
|
||||
TextureAtlasSprite templateSprite = spriteGetter.apply(fluidMaskLocation);
|
||||
if (templateSprite != null)
|
||||
{
|
||||
// build liquid layer (inside)
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateSprite, fluidSprite, NORTH_Z_FLUID, Direction.NORTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, templateSprite, fluidSprite, SOUTH_Z_FLUID, Direction.SOUTH, tint ? fluid.getAttributes().getColor() : 0xFFFFFFFF, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (coverLocation != null && (!coverIsMask || baseLocation != null))
|
||||
{
|
||||
// cover (the actual item around the other two)
|
||||
TextureAtlasSprite coverSprite = spriteGetter.apply(coverLocation);
|
||||
if (coverSprite != null)
|
||||
{
|
||||
if (coverIsMask)
|
||||
{
|
||||
TextureAtlasSprite baseSprite = spriteGetter.apply(baseLocation);
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, coverSprite, baseSprite, NORTH_Z_COVER, Direction.NORTH, 0xFFFFFFFF, 1));
|
||||
builder.addAll(ItemTextureQuadConverter.convertTexture(format, transform, coverSprite, baseSprite, SOUTH_Z_COVER, Direction.SOUTH, 0xFFFFFFFF, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, NORTH_Z_COVER, coverSprite, Direction.NORTH, 0xFFFFFFFF, 2));
|
||||
builder.add(ItemTextureQuadConverter.genQuad(format, transform, 0, 0, 16, 16, SOUTH_Z_COVER, coverSprite, Direction.SOUTH, 0xFFFFFFFF, 2));
|
||||
if (particle == null)
|
||||
{
|
||||
particle = coverSprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new BakedDynBucket(bakery, this, builder.build(), particle, format, Maps.immutableEnumMap(transformMap), Maps.newHashMap(), transform.isIdentity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
Set<ResourceLocation> texs = Sets.newHashSet();
|
||||
|
||||
texs.add(new ResourceLocation(owner.resolveTexture("particle")));
|
||||
texs.add(new ResourceLocation(owner.resolveTexture("base")));
|
||||
texs.add(new ResourceLocation(owner.resolveTexture("fluid")));
|
||||
texs.add(new ResourceLocation(owner.resolveTexture("cover")));
|
||||
|
||||
return texs;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public enum LoaderDynBucket implements ICustomModelLoader
|
||||
{
|
||||
INSTANCE;
|
||||
@@ -291,12 +389,25 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
return MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IResourceType getResourceType()
|
||||
{
|
||||
return VanillaResourceType.MODELS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
// no need to clear cache since we create a new model instance
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager, Predicate<IResourceType> resourcePredicate)
|
||||
{
|
||||
// no need to clear cache since we create a new model instance
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void register(AtlasTexture map)
|
||||
{
|
||||
// only create these textures if they are not added by a resource pack
|
||||
@@ -318,6 +429,62 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
}
|
||||
}
|
||||
|
||||
public enum Loader implements IModelLoader<ModelDynBucket>
|
||||
{
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public IResourceType getResourceType()
|
||||
{
|
||||
return VanillaResourceType.MODELS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
// no need to clear cache since we create a new model instance
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager, Predicate<IResourceType> resourcePredicate)
|
||||
{
|
||||
// no need to clear cache since we create a new model instance
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelDynBucket read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
|
||||
{
|
||||
if (!modelContents.has("fluid"))
|
||||
throw new RuntimeException("Bucket model requires 'fluid' value.");
|
||||
|
||||
ResourceLocation fluidName = new ResourceLocation(modelContents.get("fluid").getAsString());
|
||||
|
||||
Fluid fluid = ForgeRegistries.FLUIDS.getValue(fluidName);
|
||||
|
||||
boolean flip = false;
|
||||
if (modelContents.has("flipGas"))
|
||||
{
|
||||
flip = modelContents.get("flipGas").getAsBoolean();
|
||||
}
|
||||
|
||||
boolean tint = true;
|
||||
if (modelContents.has("applyTint"))
|
||||
{
|
||||
tint = modelContents.get("applyTint").getAsBoolean();
|
||||
}
|
||||
|
||||
boolean coverIsMask = true;
|
||||
if (modelContents.has("coverIsMask"))
|
||||
{
|
||||
coverIsMask = modelContents.get("coverIsMask").getAsBoolean();
|
||||
}
|
||||
|
||||
// create new model with correct liquid
|
||||
return new ModelDynBucket(fluid, flip, tint, coverIsMask);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static final class BucketBaseSprite extends TextureAtlasSprite
|
||||
{
|
||||
private final ResourceLocation bucket = new ResourceLocation("item/bucket");
|
||||
@@ -359,6 +526,7 @@ public final class ModelDynBucket implements IUnbakedModel
|
||||
/**
|
||||
* Creates a bucket cover sprite from the vanilla resource.
|
||||
*/
|
||||
@Deprecated
|
||||
private static final class BucketCoverSprite extends TextureAtlasSprite
|
||||
{
|
||||
private final ResourceLocation bucket = new ResourceLocation("item/bucket");
|
||||
|
||||
@@ -499,6 +499,12 @@ public final class ModelFluid implements IUnbakedModel
|
||||
return ItemOverrideList.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType type)
|
||||
{
|
||||
|
||||
@@ -19,25 +19,17 @@
|
||||
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BlockModelShapes;
|
||||
import net.minecraft.client.renderer.ItemModelMesher;
|
||||
import net.minecraft.client.renderer.Vector3f;
|
||||
import net.minecraft.client.renderer.color.BlockColors;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
@@ -45,76 +37,61 @@ import net.minecraft.client.renderer.model.BlockPart;
|
||||
import net.minecraft.client.renderer.model.BlockPartFace;
|
||||
import net.minecraft.client.renderer.model.BlockPartRotation;
|
||||
import net.minecraft.client.renderer.model.BuiltInModel;
|
||||
import net.minecraft.client.renderer.model.FaceBakery;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
|
||||
import net.minecraft.client.renderer.model.ItemModelGenerator;
|
||||
import net.minecraft.client.renderer.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.model.BlockModel;
|
||||
import net.minecraft.client.renderer.model.BlockModelDefinition;
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
|
||||
import net.minecraft.client.renderer.model.ModelRotation;
|
||||
import net.minecraft.client.renderer.model.MultipartBakedModel;
|
||||
import net.minecraft.client.renderer.model.SimpleBakedModel;
|
||||
import net.minecraft.client.renderer.model.Variant;
|
||||
import net.minecraft.client.renderer.model.VariantList;
|
||||
import net.minecraft.client.renderer.model.WeightedBakedModel;
|
||||
import net.minecraft.client.renderer.model.multipart.Multipart;
|
||||
import net.minecraft.client.renderer.model.multipart.Selector;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.AtlasTexture;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.state.StateContainer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.animation.AnimationItemOverrideList;
|
||||
import net.minecraftforge.client.model.animation.ModelBlockAnimation;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import net.minecraftforge.common.ForgeMod;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.Models;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import net.minecraftforge.common.model.animation.IClip;
|
||||
import net.minecraftforge.common.property.Properties;
|
||||
import net.minecraftforge.fml.client.ClientModLoader;
|
||||
import net.minecraftforge.logging.ModelLoaderErrorMessage;
|
||||
import net.minecraftforge.registries.IRegistryDelegate;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.MODELLOADING;
|
||||
@@ -274,25 +251,35 @@ public final class ModelLoader extends ModelBakery
|
||||
|
||||
public IBakedModel bakeImpl(ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, IModelState state, boolean uvlock, VertexFormat format)
|
||||
{
|
||||
if(!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT))
|
||||
{
|
||||
throw new IllegalArgumentException("can't bake vanilla models to the format that doesn't fit into the default one: " + format);
|
||||
}
|
||||
BlockModel model = this.model;
|
||||
if(model == null) return getMissingModel().bake(bakery, bakedTextureGetter, new BasicState(getMissingModel().getDefaultState(), uvlock), format);
|
||||
|
||||
List<TRSRTransformation> newTransforms = Lists.newArrayList();
|
||||
for(int i = 0; i < model.getElements().size(); i++)
|
||||
IModelGeometry<?> customGeometry = model.customData.getCustomGeometry();
|
||||
if (customGeometry != null)
|
||||
{
|
||||
BlockPart part = model.getElements().get(i);
|
||||
newTransforms.add(animation.getPartTransform(state, part, i));
|
||||
Collection<? extends IModelGeometryPart> parts = customGeometry.getParts();
|
||||
for (int i = 0; i < parts.size(); i++)
|
||||
{
|
||||
newTransforms.add(animation.getPartTransform(state, i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
List<BlockPart> elements = model.getElements();
|
||||
for (int i = 0; i < elements.size(); i++)
|
||||
{
|
||||
BlockPart part = elements.get(i);
|
||||
newTransforms.add(animation.getPartTransform(state, part, i));
|
||||
}
|
||||
}
|
||||
|
||||
ItemCameraTransforms transforms = model.getAllTransforms();
|
||||
Map<TransformType, TRSRTransformation> tMap = Maps.newEnumMap(TransformType.class);
|
||||
tMap.putAll(PerspectiveMapWrapper.getTransforms(transforms));
|
||||
tMap.putAll(PerspectiveMapWrapper.getTransforms(state));
|
||||
IModelState perState = new SimpleModelState(ImmutableMap.copyOf(tMap), state.apply(Optional.empty()));
|
||||
IModelState perState = model.customData.getCustomModelState();
|
||||
if (perState == null)
|
||||
{
|
||||
perState = new SimpleModelState(PerspectiveMapWrapper.getTransformsWithFallback(state, transforms), state.apply(Optional.empty()));
|
||||
}
|
||||
|
||||
if(model == ModelBakery.MODEL_GENERATED)
|
||||
{
|
||||
@@ -300,7 +287,39 @@ public final class ModelLoader extends ModelBakery
|
||||
}
|
||||
TextureAtlasSprite textureatlassprite = bakedTextureGetter.apply(new ResourceLocation(model.resolveTextureName("particle")));
|
||||
if(model == ModelBakery.MODEL_ENTITY) return new BuiltInModel(transforms, model.getOverrides(bakery, model, bakedTextureGetter, format), textureatlassprite);
|
||||
return bakeNormal(bakery, model, perState, state, newTransforms, format, bakedTextureGetter, uvlock);
|
||||
if(model.customData.hasCustomGeometry())
|
||||
return bakeNew(bakery, model, perState, state, newTransforms, format, bakedTextureGetter, uvlock);
|
||||
else
|
||||
return bakeNormal(bakery, model, perState, state, newTransforms, format, bakedTextureGetter, uvlock);
|
||||
}
|
||||
|
||||
private IBakedModel bakeNew(ModelBakery bakery, BlockModel model, IModelState perState, final IModelState modelState, List<TRSRTransformation> newTransforms, final VertexFormat format, final Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, boolean uvLocked)
|
||||
{
|
||||
final TRSRTransformation baseState = modelState.apply(Optional.empty()).orElse(TRSRTransformation.identity());
|
||||
|
||||
IBakedModel baked = model.customData.bake(bakery, bakedTextureGetter, new BasicState(baseState, uvLocked), format, model.getOverrides(bakery, model, bakedTextureGetter, format));
|
||||
|
||||
return new PerspectiveMapWrapper(baked, perState)
|
||||
{
|
||||
private final ItemOverrideList overrides = new AnimationItemOverrideList(bakery, VanillaModelWrapper.this, modelState, format, bakedTextureGetter, super.getOverrides());
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand, IModelData modelData)
|
||||
{
|
||||
IModelState newState = modelData.getData(Properties.AnimationProperty);
|
||||
if(newState != null)
|
||||
{
|
||||
return VanillaModelWrapper.this.bake(bakery, bakedTextureGetter, new ModelStateComposition(modelState, newState, uvlock), format).getQuads(state, side, rand, modelData);
|
||||
}
|
||||
return super.getQuads(state, side, rand, modelData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides()
|
||||
{
|
||||
return overrides;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private IBakedModel bakeNormal(ModelBakery bakery, BlockModel model, IModelState perState, final IModelState modelState, List<TRSRTransformation> newTransforms, final VertexFormat format, final Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter, boolean uvLocked)
|
||||
@@ -308,13 +327,14 @@ public final class ModelLoader extends ModelBakery
|
||||
final TRSRTransformation baseState = modelState.apply(Optional.empty()).orElse(TRSRTransformation.identity());
|
||||
TextureAtlasSprite particle = bakedTextureGetter.apply(new ResourceLocation(model.resolveTextureName("particle")));
|
||||
SimpleBakedModel.Builder builder = (new SimpleBakedModel.Builder(model, model.getOverrides(bakery, model, bakedTextureGetter, format))).setTexture(particle);
|
||||
for(int i = 0; i < model.getElements().size(); i++)
|
||||
List<BlockPart> elements = model.getElements();
|
||||
for(int i = 0; i < elements.size(); i++)
|
||||
{
|
||||
if(modelState.apply(Optional.of(Models.getHiddenModelPart(ImmutableList.of(Integer.toString(i))))).isPresent())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BlockPart part = model.getElements().get(i);
|
||||
BlockPart part = elements.get(i);
|
||||
TRSRTransformation transformation = baseState;
|
||||
if(newTransforms.get(i) != null)
|
||||
{
|
||||
@@ -350,7 +370,7 @@ public final class ModelLoader extends ModelBakery
|
||||
{
|
||||
return VanillaModelWrapper.this.bake(bakery, bakedTextureGetter, new ModelStateComposition(modelState, newState, uvlock), format).getQuads(state, side, rand, modelData);
|
||||
}
|
||||
return super.getQuads(state, side, rand);
|
||||
return super.getQuads(state, side, rand, modelData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -368,16 +388,19 @@ public final class ModelLoader extends ModelBakery
|
||||
return this;
|
||||
|
||||
List<BlockPart> elements = Lists.newArrayList(); //We have to duplicate this so we can edit it below.
|
||||
for (BlockPart part : this.model.getElements())
|
||||
if (!model.customData.hasCustomGeometry())
|
||||
{
|
||||
elements.add(new BlockPart(part.positionFrom, part.positionTo, Maps.newHashMap(part.mapFaces), part.partRotation, part.shade));
|
||||
for (BlockPart part : this.model.getElements())
|
||||
{
|
||||
elements.add(new BlockPart(part.positionFrom, part.positionTo, Maps.newHashMap(part.mapFaces), part.partRotation, part.shade));
|
||||
}
|
||||
}
|
||||
|
||||
BlockModel newModel = new BlockModel(this.model.getParentLocation(), elements,
|
||||
Maps.newHashMap(this.model.textures), this.model.isAmbientOcclusion(), this.model.isGui3d(), //New Textures man VERY IMPORTANT
|
||||
model.getAllTransforms(), Lists.newArrayList(model.getOverrides()));
|
||||
newModel.name = this.model.name;
|
||||
newModel.parent = this.model.parent;
|
||||
newModel.customData.copyFrom(this.model.customData);
|
||||
|
||||
Set<String> removed = Sets.newHashSet();
|
||||
|
||||
@@ -408,9 +431,12 @@ public final class ModelLoader extends ModelBakery
|
||||
newModel.textures.putAll(remapped);
|
||||
|
||||
//Remove any faces that use a null texture, this is for performance reasons, also allows some cool layering stuff.
|
||||
for (BlockPart part : newModel.getElements())
|
||||
if (!newModel.customData.hasCustomGeometry())
|
||||
{
|
||||
part.mapFaces.entrySet().removeIf(entry -> removed.contains(entry.getValue().texture));
|
||||
for (BlockPart part : newModel.getElements())
|
||||
{
|
||||
part.mapFaces.entrySet().removeIf(entry -> removed.contains(entry.getValue().texture));
|
||||
}
|
||||
}
|
||||
|
||||
return new VanillaModelWrapper(location, newModel, uvlock, animation);
|
||||
@@ -436,6 +462,7 @@ public final class ModelLoader extends ModelBakery
|
||||
BlockModel newModel = new BlockModel(model.getParentLocation(), model.getElements(), model.textures, value, model.isGui3d(), model.getAllTransforms(), Lists.newArrayList(model.getOverrides()));
|
||||
newModel.parent = model.parent;
|
||||
newModel.name = model.name;
|
||||
newModel.customData.copyFrom(model.customData);
|
||||
return new VanillaModelWrapper(location, newModel, uvlock, animation);
|
||||
}
|
||||
|
||||
@@ -449,6 +476,7 @@ public final class ModelLoader extends ModelBakery
|
||||
BlockModel newModel = new BlockModel(model.getParentLocation(), model.getElements(), model.textures, model.ambientOcclusion, value, model.getAllTransforms(), Lists.newArrayList(model.getOverrides()));
|
||||
newModel.parent = model.parent;
|
||||
newModel.name = model.name;
|
||||
newModel.customData.copyFrom(model.customData);
|
||||
return new VanillaModelWrapper(location, newModel, uvlock, animation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import net.minecraftforge.common.model.animation.IAnimationStateMachine;
|
||||
/**
|
||||
* Central hub for custom model loaders.
|
||||
*/
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public class ModelLoaderRegistry
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -193,6 +194,27 @@ public class ModelLoaderRegistry
|
||||
return model;
|
||||
}
|
||||
|
||||
public static boolean isCustomModel(ResourceLocation location)
|
||||
{
|
||||
ResourceLocation actual = getActualLocation(location);
|
||||
for(ICustomModelLoader loader : loaders)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(loader.accepts(actual))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOGGER.warn("Exception checking if model {} can be loaded with loader {}", location, loader, e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this if you don't care about the exception and want some model anyway.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.*;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.MissingTextureSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.resources.IReloadableResourceManager;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.JSONUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.b3d.B3DLoader;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometry;
|
||||
import net.minecraftforge.client.model.geometry.ISimpleModelGeometry;
|
||||
import net.minecraftforge.client.model.obj.OBJLoader2;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Central hub for custom model loaders.
|
||||
*/
|
||||
public class ModelLoaderRegistry2
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
public static final String WHITE_TEXTURE = "forge:white";
|
||||
|
||||
private static final Map<ResourceLocation, IModelLoader<?>> loaders = Maps.newHashMap();
|
||||
private static volatile boolean registryFrozen = false;
|
||||
|
||||
// Forge built-in loaders
|
||||
public static void init()
|
||||
{
|
||||
registerLoader(new ResourceLocation("minecraft","elements"), VanillaProxy.Loader.INSTANCE);
|
||||
registerLoader(new ResourceLocation("forge","obj"), OBJLoader2.INSTANCE);
|
||||
registerLoader(new ResourceLocation("forge","bucket"), ModelDynBucket.Loader.INSTANCE);
|
||||
registerLoader(new ResourceLocation("forge","composite"), CompositeModel.Loader.INSTANCE);
|
||||
|
||||
// Model adapters from the old loading system
|
||||
registerLoader(new ResourceLocation("forge","b3d"), new ModelLoaderAdapter(B3DLoader.INSTANCE));
|
||||
registerLoader(new ResourceLocation("forge","fluid"), new ModelLoaderAdapter(ModelFluid.FluidLoader.INSTANCE));
|
||||
registerLoader(new ResourceLocation("forge","multi-layer"), new ModelLoaderAdapter(MultiLayerModel.Loader.INSTANCE));
|
||||
}
|
||||
|
||||
public static void initComplete()
|
||||
{
|
||||
registryFrozen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes system aware of your loader.
|
||||
*/
|
||||
public static void registerLoader(ResourceLocation id, IModelLoader<?> loader)
|
||||
{
|
||||
if (registryFrozen)
|
||||
throw new IllegalStateException("Can not register model loaders after models have started loading. Please use FMLClientSetupEvent or ModelRegistryEvent to register your loaders.");
|
||||
synchronized(loaders)
|
||||
{
|
||||
loaders.put(id, loader);
|
||||
((IReloadableResourceManager) Minecraft.getInstance().getResourceManager()).addReloadListener(loader);
|
||||
}
|
||||
}
|
||||
|
||||
public static IModelGeometry<?> getModel(ResourceLocation loaderId, JsonDeserializationContext deserializationContext, JsonObject data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!loaders.containsKey(loaderId))
|
||||
{
|
||||
throw new IllegalStateException(String.format("Model loader '%s' not found. Registered loaders: %s", loaderId,
|
||||
loaders.keySet().stream().map(ResourceLocation::toString).collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
IModelLoader<?> loader = loaders.get(loaderId);
|
||||
|
||||
return loader.read(deserializationContext, data);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IModelGeometry<?> deserializeGeometry(JsonDeserializationContext deserializationContext, JsonObject object) {
|
||||
if (!object.has("loader")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ResourceLocation loader = new ResourceLocation(JSONUtils.getString(object,"loader"));
|
||||
return getModel(loader, deserializationContext, object);
|
||||
}
|
||||
|
||||
/* Explanation:
|
||||
* This takes anything that looks like a valid resourcepack texture location, and tries to extract a resourcelocation out of it.
|
||||
* 1. it will ignore anything up to and including an /assets/ folder,
|
||||
* 2. it will take the next path component as a namespace,
|
||||
* 3. it will match but skip the /textures/ part of the path,
|
||||
* 4. it will take the rest of the path up to but excluding the .png extension as the resource path
|
||||
* It's a best-effort situation, to allow model files exported by modelling software to be used without post-processing.
|
||||
* Example:
|
||||
* C:\Something\Or Other\src\main\resources\assets\mymodid\textures\item\my_thing.png
|
||||
* ........................................--------_______----------_____________----
|
||||
* <namespace> <path>
|
||||
* Result after replacing '\' to '/': mymodid:item/my_thing
|
||||
*/
|
||||
private static final Pattern FILESYSTEM_PATH_TO_RESLOC =
|
||||
Pattern.compile("(?:.*[\\\\/]assets[\\\\/](?<namespace>[a-z_-]+)[\\\\/]textures[\\\\/])?(?<path>[a-z_\\\\/-]+)\\.png");
|
||||
|
||||
public static ResourceLocation resolveTexture(@Nullable String tex, IModelConfiguration owner)
|
||||
{
|
||||
if (tex == null)
|
||||
return new ResourceLocation(WHITE_TEXTURE);
|
||||
if (tex.startsWith("#"))
|
||||
return new ResourceLocation(owner.resolveTexture(tex));
|
||||
|
||||
// Attempt to convert a common (windows/linux/mac) filesystem path to a ResourceLocation.
|
||||
// This makes no promises, if it doesn't work, too bad, fix your mtl file.
|
||||
Matcher match = FILESYSTEM_PATH_TO_RESLOC.matcher(tex);
|
||||
if (match.matches())
|
||||
{
|
||||
String namespace = match.group("namespace");
|
||||
String path = match.group("path").replace("\\", "/");
|
||||
if (namespace != null)
|
||||
return new ResourceLocation(namespace, path);
|
||||
return new ResourceLocation(path);
|
||||
}
|
||||
|
||||
return new ResourceLocation(tex);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IModelState deserializeModelTransforms(JsonDeserializationContext deserializationContext, JsonObject modelData)
|
||||
{
|
||||
if (!modelData.has("transform"))
|
||||
return null;
|
||||
|
||||
return deserializeTransform(deserializationContext, modelData.get("transform")).orElse(null);
|
||||
}
|
||||
|
||||
public static Optional<IModelState> deserializeTransform(JsonDeserializationContext context, JsonElement transformData)
|
||||
{
|
||||
// TODO: remove the default string option, in favor of the base models
|
||||
// "forge:default-item" --> "parent": "forge:item/default"
|
||||
// "forge:default-tool" --> "parent": "forge:item/default-tool"
|
||||
// "forge:default-block" --> "parent": "minecraft:block/block"
|
||||
if (transformData.isJsonPrimitive() && transformData.getAsJsonPrimitive().isString())
|
||||
{
|
||||
String transform = transformData.getAsString();
|
||||
Optional<IModelState> state = ForgeBlockStateV1.Transforms.get(transform);
|
||||
if (!state.isPresent())
|
||||
{
|
||||
throw new JsonParseException("transform: unknown default string: " + transform);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
else if (!transformData.isJsonObject())
|
||||
{
|
||||
try
|
||||
{
|
||||
TRSRTransformation base = context.deserialize(transformData, TRSRTransformation.class);
|
||||
return Optional.of(TRSRTransformation.blockCenterToCorner(base));
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
throw new JsonParseException("transform: expected a string, object or valid base transformation, got: " + transformData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonObject transform = transformData.getAsJsonObject();
|
||||
EnumMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms = Maps.newEnumMap(ItemCameraTransforms.TransformType.class);
|
||||
|
||||
deserializeTRSR(context, transforms, transform, "thirdperson", ItemCameraTransforms.TransformType.THIRD_PERSON_RIGHT_HAND);
|
||||
deserializeTRSR(context, transforms, transform, "thirdperson_righthand", ItemCameraTransforms.TransformType.THIRD_PERSON_RIGHT_HAND);
|
||||
deserializeTRSR(context, transforms, transform, "thirdperson_lefthand", ItemCameraTransforms.TransformType.THIRD_PERSON_LEFT_HAND);
|
||||
|
||||
deserializeTRSR(context, transforms, transform, "firstperson", ItemCameraTransforms.TransformType.FIRST_PERSON_RIGHT_HAND);
|
||||
deserializeTRSR(context, transforms, transform, "firstperson_righthand", ItemCameraTransforms.TransformType.FIRST_PERSON_RIGHT_HAND);
|
||||
deserializeTRSR(context, transforms, transform, "firstperson_lefthand", ItemCameraTransforms.TransformType.FIRST_PERSON_LEFT_HAND);
|
||||
|
||||
deserializeTRSR(context, transforms, transform, "head", ItemCameraTransforms.TransformType.HEAD);
|
||||
deserializeTRSR(context, transforms, transform, "gui", ItemCameraTransforms.TransformType.GUI);
|
||||
deserializeTRSR(context, transforms, transform, "ground", ItemCameraTransforms.TransformType.GROUND);
|
||||
deserializeTRSR(context, transforms, transform, "fixed", ItemCameraTransforms.TransformType.FIXED);
|
||||
|
||||
int k = transform.entrySet().size();
|
||||
if(transform.has("matrix")) k--;
|
||||
if(transform.has("translation")) k--;
|
||||
if(transform.has("rotation")) k--;
|
||||
if(transform.has("scale")) k--;
|
||||
if(transform.has("post-rotation")) k--;
|
||||
if(k > 0)
|
||||
{
|
||||
throw new JsonParseException("transform: allowed keys: 'thirdperson', 'firstperson', 'gui', 'head', 'matrix', 'translation', 'rotation', 'scale', 'post-rotation'");
|
||||
}
|
||||
TRSRTransformation base = TRSRTransformation.identity();
|
||||
if(!transform.entrySet().isEmpty())
|
||||
{
|
||||
base = context.deserialize(transform, TRSRTransformation.class);
|
||||
base = TRSRTransformation.blockCenterToCorner(base);
|
||||
}
|
||||
IModelState state;
|
||||
if(transforms.isEmpty())
|
||||
{
|
||||
state = base;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = new SimpleModelState(Maps.immutableEnumMap(transforms), Optional.of(base));
|
||||
}
|
||||
return Optional.of(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deserializeTRSR(JsonDeserializationContext context, EnumMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms, JsonObject transform, String name, ItemCameraTransforms.TransformType itemCameraTransform)
|
||||
{
|
||||
if(transform.has(name))
|
||||
{
|
||||
TRSRTransformation t = context.deserialize(transform.remove(name), TRSRTransformation.class);
|
||||
transforms.put(itemCameraTransform, TRSRTransformation.blockCenterToCorner(t));
|
||||
}
|
||||
}
|
||||
|
||||
public static IBakedModel bakeHelper(BlockModel blockModel, ModelBakery modelBakery, BlockModel otherModel, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
try
|
||||
{
|
||||
IBakedModel model;
|
||||
IModelGeometry<?> customModel = blockModel.customData.getCustomGeometry();
|
||||
IModelState customModelState = blockModel.customData.getCustomModelState();
|
||||
if (customModelState != null)
|
||||
{
|
||||
sprite = new ModelStateComposition(customModelState.apply(Optional.empty()).orElse(TRSRTransformation.identity()), sprite.getState(), sprite.isUvLock());
|
||||
}
|
||||
if (customModel != null)
|
||||
{
|
||||
model = customModel.bake(blockModel.customData, modelBakery, spriteGetter, sprite, format, blockModel.getOverrides(modelBakery, otherModel, spriteGetter, format));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT))
|
||||
{
|
||||
// TODO: Provide custom vanilla baking handler for special formats.
|
||||
throw new IllegalArgumentException("can't bake vanilla models to the format that doesn't fit into the default one: " + format);
|
||||
}
|
||||
model = blockModel.bakeVanilla(modelBakery, otherModel, spriteGetter, sprite, format);
|
||||
}
|
||||
|
||||
if (customModelState != null && !model.doesHandlePerspectives())
|
||||
{
|
||||
model = new PerspectiveMapWrapper(model, customModelState);
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// Minecraft eats the stack trace, so print it first.
|
||||
LOGGER.error("Error baking model", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModelLoaderAdapter implements IModelLoader<ModelAdapter>
|
||||
{
|
||||
private final ICustomModelLoader parent;
|
||||
|
||||
public ModelLoaderAdapter(ICustomModelLoader parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelAdapter read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
|
||||
{
|
||||
if (!modelContents.has("model"))
|
||||
throw new RuntimeException("ModelLoaderAdapter requires a 'model' key that points to a model file.");
|
||||
|
||||
String modelLocation = modelContents.get("model").getAsString();
|
||||
|
||||
boolean forceLoad = JSONUtils.getBoolean(modelContents,"force", false);
|
||||
|
||||
try
|
||||
{
|
||||
ResourceLocation loc = new ResourceLocation(modelLocation);
|
||||
|
||||
if (!forceLoad && !parent.accepts(loc))
|
||||
throw new IllegalStateException("The loader does not accept the requested model.");
|
||||
|
||||
IUnbakedModel model = parent.loadModel(loc);
|
||||
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
|
||||
if (modelContents.has("custom"))
|
||||
{
|
||||
for (Map.Entry<String, JsonElement> e : modelContents.get("custom").getAsJsonObject().entrySet())
|
||||
{
|
||||
if (e.getValue().isJsonNull())
|
||||
builder.put(e.getKey(), null);
|
||||
else
|
||||
builder.put(e.getKey(), e.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
return new ModelAdapter(model, builder.build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("Error loading model from legacy model loader", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModelAdapter implements IModelGeometry<ModelAdapter>
|
||||
{
|
||||
private final ImmutableMap<String, String> customData;
|
||||
private IUnbakedModel wrappedModel;
|
||||
private boolean processed;
|
||||
|
||||
public ModelAdapter(IUnbakedModel wrappedModel, ImmutableMap<String,String> customData)
|
||||
{
|
||||
this.wrappedModel = wrappedModel;
|
||||
this.customData = customData;
|
||||
}
|
||||
|
||||
private void process(IModelConfiguration owner, @Nullable TextureAtlasSprite particle)
|
||||
{
|
||||
if (!processed)
|
||||
{
|
||||
processed = true;
|
||||
wrappedModel = wrappedModel.process(customData);
|
||||
if (owner instanceof BlockModel)
|
||||
{
|
||||
Map<String, String> replacements = Maps.newHashMap();
|
||||
BlockModel src = (BlockModel)owner;
|
||||
while (src != null)
|
||||
{
|
||||
for (Map.Entry<String, String> entry : src.textures.entrySet())
|
||||
{
|
||||
replacements.putIfAbsent(entry.getKey(), entry.getValue());
|
||||
}
|
||||
src = src.parent;
|
||||
}
|
||||
|
||||
for (String key : replacements.keySet())
|
||||
{
|
||||
String value = replacements.get(key);
|
||||
boolean replaced = value.startsWith("#");
|
||||
while (value.startsWith("#"))
|
||||
{
|
||||
String newkey = value.substring(1);
|
||||
value = replacements.get(newkey);
|
||||
}
|
||||
if (replaced)
|
||||
{
|
||||
replacements.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
wrappedModel = wrappedModel.retexture(ImmutableMap.copyOf(replacements));
|
||||
}
|
||||
if (owner.useSmoothLighting()) wrappedModel = wrappedModel.smoothLighting(true);
|
||||
if (owner.isShadedInGui()) wrappedModel = wrappedModel.gui3d(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
process(owner, null);
|
||||
return wrappedModel.getTextures(modelGetter, missingTextureErrors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides)
|
||||
{
|
||||
TextureAtlasSprite particle = spriteGetter.apply(new ResourceLocation(owner.resolveTexture("particle")));
|
||||
process(owner, particle);
|
||||
|
||||
sprite = new ModelStateComposition(owner.getCombinedState(), sprite.getState(), sprite.isUvLock());
|
||||
|
||||
return wrappedModel.bake(bakery, spriteGetter, sprite, format);
|
||||
}
|
||||
}
|
||||
|
||||
public static class VanillaProxy implements ISimpleModelGeometry<VanillaProxy>
|
||||
{
|
||||
private final List<BlockPart> elements;
|
||||
|
||||
public VanillaProxy(List<BlockPart> list)
|
||||
{
|
||||
this.elements = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
if(!Attributes.moreSpecific(format, Attributes.DEFAULT_BAKED_FORMAT))
|
||||
{
|
||||
// TODO: Provide custom handler for special formats.
|
||||
throw new IllegalArgumentException("can't bake vanilla models to the format that doesn't fit into the default one: " + format);
|
||||
}
|
||||
|
||||
for(BlockPart blockpart : elements) {
|
||||
for(Direction direction : blockpart.mapFaces.keySet()) {
|
||||
BlockPartFace blockpartface = blockpart.mapFaces.get(direction);
|
||||
TextureAtlasSprite textureatlassprite1 = spriteGetter.apply(new ResourceLocation(owner.resolveTexture(blockpartface.texture)));
|
||||
if (blockpartface.cullFace == null) {
|
||||
modelBuilder.addGeneralQuad(BlockModel.makeBakedQuad(blockpart, blockpartface, textureatlassprite1, direction, sprite));
|
||||
} else {
|
||||
modelBuilder.addFaceQuad(
|
||||
sprite.getState().apply(Optional.empty())
|
||||
.map(trsr -> trsr.rotateTransform(blockpartface.cullFace)).orElse(blockpartface.cullFace),
|
||||
BlockModel.makeBakedQuad(blockpart, blockpartface, textureatlassprite1, direction, sprite));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
Set<ResourceLocation> textures = Sets.newHashSet();
|
||||
|
||||
for(BlockPart part : elements) {
|
||||
for(BlockPartFace face : part.mapFaces.values()) {
|
||||
String texture = owner.resolveTexture(face.texture);
|
||||
if (Objects.equals(texture, MissingTextureSprite.getLocation().toString())) {
|
||||
missingTextureErrors.add(String.format("%s in %s", face.texture, owner.getModelName()));
|
||||
}
|
||||
|
||||
textures.add(new ResourceLocation(texture));
|
||||
}
|
||||
}
|
||||
|
||||
return textures;
|
||||
}
|
||||
|
||||
public static class Loader implements IModelLoader<VanillaProxy>
|
||||
{
|
||||
public static final Loader INSTANCE = new Loader();
|
||||
|
||||
private Loader()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VanillaProxy read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
|
||||
{
|
||||
List<BlockPart> list = this.getModelElements(deserializationContext, modelContents);
|
||||
return new VanillaProxy(list);
|
||||
}
|
||||
|
||||
private List<BlockPart> getModelElements(JsonDeserializationContext deserializationContext, JsonObject object) {
|
||||
List<BlockPart> list = Lists.newArrayList();
|
||||
if (object.has("elements")) {
|
||||
for(JsonElement jsonelement : JSONUtils.getJsonArray(object, "elements")) {
|
||||
list.add(deserializationContext.deserialize(jsonelement, BlockPart.class));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExpandedBlockModelDeserializer extends BlockModel.Deserializer
|
||||
{
|
||||
public static final Gson INSTANCE = (new GsonBuilder())
|
||||
.registerTypeAdapter(BlockModel.class, new ExpandedBlockModelDeserializer())
|
||||
.registerTypeAdapter(BlockPart.class, new BlockPart.Deserializer())
|
||||
.registerTypeAdapter(BlockPartFace.class, new BlockPartFace.Deserializer())
|
||||
.registerTypeAdapter(BlockFaceUV.class, new BlockFaceUV.Deserializer())
|
||||
.registerTypeAdapter(ItemTransformVec3f.class, new ItemTransformVec3f.Deserializer())
|
||||
.registerTypeAdapter(ItemCameraTransforms.class, new ItemCameraTransforms.Deserializer())
|
||||
.registerTypeAdapter(ItemOverride.class, new ItemOverride.Deserializer())
|
||||
.registerTypeAdapter(TRSRTransformation.class, ForgeBlockStateV1.TRSRDeserializer.INSTANCE)
|
||||
.create();
|
||||
|
||||
public BlockModel deserialize(JsonElement element, Type targetType, JsonDeserializationContext deserializationContext) throws JsonParseException {
|
||||
BlockModel model = super.deserialize(element, targetType, deserializationContext);
|
||||
JsonObject jsonobject = element.getAsJsonObject();
|
||||
IModelGeometry<?> geometry = deserializeGeometry(deserializationContext, jsonobject);
|
||||
|
||||
List<BlockPart> elements = model.getElements();
|
||||
if (geometry != null) {
|
||||
elements.clear();
|
||||
model.customData.setCustomGeometry(geometry);
|
||||
}
|
||||
|
||||
IModelState modelState = deserializeModelTransforms(deserializationContext, jsonobject);
|
||||
if (modelState != null)
|
||||
{
|
||||
model.customData.setCustomModelState(modelState);
|
||||
}
|
||||
|
||||
if (jsonobject.has("visibility"))
|
||||
{
|
||||
JsonObject visibility = JSONUtils.getJsonObject(jsonobject, "visibility");
|
||||
for(Map.Entry<String, JsonElement> part : visibility.entrySet())
|
||||
{
|
||||
model.customData.visibilityData.setVisibilityState(part.getKey(), part.getValue().getAsBoolean());
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,9 @@ public final class MultiLayerModel implements IUnbakedModel
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final MultiLayerModel INSTANCE = new MultiLayerModel(ImmutableMap.of());
|
||||
|
||||
private final ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models;
|
||||
private final ImmutableMap<Optional<BlockRenderLayer>, ? extends ResourceLocation> models;
|
||||
|
||||
public MultiLayerModel(ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models)
|
||||
public MultiLayerModel(ImmutableMap<Optional<BlockRenderLayer>, ? extends ResourceLocation> models)
|
||||
{
|
||||
this.models = models;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public final class MultiLayerModel implements IUnbakedModel
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> buildModels(ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models, ISprite sprite, VertexFormat format, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter)
|
||||
private static ImmutableMap<Optional<BlockRenderLayer>, IBakedModel> buildModels(ImmutableMap<Optional<BlockRenderLayer>, ? extends ResourceLocation> models, ISprite sprite, VertexFormat format, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter)
|
||||
{
|
||||
ImmutableMap.Builder<Optional<BlockRenderLayer>, IBakedModel> builder = ImmutableMap.builder();
|
||||
for(Optional<BlockRenderLayer> key : models.keySet())
|
||||
@@ -108,7 +108,7 @@ public final class MultiLayerModel implements IUnbakedModel
|
||||
@Override
|
||||
public MultiLayerModel process(ImmutableMap<String, String> customData)
|
||||
{
|
||||
ImmutableMap.Builder<Optional<BlockRenderLayer>, ModelResourceLocation> builder = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<Optional<BlockRenderLayer>, ResourceLocation> builder = ImmutableMap.builder();
|
||||
for(String key : customData.keySet())
|
||||
{
|
||||
if("base".equals(key))
|
||||
@@ -123,12 +123,12 @@ public final class MultiLayerModel implements IUnbakedModel
|
||||
}
|
||||
}
|
||||
}
|
||||
ImmutableMap<Optional<BlockRenderLayer>, ModelResourceLocation> models = builder.build();
|
||||
ImmutableMap<Optional<BlockRenderLayer>, ResourceLocation> models = builder.build();
|
||||
if(models.isEmpty()) return INSTANCE;
|
||||
return new MultiLayerModel(models);
|
||||
}
|
||||
|
||||
private ModelResourceLocation getLocation(String json)
|
||||
private ResourceLocation getLocation(String json)
|
||||
{
|
||||
JsonElement e = new JsonParser().parse(json);
|
||||
if(e.isJsonPrimitive() && e.getAsJsonPrimitive().isString())
|
||||
@@ -201,6 +201,12 @@ public final class MultiLayerModel implements IUnbakedModel
|
||||
return base.getParticleTexture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -199,6 +199,12 @@ public final class MultiModel implements IUnbakedModel
|
||||
return quads.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -23,26 +23,31 @@ import java.util.EnumMap;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.model.data.IDynamicBakedModel;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
import net.minecraftforge.common.model.IModelState;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import java.util.List;
|
||||
|
||||
public class PerspectiveMapWrapper implements IBakedModel
|
||||
public class PerspectiveMapWrapper implements IDynamicBakedModel
|
||||
{
|
||||
private final IBakedModel parent;
|
||||
private final ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms;
|
||||
private final OverrideListWrapper overrides = new OverrideListWrapper();
|
||||
|
||||
public PerspectiveMapWrapper(IBakedModel parent, ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms)
|
||||
{
|
||||
@@ -83,6 +88,25 @@ public class PerspectiveMapWrapper implements IBakedModel
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> getTransformsWithFallback(IModelState state, ItemCameraTransforms transforms)
|
||||
{
|
||||
EnumMap<ItemCameraTransforms.TransformType, TRSRTransformation> map = new EnumMap<>(ItemCameraTransforms.TransformType.class);
|
||||
for(ItemCameraTransforms.TransformType type : ItemCameraTransforms.TransformType.values())
|
||||
{
|
||||
Optional<TRSRTransformation> tr = state.apply(Optional.of(type));
|
||||
if(tr.isPresent())
|
||||
{
|
||||
map.put(type, tr.get());
|
||||
}
|
||||
else if (transforms.hasCustomTransform(type))
|
||||
{
|
||||
map.put(type, TRSRTransformation.blockCenterToCorner(TRSRTransformation.from(transforms.getTransform(type))));
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
public static Pair<? extends IBakedModel, Matrix4f> handlePerspective(IBakedModel model, ImmutableMap<ItemCameraTransforms.TransformType, TRSRTransformation> transforms, ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
TRSRTransformation tr = transforms.getOrDefault(cameraTransformType, TRSRTransformation.identity());
|
||||
@@ -110,12 +134,48 @@ public class PerspectiveMapWrapper implements IBakedModel
|
||||
@Override public TextureAtlasSprite getParticleTexture() { return parent.getParticleTexture(); }
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override public ItemCameraTransforms getItemCameraTransforms() { return parent.getItemCameraTransforms(); }
|
||||
@Override public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) { return parent.getQuads(state, side, rand); }
|
||||
@Override public ItemOverrideList getOverrides() { return parent.getOverrides(); }
|
||||
@Override public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand, @Nonnull IModelData extraData)
|
||||
{
|
||||
return parent.getQuads(state, side, rand, extraData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides()
|
||||
{
|
||||
return overrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType)
|
||||
{
|
||||
return handlePerspective(this, transforms, cameraTransformType);
|
||||
}
|
||||
|
||||
private class OverrideListWrapper extends ItemOverrideList
|
||||
{
|
||||
public OverrideListWrapper()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBakedModel getModelWithOverrides(IBakedModel model, ItemStack stack, @Nullable World worldIn, @Nullable LivingEntity entityIn)
|
||||
{
|
||||
model = parent.getOverrides().getModelWithOverrides(parent, stack, worldIn, entityIn);
|
||||
return new PerspectiveMapWrapper(model, transforms);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<ItemOverride> getOverrides()
|
||||
{
|
||||
return parent.getOverrides().getOverrides();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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 com.google.common.collect.Lists;
|
||||
import net.minecraft.client.renderer.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
import javax.vecmath.Vector4f;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class QuadTransformer
|
||||
{
|
||||
private final VertexFormat format;
|
||||
private final int positionOffset;
|
||||
private final int normalOffset;
|
||||
private final TRSRTransformation transform;
|
||||
|
||||
public QuadTransformer(VertexFormat format, TRSRTransformation transform)
|
||||
{
|
||||
this.format = format;
|
||||
this.positionOffset = findPositionOffset(format);
|
||||
this.normalOffset = findNormalOffset(format);
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
private void processVertices(int[] inData, int[] outData)
|
||||
{
|
||||
int stride = format.getSize();
|
||||
int count = (inData.length * 4) / stride;
|
||||
for (int i=0;i<count;i++)
|
||||
{
|
||||
int offset = positionOffset + i * stride;
|
||||
float x = Float.intBitsToFloat(getAtByteOffset(inData, offset ));
|
||||
float y = Float.intBitsToFloat(getAtByteOffset(inData, offset + 4));
|
||||
float z = Float.intBitsToFloat(getAtByteOffset(inData, offset + 8));
|
||||
|
||||
Vector4f pos = new Vector4f(x, y, z, 1);
|
||||
transform.transformPosition(pos);
|
||||
if (pos.w != 1.0f)
|
||||
pos.scale(1 / pos.w);
|
||||
|
||||
putAtByteOffset(outData, offset, Float.floatToRawIntBits(pos.getX()));
|
||||
putAtByteOffset(outData,offset + 4, Float.floatToRawIntBits(pos.getY()));
|
||||
putAtByteOffset(outData,offset + 8, Float.floatToRawIntBits(pos.getZ()));
|
||||
}
|
||||
|
||||
if (normalOffset >= 0)
|
||||
{
|
||||
for (int i=0;i<count;i++)
|
||||
{
|
||||
int offset = normalOffset + i * stride;
|
||||
int normalIn = getAtByteOffset(inData,offset);
|
||||
if (normalIn != 0)
|
||||
{
|
||||
float x = (((normalIn) >> 24) & 0xFF) / 255.0f;
|
||||
float y = (((normalIn << 8) >> 24) & 0xFF) / 255.0f;
|
||||
float z = (((normalIn << 16) >> 24) & 0xFF) / 255.0f;
|
||||
|
||||
Vector3f pos = new Vector3f(x, y, z);
|
||||
transform.transformNormal(pos);
|
||||
pos.normalize();
|
||||
|
||||
int normalOut = (((int) (x / 255.0) & 0xFF) << 24) |
|
||||
(((int) (y / 255.0) & 0xFF) << 16) |
|
||||
(((int) (z / 255.0) & 0xFF) << 8) |
|
||||
(normalIn & 0xFF);
|
||||
|
||||
putAtByteOffset(outData, offset, normalOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getAtByteOffset(int[] inData, int offset)
|
||||
{
|
||||
int index = offset / 4;
|
||||
int lsb = inData[index];
|
||||
|
||||
int shift = (offset % 4) * 8;
|
||||
if (shift == 0)
|
||||
return inData[index];
|
||||
|
||||
int msb = inData[index+1];
|
||||
|
||||
return (lsb >>> shift) | (msb << (32-shift));
|
||||
}
|
||||
|
||||
private static void putAtByteOffset(int[] outData, int offset, int value)
|
||||
{
|
||||
int index = offset / 4;
|
||||
int shift = (offset % 4) * 8;
|
||||
|
||||
if (shift == 0)
|
||||
outData[index] = value;
|
||||
|
||||
int lsbMask = 0xFFFFFFFF >>> (32-shift);
|
||||
int msbMask = 0xFFFFFFFF << shift;
|
||||
|
||||
outData[index] = (outData[index] & lsbMask) | (value << shift);
|
||||
outData[index+1] = (outData[index+1] & msbMask) | (value >>> (32-shift));
|
||||
}
|
||||
|
||||
private static int findPositionOffset(VertexFormat fmt)
|
||||
{
|
||||
int index;
|
||||
VertexFormatElement element = null;
|
||||
for (index = 0; index < fmt.getElementCount(); index++)
|
||||
{
|
||||
VertexFormatElement el = fmt.getElement(index);
|
||||
if (el.getUsage() == VertexFormatElement.Usage.POSITION)
|
||||
{
|
||||
element = el;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == fmt.getElementCount() || element == null)
|
||||
throw new RuntimeException("Expected vertex format to have a POSITION attribute");
|
||||
if (element.getType() != VertexFormatElement.Type.FLOAT)
|
||||
throw new RuntimeException("Expected POSITION attribute to have data type FLOAT");
|
||||
if (element.getSize() < 3)
|
||||
throw new RuntimeException("Expected POSITION attribute to have at least 3 dimensions");
|
||||
return fmt.getOffset(index);
|
||||
}
|
||||
|
||||
private static int findNormalOffset(VertexFormat fmt)
|
||||
{
|
||||
int index;
|
||||
VertexFormatElement element = null;
|
||||
for (index = 0; index < fmt.getElementCount(); index++)
|
||||
{
|
||||
VertexFormatElement el = fmt.getElement(index);
|
||||
if (el.getUsage() == VertexFormatElement.Usage.NORMAL)
|
||||
{
|
||||
element = el;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == fmt.getElementCount() || element == null)
|
||||
return -1;
|
||||
if (element.getType() != VertexFormatElement.Type.BYTE)
|
||||
throw new RuntimeException("Expected NORMAL attribute to have data type BYTE");
|
||||
if (element.getSize() < 3)
|
||||
throw new RuntimeException("Expected NORMAL attribute to have at least 3 dimensions");
|
||||
return fmt.getOffset(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single quad, producing a new quad.
|
||||
* @param input A single quad to transform.
|
||||
* @return A new BakedQuad object with the new position.
|
||||
*/
|
||||
public BakedQuad processOne(BakedQuad input)
|
||||
{
|
||||
int[] inData = input.getVertexData();
|
||||
int[] outData = Arrays.copyOf(inData, inData.length);
|
||||
processVertices(inData, outData);
|
||||
|
||||
return new BakedQuad(outData, input.getTintIndex(), input.getFace(), input.getSprite(), input.shouldApplyDiffuseLighting(), format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a single quad, modifying the input quad.
|
||||
* @param input A single quad to transform.
|
||||
* @return The input BakedQuad object with the new position applied.
|
||||
*/
|
||||
public BakedQuad processOneInPlace(BakedQuad input)
|
||||
{
|
||||
int[] data = input.getVertexData();
|
||||
processVertices(data, data);
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes multiple quads, producing a new array of new quads.
|
||||
* @param inputs The list of quads to transform
|
||||
* @return A new array of new BakedQuad objects.
|
||||
*/
|
||||
public List<BakedQuad> processMany(List<BakedQuad> inputs)
|
||||
{
|
||||
if(inputs.size() == 0)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<BakedQuad> outputs = Lists.newArrayList();
|
||||
for(BakedQuad input : inputs)
|
||||
{
|
||||
int[] inData = input.getVertexData();
|
||||
int[] outData = Arrays.copyOf(inData, inData.length);
|
||||
processVertices(inData, outData);
|
||||
|
||||
outputs.add(new BakedQuad(outData, input.getTintIndex(), input.getFace(), input.getSprite(), input.shouldApplyDiffuseLighting(), format));
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes multiple quads in place, modifying the input quads.
|
||||
* @param inputs The list of quads to transform
|
||||
*/
|
||||
public void processManyInPlace(List<BakedQuad> inputs)
|
||||
{
|
||||
if(inputs.size() == 0)
|
||||
return;
|
||||
|
||||
for(BakedQuad input : inputs)
|
||||
{
|
||||
int[] data = input.getVertexData();
|
||||
processVertices(data, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,6 +519,12 @@ public class ModelBlockAnimation
|
||||
|
||||
@Nullable
|
||||
public TRSRTransformation getPartTransform(IModelState state, BlockPart part, int i)
|
||||
{
|
||||
return getPartTransform(state, i);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TRSRTransformation getPartTransform(IModelState state, int i)
|
||||
{
|
||||
ImmutableCollection<MBJointWeight> infos = getJoint(i);
|
||||
if(!infos.isEmpty())
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
@@ -410,7 +411,7 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
private final ResourceLocation modelLocation;
|
||||
private final B3DModel model;
|
||||
private final ImmutableSet<String> meshes;
|
||||
private final ImmutableMap<String, ResourceLocation> textures;
|
||||
private final ImmutableMap<String, String> textures;
|
||||
private final boolean smooth;
|
||||
private final boolean gui3d;
|
||||
private final int defaultKey;
|
||||
@@ -420,7 +421,7 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
this(modelLocation, model, meshes, smooth, gui3d, defaultKey, buildTextures(model.getTextures()));
|
||||
}
|
||||
|
||||
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, boolean smooth, boolean gui3d, int defaultKey, ImmutableMap<String, ResourceLocation> textures)
|
||||
public ModelWrapper(ResourceLocation modelLocation, B3DModel model, ImmutableSet<String> meshes, boolean smooth, boolean gui3d, int defaultKey, ImmutableMap<String, String> textures)
|
||||
{
|
||||
this.modelLocation = modelLocation;
|
||||
this.model = model;
|
||||
@@ -431,16 +432,16 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
this.defaultKey = defaultKey;
|
||||
}
|
||||
|
||||
private static ImmutableMap<String, ResourceLocation> buildTextures(List<Texture> textures)
|
||||
private static ImmutableMap<String, String> buildTextures(List<Texture> textures)
|
||||
{
|
||||
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
|
||||
for(Texture t : textures)
|
||||
{
|
||||
String path = t.getPath();
|
||||
String location = getLocation(path);
|
||||
if(!location.startsWith("#")) location = "#" + location;
|
||||
builder.put(path, new ResourceLocation(location));
|
||||
builder.put(path, location);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
@@ -454,7 +455,7 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextures(Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
return Collections2.filter(textures.values(), loc -> !loc.getPath().startsWith("#"));
|
||||
return textures.values().stream().filter(loc -> !loc.startsWith("#")).map(ResourceLocation::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -469,16 +470,16 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
{
|
||||
ImmutableMap.Builder<String, TextureAtlasSprite> builder = ImmutableMap.builder();
|
||||
TextureAtlasSprite missing = spriteGetter.apply(new ResourceLocation("missingno"));
|
||||
for(Map.Entry<String, ResourceLocation> e : textures.entrySet())
|
||||
for(Map.Entry<String, String> e : textures.entrySet())
|
||||
{
|
||||
if(e.getValue().getPath().startsWith("#"))
|
||||
if(e.getValue().startsWith("#"))
|
||||
{
|
||||
LOGGER.fatal("unresolved texture '{}' for b3d model '{}'", e.getValue().getPath(), modelLocation);
|
||||
LOGGER.fatal("unresolved texture '{}' for b3d model '{}'", e.getValue(), modelLocation);
|
||||
builder.put(e.getKey(), missing);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.put(e.getKey(), spriteGetter.apply(e.getValue()));
|
||||
builder.put(e.getKey(), spriteGetter.apply(new ResourceLocation(e.getValue())));
|
||||
}
|
||||
}
|
||||
builder.put("missingno", missing);
|
||||
@@ -488,16 +489,19 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
@Override
|
||||
public ModelWrapper retexture(ImmutableMap<String, String> textures)
|
||||
{
|
||||
ImmutableMap.Builder<String, ResourceLocation> builder = ImmutableMap.builder();
|
||||
for(Map.Entry<String, ResourceLocation> e : this.textures.entrySet())
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
for(Map.Entry<String, String> e : this.textures.entrySet())
|
||||
{
|
||||
String path = e.getKey();
|
||||
String loc = getLocation(path);
|
||||
if(textures.containsKey(loc))
|
||||
// FIXME: Backward compatibilty: support finding textures that start with #, even though this is not how vanilla works
|
||||
if(loc.startsWith("#") && (textures.containsKey(loc) || textures.containsKey(loc.substring(1))))
|
||||
{
|
||||
String alt = loc.substring(1);
|
||||
String newLoc = textures.get(loc);
|
||||
if(newLoc == null) newLoc = getLocation(path);
|
||||
builder.put(e.getKey(), new ResourceLocation(newLoc));
|
||||
if(newLoc == null) newLoc = textures.get(alt);
|
||||
if(newLoc == null) newLoc = path.substring(1);
|
||||
builder.put(e.getKey(), newLoc);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -816,6 +820,12 @@ public enum B3DLoader implements ICustomModelLoader
|
||||
return textures.values().asList().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -32,7 +32,12 @@ public class ModelDataMap implements IModelData
|
||||
{
|
||||
this.backingMap = new IdentityHashMap<>(map);
|
||||
}
|
||||
|
||||
|
||||
protected ModelDataMap()
|
||||
{
|
||||
this.backingMap = new IdentityHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(ModelProperty<?> prop)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
||||
+19
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.generators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.geometry;
|
||||
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModelConfiguration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* General interface for any model that can be baked, superset of vanilla {@link net.minecraft.client.renderer.model.IUnbakedModel}.
|
||||
* Models can be baked to different vertex formats and with different state.
|
||||
*/
|
||||
public interface IModelGeometry<T extends IModelGeometry<T>>
|
||||
{
|
||||
default Collection<? extends IModelGeometryPart> getParts() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
default Optional<? extends IModelGeometryPart> getPart(String name) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides);
|
||||
|
||||
Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.geometry;
|
||||
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel;
|
||||
import net.minecraft.client.renderer.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModelBuilder;
|
||||
import net.minecraftforge.client.model.IModelConfiguration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface IModelGeometryPart
|
||||
{
|
||||
String name();
|
||||
|
||||
void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format);
|
||||
|
||||
default Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors) {
|
||||
// No texture dependencies
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.geometry;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import net.minecraft.client.renderer.model.IUnbakedModel;
|
||||
import net.minecraft.client.renderer.model.ModelBakery;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModelBuilder;
|
||||
import net.minecraftforge.client.model.IModelConfiguration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface IMultipartModelGeometry<T extends IMultipartModelGeometry<T>> extends ISimpleModelGeometry<T>
|
||||
{
|
||||
@Override
|
||||
Collection<? extends IModelGeometryPart> getParts();
|
||||
|
||||
Optional<? extends IModelGeometryPart> getPart(String name);
|
||||
|
||||
@Override
|
||||
default void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
getParts().stream().filter(part -> owner.getPartVisibility(part))
|
||||
.forEach(part -> part.addQuads(owner, modelBuilder, bakery, spriteGetter, sprite, format));
|
||||
}
|
||||
|
||||
@Override
|
||||
default Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
Set<ResourceLocation> combined = Sets.newHashSet();
|
||||
for (IModelGeometryPart part : getParts())
|
||||
combined.addAll(part.getTextureDependencies(owner, modelGetter, missingTextureErrors));
|
||||
return combined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.geometry;
|
||||
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModelBuilder;
|
||||
import net.minecraftforge.client.model.IModelConfiguration;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ISimpleModelGeometry<T extends ISimpleModelGeometry<T>> extends IModelGeometry<T>
|
||||
{
|
||||
@Override
|
||||
default IBakedModel bake(IModelConfiguration owner, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format, ItemOverrideList overrides)
|
||||
{
|
||||
TextureAtlasSprite particle = spriteGetter.apply(new ResourceLocation(owner.resolveTexture("particle")));
|
||||
|
||||
IModelBuilder<?> builder = IModelBuilder.of(owner, overrides, particle);
|
||||
|
||||
addQuads(owner, builder, bakery, spriteGetter, sprite, format);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format);
|
||||
|
||||
@Override
|
||||
Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.obj;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import joptsimple.internal.Strings;
|
||||
import net.minecraft.resources.IResource;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class LineReader implements AutoCloseable
|
||||
{
|
||||
InputStreamReader lineStream;
|
||||
BufferedReader lineReader;
|
||||
|
||||
public LineReader(IResource resource)
|
||||
{
|
||||
this.lineStream = new InputStreamReader(resource.getInputStream(), Charsets.UTF_8);
|
||||
this.lineReader = new BufferedReader(lineStream);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String[] readAndSplitLine(boolean ignoreEmptyLines) throws IOException
|
||||
{
|
||||
//noinspection LoopConditionNotUpdatedInsideLoop
|
||||
do
|
||||
{
|
||||
String currentLine = lineReader.readLine();
|
||||
if (currentLine == null)
|
||||
return null;
|
||||
|
||||
List<String> lineParts = new ArrayList<>();
|
||||
|
||||
if (currentLine.startsWith("#"))
|
||||
currentLine = "";
|
||||
|
||||
if (currentLine.length() > 0)
|
||||
{
|
||||
|
||||
boolean hasContinuation;
|
||||
do
|
||||
{
|
||||
hasContinuation = currentLine.endsWith("\\");
|
||||
String tmp = hasContinuation ? currentLine.substring(0, currentLine.length() - 1) : currentLine;
|
||||
|
||||
Arrays.stream(tmp.split("[\t ]+")).filter(s -> !Strings.isNullOrEmpty(s)).forEach(lineParts::add);
|
||||
|
||||
if (hasContinuation)
|
||||
{
|
||||
currentLine = lineReader.readLine();
|
||||
if (currentLine == null)
|
||||
break;
|
||||
|
||||
if (currentLine.length() == 0 || currentLine.startsWith("#"))
|
||||
break;
|
||||
}
|
||||
} while (hasContinuation);
|
||||
}
|
||||
|
||||
if (lineParts.size() > 0)
|
||||
return lineParts.toArray(new String[0]);
|
||||
}
|
||||
while (ignoreEmptyLines);
|
||||
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{
|
||||
lineReader.close();
|
||||
lineStream.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.obj;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import joptsimple.internal.Strings;
|
||||
|
||||
import javax.vecmath.Vector4f;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class MaterialLibrary2
|
||||
{
|
||||
public static final MaterialLibrary2 EMPTY = new MaterialLibrary2();
|
||||
final Map<String, Material> materials = Maps.newHashMap();
|
||||
|
||||
private MaterialLibrary2()
|
||||
{
|
||||
}
|
||||
|
||||
public MaterialLibrary2(LineReader reader) throws IOException
|
||||
{
|
||||
Material currentMaterial = null;
|
||||
|
||||
String[] line;
|
||||
while((line = reader.readAndSplitLine(true)) != null)
|
||||
{
|
||||
switch(line[0])
|
||||
{
|
||||
case "newmtl":
|
||||
{
|
||||
String name = Strings.join(Arrays.copyOfRange(line, 1, line.length), " ");
|
||||
currentMaterial = new Material(name);
|
||||
materials.put(name, currentMaterial);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Ka":
|
||||
currentMaterial.ambientColor = OBJModel2.parseVector4(line);
|
||||
break;
|
||||
|
||||
case "map_Ka":
|
||||
// Ignores all options params
|
||||
currentMaterial.ambientColorMap = line[line.length-1];
|
||||
break;
|
||||
|
||||
case "Kd":
|
||||
currentMaterial.diffuseColor = OBJModel2.parseVector4(line);
|
||||
break;
|
||||
|
||||
case "forge_TintIndex":
|
||||
currentMaterial.diffuseTintIndex = Integer.parseInt(line[1]);
|
||||
break;
|
||||
|
||||
case "map_Kd":
|
||||
// Ignores all options params
|
||||
currentMaterial.diffuseColorMap = line[line.length-1];
|
||||
break;
|
||||
|
||||
case "Ks":
|
||||
currentMaterial.specularColor = OBJModel2.parseVector4(line);
|
||||
break;
|
||||
|
||||
case "Ns":
|
||||
currentMaterial.specularHighlight = Float.parseFloat(line[1]);
|
||||
break;
|
||||
|
||||
case "map_Ks":
|
||||
// Ignores all options params
|
||||
currentMaterial.specularColorMap = line[line.length-1];
|
||||
break;
|
||||
|
||||
case "d":
|
||||
// Ignores all options params
|
||||
currentMaterial.dissolve = Float.parseFloat(line[1]);
|
||||
break;
|
||||
case "Tr":
|
||||
// Ignores all options params
|
||||
currentMaterial.transparency = Float.parseFloat(line[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Material getMaterial(String mat)
|
||||
{
|
||||
if (!materials.containsKey(mat))
|
||||
throw new NoSuchElementException("The material was not found in the library: " + mat);
|
||||
return materials.get(mat);
|
||||
}
|
||||
|
||||
public class Material
|
||||
{
|
||||
public final String name;
|
||||
public Vector4f ambientColor = new Vector4f(0,0,0,1);
|
||||
public String ambientColorMap;
|
||||
public Vector4f diffuseColor = new Vector4f(1,1,1,1);
|
||||
public String diffuseColorMap;
|
||||
public Vector4f specularColor = new Vector4f(0,0,0,1);
|
||||
public float specularHighlight = 0;
|
||||
public String specularColorMap;
|
||||
|
||||
public float dissolve = 1.0f;
|
||||
public float transparency = 0.0f;
|
||||
|
||||
// non-standard
|
||||
public int diffuseTintIndex = 0;
|
||||
|
||||
public Material(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.apache.logging.log4j.Logger;
|
||||
* To enable your mod call instance.addDomain(modid).
|
||||
* If you need more control over accepted resources - extend the class, and register a new instance with ModelLoaderRegistry.
|
||||
*/
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public enum OBJLoader implements ICustomModelLoader {
|
||||
INSTANCE;
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.obj;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.IResource;
|
||||
import net.minecraft.resources.IResourceManager;
|
||||
import net.minecraft.util.JSONUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.IModelLoader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class OBJLoader2 implements IModelLoader<OBJModel2>
|
||||
{
|
||||
public static OBJLoader2 INSTANCE = new OBJLoader2();
|
||||
|
||||
private final Map<OBJModel2.ModelSettings, OBJModel2> modelCache = Maps.newHashMap();
|
||||
private final Map<ResourceLocation, MaterialLibrary2> materialCache = Maps.newHashMap();
|
||||
|
||||
private IResourceManager manager = Minecraft.getInstance().getResourceManager();
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
modelCache.clear();
|
||||
materialCache.clear();
|
||||
manager = resourceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OBJModel2 read(JsonDeserializationContext deserializationContext, JsonObject modelContents)
|
||||
{
|
||||
if (!modelContents.has("model"))
|
||||
throw new RuntimeException("OBJ Loader requires a 'model' key that points to a valid .OBJ model.");
|
||||
|
||||
String modelLocation = modelContents.get("model").getAsString();
|
||||
|
||||
boolean detectCullableFaces = JSONUtils.getBoolean(modelContents, "detectCullableFaces", true);
|
||||
boolean diffuseLighting = JSONUtils.getBoolean(modelContents, "diffuseLighting", false);
|
||||
boolean flipV = JSONUtils.getBoolean(modelContents, "flip-v", false);
|
||||
boolean ambientToFullbright = JSONUtils.getBoolean(modelContents, "ambientToFullbright", true);
|
||||
@Nullable
|
||||
String materialLibraryOverrideLocation = modelContents.has("materialLibraryOverride") ? JSONUtils.getString(modelContents, "materialLibraryOverride") : null;
|
||||
|
||||
return loadModel(new OBJModel2.ModelSettings(new ResourceLocation(modelLocation), detectCullableFaces, diffuseLighting, flipV, ambientToFullbright, materialLibraryOverrideLocation));
|
||||
}
|
||||
|
||||
public OBJModel2 loadModel(OBJModel2.ModelSettings settings)
|
||||
{
|
||||
return modelCache.computeIfAbsent(settings, (data) -> {
|
||||
IResource resource;
|
||||
try
|
||||
{
|
||||
resource = manager.getResource(settings.modelLocation);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException("Could not find OBJ model", e);
|
||||
}
|
||||
|
||||
try(LineReader rdr = new LineReader(resource))
|
||||
{
|
||||
return new OBJModel2(rdr, settings);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("Could not read OBJ model", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MaterialLibrary2 loadMaterialLibrary(ResourceLocation materialLocation)
|
||||
{
|
||||
return materialCache.computeIfAbsent(materialLocation, (location) -> {
|
||||
IResource resource;
|
||||
try
|
||||
{
|
||||
resource = manager.getResource(location);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException("Could not find OBJ material library", e);
|
||||
}
|
||||
|
||||
try(LineReader rdr = new LineReader(resource))
|
||||
{
|
||||
return new MaterialLibrary2(rdr);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("Could not read OBJ material library", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
@Deprecated // Use the new model loading system and data generators instead.
|
||||
public class OBJModel implements IUnbakedModel
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
@@ -1535,6 +1536,12 @@ public class OBJModel implements IUnbakedModel
|
||||
return new OBJBakedModel(this.model, this.state, this.format, this.textures);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesHandlePerspectives()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(TransformType cameraTransformType)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,671 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.obj;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import joptsimple.internal.Strings;
|
||||
import net.minecraft.client.renderer.model.*;
|
||||
import net.minecraft.client.renderer.texture.ISprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormatElement;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.model.*;
|
||||
import net.minecraftforge.client.model.geometry.IModelGeometryPart;
|
||||
import net.minecraftforge.client.model.geometry.IMultipartModelGeometry;
|
||||
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
|
||||
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
|
||||
import net.minecraftforge.common.model.TRSRTransformation;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Vector2f;
|
||||
import javax.vecmath.Vector3f;
|
||||
import javax.vecmath.Vector4f;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OBJModel2 implements IMultipartModelGeometry<OBJModel2>
|
||||
{
|
||||
private static Vector2f[] DEFAULT_COORDS = {
|
||||
new Vector2f(0, 0),
|
||||
new Vector2f(0, 1),
|
||||
new Vector2f(1, 1),
|
||||
new Vector2f(1, 0),
|
||||
};
|
||||
|
||||
private final Map<String, ModelGroup> parts = Maps.newHashMap();
|
||||
|
||||
private final List<Vector3f> positions = Lists.newArrayList();
|
||||
private final List<Vector2f> texCoords = Lists.newArrayList();
|
||||
private final List<Vector3f> normals = Lists.newArrayList();
|
||||
private final List<Vector4f> colors = Lists.newArrayList();
|
||||
|
||||
public final boolean detectCullableFaces;
|
||||
public final boolean diffuseLighting;
|
||||
public final boolean flipV;
|
||||
public final boolean ambientToFullbright;
|
||||
|
||||
public final ResourceLocation modelLocation;
|
||||
|
||||
@Nullable
|
||||
public final String materialLibraryOverrideLocation;
|
||||
|
||||
|
||||
OBJModel2(LineReader reader, ModelSettings settings) throws IOException
|
||||
{
|
||||
this.modelLocation = settings.modelLocation;
|
||||
this.detectCullableFaces = settings.detectCullableFaces;
|
||||
this.diffuseLighting = settings.diffuseLighting;
|
||||
this.flipV = settings.flipV;
|
||||
this.ambientToFullbright = settings.ambientToFullbright;
|
||||
this.materialLibraryOverrideLocation = settings.materialLibraryOverrideLocation;
|
||||
|
||||
// for relative references to material libraries
|
||||
String modelDomain = modelLocation.getNamespace();
|
||||
String modelPath = modelLocation.getPath();
|
||||
int lastSlash = modelPath.lastIndexOf('/');
|
||||
if (lastSlash >= 0)
|
||||
modelPath = modelPath.substring(0,lastSlash+1); // include the '/'
|
||||
else
|
||||
modelPath = "";
|
||||
|
||||
MaterialLibrary2 mtllib = MaterialLibrary2.EMPTY;
|
||||
MaterialLibrary2.Material currentMat = null;
|
||||
String currentSmoothingGroup = null;
|
||||
ModelGroup currentGroup = null;
|
||||
ModelObject currentObject = null;
|
||||
ModelMesh currentMesh = null;
|
||||
|
||||
boolean objAboveGroup = false;
|
||||
|
||||
if (materialLibraryOverrideLocation != null)
|
||||
{
|
||||
String lib = materialLibraryOverrideLocation;
|
||||
if (lib.contains(":"))
|
||||
mtllib = OBJLoader2.INSTANCE.loadMaterialLibrary(new ResourceLocation(lib));
|
||||
else
|
||||
mtllib = OBJLoader2.INSTANCE.loadMaterialLibrary(new ResourceLocation(modelDomain, modelPath + lib));
|
||||
}
|
||||
|
||||
String[] line;
|
||||
while((line = reader.readAndSplitLine(true)) != null)
|
||||
{
|
||||
switch(line[0])
|
||||
{
|
||||
case "mtllib": // Loads material library
|
||||
{
|
||||
if (materialLibraryOverrideLocation != null)
|
||||
break;
|
||||
|
||||
String lib = line[1];
|
||||
if (lib.contains(":"))
|
||||
mtllib = OBJLoader2.INSTANCE.loadMaterialLibrary(new ResourceLocation(lib));
|
||||
else
|
||||
mtllib = OBJLoader2.INSTANCE.loadMaterialLibrary(new ResourceLocation(modelDomain, modelPath + lib));
|
||||
break;
|
||||
}
|
||||
|
||||
case "usemtl": // Sets the current material (starts new mesh)
|
||||
{
|
||||
String mat = Strings.join(Arrays.copyOfRange(line, 1, line.length), " ");
|
||||
MaterialLibrary2.Material newMat = mtllib.getMaterial(mat);
|
||||
if (!Objects.equals(newMat, currentMat))
|
||||
{
|
||||
currentMat = newMat;
|
||||
if (currentMesh != null && currentMesh.mat == null && currentMesh.faces.size() == 0)
|
||||
{
|
||||
currentMesh.mat = currentMat;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start new mesh
|
||||
currentMesh = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "v": // Vertex
|
||||
positions.add(parseVector4To3(line));
|
||||
break;
|
||||
case "vt": // Vertex texcoord
|
||||
texCoords.add(parseVector2(line));
|
||||
break;
|
||||
case "vn": // Vertex normal
|
||||
normals.add(parseVector3(line));
|
||||
break;
|
||||
case "vc": // Vertex color (non-standard)
|
||||
colors.add(parseVector4(line));
|
||||
break;
|
||||
|
||||
case "f": // Face
|
||||
{
|
||||
if (currentMesh == null)
|
||||
{
|
||||
currentMesh = new ModelMesh(currentMat, currentSmoothingGroup);
|
||||
if (currentObject != null)
|
||||
{
|
||||
currentObject.meshes.add(currentMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentGroup == null)
|
||||
{
|
||||
currentGroup = new ModelGroup("");
|
||||
parts.put("", currentGroup);
|
||||
}
|
||||
currentGroup.meshes.add(currentMesh);
|
||||
}
|
||||
}
|
||||
|
||||
int[][] vertices = new int[line.length-1][];
|
||||
for(int i=0;i<vertices.length;i++)
|
||||
{
|
||||
String vertexData = line[i+1];
|
||||
String[] vertexParts = vertexData.split("/");
|
||||
int[] vertex = Arrays.stream(vertexParts).mapToInt(num -> Strings.isNullOrEmpty(num) ? 0 : Integer.parseInt(num)).toArray();
|
||||
if (vertex[0] < 0) vertex[0] = positions.size() + vertex[0];
|
||||
else vertex[0]--;
|
||||
if (vertex.length > 1)
|
||||
{
|
||||
if (vertex[1] < 0) vertex[1] = texCoords.size() + vertex[1];
|
||||
else vertex[1]--;
|
||||
if (vertex.length > 2)
|
||||
{
|
||||
if (vertex[2] < 0) vertex[2] = normals.size() + vertex[2];
|
||||
else vertex[2]--;
|
||||
if (vertex.length > 3)
|
||||
{
|
||||
if (vertex[3] < 0) vertex[3] = colors.size() + vertex[3];
|
||||
else vertex[3]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
vertices[i] = vertex;
|
||||
}
|
||||
|
||||
currentMesh.faces.add(vertices);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "s": // Smoothing group (starts new mesh)
|
||||
{
|
||||
String smoothingGroup = "off".equals(line[1]) ? null : line[1];
|
||||
if (!Objects.equals(currentSmoothingGroup, smoothingGroup))
|
||||
{
|
||||
currentSmoothingGroup = smoothingGroup;
|
||||
if (currentMesh != null && currentMesh.smoothingGroup == null && currentMesh.faces.size() == 0)
|
||||
{
|
||||
currentMesh.smoothingGroup = currentSmoothingGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start new mesh
|
||||
currentMesh = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "g":
|
||||
{
|
||||
String name = line[1];
|
||||
if (objAboveGroup)
|
||||
{
|
||||
currentObject = new ModelObject(currentGroup.name() + "/" + name);
|
||||
currentGroup.parts.put(name, currentObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentGroup = new ModelGroup(name);
|
||||
parts.put(name, currentGroup);
|
||||
currentObject = null;
|
||||
}
|
||||
// Start new mesh
|
||||
currentMesh = null;
|
||||
break;
|
||||
}
|
||||
|
||||
case "o":
|
||||
{
|
||||
String name = line[1];
|
||||
if (objAboveGroup || currentGroup == null)
|
||||
{
|
||||
objAboveGroup = true;
|
||||
|
||||
currentGroup = new ModelGroup(name);
|
||||
parts.put(name, currentGroup);
|
||||
currentObject = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentObject = new ModelObject(currentGroup.name() + "/" + name);
|
||||
currentGroup.parts.put(name, currentObject);
|
||||
}
|
||||
// Start new mesh
|
||||
currentMesh = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3f parseVector4To3(String[] line)
|
||||
{
|
||||
switch (line.length) {
|
||||
case 1: return new Vector3f(0,0,0);
|
||||
case 2: return new Vector3f(Float.parseFloat(line[1]), 0, 0);
|
||||
case 3: return new Vector3f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), 0);
|
||||
case 4: return new Vector3f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), Float.parseFloat(line[3]));
|
||||
default:
|
||||
{
|
||||
Vector4f vec4 = parseVector4(line);
|
||||
return new Vector3f(
|
||||
vec4.getX() / vec4.getW(),
|
||||
vec4.getY() / vec4.getW(),
|
||||
vec4.getZ() / vec4.getW()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector2f parseVector2(String[] line)
|
||||
{
|
||||
switch (line.length) {
|
||||
case 1: return new Vector2f(0,0);
|
||||
case 2: return new Vector2f(Float.parseFloat(line[1]), 0);
|
||||
default: return new Vector2f(Float.parseFloat(line[1]), Float.parseFloat(line[2]));
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3f parseVector3(String[] line)
|
||||
{
|
||||
switch (line.length) {
|
||||
case 1: return new Vector3f(0,0,0);
|
||||
case 2: return new Vector3f(Float.parseFloat(line[1]), 0, 0);
|
||||
case 3: return new Vector3f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), 0);
|
||||
default: return new Vector3f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), Float.parseFloat(line[3]));
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector4f parseVector4(String[] line)
|
||||
{
|
||||
switch (line.length) {
|
||||
case 1: return new Vector4f(0,0,0,1);
|
||||
case 2: return new Vector4f(Float.parseFloat(line[1]), 0, 0,1);
|
||||
case 3: return new Vector4f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), 0,1);
|
||||
case 4: return new Vector4f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), Float.parseFloat(line[3]),1);
|
||||
default: return new Vector4f(Float.parseFloat(line[1]), Float.parseFloat(line[2]), Float.parseFloat(line[3]), Float.parseFloat(line[4]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends IModelGeometryPart> getParts()
|
||||
{
|
||||
return parts.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<? extends IModelGeometryPart> getPart(String name)
|
||||
{
|
||||
return Optional.ofNullable(parts.get(name));
|
||||
}
|
||||
|
||||
private Pair<BakedQuad,Direction> makeQuad(int[][] indices, int tintIndex, Vector4f colorTint, Vector4f ambientColor, boolean isFullbright, TextureAtlasSprite texture, VertexFormat format, Optional<TRSRTransformation> transform)
|
||||
{
|
||||
boolean needsNormalRecalculation = false;
|
||||
for (int[] ints : indices)
|
||||
{
|
||||
needsNormalRecalculation |= ints.length < 3;
|
||||
}
|
||||
Vector3f faceNormal = new Vector3f(0,0,0);
|
||||
if (needsNormalRecalculation) {
|
||||
Vector3f a = positions.get(indices[0][0]);
|
||||
Vector3f ab = positions.get(indices[1][0]);
|
||||
Vector3f ac = positions.get(indices[2][0]);
|
||||
Vector3f abs = new Vector3f(ab);
|
||||
abs.sub(a);
|
||||
Vector3f acs = new Vector3f(ac);
|
||||
acs.sub(a);
|
||||
abs.cross(abs,acs);
|
||||
abs.normalize();
|
||||
faceNormal = abs;
|
||||
}
|
||||
|
||||
Vector4f[] pos = new Vector4f[4];
|
||||
Vector3f[] norm = new Vector3f[4];
|
||||
|
||||
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format);
|
||||
|
||||
builder.setQuadTint(tintIndex);
|
||||
builder.setTexture(texture);
|
||||
builder.setApplyDiffuseLighting(!isFullbright);
|
||||
|
||||
Vector2f uv2 = new Vector2f(0, 0);
|
||||
if (ambientToFullbright)
|
||||
{
|
||||
int fakeLight = (int) ((ambientColor.getX() + ambientColor.getY() + ambientColor.getZ()) * 15 / 3.0f);
|
||||
uv2 = new Vector2f((fakeLight << 4) / 32767.0f, (fakeLight << 4) / 32767.0f);
|
||||
builder.setApplyDiffuseLighting(fakeLight == 0);
|
||||
}
|
||||
|
||||
TRSRTransformation transformation = transform.orElse(null);
|
||||
boolean hasTransform = transform.isPresent() && !transform.get().isIdentity();
|
||||
|
||||
for(int i=0;i<4;i++)
|
||||
{
|
||||
int[] index = indices[Math.min(i,indices.length-1)];
|
||||
Vector3f pos0 = positions.get(index[0]);
|
||||
Vector4f position = new Vector4f(pos0.x, pos0.y, pos0.z, 1);
|
||||
Vector2f texCoord = index.length >= 2 && texCoords.size() > 0 ? texCoords.get(index[1]) : DEFAULT_COORDS[i];
|
||||
Vector3f norm0 = !needsNormalRecalculation && index.length >= 3 && normals.size() > 0 ? normals.get(index[2]) : faceNormal;
|
||||
Vector3f normal = norm0;
|
||||
Vector4f color = index.length >= 4 && colors.size() > 0 ? colors.get(index[3]) : new Vector4f(1, 1, 1, 1);
|
||||
if (hasTransform)
|
||||
{
|
||||
normal = new Vector3f(norm0);
|
||||
transformation.transformPosition(position);
|
||||
transformation.transformNormal(normal);
|
||||
};
|
||||
Vector4f tintedColor = new Vector4f(
|
||||
color.getX() * colorTint.getX(),
|
||||
color.getY() * colorTint.getY(),
|
||||
color.getZ() * colorTint.getZ(),
|
||||
color.getW() * colorTint.getW());
|
||||
putVertexData(builder, format, position, texCoord, normal, tintedColor, uv2, texture);
|
||||
pos[i] = position;
|
||||
norm[i] = normal;
|
||||
}
|
||||
|
||||
builder.setQuadOrientation(Direction.getFacingFromVector(norm[0].x, norm[0].y,norm[0].z));
|
||||
|
||||
Direction cull = null;
|
||||
if (detectCullableFaces)
|
||||
{
|
||||
if (MathHelper.epsilonEquals(pos[0].getX(), 0) && // vertex.position.x
|
||||
MathHelper.epsilonEquals(pos[1].getX(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[2].getX(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[3].getX(), 0) &&
|
||||
norm[0].getX() < 0) // vertex.normal.x
|
||||
{
|
||||
cull = Direction.WEST;
|
||||
}
|
||||
else if (MathHelper.epsilonEquals(pos[0].getX(), 1) && // vertex.position.x
|
||||
MathHelper.epsilonEquals(pos[1].getX(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[2].getX(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[3].getX(), 1) &&
|
||||
norm[0].getX() > 0) // vertex.normal.x
|
||||
{
|
||||
cull = Direction.EAST;
|
||||
}
|
||||
else if (MathHelper.epsilonEquals(pos[0].getZ(), 0) && // vertex.position.z
|
||||
MathHelper.epsilonEquals(pos[1].getZ(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[2].getZ(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[3].getZ(), 0) &&
|
||||
norm[0].getZ() < 0) // vertex.normal.z
|
||||
{
|
||||
cull = Direction.NORTH; // can never remember
|
||||
}
|
||||
else if (MathHelper.epsilonEquals(pos[0].getZ(), 1) && // vertex.position.z
|
||||
MathHelper.epsilonEquals(pos[1].getZ(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[2].getZ(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[3].getZ(), 1) &&
|
||||
norm[0].getZ() > 0) // vertex.normal.z
|
||||
{
|
||||
cull = Direction.SOUTH;
|
||||
}
|
||||
else if (MathHelper.epsilonEquals(pos[0].getY(), 0) && // vertex.position.y
|
||||
MathHelper.epsilonEquals(pos[1].getY(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[2].getY(), 0) &&
|
||||
MathHelper.epsilonEquals(pos[3].getY(), 0) &&
|
||||
norm[0].getY() < 0) // vertex.normal.z
|
||||
{
|
||||
cull = Direction.DOWN; // can never remember
|
||||
}
|
||||
else if (MathHelper.epsilonEquals(pos[0].getY(), 1) && // vertex.position.y
|
||||
MathHelper.epsilonEquals(pos[1].getY(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[2].getY(), 1) &&
|
||||
MathHelper.epsilonEquals(pos[3].getY(), 1) &&
|
||||
norm[0].getY() > 0) // vertex.normal.y
|
||||
{
|
||||
cull = Direction.UP;
|
||||
}
|
||||
}
|
||||
|
||||
return Pair.of(builder.build(), cull);
|
||||
}
|
||||
|
||||
private void putVertexData(IVertexConsumer consumer, VertexFormat format, Vector4f position0, Vector2f texCoord0, Vector3f normal0, Vector4f color0, Vector2f uv2, TextureAtlasSprite texture)
|
||||
{
|
||||
int elementCount = format.getElementCount();
|
||||
for(int j=0;j<elementCount;j++)
|
||||
{
|
||||
VertexFormatElement e = format.getElement(j);
|
||||
switch(e.getUsage())
|
||||
{
|
||||
case POSITION:
|
||||
consumer.put(j, position0.getX(), position0.getY(), position0.getZ(), position0.getW());
|
||||
break;
|
||||
case COLOR:
|
||||
consumer.put(j, color0.getX(), color0.getY(), color0.getZ(), color0.getW());
|
||||
break;
|
||||
case UV:
|
||||
switch (e.getIndex())
|
||||
{
|
||||
case 0:
|
||||
consumer.put(j,
|
||||
texture.getInterpolatedU(texCoord0.getX() * 16),
|
||||
texture.getInterpolatedV((flipV ? (1 - texCoord0.getY()) : texCoord0.getY()) * 16)
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
consumer.put(j, uv2.x, uv2.y);
|
||||
break;
|
||||
default:
|
||||
consumer.put(j);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NORMAL:
|
||||
consumer.put(j, normal0.getX(), normal0.getY(), normal0.getZ());
|
||||
break;
|
||||
default:
|
||||
consumer.put(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModelSettings
|
||||
{
|
||||
@Nonnull
|
||||
public final ResourceLocation modelLocation;
|
||||
public final boolean detectCullableFaces;
|
||||
public final boolean diffuseLighting;
|
||||
public final boolean flipV;
|
||||
public final boolean ambientToFullbright;
|
||||
@Nullable
|
||||
public final String materialLibraryOverrideLocation;
|
||||
|
||||
public ModelSettings(@Nonnull ResourceLocation modelLocation, boolean detectCullableFaces, boolean diffuseLighting, boolean flipV, boolean ambientToFullbright,
|
||||
@Nullable String materialLibraryOverrideLocation)
|
||||
{
|
||||
this.modelLocation = modelLocation;
|
||||
this.detectCullableFaces = detectCullableFaces;
|
||||
this.diffuseLighting = diffuseLighting;
|
||||
this.flipV = flipV;
|
||||
this.ambientToFullbright = ambientToFullbright;
|
||||
this.materialLibraryOverrideLocation = materialLibraryOverrideLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ModelSettings that = (ModelSettings) o;
|
||||
return equals(that);
|
||||
}
|
||||
|
||||
public boolean equals(@Nonnull ModelSettings that)
|
||||
{
|
||||
return detectCullableFaces == that.detectCullableFaces &&
|
||||
diffuseLighting == that.diffuseLighting &&
|
||||
flipV == that.flipV &&
|
||||
ambientToFullbright == that.ambientToFullbright &&
|
||||
modelLocation.equals(that.modelLocation) &&
|
||||
Objects.equals(materialLibraryOverrideLocation, that.materialLibraryOverrideLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(modelLocation, detectCullableFaces, diffuseLighting, flipV, ambientToFullbright, materialLibraryOverrideLocation);
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelObject implements IModelGeometryPart
|
||||
{
|
||||
public final String name;
|
||||
|
||||
List<ModelMesh> meshes = Lists.newArrayList();
|
||||
|
||||
ModelObject(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
for(ModelMesh mesh : meshes)
|
||||
{
|
||||
MaterialLibrary2.Material mat = mesh.mat;
|
||||
if (mat == null)
|
||||
continue;
|
||||
TextureAtlasSprite texture = spriteGetter.apply(ModelLoaderRegistry2.resolveTexture(mat.diffuseColorMap, owner));
|
||||
int tintIndex = mat.diffuseTintIndex;
|
||||
Vector4f colorTint = mat.diffuseColor;
|
||||
boolean isFullbright = ambientToFullbright && mesh.isFullbright();
|
||||
|
||||
if (format.equals(DefaultVertexFormats.ITEM) && isFullbright)
|
||||
{
|
||||
format = DefaultVertexFormats.BLOCK;
|
||||
}
|
||||
|
||||
for (int[][] face : mesh.faces)
|
||||
{
|
||||
Pair<BakedQuad, Direction> quad = makeQuad(face, tintIndex, colorTint, mat.ambientColor, isFullbright, texture, format, sprite.getState().apply(Optional.empty()));
|
||||
if (quad.getRight() == null)
|
||||
modelBuilder.addGeneralQuad(quad.getLeft());
|
||||
else
|
||||
modelBuilder.addFaceQuad(quad.getRight(), quad.getLeft());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
return meshes.stream().map(mesh -> ModelLoaderRegistry2.resolveTexture(mesh.mat.diffuseColorMap, owner)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public boolean hasAnyFullBright()
|
||||
{
|
||||
return meshes.stream().anyMatch(ModelMesh::isFullbright);
|
||||
}
|
||||
}
|
||||
|
||||
public class ModelGroup extends ModelObject
|
||||
{
|
||||
final Map<String, ModelObject> parts = Maps.newHashMap();
|
||||
|
||||
ModelGroup(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
public Collection<? extends IModelGeometryPart> getParts()
|
||||
{
|
||||
return parts.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, ModelBakery bakery, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, ISprite sprite, VertexFormat format)
|
||||
{
|
||||
super.addQuads(owner, modelBuilder, bakery, spriteGetter, sprite, format);
|
||||
|
||||
getParts().stream().filter(part -> owner.getPartVisibility(part))
|
||||
.forEach(part -> part.addQuads(owner, modelBuilder, bakery, spriteGetter, sprite, format));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getTextureDependencies(IModelConfiguration owner, Function<ResourceLocation, IUnbakedModel> modelGetter, Set<String> missingTextureErrors)
|
||||
{
|
||||
Set<ResourceLocation> combined = Sets.newHashSet();
|
||||
combined.addAll(super.getTextureDependencies(owner, modelGetter, missingTextureErrors));
|
||||
for (IModelGeometryPart part : getParts())
|
||||
combined.addAll(part.getTextureDependencies(owner, modelGetter, missingTextureErrors));
|
||||
return combined;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyFullBright()
|
||||
{
|
||||
return super.hasAnyFullBright() || parts.values().stream().anyMatch(ModelObject::hasAnyFullBright);
|
||||
}
|
||||
}
|
||||
|
||||
private class ModelMesh
|
||||
{
|
||||
@Nullable
|
||||
public MaterialLibrary2.Material mat;
|
||||
@Nullable
|
||||
public String smoothingGroup;
|
||||
public final List<int[][]> faces = Lists.newArrayList();
|
||||
|
||||
public ModelMesh(@Nullable MaterialLibrary2.Material currentMat, @Nullable String currentSmoothingGroup)
|
||||
{
|
||||
this.mat = currentMat;
|
||||
this.smoothingGroup = currentSmoothingGroup;
|
||||
}
|
||||
|
||||
public boolean isFullbright()
|
||||
{
|
||||
return mat != null && mat.ambientColor.epsilonEquals(new Vector4f(1,1,1,1), 1/256f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
* 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.common.data;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.common.util;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Proxy object for a value that is calculated on first access
|
||||
* @param <T> The type of the value
|
||||
*/
|
||||
public interface Lazy<T> extends Supplier<T>
|
||||
{
|
||||
/**
|
||||
* Constructs a lazy-initialized object
|
||||
* @param supplier The supplier for the value, to be called the first time the value is needed.
|
||||
*/
|
||||
static <T> Lazy<T> of(@Nonnull Supplier<T> supplier)
|
||||
{
|
||||
return new Lazy.Fast<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a thread-safe lazy-initialized object
|
||||
* @param supplier The supplier for the value, to be called the first time the value is needed.
|
||||
*/
|
||||
static <T> Lazy<T> concurrentOf(@Nonnull Supplier<T> supplier)
|
||||
{
|
||||
return new Lazy.Concurrent<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-thread-safe implementation.
|
||||
*/
|
||||
final class Fast<T> implements Lazy<T>
|
||||
{
|
||||
private Supplier<T> supplier;
|
||||
private T instance;
|
||||
|
||||
private Fast(Supplier<T> supplier)
|
||||
{
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final T get()
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
instance = supplier.get();
|
||||
supplier = null;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-safe implementation.
|
||||
*/
|
||||
final class Concurrent<T> implements Lazy<T>
|
||||
{
|
||||
private static final Object lock = new Object();
|
||||
private volatile Supplier<T> supplier;
|
||||
private volatile T instance;
|
||||
|
||||
private Concurrent(Supplier<T> supplier)
|
||||
{
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final T get()
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
instance = supplier.get();
|
||||
supplier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.common.util;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Proxy object for a value that is calculated on first access.
|
||||
* Same as {@link Lazy}, but with a nonnull contract.
|
||||
* @param <T> The type of the value
|
||||
*/
|
||||
public interface NonNullLazy<T> extends NonNullSupplier<T>
|
||||
{
|
||||
/**
|
||||
* Constructs a lazy-initialized object
|
||||
* @param supplier The supplier for the value, to be called the first time the value is needed.
|
||||
*/
|
||||
static <T> NonNullLazy<T> of(@Nonnull NonNullSupplier<T> supplier)
|
||||
{
|
||||
return new NonNullLazy.Fast<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a thread-safe lazy-initialized object
|
||||
* @param supplier The supplier for the value, to be called the first time the value is needed.
|
||||
*/
|
||||
static <T> NonNullLazy<T> concurrentOf(@Nonnull NonNullSupplier<T> supplier)
|
||||
{
|
||||
return new NonNullLazy.Concurrent<>(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-thread-safe implementation.
|
||||
*/
|
||||
final class Fast<T> implements NonNullLazy<T>
|
||||
{
|
||||
private NonNullSupplier<T> supplier;
|
||||
private T instance;
|
||||
|
||||
private Fast(NonNullSupplier<T> supplier)
|
||||
{
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final T get()
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
instance = supplier.get();
|
||||
supplier = null;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-safe implementation.
|
||||
*/
|
||||
final class Concurrent<T> implements NonNullLazy<T>
|
||||
{
|
||||
private static final Object lock = new Object();
|
||||
private volatile NonNullSupplier<T> supplier;
|
||||
private volatile T instance;
|
||||
|
||||
private Concurrent(NonNullSupplier<T> supplier)
|
||||
{
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final T get()
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
synchronized (lock)
|
||||
{
|
||||
if (supplier != null)
|
||||
{
|
||||
instance = supplier.get();
|
||||
supplier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry;
|
||||
import net.minecraftforge.client.model.ModelLoaderRegistry2;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
@@ -99,6 +100,7 @@ public class ClientModLoader
|
||||
mcResourceManager.addReloadListener(ClientModLoader::onreload);
|
||||
mcResourceManager.addReloadListener(BrandingControl.resourceManagerReloadListener());
|
||||
ModelLoaderRegistry.init();
|
||||
ModelLoaderRegistry2.init();
|
||||
}
|
||||
|
||||
private static CompletableFuture<Void> onreload(final IFutureReloadListener.IStage stage, final IResourceManager resourceManager, final IProfiler prepareProfiler, final IProfiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
|
||||
@@ -125,6 +127,7 @@ public class ClientModLoader
|
||||
|
||||
private static void postSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
|
||||
RenderingRegistry.loadEntityRenderers(mc.getRenderManager());
|
||||
ModelLoaderRegistry2.initComplete();
|
||||
}
|
||||
|
||||
private static void preSidedRunnable(Consumer<Supplier<Event>> perModContainerEventProcessor) {
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.fml.packs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2019.
|
||||
*
|
||||
* 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.fml.packs;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
Reference in new issue
Block a user