Adjust the appearance of a .obj file in three.js dynamically

Currently, I am attempting to dynamically swap the image texture on a loaded .obj file in three.js. Below is the modified code extracted from the three.js examples:


        var container, stats;
        var camera, scene, renderer;
        var mouseX = 0, mouseY = 0;
        var windowHalfX = window.innerWidth / 2;
        var windowHalfY = window.innerHeight / 2;


        init();
        animate();


        function init() {

            container = document.createElement( 'div' );
            document.body.appendChild( container );

            camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
            camera.position.z = 100;

            //scene
            scene = new THREE.Scene();

            var ambient = new THREE.AmbientLight( 0x101030 );
            scene.add( ambient );

            var directionalLight = new THREE.DirectionalLight( 0xffeedd );
            directionalLight.position.set( 0, 0, 1 );
            scene.add( directionalLight );

            //manager
            var manager = new THREE.LoadingManager();
            manager.onProgress = function ( item, loaded, total ) {

                console.log( item, loaded, total );

            };

            //model
            var loader = new THREE.OBJLoader( manager );
            loader.load( 'obj/female02/female02.obj', function ( object ) {
                object.traverse( function ( child ) {

                    if ( child instanceof THREE.Mesh ) {
                        //create a global var to reference later when changing textures
                        myMesh = child;
                        //apply texture
                        myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/ash_uvgrid01.jpg');
                        myMesh.material.needsUpdate = true;
                    }

                } );


                object.position.y = - 80;
                scene.add( object );

            } );

            //render
            renderer = new THREE.WebGLRenderer();
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );

            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            window.addEventListener( 'resize', onWindowResize, false );

        }

        function newTexture() {
            myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/land_ocean_ice_cloud_2048.jpg');
            myMesh.material.needsUpdate = true;
        }

        function onWindowResize() {

            windowHalfX = window.innerWidth / 2;
            windowHalfY = window.innerHeight / 2;

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize( window.innerWidth, window.innerHeight );

        }

        function onDocumentMouseMove( event ) {

            mouseX = ( event.clientX - windowHalfX ) / 2;
            mouseY = ( event.clientY - windowHalfY ) / 2;

        }

        //animate
        function animate() {

            requestAnimationFrame( animate );
            render();

        }

        function render() {

            camera.position.x += ( mouseX - camera.position.x ) * .05;
            camera.position.y += ( - mouseY - camera.position.y ) * .05;

            camera.lookAt( scene.position );

            renderer.render( scene, camera );

        }

The only additional feature added was the 'newTexture' function and a mesh reference named 'myMesh'. You can find the original example here. However, despite no error logs, the .obj file doesn't update as expected. It seems like I might have overlooked something crucial here..

Update: In response to the insightful answer below, here's the revised code with extra functionalities for swapping textures using an input field:

  
var container, stats; 
var camera, scene, renderer; 
var mouseX = 0, mouseY = 0; 
var windowHalfX = window.innerWidth / 2; 
var windowHalfY = window.innerHeight / 2; 
var globalObject; 

init(); 
animate(); 

function init() { 
    container = document.createElement('div'); 
    document.body.appendChild(container); 
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000); 
    camera.position.z = 100; 
    //scene 
    scene = new THREE.Scene(); 
    var ambient = new THREE.AmbientLight(0x101030); 
    scene.add(ambient); 
    var directionalLight = new THREE.DirectionalLight(0xffeedd); 
    directionalLight.position.set(0, 0, 1); 
    scene.add(directionalLight); 
    //manager 
    var manager = new THREE.LoadingManager(); 
    manager.onProgress = function(item, loaded, total) { 
        console.log(item, loaded, total); 
    }; 
    //model 
    var loader = new THREE.OBJLoader(manager); 
    loader.load('obj/female02/female02.obj', function(object) { 
        //store global reference to .obj 
        globalObject = object; 
        object.traverse(function(child) { 
            if (child instanceof THREE.Mesh) { 
                child.material.map = THREE.ImageUtils.loadTexture('textures/grid.jpg'); 
                child.material.needsUpdate = true; 
            } 
         }); 
         object.position.y = -80; 
         scene.add(object); 
     }); 
    
     renderer = new THREE.WebGLRenderer(); 
     renderer.setSize(window.innerWidth, window.innerHeight); 
     container.appendChild(renderer.domElement); 
     
     document.addEventListener('mousemove', onDocumentMouseMove, false); 
     window.addEventListener('resize', onWindowResize, false); 
} 

function onWindowResize() { 
    windowHalfX = window.innerWidth / 2; 
    windowHalfY = window.innerHeight / 2; 
    camera.aspect = window.innerWidth / window.innerHeight; 
    camera.updateProjectionMatrix(); 
    renderer.setSize(window.innerWidth, window.innerHeight); 
} 

function onDocumentMouseMove(event) { 
    mouseX = (event.clientX - windowHalfX) / 2; 
    mouseY = (event.clientY - windowHalfY) / 2; 
} 

//animate 
function animate() { 
    requestAnimationFrame(animate); 
    render(); 
} 

function render() { 
    camera.position.x += (mouseX - camera.position.x) * 0.05; 
    camera.position.y += (-mouseY - camera.position.y) * 0.05; 
    camera.lookAt(scene.position); 
    renderer.render(scene, camera); 
} 

function newTexture() { 
    var newTexturePath = "textures/" + document.getElementById("texture").value + ""; 
    globalObject.traverse(function(child) { 
        if (child instanceof THREE.Mesh) { 
            child; 
            child.material.map = THREE.ImageUtils.loadTexture(newTexturePath); 
            child.material.needsUpdate = true; 
        } 
    }); 
}

Answer №1

The issue at hand arises from the presence of multiple child meshes within the object variable. By only storing a single global variable, myMesh, you are essentially only updating the last child mesh. As a result, when attempting to update the texture using this singular global variable, only one of the meshes receives the texture update - likely a small, inconspicuous part that isn't easily visible.

To address this, consider the following solutions:

  • Create a global array variable called myMeshes and push each instance of myMesh into it whenever necessary. Then, in your newTexture() function, iterate through all items in this array and update their maps/materials accordingly.
  • Alternatively, store the entirety of the object variable (the one returned from the callback of the OBJLoader) in a single global variable. Within your newTexture() function, traverse through all the meshes as demonstrated in the code snippet (utilizing traverse() and an if statement) to update their maps/materials.

Lastly, don't forget to call newTexture() somewhere within your code for everything to properly take effect ;)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing SCSS to implement custom animations according to specific element IDs

How can I add different animations based on the ID of two DIVs with the same class when clicked? JSX: <div className="card"> <div id="front" className={frontClasses.join(' ')} onClick={clickedFront}> OPEN ...

Verifying in PHP if the request is intended for a JavaScript worker

After doing my research on MDN for the referrer-policy, as well as searching through Google, DuckDuckGo and Stack Overflow, I still find myself stuck on a seemingly simple yet elusive issue. Journey of Data the browser sends a request to the server the ...

Keeping HTML and JS Variables in Sync for Dynamic Updates

Next to a number input, there are minus and plus buttons. Below these buttons, there is a "buy now" button that grabs values from the HTML and passes them to the href URL. How can I update the quantity in the href URL every time I click the minus/plus butt ...

Building a React component to display JSON data in a tabular format

I am trying to create something similar to the layout in the following link: Table example Imagine I have two arrays: names: ["Prisma 2 Case", "PP-Bizon", ...] imgs: ["img1.png", "img2.png", ...] One array contain ...

Checking with Protractor to see if the modal is displayed

I am currently working on a Protractor test to check if a bootstrap modal window that confirms the deletion of a record is visible at this time. The record that needs to be deleted is displayed in an angular ng-repeat, so I have to trigger the delete butt ...

Ways to trigger an onClick event multiple times in React

I have a code where I pass it to the onClick event and I want it to execute 3 times: (this code basically selects a random product from products and then updates the state using Hooks to display it in the shopping cart. Instead of just one product, I want ...

Need to update React textarea with value that is currently set as readonly

In my React application, I have a textarea that is populated with a specific value. My goal is to allow this textarea to be updated and then submit the form in order to update the corresponding row in the database. <textarea id="description" className= ...

Using a Function as an Argument to Return an Unnamed Object

There seems to be a common trend in JavaScript code where a function is passed as a parameter, which in turn returns an anonymous object. myFunction(function() { return { foo: 'bar' }; }); What benefits or reasons are there for u ...

Eliminate specific content from within a div tag

Looking for a solution with the following HTML. <div id="textDiv"> <p> MonkeyDonkey is not a bird. Monkey is an animal. </p> </div> In this case, I am trying to delete the word "Donkey". I attempted the code below but it did no ...

Retrieve the response text from a jQuery.get() call and return it

Attempting to achieve the following: var msg = $.get("my_script.php"); Expecting msg to be assigned to the text returned by my_script.php, which should be the responseText of the jqXHR object. However, it seems that msg is consistently set to "[object XM ...

emailProtected pre-publish: Running `python build.py && webpack` command

i am currently using scratch-blocks through the Linux terminal I have encountered a problem which involves running the following command: python build.py && webpack [email protected] prepublish: python build.py && webpack Can anyon ...

What is the best method to close a polygon infowindow when hovering over a map marker?

I am currently working on integrating Google Maps which includes a set of polygons representing state boundaries and markers representing cities within each polygon. I want to display information when hovering over a polygon/state. My question is: how can ...

Text hyperlink populates form fields

I am interested in learning how to create a clickable text link that can automatically populate specific information in a form located above it. For example, I have a donation form with fields for amount, country, and projects. Below this form, there are ...

How can I access the feed of a single user using the Facebook API

I have yet to experience working with Facebook APIs, but I am interested in developing a basic app that will display posts from a specific Facebook user. I would prefer not to enable login for multiple users, just keep it simple. My goal is to create an a ...

What is the method for notifying the latitude within this Google Maps javascript code?

There's a specific line that triggers an alert showing "undefined". However, when I just alert results[0].geometry.location, it displays (41.321, 41.556). My goal is to alert this value and then convert it (as an integer) to my id_lat and id_longs... ...

Switching from PHP to JavaScript before returning to PHP to establish and manage sessions

Currently, I am in the process of resolving an issue I am facing. The problem arises when utilizing the following code for someone trying to sign into the admin panel: <script> function myFunction() { //alert('you can type now, end ...

Utilizing AngularJS 1.X to implement filters on transformed information

When using the combination of ng-repeat and a filter, it creates a highly effective search function. However, once the data is converted, the use of a filter for searching becomes limited. For Instance JavaScript $scope.ints = [0,1,2,4,5,6,7,8,9, ..., 9 ...

Encountered an error while trying to retrieve data using the combination of axios, nodejs

Having trouble with my fetch request taking too long and then failing I've tested it on Chrome, Edge, and Postman without success All other fetch requests from the Pixabay API are working perfectly fine I compared my code to previous projects I&apo ...

Changing the div background color based on the status of the `.play()` function

I need assistance with changing the background color of a div based on whether an audio file is playing or not. $('.uprow2').on('click', function() { $('#karma')[0].play(); }); .uprow2 { width: 680px; height: 60px; ...

Ways to conceal the warning message "Getting [Vue warn]: Invalid prop: type check failed for prop ..." during testing?

I am currently working on a VUE component that takes in a prop of type Number. I am trying to write a test, using vue test utils and jest, to cover the scenario where the prop is not a number and see how it renders in such a situation (let's say the v ...