(Three.js) How to clone and move 3D models along different paths

I have loaded an animated 3D model of a baby in my Three.js scene. I can update its position in my animate loop and it moves along the path I want. But now I want to load in more than one model and have them all move in different paths.

I looked up tutorials like this and their example but I can’t adapt the code correctly to my project. I don’t see any multiple instances/clones of the babies let alone seeing them animated + moving along a path (sc here). How do I add multiple animated models but also update their position in the animate() loop to have them move along different paths?

My original code when just one animated model loads and moves on a path:

import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
import { FontLoader } from 'FontLoader';
import {GLTFLoader} from 'https://unpkg.com/[email protected]/examples/jsm/loaders/GLTFLoader.js';
import * as SkeletonUtils from 'https://unpkg.com/[email protected]/examples/jsm/utils/SkeletonUtils.js';

//see three.js version
//console.log(THREE.REVISION);



//All titles; cleaned scraped datas
const sentences = [
    "AI: Its nature and future", "Nature and scope of AI techniques", "Advancing mathematics by guiding human intuition with AI", "Cooperative AI: machines must learn to find common ground", ...... "A comprehensive survey of ai-generated content (aigc): A history of generative ai from gan to chatgpt"
];

//dict obj of all unique words (occuring more than once) across all titles
const uniq_words = {
    "ai": 218,
    "its": 11,
    "nature": 16,
    ....
    "making": 2,
    "ultrafiltration": 2
};

//stores every unique word and the coordinates of all titles containing that word
const word_and_coord = {};

//3 basic needs to display aanything in three js
let camera, scene, renderer;

//stores each title temporarily
let message;

//stores every title and its world position/coordinate/Vector3 value in the scene
const title_and_coord = {};

//adding a camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(0, 0, 1500);

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

//don't change anything below because it messes resizing
//not even spacing or formatting
renderer = new THREE.WebGLRenderer( {antialias: true} );
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
//controls.update();
controls.addEventListener('change', animate);
window.addEventListener('resize', onWindowResize);

const light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );

//invisible path to follow for the baby
let curve = null;

//creating a font and using it as the geometry
const loader = new FontLoader();
loader.load('./fonts/Montserrat_Regular.json', function (font){

    const color = 0xFFF1DF;
    const matLite = new THREE.MeshBasicMaterial({
        color: color,
        opacity: 1.0,
        side: THREE.DoubleSide
    });

    for (let i = 0; i < sentences.length; i++) {
        
        var title_coord = new THREE.Vector3();
        message = sentences[i];

        const shapes = font.generateShapes(message, 100);
        const geometry = new THREE.ShapeGeometry(shapes);

        //generate a 1 or -1
        var plusOrMinus = Math.random() < 0.5 ? -1 : 1;
        var plusOrMinus2 = Math.round(Math.random()) * 2 - 1;
        
        var title = new THREE.Mesh(geometry, matLite);
            title.position.x = i*90*plusOrMinus*Math.random();


            title.position.y = plusOrMinus2 * (300 + ((Math.random()*100)) + (Math.random()*1000));

            //z is very spread out for this combination of values
            title.position.z = ( 700 + ((Math.random()*100) + 1) + (i*Math.random()*1000) );
            //plusOrMinus * ( 900 + ((Math.random()*10) + 0) + (Math.random()*100) + (Math.random()*1000) );

            //title.position.z = plusOrMinus2 * (900 + ((Math.random()*10) + 0) + (Math.random()*100) + (Math.random()*1000));

            //title.position.y = plusOrMinus2 * ( 900 + ((Math.random()*10) + 0) + (Math.random()*100) + (Math.random()*1000) );

            //title.rotateY(Math.random() * 1.1 * 3.14 * plusOrMinus2);
            title.scale.setScalar(0.5)
            
            scene.add(title);
            title_and_coord[message] = title.getWorldPosition(title_coord);
            
            //printing each title's coord
            //console.log(title.getWorldPosition(title_coord));

    //title loop ends here
    }
    
    /*
    Rendering lines across titles with shared words
    
    PSEUDOCODE:
    - Loop through each unique word in uniq_words dictionary (604 at present)
        - Loop through all the titles (508 at present)
            - if the word appears in the title
                - Make a new dict/add to a dict 
                where the key is the word and value is the world coordinates of the title
                for example:
                    {
                    "ai" : [ {242.34, 234.989, 8756.21}, {}, ...],
                    "its" : [ {922.34, 834.989, 3177.21}, Vector3, Vector3, ....],
                    .....
                    }
        Once dict is complete, loop through it and draw lines intersecting at all those coordinates
    */
    
    for (let word in uniq_words) {
        var shared_coords = [];
        
        //console.log(title_and_coord)
        Object.keys(title_and_coord).forEach(key => {
            //console.log(title_and_coord[key]);

            const lwrcase = key.toLowerCase();
            if (lwrcase.includes(word)){

                //append the coord of title to list/array initialized before the loop
                shared_coords.push(title_and_coord[key]);
            }
        });
        word_and_coord[word] = shared_coords;
        
    }
    //console.log(word_and_coord);

    
    //setting up the main associations/mappings/lines 
    const material = new THREE.LineDashedMaterial({
        color: 0xFF8700, 
        dashSize: 20, 
        gapSize: 7.5,
        lineWidth: 0.1
    }); 

    //loop through every word and its matching coordinates
    for (const [key, value] of Object.entries(word_and_coord)) {
        //console.dir(`Key: ${key}, Value: ${value}`);

        //.setFromPoints(x) needs x to be a list/array of Vec3 values
        //value of word_and_coord obj is already an array of Vec3 values
        const geometry = new THREE.BufferGeometry().setFromPoints(value);
        const line = new THREE.Line( geometry, material );
        //to have dashes on a line you have to call .computeLineDistance() on your geometry
        line.computeLineDistances();
        scene.add(line);

    }
    //console.log(word_and_coord);
});

let mixer = null;
let elapsedTime = 0;  // To keep track of time
const pathDuration = 10000;  // Duration in seconds for one complete loop
let baby = null;

//3d baby model
const loader_3d = new GLTFLoader().setPath('baby_1motions/');
loader_3d.load('scene.gltf', (gltf) => {
    baby = gltf.scene;
    scene.add(baby);

    var light = new THREE.AmbientLight(0xffffff);
    scene.add(light);

    //console.log(gltf.animations);
    mixer = new THREE.AnimationMixer(baby);
    const action = mixer.clipAction(gltf.animations[0]);
    action.play();

})

const clock = new THREE.Clock();


//render the entire scene
function animate() {

    //console.log(word_and_coord);
    const delta = clock.getDelta();
    elapsedTime += delta;

    if(mixer){
        mixer.update(delta);
    }

    if (baby){
        const t = (elapsedTime % pathDuration) / pathDuration;  // Normalize to [0, 1]
        curve = new THREE.CatmullRomCurve3(word_and_coord["ai"], true);
        const position = curve.getPoint(t);
        baby.position.copy(position);
    }
        
    renderer.render( scene, camera );

}

renderer.setAnimationLoop(animate);

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

These were the changes made when adding more models (comes in after fontloader block ends):

let mixer = null;
let elapsedTime = 0;  // To keep track of time
const pathDuration = 10000;  // Duration in seconds for one complete loop
let baby = null;
let baby_global;
let baby_gltf_global;
let clips;

//3d baby model
const loader_3d = new GLTFLoader().setPath('baby_1motions/');
loader_3d.load('scene.gltf', (gltf) => {
    baby = gltf.scene;
    baby_global = gltf.scene;
    baby_gltf_global = gltf;
    clips = gltf.animations;
})

//cloning babies
const objects = [];
const mixers = [];
let babyClone = null;
for (const [key, value] of Object.entries(word_and_coord)){

    babyClone = SkeletonUtils.clone(baby_global);
    babyClone.position.copy(value, true);
    
    scene.add(babyClone);
    var light2 = new THREE.AmbientLight(0xffffff);
    scene.add(light2);
    objects.push(babyClone);

    const mixer2 = new THREE.AnimationMixer(babyClone);
    const clip2 = THREE.AnimationClip.findByName(clips, 'crawling_mixamo');
    const action2 = mixer2.clipAction(clip2);
    action2.play();
    mixers.push(mixer2);

}


const clock = new THREE.Clock();
//const timer = new Timer();
//render the entire scene
function animate() {

    //console.log(word_and_coord);
    const delta = clock.getDelta();
    elapsedTime += delta;

    if (mixers){
        mixers.forEach(function(mixer) {
            mixer.update(delta);
        });
    }
    

    if (babyClone){
        for (const [key, value] of Object.entries(word_and_coord)){
            curve_new = new THREE.CatmullRomCurve3(value, true);
            const position3 = curve_new.getPoint(t);
            babyClone.position.copy(position3);
        }
        
    }
        
    renderer.render( scene, camera );

}

P.S. I already have the Vec3 coordinates for making the different paths – they are stored as values in a dict object called word_and_coord .

Sorry for the messy code – still a beginner figuring this stuff out!

New contributor

Iti Gupta is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật