Jump to content

How to set texture for dynamically registered blocks from already existing blocks [1.18.2]


X-Lomir

Recommended Posts

I'm trying to develop a mod that adds vertical slabs for every block that already has slabs and stairs, vanilla or not.

For this reason I can't put textures in my resources folder and instead I need to take the textures of the already existing slabs/stairs and rotate them before applying them to my vertical slabs.

I have already setup a class extending Block and implementing SimpleWaterloggedBlock for the logic a generic vertical slab has to use, in particular for its shape and how it combines with others (similar to stairs) and it does work.

My idea is now to get the list of blocks that have slabs & stairs versions, take their textures, rotate them, apply them on each shape of a new instance of VerticalSlabBlock and finally register the block.

The problems I'm facing are:

  1. How to get the list of blocks that have slabs and stairs (a loop over all the existing blocks? Seems inefficient) and then loop through this list to register my VerticalSlabBlocks?
  2. Given a slab block and a stair block, how do I get their textures, rotate them and apply them when I construct a new instance of VerticalSlabBlock?
  3. While slab and stairs textures rotated will work for 8 different vertical slab shapes, there are 4 shapes (the "corners") that I still need to get dynamically based on the block, is there a way to "crop" textures? Otherwise what else could I do?
  4. I have a static final VoxelShape array called SHAPES that holds all 12 possible shapes, how do I associate for each shape its corresponding texture in my constructor?

Thanks in advance, if some code I wrote or a better explanation is needed, I will gladly provide it.

Link to comment
Share on other sites

  • Replies 92
  • Created
  • Last Reply

Top Posters In This Topic

7 hours ago, diesieben07 said:

You cannot dynamically register blocks in this way. You'll have to make a fixed set of blocks that can mimic all existing blocks. You will need a BlockEntity and a custom model for this.

Why a set of blocks? If I have a block that can mimic any existing block, wouldn't that one suffice already?

But anyway that would kind of defeat my mod purpose as I wanted to add vertical slabs only for blocks that already have horizontal slabs and stairs, I wanted each to have its own crafting recipe according to the block they derive from and I wanted to offer out of the box support to (almost) any mod adding its own block with slabs and stairs.

Link to comment
Share on other sites

8 minutes ago, diesieben07 said:

You can do this, you just cannot do it by adding blocks dynamically.

Then could you provide me a way to understand how to do that without adding blocks dynamically? I'm quite new at modding, so dynamically adding the blocks is the only thing I could think of, any hint on how to proceed differently would be greatly appreciated

Link to comment
Share on other sites

11 hours ago, diesieben07 said:

You need a BlockEntity which stores the BlockState that the block at that position should look like.

But this way won't I have only 1 block? Which means that in creative one won't be able to see each vertical slab, and also how would it work for the crafting recipes? I want each vertical slab to be crafted like its corresponding horizontal slab, so same items, but with the crafting grid rotated 90 degrees

Link to comment
Share on other sites

Okay, I think I'm understanding the way you're showing me.

I created a custom BlockEntity and a custom BlockEntityRenderer, registered my BE and associated it to my BER.

My VerticalSlabBlock now implements the EntityBlock interface; my BE only has a constructor as shown in the Forge documentation.

In my BER I overrided the method render like this:

@Override
  public void render(VerticalSlabBlockEntity blockEntity, float partialTicks, PoseStack poseStack, MultiBufferSource bufferSource, int combinedLight, int combinedOverlay) {
    context.getBlockRenderDispatcher().renderSingleBlock(blockEntity.getBlockState(), poseStack, bufferSource, combinedLight, combinedOverlay, blockEntity.getModelData());
  }

Now, as I was expecting when I load the game it gives off many "Exception loading blockstate definition" and when I try to place my vertical slab the game crashes saying "java.lang.NullPointerException: Registry Object not present: justverticalslabs:vertical_slab_block_entity" ("vertical_slab_block_entity" is the string I passed to BLOCK_ENTITIES.register()).

I would like now, to test and then expand the logic, force the oak planks texture to my vertical slab and its BlockItem. However I have no clear idea on how to do that. I'm guessing I have to use the saveMetadata() in my BE to add the id (or some other property) of the oak planks block and then use that in my BER to render the texture also based on the current shape (which is in the blockState) of my vertical slab. But I have no idea where to call saveMetadata(), which property of the oak planks to save, how to render the texture based on the current VoxelShape and how to propagate this to the associated BlockItem

Link to comment
Share on other sites

So, sorry for the delayed reply, I tried some stuff but none worked.

I did create the BlockEntity, the ModelLoader, the Model (IModelGeometry) and the BakedModel (IDynamicBakedModel).

The problems I have so far are:

  • what is the appropriate time to do set the BlockState in the BlockEntity? For now I'm setting it as Blocks.OAK_PLANKS.defaultBlockState() and updating it with the tags methods, but it's not clear to me when and how those tags methods are called.
  • I am quite lost on how to implement the ModelLoader, the Model and the BakedModel, I tried a few stuff but everytime I tried something out I would get a NullPointerException.

I'm leaving here the GitHub repo in case seeing some of the code helps: https://github.com/Nyphet/just-vertical-slabs

Thanks again

Link to comment
Share on other sites

So, I added my JSON models and they seem to work, I set them to use the oak_planks texture for now directly inside JSON, but I will need to change it such that I don't specify textures in my JSON models and instead use my custom ModelLoader to load the correct textures.

I added in my "main" JSON model my custom loader (or at least I think I did so) and this is not giving any error.

However I am getting errors since I added my JSON models and the deserialization in VerticalSlabModelLoader#read (and I think the culprit is the latter): here more info on the error.

And I am still getting a NullPointerException when I try to place a vertical slab, which was not happening before I added the BlockEntity, so I'm guessing I've done something wrong about it; again more info on the error here.

Anyway, I guess I don't need the shape property I was getting in my VerticalSlabBakedModel#getQuads because I already have the vertices given by my JSON model for the given direction, so I changed my code a bit and now it only remains to use the sprites from the bakedQuads of my referringBlockState, however I'm not exactly sure how.

Anyway once these things are all done, I guess all that'll be left is to cycle through all the existing blocks and check which ones have slabs, then pass each block defaultBlockState into a new instance of a VerticalSlabBlock and setting the creative tab to the same tab as the block using fillItemCategory on the BlockItem.

The full code is still available here in case it's of any use.

Link to comment
Share on other sites

5 hours ago, diesieben07 said:

Precisely this is what you cannot do. There will only be one instance of VerticalSlabBlock. You cannot create blocks dynamically like you described here. This is the sole reason you need to go through this process.

Okay, I think I'm getting this now:

As you had said, potions provide a clear example on how to handle the creative tabs part. My guess is that I can do something similar and when I create each new ItemStack I also give it the CompoundTag with the BlockState of the block my vertical slab will need to mimic. This way I should be able to add all the vertical slabs I want to any creative tab, with each vertical slab mimicking its own block, without needing to create more than 1 instance of my VerticalSlabBlock.

For the crafting recipe I should get the BlockState of the Block from the BlockItem being used in the crafting recipe and save it in my Block CompoundTag, so that both the new BlockItem from the crafting recipe result and the actual Block when placed will mimic the correct block.

So, if my previous statements are correct, the only question remaining is how to get the list of blocks already having horizontal slabs. Should I loop through all the blocks registered in the game and check if for each there exists another block with the same id + "_slab" or something similar? Or is there a better way?

 

6 hours ago, diesieben07 said:

I described this in detail in my previous post.

Yeah I know, you've actually been very detailed and patient, it's just me being not much knowledgeable about this stuff.

I moved the most updated code to another branch (here), does my VerticalSlabBakedModel#getQuads make any sense now? I know I'm still not getting the sprites from the referringBakedQuads, but I'm not exactly sure how to proceed on that since I'm already looping on another list and I need to find a way to associate the correct referringBakedQuad to each bakedQuad of my original JSON model. Speaking of which, is there a way to not specify textures in the JSON models? As of now my JSON models refer to "minecraft:block/oak_planks" for their textures, but if possible I would prefer that no "default" texture is provided in the JSON models and instead textures are provided exclusively by the custom loader.

About the rest, now I finally can place again my vertical slabs and they have the correct combining behavior, and they also have a texture that allows me to finally see them rather than just seeing some black and purple squares, all thanks to your advice!

Link to comment
Share on other sites

18 hours ago, diesieben07 said:

None of your models actually use your custom loader. Instead you have a "loader" property set in your blockstate JSON, where it is ignored.

This I had realized and I moved that "loader" property from the blockstate JSON to the three block models JSON, but I get this error for each block model JSON: 

Unable to load model: 'justverticalslabs:block/vertical_slab' referenced from: justverticalslabs:vertical_slab#facing=north,shape=straight,waterlogged=true: java.lang.IllegalStateException: Model loader 'justverticalslabs:vertical_slab_loader' not found. Registered loaders: forge:obj, forge:composite, forge:item-layers, forge:multi-layer, forge:bucket, minecraft:elements, forge:separate-perspective

However I thought I had registered it, but it doesn't seem to be the case.

Link to comment
Share on other sites

On 4/9/2022 at 6:26 PM, diesieben07 said:

Your loader needs to deserialize this embedded JSON into a BlockModel using the provided JsonDeserializationContext. Pass this BlockModel on to your VerticalSlabModel.

I now registered to the correct event bus and now VerticalSlabModelLoader#read gets called, however when I try to use the JsonDeserializationContext to deserialize the given JsonObject, it goes in stack overflow:

java.lang.StackOverflowError
	at com.google.gson.internal.bind.TypeAdapters$27.read(TypeAdapters.java:658) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TypeAdapters$27.read(TypeAdapters.java:655) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.Streams.parse(Streams.java:48) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:65) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at net.minecraft.client.renderer.block.model.BlockElementFace$Deserializer.deserialize(BlockElementFace.java:39) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraft.client.renderer.block.model.BlockElementFace$Deserializer.deserialize(BlockElementFace.java:30) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at net.minecraft.client.renderer.block.model.BlockElement$Deserializer.filterNullFromFaces(BlockElement.java:136) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraft.client.renderer.block.model.BlockElement$Deserializer.getFaces(BlockElement.java:122) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraft.client.renderer.block.model.BlockElement$Deserializer.deserialize(BlockElement.java:77) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraft.client.renderer.block.model.BlockElement$Deserializer.deserialize(BlockElement.java:68) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at net.minecraft.client.renderer.block.model.BlockModel$Deserializer.getElements(BlockModel.java:382) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraft.client.renderer.block.model.BlockModel$Deserializer.deserialize(BlockModel.java:312) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2376!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:382) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:368) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:21) ~[%2380!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:1) ~[%2380!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry.getModel(ModelLoaderRegistry.java:120) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry.deserializeGeometry(ModelLoaderRegistry.java:136) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:384) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:368) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:21) ~[%2380!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:1) ~[%2380!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry.getModel(ModelLoaderRegistry.java:120) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry.deserializeGeometry(ModelLoaderRegistry.java:136) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:384) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at net.minecraftforge.client.model.ModelLoaderRegistry$ExpandedBlockModelDeserializer.deserialize(ModelLoaderRegistry.java:368) ~[forge-1.18.2-40.0.35_mapped_official_1.18.2-recomp.jar%2375%2381!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:963) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.Gson.fromJson(Gson.java:1034) ~[gson-2.8.9.jar%2340!/:?]
	at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.deserialize(TreeTypeAdapter.java:162) ~[gson-2.8.9.jar%2340!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:21) ~[%2380!/:?]
	at crystalspider.justverticalslabs.VerticalSlabModelLoader.read(VerticalSlabModelLoader.java:1) ~[%2380!/:?]

and the stack trace goes on and on repeating.

I tried debugging and searching online but I couldn't find a way to fix this. I commented out this part and the related parts and the game doesn't crash, however the slabs are (of course) rendered transparent.

The most updated code is available here.

Link to comment
Share on other sites

You're a real life saver!

Now it works as it should. I also changed the implementation of VerticalSlabBakedModel#getQuads to actually get the sprite of the block I'm mimicking and there were no big problems.

However I still have a few unclear points I'd like to ask you:

  • Every vertical slab in any shape renders just fine apart from the "big corners" ones, where their "inner" sides are not rendered and I don't know why. Here a screenshot of the bug.
  • Every time particle rendering is involved with a vertical slab, the game crashes with a NullPointerException. I'm guessing it has to do with VerticalSlabBakedModel#getParticleIcon where the auto-generated method returns null, while I should be returning the same TextureAtlasSprite as the block I'm mimicking. Problem is: how do I get that info in that method with no parameter?
  • When in the inventory, vertical slabs are invisible. Maybe setting the parent in the item model JSON is not enough? Should I do something with those ItemOverrides I get in VerticalSlabModel#bake?
  • In my VerticalSlabBlockItem#fillItemCategory I add to each ItemStack a Tag with key "BlockEntityTag" containing the BlockState of the block to mimic, however when placed they all mimic oak planks. I think it's because the Tag doesn't transfer from item to block, and then the default value of VerticalSlabBlockEntity#referringBlockState gets used. I tried removing the default value using "BlockStateTag" as key for the tag, but that gives me errors when placing the block and crashing the game, whereas with "BlockEntityTag" I can place the blocks but NbtUtils read/write BlockState gives non-blocking errors (BlockState is null). I thought that by using "BlockEntityTag" as key I would take advantage of what's already implemented in BlockItem#updateCustomBlockEntityTag, but it doesn't seem to be the case.

I thank you again for all the help and the patience, most updated code always available here.

Link to comment
Share on other sites

Oh and I actually forgot a point.

I would like to cache the bakedQuads, but I'm not sure what's the best way to implement it.

I thought of, in VerticalSlabBakedModel constructor, to iterate through all 6 Directions and generate there the cache, but this way I would have no info on the mimicked block, so can't do.

So I thought of saving the bakedQuads I create in getQuads on the first call, and then use the cache for the next calls. Question is now whether I should make my cache static or not (I would say yes) and, if static, whether to make it double layered or single layered (I'd say double).

With "double layered" I mean having a HashMap with keys as the mimicked block id and values as HashMaps. These HashMaps would have as keys the directions and as values the bakedQuads.

With "single layered" I mean having, like I have in my current code, a custom class for the HashMap keys which uses directions and block states together, and values of the HashMap directly the bakedQuads.

Thank you once again.

Link to comment
Share on other sites

So, I think I implemented everything you said and the results are a bit confusing to me ahah

What I did was:

  • Remove default value of VerticalSlabBlockEntity#referringBlockState
  • In VerticalSlabBlockEntity, save/load the referringBlockState value under a key "referringBlockState"
  • In VerticalSlabBlockItem, add a Tag element, with key "referringBlockState" and value the BlockState of the block I'm mimicking, under the key "BlockEntityTag"
  • Create a class extending ItemOverrides and override the resolve method. In that method I return a new VerticalSlabBakedModel passing to it the BakedModel and the referringBlockState retrieved from the ItemStack.
  • Implement the two getParticleIcon methods in VerticalSlabBakedModel
  • In VerticalSlabBakedModel#getQuads use either the IModelData referringBlockState or the referringBlockState field depending on whether the referringBlockState field is null or not (this to make use of the referringBlockState I save in VerticalSlabItemOverrides#resolve)

And everything, from console, seems to be working fine because I added some console logs and (almost) everywhere I get the correct value for the referringBlockState, whether it's BlockEntity or BakedModel.

But here's where things get confusing: despite what stated above, the first time I place a vertical slab in both VerticalSlabBakedModel#getQuads and VerticalSlabBakedModel#getParticleIcon referringBlockState is null, making the slab completely transparent. However, if the chunk is reloaded, in both methods referringBlockState becomes the correct one (in my test cases dark_oak_planks and cobblestone). After reloading indeed if I jump or break the slab the particles are the corrected ones. However the slab is rendered with oak planks texture rather than dark oak planks or cobblestone, and I have no idea why.

The only place I see it could be getting the oak planks texture is the JSON model itself, but if that was an issue why then for particles it's not? I'm quite lost.

Furthermore items are still rendered transparent, despite the fact I can see from console logs the getQuads method get called with the correct referringBlockState value.

20 hours ago, diesieben07 said:

In your VerticalSlabBakedModel have a Map<BlockState, List<BakedQuad>>. Here you cache the list of baked quads based on the referringBlockState. If it is not present yet, compute them like you do now. If it is, simply return them. You might want to eventually evict items from this cache, but that might lead to thrashing if you don't do it properly.

This I will implement later on, but I guess that Map will be static, right?

20 hours ago, diesieben07 said:

Not sure the exact cause of this, my guess is the shape you return is wrong and thus Minecraft culls away more than it should.

This I'm not sure either, I don't think the shape is wrong because I had done many checks about that, but maybe in the JSON models I messed something up with the "cullface" properties. I think it's worth mentioning that before the custom loader was effective, textures from the JSON models were rendered fine and there were no culling problems I could see.

Link to comment
Share on other sites

Okay, I kinda feel stupid sometimes because when you explain stuff it becomes so clear that I wonder how I didn't get it before ahah

I fixed the problem and also implemented the cache.

All the problems I mentioned in my previous comment are still there, apart from the items not being rendered. Now items are rendered, but also they all have oak planks texture despite referringBlockState being of another block and it looks like they lost all "display" property from the JSON model. Before having the custom loader they would be scaled and rotated, now they are full size and not rotated.

Could it be that somehow some properties from the JSON model are not properly received by the custom loader and that's why items are not displayed correctly and why the "big corners" slabs are transparent?

Why everything has oak planks texture is still a mystery to me. I tried removing textures from the JSON models but that just gives off missing texture errors and block and items get that black and purple texture, regardless of whether I reload the chunk or not.

Another information I just discovered is that what happens when I wrote "reload the chunk" actually does not work with the classic F3+A command, but only if I save and quit the world/ go far away till the chunk is not loaded anymore and then reopen the world/go back to the chunk.

Link to comment
Share on other sites

18 minutes ago, diesieben07 said:

Your cache should just use the BlockState in the key, there is no need to use toString.

Isn't getQuads called once for every direction? If so I thought I needed to take in account also the Direction other than the BlockState to know which list of BakedQuad I should return. Also I thought storing just the String representing the BlockState would be less memory consuming than storing the whole instance. Maybe also better performing depending on whether checks on strings are faster than check on objects. I could however not use the Direction together with BlockState as key and instead have a list of list of BakedQuad and return the appropriate list using the Direction as index, but I would prefer not to have nested lists.

27 minutes ago, diesieben07 said:

The reason your model is initially invisible is that the block doesn't know the correct BlockState on the client. This is because Minecraft first sets the block when placing the item and then updates its entity tag in a separate packet (when the chunk is loaded as a whole this happens in one step using getUpdateTag). You have to override onDataPacket in your BlockEntity class and tell Minecraft that your block needs re-rendering. First call requestModelDataUpdate on the BlockEntity itself to tell Forge that the model data has now changed. Then if you are on the client, call sendBlockUpdated

This immediately fixed it, thanks!

27 minutes ago, diesieben07 said:

The reason you only get the oak planks texture is because I was mistaken about how to retexture a baked quad. Simply setting its TextureAtlasSprite is not enough, the texture is also baked into its vertex data. Because your embedded model uses the oak plank texture, this is what is shown. As for how to fix this: I do not know exactly. You most likely need to actually dig into the quad's vertex data and copy over the correct UVs. How to do this is beyond my knowledge of rendering. You should probably ask on the Forge discord.

Okay, I will try! By any chance do you have any clue on why items are displayed not scaled and rotated? Do you think it's part of the same problem?

Link to comment
Share on other sites

Sorry for the delayed reply.

I am extremely thankful to you, the snippet you sent last time is a bless!

However it doesn't work perfectly, but it is surely something I can try to move from.

I updated the JSON models in both branches, with master branch not using the custom loader. Here you can see what the desired result would be, both for blocks and items.

Comparing it to the result in dev branch it shows that there are 3 problems:

  1. As we've mentioned already, the items seem to ignore the display property
  2. Cullfaces seem to work differently, I have no idea why. If you look at master, everything seems to work just fine, but as we switch to dev, cullfaces are now behaving differently. However if in the properties that do not have an explicit cullface I set the cullface, the result on dev is better (while it breaks on master), but still there's the "big corners" problem.
  3. Textures are stretched. Basically for every sprite of my vertical slab, the whole sprite of the same side of the referring block is applied, while it should be "cut" instead. If you compare it to the visual result in master (or just to the original block) it becomes evident. My guess is that by manually adjusting stuff we are preventing Minecraft from doing what it does by default.

Apart from changing the JSON models, I had no time to do some actual coding. As soon as I can I will try and report back too.

Thanks again for your time and efforts!

Link to comment
Share on other sites

I managed to get the items display correctly.

I overrided IForgeBakedModel#doesHandlePerspectives and IForgeBakedModel#handlePerspective and I now set with Java the rotation, translation and scale for each perspective. I'm still not sure why they are not picked from the JSON model, I think it's because Forge overrides the default behavior of BakedModel#getTransforms(), but again I'm not sure.

Anyway problem #1 solved, most updated code is as always on dev.

Actually there is still 1 little problem in item rendering, which is the shadows on the item. If you compare how vertical slab items are rendered in master vs dev or how other block items vs vertical slab items in dev, it becomes quite obvious that in dev there are some shadows missing or at most they are too bright.

About 2nd and 3rd problems, I thought that maybe they are one and the same. Maybe since the referring block would cull certain faces then when I apply its UV onto my vertical slabs also the "invisible" UVs are applied? I actually have no idea if this makes sense ahah

Btw I was wondering, is it so much more inefficient to create and cache models at runtime compared to having JSON to read them from? Because I was wondering why in Minecraft all models are JSON instead of having, maybe, a few JSON and then dynamically generate models depending on a few parameters.

P.S. do you know anything about BakedModel#isGui3d ? I read on the docs, but I didn't understand very well. It doesn't seem to change anything about rendering when I change its return value.

Link to comment
Share on other sites

18 hours ago, X-Lomir said:

Actually there is still 1 little problem in item rendering, which is the shadows on the item. If you compare how vertical slab items are rendered in master vs dev or how other block items vs vertical slab items in dev, it becomes quite obvious that in dev there are some shadows missing or at most they are too bright.

This fix was quite easy: just needed to set to true the return value of VerticalSlabBakedModel#usesBlockLight.

18 hours ago, X-Lomir said:

About 2nd and 3rd problems, I thought that maybe they are one and the same. Maybe since the referring block would cull certain faces then when I apply its UV onto my vertical slabs also the "invisible" UVs are applied? I actually have no idea if this makes sense ahah

This I understood the actual problems. About cullfaces, it's because faces that must not be culled are handled in getQuads when side is null. I had an if checking that parameter to be different from null and return an empty list otherwise, so that's to be removed. However for side = null the referring block, being a full opaque block, has no UVs. And this would also cause no textures to be applied, so also this must be fixed. Furthermore, for example, the south face of the normal vertical slab is not handled for side = south, but, as said before, for side = null. So for side = south the list of original baked quads is empty, so it never enters the for loop where the textures from the referring block are applied. And it's the same for the other corner versions of the vertical slabs. This also explains why changing in the JSON models the cullfaces holds better/worse results on dev.

For the 3rd problem, that was clear from before: the vertexes of the referring block are applied to the vertexes of the vertical slab, making the texture stretch.

I will now try to solve both problems and write again here once I'm done, so that if in future some one will ever need to do something similar they won't have to do it all over again (also I may need some more help about craftings after ;P)

Link to comment
Share on other sites

Culling problem fixed and also I discovered a problem with my cache and fixed that too.

I also discovered that vertical slabs will not emit light regardless of whether the referred block emits light (for example a vertical slab made of glowstone does not emit light as it should).

I can't seem to wrap my head around how vertexes work, so I'm putting this aside for now (moving the most updated code for render in rendering branch) and I will start working on setting the correct block properties for the given referredBlockState along with crafting recipes and picking up the block in creative.

I wrote on Forge Discord, or at least what I think it's Forge Discord, and I hope someone will be able to help with the vertexes.

I will report back here when I have a solution or have any more problems I can't solve on my own.

Also thank you again, you've been of great great help!

Link to comment
Share on other sites

Quick updates about rendering (I'm still putting aside this anyway):

  • Light is not an actual problem, it really just depends on the block property
  • An actual problem I discovered is that now the breaking progress is not shown anymore
  • No one replied on Discord 🙃

Meanwhile I made so that vertical slabs correctly drop themselves when broken, created a separated creative tab and made so that in creative you can correctly pick up a vertical slab. I'm having troubles with crafting and block properties.

Block properties are important because a vertical slab made of stone should have the same properties of a normal slab made of stone, same goes on with any material (and I hope normal slabs have the same properties of their respective blocks). But it seems to me that only the Block instance can set block properties, however I'd need to have block properties depending on the block I'm mimicking. Is there anyway to do this? I saw that some properties have getters that allow me to get the block entity, however I'm not sure if it will work correctly for multiplayer and anyway it doesn't always work because sometimes (I don't know why) the referringBlockState is null. Most updated code is here.

Crafting recipes I saw about having conditions based on NBTs, Tags, output items with specific NBTs, but I couldn't find a way to get the blockstate/NBT from the ingredient and set the output accordingly. The crafting recipes I need are the following: from blocks to vertical slab, from vertical slabs to block, from vertical slab to slab, from slab to vertical slab. So, for example, with the "from blocks to vertical slab" recipe I would need to get the default block state of the blocks being used in the crafting and set the resulting vertical slab item NBTs accordingly. Vice versa for "from vertical slabs to block", where I'd need to read the block state and output the proper block. "from vertical slab to slab" and "from slab to vertical slab" recipes would need to work similarly to the previous twos, but with the extra step of retrieving the block used to make the slab.

Link to comment
Share on other sites

So, I updated the branch with all the property overrides I could think of doing, I'm left with these ones:

  • materialColor (don't know if it will practically change anything tho)
  • material (but maybe by superseding all material derived properties superseding the material itself is not necessary?)
  • speedFactor
  • jumpFactor
  • isSlimeBlock (form IForgeBlock)
  • isStickyBlock (form IForgeBlock)
  • canStickTo (form IForgeBlock)
  • isSignalSource (form BlockBehavior)
  • getPistonPushReaction (material#getPushReaction)

But also I don't know if I'm leaving something behind or if I'm overdoing something. I just went through Block, IForgeBlock, BlockBehavior and BlockBehavior$Properties and overrided everything I could/I think I needed to.

Some I think may not work well tho, for instance I overrided getSoundType and although it works find most of the times, when placing a vertical slab it resorts to the default sound (stone). I can see that method being called 4 times when I place a vertical slab, 2 for each side, and when it's client side referringBlockState is null thus preventing the mimicked sound to play. But I have no idea how to fix it.

About the crafting, I will look some more into it, I had tried to make a custom recipe but without success, I will try again.

Link to comment
Share on other sites

12 minutes ago, diesieben07 said:

You probably need to create multiple slabs with different materials and then pick the right one of them depending on the block you mimic.

Mmh... I'm not sure if I want to do this because I'd like this mod to be as abstract as possible to support as many other mods as possible, so maybe restraining to Vanilla materials is not the best. For the time being I will stick with superseding all the derived properties I can, if I see something being off I may switch to having different slabs with different materials.

37 minutes ago, diesieben07 said:

Nothing you can do. But in theory Forge could add hooks for these.

Well, I guess I will just add these to the todo list if and when there will be a way to implement them.

38 minutes ago, diesieben07 said:

You really should not mimic these, as you have a BlockEntity, so your block cannot be moved by pistons anyways.

Yeah, you're right, I forgot about this.

39 minutes ago, diesieben07 said:

Do you really want this? Slabs are expected to not emit redstone.

I don't know. I thought that maybe if a redstone block slab is in the game due to some other mod, and thus also a redstone block vertical slab, it would have been nice to mimic the redstone emitting property, but now that you said this I'm not sure anymore ahah

51 minutes ago, diesieben07 said:

Most likely the method is just called before the client knows about the state. I do not know how to fix this to be quite honest.

I will definitely dig more into this and try to search for a solution.

Link to comment
Share on other sites

19 hours ago, diesieben07 said:

Most likely the method is just called before the client knows about the state. I do not know how to fix this to be quite honest.

So I think I found a solution.

In BlockItem#updateCustomBlockEntityTag the entity tag is updated only if level.getServer() != null. This is a problem because when it gets called client side it doesn't update the entity tag and so my BlockEntity doesn't have a referringBlockState. Indeed in multiplayer others would actually hear the correct sound (BlockEntity is updated, sound can be retrieved, level is server side, level#playSound will play it for anyone but the player), it's only the player placing the block that would not hear the correct sound (BlockEntity is not updated, sound defaults to Stone, level is client side, level#playSound will play it for the player only).

My solution is to override BlockItem#updateCustomBlockEntityTag (not the static one) and if BlockItem#updateCustomBlockEntityTag (the static one) returns false, then I check if it was because level#getServer() returned null and if so I do the same BlockItem#updateCustomBlockEntityTag (the static one) would do if level#getServer() did not return null.

It works and doesn't seem to be creating any problems, however I'm wondering if

  1. will it actually create some kind of problem because I'm updating my BlockEntity also client side (and if it doesn't create problems, why does it update only server side by default?)
  2. is there no better way of doing this other than copy-pasting the logic from BlockItem#updateCustomBlockEntityTag (the static one). Ideally it would be nice to have all the logic that actually updates the BlockEntity in a separated static method and then having BlockItem#updateCustomBlockEntityTag (the static one) call it if level#getServer() != null (and return false otherwise). This way it would also be possible to forcefully updating the BlockEntity by calling the method wrapped in the check, which is exactly what I'd need to.
Link to comment
Share on other sites

27 minutes ago, diesieben07 said:

Alternatively you can make a custom getSoundType variant

I would be interested in this, but I'm not sure what a variant is. I tried searching online but I found nothing useful.

Also in BlockItem#place getSoundType is never called with the itemStack, what am I missing?

I'm sorry if I'm missing something obvious or that should just be Java knowledge.  

Link to comment
Share on other sites

3 minutes ago, diesieben07 said:

Exactly, this is why I suggest you make a custom version of it.

Okay, so I just create this new method that takes an itemStack and returns a SoundType. But when will it get called? And also in BlockItem#place the "wrong" getSoundType would still be called.

I am so sure I'm missing something obvious but I just can't get what it is 😅

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.