Posted August 29, 20196 yr I'm trying to learn how to use OBJ models for my mod, but I'm having some difficulty getting started. I've been trying to find some tutorials or documentation on how to do import them and use them, but what I've found is out of date, and some of the functions are removed/renamed. I've looked at a few tutorials, including the 1.13 model section of the Forge Docs website (uses IStateMapper, which no longer exists), as well as this Forge forum tutorial (outdated, and many of the functions used no longer exist). I also peeked at ModelLoader, but the function #setCustomModelResourceLocation (which a lot of what I've found use) commented out, so I'm assuming there's a new way to do it. Would anyone be able to point me towards current tutorials, documentation, or code examples so I can figure out how to use OBJ models? Or possibly outline it for me here? I'm specifically looking for info on how to do it for a Block, but help with Items or Entities would also be useful. Many thanks!
August 29, 20196 yr Just point the "model" parameter in your blockstate to .obj file (extension is needed!) instead of json-model and include OBJLoader.INSTANCE.addDomain(YOUR_MOD_ID); in your ClientProxy (in static). Edited August 29, 20196 yr by ZigTheHedge
August 29, 20196 yr Author Wow, is it really that simple now? That seems a lot easier than previous versions. I seem to still be doing something wrong. I'm getting "[minecraft/ModelBakery]: Unable to load model: 'wabbits:block/test_block.obj' referenced from: wabbits:test_block#: java.io.FileNotFoundException: wabbits:models/block/test_block.obj.json" when launching, and the block is the standard pink and black "no-texture-found" block. It looks like ".json" is being appended to my file name? Perhaps I'm just doing something wrong? Do I need to do anything with ModelBakery? Here's my blockstate test_block.json: { "variants": { "": { "model": "wabbits:test_block.obj" } } } And my ClientProxy: public class ClientProxy implements IProxy { static { OBJLoader.INSTANCE.addDomain(Constants.MOD_ID); //MOD_ID == "wabbits" } @Override public void init() { } @Override public World getClientWorld() { return Minecraft.getInstance().world; } @Override public PlayerEntity getClientPlayer() { return Minecraft.getInstance().player; } } EDIT: I noticed poking through the Forge GitHub that the .obj file paths don't seem to need the folder in front of it, so I've changed my blockstate .json to have "": { "model": "wabbits:test_block.obj" } instead of "": { "model": "wabbits:blocks/test_block.obj" } Edited August 29, 20196 yr by TheMikeste1 Blockstate JSON update
August 29, 20196 yr Author After poking around a bit more, I found the ModelRegistryEvent. I assume I'm supposed to register my models here? This is what I have right now: @Mod.EventBusSubscriber(modid = Constants.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public class Models { @SubscribeEvent public static void registerModels(ModelRegistryEvent event) { ModelLoader.addSpecialModel(ResourceLocation.tryCreate("wabbits:models/block/test_block.obj")); OBJModel model; try { model = (OBJModel) OBJLoader.INSTANCE.loadModel(ResourceLocation.tryCreate("wabbits:models/block/test_block.obj")); } catch (Exception e) { e.printStackTrace(); } } } I'm using #addSpecialModel since #setCustomModelResourceLocation is commented out. Looking at them, they don't seem to be do the same thing so I doubt that's the solution. I also noticed that OBJLoader has #loadModel, so I attempted to load in the model. Unfortunately, with the way I'm currently set up, I get a NullPointerException from manager being null on OBJLoader line 85. I don't think I can set manager to anything (it's private), so is there a different time I do this?
August 30, 20196 yr Author So I've made some substantial progress! I eventually found an OBJ test on the MC Forge Github (models, code), and my model now renders in-game! Unfortunately, the model is totally black (it's supposed to be bright yellow). If anyone has any insight as to why that may be, I'd appreciate it. Attached are my model files in case those would be useful.. test_block.mtl test_block.obj Edited August 30, 20196 yr by TheMikeste1
August 30, 20196 yr Author 11 hours ago, ZigTheHedge said: Try flipping "V" in your blockstate "custom": { "flip-v": true } Thanks for your suggestion, but unfortunately it didn't help. I'm now trying to use "map_Kd" to get the texture, but that doesn't seem to work, either. It changes to the "no texture found" texture. If I remove map_Kd, it goes back to black. I'm going to keep trying to figure it out. I've been searching through parts of the Forge Github, but there's a lot to go through. In the mean time, if anyone is willing to give me a hand, here's my repository (model_test branch): TestBlock ClassModel Registering test_block Blockstatetest_block.obj test_block.mtl And an image of what the block currently looks like (with map_Kd): Edited August 30, 20196 yr by TheMikeste1 Added image
August 31, 20196 yr Author 8 hours ago, DefectiveProgram said: Try removing ".png" from the texture path in your .mtl maybe? Unfortunately that doesn't work either. I've tried several different paths with and without the .png. I'm probably missing something super obvious that I just can't figure out.
August 31, 20196 yr Try using a vanilla texture (Also without ".png" as that appears to be automatically appended.) I'm guessing the texture isn't being loaded.
August 31, 20196 yr Author 8 minutes ago, DefectiveProgram said: Try using a vanilla texture (Also without ".png" as that appears to be automatically appended.) I'm guessing the texture isn't being loaded. Huh, that works. I guess I should have thought of that. So with OBJ models do I need to tell it to load in my texture? I had assumed it would load them in like it does with other blocks. I notice there's a lot of z-fighting though. Also, why is it so dark?
August 31, 20196 yr You can use "TextureStitchEvent.Pre" to load the texture. And the z-fighting is either an issue with your model or the parser.
August 31, 20196 yr Author 7 minutes ago, DefectiveProgram said: You can use "TextureStitchEvent.Pre" to load the texture. That did it! Thank you so much!
September 28, 20195 yr I'm having trouble following your process here. Could you please update us on what your end result looks like to make it easier to reproduce?
October 20, 20195 yr On 9/1/2019 at 2:36 AM, TheMikeste1 said: That did it! Thank you so much! Can you please post the source code? Nvm. Sorry. I keep getting the error that you got: On 8/29/2019 at 10:41 PM, TheMikeste1 said: I seem to still be doing something wrong. I'm getting "[minecraft/ModelBakery]: Unable to load model: 'wabbits:block/test_block.obj' referenced from: wabbits:test_block#: java.io.FileNotFoundException: wabbits:models/block/test_block.obj.json" when launching, and the block is the standard pink and black "no-texture-found" block. It looks like ".json" is being appended to my file name? Perhaps I'm just doing something wrong? Do I need to do anything with ModelBakery? Any solutions? Edited October 20, 20195 yr by jun2040
October 20, 20195 yr I'm getting these exceptions. I get the black and purple texture in the game. [15:34:09] [Server-Worker-3/WARN] [minecraft/ModelBakery]: Exception loading blockstate definition: unixmod:blockstates/fabricator.json: java.io.FileNotFoundException: unixmod:blockstates/fabricator.json [15:34:09] [Server-Worker-3/WARN] [minecraft/ModelBakery]: Exception loading blockstate definition: 'unixmod:blockstates/fabricator.json' missing model for variant: 'unixmod:fabricator#' [15:34:10] [Server-Worker-3/WARN] [minecraft/ModelBakery]: Unable to load model: 'unixmod:fabricator#inventory' referenced from: unixmod:fabricator#inventory: java.io.FileNotFoundException: unixmod:models/item/fabricator.json Here's my blockstates: { "variants": { "facing=north": { "model": "unixmod:fabricator.obj" }, "facing=south": { "model": "unixmod:fabricator.obj", "y": 180 }, "facing=west": { "model": "unixmod:fabricator.obj", "y": 270 }, "facing=east": { "model": "unixmod:fabricator.obj", "y": 90 }, "facing=up": { "model": "unixmod:fabricator.obj", "x": -90 }, "facing=down": { "model": "unixmod:fabricator.obj", "x": 90 }, } } obj: mtllib block.mtl o Cube v 8.000000 8.000000 -8.000000 v 8.000000 -8.000000 -8.000000 v 8.000000 8.000000 8.000000 v 8.000000 -8.000000 8.000000 v -8.000000 8.000000 -8.000000 v -8.000000 -8.000000 -8.000000 v -8.000000 8.000000 8.000000 v -8.000000 -8.000000 8.000000 l 6 8 l 2 6 l 1 2 l 8 7 l 3 4 l 5 6 l 3 7 l 1 3 l 8 4 l 7 5 l 5 1 l 4 2 o Cube.001 v 5.000000 5.000000 -5.000000 v 5.000000 -5.000000 -5.000000 v 5.000000 5.000000 5.000000 v 5.000000 -5.000000 5.000000 v -5.000000 5.000000 -5.000000 v -5.000000 -5.000000 -5.000000 v -5.000000 5.000000 5.000000 v -5.000000 -5.000000 5.000000 vt 0.286909 0.500000 vt 0.073817 0.286909 vt 0.286909 0.286909 vt 0.500000 0.286909 vt 0.500000 0.500000 vt 0.286909 0.713091 vt 0.713091 0.500000 vt 0.286909 0.926183 vt 0.500000 0.713091 vt 0.500000 0.926183 vt 0.500000 0.073817 vt 0.286909 0.073817 vt 0.073817 0.500000 vt 0.713091 0.286909 vn -1.0000 0.0000 0.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 1.0000 0.0000 0.0000 vn 0.0000 1.0000 0.0000 vn 0.0000 0.0000 -1.0000 usemtl Material s off f 16/1/1 13/2/1 14/3/1 f 10/4/2 16/1/2 14/3/2 f 12/5/3 15/6/3 16/1/3 f 10/4/4 11/7/4 12/5/4 f 13/8/5 11/9/5 9/10/5 f 9/11/6 14/3/6 13/12/6 f 16/1/1 15/13/1 13/2/1 f 10/4/2 12/5/2 16/1/2 f 12/5/3 11/9/3 15/6/3 f 10/4/4 9/14/4 11/7/4 f 13/8/5 15/6/5 11/9/5 f 9/11/6 10/4/6 14/3/6 mtl: newmtl Material Ns 323.999994 Ka 1.000000 1.000000 1.000000 Kd 0.800000 0.000000 0.005390 Ks 0.500000 0.500000 0.500000 Ke 0.0 0.0 0.0 Ni 1.450000 d 1.000000 illum 2 Fabricator: package com.jun2040.unixmod.blocks; import net.minecraft.block.Block; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.shapes.ISelectionContext; import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.IBlockReader; public class Fabricator extends Block { public Fabricator() { super(Properties.create(Material.IRON) .sound(SoundType.METAL) .hardnessAndResistance(2.0f) ); setRegistryName("fabricator"); } @Override public BlockRenderType getRenderType(BlockState state) { return BlockRenderType.MODEL; } } ClientProxy: package com.jun2040.unixmod.setup; import com.jun2040.unixmod.UnixMod; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.world.World; import net.minecraftforge.client.model.obj.OBJLoader; public class ClientProxy implements IProxy { static { OBJLoader.INSTANCE.addDomain(UnixMod.MODID); } @Override public void init() { } @Override public World getClientWorld() { return Minecraft.getInstance().world; } @Override public PlayerEntity getClientPlayer() { return Minecraft.getInstance().player; } } Main mod class: package com.jun2040.unixmod; import com.jun2040.unixmod.blocks.Fabricator; import com.jun2040.unixmod.blocks.ModBlocks; import com.jun2040.unixmod.setup.ClientProxy; import com.jun2040.unixmod.setup.IProxy; import com.jun2040.unixmod.setup.ModSetup; import com.jun2040.unixmod.setup.ServerProxy; import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.model.IUnbakedModel; import net.minecraft.client.renderer.model.ModelResourceLocation; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.model.BasicState; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.client.model.obj.OBJLoader; import net.minecraftforge.client.model.obj.OBJModel; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.InterModComms; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent; import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.stream.Collectors; @Mod("unixmod") public class UnixMod { public static final String MODID = "sourcemod"; public static IProxy proxy = DistExecutor.runForDist(() -> () -> new ClientProxy(), () -> () -> new ServerProxy()); public static ModSetup setup = new ModSetup(); private static final Logger LOGGER = LogManager.getLogger(); public UnixMod() { FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff); MinecraftForge.EVENT_BUS.register(this); } private void setup(final FMLCommonSetupEvent event) { } private void doClientStuff(final FMLClientSetupEvent event) { OBJLoader.INSTANCE.addDomain(UnixMod.MODID); } private void enqueueIMC(final InterModEnqueueEvent event) { } private void processIMC(final InterModProcessEvent event) { } @SubscribeEvent public void onServerStarting(FMLServerStartingEvent event) { } @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public static class RegistryEvents { @SubscribeEvent public static void onBlocksRegistry(final RegistryEvent.Register<Block> event) { event.getRegistry().register(new Fabricator()); } @SubscribeEvent public static void onItemsRegistry(final RegistryEvent.Register<Item> event) { event.getRegistry().register(new BlockItem(ModBlocks.FABRICATOR, new Item.Properties()).setRegistryName("fabricator")); } @SubscribeEvent public static void onModelBakeEvent(ModelBakeEvent event) { try { IUnbakedModel model = ModelLoaderRegistry.getModelOrMissing(new ResourceLocation("unixmod:fabricator.obj")); if (model instanceof OBJModel) { IBakedModel bakedModel = model.bake(event.getModelLoader(), ModelLoader.defaultTextureGetter(), new BasicState(model.getDefaultState(), false), DefaultVertexFormats.ITEM); event.getModelRegistry().put(new ModelResourceLocation("stick", "inventory"), bakedModel); } } catch (Exception e) { e.printStackTrace(); } } } }
October 23, 20195 yr Using code from TheMikeste1 wabbits, all i get is a untextured block. On 8/31/2019 at 3:57 PM, DefectiveProgram said: You can use "TextureStitchEvent.Pre" to load the texture. And the z-fighting is either an issue with your model or the parser. Only has adding a Sprite texture. Any help would be appreciated
October 24, 20195 yr Author @ehaugw I handled the Pre Stich event here. @jun2040 I created a model.json for my test block in addition to the other files. See here. @blinky000 My models map a .png file to the sides of the block. See my .mtl file.
October 24, 20195 yr @TheMikeste1 I created the model.json for my block and the block doesn't seem to render the model for some reason. I also got some problems with the item. here's my github: https://github.com/jun2040/unixmod Edit: I commented out the model registries thing... The model works now but the item's not working and the model's texture is too dark. Edited October 24, 20195 yr by jun2040
October 24, 20195 yr jun2040 you commented out the onModelBakeEvent() ? and it worked? all my messing around has got me an empty cube
October 24, 20195 yr 1 hour ago, blinky000 said: jun2040 you commented out the onModelBakeEvent() ? and it worked? all my messing around has got me an empty cube Um. I meant I commented that out and fixed that. I uncommented it. But the textures are still dark and there's this weird thing going on with my block and things beside the block. Do you know how to register multiple models on onModelBakeEvent()? Edited October 24, 20195 yr by jun2040
October 24, 20195 yr Ok , i got TheMikeste1's model to work, but tied haven't registered item yet, and the color is much darker then the original texture. now im worried that the texture HAS to me hard coded in the mtl . previous versions you could define the texture in the blockstate
October 24, 20195 yr This thread was made before Forge blockstates were fixed for 1.14.4. With their introduction, manually stitching the texture and loading the block model is no longer necessary as those were workarounds. Item blocks (probably items as well) still need to use the ModelBakeEvent though. For item blocks it would seem you can just grab the model from the model registry, as it appears to be loaded before the event fires. Then pass it through a PerspectiveMapWrapper to apply the perspective transforms. And just put the PerspectiveMapWrapper into the model registry with the inventory variant. And here are the item block transforms to pass into the PerspectiveMapWrapper: private static final TRSRTransformation THIRD_PERSON_BLOCK = Transforms.convert(0, 2.5f, 0, 75, 45, 0, 0.375f); private static final ImmutableMap<TransformType, TRSRTransformation> BLOCK_TRANSFORMS = ImmutableMap.<TransformType, TRSRTransformation>builder() .put(TransformType.GUI, Transforms.convert(0, 0, 0, 30, 225, 0, 0.625f)) .put(TransformType.GROUND, Transforms.convert(0, 3, 0, 0, 0, 0, 0.25f)).put(TransformType.FIXED, Transforms.convert(0, 0, 0, 0, 0, 0, 0.5f)) .put(TransformType.THIRD_PERSON_RIGHT_HAND, THIRD_PERSON_BLOCK) .put(TransformType.THIRD_PERSON_LEFT_HAND, Transforms.leftify(THIRD_PERSON_BLOCK)) .put(TransformType.FIRST_PERSON_RIGHT_HAND, Transforms.convert(0, 0, 0, 0, 45, 0, 0.4f)) .put(TransformType.FIRST_PERSON_LEFT_HAND, Transforms.convert(0, 0, 0, 0, 225, 0, 0.4f)) .build(); 2 hours ago, blinky000 said: now im worried that the texture HAS to me hard coded in the mtl . previous versions you could define the texture in the blockstate Yeah, the blockstate loader seems to completely ignore the textures tag. (I haven't experimented with items, so I can't help as much there.) Edited October 24, 20195 yr by DefectiveProgram
October 24, 20195 yr Using perspective handling from previous post I just got working block & item rendering from custom OBJ model, there is code snipped if it helps: private static final TRSRTransformation THIRD_PERSON_BLOCK = Transforms.convert(0, 2.5f, 0, 75, 45, 0, 0.375f); private static final ImmutableMap<TransformType, TRSRTransformation> BLOCK_TRANSFORMS = ImmutableMap.<TransformType, TRSRTransformation>builder() .put(TransformType.GUI, Transforms.convert(0, 0, 0, 30, 225, 0, 0.625f)) .put(TransformType.GROUND, Transforms.convert(0, 3, 0, 0, 0, 0, 0.25f)) .put(TransformType.FIXED, Transforms.convert(0, 0, 0, 0, 0, 0, 0.5f)) .put(TransformType.THIRD_PERSON_RIGHT_HAND, THIRD_PERSON_BLOCK) .put(TransformType.THIRD_PERSON_LEFT_HAND, Transforms.leftify(THIRD_PERSON_BLOCK)) .put(TransformType.FIRST_PERSON_RIGHT_HAND, Transforms.convert(0, 0, 0, 0, 45, 0, 0.4f)) .put(TransformType.FIRST_PERSON_LEFT_HAND, Transforms.convert(0, 0, 0, 0, 225, 0, 0.4f)) .build(); @SubscribeEvent public static void onModelBakeEvent(ModelBakeEvent event) { try { IUnbakedModel model = ModelLoaderRegistry.getModelOrLogError(new ResourceLocation("toomanyores:block/water_wheel.obj"), "Missing water wheel model"); if (model instanceof OBJModel) { IBakedModel bakedModel = model.bake(event.getModelLoader(), ModelLoader.defaultTextureGetter(), new BasicState(model.getDefaultState(), true), DefaultVertexFormats.BLOCK); IBakedModel bakedInvModel = model.bake(event.getModelLoader(), ModelLoader.defaultTextureGetter(), new BasicState(model.getDefaultState(), true), DefaultVertexFormats.ITEM); bakedInvModel = new PerspectiveMapWrapper(bakedInvModel, BLOCK_TRANSFORMS); event.getModelRegistry().put(new ModelResourceLocation("toomanyores:waterwheel", ""), bakedModel); event.getModelRegistry().put(new ModelResourceLocation("toomanyores:waterwheel", "inventory"), bakedInvModel); } } catch (Exception e) { e.printStackTrace(); } }
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.