feat: 添加跨平台运行时、资产系统和UI适配功能 (#256)
* feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
This commit is contained in:
211
thirdparty/rapier.js/testbed3d/src/Testbed.ts
vendored
Normal file
211
thirdparty/rapier.js/testbed3d/src/Testbed.ts
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
import {Graphics} from "./Graphics";
|
||||
import {Gui} from "./Gui";
|
||||
import type {DebugInfos} from "./Gui";
|
||||
import {xxhash128} from "hash-wasm";
|
||||
import type * as RAPIER from "@dimforge/rapier3d";
|
||||
|
||||
type RAPIER_API = typeof import("@dimforge/rapier3d");
|
||||
|
||||
type Builders = Map<string, (RAPIER: RAPIER_API, testbed: Testbed) => void>;
|
||||
|
||||
class SimulationParameters {
|
||||
backend: string;
|
||||
prevBackend: string;
|
||||
demo: string;
|
||||
numSolverIters: number;
|
||||
running: boolean;
|
||||
stepping: boolean;
|
||||
debugInfos: boolean;
|
||||
debugRender: boolean;
|
||||
step: () => void;
|
||||
restart: () => void;
|
||||
takeSnapshot: () => void;
|
||||
restoreSnapshot: () => void;
|
||||
backends: Array<string>;
|
||||
builders: Builders;
|
||||
|
||||
constructor(backends: Array<string>, builders: Builders) {
|
||||
this.backend = "rapier";
|
||||
this.prevBackend = "rapier";
|
||||
this.demo = "collision groups";
|
||||
this.numSolverIters = 4;
|
||||
this.running = true;
|
||||
this.stepping = false;
|
||||
this.debugRender = false;
|
||||
this.step = () => {};
|
||||
this.restart = () => {};
|
||||
this.takeSnapshot = () => {};
|
||||
this.restoreSnapshot = () => {};
|
||||
this.backends = backends;
|
||||
this.builders = builders;
|
||||
this.debugInfos = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class Testbed {
|
||||
RAPIER: RAPIER_API;
|
||||
gui: Gui;
|
||||
graphics: Graphics;
|
||||
inhibitLookAt: boolean;
|
||||
parameters: SimulationParameters;
|
||||
demoToken: number;
|
||||
mouse: {x: number; y: number};
|
||||
events: RAPIER.EventQueue;
|
||||
world: RAPIER.World;
|
||||
preTimestepAction?: (gfx: Graphics) => void;
|
||||
stepId: number;
|
||||
prevDemo: string;
|
||||
lastMessageTime: number;
|
||||
snap: Uint8Array;
|
||||
snapStepId: number;
|
||||
|
||||
constructor(RAPIER: RAPIER_API, builders: Builders) {
|
||||
let backends = ["rapier"];
|
||||
this.RAPIER = RAPIER;
|
||||
let parameters = new SimulationParameters(backends, builders);
|
||||
this.gui = new Gui(this, parameters);
|
||||
this.graphics = new Graphics();
|
||||
this.inhibitLookAt = false;
|
||||
this.parameters = parameters;
|
||||
this.demoToken = 0;
|
||||
this.mouse = {x: 0, y: 0};
|
||||
this.events = new RAPIER.EventQueue(true);
|
||||
|
||||
this.switchToDemo(builders.keys().next().value);
|
||||
|
||||
window.addEventListener("mousemove", (event) => {
|
||||
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
this.mouse.y = 1 - (event.clientY / window.innerHeight) * 2;
|
||||
});
|
||||
}
|
||||
|
||||
setpreTimestepAction(action: (gfx: Graphics) => void) {
|
||||
this.preTimestepAction = action;
|
||||
}
|
||||
|
||||
setWorld(world: RAPIER.World) {
|
||||
document.onkeydown = null; // Reset key events.
|
||||
document.onkeyup = null; // Reset key events.
|
||||
|
||||
this.preTimestepAction = null;
|
||||
this.world = world;
|
||||
this.world.numSolverIterations = this.parameters.numSolverIters;
|
||||
this.demoToken += 1;
|
||||
this.stepId = 0;
|
||||
this.gui.resetTiming();
|
||||
|
||||
world.forEachCollider((coll) => {
|
||||
this.graphics.addCollider(this.RAPIER, world, coll);
|
||||
});
|
||||
|
||||
this.lastMessageTime = new Date().getTime();
|
||||
}
|
||||
|
||||
lookAt(pos: Parameters<Graphics["lookAt"]>[0]) {
|
||||
if (!this.inhibitLookAt) {
|
||||
this.graphics.lookAt(pos);
|
||||
}
|
||||
|
||||
this.inhibitLookAt = false;
|
||||
}
|
||||
|
||||
switchToDemo(demo: string) {
|
||||
if (demo == this.prevDemo) {
|
||||
this.inhibitLookAt = true;
|
||||
}
|
||||
|
||||
this.prevDemo = demo;
|
||||
this.graphics.reset();
|
||||
|
||||
this.parameters.prevBackend = this.parameters.backend;
|
||||
this.parameters.builders.get(demo)(this.RAPIER, this);
|
||||
}
|
||||
|
||||
switchToBackend(backend: string) {
|
||||
this.switchToDemo(this.parameters.demo);
|
||||
}
|
||||
|
||||
takeSnapshot() {
|
||||
this.snap = this.world.takeSnapshot();
|
||||
this.snapStepId = this.stepId;
|
||||
}
|
||||
|
||||
restoreSnapshot() {
|
||||
if (!!this.snap) {
|
||||
this.world.free();
|
||||
this.world = this.RAPIER.World.restoreSnapshot(this.snap);
|
||||
this.stepId = this.snapStepId;
|
||||
}
|
||||
}
|
||||
|
||||
run() {
|
||||
if (this.parameters.running || this.parameters.stepping) {
|
||||
this.world.numSolverIterations = this.parameters.numSolverIters;
|
||||
|
||||
if (!!this.preTimestepAction) {
|
||||
this.preTimestepAction(this.graphics);
|
||||
}
|
||||
|
||||
let t0 = new Date().getTime();
|
||||
this.world.step(this.events);
|
||||
this.gui.setTiming(new Date().getTime() - t0);
|
||||
this.stepId += 1;
|
||||
|
||||
if (!!this.parameters.debugInfos) {
|
||||
let t0 = performance.now();
|
||||
let snapshot = this.world.takeSnapshot();
|
||||
let t1 = performance.now();
|
||||
let snapshotTime = t1 - t0;
|
||||
|
||||
let debugInfos: DebugInfos = {
|
||||
token: this.demoToken,
|
||||
stepId: this.stepId,
|
||||
worldHash: "",
|
||||
worldHashTime: 0,
|
||||
snapshotTime: 0,
|
||||
timingStep: this.world.timingStep(),
|
||||
timingCollisionDetection:
|
||||
this.world.timingCollisionDetection(),
|
||||
timingBroadPhase: this.world.timingBroadPhase(),
|
||||
timingNarrowPhase: this.world.timingNarrowPhase(),
|
||||
timingSolver: this.world.timingSolver(),
|
||||
timingVelocityAssembly: this.world.timingVelocityAssembly(),
|
||||
timingVelocityResolution:
|
||||
this.world.timingVelocityResolution(),
|
||||
timingVelocityUpdate: this.world.timingVelocityUpdate(),
|
||||
timingVelocityWriteback:
|
||||
this.world.timingVelocityWriteback(),
|
||||
timingCcd: this.world.timingCcd(),
|
||||
timingCcdToiComputation:
|
||||
this.world.timingCcdToiComputation(),
|
||||
timingCcdBroadPhase: this.world.timingCcdBroadPhase(),
|
||||
timingCcdNarrowPhase: this.world.timingCcdNarrowPhase(),
|
||||
timingCcdSolver: this.world.timingCcdSolver(),
|
||||
timingIslandConstruction:
|
||||
this.world.timingIslandConstruction(),
|
||||
timingUserChanges: this.world.timingUserChanges(),
|
||||
};
|
||||
t0 = performance.now();
|
||||
xxhash128(snapshot).then((hash) => {
|
||||
debugInfos.worldHash = hash;
|
||||
t1 = performance.now();
|
||||
let worldHashTime = t1 - t0;
|
||||
debugInfos.worldHashTime = worldHashTime;
|
||||
debugInfos.snapshotTime = snapshotTime;
|
||||
this.gui.setDebugInfos(debugInfos);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.parameters.stepping) {
|
||||
this.parameters.running = false;
|
||||
this.parameters.stepping = false;
|
||||
}
|
||||
|
||||
this.gui.stats.begin();
|
||||
this.graphics.render(this.world, this.parameters.debugRender);
|
||||
this.gui.stats.end();
|
||||
|
||||
requestAnimationFrame(() => this.run());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user