Minecraft Forge 1.7.2/1.7.10 Converting Techne Models Into Hierarchical Models

Background

If you have a complex model that you'd like to animate, you'll quickly find that any time you rotate one piece, keeping all the other pieces "connected" takes a lot of math because you have to both move and rotate those pieces to stay together.

To solve this problem, the ModelRenderer class allows a hierarchy where one can be "child" to another.  The position and rotation of the child then become relative to the parent part.  These can be chained so that you can have a child of a child.  For example, if you had fingers as child of a hand that is child of a forearm, then if you move the fingers they move on their own but if you move the hand it would move the hand with the fingers and if you move the forearm it would move the forearm with the hand and fingers.)

This is great if you're creating your model directly in Java; however, if you have created a complex model in Techne you may find that it can be a lot of work to convert it into a hierarchical model. This is because you have to take all the rotation points and make them relative, meaning subtracting out the parent's rotation point.  If you have a lot of parts, this is time-consuming and error-prone.  Instead you can use a simple helper function method which I call convertToChild() to do this for you.

ConvertToChild() Helper Function Method

Add this method to your model class:

// This is really useful for converting the source from a Techne model export
// which will have absolute rotation points that need to be converted before
// creating the addChild() relationship
protected void convertToChild(ModelRenderer parParent, ModelRenderer parChild)
{
   // move child rotation point to be relative to parent
   parChild.rotationPointX -= parParent.rotationPointX;
   parChild.rotationPointY -= parParent.rotationPointY;
   parChild.rotationPointZ -= parParent.rotationPointZ;
   // make rotations relative to parent
   parChild.rotateAngleX -= parParent.rotateAngleX;
   parChild.rotateAngleY -= parParent.rotateAngleY;
   parChild.rotateAngleZ -= parParent.rotateAngleZ;
   // create relationship
   parParent.addChild(parChild);
}

Basically this method makes the rotation point and rotation angle to be relative (by subtracting out the parent's values) and then does the addChild().

Warning: Make sure your rotation points in Techne are in proper place before trying to convert it. The rotation point should be where you want rotation to occur, which is usually the connection point between pieces.  For the main body that might be the center, for the an arm that would be the shoulder.  This seems obvious but many people mistakenly use offsets to make their model look correct despite incorrect rotation points.

Warning: It is very important in models with multiple levels of child parts to use the convertToChild() in the proper order, starting with the extremities and moving towards the ultimate parent.  For example, if you had a body with an arm that had a forearm, hand and fingers, you should convert the fingers first as child to hand, then hand to forearm, etc.

Example: Convert The Vanilla ModelBiped to Hierarchical Model


      head = new ModelRenderer(this, 0, 0);
      head.addBox(-4F, -8F, -4F, 8, 8, 8);
      head.setRotationPoint(0F, 0F, 0F);
      head.setTextureSize(64, 32);
      head.mirror = true;
      setRotation(head, 0F, 0F, 0F);
      body = new ModelRenderer(this, 16, 16);
      body.addBox(-4F, 0F, -2F, 8, 12, 4);
      body.setRotationPoint(0F, 0F, 0F);
      body.setTextureSize(64, 32);
      body.mirror = true;
      setRotation(body, 0F, 0F, 0F);
      convertToChild(body, head);
      rightarm = new ModelRenderer(this, 40, 16);
      rightarm.addBox(-3F, -2F, -2F, 4, 12, 4);
      rightarm.setRotationPoint(-5F, 2F, 0F);
      rightarm.setTextureSize(64, 32);
      rightarm.mirror = true;
      setRotation(rightarm, 0F, 0F, 0F);
      convertToChild(body, rightarm);
      leftarm = new ModelRenderer(this, 40, 16);
      leftarm.addBox(-1F, -2F, -2F, 4, 12, 4);
      leftarm.setRotationPoint(5F, 2F, 0F);
      leftarm.setTextureSize(64, 32);
      leftarm.mirror = true;
      setRotation(leftarm, 0F, 0F, 0F);
      convertToChild(body, leftarm);
      rightleg = new ModelRenderer(this, 0, 16);
      rightleg.addBox(-2F, 0F, -2F, 4, 12, 4);
      rightleg.setRotationPoint(-2F, 12F, 0F);
      rightleg.setTextureSize(64, 32);
      rightleg.mirror = true;
      setRotation(rightleg, 0F, 0F, 0F);
      convertToChild(body, rightleg);
      leftleg = new ModelRenderer(this, 0, 16);
      leftleg.addBox(-2F, 0F, -2F, 4, 12, 4);
      leftleg.setRotationPoint(2F, 12F, 0F);
      leftleg.setTextureSize(64, 32);
      leftleg.mirror = true;
      setRotation(leftleg, 0F, 0F, 0F);
      convertToChild(body, leftleg);
      head = new ModelRenderer(this, 0, 0);
      head.addBox(-4F, -8F, -4F, 8, 8, 8);
      head.setRotationPoint(0F, 0F, 0F);
      head.setTextureSize(64, 32);
      head.mirror = true;
      setRotation(head, 0F, 0F, 0F);
      body = new ModelRenderer(this, 16, 16);
      body.addBox(-4F, 0F, -2F, 8, 12, 4);
      body.setRotationPoint(0F, 0F, 0F);
      body.setTextureSize(64, 32);
      body.mirror = true;
      setRotation(body, 0F, 0F, 0F);
      convertToChild(body, head);
      rightarm = new ModelRenderer(this, 40, 16);
      rightarm.addBox(-3F, -2F, -2F, 4, 12, 4);
      rightarm.setRotationPoint(-5F, 2F, 0F);
      rightarm.setTextureSize(64, 32);
      rightarm.mirror = true;
      setRotation(rightarm, 0F, 0F, 0F);
      convertToChild(body, rightarm);
      leftarm = new ModelRenderer(this, 40, 16);
      leftarm.addBox(-1F, -2F, -2F, 4, 12, 4);
      leftarm.setRotationPoint(5F, 2F, 0F);
      leftarm.setTextureSize(64, 32);
      leftarm.mirror = true;
      setRotation(leftarm, 0F, 0F, 0F);
      convertToChild(body, leftarm);
      rightleg = new ModelRenderer(this, 0, 16);
      rightleg.addBox(-2F, 0F, -2F, 4, 12, 4);
      rightleg.setRotationPoint(-2F, 12F, 0F);
      rightleg.setTextureSize(64, 32);
      rightleg.mirror = true;
      setRotation(rightleg, 0F, 0F, 0F);
      convertToChild(body, rightleg);
      leftleg = new ModelRenderer(this, 0, 16);
      leftleg.addBox(-2F, 0F, -2F, 4, 12, 4);
      leftleg.setRotationPoint(2F, 12F, 0F);
      leftleg.setTextureSize(64, 32);
      leftleg.mirror = true;
      setRotation(leftleg, 0F, 0F, 0F);
      convertToChild(body, leftleg);

7 comments:

  1. Where do I put the Techne models?

    ReplyDelete
  2. Where do I put the Techne models?

    ReplyDelete
  3. Why is it that your convertToChild method makes duplicates of the old one and keeps the new one?

    ReplyDelete
    Replies
    1. I don't make any copies. I'm working directly on the passed in objects and modifying them directly. So maybe I don't understand your question.

      Delete
  4. Oh my god, you just saved me a shit ton of work! Thank you so much!!

    ReplyDelete
  5. Hey Jabelar for some reason when I make the child the same two pieces that are children are placed there at the center of the body. Am I suppossed to removed the render section?

    ReplyDelete
  6. Is it possible to apply mirror transformation to a .obj model using code?

    ReplyDelete