Posted September 1, 201510 yr Hello I have blocks with a texture composed of a background and a foreground layer, both of which can be of any color. The colors are static for each block, but they are "discovered" on initialisation (i.e in preInit()), and unknown at compile time. I have been told this could be achieved with either the "tintindex" field of the json models, or with a custom baked model, however, I can't figure out how to use either of those I also thought I could generate and save the textures to disk on the first run, but I'm not too sure how to integrate the external folder in the json models
September 4, 201510 yr Author No answer in a while, so I guess I'll clarify the question: what would be the best (easiest) way to do this, and how would I go about implementing it? Thanks
September 4, 201510 yr Hi Some examples of these Some info on the tint index http://greyminecraftcoder.blogspot.com.au/2014/12/block-models-18.html using Block.colorMultiplier() Alternatively, this tutorial project shows how you can bake your own models with custom faces / face colours https://github.com/TheGreyGhost/MinecraftByExample (see MBE04, MBE05 for block model generation and MBE15 for how to generate quads) -TGG
September 5, 201510 yr Author Thanks, I had seen those already (and the example on the TESR has been quite useful), but I don't seem to be able to understand how to put it together to do what I want... My biggest issue is probably because my models don't need to be "smart" (they're static for each block), they just need to be generated on startup
September 6, 201510 yr Hi The key bits to put together are: - the loading methods in MBE04 or MBE05 which are used to insert your model into the registry. Your model can be just an IBakedModel, it doesn't have to be smart. - your IBakedModel class should return the custom quads for your model in getGeneralQuads in MBE15. -TGG
September 7, 201510 yr Hi If you're using arbitrary colours, then yes you will. If you want, you could copy the face information from the loaded json model and overwrite just the colour ints in the int[], leaving the x,y,z, u,v information unchanged. -TGG /** * Converts the vertex information to the int array format expected by BakedQuads. * @param x x coordinate * @param y y coordinate * @param z z coordinate * @param color RGBA colour format - white for no effect, non-white to tint the face with the specified colour * @param texture the texture to use for the face * @param u u-coordinate of the texture (0 - 16) corresponding to [x,y,z] * @param v v-coordinate of the texture (0 - 16) corresponding to [x,y,z] * @return */ private int[] vertexToInts(float x, float y, float z, int color, TextureAtlasSprite texture, float u, float v) { return new int[] { Float.floatToRawIntBits(x), Float.floatToRawIntBits(y), Float.floatToRawIntBits(z), color, Float.floatToRawIntBits(texture.getInterpolatedU(u)), Float.floatToRawIntBits(texture.getInterpolatedV(v)), 0 }; }
September 7, 201510 yr Also you can use some IModel implementation for that. It allows to render multiple IBakedModel-form models. I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP) II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.
September 20, 201510 yr Author I've been messing around with this again, but I still can't get my blocks to render... The code I've tried most recently is in this commit (note that i'm trying to get them to render as dirt to test that stuff is working)
September 22, 20159 yr 1. You should wrap the existing model. First just set all methods return the result of same method in existing model. Then change some methods in your needs 2. You can change the tint value when quads are baked. You can first get the quads from existing model's getGeneralQuads, and change the tint, after that return that in your own getGeneralQuads. I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP) II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.
September 22, 20159 yr thing is, I don't have an existing modelfor this block... You know what TGG said earlier about copying the code from the .JSON to generate the quads yourself? First, make a regular block model in .JSON and register it normally. Then, wrap it in the IBakedModel type to change the color or do whatever you need. Do this in the ModelBakeEvent. Development of Plugins [2012 - 2014] Development of Mods [2012 - Current]
September 28, 20159 yr Author I think I'm starting to understand, but I'm having trouble figuring out how to get a TextureAtlasSprite from an image in my resource folder
September 30, 20159 yr Author So I managed to get the models to load (they render as dirt when returning the sane thing as dirt in all methods), but when I try to make the BakedQuads I get an IndexOutOfBoundsException Model class: /** Runes of Wizardry Mod for Minecraft * Licensed under the GNU GPL version 3 * * this file was created by Xilef11 on 2015-09-06 */ package com.zpig333.runesofwizardry.client.model; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javafx.scene.shape.VertexFormat; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.FaceBakery; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import org.apache.commons.lang3.ArrayUtils; import com.zpig333.runesofwizardry.api.IDustStorageBlock; import com.zpig333.runesofwizardry.client.TextureStitchEventHandler; import com.zpig333.runesofwizardry.core.References; import com.zpig333.runesofwizardry.core.WizardryLogger; /** * @author Xilef11 * */ public class ModelDustStorage implements IBakedModel { private static Map<String,ModelResourceLocation> resourceMap = new HashMap<String, ModelResourceLocation>(); private IDustStorageBlock block; private int meta; private int bgColor,fgColor; // create a tag (ModelResourceLocation) for our model. public final ModelResourceLocation modelResourceLocation; public ModelDustStorage(IDustStorageBlock block, int meta) { WizardryLogger.logInfo("Creating model for block: "+block.getName()+" "+meta); this.block=block; this.meta=meta; this.modelResourceLocation = new ModelResourceLocation(getModelResourceLocationPath(block,meta)); this.bgColor = block.getIDust().getPrimaryColor(new ItemStack(block.getIDust(),1,meta)); this.fgColor = block.getIDust().getSecondaryColor(new ItemStack(block.getIDust(),1,meta)); } public static String getModelResourceLocationPath(IDustStorageBlock block, int meta){ return References.texture_path+block.getName()+"_"+meta; } public static ModelResourceLocation getModelResourceLocation(String path){ ModelResourceLocation current = resourceMap.get(path); if(current==null){ current=new ModelResourceLocation(path); resourceMap.put(path, current); } return current; } public static ModelResourceLocation getModelResourceLocation(IDustStorageBlock block, int meta){ return getModelResourceLocation(getModelResourceLocationPath(block, meta)); } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#getFaceQuads(net.minecraft.util.EnumFacing) */ @Override public List getFaceQuads(EnumFacing face) { List r = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(Blocks.dirt.getDefaultState()).getFaceQuads(face); List<BakedQuad> result = new LinkedList<BakedQuad>(); int[] bg =null; int[] fg = null; TextureAtlasSprite bgTex = TextureStitchEventHandler.getDustStorageBG(); TextureAtlasSprite fgTex = TextureStitchEventHandler.getDustStorageFG(); //looks like a bakedquad is a full square, and we have to pass it all its vertices in the int array... //also, tintindex should be -1 //FIXME no more crash, but rendering is broken again //Feels like the ints are not in the right order... if(face==EnumFacing.EAST){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 0, 0, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 1, 0, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 1, 1, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(1.001F, 0, 1, fgColor, fgTex, 0, 16)); }else if(face==EnumFacing.WEST){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 0, 1, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 1, 1, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 1, 0, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(-0.001F, 0, 0, fgColor, fgTex, 0, 16)); }else if(face==EnumFacing.NORTH){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(0, 0, -0.001F, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(0, 1, -0.001F, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(1, 1, -0.001F, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(1, 0, -0.001F, fgColor, fgTex, 0, 16)); }else if(face==EnumFacing.SOUTH){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(1, 0, 1.001F, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(1, 1, 1.001F, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, 1, 1.001F, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, 0, 1.001F, fgColor, fgTex, 0, 16)); }else if(face==EnumFacing.DOWN){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 0, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 0, 1, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 1, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 0, 0, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(1,-0.001F,0, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(1,-0.001F,1, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, -0.001F,1, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, -0.001F,0, fgColor, fgTex, 0, 16)); }else if(face==EnumFacing.UP){ //BG color bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 1, bgColor, bgTex, 16, 16)); bg = ArrayUtils.addAll(bg, vertexToInts(1, 1, 0, bgColor, bgTex, 16, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 0, bgColor, bgTex, 0, 0)); bg = ArrayUtils.addAll(bg, vertexToInts(0, 1, 1, bgColor, bgTex, 0, 16)); //fg fg = ArrayUtils.addAll(fg, vertexToInts(1,1.001F,1, fgColor, fgTex, 16, 16)); fg = ArrayUtils.addAll(fg, vertexToInts(1,1.001F,0, fgColor, fgTex, 16, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, 1.001F,0, fgColor, fgTex, 0, 0)); fg = ArrayUtils.addAll(fg, vertexToInts(0, 1.001F,1, fgColor, fgTex, 0, 16)); }else{ throw new IllegalArgumentException("Wrong EnumFacing: "+face);//is that even possible... } //int[] test = ArrayUtils.addAll(new int[]{1,2,3,4},new int[]{5,6,7,8}); result.add(new BakedQuad(bg, -1, face)); result.add(new BakedQuad(fg, -1, face)); return result; // TODO Auto-generated method stub } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#getGeneralQuads() */ @Override public List getGeneralQuads() { List<BakedQuad> res = new LinkedList<BakedQuad>(); for(EnumFacing face : EnumFacing.VALUES){ res.addAll(getFaceQuads(face)); } //return res; // TODO Auto-generated method stub List r = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(Blocks.dirt.getDefaultState()).getGeneralQuads(); return r; } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#isAmbientOcclusion() */ @Override public boolean isAmbientOcclusion() { //return true; return false; } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#isGui3d() */ @Override public boolean isGui3d() { return true; } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#isBuiltInRenderer() */ @Override public boolean isBuiltInRenderer() { return false; } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#getTexture() */ @Override public TextureAtlasSprite getTexture() { //TODO getTexture might need to get tweaked return TextureStitchEventHandler.getDustStorageBG(); } /* (non-Javadoc) * @see net.minecraft.client.resources.model.IBakedModel#getItemCameraTransforms() */ @Override public ItemCameraTransforms getItemCameraTransforms() { return ItemCameraTransforms.DEFAULT; } /** * Converts the vertex information to the int array format expected by BakedQuads. * @param x x coordinate * @param y y coordinate * @param z z coordinate * @param color RGBA colour format - white for no effect, non-white to tint the face with the specified colour * @param texture the texture to use for the face * @param u u-coordinate of the texture (0 - 16) corresponding to [x,y,z] * @param v v-coordinate of the texture (0 - 16) corresponding to [x,y,z] * @return */ private static int[] vertexToInts(float x, float y, float z, int color, TextureAtlasSprite texture, float u, float v) { return new int[] { Float.floatToRawIntBits(x), Float.floatToRawIntBits(y), Float.floatToRawIntBits(z), color, Float.floatToRawIntBits(texture.getInterpolatedU(u)), Float.floatToRawIntBits(texture.getInterpolatedV(v)), 0 }; } } ModelBake Event Handler /** Runes of Wizardry Mod for Minecraft * Licensed under the GNU GPL version 3 * * this file was created by Xilef11 on 2015-09-06 */ package com.zpig333.runesofwizardry.client; import com.zpig333.runesofwizardry.api.DustRegistry; import com.zpig333.runesofwizardry.api.IDustStorageBlock; import com.zpig333.runesofwizardry.client.model.ModelDustStorage; import com.zpig333.runesofwizardry.core.WizardryLogger; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; /** * @author Xilef11 * */ public class ModelBakeEventHandler { public static final ModelBakeEventHandler instance=new ModelBakeEventHandler(); private ModelBakeEventHandler() {}; @SubscribeEvent public void onModelBake(ModelBakeEvent event){ WizardryLogger.logInfo("Registering models on ModelBakeEvent"); // Find the existing mapping for the block - it will have been added automatically because // we registered a custom BlockStateMapper for it (using ModelLoader.setCustomStateMapper) // Replace the mapping with our ISmartBlockModel. for(IDustStorageBlock block: DustRegistry.getAllBlocks()){ WizardryLogger.logInfo("ModelBake: processing "+block.getName());//XXX this happens for(int meta : block.getIDust().getMetaValues()){ WizardryLogger.logInfo("meta is "+meta);//XXX this happens ModelResourceLocation location = ModelDustStorage.getModelResourceLocation(block, meta); Object object = event.modelRegistry.getObject(location); WizardryLogger.logInfo("object is "+object); if (object instanceof IBakedModel) {//FIXME object is null IBakedModel existingModel = (IBakedModel)object; ModelDustStorage customModel = new ModelDustStorage(block, meta); event.modelRegistry.putObject(location, customModel); }else if(object==null){ ModelDustStorage model = new ModelDustStorage(block, meta); event.modelRegistry.putObject(location, model); } } } } } Methods (in client proxy) that load the stuff... /* (non-Javadoc) * @see com.zpig333.runesofwizardry.proxy.CommonProxy#registerDustStorageRendering() */ @Override public void registerDustStorageRendering() { WizardryLogger.logInfo("Registering Dust Storage rendering"); // We need to tell Forge how to map our BlockCamouflage's IBlockState to a ModelResourceLocation. // For example, the BlockStone granite variant has a BlockStateMap entry that looks like // "stone[variant=granite]" (iBlockState) -> "minecraft:granite#normal" (ModelResourceLocation) // For the camouflage block, we ignore the iBlockState completely and always return the same ModelResourceLocation, // which is done using the anonymous class below for(final IDustStorageBlock block : DustRegistry.getAllBlocks()){ WizardryLogger.logInfo("Creating StateMapper for "+block.getName());//XXX This happens StateMapperBase mapper = new StateMapperBase() { @Override protected ModelResourceLocation getModelResourceLocation(IBlockState iBlockState) { int meta = (Integer) iBlockState.getValue(IDustStorageBlock.PROPERTYMETA); return ModelDustStorage.getModelResourceLocation(block, meta); } }; ModelLoader.setCustomStateMapper(block, mapper); } // ModelBakeEvent will be used to add our ISmartBlockModel to the ModelManager's registry (the // registry used to map all the ModelResourceLocations to IBlockModels). For the stone example there is a map from // ModelResourceLocation("minecraft:granite#normal") to an IBakedModel created from models/block/granite.json. // For the camouflage block, it will map from // CamouflageISmartBlockModelFactory.modelResourceLocation to our CamouflageISmartBlockModelFactory instance MinecraftForge.EVENT_BUS.register(ModelBakeEventHandler.instance); //register the handler to create the textures MinecraftForge.EVENT_BUS.register(new TextureStitchEventHandler()); } public void registerDustStorageItemRendering() { WizardryLogger.logInfo("Registering dust storage item rendering"); // This is currently necessary in order to make your block render properly when it is an item (i.e. in the inventory // or in your hand or thrown on the ground). // Minecraft knows to look for the item model based on the GameRegistry.registerBlock. However the registration of // the model for each item is normally done by RenderItem.registerItems(), and this is not currently aware // of any extra items you have created. Hence you have to do it manually. This will probably change in future. // It must be done in the init phase, not preinit, and must be done on client only. for(IDustStorageBlock b:DustRegistry.getAllBlocks()){ WizardryLogger.logInfo("Processing item: "+b.getName()); Item itemBlockDustStorage = GameRegistry.findItem(References.modid, b.getName()); for(int meta: b.getIDust().getMetaValues()){ WizardryLogger.logInfo("meta: "+meta); //ModelResourceLocation itemModelResourceLocation = new ModelResourceLocation(ModelDustStorage.getModelResourceLocationPath(b, meta), "inventory"); ModelResourceLocation itemModelResourceLocation = ModelDustStorage.getModelResourceLocation(b, meta); Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(itemBlockDustStorage, meta, itemModelResourceLocation); } } } TextureStitch Event Handler package com.zpig333.runesofwizardry.client; import com.zpig333.runesofwizardry.core.References; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; public class TextureStitchEventHandler { private static TextureAtlasSprite dust_storage_bg, dust_storage_fg; public static TextureAtlasSprite getDustStorageBG(){ return dust_storage_bg; } public static TextureAtlasSprite getDustStorageFG(){ return dust_storage_fg; } @SubscribeEvent public void onTextureStitch(TextureStitchEvent.pre event){ dust_storage_bg = event.map.registerSprite(new ResourceLocation(References.texture_path+"blocks/dustStorage_bg")); dust_storage_fg = event.map.registerSprite(new ResourceLocation(References.texture_path+"blocks/dustStorage_fg")); } } EDIT: Turns out I derped and didn't notice the int[] was supposed to contian the data for all four vertices of the quad... I no longer crash, but the blocks get rendered in dark colors with no texture EDIT2: textures were fixed, I had to register for TextureStitchEvent.pre . However, the two layers don't blend (alpha in the foreground renders as black) and the colors are off.
October 5, 20159 yr Author I managed to fix the color, turns out I had to modify TGG's vertexToInts, since my color was in the 0x00RRGGBB format, while the int[] expected 0x00BBGGRR ( so I used Integer.reverseBytes(color)>>8 ). I still have a few issues though, the transparent pixels in the foreground layer are rendered as black instead of transparent - solved by setting my block to render in the CUTOUT layer and adding 0xFF000000 to my colors my block extends BlockFalling, but when it falls it becomes invisible instead of using my model - working now, don't know why In the inventory, the texture returned from MyModel#gettexture() is used instead of the quads I made - fixed by using ColoredBakedQuad instead of BakedQuad The console still gets spammed with model not found messages
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.