From 84cfd4c9fcc943f381e580cbb1363b4f65a26112 Mon Sep 17 00:00:00 2001 From: elix-x Date: Tue, 28 Jun 2016 15:24:38 +0200 Subject: [PATCH] Created and tested UVLLoader Created and tested UVLLoader. Yes, it may not conform to code standarts, but this will be fixed later. --- .../client/render/UVLightmapJsonTest.java | 392 ++++++++++++++++++ .../blockstates/uvlblock.json | 5 + .../models/block/uvlblock.model | Bin 0 -> 2453 bytes .../models/block/uvlblock.uvl.json | 86 ++++ .../models/item/uvlblock.json | 3 + .../textures/blocks/BlockControllerLights.png | Bin 0 -> 1410 bytes .../blocks/BlockControllerLights.png.mcmeta | 19 + 7 files changed, 505 insertions(+) create mode 100644 src/test/java/appeng/client/render/UVLightmapJsonTest.java create mode 100644 src/test/resources/assets/uvlightmapjsontest/blockstates/uvlblock.json create mode 100644 src/test/resources/assets/uvlightmapjsontest/models/block/uvlblock.model create mode 100644 src/test/resources/assets/uvlightmapjsontest/models/block/uvlblock.uvl.json create mode 100644 src/test/resources/assets/uvlightmapjsontest/models/item/uvlblock.json create mode 100644 src/test/resources/assets/uvlightmapjsontest/textures/blocks/BlockControllerLights.png create mode 100644 src/test/resources/assets/uvlightmapjsontest/textures/blocks/BlockControllerLights.png.mcmeta diff --git a/src/test/java/appeng/client/render/UVLightmapJsonTest.java b/src/test/java/appeng/client/render/UVLightmapJsonTest.java new file mode 100644 index 00000000..cf624949 --- /dev/null +++ b/src/test/java/appeng/client/render/UVLightmapJsonTest.java @@ -0,0 +1,392 @@ +package appeng.client.render; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.util.vector.Vector3f; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.BlockFaceUV; +import net.minecraft.client.renderer.block.model.BlockPart; +import net.minecraft.client.renderer.block.model.BlockPartFace; +import net.minecraft.client.renderer.block.model.BlockPartRotation; +import net.minecraft.client.renderer.block.model.FaceBakery; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.block.model.ItemCameraTransforms; +import net.minecraft.client.renderer.block.model.ItemOverride; +import net.minecraft.client.renderer.block.model.ItemTransformVec3f; +import net.minecraft.client.renderer.block.model.ModelBakery; +import net.minecraft.client.renderer.block.model.ModelBlock; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +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.resources.IResource; +import net.minecraft.client.resources.IResourceManager; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.JsonUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.model.ICustomModelLoader; +import net.minecraftforge.client.model.IModel; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.client.model.animation.ModelBlockAnimation; +import net.minecraftforge.client.model.pipeline.LightUtil; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; +import net.minecraftforge.client.model.pipeline.VertexLighterFlat; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.model.IModelState; +import net.minecraftforge.common.model.ITransformation; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.relauncher.ReflectionHelper; + +@Mod(modid = "UVLightmapJsonTest", name = "UVLightmapJsonTest", version ="0.0.0") +public class UVLightmapJsonTest +{ + + private static final ModelResourceLocation uvlblockModel = new ModelResourceLocation( new ResourceLocation( "UVLightmapJsonTest", "uvlblock" ), "normal" ); + + public static Block uvlblock; + public static Item uvlblockItem; + + @EventHandler + public void preInit( FMLPreInitializationEvent event ) + { + MinecraftForge.EVENT_BUS.register( this ); + ModelLoaderRegistry.registerLoader( UVLModelLoader.INSTANCE ); + + GameRegistry.register( uvlblock = new Block( Material.IRON ){ + + final AxisAlignedBB box = new AxisAlignedBB( 0.25, 0, 7 / 16d, 0.75, 1, 9 / 16d ); + + public boolean isFullBlock(IBlockState state) + { + return false; + } + + public boolean isOpaqueCube(IBlockState state) + { + return false; + } + + public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) + { + return box; + } + + public BlockRenderLayer getBlockLayer() + { + return BlockRenderLayer.CUTOUT; + } + + }.setLightLevel( 0.2f ).setCreativeTab( CreativeTabs.DECORATIONS ).setRegistryName( new ResourceLocation( "UVLightmapJsonTest", "uvlblock" ) ) ); + GameRegistry.register( uvlblockItem = new ItemBlock( uvlblock ).setRegistryName( new ResourceLocation( "UVLightmapJsonTest", "uvlblock" ) ) ); + + ModelBakery.registerItemVariants( uvlblockItem, uvlblockModel ); + + } + + @EventHandler + public void init( FMLInitializationEvent event ) + { + Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(uvlblockItem, 0, uvlblockModel); + } + + @SubscribeEvent + public void modelsBake( ModelBakeEvent event ) + { + UVLModelLoader.INSTANCE.loader = event.getModelLoader(); + } + + public enum UVLModelLoader implements ICustomModelLoader + { + INSTANCE; + + static final Constructor vanillaModelWrapper; + static final Field faceBakery; + + static + { + try + { + Field modifiers = Field.class.getDeclaredField( "modifiers" ); + modifiers.setAccessible( true ); + + faceBakery = ReflectionHelper.findField( ModelBakery.class, "faceBakery" ); + modifiers.set( faceBakery, faceBakery.getModifiers() & ( ~Modifier.FINAL ) ); + + Class clas = Class.forName( ModelLoader.class.getName() + "$VanillaModelWrapper" ); + vanillaModelWrapper = clas.getDeclaredConstructor( ModelLoader.class, ResourceLocation.class, ModelBlock.class, boolean.class, ModelBlockAnimation.class); + vanillaModelWrapper.setAccessible( true ); + } + catch ( Exception e ) + { + throw Throwables.propagate( e ); + } + } + + private static Object deserializer(Class clas) + { + try + { + clas = Class.forName( clas.getName() + "$Deserializer" ); + Constructor constr = clas.getDeclaredConstructor(); + constr.setAccessible( true ); + return constr.newInstance(); + } + catch ( Exception e ) + { + throw Throwables.propagate( e ); + } + } + + private static M vanillaModelWrapper(ModelLoader loader, ResourceLocation location, ModelBlock model, boolean uvlock, ModelBlockAnimation animation) + { + try + { + return (M) vanillaModelWrapper.newInstance( loader, location, model, uvlock, animation ); + } + catch ( Exception e ) + { + throw Throwables.propagate( e ); + } + } + + private static void setFaceBakery(ModelBakery modelBakery, FaceBakery faceBakery) + { + try + { + UVLModelLoader.faceBakery.set( modelBakery, faceBakery ); + } + catch ( Exception e ) + { + throw Throwables.propagate( e ); + } + } + + private IResourceManager resourceManager; + private ModelLoader loader; + + @Override + public void onResourceManagerReload( IResourceManager resourceManager ) + { + this.resourceManager = resourceManager; + } + + @Override + public boolean accepts( ResourceLocation modelLocation ) + { + return modelLocation.getResourcePath().endsWith( ".uvl" ); + } + + @Override + public IModel loadModel( ResourceLocation modelLocation ) throws Exception + { + return new UVLModelWrapper( modelLocation ); + } + + public class UVLModelWrapper implements IModel + { + final Gson UVLSERIALIZER = (new GsonBuilder()).registerTypeAdapter(ModelBlock.class, deserializer(ModelBlock.class)).registerTypeAdapter(BlockPart.class, deserializer(BlockPart.class)).registerTypeAdapter(BlockPartFace.class, new BlockPartFaceOverrideSerializer()).registerTypeAdapter(BlockFaceUV.class, deserializer(BlockFaceUV.class)).registerTypeAdapter(ItemTransformVec3f.class, deserializer(ItemTransformVec3f.class)).registerTypeAdapter(ItemCameraTransforms.class, deserializer(ItemCameraTransforms.class)).registerTypeAdapter(ItemOverride.class, deserializer(ItemOverride.class)).create(); + + private Map> uvlightmap = new HashMap<>(); + + private final IModel parent; + + public UVLModelWrapper( ResourceLocation modelLocation ) + { + String modelPath = modelLocation.getResourcePath(); + if(modelLocation.getResourcePath().startsWith("models/")) + { + modelPath = modelPath.substring("models/".length()); + } + ResourceLocation armatureLocation = new ResourceLocation(modelLocation.getResourceDomain(), "armatures/" + modelPath + ".json"); + ModelBlockAnimation animation = ModelBlockAnimation.loadVanillaAnimation(resourceManager, armatureLocation); + ModelBlock model; + { + Reader reader = null; + IResource iresource = null; + ModelBlock lvt_5_1_ = null; + + try + { + String s = modelLocation.getResourcePath(); + + iresource = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation(modelLocation.getResourceDomain(), "models/" + modelPath + ".json")); + reader = new InputStreamReader(iresource.getInputStream(), Charsets.UTF_8); + + lvt_5_1_ = JsonUtils.gsonDeserialize(UVLSERIALIZER, reader, ModelBlock.class, false); + lvt_5_1_.name = modelLocation.toString(); + } + catch ( IOException e ) + { + e.printStackTrace(); + } + finally + { + IOUtils.closeQuietly(reader); + IOUtils.closeQuietly((Closeable)iresource); + } + + model = lvt_5_1_; + } + + this.parent = vanillaModelWrapper(loader, modelLocation, model, false, animation); + } + + @Override + public Collection getDependencies() + { + return parent.getDependencies(); + } + + @Override + public Collection getTextures() + { + return parent.getTextures(); + } + + @Override + public IBakedModel bake( IModelState state, VertexFormat format, Function bakedTextureGetter ) + { + setFaceBakery( loader, new FaceBakeryOverride() ); + IBakedModel model = parent.bake( state, format, bakedTextureGetter ); + setFaceBakery( loader, new FaceBakery() ); + return model; + } + + @Override + public IModelState getDefaultState() + { + return parent.getDefaultState(); + } + + public class BlockPartFaceOverrideSerializer implements JsonDeserializer + { + public BlockPartFace deserialize( JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_ ) throws JsonParseException + { + JsonObject jsonobject = p_deserialize_1_.getAsJsonObject(); + EnumFacing enumfacing = this.parseCullFace(jsonobject); + int i = this.parseTintIndex(jsonobject); + String s = this.parseTexture(jsonobject); + BlockFaceUV blockfaceuv = (BlockFaceUV)p_deserialize_3_.deserialize(jsonobject, BlockFaceUV.class); + BlockPartFace blockFace = new BlockPartFace(enumfacing, i, s, blockfaceuv); + uvlightmap.put( blockFace, parseUVL( jsonobject ) ); + return blockFace; + } + + protected int parseTintIndex(JsonObject object) + { + return JsonUtils.getInt(object, "tintindex", -1); + } + + private String parseTexture(JsonObject object) + { + return JsonUtils.getString(object, "texture"); + } + + @Nullable + private EnumFacing parseCullFace(JsonObject object) + { + String s = JsonUtils.getString(object, "cullface", ""); + return EnumFacing.byName(s); + } + + protected Pair parseUVL(JsonObject object) + { + if( !object.has( "uvlightmap" )) + { + return null; + } + object = object.get( "uvlightmap" ).getAsJsonObject(); + return new ImmutablePair( JsonUtils.getFloat(object, "sky", 0), JsonUtils.getFloat(object, "block", 0) ); + } + } + + public class FaceBakeryOverride extends FaceBakery + { + + @Override + public BakedQuad makeBakedQuad( Vector3f posFrom, Vector3f posTo, BlockPartFace face, TextureAtlasSprite sprite, EnumFacing facing, ITransformation modelRotationIn, BlockPartRotation partRotation, boolean uvLocked, boolean shade ) + { + BakedQuad quad = super.makeBakedQuad( posFrom, posTo, face, sprite, facing, modelRotationIn, partRotation, uvLocked, shade ); + + Pair brightness = uvlightmap.get( face ); + if( brightness != null) + { + VertexFormat format = new VertexFormat(quad.getFormat()); + if( !format.getElements().contains( DefaultVertexFormats.TEX_2S )) + { + format.addElement( DefaultVertexFormats.TEX_2S ); + } + UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder( format ); + VertexLighterFlat trans = new VertexLighterFlat( Minecraft.getMinecraft().getBlockColors() ) + { + + @Override + protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z) + { + lightmap[0] = brightness.getRight(); + lightmap[1] = brightness.getLeft(); + } + + }; + trans.setParent( builder ); + LightUtil.putBakedQuad( trans, quad ); + return builder.build(); + } + else + { + return quad; + } + } + + } + + } + + } + +} diff --git a/src/test/resources/assets/uvlightmapjsontest/blockstates/uvlblock.json b/src/test/resources/assets/uvlightmapjsontest/blockstates/uvlblock.json new file mode 100644 index 00000000..dd70b64e --- /dev/null +++ b/src/test/resources/assets/uvlightmapjsontest/blockstates/uvlblock.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "UVLightmapJsonTest:uvlblock.uvl" } + } +} diff --git a/src/test/resources/assets/uvlightmapjsontest/models/block/uvlblock.model b/src/test/resources/assets/uvlightmapjsontest/models/block/uvlblock.model new file mode 100644 index 0000000000000000000000000000000000000000..97a98f0a421303684b08c7e49ab2c7e77e5706dd GIT binary patch literal 2453 zcma)8c{tSj8lS;rjJ3!aC6!B!j3qZ~wrnwj#vr-3!f0qP#x^0#OcYs@3|ZowvX5lD zs*ypNX&Fm$I3&BV4My1-l{w=+_qjPe&$;(}e$V?`{`kJn_xFC@_w)IpkisHz05BK~ zun7EM3)lgO;2lr6gbmR23nc_DQXB|z<}wM~BU_1*CJhcZqi@D3ttI2$`Fl$+Ty3xE zvd}|Ne$=pawyQb>vwA2IG|tY=WfOh+0?3_*9gb*1ETOwAq>FdwJqL7niPTz-$*I&H z>}&%|*RGsg@#Y%tsaU_z6>MMYPg0E~O;SXd&qfaH3&WuI!)@%+Dm1*(DpXU@ux|#q~z|D~wo-JDCm9n2) z**lq}y>2pq!RLf9Pr`clym9fLivaaE;aIXr!iurchL7o;-mAJ1TBAV^(Y;OC8}{jQ zeLl@?!IMPZZ))oo=v=qL24+&N)77`~HHgkZ=Ol_!J*aA>Y@wp+jlKJAV(uu!XjoD* z{B0i1IDx{<_@DxY<<2`J`bNTQ#ii?X!^4aHX1uHPM6XmnHu+mo#@T*be+AlRM%9I*GI>|Zpp0NLoctS!rZSvLb58Mr;;>*KgxumGowZR@n^0cuZ!>U; z4XUXL2SN_HNp$zb&2~|QE|wB#WQEKc`YVxiYI5HV6FJDDg~+)WAe}7Q9hvd5FGB8{ zpiBTeYe%bU3(vT1T4MGGN1+9@I0)j;X;Bue`Y$aO+l*Ke0>eTG0Rh+$dz|m(u+VLW zI1jHl<=+G>!P&^saXXmY%yO2~Tc!3+#sDkL0pF5GciHRDC*(k$myYfWv@ zmov)h8QtzA~R)_tfTTJIwC4Q)0A1>(*#(S0w;G~ zVIPfjMMpGmwWpVHT;)aN9Q*}QXUVv#WSXOYad|JVt$+IQtoZ}f*KsCMTq;^?ASiWi z%Zpi^oF_Nk1OhUBJG+m?c*No&NhChWZHd-G;_2H5s2=B9+}YxBJlyR1T}ZhHM@m|iQGWL$%M5KT zkzVUkJeKl=0;jlBiK<=TI@QAVY;;<)EE~Ne4xedwkB_aX($XDa9%%Q(1alxMlt-Io zBkra%8q)Gg3ww}Yg7GEl`26zcR}G@h5_tK#{uQ5tb!O3pR|=nO@l3SKk1g;Qf5Dv8|r>a#LFys4kBGTsmF_dcqvVGt3V`~HSKHHqhjuIvh44^G5;4RnGa(nr+R@Nia? zQpo(-uW6r2A4fJxMYx$;GlQgI5GKEo?!!rda`Wxwj;77e7?K#?)G_J-duz1CDo~l_$0VP6 zF}h)aZpw8Hhkg1*=&8pi@5Vfy!QTN36N++i4@TKtc?`dtly4A>9vsuWoAxt#6uBJ7 zuyIXsQR1h@7qyJ+vU$?kym@!6{xXg=SxGex;YM-NB5+v7ipRX(7drozc(Jt-cW#5< zZNQ6A+_r_zxyr|P` zGzx|2V0AdaUw3pn*P+X&*%K}(ggT|T-Jak*QFC*-!TpsH(>4Y(#cd!kU0y#S;=M#DgS%Rp)kdF=LH*T69ftg6p{mc zKV0mzkZlL}BfcLseq6Ux&5ucT)^b}8zk?;H@c$RbAM@?}(QO94 dqg?c_&U>c zv7h@-A}f&3S>O>_%)r1c1j3A$?$-SQ3QCo@MwA5Sru2b%QJ)5S3)!+Gf}!*w$5JglYW>o;ET75Tx|=C|lTqfq?e#&ru0d@tB= zjOqSi#;%H*6z01H0mf&|&nkUierb7sfB%11?z+E!@4pT;{_*tZzBp5DR{^FDt63uN z95qEmC-~m!&+Othxz0PgP^jVA1om8o**AoKxTqIpFr`m*maA#HzDx2pt0se#Ol}s_ z+-1T)-F%llpYvcxf-Coi)eaBkrYJ2aO!6)`B{A!i zr#VPNOJVj7`O6~W8SA9Y9;+~%Rph_rV75{9L#N-93oPff+HHQYtS>d*#vR09VsYyV zim}I2u&7V1wK{;yl{H29d-AMDOlM0Er_EtmVEM#R`h?mx z7Q2g`GP4duKApfm|H0P0V`Yw94Q%eSXE`i?>9Q|y@=Hr~3%!VoOmDOTcCaty$e43O zXpT{v`j!V@pH}Y8Vtb$8|2lMY5Q}2Nip8G57;_3_ym{pBjAc6xA07<0eO0XT5GaYr*MI ze4^QGf^QCI%_3n-lg7=b6#4BAX5a03+a$uk+h=Cl=y$oZX0h7kV!wiz4U1TBgf1xH z_Y%Bdu~F3`mB&Bh;ODQmfe~(VuRdn|ELz&~xaum8-22{oBVJm0gTe~DWM4fbkSCU literal 0 HcmV?d00001 diff --git a/src/test/resources/assets/uvlightmapjsontest/textures/blocks/BlockControllerLights.png.mcmeta b/src/test/resources/assets/uvlightmapjsontest/textures/blocks/BlockControllerLights.png.mcmeta new file mode 100644 index 00000000..3a077b37 --- /dev/null +++ b/src/test/resources/assets/uvlightmapjsontest/textures/blocks/BlockControllerLights.png.mcmeta @@ -0,0 +1,19 @@ +{ + "animation": { + "frametime": 3, + "frames": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ] + } +}