@ninkch/dsh-infinite-canvas 0.19.4 → 0.19.6
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/README.md +4 -4
- package/README.zh-CN.md +6 -6
- package/assets/canvas-director/SKILL.md +14 -14
- package/assets/canvas-director/forms/interview.md +1 -1
- package/assets/canvas-director/manifest.json +8 -8
- package/assets/canvas-director/pack.js +8 -12
- package/assets/canvas-studio/SKILL.md +2 -2
- package/assets/director/clips/dodge_back.json +1 -0
- package/assets/director/clips/idle_02.json +1 -0
- package/assets/director/clips/idle_03.json +1 -0
- package/assets/director/clips/jump.json +1 -0
- package/assets/director/clips/manifest.json +156 -0
- package/assets/director/clips/run.json +1 -0
- package/assets/director/clips/run_back.json +1 -0
- package/assets/director/clips/run_forward.json +1 -0
- package/assets/director/clips/run_left.json +1 -0
- package/assets/director/clips/run_right.json +1 -0
- package/assets/director/clips/run_stop.json +1 -0
- package/assets/director/clips/running_jump.json +1 -0
- package/assets/director/clips/sit_laugh.json +1 -0
- package/assets/director/clips/turn_left.json +1 -0
- package/assets/director/clips/turn_right.json +1 -0
- package/assets/director/clips/walk.json +1 -0
- package/assets/director/clips/walk_back.json +1 -0
- package/assets/director/clips/walk_forward.json +1 -0
- package/assets/director/clips/walk_left.json +1 -0
- package/assets/director/clips/walk_right.json +1 -0
- package/assets/director/engine.js +291 -65
- package/assets/director/models/explorer.json +1 -0
- package/assets/director/models/index.json +10 -0
- package/assets/director/models/soldier.json +1 -1
- package/assets/director/models/xbot.json +1 -1
- package/client.js +19727 -19577
- package/docs/guide.en.md +5 -5
- package/docs/guide.zh-CN.md +9 -9
- package/index-core-skill-profile.js +2 -6
- package/index.js +10 -25
- package/package.json +5 -1
- package/skill-pack.js +19 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const ENGINE_VERSION = '
|
|
1
|
+
export const ENGINE_VERSION = 'explorer-2'; // 改引擎必递增:client 靠它提示用户刷新页面 // 改引擎必递增:client 靠它提示用户刷新页面
|
|
2
2
|
/**
|
|
3
3
|
* 3D 导演台引擎(独立 ES 模块,零依赖)。
|
|
4
4
|
*
|
|
@@ -786,6 +786,195 @@ export const BONE_POSES = [
|
|
|
786
786
|
{ bone: 'LeftArm', to: [0.78, 0.22, 0.58] }, { bone: 'RightArm', to: [-0.80, 0.18, 0.56] },
|
|
787
787
|
{ bone: 'LeftForeArm', to: [0.62, 0.06, 0.78] }, { bone: 'RightForeArm', to: [-0.64, 0.02, 0.76] },
|
|
788
788
|
] },
|
|
789
|
+
// ── 导演姿势库(复刻 awplanet DIRECTOR_POSE_RECIPES,19 套)──
|
|
790
|
+
{ id: 'ap_stand_relaxed', zh: '站姿 · 放松', en: 'Stand relaxed', rootY: 0,
|
|
791
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
792
|
+
base: {
|
|
793
|
+
Spine: [-0.08, 0.02, -0.05],
|
|
794
|
+
LeftArm: [0.16, 0.02, 0.18],
|
|
795
|
+
RightArm: [0.12, -0.02, -0.16],
|
|
796
|
+
LeftUpLeg: [0.03, 0.04, 0.08],
|
|
797
|
+
RightUpLeg: [-0.02, -0.02, -0.05]
|
|
798
|
+
} },
|
|
799
|
+
{ id: 'ap_stand_contrapposto', zh: '站姿 · 重心对立', en: 'Stand contrapposto', rootY: 0,
|
|
800
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
801
|
+
base: {
|
|
802
|
+
Spine: [-0.05, 0.05, -0.16],
|
|
803
|
+
Spine1: [0.03, -0.04, 0.12],
|
|
804
|
+
Spine2: [0.03, -0.04, 0.12],
|
|
805
|
+
LeftUpLeg: [0.02, 0.1, 0.16],
|
|
806
|
+
RightUpLeg: [-0.04, -0.04, -0.11],
|
|
807
|
+
LeftArm: [0.08, -0.06, 0.34],
|
|
808
|
+
RightArm: [0.04, 0.04, -0.22]
|
|
809
|
+
} },
|
|
810
|
+
{ id: 'ap_stand_alert', zh: '站姿 · 警戒', en: 'Stand alert', rootY: 0,
|
|
811
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
812
|
+
base: {
|
|
813
|
+
Spine: [0.08, 0, 0],
|
|
814
|
+
Spine1: [0.08, 0, 0],
|
|
815
|
+
Neck: [0.06, 0, 0],
|
|
816
|
+
LeftArm: [-0.08, 0.02, 0.08],
|
|
817
|
+
RightArm: [-0.08, -0.02, -0.08],
|
|
818
|
+
LeftUpLeg: [-0.03, 0, 0.04],
|
|
819
|
+
RightUpLeg: [-0.03, 0, -0.04]
|
|
820
|
+
} },
|
|
821
|
+
{ id: 'ap_stand_lean', zh: '站姿 · 倚靠', en: 'Stand lean', rootY: 0,
|
|
822
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
823
|
+
base: {
|
|
824
|
+
Spine: [-0.16, 0.08, -0.2],
|
|
825
|
+
Spine1: [0.05, -0.04, 0.13],
|
|
826
|
+
Spine2: [0.05, -0.04, 0.13],
|
|
827
|
+
LeftUpLeg: [0.1, 0.05, 0.1],
|
|
828
|
+
LeftArm: [0.22, -0.1, 0.38],
|
|
829
|
+
RightArm: [0.05, 0.08, -0.18]
|
|
830
|
+
} },
|
|
831
|
+
{ id: 'ap_crossed_arms', zh: '站姿 · 抱臂', en: 'Crossed arms', rootY: 0,
|
|
832
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
833
|
+
base: {
|
|
834
|
+
Spine: [-0.04, 0, 0.02],
|
|
835
|
+
LeftArm: [0.72, 0.18, 0.78],
|
|
836
|
+
RightArm: [0.72, -0.18, -0.78],
|
|
837
|
+
LeftForeArm: [0.34, -0.1, -1.18],
|
|
838
|
+
RightForeArm: [0.34, 0.1, 1.18]
|
|
839
|
+
} },
|
|
840
|
+
{ id: 'ap_hands_hips', zh: '站姿 · 叉腰', en: 'Hands on hips', rootY: 0,
|
|
841
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
842
|
+
base: {
|
|
843
|
+
Spine: [0.02, 0, -0.04],
|
|
844
|
+
LeftArm: [0.48, -0.1, 0.68],
|
|
845
|
+
RightArm: [0.48, 0.1, -0.68],
|
|
846
|
+
LeftForeArm: [0.16, -0.2, -0.92],
|
|
847
|
+
RightForeArm: [0.16, 0.2, 0.92]
|
|
848
|
+
} },
|
|
849
|
+
{ id: 'ap_sit_low', zh: '坐 · 低坐', en: 'Sit low', rootY: -0.52,
|
|
850
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
851
|
+
base: {
|
|
852
|
+
Spine: [-0.12, 0, 0],
|
|
853
|
+
LeftUpLeg: [-1.18, 0.06, 0.12],
|
|
854
|
+
RightUpLeg: [-1.18, -0.06, -0.12],
|
|
855
|
+
LeftLeg: [1.18, 0, 0],
|
|
856
|
+
RightLeg: [1.18, 0, 0],
|
|
857
|
+
LeftArm: [0.32, 0.04, 0.18],
|
|
858
|
+
RightArm: [0.32, -0.04, -0.18]
|
|
859
|
+
} },
|
|
860
|
+
{ id: 'ap_sit_side', zh: '坐 · 侧坐', en: 'Sit side', rootY: -0.52,
|
|
861
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
862
|
+
base: {
|
|
863
|
+
Spine: [-0.08, 0.14, -0.1],
|
|
864
|
+
LeftUpLeg: [-1, 0.2, 0.46],
|
|
865
|
+
RightUpLeg: [-1.18, -0.26, 0.18],
|
|
866
|
+
LeftLeg: [1.02, 0, -0.12],
|
|
867
|
+
RightLeg: [1.08, 0, 0.18]
|
|
868
|
+
} },
|
|
869
|
+
{ id: 'ap_crouch', zh: '蹲 · 预备', en: 'Crouch ready', rootY: -0.28,
|
|
870
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
871
|
+
base: {
|
|
872
|
+
Spine: [-0.32, 0, 0],
|
|
873
|
+
LeftUpLeg: [-0.92, 0.04, 0.12],
|
|
874
|
+
RightUpLeg: [-0.92, -0.04, -0.12],
|
|
875
|
+
LeftLeg: [1.32, 0, 0],
|
|
876
|
+
RightLeg: [1.32, 0, 0],
|
|
877
|
+
LeftArm: [0.55, -0.08, 0.25],
|
|
878
|
+
RightArm: [0.55, 0.08, -0.25]
|
|
879
|
+
} },
|
|
880
|
+
{ id: 'ap_kneel', zh: '跪 · 单膝', en: 'Kneel one knee', rootY: -0.42,
|
|
881
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
882
|
+
base: {
|
|
883
|
+
Spine: [-0.18, 0, 0],
|
|
884
|
+
LeftUpLeg: [-1.22, 0.06, 0.12],
|
|
885
|
+
LeftLeg: [1.52, 0, 0],
|
|
886
|
+
RightUpLeg: [-0.35, -0.08, -0.12],
|
|
887
|
+
RightLeg: [0.72, 0, 0],
|
|
888
|
+
LeftArm: [0.35, -0.05, 0.18],
|
|
889
|
+
RightArm: [0.35, 0.05, -0.18]
|
|
890
|
+
} },
|
|
891
|
+
{ id: 'ap_kneel_both', zh: '跪 · 双膝', en: 'Kneel both knees', rootY: -0.55,
|
|
892
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
893
|
+
base: {
|
|
894
|
+
Spine: [-0.1, 0, 0],
|
|
895
|
+
LeftUpLeg: [-1.28, 0.08, 0.12],
|
|
896
|
+
RightUpLeg: [-1.28, -0.08, -0.12],
|
|
897
|
+
LeftLeg: [1.48, 0, 0],
|
|
898
|
+
RightLeg: [1.48, 0, 0],
|
|
899
|
+
LeftArm: [0.22, -0.04, 0.14],
|
|
900
|
+
RightArm: [0.22, 0.04, -0.14]
|
|
901
|
+
} },
|
|
902
|
+
{ id: 'ap_lie_prone', zh: '卧 · 俯卧', en: 'Lie prone', rootY: -0.82,
|
|
903
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
904
|
+
base: {
|
|
905
|
+
Spine: [-1.32, 0, 0],
|
|
906
|
+
Spine1: [0.1, 0, 0],
|
|
907
|
+
Spine2: [0.1, 0, 0],
|
|
908
|
+
LeftUpLeg: [0.12, 0.04, 0.1],
|
|
909
|
+
RightUpLeg: [0.12, -0.04, -0.1],
|
|
910
|
+
LeftArm: [0.92, -0.1, 0.38],
|
|
911
|
+
RightArm: [0.92, 0.1, -0.38]
|
|
912
|
+
} },
|
|
913
|
+
{ id: 'ap_lie_back', zh: '卧 · 仰卧', en: 'Lie back', rootY: -0.82,
|
|
914
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
915
|
+
base: {
|
|
916
|
+
Spine: [1.28, 0, 0],
|
|
917
|
+
LeftUpLeg: [-0.16, 0.08, 0.12],
|
|
918
|
+
RightUpLeg: [-0.16, -0.08, -0.12],
|
|
919
|
+
LeftLeg: [0.42, 0, 0],
|
|
920
|
+
RightLeg: [0.42, 0, 0],
|
|
921
|
+
LeftArm: [0.7, -0.2, 0.62],
|
|
922
|
+
RightArm: [0.7, 0.2, -0.62]
|
|
923
|
+
} },
|
|
924
|
+
{ id: 'ap_point_forward', zh: '指向前方', en: 'Point forward', rootY: 0,
|
|
925
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
926
|
+
base: {
|
|
927
|
+
Spine: [0.03, -0.1, 0],
|
|
928
|
+
RightArm: [-0.12, -0.52, -1.18],
|
|
929
|
+
RightForeArm: [-0.08, -0.12, -0.28],
|
|
930
|
+
LeftArm: [0.18, 0.06, 0.18]
|
|
931
|
+
} },
|
|
932
|
+
{ id: 'ap_reach_up', zh: '举手够物', en: 'Reach up', rootY: 0,
|
|
933
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
934
|
+
base: {
|
|
935
|
+
Spine: [0.08, 0, 0.06],
|
|
936
|
+
RightArm: [-1.42, -0.14, -0.36],
|
|
937
|
+
RightForeArm: [-0.18, 0.06, -0.08],
|
|
938
|
+
LeftArm: [0.2, 0.02, 0.22]
|
|
939
|
+
} },
|
|
940
|
+
{ id: 'ap_guard_ready', zh: '戒备势', en: 'Guard ready', rootY: 0,
|
|
941
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
942
|
+
base: {
|
|
943
|
+
Spine: [-0.16, 0, 0],
|
|
944
|
+
LeftArm: [0.76, -0.08, 0.42],
|
|
945
|
+
RightArm: [0.76, 0.08, -0.42],
|
|
946
|
+
LeftForeArm: [0.42, -0.18, -0.76],
|
|
947
|
+
RightForeArm: [0.42, 0.18, 0.76],
|
|
948
|
+
LeftUpLeg: [-0.12, 0.06, 0.1],
|
|
949
|
+
RightUpLeg: [-0.12, -0.06, -0.1]
|
|
950
|
+
} },
|
|
951
|
+
{ id: 'ap_surrender', zh: '投降势', en: 'Surrender', rootY: 0,
|
|
952
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
953
|
+
base: {
|
|
954
|
+
Spine: [0.04, 0, 0],
|
|
955
|
+
LeftArm: [-1.28, 0.1, 0.46],
|
|
956
|
+
RightArm: [-1.28, -0.1, -0.46],
|
|
957
|
+
LeftForeArm: [-0.24, 0.05, -0.22],
|
|
958
|
+
RightForeArm: [-0.24, -0.05, 0.22]
|
|
959
|
+
} },
|
|
960
|
+
{ id: 'ap_hands_head', zh: '双手抱头', en: 'Hands on head', rootY: 0,
|
|
961
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
962
|
+
base: {
|
|
963
|
+
Spine: [-0.08, 0, 0],
|
|
964
|
+
LeftArm: [-0.84, 0.18, 0.82],
|
|
965
|
+
RightArm: [-0.84, -0.18, -0.82],
|
|
966
|
+
LeftForeArm: [-0.54, 0, -1.12],
|
|
967
|
+
RightForeArm: [-0.54, 0, 1.12]
|
|
968
|
+
} },
|
|
969
|
+
{ id: 'ap_talk_open', zh: '说话 · 张开', en: 'Talk open', rootY: 0,
|
|
970
|
+
// 来源:awplanet DIRECTOR_POSE_RECIPES(加法欧拉)→ 本表 base 四元数(同语义)
|
|
971
|
+
base: {
|
|
972
|
+
Spine: [0.02, 0.06, -0.04],
|
|
973
|
+
LeftArm: [0.1, -0.16, 0.58],
|
|
974
|
+
RightArm: [0.1, 0.16, -0.58],
|
|
975
|
+
LeftForeArm: [0.34, -0.12, -0.34],
|
|
976
|
+
RightForeArm: [0.34, 0.12, 0.34]
|
|
977
|
+
} },
|
|
789
978
|
];
|
|
790
979
|
|
|
791
980
|
|
|
@@ -1434,18 +1623,18 @@ export function decodeActionClips(list) {
|
|
|
1434
1623
|
*/
|
|
1435
1624
|
// waist:腰部横向系数(男性 >1 直腰方厚,女性 <1 收腰);chestFlat:男性压扁胸部正面
|
|
1436
1625
|
const BODY_SHAPE_OF = {
|
|
1437
|
-
m_standard: { bust: 0, belly: 0.00, muscle: 0.
|
|
1438
|
-
f_standard: { bust: 1.00, belly: 0.04, muscle: 0.00, shoulder: 0.94, hip: 1.10, waist: 0.88, chestFlat: 0 },
|
|
1439
|
-
m_teen: { bust: 0, belly: 0.02, muscle: 0.
|
|
1440
|
-
f_teen: { bust: 0.62, belly: 0.03, muscle: 0.00, shoulder: 0.90, hip: 1.02, waist: 0.90, chestFlat: 0 },
|
|
1441
|
-
m_child: { bust: 0, belly: 0.06, muscle: 0.
|
|
1442
|
-
f_child: { bust: 0.18, belly: 0.06, muscle: 0.00, shoulder: 0.82, hip: 0.90, waist: 0.94, chestFlat: 0 },
|
|
1443
|
-
m_slim: { bust: 0, belly: -0.02, muscle: 0.
|
|
1444
|
-
f_slim: { bust: 0.78, belly: -0.03, muscle: 0.00, shoulder: 0.88, hip: 0.98, waist: 0.84, chestFlat: 0 },
|
|
1445
|
-
m_muscular: { bust: 0, belly: -0.02, muscle: 0.
|
|
1446
|
-
f_muscular: { bust: 0.90, belly: -0.02, muscle: 0.38, shoulder: 1.06, hip: 1.10, waist: 0.92, chestFlat: 0 },
|
|
1447
|
-
m_plump: { bust: 0, belly: 0.
|
|
1448
|
-
f_plump: { bust: 1.15, belly: 0.32, muscle: 0.02, shoulder: 1.02, hip: 1.24, waist: 1.00, chestFlat: 0 },
|
|
1626
|
+
m_standard: { bust: 0, belly: 0.00, muscle: 0.50, shoulder: 1.14, hip: 0.96, waist: 1.24, chestFlat: 0.10, head: 1.00 },
|
|
1627
|
+
f_standard: { bust: 1.00, belly: 0.04, muscle: 0.00, shoulder: 0.94, hip: 1.10, waist: 0.88, chestFlat: 0, head: 1.00 },
|
|
1628
|
+
m_teen: { bust: 0, belly: 0.02, muscle: 0.30, shoulder: 1.00, hip: 0.98, waist: 1.12, chestFlat: 0.08, head: 1.08 },
|
|
1629
|
+
f_teen: { bust: 0.62, belly: 0.03, muscle: 0.00, shoulder: 0.90, hip: 1.02, waist: 0.90, chestFlat: 0, head: 1.00 },
|
|
1630
|
+
m_child: { bust: 0, belly: 0.06, muscle: 0.10, shoulder: 0.86, hip: 0.88, waist: 1.06, chestFlat: 0.04, head: 1.18 },
|
|
1631
|
+
f_child: { bust: 0.18, belly: 0.06, muscle: 0.00, shoulder: 0.82, hip: 0.90, waist: 0.94, chestFlat: 0, head: 1.00 },
|
|
1632
|
+
m_slim: { bust: 0, belly: -0.02, muscle: 0.25, shoulder: 1.04, hip: 0.96, waist: 1.10, chestFlat: 0.10, head: 1.00 },
|
|
1633
|
+
f_slim: { bust: 0.78, belly: -0.03, muscle: 0.00, shoulder: 0.88, hip: 0.98, waist: 0.84, chestFlat: 0, head: 1.00 },
|
|
1634
|
+
m_muscular: { bust: 0, belly: -0.02, muscle: 0.85, shoulder: 1.30, hip: 1.08, waist: 1.20, chestFlat: 0.12, head: 0.98 },
|
|
1635
|
+
f_muscular: { bust: 0.90, belly: -0.02, muscle: 0.38, shoulder: 1.06, hip: 1.10, waist: 0.92, chestFlat: 0, head: 1.00 },
|
|
1636
|
+
m_plump: { bust: 0, belly: 0.55, muscle: 0.18, shoulder: 1.16, hip: 1.24, waist: 1.30, chestFlat: 0.02, head: 1.00 },
|
|
1637
|
+
f_plump: { bust: 1.15, belly: 0.32, muscle: 0.02, shoulder: 1.02, hip: 1.24, waist: 1.00, chestFlat: 0, head: 1.00 },
|
|
1449
1638
|
};
|
|
1450
1639
|
export function bodyShapeOf(typeId) { return BODY_SHAPE_OF[typeId] || BODY_SHAPE_OF.m_standard; }
|
|
1451
1640
|
|
|
@@ -1455,7 +1644,18 @@ function applyBodyShape(asset, pos, shape) {
|
|
|
1455
1644
|
const bones = asset.bones;
|
|
1456
1645
|
const J = asset.joints, W = asset.weights;
|
|
1457
1646
|
const nv = pos.length / 3;
|
|
1458
|
-
const { bust = 0, belly = 0, muscle = 0, shoulder = 1, hip = 1, waist = 1, chestFlat = 0 } = shape;
|
|
1647
|
+
const { bust = 0, belly = 0, muscle = 0, shoulder = 1, hip = 1, waist = 1, chestFlat = 0, head = 1 } = shape;
|
|
1648
|
+
// 形变带按「相对身高」计算,不再用绝对米:xbot 是米制(rawHeight≈1.8),
|
|
1649
|
+
// soldier≈44、explorer≈194 都是厘米制——绝对带在它们身上会错位到脚踝(形变静默失效)。
|
|
1650
|
+
// k = 该资产身高 / 基准身高 1.809(这些带最初按 xbot 调的)。
|
|
1651
|
+
let Hm = asset._shapeH;
|
|
1652
|
+
if (!Hm) {
|
|
1653
|
+
let mn = Infinity, mx = -Infinity;
|
|
1654
|
+
for (let i = 1; i < pos.length; i += 3) { const y = pos[i]; if (y < mn) mn = y; if (y > mx) mx = y; }
|
|
1655
|
+
Hm = (Number.isFinite(mn) && mx > mn) ? (mx - mn) : 1.8;
|
|
1656
|
+
asset._shapeH = Hm;
|
|
1657
|
+
}
|
|
1658
|
+
const k = Hm / 1.809; // 长度缩放(y 带中心/宽度、横向高斯宽度都用它)
|
|
1459
1659
|
for (let v = 0; v < nv; v++) {
|
|
1460
1660
|
// 主导骨骼
|
|
1461
1661
|
let bi = -1, bw = 0;
|
|
@@ -1468,35 +1668,37 @@ function applyBodyShape(asset, pos, shape) {
|
|
|
1468
1668
|
const isArm = /arm|hand|forearm|shoulder|clav/.test(nm);
|
|
1469
1669
|
const isLeg = /thigh|leg|shin|foot/.test(nm);
|
|
1470
1670
|
// 腰部横向:男性直腰(>1),女性收腰(<1)——这是男女轮廓最关键的区别
|
|
1471
|
-
if (isTorso && /spine1|spine|hips/.test(nm) && y > 0.
|
|
1472
|
-
const gw = Math.exp(-Math.pow((y -
|
|
1671
|
+
if (isTorso && /spine1|spine|hips/.test(nm) && y > 0.47 * Hm && y < 0.675 * Hm) {
|
|
1672
|
+
const gw = Math.exp(-Math.pow((y - 0.5694 * Hm) / (0.0774 * Hm), 2));
|
|
1473
1673
|
nx *= 1 + (waist - 1) * gw;
|
|
1474
1674
|
}
|
|
1475
1675
|
// 男性胸部正面压扁(女性不压)
|
|
1476
|
-
if (chestFlat > 0 && isTorso && /chest|spine2/.test(nm) && y >
|
|
1676
|
+
if (chestFlat > 0 && isTorso && /chest|spine2/.test(nm) && y > 0.663 * Hm && z > 0) {
|
|
1477
1677
|
nz *= 1 - chestFlat;
|
|
1478
1678
|
}
|
|
1479
1679
|
// 肩胯宽度
|
|
1480
1680
|
if (isTorso && /chest|spine2|spine1|spine|clav/.test(nm)) nx *= shoulder;
|
|
1481
1681
|
if (isTorso && /hips/.test(nm)) { nx *= hip; nz *= hip * 0.98; }
|
|
1482
1682
|
// 腹部体积:腹部(y 在中段)向前鼓
|
|
1483
|
-
if (belly > 0 && isTorso && y > 0.
|
|
1484
|
-
const g = Math.exp(-Math.pow((y -
|
|
1485
|
-
nz += belly * 0.085 * g * Math.exp(-Math.pow(x / 0.19, 2));
|
|
1683
|
+
if (belly > 0 && isTorso && y > 0.4146 * Hm && y < 0.8016 * Hm) {
|
|
1684
|
+
const g = Math.exp(-Math.pow((y - 0.597 * Hm) / (0.1216 * Hm), 2));
|
|
1685
|
+
nz += belly * 0.085 * k * g * Math.exp(-Math.pow(x / (0.19 * k), 2));
|
|
1486
1686
|
nx *= 1 + belly * 0.10 * g;
|
|
1487
1687
|
}
|
|
1488
1688
|
if (belly < 0 && isTorso) { nx *= 1 + belly * 0.25; nz *= 1 + belly * 0.25; }
|
|
1489
1689
|
// 胸部体积(女性):胸前两侧隆起
|
|
1490
|
-
if (bust > 0 && isTorso && y >
|
|
1491
|
-
const gg = Math.exp(-Math.pow((y -
|
|
1492
|
-
const lobe = Math.exp(-Math.pow((Math.abs(x) - 0.075) / 0.055, 2));
|
|
1493
|
-
nz += bust * 0.075 * gg * lobe;
|
|
1690
|
+
if (bust > 0 && isTorso && y > 0.630 * Hm && y < 0.840 * Hm && z > 0) {
|
|
1691
|
+
const gg = Math.exp(-Math.pow((y - 0.735 * Hm) / (0.0553 * Hm), 2));
|
|
1692
|
+
const lobe = Math.exp(-Math.pow((Math.abs(x) - 0.075 * k) / (0.055 * k), 2));
|
|
1693
|
+
nz += bust * 0.075 * k * gg * lobe;
|
|
1494
1694
|
}
|
|
1495
|
-
//
|
|
1695
|
+
// 肌肉围度:四肢加粗(乘数 0.55 对齐 storyai 的 15-27% 单部位幅度)
|
|
1496
1696
|
if (muscle > 0 && (isArm || isLeg)) {
|
|
1497
|
-
const k = 1 + muscle * (/forearm|hand|shin|foot/.test(nm) ? 0.
|
|
1697
|
+
const k = 1 + muscle * (/forearm|hand|shin|foot/.test(nm) ? 0.16 : 0.55);
|
|
1498
1698
|
nx *= k; nz *= k;
|
|
1499
1699
|
}
|
|
1700
|
+
// 头部缩放不在这里做:顶点级缩放会把头拉长(骨骼没跟上去 → 视觉「飞头」)。
|
|
1701
|
+
// 改由 skinModel 在蒙皮前对 Head/Neck 骨骼矩阵缩放(applyHeadBoneScale)。
|
|
1500
1702
|
if (nx !== x || ny !== y || nz !== z) { pos[v * 3] = nx; pos[v * 3 + 1] = ny; pos[v * 3 + 2] = nz; }
|
|
1501
1703
|
}
|
|
1502
1704
|
}
|
|
@@ -2364,13 +2566,35 @@ export function skinModel(asset, clipName, time, outPos, outNrm, shape) {
|
|
|
2364
2566
|
nrm[v * 3] = nx / l; nrm[v * 3 + 1] = ny / l; nrm[v * 3 + 2] = nz / l;
|
|
2365
2567
|
}
|
|
2366
2568
|
if (shape) applyBodyShape(asset, pos, shape);
|
|
2569
|
+
// 头部放大:顶点级按高度 smoothstep 渐变(颈根 1× → 头顶 k×)。
|
|
2570
|
+
// 骨骼级缩放会把颈部顶点推离、在 0.84–0.86H 留空档 → 头身分离(用户实测)。
|
|
2571
|
+
if (shape && Number(shape.head) && Number(shape.head) !== 1) headVertexScale(pos, Number(shape.head));
|
|
2367
2572
|
return { pos, nrm };
|
|
2368
2573
|
}
|
|
2369
2574
|
|
|
2575
|
+
/** 头部顶点级缩放:以身高 78%(颈根)为枢轴,向头顶 smoothstep 渐变放大。
|
|
2576
|
+
* 保证颈→头网格连续:颈根不动、越靠头顶放大越多,不产生顶点空档(骨骼级缩放会断开)。 */
|
|
2577
|
+
function headVertexScale(pos, k) {
|
|
2578
|
+
if (!Number.isFinite(k) || k === 1 || !pos || !pos.length) return;
|
|
2579
|
+
const nv = pos.length / 3;
|
|
2580
|
+
let maxY = -Infinity;
|
|
2581
|
+
for (let v = 0; v < nv; v++) { const y = pos[v * 3 + 1]; if (y > maxY) maxY = y; }
|
|
2582
|
+
if (!Number.isFinite(maxY) || maxY <= 0) return;
|
|
2583
|
+
const pivot = maxY * 0.78;
|
|
2584
|
+
const span = Math.max(1e-6, maxY - pivot);
|
|
2585
|
+
for (let v = 0; v < nv; v++) {
|
|
2586
|
+
const y = pos[v * 3 + 1];
|
|
2587
|
+
if (y <= pivot) continue;
|
|
2588
|
+
const t2 = Math.min(1, (y - pivot) / span);
|
|
2589
|
+
const w = t2 * t2 * (3 - 2 * t2);
|
|
2590
|
+
const kk = 1 + (k - 1) * w;
|
|
2591
|
+
pos[v * 3] *= kk;
|
|
2592
|
+
pos[v * 3 + 1] = pivot + (y - pivot) * kk;
|
|
2593
|
+
pos[v * 3 + 2] *= kk;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2370
2597
|
/**
|
|
2371
|
-
* 播放头 → 场景副本:模型对象的动作时间跟随播放头,带关键帧的相机做位置/注视点插值。
|
|
2372
|
-
* 预演与导出视频都走这一个函数,保证"看到的"和"导出的"一致。
|
|
2373
|
-
*/
|
|
2374
2598
|
/* ============================================================
|
|
2375
2599
|
* 运动路径(对照 Blender BKE_where_on_path + Follow Path 约束)
|
|
2376
2600
|
* 关键帧串成 Catmull-Rom 样条 → 按时间参数在曲线上取「位置 + 切线」
|
|
@@ -3226,15 +3450,17 @@ export function applyPlayhead(scene, t) {
|
|
|
3226
3450
|
const bl = poseBlendAt(BONE_POSES, o.poseAnim, poseSampleTime);
|
|
3227
3451
|
if (bl) { next.poseBlend = bl; next.poseId = null; }
|
|
3228
3452
|
}
|
|
3229
|
-
//
|
|
3453
|
+
// 姿态根位移(下蹲/轻功/倒地需要整体升降):**只在骨骼层生效**(见 applyBonePoseToLocal
|
|
3454
|
+
// 的 ①b2 段:把 Hips 整体上下平移)。这里绝不能再往 pos.y 里加一次——
|
|
3455
|
+
// 那是重复应用,而且会把物体的逻辑原点(人物脚底)污染成"髋部高度",
|
|
3456
|
+
// 导致 gizmo 轴心悬空、路径/关键帧的 y 基准也跟着漂。
|
|
3457
|
+
// (历史 bug:这一行曾把 rootY 加到 next.pos.y 上,站立时 pos.y≈1.04,轴心飘到腰腹。)
|
|
3458
|
+
// 姿态中心偏移(**仅供需要「跟随身体」的场合**,例如要让 gizmo 贴住倒地的人):
|
|
3459
|
+
// 单独存成 poseCenterY,绝不并进 pos.y —— pos.y 永远是物体原点。
|
|
3230
3460
|
{
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
const bl = poseBlendAt(BONE_POSES, o.poseAnim, poseSampleTime);
|
|
3235
|
-
if (bl) { let acc = 0, w = 0; for (const e of bl) { acc += (Number(e.rootY) || 0) * e.w; w += e.w; } ry = w > 0 ? acc / w : 0; }
|
|
3236
|
-
}
|
|
3237
|
-
if (ry) next.pos = v3(next.pos.x, next.pos.y + ry, next.pos.z);
|
|
3461
|
+
// 姿态根位移只用于**渲染骨骼**(applyBonePoseToLocal 负责),此处不再写 pos.y。
|
|
3462
|
+
// 保留一个显式的空块形式以免误读:确需场景级整体升降时,请改 sh 的 ground 或加关键帧,
|
|
3463
|
+
// 不要把姿态位移混进物体原点。
|
|
3238
3464
|
}
|
|
3239
3465
|
// 姿态中心偏移(供 gizmo / 拾取跟随身体,而非留在地面原点)
|
|
3240
3466
|
if (next.kind === 'model' && gltfAssetOf(next.assetId)) {
|
|
@@ -3368,19 +3594,26 @@ export function resetView(scene) {
|
|
|
3368
3594
|
* 不必真的重建 12 套网格(那样包体积会爆炸)。
|
|
3369
3595
|
*/
|
|
3370
3596
|
export const MANNEQUIN_TYPES = [
|
|
3371
|
-
{ id: 'm_standard', zh: '男性 · 标准', en: 'Male standard', h: 1.78, w: 1.00, d: 1.00 , color: '#
|
|
3372
|
-
{ id: 'f_standard', zh: '女性 · 标准', en: 'Female standard', h: 1.66, w: 0.92, d: 0.94 , color: '#
|
|
3373
|
-
{ id: 'm_teen', zh: '男性 · 少年', en: 'Male teen', h: 1.
|
|
3374
|
-
{ id: 'f_teen', zh: '女性 · 少年', en: 'Female teen', h: 1.
|
|
3375
|
-
{ id: 'm_child', zh: '男性 · 儿童', en: 'Male child', h:
|
|
3376
|
-
{ id: 'f_child', zh: '女性 · 儿童', en: 'Female child', h:
|
|
3377
|
-
{ id: 'm_slim', zh: '男性 · 纤细', en: 'Male slim', h: 1.80, w: 0.88, d: 0.90 , color: '#
|
|
3378
|
-
{ id: 'f_slim', zh: '女性 · 纤细', en: 'Female slim', h: 1.68, w: 0.84, d: 0.88 , color: '#
|
|
3379
|
-
{ id: 'm_muscular', zh: '男性 · 健壮', en: 'Male muscular', h: 1.82, w: 1.12, d: 1.08 , color: '#
|
|
3380
|
-
{ id: 'f_muscular', zh: '女性 · 健壮', en: 'Female muscular', h: 1.70, w: 1.02, d: 1.02 , color: '#
|
|
3381
|
-
{ id: 'm_plump', zh: '男性 · 丰腴', en: 'Male plump', h: 1.74, w: 1.18, d: 1.14 , color: '#
|
|
3382
|
-
{ id: 'f_plump', zh: '女性 · 丰腴', en: 'Female plump', h: 1.62, w: 1.14, d: 1.12 , color: '#
|
|
3597
|
+
{ id: 'm_standard', zh: '男性 · 标准', en: 'Male standard', h: 1.78, w: 1.00, d: 1.00 , color: '#2f7fe8' },
|
|
3598
|
+
{ id: 'f_standard', zh: '女性 · 标准', en: 'Female standard', h: 1.66, w: 0.92, d: 0.94 , color: '#e857a8' },
|
|
3599
|
+
{ id: 'm_teen', zh: '男性 · 少年', en: 'Male teen', h: 1.15, w: 0.90, d: 0.92 , color: '#17b877' },
|
|
3600
|
+
{ id: 'f_teen', zh: '女性 · 少年', en: 'Female teen', h: 1.12, w: 0.86, d: 0.90 , color: '#8b5cf6' },
|
|
3601
|
+
{ id: 'm_child', zh: '男性 · 儿童', en: 'Male child', h: 0.80, w: 0.90, d: 0.96 , color: '#7cc93f' },
|
|
3602
|
+
{ id: 'f_child', zh: '女性 · 儿童', en: 'Female child', h: 0.80, w: 0.88, d: 0.95 , color: '#f0a832' },
|
|
3603
|
+
{ id: 'm_slim', zh: '男性 · 纤细', en: 'Male slim', h: 1.80, w: 0.88, d: 0.90 , color: '#38a8e8' },
|
|
3604
|
+
{ id: 'f_slim', zh: '女性 · 纤细', en: 'Female slim', h: 1.68, w: 0.84, d: 0.88 , color: '#e85d6f' },
|
|
3605
|
+
{ id: 'm_muscular', zh: '男性 · 健壮', en: 'Male muscular', h: 1.82, w: 1.12, d: 1.08 , color: '#f07818' },
|
|
3606
|
+
{ id: 'f_muscular', zh: '女性 · 健壮', en: 'Female muscular', h: 1.70, w: 1.02, d: 1.02 , color: '#d83050' },
|
|
3607
|
+
{ id: 'm_plump', zh: '男性 · 丰腴', en: 'Male plump', h: 1.74, w: 1.18, d: 1.14 , color: '#5865d8' },
|
|
3608
|
+
{ id: 'f_plump', zh: '女性 · 丰腴', en: 'Female plump', h: 1.62, w: 1.14, d: 1.12 , color: '#a05fd8' },
|
|
3383
3609
|
];
|
|
3610
|
+
|
|
3611
|
+
/** 体型的标志色(MANNEQUIN_TYPES.color):ob.color 为空时的默认着色,
|
|
3612
|
+
* 让 12 体型在视口/缩略图里像 storyai 资产页那样一眼可辨。 */
|
|
3613
|
+
export function mannequinColorOf(typeId) {
|
|
3614
|
+
const t2 = (MANNEQUIN_TYPES || []).find((x) => x.id === typeId);
|
|
3615
|
+
return (t2 && t2.color) || '';
|
|
3616
|
+
}
|
|
3384
3617
|
export function mannequinTypeOf(id) { return MANNEQUIN_TYPES.find((t) => t.id === id) || MANNEQUIN_TYPES[0]; }
|
|
3385
3618
|
|
|
3386
3619
|
/** 资产包围盒(用于自动归一化到人高 / 落地)。 */
|
|
@@ -5403,7 +5636,7 @@ export function createDirectorGlRenderer(canvas) {
|
|
|
5403
5636
|
if (locSkin && locSkin.uRoughness) gl.uniform1f(locSkin.uRoughness, mat && mat.roughness !== undefined ? Number(mat.roughness) : 1);
|
|
5404
5637
|
if (locSkin && locSkin.uMetallic) gl.uniform1f(locSkin.uMetallic, mat && mat.metallic !== undefined ? Number(mat.metallic) : 0);
|
|
5405
5638
|
if (locSkin && locSkin.uSpecular) gl.uniform1f(locSkin.uSpecular, mat && mat.specular !== undefined ? Number(mat.specular) : 0.5);
|
|
5406
|
-
const cc3 = directorHexToRgb(ob.color || CLAY);
|
|
5639
|
+
const cc3 = directorHexToRgb(ob.color || mannequinColorOf(ob.typeId) || CLAY);
|
|
5407
5640
|
if (drawDynamic(sk.pos, sk.nrm, asset.indices, model, cc3, !!(o.selectedId && ob.id === o.selectedId), viewProj, lightPack, Number(env.exposure) || 1)) {
|
|
5408
5641
|
stats.faces += asset.tris;
|
|
5409
5642
|
stats.tris += asset.tris;
|
|
@@ -5419,7 +5652,7 @@ export function createDirectorGlRenderer(canvas) {
|
|
|
5419
5652
|
const rec = uploadSkin(ob.assetId, asset);
|
|
5420
5653
|
const t = Number(ob.animTime) || 0;
|
|
5421
5654
|
const bones = asset.bones ? boneMatrices(asset, t) : null;
|
|
5422
|
-
const cc2 = directorHexToRgb(ob.color || CLAY);
|
|
5655
|
+
const cc2 = directorHexToRgb(ob.color || mannequinColorOf(ob.typeId) || CLAY);
|
|
5423
5656
|
const model = directorMat4Model(ob.pos, Number(ob.rotY) || 0, v3(Number(ob.s) || 1, Number(ob.s) || 1, Number(ob.s) || 1));
|
|
5424
5657
|
if (drawSkin(asset, rec, model, cc2, viewProj, lightPack, Number(env.exposure) || 1, !!(o.selectedId && ob.id === o.selectedId), bones)) {
|
|
5425
5658
|
stats.faces += asset.tris;
|
|
@@ -5442,7 +5675,7 @@ export function createDirectorGlRenderer(canvas) {
|
|
|
5442
5675
|
gl.vertexAttribPointer(loc.aNormal, 3, gl.FLOAT, false, 0, 0);
|
|
5443
5676
|
gl.uniformMatrix4fv(loc.uModel, false, directorMat4Identity());
|
|
5444
5677
|
gl.uniformMatrix3fv(loc.uNormalMat, false, directorMat3NormalFrom(v3(0, 0, 0), 0, v3(1, 1, 1)));
|
|
5445
|
-
const cc = directorHexToRgb(ob.color || CLAY);
|
|
5678
|
+
const cc = directorHexToRgb(ob.color || mannequinColorOf(ob.typeId) || CLAY);
|
|
5446
5679
|
gl.uniform3f(loc.uColor, cc[0], cc[1], cc[2]);
|
|
5447
5680
|
gl.uniform1f(loc.uSelected, (o.selectedId && ob.id === o.selectedId) ? 1 : 0);
|
|
5448
5681
|
gl.drawArrays(gl.TRIANGLES, 0, rec.count);
|
|
@@ -5554,13 +5787,10 @@ export function createDirectorGlRenderer(canvas) {
|
|
|
5554
5787
|
if (o.gizmo && o.selectedId) {
|
|
5555
5788
|
const sel = (scene.objects || []).find((x) => x.id === o.selectedId);
|
|
5556
5789
|
if (sel) {
|
|
5557
|
-
//
|
|
5790
|
+
// 轴心恒 = 物体自身坐标系原点(人物 = 脚底中心),轴朝向由 objectAxes(sel) 给出
|
|
5558
5791
|
// → 物体旋转时轴跟着转(人物倒地后 z 轴仍指向头),不是固定世界坐标。
|
|
5559
|
-
//
|
|
5560
|
-
const
|
|
5561
|
-
const origin = followBody
|
|
5562
|
-
? v3(sel.pos.x, sel.pos.y + (Number(sel.poseCenterY) || 0), sel.pos.z)
|
|
5563
|
-
: v3(sel.pos.x, sel.pos.y, sel.pos.z);
|
|
5792
|
+
// 绝不随姿态/髋高上浮:任何"智能抬升"都会让轴悬空(0.19.3/0.19.4 的教训)。
|
|
5793
|
+
const origin = v3(sel.pos.x, sel.pos.y, sel.pos.z);
|
|
5564
5794
|
// 屏幕恒定大小(Blender: wm_gizmo_calculate_scale):轴长 105 CSS px,环 0.62×轴长。
|
|
5565
5795
|
// 尺寸唯一来源 gizmoMetrics,拾取用同一个函数 → 不可能再出现「画的位置和点不中」。
|
|
5566
5796
|
const gm = gizmoMetrics(cam, sel, hCss, origin);
|
|
@@ -5703,7 +5933,7 @@ export function directorRenderScene(ctx, w, h, scene, opts) {
|
|
|
5703
5933
|
(scene.objects || []).forEach((ob) => {
|
|
5704
5934
|
const fs = directorObjectFaces(ob);
|
|
5705
5935
|
stats.faces += fs.length;
|
|
5706
|
-
fs.forEach((f) => items.push({ f, color: ob.color || CLAY, base: ob }));
|
|
5936
|
+
fs.forEach((f) => items.push({ f, color: ob.color || mannequinColorOf(ob.typeId) || CLAY, base: ob }));
|
|
5707
5937
|
});
|
|
5708
5938
|
if (false && env.groundVisible !== false) {
|
|
5709
5939
|
const s = 400, op = 0; // 不画地面填充,只画网格
|
|
@@ -5947,12 +6177,8 @@ export function directorPickGizmo(scene, selectedId, w, h, sx, sy) {
|
|
|
5947
6177
|
if (!cam) return null;
|
|
5948
6178
|
// 与绘制完全同源:精确屏幕投影 + 同一个 gizmoMetrics(w/h 都是 CSS 尺寸)
|
|
5949
6179
|
const project = (p) => directorScreenOf(cam, w, h, p);
|
|
5950
|
-
//
|
|
5951
|
-
const
|
|
5952
|
-
const pickOrigin = pickFollow
|
|
5953
|
-
? v3(sel.pos.x, sel.pos.y + (Number(sel.poseCenterY) || 0), sel.pos.z)
|
|
5954
|
-
: v3(sel.pos.x, sel.pos.y, sel.pos.z);
|
|
5955
|
-
const gm = gizmoMetrics(cam, sel, h, pickOrigin);
|
|
6180
|
+
// 与绘制完全同源:轴心恒 = 物体原点(规则必须与绘制一字不差,否则点不中画出来的轴)
|
|
6181
|
+
const gm = gizmoMetrics(cam, sel, h, v3(sel.pos.x, sel.pos.y, sel.pos.z));
|
|
5956
6182
|
const origin = gm.origin, L = gm.L, R = gm.R;
|
|
5957
6183
|
const ax = objectAxes(sel);
|
|
5958
6184
|
const AXES = [['x', ax.x], ['y', ax.y], ['z', ax.z]];
|