new Block("mymod:mystone").setShape(Shapes.CUBE).setTexture("mymod:stone_texture").setStepSound(Sounds.STEP_ON_STONE)...
you now have to do some of this inline (the part that can't be customized in data packs):
new Block("mymod:mystone").setStepSound(Sounds.STEP_ON_STONE)...
but the rest is looked up across 5 different cross-referenced JSON files full of magic values with no autocomplete or syntax highlighting. Start with an indirection layer in assets/mymod/blockstates/mystone.json: [2]
{"variants": {"": {"model": "mystone"}}}
then of course you have to actually specify how to display the block: [3]
{"parent": "minecraft:block/cube_all", "textures": {"all": "fabric-docs-reference:block/steel_block"}}
(you see that? there are inheritance and variables in Minecraft's ad-hoc JSON language)
You need a second file to specify how to display the item when it's held in your hand. Usually it's similar boilerplate. But have a look at the abomination that is "item property overrides" [4] [5] as an example of inner-platforming. Instead of render(is_cast ? cast_model : uncast_model); there's this whole infrastructure of a registry of item property predicates written in Java which can then be referenced in JSON to select a different model file under specific conditions.
[1] https://thedailywtf.com/articles/the_inner-platform_effect
[2] https://docs.minecraftforge.net/en/1.12.x/models/blockstates...
[3] https://docs.fabricmc.net/develop/data-generation/block-mode...
[4] https://docs.minecraftforge.net/en/1.12.x/models/overrides/
[5] https://minecraft.fandom.com/wiki/Tutorials/Models#Example:_...