feat(body_mesh): Add body mesh separator module with edge dissolution
- Add body_mesh submodule with VRM_OT_separate_non_skin_faces operator - Add VRM_PT_body_mesh_separator panel for UI integration - Add utility functions for mesh operations and edge dissolution logic - Add comprehensive edge dissolution configuration files for all body parts (head, torso, arms, legs, hands) with UV coordinates - Add test utilities for validating mesh separation operations - Add .gitignore to exclude Python cache files and test artifacts - Register body_mesh module in editor package initialization - Update registration.py to include new body mesh components
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.hypothesis/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
@@ -1 +1,2 @@
|
|||||||
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
from . import body_mesh
|
||||||
|
|||||||
8
editor/body_mesh/__init__.py
Normal file
8
editor/body_mesh/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
from .ops import VRM_OT_separate_non_skin_faces
|
||||||
|
from .panel import VRM_PT_body_mesh_separator
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VRM_OT_separate_non_skin_faces",
|
||||||
|
"VRM_PT_body_mesh_separator",
|
||||||
|
]
|
||||||
BIN
editor/body_mesh/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
editor/body_mesh/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
editor/body_mesh/__pycache__/ops.cpython-313.pyc
Normal file
BIN
editor/body_mesh/__pycache__/ops.cpython-313.pyc
Normal file
Binary file not shown.
BIN
editor/body_mesh/__pycache__/test_utils.cpython-313.pyc
Normal file
BIN
editor/body_mesh/__pycache__/test_utils.cpython-313.pyc
Normal file
Binary file not shown.
BIN
editor/body_mesh/__pycache__/utils.cpython-313.pyc
Normal file
BIN
editor/body_mesh/__pycache__/utils.cpython-313.pyc
Normal file
Binary file not shown.
48606
editor/body_mesh/dissolve_edges_all_uv.json
Normal file
48606
editor/body_mesh/dissolve_edges_all_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
31
editor/body_mesh/dissolve_edges_arms.json
Normal file
31
editor/body_mesh/dissolve_edges_arms.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"description": "VRM模型手臂区域需要融并的边索引列表(用于三角面转四边面)",
|
||||||
|
"region": "arms",
|
||||||
|
"total_edges": 834,
|
||||||
|
"edge_indices": [
|
||||||
|
368, 369, 374, 379, 381, 383, 385, 388, 389, 392, 393, 396, 397, 400, 402, 404, 406, 407, 411, 417, 418, 419, 423, 425, 426, 428, 430, 432, 439, 442, 445, 452, 458, 460, 463, 465, 467, 470, 474, 479, 483, 486, 487, 490, 493, 495, 498, 502, 504, 506, 510, 515, 519, 521, 524,
|
||||||
|
726, 744, 746, 748, 754, 756, 757, 760, 763, 765, 768, 769, 771, 776, 779, 780, 781, 784, 791, 794, 797, 800, 803, 808, 812, 814, 817, 820, 822, 825, 829, 831, 835, 839, 841, 846, 848, 850, 851, 854, 858, 859, 864,
|
||||||
|
1637, 1640, 1642, 1752, 1753, 1756, 1760,
|
||||||
|
2638, 2644, 2645, 2649, 2650, 2656, 2657, 2673, 2676, 2677, 2682, 2690, 2693, 2696, 2700, 2702, 2703, 2705, 2707, 2709, 2710, 2713, 2716, 2721, 2726, 2734, 2735, 2736, 2739, 2742, 2745, 2749, 2750, 2755, 2762, 2763, 2765, 2767, 2768, 2773, 2778, 2780, 2782, 2786, 2793, 2798,
|
||||||
|
2966, 2968, 2978, 2980, 2983, 2986, 2988, 2992, 2999, 3001, 3003, 3006, 3011, 3012, 3013, 3018, 3022, 3025, 3028, 3032, 3039, 3042, 3044, 3048, 3051, 3055, 3058, 3060, 3065, 3066, 3067, 3070, 3072, 3074, 3076, 3080, 3081, 3082, 3085, 3086, 3090, 3091, 3092, 3095, 3100, 3104, 3105, 3109, 3110, 3111, 3114, 3117, 3120, 3121,
|
||||||
|
3766, 3767, 3775, 3873, 3880,
|
||||||
|
4786, 4791, 4793, 4802, 4805, 4813, 4819, 4822, 4823, 4830, 4831, 4836, 4841, 4844, 4847, 4849, 4851, 4854, 4859, 4861, 4864, 4866, 4871, 4872, 4875, 4878, 4879, 4883, 4886, 4887, 4889, 4892, 4896, 4897, 4898, 4902, 4907, 4909, 4913, 4915, 4916, 4917, 4918, 4921, 4926,
|
||||||
|
5104, 5105, 5110, 5113, 5116, 5119, 5122, 5125, 5128, 5134, 5136, 5140, 5142, 5144, 5150, 5154, 5156, 5157, 5162, 5168, 5175, 5178, 5181, 5182, 5187, 5193, 5198, 5201, 5204, 5212, 5215, 5219, 5221, 5224, 5228, 5229, 5233, 5236, 5237, 5238, 5247, 5248, 5250, 5252, 5255,
|
||||||
|
5904, 5906, 5907, 5911, 5916, 6027, 6030, 6036, 6038,
|
||||||
|
6894, 6896, 6898, 6900, 6906, 6907, 6910, 6912, 6915, 6918, 6921, 6930, 6931, 6933, 6937, 6939, 6940, 6951, 6954, 6957, 6958, 6965, 6970, 6974, 6977, 6983, 6985, 6990, 6992, 6996, 7000, 7001, 7002, 7010, 7011, 7015, 7018, 7020, 7021, 7022, 7027, 7028, 7030, 7036, 7040, 7045, 7050, 7051,
|
||||||
|
7233, 7240, 7241, 7244, 7245, 7248, 7249, 7252, 7253, 7256, 7257, 7260, 7261, 7264, 7265, 7268, 7269, 7272, 7274, 7276, 7278, 7279, 7283, 7289, 7290, 7291, 7295, 7297, 7298, 7300, 7302, 7304, 7311, 7314, 7317, 7324, 7330, 7332, 7335, 7337, 7339, 7340, 7342, 7346, 7349, 7352, 7355, 7356, 7359, 7360, 7361, 7369, 7370, 7374, 7379, 7381, 7386, 7389, 7391, 7398,
|
||||||
|
8033, 8153, 8158, 8161, 8163,
|
||||||
|
9113, 9114, 9119, 9120, 9125, 9127, 9133, 9134, 9137, 9138, 9141, 9142, 9145, 9146, 9148, 9150, 9151, 9155, 9156, 9159, 9161, 9169, 9172, 9174, 9176, 9177, 9181, 9182, 9188, 9193, 9195, 9201, 9206, 9208, 9211, 9212, 9217, 9218, 9223, 9225, 9231, 9235, 9236, 9243, 9246, 9251, 9255, 9256, 9260, 9266,
|
||||||
|
9481, 9484, 9485, 9490, 9498, 9501, 9504, 9508, 9510, 9511, 9513, 9515, 9517, 9518, 9522, 9524, 9529, 9534, 9536, 9539, 9543, 9546, 9548, 9551, 9553, 9559, 9562, 9563, 9566, 9567, 9572, 9574, 9576, 9579, 9581, 9585, 9592, 9594, 9595, 9599, 9602, 9603, 9604, 9606,
|
||||||
|
10291, 10293, 10294, 10297, 10419, 10420, 10428,
|
||||||
|
11283, 11284, 11292, 11293, 11309, 11311, 11313, 11319, 11321, 11322, 11325, 11328, 11330, 11333, 11334, 11336, 11341, 11344, 11345, 11346, 11349, 11356, 11358, 11362, 11365, 11368, 11373, 11375, 11377, 11378, 11379, 11381, 11385, 11386, 11390, 11392, 11397, 11399, 11400, 11403, 11406, 11408, 11411, 11413, 11414, 11419, 11422, 11425, 11430, 11432,
|
||||||
|
11619, 11629, 11632, 11640, 11646, 11649, 11650, 11657, 11658, 11663, 11668, 11671, 11673, 11678, 11681, 11686, 11688, 11691, 11694, 11698, 11699, 11702, 11706, 11710, 11713, 11715, 11717, 11719, 11725, 11728, 11729, 11730, 11734, 11735, 11741, 11742, 11749, 11750, 11753, 11757, 11760,
|
||||||
|
12379, 12380, 12383, 12387, 12504, 12506, 12507, 12511, 12516,
|
||||||
|
13427, 13429, 13431, 13436, 13442, 13444, 13447, 13450, 13452, 13456, 13463, 13465, 13467, 13470, 13475, 13476, 13477, 13482, 13486, 13489, 13492, 13496, 13501, 13504, 13506, 13509, 13513, 13515, 13516, 13517, 13520, 13522, 13525, 13527, 13529, 13533, 13539, 13540, 13545, 13546, 13549, 13552, 13556, 13557, 13560, 13564, 13570, 13572, 13575,
|
||||||
|
13758, 13759, 13764, 13765, 13768, 13771, 13774, 13777, 13780, 13783, 13786, 13795, 13796, 13798, 13802, 13804, 13805, 13816, 13819, 13822, 13823, 13830, 13835, 13839, 13842, 13846, 13848, 13853, 13855, 13858, 13861, 13867, 13870, 13872, 13876, 13877, 13878, 13883, 13884, 13887, 13890, 13895, 13899, 13902, 13905, 13906, 13909, 13915, 13916,
|
||||||
|
14530, 14537, 14658,
|
||||||
|
15540, 15542, 15545, 15548, 15549, 15555, 15558, 15561, 15564, 15570, 15572, 15576, 15578, 15580, 15586, 15590, 15592, 15593, 15598, 15604, 15611, 15614, 15617, 15618, 15623, 15628, 15632, 15636, 15639, 15642, 15645, 15649, 15650, 15654, 15655, 15663, 15665, 15666, 15668, 15673, 15674, 15677, 15679, 15680, 15682, 15684, 15687,
|
||||||
|
15873, 15874, 15879, 15880, 15883, 15884, 15887, 15888, 15891, 15892, 15895, 15896, 15899, 15900, 15903, 15904, 15906, 15908, 15909, 15913, 15914, 15917, 15919, 15927, 15930, 15932, 15934, 15935, 15939, 15940, 15946, 15951, 15953, 15956, 15959, 15964, 15966, 15970, 15974, 15978, 15981, 15982, 15988, 15990, 15993, 15996, 15998, 15999, 16007, 16011, 16012, 16020, 16021, 16022,
|
||||||
|
16814, 16818, 16821, 16827, 16829, 16951, 16953, 16954, 16957
|
||||||
|
]
|
||||||
|
}
|
||||||
6237
editor/body_mesh/dissolve_edges_arms_uv.json
Normal file
6237
editor/body_mesh/dissolve_edges_arms_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
293
editor/body_mesh/dissolve_edges_hands.json
Normal file
293
editor/body_mesh/dissolve_edges_hands.json
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
{
|
||||||
|
"description": "VRM模型手掌区域需要融并的边索引列表(用于三角面转四边面)",
|
||||||
|
"region": "hands",
|
||||||
|
"total_edges": 286,
|
||||||
|
"edge_indices": [
|
||||||
|
1696,
|
||||||
|
1700,
|
||||||
|
1709,
|
||||||
|
1716,
|
||||||
|
1723,
|
||||||
|
1727,
|
||||||
|
1732,
|
||||||
|
1736,
|
||||||
|
1742,
|
||||||
|
1744,
|
||||||
|
1747,
|
||||||
|
1809,
|
||||||
|
1811,
|
||||||
|
1814,
|
||||||
|
1816,
|
||||||
|
1820,
|
||||||
|
1821,
|
||||||
|
1827,
|
||||||
|
1828,
|
||||||
|
1829,
|
||||||
|
1831,
|
||||||
|
1835,
|
||||||
|
1839,
|
||||||
|
1841,
|
||||||
|
1844,
|
||||||
|
1847,
|
||||||
|
1852,
|
||||||
|
1856,
|
||||||
|
1860,
|
||||||
|
1864,
|
||||||
|
1868,
|
||||||
|
1869,
|
||||||
|
1874,
|
||||||
|
1876,
|
||||||
|
3823,
|
||||||
|
3825,
|
||||||
|
3826,
|
||||||
|
3829,
|
||||||
|
3830,
|
||||||
|
3835,
|
||||||
|
3837,
|
||||||
|
3838,
|
||||||
|
3839,
|
||||||
|
3845,
|
||||||
|
3850,
|
||||||
|
3852,
|
||||||
|
3859,
|
||||||
|
3864,
|
||||||
|
3867,
|
||||||
|
3869,
|
||||||
|
3934,
|
||||||
|
3936,
|
||||||
|
3937,
|
||||||
|
3942,
|
||||||
|
3948,
|
||||||
|
3953,
|
||||||
|
3960,
|
||||||
|
3963,
|
||||||
|
3965,
|
||||||
|
3971,
|
||||||
|
3973,
|
||||||
|
3975,
|
||||||
|
3978,
|
||||||
|
3979,
|
||||||
|
3982,
|
||||||
|
3986,
|
||||||
|
3992,
|
||||||
|
3995,
|
||||||
|
3998,
|
||||||
|
5960,
|
||||||
|
5963,
|
||||||
|
5964,
|
||||||
|
5971,
|
||||||
|
5973,
|
||||||
|
5981,
|
||||||
|
5984,
|
||||||
|
5988,
|
||||||
|
5992,
|
||||||
|
5994,
|
||||||
|
5999,
|
||||||
|
6000,
|
||||||
|
6003,
|
||||||
|
6006,
|
||||||
|
6008,
|
||||||
|
6016,
|
||||||
|
6019,
|
||||||
|
6021,
|
||||||
|
6023,
|
||||||
|
6089,
|
||||||
|
6092,
|
||||||
|
6093,
|
||||||
|
6095,
|
||||||
|
6099,
|
||||||
|
6107,
|
||||||
|
6109,
|
||||||
|
6110,
|
||||||
|
6114,
|
||||||
|
6117,
|
||||||
|
6120,
|
||||||
|
6124,
|
||||||
|
6130,
|
||||||
|
6134,
|
||||||
|
6135,
|
||||||
|
6138,
|
||||||
|
6140,
|
||||||
|
6147,
|
||||||
|
6150,
|
||||||
|
6151,
|
||||||
|
8089,
|
||||||
|
8099,
|
||||||
|
8101,
|
||||||
|
8102,
|
||||||
|
8105,
|
||||||
|
8111,
|
||||||
|
8116,
|
||||||
|
8128,
|
||||||
|
8135,
|
||||||
|
8137,
|
||||||
|
8139,
|
||||||
|
8141,
|
||||||
|
8143,
|
||||||
|
8145,
|
||||||
|
8147,
|
||||||
|
8150,
|
||||||
|
8217,
|
||||||
|
8221,
|
||||||
|
8230,
|
||||||
|
8237,
|
||||||
|
8246,
|
||||||
|
8251,
|
||||||
|
8256,
|
||||||
|
8261,
|
||||||
|
8263,
|
||||||
|
8266,
|
||||||
|
10348,
|
||||||
|
10352,
|
||||||
|
10356,
|
||||||
|
10358,
|
||||||
|
10361,
|
||||||
|
10366,
|
||||||
|
10368,
|
||||||
|
10370,
|
||||||
|
10374,
|
||||||
|
10377,
|
||||||
|
10381,
|
||||||
|
10383,
|
||||||
|
10385,
|
||||||
|
10386,
|
||||||
|
10394,
|
||||||
|
10395,
|
||||||
|
10399,
|
||||||
|
10403,
|
||||||
|
10408,
|
||||||
|
10409,
|
||||||
|
10413,
|
||||||
|
10470,
|
||||||
|
10478,
|
||||||
|
10480,
|
||||||
|
10481,
|
||||||
|
10484,
|
||||||
|
10485,
|
||||||
|
10490,
|
||||||
|
10492,
|
||||||
|
10493,
|
||||||
|
10494,
|
||||||
|
10500,
|
||||||
|
10505,
|
||||||
|
10514,
|
||||||
|
10518,
|
||||||
|
10522,
|
||||||
|
10524,
|
||||||
|
12435,
|
||||||
|
12437,
|
||||||
|
12440,
|
||||||
|
12442,
|
||||||
|
12446,
|
||||||
|
12447,
|
||||||
|
12453,
|
||||||
|
12454,
|
||||||
|
12455,
|
||||||
|
12457,
|
||||||
|
12461,
|
||||||
|
12465,
|
||||||
|
12467,
|
||||||
|
12470,
|
||||||
|
12471,
|
||||||
|
12473,
|
||||||
|
12478,
|
||||||
|
12482,
|
||||||
|
12486,
|
||||||
|
12490,
|
||||||
|
12494,
|
||||||
|
12495,
|
||||||
|
12500,
|
||||||
|
12502,
|
||||||
|
12564,
|
||||||
|
12565,
|
||||||
|
12572,
|
||||||
|
12574,
|
||||||
|
12582,
|
||||||
|
12585,
|
||||||
|
12589,
|
||||||
|
12593,
|
||||||
|
12595,
|
||||||
|
12601,
|
||||||
|
12603,
|
||||||
|
12604,
|
||||||
|
12607,
|
||||||
|
12609,
|
||||||
|
12617,
|
||||||
|
12620,
|
||||||
|
12623,
|
||||||
|
12624,
|
||||||
|
14591,
|
||||||
|
14593,
|
||||||
|
14594,
|
||||||
|
14599,
|
||||||
|
14606,
|
||||||
|
14610,
|
||||||
|
14617,
|
||||||
|
14620,
|
||||||
|
14622,
|
||||||
|
14628,
|
||||||
|
14630,
|
||||||
|
14633,
|
||||||
|
14634,
|
||||||
|
14637,
|
||||||
|
14641,
|
||||||
|
14647,
|
||||||
|
14650,
|
||||||
|
14653,
|
||||||
|
14710,
|
||||||
|
14720,
|
||||||
|
14722,
|
||||||
|
14723,
|
||||||
|
14726,
|
||||||
|
14732,
|
||||||
|
14737,
|
||||||
|
14749,
|
||||||
|
14756,
|
||||||
|
14758,
|
||||||
|
14760,
|
||||||
|
14762,
|
||||||
|
14764,
|
||||||
|
14766,
|
||||||
|
14768,
|
||||||
|
14771,
|
||||||
|
16880,
|
||||||
|
16883,
|
||||||
|
16884,
|
||||||
|
16886,
|
||||||
|
16891,
|
||||||
|
16898,
|
||||||
|
16900,
|
||||||
|
16901,
|
||||||
|
16905,
|
||||||
|
16908,
|
||||||
|
16911,
|
||||||
|
16915,
|
||||||
|
16929,
|
||||||
|
16931,
|
||||||
|
16936,
|
||||||
|
16938,
|
||||||
|
16941,
|
||||||
|
16942,
|
||||||
|
17008,
|
||||||
|
17012,
|
||||||
|
17016,
|
||||||
|
17018,
|
||||||
|
17021,
|
||||||
|
17026,
|
||||||
|
17028,
|
||||||
|
17030,
|
||||||
|
17034,
|
||||||
|
17037,
|
||||||
|
17041,
|
||||||
|
17043,
|
||||||
|
17045,
|
||||||
|
17046,
|
||||||
|
17054,
|
||||||
|
17055,
|
||||||
|
17059,
|
||||||
|
17063,
|
||||||
|
17067,
|
||||||
|
17069,
|
||||||
|
17073
|
||||||
|
]
|
||||||
|
}
|
||||||
1957
editor/body_mesh/dissolve_edges_hands_uv.json
Normal file
1957
editor/body_mesh/dissolve_edges_hands_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
15
editor/body_mesh/dissolve_edges_head.json
Normal file
15
editor/body_mesh/dissolve_edges_head.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"description": "VRM模型头部区域需要融并的边索引列表(用于三角面转四边面)",
|
||||||
|
"region": "head",
|
||||||
|
"total_edges": 420,
|
||||||
|
"edge_indices": [
|
||||||
|
2107, 2109, 2114, 2116, 2122, 2123, 2126, 2128, 2129, 2131, 2133, 2136, 2139, 2141, 2146, 2155, 2159, 2163, 2165, 2168, 2170, 2173, 2178, 2179, 2183, 2185, 2190, 2195, 2198, 2209, 2211, 2215, 2217, 2219, 2221, 2226, 2228, 2233, 2236, 2239, 2242, 2246, 2247, 2248, 2252, 2253, 2254, 2259, 2262, 2263, 2266, 2267, 2271, 2275, 2278,
|
||||||
|
4245, 4247, 4252, 4254, 4255, 4256, 4259, 4265, 4273, 4281, 4282, 4291, 4294, 4295, 4299, 4305, 4307, 4308, 4310, 4314, 4318, 4321, 4327, 4329, 4337, 4342, 4343, 4347, 4348, 4352, 4356, 4361, 4365, 4366, 4368, 4372, 4377, 4378, 4380, 4382, 4384, 4390, 4391, 4395, 4400, 4404, 4405, 4406, 4411, 4412,
|
||||||
|
6362, 6363, 6368, 6370, 6373, 6380, 6382, 6386, 6388, 6390, 6393, 6397, 6400, 6404, 6408, 6411, 6412, 6418, 6420, 6421, 6422, 6425, 6433, 6438, 6441, 6448, 6453, 6457, 6460, 6466, 6469, 6474, 6478, 6481, 6483, 6485, 6491, 6494, 6497, 6498, 6500, 6503, 6506, 6507, 6513, 6518, 6519, 6521, 6526, 6527, 6530, 6531,
|
||||||
|
8579, 8582, 8587, 8590, 8593, 8595, 8596, 8601, 8606, 8609, 8610, 8612, 8615, 8617, 8623, 8629, 8630, 8636, 8640, 8643, 8648, 8655, 8657, 8658, 8662, 8665, 8667, 8668, 8670, 8671, 8676, 8678, 8685, 8687, 8691, 8694, 8696, 8698, 8699, 8703, 8707, 8708, 8710, 8712, 8714, 8718, 8719, 8721, 8727, 8731, 8733, 8735, 8738, 8745,
|
||||||
|
10758, 10760, 10765, 10771, 10772, 10777, 10780, 10782, 10783, 10785, 10789, 10790, 10797, 10802, 10811, 10812, 10823, 10824, 10827, 10830, 10831, 10832, 10838, 10844, 10845, 10852, 10858, 10859, 10863, 10866, 10870, 10873, 10874, 10875, 10876, 10877, 10881, 10885, 10891, 10896, 10899, 10902, 10905, 10906, 10910, 10916, 10921, 10922, 10925, 10926,
|
||||||
|
12887, 12892, 12894, 12897, 12899, 12904, 12905, 12912, 12917, 12920, 12923, 12926, 12931, 12933, 12936, 12939, 12940, 12941, 12944, 12952, 12954, 12955, 12958, 12961, 12964, 12966, 12967, 12973, 12979, 12984, 12986, 12989, 12992, 12995, 12998, 13002, 13004, 13008, 13011, 13014, 13016, 13018, 13026, 13027, 13032, 13036, 13038, 13041, 13042, 13046,
|
||||||
|
14999, 15001, 15006, 15008, 15011, 15012, 15013, 15018, 15019, 15022, 15026, 15027, 15028, 15033, 15039, 15040, 15044, 15047, 15051, 15056, 15061, 15063, 15066, 15069, 15070, 15071, 15072, 15076, 15078, 15082, 15083, 15086, 15093, 15097, 15100, 15103, 15106, 15109, 15110, 15113, 15118, 15120, 15121, 15129, 15130, 15133, 15134, 15138, 15140, 15144, 15146, 15149, 15153, 15154, 15155, 15158, 15160, 15169,
|
||||||
|
17278, 17280, 17285, 17287, 17292, 17293, 17296, 17297, 17300, 17305, 17307, 17309, 17312, 17320, 17321, 17324, 17326, 17329, 17332, 17337, 17339, 17342, 17344, 17348, 17349, 17352, 17355, 17362, 17365, 17367, 17370, 17372, 17375, 17379, 17383, 17386, 17388, 17393, 17396, 17399, 17400, 17405, 17410, 17415, 17419, 17425, 17428, 17433, 17435, 17441, 17442
|
||||||
|
]
|
||||||
|
}
|
||||||
2617
editor/body_mesh/dissolve_edges_head_uv.json
Normal file
2617
editor/body_mesh/dissolve_edges_head_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
2185
editor/body_mesh/dissolve_edges_legs.json
Normal file
2185
editor/body_mesh/dissolve_edges_legs.json
Normal file
File diff suppressed because it is too large
Load Diff
15677
editor/body_mesh/dissolve_edges_legs_uv.json
Normal file
15677
editor/body_mesh/dissolve_edges_legs_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
1173
editor/body_mesh/dissolve_edges_torso.json
Normal file
1173
editor/body_mesh/dissolve_edges_torso.json
Normal file
File diff suppressed because it is too large
Load Diff
8647
editor/body_mesh/dissolve_edges_torso_uv.json
Normal file
8647
editor/body_mesh/dissolve_edges_torso_uv.json
Normal file
File diff suppressed because it is too large
Load Diff
220
editor/body_mesh/ops.py
Normal file
220
editor/body_mesh/ops.py
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
from collections.abc import Set as AbstractSet
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
from bpy.types import Context, Operator
|
||||||
|
|
||||||
|
from .utils import (
|
||||||
|
MaterialInfo,
|
||||||
|
find_body_mesh,
|
||||||
|
generate_unique_name,
|
||||||
|
get_material_face_count,
|
||||||
|
is_skin_material,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bpy.types import Object
|
||||||
|
|
||||||
|
|
||||||
|
class VRM_OT_separate_non_skin_faces(Operator):
|
||||||
|
"""将Body网格中非皮肤材质的面分离为独立对象"""
|
||||||
|
|
||||||
|
bl_idname = "vrm.separate_non_skin_faces"
|
||||||
|
bl_label = "Separate Non-Skin Faces"
|
||||||
|
bl_description = "将Body网格中非皮肤材质的面分离为独立对象"
|
||||||
|
bl_options: AbstractSet[str] = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: Context) -> bool:
|
||||||
|
"""检查是否可以执行分离操作"""
|
||||||
|
armature = cls._get_armature(context)
|
||||||
|
if armature is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
body_mesh = find_body_mesh(armature)
|
||||||
|
if body_mesh is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 检查是否有非皮肤材质
|
||||||
|
mesh_data = body_mesh.data
|
||||||
|
if mesh_data is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for material in mesh_data.materials:
|
||||||
|
if material is not None and not is_skin_material(material.name):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_armature(cls, context: Context) -> Optional["Object"]:
|
||||||
|
"""获取当前选中的Armature对象"""
|
||||||
|
active_object = context.active_object
|
||||||
|
if active_object is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if active_object.type == "ARMATURE":
|
||||||
|
return active_object
|
||||||
|
|
||||||
|
# 如果选中的是网格,检查其父对象是否为Armature
|
||||||
|
if active_object.parent is not None and active_object.parent.type == "ARMATURE":
|
||||||
|
return active_object.parent
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def execute(self, context: Context) -> set[str]:
|
||||||
|
"""执行分离操作"""
|
||||||
|
try:
|
||||||
|
return self._execute_separation(context)
|
||||||
|
except Exception as e:
|
||||||
|
self.report({"ERROR"}, f"分离操作失败: {str(e)}")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
def _execute_separation(self, context: Context) -> set[str]:
|
||||||
|
"""执行分离操作的核心逻辑"""
|
||||||
|
armature = self._get_armature(context)
|
||||||
|
if armature is None:
|
||||||
|
self.report({"ERROR"}, "未找到Armature对象")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
body_mesh = find_body_mesh(armature)
|
||||||
|
if body_mesh is None:
|
||||||
|
self.report({"ERROR"}, "未找到Body网格对象")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
mesh_data = body_mesh.data
|
||||||
|
if mesh_data is None:
|
||||||
|
self.report({"ERROR"}, "Body网格数据无效")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# 收集非皮肤材质信息
|
||||||
|
non_skin_materials: list[MaterialInfo] = []
|
||||||
|
for i, material in enumerate(mesh_data.materials):
|
||||||
|
if material is not None and not is_skin_material(material.name):
|
||||||
|
face_count = get_material_face_count(mesh_data, i)
|
||||||
|
if face_count > 0:
|
||||||
|
non_skin_materials.append(
|
||||||
|
MaterialInfo(
|
||||||
|
name=material.name,
|
||||||
|
index=i,
|
||||||
|
face_count=face_count,
|
||||||
|
is_skin=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not non_skin_materials:
|
||||||
|
self.report({"INFO"}, "没有找到非皮肤材质的面")
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# 获取现有对象名称集合
|
||||||
|
existing_names = set(obj.name for obj in context.blend_data.objects)
|
||||||
|
|
||||||
|
# 为每个非皮肤材质执行分离
|
||||||
|
separated_count = 0
|
||||||
|
for mat_info in non_skin_materials:
|
||||||
|
result = self._separate_material_faces(
|
||||||
|
context, body_mesh, armature, mat_info, existing_names
|
||||||
|
)
|
||||||
|
if result:
|
||||||
|
separated_count += 1
|
||||||
|
existing_names.add(result.name)
|
||||||
|
|
||||||
|
if separated_count > 0:
|
||||||
|
self.report({"INFO"}, f"成功分离了 {separated_count} 个材质的面")
|
||||||
|
return {"FINISHED"}
|
||||||
|
else:
|
||||||
|
self.report({"WARNING"}, "没有成功分离任何面")
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
def _separate_material_faces(
|
||||||
|
self,
|
||||||
|
context: Context,
|
||||||
|
body_obj: "Object",
|
||||||
|
armature: "Object",
|
||||||
|
mat_info: MaterialInfo,
|
||||||
|
existing_names: set[str],
|
||||||
|
) -> Optional["Object"]:
|
||||||
|
"""分离指定材质的面为新对象"""
|
||||||
|
mesh_data = body_obj.data
|
||||||
|
if mesh_data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 确保在对象模式
|
||||||
|
if context.mode != "OBJECT":
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
|
||||||
|
# 取消所有选择
|
||||||
|
bpy.ops.object.select_all(action="DESELECT")
|
||||||
|
|
||||||
|
# 选择Body网格并设为活动对象
|
||||||
|
body_obj.select_set(True)
|
||||||
|
context.view_layer.objects.active = body_obj
|
||||||
|
|
||||||
|
# 进入编辑模式
|
||||||
|
bpy.ops.object.mode_set(mode="EDIT")
|
||||||
|
|
||||||
|
# 取消所有选择
|
||||||
|
bpy.ops.mesh.select_all(action="DESELECT")
|
||||||
|
|
||||||
|
# 切换到面选择模式
|
||||||
|
bpy.ops.mesh.select_mode(type="FACE")
|
||||||
|
|
||||||
|
# 返回对象模式以访问网格数据
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
|
||||||
|
# 选择使用指定材质的面
|
||||||
|
for polygon in mesh_data.polygons:
|
||||||
|
if polygon.material_index == mat_info.index:
|
||||||
|
polygon.select = True
|
||||||
|
|
||||||
|
# 返回编辑模式执行分离
|
||||||
|
bpy.ops.object.mode_set(mode="EDIT")
|
||||||
|
|
||||||
|
# 执行分离操作
|
||||||
|
bpy.ops.mesh.separate(type="SELECTED")
|
||||||
|
|
||||||
|
# 返回对象模式
|
||||||
|
bpy.ops.object.mode_set(mode="OBJECT")
|
||||||
|
|
||||||
|
# 查找新创建的对象
|
||||||
|
new_obj = self._find_new_separated_object(context, body_obj)
|
||||||
|
if new_obj is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 生成唯一名称
|
||||||
|
base_name = self._generate_object_name(mat_info.name)
|
||||||
|
unique_name = generate_unique_name(base_name, existing_names)
|
||||||
|
new_obj.name = unique_name
|
||||||
|
|
||||||
|
# 设置父子关系(保持与原Armature的关系)
|
||||||
|
new_obj.parent = armature
|
||||||
|
new_obj.parent_type = "OBJECT"
|
||||||
|
|
||||||
|
return new_obj
|
||||||
|
|
||||||
|
def _find_new_separated_object(
|
||||||
|
self, context: Context, original_obj: "Object"
|
||||||
|
) -> Optional["Object"]:
|
||||||
|
"""查找分离操作后新创建的对象"""
|
||||||
|
# 分离后,新对象会被选中,原对象也会被选中
|
||||||
|
for obj in context.selected_objects:
|
||||||
|
if obj != original_obj and obj.type == "MESH":
|
||||||
|
return obj
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _generate_object_name(self, material_name: str) -> str:
|
||||||
|
"""根据材质名称生成对象名称"""
|
||||||
|
# 简化材质名称,移除常见前缀和后缀
|
||||||
|
simplified_name = material_name
|
||||||
|
# 移除 "(Instance)" 后缀
|
||||||
|
if simplified_name.endswith(" (Instance)"):
|
||||||
|
simplified_name = simplified_name[:-11]
|
||||||
|
# 移除常见的VRM材质前缀
|
||||||
|
prefixes_to_remove = ["N00_000_00_", "N00_001_00_", "N00_002_00_"]
|
||||||
|
for prefix in prefixes_to_remove:
|
||||||
|
if simplified_name.startswith(prefix):
|
||||||
|
simplified_name = simplified_name[len(prefix) :]
|
||||||
|
break
|
||||||
|
|
||||||
|
return f"Body_{simplified_name}"
|
||||||
113
editor/body_mesh/panel.py
Normal file
113
editor/body_mesh/panel.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
from collections.abc import Set as AbstractSet
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
from bpy.types import Context, Panel
|
||||||
|
|
||||||
|
from .utils import (
|
||||||
|
MaterialInfo,
|
||||||
|
find_body_mesh,
|
||||||
|
get_material_face_count,
|
||||||
|
is_skin_material,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bpy.types import Object
|
||||||
|
|
||||||
|
|
||||||
|
class VRM_PT_body_mesh_separator(Panel):
|
||||||
|
"""Body网格分离面板"""
|
||||||
|
|
||||||
|
bl_idname = "VRM_PT_body_mesh_separator"
|
||||||
|
bl_label = "Body Mesh Separator"
|
||||||
|
bl_space_type = "VIEW_3D"
|
||||||
|
bl_region_type = "UI"
|
||||||
|
bl_category = "VRM"
|
||||||
|
bl_options: AbstractSet[str] = set()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context: Context) -> bool:
|
||||||
|
"""检查是否应该显示面板 - 仅在选中包含Body网格的Armature时显示"""
|
||||||
|
armature = cls._get_armature(context)
|
||||||
|
if armature is None:
|
||||||
|
return False
|
||||||
|
body_mesh = find_body_mesh(armature)
|
||||||
|
return body_mesh is not None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_armature(cls, context: Context) -> Optional["Object"]:
|
||||||
|
"""获取当前选中的Armature对象"""
|
||||||
|
active_object = context.active_object
|
||||||
|
if active_object is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if active_object.type == "ARMATURE":
|
||||||
|
return active_object
|
||||||
|
|
||||||
|
# 如果选中的是网格,检查其父对象是否为Armature
|
||||||
|
if active_object.parent is not None and active_object.parent.type == "ARMATURE":
|
||||||
|
return active_object.parent
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def draw(self, context: Context) -> None:
|
||||||
|
"""绘制面板UI"""
|
||||||
|
layout = self.layout
|
||||||
|
armature = self._get_armature(context)
|
||||||
|
|
||||||
|
if armature is None:
|
||||||
|
layout.label(text="未找到Armature对象", icon="ERROR")
|
||||||
|
return
|
||||||
|
|
||||||
|
body_mesh = find_body_mesh(armature)
|
||||||
|
if body_mesh is None:
|
||||||
|
layout.label(text="未找到Body网格对象", icon="ERROR")
|
||||||
|
return
|
||||||
|
|
||||||
|
mesh_data = body_mesh.data
|
||||||
|
if mesh_data is None:
|
||||||
|
layout.label(text="Body网格数据无效", icon="ERROR")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 显示材质统计信息
|
||||||
|
box = layout.box()
|
||||||
|
box.label(text="材质列表", icon="MATERIAL")
|
||||||
|
|
||||||
|
# 收集材质信息
|
||||||
|
materials_info: list[MaterialInfo] = []
|
||||||
|
for i, material in enumerate(mesh_data.materials):
|
||||||
|
if material is not None:
|
||||||
|
face_count = get_material_face_count(mesh_data, i)
|
||||||
|
is_skin = is_skin_material(material.name)
|
||||||
|
materials_info.append(
|
||||||
|
MaterialInfo(
|
||||||
|
name=material.name,
|
||||||
|
index=i,
|
||||||
|
face_count=face_count,
|
||||||
|
is_skin=is_skin,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 显示材质列表
|
||||||
|
has_non_skin = False
|
||||||
|
for mat_info in materials_info:
|
||||||
|
row = box.row()
|
||||||
|
if mat_info.is_skin:
|
||||||
|
row.label(text=f"[皮肤] {mat_info.name}", icon="CHECKMARK")
|
||||||
|
else:
|
||||||
|
row.label(text=f"[非皮肤] {mat_info.name}", icon="MESH_DATA")
|
||||||
|
has_non_skin = True
|
||||||
|
row.label(text=f"{mat_info.face_count} 面")
|
||||||
|
|
||||||
|
# 显示分离操作按钮
|
||||||
|
layout.separator()
|
||||||
|
col = layout.column()
|
||||||
|
col.enabled = has_non_skin
|
||||||
|
col.operator(
|
||||||
|
"vrm.separate_non_skin_faces",
|
||||||
|
text="分离非皮肤面",
|
||||||
|
icon="MESH_DATA",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not has_non_skin:
|
||||||
|
layout.label(text="没有非皮肤材质可分离", icon="INFO")
|
||||||
60
editor/body_mesh/test_utils.py
Normal file
60
editor/body_mesh/test_utils.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
"""
|
||||||
|
Property-based tests for body_mesh utility functions.
|
||||||
|
|
||||||
|
Feature: vrm-body-mesh-separator
|
||||||
|
"""
|
||||||
|
from hypothesis import given, settings
|
||||||
|
from hypothesis import strategies as st
|
||||||
|
|
||||||
|
from .utils import generate_unique_name, is_skin_material
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsSkinMaterial:
|
||||||
|
"""
|
||||||
|
Property 2: 材质分类正确性
|
||||||
|
*For any* 材质名称字符串,`is_skin_material`函数应该返回True当且仅当该名称包含"SKIN"子字符串(不区分大小写)。
|
||||||
|
**Validates: Requirements 2.2, 2.3**
|
||||||
|
"""
|
||||||
|
|
||||||
|
@given(st.text())
|
||||||
|
@settings(max_examples=100)
|
||||||
|
def test_skin_material_classification_property(self, material_name: str) -> None:
|
||||||
|
"""
|
||||||
|
Feature: vrm-body-mesh-separator, Property 2: 材质分类正确性
|
||||||
|
For any material name string, is_skin_material returns True
|
||||||
|
if and only if the name contains "SKIN" (case-insensitive).
|
||||||
|
"""
|
||||||
|
result = is_skin_material(material_name)
|
||||||
|
expected = "SKIN" in material_name.upper()
|
||||||
|
assert result == expected, (
|
||||||
|
f"is_skin_material('{material_name}') returned {result}, "
|
||||||
|
f"expected {expected}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGenerateUniqueName:
|
||||||
|
"""
|
||||||
|
Property 6: 唯一命名保证
|
||||||
|
*For any* 基础名称和已存在名称集合,`generate_unique_name`返回的名称不应该在已存在名称集合中。
|
||||||
|
**Validates: Requirements 4.2**
|
||||||
|
"""
|
||||||
|
|
||||||
|
@given(
|
||||||
|
st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
|
||||||
|
st.sets(st.text(min_size=1, max_size=50), max_size=20),
|
||||||
|
)
|
||||||
|
@settings(max_examples=100)
|
||||||
|
def test_unique_name_generation_property(
|
||||||
|
self, base_name: str, existing_names: set[str]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Feature: vrm-body-mesh-separator, Property 6: 唯一命名保证
|
||||||
|
For any base name and set of existing names, generate_unique_name
|
||||||
|
returns a name that is not in the existing names set.
|
||||||
|
"""
|
||||||
|
result = generate_unique_name(base_name, existing_names)
|
||||||
|
assert result not in existing_names, (
|
||||||
|
f"generate_unique_name('{base_name}', {existing_names}) "
|
||||||
|
f"returned '{result}' which is in existing_names"
|
||||||
|
)
|
||||||
88
editor/body_mesh/utils.py
Normal file
88
editor/body_mesh/utils.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bpy.types import Mesh, Object
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MaterialInfo:
|
||||||
|
"""材质信息数据类"""
|
||||||
|
name: str
|
||||||
|
index: int
|
||||||
|
face_count: int
|
||||||
|
is_skin: bool
|
||||||
|
|
||||||
|
|
||||||
|
def find_body_mesh(armature: "Object") -> Optional["Object"]:
|
||||||
|
"""
|
||||||
|
在Armature的子对象中查找名为"Body"的网格对象
|
||||||
|
|
||||||
|
Args:
|
||||||
|
armature: Armature对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Body网格对象,如果不存在则返回None
|
||||||
|
"""
|
||||||
|
if armature is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for child in armature.children:
|
||||||
|
if child.type == "MESH" and child.name == "Body":
|
||||||
|
return child
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_skin_material(material_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
判断材质是否为皮肤材质
|
||||||
|
|
||||||
|
Args:
|
||||||
|
material_name: 材质名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
如果材质名包含"SKIN"则返回True(不区分大小写)
|
||||||
|
"""
|
||||||
|
return "SKIN" in material_name.upper()
|
||||||
|
|
||||||
|
|
||||||
|
def get_material_face_count(mesh: "Mesh", material_index: int) -> int:
|
||||||
|
"""
|
||||||
|
获取指定材质的面数量
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mesh: 网格数据
|
||||||
|
material_index: 材质索引
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
使用该材质的面数量
|
||||||
|
"""
|
||||||
|
count = 0
|
||||||
|
for polygon in mesh.polygons:
|
||||||
|
if polygon.material_index == material_index:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def generate_unique_name(base_name: str, existing_names: set[str]) -> str:
|
||||||
|
"""
|
||||||
|
生成唯一的对象名称
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_name: 基础名称
|
||||||
|
existing_names: 已存在的名称集合
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
唯一的名称
|
||||||
|
"""
|
||||||
|
if base_name not in existing_names:
|
||||||
|
return base_name
|
||||||
|
|
||||||
|
suffix = 1
|
||||||
|
while True:
|
||||||
|
candidate = f"{base_name}.{suffix:03d}"
|
||||||
|
if candidate not in existing_names:
|
||||||
|
return candidate
|
||||||
|
suffix += 1
|
||||||
@@ -43,6 +43,8 @@ from .editor import (
|
|||||||
subscription,
|
subscription,
|
||||||
validation,
|
validation,
|
||||||
)
|
)
|
||||||
|
from .editor.body_mesh import ops as body_mesh_ops
|
||||||
|
from .editor.body_mesh import panel as body_mesh_panel
|
||||||
from .editor.mtoon1 import handler as mtoon1_handler
|
from .editor.mtoon1 import handler as mtoon1_handler
|
||||||
from .editor.mtoon1 import ops as mtoon1_ops
|
from .editor.mtoon1 import ops as mtoon1_ops
|
||||||
from .editor.mtoon1 import panel as mtoon1_panel
|
from .editor.mtoon1 import panel as mtoon1_panel
|
||||||
@@ -267,6 +269,7 @@ classes: list[
|
|||||||
panel.VRM_PT_controller_unsupported_blender_version_warning,
|
panel.VRM_PT_controller_unsupported_blender_version_warning,
|
||||||
panel.VRM_PT_controller,
|
panel.VRM_PT_controller,
|
||||||
panel.VRM_PT_vrm_armature_object_property,
|
panel.VRM_PT_vrm_armature_object_property,
|
||||||
|
body_mesh_panel.VRM_PT_body_mesh_separator,
|
||||||
vrm0_menu.VRM_MT_vrm0_blend_shape_master,
|
vrm0_menu.VRM_MT_vrm0_blend_shape_master,
|
||||||
vrm0_ui_list.VRM_UL_vrm0_first_person_mesh_annotation,
|
vrm0_ui_list.VRM_UL_vrm0_first_person_mesh_annotation,
|
||||||
vrm0_ui_list.VRM_UL_vrm0_blend_shape_bind,
|
vrm0_ui_list.VRM_UL_vrm0_blend_shape_bind,
|
||||||
@@ -430,6 +433,7 @@ classes: list[
|
|||||||
ops.VRM_OT_show_blend_file_compatibility_warning,
|
ops.VRM_OT_show_blend_file_compatibility_warning,
|
||||||
ops.VRM_OT_show_blend_file_addon_compatibility_warning,
|
ops.VRM_OT_show_blend_file_addon_compatibility_warning,
|
||||||
ops.VRM_OT_make_estimated_humanoid_t_pose,
|
ops.VRM_OT_make_estimated_humanoid_t_pose,
|
||||||
|
body_mesh_ops.VRM_OT_separate_non_skin_faces,
|
||||||
validation.VrmValidationError,
|
validation.VrmValidationError,
|
||||||
validation.WM_OT_vrm_validator,
|
validation.WM_OT_vrm_validator,
|
||||||
export_scene.WM_OT_vrm_export_human_bones_assignment,
|
export_scene.WM_OT_vrm_export_human_bones_assignment,
|
||||||
|
|||||||
Reference in New Issue
Block a user