-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathutils.py
8023 lines (6495 loc) · 314 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# BEGIN LICENSE & COPYRIGHT BLOCK.
#
# Copyright (C) 2022-2024 Kiril Strezikozin
# BakeMaster Blender Add-on (version 2.7.0)
#
# This file is a part of BakeMaster Blender Add-on, a plugin for texture
# baking in open-source Blender 3d modelling software.
# The author can be contacted at <[email protected]>.
#
# Redistribution and use for any purpose including personal, educational, and
# commercial, with or without modification, are permitted provided
# that the following conditions are met:
#
# 1. The current acquired License allows copies/redistributions of this
# software be made to UNLIMITED END USER SEATS (OPEN SOURCE LICENSE).
# 2. Redistributions of this source code or partial usage of this source code
# must follow the terms of this license and retain the above copyright
# notice, and the following disclaimer.
# 3. The name of the author may be used to endorse or promote products derived
# from this software. In such a case, a prior written permission from the
# author is required.
#
# This program is free software and is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# You should have received a copy of the GNU General Public License in the
# GNU.txt file along with this program. If not,
# see <http://www.gnu.org/licenses/>.
#
# END LICENSE & COPYRIGHT BLOCK.
import os
import sys
import inspect
import traceback
import functools
from types import ModuleType
from typing import Any, Callable, Literal, Union, Optional
import bpy
import math
import colorsys
from mathutils import Vector
from .labels import BM_Labels
from .presets import (
BM_OT_FULL_OBJECT_Preset_Add,
BM_OT_OBJECT_Preset_Add,
BM_OT_DECAL_Preset_Add,
BM_OT_HL_Preset_Add,
BM_OT_UV_Preset_Add,
BM_OT_CSH_Preset_Add,
BM_OT_OUT_Preset_Add,
BM_OT_FULL_MAP_Preset_Add,
BM_OT_MAP_Preset_Add,
BM_OT_CHNLP_Preset_Add,
BM_OT_BAKE_Preset_Add,
BM_OT_CM_Preset_Add,
)
DEBUG: bool = False
MAP_PREVIEW_MODE: Literal[0, 1] = 0 # 0 - User, 1 - Bake.
"""
Map preview mode. Set by Bake Operator:
0 (User) mode: preview maps using Emmision nodes.
1 (Bake) mode: preview maps using Diffuse BSDF nodes.
Diffuse nodes are used during baking for the final map preview to support map
transparency. Emission nodes output opaque black. Baked image data is confirmed
to be identical.
IMPORTANT: Implemented but not used, COMBINED + Emit pass preserves
transparency for maps with default EMIT bake type.
"""
def debug(
*values: object,
sep: Optional[str] = ' ',
end: Optional[str] = '\n',
file=sys.stdout,
flush: bool = False,
) -> None:
"""
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
"""
return print(
*values,
sep=sep,
end=end,
file=file,
flush=flush
) if DEBUG else None
def _build_dynamic_type_constraints(
*t_prefixes: str,
module: Optional[ModuleType] = bpy.types,
t_parent: Optional[type] = bpy.types.NodeSocketStandard
) -> tuple[type, ...]:
"""
Returns a tuple with bpy.types.NodeSocketStandard types, names of which
start with any of the given prefix strings. Use to declare dynamic type
meta classes.
Optional keyword arguments:
module: module to lookup types in. Default is bpy.types.
t_parent: each type must be a subclass of this type. Pass None to skip.
"""
constraints: tuple[type, ...] = ()
for _T_str in dir(module):
_T = getattr(module, _T_str)
if not isinstance(_T, type):
continue
elif t_parent is not None and not issubclass(_T, t_parent):
continue
if any(_T_str.startswith(prefix) for prefix in t_prefixes):
constraints += (_T,)
return constraints
class _Dynamic_T_Meta():
__t_prefixes: tuple[str, ...]
__constraints__: tuple[type, ...]
@classmethod
def constraints(cls) -> tuple[type, ...]:
return cls.__constraints__
def __instancecheck__(self, instance: Any) -> bool:
"""Implement isinstance(instance, cls). Time complexity is O(n)"""
return type(instance) in self.__constraints__
def __subclasscheck__(self, subclass: Any) -> bool:
"""Implement issubclass(subclass, cls). Time complexity is O(n)"""
return subclass in self.__constraints__
def __repr__(self) -> str:
return "<class '" + self.__name__ + "'> TypeClass of: " + str(
self.__constraints__)
class _Dynamic_NodeSocket_T():
@property
def default_value(self) -> Any:
"""
Input value used for unconnected socket.
TypeClass has this property for clarity only. There are no type checks.
The actual value is set at runtime for a real NodeSocketStandard
instance.
"""
return self.__default_value
@default_value.setter
def default_value(self, value: Any) -> None:
"""
Input value used for unconnected socket.
TypeClass has this property for clarity only. There are no type checks.
The actual value is set at runtime for a real NodeSocketStandard
instance.
"""
self.__default_value = value
return None
@property
def links(self) -> tuple:
"""
List of node links from or to this socket.
Read-only. Takes O(len(nodetree.links)) time on runtime.
TypeClass has this property for clarity only. The actual value is
updated at runtime for a real NodeSocketStandard instance.
"""
return ()
class _NodeSocketBakeable_Meta(
_Dynamic_T_Meta,
type(bpy.types.NodeSocketStandard)
):
__t_prefixes = (
'NodeSocketBool',
'NodeSocketFloat',
'NodeSocketVector',
'NodeSocketInt',
'NodeSocketColor'
)
__constraints__ = _build_dynamic_type_constraints(*__t_prefixes)
class NodeSocketBakeable(
bpy.types.NodeSocketStandard,
metaclass=_NodeSocketBakeable_Meta
):
"""
TypeClass with bakeable NodeSocket types as constraints.
Use for type annotations and to check instancing, subclassing.
"""
pass
class _NodeSocketVectorLike_Meta(
_Dynamic_T_Meta,
type(bpy.types.NodeSocketStandard)
):
__constraints__ = _build_dynamic_type_constraints('NodeSocketVector')
class NodeSocketVectorLike(
bpy.types.NodeSocketStandard,
metaclass=_NodeSocketVectorLike_Meta
):
"""
TypeClass with Vector-like NodeSocket types as constraints.
Use for type annotations and to check instancing, subclassing.
"""
pass
class DefaultPreset_Apply:
"""
Apply default preset to the new item.
"""
@classmethod
def poll(cls, context: bpy.types.Context) -> bool:
bm_props = context.scene.bm_props
return bm_props.global_presets_use_default
@classmethod
def search_preset_path(cls, subdir: str, preset_name: str) -> str:
ext_valid = {".py", ".xml"}
filter_ext = lambda ext: ext.lower() in ext_valid
filter_path = None
display_name = lambda name: bpy.path.display_name(
name, title_case=False)
searchpaths = bpy.utils.preset_paths(subdir)
files = []
for directory in searchpaths:
files.extend([
(f, os.path.join(directory, f))
for f in os.listdir(directory)
if (not f.startswith("."))
if ((filter_ext is None)
or (filter_ext(os.path.splitext(f)[1])))
if ((filter_path is None)
or (filter_path(f)))
])
for f, filepath in files:
name = display_name(
filepath) if display_name else bpy.path.display_name(f)
if name == preset_name:
return filepath
return ""
@classmethod
def execute(cls, context, ops: list[tuple[type, str, bool]]) -> None:
bm_props = context.scene.bm_props
if not cls.poll(context):
return None
for op, master_tag, verif in ops:
if not verif:
continue
prop_name = "p_master_" + master_tag
preset_name = getattr(bm_props, prop_name)
if preset_name == "":
continue
filepath = cls.search_preset_path(op.preset_subdir, preset_name)
if filepath == "":
continue
menu_idname = op.preset_menu
preset_label = op.bl_label
print("Executing default preset: '",
preset_name, "' (filepath=", filepath, ")", sep="")
bpy.ops.bakemaster.execute_preset_bakemaster(
filepath=filepath,
# `prop_name` below is the master prop (storing the default
# preset), the recently used preset name won't update.
# To force it to update, use: `"p_ln_" + master_tag`, but this
# may be unwanted since the default preset is executed
# internally, thus the UI only keeps reflecting the user's
# actions.
prop_name=prop_name,
preset_name=preset_name,
menu_idname=menu_idname,
preset_label=preset_label,
single_item=True
)
return None
@classmethod
def obj(cls, context: bpy.types.Context, item) -> None:
ops: list[tuple[type, str, bool]] = [
(BM_OT_FULL_OBJECT_Preset_Add, "fullobj", True),
(BM_OT_OBJECT_Preset_Add, "obj", True),
(BM_OT_DECAL_Preset_Add, "decal", True),
(BM_OT_HL_Preset_Add, "hl", not item.hl_use_unique_per_map),
(BM_OT_UV_Preset_Add, "uv", not item.uv_use_unique_per_map),
(BM_OT_CSH_Preset_Add, "csh", True),
(BM_OT_OUT_Preset_Add, "out", not item.out_use_unique_per_map),
(BM_OT_FULL_MAP_Preset_Add, "fullmap", True),
(BM_OT_BAKE_Preset_Add, "bake", True),
]
cls.execute(context, ops)
return None
@classmethod
def map(cls, context: bpy.types.Context, parent, _item) -> None:
ops: list[tuple[type, str, bool]] = [
(BM_OT_MAP_Preset_Add, "map", True),
(BM_OT_OUT_Preset_Add, "out", parent.out_use_unique_per_map),
(BM_OT_HL_Preset_Add, "hl", parent.hl_use_unique_per_map),
(BM_OT_UV_Preset_Add, "uv", parent.uv_use_unique_per_map),
]
cls.execute(context, ops)
return None
@classmethod
def channel_pack(cls, context: bpy.types.Context, _item) -> None:
ops: list[tuple[type, str, bool]] = [
(BM_OT_CHNLP_Preset_Add, "chnlp", True),
]
cls.execute(context, ops)
return None
###############################################################
### BM Gets Funcs ###
###############################################################
def BM_Object_Get(self, context):
if self is None:
object = [
context.scene.bm_table_of_objects[context.scene.bm_props.global_active_index], True]
else:
if hasattr(self, "global_map_object_index"):
object1 = context.scene.bm_table_of_objects[self.global_map_object_index]
else:
object1 = self
object = [object1, True]
try:
context.scene.objects[object[0].global_object_name]
except (KeyError, AttributeError, UnboundLocalError):
object[1] = False
return object
def BM_Map_Get(self, object):
if self is not None and hasattr(self, "global_map_object_index"):
return self
map = object.global_maps[object.global_maps_active_index]
return map
###############################################################
### Name Matching Funcs ###
###############################################################
def BM_Table_of_Objects_NameMatching_GetAllObjectNames(context):
names = []
for object in context.scene.bm_table_of_objects:
names.append(object.global_object_name)
return names
def BM_Table_of_Objects_NameMatching_GenerateNameChunks(name: str):
chunks = []
chunk_start_index = 0
for index, char in enumerate(name):
if char == "_":
chunks.append(name[chunk_start_index:index])
chunk_start_index = index + 1
if index == len(name) - 1:
chunks.append(name[chunk_start_index:len(name)])
return [chunk for chunk in chunks if chunk.replace(" ", "") != ""]
def BM_Table_of_Objects_NameMatching_GetNameChunks(chunks: list, combine_type: str, context=None):
# get prefixes
lowpoly_prefix_raw = context.scene.bm_props.global_lowpoly_tag
highpoly_prefix_raw = context.scene.bm_props.global_highpoly_tag
cage_prefix_raw = context.scene.bm_props.global_cage_tag
decal_prefix_raw = context.scene.bm_props.global_decal_tag
combined_name = []
if combine_type == 'ROOT':
for index, chunk in enumerate(chunks):
if chunk in [lowpoly_prefix_raw, highpoly_prefix_raw, cage_prefix_raw]:
break
else:
combined_name.append(chunk)
elif combine_type == 'TALE':
combine_from_index = 0
for index, chunk in enumerate(chunks):
if chunk in [lowpoly_prefix_raw, highpoly_prefix_raw, cage_prefix_raw]:
combine_from_index = index + 1
break
for index in range(combine_from_index, len(chunks)):
combined_name.append(chunks[index])
elif combine_type == 'FULL':
for chunk in chunks:
combined_name.append(chunk)
return combined_name
def BM_Table_of_Objects_NameMatching_CombineToRaw(chunked_name: str):
combined_name = ""
for chunk in chunked_name:
combined_name += chunk + "_"
return combined_name[:-1]
def BM_Table_of_Objects_NameMatching_IndexesIntersaction(indexes: list):
# removing empty shells
indexes = [shell for shell in indexes if len(shell) != 0]
intersaction = []
# loop through every number
for shell in indexes:
for number in shell:
# count how many times this number is present
number_presents = len(
[shell1 for shell1 in indexes if number in shell1])
# if that count = len of all shells -> presented in all shells
if number_presents == len(indexes):
intersaction.append(number)
# remove duplicates and return
return list(dict.fromkeys(intersaction))
def BM_Table_of_Objects_NameMatching_CombineGroups(groups: list):
sorted_groups = []
groups_lens = []
combined = []
# sorting each groups' shell and all shells by their len
for group in groups:
sorted_groups.append(sorted(group))
groups_lens.append(len(group))
sorted_groups = [g for _, g in sorted(
zip(groups_lens, sorted_groups), reverse=False)]
combined = [list(g) for g in sorted_groups]
# remove repetitive indexes
deleted = []
for index, group in enumerate(sorted_groups):
deleted.append([])
for n_index, number in enumerate(group):
for index_1, group_1 in enumerate(sorted_groups):
if number in group_1 and index_1 != index:
try:
# if index was already deleted
deleted[index].index(number)
except (IndexError, ValueError):
try:
# if found was already deleted
deleted[index_1].index(number)
except (IndexError, ValueError):
# delete the index
deleted[index].append(number)
del combined[index][combined[index].index(number)]
break
else:
pass
else:
continue
# return non-empty groups
return [group for group in combined if len(group)]
def BM_Table_of_Objects_NameMatching_Construct(context, objects_names_input):
# funcs pointers
NameChunks = BM_Table_of_Objects_NameMatching_GenerateNameChunks
GetChunks = BM_Table_of_Objects_NameMatching_GetNameChunks
Intersaction = BM_Table_of_Objects_NameMatching_IndexesIntersaction
CombineToRaw = BM_Table_of_Objects_NameMatching_CombineToRaw
CombineGroups = BM_Table_of_Objects_NameMatching_CombineGroups
# get prefixes
lowpoly_prefix_raw = context.scene.bm_props.global_lowpoly_tag
highpoly_prefix_raw = context.scene.bm_props.global_highpoly_tag
cage_prefix_raw = context.scene.bm_props.global_cage_tag
decal_prefix_raw = context.scene.bm_props.global_decal_tag
roots = []
detached = []
# calculating roots[]
used_obj_names = []
# loop through all names
for object_name in objects_names_input:
# if name contains 'lowpoly'
if lowpoly_prefix_raw in NameChunks(object_name):
used_obj_names.append(object_name)
# create root_name
object_name_chunked = NameChunks(object_name)
root_name = GetChunks(object_name_chunked, 'ROOT', context)
tale_name = GetChunks(object_name_chunked, 'TALE', context)
# find any objects in the input_objects with the same root_name and tale_name
pairs = [object_name]
for object_name_pair in objects_names_input:
if object_name_pair in used_obj_names:
continue
pair_name_chunked = NameChunks(object_name_pair)
pair_name_chunked_no_decal = NameChunks(object_name_pair)
try:
pair_name_chunked_no_decal.remove(decal_prefix_raw)
except ValueError:
pass
pair_root = GetChunks(pair_name_chunked, 'ROOT', context)
pair_tale = GetChunks(
pair_name_chunked_no_decal, 'TALE', context)
if pair_root == root_name and pair_tale == tale_name:
# add it to the current shell
pairs.append(object_name_pair)
used_obj_names.append(object_name_pair)
# add current shell to the roots[]
roots.append([root_name, pairs])
# try pairing highs and cages that are left to lowpolies
for root in roots:
for object_name in objects_names_input:
if object_name in root[1] or object_name in used_obj_names:
continue
if highpoly_prefix_raw in NameChunks(object_name) or cage_prefix_raw in NameChunks(object_name):
object_name_chunked = NameChunks(object_name)
try:
object_name_chunked.remove(decal_prefix_raw)
except ValueError:
pass
root_name = GetChunks(object_name_chunked, 'ROOT', context)
if CombineToRaw(root_name).find(CombineToRaw(root[0])) == 0:
used_obj_names.append(object_name)
root[1].append(object_name)
# roots with no pairs - add to detached[], and items not added to roots as well
detached = [
object_name for root in roots for object_name in root[1] if len(root[1]) <= 1]
for object_name in objects_names_input:
if len([object_name for root in roots if object_name in root[1]]) == 0:
# object_name is nowhere in roots
detached.append(object_name)
# recalculate roots
roots = [root for root in roots if len(root[1]) > 1]
# sorting roots from shortest root to longest
roots_lens = []
for root in roots:
roots_lens.append(len(CombineToRaw(root[0])))
roots = [root for _, root in sorted(zip(roots_lens, roots), reverse=False)]
# grouping roots (finding all pairs)
used_root_indexes = []
groups = []
for index, shell in enumerate(roots):
# add current shell to checked
used_root_indexes.append(index)
root_chunked = shell[0]
root_matched_chunks_indexes = []
# loop through chunked root_name
for chunk_index, chunk in enumerate(root_chunked):
root_matched_chunks_indexes.append([index])
# loop through all roots
for index_pair, shell_pair in enumerate(roots):
root_pair_chunked = shell_pair[0]
# if found root contains the chunk
try:
root_pair_chunked.index(chunk)
except ValueError:
continue
else:
# found root chunk is on the same place as the root_name chunk -> found pair
if root_pair_chunked.index(chunk) == chunk_index:
root_matched_chunks_indexes[chunk_index].append(
index_pair)
used_root_indexes.append(index_pair)
# add intersaction of matched roots indexes to the groups[] shell
groups.append(Intersaction(root_matched_chunks_indexes))
# groups checked_combine
# combine repetitive groups' shells
groups = CombineGroups(groups)
# return
return groups, roots, detached
def BM_Table_of_Objects_NameMatching_Deconstruct(context):
to_remove = []
for index, object in enumerate(context.scene.bm_table_of_objects):
if any([object.nm_is_universal_container, object.nm_is_local_container]):
to_remove.append(index)
object.nm_is_detached = False
object.nm_master_index = -1
object.nm_container_name_old = ""
object.nm_container_name = ""
object.nm_this_indent = 0
object.nm_is_universal_container = False
object.nm_is_local_container = False
object.nm_is_expanded = True
object.nm_item_container = ""
object.nm_container_items = []
for index in to_remove[::-1]:
context.scene.bm_table_of_objects.remove(index)
context.scene.bm_props.global_active_index = 0
def BM_Table_of_Objects_NameMatching_UpdateAllNMIndexes(context):
uni_index = -1
local_index = -1
item_index = -1
for object in context.scene.bm_table_of_objects:
if object.nm_is_universal_container:
uni_index += 1
local_index = -1
item_index = -1
object.nm_master_index = uni_index
elif object.nm_is_local_container:
local_index += 1
item_index = -1
object.nm_master_index = local_index
object.nm_item_uni_container_master_index = uni_index
elif object.nm_is_detached is False:
item_index += 1
object.nm_master_index = item_index
object.nm_item_local_container_master_index = local_index
object.nm_item_uni_container_master_index = uni_index
else:
uni_index += 1
local_index = -1
item_index = -1
object.nm_master_index = uni_index
# NameMatching Update
def BM_SCENE_PROPS_global_use_name_matching_Update(self, context):
if len(context.scene.bm_table_of_objects) == 0:
return
GetAllObjectNames = BM_Table_of_Objects_NameMatching_GetAllObjectNames
NameChunks = BM_Table_of_Objects_NameMatching_GenerateNameChunks
CombineToRaw = BM_Table_of_Objects_NameMatching_CombineToRaw
# get prefixes
lowpoly_prefix_raw = context.scene.bm_props.global_lowpoly_tag
highpoly_prefix_raw = context.scene.bm_props.global_highpoly_tag
cage_prefix_raw = context.scene.bm_props.global_cage_tag
decal_prefix_raw = context.scene.bm_props.global_decal_tag
# trash texsets
to_remove = []
for index, item in enumerate(context.scene.bm_props.global_texturesets_table):
to_remove.append(index)
for index in to_remove[::-1]:
context.scene.bm_props.global_texturesets_table.remove(index)
if self.global_use_name_matching is True:
# trash all highpolies and unset cages
for object in context.scene.bm_table_of_objects:
object.hl_use_cage = False
object.hl_use_unique_per_map = False
BM_ITEM_PROPS_hl_use_unique_per_map_Update_TrashHighpolies(
object, object, context)
object.hl_is_lowpoly = False
object.hl_is_cage = False
object.hl_is_highpoly = False
object.hl_is_decal = False
BM_Table_of_Objects_NameMatching_Deconstruct(context)
# get groups, roots, detached from construct
groups, roots, detached = BM_Table_of_Objects_NameMatching_Construct(
context, GetAllObjectNames(context))
# trash all from bm_table_of_objects
to_remove = []
for index, item in enumerate(context.scene.bm_table_of_objects):
to_remove.append(index)
context.scene.bm_props.global_active_index = 0
for index in to_remove[::-1]:
context.scene.bm_table_of_objects.remove(index)
last_uni_c_index = 0
# constructing Table_of_Objects items
for index, shell in enumerate(groups):
# adding universal container to the bm_table_of_objects
universal_container = context.scene.bm_table_of_objects.add()
# Default Preset
context.scene.bm_props.global_active_index = len(context.scene.bm_table_of_objects) - 1
DefaultPreset_Apply.obj(context, universal_container)
universal_container.nm_master_index = index
last_uni_c_index = index
# name is set to the root_name of the first object in the shell
universal_container.nm_container_name_old = BM_ITEM_PROPS_nm_container_name_GlobalUpdate_OnCreate(
context, CombineToRaw(roots[shell[0]][0]))
universal_container.nm_container_name = universal_container.nm_container_name_old
universal_container.nm_this_indent = 0
universal_container.nm_is_universal_container = True
universal_container.nm_is_expanded = True
# objs[] : 0 - lowpolies, 1 - highpolies, 2 - cages
object_names = [[], [], []]
for number in shell:
# adding each object_name in the root objects to matched categories
# based on if their names contain low_ high_ cage_ prefixes
for object_name in roots[number][1]:
try:
NameChunks(object_name).index(lowpoly_prefix_raw)
except ValueError:
pass
else:
object_names[0].append(object_name)
try:
NameChunks(object_name).index(highpoly_prefix_raw)
except ValueError:
pass
else:
object_names[1].append(object_name)
try:
NameChunks(object_name).index(cage_prefix_raw)
except ValueError:
pass
else:
object_names[2].append(object_name)
# adding local containers to the bm_table_of_objects if needed
# and adding all object_name in object_names to the bm_table_of_objects
names_starters = ["Lowpolies", "Highpolies", "Cages"]
prefix_props = ["hl_is_lowpoly", "hl_is_highpoly", "hl_is_cage"]
container_types_props = [
"nm_is_lowpoly_container", "nm_is_highpoly_container", "nm_is_cage_container"]
local_containers_index = -1
for local_index, local_names in enumerate(object_names):
if len(local_names):
local_containers_index += 1
local_container = context.scene.bm_table_of_objects.add()
# Default Preset
context.scene.bm_props.global_active_index = len(context.scene.bm_table_of_objects) - 1
DefaultPreset_Apply.obj(context, local_container)
local_container.nm_master_index = local_containers_index
local_container.nm_container_name_old = names_starters[local_index]
local_container.nm_container_name = names_starters[local_index]
local_container.nm_this_indent = 1
local_container.nm_is_local_container = True
local_container.nm_item_uni_container_master_index = index
local_container.nm_is_expanded = True
setattr(local_container,
container_types_props[local_index], True)
for obj_index, object_name in enumerate(local_names):
# do not add detached names
if object_name in detached:
continue
new_item = context.scene.bm_table_of_objects.add()
# Default Preset
context.scene.bm_props.global_active_index = len(context.scene.bm_table_of_objects) - 1
DefaultPreset_Apply.obj(context, new_item)
new_item.global_object_name = object_name
new_item.nm_master_index = obj_index
new_item.nm_this_indent = 2
new_item.nm_item_uni_container_master_index = index
new_item.nm_item_local_container_master_index = local_index
new_item.nm_is_expanded = True
# setattr(new_item, prefix_props[local_index], True)
# auto configure decals, highpolies, and cages
universal_container.nm_uni_container_is_global = True
# adding detached as regular items
last_uni_c_index += 1
for index, object_name in enumerate(detached):
new_item = context.scene.bm_table_of_objects.add()
# Default Preset
context.scene.bm_props.global_active_index = len(context.scene.bm_table_of_objects) - 1
DefaultPreset_Apply.obj(context, new_item)
new_item.global_object_name = object_name
new_item.nm_is_detached = True
new_item.nm_master_index = index + last_uni_c_index
new_item.nm_is_expanded = True
# update uni containers names
for index, object in enumerate(context.scene.bm_table_of_objects):
if object.nm_is_universal_container:
object.nm_container_name = BM_ITEM_PROPS_nm_container_name_GlobalUpdate_OnCreate(
context, object.nm_container_name, index)
else:
# trash all highpolies and unset cages
for object in context.scene.bm_table_of_objects:
object.hl_use_cage = False
object.hl_use_unique_per_map = False
BM_ITEM_PROPS_hl_use_unique_per_map_Update_TrashHighpolies(
object, object, context)
object.hl_is_lowpoly = False
object.hl_is_cage = False
object.hl_is_highpoly = False
object.hl_is_decal = False
BM_Table_of_Objects_NameMatching_Deconstruct(context)
def BM_ITEM_PROPS_nm_container_name_Update(self, context):
# avoid setting name that already exists in the bm_table
if self.nm_is_local_container:
return
if self.nm_container_name != self.nm_container_name_old:
wrong_name = False
for index, object in enumerate(context.scene.bm_table_of_objects):
if object == self:
continue
if context.scene.bm_props.global_use_name_matching and object.nm_container_name == self.nm_container_name:
wrong_name = True
break
elif object.global_object_name == self.nm_container_name:
wrong_name = True
break
if wrong_name:
self.nm_container_name = self.nm_container_name_old
else:
self.nm_container_name_old = self.nm_container_name
def BM_ITEM_PROPS_nm_container_name_GlobalUpdate_OnCreate(context, name, index=-1):
# when creating new container, make sure its name is unique
name_index = 0
for object_index, object in enumerate(context.scene.bm_table_of_objects):
if object_index == index:
continue
if object.global_object_name == name:
name_index += 1
if name_index == 0:
return name
name_index_str = str(name_index)
name_zeros = "0" * (3 - len(name_index_str)
) if len(name_index_str) < 3 else ""
return "{}.{}{}".format(name, name_zeros, str(name_index_str))
def BM_ITEM_PROPS_nm_uni_container_is_global_Update(self, context):
if self.nm_uni_container_is_global:
self.hl_use_cage = True
container_objects = []
for object in context.scene.bm_table_of_objects:
if object.nm_item_uni_container_master_index == self.nm_master_index and object.nm_is_local_container is False:
container_objects.append(object.global_object_name)
# trash highpolies and unset cage
object.hl_use_cage = False
object.hl_use_unique_per_map = False
BM_ITEM_PROPS_hl_use_unique_per_map_Update_TrashHighpolies(
object, object, context)
object.hl_is_lowpoly = False
object.hl_is_cage = False
object.hl_is_highpoly = False
object.hl_is_decal = False
object.decal_is_decal = False
_, roots, detached = BM_Table_of_Objects_NameMatching_Construct(
context, container_objects)
GetChunks = BM_Table_of_Objects_NameMatching_GenerateNameChunks
# get prefixes
lowpoly_prefix_raw = context.scene.bm_props.global_lowpoly_tag
highpoly_prefix_raw = context.scene.bm_props.global_highpoly_tag
cage_prefix_raw = context.scene.bm_props.global_cage_tag
decal_prefix_raw = context.scene.bm_props.global_decal_tag
# decal objects are likely to be dropped into detached
for detached_name in detached:
detached_sources = [index for index, object in enumerate(
context.scene.bm_table_of_objects) if object.global_object_name == detached_name]
detached_object = None
if len(detached_sources):
detached_object = context.scene.bm_table_of_objects[detached_sources[0]]
context.scene.bm_props.global_active_index = detached_sources[0]
else:
continue
# set object as decal object
if decal_prefix_raw in GetChunks(detached_name):
detached_object.decal_is_decal = True
for root in roots:
# root[0] - root_name chunks
# root[1] - objects' names
# get object name, source object
lowpoly_object_name = root[1][0]
lowpoly_sources = [index for index, object in enumerate(
context.scene.bm_table_of_objects) if object.global_object_name == lowpoly_object_name]
lowpoly_object = None
if len(lowpoly_sources):
lowpoly_object = context.scene.bm_table_of_objects[lowpoly_sources[0]]
context.scene.bm_props.global_active_index = lowpoly_sources[0]
else:
continue
# set object as decal object, do not set highpolies and cage
if decal_prefix_raw in GetChunks(lowpoly_object_name):
lowpoly_object.decal_is_decal = True
continue
highpolies = []
cage = "NONE"
marked_decals = []
# find highpolies, cage
for object_name in root[1]:
if object_name == lowpoly_object_name:
continue
object_name_chunked = GetChunks(object_name)
# found highpoly
if highpoly_prefix_raw in object_name_chunked:
highpolies.append(object_name)
if decal_prefix_raw in object_name_chunked:
marked_decals.append(1)
else:
marked_decals.append(0)
# found cage
elif cage_prefix_raw in object_name_chunked and cage == "NONE":
cage = object_name
# add highpolies
lowpoly_object.hl_highpoly_table_active_index = 0
for highpoly_index, highpoly in enumerate(highpolies):
new_highpoly = lowpoly_object.hl_highpoly_table.add()
new_highpoly.global_holder_index = lowpoly_sources[0]
new_highpoly.global_item_index = highpoly_index + 1
# try:
BM_ITEM_PROPS_hl_add_highpoly_Update(new_highpoly, context)
new_highpoly.global_object_name = highpoly
lowpoly_object.hl_highpoly_table_active_index = len(
lowpoly_object.hl_highpoly_table) - 1
lowpoly_object.hl_is_lowpoly = True
# except TypeError:
# lowpoly_object.hl_highpoly_table.remove(highpoly_index)
# pass
# mark highpoly source object as decal if decal tag had been found previously
if marked_decals[highpoly_index] == 1 and new_highpoly.global_highpoly_object_index != -1:
context.scene.bm_table_of_objects[new_highpoly.global_highpoly_object_index].hl_is_decal = True
# set cage
if cage != "NONE" and len(highpolies) != 0:
try:
lowpoly_object.hl_use_cage = True
lowpoly_object.hl_cage = cage
except TypeError:
lowpoly_object.hl_use_cage = False
else:
data = {
# 'decal_is_decal' : self.decal_is_decal,
'decal_use_custom_camera': self.decal_use_custom_camera,
'decal_custom_camera': self.decal_custom_camera,
'decal_upper_coordinate': self.decal_upper_coordinate,
'decal_rotation': self.decal_rotation,
'decal_use_flip_vertical': self.decal_use_flip_vertical,
'decal_use_flip_horizontal': self.decal_use_flip_horizontal,
'decal_use_adapt_res': self.decal_use_adapt_res,
'decal_use_precise_bounds': self.decal_use_precise_bounds,
'decal_boundary_offset': self.decal_boundary_offset,
'hl_use_bake_individually': self.hl_use_bake_individually,
'hl_decals_use_separate_texset': self.hl_decals_use_separate_texset,
'hl_decals_separate_texset_prefix': self.hl_decals_separate_texset_prefix,
# 'hl_use_cage' : self.hl_use_cage,
'hl_cage_type': self.hl_cage_type,
'hl_cage_extrusion': self.hl_cage_extrusion,
'hl_max_ray_distance': self.hl_max_ray_distance,
# 'hl_use_unique_per_map' : self.hl_use_unique_per_map,
'uv_bake_data': self.uv_bake_data,
'uv_bake_target': self.uv_bake_target,
'uv_type': self.uv_type,
'uv_snap_islands_to_pixels': self.uv_snap_islands_to_pixels,