Интеграция с Angular

Создание Angular проекта и установка Babylon.js

Для начала необходимо создать новый Angular проект с использованием Angular CLI:

ng new babylon-angular-app
cd babylon-angular-app

Установка Babylon.js осуществляется через npm:

npm install @babylonjs/core @babylonjs/gui @babylonjs/loaders

@babylonjs/core содержит ядро движка и основные компоненты сцены. @babylonjs/gui добавляет возможность работы с интерфейсом. @babylonjs/loaders необходим для импорта 3D-моделей в популярных форматах.

Создание компонента для сцены Babylon.js

Создается отдельный компонент, отвечающий за визуализацию 3D сцены:

ng generate component BabylonScene

В шаблоне компонента (babylon-scene.component.html) необходимо определить контейнер для канваса:

<div #renderCanvasContainer class="canvas-container">
    <canvas #renderCanvas></canvas>
</div>

В стиле можно задать размеры канваса:

.canvas-container {
    width: 100%;
    height: 100vh;
    overflow: hidden;
}
canvas {
    width: 100%;
    height: 100%;
    display: block;
}

Инициализация сцены и движка

В файле компонента (babylon-scene.component.ts) создается движок и сцена:

import { Component, AfterViewInit, ViewChild, ElementRef, OnDestroy } from '@angular/core';
import { Engine, Scene, ArcRotateCamera, Vector3, HemisphericLight, MeshBuilder } from '@babylonjs/core';

@Component({
  selector: 'app-babylon-scene',
  templateUrl: './babylon-scene.component.html',
  styleUrls: ['./babylon-scene.component.css']
})
export class BabylonSceneComponent implements AfterViewInit, OnDestroy {
  @ViewChild('renderCanvas', { static: true }) renderCanvas!: ElementRef<HTMLCanvasElement>;

  private engine!: Engine;
  private scene!: Scene;

  ngAfterViewInit(): void {
    this.engine = new Engine(this.renderCanvas.nativeElement, true);
    this.scene = new Scene(this.engine);

    this.createCamera();
    this.createLight();
    this.createObjects();

    this.engine.runRenderLoop(() => {
      this.scene.render();
    });

    window.addEventListener('resize', () => {
      this.engine.resize();
    });
  }

  private createCamera(): void {
    const camera = new ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 2.5, 10, Vector3.Zero(), this.scene);
    camera.attachControl(this.renderCanvas.nativeElement, true);
  }

  private createLight(): void {
    new HemisphericLight('light', new Vector3(0, 1, 0), this.scene);
  }

  private createObjects(): void {
    MeshBuilder.CreateBox('box', { size: 2 }, this.scene);
    MeshBuilder.CreateSphere('sphere', { diameter: 2, segments: 16 }, this.scene);
  }

  ngOnDestroy(): void {
    this.engine.dispose();
  }
}

Ключевые моменты:

  • Использование @ViewChild для получения ссылки на канвас.
  • Инициализация движка с флагом true для включения антиалиасинга.
  • Создание ArcRotateCamera для управления сценой через мышь.
  • Добавление света HemisphericLight для равномерного освещения объектов.
  • Очистка ресурсов через ngOnDestroy для предотвращения утечек памяти.

Импорт компонента в основной модуль

Компонент подключается в app.module.ts и используется в шаблоне app.component.html:

<app-babylon-scene></app-babylon-scene>

Загрузка внешних моделей и текстур

Для работы с внешними 3D-моделями используется модуль @babylonjs/loaders. Пример загрузки GLTF модели:

import { SceneLoader } from '@babylonjs/core';

private loadModel(): void {
  SceneLoader.Append('/assets/models/', 'model.gltf', this.scene, (scene) => {
    console.log('Модель загружена');
  });
}

Модели следует размещать в папке src/assets/models. Важно, чтобы путь указывал именно на директорию, где лежит модель, а не на полный путь к файлу, так как SceneLoader.Append автоматически ищет файл внутри указанной директории.

Интеграция GUI в Angular

Babylon.js GUI позволяет создавать пользовательские интерфейсы прямо поверх 3D сцены:

import { AdvancedDynamicTexture, Button, Control } from '@babylonjs/gui';

private createGUI(): void {
  const guiTexture = AdvancedDynamicTexture.CreateFullscreenUI('UI');
  const button = Button.CreateSimpleButton('but', 'Нажми меня');
  button.width = '150px';
  button.height = '40px';
  button.color = 'white';
  button.background = 'blue';
  button.onPointerUpObservable.add(() => {
    alert('Кнопка нажата');
  });
  guiTexture.addControl(button);
}

GUI полностью интегрируется с Angular, что позволяет использовать реактивные подходы для управления событиями и состояниями.

Оптимизация и производительность

  • Уменьшение количества полигонов и использование Level of Detail (LOD) для моделей.
  • Использование scene.freezeActiveMeshes() для статических объектов.
  • Контроль за частотой рендеринга через engine.runRenderLoop и scene.render().
  • Очистка ресурсов при смене маршрутов Angular, чтобы избежать накопления объектов в памяти.

Адаптация под Angular маршрутизацию

Если сцена Babylon.js находится на отдельном маршруте, важно запускать и уничтожать движок при инициализации и уничтожении компонента:

ngOnInit(): void {
  this.initScene();
}

ngOnDestroy(): void {
  this.engine.stopRenderLoop();
  this.engine.dispose();
}

Это предотвращает падения производительности при навигации между страницами.

Заключение по архитектуре интеграции

Структура Angular приложения с Babylon.js должна обеспечивать:

  • Отделение логики рендеринга от бизнес-логики.
  • Легкую замену и загрузку моделей через сервисы Angular.
  • Безопасное управление ресурсами сцены и движка.
  • Полное использование реактивной системы Angular для событий GUI и управления состояниями.

Такой подход позволяет создавать масштабируемые и производительные 3D-приложения на базе Babylon.js в экосистеме Angular.