Last time, I talked about setting up Forge Multipart in a mod in order to add a part for putting in the world. This time, I’ll talk about giving that part physical boundaries and making it render with an OBJ model made in Blender.

So to get started, a model is needed. Although the model won’t be used until a little later, it’s an important first step for defining exactly the boundaries of the part.

I won’t go into detail on how to use blender, but there are a few little things that I needed to figure out to get the exported OBJ file working as expected.

Make sure your normals are pointing the right direction. I didn’t realize that mine somehow ended up incorrect. Since Blender renders both sides of a face in its 3d viewer, I didn’t see what was going on until I tried to render the model in Minecraft. Essentially the model looked inverted. The solution was just inverting the normals.

In the OBJ export dialogue, make sure to disable “Write Materials” and enable “Triangulate Faces.”

As for the texture, again I won’t go into great detail. On the left hand side of the 3d view, in edit mode, go to the “Shading / UVs” tab, click the drop-down in “UV Mapping”, and select “Smart UV Project.” Then choose the screen layout “UV Editing,” go to the “UVs” menu at the bottom, and click “Export UV Layout.” This will give you a file with an outline for the texture that you can edit over to get the desired appearance.

Now that there’s a model, the second step is defining the bounding boxes for StoragePart to match the model. There are two boxes. The large one at the base goes from (0.125, 0, 0.125) to (0.875, 0.0625, 0.875), and the smaller one on top goes from (0.3125, 0.0625, 0.3125) to (0.6875, 0.125, 0.6875). Those numbers, and consequently the OBJ model, represent the part being pointed down. We’ll worry about rotation later, so for now these numbers will never change during the runtime of the mod. Which means these can be made constant in the StoragePart class, to save from allocating new boxes every time getCollisionBoxes is called.

So now the part has its bounding boxes set up as though it were pointed down. If you load up the world, you’d be able to hover over these boxes to see the outlines.

The next step is to render the model.

This is a static utility method that will simply render the part at whatever (x,y,z) you ask it to render at. The model file goes in the assets folder at models/storage.obj, and the texture goes in textures/parts/storage.png.

The AdvancedModelLoader class provides a method for loading a model from a resource location, which makes it super easy to load the OBJ model with AdvancedModelLoader.loadModel.

The two interesting lines are the bindTexture and renderAll calls. The bindTexture call makes it so your model will render with your texture, instead of whatever texture was used last. Then the renderAll call does exactly what it sounds like; it renders your model with the bound texture.

The last thing to do is call the render method from the part. The renderDynamic method is the one we want to override in StoragePart for rendering. The other option is renderStatic, but there’s a bit of incompatibility with that, the tesselator, and the model that I won’t go into.

Finally the part shows up in the world!

(Sidenote: I realize using a static method isn’t very elegant. I tried to think of a good way to make a renderer object that is tied to the part constructor, but I just couldn’t get it all to line up how I wanted. Also, yes, my texture is awful)

Transformations using CodeChickenLib

Rotating the part to its orientation (defined in StoragePart as the direction variable) is the next thing to do. I’ve seen lots of mods implement this by hand coding different glRotatef calls for each direction in the renderer, and by manually writing out all the coordinates for 6 different rotations of the box. But that isn’t necessary. CodeChickenLib has some nice API for transforming vectors.

The Transformation class is an abstract class that defines various methods for transforming vectors, and combining transformations. What we’re interested in are the Cuboid6.apply(Transformation), and Transformation.glApply() methods.

Cuboid6.apply(Transformation) mutates the Cuboid6 by transforming its min and max vectors with the transformation. For example, with the Translation transformation.

This would move the box +1 in the x direction, +2 in the y direction, and +3 in the z direction.

Similarly, the Transformation.glApply() method calls the appropriate OpenGL calls to render such a transformation.

The Rotation transformation class is similar to the Translation class.

The advantages to using the Rotation class are they can be used to rotate Cuboid6s, and they’re composable. Meaning you can chain several transformations together and hold it as an object for later use.

(Note: When chaining transformations, CodeChickenLib does try to combine them into one transformation matrix. It’s pretty cool, and I imagine it’s a little more efficient)

Defining a rotations helper class

Our main goal with the Rotation transformation is to take something that faces one ForgeDirection, and make it face another ForgeDirection. So there should be a static method to do that.

Now, there might as well be a cached version of this, so that we don’t have to construct new Transformation objects every time we want to do a transformation.

There. Now we have a gc-overhead-less way of fetching rotations from any direction to any direction.

Finally, there needs to be a method for generating all the necessary rotations of a part’s collision boxes.

Notice that the canonical cube is copied for each direction. Remember that Cuboid6.apply mutates the cube instance. It’s not creating a new one. So the cube needs to be copied.

With this method, StoragePart can simply make the boxes for the default facing that the model has, and get back all the rotations it needs.

The last thing to do will be applying the transformations to the renderer. Luckily, by the nature of the Transformation class, this is really easy.

And that’s it! The part now rotates visually and physically, and all the boilerplate for that got turned into some useful rotations API.

Next time, I’ll finish this up by showing how to use CC-Tweaks API to make this part a piece of the network that provides a peripheral.