본문으로 건너뛰기

오버뷰

이 섹션에서는 babylon-mmd가 제공하는 기능들에 대한 개요를 설명합니다.

라이브러리 개요

babylon-mmd는 미쿠미쿠댄스(MMD) 모델과 애니메이션을 위한 Babylon.js 로더와 런타임을 제공하는 타입스크립트로 작성된 라이브러리입니다. 현재 npm 패키지로 배포되고 있습니다.

이 라이브러리는 ESM 또는 UMD 모듈로 사용할 수 있습니다. UMD 빌드는 Babylon.js 플레이그라운드와 같은 환경에서 사용할 수 있습니다.

이 문서는 웹팩과 같은 번들러를 사용하는 ESM 모듈 기반 프로젝트에서의 사용법을 기준으로 작성되었습니다.

babylon-mmd의 Hello World

이 섹션에서는 간단한 예제를 통해 babylon-mmd의 개요를 살펴보겠습니다. 이 예제는 MMD 모델을 로드하고, 카메라와 라이팅을 설정하고, 오디오와 함께 애니메이션을 재생하는 방법을 보여줍니다.

정보

예제를 간결하게 유지하기 위해 사이드 이펙트를 제외한 임포트 문은 생략되었습니다.

// side effects that register the loader
import "babylon-mmd/esm/Loader/pmxLoader";

// side effects that register the animation runtime
import "babylon-mmd/esm/Runtime/Animation/mmdRuntimeCameraAnimation";
import "babylon-mmd/esm/Runtime/Animation/mmdRuntimeModelAnimation";

async function build(canvas: HTMLCanvasElement, engine: Engine): Scene {
const scene = new Scene(engine);
scene.ambientColor = new Color3(0.5, 0.5, 0.5);

const camera = new MmdCamera("mmdCamera", new Vector3(0, 10, 0), scene);

const directionalLight = new DirectionalLight("DirectionalLight", new Vector3(0.5, -1, 1), scene);
directionalLight.intensity = 1.0;

const ground = CreateGround("ground1", { width: 60, height: 60, subdivisions: 2, updatable: false }, scene);

const mmdWasmInstance = await GetMmdWasmInstance(new MmdWasmInstanceTypeSPR());
const physicsRuntime = new MultiPhysicsRuntime(mmdWasmInstance);
physicsRuntime.setGravity(new Vector3(0, -98, 0));
physicsRuntime.register(scene);

// MMD runtime for solving morph, append transform, IK, animation, physics
const mmdRuntime = new MmdRuntime(scene, new MmdBulletPhysics(physicsRuntime));
mmdRuntime.register(scene);

// For synced audio playback
const audioPlayer = new StreamAudioPlayer(scene);
audioPlayer.source = "your_audio_path.mp3";
mmdRuntime.setAudioPlayer(audioPlayer);

// You can also run the animation before it loads. This will allow the audio to run first.
mmdRuntime.playAnimation();

// create a youtube-like player control
new MmdPlayerControl(scene, mmdRuntime, audioPlayer);

const vmdLoader = new VmdLoader(scene);

const cameraAnimation = await vmdLoader.loadAsync("camera_motion", "your_camera_motion_path.vmd");
const cameraRuntimeAnimationHandle = camera.createRuntimeAnimation(cameraAnimation);
camera.setRuntimeAnimation(cameraRuntimeAnimationHandle);
mmdRuntime.addAnimatable(camera);

const assetContainer = await LoadAssetContainerAsync("path/to/your_file.pmx", scene);
assetContainer.addAllToScene();
const mmdMesh = assetContainer.meshes[0] as MmdMesh;

const mmdModel = mmdRuntime.createMmdModel(mmdMesh);
const modelMotion = await vmdLoader.loadAsync("model_motion", "your_model_motion_path.vmd");
const modelRuntimeAnimationHandle = mmdModel.createRuntimeAnimation(modelMotion);
mmdModel.setRuntimeAnimation(modelRuntimeAnimationHandle);

return scene;
}

Babylon.js 플레이그라운드에서 시도해 볼 수 있습니다. https://www.babylonjs-playground.com/#S7XDNP

각 요소가 제공하는 기능을 살펴보겠습니다.

  • 1-6줄: 씬 로딩에 필요한 사이드 이펙트를 등록합니다.

  • 9-17줄: 씬을 생성하고 카메라와 라이팅을 설정합니다.

  • 19-34줄: MMD 런타임을 생성하고 피직스 엔진을 설정합니다. 또한 오디오와 애니메이션을 동기화하기 위한 오디오 플레이어를 구성합니다.

  • 36-37줄: MMD 플레이어 컨트롤을 생성합니다.

  • 39-44줄: VMD 로더를 사용하여 카메라 애니메이션을 로드하고 카메라에 런타임 애니메이션을 설정합니다.

  • 46-53줄: PMX 로더를 사용하여 MMD 모델을 로드하고 VMD 로더를 사용하여 모델 애니메이션을 로드합니다. 그리고 런타임 애니메이션을 설정합니다.

사이드 이펙트 (1-6줄)

// side effects that register the loader
import "babylon-mmd/esm/Loader/pmxLoader";

// side effects that register the animation runtime
import "babylon-mmd/esm/Runtime/Animation/mmdRuntimeCameraAnimation";
import "babylon-mmd/esm/Runtime/Animation/mmdRuntimeModelAnimation";

이 코드는 babylon-mmd의 PMX 로더와 애니메이션 런타임을 Babylon.js SceneLoader에 등록합니다. 이를 통해 PMX 파일을 로드하고 카메라 및 모델 애니메이션을 재생할 수 있습니다.

PMX 로더뿐만 아니라 다른 MMD 모델 로더도 동일한 방식으로 사용할 수 있습니다. 예를 들어, PMD 로더를 사용하려면 다음과 같이 추가할 수 있습니다:

import "babylon-mmd/esm/Loader/pmdLoader";

또는 BPMX 로더를 사용하려면 다음과 같이 추가할 수 있습니다:

import "babylon-mmd/esm/Loader/Optimized/bpmxLoader";
경고

"babylon-mmd" 경로에서 하나의 심볼이라도 임포트되면, 모든 가능한 사이드 이펙트가 적용됩니다.

이것은 Babylon.js 규칙을 따릅니다. 따라서 트리 쉐이킹이 제대로 작동하려면 모든 임포트는 전체 경로로 작성되어야 합니다.

트리 쉐이킹을 올바르게 수행하려면 이 Babylon.js 문서를 참조하세요.

씬 생성 (9-17줄)

const scene = new Scene(engine);
scene.ambientColor = new Color3(0.5, 0.5, 0.5);

const camera = new MmdCamera("mmdCamera", new Vector3(0, 10, 0), scene);

const directionalLight = new DirectionalLight("DirectionalLight", new Vector3(0.5, -1, 1), scene);
directionalLight.intensity = 1.0;

CreateGround("ground1", { width: 60, height: 60, subdivisions: 2, updatable: false }, scene);

이 코드는 Babylon.js 씬을 생성하고 기본 라이팅과 카메라를 설정합니다.

여기서 씬의 ambientColor는 rgb(0.5, 0.5, 0.5)로 설정됩니다. 이것은 임의의 값이 아니라, MMD 머티리얼 구현과 동일한 동작을 재현하기 위해 앰비언트 컬러를 0-0.5 범위로 매핑하기 위해 설정된 값입니다.

디렉셔널 라이트를 사용하는 이유도 MMD 머티리얼의 라이팅 모델을 재현하기 위한 것이며 임의의 설정이 아닙니다.

MMD 런타임 생성 (19-34줄)

const mmdWasmInstance = await GetMmdWasmInstance(new MmdWasmInstanceTypeSPR());
const physicsRuntime = new MultiPhysicsRuntime(mmdWasmInstance);
physicsRuntime.setGravity(new Vector3(0, -98, 0));
physicsRuntime.register(scene);

// MMD runtime for solving morph, append transform, IK, animation, physics
const mmdRuntime = new MmdRuntime(scene, new MmdBulletPhysics(physicsRuntime));
mmdRuntime.register(scene);

// For synced audio playback
const audioPlayer = new StreamAudioPlayer(scene);
audioPlayer.source = "your_audio_path.mp3";
mmdRuntime.setAudioPlayer(audioPlayer);

// You can also run the animation before it loads. This will allow the audio to run first.
mmdRuntime.playAnimation();

이 코드는 MMD 런타임을 생성하고 피직스 엔진을 설정합니다. 또한 오디오와 애니메이션을 동기화하기 위한 오디오 플레이어를 구성합니다.

웹어셈블리 바이너리

상단의 GetMmdWasmInstance 함수는 babylon-mmd의 WASM 바이너리를 로드합니다.

babylon-mmd는 처리 성능을 향상시키기 위해 Rust로 작성되고 WASM 바이너리로 컴파일된 일부 기능을 제공합니다. 웹어셈블리 부분에 해당하는 타입스크립트 구현이 존재하기 때문에 선택적으로 사용할 수 있습니다.

예를 들어, 타입스크립트로 작성된 MMD 런타임 MmdRuntime과 동일한 기능을 제공하는 MmdWasmRuntime이라는 WASM 런타임이 있습니다.

웹어셈블리 바이너리는 주로 MMD 모델 애니메이션 처리 로직을 제공하고 추가적으로 불릿 피직스 엔진을 제공합니다.

const mmdWasmInstance = await GetMmdWasmInstance(new MmdWasmInstanceTypeSPR());

이 예제에서는 WASM 바이너리 타입을 결정하기 위해 MmdWasmInstanceTypeSPR을 사용합니다. SPR은 각각 Single threaded(싱글 스레드), Physics Engine Included(피직스 엔진 포함), Release Build(릴리즈 빌드)를 의미합니다.

즉, 우리가 사용하는 바이너리는 피직스 엔진이 포함된 싱글 스레드 릴리즈 빌드입니다.

다른 WASM 바이너리 타입으로는 SR, SPD 등이 있습니다. SR은 Single threaded, Release Build를 의미하며 피직스 엔진이 포함되지 않은 바이너리입니다. SPD는 Single threaded, Physics Engine Included, Debug Build를 의미합니다.

const physicsRuntime = new MultiPhysicsRuntime(mmdWasmInstance);
physicsRuntime.setGravity(new Vector3(0, -98, 0));
physicsRuntime.register(scene);

babylon-mmd의 WASM 바이너리가 제공하는 불릿 피직스 엔진을 사용하여 피직스 엔진 인스턴스를 생성합니다.

여기서 중력 가속도를 일반적인 지구 중력 가속도인 -9.8 m/s² 대신 -98 m/s²로 설정합니다. 이 값은 MMD의 피직스 엔진 설정과 일치하도록 설정된 것입니다.

// MMD runtime for solving morph, append transform, IK, animation, physics
const mmdRuntime = new MmdRuntime(scene, new MmdBulletPhysics(physicsRuntime));
mmdRuntime.register(scene);

// For synced audio playback
const audioPlayer = new StreamAudioPlayer(scene);
audioPlayer.source = "your_audio_path.mp3";
mmdRuntime.setAudioPlayer(audioPlayer);

// You can also run the animation before it loads. This will allow the audio to run first.
mmdRuntime.playAnimation();

그런 다음 MMD 런타임을 생성합니다.

MMD 런타임은 MMD 애니메이션에 참여하는 요소들을 동기화하고 조율합니다.

// You can also run the animation before it loads. This will allow the audio to run first.
mmdRuntime.playAnimation();

3D 모델과 애니메이션을 로드하는 데 시간이 걸리므로, MmdRuntime.playAnimation()을 호출하여 기다리는 동안 오디오를 미리 재생할 수 있습니다.

애니메이션이 재생되는 동안에도 모델, 카메라, 애니메이션을 MmdRuntime에 동적으로 추가할 수 있습니다.

MMD 플레이어 컨트롤 생성 (36-37줄)

// create a youtube-like player control
new MmdPlayerControl(scene, mmdRuntime, audioPlayer);

이 코드는 MMD 플레이어 컨트롤을 생성합니다. 이 컨트롤은 MMD 애니메이션을 재생, 일시 정지, 오디오 조정 등을 위한 UI를 제공합니다.

이 코드는 빠른 테스트 목적으로 제공되며, 실제 사용을 위해서는 자체 구현을 권장합니다.

VMD 로더 (39-44줄)

const vmdLoader = new VmdLoader(scene);

const cameraAnimation = await vmdLoader.loadAsync("camera_motion", "your_camera_motion_path.vmd");
const cameraRuntimeAnimationHandle = camera.createRuntimeAnimation(cameraAnimation);
camera.setRuntimeAnimation(cameraRuntimeAnimationHandle);
mmdRuntime.addAnimatable(camera);

이 코드는 VMD 로더를 사용하여 카메라 애니메이션을 로드하고 애니메이션을 카메라에 바인딩합니다.

babylon-mmd의 애니메이션 시스템

babylon-mmd는 기본적으로 Babylon.js의 애니메이션 시스템을 사용하지 않고 자체 애니메이션 시스템을 구현합니다.

이는 Babylon.js의 애니메이션 시스템이 대규모 애니메이션 데이터 처리에 최적화되어 있지 않고 MMD의 애니메이션 런타임 사양을 완전히 지원할 수 없기 때문입니다.

babylon-mmd가 제공하는 애니메이션 시스템은 MmdAnimation 컨테이너로 애니메이션 데이터를 관리합니다. 그리고 애니메이션이 재생되려면 특정 객체에 바인딩되어야 합니다.

const cameraRuntimeAnimationHandle = camera.createRuntimeAnimation(cameraAnimation);

바인딩된 애니메이션은 MmdRuntimeAnimation이라고 합니다. 이러한 객체는 일반적으로 직접 접근하는 것이 권장되지 않으므로, MmdCamera.createRuntimeAnimation은 이들에 접근하기 위한 핸들을 반환합니다.

PMX 로더 (46-53줄)

const assetContainer = await LoadAssetContainerAsync("path/to/your_file.pmx", scene);
assetContainer.addAllToScene();
const mmdMesh = assetContainer.meshes[0] as MmdMesh;

const mmdModel = mmdRuntime.createMmdModel(mmdMesh);
const modelMotion = await vmdLoader.loadAsync("model_motion", "your_model_motion_path.vmd");
const modelRuntimeAnimationHandle = mmdModel.createRuntimeAnimation(modelMotion);
mmdModel.setRuntimeAnimation(modelRuntimeAnimationHandle);

이 코드는 PMX 로더를 사용하여 MMD 모델을 로드하고 VMD 로더를 사용하여 모델 애니메이션을 로드합니다. 그런 다음 런타임 애니메이션을 설정합니다.

MMD 모델 로더

babylon-mmd는 PMX, PMD, BPMX와 같은 다양한 MMD 모델 포맷을 지원합니다. 이 예제에서는 PMX 로더를 사용하여 모델을 로드합니다.

const assetContainer = await LoadAssetContainerAsync("path/to/your_file.pmx", scene);
assetContainer.addAllToScene();
const mmdMesh = assetContainer.meshes[0] as MmdMesh;

이 코드는 Babylon.js의 SceneLoader를 사용하여 PMX 파일을 로드합니다.

이전에 import "babylon-mmd/esm/Loader/pmxLoader";로 PMX 로더를 등록했기 때문에, LoadAssetContainerAsync 함수가 PMX 파일을 올바르게 로드할 수 있습니다.

그런 다음 assetContainer의 로드된 메시들 중 첫 번째 메시를 사용하기 위해 MmdMesh 타입으로 캐스팅합니다.

MMD 모델 로더는 항상 MMD 모델 루트 메시를 meshes[0]에 배치하므로, 이 캐스팅은 항상 유효합니다.

MMD 모델을 런타임에 추가하기

const mmdModel = mmdRuntime.createMmdModel(mmdMesh);
const modelMotion = await vmdLoader.loadAsync("model_motion", "your_model_motion_path.vmd");
const modelRuntimeAnimationHandle = mmdModel.createRuntimeAnimation(modelMotion);
mmdModel.setRuntimeAnimation(modelRuntimeAnimationHandle);

MmdRuntime.createMmdModel 함수를 사용하여 MMD 메시에서 런타임에 의해 제어되는 MMD 모델을 생성할 수 있습니다. MmdModel이 생성되면 MMD 루트 메시 아래의 모든 메시와 머티리얼은 MMD 런타임에 의해 제어됩니다.

MmdModel에 애니메이션을 바인딩하는 방법은 카메라 애니메이션과 동일합니다.

결론

이 섹션에서는 babylon-mmd의 기본 사용법을 살펴보았습니다.

새롭게 소개된 개념은 다음과 같습니다:

  • MmdRuntime: MMD 애니메이션을 처리하는 런타임입니다. MMD 모델과 애니메이션을 관리하고, 피직스 엔진과 오디오 플레이어를 통합합니다.
  • MmdWasmInstance: MMD 애니메이션을 처리하기 위한 웹어셈블리 인스턴스입니다. WASM 바이너리를 사용하여 성능을 향상시킵니다. 이 사용은 선택 사항입니다.
  • MmdAnimation: MMD 애니메이션 데이터를 관리하는 컨테이너입니다. 런타임 애니메이션을 생성하고 바인딩할 수 있습니다.
  • MmdMesh: MMD 모델의 메시를 나타내는 객체입니다. PMX, PMD, BPMX와 같은 다양한 MMD 모델 포맷을 지원합니다.
  • MmdModel: MMD 모델을 런타임에 추가하고 애니메이션을 바인딩하는 객체입니다. MMD 모델의 루트 메시 아래의 모든 메시와 머티리얼을 제어합니다.
  • MmdPlayerControl: MMD 애니메이션을 제어하기 위한 UI 컨트롤입니다. 재생, 일시 정지, 오디오 조정 등을 할 수 있습니다.