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 000000000..cf624949b --- /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 000000000..dd70b64ea --- /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 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 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 000000000..3a077b37e --- /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 + ] + } +}