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
|
#!/usr/bin/env python3
def getObjectIds(fx):
fx_ids = set()
with open(fx, "r") as f:
for line in f:
parts = line.split()
if line.startswith("---") and len(parts) == 3:
obj_id = parts[2]
# Remove & from beginning of object ID.
obj_id = obj_id[1:]
fx_ids.add(obj_id)
return fx_ids
def getOldIds(old_fx_ids, unified_fx_ids):
id_mapping = {}
for old_id in old_fx_ids:
id_mapping[old_id] = str(old_id)
unified_fx_ids.add(int(old_id))
return id_mapping
# Map an object ID from old namespace to unified.
def getNewIds(old_fx_ids, unified_fx_ids):
id_mapping = {}
for old_id in old_fx_ids:
new_id = int(old_id)
# 9100000 is the ID of the animator. There's only one, and we take care
# to merge it. So no need to create a new identifier for it.
if new_id != 9100000:
while (new_id in unified_fx_ids or new_id in old_fx_ids):
new_id += 1
unified_fx_ids.add(new_id)
id_mapping[old_id] = str(new_id)
return id_mapping
def replaceIds(old_str, id_map):
result = old_str
lines = []
for line in result.split("\n"):
for old_id, new_id in id_map.items():
line = line.replace(old_id, new_id)
lines.append(line)
return '\n'.join(lines)
def cat(path):
lines = []
with open(path, "r") as f:
for line in f:
lines.append(line)
return ''.join(lines)
# Extract the first block whose '---' line matches `prefix`.
def extractFirstBlock(fx, begin_prefix, end_prefix):
lines = []
in_block = False
for line in fx.split("\n"):
if in_block and line.startswith(end_prefix):
break
if line.startswith(begin_prefix):
in_block = True
if in_block:
lines.append(line)
return '\n'.join(lines) + "\n"
def mergeAnimatorControllers(fx0, fx1, fx0_ids, fx1_ids):
fx0_anim = extractFirstBlock(cat(fx0), "--- !u!91", "---")
fx1_anim = extractFirstBlock(cat(fx1), "--- !u!91", "---")
fx0_anim_params = extractFirstBlock(fx0_anim, " m_AnimatorParameters:", " m_AnimatorLayers:")
fx0_anim_params = "\n".join(fx0_anim_params.split("\n")[1:])
fx0_anim_params = replaceIds(fx0_anim_params, fx0_ids)
fx0_anim_layers = extractFirstBlock(fx0_anim, " m_AnimatorLayers:", "---")
fx0_anim_layers = "\n".join(fx0_anim_layers.split("\n")[1:])
fx0_anim_layers = replaceIds(fx0_anim_layers, fx0_ids)
fx1_anim_params = extractFirstBlock(fx1_anim, " m_AnimatorParameters:", " m_AnimatorLayers:")
fx1_anim_params = "\n".join(fx1_anim_params.split("\n")[1:])
fx1_anim_params = replaceIds(fx1_anim_params, fx1_ids)
fx1_anim_layers = extractFirstBlock(fx1_anim, " m_AnimatorLayers:", "---")
fx1_anim_layers = "\n".join(fx1_anim_layers.split("\n")[1:])
fx1_anim_layers = replaceIds(fx1_anim_layers, fx1_ids)
result = """
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: FX
serializedVersion: 5
m_AnimatorParameters:
"""[1:]
result += fx0_anim_params
result += fx1_anim_params
result += " m_AnimatorLayers:\n"
result += fx0_anim_layers
result += fx1_anim_layers
return result
# Merge two FX layers.
# fx0, fx1 are both paths to animators.
# The object IDs for fx1 are reassigned to not collide with those used by fx0.
def mergeFX(fx0, fx1):
fx0_ids = getObjectIds(fx0)
fx1_ids = getObjectIds(fx1)
# Get unique identifiers for all objects in both layers.
new_ids = set()
fx0_id_mapping = getOldIds(fx0_ids, new_ids)
fx1_id_mapping = getNewIds(fx1_ids, new_ids)
# Merge animators
anim_ctrl = mergeAnimatorControllers(fx0, fx1, fx0_id_mapping, fx1_id_mapping)
# Remove animators and prefix
fx0_str = cat(fx0)
fx1_str = cat(fx1)
fx0_anim = extractFirstBlock(fx0_str, "--- !u!91", "---")
fx1_anim = extractFirstBlock(fx1_str, "--- !u!91", "---")
fx0_str = fx0_str.replace(fx0_anim, "")
fx1_str = fx1_str.replace(fx1_anim, "")
prefix = """
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
"""[1:]
fx0_str = fx0_str.replace(prefix, "")
fx1_str = fx1_str.replace(prefix, "")
# Replace old IDs
#fx0_str = replaceIds(fx0_str, fx0_id_mapping)
fx1_str = replaceIds(fx1_str, fx1_id_mapping)
# Output
return deleteEmptyLines(prefix + anim_ctrl + fx0_str + fx1_str)
def deleteEmptyLines(fx):
return fx.replace("\n\n", "\n")
if __name__ == "__main__":
#mergeFX("FX.controller", "TaSTT/generated_fx.controller")
#print(extractFirstBlock("../FX.controller", "--- !u!91", "---"))
print(mergeFX("TaSTT_fx.controller", "../FX.controller"))
|