-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmerge_model_into_animations.py
156 lines (151 loc) · 8.15 KB
/
merge_model_into_animations.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
# Tool to push a gltf model into animations. A companion to ed8pkg2gltf.py.
#
# GitHub eArmada8/ed8pkg2gltf
import glob, os, json, struct, numpy, io, sys
from pygltflib import *
def read_gltf_stream (gltf, accessor_num):
accessor = gltf.accessors[accessor_num]
bufferview = gltf.bufferViews[accessor.bufferView]
buffer = gltf.buffers[bufferview.buffer]
componentType = {5120: 'b', 5121: 'B', 5122: 'h', 5123: 'H', 5125: 'I', 5126: 'f'}
componentSize = {5120: 1, 5121: 1, 5122: 2, 5123: 2, 5125: 4, 5126: 4}
componentCount = {'SCALAR': 1, 'VEC2': 2, 'VEC3': 3, 'VEC4': 4, 'MAT2': 4, 'MAT3': 9, 'MAT4': 16}
componentFormat = "<{0}{1}".format(componentCount[accessor.type],\
componentType[accessor.componentType])
componentStride = componentCount[accessor.type] * componentSize[accessor.componentType]
data = []
with io.BytesIO(gltf.get_data_from_buffer_uri(buffer.uri)) as f:
f.seek(bufferview.byteOffset + accessor.byteOffset, 0)
for i in range(accessor.count):
data.append(list(struct.unpack(componentFormat, f.read(componentStride))))
if (bufferview.byteStride is not None) and (bufferview.byteStride > componentStride):
f.seek(bufferview.byteStride - componentStride, 1)
if accessor.normalized == True:
for i in range(len(data)):
if componentType == 'b':
data[i] = [x / ((2**(8-1))-1) for x in data[i]]
elif componentType == 'B':
data[i] = [x / ((2**8)-1) for x in data[i]]
elif componentType == 'h':
data[i] = [x / ((2**(16-1))-1) for x in data[i]]
elif componentType == 'H':
data[i] = [x / ((2**16)-1) for x in data[i]]
return(data)
def apply_animations_to_model_gltf (model_gltf, ani_gltf):
# Apply animation pose to model
for i in range(len(model_gltf.nodes)):
if model_gltf.nodes[i].name in [x.name for x in ani_gltf.nodes]:
ani_node = [j for j in range(len(ani_gltf.nodes)) if ani_gltf.nodes[j].name == model_gltf.nodes[i].name][0]
for key in ['matrix', 'translation', 'rotation', 'scale']:
if getattr(model_gltf.nodes[i], key) is not None:
setattr(model_gltf.nodes[i], key, None)
if getattr(ani_gltf.nodes[ani_node], key) is not None:
setattr(model_gltf.nodes[i], key, getattr(ani_gltf.nodes[ani_node], key))
# Copy animations into model
model_gltf.convert_buffers(BufferFormat.BINARYBLOB)
binary_blob = model_gltf.binary_blob()
blob_len = len(model_gltf.binary_blob())
for i in range(len(ani_gltf.animations)):
animation = Animation()
for j in range(len(ani_gltf.animations[i].channels)):
sampler_input_acc = ani_gltf.animations[i].samplers[ani_gltf.animations[i].channels[j].sampler].input
sampler_input = read_gltf_stream(ani_gltf, sampler_input_acc)
sampler_output_acc = ani_gltf.animations[i].samplers[ani_gltf.animations[i].channels[j].sampler].output
sampler_output = read_gltf_stream(ani_gltf, sampler_output_acc)
sampler_interpolation = ani_gltf.animations[i].samplers[ani_gltf.animations[i].channels[j].sampler].interpolation
target_path = ani_gltf.animations[i].channels[j].target.path
target_node_name = ani_gltf.nodes[ani_gltf.animations[i].channels[j].target.node].name
if target_node_name in [x.name for x in model_gltf.nodes]:
target_node = [k for k in range(len(model_gltf.nodes)) if model_gltf.nodes[k].name == target_node_name][0]
ani_sampler = AnimationSampler()
blobdata = numpy.array(sampler_input,dtype="float32").tobytes()
bufferview = BufferView()
bufferview.buffer = 0
bufferview.byteOffset = blob_len
bufferview.byteLength = len(blobdata)
binary_blob += blobdata
blob_len += len(blobdata)
padding_length = 4 - len(blobdata) % 4
binary_blob += b'\x00' * padding_length
blob_len += padding_length
accessor = Accessor()
accessor.bufferView = len(model_gltf.bufferViews)
accessor.componentType = 5126
accessor.type = {1: 'SCALAR', 2: 'VEC2', 3: 'VEC3', 4: 'VEC4'}[len(sampler_input[0])]
accessor.count = len(sampler_input)
accessor.min = min(sampler_input)
accessor.max = max(sampler_input)
ani_sampler.input = len(model_gltf.accessors)
model_gltf.accessors.append(accessor)
model_gltf.bufferViews.append(bufferview)
blobdata = numpy.array(sampler_output,dtype="float32").tobytes()
bufferview = BufferView()
bufferview.buffer = 0
bufferview.byteOffset = blob_len
bufferview.byteLength = len(blobdata)
binary_blob += blobdata
blob_len += len(blobdata)
padding_length = 4 - len(blobdata) % 4
binary_blob += b'\x00' * padding_length
blob_len += padding_length
accessor = Accessor()
accessor.bufferView = len(model_gltf.bufferViews)
accessor.componentType = 5126
accessor.type = {1: 'SCALAR', 2: 'VEC2', 3: 'VEC3', 4: 'VEC4'}[len(sampler_output[0])]
accessor.count = len(sampler_input)
ani_sampler.output = len(model_gltf.accessors)
model_gltf.accessors.append(accessor)
model_gltf.bufferViews.append(bufferview)
ani_sampler.interpolation = sampler_interpolation
ani_channel = AnimationChannel()
ani_channel.sampler = len(animation.samplers)
ani_channel.target = AnimationChannelTarget()
ani_channel.target.path = target_path
ani_channel.target.node = target_node
animation.samplers.append(ani_sampler)
animation.channels.append(ani_channel)
model_gltf.animations.append(animation)
model_gltf.buffers[0].byteLength = blob_len
model_gltf.set_binary_blob(binary_blob)
return(model_gltf)
def process_animation (animation, animation_metadata):
print("Processing {0}...".format(animation))
if os.path.exists(animation+'.gltf'):
ani_filename = animation+'.gltf'
output = 'GLTF'
elif os.path.exists(animation+'.glb'):
ani_filename = animation+'.glb'
output = 'GLB'
else:
print("Animation {} not found, skipping...".format(animation))
return False
ani_gltf = GLTF2().load(ani_filename)
if len(ani_filename.split('_')) > 1:
model_filename = ani_filename.split('_')[0]
if os.path.exists(model_filename+'.glb'):
model_gltf = GLTF2.load(model_filename+'.glb')
elif os.path.exists(model_filename+'.gltf'):
model_gltf = GLTF2.load(model_filename+'.gltf')
else:
print("Model {0}.glb/.gltf not found, skipping animation {1}...".format(model_filename, animation))
return False
new_model = apply_animations_to_model_gltf (model_gltf, ani_gltf)
new_model.convert_buffers(BufferFormat.BINARYBLOB)
if output == 'GLB':
new_model.save_binary("{}.glb".format(animation))
return True
else:
new_model.save("{}.gltf".format(animation))
return True
if __name__ == '__main__':
# Set current directory
if getattr(sys, 'frozen', False):
os.chdir(os.path.dirname(sys.executable))
else:
os.chdir(os.path.abspath(os.path.dirname(__file__)))
animation_metadata = {}
if os.path.exists("animation_metadata.json"):
with open("animation_metadata.json",'rb') as f:
animation_metadata = json.loads(f.read())
for animation in animation_metadata['animations']:
process_animation(animation, animation_metadata)