forked from JonnylikesJazz/Wadblender-Anim-Tool
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathread.py
More file actions
370 lines (295 loc) · 13.5 KB
/
read.py
File metadata and controls
370 lines (295 loc) · 13.5 KB
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
from . import model
from . import data
from importlib import reload
import io
def read_mesh(mesh, texture_samples, map_width, map_height):
vertices = [(e.vx, e.vy, e.vz) for e in mesh["vertices"]]
normals = [(e.vx, e.vy, e.vz) for e in mesh["normals"]]
bs = mesh["bounding_sphere"]
bounding_sphere_center = model.Point(bs.cx, bs.cy, bs.cz)
bounding_sphere_radius = bs.radius
shades = mesh["shades"]
polygons = []
for polygon in mesh["polygons"]:
tex = texture_samples[polygon.texture_index]
# top left in uv coordinates
x0 = tex.mapX / map_width
y0 = 1 - tex.mapY / map_height
# bottom right in uv coordinates
x1 = (tex.mapX + tex.width) / map_width
y1 = 1 - (tex.mapY + tex.height) / map_height
"""
a b
###########
# #
# #
###########
d c
"""
a = (x0, y0)
b = (x1, y0)
c = (x1, y1)
d = (x0, y1)
uvrect = [a, b, c, d]
if tex.flipX == 0:
uvrect = [b, a, d, c]
if tex.flipY == 0:
uvrect = [d, c, b, a]
if polygon.texture_flipped == 1:
uvrect = [b, a, d, c]
poly_model = model.Polygon(polygon.vertices,
uvrect,
polygon.texture_shape,
polygon.intensity,
polygon.shine,
polygon.opacity)
polygons.append(poly_model)
return model.Mesh(vertices, polygons, normals,
bounding_sphere_center, bounding_sphere_radius, shades)
def readWAD(f):
reload(model)
reload(data)
version = data.read_uint32(f)
assert 129 <= version <= 130
#####################
### TEXTURES DATA ###
#####################
# extract position, size and attitude of each texture sample
texture_samples_count = data.read_uint32(f)
texture_samples = [data.TextureSamples.decode(f)
for _ in range(texture_samples_count)]
# extract texture map
bytes_size = data.read_uint32(f)
map_width = 256
map_height = bytes_size // 256 // 3
texture_map = data.read_texture_map(f, bytes_size, map_width, map_height)
_pages_count = bytes_size // 256 // 256 // 3
for sample in texture_samples:
x, y, w, h = sample.mapX, sample.mapY, sample.width, sample.height
assert x + w <= map_width and y + h <= map_height
###################
### MESHES DATA ###
###################
mesh_pointers_count = data.read_uint32(f)
mesh_pointers = [data.read_uint32(f) for _ in range(mesh_pointers_count)]
# extract meshes
words_size = data.read_uint32(f)
mesh_data = []
offset_idx = 0
offsets = set()
while offset_idx < words_size * 2: # for each mesh
offsets.add(offset_idx)
mesh = {}
mesh["idx"] = offset_idx
mesh["bounding_sphere"] = data.BoundingSphere.decode(f)
# table storing the XYZ coordinates of the vertices
vertices_count = data.read_uint16(f)
mesh["vertices"] = [data.ShortVector3D.decode(f)
for _ in range(vertices_count)]
ns = data.read_int16(f) # number of normals Or shades
normals_count = max(0, ns)
mesh["normals"] = [data.ShortVector3D.decode(f)
for _ in range(normals_count)]
# normalization
for normal in mesh["normals"]:
normal /= 16300
shades_count = max(0, -ns)
shades = [data.read_int16(f) for _ in range(shades_count)]
mesh["shades"] = [int(255 - shade * 255 / 8191)
for shade in shades]
# extract mesh polygons
poly_count = data.read_uint16(f)
mesh["polygons"] = [data.Polygon.decode(f) for _ in range(poly_count)]
# if the number of quads is odd, there is a 2 bytes padding
quads_count = 0
for polygon in mesh["polygons"]:
assert 0 <= polygon.texture_index < len(texture_samples)
if polygon.shape == 9:
quads_count += 1
if quads_count % 2 == 1:
f.read(2)
offset_idx += 2
offset_idx += 16 + 6 * vertices_count + 12 * poly_count + \
6 * normals_count + 2 * shades_count + 2 * quads_count
mesh_data.append(mesh)
for address in mesh_pointers:
assert address in offsets
assert offset_idx == words_size * 2
#######################
### ANIMATIONS DATA ###
#######################
animations_count = data.read_uint32(f)
animations_data = [data.Animation.decode(f)
for _ in range(animations_count)]
for animation in animations_data:
assert 0 <= animation.next_animation < len(animations_data)
state_changes_count = data.read_uint32(f)
state_changes_data = [data.StateChanges.decode(f)
for _ in range(state_changes_count)]
dispatches_count = data.read_uint32(f)
dispatches_data = [data.Dispatches.decode(f)
for _ in range(dispatches_count)]
for dispatch in dispatches_data:
if not 0 <= dispatch.next_anim < len(animations_data):
print("Dispatch pointing to invalid animation {}".format(dispatch))
# extract commands for all the animation segments
words_size = data.read_uint32(f)
commands_data = []
idx = 0
while idx < words_size * 2:
command = data.read_uint16(f)
instruction = [command]
if command == 1:
instruction += [data.read_int16(f) for _ in range(3)]
elif command == 2:
instruction += [data.read_int16(f) for _ in range(2)]
elif command == 5 or command == 6:
instruction += [data.read_uint16(f) for _ in range(2)]
commands_data.append((idx, instruction))
idx += len(instruction) * 2
# extract links between meshes (also called joints, skeleton or mesh tree)
dwords_size = data.read_uint32(f)
links_data = [data.read_int32(f) for _ in range(dwords_size)]
# animations keyframes (will be parsed later)
keyframes_words_size = data.read_uint32(f)
keyframes_data_bytes = f.read(2 * keyframes_words_size)
###################
### MODELS DATA ###
###################
movables_count = data.read_uint32(f)
movables_data = [data.Movable.decode(f) for _ in range(movables_count)]
statics_count = data.read_uint32(f)
statics_data = [data.Static.decode(f) for _ in range(statics_count)]
assert f.read(1) == b'' # check end of file
#######################
### POST PROCESSING ###
#######################
# collect all the data relative to each movable
movables = []
for mov_idx, mov_data in enumerate(movables_data):
movable = {}
movable['idx'] = mov_data.obj_ID
# get the index of its meshes in the meshes data package
movable['mesh_indices'] = []
meshes_count = mov_data.num_pointers
first_mesh_pointer = mov_data.pointers_index
for i in range(meshes_count):
mesh_pointer = mesh_pointers[first_mesh_pointer + i]
mesh_idx = next(idx
for idx, mesh in enumerate(mesh_data)
if mesh["idx"] == mesh_pointer)
movable['mesh_indices'].append(mesh_idx)
# get links data (op, dx, dy, dz)
first_link_pointer = mov_data.links_index
links_count = meshes_count - 1
movable['links'] = []
for i in range(links_count):
pointer = first_link_pointer + i * 4
movable['links'].append(links_data[pointer:pointer + 4])
# get animation data
idx = mov_idx
mov_anims_count = 0
while mov_data.anims_index >= 0:
idx += 1
if idx >= movables_count:
mov_anims_count = len(animations_data) - mov_data.anims_index
break
if movables_data[idx].anims_index >= 0:
mov_anims_count = movables_data[idx].anims_index - \
mov_data.anims_index
break
if mov_anims_count > 0:
movable['animations'] = []
for anim_idx in range(mov_anims_count):
cur_anim = animations_data[mov_data.anims_index + anim_idx]
# get next animation to deternime the keyframe buffer size
next_anim = None
next_anim_idx = mov_data.anims_index + anim_idx
while True:
next_anim_idx += 1
if next_anim_idx >= animations_count:
break
if animations_data[next_anim_idx].keyframe_size > 0:
next_anim = animations_data[next_anim_idx]
break
# parse animation keyframes
if cur_anim.keyframe_size > 0:
if next_anim:
keyframes_count = next_anim.keyframe_offset - cur_anim.keyframe_offset
else:
keyframes_count = keyframes_words_size * 2 - cur_anim.keyframe_offset
keyframes_count //= cur_anim.keyframe_size * 2
f = io.BytesIO(keyframes_data_bytes)
f.seek(cur_anim.keyframe_offset)
keyframes_data = [data.Keyframes.decode(f, meshes_count, cur_anim.keyframe_size)
for _ in range(keyframes_count)]
else:
keyframes_count = 0
keyframes_data = []
keyframes = []
for kf in keyframes_data:
offset_idx = (kf.off.vx, kf.off.vy, kf.off.vz)
bb1 = (kf.bb1.vx, kf.bb1.vy, kf.bb1.vz)
bb2 = (kf.bb2.vx, kf.bb2.vy, kf.bb2.vz)
keyframe = model.Keyframe(
offset_idx, kf.rotations, bb1, bb2)
keyframes.append(keyframe)
next_animation = cur_anim.next_animation - mov_data.anims_index
# Get animation state changes
anim_state_changes_count = cur_anim.num_state_changes
state_changes = {}
for i in range(anim_state_changes_count):
state_change_data = state_changes_data[cur_anim.changes_index + i]
if state_change_data.num_dispatches > 0:
# get animation dispatches
dispatches = []
for j in range(state_change_data.num_dispatches):
d = dispatches_data[state_change_data.dispatches_index + j]
dispatch = model.Dispatch(
d.in_range,
d.out_range,
d.next_anim - mov_data.anims_index,
d.frame_in)
dispatches.append(dispatch)
state_changes[state_change_data.state_ID] = dispatches
# Get animation commands
commands = []
if cur_anim.num_commands > 0:
try:
command_it = next(i for i, c in enumerate(commands_data) if c[0] == 2 * cur_anim.commands_offset)
for idx in range(cur_anim.num_commands):
if command_it + idx < len(commands):
e = tuple(commands_data[command_it + idx][1])
commands.append(e)
except StopIteration:
print("Invalid command")
animation = model.Animation(
cur_anim.state_ID, keyframes, state_changes, commands,
cur_anim.frame_duration, cur_anim.speed,
cur_anim.acceleration, cur_anim.frame_start,
cur_anim.frame_end, cur_anim.frame_in, next_animation)
movable['animations'].append(animation)
else:
movable['animations'] = []
movables.append(movable)
# for each static
statics_model = []
for static in statics_data:
mesh_pointer = mesh_pointers[static.pointers_index]
mesh_idx = next(addressIndex
for addressIndex, mesh in enumerate(mesh_data)
if mesh["idx"] == mesh_pointer)
mesh = read_mesh(mesh_data[mesh_idx],
texture_samples, map_width, map_height)
statics_model.append(model.Static(static.obj_ID, mesh))
movables_model = []
for movable in movables:
meshesTO = [mesh_data[i] for i in movable['mesh_indices']]
meshes = []
for meshto in meshesTO:
mesh = read_mesh(meshto, texture_samples, map_width, map_height)
meshes.append(mesh)
movable = model.Movable(
movable['idx'], meshes, movable['links'], movable['animations'])
movables_model.append(movable)
return model.Wad(version, statics_model, map_width, map_height, texture_map, movables_model)