Обработать загруженные данные
Извлечение данных из файла glTF обычно следует предсказуемому шаблону, особенно если файл содержит одну анимированную модель, как эти три файла. Это означает, что мы можем создать функцию setupModel, а затем запустить ее для каждого из трех файлов. Сделаем это в отдельном модуле. Откройте или создайте модуль birds / setupModel.js и создайте функцию по уже знакомому шаблону:
function setupModel(data) {}
export { setupModel };
Идея этой функции заключается в том, что мы можем передать загруженные данные и получить обратно модель птицы, готовую к добавлению в сцену. Затем импортируйте этот новый модуль в birds.js и передайте загруженные данные. Наконец, верните результаты для использования в World.
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { setupModel } from './setupModel.js';
async function loadBirds() {
const loader = new GLTFLoader();
const parrotData = await loader.loadAsync('/assets/models/Parrot.glb');
console.log('Squaaawk!', parrotData);
const parrot = setupModel(parrotData);
return { parrot }
}
Извлечь сетку из загруженных данных
На данный момент у нас есть необработанные загруженные данные в функции setupModel. Следующим шагом является извлечение модели, а затем выполнение любой обработки для ее подготовки к использованию. Объем работы, которую нам здесь нужно сделать, зависит от модели и того, что мы хотим с ней делать. Здесь все, что нам нужно сделать, это извлечь сетку, но в следующей главе у нас будет немного больше работы, поскольку мы подключим анимационный клип к сетке. Снова посмотрите на загруженные данные в консоли и разверните файл gltfData.scene. Это группа, и любые сетки, которые есть в файле, будут дочерними по отношению к группе. Доступ к ним можно получить с помощью массива group.children. Если вы посмотрите внутрь, то увидите, что внутри glTF.scene.children только один объект, так что это должна быть наша модель попугая. Используя эти знания, мы можем завершить функцию setupModel:
function setupModel(data) {
const model = data.scene.children[0];
return model;
}
Примечание A: если вы нажмете переключатель, чтобы завершить сцену в редакторе, а затем просмотреть массив gltfData.scene.children в консоли, он будет пустым. Это потому, что к тому моменту, когда вы посмотрите на нее, сетка уже была удалена и добавлена к сцене.
Примечание B: вы также можете просто добавить gltf.scene в свою сцену, поскольку это группа. Это добавит дополнительный узел к вашему графу сцены, но все по-прежнему будет работать. Однако рекомендуется делать граф сцены как можно более простым, поскольку каждый узел означает, что для визуализации сцены требуются дополнительные вычисления.
Добавьте сетку в сцену
В World, loadBirds теперь возвращает сетку попугая, и вы можете добавить ее в сцену:
async init() {
const { parrot } = await loadBirds();
scene.add(parrot);
}
Загрузите две другие птицы
Вы можете использовать один экземпляр GLTFLoader для загрузки любого количества файлов. При выполнении нескольких асинхронных операций с асинхронными функциями вы должны (в большинстве случаев) использовать Promise.all. Мы рассмотрим причину этого более подробно в приложении, но вот краткая версия. Во-первых, вот очевидный способ загрузки двух других файлов:
// Don't do this!
const parrotData = await loader.loadAsync('/assets/models/Parrot.glb');
const flamingoData = await loader.loadAsync('/assets/models/Flamingo.glb');
const storkData = await loader.loadAsync('/assets/models/Stork.glb');
const parrot = setupModel(parrotData);
const flamingo = setupModel(flamingoData);
const stork = setupModel(storkData);
В этом подходе есть проблема. Как мы заявили выше, await означает здесь ждать, пока файл не загрузится. Это означает, что приложение будет ждать, пока попугай полностью загрузится, затем начнет загружать фламинго, дождется полной загрузки и, наконец, начнет загружать аиста. При таком подходе загрузка займет почти в три раза больше времени, чем следовало бы. Вместо этого мы хотим, чтобы все три файла загружались одновременно, и самый простой способ сделать это - использовать Promise.all.
const [parrotData, flamingoData, storkData] = await Promise.all([
loader.loadAsync('/assets/models/Parrot.glb'),
loader.loadAsync('/assets/models/Flamingo.glb'),
loader.loadAsync('/assets/models/Stork.glb'),
]);
Затем мы можем обработать загруженные данные каждого файла с помощью функции setupModel. Как только мы это сделаем, вот наша (почти полная) функция loadModels:
async function loadBirds() {
const loader = new GLTFLoader();
const [parrotData, flamingoData, storkData] = await Promise.all([
loader.loadAsync('/assets/models/Parrot.glb'),
loader.loadAsync('/assets/models/Flamingo.glb'),
loader.loadAsync('/assets/models/Stork.glb'),
]);
console.log('Squaaawk!', parrotData);
const parrot = setupModel(parrotData);
const flamingo = setupModel(flamingoData);
const stork = setupModel(storkData);
return {
parrot,
flamingo,
stork,
};
}
В World у вас теперь есть все три модели. Добавьте их в свою сцену:
async init() {
const { parrot, flamingo, stork } = await loadBirds();
scene.add(parrot, flamingo, stork);
}
Замечательно! Что ж…
Прямо как в зоопарке!
Переместите птиц в позицию
Возможно, для моделей, загруженных из файла glTF, уже указана позиция, но в данном случае это не так, поэтому все три модели начинаются с точки (0,0,0), и все они перемешаны друг с другом. Мы изменим положение каждой птицы, чтобы она выглядела так, как будто она летит строем:
const parrot = setupModel(parrotData);
parrot.position.set(0, 0, 2.5);
const flamingo = setupModel(flamingoData);
flamingo.position.set(7.5, 0, -10);
const stork = setupModel(storkData);
stork.position.set(0, -2.5, -10);
Последний модуль birds.js
Модуль birds.js завершен. Вот последний код:
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { setupModel } from './setupModel.js';
async function loadBirds() {
const loader = new GLTFLoader();
const [parrotData, flamingoData, storkData] = await Promise.all([
loader.loadAsync('/assets/models/Parrot.glb'),
loader.loadAsync('/assets/models/Flamingo.glb'),
loader.loadAsync('/assets/models/Stork.glb'),
]);
console.log('Squaaawk!', parrotData);
const parrot = setupModel(parrotData);
parrot.position.set(0, 0, 2.5);
const flamingo = setupModel(flamingoData);
flamingo.position.set(7.5, 0, -10);
const stork = setupModel(storkData);
stork.position.set(0, -2.5, -10);
return {
parrot,
flamingo,
stork,
};
}
export { loadBirds };
Сосредоточьте камеру на попугае
Последнее, что мы сделаем, - это настроим цель OrbitControls. В настоящее время это положение по умолчанию, центр сцены. Теперь, когда мы переместили птиц в строю, они оказались где-то вокруг хвоста попугая. Было бы лучше, если бы камера фокусировалась на центре птицы, а не на ее хвосте. Это можно легко настроить, скопировав parrot.position в controls.target. Однако для этого нам нужно получить доступ к элементам управления в .init, поэтому сначала давайте преобразуем его в переменную в области модуля.
let camera;
let controls;
let renderer;
let scene;
let loop;
constructor(container) {
camera = createCamera();
renderer = createRenderer();
scene = createScene();
loop = new Loop(camera, scene, renderer);
container.append(renderer.domElement);
controls = createControls(camera, renderer.domElement);
const { ambientLight, mainLight } = createLights();
loop.updatables.push(controls);
scene.add(ambientLight, mainLight);
const resizer = new Resizer(container, camera, renderer);
}
Теперь элементы управления доступны из .init, и мы можем переместить цель в центр попугая.
async init() {
const { parrot, flamingo, stork } = await loadBirds();
// move the target to the center of the front bird
controls.target.copy(parrot.position);
scene.add(parrot, flamingo, stork);
}
Далее мы познакомим вас с системой анимации three.js и покажем, как воспроизводить анимационные клипы, загруженные вместе с моделями птиц.
Спасибо за просмотр. К сожалению, на момент написания статьи у меня не получилось загрузить модель орла. Может у вас получится добавить новую модель? Попробуйте. Буду рад вашей обратной связи.