Change DDS Texture in runtime

Hello

Is anyone able to help me with this piece of code? It works fine with the exception of the texture change on runtime, it “destroys” the textures, they get all messed up, without that code the texture from MTL is loaded correctly (also DDS).

        const manager = new THREE.LoadingManager()
        manager.addHandler(/\.dds$/i, new DDSLoader())

        const mtlLoader = new MTLLoader(manager)
        mtlLoader.setPath(url!)
        mtlLoader.load(`${model}.mtl`, (materials) => {
            materials.preload()

            const objLoader = new OBJLoader(manager)
            objLoader.setMaterials(materials)
            objLoader.setPath(url!)
            objLoader.load(`${model}.obj`, (object) => {
                object.scale.setScalar(1 / 200)
                if (type && type === 'WING') {
                    object.position.y = -0.9
                } else {
                    object.position.y = -0.3
                }
                object.rotateX(-90 * (Math.PI / 180))

                const materialName = Object.values(materials.materialsInfo).filter(({ map_kd }) => map_kd === sourceSkin)[0].name
                object.traverse((child) => {
                    if (child instanceof THREE.Mesh && type === 'ARMOR' && child.material.name === materialName) {
                        child.material.map = new DDSLoader().load(`${url}${targetSkin}`, () => {
                            child.material.needsUpdate = true
                        })
                    }
                    child.frustumCulled = false
                    if ((child as any).material instanceof THREE.MeshPhongMaterial) {
                        (child as any).material.side = THREE.DoubleSide
                    }
                })
                scene.add(object)
            }, undefined, (error) => { console.error(error) })
        })

The object usually has 2, sometimes 3 texture files, I have the materialName variable to find the one I need to change, and only change that one keeping the other ones intact.

With texture change in runtime:
image

Without texture change in runtime, texture loaded from MTL:
image

Piece of code that has the model and the corresponding textures.

"0": {
    "model": "pc2/assassin/assassin_novice",
    "sourceSkin": "pc2/assassin/assassin_novice_red.dds",
    "targetSkin": "pc2/assassin/assassin_novice_blue.dds"
},

The models (MTL) only have reference to the “sourceSkin”, the “targetSkin” is made somewhere else.

All my DDS textures are Flipped on Y.

nvtt_export -f bc3 -q production --save-flip-y -o "./material/textures/armors/converted/${folderRoot}/${folderRace}/${item}" "./material/textures/armors/${folderRoot}/${folderRace}/${item}"

Any idea on how I can solve this?

Thank you

Hello,

After a while I tinkering with the code I thought about another solution, that maybe isn’t the most pristine but that works as intended.

Instead of trying to load a new texture, I just change the texture before loading the original:

        const manager = new THREE.LoadingManager()
        manager.addHandler(/\.dds$/i, new DDSLoader())

        const mtlLoader = new MTLLoader(manager)
        mtlLoader.setPath(url!)
        mtlLoader.load(`${model}.mtl`, (materials) => {
            if (type === 'ARMOR') {
                const filtered = _.pickBy(materials.materialsInfo, (value) => value.map_kd === sourceSkin.toLowerCase())
                // eslint-disable-next-line no-return-assign
                const filteredUpdated = _.forOwn(filtered, (o) => o.map_kd = targetSkin)
                materials.materialsInfo = { ...materials.materialsInfo, ...filteredUpdated }
            }

            materials.preload()

            const objLoader = new OBJLoader(manager)
            objLoader.setMaterials(materials)
            objLoader.setPath(url!)
            objLoader.load(`${model}.obj`, (object) => {
                object.scale.setScalar(1 / 200)
                if (type && type === 'WING') {
                    object.position.y = -0.9
                } else {
                    object.position.y = -0.3
                }
                object.rotateX(-90 * (Math.PI / 180))

                object.traverse((child) => {
                    child.frustumCulled = false
                    if ((child as any).material instanceof THREE.MeshPhongMaterial) {
                        (child as any).material.side = THREE.DoubleSide
                    }
                })
                scene.add(object)
            }, undefined, (error) => { console.error(error) })
        })

The code may still need some optimizations and remove the need for lodash.

The current result can be seen here: https://dev.m2icondb.pages.dev/ (body armor and costume body armor). Still need to adjust the height and positioning.

Also, I tried switching to glTF, I tried glb, gltf and gltf with inline buffers of data, none worked, the textures were never loaded, even on glb and gltf with the inline buffers. I’ll stick with OBJ/MTL for now.

The glTF asset did probably not work since the texture data are compressed via S3TC. In earlier days of glTF it was possible include .dds files via the MSFT_texture_dds extension. However, this extension is no longer supported. Instead, KHR_texture_basisu should be used which enables are more modern texture compression workflow. More details here:

Keep in mind that a more modern glTF based workflow is preferable over OBJ/MTL. If you have the time, consider to switch the 3D format.