Jump to content

Struggling to understand IModel


PoisonedPorkchop

Recommended Posts

I've recently been trying to make a block whose textures I can change by right clicking a side (the texture should only change on that side). I've been banging my head against the wall with this new modeling system, unable to understand how to actually create this. I've looked into TESR and IModel, and for such a simple thing it seems really stupid that I can't seem to figure out how to do it.

 

My issue with TESR is that it renders really dark unless i make it allow light to travel through the base block. I can't quite pinpoint why chests and such don't have this issue, and why they don't even have a block model which everything else does, when if I don't have a block model, I get a no texture (purple and black) block. Having both the TESR and the block model both render causes z fighting. Regardless, I've read many times that TESR isn't really intended for something like this, but rather something mainly for animations. This doesn't mean I wouldn't like to know more about it.

 

My issue with the IModel is that I've no idea how to create one, and how to use the data in it to actually render different textures. I have read that you need an ICustomModelLoader, but I've no idea how to actually implement one. The forge docs seem to say some stuff about the class but not super useful about an actual implementation. I've spent hours searching for a tutorial on an implementation of this and I can't find a single one.

 

Surely I'm missing something big here, does anyone know the answers to my questions? I feel like rendering stuff used to be so easy and now I have to rip myself apart just to figure out how to render a block with dynamic textures.

Link to comment
Share on other sites

1.12 or 1.13? You want an IBakedModel not an IModel btw, the IModel is just the raw model loaded from JSON which is then baked to an IBakedModel that is used in rendering. 

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

This has given me a fairly good understanding but now I run into another issue. It appears that BakedQuads drive what appears on a face of a block. Finding this out, I tried to create a custom BakedQuad for use as in my custom BakedModel, but it appears to not be having the effects I would desire.

 

public class VariableTextureBakedQuad extends BakedQuad {

   public VariableTextureBakedQuad(BakedQuad quad, TextureAtlasSprite sprite) {
      super(quad.getVertexData(), quad.getTintIndex(), quad.getFace(), sprite, quad.shouldApplyDiffuseLighting(), quad.getFormat());

   }

   /**
    * Joined 4 vertex records, each stores packed data according to the VertexFormat of the quad. Vanilla minecraft
    * uses DefaultVertexFormats.BLOCK, Forge uses (usually) ITEM, use BakedQuad.getFormat() to get the correct format.
    */
// protected final int[] vertexData;
// protected final int tintIndex;
// protected final EnumFacing face;
// protected final TextureAtlasSprite sprite;

   public TextureAtlasSprite getSprite()
   {
      return this.sprite;
   }

   public int[] getVertexData()
   {
      return this.vertexData;
   }

   public boolean hasTintIndex()
   {
      return this.tintIndex != -1;
   }

   public int getTintIndex()
   {
      return this.tintIndex;
   }

   public EnumFacing getFace()
   {
      return this.face;
   }

// protected final net.minecraft.client.renderer.vertex.VertexFormat format;
// protected final boolean applyDiffuseLighting;

   @Override
   public void pipe(net.minecraftforge.client.model.pipeline.IVertexConsumer consumer)
   {
      net.minecraftforge.client.model.pipeline.LightUtil.putBakedQuad(consumer, this);
   }

   public net.minecraft.client.renderer.vertex.VertexFormat getFormat()
   {
      return format;
   }

   public boolean shouldApplyDiffuseLighting()
   {
      return applyDiffuseLighting;
   }
}

 

Which is created here:

private IBakedModel modelWhenNotCamouflaged;
private List<BakedQuad>[] quads;
private TextureAtlasSprite sprite;

public CustomBakedModel(IBakedModel unCamouflagedModel, Block yeetblock)
{
   quads = new List[6];
   modelWhenNotCamouflaged = unCamouflagedModel;
   List<BakedQuad> quadlist = new ArrayList<>();
   sprite = Minecraft.getMinecraft().getTextureMapBlocks().registerSprite(new ResourceLocation("customblock","blocks/custom_block2"));
   VariableTextureBakedQuad quad = new VariableTextureBakedQuad(modelWhenNotCamouflaged.getQuads(null,EnumFacing.UP,0).get(0),sprite);
   quadlist.add(quad);
   quads[0] = quadlist;
}

 

Quad is used here:

public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
   if(side != EnumFacing.UP)
      return modelWhenNotCamouflaged.getQuads(state, side, rand);
   else {
      return quads[0];
   }
}

 

Despite doing this, the custom sprite is not showing up on the top of the block as the quad being used should do (it is showing up as the original blocks texture). My current theory is that the sprite included in the BakedQuad class doesn't have anything to do with the actual texture rendered, as even when I tried to use reflection to change the texture of them all the quads, it didn't have any effect.

 

This leads me to believe that the TextureAtlasSprite is actually the entire texture atlas, and the only other place I can see where the texture coordinates could be stored is in the vertex data, but I've no idea how to manipulate the texture coordinates I suspect to be in there to point to another texture. Does anyone have any knowledge on this?

Link to comment
Share on other sites

35 minutes ago, PoisonedPorkchop said:

My current theory is that the sprite included in the BakedQuad class doesn't have anything to do with the actual texture rendered, as even when I tried to use reflection to change the texture of them all the quads, it didn't have any effect.

That is correct.

 

36 minutes ago, PoisonedPorkchop said:

This leads me to believe that the TextureAtlasSprite is actually the entire texture atlas

TextureAtlasSprite is, well, a sprite on the atlas. It's not the atlas itself.

 

36 minutes ago, PoisonedPorkchop said:

and the only other place I can see where the texture coordinates could be stored is in the vertex data

This is correct. The vertex data contains the UV coordinates to use.

 

37 minutes ago, PoisonedPorkchop said:

sprite = Minecraft.getMinecraft().getTextureMapBlocks().registerSprite(new ResourceLocation("customblock","blocks/custom_block2"));

Don't do that here. This needs to be done in the texture stich event.

 

As for what you want to do there is a BakedQuadRetextured class already.

Link to comment
Share on other sites

I've run into a small issue that I'm quite unsure what is causing it. Right clicking on the block appears to give me a blockstate that does not have my unlisted property, though everywhere else I use it, it does have the property.

 

@Override
public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos) {
   if (state instanceof IExtendedBlockState) {  // avoid crash in case of mismatch
      IExtendedBlockState retval = (IExtendedBlockState)state;
      retval = retval.withProperty(TOP, world.getBlockState(pos.add(0,1,0)));
      ExampleMod.logger.info("ABOVE BLOCK: " + retval.getValue(TOP).getBlock().getUnlocalizedName());
      return retval;
   }
   return state;
}

@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
   if(worldIn.isRemote) {
      if (state instanceof IExtendedBlockState) {  // avoid crash in case of mismatch
         IExtendedBlockState estate = (IExtendedBlockState)state;
         ExampleMod.logger.info("GETTING EXTENDED STATE C: " + estate.getValue(TOP));
      }
   }
   else
   {
      if (state instanceof IExtendedBlockState) {  // avoid crash in case of mismatch
         IExtendedBlockState estate = (IExtendedBlockState)state;
         ExampleMod.logger.info("GETTING EXTENDED STATE S: " + estate.getValue(TOP));
      }
   }
   return true;
}

In the above code, the print with "ABOVE BLOCK: " prints correctly, but in the onBlockActivated method, when I right click on the block, it says the value of TOP is null. Have any idea what may be causing this? I want to change the property by right clicking the block, then update the block texture based upon the changes I make to the unlisted property in the onBlockActivated, but I need to know the previous value of TOP in order to get my next value.

Link to comment
Share on other sites

Alright, and how would I go about updating the model for an existing block, making it call getQuads again? This would occur after I change some data in the tile entity after a right click, so that the resulting quad would be different

Edited by PoisonedPorkchop
Link to comment
Share on other sites

@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
{
   if(!worldIn.isRemote) {
      getTileEntity(worldIn,pos).giveClick();
      ExampleMod.logger.info("CLICKS: " + getTileEntity(worldIn,pos).getTimesClicked());
      if (state instanceof IExtendedBlockState) {  // avoid crash in case of mismatch
         IExtendedBlockState retval = (IExtendedBlockState)state;
         retval = retval.withProperty(TOP, getTileEntity(worldIn,pos).getTimesClicked());
         ExampleMod.logger.info("QUAD CHANGE");
         worldIn.notifyBlockUpdate(pos,state,retval,3);
      }
   }
   return true;
}

I tried doing this, but the block isn't calling getQuads upon a right click, how do I change the quads in this case?

public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand)
{
   if(side == EnumFacing.UP) {
      if(state instanceof IExtendedBlockState)
      {
         IExtendedBlockState extendedBlockState = (IExtendedBlockState) state;
         ExampleMod.logger.info("CUSTOM QUAD ENACTED: " + extendedBlockState.getValue(TOP));
         return quads[extendedBlockState.getValue(TOP) % quads.length];
      }
   }
   return modelWhenNotCamouflaged.getQuads(state, side, rand);
}
Edited by PoisonedPorkchop
Link to comment
Share on other sites

41 minutes ago, diesieben07 said:

You are still messing with IExtendedBlockState in onBlockActivated. Don't.

The reason I am is for newState in notifyBlockUpdate, which the code of is here.

/**
 * Flags are as in setBlockState
 */
public void notifyBlockUpdate(BlockPos pos, IBlockState oldState, IBlockState newState, int flags)
{
    for (int i = 0; i < this.eventListeners.size(); ++i)
    {
        ((IWorldEventListener)this.eventListeners.get(i)).notifyBlockUpdate(this, pos, oldState, newState, flags);
    }
}

I figured since it takes parameter newState, I figured calling this method would call getQuads (which does take an IExtendedBlockState which I am using an unlisted property from) from my custom IBakedModel class, but it appears that calling notifyBlockUpdate is not calling getQuads.

 

If this is not the way to change the quads of my block, what is?

Edited by PoisonedPorkchop
Link to comment
Share on other sites

1 hour ago, diesieben07 said:

As far as the world is concerned the state did not change. Just use the same block state.

Ok, well how if I'm not updating the IBakedQuads via an IExtendedBlockState, how else do I update the quad?

I figured updating the block would make the game call getQuads from the IBakedModel, but this is not the case.

Edited by PoisonedPorkchop
Link to comment
Share on other sites

Extended blockstates are gone in 1.13.2, use the new & better IModelAPI instead.

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • I'm using Modrinth as a launcher for a forge modpack on 1.20.1, and can't diagnose the issue on the crash log myself. Have tried repairing the Minecraft instillation as well as removing a few mods that have been problematic for me in the past to no avail. Crash log is below, if any further information is necessary let me know. Thank you! https://paste.ee/p/k6xnS
    • Hey folks. I am working on a custom "Mecha" entity (extended from LivingEntity) that the player builds up from blocks that should get modular stats depending on the used blocks. e.g. depending on what will be used for the legs, the entity will have a different jump strength. However, something unexpected is happening when trying to override a few of LivingEntity's functions and using my new own "Mecha" specific fields: instead of their actual instance-specific value, the default value is used (0f for a float, null for an object...) This is especially strange as when executing with the same entity from a point in the code specific to the mecha entity, the correct value is used. Here are some code snippets to better illustrate what I mean: /* The main Mecha class, cut down for brevity */ public class Mecha extends LivingEntity { protected float jumpMultiplier; //somewhere later during the code when spawning the entity, jumpMultiplier is set to something like 1.5f //changing the access to public didn't help @Override //Overridden from LivingEntity, this function is only used in the jumpFromGround() function, used in the aiStep() function, used in the LivingEntity tick() function protected float getJumpPower() { //something is wrong with this function //for some reason I can't correctly access the fields and methods from the instanciated entity when I am in one of those overridden protected functions. this is very annoying LogUtils.getLogger().info(String.valueOf(this.jumpMultiplier))) //will print 0f return this.jumpMultiplier * super.getJumpPower(); } //The code above does not operate properly. Written as is, the entity will not jump, and adding debug logs shows that when executing the code, the value of this.jumpMultiplier is 0f //in contrast, it will be the correct value when done here: @Override public void tick() { super.tick(); //inherited LivingEntity logic //Custom logic LogUtils.getLogger().info(String.valueOf(this.jumpMultiplier))) //will print 1.5f } } My actual code is slightly different, as the jumpMuliplier is stored in another object (so I am calling "this.legModule.getJumpPower()" instead of the float), but even using a simple float exactly like in the code above didn't help. When running my usual code, the object I try to use is found to be null instead, leading to a crash from a nullPointerException. Here is the stacktrace of said crash: The full code can be viewed here. I have found a workaround in the case of jump strength, but have already found the same problem for another parameter I want to do, and I do not understand why the code is behaving as such, and I would very much like to be able to override those methods as intended - they seemed to work just fine like that for vanilla mobs... Any clues as to what may be happening here?
    • Please delete post. Had not noticed the newest edition for 1.20.6 which resolves the issue.
    • https://paste.ee/p/GTgAV Here's my debug log, I'm on 1.18.2 with forge 40.2.4 and I just want to get it to work!! I cant find any mod names in the error part and I would like some help from the pros!! I have 203 mods at the moment.
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.