Files
esengine/packages/rendering/mesh-3d/src/SkeletonComponent.ts
YHH 155411e743 refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages

## Package Structure Reorganization
- Reorganized 55 packages into categorized subdirectories:
  - packages/framework/ - Generic framework (Laya/Cocos compatible)
  - packages/engine/ - ESEngine core modules
  - packages/rendering/ - Rendering modules (WASM dependent)
  - packages/physics/ - Physics modules
  - packages/streaming/ - World streaming
  - packages/network-ext/ - Network extensions
  - packages/editor/ - Editor framework and plugins
  - packages/rust/ - Rust WASM engine
  - packages/tools/ - Build tools and SDK

## Framework Package Decoupling
- Decoupled behavior-tree and blueprint packages from ESEngine dependencies
- Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent)
- ESEngine-specific code moved to esengine/ subpath exports
- Framework packages now usable with Cocos/Laya without ESEngine

## CI Configuration
- Updated CI to only type-check and lint framework packages
- Added type-check:framework and lint:framework scripts

## Breaking Changes
- Package import paths changed due to directory reorganization
- ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine')

* fix: update es-engine file path after directory reorganization

* docs: update README to focus on framework over engine

* ci: only build framework packages, remove Rust/WASM dependencies

* fix: remove esengine subpath from behavior-tree and blueprint builds

ESEngine integration code will only be available in full engine builds.
Framework packages are now purely engine-agnostic.

* fix: move network-protocols to framework, build both in CI

* fix: update workflow paths from packages/core to packages/framework/core

* fix: exclude esengine folder from type-check in behavior-tree and blueprint

* fix: update network tsconfig references to new paths

* fix: add test:ci:framework to only test framework packages in CI

* fix: only build core and math npm packages in CI

* fix: exclude test files from CodeQL and fix string escaping security issue
2025-12-26 14:50:35 +08:00

280 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* SkeletonComponent - 3D skeleton data component for skinned meshes.
* SkeletonComponent - 用于蒙皮网格的 3D 骨骼数据组件。
*/
import { Component, ECSComponent, Serializable } from '@esengine/ecs-framework';
import type { ISkeletonData, ISkeletonJoint } from '@esengine/asset-system';
/**
* Local transform for a bone/joint.
* 骨骼/关节的局部变换。
*/
export interface BoneTransform {
/** Position XYZ. | 位置 XYZ。 */
position: [number, number, number];
/** Rotation quaternion XYZW. | 旋转四元数 XYZW。 */
rotation: [number, number, number, number];
/** Scale XYZ. | 缩放 XYZ。 */
scale: [number, number, number];
}
/**
* 3D Skeleton component for skeletal animation.
* 用于骨骼动画的 3D 骨骼组件。
*
* Requires MeshComponent for skeleton data source.
* 需要 MeshComponent 作为骨骼数据来源。
*/
@ECSComponent('Skeleton', { requires: ['Mesh', 'Animation3D'] })
@Serializable({ version: 1, typeId: 'Skeleton' })
export class SkeletonComponent extends Component {
// ===== Runtime Data | 运行时数据 =====
/**
* 骨骼数据(从 MeshAsset 加载)
* Skeleton data (loaded from MeshAsset)
*/
private _skeletonData: ISkeletonData | null = null;
/**
* 烘烤的骨骼矩阵(输出给渲染器)
* Baked bone matrices (output for renderer)
*
* Each matrix is a 4x4 column-major matrix (16 floats).
* 每个矩阵是 4x4 列优先矩阵16 个浮点数)。
*/
private _boneMatrices: Float32Array = new Float32Array(0);
/**
* 当前帧的骨骼局部变换
* Current frame's bone local transforms
*/
private _boneTransforms: BoneTransform[] = [];
/**
* 骨骼世界变换矩阵缓存
* Bone world transform matrix cache
*/
private _worldMatrices: Float32Array = new Float32Array(0);
/**
* 是否需要更新骨骼矩阵
* Whether bone matrices need update
*/
private _dirty: boolean = true;
// ===== Public Getters | 公共获取器 =====
/**
* 获取骨骼数据
* Get skeleton data
*/
public get skeletonData(): ISkeletonData | null {
return this._skeletonData;
}
/**
* 获取关节数量
* Get joint count
*/
public get jointCount(): number {
return this._skeletonData?.joints.length ?? 0;
}
/**
* 获取烘烤的骨骼矩阵
* Get baked bone matrices
*/
public get boneMatrices(): Float32Array {
return this._boneMatrices;
}
/**
* 获取骨骼局部变换
* Get bone local transforms
*/
public get boneTransforms(): readonly BoneTransform[] {
return this._boneTransforms;
}
/**
* 骨骼是否已加载
* Whether skeleton is loaded
*/
public get isLoaded(): boolean {
return this._skeletonData !== null && this._skeletonData.joints.length > 0;
}
/**
* 获取关节列表
* Get joint list
*/
public get joints(): readonly ISkeletonJoint[] {
return this._skeletonData?.joints ?? [];
}
// ===== Public Methods | 公共方法 =====
/**
* 设置骨骼数据(由系统调用)
* Set skeleton data (called by system)
*/
public setSkeletonData(data: ISkeletonData): void {
this._skeletonData = data;
const jointCount = data.joints.length;
// Initialize bone matrices (each joint has a 4x4 matrix = 16 floats)
// 初始化骨骼矩阵(每个关节有 4x4 矩阵 = 16 个浮点数)
this._boneMatrices = new Float32Array(jointCount * 16);
this._worldMatrices = new Float32Array(jointCount * 16);
// Initialize bone transforms with identity
// 用单位变换初始化骨骼变换
this._boneTransforms = [];
for (let i = 0; i < jointCount; i++) {
this._boneTransforms.push({
position: [0, 0, 0],
rotation: [0, 0, 0, 1], // Identity quaternion
scale: [1, 1, 1]
});
}
// Initialize bone matrices to identity
// 将骨骼矩阵初始化为单位矩阵
for (let i = 0; i < jointCount; i++) {
this.setIdentityMatrix(this._boneMatrices, i * 16);
this.setIdentityMatrix(this._worldMatrices, i * 16);
}
this._dirty = true;
}
/**
* 设置指定骨骼的局部变换
* Set local transform for a bone
*/
public setBoneTransform(jointIndex: number, transform: Partial<BoneTransform>): void {
if (jointIndex < 0 || jointIndex >= this._boneTransforms.length) {
return;
}
const bone = this._boneTransforms[jointIndex];
if (transform.position) {
bone.position = [...transform.position];
}
if (transform.rotation) {
bone.rotation = [...transform.rotation];
}
if (transform.scale) {
bone.scale = [...transform.scale];
}
this._dirty = true;
}
/**
* 标记骨骼矩阵需要更新
* Mark bone matrices as dirty
*/
public markDirty(): void {
this._dirty = true;
}
/**
* 检查是否需要更新
* Check if update is needed
*/
public isDirty(): boolean {
return this._dirty;
}
/**
* 清除脏标记(由系统在更新后调用)
* Clear dirty flag (called by system after update)
*/
public clearDirty(): void {
this._dirty = false;
}
/**
* 获取指定骨骼的世界矩阵
* Get world matrix for a bone
*/
public getWorldMatrix(jointIndex: number): Float32Array | null {
if (jointIndex < 0 || jointIndex >= this.jointCount) {
return null;
}
return this._worldMatrices.subarray(jointIndex * 16, (jointIndex + 1) * 16);
}
/**
* 设置指定骨骼的世界矩阵(由 SkeletonBakingSystem 调用)
* Set world matrix for a bone (called by SkeletonBakingSystem)
*/
public setWorldMatrix(jointIndex: number, matrix: Float32Array): void {
if (jointIndex < 0 || jointIndex >= this.jointCount) {
return;
}
const offset = jointIndex * 16;
for (let i = 0; i < 16; i++) {
this._worldMatrices[offset + i] = matrix[i];
}
}
/**
* 设置指定骨骼的最终矩阵(由 SkeletonBakingSystem 调用)
* Set final matrix for a bone (called by SkeletonBakingSystem)
*/
public setFinalMatrix(jointIndex: number, matrix: Float32Array): void {
if (jointIndex < 0 || jointIndex >= this.jointCount) {
return;
}
const offset = jointIndex * 16;
for (let i = 0; i < 16; i++) {
this._boneMatrices[offset + i] = matrix[i];
}
}
/**
* 按名称查找骨骼索引
* Find bone index by name
*/
public findBoneIndex(name: string): number {
if (!this._skeletonData) return -1;
for (let i = 0; i < this._skeletonData.joints.length; i++) {
if (this._skeletonData.joints[i].name === name) {
return i;
}
}
return -1;
}
/**
* 重置组件
* Reset component
*/
reset(): void {
this._skeletonData = null;
this._boneMatrices = new Float32Array(0);
this._worldMatrices = new Float32Array(0);
this._boneTransforms = [];
this._dirty = true;
}
// ===== Private Methods | 私有方法 =====
/**
* Set identity matrix at offset in array.
* 在数组的偏移位置设置单位矩阵。
*/
private setIdentityMatrix(arr: Float32Array, offset: number): void {
arr[offset] = 1; arr[offset + 1] = 0; arr[offset + 2] = 0; arr[offset + 3] = 0;
arr[offset + 4] = 0; arr[offset + 5] = 1; arr[offset + 6] = 0; arr[offset + 7] = 0;
arr[offset + 8] = 0; arr[offset + 9] = 0; arr[offset + 10] = 1; arr[offset + 11] = 0;
arr[offset + 12] = 0; arr[offset + 13] = 0; arr[offset + 14] = 0; arr[offset + 15] = 1;
}
}