Jump to content
Search Community

Three.js and targeting a part of the 3D model to animate with GSAP

Creativist-Lab test
Moderator Tag

Recommended Posts

Hello all,

 

I just started out with Three.js and GSAP so I'm probably overlooking something very simple. But I can't seem to target a part of my 3D model to animate it. I only managed to animate the whole model by assigning the container where the model is located (just to see if GSAP is working). When I try to target a part of the model I always get the following error:  "name" is not defined. Anyone that can point me in the right direction on how I can target the different elements within a 3D model? This is how I set things up:

// Model
let myModel;
let modelPath_MyModel = 'https://mywebsite/wp-content/uploads/2021/05/new_brain.glb';
let isLoaded = false;
let sphere;

// Material
let texturePath_Enviroment = 'https://mywebsite/wp-content/uploads/2021/05/Matcap-test21-min.png';
let mat_Enviroment;
let mat_GlassMatCapBack;
let mat_GlassMatCapFront;


let mat_BrainCoreBlue;
let mat_BrainInnerOutterBlue;

let mat_BrainCoreRed;
let mat_BrainInnerOutterRed;

const statsEnabled = false;

let container, stats;

// three
let scene;
let camera;
let renderer;
let controls;
let delta;
let mouseX = 0;
let mouseY = 0;


let targetX = 0;
let targetY = 0;

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

function initMainApp () {
    buildScene();
    buildLightEnviroment();
    buildMaterial();
    buildModel();
    watchLoadingManager();
    buildSphereGeometry();
}

function buildScene () {
    const container = document.querySelector('#mainCanvas');
  
    scene = new THREE.Scene();
    
    camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 0.1, 100);
    camera.position.z = 14;
    camera.position.y = 1;

    const cameraHolder = new THREE.Group();
    cameraHolder.add(camera);
    scene.add(cameraHolder);

    renderer = new THREE.WebGLRenderer({ container, antialias: true, alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(1.5);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    renderer.shadowMap.needsUpdate = true;
   
                    stats = new Stats();
    container.appendChild(renderer.domElement);
 
    controls = new THREE.OrbitControls(camera, renderer.domElement );
    controls.enableDamping = true;
    controls.dampingFactor = 0.08;
    controls.enableZoom = false;
    controls.enablePan = false;
    controls.enabled = true;
    controls.maxAzimuthAngle = Math.PI * 0.2;
    controls.minAzimuthAngle = Math.PI * 1.7;
    controls.maxPolarAngle = 1.7;
    controls.minPolarAngle = 1.2;
    controls.autoRotate = false;
    controls.autoRotateSpeed = 2;
  }

function buildLightEnviroment () {
    const light_Directional = new THREE.DirectionalLight(0xFFFFFF);
    light_Directional.position.set(3, 3, 3);
    light_Directional.intensity = 0.5;
    light_Directional.castShadow = true;
    light_Directional.shadow.bias = -0.00001;
    light_Directional.shadow.mapSize.width = 2048;
    light_Directional.shadow.mapSize.height = 2048;
    scene.add(light_Directional);

    const light_Ambient = new THREE.AmbientLight(0xFFFFFF);
    light_Ambient.intensity = 0.15;
    scene.add(light_Ambient);
 
  const lightpoint3 = new THREE.PointLight(0xFFFFFF,0.4);
  lightpoint3.position.set(100,200,500);
   scene.add(lightpoint3);

    mat_Enviroment = new THREE.TextureLoader().load(texturePath_Enviroment);
    mat_Enviroment.mapping = THREE.SphericalReflectionMapping;
}

function buildSphereGeometry () {
const geometry = new THREE.SphereGeometry( 0.50, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xff4514} );
const sphere = new THREE.Mesh( geometry, material );
  sphere.position.y = 1;
  sphere.position.x = -0.6;
  sphere.position.z = 1.1;
scene.add( sphere );
 
  const geometry1 = new THREE.SphereGeometry( 0.50, 32, 32 );
 
const material1 = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const sphere2 = new THREE.Mesh( geometry1, material1 );
  sphere2.position.y = 2.8;
  sphere2.position.x = 0;
scene.add( sphere2 );
 
   const geometry2 = new THREE.SphereGeometry( 0.50, 32, 32 );
 
const material2 = new THREE.MeshBasicMaterial( {color: 0x03acee} );
const sphere3 = new THREE.Mesh( geometry2, material2 );
  sphere3.position.y = 1.5;
  sphere3.position.x = 0.6;
  sphere3.position.z = -0.9;
scene.add( sphere3 );
}

function buildModel () {
    const loader_GLTF = new THREE.GLTFLoader();
    loader_GLTF.load(modelPath_MyModel, function (gltf) {

        myModel = gltf.scene;
        scene.add(myModel);
        myModel.position.y = -2.3;

        myModel.traverse(function (child) {
            if (child.isMesh) {

                child.frustumCulled = false;

                if ( child.name == 'brain_right_core') {
                     
          brainRight = child;

                       
          child.material = mat_BrainCoreBlue;
                    child.renderOrder = 1;

                    child.castShadow = true;
                    child.receiveShadow = true;
               

                } else if ( child.name == 'brain_right_inner'  ) {
          child.material = mat_BrainInnerOutterBlue;
                    child.renderOrder = 2;
          
                } else if ( child.name == 'brain_right_outer' ) {
                    child.material = mat_BrainInnerOutterBlue;
                    child.renderOrder = 4;

                } else if ( child.name == 'brain_left_core') {
                    child.material = mat_BrainCoreRed;
                    child.renderOrder = 2;

                } else if ( child.name == 'brain_left_inner') {
                    child.material = mat_BrainInnerOutterRed;
                    child.renderOrder = 3;

                    child.castShadow = true;
                    child.receiveShadow = true;

                } else if ( child.name == 'brain_left_outer') {
                    child.material = mat_BrainInnerOutterRed;
                    child.renderOrder = 5;

                } else if ( child.name == 'bottle') {
                    child.material = mat_GlassMatCapBack;
                    child.renderOrder = 6;

                    let bottleClone = child.clone();
                    bottleClone.material = mat_GlassMatCapFront;

                    scene.getObjectByName( "BrainBottle" ).add( bottleClone );
          
         }
      }
        });});
}

function buildMaterial () {

    mat_GlassMatCapBack = new THREE.MeshMatcapMaterial({
        color: 0xFFFFFF,
        matcap: mat_Enviroment,
        side: THREE.BackSide,
        transparent: true,
        opacity: 0.35,
        blending: THREE.AdditiveBlending, // THREE.CustomBlending for normal look
    });

    mat_GlassMatCapFront = new THREE.MeshMatcapMaterial({
        color: 0xFFFFFF,
        matcap: mat_Enviroment,
        side: THREE.FrontSide,
        transparent: true,
        opacity: 1,
        blending: THREE.AdditiveBlending, // THREE.CustomBlending for normal look
    });

    mat_BrainCoreBlue  = new THREE.MeshPhongMaterial({
    color: 0x25cdda,
        emissive: 0x000000,
        blending: THREE.CustomBlending,
        side: THREE.FrontSide,
        depthWrite: false,
        depthTest: true,
        transparent: true,
        opacity: 0.60,
    
    });

    mat_BrainInnerOutterBlue = new THREE.MeshPhongMaterial({
        color: 0x25cdda,
        emissive: 0x00FFFF,
        blending: THREE.CustomBlending,
        side: THREE.FrontSide,
        depthWrite: false,
        depthTest: true,
        transparent: true,
        opacity: 0.20,
    });

    mat_BrainCoreRed = new THREE.MeshPhongMaterial({
        color: 0xcb5332,
        emissive: 0x000000,
        blending: THREE.CustomBlending,
        side: THREE.FrontSide,
        depthWrite: false,
        depthTest: true,
        transparent: true,
        opacity: 0.80,
    });

    mat_BrainInnerOutterRed = new THREE.MeshPhongMaterial({
        color: 0xcb5332,
        emissive: 0xFFFF00,
        blending: THREE.CustomBlending,
        side: THREE.FrontSide,
        depthWrite: false,
        depthTest: true,
        transparent: true,
        opacity: 0.20,
    });
}

// EVENTS

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

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

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

function onDocumentMouseMove( event ) {

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

            }

function animate () {
    requestAnimationFrame(animate);
     render();
                if ( statsEnabled ) stats.update();

            }

            function render() {

                targetX = mouseX * .001;
                targetY = mouseY * .001;
        
            if ( myModel );{
       
                    myModel.rotation.y += 0.05 * ( targetX - myModel.rotation.y );
                    myModel.rotation.x += 0.05 * ( targetY - myModel.rotation.x );
          }
        
          renderer.render(scene, camera);

    controls.update();
}        
     
function startApplication () {
   
    animate();
}

function watchLoadingManager () {
    THREE.DefaultLoadingManager.onStart = function ( url, itemsLoaded, itemsTotal ) {

    };

    THREE.DefaultLoadingManager.onLoad = function () {
        console.log('Loading Complete!');
        isLoaded = true;
        startApplication();

    };

    THREE.DefaultLoadingManager.onProgress = function ( url, itemsLoaded, itemsTotal ) {
        // console.log('Loading file: ' + url + '.\nLoaded ' + itemsLoaded + ' of ' + itemsTotal + ' files.');

    };

    THREE.DefaultLoadingManager.onError = function ( url ) {
        // console.log('There was an error loading ' + url);

    };
}

initMainApp();

//GSAP

gsap.to(brainRight.position,{duration: 2, delay: 2, rotate: 720, scale: 0.3, ease: 'bounce' });

 

 

Thanks in advance.

Link to comment
Share on other sites

Ah, thanks pal!

I'm certainly more comfortable with SVG - learning a little three.js at the moment, so I'm also newbie level here. ☺️


I chatted to a mate who has more three.js know-how and he said that you may need to use the traverse function on the model container.
I see you're using it inside the buildModel function...
 

modelContainer.traverse( child => {
 if(child.name === 'your_name') {
   gsap.to(child, {})
}
}


Maybe give something like this a go and if that doesn't help - could you maybe make a minimal demo? There's more chance of us being able to help when we can poke the code around a bit ☺️

  • Like 2
Link to comment
Share on other sites

That error means that the target object doesn't have a "rotate" property at all. Are you positive that it does? Maybe try to console.log() it right before the tween to verify. 

 

I'm not a THREE.js guy, but this looks wrong to me: 

gsap.to(brainRight.position,{duration: 2, delay: 2, rotate: 720, scale: 0.3, ease: 'bounce' });

I assume brainRight.position would have values like x, y, and z but not rotate or scale which is what you're attempting to animate there. See what I mean? 

 

Isn't it supposed to be more like:

gsap.to(brainRight.rotation, {duration: 2, delay: 2, z: 720, ease: 'bounce' });
gsap.to(brainRight.scale, {duration: 2, delay: 2, x: 0.3, y: 0.3, z: 0.3, ease: 'bounce' });

?

  • Like 2
Link to comment
Share on other sites

19 minutes ago, GreenSock said:

That error means that the target object doesn't have a "rotate" property at all. Are you positive that it does? Maybe try to console.log() it right before the tween to verify. 

 

I'm not a THREE.js guy, but this looks wrong to me: 


gsap.to(brainRight.position,{duration: 2, delay: 2, rotate: 720, scale: 0.3, ease: 'bounce' });

I assume brainRight.position would have values like x, y, and z but not rotate or scale which is what you're attempting to animate there. See what I mean? 

 

Isn't it supposed to be more like:


gsap.to(brainRight.rotation, {duration: 2, delay: 2, z: 720, ease: 'bounce' });
gsap.to(brainRight.scale, {duration: 2, delay: 2, x: 0.3, y: 0.3, z: 0.3, ease: 'bounce' });

?

Oh thank you! I have been mixing and matching for too long so I didn't even see this. Yes I'm able to target parts of the model now instead of having the whole canvas being animated. Thanks a lot everyone for helping me out!

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...