资源分享 https://share.weiyun.com/KR56bGcq


babylon.js比 three.js要简单很多

blender投影切割多余线 blender无光投影材质_javascript

加载 electron 离屏窗口渲染到场景中。加载mmd模型文件pmx ,加载动作文件vmd

blender投影切割多余线 blender无光投影材质_blender投影切割多余线_02

  • 模型地址: https://www.aplaybox.com/
  • [角色-三月七] https://www.aplaybox.com/details/model/3Z9TvNRzUCT2
  • mmd - MikuMikuDance
/**
 * @file babylon.js 
 * @warning @babylonjs/havok 物理引擎有wasm文件,添加到vite配置文件 optimizeDeps 中排除
 * @file 模型地址: https://www.aplaybox.com/
 * @file [角色-三月七] https://www.aplaybox.com/details/model/3Z9TvNRzUCT2
 * @file [摇摆动作] https://www.aplaybox.com/details/motion/MROM14tKgFlB
 * 
 * @author 一切时空过去未来
 * 
 * @description 加载mmd模型,  .pmx .vmd 文件, 某些需要使用blender调整模型
 * @file blender地址: https://www.blender.org/download/
 * @file blender插件blender_mmd_tools:  https://github.com/UuuNyaa/blender_mmd_tools/releases
 * @file 纯Blender流程制作MMD 3D动画】 https://www.bilibili.com/video/BV1Pz4y1h7Aq/?share_source=copy_web&vd_source=25362a4390fdd7435668ff21e468a64a
 */
  

import HavokPhysics from'@babylonjs/havok'       //物理引擎有wasm文件,添加到vite配置文件 optimizeDeps 中排除
import _ from 'lodash-es'

export{ 启动babylon }


/**
 * @function 启动babylon
 * @param {Object}  传入canvas节点 
 * @returns 
 * @description 由于 babylonjs 包太大, 在 vite 里用 manualChunks 单独打包
 */
async function 启动babylon(canvas节点){
    if(!canvas节点 || canvas节点.存在动画) return
    canvas节点.存在动画 = true

    const __静态资源 = window.__静态资源         //vite中配置资源   publicDir: '🏜️静态资源'
    const 模型路径 = `${__静态资源}3D模型/`
    const mmd模型路径 = `${__静态资源}mmd模型/`
    const 全景天空={
        天空1:`${__静态资源}图片/全景/1.jpg`,
        天空2:`${__静态资源}图片/全景/2.jpg`,
        天空3:`${__静态资源}图片/全景/3.jpg`,
    }
 
     
    /** @type {import('@babylonjs/core/Legacy/legacy')} */
    const { useIntersectionObserver , useResizeObserver } = await import('@vueuse/core')
    const BABYLON   = await import('@babylonjs/core/Legacy/legacy')
    const GUI       = await import("@babylonjs/gui")
    const Loaders   = await import('@babylonjs/loaders')
    const MMD       = await import('babylon-mmd')
    // const Inspector = await import( '@babylonjs/inspector')
    // import * as cannon from "cannon";
    // import { GrassProceduralTexture  } from "@babylonjs/procedural-textures"
    // import { WebXRLayers } from "@babylonjs/core/XR/features/WebXRLayers";  
    // import { WebXRDomOverlay } from "@babylonjs/core/XR/features/WebXRDOMOverlay";

    const { port1:本地渲染port, port2:远程渲染port } = new MessageChannel()
    const { port1:本地操作port, port2:远程操作port } = new MessageChannel()
    window.postMessage("创建离屏窗口","*",[ 远程渲染port,远程操作port ] )
   
    let scene,engine, camera, webXR, inputMap = {} ;
    let vmdLoader, mmdRuntime
   
    //----------- 启动 -------------------
    async function init() {
        
        //  建立基础场景
        
        engine = await BABYLON.EngineFactory.CreateAsync(canvas节点);
        scene = new BABYLON.Scene(engine);
        canvas节点.存在动画 = scene
        // scene.autoClear = false;                    // Color buffer
        // scene.autoClearDepthAndStencil = false;     // Depth and stencil, obviously
        
        const havokInstance = await HavokPhysics();
        const havok插件 = new BABYLON.HavokPlugin(true, havokInstance);
        scene.enablePhysics(new BABYLON.Vector3(0, -9.81 * 10 , 0), havok插件);
        
        vmdLoader = new MMD.VmdLoader(scene);
        mmdRuntime = new MMD.MmdRuntime(scene , new MMD.MmdPhysics(scene));
        mmdRuntime.register(scene);
        // mmdRuntime.playAnimation();
        // mmdRuntime.pauseAnimation();
        // mmdRuntime.seekAnimation(0)
        
 
        // camera = new BABYLON.ArcRotateCamera('自由摄像机', new BABYLON.Vector3(0, 6, 20) , scene);
        camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2, Math.PI / 2.5, 10, new BABYLON.Vector3(0, 5, 0), scene);
        scene.activeCamera = camera;
        scene.activeCamera.attachControl(canvas节点, true);
        camera.upperBetaLimit = Math.PI / 1.5;
        camera.lowerBetaLimit = Math.PI / 5;
        camera.lowerRadiusLimit = 2;
        camera.upperRadiusLimit = 30;
        camera.wheelPrecision = 0.001;
        camera.wheelDeltaPercentage = 0.01;
        camera.targetScreenOffset = new BABYLON.Vector2(-2, -7)
        camera.position = new BABYLON.Vector3(0, 0, 20)
        // camera.lowerRadiusLimit = 2;
        // camera.upperRadiusLimit = 10;
        // camera.wheelDeltaPercentage = 0.01;
        // camera.attachControl(true);
        // camera.setTarget(new BABYLON.Vector3(0, 7, 0));
        // camera.keysUp.push(87);    //WASD移动
        // camera.keysDown.push(83);
        // camera.keysLeft.push(65);
        // camera.keysRight.push(68);
        // camera.speed= 2;
        // camera.inertia=0.3;        //惯性
        // camera.minZ=0.5;           //屏蔽物体的距离 防止物体遮挡
        // camera.checkCollisions = true;
        // camera.applyGravity = true;
        // camera.ellipsoid = new BABYLON.Vector3(1, 4, 1);
        // scene.onBeforeCameraRenderObservable.add(()=> { if(camera.position.y < -20) camera.position = new BABYLON.Vector3(0, 4, 20) } )
        
        /
        // 天空 环境光 地面
        /
        let 天空   = new BABYLON.PhotoDome("全景天空", 全景天空.天空1, {size: 1000}, scene );  
        天空.material.backFaceCulling = true          //背面不显示
        天空.freezeWorldMatrix(); 
        天空.material.freeze(); 
    
        scene.ambientColor = new BABYLON.Color3(1, 1, 1);
        let 环境光 = new BABYLON.HemisphericLight("环境光", new BABYLON.Vector3(0, 1, 0), scene);
        环境光.intensity = 0.6;
        环境光.specular = BABYLON.Color3.Black();
        let 直线光 = new BABYLON.DirectionalLight("直线光", new BABYLON.Vector3(0, -0.5, -1.0), scene);
        直线光.position = new BABYLON.Vector3(0, 5, 5);

        let 世界地面 = BABYLON.MeshBuilder.CreateGround('世界地面', {width: 200, height:200 }, scene );
        世界地面.material = new BABYLON.StandardMaterial("woodMaterial", scene)  
        世界地面.material.diffuseColor   = BABYLON.Color3.Gray();         //反光颜色, 无光黑色
        世界地面.material.specularColor  = new BABYLON.Color3(0, 0, 0);
        世界地面.material.emissiveColor  = new BABYLON.Color3(0, 0, 0);   //自发光
        世界地面.material.backFaceCulling = true      
        世界地面.material.alpha = 0.5;
        世界地面.checkCollisions = true;                // 检测碰撞   mass = 0 表示静止
        // 木头材质.diffuseTexture = new GrassProceduralTexture("text", 512, scene);   
        
         
        ///
        //  监听事件
        ///
        canvas节点.setAttribute("tabindex","-1")                     //可以监听键盘事件   
        canvas节点.focus() 
        useResizeObserver(canvas节点, (entries)=>{  engine.resize()   })
        useIntersectionObserver(canvas节点, ([{ isIntersecting }], observerElement)=>{
            if(isIntersecting)   engine.runRenderLoop( () => scene.render() )  
            else                 engine.stopRenderLoop()       //页面不可见时,暂停渲染
            本地渲染port.postMessage({类型:"babylon动画可见",可见:isIntersecting})
        })
        // Keyboard events
        scene.actionManager = new BABYLON.ActionManager(scene);
        scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyDownTrigger, function (evt) {
            inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
        }));
        scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnKeyUpTrigger, function (evt) {
            inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown";
        }));
        
        /
        // 加载资源
        /
        sub打开网页浏览器()
        sub菜单UI界面()

        sub添加3D模型()
        // sub开启VR模式()
    }

    /**
     * @name 网页浏览器
     * @description 坐标系在(1 1 1) 看(0 0 0),正好是 X(左边) Y(上) Z(右边)
     * @description 实际显示大小,由 Mesh Plane的大小决定,且宽高比 必须是 1:1 才不失真
     * @description 实际网页比例,宽度是由主进程 BrowserWindow决定,且ImageData宽度必须相同, 高度自动计算
     * @description GUI.Rectangle ,相机位置必须在正面, 否则点击无效
     * @description
     * @code```js
     *   转换html窗口画面数据, 两种方法  ImageBitmap 和 VideoFrame
     *   let imgdata = new ImageData(new Uint8ClampedArray(uint8array),1000)       //Bitmap 数据为 Uint8Array    转为 Uint8ClampedArray
     *   let frame = await createImageBitmap(imgdata)   
     * ```
     * @code```js     canvas offscreen
     * const offscreen = new OffscreenCanvas(1000, 750);
     * const offscreen画笔 = offscreen.getContext("2d");
     * offscreen画笔.putImageData(new ImageData(new Uint8ClampedArray(uint8array),1000) ,0,0)
     * offscreen画笔.drawImage(frame,0, 0, 1000, 750)
     * offscreen画笔.reset()
     * // let frame = new VideoFrame(uint8array, {
            //            codedWidth: 1000,
            //            codedHeight: 750,
            //            timestamp:0,
            //            format:"BGRA",         // "BGRA" | "BGRX"  
            // })
     * ```
     */
    async function sub打开网页浏览器(){
        const axes = new BABYLON.Debug.AxesViewer(scene, 1)   // 红色X  绿色Y  蓝色Z 
        /
        //网页屏幕
        
        const 网页屏幕mesh = BABYLON.MeshBuilder.CreatePlane("网页屏幕", { size:15 });   //必须1:1 否则失真
        网页屏幕mesh.position = new BABYLON.Vector3(0, 5, 0)       //  屏幕中心在  0 0 0
        网页屏幕mesh.rotation.y = Math.PI     //旋转180
        const 网页屏幕 = GUI.AdvancedDynamicTexture.CreateForMesh(网页屏幕mesh);
        网页屏幕.hasAlpha = true              //允许透明
        const 网页屏幕画笔 = 网页屏幕.getContext()
        
        //
        //渲染 html 图片
        //
        //主进程 app.commandLine.appendSwitch('enable-features','SharedArrayBuffer')    才能用 SharedArrayBuffer
        // const offscreen = new OffscreenCanvas(1000, 750);
        // const offscreen画笔 = offscreen.getContext("2d");
        // let 共享 = new SharedArrayBuffer(10*1024*1024)
        // let newView = new Uint8Array(共享)
 
        本地渲染port.onmessage =  event =>{
            let 数据 = event.data ,dirty = 数据.obj ,buffer = 数据.buffer
            let frame = new VideoFrame(buffer , {
                       codedWidth: dirty.width,
                       codedHeight: dirty.height,
                       timestamp:0,
                       format:"BGRX",         // "BGRA" | "BGRX"  
            })
            网页屏幕画笔.clearRect(dirty.x, dirty.y,dirty.width,dirty.height)
            网页屏幕画笔.drawImage(frame,dirty.x, dirty.y,dirty.width,dirty.height)
            网页屏幕.update()
            frame.close()
        }
        本地渲染port.start()

        
        //鼠标事件矩形
           
        const 事件矩形 = new GUI.Rectangle("鼠标事件矩形");  //默认尺寸 100%
        事件矩形.isPointerBlocker = true;                  //make sure gui events are triggered before the scene events
        事件矩形.cornerRadius = 0
        事件矩形.thickness = 0
        事件矩形.color = "White";
        事件矩形.zIndex = 999;
        事件矩形.width=1000;
        事件矩形.height=750;
        事件矩形.hoverCursor = "pointer"
        事件矩形.isPickable = true
        网页屏幕.addControl(事件矩形);
        网页屏幕.moveToNonOverlappedPosition()
        let 记录鼠标位置 = {x:0,y:0} 
        const 获取mouse坐标 = (eventData)=>{ return {clientX:eventData.x , clientY:eventData.y, button:eventData.buttonIndex } }
        const 获取wheel坐标 = (eventData)=>{ return {clientX:记录鼠标位置.x , clientY:记录鼠标位置.y, wheel:{x:eventData.x, y:eventData.y}  } }
        事件矩形.onPointerClickObservable.add((eventData, eventState)=>{ 本地操作port.postMessage({类型:"click" , 属性: 获取mouse坐标(eventData) }) })   //坐标
        事件矩形.onPointerDownObservable.add((eventData, eventState)=>{  本地操作port.postMessage({类型:"pointerdown" , 属性: 获取mouse坐标(eventData) })   })
        事件矩形.onPointerUpObservable.add((eventData, eventState)=>{    本地操作port.postMessage({类型:"pointerup" , 属性: 获取mouse坐标(eventData) }) })
        事件矩形.onPointerMoveObservable.add((eventData, eventState)=>{  本地操作port.postMessage({类型:"pointermove" , 属性:  {clientX:eventData.x , clientY:eventData.y} }) ;记录鼠标位置.x = eventData.x; 记录鼠标位置.y = eventData.y;  })
        事件矩形.onPointerOutObservable.add((eventData, eventState)=>{   本地操作port.postMessage({类型:"pointerout" })  })
        事件矩形.onWheelObservable.add((eventData, eventState)=>{        本地操作port.postMessage({类型:"wheel" , 属性: 获取wheel坐标(eventData)}) })
     
       
    }
    function sub菜单UI界面(){
        //
        // UI控制面板
        //
 
        let 全屏菜单 = GUI.AdvancedDynamicTexture.CreateFullscreenUI("全屏菜单");
        let 进入全屏 = GUI.Button.CreateSimpleButton("进入全屏", "进入全屏");
        进入全屏.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
        进入全屏.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
        进入全屏.textHorizontalAlignment = GUI.TextBlock.HORIZONTAL_ALIGNMENT_LEFT;
        进入全屏.width = "90px";
        进入全屏.height = "40px";
        进入全屏.color = "white";
        进入全屏.background = "transparent"
        进入全屏.fontSize = 14;
        进入全屏.zIndex = 999
        全屏菜单.addControl(进入全屏);
        进入全屏.onPointerClickObservable.add(() => {  engine.enterFullscreen() });
        进入全屏.onWheelObservable.add(()=>{ })
        engine.onResizeObservable.add( 状态 => 进入全屏.isVisible = 状态.isFullscreen ? false :true  )

        let 显示fps = new GUI.TextBlock();
        显示fps.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
        显示fps.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
        显示fps.textHorizontalAlignment = GUI.TextBlock.HORIZONTAL_ALIGNMENT_LEFT;
        显示fps.text = "fps";
        显示fps.height = "50px";
        显示fps.color = "white";
        显示fps.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
        显示fps.fontSize = "30px"
        显示fps.resizeToFit = true
        全屏菜单.addControl(显示fps);
        const 显示帧率 = _.throttle(()=>{ 显示fps.text = parseInt(engine.getFps()) }, 500)
        scene.onBeforeRenderObservable.add( 状态 => 显示帧率() )

        // 2D GUI
        //   let 界面定位物体 = BABYLON.MeshBuilder.CreatePlane("plane", {size:25,});
        //   界面定位物体.position = new BABYLON.Vector3(0, 3, 2)

        //   let advancedTexture = GUI.AdvancedDynamicTexture.CreateForMesh(界面定位物体);
        //   let 平面按钮容器 = new GUI.StackPanel();
        //   advancedTexture.addControl(平面按钮容器);
        //   let 显示文本 = new GUI.TextBlock();
        //   显示文本.text = "欢迎使用2D界面";
        //   显示文本.height = "50px";
        //   显示文本.color = "white";
        //   显示文本.textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
        //   显示文本.fontSize = "30px"
        //   显示文本.resizeToFit = true
        //   平面按钮容器.addControl(显示文本);
          
        //   let num = 1
        //   const button = GUI.Button.CreateSimpleButton("but", "点击");
        //   button.textBlock.text = '点击';
        //   button.width = "200px";
        //   button.height = "50px";
        //   button.color = "white";
        //   button.background = "#eeeeee20";
        //   button.fontSize = 30;
        //   button.onPointerClickObservable.add((index,eventState) => {  
        //         eventState.currentTarget.textBlock.text = '你好 ' + num++
 
              
        //   })
        //   平面按钮容器.addControl(button);
  
        //   const 图片展示 = new GUI.Image("图片展示", 天空图片);
        //   图片展示.height = "300px"
        //   平面按钮容器.addControl(图片展示);
      

        // 3D GUI
        // var 显示面板 = new  GUI.HolographicBackplate("holoSlate");
        // 显示面板.content = 布局网格;
        // 显示面板.dimensions = new BABYLON.Vector2(100, 100);  //大小
        // 显示面板.position = new BABYLON.Vector3(2, 0, 3);
        // 显示面板.title = "HolographicBackplate";
        // 显示面板.scaling = new BABYLON.Vector3(3, 3, 3);
        // UI管理器.addControl(显示面板);
        // var 布局网格 = new  GUI.Grid("bioGrid");
        // 布局网格.background = "transparent";
        // 布局网格.adaptHeightToChildren = true
        // 布局网格.adaptWidthToChildren = true
 
        // var UI管理器 = new  GUI.GUI3DManager(scene);
        // // UI管理器.useRealisticScaling = true       //真实大小
        // var anchor = new BABYLON.AbstractMesh("anchor", scene)
        // const Button3D = new  GUI.Button3D("reset");
        // Button3D.linkToTransformNode(anchor);
        // Button3D.position = new BABYLON.Vector3(-5, 2, -5);
        // var text1 = new  GUI.TextBlock();
        // text1.text = "欢迎使用";
        // text1.color = "white";
        // text1.fontSize = 30;
        // Button3D.content = text1;
        // UI管理器.addControl(Button3D);
        // Button3D.onPointerUpObservable.add(()=> {text1.text = "点击"+ num++ });   

        // 显示信息.textWrapping =  GUI.TextWrapping.WordWrap;
        // 显示信息.textHorizontalAlignment =  GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
        // 显示信息.textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
        // 显示信息.setPadding("0%", "5%", "0%", "5%"); //边距
        // 显示信息.horizontalAlignment =  GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
        // 显示信息.verticalAlignment =  GUI.Control.VERTICAL_ALIGNMENT_TOP;
 
        // var UI管理器2 = new  GUI.GUI3DManager(scene);
        // const panel = new  GUI.PlanePanel();
        // UI管理器2.addControl(panel)
        // panel.position = new BABYLON.Vector3(2, 2, 6);

        // for (let index = 0; index < 5; index++) {
        //     let button = new  GUI.Button3D("click me"+index)
        //     let text1 = new  GUI.TextBlock();
        //     text1.text = "欢迎使用";
        //     text1.color = "white";
        //     text1.fontSize = 30;
        //     button.content = text1
        //     button.onPointerUpObservable.add((index,eventState)=>eventState.currentTarget.content.text = "欢迎使用" +num++  );  
        //     panel.addControl(button);
        // }
     
    }
    async function sub开启VR模式(){
        
        // VR 模式
        
        if(navigator.xr) {      	                   // 会话 session -> 参考空间 referenceSpace
            webXR = await scene.createDefaultXRExperienceAsync({
                uiOptions: {
                    sessionMode:'immersive-vr'    ,     // 'immersive-vr' | 'immersive-ar' | 'inline',
                    referenceSpaceType: "local-floor",  // 如果是站姿原地使用,适合 local-floor 类型,会增加相对于地板的高度 // 如果使用者是坐姿原地使用,不会离开起始位置太多,适合用 local 类型,让原点更稳定
                },
                disableDefaultUI: false,                 //关闭默认进入 按钮    会有按钮,点击进入全景vr模式
                floorMeshes: [世界地面],
                inputOptions: true,                      //开启 XR 控制器
                disableTeleportation: false,              //启用传送功能
                optionalFeatures: true,                   //打开所有
            });
            let fm = webXR.baseExperience.featuresManager
            let 传送环Material = new BABYLON.StandardMaterial("传送环", scene);
            传送环Material.backFaceCulling = false;
            传送环Material.diffuseColor = BABYLON.Color3.White();
            const 传送能力 = fm.enableFeature(BABYLON.WebXRFeatureName.TELEPORTATION, "stable", {
                xrInput: webXR.input,
                floorMeshes: [世界地面],
                // renderingGroupId: 1,
                defaultTargetMeshOptions: {
                    teleportationFillColor: "#00000000",
                    teleportationBorderColor: "white",
                    torusArrowMaterial: 传送环Material,
                },
            });
            // const domOverlayFeature = fm.enableFeature(BABYLON.WebXRFeatureName.DOM_OVERLAY, "latest",   { element: el }, undefined, false);
            webXR.baseExperience.onInitialXRPoseSetObservable.add((xrCamera) => { xrCamera.y = 2; });   // floor is at y === 2
            webXR.baseExperience.onStateChangedObservable.add((state) => {  });
            
            // const domOverlayFeature = fm.enableFeature(BABYLON.WebXRDomOverlay.Name, "latest", { element: el }, undefined, false);
            // BABYLON.WebXRLayers
            // const xrCamera = this.WebXR.baseExperience.camera
            // const sm = this.WebXR.baseExperience.sessionManager
            // const fm = this.WebXR.baseExperience.featuresManager
            // fm.enableFeature(BABYLON.WebXRFeatureName.LAYERS,"latest",{},true,false) ;
            // const referenceSpace = sessionManager.setReferenceSpaceTypeAsync( /*referenceSpaceType = 'local-floor'*/ );
            // const renderTarget = sessionManager.getWebXRRenderTarget( /*outputCanvasOptions: WebXRManagedOutputCanvasOptions*/ );
            // const xrWebGLLayer = renderTarget.initializeXRLayerAsync(this.sessionManager.session);
            // sessionManager.runXRRenderLoop();

            // VR 手柄
            var Box_Left_Trigger = BABYLON.MeshBuilder.CreateBox("Box_Left_Trigger",{},this.scene);
            Box_Left_Trigger.position = new BABYLON.Vector3(-2.5,1,3);
            var Box_Left_Squeeze = BABYLON.MeshBuilder.CreateBox("Box_Left_Squeeze",{},this.scene);
            Box_Left_Squeeze.position = new BABYLON.Vector3(-2.5,-1,3);
            var Sphere_Left_YButton = BABYLON.MeshBuilder.CreateSphere("Sphere_Left_YButton", {diameter:1}, this.scene);
            Sphere_Left_YButton.position = new BABYLON.Vector3(-2,0,3);
            var Sphere_Left_XButton = BABYLON.MeshBuilder.CreateSphere("Sphere_Left_XButton", {diameter:1}, this.scene);
            Sphere_Left_XButton.position = new BABYLON.Vector3(-2,0,2);
            var Box_Left_ThumbStick = BABYLON.MeshBuilder.CreateBox("Box_Left_ThumbStick",{size:0.5},this.scene);
            Box_Left_ThumbStick.position = new BABYLON.Vector3(-1,0,1);
            var Box_Right_Trigger = BABYLON.MeshBuilder.CreateBox("右_射击按钮",{},this.scene);
            Box_Right_Trigger.position = new BABYLON.Vector3(2.5,1,3);
            var Box_Right_Squeeze = BABYLON.MeshBuilder.CreateBox("右_握持按钮",{},this.scene);
            Box_Right_Squeeze.position = new BABYLON.Vector3(2.5,-1,3);
            var Sphere_Right_BButton = BABYLON.MeshBuilder.CreateSphere("右_B按钮", {diameter:1}, this.scene);
            Sphere_Right_BButton.position = new BABYLON.Vector3(2,0,3);
            var Sphere_Right_AButton = BABYLON.MeshBuilder.CreateSphere("右_A按钮", {diameter:1}, this.scene);
            Sphere_Right_AButton.position = new BABYLON.Vector3(2,0,2);
            var Box_Right_ThumbStick = BABYLON.MeshBuilder.CreateBox("右_摇杆",{size:0.5},this.scene);
            Box_Right_ThumbStick.position = new BABYLON.Vector3(1,0,1);
            
            this.WebXR.input.onControllerAddedObservable.add((controller) => {
                controller.onMotionControllerInitObservable.add((motionController) => {
                    if (motionController.handness === 'right') {
                         const xr_ids = motionController.getComponentIds();
                         let 射击按钮 = motionController.getComponent(xr_ids[0]);//xr-standard-trigger
                         射击按钮.onButtonStateChangedObservable.add(() => {
                             if (射击按钮.pressed) {
                                 Box_Right_Trigger.scaling= new BABYLON.Vector3(1.2,1.2,1.2);
                                 文本.text =Box_Right_Trigger.name ;
                             }else{
                                 Box_Right_Trigger.scaling= new BABYLON.Vector3(1,1,1);
                                 文本.text = ""
                             }
                         });
                         let 握持按钮 = motionController.getComponent(xr_ids[1]);//xr-standard-squeeze
                         握持按钮.onButtonStateChangedObservable.add(() => {
                             if (握持按钮.pressed) {
                                 Box_Right_Squeeze.scaling= new BABYLON.Vector3(1.2,1.2,1.2);
                                 文本.text =Box_Right_Squeeze.name ;
                             }else{
                                 Box_Right_Squeeze.scaling=new BABYLON.Vector3(1,1,1);
                                 文本.text = ""
                             }
                         });
                         let 摇杆按钮 = motionController.getComponent(xr_ids[2]);//xr-standard-thumbstick
                         摇杆按钮.onButtonStateChangedObservable.add(() => {
                             if (摇杆按钮.pressed) {
                                 Box_Right_ThumbStick.scaling= new BABYLON.Vector3(1.2,1.2,1.2);
                                 文本.text =Box_Right_ThumbStick.name ;
                                 
                             }else{
                                 Box_Right_ThumbStick.scaling=new BABYLON.Vector3(1,1,1);
                                 文本.text = ""
                             }
         
                         });
                         摇杆按钮.onAxisValueChangedObservable.add((axes) => {
                             //Box_Right_ThumbStick is moving according to stick axes but camera rotation is also changing..
                            // Box_Right_ThumbStick.position.x += (axes.x)/100;
                            // Box_Right_ThumbStick.position.y += (axes.y)/100;
                            // console.log(values.x, values.y);
                         });
         
                         let A按钮 = motionController.getComponent(xr_ids[3]);//a-button
                         A按钮.onButtonStateChangedObservable.add(() => {
                             if (A按钮.pressed) {
                                 Sphere_Right_AButton.scaling= new BABYLON.Vector3(1.2,1.2,1.2);
                                 文本.text =Sphere_Right_AButton.name ;
                             }else{
                                 Sphere_Right_AButton.scaling=new BABYLON.Vector3(1,1,1);  
                                 文本.text = ""
                             }
                         });
                         let B按钮 = motionController.getComponent(xr_ids[4]);//b-button
                         B按钮.onButtonStateChangedObservable.add(() => {
                             if (B按钮.pressed) {
                                 Sphere_Right_BButton.scaling= new BABYLON.Vector3(1.2,1.2,1.2);
                                 文本.text =Sphere_Right_BButton.name ;
                                
                             }else{
                                 Sphere_Right_BButton.scaling=new BABYLON.Vector3(1,1,1);  
                                 文本.text = ""
                             }
                         });
                         /* not worked.
                         let thumbrestComponent = motionController.getComponent(xr_ids[5]);//thumrest
                         thumbrestComponent.onButtonStateChangedObservable.add(() => {
                             //not worked
                             if ((thumbrestComponent.value>0.1&&thumbrestComponent.value<0.6) {
                                 sphere1.position.y=10;
                             }
                             if(thumbrestComponent.touched){
                                  sphere1.position.y=10;
                             }
         
                         });  
                         */              
           
                        /*
                         const xr_ids = motionController.getComponentIds();
                         for (let i=0;i<xr_ids.length;i++){
                             console.log("right:"+xr_ids[i]);
                         }
                        */
                    }
                })
            });

        }
    }
    async function sub添加3D模型(){
        let 主角, animating = true, 记录meshArray = [], 记录动作Array = []     //{文件名:文件名, 动作:vmd动作}
        const 打开窗口选取文件 = ()=>{
            return new Promise((成功后回调,失败后回调)=>{
                const input文件按钮 = document.createElement("input")   //这个按钮不好看,隐藏 ,触发 button事件
                input文件按钮.type = "file"
                input文件按钮.accept = ".gltf,.glb,.fbx,.obj,.pmx,.vmd"
                input文件按钮.onchange= async (事件)=>{ 
                    const File对象数组 = 事件.target.files;
                    成功后回调(File对象数组[0])
                }
                input文件按钮.onended= 事件 =>{ 成功后回调("") }
                input文件按钮.click()
            })
        }
          
        const mmd删除模型 = (mesh)=>{
            if(mesh?.扩展名=="pmx"){
                if(mesh?.动画播放器mmd && mesh?.mmdRuntime){
                    mesh.mmdRuntime.destroyMmdModel(mesh.动画播放器mmd)
                    mesh.mmdRuntime.unregister(scene)
                } 
                mesh.dispose()
            }
        }
        const mmd删除全部展示模型 = ()=>{
            记录meshArray.forEach( mesh=>{
                (mesh?.扩展名=="pmx") ?  mmd删除模型(mesh) : mesh.dispose()          //glb 
            })
            记录meshArray = []
        }
        const mmd启停动画_防抖 = _.debounce((mesh)=>{
            if(mesh?.mmdRuntime){
                if(mesh.mmdRuntime.isAnimationPlaying){
                    mesh.mmdRuntime.pauseAnimation();
                }
                else{ 
                    mesh.mmdRuntime.seekAnimation(0,true)
                    mesh.mmdRuntime.playAnimation();  
                } 
            }
        },100)
        const mmd模型加载动作 = (mesh, 传入动作名)=>{
            //加载动作后,不能缩放mesh,否则错误
            if(mesh?.扩展名=="pmx"){
                let vmd  =   (typeof 传入动作名=="object")   ?   传入动作名
                            :(!传入动作名 && mesh?.动作vmd)   ?   mesh.动作vmd               //播放自带的动作
                            :(typeof 传入动作名 =="string" && 记录动作Array.length) ?
                                    _.find(记录动作Array, {文件名: 传入动作名})  ||  记录动作Array[0] 
                            :(记录动作Array.length)  ?  _.find(记录动作Array, {文件名: "空闲.vmd"})  ||  记录动作Array[0]
                            : null 
                if(!vmd)  return
                
                mesh.动作vmd = vmd
                if(! mesh?.mmdRuntime){
                    mesh.mmdRuntime = new MMD.MmdRuntime(scene , new MMD.MmdPhysics(scene));
                    mesh.mmdRuntime.register(scene);
                    mesh.mmdRuntime.playAnimation()
                }
                if(! mesh?.动画播放器mmd) mesh.动画播放器mmd = mesh.mmdRuntime.createMmdModel(mesh);  //自带一个
                let 动画数组 = mesh.动画播放器mmd.runtimeAnimations
                if(!动画数组.some(项=>项.animation == vmd.动作)) { mesh.动画播放器mmd.addAnimation(vmd.动作)  }          // mesh.动画播放器mmd.resetState()
                mesh.动画播放器mmd.setAnimation(vmd.文件名);
          
                mesh.mmdRuntime.seekAnimation(0,true)                     //从头开始播放
                mesh.mmdRuntime.playAnimation()
                mesh.mmdRuntime.afterPhysics()
            }
        }

        const mmd导入本地模型 = async (传入路径)=>{
            let 文件名 = /(?<=\\|\/?)[^\\\/]+$/g.exec(传入路径)[0]
            //一种是本地文件, 一种是vite调试文件
            let 路径 = /^https?:\/\//g.test(传入路径) ? 传入路径 : /^file:/g.test(传入路径) ? 传入路径 : "file:" + 传入路径
            let 目录 = 路径.replace(new RegExp(`${文件名}$`),"")
            let 扩展名 = (/[^\.]+$/g.exec(文件名)[0]).toLowerCase()
            console.warn("导入模型", 路径)
            if(扩展名=="vmd"){   //动作文件
                let vmd动作 = await vmdLoader.loadAsync(文件名, 路径);
                let vmd对象 = { 文件名, 扩展名, 动作:vmd动作}
                return vmd对象
            }else{
                let mesh = await BABYLON.SceneLoader.ImportMeshAsync("",目录,文件名, scene ).then(加载结果=> 加载结果.meshes[0]);
                mesh.文件名 = 文件名, mesh.扩展名 = 扩展名, mesh.路径 = 路径, mesh.目录 = 目录 
                return mesh
            }
        }
        
        let 全屏菜单 = GUI.AdvancedDynamicTexture.CreateFullscreenUI("添加模型");
        let 添加模型 = GUI.Button.CreateSimpleButton("添加模型", "添加模型");
        添加模型.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
        添加模型.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
        添加模型.textHorizontalAlignment = GUI.TextBlock.HORIZONTAL_ALIGNMENT_LEFT;
        添加模型.width = "90px";
        添加模型.height = "40px";
        添加模型.color = "white";
        添加模型.background = "transparent"
        添加模型.fontSize = 14;
        添加模型.zIndex = 999
        全屏菜单.addControl(添加模型);
        添加模型.onPointerClickObservable.add( async() => {  
            let 文件路径 = (await 打开窗口选取文件() ).path
            let mesh = await mmd导入本地模型(文件路径)
            if(mesh?.扩展名=="vmd"){          //mmd动作文件
                记录动作Array.unshift(mesh)
                mmd模型加载动作(记录meshArray[0],  mesh.文件名)
            }
            else{ 
                mesh.position = new BABYLON.Vector3(6, 0, 5)
                mesh.scaling.scaleInPlace(0.5);
                mesh.lookAt( new BABYLON.Vector3(0, 0, -100) )

                if(mesh?.扩展名=="pmx"){      //mmd文件
                    mmd删除全部展示模型()
                    mmd模型加载动作(mesh,"舞蹈_快乐摇摆.vmd")
                }
                记录meshArray.push(mesh)

                mesh.actionManager = new BABYLON.ActionManager(scene);
                mesh.actionManager.registerAction(
                    new BABYLON.ExecuteCodeAction(
                        BABYLON.ActionManager.OnLeftPickTrigger,
                        (actionEvent)=>{ 
                            缩放比例 +=0.5 ; 
                            mesh.scaling.scaleInPlace(缩放比例);    
                        }
                    )
                );
                mesh.actionManager.registerAction(
                    new BABYLON.ExecuteCodeAction(
                        BABYLON.ActionManager.OnRightPickTrigger,
                        (actionEvent)=>{ 
                            if(缩放比例>1)   缩放比例 -=0.5 ; 
                            mesh.scaling.scaleInPlace(缩放比例);   
                  
                        }
                    )
                );
            }
        });
        
        //全局快捷键
        scene.actionManager.registerAction(
            new BABYLON.ExecuteCodeAction(
                BABYLON.ActionManager.OnKeyUpTrigger,
                (actionEvent)=> { if(actionEvent.sourceEvent.key=="Delete"){   mmd删除全部展示模型()   };  }
            )
        );
        //导入模型
        let [ 导入主角 ,走路vmd, 空闲vmd, 跳跃vmd, 快乐摇摆vmd, 大摆锤vmd ] = await Promise.all([
            mmd导入本地模型(`${mmd模型路径}女主角_三月七/三月七.pmx`),
            // mmd导入本地模型(`${mmd模型路径}女主角_荧/荧.pmx`),
            mmd导入本地模型(`${mmd模型路径}动作/走路.vmd`),
            mmd导入本地模型(`${mmd模型路径}动作/空闲.vmd`),
            mmd导入本地模型(`${mmd模型路径}动作/跳跃.vmd`),
            mmd导入本地模型(`${mmd模型路径}舞蹈/舞蹈_快乐摇摆.vmd`),
            mmd导入本地模型(`${mmd模型路径}舞蹈/舞蹈_大摆锤.vmd`)
        ])
        记录动作Array = [走路vmd, 空闲vmd, 跳跃vmd, 快乐摇摆vmd, 大摆锤vmd ]

        主角 = 导入主角 
        主角.position = new BABYLON.Vector3(0, 0, 5)    
        主角.scaling.scaleInPlace(0.5);
        camera.target = 主角;             //跟随
         
        mmd模型加载动作(主角 ,空闲vmd)
        主角.mmdRuntime.onAnimationTickObservable.add((a,b,c,d)=>{
            if(主角.mmdRuntime.currentFrameTime>=主角.mmdRuntime.animationFrameTimeDuration){
                mmd模型加载动作(主角)      //播放完成,循环当前动画
            }
        })

        //按键控制主角动画
        scene.onBeforeRenderObservable.add(() => { 
            let keydown = false;
            const 计算向量=()=>{
                //Manage the movements of the character (e.g. position, direction)
                // let 射线 = camera.getForwardRay()
                let 终点= 主角.position
                let 起点 = camera.position
                // let plane =  new BABYLON.Plane(0,1,0,0)  // Ax + By + Cz + D =0   设任意平面上一点(x,y,z),(A,B,C)是法向量
                // let 射线与平面相交点 =  终点.projectOnPlane(plane, 起点) 
                // 点在平面上投影 new BABYLON.Vector3(终点.x,0,终点.z)
                let 纵向向量 = new BABYLON.Vector3(终点.x - 起点.x, 0, 终点.z - 起点.z) .normalize()
                let 横向向量 = 纵向向量.cross(BABYLON.Vector3.Up())
                return{
                    前方视线向量:纵向向量.scale(100).negate(), 
                    纵向向量, 
                    横向向量  
                }
            }
            if (inputMap["w"]) {  
                let 向量 = 计算向量()
                主角.lookAt(向量.前方视线向量) ; 
                主角.position = 主角.position.add(向量.纵向向量.scale(0.2))  // scaleInPlace 会修改自身
                keydown = true;
            }
            if (inputMap["s"]) {
                let 向量 = 计算向量()
                主角.lookAt(向量.前方视线向量)
                主角.position = 主角.position.add(向量.纵向向量.scaleInPlace(0.2).negate()) 
                keydown = true;
            }
            if (inputMap["a"]) {
                let 向量 = 计算向量()
                主角.lookAt(向量.横向向量.scale(100).negate())
                主角.position = 主角.position.add(向量.横向向量.scaleInPlace(0.2)) 
                keydown = true;
            }
            if (inputMap["d"]) {
                let 向量 = 计算向量()
                主角.lookAt(向量.横向向量.scale(100))
                主角.position = 主角.position.add(向量.横向向量.scaleInPlace(0.2).negate()) 
                keydown = true;
            }
            if (inputMap["1"]) {  keydown = true;    }
            if (inputMap["2"]) {  keydown = true;    }
            if (inputMap[" "]) {  keydown = true; mmd启停动画_防抖(记录meshArray[0])   }
    
            //管理播放动画
            if (keydown) {
                if (!animating) {
                    animating = true;
                    if (inputMap["s"])      {}
                    else if(inputMap["1"])   mmd模型加载动作(主角, 快乐摇摆vmd) 
                    else if(inputMap["2"])   mmd模型加载动作(主角, 大摆锤vmd) 
                    else if(inputMap[" "])   mmd模型加载动作(主角, 跳跃vmd) 
                    else                     mmd模型加载动作(主角, 走路vmd)  
                }
            }
            else {
                if(animating) {
                    animating = false;
                    let 等待跳跃 = false
                    if(主角.动作vmd == 跳跃vmd && !等待跳跃){
                        等待跳跃 = true
                        setTimeout(()=>{ mmd模型加载动作(主角, 空闲vmd) ; 等待跳跃=false }, 500)
                    }
                    else if(!等待跳跃) mmd模型加载动作(主角, 空闲vmd)
                }
            }
        });
     
        console.log("主角模型",主角)
    }
    
    function sub视频播放模块(){
            //视频播放
            // let 全景播放器 = new BABYLON.VideoDome( "videoDome",
            //     ["./静态资源/test.mp4"], {
            //         resolution: 32,
            //         clickToPlay: true,
            //         halfDomeMode: true
            //     }, scene)
            // 全景播放器.videoMode = BABYLON.VideoDome.MODE_MONOSCOPIC;
            var 配置 = {
                height: 5.4762, 
                width: 7.3967, 
                sideOrientation: BABYLON.Mesh.DOUBLESIDE
            };
            var 播放器网格 = BABYLON.MeshBuilder.CreatePlane("plane", 配置, scene);
            播放器网格.position = (new BABYLON.Vector3(0,0,0.1));
            var 视频纹理 = new BABYLON.VideoTexture("vidtex","", scene);
            // 视频纹理.coordinatesMode = BABYLON.VideoTexture.PLANAR_MODE
            var 视频材质 = new BABYLON.StandardMaterial("m", scene);
            视频材质.diffuseTexture = 视频纹理;
            视频材质.roughness = 1;
            视频材质.emissiveColor = BABYLON.Color3.White();
            播放器网格.material = 视频材质;
            scene.onPointerObservable.add((evt)=>{
                    if(evt.pickInfo.pickedMesh === 播放器网格){
                            视频纹理.video.src = "./静态资源/test.mp4"
                            视频纹理.video.play();
                            // 视频纹理.video.pause();
                    }
                }, BABYLON.PointerEventTypes.POINTERPICK);
            const 六自由度控制行为 = new BABYLON.SixDofDragBehavior();
            六自由度控制行为.dragDeltaRatio = 0.2;
            播放器网格.addBehavior(六自由度控制行为)

    }


    init()
}



      
        


/*
 //------- 加载普通模型 -----------------

BABYLON.SceneLoader.ImportMesh("", 模型路径, "girl.glb", scene, function (newMeshes, particleSystems, skeletons, animationGroups) {
            let hero = newMeshes[0];
            hero.position = new BABYLON.Vector3(0, 0, 5)    
            hero.scaling.scaleInPlace(0.3);
            camera.target = hero;     //跟随
          
            let 前进速度 = 0.3, 后退速度 = 0.5, 旋转速度 = 0.1;
            let animating = true;

            const walkAnim = _.find(animationGroups, {name:"Walking"});
            const walkBackAnim = _.find(animationGroups, {name:"WalkingBack"}); 
            const idleAnim = _.find(animationGroups, {name:"Idle"}); 
            const sambaAnim = _.find(animationGroups, {name:"Samba"});  
            
            const 物体前方射线拾取物体 = ()=>{
                function vecToLocal(vector, mesh){
                    var m = mesh.getWorldMatrix();
                    var v = BABYLON.Vector3.TransformCoordinates(vector, m);
                    return v;		 
                }
                var origin = hero.position;
                let forward = vecToLocal(new BABYLON.Vector3(0,0,1), hero);
                var direction = forward.subtract(origin);
                direction = BABYLON.Vector3.Normalize(direction);
                var ray = new BABYLON.Ray(origin, direction, 100);
                var hit = scene.pickWithRay(ray);
                if (hit.pickedMesh){
                //    hit.pickedMesh.scaling.y += 0.01;  
                }
            }
            


            //Rendering loop (executed for everyframe)
            scene.onBeforeRenderObservable.add(() => { 
                // 物体前方射线拾取物体()

                let keydown = false;
                const 计算向量=()=>{
                    //Manage the movements of the character (e.g. position, direction)
                    // let 射线 = camera.getForwardRay()
                    let 终点= hero.position
                    let 起点 = camera.position
                    // let plane =  new BABYLON.Plane(0,1,0,0)  // Ax + By + Cz + D =0   设任意平面上一点(x,y,z),(A,B,C)是法向量
                    // let 射线与平面相交点 =  终点.projectOnPlane(plane, 起点) 
                    // 点在平面上投影 new BABYLON.Vector3(终点.x,0,终点.z)
                    let 纵向向量 = new BABYLON.Vector3(终点.x - 起点.x, 0, 终点.z - 起点.z) .normalize()
                    let 横向向量 = 纵向向量.cross(BABYLON.Vector3.Up())
                    return{
                        前方视线向量:纵向向量.scale(100).negate(), 
                        纵向向量, 
                        横向向量  
                    }
                }

                if (inputMap["w"]) {  
                    let 向量 = 计算向量()
                    hero.lookAt(向量.前方视线向量) ; 
                    // hero.moveWithCollisions( 向量.纵向向量.scaleInPlace(0.2) )  
                    hero.position = hero.position.add(向量.纵向向量.scale(0.2))  // scaleInPlace 会修改自身
                    keydown = true;
                }
                if (inputMap["s"]) {
                    let 向量 = 计算向量()
                    hero.lookAt(向量.前方视线向量)
                    // hero.moveWithCollisions(hero.forward.scaleInPlace(-后退速度));
                    // hero.moveWithCollisions(向量.纵向向量.scaleInPlace(0.2).negate());
                    hero.position = hero.position.add(向量.纵向向量.scaleInPlace(0.2).negate()) 
                    keydown = true;
                }
                if (inputMap["a"]) {
                    let 向量 = 计算向量()
                    hero.lookAt(向量.横向向量.scale(100).negate())
                    hero.position = hero.position.add(向量.横向向量.scaleInPlace(0.2)) 
                    keydown = true;
                }
                if (inputMap["d"]) {
                    let 向量 = 计算向量()
                    hero.lookAt(向量.横向向量.scale(100))
                    hero.position = hero.position.add(向量.横向向量.scaleInPlace(0.2).negate()) 
                    keydown = true;
                }
                if (inputMap["b"]) {
                    keydown = true;
                }
                if (inputMap[" "]) {
                    mmd启停动画_防抖()
                }
        
                //管理播放动画
                if (keydown) {
                    if (!animating) {
                        animating = true;
                        if (inputMap["s"])      walkBackAnim.start(true, 1.0, walkBackAnim.from, walkBackAnim.to, false);
                        else if(inputMap["b"])  sambaAnim.start(true, 1.0, sambaAnim.from, sambaAnim.to, false);
                        else                    walkAnim.start(true, 1.0, walkAnim.from, walkAnim.to, false);
                    }
                }
                else {
                    if (animating) {
                        //Default animation is idle when no key is down     
                        idleAnim.start(true, 1.0, idleAnim.from, idleAnim.to, false);
                        //Stop all animations besides Idle Anim when no key is down
                        sambaAnim.stop();
                        walkAnim.stop();
                        walkBackAnim.stop();
                        //Ensure animation are played only once per rendering loop
                        animating = false;
                    }
                }
            });
 
        });









  let 角色模型 = BABYLON.MeshBuilder.CreateCapsule('sphere1', { segments: 10,  diameter: 1,  });
        角色模型.material = new BABYLON.StandardMaterial("myMaterial", scene);
        角色模型.material.diffuseColor =  BABYLON.Color3.Green();//漫反射颜色
        角色模型.position = new BABYLON.Vector3(0, 0, 3);  ;
        角色模型.checkCollisions = true;                               //检测碰撞
        角色模型.ellipsoid = new BABYLON.Vector3(1, 1, 1);             //碰撞的表面  长 高 宽
        角色模型.ellipsoidOffset = new BABYLON.Vector3(1, 1, 1);

        scene.addMesh(角色模型)
        const 六自由度控制行为 = new BABYLON.SixDofDragBehavior();
        六自由度控制行为.dragDeltaRatio = 0.2;
        var boundingBox = BABYLON.BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(角色模型)
        boundingBox.addBehavior(六自由度控制行为)
        
         BABYLON.SceneLoader.Append(模型路径, "植物.glb", scene, function (scene) {   });   // 模型添加成功后,执行场景对象的一些方法
    
        BABYLON.SceneLoader.ImportMesh("", 模型路径, "植物.glb", scene, function (newMeshes, particleSystems,skeletons,animationGroups){
   
            let hero = newMeshes[0];
            hero.position = new BABYLON.Vector3(3, 0, -5)
            //Scale the model down        
            hero.scaling.scaleInPlace(1);
            hero.lookAt( new BABYLON.Vector3(1, 0, 1) )
            
            let 摇摆动画 =_.find(animationGroups,{name:"摇摆"})
            摇摆动画.stop();
  
            //Lock camera on the character 
            // camera.target = hero;

            //Get the Samba animation Group
            // const sambaAnim = scene.getAnimationGroupByName("Samba");

            // //Play the Samba animation  
            // sambaAnim.start(true, 1.0, sambaAnim.from, sambaAnim.to, false);
        });
        BABYLON.SceneLoader.ImportMesh("", 模型路径, "cute.glb", scene, function (newMeshes, particleSystems,skeletons,animationGroups){
 
            let hero = newMeshes[0];
            hero.position = new BABYLON.Vector3(20, 0, 5)
   
            hero.scaling.scaleInPlace(0.6);
            hero.lookAt( new BABYLON.Vector3(0, 0, 1) )
            
            let 摇摆动画 =_.find(animationGroups,{name:"摇摆"})
            摇摆动画.stop();
            //Lock camera on the character 
            // camera.target = hero;

            //Get the Samba animation Group
            // const sambaAnim = scene.getAnimationGroupByName("Samba");

            // //Play the Samba animation  
            // sambaAnim.start(true, 1.0, sambaAnim.from, sambaAnim.to, false);
        });
        
             






*/