<!DOCTYPE html>
<!-- ColorfulxMelody YYB 式 初音ミク MUSIC CAFE v1.00 & YYB 2K rin -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Babylon Template with VR Joystick Movement & UI</title>
<style>
html,
body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
</style>
</head>
<body>
<canvas id="renderCanvas" touch-action="none"></canvas>
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="https://cdn.babylonjs.com/gui/babylon.gui.min.js"></script>
<script src="https://cdn.babylonjs.com/havok/HavokPhysics_umd.js"></script>
<script src="https://www.unpkg.com/babylon-mmd/umd/babylon.mmd.min.js"></script>
<script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
<script>
const canvas = document.getElementById("renderCanvas"); // Get the canvas element
const engine = new BABYLON.Engine(canvas, true); // Generate the BABYLON 3D engine
const assetsPath = 'assets/'
const pmxPath = assetsPath + 'mmd/musiccafemiku/'
const pmxModel = pmxPath + 'mcmiku.pmx'
const vmdPath = assetsPath + 'ColorfulxMelody/'
const vmdModel = vmdPath + 'mmd_ColorfulxMelody_MIK.vmd'
const wavPath = assetsPath + 'ColorfulxMelody/'
const wavModel = wavPath + 'pv_709.wav'
const camPath = assetsPath + 'ColorfulxMelody/';
const camModel = camPath + 'came2.vmd'
// ▼▼▼ ここから追加 ▼▼▼
// --- 2体目のモデル ---
const pmxPath_2 = assetsPath + 'mmd/2krin/';
const pmxModel_2 = pmxPath_2 + 'YYB2Krin.pmx';
// --- 2体目のモーション ---
const vmdPath_2 = assetsPath + 'ColorfulxMelody/'
const vmdModel_2 = vmdPath_2 + 'mmd_ColorfulxMelody_RIN.vmd'; // 2体目のモーションファイル
// ▲▲▲ ここまで追加 ▲▲▲
// ▼▼▼ ここから追加 ▼▼▼
// --- ステージのモデル ---
const stagePath = assetsPath + 'stage/sea_bench/'; // ステージのフォルダへのパス
const stageModel = stagePath + 'bench.pmx'; // ステージのPMXファイル名
// ▲▲▲ ここまで追加 ▲▲▲
const offsetY = -100
// Add your code here matching the playground format
const createScene = async function (engine) {
const scene = new BABYLON.Scene(engine);
let adt;
let coordinateTextBlock;
let mmdModel;
let latestBonePosition = new BABYLON.Vector3(0, 0, 0); // ボーン座標を格納するグローバル変数
let lastPlayClickTime = 0; // 再生ボタンが最後にクリックされた時刻を記録する変数
const camera = new BABYLONMMD.MmdCamera("mmdCamera", new BABYLON.Vector3(0, 10, 0), scene);
// ▼▼▼ ここからスカイボックスのコードを追加 ▼▼▼
// 1. Skybox用のテクスチャ(空の画像)をURLから読み込む
const skyboxTexture = new BABYLON.CubeTexture("https://assets.babylonjs.com/environments/skybox.dds", scene);
// 2. 読み込んだテクスチャを使って、シーンにスカイボックスを作成する
// createDefaultSkybox(texture, pbr, scale)
const skybox = scene.createDefaultSkybox(skyboxTexture, true, 1000);
// ▲▲▲ ここまで追加 ▲▲▲
// ▼▼▼ このブロックを追加(古いSkyboxのコードは削除) ▼▼▼
// PhotoDomeを生成し、あなたのパノラマ画像を指定します
// const dome = new BABYLON.PhotoDome(
// "skyDome", // オブジェクトの名前(任意)
// "./pic/h154.png", // ← あなたのPNGファイルへのパスに書き換えてください
// {
// resolution: 32, // ドームの滑らかさ(32が一般的)
// size: 1000 // ドームの大きさ(シーン全体が収まるように)
// },
// scene
// );
// ▲▲▲ ここまで ▲▲▲
const ground = BABYLON.MeshBuilder.CreateGround("Ground", { width: 200, height: 200, subdivisions: 2, updatable: false }, scene);
ground.receiveShadows = true;
const hemisphericLight = new BABYLON.HemisphericLight("HemisphericLight", new BABYLON.Vector3(0, 10, -10), scene);
hemisphericLight.intensity = 0.3;
hemisphericLight.specular = new BABYLON.Color3(0, 0, 0);
hemisphericLight.groundColor = new BABYLON.Color3(1.1, 1.1, 1.1);
const shadowLight = new BABYLON.DirectionalLight("shadowLight", new BABYLON.Vector3(-1, -2, 1), scene);
shadowLight.position = new BABYLON.Vector3(20, 100, 100);
const shadowGenerator = new BABYLON.ShadowGenerator(1024, shadowLight, true);
shadowGenerator.useBlurExponentialShadowMap = true;
shadowGenerator.blurKernel = 32;
// ▼▼▼ ここから追加 ▼▼▼
// --- ステージのメッシュを読み込む ---
const stageMesh = await BABYLON.SceneLoader.ImportMeshAsync(undefined, stageModel, undefined, scene).then((result) => result.meshes[0]);
// ▼▼▼ 必要に応じて位置や大きさを調整 ▼▼▼
// 例:ステージ全体を少し下に移動する
// stageMesh.position.y = -1;
// 例:ステージ全体を1.2倍に拡大する
// stageMesh.scaling = new BABYLON.Vector3(1.2, 1.2, 1.2);
stageMesh.rotation.y = Math.PI / 2;
stageMesh.position.x = 40;
// ★ポイント:ステージにはアニメーションを付けないので、mmdRuntimeへの登録は不要です。
// ▲▲▲ ここまで追加 ▲▲▲
// ▼▼▼ 地面と影の調整を追加 ▼▼▼
// 1. ステージの全てのメッシュが影を受け取るように設定
stageMesh.getChildMeshes().forEach(mesh => mesh.receiveShadows = true);
// 2. 元からあった灰色の地面を削除(または非表示)
ground.dispose(); // 完全に削除する場合
// ground.visibility = false; // 非表示にするだけの場合
// ▲▲▲ ここまで追加 ▲▲▲
const mmdMesh = await BABYLON.SceneLoader.ImportMeshAsync(undefined, pmxModel, undefined, scene).then((result) => result.meshes[0]);
shadowGenerator.addShadowCaster(mmdMesh);
mmdMesh.receiveShadows = true;
// ▼▼▼ ここから追加 ▼▼▼
// --- 2体目のメッシュを読み込む ---
const mmdMesh_2 = await BABYLON.SceneLoader.ImportMeshAsync(undefined, pmxModel_2, undefined, scene).then((result) => result.meshes[0]);
// 1体目と重ならないように、X軸方向に2ずらす
mmdMesh_2.position.x = 2;
// 2体目も影を落とすように設定
shadowGenerator.addShadowCaster(mmdMesh_2);
mmdMesh_2.receiveShadows = true;
// ▲▲▲ ここまで追加 ▲▲▲
const vmdLoader = new BABYLONMMD.VmdLoader(scene);
const modelMotion = await vmdLoader.loadAsync("model_motion", vmdModel);
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.8 * 8, 0), havokPlugin);
const mmdRuntime = new BABYLONMMD.MmdRuntime(scene, new BABYLONMMD.MmdPhysics(scene));
mmdRuntime.register(scene);
mmdModel = mmdRuntime.createMmdModel(mmdMesh);
// ▼▼▼ このコードを追加してボーン名を調べる ▼▼▼
if (mmdMesh.skeleton) {
console.log("--- 利用可能なボーン名リスト ---");
mmdMesh.skeleton.bones.forEach(bone => {
console.log(bone.name);
});
console.log("---------------------------------");
}
// ▲▲▲ ここまで追加 ▲▲▲
mmdModel.addAnimation(modelMotion);
mmdModel.setAnimation("model_motion");
// ▼▼▼ ここから追加 ▼▼▼
// --- 2体目のモーションを読み込んで適用 ---
const modelMotion_2 = await vmdLoader.loadAsync("model_motion_2", vmdModel_2); // 1つ目と違う名前を付ける
const mmdModel_2 = mmdRuntime.createMmdModel(mmdMesh_2); // 2体目のMMDモデルを作成
mmdModel_2.addAnimation(modelMotion_2);
mmdModel_2.setAnimation("model_motion_2");
// ▲▲▲ ここまで追加 ▲▲▲
mmdRuntime.setCamera(camera);
const cameraMotion = await vmdLoader.loadAsync("camera_motion", camModel);
camera.addAnimation(cameraMotion);
camera.setAnimation("camera_motion");
const audioPlayer = new BABYLONMMD.StreamAudioPlayer(scene);
audioPlayer.preservesPitch = false;
audioPlayer.source = wavModel;
mmdRuntime.setAudioPlayer(audioPlayer);
mmdRuntime.playAnimation();
const mmdPlayerControl = new BABYLONMMD.MmdPlayerControl(scene, mmdRuntime, audioPlayer);
mmdPlayerControl.showPlayerControl();
// ▼▼▼ こちらが正しいコードです ▼▼▼
// sceneオブジェクトの「物理演算完了後」イベントに処理を追加する
scene.onAfterPhysicsObservable.add(() => {
try {
if (mmdModel && mmdModel.skeleton) {
const targetBone = mmdModel.skeleton.bones.find(b => b.name === '上半身');
if (targetBone) {
// グローバル変数に最新の座標をコピー
const finalMatrix = targetBone.getFinalMatrix(); // ボーンの最終的なワールド行列を取得
finalMatrix.decompose(undefined, undefined, latestBonePosition); // 行列から位置情報だけを抽出する
}
}
} catch (e) {
// エラーが発生してもコンソールに表示するだけで、プログラムは止めない
console.error("onAfterPhysicsObservableでエラー:", e);
}
});
// =================================================================
// ▼▼▼ VR機能、UIパネル、ジョイスティック移動処理 ▼▼▼
// =================================================================
const xr = await scene.createDefaultXRExperienceAsync({
uiOptions: {
sessionMode: 'immersive-vr', // 最初に表示される「Enter VR」ボタンはVRモード用
supportedSessionModes: ['immersive-vr', 'immersive-ar'] // VRとARの両方をサポートする
}
});
// ▼▼▼ 追従モードの状態を管理する変数 ▼▼▼
let isFollowing = false;
let followOffset = new BABYLON.Vector3();
// --- 左手コントローラーにUIパネルを追加 ---
xr.input.onControllerAddedObservable.add((controller) => {
if (controller.inputSource.handedness === 'left') {
let uiParent = scene.getTransformNodeByName("leftUIParent");
if (!uiParent) {
// 1. UIの親となるTransformNodeを作成
uiParent = new BABYLON.TransformNode("leftUIParent", scene);
uiParent.position = new BABYLON.Vector3(0, 0.05, 0.08);
uiParent.rotation = new BABYLON.Vector3(Math.PI / 4, 0, 0);
// 2. UIを表示するための平面メッシュを作成(高さを広げる)
const uiPlane = BABYLON.MeshBuilder.CreatePlane("uiPlane", { width: 0.25, height: 0.6 }, scene);
uiPlane.parent = uiParent;
uiPlane.visibility = 0.9;
// 3. 平面メッシュにGUIテクスチャを適用
adt = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(uiPlane);
// 4. ボタンを縦に並べるためのStackPanelを作成
const stackPanel = new BABYLON.GUI.StackPanel();
stackPanel.isVertical = true;
stackPanel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGN_MENT_TOP; // 上揃えに変更
adt.addControl(stackPanel);
const buttonHeight = "60px";
// 5. 「最初から」ボタン
const resetButton = BABYLON.GUI.Button.CreateSimpleButton("resetButton", "最初から");
resetButton.width = "100%";
resetButton.height = buttonHeight;
resetButton.color = "white";
resetButton.background = "#4CAF50"; // 緑色
resetButton.fontSize = 24;
resetButton.onPointerClickObservable.add(() => {
// 常にアニメーションを0秒地点に戻す
mmdRuntime.seekAnimation(0);
});
stackPanel.addControl(resetButton);
// 6. 「再生/停止」トグルボタン
const playPauseButton = BABYLON.GUI.Button.CreateSimpleButton("playPauseButton", "停止"); // 最初は再生されているので「停止」
playPauseButton.width = "100%";
playPauseButton.height = buttonHeight;
playPauseButton.color = "white";
playPauseButton.background = "#f44336"; // 赤色
playPauseButton.fontSize = 24;
playPauseButton.onPointerClickObservable.add(() => {
// もしアニメーションが再生中なら
if (mmdRuntime.isAnimationPlaying) {
// 動きを一時停止する
mmdRuntime.pauseAnimation();
// ボタンの文字を「再生」に変え、色を青系にする
playPauseButton.textBlock.text = "再生";
playPauseButton.background = "#2196F3";
} else { // もしアニメーションが停止中なら
// 動きを再生する
mmdRuntime.playAnimation();
// ボタンの文字を「停止」に変え、色を赤系に戻す
playPauseButton.textBlock.text = "停止";
playPauseButton.background = "#f44336";
}
});
stackPanel.addControl(playPauseButton);
//boneListTextBlock.text = `現在の時刻: ${currentTime} 次回の判定のために記憶: ${lastPlayClickTime} 経過時間: ${timeSinceLastClick}`;
// 7. 「正面」ボタン
const frontButton = BABYLON.GUI.Button.CreateSimpleButton("frontButton", "正面");
frontButton.width = "100%"; frontButton.height = buttonHeight; frontButton.color = "white";
frontButton.background = "#2196F3"; frontButton.fontSize = 24;
frontButton.onPointerClickObservable.add(() => {
isFollowing = false; // 追従モードを解除
xr.baseExperience.camera.position.set(0, 16, -20);
xr.baseExperience.camera.setTarget(new BABYLON.Vector3(0, 10, 0));
});
stackPanel.addControl(frontButton);
// 8. 「背面」ボタン
const backButton = BABYLON.GUI.Button.CreateSimpleButton("backButton", "背面");
backButton.width = "100%"; backButton.height = buttonHeight; backButton.color = "white";
backButton.background = "#FF9800"; backButton.fontSize = 24;
backButton.onPointerClickObservable.add(() => {
isFollowing = false; // 追従モードを解除
xr.baseExperience.camera.position.set(0, 16, 20);
xr.baseExperience.camera.setTarget(new BABYLON.Vector3(0, 10, 0));
});
stackPanel.addControl(backButton);
// 7. 「ARモード」ボタン
const arButton = BABYLON.GUI.Button.CreateSimpleButton("arButton", "ARモード");
arButton.width = "100%";
arButton.height = buttonHeight;
arButton.color = "white";
arButton.background = "#9C27B0"; // 紫色
arButton.fontSize = 24;
arButton.onPointerClickObservable.add(async () => { // ← (1) 関数の前に「async」を追加
// 現在もしVR/ARモードに入っていたら、一度終了する
if (xr.baseExperience.state === BABYLON.WebXRState.IN_XR) {
await xr.baseExperience.exitXRAsync(); // ← (2) 「await」で終了が完了するのを待つ
}
// ARモードで入り直す
await xr.baseExperience.enterXRAsync("immersive-ar", "local-floor");
});
stackPanel.addControl(arButton);
// ▼▼▼ 9. 「追従/解除」ボタンを作成 ▼▼▼
const followButton = BABYLON.GUI.Button.CreateSimpleButton("followButton", "追従");
followButton.width = "100%";
followButton.height = buttonHeight;
followButton.color = "white";
followButton.background = "#607D8B"; // グレー系の色
followButton.fontSize = 24;
followButton.onPointerClickObservable.add(() => {
isFollowing = !isFollowing; // 追従モードをトグル
if (isFollowing) {
// 追従開始:現在のカメラとモデルの「真の中心」との相対位置を計算して保存
const xrCamera = xr.baseExperience.camera;
followOffset = xrCamera.position.subtract(latestBonePosition);
followButton.textBlock.text = "解除";
followButton.background = "#9C27B0"; // 紫色に変更
} else {
// 追従解除
followButton.textBlock.text = "追従";
followButton.background = "#607D8B"; // 元の色に戻す
}
});
stackPanel.addControl(followButton);
// ▼▼▼ 追加するコード ▼▼▼
// 座標表示用のテキストブロック
coordinateTextBlock = new BABYLON.GUI.TextBlock("coordinateTextBlock");
coordinateTextBlock.text = "X: 0.00 Y: 0.00 Z: 0.00";
coordinateTextBlock.color = "white";
coordinateTextBlock.fontSize = 20;
coordinateTextBlock.height = "50px";
stackPanel.addControl(coordinateTextBlock);
// ▼▼▼ ここからが今回の追加機能 ▼▼▼
// 5. ボーン名を表示するためのボタン
const showBonesButton = BABYLON.GUI.Button.CreateSimpleButton("showBonesButton", "ボーン名表示");
showBonesButton.width = "100%";
showBonesButton.height = buttonHeight;
showBonesButton.color = "white";
showBonesButton.background = "#3F51B5"; // 藍色
showBonesButton.fontSize = 24;
stackPanel.addControl(showBonesButton);
// 6. ボーン名リストを表示するためのテキストブロック
const boneListTextBlock = new BABYLON.GUI.TextBlock("boneListTextBlock", "↑ボタンを押してボーン名を表示");
boneListTextBlock.color = "white";
boneListTextBlock.fontSize = 16;
boneListTextBlock.textWrapping = true; // 折り返しを有効にする
boneListTextBlock.resizeToFit = true; // テキスト量に合わせて高さを自動調整
boneListTextBlock.paddingTop = "10px";
stackPanel.addControl(boneListTextBlock);
// ボタンが押された時の処理
showBonesButton.onPointerClickObservable.add(() => {
let debugMessage = ""; // 表示するメッセージを格納する変数
if (mmdModel && mmdModel.skeleton) {
debugMessage += "スケルトンを発見。\n"; // \nは改行
const bones = mmdModel.skeleton.bones;
const boneCount = bones.length;
debugMessage += `ボーンの数: ${boneCount}個\n`;
if (boneCount > 0) {
// bone.nameの配列を作成し、カンマ区切りの文字列に変換
const boneNames = bones.map(bone => bone.name).join(', ');
debugMessage += "名前リスト: " + boneNames;
} else {
debugMessage += "ボーン配列は空です。";
}
} else if (mmdModel) {
debugMessage = "モデルはありますが、スケルトンが見つかりません。";
} else {
debugMessage = "MMDモデル自体が見つかりません。";
}
// 最終的なデバッグメッセージをテキストブロックに表示
boneListTextBlock.text = debugMessage;
});
// ▲▲▲ ここまで追加 ▲▲▲
}
uiParent.parent = controller.grip;
}
});
xr.baseExperience.onStateChangedObservable.add((state) => {
if (state === BABYLON.WebXRState.IN_XR) {
// XRモードに入った時の処理
camera.setAnimation(null);
// もしARモードで入ったなら、地面を非表示にする
if (xr.baseExperience.sessionManager.sessionMode === 'immersive-ar') {
ground.visibility = false;
}
} else if (state === BABYLON.WebXRState.NOT_IN_XR) {
// XRモードを終了した時の処理
camera.setAnimation("camera_motion");
isFollowing = false;
// 地面を再表示する(元の状態に戻す)
ground.visibility = true;
}
});
// 毎フレームごとの処理
scene.onBeforeRenderObservable.add(() => {
if (xr.baseExperience.state !== BABYLON.WebXRState.IN_XR) return;
// ▼▼▼ ここから修正 ▼▼▼
// --- VRカメラ自身の座標をUIに表示する ---
// if (coordinateTextBlock) {const cameraPosition = xr.baseExperience.camera.position;
// coordinateTextBlock.text = `CAMERA X: ${cameraPosition.x.toFixed(2)} Y: ${cameraPosition.y.toFixed(2)} Z: ${cameraPosition.z.toFixed(2)}`;}
// ▲▲▲ ここまで修正 ▲▲▲
// ▼▼▼ ここからが最終修正 ▼▼▼
// mmdMeshと、最も重要なmmdMesh.skeletonが準備完了しているか、最初に確認する
if (mmdModel && mmdModel.skeleton) {
// グローバル変数に格納された最新のボーン座標をUIに表示する
if (coordinateTextBlock) {
coordinateTextBlock.text = `上半身 X: ${latestBonePosition.x.toFixed(2)} Y: ${latestBonePosition.y.toFixed(2)} Z: ${latestBonePosition.z.toFixed(2)}`;
}
// --- 追従モードの処理 ---
// --- 追従モードの処理 ---
if (isFollowing) {
// グローバル変数(上半身の現在位置)をコピーし、それにオフセットを足してカメラの目標位置を決めます
// ※ .clone() は、元の latestBonePosition の値を誤って変更しないための「おまじない」です
const desiredPosition = latestBonePosition.clone().add(followOffset);
// カメラの位置を、計算した目標位置へ更新します
xr.baseExperience.camera.position.copyFrom(desiredPosition);
// カメラの視線を、モデルの現在位置(上半身)に向けます
xr.baseExperience.camera.setTarget(latestBonePosition);
}
}
// ▲▲▲ ここまでが最終修正 ▲▲▲
// --- ジョイスティック操作の処理 ---
// isFollowingがfalseの時だけジョイスティック操作を許可
if (!isFollowing) {
const deltaMillis = engine.getDeltaTime();
const xrCamera = xr.baseExperience.camera;
// 左コントローラー: 平行移動
const leftController = xr.input.controllers.find(c => c.inputSource.handedness === 'left');
if (leftController?.motionController) {
const thumbstick = leftController.motionController.getComponent("xr-standard-thumbstick");
if (thumbstick?.axes) {
const moveSpeed = 6.0 * deltaMillis / 1000;
const forward = xrCamera.getDirection(BABYLON.Vector3.Forward());
forward.y = 0;
xrCamera.position.addInPlace(forward.normalize().scale(-thumbstick.axes.y * moveSpeed));
const right = xrCamera.getDirection(BABYLON.Vector3.Right());
xrCamera.position.addInPlace(right.scale(thumbstick.axes.x * moveSpeed));
}
}
// 右コントローラー: 回転と上下移動
const rightController = xr.input.controllers.find(c => c.inputSource.handedness === 'right');
if (rightController?.motionController) {
const thumbstick = rightController.motionController.getComponent("xr-standard-thumbstick");
if (thumbstick?.axes) {
const rotationThreshold = 0.2;
if (Math.abs(thumbstick.axes.x) > rotationThreshold) {
const rotSpeed = 0.4 * deltaMillis / 1000;
if (xrCamera.parent) {
xrCamera.parent.rotate(BABYLON.Vector3.Up(), thumbstick.axes.x * rotSpeed, BABYLON.Space.WORLD);
}
}
if (Math.abs(thumbstick.axes.y) > rotationThreshold) {
const verticalSpeed = 2.0 * deltaMillis / 1000;
xrCamera.position.y += -thumbstick.axes.y * verticalSpeed;
}
}
}
}
});
return scene;
};
(async function () {
canvas.focus();
const scene = await createScene(engine); //Call the createScene function
// Register a render loop to repeatedly render the scene
engine.runRenderLoop(async function () {
scene.render();
});
// Watch for browser/canvas resize events
window.addEventListener("resize", function () {
engine.resize();
});
})()
</script>
</body>
</html>
使用変数
-------( Function ) | |
action | |
adt | |
arButton | |
assetsPath | |
audioPlayer | |
b | |
backButton | |
background | |
blurKernel | |
bone | |
boneCount | |
boneListTextBlock | |
boneNames | |
bones | |
br> // Register a render loop to repeatedly render the scene engine.runRenderLoop -------( Function ) | |
buttonHeight | |
c | |
camera | |
cameraMotion | |
cameraPosition | |
camModel | |
camPath | |
canvas | |
charset | |
color | |
content | |
createScene | |
debugMessage | |
deltaMillis | |
desiredPosition | |
dome | |
engine | |
equiv | |
finalMatrix | |
followButton | |
followOffset | |
fontSize | |
forward | |
frontButton | |
ground | |
groundColor | |
handedness | |
havokInstance | |
havokPlugin | |
height | |
hemisphericLight | |
id | |
intensity | |
isFollowing | |
isVertical | |
lastPlayClickTime | |
latestBonePosition | |
leftController | |
mesh | |
mmdMesh | |
mmdMesh_2 | |
mmdModel | |
mmdModel_2 | |
mmdPlayerControl | |
mmdRuntime | |
modelMotion | |
modelMotion_2 | |
moveSpeed | |
name | |
offsetY | |
oordinateTextBlock | |
paddingTop | |
parent | |
playPauseButton | |
pmxModel | |
pmxModel_2 | |
pmxPath | |
pmxPath_2 | |
ponentialShadowMap | |
position | |
preservesPitch | |
receiveShadows | |
resetButton | |
resizeToFit | |
right | |
rightController | |
rotation | |
rotationThreshold | |
rotSpeed | |
scaling | |
scene | |
sessionMode | |
shadowGenerator | |
shadowLight | |
showBonesButton | |
skybox | |
skyboxTexture | |
source | |
specular | |
src | |
stackPanel | |
stageMesh | |
stageModel | |
stagePath | |
state | |
targetBone | |
text | |
textWrapping | |
thumbstick | |
uiParent | |
uiPlane | |
verticalAlignment | |
verticalSpeed | |
visibility | |
vmdLoader | |
vmdModel | |
vmdModel_2 | |
vmdPath | |
vmdPath_2 | |
wavModel | |
wavPath | |
width | |
x | |
xmlns | |
xr | |
xrCamera | |
y |