LWJGL Forum

Programming => Lightweight Java Gaming Library => Topic started by: mudlee on May 03, 2018, 16:54:01

Title: [SOLVED] Assimp OBJ loading
Post by: mudlee on May 03, 2018, 16:54:01
Hi. I have some problem with assimp when loading obj files.

I've tried four ways to use assimp:


What I observed, that both obj somehow have two materials and both have a fix RGB 0.6-0.6-0.6 diffuse color. Neither are true. Obj contains one material and different diffuse colors. Somehow, the FBX export contains one material and correct diffuse color, so I suppose fbx works properly. The mesh itself looks ok in all cases.

Here are all the files I used: https://drive.google.com/open?id=1E6g-rqI3QPTFfMnMi90CPjKzcxx9tyYp (https://drive.google.com/open?id=1E6g-rqI3QPTFfMnMi90CPjKzcxx9tyYp)
Here is my code:

    public static Mesh loadOBJ(String resourcePath) {
        LOGGER.debug("# Trying to load OBJ model {}...", resourcePath);
        AIScene scene = Assimp.aiImportFileFromMemory(
                ResourceLoader.loadToByteBuffer(resourcePath),
                Assimp.aiProcess_JoinIdenticalVertices | Assimp.aiProcess_Triangulate | Assimp.aiProcess_FixInfacingNormals,
                "obj"
        );

        if (scene == null) {
            throw new RuntimeException(String.format("Could not load %s", resourcePath));
        }

        // MESHES
        int numMeshes = scene.mNumMeshes();
        LOGGER.debug("# Processing {} meshes...", numMeshes);
        PointerBuffer aiMeshes = scene.mMeshes();

        if (aiMeshes == null) {
            throw new RuntimeException("aiMeshes PointBuffer was null for model " + resourcePath);
        }

        Mesh[] meshes = new Mesh[numMeshes];

        for (int i = 0; i < numMeshes; i++) {
            LOGGER.debug("Processing [{}] mesh...", i);
            AIMesh aiMesh = AIMesh.create(aiMeshes.get(i));
            Mesh mesh = new Mesh(
                    processVertices(aiMesh),
                    processIndices(aiMesh),
                    processNormals(aiMesh),
                    new ArrayList<>()
            );
            LOGGER.debug("Mesh [{}] loaded. Verts: {}, indices: {}, textureCoords: {}, normals: {}", i, mesh.getVertices().length, mesh.getIndices().length, 0, mesh.getNormals().length);
            meshes[i] = mesh;
        }

        // MATERIALS
        int numMaterials = scene.mNumMaterials();
        LOGGER.debug("# Processing {} materials...", numMaterials);
        PointerBuffer aiMaterials = scene.mMaterials();

        if (aiMaterials == null) {
            throw new RuntimeException("aiMaterials PointBuffer was null for model " + resourcePath);
        }

        List<Material> materials = new ArrayList<>();
        for (int i = 0; i < numMaterials; i++) {
            LOGGER.debug("Processing [{}] material...", i);
            AIMaterial aiMaterial = AIMaterial.create(aiMaterials.get(i));
            Material material = processMaterial(aiMaterial);
            LOGGER.debug("Material [{}] loaded.", i);
            materials.add(material);
        }

        LOGGER.debug("# Model {} has been loaded. Meshes: {}, Materials: {}", resourcePath, numMeshes, numMaterials);
        return meshes[0];
    }

    private static Material processMaterial(AIMaterial aiMaterial) {
        //AIString path = AIString.calloc();
        //Assimp.aiGetMaterialTexture(aiMaterial, Assimp.aiTextureType_DIFFUSE, 0, path, (IntBuffer) null,null, null, null, null, null);

        printColor(aiMaterial,Assimp.AI_MATKEY_COLOR_DIFFUSE);
        printColor(aiMaterial,Assimp.AI_MATKEY_COLOR_AMBIENT);
        printColor(aiMaterial,Assimp.AI_MATKEY_COLOR_SPECULAR);

        printTexture(aiMaterial,Assimp.aiTextureType_DIFFUSE);
        printTexture(aiMaterial,Assimp.aiTextureType_SPECULAR);
        printTexture(aiMaterial,Assimp.aiTextureType_AMBIENT);
        printTexture(aiMaterial,Assimp.aiTextureType_EMISSIVE);
        printTexture(aiMaterial,Assimp.aiTextureType_HEIGHT);
        printTexture(aiMaterial,Assimp.aiTextureType_NORMALS);
        printTexture(aiMaterial,Assimp.aiTextureType_SHININESS);
        printTexture(aiMaterial,Assimp.aiTextureType_OPACITY);
        printTexture(aiMaterial,Assimp.aiTextureType_DISPLACEMENT);
        printTexture(aiMaterial,Assimp.aiTextureType_LIGHTMAP);
        printTexture(aiMaterial,Assimp.aiTextureType_REFLECTION);
        printTexture(aiMaterial,Assimp.aiTextureType_UNKNOWN);

        //List all the properties and whether they match
        PointerBuffer props = aiMaterial.mProperties();
        for(int j = 0; j < aiMaterial.mNumProperties(); j++) {
            AIMaterialProperty prop = AIMaterialProperty.create(props.get());
            System.out.println(
                    "key=\"" + prop.mKey().dataString() +
                            "\", type=" + prop.mType() +
                            ", index=" + prop.mIndex() +
                            " (" + (prop.mKey().dataString().equals(Assimp.AI_MATKEY_COLOR_DIFFUSE) &&
                            prop.mType() == Assimp.aiTextureType_DIFFUSE &&
                            prop.mIndex() == 0)
                            + ")"
            );
        }

        return null;
    }

    private static void printTexture(AIMaterial aiMaterial, int what){
        AIString path = AIString.calloc();
        int result=Assimp.aiGetMaterialTexture(aiMaterial, what, 0, path, (IntBuffer) null,null, null, null, null, null);
        String textPath = path.dataString();
        System.out.println(textPath+" - texture result: "+result);
    }

    private static void printColor(AIMaterial aiMaterial, String what){
        AIColor4D colour = AIColor4D.create();
        int result = Assimp.aiGetMaterialColor(aiMaterial, what, Assimp.aiTextureType_NONE, 0,colour);
        System.out.println(colour.r()+"-"+colour.g()+"-"+colour.b()+" - result: "+result);
    }


The ouput of the code:

[18:53:17.146] 936  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - # Processing 1 meshes...
[18:53:17.146] 936  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Processing [0] mesh...
[18:53:17.162] 952  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Mesh [0] loaded. Verts: 8604, indices: 2904, textureCoords: 0, normals: 8604
[18:53:17.162] 952  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - # Processing 2 materials...
[18:53:17.162] 952  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Processing [0] material...
0.6-0.6-0.6 - result: 0
0.0-0.0-0.0 - result: 0
0.0-0.0-0.0 - result: 0
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
key="?mat.name", type=3, index=0 (false)
key="$mat.shadingm", type=4, index=0 (false)
key="$clr.ambient", type=1, index=0 (false)
key="$clr.diffuse", type=1, index=0 (true)
key="$clr.specular", type=1, index=0 (false)
key="$clr.emissive", type=1, index=0 (false)
key="$mat.shininess", type=1, index=0 (false)
key="$mat.opacity", type=1, index=0 (false)
key="$clr.transparent", type=1, index=0 (false)
key="$mat.refracti", type=1, index=0 (false)
[18:53:17.170] 960  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Material [0] loaded.
[18:53:17.170] 960  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Processing [1] material...
0.6-0.6-0.6 - result: 0
0.0-0.0-0.0 - result: 0
0.0-0.0-0.0 - result: 0
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
- texture result: -1
key="?mat.name", type=3, index=0 (false)
key="$mat.shadingm", type=4, index=0 (false)
key="$clr.ambient", type=1, index=0 (false)
key="$clr.diffuse", type=1, index=0 (true)
key="$clr.specular", type=1, index=0 (false)
key="$clr.emissive", type=1, index=0 (false)
key="$mat.shininess", type=1, index=0 (false)
key="$mat.opacity", type=1, index=0 (false)
key="$clr.transparent", type=1, index=0 (false)
key="$mat.refracti", type=1, index=0 (false)
[18:53:17.171] 961  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - Material [1] loaded.
[18:53:17.171] 961  [main] DEBUG com.intermetto.engine.util.StaticMeshLoader  - # Model /models/monkey.obj has been loaded. Meshes: 1, Materials: 2
Title: Re: Assimp OBJ loading
Post by: KaiHH on May 03, 2018, 18:27:44
The Wavefront OBJ exporter in Blender is rather buggy when it comes to exporting material properties. Btw. were you using Cycles in Blender or the default Blender Render?
The only truth really is the mtl file. If you see wrong colors there, then, well... the Blender export is simply broken. There is nothing LWJGL3 or Assimp can do about that.
Btw. what is your actual question?
Title: Re: Assimp OBJ loading
Post by: spasi on May 03, 2018, 19:03:24
There's nothing wrong with the .obj/.mtl files. The problem is the call to aiImportFileFromMemory. The buffer includes only the .obj contents and Assimp is not able (it doesn't even try) to resolve the referenced .mtl file. There are two solutions to this:

- Use aiImportFile. This requires that the resources are stored on disk somewhere.
- If the resources are packed in another data file (e.g. a .jar file) and you don't want to extract them to disk, you must use aiImportFileEx. The last argument is an AIFileIO structure that can be used to define a virtual filesystem. The callbacks you setup there will be used by Assimp while parsing a scene. I've successfully used it in the past and you can nicely implement extraction/decompression to memory buffers, avoiding expensive IO.

There's also a bug in your code, prop.mType() does not contain aiTextureType_* values, but aiPTI_* values. It identifies the property's data type. For example, aiPTI_Float means prop.mData() is an array of floats, you can simply do prop.mData().asFloatBuffer() and read the data directly.

Title: Re: Assimp OBJ loading
Post by: mudlee on May 04, 2018, 04:56:41
Hi.

thx for the help, it looks like spasi is right, that's why I get no textures. The material stuff is might be a different bug, will check it out and get back here if I find something.
Title: Re: Assimp OBJ loading
Post by: mudlee on May 04, 2018, 17:20:08
OK, the problem was that Spasi mentioned. Now it can read mtl file as I use aiImportFile.
It still reading 2 materials instead of one, but from aiMesh, its materialIndex can be queried.
Title: Re: [SOLVED] Assimp OBJ loading
Post by: mudlee on May 04, 2018, 17:42:44
One more thing though.

I use MyClass.class.getResource(resourcePath).getPath() for getting file's path but it's not working obiously when I package a jar. It would work if I use getResourceAsStream, but it returns an InputStream, not a path. Is there any solution to refer a file that works for assimp and for jar files as well?
Title: Re: [SOLVED] Assimp OBJ loading
Post by: spasi on May 04, 2018, 18:06:26
No, as I said above, you must either extract the resources to disk or use aiImportFileEx + an AIFileIO that knows how to resolve resources from the jar and fill buffers with the corresponding data.
Title: Re: [SOLVED] Assimp OBJ loading
Post by: mudlee on May 09, 2018, 07:31:14
Sorry, now I understand it and it does work.

One note to the mtl loading problem. With the guy who wrote the lwjgl book we found that mtl will only be imported correctly if the mtl declaration in the obj file is before the object group. Also, objs always have 2 materials somehow, we have to only care about the one with the index defined in the AIMesh.