junkerstock
 vrb-test2 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Babylon.js - Interactive Spheres Scene</title>
<script src="https://preview.babylonjs.com/babylon.js"></script>
<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"></canvas>

<script>
window.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });

// --- シーン作成関数 ---
const createScene = function() {
const scene = new BABYLON.Scene(engine);
// 背景色をA-Frame版と合わせる
scene.clearColor = new BABYLON.Color3.FromHexString("#ECECEC");

// 1. カメラと操作の設定 (A-Frameのrigとcamera, look-controls, custom-controlsを再現)
// UniversalCameraはWASD/矢印キーでの移動とマウスでの視点操作を標準サポート
const camera = new BABYLON.UniversalCamera("camera", new BABYLON.Vector3(0, 1.6, 5), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);

// 移動速度とキー設定
camera.speed = 0.15; // A-Frameの speed:3 に近い体感速度に調整
camera.keysUp = [87, 38]; // W, ArrowUp
camera.keysDown = [83, 40]; // S, ArrowDown
camera.keysLeft = [65, 37]; // A, ArrowLeft
camera.keysRight = [68, 39]; // D, ArrowRight
camera.angularSensibility = 2000; // マウス感度

// 2. ライトの設定
const ambientLight = new BABYLON.HemisphericLight("ambientLight", new BABYLON.Vector3(0, 1, 0), scene);
ambientLight.intensity = 0.8;
ambientLight.specular = BABYLON.Color3.Black();

const directionalLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-1, 1.5, 1), scene);
directionalLight.intensity = 0.8;

// 3. 情報パネルの作成 (A-FrameのinfoPanelをDynamicTextureで再現)
const panelWidth = 2.5;
const panelHeight = 1.2;
const infoPanel = BABYLON.MeshBuilder.CreatePlane("infoPanel", { width: panelWidth, height: panelHeight }, scene);
infoPanel.material = new BABYLON.StandardMaterial("panelMat", scene);
infoPanel.material.backFaceCulling = false; // side: doubleの再現
infoPanel.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL; // look-atの再現
infoPanel.isVisible = false; // 最初は非表示

const textureResolution = 512;
const panelTexture = new BABYLON.DynamicTexture("panelTexture", {
width: textureResolution,
height: Math.round(textureResolution * (panelHeight / panelWidth))
}, scene, true);
infoPanel.material.diffuseTexture = panelTexture;
infoPanel.material.emissiveColor = new BABYLON.Color3(0.2, 0.2, 0.2); // 暗い場所でも少し見えるように

// パネルのテキストを更新する関数 (troika-textの更新を再現)
function updatePanelText(text) {
const ctx = panelTexture.getContext();
const size = panelTexture.getSize();
const fontSize = 28;
const font = `bold ${fontSize}px sans-serif`;
const lines = text.split('\n');

// 背景を塗りつぶし
ctx.fillStyle = "#333333";
ctx.fillRect(0, 0, size.width, size.height);

// テキストを一行ずつ描画
ctx.font = font;
ctx.fillStyle = "white";
lines.forEach((line, index) => {
const y = 50 + index * (fontSize + 15); // 行間の調整
ctx.fillText(line, 25, y);
});

panelTexture.update(); // テクスチャを更新
}

// 4. 球体の動的生成
const numSpheres = 30;
const spread = 20;
for (let i = 0; i < numSpheres; i++) {
const radius = Math.random() * 0.4 + 0.2;
const sphere = BABYLON.MeshBuilder.CreateSphere(`sphere${i}`, { diameter: radius * 2 }, scene);

const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + radius;
const z = (Math.random() - 0.5) * spread;
sphere.position = new BABYLON.Vector3(x, y, z);

const color = `hsl(${Math.random() * 360}, 70%, 60%)`;
sphere.material = new BABYLON.StandardMaterial(`mat${i}`, scene);
sphere.material.diffuseColor = BABYLON.Color3.FromHexString(color.replace('hsl', 'hsla').replace(/%/g, '').replace(')', ', 1)'));

// A-Frameのdata-info属性の代わりにmetadataを使用
sphere.metadata = {
info: `球 ${i + 1}\n色: ${color}\n半径: ${radius.toFixed(2)}`
};
}

// 5. クリックイベントとパネル表示処理
scene.onPointerDown = function (evt, pickResult) {
// 球体がクリックされた場合
if (pickResult.hit && pickResult.pickedMesh && pickResult.pickedMesh.metadata) {
const clickedSphere = pickResult.pickedMesh;

// パネルの位置を計算
const targetWorldPosition = clickedSphere.getWorldMatrix().getTranslation();
const cameraWorldPosition = camera.globalPosition;
const sphereRadius = clickedSphere.getBoundingInfo().boundingSphere.radius;

const offsetDistance = sphereRadius + 1.2; // A-Frame版より少し離す
const direction = cameraWorldPosition.subtract(targetWorldPosition).normalize();

const panelPosition = targetWorldPosition.add(direction.scale(offsetDistance));
infoPanel.position.copyFrom(panelPosition);

// パネルのテキストを更新して表示
updatePanelText(clickedSphere.metadata.info);
infoPanel.isVisible = true;
} else {
// 何もない場所がクリックされたらパネルを非表示にする
infoPanel.isVisible = false;
}
};

return scene;
};

// --- 実行 ---
const scene = createScene();
engine.runRenderLoop(function() {
scene.render();
});
window.addEventListener('resize', function() {
engine.resize();
});
});
</script>
</body>
</html>


使用変数

) { engine.resize -------( Function )
) { scene.render -------( Function )
) { const canvas = document.getElementById -------( Function )
ambientLight
ameraWorldPosition
angularSensibility
argetWorldPosition
backFaceCulling
billboardMode
camera
canvas
charset
clearColor
clickedSphere
color
createScene
ctx
diffuseColor
diffuseTexture
direction
directionalLight
emissiveColor
engine
fillStyle
font
fontSize
i
id
infoPanel
intensity
isVisible
keysDown
keysLeft
keysRight
keysUp
lines
material
metadata
numSpheres
offsetDistance
onPointerDown
panelHeight
panelPosition
panelTexture
panelWidth
position
radius
scene
size
specular
speed
sphere
sphereRadius
spread
src
textureResolution
updatePanelText -------( Function )
x
y
z