@fengyizhi/scene-lib 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -379,6 +379,14 @@ interface EnvironmentConfig {
379
379
  ceilingUseBaseColor?: boolean;
380
380
  };
381
381
  }
382
+ /** 实例化组(InstancedMesh 优化) */
383
+ interface InstanceGroup {
384
+ id: string;
385
+ /** 资产 ID(同组的节点必须共享同一 assetId) */
386
+ assetId: string;
387
+ /** 参与实例化的节点 ID 列表 */
388
+ nodeIds: string[];
389
+ }
382
390
  interface SceneDoc {
383
391
  schemaVersion: 3;
384
392
  /** 场景 ID(服务器分配) */
@@ -396,6 +404,8 @@ interface SceneDoc {
396
404
  dashboard?: DashboardConfig;
397
405
  /** 告警配置 */
398
406
  alarm?: AlarmConfig;
407
+ /** 实例化组(性能优化) */
408
+ instanceGroups?: InstanceGroup[];
399
409
  }
400
410
  /** 场景元数据(列表用,不包含完整 SceneDoc) */
401
411
  interface SceneMeta {
@@ -403,6 +413,10 @@ interface SceneMeta {
403
413
  name: string;
404
414
  createdAt: number;
405
415
  updatedAt: number;
416
+ /** 创建者用户 ID */
417
+ ownerId: string;
418
+ /** 是否为模板场景 */
419
+ isTemplate?: boolean;
406
420
  }
407
421
  declare function nextId(prefix?: string): string;
408
422
  declare function createNode(primitive: PrimitiveKind, name: string): NodeDoc;
@@ -477,6 +491,18 @@ declare class AddNodeCommand implements Command {
477
491
  do(store: SceneStore): void;
478
492
  undo(store: SceneStore): void;
479
493
  }
494
+ /** 批量添加节点(用于复制操作,一次撤销全部) */
495
+ declare class BatchAddCommand implements Command {
496
+ readonly label: string;
497
+ private nodes;
498
+ private parentIds;
499
+ constructor(items: {
500
+ node: NodeDoc;
501
+ parentId: string | null;
502
+ }[]);
503
+ do(store: SceneStore): void;
504
+ undo(store: SceneStore): void;
505
+ }
480
506
  declare class RemoveNodeCommand implements Command {
481
507
  private readonly id;
482
508
  readonly label = "\u5220\u9664\u8282\u70B9";
@@ -503,6 +529,7 @@ declare class GroupCommand implements Command {
503
529
  private originalParents;
504
530
  private originalPositions;
505
531
  private removedEmptyGroups;
532
+ private commonParentId;
506
533
  constructor(selectedIds: string[]);
507
534
  do(store: SceneStore): void;
508
535
  undo(store: SceneStore): void;
@@ -514,6 +541,10 @@ declare class UngroupCommand implements Command {
514
541
  private children;
515
542
  private groupParentId;
516
543
  private groupName;
544
+ /** 记录 group 的变换(用于 undo 恢复) */
545
+ private groupTransform;
546
+ /** 记录子节点在 group 内的原始位置(用于 undo 恢复) */
547
+ private originalChildPositions;
517
548
  constructor(groupId: string);
518
549
  do(store: SceneStore): void;
519
550
  undo(store: SceneStore): void;
@@ -558,13 +589,17 @@ declare class SetAlarmCommand implements Command {
558
589
  do(store: SceneStore): void;
559
590
  undo(store: SceneStore): void;
560
591
  }
561
- /** 拖动节点改变父节点 */
592
+ /** 拖动节点改变父节点(保持世界坐标不变) */
562
593
  declare class ReparentCommand implements Command {
563
594
  private readonly nodeId;
564
595
  private readonly newParentId;
565
596
  readonly label = "\u79FB\u52A8\u8282\u70B9";
566
597
  private oldParentId;
567
598
  private oldIndex;
599
+ /** 记录旧位置(用于 undo 恢复) */
600
+ private oldPosition;
601
+ private oldRotation;
602
+ private oldScale;
568
603
  constructor(nodeId: string, newParentId: string | null);
569
604
  do(store: SceneStore): void;
570
605
  undo(store: SceneStore): void;
@@ -764,6 +799,36 @@ declare class MovePathPointToEndCommand implements Command {
764
799
  do(store: SceneStore): void;
765
800
  undo(store: SceneStore): void;
766
801
  }
802
+ /** 对齐命令:将多个节点对齐到指定轴向的指定位置 */
803
+ declare class AlignCommand implements Command {
804
+ private readonly nodeIds;
805
+ private readonly axis;
806
+ private readonly position;
807
+ private readonly targetPositions;
808
+ readonly label: string;
809
+ private beforePositions;
810
+ constructor(nodeIds: string[], axis: 'x' | 'y' | 'z', position: 'min' | 'center' | 'max', targetPositions: Map<string, [number, number, number]>);
811
+ do(store: SceneStore): void;
812
+ undo(store: SceneStore): void;
813
+ }
814
+ /** 启用实例化渲染 */
815
+ declare class EnableInstancingCommand implements Command {
816
+ private readonly nodeIds;
817
+ readonly label = "\u542F\u7528\u5B9E\u4F8B\u5316\u6E32\u67D3";
818
+ private groupId;
819
+ constructor(nodeIds: string[]);
820
+ do(store: SceneStore): void;
821
+ undo(store: SceneStore): void;
822
+ }
823
+ /** 禁用实例化渲染 */
824
+ declare class DisableInstancingCommand implements Command {
825
+ private readonly nodeIds;
826
+ readonly label = "\u7981\u7528\u5B9E\u4F8B\u5316\u6E32\u67D3";
827
+ private removedGroup;
828
+ constructor(nodeIds: string[]);
829
+ do(store: SceneStore): void;
830
+ undo(store: SceneStore): void;
831
+ }
767
832
 
768
833
  type TransformMode = 'translate' | 'rotate' | 'scale';
769
834
  interface PickEvent {
@@ -776,6 +841,8 @@ interface ViewerCallbacks {
776
841
  onTransformEnd(id: string, before: TransformState, after: TransformState): void;
777
842
  /** 资产加载完成后生成缩略图 */
778
843
  onThumbnailGenerated?(assetId: string, dataUrl: string): void;
844
+ /** 相机视角变化 */
845
+ onCameraChange?(): void;
779
846
  /** 路径点移动 */
780
847
  onPathPointMoved?(nodeId: string, effectIndex: number, pointIndex: number, newPosition: Vec3): void;
781
848
  /** 添加路径点 */
@@ -817,6 +884,12 @@ declare class SceneViewer {
817
884
  private pendingAssetNodes;
818
885
  /** 动画基础变换(nodeId → 数据模型原始变换) */
819
886
  private animBases;
887
+ /** FPS 统计 */
888
+ private fpsFrames;
889
+ private fpsTime;
890
+ private fpsCurrent;
891
+ /** 场景统计缓存 */
892
+ private statsCache;
820
893
  /** 缩略图渲染器(离屏) */
821
894
  private thumbRenderer?;
822
895
  private thumbScene;
@@ -827,6 +900,10 @@ declare class SceneViewer {
827
900
  private lastFrameTime;
828
901
  /** 拖拽中冻结动画 */
829
902
  private animationFrozen;
903
+ /** 性能模式:true 时使用 renderer(无后处理),false 时使用 composer(有后处理) */
904
+ private performanceMode;
905
+ /** 顶层节点集合(没有父节点的节点,用于视锥剔除优化) */
906
+ private topLevelNodes;
830
907
  /** 相机是否已从文档恢复(只在首次 sync 时恢复一次) */
831
908
  private cameraRestored;
832
909
  /** 保存原始材质参数(覆盖前),用于撤销覆盖时恢复 */
@@ -837,6 +914,8 @@ declare class SceneViewer {
837
914
  private textureCache;
838
915
  /** 标签对象缓存(nodeId → label mesh) */
839
916
  private labelObjects;
917
+ /** 实例化网格缓存(groupId → InstancedMesh 数组) */
918
+ private instanceMeshes;
840
919
  private textureLoader;
841
920
  /** HDRI 环境贴图缓存 */
842
921
  private hdriTexture;
@@ -908,6 +987,8 @@ declare class SceneViewer {
908
987
  private pathEditor;
909
988
  /** 是否为编辑器模式(编辑器模式下动画默认播放,不依赖数据) */
910
989
  private isEditorMode;
990
+ /** 相机视角变化回调 */
991
+ onCameraChange?: () => void;
911
992
  constructor(host: HTMLElement, store: SceneStore, cb: ViewerCallbacks, dashboardOverlayEl?: HTMLDivElement, isEditorMode?: boolean);
912
993
  setMode(mode: TransformMode): void;
913
994
  /** 设置路径编辑的活动效果 */
@@ -916,7 +997,7 @@ declare class SceneViewer {
916
997
  isPathEditing(): boolean;
917
998
  /** 设置路径编辑控制点大小 */
918
999
  setPathHandleSize(size: number): void;
919
- /** 将当前相机状态写入 SceneDoc(保存前调用) */
1000
+ /** 将当前相机状态写入 SceneDoc(保存前调用,不触发 notifyChanged) */
920
1001
  saveCamera(): void;
921
1002
  /** 从 SceneDoc 恢复相机位置 */
922
1003
  private restoreCamera;
@@ -926,6 +1007,14 @@ declare class SceneViewer {
926
1007
  sync(): void;
927
1008
  /** 强制恢复相机(草稿恢复后调用) */
928
1009
  forceRestoreCamera(): void;
1010
+ /** 同步实例化组:创建/更新 InstancedMesh */
1011
+ private syncInstanceGroups;
1012
+ /** 设置性能模式 */
1013
+ setPerformanceMode(enabled: boolean): void;
1014
+ /** 更新顶层节点集合(没有父节点的节点) */
1015
+ private updateTopLevelNodes;
1016
+ /** 视锥剔除:隐藏不在相机视锥内的顶层节点,大幅减少 CPU 遍历开销 */
1017
+ private updateFrustumCulling;
929
1018
  /** 创建标签 mesh */
930
1019
  private createLabel;
931
1020
  /** 应用场景环境配置(背景色、半球光、平行光、HDRI) */
@@ -1045,6 +1134,14 @@ declare class SceneViewer {
1045
1134
  private updateEffects;
1046
1135
  dispose(): void;
1047
1136
  private createMesh;
1137
+ /**
1138
+ * 参考 JCISceneParser 的 GdeepCloneMaterials
1139
+ * 几何体共享(不 clone),材质独立克隆
1140
+ * - Object3D/Group:clone(false) 不递归,手动处理 children
1141
+ * - Mesh:共享 geometry,克隆 material
1142
+ * - Light:完整克隆
1143
+ */
1144
+ private gdeepCloneMaterials;
1048
1145
  /** 判断对象是否为灯光 */
1049
1146
  private isLight;
1050
1147
  /** 根据节点数据创建灯光对象 */
@@ -1101,6 +1198,8 @@ declare class SceneViewer {
1101
1198
  private refreshSelection;
1102
1199
  /** 根据 Box3 创建包围盒的几何体 */
1103
1200
  private createBoxGeometry;
1201
+ /** 用局部坐标 min/max 创建包围盒几何体 */
1202
+ private createBoxGeometryFromMinMax;
1104
1203
  /** 计算 group 的所有后代的包围盒(本地空间,相对于组的 Object3D) */
1105
1204
  private computeGroupLocalBounds;
1106
1205
  /** 计算 group 的所有后代的包围盒(世界空间) */
@@ -1117,6 +1216,27 @@ declare class SceneViewer {
1117
1216
  private resize;
1118
1217
  /** 屏幕坐标 → 地面(y=0)世界坐标 */
1119
1218
  screenToGround(viewportX: number, viewportY: number): Vec3 | null;
1219
+ /** 获取当前 FPS */
1220
+ getFps(): number;
1221
+ /** 标记场景统计过期(模型加载/删除后调用) */
1222
+ markStatsDirty(): void;
1223
+ /** 获取场景顶点数和三角面数(带缓存,只统计根节点避免重复) */
1224
+ getSceneStats(): {
1225
+ vertices: number;
1226
+ faces: number;
1227
+ drawCalls: number;
1228
+ };
1229
+ /** 获取指定节点的顶点数、面数、DrawCalls */
1230
+ getNodeStats(nodeIds: string[]): {
1231
+ vertices: number;
1232
+ faces: number;
1233
+ drawCalls: number;
1234
+ };
1235
+ /** 获取节点的世界空间包围盒 */
1236
+ getObjectBoundingBox(nodeId: string): {
1237
+ min: [number, number, number];
1238
+ max: [number, number, number];
1239
+ } | null;
1120
1240
  }
1121
1241
 
1122
1242
  /**
@@ -1148,6 +1268,7 @@ declare class SceneViewComponent implements AfterViewInit, OnChanges, OnDestroy
1148
1268
  assetId: string;
1149
1269
  dataUrl: string;
1150
1270
  }>;
1271
+ cameraChange: EventEmitter<void>;
1151
1272
  pathPointMoved: EventEmitter<{
1152
1273
  nodeId: string;
1153
1274
  effectIndex: number;
@@ -1271,14 +1392,31 @@ declare class SceneViewComponent implements AfterViewInit, OnChanges, OnDestroy
1271
1392
  showAlarm(nodeId: string, data: any): void;
1272
1393
  /** 隐藏告警弹框 */
1273
1394
  hideAlarm(nodeId: string): void;
1395
+ /** 设置性能模式 */
1396
+ setPerformanceMode(enabled: boolean): void;
1274
1397
  /** 拖拽悬停:允许放置 */
1275
1398
  onDragOver(e: DragEvent): void;
1276
1399
  /** 拖拽放置:解析资产信息并发射事件 */
1277
1400
  onDrop(e: DragEvent): void;
1278
1401
  /** 获取 viewer 实例(供外部调用高级功能如告警弹窗) */
1279
1402
  getViewer(): SceneViewer | null;
1403
+ getFps(): number;
1404
+ getSceneStats(): {
1405
+ vertices: number;
1406
+ faces: number;
1407
+ drawCalls: number;
1408
+ };
1409
+ getNodeStats(nodeIds: string[]): {
1410
+ vertices: number;
1411
+ faces: number;
1412
+ drawCalls: number;
1413
+ };
1414
+ getObjectBoundingBox(nodeId: string): {
1415
+ min: [number, number, number];
1416
+ max: [number, number, number];
1417
+ } | null;
1280
1418
  static ɵfac: i0.ɵɵFactoryDeclaration<SceneViewComponent, never>;
1281
- static ɵcmp: i0.ɵɵComponentDeclaration<SceneViewComponent, "twin-scene-view", never, { "store": { "alias": "store"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "isEditorMode": { "alias": "isEditorMode"; "required": false; }; }, { "select": "select"; "transformEnd": "transformEnd"; "dropAsset": "dropAsset"; "thumbnailGenerated": "thumbnailGenerated"; "pathPointMoved": "pathPointMoved"; "addPathPoint": "addPathPoint"; "deletePathPoint": "deletePathPoint"; "pathEditingChanged": "pathEditingChanged"; "pathPointRightClick": "pathPointRightClick"; "segmentRightClick": "segmentRightClick"; "modelSurfaceClick": "modelSurfaceClick"; "movePathPointToEnd": "movePathPointToEnd"; }, never, never, true, never>;
1419
+ static ɵcmp: i0.ɵɵComponentDeclaration<SceneViewComponent, "twin-scene-view", never, { "store": { "alias": "store"; "required": false; }; "mode": { "alias": "mode"; "required": false; }; "isEditorMode": { "alias": "isEditorMode"; "required": false; }; }, { "select": "select"; "transformEnd": "transformEnd"; "dropAsset": "dropAsset"; "thumbnailGenerated": "thumbnailGenerated"; "cameraChange": "cameraChange"; "pathPointMoved": "pathPointMoved"; "addPathPoint": "addPathPoint"; "deletePathPoint": "deletePathPoint"; "pathEditingChanged": "pathEditingChanged"; "pathPointRightClick": "pathPointRightClick"; "segmentRightClick": "segmentRightClick"; "modelSurfaceClick": "modelSurfaceClick"; "movePathPointToEnd": "movePathPointToEnd"; }, never, never, true, never>;
1282
1420
  }
1283
1421
 
1284
1422
  /**
@@ -1531,5 +1669,5 @@ declare const DRACO_DECODER_PATH = "assets/draco/gltf/";
1531
1669
  /** Draco 编码器路径(打包进 npm 包) */
1532
1670
  declare const DRACO_ENCODER_PATH = "assets/draco/encoder/";
1533
1671
 
1534
- export { AddAnimationCommand, AddEffectCommand, AddNodeCommand, BatchRemoveCommand, DRACO_DECODER_PATH, DRACO_ENCODER_PATH, FlyPathCommand, GroupCommand, History, InsertPathPointCommand, KeyframeCommand, MaterialOverrideCommand, MovePathPointToEndCommand, ParticleRuntime, PathEditor, PathParticleRuntime, RemoveAnimationCommand, RemoveEffectCommand, RemoveNodeCommand, RemovePathPointCommand, ReparentCommand, SceneStore, SceneViewComponent, SceneViewer, SegmentedPath, SetAlarmCommand, SetDashboardCommand, SetEnvironmentCommand, SetLightPropertyCommand, SetNodeNameCommand, SetNodeTextCommand, SyncMaterialCommand, ToggleSegmentTypeCommand, TransformCommand, UngroupCommand, UpdateAnimationCommand, UpdateEffectCommand, UpdatePathPointsCommand, clone, createNode, createSceneDoc, nextId };
1535
- export type { AlarmConfig, AlarmPopupData, AnimationRule, CameraDoc, CameraKeyframe, Command, DashboardConfig, DashboardDataPoint, DashboardWidget, EnvironmentConfig, FlyPath, MaterialOverride, NodeDoc, ParticleEffect, PathEditorCallbacks, PathParticleEffect, PickEvent, PrimitiveKind, SceneDoc, SceneMeta, ScreenContentEffect, TransformMode, TransformState, UvAnimationEffect, Vec3, ViewerCallbacks, VisualEffect, VisualEffectBase, VisualEffectKind };
1672
+ export { AddAnimationCommand, AddEffectCommand, AddNodeCommand, AlignCommand, BatchAddCommand, BatchRemoveCommand, DRACO_DECODER_PATH, DRACO_ENCODER_PATH, DisableInstancingCommand, EnableInstancingCommand, FlyPathCommand, GroupCommand, History, InsertPathPointCommand, KeyframeCommand, MaterialOverrideCommand, MovePathPointToEndCommand, ParticleRuntime, PathEditor, PathParticleRuntime, RemoveAnimationCommand, RemoveEffectCommand, RemoveNodeCommand, RemovePathPointCommand, ReparentCommand, SceneStore, SceneViewComponent, SceneViewer, SegmentedPath, SetAlarmCommand, SetDashboardCommand, SetEnvironmentCommand, SetLightPropertyCommand, SetNodeNameCommand, SetNodeTextCommand, SyncMaterialCommand, ToggleSegmentTypeCommand, TransformCommand, UngroupCommand, UpdateAnimationCommand, UpdateEffectCommand, UpdatePathPointsCommand, clone, createNode, createSceneDoc, nextId };
1673
+ export type { AlarmConfig, AlarmPopupData, AnimationRule, CameraDoc, CameraKeyframe, Command, DashboardConfig, DashboardDataPoint, DashboardWidget, EnvironmentConfig, FlyPath, InstanceGroup, MaterialOverride, NodeDoc, ParticleEffect, PathEditorCallbacks, PathParticleEffect, PickEvent, PrimitiveKind, SceneDoc, SceneMeta, ScreenContentEffect, TransformMode, TransformState, UvAnimationEffect, Vec3, ViewerCallbacks, VisualEffect, VisualEffectBase, VisualEffectKind };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fengyizhi/scene-lib",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=16.0.0",
6
6
  "@angular/core": ">=16.0.0",