Jump to content

[SOLVED] Dynamic blocks models and complex underlying block states


desht

Recommended Posts

I'm working through TheGreyGhost's dynamic models tutorial on GitHub right now: https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe04_block_dynamic_block_model1

 

and getting it close to working, but there's one problem I can't quite get my head round, and most likely due to me not fully understanding how variants and model loading works.

 

The background: I have an existing block with several blockstate properties which affect the block rendering.  That's all working fine, and you can see the blockstate & model JSON at:

https://github.com/desht/ModularRouters/blob/master/src/main/resources/assets/modularrouters/blockstates/itemRouter.json and https://github.com/desht/ModularRouters/blob/master/src/main/resources/assets/modularrouters/models/block/itemRouter.json

 

Now, I want to add some dynamic model functionality to this: when the tile entity holds a specific upgrade item, I want the block appearance to dynamically reflect the texture of the blockstate that has been saved in that upgrade item's NBT.  That part's working fine - I can get the "camo" blockstate via an unlisted property in the IExtendedBlockState. 

 

Here's the code I have so far in my RouterModel (extends IBakedModel) class:

 

    
    @Override
    public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) {
        return handleBlockState(state).getQuads(state, side, rand);
    }

    private IBakedModel handleBlockState(IBlockState state) {
        IBakedModel ret = existingModel;

        if (state instanceof IExtendedBlockState) {
            IExtendedBlockState ext = (IExtendedBlockState) state;
            IBlockState camoState = ext.getValue(BlockItemRouter.HELD_STATE);

            Minecraft mc = Minecraft.getMinecraft();
            BlockRendererDispatcher blockRendererDispatcher = mc.getBlockRendererDispatcher();
            BlockModelShapes blockModelShapes = blockRendererDispatcher.getBlockModelShapes();
            if (camoState != null) {
                // works fine - block gets camouflaged
                ret = blockModelShapes.getModelForState(camoState);
            } else {
                // FAILS! attempt to get model for underlying state just gets the missing texture checkerboard
                ret = blockModelShapes.getModelForState(state);
            }
        }

 

When the router has the camo upgrade, it works, and renders using the camo texture.  But when it doesn't, its normal textures no longer work.

 

I'm also setting up a custom state mapper in my client proxy preInit:

 

  StateMapperBase ignoreState = new StateMapperBase() {
            @Override
            protected ModelResourceLocation getModelResourceLocation(IBlockState iBlockState) {
                return RouterModel.variantTag;  // new ModelResourceLocation("modularrouters:itemRouter", "normal")
            }
    };
    ModelLoader.setCustomStateMapper(ModBlocks.itemRouter, ignoreState);

 

...which appears to be necessary, otherwise my custom RouterModel doesn't get used at all.  But I have a feeling that's causing all the possible variants from my block to be ignored, and no model to be created for them (hence the missing textures).  Am I on the right track here?

 

I guess the real question is what do I need to do to get the regular models loaded for my block variants, but also be able to use a custom IBakedModel?  As far as I can see, TheGreyGhost's tutorial doesn't account for the camouflaged block potentially having multiple variants and models...

 

Update: thinking about this some more... my call to

setCustomStateMapper()

above returns a "modularrouters:itemRouter#normal" variant regardless of block state, and my ModelBakeEventHandler calls

event.getModelRegistry().putObject(RouterModel.variantTag, customModel)

- only the #normal variant is ever registered.  But I need to register this custom model for every possible variant - do I need lots of calls to

event.getModelRegistry().putObject(...)

, one for each variant?  Or is there a better way to do this?

Link to comment
Share on other sites

Hi

 

You're right, the StateMapperBase collapses all of your variants into just one ModelResourceLocation so none of the other variant models are registered, which is why you get a missing whenever you try to retrieve the baked model.

 

So either -

1) your getQuads has to select the correct model from its own register, based on the blockState; or

2) You can get rid of the StateMapperBase completely, which means all your block variants will be registered; your ModelBakeEventHandler will then need to find and replace all of the variant models with your CustomBakedModel.  Since your camo property is an IUnlistedProperty, it won't generate extra entries. 

 

(2) is likely to be the simplest method I think.

 

-TGG

Link to comment
Share on other sites

What is better depends on what changes with a state change: Do only certain textures change or does the model itself (size, position, etc. of elements) change?

If you only need to swap textures you can register that normal variant and depending on the state being provided to getQuads in your model you swap the textures. I for example have a block with 10 different types, 4 possible direction values and 6 booleans for connected textures. The blockstate file only manages the type and the direction, the textures are swapped in the custom model implementation (if you are interested in how I swap the texture, I can provide the method when I am back at my PC).

Link to comment
Share on other sites

Well, I got this pretty much working today, going with option (2) - I ended up registering a custom model (RouterModel) for each variant of my block, passing the uncamouflaged model as a parameter to my custom baked model.  Then I can return either the camo model or the underlying (real) model based on the presence or absence of an unlisted property.  The code's all on GH now: see in particular:

The only little problem I have now is that if I use a non-full block (say, a slab) as the camo block, it almost works, apart from the faces of any blocks beside it don't get drawn - presumably because the block renderer thinks it doesn't need to, the real block being a full cube.  Image here:

- the sandstone slab in front is actually a camouflaged item router, as The One Probe reveals, and the improperly rendered block behind is red sandstone.

 

Any thoughts on how to get around that?

Link to comment
Share on other sites

The camo block (the router?) needs to not tell Minecraft that it is a full, opaque, cube.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

 

The only little problem I have now is that if I use a non-full block (say, a slab) as the camo block, it almost works, apart from the faces of any blocks beside it don't get drawn - presumably because the block renderer thinks it doesn't need to, the real block being a full cube.  Image here:

- the sandstone slab in front is actually a camouflaged item router, as The One Probe reveals, and the improperly rendered block behind is red sandstone.

 

Any thoughts on how to get around that?

Try adding this to your block class:

  // used by the renderer to control lighting and visibility of other blocks.
  // set to false because this block doesn't fill the entire 1x1x1 space
  @Override
  public boolean isOpaqueCube(IBlockState iBlockState) {
    return false;
  }

  // used by the renderer to control lighting and visibility of other blocks, also by
  // (eg) wall or fence to control whether the fence joins itself to this block
  // set to false because this block doesn't fill the entire 1x1x1 space
  @Override
  public boolean isFullCube(IBlockState iBlockState) {
    return false;
  }

-TGG

 

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



×
×
  • Create New...

Important Information

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