2014-12-28 04:10:54 +00:00
package net.minecraftforge.client.model ;
import java.io.IOException ;
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Map ;
import java.util.Map.Entry ;
import java.util.Set ;
2015-02-04 06:45:23 +00:00
import net.minecraft.block.Block ;
2014-12-28 04:10:54 +00:00
import net.minecraft.block.state.IBlockState ;
import net.minecraft.client.renderer.BlockModelShapes ;
2015-02-04 06:45:23 +00:00
import net.minecraft.client.renderer.ItemMeshDefinition ;
import net.minecraft.client.renderer.ItemModelMesher ;
2014-12-28 04:10:54 +00:00
import net.minecraft.client.renderer.block.model.ItemCameraTransforms ;
import net.minecraft.client.renderer.block.model.ItemModelGenerator ;
import net.minecraft.client.renderer.block.model.ModelBlock ;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition ;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition.MissingVariantException ;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition.Variant ;
import net.minecraft.client.renderer.block.model.ModelBlockDefinition.Variants ;
2015-02-04 06:45:23 +00:00
import net.minecraft.client.renderer.block.statemap.IStateMapper ;
2014-12-28 04:10:54 +00:00
import net.minecraft.client.renderer.texture.IIconCreator ;
import net.minecraft.client.renderer.texture.TextureAtlasSprite ;
import net.minecraft.client.renderer.texture.TextureMap ;
import net.minecraft.client.renderer.vertex.VertexFormat ;
import net.minecraft.client.resources.IResourceManager ;
import net.minecraft.client.resources.model.BuiltInModel ;
import net.minecraft.client.resources.model.ModelBakery ;
import net.minecraft.client.resources.model.ModelResourceLocation ;
import net.minecraft.client.resources.model.ModelRotation ;
import net.minecraft.client.resources.model.WeightedBakedModel ;
import net.minecraft.item.Item ;
import net.minecraft.util.IRegistry ;
import net.minecraft.util.ResourceLocation ;
import net.minecraftforge.fml.common.FMLLog ;
import net.minecraftforge.fml.common.registry.GameData ;
2015-02-04 06:45:23 +00:00
import net.minecraftforge.fml.common.registry.RegistryDelegate ;
2014-12-28 04:10:54 +00:00
2015-02-04 06:45:23 +00:00
import org.apache.commons.lang3.tuple.Pair ;
2014-12-28 04:10:54 +00:00
import org.apache.logging.log4j.Level ;
import com.google.common.base.Function ;
import com.google.common.base.Functions ;
import com.google.common.collect.ImmutableList ;
import com.google.common.collect.ImmutableMap ;
import com.google.common.collect.ImmutableSet ;
import com.google.common.collect.Lists ;
2015-02-04 06:45:23 +00:00
import com.google.common.collect.Maps ;
import com.google.common.collect.Sets ;
2014-12-28 04:10:54 +00:00
public class ModelLoader extends ModelBakery
{
private final Map < ModelResourceLocation , IModel > stateModels = new HashMap < ModelResourceLocation , IModel > ( ) ;
private final Set < ResourceLocation > resolveTextures = new HashSet < ResourceLocation > ( ) ;
private final Set < ResourceLocation > textures = new HashSet < ResourceLocation > ( ) ;
private final Set < ResourceLocation > loadingModels = new HashSet < ResourceLocation > ( ) ;
2015-02-04 06:45:23 +00:00
private final Set < ModelResourceLocation > missingVariants = Sets . newHashSet ( ) ;
private boolean isLoading = false ;
public boolean isLoading ( )
{
return isLoading ;
}
2014-12-28 04:10:54 +00:00
public ModelLoader ( IResourceManager manager , TextureMap map , BlockModelShapes shapes )
{
super ( manager , map , shapes ) ;
VanillaLoader . instance . setLoader ( this ) ;
ModelLoaderRegistry . clearModelCache ( ) ;
}
@Override
public IRegistry setupModelRegistry ( )
{
2015-02-04 06:45:23 +00:00
isLoading = true ;
2014-12-28 04:10:54 +00:00
loadBlocks ( ) ;
loadItems ( ) ;
stateModels . put ( MODEL_MISSING , getModel ( new ResourceLocation ( MODEL_MISSING . getResourceDomain ( ) , MODEL_MISSING . getResourcePath ( ) ) ) ) ;
textures . remove ( TextureMap . LOCATION_MISSING_TEXTURE ) ;
textures . addAll ( LOCATIONS_BUILTIN_TEXTURES ) ;
textureMap . loadSprites ( resourceManager , new IIconCreator ( )
{
public void registerSprites ( TextureMap map )
{
for ( ResourceLocation t : textures )
{
sprites . put ( t , map . registerSprite ( t ) ) ;
}
}
} ) ;
sprites . put ( new ResourceLocation ( " missingno " ) , textureMap . getMissingSprite ( ) ) ;
Function < ResourceLocation , TextureAtlasSprite > textureGetter = Functions . forMap ( sprites , textureMap . getMissingSprite ( ) ) ;
for ( Entry < ModelResourceLocation , IModel > e : stateModels . entrySet ( ) )
{
bakedRegistry . putObject ( e . getKey ( ) , e . getValue ( ) . bake ( e . getValue ( ) . getDefaultState ( ) , Attributes . DEFAULT_BAKED_FORMAT , textureGetter ) ) ;
}
return bakedRegistry ;
}
private void loadBlocks ( )
{
Map < IBlockState , ModelResourceLocation > stateMap = blockModelShapes . getBlockStateMapper ( ) . putAllStateModelLocations ( ) ;
2015-02-02 21:06:57 +00:00
Collection < ModelResourceLocation > variants = Lists . newArrayList ( stateMap . values ( ) ) ;
variants . add ( new ModelResourceLocation ( " minecraft:item_frame " , " normal " ) ) ; //Vanilla special cases item_frames so must we
variants . add ( new ModelResourceLocation ( " minecraft:item_frame " , " map " ) ) ;
loadVariants ( variants ) ;
2014-12-28 04:10:54 +00:00
}
@Override
protected void registerVariant ( ModelBlockDefinition definition , ModelResourceLocation location )
{
Variants variants = null ;
try
{
variants = definition . getVariants ( location . getVariant ( ) ) ;
}
2015-02-04 06:45:23 +00:00
catch ( MissingVariantException e )
2014-12-28 04:10:54 +00:00
{
2015-02-04 06:45:23 +00:00
missingVariants . add ( location ) ;
2014-12-28 04:10:54 +00:00
}
2015-02-04 06:45:23 +00:00
if ( variants ! = null & & ! variants . getVariants ( ) . isEmpty ( ) )
2014-12-28 04:10:54 +00:00
{
try
{
stateModels . put ( location , new WeightedRandomModel ( variants ) ) ;
}
catch ( Throwable e )
{
throw new RuntimeException ( e ) ;
}
}
}
private void loadItems ( )
{
registerVariantNames ( ) ;
for ( Item item : GameData . getItemRegistry ( ) . typeSafeIterable ( ) )
{
for ( String s : ( List < String > ) getVariantNames ( item ) )
{
ResourceLocation file = getItemLocation ( s ) ;
ModelResourceLocation memory = new ModelResourceLocation ( s , " inventory " ) ;
resolveTextures . add ( ModelLoaderRegistry . getActualLocation ( file ) ) ;
IModel model = getModel ( file ) ;
2015-02-04 06:45:23 +00:00
if ( model = = null | | model = = getMissingModel ( ) )
{
missingVariants . add ( memory ) ;
}
else stateModels . put ( memory , model ) ;
2014-12-28 04:10:54 +00:00
}
}
}
public IModel getModel ( ResourceLocation location )
{
if ( ! ModelLoaderRegistry . loaded ( location ) ) loadAnyModel ( location ) ;
return ModelLoaderRegistry . getModel ( location ) ;
}
@Override
protected ResourceLocation getModelLocation ( ResourceLocation model )
{
return new ResourceLocation ( model . getResourceDomain ( ) , model . getResourcePath ( ) + " .json " ) ;
}
private void loadAnyModel ( ResourceLocation location )
{
if ( loadingModels . contains ( location ) )
{
throw new IllegalStateException ( " circular model dependencies involving model " + location ) ;
}
loadingModels . add ( location ) ;
IModel model = ModelLoaderRegistry . getModel ( location ) ;
for ( ResourceLocation dep : model . getDependencies ( ) )
{
getModel ( dep ) ;
}
textures . addAll ( model . getTextures ( ) ) ;
loadingModels . remove ( location ) ;
}
private class VanillaModelWrapper implements IModel
{
private final ResourceLocation location ;
private final ModelBlock model ;
public VanillaModelWrapper ( ResourceLocation location , ModelBlock model )
{
this . location = location ;
this . model = model ;
}
public Collection < ResourceLocation > getDependencies ( )
{
if ( model . getParentLocation ( ) = = null | | model . getParentLocation ( ) . getResourcePath ( ) . startsWith ( " builtin/ " ) ) return Collections . emptyList ( ) ;
return Collections . singletonList ( model . getParentLocation ( ) ) ;
}
public Collection < ResourceLocation > getTextures ( )
{
// setting parent here to make textures resolve properly
if ( model . getParentLocation ( ) ! = null )
{
IModel parent = getModel ( model . getParentLocation ( ) ) ;
if ( parent instanceof VanillaModelWrapper )
{
model . parent = ( ( VanillaModelWrapper ) parent ) . model ;
}
else
{
throw new IllegalStateException ( " vanilla model " + model + " can't have non-vanilla parent " ) ;
}
}
if ( ! resolveTextures . contains ( location ) ) return Collections . emptyList ( ) ;
ImmutableSet . Builder < ResourceLocation > builder = ImmutableSet . builder ( ) ;
builder . add ( new ResourceLocation ( model . resolveTextureName ( " particle " ) ) ) ;
if ( hasItemModel ( model ) )
{
for ( String s : ( List < String > ) ItemModelGenerator . LAYERS )
{
String r = model . resolveTextureName ( s ) ;
ResourceLocation loc = new ResourceLocation ( r ) ;
if ( ! r . equals ( s ) )
{
builder . add ( loc ) ;
}
// mojang hardcode
if ( model . getRootModel ( ) = = MODEL_COMPASS & & ! loc . equals ( TextureMap . LOCATION_MISSING_TEXTURE ) )
{
TextureAtlasSprite . setLocationNameCompass ( loc . toString ( ) ) ;
}
else if ( model . getRootModel ( ) = = MODEL_CLOCK & & ! loc . equals ( TextureMap . LOCATION_MISSING_TEXTURE ) )
{
TextureAtlasSprite . setLocationNameClock ( loc . toString ( ) ) ;
}
}
}
if ( location . getResourcePath ( ) . startsWith ( " models/block/ " ) | | ! ModelLoader . this . isBuiltinModel ( model . getRootModel ( ) ) )
{
builder . addAll ( ModelLoader . this . getTextureLocations ( model ) ) ;
}
return builder . build ( ) ;
}
public IFlexibleBakedModel bake ( IModelState state , VertexFormat format , Function < ResourceLocation , TextureAtlasSprite > bakedTextureGetter )
{
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 ) ;
}
ModelBlock model = this . model ;
if ( hasItemModel ( model ) ) model = makeItemModel ( model ) ;
if ( isCustomRenderer ( model ) ) return new IFlexibleBakedModel . Wrapper ( new BuiltInModel ( new ItemCameraTransforms ( model . getThirdPersonTransform ( ) , model . getFirstPersonTransform ( ) , model . getHeadTransform ( ) , model . getInGuiTransform ( ) ) ) , Attributes . DEFAULT_BAKED_FORMAT ) ;
2015-02-03 07:33:32 +00:00
return new IFlexibleBakedModel . Wrapper ( bakeModel ( model , state . apply ( this ) , state instanceof UVLock ) , Attributes . DEFAULT_BAKED_FORMAT ) ;
2014-12-28 04:10:54 +00:00
}
public IModelState getDefaultState ( )
{
return ModelRotation . X0_Y0 ;
}
}
2015-02-03 07:33:32 +00:00
public static class UVLock implements IModelState
{
private final IModelState state ;
public UVLock ( IModelState state )
{
this . state = state ;
}
public TRSRTransformation apply ( IModelPart part )
{
return state . apply ( part ) ;
}
}
2014-12-28 04:10:54 +00:00
// Weighted models can contain multiple copies of 1 model with different rotations - this is to make it work with IModelState (different copies will be different objects).
private static class WeightedPartWrapper implements IModel
{
private final IModel model ;
public WeightedPartWrapper ( IModel model )
{
this . model = model ;
}
public Collection < ResourceLocation > getDependencies ( )
{
return model . getDependencies ( ) ;
}
public Collection < ResourceLocation > getTextures ( )
{
return model . getTextures ( ) ;
}
public IFlexibleBakedModel bake ( IModelState state , VertexFormat format , Function < ResourceLocation , TextureAtlasSprite > bakedTextureGetter )
{
return model . bake ( state , format , bakedTextureGetter ) ;
}
public IModelState getDefaultState ( )
{
return model . getDefaultState ( ) ;
}
}
private class WeightedRandomModel implements IModel
{
private final List < Variant > variants ;
private final List < ResourceLocation > locations = new ArrayList < ResourceLocation > ( ) ;
private final List < IModel > models = new ArrayList < IModel > ( ) ;
private final IModelState defaultState ;
public WeightedRandomModel ( Variants variants )
{
this . variants = variants . getVariants ( ) ;
ImmutableMap . Builder < IModelPart , TRSRTransformation > builder = ImmutableMap . builder ( ) ;
for ( Variant v : ( List < Variant > ) variants . getVariants ( ) )
{
ResourceLocation loc = v . getModelLocation ( ) ;
resolveTextures . add ( ModelLoaderRegistry . getActualLocation ( loc ) ) ;
locations . add ( loc ) ;
IModel model = new WeightedPartWrapper ( getModel ( loc ) ) ;
models . add ( model ) ;
builder . put ( model , new TRSRTransformation ( v . getRotation ( ) ) ) ;
}
defaultState = new MapModelState ( builder . build ( ) ) ;
}
public Collection < ResourceLocation > getDependencies ( )
{
return ImmutableList . copyOf ( locations ) ;
}
public Collection < ResourceLocation > getTextures ( )
{
/ * ImmutableSet . Builder < ResourceLocation > builder = ImmutableSet . builder ( ) ;
for ( ResourceLocation loc : locations )
{
builder . addAll ( getModel ( loc ) . getTextures ( ) ) ;
}
return builder . build ( ) ; * /
return Collections . emptyList ( ) ;
}
2015-02-03 07:33:32 +00:00
private IModelState addUV ( boolean uv , IModelState state )
{
if ( uv ) return new UVLock ( state ) ;
return state ;
}
2014-12-28 04:10:54 +00:00
public IFlexibleBakedModel bake ( IModelState state , VertexFormat format , Function < ResourceLocation , TextureAtlasSprite > bakedTextureGetter )
{
if ( ! Attributes . moreSpecific ( format , Attributes . DEFAULT_BAKED_FORMAT ) )
{
throw new IllegalArgumentException ( " can't bake vanilla weighted models to the format that doesn't fit into the default one: " + format ) ;
}
if ( variants . size ( ) = = 1 )
{
Variant v = variants . get ( 0 ) ;
IModel model = models . get ( 0 ) ;
2015-02-03 07:33:32 +00:00
return model . bake ( addUV ( v . isUvLocked ( ) , state . apply ( model ) ) , format , bakedTextureGetter ) ;
2014-12-28 04:10:54 +00:00
}
WeightedBakedModel . Builder builder = new WeightedBakedModel . Builder ( ) ;
for ( int i = 0 ; i < variants . size ( ) ; i + + )
{
IModel model = models . get ( i ) ;
2015-02-03 07:33:32 +00:00
Variant v = variants . get ( i ) ;
builder . add ( model . bake ( addUV ( v . isUvLocked ( ) , state . apply ( model ) ) , format , bakedTextureGetter ) , variants . get ( i ) . getWeight ( ) ) ;
2014-12-28 04:10:54 +00:00
}
return new IFlexibleBakedModel . Wrapper ( builder . build ( ) , Attributes . DEFAULT_BAKED_FORMAT ) ;
}
public IModelState getDefaultState ( )
{
return defaultState ;
}
}
private boolean isBuiltinModel ( ModelBlock model )
{
return model = = MODEL_GENERATED | | model = = MODEL_COMPASS | | model = = MODEL_CLOCK | | model = = MODEL_ENTITY ;
}
public IModel getMissingModel ( )
{
return getModel ( new ResourceLocation ( MODEL_MISSING . getResourceDomain ( ) , MODEL_MISSING . getResourcePath ( ) ) ) ;
}
static enum VanillaLoader implements ICustomModelLoader
{
instance ;
private ModelLoader loader ;
void setLoader ( ModelLoader loader )
{
this . loader = loader ;
}
ModelLoader getLoader ( )
{
return loader ;
}
public void onResourceManagerReload ( IResourceManager resourceManager )
{
// do nothing, cause loader will store the reference to the resourceManager
}
public boolean accepts ( ResourceLocation modelLocation )
{
return true ;
}
public IModel loadModel ( ResourceLocation modelLocation )
{
try
{
return loader . new VanillaModelWrapper ( modelLocation , loader . loadModel ( modelLocation ) ) ;
}
catch ( IOException e )
{
2015-02-04 06:45:23 +00:00
if ( loader . isLoading )
{
// holding error until onPostBakeEvent
}
else FMLLog . log ( Level . ERROR , e , " Exception loading model %s with vanilla loader, skipping " , modelLocation ) ;
2014-12-28 04:10:54 +00:00
return loader . getMissingModel ( ) ;
}
}
}
2015-02-04 06:45:23 +00:00
public void onPostBakeEvent ( IRegistry modelRegistry )
{
for ( ModelResourceLocation missing : missingVariants )
{
if ( modelRegistry . getObject ( missing ) = = null )
{
FMLLog . severe ( " Model definition for location %s not found " , missing ) ;
}
}
isLoading = false ;
}
private static final Map < RegistryDelegate < Block > , IStateMapper > customStateMappers = Maps . newHashMap ( ) ;
public static void setCustomStateMapper ( Block block , IStateMapper mapper )
{
customStateMappers . put ( block . delegate , mapper ) ;
}
public static void onRegisterAllBlocks ( BlockModelShapes shapes )
{
for ( Entry < RegistryDelegate < Block > , IStateMapper > e : customStateMappers . entrySet ( ) )
{
shapes . registerBlockWithStateMapper ( e . getKey ( ) . get ( ) , e . getValue ( ) ) ;
}
}
private static final Map < RegistryDelegate < Item > , ItemMeshDefinition > customMeshDefinitions = com . google . common . collect . Maps . newHashMap ( ) ;
private static final Map < RegistryDelegate < Item > , Pair < Integer , ModelResourceLocation > > customModels = com . google . common . collect . Maps . newHashMap ( ) ;
public static void setCustomModelResourceLocation ( Item item , int metadata , ModelResourceLocation model )
{
customModels . put ( item . delegate , Pair . of ( metadata , model ) ) ;
}
public static void setCustomMeshDefinition ( Item item , ItemMeshDefinition meshDefinition )
{
customMeshDefinitions . put ( item . delegate , meshDefinition ) ;
}
public static void onRegisterItems ( ItemModelMesher mesher )
{
for ( Map . Entry < RegistryDelegate < Item > , ItemMeshDefinition > e : customMeshDefinitions . entrySet ( ) )
{
mesher . register ( e . getKey ( ) . get ( ) , e . getValue ( ) ) ;
}
for ( Entry < RegistryDelegate < Item > , Pair < Integer , ModelResourceLocation > > e : customModels . entrySet ( ) )
{
mesher . register ( e . getKey ( ) . get ( ) , e . getValue ( ) . getLeft ( ) , e . getValue ( ) . getRight ( ) ) ;
}
}
2014-12-28 04:10:54 +00:00
}