How to show this "peter 3d model" same as its shows, using .obj and textures

Hello friends, can anyone help me little bit.

how to show this “peter 3d model” same as its shows, using .obj and textures.

It showing like this :sweat_smile:

in below child.name material :arrow_heading_down:
‘White_Scarf’
‘Red_Collar’
‘EyeLeft’
‘EyeRight’
‘EyeInternal’
'EyeInternal.
‘PeterHead’
‘Button’
‘Green_Jacket’
‘HairSimple’
‘LashesSimple’
‘BrowsSimple’
‘MoustacheSimple’

i have below textures exist :arrow_heading_down:
PEyeMatMask.jpg
PEyeOuterTex.jpg
PHead_Color.png
PHead_Height.png
PIris_texture.jpg

Thanks :pray:

<script lang="ts" setup>
import { find } from 'lodash'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import { TextureLoader } from 'three/src/loaders/TextureLoader'

const projectStore = useProjectStore()

const container: any = ref(null)

const isFetching = ref(false)
const fetchingPercentage = ref(0)

let renderer: any, camera: any, scene: any
let controls: any

const currentProject = computed(() => projectStore.currentProject)

const getSize = () => {
  const width = window.innerHeight * 0.93
  const height = window.innerHeight * 0.55
  return {
    width, height,
  }
}

const aspectRatio = computed(() => {
  return 9
})

const createRenderer = () => {
  const { width, height } = getSize()

  renderer = new THREE.WebGLRenderer()
  renderer.setSize(width, height)
  container.value.appendChild(renderer.domElement)
}

const createCamera = () => {
  camera = new THREE.PerspectiveCamera(aspectRatio.value, window.innerWidth / window.innerHeight, 0.5, 1000)
  camera.position.z = 5
}

const createScene = () => {
  scene = new THREE.Scene()
  scene.background = new TextureLoader().load(currentProject.value.default_project_background_attachment[0].image)

  const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.5) // Soft white light
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1)
  directionalLight.position.set(3, 3, 3).normalize() // Adjust position as needed
  scene.add(directionalLight)
}

const loadTexture = (texturePath: any) => {
  return new Promise((resolve, reject) => {
    const textureLoader = new TextureLoader()

    textureLoader.load(
      texturePath.image,
      (texture) => {
        resolve(texture)
      },
      undefined,
      (error) => {
        console.error('An error happened while loading texture', error)
        reject(error)
      },
    )
  })
}

const loadObject = async () => {
  const objLoader = new OBJLoader()

  objLoader.load(
    currentProject.value.three_d_asset_file?.url,
    async (object) => {
      const box = new THREE.Box3().setFromObject(object)
      const center = box.getCenter(new THREE.Vector3())
      object.position.sub(center)

      if (utils.isEmpty(currentProject.value.three_d_asset_textures_attachment)) {
        scene.add(object)
        isFetching.value = false
        return
      }

      const textures: any = []
      for (const texturePath of currentProject.value.three_d_asset_textures_attachment) {
        const model = await loadTexture(texturePath)
        textures.push({ model, name: texturePath.media.name })
      }

      const PEyeMatMask = find(textures, txt => txt.name === 'PEyeMatMask.png')?.model
      const PEyeOuterTex = find(textures, txt => txt.name === 'PEyeOuterTex.jpg')?.model
      const PHead_Color = find(textures, txt => txt.name === 'PHead_Color.png')?.model
      const PHead_Height = find(textures, txt => txt.name === 'PHead_Height.png')?.model
      const PIris_texture = find(textures, txt => txt.name === 'PIris_texture.png')?.model

      object.traverse && object.traverse((child) => {
        if (child instanceof THREE.Mesh) {
          if (child.name === 'White_Scarf') {
            child.material.map = PEyeMatMask
            child.material.needsUpdate = true
          }

          if (child.name === 'Red_Collar') {
            child.material.map = PEyeOuterTex
            child.material.needsUpdate = true
          }

          if (child.name === 'EyeLeft') {
            child.material.map = PIris_texture
            child.material.needsUpdate = true
          }

          if (child.name === 'EyeRight') {
            child.material.map = PIris_texture
            child.material.needsUpdate = true
          }

          if (child.name === 'EyeInternal') {
            child.material.map = PIris_texture
            child.material.needsUpdate = true
          }

          if (child.name === 'EyeInternal.001') {
            child.material.map = PIris_texture
            child.material.needsUpdate = true
          }

          if (child.name === 'PeterHead') {
            child.material.map = PHead_Color
            child.material.needsUpdate = true
          }

          if (child.name === 'Button') {
            child.material.map = PEyeMatMask
            child.material.needsUpdate = true
          }

          if (child.name === 'Green_Jacket') {
            child.material.map = PHead_Height
            child.material.needsUpdate = true
          }

          if (child.name === 'HairSimple') {
            child.material.map = PHead_Height
            child.material.needsUpdate = true
          }

          if (child.name === 'LashesSimple') {
            child.material.map = PEyeOuterTex
            child.material.needsUpdate = true
          }

          if (child.name === 'BrowsSimple') {
            child.material.map = PHead_Color
            child.material.needsUpdate = true
          }

          if (child.name === 'MoustacheSimple') {
            child.material.map = PHead_Color
            child.material.needsUpdate = true
          }
        }
      })

      scene.add(object)
      isFetching.value = false
    },
    (xhr) => {
      fetchingPercentage.value = Math.round((xhr.loaded / xhr.total) * 100)
    },
    (error) => {
      console.error('An error occurred while loading the OBJ file:', error)
    },
  )
}

const animate = () => {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}

const createControls = () => {
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.25
  controls.enableZoom = true
}

function loadInitialData() {
  isFetching.value = true

  setTimeout(() => {
    createRenderer()
    createCamera()
    createScene()
    loadObject()
    createControls()
    animate()
  }, 300)
}

loadInitialData()
</script>

<template>
  <div>
    <div ref="container" class="rounded-sm overflow-hidden cursor-grab" />
  </div>
</template>

Normally you would use OBJ Loader + MTL Loader + Textures, where MTL file is defining materials and textures used (assuming this MTL file was included with the OBJ file you downloaded).

If you download GLB file of that model and load it in my GLTF Viewer then you can export it to OBJ format which will include MTL file and textures, this way you can inspect the MTL file and see how it’s structured.

You might be better off trying to use GLB file instead of dealing with OBJ. This since GLB file should already include textures and would probably reduce your code by 50%.

1 Like

@KomelAbbas if you decide to stick with OBJ format then try to figure out whether all textures are intended for the map slot and not individually maybe for metalnessMap or normalMap or any other.

Any child material can possibly be using multiple textures in different slots.

1 Like

Thanks @GitHubDragonFly i will try this. :ok_hand:

Thank you for the replay :pray:

I have tried glb format and look like it resolve most of problem.
but can you let me know why it not showing same as it original form.

const loadObject = async () => {
  const loader = new GLTFLoader()
  loader.load(
    currentProject.value.three_d_asset_file?.url,
    (gltf) => {
      const box = new THREE.Box3().setFromObject(gltf.scene)
      const center = box.getCenter(new THREE.Vector3())
      gltf.scene.position.sub(center)

      scene.add(gltf.scene)
      isFetching.value = false
    },
    (xhr) => {
      fetchingPercentage.value = Math.round((xhr.loaded / xhr.total) * 100)
    },
    (error) => {
      console.error('An error occurred while loading the model:', error)
    },
  )
}

@KomelAbbas you should try loading that model in my GLTF Viewer to see how it shows there. The viewer has some light, tone mapping and environment controls so try adjusting those to achieve the best look possible.

Also try using other online GLTF Viewers to see if they do a better job in showing that model.

You might just need to add some lights to your code to improve the visual appearance.

1 Like

@KomelAbbas you should also try some other models with your current code.

If any other model shows properly then start questioning the other models.

1 Like

Thank you @GitHubDragonFly and @yesbird :pray:
The .glb format works fine after changing some lighting options.

1 Like