Three.js使用WebWorker进行八叉树碰撞检测
生活随笔
收集整理的這篇文章主要介紹了
Three.js使用WebWorker进行八叉树碰撞检测
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
經(jīng)過一番探索后還是采用了整個碰撞檢測都交給worker來做 原因
如果是小的模型還是不需要這么做的 js線程足夠處理構(gòu)建時的開銷
步驟
代碼
結(jié)構(gòu)化物體數(shù)據(jù)并傳遞到worker
const modelStruct = ModelTranslate.generateWorkerStruct(obj); this.worker.postMessage({ type: "build", modelStruct });worker接收并轉(zhuǎn)換成有效的數(shù)據(jù)并進(jìn)行八叉樹構(gòu)建
onmessage = function (e) {switch (e.data.type) {case "build":const model = ModelTranslate.parseWorkerStruct(e.data.modelStruct);buildFromGraphNode.fromGraphNode(model);break; };主線程發(fā)送膠囊體的信息到worker中
this.playerCollider = new Capsule(); //... this.worker.postMessage({type: "collider",collider: this.playerCollider,});worker中根據(jù)碰撞體的信息構(gòu)建有效的碰撞體 將檢測結(jié)果返回
onmessage = function (e) {switch (e.data.type) {case "build":const model = ModelTranslate.parseWorkerStruct(e.data.modelStruct);buildFromGraphNode.fromGraphNode(model);break;case "collider":buildFromGraphNode.collider(e.data.collider);break;} };webWorker代碼
/** @Author: hongbin* @Date: 2023-02-25 12:06:52* @LastEditors: hongbin* @LastEditTime: 2023-02-26 19:21:06* @Description: 八叉樹構(gòu)建worker*/ import { Capsule } from "three/examples/jsm/math/Capsule"; import { Octree } from "../expand/Octree"; import { ModelTranslate } from "./ModelTranslate";class BuildFromGraphNode {worldOctree!: Octree;playerCollider!: Capsule;constructor() {this.init();}init() {this.playerCollider = new Capsule();this.worldOctree = new Octree();}collider({start,end,radius,}: {start: { x: number; y: number; z: number };end: { x: number; y: number; z: number };radius: number;}) {this.playerCollider.start.set(start.x, start.y, start.z);this.playerCollider.end.set(end.x, end.y, end.z);this.playerCollider.radius = radius;const result = this.worldOctree.capsuleIntersect(this.playerCollider);postMessage({type: "colliderResult",result,});}fromGraphNode(obj: Object3D) {const start = performance.now();this.worldOctree.fromGraphNode(obj);postMessage({type: "graphNodeBuildComplete",msg: `構(gòu)建八叉樹結(jié)構(gòu)成功 用時 ${performance.now() - start}`,// graphNode: this.worldOctree,});} }const buildFromGraphNode = new BuildFromGraphNode();/*** 監(jiān)聽主線程發(fā)來的數(shù)信息*/ onmessage = function (e) {switch (e.data.type) {case "connect":postMessage({msg: "連接成功",});break;case "build":const model = ModelTranslate.parseWorkerStruct(e.data.modelStruct);buildFromGraphNode.fromGraphNode(model);break;case "collider":buildFromGraphNode.collider(e.data.collider);break;} };export {};八叉樹控制器代碼
/** @Author: hongbin* @Date: 2023-02-02 13:37:11* @LastEditors: hongbin* @LastEditTime: 2023-02-26 19:23:22* @Description: 八叉樹控制器*/ import * as THREE from "three"; import { Sphere, Vector3 } from "three"; import { Capsule } from "../expand/Capsule"; import { Octree } from "../expand/Octree"; import { Octree as WorkerOctree } from "../expand/WorkerOctree"; import { ThreeHelper } from "@/src/ThreeHelper"; import { OctreeHelper } from "three/examples/jsm/helpers/OctreeHelper"; import { ModelTranslate } from "../worker/ModelTranslate";interface VVector3 {x: number;y: number;z: number; }interface IWorkerSubTree {box: {isBox3: true;max: VVector3;min: VVector3;};triangles: { a: VVector3; b: VVector3; c: VVector3 }[];subTrees: IWorkerSubTree[]; }export class OctreeControls {private worldOctree: Octree;private worldWorldOctree?: WorkerOctree;playerCollider: Capsule;playerOnFloor = false;/** 構(gòu)建八叉樹完成 **/buildComplete = false;private _collide = (result: any) => {};private _player?: Object3D;private _box = new THREE.Box3();private worker?: Worker;private _normal = new Vector3();// octreeHelper: OctreeHelper;constructor() {this.worldOctree = new Octree();this.playerCollider = new Capsule();//@ts-ignore// this.octreeHelper = new OctreeHelper(this.worldOctree, 0xff0);this.useWebWorker();}/*** 碰撞回調(diào)*/collide(_collide: (result?: { normal: Vector3; depth: number }) => void) {this._collide = _collide;}/*** 與球體進(jìn)行碰撞*/sphereCollider(sphere: Sphere) {const result = this.worldOctree.sphereIntersect(sphere);return result;}handleCollider(result: ReturnType<typeof this.worldOctree["capsuleIntersect"]>) {this.playerOnFloor = false;if (result) {this.playerOnFloor = result.normal.y > 0;}if (this.isLog) {console.log(result, this.playerOnFloor);}this._collide(result);}/*** 碰撞檢測*/playerCollisions() {if (!this.buildComplete) return (this.playerOnFloor = true);// 如果啟用了webworker 交給worker處理if (this.worker) {this.worker.postMessage({type: "collider",collider: this.playerCollider,});return;}const world = this.worldWorldOctree? this.worldWorldOctree: this.worldOctree;const result = world.capsuleIntersect(this.playerCollider);this.handleCollider(result);}private isLog = false;console(gui: ThreeHelper["gui"]) {const plane = {log: () => {this.isLog = !this.isLog;},helper: () => {this.helper();},};gui?.add(plane, "log").name("打印八叉樹檢測結(jié)果");gui?.add(plane, "helper").name("查看八叉樹碰撞體");}private _translate = (v: Vector3) => {};onTranslate(call: typeof this._translate) {this._translate = call;}/*** 增量更新膠囊體的位置* 應(yīng)同步人物的移動*/translatePlayerCollider(v: Vector3) {this.playerCollider.translate(v);this._translate(v);}playerBox3() {return this._box;}helper() {if (!this._player) return;const radius = this.playerCollider.radius;const start = this.playerCollider.start;const end = this.playerCollider.end;{const mesh = ThreeHelper.instance.generateRect({width: 0.1,height: 0.01,depth: 0.01,},{ color: 0x00ffa0 });mesh.position.copy(this.playerCollider.start);// mesh.position.y -= radius;ThreeHelper.instance.add(mesh);}{const mesh = ThreeHelper.instance.generateRect({width: 0.1,height: 0.01,depth: 0.01,},{ color: 0xff3a00 });mesh.position.copy(this.playerCollider.end);// mesh.position.y += radius;ThreeHelper.instance.add(mesh);}{this._player.updateWorldMatrix(false, false);const Capsule = new THREE.Group();Capsule.applyMatrix4(this._player.matrixWorld);const length = start.clone().sub(end).length();const geometry = new THREE.CapsuleGeometry(radius, length, 4, 8);const material = new THREE.MeshBasicMaterial({color: 0x00ff00,wireframe: true,});const capsule = new THREE.Mesh(geometry, material);Capsule.add(capsule);Capsule.position.y += length / 2 + radius;// this._player.add(Capsule);ThreeHelper.instance.add(Capsule);}}/*** 計算碰撞體的膠囊體(碰撞范圍 膠囊的高度 和半徑)*/computeCollider() {if (!this._player) throw new Error("未執(zhí)行 player() 方法");const size = this._player.userData._size;// 半徑取 寬和長 大的一側(cè)const radius = Math.max(size.x, size.z) / 2;const { x, y, z } = this._player.position;const collider = {// 頭start: new THREE.Vector3(x, y + size.y - radius, z),// 腳end: new THREE.Vector3(x, y + radius - 0.01, z),radius,};return collider;}/*** 傳入玩家對象 計算玩家膠囊體的數(shù)據(jù)*/player(obj: Object3D) {this._player = obj;const defaultCollider = this.computeCollider();this.playerCollider.start.copy(defaultCollider.start);this.playerCollider.end.copy(defaultCollider.end);this.playerCollider.radius = defaultCollider.radius;}/*** 根據(jù)傳入對象構(gòu)建該對象的八叉樹結(jié)構(gòu)* 模型越大越耗時*/fromGraphNode(obj: Object3D, call?: VoidFunction) {if (this.worker) {const modelStruct = ModelTranslate.generateWorkerStruct(obj);this.worker.postMessage({ type: "build", modelStruct });} else {this.worldOctree.fromGraphNode(obj);console.log(this.worldOctree);// this.octreeHelper.update();this.buildComplete = true;call && call();}}/*** 格式化從web worker中拿到的八叉樹結(jié)構(gòu)* 開銷也非常大雖然只是格式轉(zhuǎn)變但要便利的次數(shù)依然十分龐大還是會對線程造成堵塞*/formatSubTrees(subTree: IWorkerSubTree) {const octree = new Octree();const min = new THREE.Vector3().copy(subTree.box.min as Vector3);const max = new THREE.Vector3().copy(subTree.box.max as Vector3);octree["box"] = new THREE.Box3(min, max);octree["triangles"] = subTree.triangles.map((triangle) => {const a = new THREE.Vector3().copy(triangle.a as Vector3);const b = new THREE.Vector3().copy(triangle.b as Vector3);const c = new THREE.Vector3().copy(triangle.c as Vector3);return new THREE.Triangle(a, b, c);});octree.subTrees = subTree.subTrees.map((subTree) =>this.formatSubTrees(subTree));return octree;}/*** 使用從web worker 構(gòu)建的八叉樹結(jié)構(gòu)*/updateGraphNode(subTree: IWorkerSubTree, call?: VoidFunction) {// const Octree = this.formatSubTrees(subTrees);this.worldWorldOctree = new WorkerOctree(subTree);this.buildComplete = true;call && call();}/*** 使用webWorker進(jìn)行八叉樹構(gòu)建、檢測*/useWebWorker() {/** 構(gòu)建八叉樹的web worker */const worker = new Worker(new URL("../worker/OctreeBuild.ts", import.meta.url));worker.onmessage = (e) => {this.worker = worker;if (e.data.type === "graphNodeBuildComplete") {console.log("八叉樹構(gòu)建完成", e.data);this.buildComplete = true;} else if (e.data.type == "colliderResult") {if (e.data.result) {const { normal } = e.data.result;this._normal.copy(normal);e.data.result.normal = this._normal;}this.handleCollider(e.data.result);if (this.isLog) {console.log(e.data);}}};worker.postMessage({ type: "connect" });worker.onerror = (err) => {console.error("work出錯:", err, err.message);};} }主線程使用八叉樹控制器代碼
//初始化 const octreeControls = new OctreeControls(); //構(gòu)建八叉樹 octreeControls.fromGraphNode(group);//碰撞檢測回調(diào) octreeControls.collide((result) => {// 碰撞的方向 * 深度if (result) {const v = result.normal.multiplyScalar(result.depth);v.y -= 0.0001;//根據(jù)碰撞的方向和深度 移動人物和碰撞題ObserverControl.translate(v);}}); //逐幀檢測 octreeControls.playerCollisions();總結(jié)
以上是生活随笔為你收集整理的Three.js使用WebWorker进行八叉树碰撞检测的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mac终端切换root用户
- 下一篇: 行驶车辆状态估计,无迹卡尔曼滤波,扩展卡