Skip to content
GoldSloth edited this page Feb 4, 2025 · 3 revisions

This example file extends the wavefront obj loading capabilities of FlansMod to recalculate normals from the model vertices, and locate models inside of content packs. This should work on any version of FlansMod, though I have not yet tested outside of FMUSE - any issues please let me know in Discord.

The addObj method will look through every folder in the Flan directory and check if the obj filename you have supplied is in their objModels subfolder. Note that .jar or .zip folders aren't currently supported. image

// Change this to your package name, e.g. com.flansmod.client.model.mw
package com.flansmod.client.model.YOURPACKAGENAME;

// Import flans stuff
import com.flansmod.client.model.ModelPlane;
import com.flansmod.client.tmt.*;
import com.flansmod.common.FlansMod;
import com.flansmod.common.vector.Vector3f;
import net.minecraft.util.Vec3;

// Import stuff needed for handling obj file
import java.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

// Must correspond to filename (ModelChair.java)
// ModelPlane, ModelVehicle, ModelGun, ModelBullet e.t.c.
public class ModelYOURMODELNAME extends ModelPlane
{
    public ModelYOURMODELNAME()
    {
        // This section is where your model is created.

        int textureX = 0; //Enter your texture size here
        int textureY = 0; //Enter your texture size here

        /*
        Define the parts you need here, e.g.

        Setup a list of two models for the core
        bodyModel = new ModelRendererTurbo[2];
        bodyModel[0] = new ModelRendererTurbo(this, 0, 0, textureX, textureY);
        bodyModel[1] = new ModelRendererTurbo(this, 0, 0, textureX, textureY);

        addObj(bodyModel[0], "bmp1.obj");
        addObj(bodyModel[1], "bmp2.obj");

        If you're not sure what to add in this part, create a model in toolbox with a box for each obj file you have in each part. You will need separate obj files for different parts, e.g. for wheels to rotate they must be in a separate obj file.

        Export this as a java model, and remove addBox() e.t.c. and replace with addObj().
         */

        // Change this if you need.
        translateAll(0F, 0F, 0F);
    }

    // Include this code in the file, but don't modify it.
    public static void addObj(ModelRendererTurbo mrt, String filePath) {
        try {
            // Get texture group and transform group via reflection, as these are not exposed.
            Field fieldTextureGroup = ModelRendererTurbo.class.getDeclaredField("textureGroup");
            fieldTextureGroup.setAccessible(true);

            Map<String, TextureGroup> txtG = (Map<String, TextureGroup>)fieldTextureGroup.get(mrt);

            Field fieldTransformGroup = ModelRendererTurbo.class.getDeclaredField("transformGroup");
            fieldTransformGroup.setAccessible(true);

            Map<String, TransformGroup> trfG = (Map<String, TransformGroup>)fieldTransformGroup.get(mrt);

            String foundFileLocation = null;
            for (File packDir : Objects.requireNonNull(FlansMod.flanDir.listFiles())) {
                if (packDir.isDirectory()) {
                    String newPath = packDir.getAbsolutePath() + "/objModels/" + filePath;

                    File file = new File(newPath);

                    if (file.exists()) {
                        foundFileLocation = packDir.getName() + "/objModels/" + filePath;
                    }
                }
            }

            if (foundFileLocation == null)
            {
                FlansMod.log("Couldn't find OBJ file in /Flan/*/objModels/" + filePath);
                return;
            }

            ModelPoolEntry entry = ModelPool.addFile(foundFileLocation, ModelPool.OBJ, trfG, txtG);
            if (entry == null) return;

            PositionTextureVertex[] verts = Arrays.copyOf(entry.vertices, entry.vertices.length);
            TexturedPolygon[] poly = new TexturedPolygon[entry.faces.length];

            int i = 0;
            for (TexturedPolygon tp : entry.faces)
            {
                TexturedPolygon newTp = new TexturedPolygon(tp.vertexPositions);

                Vec3 normalVector = Vec3.createVectorHelper(0,1,0);

                if (tp.vertexPositions.length > 2) {
                    Vec3 v1 = Vec3.createVectorHelper(
                            tp.vertexPositions[1].vector3D.xCoord - tp.vertexPositions[0].vector3D.xCoord,
                            tp.vertexPositions[1].vector3D.yCoord - tp.vertexPositions[0].vector3D.yCoord,
                            tp.vertexPositions[1].vector3D.zCoord - tp.vertexPositions[0].vector3D.zCoord
                    );

                    Vec3 v2 = Vec3.createVectorHelper(
                            tp.vertexPositions[2].vector3D.xCoord - tp.vertexPositions[0].vector3D.xCoord,
                            tp.vertexPositions[2].vector3D.yCoord - tp.vertexPositions[0].vector3D.yCoord,
                            tp.vertexPositions[2].vector3D.zCoord - tp.vertexPositions[0].vector3D.zCoord
                    );

                    normalVector = v1.crossProduct(v2).normalize();
                }

                newTp.setNormals((float)normalVector.xCoord, (float)normalVector.yCoord, (float)normalVector.zCoord);

                poly[i++] = newTp;
            }

            mrt.copyTo(verts, poly, false);
        } catch (Exception ex) {
            FlansMod.log("OBJ failed to load :(");
        }

    }
}

Example file

// Package for your model, pre-1.12 format packs may use com.flansmod.client.model.modernweapons instead
// This should correspond to the folder path your model is in.
package com.flansmod.modernweapons.client.model;

// Import flans stuff
import com.flansmod.client.model.ModelPlane;
import com.flansmod.client.tmt.*;
import com.flansmod.common.FlansMod;
import com.flansmod.common.vector.Vector3f;
import net.minecraft.util.Vec3;

// Import stuff needed for handling obj file
import java.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;

// Must correspond to filename (ModelChair.java)
// ModelPlane, ModelVehicle, ModelGun, ModelBullet e.t.c.
public class ModelChair extends ModelPlane
{
    public ModelChair()
    {
        // This section is where your model is created.

        int textureX = 512; //The x-size of the texture
        int textureY = 256;	//The y-size of the texture

        // Create a list of models for the body that is length 1.
        bodyModel = new ModelRendererTurbo[1];

        // Initialise a model, 0,0 corresponds to texture offset of 0,0 pixels.
        bodyModel[0] = new ModelRendererTurbo(this, 0, 0, textureX, textureY);
        // Call custom function with the model you want to load an obj into, and the filename of the obj.
        addObj(bodyModel[0], "alfa147.obj");
        // Set rotation point (AKA Position in toolbox)
        bodyModel[0].setRotationPoint(0F, 0F, 0F);
        // Rotate by 90 degrees in Y axis, (2 * 3.14 = 360 degrees)
        bodyModel[0].rotateAngleY = 3.14F / 2;


        // Create a list of models for the left flap that is length 1.
        pitchFlapLeftModel = new ModelRendererTurbo[1];

        // Similar to above.
        pitchFlapLeftModel[0] = new ModelRendererTurbo(this, 0, 0, textureX, textureY);
        addObj(pitchFlapLeftModel[0], "cow-nonormals.obj");
        pitchFlapLeftModel[0].setRotationPoint(0F, 0F, 0F);

        // Move everything in the model.
        translateAll(0F, 0F, 0F);
    }

    // Copy and paste this bit into your model.
    public static void addObj(ModelRendererTurbo mrt, String filePath) {
        try {
            // Get texture group and transform group via reflection, as these are not exposed.
            Field fieldTextureGroup = ModelRendererTurbo.class.getDeclaredField("textureGroup");
            fieldTextureGroup.setAccessible(true);

            Map<String, TextureGroup> txtG = (Map<String, TextureGroup>)fieldTextureGroup.get(mrt);

            Field fieldTransformGroup = ModelRendererTurbo.class.getDeclaredField("transformGroup");
            fieldTransformGroup.setAccessible(true);

            Map<String, TransformGroup> trfG = (Map<String, TransformGroup>)fieldTransformGroup.get(mrt);

            // We will look to see if the model file exists in /Flan/*/objModels/objFile.obj
            // To find out which pack (*) the model is in.
            String foundFileLocation = null;
            for (File packDir : Objects.requireNonNull(FlansMod.flanDir.listFiles())) {
                if (packDir.isDirectory()) {
                    String newPath = packDir.getAbsolutePath() + "/objModels/" + filePath;

                    File file = new File(newPath);

                    if (file.exists()) {
                        foundFileLocation = packDir.getName() + "/objModels/" + filePath;
                    }
                }
            }

            if (foundFileLocation == null)
            {
                FlansMod.log("Couldn't find OBJ file in /Flan/*/objModels/" + filePath);
                return;
            }

            // Use flans obj loader
            ModelPoolEntry entry = ModelPool.addFile(foundFileLocation, ModelPool.OBJ, trfG, txtG);
            if (entry == null) return;

            // Take a copy of vertices and faces,
            PositionTextureVertex[] verts = Arrays.copyOf(entry.vertices, entry.vertices.length);
            TexturedPolygon[] poly = new TexturedPolygon[entry.faces.length];

            // Recalculate normals assuming every face has at least three vertices.
            int i = 0;
            for (TexturedPolygon tp : entry.faces)
            {
                TexturedPolygon newTp = new TexturedPolygon(tp.vertexPositions);

                Vec3 normalVector = Vec3.createVectorHelper(0,1,0);

                if (tp.vertexPositions.length > 2) {
                    Vec3 v1 = Vec3.createVectorHelper(
                            tp.vertexPositions[1].vector3D.xCoord - tp.vertexPositions[0].vector3D.xCoord,
                            tp.vertexPositions[1].vector3D.yCoord - tp.vertexPositions[0].vector3D.yCoord,
                            tp.vertexPositions[1].vector3D.zCoord - tp.vertexPositions[0].vector3D.zCoord
                    );

                    Vec3 v2 = Vec3.createVectorHelper(
                            tp.vertexPositions[2].vector3D.xCoord - tp.vertexPositions[0].vector3D.xCoord,
                            tp.vertexPositions[2].vector3D.yCoord - tp.vertexPositions[0].vector3D.yCoord,
                            tp.vertexPositions[2].vector3D.zCoord - tp.vertexPositions[0].vector3D.zCoord
                    );

                    normalVector = v1.crossProduct(v2).normalize();
                }

                newTp.setNormals((float)normalVector.xCoord, (float)normalVector.yCoord, (float)normalVector.zCoord);

                poly[i++] = newTp;
            }

            // Copy modified vertices, faces back to model.
            mrt.copyTo(verts, poly, false);
        } catch (Exception ex) {
            FlansMod.log("OBJ failed to load :(");
        }

    }
}
Clone this wiki locally