[SOLVED] Assimp OBJ loading

Started by mudlee, May 03, 2018, 16:54:01

Previous topic - Next topic

mudlee

Hi. I have some problem with assimp when loading obj files.

I've tried four ways to use assimp:


  • - obj from MagicaVoxel - sample tower, texturized
  • - obj from blender - simply just colored
  • - fbx from blender - same colored model as used for the obj export
  • - blend from blender - works as fbx

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
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

KaiHH

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?

spasi

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.


mudlee

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.

mudlee

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.

mudlee

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?

spasi

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.

mudlee

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.