Browse Source

feat: new case-babylon

RyaneMax 9 months ago
parent
commit
bd3a64a6d8

+ 31 - 0
package-lock.json

@@ -16,6 +16,9 @@
         "@angular/platform-browser": "^18.0.0",
         "@angular/platform-browser-dynamic": "^18.0.0",
         "@angular/router": "^18.0.0",
+        "@babylonjs/core": "^7.14.0",
+        "@babylonjs/loaders": "^7.14.0",
+        "@babylonjs/materials": "^7.14.0",
         "@capacitor/app": "6.0.0",
         "@capacitor/core": "6.1.0",
         "@capacitor/haptics": "6.0.0",
@@ -2621,6 +2624,28 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babylonjs/core": {
+      "version": "7.14.0",
+      "resolved": "https://registry.npmmirror.com/@babylonjs/core/-/core-7.14.0.tgz",
+      "integrity": "sha512-1yTDyd+5W3GuarFoEYKtEr3dBE7/4N/a/2kwsz3DzBu7cPPjJgG0elRgfCkUm/bZ+9zHxUNfgip7sDF7elhYzQ=="
+    },
+    "node_modules/@babylonjs/loaders": {
+      "version": "7.14.0",
+      "resolved": "https://registry.npmmirror.com/@babylonjs/loaders/-/loaders-7.14.0.tgz",
+      "integrity": "sha512-TzxXXabwQ3qbUlSZUASU6AljPCtt0rQmlRQfsVvUp4TKBz8zYWNhTcWbO4hhBhwB2yWtuUwZAT04h2W8aKV5Wg==",
+      "peerDependencies": {
+        "@babylonjs/core": "^7.0.0",
+        "babylonjs-gltf2interface": "^7.0.0"
+      }
+    },
+    "node_modules/@babylonjs/materials": {
+      "version": "7.14.0",
+      "resolved": "https://registry.npmmirror.com/@babylonjs/materials/-/materials-7.14.0.tgz",
+      "integrity": "sha512-cqg4knLEDl3EinfYsrqcfG9QXMn+8OcFel0kbys5wvi4w38kx7/PvoEd5zLor0PivfHQdpK7gF77jeyZaZChRg==",
+      "peerDependencies": {
+        "@babylonjs/core": "^7.0.0"
+      }
+    },
     "node_modules/@capacitor/app": {
       "version": "6.0.0",
       "resolved": "https://registry.npmmirror.com/@capacitor/app/-/app-6.0.0.tgz",
@@ -7133,6 +7158,12 @@
         "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
       }
     },
+    "node_modules/babylonjs-gltf2interface": {
+      "version": "7.14.0",
+      "resolved": "https://registry.npmmirror.com/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.14.0.tgz",
+      "integrity": "sha512-yfoU8HA7k/qStdyFFCK3CXzUpyN/pksnPXj0I9kEf76XOospRm5VdLFvweombIyQQtMNV+aDAB8STqgECU3LDA==",
+      "peer": true
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",

+ 3 - 0
package.json

@@ -21,6 +21,9 @@
     "@angular/platform-browser": "^18.0.0",
     "@angular/platform-browser-dynamic": "^18.0.0",
     "@angular/router": "^18.0.0",
+    "@babylonjs/core": "^7.14.0",
+    "@babylonjs/loaders": "^7.14.0",
+    "@babylonjs/materials": "^7.14.0",
     "@capacitor/app": "6.0.0",
     "@capacitor/core": "6.1.0",
     "@capacitor/haptics": "6.0.0",

+ 4 - 2
src/app/app-routing.module.ts

@@ -29,14 +29,16 @@ const routes: Routes = [
         path: 'echarts',
         loadChildren: () => import('../modules/study/case-echarts/case-echarts.module').then( m => m.CaseEchartsPageModule)
       },
+      {
+        path: 'babylon',
+        loadChildren: () => import('../modules/study/case-js-module/case-babylon/case-babylon.module').then( m => m.CaseBabylonPageModule)
+      },
     ]
   },
   {
     path: 'aigc',
     loadChildren: () => import('../modules/aigc/aigc.module').then(m => m.AigcModule)
   },
-
-
  
 ];
 @NgModule({

+ 1 - 0
src/app/tab1/tab1.page.html

@@ -39,6 +39,7 @@
     <ion-card-content>
       <ion-button expand="block" routerLink="/study/slider">展示:Swiper轮播图插件</ion-button>
       <ion-button expand="block" routerLink="/study/echarts">展示:Echart数据图表</ion-button>
+      <ion-button expand="block" routerLink="/study/babylon">展示:Babylon3D渲染</ion-button>
     </ion-card-content>
   </ion-card>
 

+ 2 - 0
src/assets/3dmodel/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 12 - 0
src/modules/study/case-js-module/case-babylon/README.md

@@ -0,0 +1,12 @@
+# Babylon.js使用示例
+
+- 官方网站 https://doc.babylonjs.com/
+
+# 依赖安装
+- npm安装 https://doc.babylonjs.com/setup/frameworkPackages/es6Support
+
+``` bash
+npm install @babylonjs/core -S
+npm install @babylonjs/materials -S
+npm install @babylonjs/loaders -S
+```

+ 17 - 0
src/modules/study/case-js-module/case-babylon/case-babylon-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { CaseBabylonPage } from './case-babylon.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: CaseBabylonPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class CaseBabylonPageRoutingModule {}

+ 20 - 0
src/modules/study/case-js-module/case-babylon/case-babylon.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { CaseBabylonPageRoutingModule } from './case-babylon-routing.module';
+
+import { CaseBabylonPage } from './case-babylon.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    CaseBabylonPageRoutingModule
+  ],
+  declarations: [CaseBabylonPage]
+})
+export class CaseBabylonPageModule {}

+ 6 - 0
src/modules/study/case-js-module/case-babylon/case-babylon.page.html

@@ -0,0 +1,6 @@
+<div class="list">
+  <div (click)="showCharacter(item?.name)" class="character" *ngFor="let item of CharacterMeshList">
+    <h1>{{item?.name}}</h1>
+  </div>
+</div>
+<canvas id="renderCanvas" touch-action="none"></canvas>

+ 20 - 0
src/modules/study/case-js-module/case-babylon/case-babylon.page.scss

@@ -0,0 +1,20 @@
+
+#renderCanvas {
+    width: 100vw;
+    height: 100vh;
+    display: block;
+    font-size: 0;
+    z-index: 1;
+}
+
+.list{
+    position: absolute;
+    top:20px;
+    z-index: 10;
+    width: 100vw;
+    display: flex;
+    justify-content: center;
+    .character{
+        
+    }
+}

+ 17 - 0
src/modules/study/case-js-module/case-babylon/case-babylon.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { CaseBabylonPage } from './case-babylon.page';
+
+describe('CaseBabylonPage', () => {
+  let component: CaseBabylonPage;
+  let fixture: ComponentFixture<CaseBabylonPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CaseBabylonPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 161 - 0
src/modules/study/case-js-module/case-babylon/case-babylon.page.ts

@@ -0,0 +1,161 @@
+import { Component, OnInit } from '@angular/core';
+import * as BABYLON from "@babylonjs/core/Legacy/legacy";
+import { Engine, Scene } from "@babylonjs/core";
+import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera";
+import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight";
+import { Vector3 } from "@babylonjs/core/Maths/math.vector";
+import { CreateGround } from "@babylonjs/core/Meshes/Builders/groundBuilder";
+import { CreateSphere } from "@babylonjs/core/Meshes/Builders/sphereBuilder";
+import { GridMaterial } from "@babylonjs/materials/grid/gridMaterial";
+
+import "@babylonjs/loaders/glTF";
+
+@Component({
+  selector: 'app-case-babylon',
+  templateUrl: './case-babylon.page.html',
+  styleUrls: ['./case-babylon.page.scss'],
+})
+export class CaseBabylonPage implements OnInit {
+  private engine: BABYLON.Engine|undefined;
+  private scene: BABYLON.Scene|undefined;
+
+  CharacterMap:any = {}
+  CharacterMeshList = [
+    {
+      name:"机器人",
+      dirPath:"assets/3dmodel/robot_rk11/",
+      filePath:"scene.gltf"
+    },
+    {
+      name:"卡通人",
+      dirPath:"assets/3dmodel/vtuber/",
+      filePath:"scene.gltf"
+    }
+  ]
+  constructor() { }
+
+  ngOnInit() {
+    this.initBabylon()
+  }
+
+  initBabylon(){
+    // Get the canvas element from the DOM.
+    const canvas:any = document.getElementById("renderCanvas");
+    // Set the canvas size to match the device pixel ratio
+    const devicePixelRatio = window.devicePixelRatio || 1;
+    canvas.width = window.innerWidth * devicePixelRatio;
+    canvas.height = window.innerHeight * devicePixelRatio;
+    this.engine = new BABYLON.Engine(canvas, true);
+    this.scene = new BABYLON.Scene(this.engine);
+
+    // Create a basic light, aiming 0,1,0 - meaning, to the sky.
+    const light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), this.scene);
+
+    // 加载粒子效果覆盖地面
+    this.loadGroundParticle()
+    
+    // 设置初始相机
+    // Add an ArcRotateCamera to the scene and attach it to the canvas
+    let initPositon = new BABYLON.Vector3(0,12,0)
+    let beta = Math.PI / 2.5 // 镜头初始 纬度 Math.PI / 3 斜下45度
+    let radius = 20 // 镜头初始半径
+    const camera = new BABYLON.ArcRotateCamera('camera1', Math.PI / 2, beta, radius, initPositon, this.scene);
+    camera.attachControl(canvas, true);
+
+    // Load the FBX model
+
+    this.CharacterMeshList.forEach(character=>{
+      BABYLON.SceneLoader.ImportMesh('', character.dirPath, character.filePath, this.scene, (meshes, particleSystems, skeletons) => {
+        console.log(meshes)
+        // characterMesh.isVisible
+        meshes.forEach(mesh=>mesh.isVisible=false)
+        this.CharacterMap[character?.name] = {
+          meshes:meshes,
+          particleSystems:particleSystems,
+          skeletons:skeletons
+        }
+        if (skeletons.length > 0) {
+          this.scene?.beginAnimation(skeletons[0], 0, 100, true);
+        }
+      });
+    })
+    setTimeout(() => {
+      this.showCharacter("机器人")
+    }, 3000);
+    
+
+    // Register a render loop to repeatedly render the scene.
+    this.engine.runRenderLoop(() => {
+      this.scene?.render();
+    });
+
+    // Watch for browser/canvas resize events
+    window.addEventListener('resize', () => {
+      this.engine?.resize();
+    });
+  }
+
+  showCharacter(name?:string){
+    if(!name) return
+
+    Object.keys(this.CharacterMap).forEach(tempname=>{
+      let character = this.CharacterMap[tempname];
+      if(tempname==name){
+        if(character.meshes?.length){
+          character.meshes.forEach((mesh:any)=>mesh.isVisible=true)
+        }
+      }else{
+        if(character.meshes?.length){
+          character.meshes.forEach((mesh:any)=>mesh.isVisible=false)
+        }
+      }
+    })
+
+   
+  }
+  loadGroundParticle(){
+      if(!this.scene) return
+      // 创建粒子系统
+      const particleSystem = new BABYLON.ParticleSystem("particles", 2000, this.scene);
+
+      // 纹理
+      particleSystem.particleTexture = new BABYLON.Texture("textures/flare.png", this.scene);
+
+      // 发射器
+      particleSystem.emitter = new BABYLON.Vector3(0, 0, 0); // 发射器位置
+      particleSystem.minEmitBox = new BABYLON.Vector3(-300, 0, -300); // 最小发射范围
+      particleSystem.maxEmitBox = new BABYLON.Vector3(300, 0, 300); // 最大发射范围
+
+      // 粒子颜色
+      particleSystem.color1 = new BABYLON.Color4(1, 1, 1, 1);
+      particleSystem.color2 = new BABYLON.Color4(0.5, 0.5, 1, 1);
+      particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.5);
+
+      // 粒子大小
+      particleSystem.minSize = 0.1;
+      particleSystem.maxSize = 0.5;
+
+      // 粒子生命周期
+      particleSystem.minLifeTime = 0.3;
+      particleSystem.maxLifeTime = 1.5;
+
+      // 发射速率
+      particleSystem.emitRate = 1000;
+
+      // 粒子方向
+      particleSystem.direction1 = new BABYLON.Vector3(-1, 1, -1);
+      particleSystem.direction2 = new BABYLON.Vector3(1, 1, 1);
+
+      // 重力
+      particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
+
+      // 粒子速度
+      particleSystem.minEmitPower = 0.5;
+      particleSystem.maxEmitPower = 1.5;
+      particleSystem.updateSpeed = 0.01;
+
+      // 开始粒子系统
+      particleSystem.start();
+  }
+
+}