🔬 AI発明ギャラリー syunsuke kinngugidora
🔒 サンドボックス内で実行中 ⛶ 全画面で遊ぶ
HTML / CSS / JS
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>キングギドラ風 3Dモデル(バージョン切り替え)</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #000;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        #canvas-container {
            width: 100vw;
            height: 100vh;
            display: block;
        }
        #ui-layer {
            position: absolute;
            top: 20px;
            left: 20px;
            color: #FFD700;
            pointer-events: auto;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
            background: rgba(0, 0, 0, 0.5);
            padding: 15px;
            border-radius: 8px;
            border: 1px solid #FFD700;
        }
        h1 {
            margin: 0 0 10px 0;
            font-size: 20px;
            letter-spacing: 2px;
        }
        p {
            margin: 0 0 10px 0;
            font-size: 14px;
            color: #ddd;
        }
        select {
            background-color: #222;
            color: #FFD700;
            border: 1px solid #FFD700;
            padding: 8px;
            font-size: 16px;
            border-radius: 4px;
            outline: none;
            cursor: pointer;
            width: 100%;
        }
        select:hover {
            background-color: #333;
        }
        #loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #FFD700;
            font-size: 20px;
            font-weight: bold;
            transition: opacity 0.5s;
            pointer-events: none;
        }
    </style>
    <!-- Three.js本体 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <!-- OrbitControls(カメラ操作用) -->
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
    <div id="ui-layer">
        <h1 id="title-text">黄金の三つ首竜</h1>
        <p>ドラッグ:視点移動 / スクロール:ズーム</p>
        <select id="version-select" onchange="changeVersion()">
            <option value="monsterverse" selected>ハリウッド版 (MonsterVerse)</option>
            <option value="heisei">平成版 (Heisei / VS) + 昼の街とゴジラ</option>
            <option value="showa">初代版 (Showa) + 怪獣島</option>
        </select>
    </div>
    <div id="loading">モデルを生成中...</div>
    <div id="canvas-container"></div>

    <script>
        let scene, camera, renderer, controls;
        let ghidorahGroup, leftWingPivot, rightWingPivot, bodyGroup, environmentGroup;
        let neckGroups = []; 
        let tailGroups = []; 
        let clock = new THREE.Clock();
        
        // ライトのグローバル変数
        let ambientLight, dirLight, hemiLight, pointLight;

        // バージョン定義 (環境・背景色を追加)
        const VERSIONS = {
            monsterverse: {
                title: "ハリウッド版 (Flying Pose)",
                color: 0xDDAA00,     
                metalness: 0.8,
                roughness: 0.35,
                bodyScale: [1.0, 1.8, 1.2],
                neckScale: 1.4,      
                neckCount: 40,
                tailScale: 1.3,      
                tailCount: 45,
                hasMace: true,       
                wingType: 'wyvern',  
                wingScale: 1.2,
                pose: 'flying',
                bgColor: 0x0a224a,    // 深い夜空
                fogDensity: 0.003,
                envType: 'none',      // 背景なし
                lightIntensity: 2.5,
                ambientColor: 0x556688
            },
            heisei: {
                title: "平成版 (Standing in Day City)",
                color: 0xE5D5A5,     // シャンパンゴールド系
                metalness: 0.85,
                roughness: 0.25,
                bodyScale: [1.6, 1.5, 1.5], // よりマッシブに
                neckScale: 1.1,      
                neckCount: 35,
                tailScale: 1.1,
                tailCount: 40,
                hasMace: false,      
                wingType: 'fan',     
                wingScale: 1.4,
                pose: 'standing',    // 直立ポーズ
                bgColor: 0x66AADD,   // 昼の青空
                fogDensity: 0.002,   // 視界を少しクリアに
                envType: 'day_city', // 昼の街ステージON
                lightIntensity: 3.5, // 太陽光を強く
                ambientColor: 0xAACCDD // 明るい環境光
            },
            showa: {
                title: "初代版 (Standing in Monster Island)",
                color: 0xCC9933,     // 真鍮のような落ち着いた金(ソフビ風)
                metalness: 0.6,
                roughness: 0.6,
                bodyScale: [1.5, 1.4, 1.4], // ずんぐり
                neckScale: 1.0,      // 首の長さを延長
                neckCount: 32,       
                tailScale: 0.9,      // 尻尾の長さを延長
                tailCount: 35,       
                hasMace: false,
                wingType: 'fan_showa',// 初代特有の丸みのある扇型
                wingScale: 1.1,
                pose: 'standing',    // 直立ポーズ
                bgColor: 0x82a4b3,   // 特撮ホリゾント風の少し濁った青空
                fogDensity: 0.002,   // 砂埃・空気感
                envType: 'island',   // 怪獣島ステージON
                lightIntensity: 2.0, // 昼間の光
                ambientColor: 0x88aabb // 環境光も少し明るい昼色に
            }
        };

        let currentVersion = 'monsterverse';

        // 共通マテリアル
        let goldMaterial = new THREE.MeshStandardMaterial({
            side: THREE.DoubleSide
        });

        const eyeMaterial = new THREE.MeshStandardMaterial({
            color: 0xFF0000,
            emissive: 0x880000,
            metalness: 0.1,
            roughness: 0.1
        });

        window.addEventListener('DOMContentLoaded', init);

        function init() {
            const container = document.getElementById('canvas-container');

            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x0a224a); 
            scene.fog = new THREE.FogExp2(0x0a224a, 0.003); 

            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
            camera.position.set(0, -30, 150);

            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.shadowMap.enabled = true;
            renderer.shadowMap.type = THREE.PCFSoftShadowMap;
            container.appendChild(renderer.domElement);

            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.05;
            controls.target.set(0, 0, 0);
            controls.maxDistance = 400;
            controls.minDistance = 50;

            setupLighting();

            // 初期生成
            applyVersionSettings();
            buildGhidorah();

            document.getElementById('loading').style.opacity = 0;
            setTimeout(() => document.getElementById('loading').remove(), 500);

            window.addEventListener('resize', onWindowResize);
            animate();
        }

        // バージョン切り替え処理
        window.changeVersion = function() {
            const select = document.getElementById('version-select');
            currentVersion = select.value;

            if (ghidorahGroup) {
                scene.remove(ghidorahGroup);
            }

            neckGroups = [];
            tailGroups = [];
            leftWingPivot = null;
            rightWingPivot = null;

            applyVersionSettings();
            buildGhidorah();
        };

        function applyVersionSettings() {
            const vData = VERSIONS[currentVersion];
            
            // マテリアルの更新
            goldMaterial.color.setHex(vData.color);
            goldMaterial.metalness = vData.metalness;
            goldMaterial.roughness = vData.roughness;
            
            document.getElementById('title-text').innerText = vData.title;

            // 背景色とフォグの更新
            scene.background.setHex(vData.bgColor);
            scene.fog.color.setHex(vData.bgColor);
            scene.fog.density = vData.fogDensity;

            // ライトの強さと色の更新
            if (dirLight) dirLight.intensity = vData.lightIntensity;
            if (ambientLight) ambientLight.color.setHex(vData.ambientColor);

            // 環境(ステージ)の再構築
            buildEnvironment(vData);
        }

        function setupLighting() {
            ambientLight = new THREE.AmbientLight(0x556688, 1.2);
            scene.add(ambientLight);

            dirLight = new THREE.DirectionalLight(0xffffff, 2.5);
            dirLight.position.set(50, 50, 50);
            dirLight.castShadow = true;
            dirLight.shadow.mapSize.width = 2048;
            dirLight.shadow.mapSize.height = 2048;
            dirLight.shadow.camera.near = 10;
            dirLight.shadow.camera.far = 500;
            // 影の範囲を広く設定
            dirLight.shadow.camera.left = -200;
            dirLight.shadow.camera.right = 200;
            dirLight.shadow.camera.top = 200;
            dirLight.shadow.camera.bottom = -200;
            scene.add(dirLight);

            hemiLight = new THREE.HemisphereLight(0x8899bb, 0x334455, 1.0);
            scene.add(hemiLight);

            pointLight = new THREE.PointLight(0xFFFFFF, 1.5, 300);
            pointLight.position.set(0, 30, 50);
            scene.add(pointLight);
        }

        function buildEnvironment(vData) {
            // 古いステージを削除
            if (environmentGroup) {
                scene.remove(environmentGroup);
            }

            environmentGroup = new THREE.Group();
            scene.add(environmentGroup);

            if (vData.envType === 'island') {
                // 地面 (荒涼とした土)
                const groundGeo = new THREE.PlaneGeometry(1500, 1500, 64, 64);
                const pos = groundGeo.attributes.position;
                for (let i = 0; i < pos.count; i++) {
                    pos.setZ(i, Math.random() * 5); // ゴツゴツさせる
                }
                groundGeo.computeVertexNormals();
                
                const groundMat = new THREE.MeshStandardMaterial({ 
                    color: 0x5c4033, // 特撮の土の色
                    roughness: 1.0,
                    metalness: 0.0
                });
                const ground = new THREE.Mesh(groundGeo, groundMat);
                ground.rotation.x = -Math.PI / 2;
                ground.position.y = -16; // ギドラの足がぴったり着く高さ
                ground.receiveShadow = true;
                environmentGroup.add(ground);

                // 岩山の配置 (怪獣島らしさ)
                for(let i=0; i<35; i++) {
                    const rockSize = Math.random() * 25 + 10;
                    const rockGeo = new THREE.DodecahedronGeometry(rockSize, 1);
                    
                    // 岩をランダムに歪ませる
                    const rPos = rockGeo.attributes.position;
                    for(let j=0; j<rPos.count; j++) {
                        rPos.setXYZ(j, 
                            rPos.getX(j) * (1 + Math.random()*0.3), 
                            rPos.getY(j) * (1 + Math.random()*0.3), 
                            rPos.getZ(j) * (1 + Math.random()*0.3)
                        );
                    }
                    rockGeo.computeVertexNormals();

                    const rockMat = new THREE.MeshStandardMaterial({ 
                        color: 0x665544, 
                        roughness: 0.9 
                    });
                    const rock = new THREE.Mesh(rockGeo, rockMat);
                    
                    // 手前を塞がないように、奥や左右に配置
                    const angle = Math.random() * Math.PI; 
                    const radius = Math.random() * 200 + 80;
                    rock.position.set(
                        Math.cos(angle) * radius * 1.5, 
                        -16 + rockSize * 0.5, 
                        -Math.sin(angle) * radius - 30 // Zは奥(マイナス)へ
                    );
                    rock.rotation.set(Math.random()*Math.PI, Math.random()*Math.PI, 0);
                    rock.scale.set(1, Math.random() * 2 + 1.2, 1);
                    rock.castShadow = true;
                    rock.receiveShadow = true;
                    environmentGroup.add(rock);
                }
            } else if (vData.envType === 'city' || vData.envType === 'day_city') {
                // 街のジオラマ(昼夜共通ベース)
                const isDay = vData.envType === 'day_city';
                
                const groundGeo = new THREE.PlaneGeometry(1500, 1500);
                const groundMat = new THREE.MeshStandardMaterial({ 
                    color: isDay ? 0x444444 : 0x1a1a20, // 昼は少し明るいアスファルト色
                    roughness: 0.9,
                    metalness: 0.1
                });
                const ground = new THREE.Mesh(groundGeo, groundMat);
                ground.rotation.x = -Math.PI / 2;
                ground.position.y = -15.5; 
                ground.receiveShadow = true;
                environmentGroup.add(ground);

                // ゴジラの配置(昼の街の場合のみ)
                if (isDay) {
                    const godzilla = createGodzilla();
                    // ギドラの手前(Z方向)に配置し、ギドラの方を向かせる
                    godzilla.position.set(20, -15.5, 70);
                    godzilla.lookAt(0, -10, 0);
                    environmentGroup.add(godzilla);
                }

                // ビル群の生成
                const buildingGeo = new THREE.BoxGeometry(1, 1, 1);
                buildingGeo.translate(0, 0.5, 0); 
                
                const mats = [
                    new THREE.MeshStandardMaterial({ color: isDay ? 0x888899 : 0x222233, roughness: 0.6 }),
                    new THREE.MeshStandardMaterial({ color: isDay ? 0x777788 : 0x333344, roughness: 0.5 }),
                    new THREE.MeshStandardMaterial({ color: isDay ? 0x9999AA : 0x111118, roughness: 0.8 }),
                ];

                for(let i=0; i<80; i++) {
                    const width = Math.random() * 15 + 10;
                    const depth = Math.random() * 15 + 10;
                    const height = Math.random() * 60 + 20;

                    const mat = mats[Math.floor(Math.random() * mats.length)];
                    const bMesh = new THREE.Mesh(buildingGeo, mat);
                    bMesh.scale.set(width, height, depth);
                    
                    // 中央(怪獣たちのいる場所)を空けて配置
                    const angle = Math.random() * Math.PI * 2;
                    const radius = Math.random() * 200 + 80; 
                    
                    bMesh.position.set(
                        Math.cos(angle) * radius, 
                        -15.5, 
                        -Math.sin(angle) * radius
                    );
                    
                    bMesh.rotation.y = Math.random() * Math.PI;
                    bMesh.castShadow = true;
                    bMesh.receiveShadow = true;
                    environmentGroup.add(bMesh);

                    // 夜の街の場合のみ航空障害灯を付ける
                    if (!isDay && Math.random() > 0.5) {
                        const lightGeo = new THREE.BoxGeometry(1.5, 1.5, 1.5);
                        const lightMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
                        const lightMesh = new THREE.Mesh(lightGeo, lightMat);
                        lightMesh.position.set(bMesh.position.x, -15.5 + height, bMesh.position.z);
                        environmentGroup.add(lightMesh);
                    }
                }
            } else if (vData.pose === 'standing') {
                // その他の直立ポーズ(予備)用:スタジオ撮影風の反射する暗い床
                const groundGeo = new THREE.PlaneGeometry(500, 500);
                const groundMat = new THREE.MeshStandardMaterial({ 
                    color: 0x111111,
                    roughness: 0.2,
                    metalness: 0.8
                });
                const ground = new THREE.Mesh(groundGeo, groundMat);
                ground.rotation.x = -Math.PI / 2;
                ground.position.y = -15.5; 
                ground.receiveShadow = true;
                environmentGroup.add(ground);
            }
        }

        function buildGhidorah() {
            const vData = VERSIONS[currentVersion];
            const isStanding = vData.pose === 'standing';
            
            ghidorahGroup = new THREE.Group();
            bodyGroup = new THREE.Group(); 
            ghidorahGroup.add(bodyGroup);
            scene.add(ghidorahGroup);
            
            // 飛行か直立かで全体の姿勢を変える
            ghidorahGroup.rotation.x = isStanding ? 0 : Math.PI / 8;

            // --- 体 ---
            const bodyGeo = new THREE.SphereGeometry(7, 16, 16);
            const bodyMesh = new THREE.Mesh(bodyGeo, goldMaterial);
            bodyMesh.scale.set(vData.bodyScale[0], vData.bodyScale[1], vData.bodyScale[2]);
            
            if (isStanding) {
                bodyMesh.position.set(0, 5, 0);
                bodyMesh.rotation.x = Math.PI / 10; // ほぼ直立(少し前傾)
            } else {
                bodyMesh.position.set(0, 0, 0);
                bodyMesh.rotation.x = Math.PI / 2.5; // 水平に近い
            }
            bodyMesh.castShadow = true;
            bodyGroup.add(bodyMesh);

            // --- 首 ---
            const nS = vData.neckScale;
            // 直立時は体の上部から、飛行時は体の前方から生やす
            const nBaseY = isStanding ? 10 : 5;
            const nBaseZ = isStanding ? 5 : 8; // 首の根本を前方に移動して翼から離す

            const centerControl = isStanding ? new THREE.Vector3(0, nBaseY + 15*nS, nBaseZ + 15*nS) : new THREE.Vector3(0, 15*nS, 25*nS);
            const centerEnd = isStanding ? new THREE.Vector3(0, nBaseY + 30*nS, nBaseZ + 20*nS) : new THREE.Vector3(0, 30*nS, 40*nS);

            createNeckWithHead({
                start: new THREE.Vector3(0, nBaseY, nBaseZ),
                control: centerControl,
                end: centerEnd,
                radiusStart: 3.2, radiusEnd: 1.5,
                animSpeed: 1.2, animPhase: 0.0,
                count: vData.neckCount
            });

            // 左右の首も外側・前方に広げる
            const leftControl = isStanding ? new THREE.Vector3(-12*nS, nBaseY + 12*nS, nBaseZ + 10*nS) : new THREE.Vector3(-15*nS, 12*nS, 20*nS);
            const leftEnd = isStanding ? new THREE.Vector3(-18*nS, nBaseY + 25*nS, nBaseZ + 15*nS) : new THREE.Vector3(-25*nS, 25*nS, 35*nS);

            createNeckWithHead({
                start: new THREE.Vector3(-4, nBaseY-1, nBaseZ-1),
                control: leftControl,
                end: leftEnd,
                radiusStart: 3.0, radiusEnd: 1.4,
                animSpeed: 1.0, animPhase: Math.PI / 3,
                count: vData.neckCount
            });

            const rightControl = isStanding ? new THREE.Vector3(12*nS, nBaseY + 12*nS, nBaseZ + 10*nS) : new THREE.Vector3(15*nS, 12*nS, 20*nS);
            const rightEnd = isStanding ? new THREE.Vector3(18*nS, nBaseY + 25*nS, nBaseZ + 15*nS) : new THREE.Vector3(25*nS, 25*nS, 35*nS);

            createNeckWithHead({
                start: new THREE.Vector3(4, nBaseY-1, nBaseZ-1),
                control: rightControl,
                end: rightEnd,
                radiusStart: 3.0, radiusEnd: 1.4,
                animSpeed: 1.4, animPhase: -Math.PI / 4,
                count: vData.neckCount
            });

            // --- 尻尾 ---
            const tS = vData.tailScale;
            const tBaseY = isStanding ? -2 : -2;
            const tBaseZ = isStanding ? -4 : -8;

            // 直立ポーズ時は、尻尾を地面(Y=-12付近)に這わせながら後方へ長く伸ばす
            const tailLControl = isStanding ? new THREE.Vector3(-8*tS, 0, -25*tS) : new THREE.Vector3(-8*tS, -10*tS, -40*tS);
            const tailLEnd = isStanding ? new THREE.Vector3(-15*tS, -12, -60*tS) : new THREE.Vector3(-15*tS, -5*tS, -80*tS);

            createTail(new THREE.Vector3(-2, tBaseY, tBaseZ), tailLControl, tailLEnd, 0, vData);

            const tailRControl = isStanding ? new THREE.Vector3(8*tS, 0, -25*tS) : new THREE.Vector3(8*tS, -10*tS, -40*tS);
            const tailREnd = isStanding ? new THREE.Vector3(15*tS, -12, -60*tS) : new THREE.Vector3(15*tS, -5*tS, -80*tS);

            createTail(new THREE.Vector3(2, tBaseY, tBaseZ), tailRControl, tailREnd, Math.PI, vData);

            // --- 足 ---
            createLeg(-5, vData, isStanding); 
            createLeg(5, vData, isStanding);  

            // --- 翼 ---
            createWings(vData, isStanding);
        }

        function createNeckWithHead(params) {
            const neckRootGroup = new THREE.Group();
            neckRootGroup.position.copy(params.start); 
            
            const localControl = new THREE.Vector3().subVectors(params.control, params.start);
            const localEnd = new THREE.Vector3().subVectors(params.end, params.start);
            
            const count = params.count;
            const segments = [];
            
            for (let i = 0; i <= count; i++) {
                const t = i / count;
                const r = params.radiusStart * (1 - t) + params.radiusEnd * t;
                
                const sphereGeo = new THREE.SphereGeometry(r, 16, 16);
                const mesh = new THREE.Mesh(sphereGeo, goldMaterial);
                mesh.scale.y = 0.8;
                mesh.castShadow = true;
                neckRootGroup.add(mesh);
                segments.push({ mesh: mesh, t: t, r: r });
            }

            const headGroup = createHeadMesh();
            neckRootGroup.add(headGroup);
            
            neckRootGroup.userData = {
                baseLocalControl: localControl,
                baseLocalEnd: localEnd,
                segments: segments,
                headGroup: headGroup,
                speed: params.animSpeed,
                phase: params.animPhase
            };
            neckGroups.push(neckRootGroup);
            bodyGroup.add(neckRootGroup);
        }

        function createHeadMesh() {
            const headGroup = new THREE.Group();
            
            const skullGeo = new THREE.BoxGeometry(2.5, 2.5, 3.5);
            const skull = new THREE.Mesh(skullGeo, goldMaterial);
            skull.position.set(0, 0, 1);
            skull.castShadow = true;
            headGroup.add(skull);

            const snoutGeo = new THREE.BoxGeometry(1.8, 1.5, 3);
            const snout = new THREE.Mesh(snoutGeo, goldMaterial);
            snout.position.set(0, -0.5, 4);
            snout.castShadow = true;
            headGroup.add(snout);

            const hornGeo = new THREE.ConeGeometry(0.4, 3, 8);
            hornGeo.translate(0, 1.5, 0); 
            
            const leftHorn = new THREE.Mesh(hornGeo, goldMaterial);
            leftHorn.position.set(-1, 1, 0);
            leftHorn.rotation.x = -Math.PI / 3;
            leftHorn.rotation.z = Math.PI / 6;
            leftHorn.castShadow = true;
            headGroup.add(leftHorn);

            const rightHorn = new THREE.Mesh(hornGeo, goldMaterial);
            rightHorn.position.set(1, 1, 0);
            rightHorn.rotation.x = -Math.PI / 3;
            rightHorn.rotation.z = -Math.PI / 6;
            rightHorn.castShadow = true;
            headGroup.add(rightHorn);

            const eyeGeo = new THREE.SphereGeometry(0.3, 8, 8);
            const leftEye = new THREE.Mesh(eyeGeo, eyeMaterial);
            leftEye.position.set(-1.3, 0.5, 2);
            headGroup.add(leftEye);

            const rightEye = new THREE.Mesh(eyeGeo, eyeMaterial);
            rightEye.position.set(1.3, 0.5, 2);
            headGroup.add(rightEye);

            return headGroup;
        }

        function createTail(start, control, end, animPhase, vData) {
            const tailRootGroup = new THREE.Group();
            tailRootGroup.position.copy(start);

            const localControl = new THREE.Vector3().subVectors(control, start);
            const localEnd = new THREE.Vector3().subVectors(end, start);

            const count = vData.tailCount;
            const segments = [];
            const radiusStart = 3.5 * (vData.bodyScale[0]); 
            const radiusEnd = 0.5;

            for (let i = 0; i <= count; i++) {
                const t = i / count;
                const r = radiusStart * (1 - t) + radiusEnd * t;
                
                const segmentGroup = new THREE.Group();
                
                const sphereGeo = new THREE.SphereGeometry(r, 16, 16);
                const mesh = new THREE.Mesh(sphereGeo, goldMaterial);
                mesh.castShadow = true;
                segmentGroup.add(mesh);

                if (i % 2 === 0 && i > 5) {
                    const spikeGeo = new THREE.ConeGeometry(r*0.4, r*1.5, 8);
                    const spike = new THREE.Mesh(spikeGeo, goldMaterial);
                    const dirX = (i % 4 === 0) ? 1 : -1;
                    const spikeDir = new THREE.Vector3(dirX, 0, 0);
                    
                    spike.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), spikeDir);
                    spike.position.copy(spikeDir.multiplyScalar(r));
                    spike.castShadow = true;
                    segmentGroup.add(spike);
                }
                
                tailRootGroup.add(segmentGroup);
                segments.push({ group: segmentGroup, t: t, r: r });
            }

            let tipObj = new THREE.Group();
            if (vData.hasMace) {
                const tipMaceGeo = new THREE.IcosahedronGeometry(2.5, 1);
                const tipMace = new THREE.Mesh(tipMaceGeo, goldMaterial);
                tipMace.castShadow = true;
                tipObj.add(tipMace);
            } else {
                const tipSpikeGeo = new THREE.ConeGeometry(0.8, 4, 8);
                const tipSpike = new THREE.Mesh(tipSpikeGeo, goldMaterial);
                tipSpike.rotation.x = Math.PI / 2;
                tipSpike.castShadow = true;
                tipObj.add(tipSpike);
            }
            tailRootGroup.add(tipObj);

            tailRootGroup.userData = {
                baseLocalControl: localControl,
                baseLocalEnd: localEnd,
                segments: segments,
                tipMace: tipObj,
                speed: 0.8,
                phase: animPhase
            };
            tailGroups.push(tailRootGroup);
            bodyGroup.add(tailRootGroup);
        }

        function createLeg(xOffset, vData, isStanding) {
            const legGroup = new THREE.Group();
            
            if (isStanding) {
                // 直立ポーズ(地面を踏みしめる)
                legGroup.position.set(xOffset * vData.bodyScale[0], 2, 0); 

                const thighGeo = new THREE.SphereGeometry(3.0 * vData.bodyScale[0], 16, 16);
                const thigh = new THREE.Mesh(thighGeo, goldMaterial);
                thigh.scale.set(1, 1.5, 1);
                thigh.position.set(0, -3, 0);
                thigh.rotation.x = Math.PI / 6; // 少し前に出す
                thigh.castShadow = true;
                legGroup.add(thigh);

                const shinGeo = new THREE.SphereGeometry(2.0 * vData.bodyScale[0], 16, 16);
                const shin = new THREE.Mesh(shinGeo, goldMaterial);
                shin.scale.set(1, 2.0, 1);
                shin.position.set(0, -9, 2);
                shin.rotation.x = -Math.PI / 12; // 地面に立てる
                shin.castShadow = true;
                legGroup.add(shin);

                const footGroup = new THREE.Group();
                footGroup.position.set(0, -13, 3);
                
                const footGeo = new THREE.BoxGeometry(3, 1.5, 4);
                const foot = new THREE.Mesh(footGeo, goldMaterial);
                foot.position.y = 0.75;
                foot.castShadow = true;
                footGroup.add(foot);

                for (let i = -1; i <= 1; i++) {
                    const clawGeo = new THREE.ConeGeometry(0.4, 2.0, 8);
                    clawGeo.translate(0, 1.0, 0);
                    const claw = new THREE.Mesh(clawGeo, goldMaterial);
                    claw.position.set(i * 1.2, 0.5, 2.0);
                    claw.rotation.x = Math.PI / 2;
                    claw.castShadow = true;
                    footGroup.add(claw);
                }
                legGroup.add(footGroup);
            } else {
                // 飛行ポーズ(後ろへ流す)
                legGroup.position.set(xOffset * vData.bodyScale[0], 0, -5); 

                const thighGeo = new THREE.SphereGeometry(3.0 * vData.bodyScale[0], 16, 16);
                const thigh = new THREE.Mesh(thighGeo, goldMaterial);
                thigh.scale.set(1, 1.5, 1);
                thigh.position.set(0, -2, 0);
                thigh.rotation.x = Math.PI / 1.5;
                thigh.castShadow = true;
                legGroup.add(thigh);

                const shinGeo = new THREE.SphereGeometry(2.0 * vData.bodyScale[0], 16, 16);
                const shin = new THREE.Mesh(shinGeo, goldMaterial);
                shin.scale.set(1, 2.0, 1);
                shin.position.set(0, -6, -3);
                shin.rotation.x = Math.PI / 1.8;
                shin.castShadow = true;
                legGroup.add(shin);

                const footGroup = new THREE.Group();
                footGroup.position.set(0, -9, -5);
                footGroup.rotation.x = Math.PI / 2;
                
                const footGeo = new THREE.BoxGeometry(2.5, 1, 3);
                const foot = new THREE.Mesh(footGeo, goldMaterial);
                foot.castShadow = true;
                footGroup.add(foot);

                for (let i = -1; i <= 1; i++) {
                    const clawGeo = new THREE.ConeGeometry(0.4, 2.0, 8);
                    clawGeo.translate(0, 1.0, 0);
                    const claw = new THREE.Mesh(clawGeo, goldMaterial);
                    claw.position.set(i * 1.0, 0, 1.5);
                    claw.rotation.x = Math.PI / 2;
                    claw.castShadow = true;
                    footGroup.add(claw);
                }
                legGroup.add(footGroup);
            }

            bodyGroup.add(legGroup);
        }

        function createWings(vData, isStanding) {
            function createStraightBone(start, end, rStart, rEnd) {
                const distance = start.distanceTo(end);
                const geo = new THREE.CylinderGeometry(rEnd, rStart, distance, 8);
                const mesh = new THREE.Mesh(geo, goldMaterial);
                const mid = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
                mesh.position.copy(mid);
                mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), new THREE.Vector3().subVectors(end, start).normalize());
                mesh.castShadow = true;
                return mesh;
            }

            const addClaw = (group, posVector, baseVector, size) => {
                const cGeo = new THREE.ConeGeometry(0.2 * size, 1.0 * size, 8);
                const cMesh = new THREE.Mesh(cGeo, goldMaterial);
                cMesh.position.copy(posVector);
                const dir = new THREE.Vector3().subVectors(posVector, baseVector).normalize();
                cMesh.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0), dir);
                cMesh.position.add(dir.multiplyScalar(0.5 * size));
                cMesh.castShadow = true;
                group.add(cMesh);
            };

            const membraneMat = new THREE.MeshStandardMaterial({
                color: (vData.wingType === 'wyvern') ? 0x907000 : vData.color, 
                metalness: vData.metalness,
                roughness: vData.roughness + 0.1,
                side: THREE.DoubleSide
            });

            function buildWingSide(isLeft) {
                const wingGroup = new THREE.Group();
                const wingShape = new THREE.Shape();
                let points = {};

                if (vData.wingType === 'wyvern') {
                    // ハリウッド版
                    points.P0 = new THREE.Vector2(0, 0);
                    points.P1 = new THREE.Vector2(12, 15);
                    points.P2 = new THREE.Vector2(35, 35);
                    points.P3 = new THREE.Vector2(45, 10);
                    points.P4 = new THREE.Vector2(35, -5);
                    points.P5 = new THREE.Vector2(18, -12);

                    wingShape.moveTo(points.P0.x, points.P0.y);
                    wingShape.quadraticCurveTo(5, 10, points.P1.x, points.P1.y);
                    wingShape.quadraticCurveTo(20, 30, points.P2.x, points.P2.y);
                    wingShape.quadraticCurveTo(35, 25, points.P3.x, points.P3.y);
                    wingShape.quadraticCurveTo(35, 5, points.P4.x, points.P4.y);
                    wingShape.quadraticCurveTo(25, -5, points.P5.x, points.P5.y);
                    wingShape.quadraticCurveTo(8, -8, points.P0.x, points.P0.y);

                } else if (vData.wingType === 'fan_showa') {
                    // 初代版(上辺が丸い扇型)
                    points.P0 = new THREE.Vector2(0, 0);
                    points.P1 = new THREE.Vector2(5, 25);  
                    points.P2 = new THREE.Vector2(20, 30); 
                    points.P3 = new THREE.Vector2(35, 25); 
                    points.P4 = new THREE.Vector2(35, 5);  
                    points.P5 = new THREE.Vector2(20, -5);

                    wingShape.moveTo(points.P0.x, points.P0.y);
                    wingShape.quadraticCurveTo(2, 10, points.P1.x, points.P1.y);
                    wingShape.quadraticCurveTo(10, 30, points.P2.x, points.P2.y);
                    wingShape.quadraticCurveTo(25, 30, points.P3.x, points.P3.y);
                    wingShape.quadraticCurveTo(38, 15, points.P4.x, points.P4.y);
                    wingShape.quadraticCurveTo(30, 0, points.P5.x, points.P5.y);
                    wingShape.quadraticCurveTo(10, -5, points.P0.x, points.P0.y);
                } else {
                    // 平成版(直線的で鋭く高く広がる扇型)
                    points.P0 = new THREE.Vector2(0, 0);
                    points.P1 = new THREE.Vector2(10, 35);  
                    points.P2 = new THREE.Vector2(28, 42); 
                    points.P3 = new THREE.Vector2(45, 25); 
                    points.P4 = new THREE.Vector2(38, 5);  
                    points.P5 = new THREE.Vector2(20, -8);

                    wingShape.moveTo(points.P0.x, points.P0.y);
                    wingShape.quadraticCurveTo(5, 18, points.P1.x, points.P1.y);
                    wingShape.quadraticCurveTo(18, 40, points.P2.x, points.P2.y);
                    wingShape.quadraticCurveTo(38, 35, points.P3.x, points.P3.y);
                    wingShape.quadraticCurveTo(42, 15, points.P4.x, points.P4.y);
                    wingShape.quadraticCurveTo(30, -2, points.P5.x, points.P5.y);
                    wingShape.quadraticCurveTo(10, -4, points.P0.x, points.P0.y);
                }

                const wingGeo = new THREE.ShapeGeometry(wingShape);
                
                const pos = wingGeo.attributes.position;
                for (let i = 0; i < pos.count; i++) {
                    const x = pos.getX(i);
                    const y = pos.getY(i);
                    const refX = vData.wingType === 'wyvern' ? points.P1.x : 0;
                    const refY = vData.wingType === 'wyvern' ? points.P1.y : 0;
                    const dist = Math.sqrt((x-refX)**2 + (y-refY)**2);
                    pos.setZ(i, Math.sin(dist * 0.1) * 2.0); 
                }
                wingGeo.computeVertexNormals();

                const membrane = new THREE.Mesh(wingGeo, membraneMat);
                membrane.castShadow = true;
                wingGroup.add(membrane);

                const getZ = (x, y) => {
                    const refX = vData.wingType === 'wyvern' ? points.P1.x : 0;
                    const refY = vData.wingType === 'wyvern' ? points.P1.y : 0;
                    return Math.sin(Math.sqrt((x-refX)**2 + (y-refY)**2) * 0.1) * 2.0;
                };

                const v0 = new THREE.Vector3(points.P0.x, points.P0.y, getZ(points.P0.x, points.P0.y));
                const v1 = new THREE.Vector3(points.P1.x, points.P1.y, getZ(points.P1.x, points.P1.y));
                const v2 = new THREE.Vector3(points.P2.x, points.P2.y, getZ(points.P2.x, points.P2.y));
                const v3 = new THREE.Vector3(points.P3.x, points.P3.y, getZ(points.P3.x, points.P3.y));
                const v4 = new THREE.Vector3(points.P4.x, points.P4.y, getZ(points.P4.x, points.P4.y));
                const v5 = new THREE.Vector3(points.P5.x, points.P5.y, getZ(points.P5.x, points.P5.y));

                if (vData.wingType === 'wyvern') {
                    wingGroup.add(createStraightBone(v0, v1, 1.2, 0.8)); 
                    wingGroup.add(createStraightBone(v1, v2, 0.8, 0.1)); 
                    wingGroup.add(createStraightBone(v1, v3, 0.6, 0.1));
                    wingGroup.add(createStraightBone(v1, v4, 0.6, 0.1));
                    wingGroup.add(createStraightBone(v1, v5, 0.6, 0.1));
                    addClaw(wingGroup, v1, v0, 2.5); 
                    addClaw(wingGroup, v2, v1, 1.5);
                    addClaw(wingGroup, v3, v1, 1.0);
                    addClaw(wingGroup, v4, v1, 1.0);
                    addClaw(wingGroup, v5, v1, 1.0);
                } else {
                    wingGroup.add(createStraightBone(v0, v1, 1.0, 0.2));
                    wingGroup.add(createStraightBone(v0, v2, 1.0, 0.2));
                    wingGroup.add(createStraightBone(v0, v3, 1.0, 0.2));
                    wingGroup.add(createStraightBone(v0, v4, 1.0, 0.2));
                    wingGroup.add(createStraightBone(v0, v5, 1.0, 0.2));
                    addClaw(wingGroup, v1, v0, 1.0);
                    addClaw(wingGroup, v2, v0, 1.0);
                    addClaw(wingGroup, v3, v0, 1.0);
                    addClaw(wingGroup, v4, v0, 1.0);
                    addClaw(wingGroup, v5, v0, 1.0);
                }
                
                if (isLeft) {
                    wingGroup.rotation.y = Math.PI;
                    wingGroup.scale.z = -1; 
                }

                wingGroup.scale.multiplyScalar(vData.wingScale);
                return wingGroup;
            }

            leftWingPivot = new THREE.Group();
            // 翼が背中でクロスするのを防ぐため、付け根を外側に広げ、少し後ろへ離す
            leftWingPivot.position.set(-8, isStanding ? 8 : 2, isStanding ? -6 : -2); 
            
            leftWingPivot.userData.baseRot = isStanding ? {
                x: Math.PI / 12,
                y: Math.PI / 8, // 後ろへの倒しを浅くして交差を回避
                z: vData.wingType === 'fan' ? -Math.PI / 12 : -Math.PI / 10 // バンザイを抑え、真横に近く張り出すように修正
            } : {
                x: Math.PI / 12,
                y: Math.PI / 6,
                z: -Math.PI / 6
            };
            leftWingPivot.rotation.set(
                leftWingPivot.userData.baseRot.x,
                leftWingPivot.userData.baseRot.y,
                leftWingPivot.userData.baseRot.z
            );
            
            const leftWingMesh = buildWingSide(true);
            leftWingPivot.add(leftWingMesh);
            bodyGroup.add(leftWingPivot);

            rightWingPivot = new THREE.Group();
            rightWingPivot.position.set(8, isStanding ? 8 : 2, isStanding ? -6 : -2); // 右翼も同様に広げる
            
            rightWingPivot.userData.baseRot = isStanding ? {
                x: Math.PI / 12,
                y: -Math.PI / 8,
                z: vData.wingType === 'fan' ? Math.PI / 12 : Math.PI / 10 // バンザイを抑え、真横に近く張り出すように修正
            } : {
                x: Math.PI / 12,
                y: -Math.PI / 6,
                z: Math.PI / 6
            };
            rightWingPivot.rotation.set(
                rightWingPivot.userData.baseRot.x,
                rightWingPivot.userData.baseRot.y,
                rightWingPivot.userData.baseRot.z
            );
            
            const rightWingMesh = buildWingSide(false);
            rightWingPivot.add(rightWingMesh);
            bodyGroup.add(rightWingPivot);
        }

        // 簡易的なゴジラを生成する関数
        function createGodzilla() {
            const godzillaGroup = new THREE.Group();
            
            // ゴジラ特有の黒灰色の皮膚と、白っぽい背びれのマテリアル
            const gMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.9, metalness: 0.1 });
            const spineMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.8, metalness: 0.2 });

            // 胴体 (前傾姿勢) - CapsuleGeometryの代わりにSphereのスケールを使用
            const bodyGeo = new THREE.SphereGeometry(5, 16, 16);
            const body = new THREE.Mesh(bodyGeo, gMat);
            body.scale.set(1, 2.2, 1); // カプセル形状をシミュレート
            body.position.set(0, 12, 0);
            body.rotation.x = Math.PI / 6;
            body.castShadow = true;
            godzillaGroup.add(body);

            // 頭部
            const headGeo = new THREE.BoxGeometry(4, 4, 6);
            const head = new THREE.Mesh(headGeo, gMat);
            head.position.set(0, 20, 4);
            head.castShadow = true;
            godzillaGroup.add(head);

            // 腕 (小さめ) - Sphereのスケールを使用
            const armGeo = new THREE.SphereGeometry(1.5, 8, 8);
            
            const armL = new THREE.Mesh(armGeo, gMat);
            armL.scale.set(1, 2.5, 1);
            armL.position.set(-4, 15, 3);
            armL.rotation.x = Math.PI / 2;
            armL.rotation.z = Math.PI / 6;
            armL.castShadow = true;
            godzillaGroup.add(armL);
            
            const armR = new THREE.Mesh(armGeo, gMat);
            armR.scale.set(1, 2.5, 1);
            armR.position.set(4, 15, 3);
            armR.rotation.x = Math.PI / 2;
            armR.rotation.z = -Math.PI / 6;
            armR.castShadow = true;
            godzillaGroup.add(armR);

            // 太い足 - Sphereのスケールを使用
            const legGeo = new THREE.SphereGeometry(3.5, 16, 16);
            
            const legL = new THREE.Mesh(legGeo, gMat);
            legL.scale.set(1, 1.8, 1);
            legL.position.set(-4, 5, -2);
            legL.castShadow = true;
            godzillaGroup.add(legL);

            const legR = new THREE.Mesh(legGeo, gMat);
            legR.scale.set(1, 1.8, 1);
            legR.position.set(4, 5, -2);
            legR.castShadow = true;
            godzillaGroup.add(legR);

            // 長く太い尻尾 (地面を這うように)
            const tailCount = 20;
            for(let i = 0; i < tailCount; i++) {
                const r = 4.5 * (1 - i / tailCount);
                const tGeo = new THREE.SphereGeometry(r, 16, 16);
                const tMesh = new THREE.Mesh(tGeo, gMat);
                
                // 後ろへ伸ばしつつ、地面に着いたら這わせる
                const yPos = 8 - i * 1.5;
                tMesh.position.set(0, Math.max(yPos, r), -6 - i * 3);
                tMesh.castShadow = true;
                godzillaGroup.add(tMesh);

                // 尻尾にも背びれを付ける
                if (i < 12 && i % 2 === 0) {
                    const spineGeo = new THREE.ConeGeometry(r*0.4, r*1.2, 4);
                    const spine = new THREE.Mesh(spineGeo, spineMat);
                    spine.position.set(0, tMesh.position.y + r*0.8, tMesh.position.z);
                    spine.rotation.x = -Math.PI / 4;
                    spine.castShadow = true;
                    godzillaGroup.add(spine);
                }
            }

            // 背びれ (背中)
            for(let i = 0; i < 6; i++) {
                const size = 1.5 - Math.abs(i - 2) * 0.2; // 中央を大きく
                const spineGeo = new THREE.ConeGeometry(size * 1.5, size * 4, 4);
                const spine = new THREE.Mesh(spineGeo, spineMat);
                spine.position.set(0, 19 - i * 2.5, -i * 2.0);
                spine.rotation.x = -Math.PI / 3;
                spine.castShadow = true;
                godzillaGroup.add(spine);
            }

            // 全体のスケールを少し大きくして迫力を出す
            godzillaGroup.scale.set(1.5, 1.5, 1.5);

            return godzillaGroup;
        }

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        function animate() {
            requestAnimationFrame(animate);

            const elapsedTime = clock.getElapsedTime();
            const vData = VERSIONS[currentVersion];
            const isStanding = vData.pose === 'standing';

            if (leftWingPivot && rightWingPivot && leftWingPivot.userData.baseRot) {
                const flapSpeed = isStanding ? 1.0 : 2.5; // 直立時はゆっくり威嚇
                const flapPhase = Math.sin(elapsedTime * flapSpeed); 
                const flapTwist = Math.cos(elapsedTime * flapSpeed); 
                
                const flapRangeZ = isStanding ? 0.15 : 0.4;  
                const flapRangeY = isStanding ? 0.05 : 0.15; 
                const flapRangeX = isStanding ? 0.05 : 0.15; 

                leftWingPivot.rotation.z = leftWingPivot.userData.baseRot.z - flapPhase * flapRangeZ;
                leftWingPivot.rotation.y = leftWingPivot.userData.baseRot.y + flapPhase * flapRangeY;
                leftWingPivot.rotation.x = leftWingPivot.userData.baseRot.x + flapTwist * flapRangeX;

                rightWingPivot.rotation.z = rightWingPivot.userData.baseRot.z + flapPhase * flapRangeZ;
                rightWingPivot.rotation.y = rightWingPivot.userData.baseRot.y - flapPhase * flapRangeY;
                rightWingPivot.rotation.x = rightWingPivot.userData.baseRot.x + flapTwist * flapRangeX;
            }

            if (neckGroups && neckGroups.length > 0) {
                neckGroups.forEach(neck => {
                    const data = neck.userData;
                    const time = elapsedTime * data.speed + data.phase;
                    
                    const swayX = Math.sin(time * 0.8) * 3.0;
                    const swayY = Math.cos(time * 1.1) * 2.0;
                    const swayZ = Math.sin(time * 0.9) * 2.0;
                    const currentControl = data.baseLocalControl.clone().add(new THREE.Vector3(swayX, swayY, swayZ));
                    
                    const headSwayX = Math.sin(time * 1.2) * 4.0;
                    const headSwayY = Math.cos(time * 1.5) * 3.0;
                    const headSwayZ = Math.sin(time * 1.3) * 4.0;
                    const currentEnd = data.baseLocalEnd.clone().add(new THREE.Vector3(headSwayX, headSwayY, headSwayZ));
                    
                    const curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(0,0,0), currentControl, currentEnd);
                    
                    const waveFreq = 10.0; 
                    const waveAmp = 1.5;   
                    const waveSpeed = 6.0; 
                    
                    const up = new THREE.Vector3(0, 1, 0);
                    const points = [];
                    const count = data.segments.length - 1;
                    
                    for (let i = 0; i <= count; i++) {
                        const t = data.segments[i].t;
                        const basePos = curve.getPoint(t);
                        const tangent = curve.getTangent(t).normalize();
                        
                        let normal = new THREE.Vector3().crossVectors(tangent, up).normalize();
                        if (normal.lengthSq() < 0.001) normal.set(1, 0, 0); 
                        
                        const wave = Math.sin(time * waveSpeed - t * waveFreq);
                        const offset = normal.multiplyScalar(wave * waveAmp * Math.pow(t, 1.2));
                        
                        points.push(basePos.add(offset));
                    }
                    
                    for (let i = 0; i <= count; i++) {
                        const seg = data.segments[i];
                        seg.mesh.position.copy(points[i]);
                        
                        if (i < count) {
                            const dir = new THREE.Vector3().subVectors(points[i+1], points[i]).normalize();
                            seg.mesh.quaternion.setFromUnitVectors(up, dir);
                        } else {
                            seg.mesh.quaternion.copy(data.segments[i-1].mesh.quaternion);
                        }
                    }
                    
                    data.headGroup.position.copy(points[count]);
                    
                    const lastTangent = new THREE.Vector3().subVectors(points[count], points[count-1]).normalize();
                    const lookTargetLocal = points[count].clone().add(lastTangent);
                    
                    const lookTargetWorld = neck.localToWorld(lookTargetLocal);
                    data.headGroup.lookAt(lookTargetWorld);
                    
                    data.headGroup.rotateZ(Math.sin(time * 2.0) * 0.1);
                    data.headGroup.rotateX(Math.sin(time * 3.0) * 0.1);
                });
            }

            if (tailGroups && tailGroups.length > 0) {
                tailGroups.forEach(tail => {
                    const data = tail.userData;
                    const time = elapsedTime * data.speed + data.phase;
                    
                    const swayX = Math.sin(time * 0.7) * 4.0;
                    const swayY = Math.cos(time * 0.9) * 2.0;
                    const currentControl = data.baseLocalControl.clone().add(new THREE.Vector3(swayX, swayY, 0));
                    
                    const endSwayX = Math.sin(time * 1.1) * 8.0;
                    const endSwayY = Math.cos(time * 1.3) * 4.0;
                    const currentEnd = data.baseLocalEnd.clone().add(new THREE.Vector3(endSwayX, endSwayY, 0));
                    
                    const curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(0,0,0), currentControl, currentEnd);
                    
                    const waveFreq = 8.0; 
                    const waveAmp = 2.5;  
                    const waveSpeed = 5.0; 
                    
                    const up = new THREE.Vector3(0, 1, 0);
                    const points = [];
                    const count = data.segments.length - 1;
                    
                    for (let i = 0; i <= count; i++) {
                        const t = i / count;
                        const basePos = curve.getPoint(t);
                        const tangent = curve.getTangent(t).normalize();
                        
                        let normal = new THREE.Vector3().crossVectors(tangent, up).normalize();
                        if (normal.lengthSq() < 0.001) normal.set(1, 0, 0);
                        
                        const wave = Math.sin(time * waveSpeed - t * waveFreq);
                        const offset = normal.multiplyScalar(wave * waveAmp * Math.pow(t, 1.5));
                        
                        points.push(basePos.add(offset));
                    }
                    
                    for (let i = 0; i <= count; i++) {
                        const seg = data.segments[i];
                        seg.group.position.copy(points[i]);
                        
                        if (i < count) {
                            const dir = new THREE.Vector3().subVectors(points[i+1], points[i]).normalize();
                            const right = new THREE.Vector3().crossVectors(up, dir).normalize();
                            const actualUp = new THREE.Vector3().crossVectors(dir, right).normalize();
                            const matrix = new THREE.Matrix4().makeBasis(right, actualUp, dir);
                            seg.group.quaternion.setFromRotationMatrix(matrix);
                        } else {
                            seg.group.quaternion.copy(data.segments[i-1].group.quaternion);
                        }
                    }
                    
                    data.tipMace.position.copy(points[count]);
                    data.tipMace.quaternion.copy(data.segments[count].group.quaternion);
                });
            }

            if (bodyGroup) {
                if (isStanding) {
                    // 直立時はソフビフィギュアのように浮遊しない
                    bodyGroup.position.y = -5;
                    bodyGroup.rotation.z = 0;
                } else {
                    bodyGroup.position.y = Math.sin(elapsedTime * 1.5) * 2.0;
                    bodyGroup.rotation.z = Math.sin(elapsedTime * 0.8) * 0.05;
                }
            }

            controls.update(); 
            renderer.render(scene, camera);
        }
    </script>
</body>
</html>

kinngugidora

キングギドラが初代、ハリウッド、平成、3匹がいます。 どれもかっこいいのが特徴です!

🤖 使用したAI
Gemini
👁 26 回閲覧
📅 投稿:2026年4月11日 🔄 更新:2026年8月13日
応援スタンプを押してみよう!