question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

OBJLoader displays strange texture mapping with cube made in Blender (problem and solution)

See original GitHub issue

Hi. Thank you for this amazing book !

In the Loading more complex models chapter, I struggled to understand why my cube’s texture was not displayed correctly. The cube was made with Blender and with correct UV. image

image

After a quick search I have found #59 issue and understood the problem.

Maybe a little reword could help to not fall in this trap for further readers. IMO the sentence “And we will get our familiar textured cube.” can be misunderstood in the way that “the obj loader correctly works with a cube and texture”.

BTW here is my kotlin code (straight forward I hope) that supports both index by position and index by texture coords. It is inspired from code in #59 but cleaner and refactored to not have code duplication.

Anyone can copy/paste and use it (no external function used) and of course you can links this issue/code in your book

image


data class Face(
    val indexGroups: List<IndexesGroup>,
)

data class IndexesGroup(val idxPos: Int = NO_VALUE, val idxTextCoords: Int = NO_VALUE, val idxVecNormal: Int = NO_VALUE) {
    companion object {
        const val NO_VALUE = -1
    }
}

@OptIn(ExperimentalTime::class)
object ObjLoader {
    fun loadMesh(fileName: String): Mesh {
        val positionsData = mutableListOf<Vector3f>()
        val texCoordsData = mutableListOf<Vector2f>()
        val normalsData = mutableListOf<Vector3f>()
        val facesData = mutableListOf<Face>()

        File(fileName).forEachLine { line ->
            val tokens = line.split(" ")
            when (tokens.first()) {
                "v" -> positionsData += Vector3f(tokens[1].toFloat(), tokens[2].toFloat(), tokens[3].toFloat())
                "vt" -> texCoordsData += Vector2f(tokens[1].toFloat(), tokens[2].toFloat())
                "vn" -> normalsData += Vector3f(tokens[1].toFloat(), tokens[2].toFloat(), tokens[3].toFloat())
                "f" -> {
                    val indexGroups = mutableListOf<IndexesGroup>()
                    tokens.drop(1).forEach { groupString ->
                        val group = groupString.split("/").map { it.toInt() - 1 }
                        indexGroups += IndexesGroup(group[0], group[1], group[2])
                    }
                    facesData += Face(indexGroups)
                }
            }
        }
        val usePosForIdx = positionsData.size >= texCoordsData.size
        return reorderLists(positionsData, texCoordsData, normalsData, facesData, usePosForIdx)
    }

    private fun reorderLists(
        positionsData: MutableList<Vector3f>,
        texCoordsData: MutableList<Vector2f>,
        normalsData: MutableList<Vector3f>,
        facesData: MutableList<Face>,
        usePosForIdx: Boolean = true
    ): Mesh {
        println("Mesh reordering with positions: $usePosForIdx")
        val arraySize = if (usePosForIdx) positionsData.size else texCoordsData.size
        val positions = FloatArray(arraySize * 3)
        val texCoords = FloatArray(arraySize * 2)
        val normals = FloatArray(arraySize * 3)
        val indices = mutableListOf<Int>()

        facesData.forEach { face ->
            face.indexGroups.forEach { groupId ->
                val index = if (usePosForIdx) groupId.idxPos else groupId.idxTextCoords

                val position = positionsData[groupId.idxPos]
                positions[index * 3] = position.x
                positions[index * 3 + 1] = position.y
                positions[index * 3 + 2] = position.z

                val coords = texCoordsData[groupId.idxTextCoords]
                texCoords[index * 2] = coords.x
                texCoords[index * 2 + 1] = 1 - coords.y

                val normal = normalsData[groupId.idxVecNormal]
                normals[index * 3] = normal.x
                normals[index * 3 + 1] = normal.y
                normals[index * 3 + 2] = normal.z

                indices += index
            }
        }
        return Mesh(positions, texCoords, normals, indices.toIntArray())
    }
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
lwjglgamedevcommented, May 9, 2021

Done!

Thank you very much for the example.

0reactions
Nutrizcommented, May 8, 2021

Hi,

You are right, I forgot this sentence in chapter 9. I have a public repository with the code, but with 2 methods used from outside the OBJLoader in repo class (measureAndLog and Log.debug) and I could move file/package in the future.

So I have created an OBJLoader gits gits with no external function. Anyone can copy/paste and use it in seconds.

Of course you can link that in your book, if I can bring my little contribution for future readers, I’m happy with that 😃

Read more comments on GitHub >

github_iconTop Results From Across the Web

Common Texture Issues and How to Fix Them in Blender
In this video, I will help you troubleshoot some common issues with textures and materials in Blender, as well as give you some...
Read more >
How to fix stretching textures and materials in Blender?
If our texture is already stretched, we need to adjust the UV map that describes how the texture is projected on to our...
Read more >
T78126 OBJ files exported from Blender don't show textured in ...
They made one Lola Bunny, but I am create 1 cube box and put texture I have fail ... Yes correct answer "...
Read more >
Problem in texturing a cube - Blender Stack Exchange
I add a texture to a cube. The texture is an image. I set Coordinates to Generated and Projection to Cube. The rendering...
Read more >
ThreeJS - How can I apply an environment map to an ...
OP: I have an environment cubemap exported from blender - I can apply the envmap to generated geometry fine, but how do I...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found