Доступ к частям импорта GLTF в основном цикле рендеринга (извините, если noobish) - PullRequest
1 голос
/ 30 сентября 2019

Я разработчик Unity, пытаюсь выучить Three.js. Одна из многих проблем, с которыми я сталкиваюсь, может показаться простой, но для меня это боль в * **.

Все, что я хочу сделать, это импортировать и анимировать 3D-логотип в моем приложении Three.js. Этот логотип состоит из 4 различных сеток (от elem1 до elem4), которые не перекрываются. Он был экспортирован как FBX, а затем преобразован в GLTF с помощью онлайн-конвертера. Нет проблем при импорте, изменении его размера и даже изменении его материала.

Моя проблема: как обращаться ко всему объекту, а также к 4 его элементам, чтобы анимировать их в моей «одушевленной» функции(Я имею в виду в моем основном цикле рендеринга)? Единственное, что я мог сделать, это создать вторую функцию «одушевления» в обратном вызове загрузчика , что мне немного странно. Я не могу найти способ ссылаться на них в основной области моего приложения.

Дамп импорта из GLTF дает эту иерархию (они называются «узлами», я прав?):

AuxScene [Scene]
    *no-name* [Object3D]
        elem1 [Mesh]
        elem2 [Mesh]
        elem3 [Mesh]
        elem4 [Mesh]

Вот мой код, очищенный от ненужных вещей:

'use strict';

// CANVAS AND RENDERER
const canvas = document.querySelector('#myCanvas');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );

// SCENE AND BACKGROUND
var scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
scene.background = loader.load( 'images/background.jpg');

// CAMERA
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 100;

// MATERIALS
var material = new THREE.MeshNormalMaterial ();


// ---------------------------------- LOGO IMPORTATION
var gltfLoader = new THREE.GLTFLoader();
var root;
var elem1, elem2, elem3, elem4;

gltfLoader.load('gltf/logo.gltf', function(gltf)  {
  root = gltf.scene;
  root.rotation.x = Math.PI / 2;
  scene.add(root);

  root.traverse( function( child ) {
    if ( child instanceof THREE.Mesh ) { child.material = material; }
  } );

  elem1 = root.getObjectByName('element1');
  elem2 = root.getObjectByName('element2');
  elem3 = root.getObjectByName('element3');
  elem4 = root.getObjectByName('element4');

  console.log(dumpObject(root).join('\n'));

  // logo animations
  var speed = 0.0005;
  var turnsBeforeStop = 4;
  requestAnimationFrame( animate2 );

  function animate2( time ) {
    root.rotation.z = Math.sin (time * 0.0005) * 0.5;
    root.rotation.x = Math.PI/3 + Math.sin(time * 0.0003) * 0.5;

    if(elem1.rotation.y < Math.PI * turnsBeforeStop){
      elem1.rotation.y = time * speed*2;
      elem2.rotation.z = time * speed*2;
      elem3.rotation.y = time * -speed;
      elem4.rotation.z = time * -speed*2;
    }
    requestAnimationFrame( animate2 );
  }
});
// ------------------------------------------------------------ END LOGO


renderer.render( scene, camera );
requestAnimationFrame( animate );

// ANIMATION MAIN LOOP
function animate( time ) {
  /*
        This is where I would like to access my logo (as a whole, and also its separate parts).
        But root.rotation or elem1.rotation won't work here and give me this error :
        TypeError: undefined is not an object (evaluating 'elem1.rotation')
  */  
  renderer.render( scene, camera );
  requestAnimationFrame( animate );
}


// OBJECT DUMPING
function dumpObject(obj, lines = [], isLast = true, prefix = '              ') {
  const localPrefix = isLast ? '└─' : '├─';
  lines.push(`${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]`);
  const newPrefix = prefix + (isLast ? '  ' : '│ ');
  const lastNdx = obj.children.length - 1;
  obj.children.forEach((child, ndx) => {
    const isLast = ndx === lastNdx;
    dumpObject(child, lines, isLast, newPrefix);
  });
  return lines;
}

Спасибо за любую помощь.

1 Ответ

1 голос
/ 30 сентября 2019

Проблема в том, что вы пытаетесь получить доступ к root до того, как ему будет присвоено значение (модель glTF). Обратите внимание, что GLTFLoader.load() является асинхронным. Поэтому обратный вызов onLoad(), который устанавливает root, вызывается не сразу, а с некоторой задержкой.

Существует несколько подходов для решения этой проблемы. Вы можете проверить в цикле анимации, если root не undefined. Это будет выглядеть так:

function animate( time ) {
    requestAnimationFrame( animate );

    if ( root ) {

        // do something with root

    }

    renderer.render( scene, camera );

}

Или вы начнете анимацию после того, как onLoad() закончится. В этом случае код будет выглядеть так:

'use strict';

// CANVAS AND RENDERER
const canvas = document.querySelector('#myCanvas');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );

// SCENE AND BACKGROUND
var scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
scene.background = loader.load( 'images/background.jpg');

// CAMERA
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 100;

// MATERIALS
var material = new THREE.MeshNormalMaterial ();


// ---------------------------------- LOGO IMPORTATION
var gltfLoader = new THREE.GLTFLoader();
var root;
var elem1, elem2, elem3, elem4;

gltfLoader.load('gltf/logo.gltf', function(gltf)  {
  root = gltf.scene;
  root.rotation.x = Math.PI / 2;
  scene.add(root);

  root.traverse( function( child ) {
    if ( child instanceof THREE.Mesh ) { child.material = material; }
  } );

  animate(); // start animating

});
// ------------------------------------------------------------ END LOGO



// ANIMATION MAIN LOOP
function animate() {
    requestAnimationFrame( animate );
    // do something with root
    renderer.render( scene, camera );
}


// OBJECT DUMPING
function dumpObject(obj, lines = [], isLast = true, prefix = '              ') {
  const localPrefix = isLast ? '└─' : '├─';
  lines.push(`${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]`);
  const newPrefix = prefix + (isLast ? '  ' : '│ ');
  const lastNdx = obj.children.length - 1;
  obj.children.forEach((child, ndx) => {
    const isLast = ndx === lastNdx;
    dumpObject(child, lines, isLast, newPrefix);
  });
  return lines;
}
...