pgtools 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +25 -25
  3. data/bin/bxm_decoder +2 -2
  4. data/bin/bxm_encoder +2 -2
  5. data/bin/clh_convert +2 -2
  6. data/bin/clp_convert +2 -2
  7. data/bin/clw_convert +2 -2
  8. data/bin/dat_creator +2 -2
  9. data/bin/dat_extractor +2 -2
  10. data/bin/dat_ls +2 -2
  11. data/bin/eff_idd_creator +2 -2
  12. data/bin/eff_idd_extractor +2 -2
  13. data/bin/exp_convert_wiiu_pc +2 -2
  14. data/bin/exp_tool +2 -2
  15. data/bin/mot_convert_wiiu_pc +2 -2
  16. data/bin/mot_tool +2 -2
  17. data/bin/pkz_extractor +2 -2
  18. data/bin/scr_creator +2 -2
  19. data/bin/scr_extractor +2 -2
  20. data/bin/wmb_cleanup +2 -2
  21. data/bin/wmb_common_bones +2 -2
  22. data/bin/wmb_convert_pc_switch +2 -2
  23. data/bin/wmb_convert_wiiu_pc +2 -2
  24. data/bin/wmb_export_assimp +2 -2
  25. data/bin/wmb_get_bone_map +2 -2
  26. data/bin/wmb_import_assimp +2 -2
  27. data/bin/wmb_import_nier +2 -2
  28. data/bin/wmb_import_wiiu +2 -2
  29. data/bin/wtb_convert_wiiu_pc +2 -2
  30. data/bin/wtx_creator +2 -2
  31. data/bin/wtx_extractor +2 -2
  32. data/lib/bayonetta/alignment.rb +0 -0
  33. data/lib/bayonetta/bone.rb +0 -0
  34. data/lib/bayonetta/bxm.rb +180 -180
  35. data/lib/bayonetta/clh.rb +159 -159
  36. data/lib/bayonetta/clp.rb +212 -212
  37. data/lib/bayonetta/clw.rb +166 -166
  38. data/lib/bayonetta/dat.rb +261 -261
  39. data/lib/bayonetta/eff.rb +314 -314
  40. data/lib/bayonetta/endianness.rb +0 -0
  41. data/lib/bayonetta/exp.rb +768 -768
  42. data/lib/bayonetta/linalg.rb +416 -416
  43. data/lib/bayonetta/material_database.yaml +2581 -2581
  44. data/lib/bayonetta/mot.rb +763 -763
  45. data/lib/bayonetta/pkz.rb +63 -63
  46. data/lib/bayonetta/scr.rb +0 -0
  47. data/lib/bayonetta/tools/bxm_decoder.rb +23 -23
  48. data/lib/bayonetta/tools/bxm_encoder.rb +37 -37
  49. data/lib/bayonetta/tools/clh_convert.rb +60 -60
  50. data/lib/bayonetta/tools/clp_convert.rb +70 -70
  51. data/lib/bayonetta/tools/clw_convert.rb +60 -60
  52. data/lib/bayonetta/tools/dat_creator.rb +57 -57
  53. data/lib/bayonetta/tools/dat_extractor.rb +94 -94
  54. data/lib/bayonetta/tools/dat_ls.rb +106 -106
  55. data/lib/bayonetta/tools/eff_idd_creator.rb +66 -66
  56. data/lib/bayonetta/tools/eff_idd_extractor.rb +73 -73
  57. data/lib/bayonetta/tools/exp_convert_wiiu_pc.rb +33 -33
  58. data/lib/bayonetta/tools/exp_tool.rb +48 -48
  59. data/lib/bayonetta/tools/mot_convert_wiiu_pc.rb +33 -33
  60. data/lib/bayonetta/tools/mot_tool.rb +0 -0
  61. data/lib/bayonetta/tools/pkz_extractor.rb +75 -75
  62. data/lib/bayonetta/tools/scr_creator.rb +63 -63
  63. data/lib/bayonetta/tools/scr_extractor.rb +78 -78
  64. data/lib/bayonetta/tools/wmb_cleanup.rb +250 -250
  65. data/lib/bayonetta/tools/wmb_common_bones.rb +45 -45
  66. data/lib/bayonetta/tools/wmb_convert_pc_switch.rb +35 -35
  67. data/lib/bayonetta/tools/wmb_convert_wiiu_pc.rb +33 -33
  68. data/lib/bayonetta/tools/wmb_export_assimp.rb +479 -479
  69. data/lib/bayonetta/tools/wmb_get_bone_map.rb +50 -50
  70. data/lib/bayonetta/tools/wmb_import_assimp.rb +735 -735
  71. data/lib/bayonetta/tools/wmb_import_geometry_wiiu_pc.rb +474 -472
  72. data/lib/bayonetta/tools/wmb_import_nier.rb +309 -309
  73. data/lib/bayonetta/tools/wtb_convert_wiiu_pc.rb +95 -95
  74. data/lib/bayonetta/tools/wtb_import_textures.rb +103 -103
  75. data/lib/bayonetta/tools/wtx_creator.rb +69 -69
  76. data/lib/bayonetta/tools/wtx_extractor.rb +85 -85
  77. data/lib/bayonetta/vertex_types.yaml +0 -0
  78. data/lib/bayonetta/vertex_types2.yaml +0 -0
  79. data/lib/bayonetta/vertex_types_nier.yaml +145 -145
  80. data/lib/bayonetta/wmb.rb +2455 -2443
  81. data/lib/bayonetta/wmb3.rb +759 -759
  82. data/lib/bayonetta/wtb.rb +481 -481
  83. data/lib/bayonetta.rb +60 -60
  84. metadata +2 -2
data/lib/bayonetta/wmb.rb CHANGED
@@ -1,2443 +1,2455 @@
1
- require 'set'
2
- require 'digest'
3
- require 'yaml'
4
- $material_db = YAML::load_file(File.join( File.dirname(__FILE__), 'material_database.yaml'))
5
-
6
- module Bayonetta
7
-
8
- class UByteList < LibBin::Structure
9
- uint32 :data
10
-
11
- def self.is_bayo2?(parent)
12
- if parent.__parent.respond_to?(:is_bayo2?)
13
- return parent.__parent.is_bayo2?
14
- elsif parent.__parent.__parent.respond_to?(:is_bayo2?)
15
- return parent.__parent.__parent.is_bayo2?
16
- end
17
- raise "Cannot determine if Bayo2 or not!"
18
- end
19
-
20
- def self.inherited(subclass)
21
- subclass.instance_variable_set(:@fields, @fields.dup)
22
- end
23
-
24
- def __convert(input, output, input_big, output_big, parent = nil, index = nil)
25
- __set_convert_state(input, output,
26
- input_big && self.class.is_bayo2?(parent) ? false : input_big,
27
- output_big && self.class.is_bayo2?(parent) ? false : output_big,
28
- parent, index)
29
- __convert_fields
30
- __unset_convert_state
31
- self
32
- end
33
-
34
- def __load(input, input_big, parent = nil, index = nil)
35
- __set_load_state(input, input_big && self.class.is_bayo2?(parent) ? false : input_big, parent, index)
36
- __load_fields
37
- __unset_load_state
38
- self
39
- end
40
-
41
- def __dump(output, output_big, parent = nil, index = nil)
42
- __set_dump_state(output, output_big && self.class.is_bayo2?(parent) ? false : output_big, parent, index)
43
- __dump_fields
44
- __unset_dump_state
45
- self
46
- end
47
-
48
- def initialize
49
- @data = 0
50
- end
51
-
52
- end
53
-
54
- class Color < UByteList
55
-
56
- def r
57
- @data & 0xff
58
- end
59
-
60
- def g
61
- (@data >> 8) & 0xff
62
- end
63
-
64
- def b
65
- (@data >> 16) & 0xff
66
- end
67
-
68
- def a
69
- (@data >> 24) & 0xff
70
- end
71
-
72
- def r=(v)
73
- @data = (@data & 0xffffff00) | (v & 0xff)
74
- v & 0xff
75
- end
76
-
77
- def g=(v)
78
- @data = (@data & 0xffff00ff) | ((v & 0xff)<<8)
79
- v & 0xff
80
- end
81
-
82
- def b=(v)
83
- @data = (@data & 0xff00ffff) | ((v & 0xff)<<16)
84
- v & 0xff
85
- end
86
-
87
- def a=(v)
88
- @data = (@data & 0x00ffffff) | ((v & 0xff)<<24)
89
- v & 0xff
90
- end
91
-
92
- def to_a
93
- [r, g, b, a]
94
- end
95
-
96
- end
97
-
98
- module VectorAccessor
99
- def [](i)
100
- case i
101
- when 0
102
- self.x
103
- when 1
104
- self.y
105
- when 2
106
- self.z
107
- else
108
- "Invalid index #{i} for a vector access!"
109
- end
110
- end
111
-
112
- def []=(i,v)
113
- case i
114
- when 0
115
- self.x = v
116
- when 1
117
- self.y = v
118
- when 2
119
- self.z = v
120
- else
121
- "Invalid index #{i} for a vector access!"
122
- end
123
- end
124
-
125
- end
126
-
127
- class Tangents < UByteList
128
- include VectorAccessor
129
-
130
- def clamp(v, max, min)
131
- if v > max
132
- v = max
133
- elsif v < min
134
- v = min
135
- end
136
- v
137
- end
138
-
139
- def x
140
- ((@data & 0xff) - 127.0)/127.0
141
- end
142
-
143
- def y
144
- (((@data >> 8) & 0xff) - 127.0)/127.0
145
- end
146
-
147
- def z
148
- (((@data >> 16) & 0xff) -127.0)/127.0
149
- end
150
-
151
- def s
152
- (((@data >> 24) & 0xff) -127.0)/127.0
153
- end
154
-
155
- def x=(v)
156
- v2 = clamp((v*127.0+127.0).round, 255, 0)
157
- @data = (@data & 0xffffff00) | v2
158
- v
159
- end
160
-
161
- def y=(v)
162
- v2 = clamp((v*127.0+127.0).round, 255, 0)
163
- @data = (@data & 0xffff00ff) | (v2 << 8)
164
- v
165
- end
166
-
167
- def z=(v)
168
- v2 = clamp((v*127.0+127.0).round, 255, 0)
169
- @data = (@data & 0xff00ffff) | (v2 << 16)
170
- v
171
- end
172
-
173
- def s=(v)
174
- v2 = clamp((v*127.0+127.0).round, 255, 0)
175
- @data = (@data & 0x00ffffff) | (v2 << 24)
176
- v
177
- end
178
-
179
- def normalize(fx, fy, fz)
180
- nrm = Math::sqrt(fx*fx+fy*fy+fz*fz)
181
- return [0.0, 0.0, 0.0] if nrm == 0.0
182
- [fx/nrm, fy/nrm, fz/nrm]
183
- end
184
-
185
- def set(x, y, z, s = nil)
186
- x, y, z = normalize(x, y, z)
187
- self.x = x
188
- self.y = y
189
- self.z = z
190
- self.s = s if s
191
- self
192
- end
193
-
194
- def to_a
195
- [x, y, z, s]
196
- end
197
-
198
- end
199
-
200
- class Mapping < LibBin::Structure
201
- half :u
202
- half :v
203
-
204
- def to_a
205
- [u, v]
206
- end
207
- end
208
-
209
- class FloatMapping < LibBin::Structure
210
- float :u
211
- float :v
212
-
213
- def to_a
214
- [u, v]
215
- end
216
- end
217
-
218
- class FloatNormal < LibBin::Structure
219
- include VectorAccessor
220
- float :x
221
- float :y
222
- float :z
223
-
224
- def to_a
225
- [x, y, z]
226
- end
227
- end
228
-
229
- class HalfNormal < LibBin::Structure
230
- include VectorAccessor
231
- half :x
232
- half :y
233
- half :z
234
- half :dummy
235
-
236
- def to_a
237
- [x, y, z]
238
- end
239
- end
240
-
241
- class Normal < LibBin::Structure
242
- include VectorAccessor
243
- attr_accessor :normal
244
- attr_accessor :normal_big_orig
245
- attr_accessor :normal_small_orig
246
- attr_accessor :wide
247
-
248
- def initialize
249
- @normal = [0.0, 0.0, 0.0]
250
- @normal_big_orig = nil
251
- @normal_small_orig = nil
252
- @wide = false
253
- end
254
-
255
- def x
256
- @normal[0]
257
- end
258
-
259
- def y
260
- @normal[1]
261
- end
262
-
263
- def z
264
- @normal[2]
265
- end
266
-
267
- def x=(v)
268
- @normal_big_orig = nil
269
- @normal_small_orig = nil
270
- @normal[0] = v
271
- end
272
-
273
- def y=(v)
274
- @normal_big_orig = nil
275
- @normal_small_orig = nil
276
- @normal[1] = v
277
- end
278
-
279
- def z=(v)
280
- @normal_big_orig = nil
281
- @normal_small_orig = nil
282
- @normal[2] = v
283
- end
284
-
285
- def __size(position, parent, index)
286
- 4
287
- end
288
-
289
- def normalize(fx, fy, fz)
290
- nrm = Math::sqrt(fx*fx+fy*fy+fz*fz)
291
- return [0.0, 0.0, 0.0] if nrm == 0.0
292
- [fx/nrm, fy/nrm, fz/nrm]
293
- end
294
-
295
- def decode_big_normal(vs)
296
- v = vs.unpack("L>").first
297
- nx = v & ((1<<10)-1)
298
- ny = (v >> 10) & ((1<<10)-1)
299
- nz = (v >> 20) & ((1<<10)-1)
300
- sx = nx & (1<<9)
301
- sy = ny & (1<<9)
302
- sz = nz & (1<<9)
303
- if sx
304
- nx ^= sx
305
- nx = -(sx-nx)
306
- end
307
- if sy
308
- ny ^= sy
309
- ny = -(sy-ny)
310
- end
311
- if sz
312
- nz ^= sz
313
- nz = -(sz-nz)
314
- end
315
-
316
- mag = ((1<<9)-1).to_f
317
- fx = nx.to_f/mag
318
- fy = ny.to_f/mag
319
- fz = nz.to_f/mag
320
-
321
- normalize(fx, fy, fz)
322
- end
323
-
324
- def redecode_wide
325
- v = normal_small_orig.unpack("L<").first
326
- nx = v & ((1<<10)-1)
327
- ny = (v >> 10) & ((1<<10)-1)
328
- nz = (v >> 20) & ((1<<10)-1)
329
- sx = nx & (1<<9)
330
- sy = ny & (1<<9)
331
- sz = nz & (1<<9)
332
- if sx
333
- nx ^= sx
334
- nx = -(sx-nx)
335
- end
336
- if sy
337
- ny ^= sy
338
- ny = -(sy-ny)
339
- end
340
- if sz
341
- nz ^= sz
342
- nz = -(sz-nz)
343
- end
344
-
345
- mag = ((1<<9)-1).to_f
346
- fx = nx.to_f/mag
347
- fy = ny.to_f/mag
348
- fz = nz.to_f/mag
349
- @normals = normalize(fx, fy, fz)
350
- end
351
-
352
- def decode_small_normal(v)
353
- n = v.unpack("c4")
354
- nx = n[3]
355
- ny = n[2]
356
- nz = n[1]
357
- mag = 127.0
358
- fx = nx.to_f/mag
359
- fy = ny.to_f/mag
360
- fz = nz.to_f/mag
361
-
362
- normalize(fx, fy, fz)
363
- end
364
-
365
- def clamp(v, max, min)
366
- if v > max
367
- v = max
368
- elsif v < min
369
- v = min
370
- end
371
- v
372
- end
373
-
374
- def encode_small_normal(normal)
375
- if @wide
376
- fx = normal[0]
377
- fy = normal[1]
378
- fz = normal[2]
379
- mag = (1<<9)-1
380
- nx = (fx*(mag).to_f).to_i
381
- ny = (fy*(mag).to_f).to_i
382
- nz = (fz*(mag).to_f).to_i
383
- nx = clamp(nx, mag, -1-mag)
384
- ny = clamp(ny, mag, -1-mag)
385
- nz = clamp(nz, mag, -1-mag)
386
- mask = (1<<10)-1
387
- v = 0
388
- v |= nz & mask
389
- v <<= 10
390
- v |= ny & mask
391
- v <<= 10
392
- v |= nx & mask
393
- [v].pack("L<")
394
- else
395
- fx = normal[0]
396
- fy = normal[1]
397
- fz = normal[2]
398
- nx = (fx*127.0).to_i
399
- ny = (fy*127.0).to_i
400
- nz = (fz*127.0).to_i
401
- nx = clamp(nx, 127, -128)
402
- ny = clamp(ny, 127, -128)
403
- nz = clamp(nz, 127, -128)
404
- [0, nz, ny, nx].pack("c4")
405
- end
406
- end
407
-
408
- def encode_big_normal(normal)
409
- fx = normal[0]
410
- fy = normal[1]
411
- fz = normal[2]
412
- mag = (1<<9)-1
413
- nx = (fx*(mag).to_f).to_i
414
- ny = (fy*(mag).to_f).to_i
415
- nz = (fz*(mag).to_f).to_i
416
- nx = clamp(nx, mag, -1-mag)
417
- ny = clamp(ny, mag, -1-mag)
418
- nz = clamp(nz, mag, -1-mag)
419
- mask = (1<<10)-1
420
- v = 0
421
- v |= nz & mask
422
- v <<= 10
423
- v |= ny & mask
424
- v <<= 10
425
- v |= nx & mask
426
- [v].pack("L>")
427
- end
428
-
429
- def load_normal
430
- s = __input.read(4)
431
- if __input_big
432
- @normal_big_orig = s
433
- @normal_small_orig = nil
434
- @normal = decode_big_normal(s)
435
- else
436
- @normal_small_orig = s
437
- @normal_big_orig = nil
438
- @normal = decode_small_normal(s)
439
- end
440
- end
441
-
442
- def dump_normal
443
- if __output_big
444
- s2 = (@normal_big_orig ? @normal_big_orig : encode_big_normal(@normal))
445
- else
446
- s2 = (@normal_small_orig ? @normal_small_orig : encode_small_normal(@normal))
447
- end
448
- __output.write(s2)
449
- end
450
-
451
- def convert_normal
452
- load_normal
453
- dump_normal
454
- end
455
-
456
- def __convert_fields
457
- convert_normal
458
- end
459
-
460
- def __load_fields
461
- load_normal
462
- end
463
-
464
- def __dump_fields
465
- dump_normal
466
- end
467
-
468
-
469
- def to_a
470
- [x, y, z]
471
- end
472
- end
473
-
474
- class Position < LibBin::Structure
475
- include VectorAccessor
476
- float :x
477
- float :y
478
- float :z
479
-
480
- def -(other)
481
- b = Position::new
482
- b.x = @x - other.x
483
- b.y = @y - other.y
484
- b.z = @z - other.z
485
- b
486
- end
487
-
488
- def -@
489
- b = Position::new
490
- b.x = -@x
491
- b.y = -@y
492
- b.z = -@z
493
- b
494
- end
495
-
496
- def +(other)
497
- b = Position::new
498
- b.x = @x + other.x
499
- b.y = @y + other.y
500
- b.z = @z + other.z
501
- b
502
- end
503
-
504
- def *(scal)
505
- b = Position::new
506
- b.x = @x*scal
507
- b.y = @y*scal
508
- b.z = @z*scal
509
- b
510
- end
511
-
512
- def to_yaml_properties
513
- [:@x, :@y, :@z]
514
- end
515
-
516
- def to_s
517
- "<#{@x}, #{@y}, #{@z}>"
518
- end
519
-
520
- def to_a
521
- [x, y, z]
522
- end
523
- end
524
-
525
- class BoneInfos < LibBin::Structure
526
- register_field :indexes, UByteList
527
- register_field :weights, UByteList
528
-
529
- def initialize
530
- @indexes = UByteList::new
531
- @weights = UByteList::new
532
- end
533
-
534
- def get_indexes_and_weights
535
- res = []
536
- 4.times { |i|
537
- bi = (@indexes.data >> (i*8)) & 0xff
538
- bw = (@weights.data >> (i*8)) & 0xff
539
- res.push [bi, bw] if bw > 0
540
- }
541
- res
542
- end
543
-
544
- def get_indexes
545
- get_indexes_and_weights.collect { |bi, _| bi }
546
- end
547
-
548
- def get_weights
549
- get_indexes_and_weights.collect { |_, bw| bw }
550
- end
551
-
552
- def remap_indexes(map)
553
- new_bone_info = get_indexes_and_weights.collect { |bi, bw| [map[bi], bw] }
554
- set_indexes_and_weights(new_bone_info)
555
- self
556
- end
557
-
558
- def set_indexes_and_weights(bone_info)
559
- raise "Too many bone information #{bone_info.inspect}!" if bone_info.length > 4
560
- @indexes.data = 0
561
- @weights.data = 0
562
- bone_info.each_with_index { |(bi, bw), i|
563
- raise "Invalid bone index #{bi}!" if bi > 255 || bi < 0
564
- @indexes.data |= ( bi << (i*8) )
565
- bw = 0 if bw < 0
566
- bw = 255 if bw > 255
567
- @weights.data |= (bw << (i*8) )
568
- }
569
- self
570
- end
571
-
572
- def to_a
573
- get_indexes_and_weights
574
- end
575
- end
576
-
577
- class BoneIndexTranslateTable < LibBin::Structure
578
- int16 :offsets, length: 16
579
- #attr_accessor :second_levels
580
- #attr_accessor :third_levels
581
- attr_reader :table
582
-
583
- def table=(t)
584
- @table = t
585
- encode
586
- t
587
- end
588
-
589
- def __size(position = 0, parent = nil, index = nil)
590
- sz = super()
591
- if @second_levels
592
- @second_levels.each { |e|
593
- sz += e.__size(position, parent, index)
594
- }
595
- end
596
- if @third_levels
597
- @third_levels.each { |e|
598
- sz += e.__size(position, parent, index)
599
- }
600
- end
601
- sz
602
- end
603
-
604
- def __convert(input, output, input_big, output_big, parent, index, level = 1)
605
- __set_convert_state(input, output, input_big, output_big, parent, index)
606
- __convert_fields
607
- if level == 1
608
- @second_levels = []
609
- @offsets.each { |o|
610
- if o != -1
611
- t = self.class::new
612
- t.__convert(input, output, input_big, output_big, self, nil, level+1)
613
- @second_levels.push t
614
- end
615
- }
616
- @third_levels = []
617
- @second_levels.each { |l|
618
- l.offsets.each { |o|
619
- if o != -1
620
- t = self.class::new
621
- t.__convert(input, output, input_big, output_big, self, nil, level+2)
622
- @third_levels.push t
623
- end
624
- }
625
- }
626
- decode
627
- else
628
- @second_levels = nil
629
- @third_levels = nil
630
- end
631
- __unset_convert_state
632
- self
633
- end
634
-
635
- def __load(input, input_big, parent, index, level = 1)
636
- __set_load_state(input, input_big, parent, index)
637
- __load_fields
638
- if level == 1
639
- @second_levels = []
640
- @offsets.each { |o|
641
- if o != -1
642
- t = self.class::new
643
- t.__load(input, input_big, self, nil, level+1)
644
- @second_levels.push t
645
- end
646
- }
647
- @third_levels = []
648
- @second_levels.each { |l|
649
- l.offsets.each { |o|
650
- if o != -1
651
- t = self.class::new
652
- t.__load(input, input_big, self, nil, level+2)
653
- @third_levels.push t
654
- end
655
- }
656
- }
657
- decode
658
- else
659
- @second_levels = nil
660
- @third_levels = nil
661
- end
662
- __unset_load_state
663
- self
664
- end
665
-
666
- def decode
667
- t = (@offsets+@second_levels.collect(&:offsets)+@third_levels.collect(&:offsets)).flatten
668
- @table = (0x0..0xfff).each.collect { |i|
669
- index = t[(i & 0xf00)>>8]
670
- next if index == -1
671
- index = t[index + ((i & 0xf0)>>4)]
672
- next if index == -1
673
- index = t[index + (i & 0xf)]
674
- next if index == 0xfff
675
- [i, index]
676
- }.compact.to_h
677
- end
678
- private :decode
679
-
680
- def encode
681
- keys = @table.keys.sort
682
- first_table = 16.times.collect { |i|
683
- lower = i*0x100
684
- upper = (i+1)*0x100
685
- keys.select { |k| k >= lower && k < upper }
686
- }
687
- off = 0x0
688
- @offsets = first_table.collect { |e| e == [] ? -1 : (off += 0x10) }
689
-
690
- second_table = first_table.select { |e| e != [] }.collect { |e|
691
- 16.times.collect { |i|
692
- lower = i*0x10
693
- upper = (i+1)*0x10
694
- e.select { |k| (k&0xff) >= lower && (k&0xff) < upper }
695
- }
696
- }
697
- @second_levels = second_table.collect { |st|
698
- tab = BoneIndexTranslateTable::new
699
- tab.offsets = st.collect { |e| e == [] ? -1 : (off += 0x10) }
700
- tab
701
- }
702
- @third_levels = []
703
- second_table.each { |e|
704
- e.select { |ee| ee != [] }.each { |ee|
705
- tab = BoneIndexTranslateTable::new
706
- tab.offsets = [0xfff]*16
707
- ee.each { |k|
708
- tab.offsets[k&0xf] = @table[k]
709
- }
710
- @third_levels.push tab
711
- }
712
- }
713
- self
714
- end
715
- private :encode
716
-
717
- def __dump(output, output_big, parent, index, level = 1)
718
- __set_dump_state(output, output_big, parent, index)
719
- encode if level == 1
720
- __dump_fields
721
- if @second_levels
722
- @second_levels.each { |e|
723
- e.__dump(output, output_big, self, nil, level+1)
724
- }
725
- end
726
- if @third_levels
727
- @third_levels.each { |e|
728
- e.__dump(output, output_big, self, nil, level+2)
729
- }
730
- end
731
- __unset_dump_state
732
- end
733
-
734
- end
735
-
736
- VERTEX_FIELDS = {
737
- position_t: [ Position, 12 ],
738
- mapping_t: [ Mapping, 4 ],
739
- normal_t: [ Normal, 4 ],
740
- tangents_t: [ Tangents, 4 ],
741
- bone_infos_t: [ BoneInfos, 8],
742
- color_t: [ Color, 4],
743
- fnormal_t: [ FloatNormal, 12 ],
744
- hnormal_t: [ HalfNormal, 8 ],
745
- fmapping_t: [ FloatMapping, 8]
746
- }
747
-
748
- class WMBFile < LibBin::Structure
749
- include Alignment
750
-
751
- VERTEX_TYPES = {}
752
- VERTEX_TYPES.update( YAML::load_file(File.join( File.dirname(__FILE__), 'vertex_types.yaml')) )
753
- VERTEX_TYPES.update( YAML::load_file(File.join( File.dirname(__FILE__), 'vertex_types2.yaml')) )
754
-
755
- class UnknownStruct < LibBin::Structure
756
- uint8 :u_a1, length: 4
757
- uint32 :u_b1
758
- int16 :u_c1, length: 4
759
- uint8 :u_a2, length: 4
760
- uint32 :u_b2
761
- int16 :u_c2, length: 4
762
- uint8 :u_a3, length: 4
763
- uint32 :u_b3
764
- end
765
-
766
- class Material < LibBin::Structure
767
- int16 :type
768
- uint16 :flag
769
- uint32 :material_data,
770
- length: '(..\materials_offsets[__index+1] ? ..\materials_offsets[__index+1] - ..\materials_offsets[__index] - 4 : ..\header\offset_meshes_offsets - __position - 4)/4'
771
-
772
- def __size(position = 0, parent = nil, index = nil)
773
- return 2 + 2 + @material_data.length * 4
774
- end
775
- end
776
-
777
- class Bayo1Material
778
- attr_reader :type
779
- attr_reader :flag
780
- attr_reader :samplers
781
- attr_reader :parameters
782
-
783
- def initialize(m)
784
- @type = m.type
785
- @flag = m.flag
786
- @layout = $material_db[@type][:layout]
787
- @samplers = {}
788
- @parameters = {}
789
- field_count = 0
790
- @layout.each { |name, t|
791
- if t == "sampler2D_t" || t == "samplerCUBE_t"
792
- @samplers[name] = m.material_data[field_count]
793
- field_count += 1
794
- else
795
- @parameters[name] = m.material_data[field_count...(field_count+4)].pack("L4").unpack("F4")
796
- field_count += 4
797
- end
798
- }
799
- end
800
-
801
- end
802
-
803
- class BatchHeader < LibBin::Structure
804
- int16 :batch_id #Bayo 2
805
- int16 :mesh_id
806
- uint16 :flags
807
- int16 :ex_mat_id
808
- uint8 :material_id
809
- uint8 :has_bone_refs
810
- uint8 :u_e1
811
- uint8 :u_e2
812
- uint32 :vertex_start
813
- uint32 :vertex_end
814
- int32 :primitive_type
815
- uint32 :offset_indices
816
- int32 :num_indices
817
- int32 :vertex_offset
818
- int32 :u_f, length: 7
819
-
820
- def initialize
821
- @batch_id = 0
822
- @mesh_id = 0
823
- @flags = 0x8001
824
- @ex_mat_id = 0
825
- @material_id = 0
826
- @has_bone_refs = 1
827
- @u_e1 = 0
828
- @u_e2 = 0
829
- @vertex_start = 0
830
- @vertex_end = 0
831
- @primitive_type = 4
832
- @offset_indices = 0x100
833
- @num_indices = 0
834
- @vertex_offset = 0
835
- @u_f = [0]*7
836
- end
837
- end
838
-
839
- class Batch < LibBin::Structure
840
- register_field :header, BatchHeader
841
- int32 :num_bone_ref, condition: 'header\has_bone_refs != 0'
842
- uint8 :bone_refs, length: 'num_bone_ref', condition: 'header\has_bone_refs != 0'
843
- float :unknown, length: 4, condition: 'header\has_bone_refs == 0'
844
- uint16 :indices, length: 'header\num_indices', offset: '__position + header\offset_indices'
845
-
846
- def initialize
847
- @header = BatchHeader::new
848
- @num_bone_ref = 0
849
- @bone_refs = []
850
- @indices = []
851
- end
852
-
853
- def duplicate(positions, vertexes, vertexes_ex)
854
- b = Batch::new
855
- if header.has_bone_refs != 0
856
- b.header = @header.dup
857
- b.num_bone_ref = @num_bone_ref
858
- b.bone_refs = @bone_refs.dup
859
- else
860
- b.unknown = @unknown
861
- end
862
- l = vertexes.length
863
- old_indices_map = vertex_indices.uniq.sort.each_with_index.collect { |vi, i| [vi, l + i] }.to_h
864
- old_indices_map.each { |vi, nvi| positions[nvi] = positions[vi] } if positions
865
- old_indices_map.each { |vi, nvi| vertexes[nvi] = vertexes[vi] }
866
- old_indices_map.each { |vi, nvi| vertexes_ex[nvi] = vertexes_ex[vi] } if vertexes_ex
867
- b.indices = vertex_indices.collect { |vi| old_indices_map[vi] }
868
- b.recompute_from_absolute_indices
869
- b
870
- end
871
-
872
- def recompute_from_absolute_indices
873
- @header.num_indices = @indices.length
874
- unless @header.num_indices == 0
875
- sorted_indices = @indices.sort.uniq
876
- @header.vertex_start = sorted_indices.first
877
- @header.vertex_end = sorted_indices.last + 1
878
- if sorted_indices.last > 0xffff
879
- offset = @header.vertex_offset = @header.vertex_start
880
- @indices.collect! { |i| i - offset }
881
- else
882
- @header.vertex_offset = 0
883
- end
884
- end
885
- self
886
- end
887
-
888
- def triangles
889
- inds = @indices.collect{ |i| i + @header.vertex_offset }
890
- if @header.primitive_type == 4
891
- inds.each_slice(3).to_a
892
- else
893
- inds.each_cons(3).each_with_index.collect do |(v0, v1, v2), i|
894
- if i.even?
895
- [v0, v1, v2]
896
- else
897
- [v1, v0, v2]
898
- end
899
- end.select { |t| t.uniq.length == 3 }
900
- end
901
- end
902
-
903
- def set_triangles(trs)
904
- @header.primitive_type = 4
905
- @indices = trs.flatten
906
- recompute_from_absolute_indices
907
- @header.num_indices = @indices.length
908
- self
909
- end
910
-
911
- def filter_vertexes(vertexes)
912
- vertex_map = vertexes.collect { |i| [i, true] }.to_h
913
- trs = triangles
914
- new_trs = trs.select { |tr| vertex_map.include?(tr[0]) && vertex_map.include?(tr[1]) && vertex_map.include?(tr[2]) }
915
- set_triangles(new_trs)
916
- end
917
-
918
- def vertex_indices
919
- indices.collect { |i| i + @header.vertex_offset }
920
- end
921
-
922
- def cleanup_bone_refs(vertexes)
923
- if header.has_bone_refs != 0
924
- bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
925
- used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
926
- new_bone_refs_list = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.sort
927
- new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
928
- translation_map = used_bone_refs_indexes.collect { |ri|
929
- [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
930
- }.to_h
931
- vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
932
- @bone_refs = new_bone_refs_list
933
- @num_bone_ref = @bone_refs.length
934
- end
935
- self
936
- end
937
-
938
- def add_ancestors_bone_refs(vertexes, bones)
939
- if header.has_bone_refs != 0
940
- bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
941
- used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
942
- new_bone_refs_list = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.sort
943
- new_bone_refs_set = Set::new(new_bone_refs_list)
944
- new_bone_refs_list.each { |bi|
945
- new_bone_refs_set.merge(bones[bi].parents.collect(&:index))
946
- }
947
- new_bone_refs_list = new_bone_refs_set.to_a.sort
948
- new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
949
- translation_map = used_bone_refs_indexes.collect { |ri|
950
- [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
951
- }.to_h
952
- vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
953
- @bone_refs = new_bone_refs_list
954
- @num_bone_ref = @bone_refs.length
955
- end
956
- end
957
-
958
- def add_previous_bone_refs(vertexes, bones)
959
- if header.has_bone_refs != 0
960
- bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
961
- used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
962
- last_bone = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.max
963
- new_bone_refs_list = (0..last_bone).to_a
964
- new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
965
- translation_map = used_bone_refs_indexes.collect { |ri|
966
- [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
967
- }.to_h
968
- vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
969
- @bone_refs = new_bone_refs_list
970
- @num_bone_ref = @bone_refs.length
971
- end
972
- end
973
-
974
- end
975
-
976
- class MeshHeader < LibBin::Structure
977
- int16 :id
978
- int16 :num_batch
979
- int16 :u_a1
980
- int16 :u_a2
981
- uint32 :offset_batch_offsets
982
- uint32 :u_b
983
- int32 :u_c, length: 4
984
- string :name, 32
985
- float :mat, length: 12
986
-
987
- def initialize
988
- @id = 0
989
- @num_batch = 0
990
- @u_a1 = 0
991
- @u_a2 = 1
992
- @offset_batch_offsets = 128
993
- @u_b = 0x80000000
994
- @u_c = [0]*4
995
- @name = [0]*32
996
- @mat = [0.0, 0.98, -0.42, 1.64, 1.10, 2.08,
997
- 0.1, -1.10, -0.12, -0.95, 0.0, 0.0]
998
- end
999
- end
1000
-
1001
- class Mesh < LibBin::Structure
1002
- register_field :header, MeshHeader
1003
- uint32 :batch_offsets, length: 'header\num_batch',
1004
- offset: '__position + header\offset_batch_offsets'
1005
- register_field :batches, Batch, count: 'header\num_batch', sequence: true,
1006
- offset: '__position + header\offset_batch_offsets + batch_offsets[__iterator]'
1007
-
1008
- def initialize
1009
- @header = MeshHeader::new
1010
- @batch_offsets = []
1011
- @batches = []
1012
- end
1013
-
1014
- def __size(position = 0, parent = nil, index = nil)
1015
- sz = @header.offset_batch_offsets
1016
- sz += @header.num_batch * 4
1017
- sz = align(sz, 0x20)
1018
- @header.num_batch.times { |i|
1019
- sz += @batches[i].__size
1020
- sz = align(sz, 0x20)
1021
- }
1022
- sz
1023
- end
1024
-
1025
- def recompute_layout
1026
- @header.num_batch = @batches.length
1027
- off = @header.num_batch * 4
1028
- @batch_offsets = []
1029
- @header.num_batch.times { |j|
1030
- off = align(off, 0x20)
1031
- @batch_offsets.push off
1032
- off += @batches[j].__size
1033
- }
1034
- end
1035
-
1036
- def duplicate(positions, vertexes, vertexes_ex)
1037
- m = Mesh::new
1038
- m.header = @header
1039
- m.batch_offsets = @batch_offsets
1040
- m.batches = @batches.collect { |b| b.duplicate(positions, vertexes, vertexes_ex) }
1041
- m
1042
- end
1043
-
1044
- end
1045
-
1046
- class ShaderName < LibBin::Structure
1047
- string :name, 16
1048
- end
1049
-
1050
- class TexInfo < LibBin::Structure
1051
- uint32 :id
1052
- int32 :info
1053
- end
1054
-
1055
- class TexInfos < LibBin::Structure
1056
- int32 :num_tex_infos
1057
- register_field :tex_infos, TexInfo, length: 'num_tex_infos'
1058
- end
1059
-
1060
- class WMBFileHeader < LibBin::Structure
1061
- uint32 :id
1062
- int32 :u_a
1063
- int32 :u_b
1064
- int32 :num_vertexes
1065
- int8 :vertex_ex_data_size
1066
- int8 :vertex_ex_data
1067
- int16 :u_e
1068
- int32 :offset_positions
1069
- uint32 :offset_vertexes
1070
- uint32 :offset_vertexes_ex_data
1071
- int32 :u_g, length: 4
1072
- int32 :num_bones
1073
- uint32 :offset_bone_hierarchy
1074
- uint32 :offset_bone_relative_position
1075
- uint32 :offset_bone_position
1076
- uint32 :offset_bone_index_translate_table
1077
- int32 :num_materials
1078
- uint32 :offset_materials_offsets
1079
- uint32 :offset_materials
1080
- int32 :num_meshes
1081
- uint32 :offset_meshes_offsets
1082
- uint32 :offset_meshes
1083
- int32 :u_k
1084
- int32 :u_l
1085
- uint32 :offset_u_j
1086
- uint32 :offset_bone_symmetries
1087
- uint32 :offset_bone_flags
1088
- uint32 :offset_shader_names
1089
- uint32 :offset_tex_infos
1090
- uint32 :u_m
1091
- uint32 :u_n
1092
- end
1093
-
1094
- register_field :header, WMBFileHeader
1095
- register_field :positions, Position, length: 'header\num_vertexes', offset: 'header\offset_positions'
1096
- register_field :vertexes, 'get_vertex_types[0]', length: 'header\num_vertexes', offset: 'header\offset_vertexes'
1097
- register_field :vertexes_ex_data, 'get_vertex_types[1]', length: 'header\num_vertexes',
1098
- offset: 'header\offset_vertexes_ex_data'
1099
- int16 :bone_hierarchy, length: 'header\num_bones', offset: 'header\offset_bone_hierarchy'
1100
- register_field :bone_relative_positions, Position, length: 'header\num_bones',
1101
- offset: 'header\offset_bone_relative_position'
1102
- register_field :bone_positions, Position, length: 'header\num_bones', offset: 'header\offset_bone_position'
1103
- register_field :bone_index_translate_table, BoneIndexTranslateTable,
1104
- offset: 'header\offset_bone_index_translate_table'
1105
- register_field :u_j, UnknownStruct, offset: 'header\offset_u_j'
1106
- int16 :bone_symmetries, length: 'header\num_bones', offset: 'header\offset_bone_symmetries'
1107
- int8 :bone_flags, length: 'header\num_bones', offset: 'header\offset_bone_flags'
1108
- register_field :shader_names, ShaderName, length: 'header\num_materials', offset: 'header\offset_shader_names'
1109
- register_field :tex_infos, TexInfos, offset: 'header\offset_tex_infos'
1110
- uint32 :materials_offsets, length: 'header\num_materials', offset: 'header\offset_materials_offsets'
1111
- register_field :materials, Material, count: 'header\num_materials', sequence: true,
1112
- offset: 'header\offset_materials + materials_offsets[__iterator]'
1113
- uint32 :meshes_offsets, length: 'header\num_meshes', offset: 'header\offset_meshes_offsets'
1114
- register_field :meshes, Mesh, count: 'header\num_meshes', sequence: true,
1115
- offset: 'header\offset_meshes + meshes_offsets[__iterator]'
1116
-
1117
- def texture_ids
1118
- return [] unless @tex_infos
1119
- @tex_infos.tex_infos.collect { |i| i.id }
1120
- end
1121
-
1122
- def materials_textures
1123
- return {} unless @tex_infos
1124
- tex_ids = texture_ids
1125
- @materials.each_with_index.collect { |m, i|
1126
- [i, m.material_data.select { |t| tex_ids.include?(t) }]
1127
- }.to_h
1128
- end
1129
-
1130
- def get_vertex_types
1131
- if @vertex_type
1132
- return [@vertex_type, @vertex_ex_type]
1133
- else
1134
- types = VERTEX_TYPES[ [ @header.u_b, @header.vertex_ex_data_size, @header.vertex_ex_data] ]
1135
- @vertex_type = Class::new(LibBin::Structure)
1136
- @vertex_size = 0
1137
- if types[0]
1138
- types[0].each { |name, type|
1139
- @vertex_type.register_field(name, VERTEX_FIELDS[type][0])
1140
- @vertex_size += VERTEX_FIELDS[type][1]
1141
- }
1142
- end
1143
- @vertex_ex_type = Class::new(LibBin::Structure)
1144
- @vertex_ex_size = 0
1145
- if types[1]
1146
- types[1].each { |name, type|
1147
- @vertex_ex_type.register_field(name, VERTEX_FIELDS[type][0])
1148
- @vertex_ex_size += VERTEX_FIELDS[type][1]
1149
- }
1150
- end
1151
- return [@vertex_type, @vertex_ex_type]
1152
- end
1153
- end
1154
-
1155
- def get_vertex_fields
1156
- if @vertex_fields
1157
- return @vertex_fields
1158
- else
1159
- types = VERTEX_TYPES[ [ @header.u_b, @header.vertex_ex_data_size, @header.vertex_ex_data] ]
1160
- @vertex_fields = []
1161
- if types[0]
1162
- types[0].each { |name, type|
1163
- @vertex_fields.push(name)
1164
- }
1165
- end
1166
- if types[1]
1167
- types[1].each { |name, type|
1168
- @vertex_fields.push(name)
1169
- }
1170
- end
1171
- return @vertex_fields
1172
- end
1173
- end
1174
-
1175
- def get_vertex_field(field, vi)
1176
- if @vertexes[vi].respond_to?(field)
1177
- return @vertexes[vi].send(field)
1178
- elsif @vertexes_ex_data && @vertexes_ex_data[vi].respond_to?(field)
1179
- return @vertexes_ex_data[vi].send(field)
1180
- elsif field == :position && @positions
1181
- return @positions[vi]
1182
- else
1183
- return nil
1184
- end
1185
- end
1186
-
1187
- def set_vertex_field(field, vi, val)
1188
- if @vertexes[vi].respond_to?(field)
1189
- return @vertexes[vi].send(:"#{field}=", val)
1190
- elsif @vertexes_ex_data && @vertexes_ex_data[vi].respond_to?(field)
1191
- return @vertexes_ex_data[vi].send(:"#{field}=", val)
1192
- elsif field == :position && @positions
1193
- return @positions[vi] = val
1194
- else
1195
- raise "Couldn't find field: #{field}!"
1196
- end
1197
- end
1198
-
1199
- def self.convert(input_name, output_name, output_big = false)
1200
- if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1201
- input = input_name
1202
- else
1203
- File.open(input_name, "rb") { |f|
1204
- input = StringIO::new(f.read, "rb")
1205
- }
1206
- end
1207
- input_big = validate_endianness(input)
1208
-
1209
- if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1210
- output = output_name
1211
- else
1212
- output = File.open(output_name, "wb")
1213
- end
1214
- output.write("\xFB"*input.size)
1215
- output.rewind
1216
-
1217
- wmb = self::new
1218
- wmb.instance_variable_set(:@__was_big, input_big)
1219
- wmb.__convert(input, output, input_big, output_big)
1220
-
1221
- input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1222
- output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1223
- wmb
1224
- end
1225
-
1226
- def self.load(input_name)
1227
- if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1228
- input = input_name
1229
- else
1230
- input = File.open(input_name, "rb")
1231
- end
1232
- if is_wmb3?(input)
1233
- input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1234
- return WMB3File::load(input_name)
1235
- end
1236
- input = StringIO::new(input.read, "rb")
1237
- input_big = validate_endianness(input)
1238
-
1239
- wmb = self::new
1240
- wmb.instance_variable_set(:@__was_big, input_big)
1241
- wmb.__load(input, input_big)
1242
- input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1243
-
1244
- wmb
1245
- end
1246
-
1247
- def was_big?
1248
- @__was_big
1249
- end
1250
-
1251
- def is_bayo2?
1252
- @header.offset_shader_names != 0 || @header.offset_tex_infos != 0
1253
- end
1254
-
1255
- def self.is_wmb3?(input)
1256
- input.rewind
1257
- id = input.read(4).unpack("a4").first
1258
- input.rewind
1259
- return id == "WMB3".b || id == "3BMW".b
1260
- end
1261
-
1262
- def self.validate_endianness(input)
1263
- input.rewind
1264
- id = input.read(4).unpack("a4").first
1265
- case id
1266
- when "WMB\0".b
1267
- input_big = false
1268
- when "\0BMW".b
1269
- input_big = true
1270
- else
1271
- raise "Invalid file type #{id}!"
1272
- end
1273
- input.rewind
1274
- input_big
1275
- end
1276
-
1277
- def dump(output_name, output_big = false)
1278
- if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1279
- output = output_name
1280
- else
1281
- output = StringIO::new("", "wb")
1282
- end
1283
- output.rewind
1284
-
1285
- __set_dump_state(output, output_big, nil, nil)
1286
- __dump_fields
1287
- __unset_dump_state
1288
-
1289
- sz = output.size
1290
- sz = align(sz, 0x20)
1291
- if sz > output.size
1292
- output.seek(sz-1)
1293
- output.write("\x00")
1294
- end
1295
-
1296
- unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1297
- File.open(output_name, "wb") { |f|
1298
- f.write output.string
1299
- }
1300
- output.close
1301
- end
1302
- self
1303
- end
1304
-
1305
- def check_normals
1306
- return self if @__was_big
1307
- wide_normals = false
1308
- @vertexes.each { |v|
1309
- if v.normal.normal_small_orig.bytes.first == 0
1310
- wide_normals = true
1311
- end
1312
- }
1313
- if wide_normals
1314
- @vertexes.each { |v|
1315
- v.normal.wide = true
1316
- v.normal.redecode_wide
1317
- }
1318
- end
1319
- self
1320
- end
1321
-
1322
- def normals_to_wide
1323
- @vertexes.each { |v|
1324
- v.normal.wide = true
1325
- v.normal.normal_big_orig = nil
1326
- v.normal.normal_small_orig = nil
1327
- }
1328
- self
1329
- end
1330
-
1331
- def normals_to_narrow
1332
- @vertexes.each { |v|
1333
- v.normal.wide = false
1334
- v.normal.normal_big_orig = nil
1335
- v.normal.normal_small_orig = nil
1336
- }
1337
- self
1338
- end
1339
-
1340
- def get_bone_structure
1341
- bones = @bone_positions.collect { |p|
1342
- Bone::new(p)
1343
- }
1344
- bones.each_with_index { |b, i|
1345
- if @bone_hierarchy[i] == -1
1346
- b.parent = nil
1347
- else
1348
- b.parent = bones[@bone_hierarchy[i]]
1349
- bones[@bone_hierarchy[i]].children.push(b)
1350
- end
1351
- b.index = i
1352
- b.relative_position = @bone_relative_positions[i]
1353
- b.symmetric = @bone_symmetries[i] if @header.offset_bone_symmetries > 0x0
1354
- b.flag = @bone_flags[i] if @header.offset_bone_flags > 0x0
1355
- }
1356
- end
1357
-
1358
- def recompute_relative_positions
1359
- @bone_hierarchy.each_with_index { |b, i|
1360
- if b != -1
1361
- #puts "bone: #{i} parent: #{b} position: #{@bone_positions[i]} pposition: #{@bone_positions[b]}"
1362
- @bone_relative_positions[i] = @bone_positions[i] - @bone_positions[b]
1363
- else
1364
- @bone_relative_positions[i] = @bone_positions[i]
1365
- end
1366
- }
1367
- self
1368
- end
1369
-
1370
- def set_bone_structure(bones)
1371
- @bone_hierarchy = []
1372
- @bone_relative_positions = []
1373
- @bone_positions = []
1374
- @bone_symmetries = [] if @header.offset_bone_symmetries > 0x0
1375
- @bone_flags = [] if @header.offset_bone_flags > 0x0
1376
- bones.each { |b|
1377
- p_index = -1
1378
- p_index = b.parent.index if b.parent
1379
- @bone_hierarchy.push p_index
1380
- @bone_positions.push b.position
1381
- rel_position = b.relative_position
1382
- unless rel_position
1383
- if b.parent
1384
- rel_position = b.position - b.parent.position
1385
- else
1386
- rel_position = b.position
1387
- end
1388
- end
1389
- @bone_relative_positions.push rel_position
1390
- @bone_symmetries.push b.symmetric if @header.offset_bone_symmetries > 0x0
1391
- @bone_flags.push b.flag if @header.offset_bone_flags > 0x0
1392
- }
1393
- @header.num_bones = bones.size
1394
- self
1395
- end
1396
-
1397
- def scale(s)
1398
- if @positions
1399
- @positions.each { |p|
1400
- p.x = p.x * s
1401
- p.y = p.y * s
1402
- p.z = p.z * s
1403
- }
1404
- end
1405
- if @vertexes && @vertexes.first.respond_to?(:position)
1406
- @vertexes.each { |v|
1407
- v.position.x = v.position.x * s
1408
- v.position.y = v.position.y * s
1409
- v.position.z = v.position.z * s
1410
- }
1411
- end
1412
- if @vertexes && @vertexes.first.respond_to?(:position2)
1413
- @vertexes.each { |v|
1414
- v.position2.x = v.position2.x * s
1415
- v.position2.y = v.position2.y * s
1416
- v.position2.z = v.position2.z * s
1417
- }
1418
- end
1419
- if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1420
- @vertexes_ex_data.each { |v|
1421
- v.position2.x = v.position2.x * s
1422
- v.position2.y = v.position2.y * s
1423
- v.position2.z = v.position2.z * s
1424
- }
1425
- end
1426
- if @bone_positions
1427
- @bone_positions.each { |p|
1428
- p.x = p.x * s
1429
- p.y = p.y * s
1430
- p.z = p.z * s
1431
- }
1432
- recompute_relative_positions
1433
- end
1434
- self
1435
- end
1436
-
1437
- def shift(x, y, z)
1438
- if @positions
1439
- @positions.each { |p|
1440
- p.x = p.x + x
1441
- p.y = p.y + y
1442
- p.z = p.z + z
1443
- }
1444
- end
1445
- if @vertexes && @vertexes.first.respond_to?(:position)
1446
- @vertexes.each { |v|
1447
- v.position.x = v.position.x + x
1448
- v.position.y = v.position.y + y
1449
- v.position.z = v.position.z + z
1450
- }
1451
- end
1452
- if @vertexes && @vertexes.first.respond_to?(:position2)
1453
- @vertexes.each { |v|
1454
- v.position2.x = v.position2.x + x
1455
- v.position2.y = v.position2.y + y
1456
- v.position2.z = v.position2.z + z
1457
- }
1458
- end
1459
- if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1460
- @vertexes_ex_data.each { |v|
1461
- v.position2.x = v.position2.x + x
1462
- v.position2.y = v.position2.y + y
1463
- v.position2.z = v.position2.z + z
1464
- }
1465
- end
1466
- if @bone_positions
1467
- @bone_positions.each { |p|
1468
- p.x = p.x + x
1469
- p.y = p.y + y
1470
- p.z = p.z + z
1471
- }
1472
- recompute_relative_positions
1473
- end
1474
- self
1475
- end
1476
-
1477
- def rotate(*args)
1478
- if args.length == 2
1479
- (rx, ry, rz), center = args
1480
- elsif args.length == 3
1481
- rx, ry, rz = args
1482
- center = nil
1483
- else
1484
- raise "Invalid arguments for rotate: #{args.inspect}!"
1485
- end
1486
- m = Linalg::get_rotation_matrix(rx, ry, rz, center: center)
1487
- if @positions
1488
- @positions.each { |p|
1489
- r = m * Linalg::Vector::new(p.x, p.y, p.z)
1490
- p.x = r.x
1491
- p.y = r.y
1492
- p.z = r.z
1493
- }
1494
- end
1495
- if @vertexes && @vertexes.first.respond_to?(:position)
1496
- @vertexes.each { |v|
1497
- r = m * Linalg::Vector::new(v.position.x, v.position.y, v.position.z)
1498
- v.position.x = r.x
1499
- v.position.y = r.y
1500
- v.position.z = r.z
1501
- }
1502
- end
1503
- if @vertexes && @vertexes.first.respond_to?(:position2)
1504
- @vertexes.each { |v|
1505
- r = m * Linalg::Vector::new(v.position2.x, v.position2.y, v.position2.z)
1506
- v.position2.x = r.x
1507
- v.position2.y = r.y
1508
- v.position2.z = r.z
1509
- }
1510
- end
1511
- if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1512
- @vertexes_ex_data.each { |v|
1513
- r = m * Linalg::Vector::new(v.position2.x, v.position2.y, v.position2.z)
1514
- v.position2.x = r.x
1515
- v.position2.y = r.y
1516
- v.position2.z = r.z
1517
- }
1518
- end
1519
- if @bone_positions
1520
- @bone_positions.each { |p|
1521
- r = m * Linalg::Vector::new(p.x, p.y, p.z)
1522
- p.x = r.x
1523
- p.y = r.y
1524
- p.z = r.z
1525
- }
1526
- recompute_relative_positions
1527
- end
1528
- if @vertexes
1529
- @vertexes.each { |v|
1530
- r = m * Linalg::Vector::new(v.normal.x, v.normal.y, v.normal.z, 0.0)
1531
- v.normal.x = r.x
1532
- v.normal.y = r.y
1533
- v.normal.z = r.z
1534
- if v.tangents.data != 0xc0c0c0ff
1535
- r = m * Linalg::Vector::new(v.tangents.x, v.tangents.y, v.tangents.z, 0.0)
1536
- v.tangents.x = r.x
1537
- v.tangents.y = r.y
1538
- v.tangents.z = r.z
1539
- end
1540
- }
1541
- end
1542
- self
1543
- end
1544
-
1545
- def set_pose(pose, exp)
1546
- table = @bone_index_translate_table.table
1547
- bones = get_bone_structure
1548
- tracks = Hash::new { |h,k| h[k] = {} }
1549
- tracks = bones.collect { |b|
1550
- [ b.relative_position.x,
1551
- b.relative_position.y,
1552
- b.relative_position.z,
1553
- 0.0, 0.0, 0.0,
1554
- 0.0,
1555
- 1.0, 1.0, 1.0,
1556
- 1.0, 1.0, 1.0 ]
1557
- }
1558
- pose.each { |b, ts|
1559
- bi = table[b]
1560
- if bi
1561
- ts.each { |ti, v|
1562
- tracks[bi][ti] = v
1563
- }
1564
- end
1565
- }
1566
- if exp
1567
- exp.apply(tracks, table)
1568
- end
1569
- matrices = tracks.each_with_index.collect { |ts, bi|
1570
- if @header.offset_bone_flags > 0x0
1571
- order = @bone_flags[bi]
1572
- else
1573
- order = nil
1574
- end
1575
- m = Linalg::get_translation_matrix(*ts[0..2])
1576
- pi = @bone_hierarchy[bi]
1577
- if pi != -1
1578
- parent_cumulative_scale = tracks[pi][10..12]
1579
- m = m * Linalg::get_inverse_scaling_matrix(*parent_cumulative_scale)
1580
- 3.times { |i| ts[10+i] *= parent_cumulative_scale[i] }
1581
- end
1582
- 3.times { |i| ts[10+i] *= ts[7+i] }
1583
- m = m * Linalg::get_rotation_matrix(*ts[3..5], order: order)
1584
- m = m * Linalg::get_scaling_matrix(*ts[10..12])
1585
- }
1586
- multiplied_matrices = []
1587
- inverse_bind_pose = bones.collect { |b|
1588
- Linalg::get_translation_matrix(-1 * b.position.x, -1 * b.position.y, -1 * b.position.z)
1589
- }
1590
- bones.each { |b|
1591
- if b.parent
1592
- multiplied_matrices[b.index] = multiplied_matrices[b.parent.index] * matrices[b.index]
1593
- else
1594
- multiplied_matrices[b.index] = matrices[b.index]
1595
- end
1596
- }
1597
- bones.each { |b|
1598
- v = multiplied_matrices[b.index] * Linalg::Vector::new( 0.0, 0.0, 0.0 )
1599
- b.position.x = v.x
1600
- b.position.y = v.y
1601
- b.position.z = v.z
1602
- b.relative_position = nil
1603
- }
1604
- set_bone_structure(bones)
1605
- multiplied_matrices = bones.collect { |b|
1606
- multiplied_matrices[b.index] * inverse_bind_pose[b.index]
1607
- }
1608
- vertex_usage = get_vertex_usage
1609
- vertex_usage.each { |vi, bs|
1610
- bone_refs = bs.first.bone_refs
1611
- bone_infos = get_vertex_field(:bone_infos, vi)
1612
- indexes_and_weights = bone_infos.get_indexes_and_weights
1613
- vertex_matrix = Linalg::get_zero_matrix
1614
- indexes_and_weights.each { |bi, bw|
1615
- i = bone_refs[bi]
1616
- vertex_matrix = vertex_matrix + multiplied_matrices[i] * (bw.to_f/255.to_f)
1617
- }
1618
- normal_matrix = vertex_matrix.inverse.transpose
1619
- vp = get_vertex_field(:position, vi)
1620
- new_vp = vertex_matrix * Linalg::Vector::new(vp.x, vp.y, vp.z)
1621
- vp.x = new_vp.x
1622
- vp.y = new_vp.y
1623
- vp.z = new_vp.z
1624
- n = get_vertex_field(:normal, vi)
1625
- new_n = (normal_matrix * Linalg::Vector::new(n.x, n.y, n.z, 0.0)).normalize
1626
- n.x = new_n.x
1627
- n.y = new_n.y
1628
- n.z = new_n.z
1629
- t = get_vertex_field(:tangents, vi)
1630
- new_t = (normal_matrix * Linalg::Vector::new(t.x, t.y, t.z, 0.0)).normalize
1631
- t.x = new_t.x
1632
- t.y = new_t.y
1633
- t.z = new_t.z
1634
- p2 = get_vertex_field(:position2, vi)
1635
- if p2
1636
- new_p2 = vertex_matrix * Linalg::Vector::new(p2.x, p2.y, p2.z)
1637
- p2.x = new_p2.x
1638
- p2.y = new_p2.y
1639
- p2.z = new_p2.z
1640
- end
1641
- }
1642
- self
1643
- end
1644
-
1645
- def restrict_bones(used_bones)
1646
- bones = get_bone_structure
1647
- used_bones_array = used_bones.to_a.sort
1648
- bone_map = used_bones_array.each_with_index.collect.to_h
1649
- new_bones = used_bones_array.collect { |bi|
1650
- b = bones[bi].dup
1651
- b.index = bone_map[b.index]
1652
- b
1653
- }
1654
- new_bones.each { |b|
1655
- b.parent = new_bones[bone_map[b.parent.index]] if b.parent
1656
- }
1657
- set_bone_structure(new_bones)
1658
-
1659
- table = @bone_index_translate_table.table
1660
- new_table = table.select { |k,v|
1661
- used_bones.include? v
1662
- }
1663
- new_table = new_table.collect { |k, v| [k, bone_map[v]] }.to_h
1664
- @bone_index_translate_table.table = new_table
1665
- @meshes.each_with_index { |m, i|
1666
- m.batches.each_with_index { |b, j|
1667
- b.bone_refs.collect! { |bi|
1668
- new_bi = bone_map[bi]
1669
- raise "Bone #{bi} was deleted bu is still used by mesh #{i} batch #{j}!" unless new_bi
1670
- new_bi
1671
- }
1672
- }
1673
- }
1674
- self
1675
- end
1676
- private :restrict_bones
1677
-
1678
- def remap_bones(bone_map)
1679
- raise "Global index specified multiple times!" unless bone_map.values.uniq.size == bone_map.size
1680
- local_to_global = @bone_index_translate_table.table.invert
1681
- unknown_bones = bone_map.keys - local_to_global.keys
1682
- raise "Unknown bones: #{unknown_bones}!" unless unknown_bones.size == 0
1683
- global_tt = {}
1684
- bone_map.each { |k, v|
1685
- global_tt[local_to_global.delete(k)] = v
1686
- }
1687
- puts global_tt
1688
- table = local_to_global.invert
1689
- new_global_indexes = bone_map.values - table.keys
1690
- raise "Global indexes: #{bone_map.values - new_global_indexes} still in use!" unless new_global_indexes.size == bone_map.size
1691
- bone_map.each { |k, v|
1692
- table[v] = k
1693
- }
1694
- @bone_symmetries.collect! { |k|
1695
- global_tt[k] ? global_tt[k] : k
1696
- } if @bone_symmetries
1697
- @bone_index_translate_table.table = table
1698
- self
1699
- end
1700
-
1701
- def order_bones
1702
- arr = @bone_index_translate_table.table.sort
1703
- old_local_to_new_local = {}
1704
- arr.each_with_index { |(_, old_local), new_local|
1705
- old_local_to_new_local[old_local] = new_local
1706
- }
1707
- new_local_to_old_local = old_local_to_new_local.invert
1708
- bones = get_bone_structure
1709
- new_bones = bones.size.times.collect { |new_bi|
1710
- b = bones[new_local_to_old_local[new_bi]]
1711
- b.index = new_bi
1712
- b
1713
- }
1714
- new_bones.each { |b|
1715
- raise "Invali hierarchy: #{b.parent.index} >= #{b.index} !" if b.parent && b.parent.index >= b.index
1716
- }
1717
- set_bone_structure(new_bones)
1718
- new_table = @bone_index_translate_table.table.collect { |k, v| [k, old_local_to_new_local[v]] }.to_h
1719
- @bone_index_translate_table.table = new_table
1720
- @meshes.each_with_index { |m, i|
1721
- m.batches.each_with_index { |b, j|
1722
- b.bone_refs.collect! { |bi|
1723
- old_local_to_new_local[bi]
1724
- }
1725
- }
1726
- }
1727
- self
1728
- end
1729
-
1730
- def delete_meshes(list)
1731
- kept_meshes = @meshes.size.times.to_a - list
1732
- @meshes = kept_meshes.collect { |i|
1733
- @meshes[i]
1734
- }
1735
- @header.num_meshes = @meshes.size
1736
- self
1737
- end
1738
-
1739
- def split_meshes(list)
1740
- kept_meshes = @meshes.size.times.to_a - list
1741
- split_meshes = @meshes.size.times.to_a - kept_meshes
1742
- new_meshes = []
1743
- split_meshes.each { |i|
1744
- @meshes[i].batches.each_with_index { |b, j|
1745
- new_mesh = @meshes[i].dup
1746
- new_mesh.header = @meshes[i].header.dup
1747
- new_mesh.header.name = @meshes[i].header.name.tr("\x00","") + ("_%02d" % j)
1748
- new_mesh.batches = [b]
1749
- new_meshes.push new_mesh
1750
- }
1751
- }
1752
- @meshes = kept_meshes.collect { |i|
1753
- @meshes[i]
1754
- }
1755
- @meshes += new_meshes
1756
- @header.num_meshes = @meshes.size
1757
- self
1758
- end
1759
-
1760
- def dummy_meshes(list)
1761
- list.map { |i| m = @meshes[i]
1762
- m.header.num_batch = 1
1763
- m.batches = [m.batches.first]
1764
- m.batches[0].set_triangles([m.batches[0].triangles.first[0]]*3)
1765
- }
1766
- self
1767
- end
1768
-
1769
- def duplicate_meshes(list)
1770
- @meshes += list.collect { |i|
1771
- @meshes[i].duplicate(@positions, @vertexes, @vertexes_ex_data)
1772
- }
1773
- @header.num_meshes = @meshes.size
1774
- @header.num_vertexes = @vertexes.size
1775
- self
1776
- end
1777
-
1778
- def swap_meshes(hash)
1779
- hash.each { |k, v|
1780
- raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1781
- raise "Mesh #{v} was not found in the model!" unless @meshes[v]
1782
- tmp = @meshes[k]
1783
- @meshes[k] = @meshes[v]
1784
- @meshes[v] = tmp
1785
- }
1786
- self
1787
- end
1788
-
1789
- def move_meshes(positions)
1790
- raise "Invalid positions!" unless positions.size > 0
1791
- positions.each { |k, v|
1792
- raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1793
- raise "Invalid target position #{v}!" unless v >= 0 && v < @meshes.length
1794
- }
1795
- raise "Duplicate mesh found!" unless positions.keys.uniq.size == positions.size
1796
- raise "Duplicate target position found!" unless positions.values.uniq.size == positions.size
1797
- m_p = positions.to_a.sort { |(m1, _), (m2, _)| m2 <=> m1 }
1798
- m_a = m_p.collect { |m, p|
1799
- [@meshes.delete_at(m), p]
1800
- }.sort { |(_, p1), (_, p2)| p1 <=> p2 }
1801
- m_a.each { |m, p|
1802
- @meshes.insert(p, m)
1803
- }
1804
- self
1805
- end
1806
-
1807
- def merge_meshes(hash)
1808
- hash.each { |k, vs|
1809
- raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1810
- vs = [vs].flatten
1811
- vs.each { |v|
1812
- raise "Mesh #{v} was not found in the model!" unless @meshes[v]
1813
- @meshes[k].batches += @meshes[v].batches
1814
- }
1815
- @meshes[k].header.num_batch = @meshes[k].batches.length
1816
- }
1817
- self
1818
- end
1819
-
1820
- def delete_batches(hash)
1821
- hash.each { |k, list|
1822
- raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1823
- list = case list
1824
- when Enumerable
1825
- list.to_a
1826
- else
1827
- [list]
1828
- end
1829
- kept_batches = @meshes[k].batches.length.times.to_a - list
1830
- @meshes[k].batches = kept_batches.collect { |i|
1831
- raise "Batch #{i} was not found in mesh #{k}!" unless @meshes[k].batches[i]
1832
- @meshes[k].batches[i]
1833
- }
1834
- @meshes[k].header.num_batch = @meshes[k].batches.length
1835
- }
1836
- self
1837
- end
1838
-
1839
- def delete_bones(list)
1840
- used_bones = (@header.num_bones.times.to_a - list)
1841
- restrict_bones(used_bones)
1842
- self
1843
- end
1844
-
1845
- def cleanup_textures(input_name, overwrite)
1846
- if File.exist?(input_name.gsub(".wmb",".wtb"))
1847
- wtb = WTBFile::new(File::new(input_name.gsub(".wmb",".wtb"), "rb"))
1848
- if overwrite
1849
- output_name = input_name.gsub(".wmb",".wtb")
1850
- else
1851
- output_name = "wtb_output/#{File.basename(input_name, ".wmb")}.wtb"
1852
- end
1853
- wtp = false
1854
- elsif File.exist?(input_name.gsub(".wmb",".wta"))
1855
- wtb = WTBFile::new(File::new(input_name.gsub(".wmb",".wta"), "rb"), true, File::new(input_name.gsub(".wmb",".wtp"), "rb"))
1856
- if overwrite
1857
- output_name = input_name.gsub(".wmb",".wta")
1858
- else
1859
- output_name = "wtb_output/#{File.basename(input_name, ".wmb")}.wta"
1860
- end
1861
- wtp = true
1862
- else
1863
- raise "Could not find texture file!"
1864
- end
1865
-
1866
- available_textures = {}
1867
- digests = []
1868
- wtb.each.with_index { |(info, t), i|
1869
- if @tex_info #Bayo 2
1870
- digest = Digest::SHA1.hexdigest(t.read)
1871
- available_textures[info[2]] = digest
1872
- digests.push( digest )
1873
- else #Bayo 1
1874
- digest = Digest::SHA1.hexdigest(t.read)
1875
- available_textures[i] = digest
1876
- digests.push( digest )
1877
- end
1878
- t.rewind
1879
- }
1880
- used_textures_digest_map = {}
1881
- used_texture_digests = Set[]
1882
- @materials.each { |m|
1883
- m.material_data[0..4].each { |tex_id|
1884
- if available_textures.key?(tex_id)
1885
- digest = available_textures[tex_id]
1886
- used_textures_digest_map[tex_id] = digest
1887
- used_texture_digests.add(digest)
1888
- end
1889
- }
1890
- }
1891
- index_list = digests.each_with_index.collect { |d,i| [i,d] }.select { |i, d|
1892
- used_texture_digests.delete?(d)
1893
- }.collect { |i,d| i }
1894
- new_wtb = WTBFile::new(nil, wtb.big, wtp)
1895
- j = 0
1896
- digest_to_tex_id_map = {}
1897
- wtb.each.with_index { |(info, t), i|
1898
- if index_list.include?(i)
1899
- new_wtb.push( t, info[1], info[2])
1900
- if @tex_info
1901
- digest_to_tex_id_map[digests[i]] = info[2]
1902
- else
1903
- digest_to_tex_id_map[digests[i]] = j
1904
- end
1905
- j += 1
1906
- end
1907
- }
1908
- new_wtb.dump(output_name)
1909
- @materials.each { |m|
1910
- m.material_data[0..4].each_with_index { |tex_id, i|
1911
- if available_textures.key?(tex_id)
1912
- digest = available_textures[tex_id]
1913
- m.material_data[i] = digest_to_tex_id_map[digest]
1914
- end
1915
- }
1916
- }
1917
- end
1918
-
1919
- def advanced_materials
1920
- if is_bayo2?
1921
- materials
1922
- else
1923
- materials.collect { |m|
1924
- if $material_db[m.type][:layout]
1925
- Bayo1Material::new(m)
1926
- else
1927
- m
1928
- end
1929
- }
1930
- end
1931
- end
1932
-
1933
- def cleanup_materials
1934
- used_materials = Set[]
1935
- @meshes.each { |m|
1936
- m.batches.each { |b|
1937
- if @tex_infos #Bayo 2
1938
- used_materials.add(b.header.ex_mat_id)
1939
- else #Bayo 1
1940
- used_materials.add(b.header.material_id)
1941
- end
1942
- }
1943
- }
1944
- materials = @header.num_materials.times.to_a
1945
- kept_materials = materials & used_materials.to_a
1946
- correspondance_table = kept_materials.each_with_index.to_h
1947
- @materials.select!.with_index { |_, i| used_materials.include?(i) }
1948
- @header.num_materials = used_materials.size
1949
- if @shader_names
1950
- @shader_names.select!.with_index { |_, i| used_materials.include?(i) }
1951
- end
1952
- @meshes.each { |m|
1953
- m.batches.each { |b|
1954
- if @tex_infos
1955
- b.header.ex_mat_id = correspondance_table[b.header.ex_mat_id]
1956
- else
1957
- b.header.material_id = correspondance_table[b.header.material_id]
1958
- end
1959
- }
1960
- }
1961
- self
1962
- end
1963
-
1964
- def cleanup_material_sizes
1965
- raise "Unsupported for Bayonetta 2!" if @shader_names
1966
- @materials.each { |m|
1967
- type = m.type
1968
- if $material_db.key?(type) && $material_db[type][:size]
1969
- size = $material_db[type][:size]
1970
- else
1971
- warn "Unknown material type #{m.type}!"
1972
- next
1973
- end
1974
- data_number = (size - 4)/4
1975
- m.material_data = m.material_data.first(data_number)
1976
- }
1977
- self
1978
- end
1979
-
1980
- def maximize_material_sizes
1981
- raise "Unsupported for Bayonetta 2!" if @shader_names
1982
- max_size_mat = $material_db.select { |k, v| v[:size] }.max_by { |k, v|
1983
- v[:size]
1984
- }
1985
- max_data_number = (max_size_mat[1][:size] - 4)/4
1986
- @materials.each { |m|
1987
- m.material_data = m.material_data + [0]*(max_data_number - m.material_data.size)
1988
- }
1989
- self
1990
- end
1991
-
1992
- def cleanup_bone_refs
1993
- @meshes.each { |m|
1994
- m.batches.each { |b|
1995
- b.cleanup_bone_refs(@vertexes)
1996
- }
1997
- }
1998
- self
1999
- end
2000
-
2001
- def add_ancestors_bone_refs
2002
- @meshes.each { |m|
2003
- m.batches.each { |b|
2004
- b.add_ancestors_bone_refs(@vertexes, get_bone_structure)
2005
- }
2006
- }
2007
- self
2008
- end
2009
-
2010
- def add_previous_bone_refs
2011
- @meshes.each { |m|
2012
- m.batches.each { |b|
2013
- b.add_previous_bone_refs(@vertexes, get_bone_structure)
2014
- }
2015
- }
2016
- self
2017
- end
2018
-
2019
- def cleanup_bones
2020
- used_bones = Set[]
2021
- @meshes.each { |m|
2022
- m.batches.each { |b|
2023
- used_bones.merge b.bone_refs
2024
- }
2025
- }
2026
- bones = get_bone_structure
2027
- used_bones.to_a.each { |bi|
2028
- used_bones.merge bones[bi].parents.collect(&:index)
2029
- }
2030
- restrict_bones(used_bones)
2031
- self
2032
- end
2033
-
2034
- def dump_bones(list = nil)
2035
- bone_struct = Struct::new(:index, :parent, :relative_position, :position, :global_index, :symmetric, :flag)
2036
- table = @bone_index_translate_table.table.invert
2037
- list = (0...@header.num_bones) unless list
2038
- list.collect { |bi|
2039
- bone_struct::new(bi, @bone_hierarchy[bi], @bone_relative_positions[bi], @bone_positions[bi], table[bi], @header.offset_bone_symmetries > 0x0 ? @bone_symmetries[bi] : -1, @header.offset_bone_flags > 0x0 ? @bone_flags[bi] : 5)
2040
- }
2041
- end
2042
-
2043
- def import_bones( list )
2044
- table = @bone_index_translate_table.table
2045
- @header.num_bones += list.length
2046
- list.each { |b|
2047
- table[b[:global_index]] = b[:index]
2048
- @bone_hierarchy.push b[:parent]
2049
- @bone_relative_positions.push b[:relative_position]
2050
- @bone_positions.push b[:position]
2051
- @bone_symmetries.push b[:symmetric] if @header.offset_bone_symmetries > 0x0
2052
- @bone_flags.push b[:flag] if @header.offset_bone_flags > 0x0
2053
- }
2054
- @bone_index_translate_table.table = table
2055
- self
2056
- end
2057
-
2058
- def remove_triangle_strips
2059
- @meshes.each { |m|
2060
- m.batches.each { |b|
2061
- b.set_triangles(b.triangles)
2062
- }
2063
- }
2064
- end
2065
-
2066
- def revert_triangles( mesh_list )
2067
- mesh_list.each { |mesh_index|
2068
- @meshes[mesh_index].batches.each { |b|
2069
- b.set_triangles( b.triangles.collect { |n1, n2, n3|
2070
- [n1, n3, n2]
2071
- })
2072
- }
2073
- }
2074
- end
2075
-
2076
- def cleanup_vertexes
2077
- used_vertex_indexes = []
2078
- @meshes.each { |m|
2079
- m.batches.each { |b|
2080
- used_vertex_indexes += b.vertex_indices
2081
- }
2082
- }
2083
- used_vertex_indexes = used_vertex_indexes.sort.uniq
2084
- @vertexes = used_vertex_indexes.collect { |i| @vertexes[i] }
2085
- @vertexes_ex_data = used_vertex_indexes.collect { |i| @vertexes_ex_data[i] } if @vertexes_ex_data
2086
- @header.num_vertexes = @vertexes.size
2087
- vertex_map = used_vertex_indexes.each_with_index.to_h
2088
- @meshes.each { |m|
2089
- m.batches.each { |b|
2090
- b.indices.collect! { |i|
2091
- vertex_map[i + b.header.vertex_offset]
2092
- }
2093
- b.recompute_from_absolute_indices
2094
- }
2095
- }
2096
- self
2097
- end
2098
-
2099
- def get_vertex_usage
2100
- vertex_usage = Hash::new { |h, k| h[k] = [] }
2101
- @meshes.each { |m|
2102
- m.batches.each { |b|
2103
- b.vertex_indices.each { |i|
2104
- vertex_usage[i].push(b)
2105
- }
2106
- }
2107
- }
2108
- vertex_usage.each { |k,v| v.uniq! }
2109
- vertex_usage
2110
- end
2111
-
2112
- # Duplicate vertexes used by several batches
2113
- def normalize_vertex_usage
2114
- vertex_usage = get_vertex_usage
2115
- vertex_usage.select! { |k, v| v.length > 1 }
2116
- batches = Set::new
2117
- vertex_usage.each { |vi, blist|
2118
- batches.merge blist[1..-1]
2119
- }
2120
- batches.each { |b|
2121
- new_batch = b.duplicate(@positions, @vertexes, @vertexes_ex_data)
2122
- b.header = new_batch.header
2123
- b.indices = new_batch.indices
2124
- }
2125
- @header.num_vertexes = @vertexes.size
2126
- self
2127
- end
2128
-
2129
- def remove_duplicate_vertexes
2130
- vertex_indices = @header.num_vertexes.times
2131
- equivalent_vertex_indices = vertex_indices.group_by { |indx|
2132
- data = get_vertex_fields.collect { |f|
2133
- get_vertex_field(f, indx).to_a
2134
- }
2135
- }
2136
- indx_map = {}
2137
- equivalent_vertex_indices.each { |k, group|
2138
- group.each { |indx|
2139
- indx_map[indx] = group.first
2140
- }
2141
- }
2142
- @meshes.each { |m|
2143
- m.batches.each { |b|
2144
- b.indices = b.vertex_indices.collect { |i|
2145
- indx_map[i]
2146
- }
2147
- b.recompute_from_absolute_indices
2148
- }
2149
- }
2150
- end
2151
-
2152
- def copy_vertex_properties(vertex_hash, **options)
2153
- vertex_usage = nil
2154
- vertex_usage = get_vertex_usage if options[:bone_infos]
2155
- vertex_hash.each { |ivi, ovis|
2156
- ovis = [ovis].flatten
2157
- ovis.each { |ovi|
2158
- iv = @vertexes[ivi]
2159
- ov = @vertexes[ovi]
2160
- if options[:position]
2161
- if @positions
2162
- @positions[ovi].x = @positions[ivi].x
2163
- @positions[ovi].y = @positions[ivi].y
2164
- @positions[ovi].z = @positions[ivi].z
2165
- end
2166
- if ov.respond_to?(:position)
2167
- ov.position.x = iv.position.x
2168
- ov.position.y = iv.position.y
2169
- ov.position.z = iv.position.z
2170
- end
2171
- if ov.respond_to?(:position2)
2172
- ov.position2.x = iv.position2.x
2173
- ov.position2.y = iv.position2.y
2174
- ov.position2.z = iv.position2.z
2175
- end
2176
- if @vertexes_ex_data
2177
- if @vertexes_ex_data[ovi].respond_to?(:position2)
2178
- @vertexes_ex_data[ovi].position2.x = @vertexes_ex_data[ivi].position2.x
2179
- @vertexes_ex_data[ovi].position2.y = @vertexes_ex_data[ivi].position2.y
2180
- @vertexes_ex_data[ovi].position2.z = @vertexes_ex_data[ivi].position2.z
2181
- end
2182
- end
2183
- end
2184
- if options[:mapping]
2185
- if ov.respond_to?(:mapping)
2186
- ov.mapping.u = iv.mapping.u
2187
- ov.mapping.v = iv.mapping.v
2188
- end
2189
- if ov.respond_to?(:mapping2)
2190
- ov.mapping2.u = iv.mapping2.u
2191
- ov.mapping2.v = iv.mapping2.v
2192
- end
2193
- if @vertexes_ex_data && @vertexes_ex_data[ovi].respond_to?(:mapping2)
2194
- @vertexes_ex_data[ovi].mapping2.u = @vertexes_ex_data[ivi].mapping2.u
2195
- @vertexes_ex_data[ovi].mapping2.v = @vertexes_ex_data[ivi].mapping2.v
2196
- end
2197
- end
2198
- if options[:normal]
2199
- ov.normal = iv.normal
2200
- end
2201
- if options[:tangents]
2202
- ov.tangents = iv.tangents
2203
- end
2204
- if options[:color]
2205
- if ov.respond_to?(:color)
2206
- ov.color = iv.color
2207
- end
2208
- if @vertexes_ex_data && @vertexes_ex_data[ovi].respond_to?(:color)
2209
- @vertexes_ex_data[ovi].color = @vertexes_ex_data[ivi].color
2210
- end
2211
- end
2212
- if options[:bone_infos] && ov.respond_to?(:bone_infos)
2213
- input_batches = vertex_usage[ivi]
2214
- raise "Unormalized vertex #{ivi} , normalize first, and recompute vertex numbers!" if input_batches.length > 1
2215
- raise "Unused vertex #{ivi}!" if input_batches.length == 0
2216
- output_batches = vertex_usage[ovi]
2217
- raise "Unormalized vertex #{ovi} , normalize first, and recompute vertex numbers!" if output_batches.length > 1
2218
- raise "Unused vertex #{ovi}!" if output_batches.length == 0
2219
- input_batch = input_batches.first
2220
- output_batch = output_batches.first
2221
- input_bone_indexes_and_weights = iv.bone_infos.get_indexes_and_weights
2222
- output_bone_indexes_and_weights = input_bone_indexes_and_weights.collect { |bi, bw|
2223
- [input_batch.bone_refs[bi], bw]
2224
- }.collect { |bi, bw|
2225
- new_bi = output_batch.bone_refs.find_index(bi)
2226
- unless new_bi
2227
- new_bi = output_batch.bone_refs.length
2228
- output_batch.bone_refs.push(bi)
2229
- output_batch.num_bone_ref = output_batch.bone_refs.length
2230
- end
2231
- [new_bi, bw]
2232
- }
2233
- ov.bone_infos.set_indexes_and_weights( output_bone_indexes_and_weights )
2234
- end
2235
- }
2236
- }
2237
- self
2238
- end
2239
-
2240
- def renumber_batches
2241
- @meshes.each_with_index { |m, i|
2242
- m.header.id = i
2243
- m.batches.each { |b|
2244
- b.header.mesh_id = i
2245
- }
2246
- }
2247
- self
2248
- end
2249
-
2250
- def remove_batch_vertex_offsets
2251
- @meshes.each { |m|
2252
- m.batches.each { |b|
2253
- b.indices = b.vertex_indices
2254
- b.recompute_from_absolute_indices
2255
- }
2256
- }
2257
- self
2258
- end
2259
-
2260
- def fix_ex_data
2261
- @vertexes.each_with_index { |v, i|
2262
- if @vertexes_ex_data[i].respond_to?(:color)
2263
- @vertexes_ex_data[i].color.data = 0xffc0c0c0
2264
- end
2265
- if @vertexes_ex_data[i].respond_to?(:mapping2)
2266
- @vertexes_ex_data[i].mapping2.u = v.mapping.u
2267
- @vertexes_ex_data[i].mapping2.v = v.mapping.v
2268
- end
2269
- }
2270
- end
2271
-
2272
- def copy_uv12(mesh_list)
2273
- raise "No UV2 in model!" unless @vertexes_ex_data[0].respond_to?(:mapping2)
2274
- mesh_list.each { |i|
2275
- @meshes[i].batches.each { |b|
2276
- b.vertex_indices.each { |vi|
2277
- @vertexes_ex_data[vi].mapping2.u = @vertexes[vi].mapping.u
2278
- @vertexes_ex_data[vi].mapping2.v = @vertexes[vi].mapping.v
2279
- }
2280
- }
2281
- }
2282
- end
2283
-
2284
- def reverse_tangents_byte_order(mesh_list)
2285
- raise "Vertex don't have tangents information!" unless @vertexes[0].respond_to?(:tangents)
2286
- vertex_indices = []
2287
- mesh_list.each { |i|
2288
- @meshes[i].batches.each { |b|
2289
- vertex_indices += b.vertex_indices
2290
- }
2291
- }
2292
- vertex_indices.uniq!
2293
- vertex_indices.each { |i|
2294
- @vertexes[i].tangents.data = [@vertexes[i].tangents.data].pack("L<").unpack("L>").first
2295
- }
2296
- end
2297
-
2298
- def recompute_batch_tangents(batch)
2299
- vertex_indices = batch.vertex_indices.uniq
2300
- tangents = vertex_indices.collect { |i| [i, Linalg::Vector::new(0, 0, 0, 0)] }.to_h
2301
- indices = batch.triangles.flatten
2302
- inconsistentuvs = 0
2303
- # https://stackoverflow.com/a/66918075
2304
- indices.each_with_index { |i, l|
2305
- j = indices[(l + 1) % 3 + (l / 3) * 3]
2306
- k = indices[(l + 2) % 3 + (l / 3) * 3]
2307
- n = Linalg::Vector::new(*@vertexes[i].normal.to_a, 0.0)
2308
- pi = Linalg::Vector::new(*@vertexes[i].position.to_a, 0.0)
2309
- mi = Linalg::Vector::new(*@vertexes[i].mapping.to_a, 0.0, 0.0)
2310
- pj = Linalg::Vector::new(*@vertexes[j].position.to_a, 0.0)
2311
- mj = Linalg::Vector::new(*@vertexes[j].mapping.to_a, 0.0, 0.0)
2312
- pk = Linalg::Vector::new(*@vertexes[k].position.to_a, 0.0)
2313
- mk = Linalg::Vector::new(*@vertexes[k].mapping.to_a, 0.0, 0.0)
2314
- v1 = pj - pi
2315
- v2 = pk - pi
2316
- t1 = mj - mi
2317
- t2 = mk - mi
2318
- uv2xArea = t1.x * t2.y - t1.y * t2.x
2319
- next if (uv2xArea.abs < 9.5367431640625e-07)
2320
- flip = uv2xArea > 0.0 ? 1.0 : -1.0
2321
- inconsistentuvs += 1 if (tangents[i].w != 0 && tangents[i].w != flip)
2322
- tangents[i].w = flip
2323
- c = v1.x * n.x + v1.y * n.y + v1.z * n.z
2324
- v1 -= n * (v1.dot(n));
2325
- v2 -= n * (v2.dot(n));
2326
- s = ((v1 * t2.y - v2 * t1.y) * flip).normalize
2327
- ac = v1.dot(v2) / (v1.length * v2.length)
2328
- ac = (ac > 1.0 ? 1.0 : (ac < -1.0 ? -1.0 : ac))
2329
- angle = Math.acos(ac);
2330
- tangents[i] += s*angle
2331
- }
2332
- warn "Found #{inconsistentuvs} inconsistent UVs" if inconsistentuvs > 0
2333
- tangents.each { |i, t|
2334
- t.normalize!
2335
- @vertexes[i].tangents.set(t.x, t.y, t.z, -t.w)
2336
- }
2337
- end
2338
-
2339
- def recompute_tangents(mesh_list)
2340
- raise "Vertex don't have tangents information!" unless @vertexes[0].respond_to?(:tangents)
2341
- mesh_list.each { |i|
2342
- @meshes[i].batches.each { |b|
2343
- recompute_batch_tangents(b)
2344
- }
2345
- }
2346
- end
2347
-
2348
- def recompute_layout
2349
- get_vertex_types
2350
-
2351
- if is_bayo2?
2352
- last_offset = 0xc0
2353
- else
2354
- last_offset = 0x80
2355
- end
2356
-
2357
- @header.num_vertexes = @vertexes.size if @vertexes
2358
-
2359
- if @header.offset_positions > 0x0
2360
- last_offset = @header.offset_positions = align(last_offset, 0x20)
2361
- last_offset += @header.num_vertexes * 12
2362
- end
2363
-
2364
- if @header.offset_vertexes > 0x0
2365
- last_offset = @header.offset_vertexes = align(last_offset, 0x20)
2366
- last_offset += @header.num_vertexes * @vertex_size
2367
- end
2368
- if @header.offset_vertexes_ex_data > 0x0
2369
- last_offset += 0x20 if is_bayo2?
2370
- last_offset = @header.offset_vertexes_ex_data = align(last_offset, 0x20)
2371
- last_offset += @header.num_vertexes * @vertex_ex_size
2372
- end
2373
- if @header.offset_bone_relative_position > 0x0
2374
- last_offset = @header.offset_bone_hierarchy = align(last_offset, 0x20)
2375
- last_offset += @header.num_bones * 2
2376
- end
2377
- if @header.offset_bone_relative_position > 0x0
2378
- last_offset = @header.offset_bone_relative_position = align(last_offset, 0x20)
2379
- last_offset += @header.num_bones * 12
2380
- end
2381
- if @header.offset_bone_position > 0x0
2382
- last_offset = @header.offset_bone_position = align(last_offset, 0x20)
2383
- last_offset += @header.num_bones * 12
2384
- end
2385
- if @header.offset_bone_index_translate_table > 0x0
2386
- last_offset = @header.offset_bone_index_translate_table = align(last_offset, 0x20)
2387
- last_offset += @bone_index_translate_table.__size
2388
- end
2389
- if @header.offset_u_j > 0x0
2390
- last_offset = @header.offset_u_j = align(last_offset, 0x20)
2391
- last_offset += @u_j.__size
2392
- end
2393
- if @header.offset_bone_symmetries > 0x0
2394
- last_offset = @header.offset_bone_symmetries = align(last_offset, 0x20)
2395
- last_offset += @header.num_bones * 2
2396
- end
2397
- if @header.offset_bone_flags > 0x0
2398
- last_offset = @header.offset_bone_flags = align(last_offset, 0x20)
2399
- last_offset += @header.num_bones
2400
- end
2401
- if @header.offset_shader_names > 0x0
2402
- last_offset = @header.offset_shader_names = align(last_offset, 0x20)
2403
- last_offset += @header.num_materials * 16
2404
- end
2405
- if @header.offset_tex_infos > 0x0
2406
- last_offset = @header.offset_tex_infos = align(last_offset, 0x20)
2407
- last_offset += 4 + @tex_infos.num_tex_infos * 8
2408
- end
2409
-
2410
- last_offset = @header.offset_materials_offsets = align(last_offset, 0x20)
2411
- off = 0
2412
- @materials_offsets = []
2413
- @header.num_materials.times { |i|
2414
- @materials_offsets.push off
2415
- off += @materials[i].__size
2416
- off = align(off, 0x4)
2417
- }
2418
-
2419
- last_offset += 4*@header.num_materials
2420
- last_offset = @header.offset_materials = align(last_offset, 0x20)
2421
-
2422
- last_offset += @materials.collect(&:__size).reduce(&:+)
2423
- # Pad last material with zeros
2424
- pad_count = (align(last_offset, 0x20) - last_offset)/4
2425
- @materials.last.material_data += [0]*pad_count
2426
- last_offset = @header.offset_meshes_offsets = align(last_offset, 0x20)
2427
-
2428
- off = 0
2429
- @meshes_offsets = []
2430
- @header.num_meshes.times { |i|
2431
- @meshes[i].recompute_layout
2432
- @meshes_offsets.push off
2433
- off += @meshes[i].__size
2434
- off = align(off, 0x20)
2435
- }
2436
-
2437
- last_offset += 4*@header.num_meshes
2438
- last_offset = @header.offset_meshes = align(last_offset, 0x20)
2439
- end
2440
-
2441
- end
2442
-
2443
- end
1
+ require 'set'
2
+ require 'digest'
3
+ require 'yaml'
4
+ $material_db = YAML::load_file(File.join( File.dirname(__FILE__), 'material_database.yaml'))
5
+
6
+ module Bayonetta
7
+
8
+ class UByteList < LibBin::Structure
9
+ uint32 :data
10
+
11
+ def self.is_bayo2?(parent)
12
+ if parent.__parent.respond_to?(:is_bayo2?)
13
+ return parent.__parent.is_bayo2?
14
+ elsif parent.__parent.__parent.respond_to?(:is_bayo2?)
15
+ return parent.__parent.__parent.is_bayo2?
16
+ end
17
+ raise "Cannot determine if Bayo2 or not!"
18
+ end
19
+
20
+ def self.inherited(subclass)
21
+ subclass.instance_variable_set(:@fields, @fields.dup)
22
+ end
23
+
24
+ def __convert(input, output, input_big, output_big, parent = nil, index = nil)
25
+ __set_convert_state(input, output,
26
+ input_big && self.class.is_bayo2?(parent) ? false : input_big,
27
+ output_big && self.class.is_bayo2?(parent) ? false : output_big,
28
+ parent, index)
29
+ __convert_fields
30
+ __unset_convert_state
31
+ self
32
+ end
33
+
34
+ def __load(input, input_big, parent = nil, index = nil)
35
+ __set_load_state(input, input_big && self.class.is_bayo2?(parent) ? false : input_big, parent, index)
36
+ __load_fields
37
+ __unset_load_state
38
+ self
39
+ end
40
+
41
+ def __dump(output, output_big, parent = nil, index = nil)
42
+ __set_dump_state(output, output_big && self.class.is_bayo2?(parent) ? false : output_big, parent, index)
43
+ __dump_fields
44
+ __unset_dump_state
45
+ self
46
+ end
47
+
48
+ def initialize
49
+ @data = 0
50
+ end
51
+
52
+ end
53
+
54
+ class Color < UByteList
55
+
56
+ def r
57
+ @data & 0xff
58
+ end
59
+
60
+ def g
61
+ (@data >> 8) & 0xff
62
+ end
63
+
64
+ def b
65
+ (@data >> 16) & 0xff
66
+ end
67
+
68
+ def a
69
+ (@data >> 24) & 0xff
70
+ end
71
+
72
+ def r=(v)
73
+ @data = (@data & 0xffffff00) | (v & 0xff)
74
+ v & 0xff
75
+ end
76
+
77
+ def g=(v)
78
+ @data = (@data & 0xffff00ff) | ((v & 0xff)<<8)
79
+ v & 0xff
80
+ end
81
+
82
+ def b=(v)
83
+ @data = (@data & 0xff00ffff) | ((v & 0xff)<<16)
84
+ v & 0xff
85
+ end
86
+
87
+ def a=(v)
88
+ @data = (@data & 0x00ffffff) | ((v & 0xff)<<24)
89
+ v & 0xff
90
+ end
91
+
92
+ def to_a
93
+ [r, g, b, a]
94
+ end
95
+
96
+ end
97
+
98
+ module VectorAccessor
99
+ def [](i)
100
+ case i
101
+ when 0
102
+ self.x
103
+ when 1
104
+ self.y
105
+ when 2
106
+ self.z
107
+ else
108
+ "Invalid index #{i} for a vector access!"
109
+ end
110
+ end
111
+
112
+ def []=(i,v)
113
+ case i
114
+ when 0
115
+ self.x = v
116
+ when 1
117
+ self.y = v
118
+ when 2
119
+ self.z = v
120
+ else
121
+ "Invalid index #{i} for a vector access!"
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ class Tangents < UByteList
128
+ include VectorAccessor
129
+
130
+ def clamp(v, max, min)
131
+ if v > max
132
+ v = max
133
+ elsif v < min
134
+ v = min
135
+ end
136
+ v
137
+ end
138
+
139
+ def x
140
+ ((@data & 0xff) - 127.0)/127.0
141
+ end
142
+
143
+ def y
144
+ (((@data >> 8) & 0xff) - 127.0)/127.0
145
+ end
146
+
147
+ def z
148
+ (((@data >> 16) & 0xff) -127.0)/127.0
149
+ end
150
+
151
+ def s
152
+ (((@data >> 24) & 0xff) -127.0)/127.0
153
+ end
154
+
155
+ def x=(v)
156
+ v2 = clamp((v*127.0+127.0).round, 255, 0)
157
+ @data = (@data & 0xffffff00) | v2
158
+ v
159
+ end
160
+
161
+ def y=(v)
162
+ v2 = clamp((v*127.0+127.0).round, 255, 0)
163
+ @data = (@data & 0xffff00ff) | (v2 << 8)
164
+ v
165
+ end
166
+
167
+ def z=(v)
168
+ v2 = clamp((v*127.0+127.0).round, 255, 0)
169
+ @data = (@data & 0xff00ffff) | (v2 << 16)
170
+ v
171
+ end
172
+
173
+ def s=(v)
174
+ v2 = clamp((v*127.0+127.0).round, 255, 0)
175
+ @data = (@data & 0x00ffffff) | (v2 << 24)
176
+ v
177
+ end
178
+
179
+ def normalize(fx, fy, fz)
180
+ nrm = Math::sqrt(fx*fx+fy*fy+fz*fz)
181
+ return [0.0, 0.0, 0.0] if nrm == 0.0
182
+ [fx/nrm, fy/nrm, fz/nrm]
183
+ end
184
+
185
+ def set(x, y, z, s = nil)
186
+ x, y, z = normalize(x, y, z)
187
+ self.x = x
188
+ self.y = y
189
+ self.z = z
190
+ self.s = s if s
191
+ self
192
+ end
193
+
194
+ def to_a
195
+ [x, y, z, s]
196
+ end
197
+
198
+ end
199
+
200
+ class Mapping < LibBin::Structure
201
+ half :u
202
+ half :v
203
+
204
+ def to_a
205
+ [u, v]
206
+ end
207
+ end
208
+
209
+ class FloatMapping < LibBin::Structure
210
+ float :u
211
+ float :v
212
+
213
+ def to_a
214
+ [u, v]
215
+ end
216
+ end
217
+
218
+ class FloatNormal < LibBin::Structure
219
+ include VectorAccessor
220
+ float :x
221
+ float :y
222
+ float :z
223
+
224
+ def to_a
225
+ [x, y, z]
226
+ end
227
+ end
228
+
229
+ class HalfNormal < LibBin::Structure
230
+ include VectorAccessor
231
+ half :x
232
+ half :y
233
+ half :z
234
+ half :dummy
235
+
236
+ def to_a
237
+ [x, y, z]
238
+ end
239
+ end
240
+
241
+ class Normal < LibBin::Structure
242
+ include VectorAccessor
243
+ attr_accessor :normal
244
+ attr_accessor :normal_big_orig
245
+ attr_accessor :normal_small_orig
246
+ attr_accessor :wide
247
+
248
+ def is_bayo2?
249
+ if __parent.__parent.respond_to?(:is_bayo2?)
250
+ return __parent.__parent.is_bayo2?
251
+ elsif __parent.__parent.__parent.respond_to?(:is_bayo2?)
252
+ return __parent.__parent.__parent.is_bayo2?
253
+ end
254
+ raise "Cannot determine if Bayo2 or not!"
255
+ end
256
+
257
+ def initialize
258
+ @normal = [0.0, 0.0, 0.0]
259
+ @normal_big_orig = nil
260
+ @normal_small_orig = nil
261
+ @wide = false
262
+ end
263
+
264
+ def x
265
+ @normal[0]
266
+ end
267
+
268
+ def y
269
+ @normal[1]
270
+ end
271
+
272
+ def z
273
+ @normal[2]
274
+ end
275
+
276
+ def x=(v)
277
+ @normal_big_orig = nil
278
+ @normal_small_orig = nil
279
+ @normal[0] = v
280
+ end
281
+
282
+ def y=(v)
283
+ @normal_big_orig = nil
284
+ @normal_small_orig = nil
285
+ @normal[1] = v
286
+ end
287
+
288
+ def z=(v)
289
+ @normal_big_orig = nil
290
+ @normal_small_orig = nil
291
+ @normal[2] = v
292
+ end
293
+
294
+ def __size(position, parent, index)
295
+ 4
296
+ end
297
+
298
+ def normalize(fx, fy, fz)
299
+ nrm = Math::sqrt(fx*fx+fy*fy+fz*fz)
300
+ return [0.0, 0.0, 0.0] if nrm == 0.0
301
+ [fx/nrm, fy/nrm, fz/nrm]
302
+ end
303
+
304
+ def unpack_wide(v)
305
+ nx = v & ((1<<10)-1)
306
+ ny = (v >> 10) & ((1<<10)-1)
307
+ nz = (v >> 20) & ((1<<10)-1)
308
+ sx = nx & (1<<9)
309
+ sy = ny & (1<<9)
310
+ sz = nz & (1<<9)
311
+ if sx
312
+ nx ^= sx
313
+ nx = -(sx-nx)
314
+ end
315
+ if sy
316
+ ny ^= sy
317
+ ny = -(sy-ny)
318
+ end
319
+ if sz
320
+ nz ^= sz
321
+ nz = -(sz-nz)
322
+ end
323
+
324
+ mag = ((1<<9)-1).to_f
325
+ fx = nx.to_f/mag
326
+ fy = ny.to_f/mag
327
+ fz = nz.to_f/mag
328
+
329
+ normalize(fx, fy, fz)
330
+ end
331
+
332
+ def decode_big_normal(vs)
333
+ unpack_wide(vs.unpack("L>").first)
334
+ end
335
+
336
+ def decode_small_normal_wide(vs)
337
+ unpack_wide(vs.unpack("L<").first)
338
+ end
339
+
340
+ def redecode_wide
341
+ @normals = decode_small_normal_wide(normal_small_orig)
342
+ end
343
+
344
+ def decode_small_normal(v)
345
+ n = v.unpack("c4")
346
+ nx = n[3]
347
+ ny = n[2]
348
+ nz = n[1]
349
+ mag = 127.0
350
+ fx = nx.to_f/mag
351
+ fy = ny.to_f/mag
352
+ fz = nz.to_f/mag
353
+
354
+ normalize(fx, fy, fz)
355
+ end
356
+
357
+ def clamp(v, max, min)
358
+ if v > max
359
+ v = max
360
+ elsif v < min
361
+ v = min
362
+ end
363
+ v
364
+ end
365
+
366
+ def pack_wide(normal)
367
+ fx = normal[0]
368
+ fy = normal[1]
369
+ fz = normal[2]
370
+ mag = (1<<9)-1
371
+ nx = (fx*(mag).to_f).to_i
372
+ ny = (fy*(mag).to_f).to_i
373
+ nz = (fz*(mag).to_f).to_i
374
+ nx = clamp(nx, mag, -1-mag)
375
+ ny = clamp(ny, mag, -1-mag)
376
+ nz = clamp(nz, mag, -1-mag)
377
+ mask = (1<<10)-1
378
+ v = 0
379
+ v |= nz & mask
380
+ v <<= 10
381
+ v |= ny & mask
382
+ v <<= 10
383
+ v |= nx & mask
384
+ v
385
+ end
386
+
387
+ def encode_small_normal_wide(normal)
388
+ [pack_wide(normal)].pack("L<")
389
+ end
390
+
391
+ def encode_small_normal(normal)
392
+ if @wide
393
+ encode_small_normal_wide(normal)
394
+ else
395
+ fx = normal[0]
396
+ fy = normal[1]
397
+ fz = normal[2]
398
+ nx = (fx*127.0).to_i
399
+ ny = (fy*127.0).to_i
400
+ nz = (fz*127.0).to_i
401
+ nx = clamp(nx, 127, -128)
402
+ ny = clamp(ny, 127, -128)
403
+ nz = clamp(nz, 127, -128)
404
+ [0, nz, ny, nx].pack("c4")
405
+ end
406
+ end
407
+
408
+ def encode_big_normal(normal)
409
+ [pack_wide(normal)].pack("L>")
410
+ end
411
+
412
+ def load_normal
413
+ s = __input.read(4)
414
+ if __input_big
415
+ @normal_big_orig = s
416
+ @normal_small_orig = nil
417
+ @normal = decode_big_normal(s)
418
+ else
419
+ @normal_small_orig = s
420
+ @normal_big_orig = nil
421
+ if is_bayo2?
422
+ @normal = decode_small_normal_wide(s)
423
+ else
424
+ @normal = decode_small_normal(s)
425
+ end
426
+ end
427
+ end
428
+
429
+ def dump_normal
430
+ s2 =
431
+ if __output_big
432
+ if @normal_big_orig
433
+ @normal_big_orig
434
+ elsif is_bayo2? && @normal_small_orig
435
+ @normal_small_orig.reverse
436
+ else
437
+ encode_big_normal(@normal)
438
+ end
439
+ else
440
+ if @normal_small_orig
441
+ @normal_small_orig
442
+ elsif is_bayo2?
443
+ @normal_big_orig ? @normal_big_orig.reverse : encode_small_normal_wide(@normal)
444
+ else
445
+ encode_small_normal(@normal)
446
+ end
447
+ end
448
+ __output.write(s2)
449
+ end
450
+
451
+ def convert_normal
452
+ load_normal
453
+ dump_normal
454
+ end
455
+
456
+ def __convert_fields
457
+ convert_normal
458
+ end
459
+
460
+ def __load_fields
461
+ load_normal
462
+ end
463
+
464
+ def __dump_fields
465
+ dump_normal
466
+ end
467
+
468
+
469
+ def to_a
470
+ [x, y, z]
471
+ end
472
+ end
473
+
474
+ class Position < LibBin::Structure
475
+ include VectorAccessor
476
+ float :x
477
+ float :y
478
+ float :z
479
+
480
+ def -(other)
481
+ b = Position::new
482
+ b.x = @x - other.x
483
+ b.y = @y - other.y
484
+ b.z = @z - other.z
485
+ b
486
+ end
487
+
488
+ def -@
489
+ b = Position::new
490
+ b.x = -@x
491
+ b.y = -@y
492
+ b.z = -@z
493
+ b
494
+ end
495
+
496
+ def +(other)
497
+ b = Position::new
498
+ b.x = @x + other.x
499
+ b.y = @y + other.y
500
+ b.z = @z + other.z
501
+ b
502
+ end
503
+
504
+ def *(scal)
505
+ b = Position::new
506
+ b.x = @x*scal
507
+ b.y = @y*scal
508
+ b.z = @z*scal
509
+ b
510
+ end
511
+
512
+ def to_yaml_properties
513
+ [:@x, :@y, :@z]
514
+ end
515
+
516
+ def to_s
517
+ "<#{@x}, #{@y}, #{@z}>"
518
+ end
519
+
520
+ def to_a
521
+ [x, y, z]
522
+ end
523
+ end
524
+
525
+ class BoneInfos < LibBin::Structure
526
+ register_field :indexes, UByteList
527
+ register_field :weights, UByteList
528
+
529
+ def initialize
530
+ @indexes = UByteList::new
531
+ @weights = UByteList::new
532
+ end
533
+
534
+ def get_indexes_and_weights
535
+ res = []
536
+ 4.times { |i|
537
+ bi = (@indexes.data >> (i*8)) & 0xff
538
+ bw = (@weights.data >> (i*8)) & 0xff
539
+ res.push [bi, bw] if bw > 0
540
+ }
541
+ res
542
+ end
543
+
544
+ def get_indexes
545
+ get_indexes_and_weights.collect { |bi, _| bi }
546
+ end
547
+
548
+ def get_weights
549
+ get_indexes_and_weights.collect { |_, bw| bw }
550
+ end
551
+
552
+ def remap_indexes(map)
553
+ new_bone_info = get_indexes_and_weights.collect { |bi, bw| [map[bi], bw] }
554
+ set_indexes_and_weights(new_bone_info)
555
+ self
556
+ end
557
+
558
+ def set_indexes_and_weights(bone_info)
559
+ raise "Too many bone information #{bone_info.inspect}!" if bone_info.length > 4
560
+ @indexes.data = 0
561
+ @weights.data = 0
562
+ bone_info.each_with_index { |(bi, bw), i|
563
+ raise "Invalid bone index #{bi}!" if bi > 255 || bi < 0
564
+ @indexes.data |= ( bi << (i*8) )
565
+ bw = 0 if bw < 0
566
+ bw = 255 if bw > 255
567
+ @weights.data |= (bw << (i*8) )
568
+ }
569
+ self
570
+ end
571
+
572
+ def to_a
573
+ get_indexes_and_weights
574
+ end
575
+ end
576
+
577
+ class BoneIndexTranslateTable < LibBin::Structure
578
+ int16 :offsets, length: 16
579
+ #attr_accessor :second_levels
580
+ #attr_accessor :third_levels
581
+ attr_reader :table
582
+
583
+ def table=(t)
584
+ @table = t
585
+ encode
586
+ t
587
+ end
588
+
589
+ def __size(position = 0, parent = nil, index = nil)
590
+ sz = super()
591
+ if @second_levels
592
+ @second_levels.each { |e|
593
+ sz += e.__size(position, parent, index)
594
+ }
595
+ end
596
+ if @third_levels
597
+ @third_levels.each { |e|
598
+ sz += e.__size(position, parent, index)
599
+ }
600
+ end
601
+ sz
602
+ end
603
+
604
+ def __convert(input, output, input_big, output_big, parent, index, level = 1)
605
+ __set_convert_state(input, output, input_big, output_big, parent, index)
606
+ __convert_fields
607
+ if level == 1
608
+ @second_levels = []
609
+ @offsets.each { |o|
610
+ if o != -1
611
+ t = self.class::new
612
+ t.__convert(input, output, input_big, output_big, self, nil, level+1)
613
+ @second_levels.push t
614
+ end
615
+ }
616
+ @third_levels = []
617
+ @second_levels.each { |l|
618
+ l.offsets.each { |o|
619
+ if o != -1
620
+ t = self.class::new
621
+ t.__convert(input, output, input_big, output_big, self, nil, level+2)
622
+ @third_levels.push t
623
+ end
624
+ }
625
+ }
626
+ decode
627
+ else
628
+ @second_levels = nil
629
+ @third_levels = nil
630
+ end
631
+ __unset_convert_state
632
+ self
633
+ end
634
+
635
+ def __load(input, input_big, parent, index, level = 1)
636
+ __set_load_state(input, input_big, parent, index)
637
+ __load_fields
638
+ if level == 1
639
+ @second_levels = []
640
+ @offsets.each { |o|
641
+ if o != -1
642
+ t = self.class::new
643
+ t.__load(input, input_big, self, nil, level+1)
644
+ @second_levels.push t
645
+ end
646
+ }
647
+ @third_levels = []
648
+ @second_levels.each { |l|
649
+ l.offsets.each { |o|
650
+ if o != -1
651
+ t = self.class::new
652
+ t.__load(input, input_big, self, nil, level+2)
653
+ @third_levels.push t
654
+ end
655
+ }
656
+ }
657
+ decode
658
+ else
659
+ @second_levels = nil
660
+ @third_levels = nil
661
+ end
662
+ __unset_load_state
663
+ self
664
+ end
665
+
666
+ def decode
667
+ t = (@offsets+@second_levels.collect(&:offsets)+@third_levels.collect(&:offsets)).flatten
668
+ @table = (0x0..0xfff).each.collect { |i|
669
+ index = t[(i & 0xf00)>>8]
670
+ next if index == -1
671
+ index = t[index + ((i & 0xf0)>>4)]
672
+ next if index == -1
673
+ index = t[index + (i & 0xf)]
674
+ next if index == 0xfff
675
+ [i, index]
676
+ }.compact.to_h
677
+ end
678
+ private :decode
679
+
680
+ def encode
681
+ keys = @table.keys.sort
682
+ first_table = 16.times.collect { |i|
683
+ lower = i*0x100
684
+ upper = (i+1)*0x100
685
+ keys.select { |k| k >= lower && k < upper }
686
+ }
687
+ off = 0x0
688
+ @offsets = first_table.collect { |e| e == [] ? -1 : (off += 0x10) }
689
+
690
+ second_table = first_table.select { |e| e != [] }.collect { |e|
691
+ 16.times.collect { |i|
692
+ lower = i*0x10
693
+ upper = (i+1)*0x10
694
+ e.select { |k| (k&0xff) >= lower && (k&0xff) < upper }
695
+ }
696
+ }
697
+ @second_levels = second_table.collect { |st|
698
+ tab = BoneIndexTranslateTable::new
699
+ tab.offsets = st.collect { |e| e == [] ? -1 : (off += 0x10) }
700
+ tab
701
+ }
702
+ @third_levels = []
703
+ second_table.each { |e|
704
+ e.select { |ee| ee != [] }.each { |ee|
705
+ tab = BoneIndexTranslateTable::new
706
+ tab.offsets = [0xfff]*16
707
+ ee.each { |k|
708
+ tab.offsets[k&0xf] = @table[k]
709
+ }
710
+ @third_levels.push tab
711
+ }
712
+ }
713
+ self
714
+ end
715
+ private :encode
716
+
717
+ def __dump(output, output_big, parent, index, level = 1)
718
+ __set_dump_state(output, output_big, parent, index)
719
+ encode if level == 1
720
+ __dump_fields
721
+ if @second_levels
722
+ @second_levels.each { |e|
723
+ e.__dump(output, output_big, self, nil, level+1)
724
+ }
725
+ end
726
+ if @third_levels
727
+ @third_levels.each { |e|
728
+ e.__dump(output, output_big, self, nil, level+2)
729
+ }
730
+ end
731
+ __unset_dump_state
732
+ end
733
+
734
+ end
735
+
736
+ VERTEX_FIELDS = {
737
+ position_t: [ Position, 12 ],
738
+ mapping_t: [ Mapping, 4 ],
739
+ normal_t: [ Normal, 4 ],
740
+ tangents_t: [ Tangents, 4 ],
741
+ bone_infos_t: [ BoneInfos, 8],
742
+ color_t: [ Color, 4],
743
+ fnormal_t: [ FloatNormal, 12 ],
744
+ hnormal_t: [ HalfNormal, 8 ],
745
+ fmapping_t: [ FloatMapping, 8]
746
+ }
747
+
748
+ class WMBFile < LibBin::Structure
749
+ include Alignment
750
+
751
+ VERTEX_TYPES = {}
752
+ VERTEX_TYPES.update( YAML::load_file(File.join( File.dirname(__FILE__), 'vertex_types.yaml')) )
753
+ VERTEX_TYPES.update( YAML::load_file(File.join( File.dirname(__FILE__), 'vertex_types2.yaml')) )
754
+
755
+ class UnknownStruct < LibBin::Structure
756
+ uint8 :u_a1, length: 4
757
+ uint32 :u_b1
758
+ int16 :u_c1, length: 4
759
+ uint8 :u_a2, length: 4
760
+ uint32 :u_b2
761
+ int16 :u_c2, length: 4
762
+ uint8 :u_a3, length: 4
763
+ uint32 :u_b3
764
+ end
765
+
766
+ class Material < LibBin::Structure
767
+ int16 :type
768
+ uint16 :flag
769
+ uint32 :material_data,
770
+ length: '(..\materials_offsets[__index+1] ? ..\materials_offsets[__index+1] - ..\materials_offsets[__index] - 4 : ..\header\offset_meshes_offsets - __position - 4)/4'
771
+
772
+ def __size(position = 0, parent = nil, index = nil)
773
+ return 2 + 2 + @material_data.length * 4
774
+ end
775
+ end
776
+
777
+ class Bayo1Material
778
+ attr_reader :type
779
+ attr_reader :flag
780
+ attr_reader :samplers
781
+ attr_reader :parameters
782
+
783
+ def initialize(m)
784
+ @type = m.type
785
+ @flag = m.flag
786
+ @layout = $material_db[@type][:layout]
787
+ @samplers = {}
788
+ @parameters = {}
789
+ field_count = 0
790
+ @layout.each { |name, t|
791
+ if t == "sampler2D_t" || t == "samplerCUBE_t"
792
+ @samplers[name] = m.material_data[field_count]
793
+ field_count += 1
794
+ else
795
+ @parameters[name] = m.material_data[field_count...(field_count+4)].pack("L4").unpack("F4")
796
+ field_count += 4
797
+ end
798
+ }
799
+ end
800
+
801
+ end
802
+
803
+ class BatchHeader < LibBin::Structure
804
+ int16 :batch_id #Bayo 2
805
+ int16 :mesh_id
806
+ uint16 :flags
807
+ int16 :ex_mat_id
808
+ uint8 :material_id
809
+ uint8 :has_bone_refs
810
+ uint8 :u_e1
811
+ uint8 :u_e2
812
+ uint32 :vertex_start
813
+ uint32 :vertex_end
814
+ int32 :primitive_type
815
+ uint32 :offset_indices
816
+ int32 :num_indices
817
+ int32 :vertex_offset
818
+ int32 :u_f, length: 7
819
+
820
+ def initialize
821
+ @batch_id = 0
822
+ @mesh_id = 0
823
+ @flags = 0x8001
824
+ @ex_mat_id = 0
825
+ @material_id = 0
826
+ @has_bone_refs = 1
827
+ @u_e1 = 0
828
+ @u_e2 = 0
829
+ @vertex_start = 0
830
+ @vertex_end = 0
831
+ @primitive_type = 4
832
+ @offset_indices = 0x100
833
+ @num_indices = 0
834
+ @vertex_offset = 0
835
+ @u_f = [0]*7
836
+ end
837
+ end
838
+
839
+ class Batch < LibBin::Structure
840
+ register_field :header, BatchHeader
841
+ int32 :num_bone_ref, condition: 'header\has_bone_refs != 0'
842
+ uint8 :bone_refs, length: 'num_bone_ref', condition: 'header\has_bone_refs != 0'
843
+ float :unknown, length: 4, condition: 'header\has_bone_refs == 0'
844
+ uint16 :indices, length: 'header\num_indices', offset: '__position + header\offset_indices'
845
+
846
+ def initialize
847
+ @header = BatchHeader::new
848
+ @num_bone_ref = 0
849
+ @bone_refs = []
850
+ @indices = []
851
+ end
852
+
853
+ def duplicate(positions, vertexes, vertexes_ex)
854
+ b = Batch::new
855
+ if header.has_bone_refs != 0
856
+ b.header = @header.dup
857
+ b.num_bone_ref = @num_bone_ref
858
+ b.bone_refs = @bone_refs.dup
859
+ else
860
+ b.unknown = @unknown
861
+ end
862
+ l = vertexes.length
863
+ old_indices_map = vertex_indices.uniq.sort.each_with_index.collect { |vi, i| [vi, l + i] }.to_h
864
+ old_indices_map.each { |vi, nvi| positions[nvi] = positions[vi] } if positions
865
+ old_indices_map.each { |vi, nvi| vertexes[nvi] = vertexes[vi] }
866
+ old_indices_map.each { |vi, nvi| vertexes_ex[nvi] = vertexes_ex[vi] } if vertexes_ex
867
+ b.indices = vertex_indices.collect { |vi| old_indices_map[vi] }
868
+ b.recompute_from_absolute_indices
869
+ b
870
+ end
871
+
872
+ def recompute_from_absolute_indices
873
+ @header.num_indices = @indices.length
874
+ unless @header.num_indices == 0
875
+ sorted_indices = @indices.sort.uniq
876
+ @header.vertex_start = sorted_indices.first
877
+ @header.vertex_end = sorted_indices.last + 1
878
+ if sorted_indices.last > 0xffff
879
+ offset = @header.vertex_offset = @header.vertex_start
880
+ @indices.collect! { |i| i - offset }
881
+ else
882
+ @header.vertex_offset = 0
883
+ end
884
+ end
885
+ self
886
+ end
887
+
888
+ def triangles
889
+ inds = @indices.collect{ |i| i + @header.vertex_offset }
890
+ if @header.primitive_type == 4
891
+ inds.each_slice(3).to_a
892
+ else
893
+ inds.each_cons(3).each_with_index.collect do |(v0, v1, v2), i|
894
+ if i.even?
895
+ [v0, v1, v2]
896
+ else
897
+ [v1, v0, v2]
898
+ end
899
+ end.select { |t| t.uniq.length == 3 }
900
+ end
901
+ end
902
+
903
+ def set_triangles(trs)
904
+ @header.primitive_type = 4
905
+ @indices = trs.flatten
906
+ recompute_from_absolute_indices
907
+ @header.num_indices = @indices.length
908
+ self
909
+ end
910
+
911
+ def filter_vertexes(vertexes)
912
+ vertex_map = vertexes.collect { |i| [i, true] }.to_h
913
+ trs = triangles
914
+ new_trs = trs.select { |tr| vertex_map.include?(tr[0]) && vertex_map.include?(tr[1]) && vertex_map.include?(tr[2]) }
915
+ set_triangles(new_trs)
916
+ end
917
+
918
+ def vertex_indices
919
+ indices.collect { |i| i + @header.vertex_offset }
920
+ end
921
+
922
+ def cleanup_bone_refs(vertexes)
923
+ if header.has_bone_refs != 0
924
+ bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
925
+ used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
926
+ new_bone_refs_list = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.sort
927
+ new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
928
+ translation_map = used_bone_refs_indexes.collect { |ri|
929
+ [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
930
+ }.to_h
931
+ vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
932
+ @bone_refs = new_bone_refs_list
933
+ @num_bone_ref = @bone_refs.length
934
+ end
935
+ self
936
+ end
937
+
938
+ def add_ancestors_bone_refs(vertexes, bones)
939
+ if header.has_bone_refs != 0
940
+ bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
941
+ used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
942
+ new_bone_refs_list = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.sort
943
+ new_bone_refs_set = Set::new(new_bone_refs_list)
944
+ new_bone_refs_list.each { |bi|
945
+ new_bone_refs_set.merge(bones[bi].parents.collect(&:index))
946
+ }
947
+ new_bone_refs_list = new_bone_refs_set.to_a.sort
948
+ new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
949
+ translation_map = used_bone_refs_indexes.collect { |ri|
950
+ [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
951
+ }.to_h
952
+ vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
953
+ @bone_refs = new_bone_refs_list
954
+ @num_bone_ref = @bone_refs.length
955
+ end
956
+ end
957
+
958
+ def add_previous_bone_refs(vertexes, bones)
959
+ if header.has_bone_refs != 0
960
+ bone_refs_map = @bone_refs.each_with_index.collect { |b, i| [i, b] }.to_h
961
+ used_bone_refs_indexes = vertex_indices.collect { |vi| vertexes[vi].bone_infos.get_indexes }.flatten.uniq
962
+ last_bone = used_bone_refs_indexes.collect{ |i| bone_refs_map[i] }.uniq.max
963
+ new_bone_refs_list = (0..last_bone).to_a
964
+ new_bone_refs_reverse_map = new_bone_refs_list.each_with_index.collect { |b, i| [b, i] }.to_h
965
+ translation_map = used_bone_refs_indexes.collect { |ri|
966
+ [ri, new_bone_refs_reverse_map[bone_refs_map[ri]]]
967
+ }.to_h
968
+ vertex_indices.uniq.sort.each { |vi| vertexes[vi].bone_infos.remap_indexes(translation_map) }
969
+ @bone_refs = new_bone_refs_list
970
+ @num_bone_ref = @bone_refs.length
971
+ end
972
+ end
973
+
974
+ end
975
+
976
+ class MeshHeader < LibBin::Structure
977
+ int16 :id
978
+ int16 :num_batch
979
+ int16 :u_a1
980
+ int16 :u_a2
981
+ uint32 :offset_batch_offsets
982
+ uint32 :u_b
983
+ int32 :u_c, length: 4
984
+ string :name, 32
985
+ float :mat, length: 12
986
+
987
+ def initialize
988
+ @id = 0
989
+ @num_batch = 0
990
+ @u_a1 = 0
991
+ @u_a2 = 1
992
+ @offset_batch_offsets = 128
993
+ @u_b = 0x80000000
994
+ @u_c = [0]*4
995
+ @name = [0]*32
996
+ @mat = [0.0, 0.98, -0.42, 1.64, 1.10, 2.08,
997
+ 0.1, -1.10, -0.12, -0.95, 0.0, 0.0]
998
+ end
999
+ end
1000
+
1001
+ class Mesh < LibBin::Structure
1002
+ register_field :header, MeshHeader
1003
+ uint32 :batch_offsets, length: 'header\num_batch',
1004
+ offset: '__position + header\offset_batch_offsets'
1005
+ register_field :batches, Batch, count: 'header\num_batch', sequence: true,
1006
+ offset: '__position + header\offset_batch_offsets + batch_offsets[__iterator]'
1007
+
1008
+ def initialize
1009
+ @header = MeshHeader::new
1010
+ @batch_offsets = []
1011
+ @batches = []
1012
+ end
1013
+
1014
+ def __size(position = 0, parent = nil, index = nil)
1015
+ sz = @header.offset_batch_offsets
1016
+ sz += @header.num_batch * 4
1017
+ sz = align(sz, 0x20)
1018
+ @header.num_batch.times { |i|
1019
+ sz += @batches[i].__size
1020
+ sz = align(sz, 0x20)
1021
+ }
1022
+ sz
1023
+ end
1024
+
1025
+ def recompute_layout
1026
+ @header.num_batch = @batches.length
1027
+ off = @header.num_batch * 4
1028
+ @batch_offsets = []
1029
+ @header.num_batch.times { |j|
1030
+ off = align(off, 0x20)
1031
+ @batch_offsets.push off
1032
+ off += @batches[j].__size
1033
+ }
1034
+ end
1035
+
1036
+ def duplicate(positions, vertexes, vertexes_ex)
1037
+ m = Mesh::new
1038
+ m.header = @header
1039
+ m.batch_offsets = @batch_offsets
1040
+ m.batches = @batches.collect { |b| b.duplicate(positions, vertexes, vertexes_ex) }
1041
+ m
1042
+ end
1043
+
1044
+ end
1045
+
1046
+ class ShaderName < LibBin::Structure
1047
+ string :name, 16
1048
+ end
1049
+
1050
+ class TexInfo < LibBin::Structure
1051
+ uint32 :id
1052
+ int32 :info
1053
+ end
1054
+
1055
+ class TexInfos < LibBin::Structure
1056
+ int32 :num_tex_infos
1057
+ register_field :tex_infos, TexInfo, length: 'num_tex_infos'
1058
+ end
1059
+
1060
+ class WMBFileHeader < LibBin::Structure
1061
+ uint32 :id
1062
+ int32 :u_a
1063
+ int32 :u_b
1064
+ int32 :num_vertexes
1065
+ int8 :vertex_ex_data_size
1066
+ int8 :vertex_ex_data
1067
+ int16 :u_e
1068
+ int32 :offset_positions
1069
+ uint32 :offset_vertexes
1070
+ uint32 :offset_vertexes_ex_data
1071
+ int32 :u_g, length: 4
1072
+ int32 :num_bones
1073
+ uint32 :offset_bone_hierarchy
1074
+ uint32 :offset_bone_relative_position
1075
+ uint32 :offset_bone_position
1076
+ uint32 :offset_bone_index_translate_table
1077
+ int32 :num_materials
1078
+ uint32 :offset_materials_offsets
1079
+ uint32 :offset_materials
1080
+ int32 :num_meshes
1081
+ uint32 :offset_meshes_offsets
1082
+ uint32 :offset_meshes
1083
+ int32 :u_k
1084
+ int32 :u_l
1085
+ uint32 :offset_u_j
1086
+ uint32 :offset_bone_symmetries
1087
+ uint32 :offset_bone_flags
1088
+ uint32 :offset_shader_names
1089
+ uint32 :offset_tex_infos
1090
+ uint32 :u_m
1091
+ uint32 :u_n
1092
+ end
1093
+
1094
+ register_field :header, WMBFileHeader
1095
+ register_field :positions, Position, length: 'header\num_vertexes', offset: 'header\offset_positions'
1096
+ register_field :vertexes, 'get_vertex_types[0]', length: 'header\num_vertexes', offset: 'header\offset_vertexes'
1097
+ register_field :vertexes_ex_data, 'get_vertex_types[1]', length: 'header\num_vertexes',
1098
+ offset: 'header\offset_vertexes_ex_data'
1099
+ int16 :bone_hierarchy, length: 'header\num_bones', offset: 'header\offset_bone_hierarchy'
1100
+ register_field :bone_relative_positions, Position, length: 'header\num_bones',
1101
+ offset: 'header\offset_bone_relative_position'
1102
+ register_field :bone_positions, Position, length: 'header\num_bones', offset: 'header\offset_bone_position'
1103
+ register_field :bone_index_translate_table, BoneIndexTranslateTable,
1104
+ offset: 'header\offset_bone_index_translate_table'
1105
+ register_field :u_j, UnknownStruct, offset: 'header\offset_u_j'
1106
+ int16 :bone_symmetries, length: 'header\num_bones', offset: 'header\offset_bone_symmetries'
1107
+ int8 :bone_flags, length: 'header\num_bones', offset: 'header\offset_bone_flags'
1108
+ register_field :shader_names, ShaderName, length: 'header\num_materials', offset: 'header\offset_shader_names'
1109
+ register_field :tex_infos, TexInfos, offset: 'header\offset_tex_infos'
1110
+ uint32 :materials_offsets, length: 'header\num_materials', offset: 'header\offset_materials_offsets'
1111
+ register_field :materials, Material, count: 'header\num_materials', sequence: true,
1112
+ offset: 'header\offset_materials + materials_offsets[__iterator]'
1113
+ uint32 :meshes_offsets, length: 'header\num_meshes', offset: 'header\offset_meshes_offsets'
1114
+ register_field :meshes, Mesh, count: 'header\num_meshes', sequence: true,
1115
+ offset: 'header\offset_meshes + meshes_offsets[__iterator]'
1116
+
1117
+ def texture_ids
1118
+ return [] unless @tex_infos
1119
+ @tex_infos.tex_infos.collect { |i| i.id }
1120
+ end
1121
+
1122
+ def materials_textures
1123
+ return {} unless @tex_infos
1124
+ tex_ids = texture_ids
1125
+ @materials.each_with_index.collect { |m, i|
1126
+ [i, m.material_data.select { |t| tex_ids.include?(t) }]
1127
+ }.to_h
1128
+ end
1129
+
1130
+ def get_vertex_types
1131
+ if @vertex_type
1132
+ return [@vertex_type, @vertex_ex_type]
1133
+ else
1134
+ types = VERTEX_TYPES[ [ @header.u_b, @header.vertex_ex_data_size, @header.vertex_ex_data] ]
1135
+ @vertex_type = Class::new(LibBin::Structure)
1136
+ @vertex_size = 0
1137
+ if types[0]
1138
+ types[0].each { |name, type|
1139
+ @vertex_type.register_field(name, VERTEX_FIELDS[type][0])
1140
+ @vertex_size += VERTEX_FIELDS[type][1]
1141
+ }
1142
+ end
1143
+ @vertex_ex_type = Class::new(LibBin::Structure)
1144
+ @vertex_ex_size = 0
1145
+ if types[1]
1146
+ types[1].each { |name, type|
1147
+ @vertex_ex_type.register_field(name, VERTEX_FIELDS[type][0])
1148
+ @vertex_ex_size += VERTEX_FIELDS[type][1]
1149
+ }
1150
+ end
1151
+ return [@vertex_type, @vertex_ex_type]
1152
+ end
1153
+ end
1154
+
1155
+ def get_vertex_fields
1156
+ if @vertex_fields
1157
+ return @vertex_fields
1158
+ else
1159
+ types = VERTEX_TYPES[ [ @header.u_b, @header.vertex_ex_data_size, @header.vertex_ex_data] ]
1160
+ @vertex_fields = []
1161
+ if types[0]
1162
+ types[0].each { |name, type|
1163
+ @vertex_fields.push(name)
1164
+ }
1165
+ end
1166
+ if types[1]
1167
+ types[1].each { |name, type|
1168
+ @vertex_fields.push(name)
1169
+ }
1170
+ end
1171
+ return @vertex_fields
1172
+ end
1173
+ end
1174
+
1175
+ def get_vertex_field(field, vi)
1176
+ if @vertexes[vi].respond_to?(field)
1177
+ return @vertexes[vi].send(field)
1178
+ elsif @vertexes_ex_data && @vertexes_ex_data[vi].respond_to?(field)
1179
+ return @vertexes_ex_data[vi].send(field)
1180
+ elsif field == :position && @positions
1181
+ return @positions[vi]
1182
+ else
1183
+ return nil
1184
+ end
1185
+ end
1186
+
1187
+ def set_vertex_field(field, vi, val)
1188
+ if @vertexes[vi].respond_to?(field)
1189
+ return @vertexes[vi].send(:"#{field}=", val)
1190
+ elsif @vertexes_ex_data && @vertexes_ex_data[vi].respond_to?(field)
1191
+ return @vertexes_ex_data[vi].send(:"#{field}=", val)
1192
+ elsif field == :position && @positions
1193
+ return @positions[vi] = val
1194
+ else
1195
+ raise "Couldn't find field: #{field}!"
1196
+ end
1197
+ end
1198
+
1199
+ def self.convert(input_name, output_name, output_big = false)
1200
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1201
+ input = input_name
1202
+ else
1203
+ File.open(input_name, "rb") { |f|
1204
+ input = StringIO::new(f.read, "rb")
1205
+ }
1206
+ end
1207
+ input_big = validate_endianness(input)
1208
+
1209
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1210
+ output = output_name
1211
+ else
1212
+ output = File.open(output_name, "wb")
1213
+ end
1214
+ output.write("\xFB"*input.size)
1215
+ output.rewind
1216
+
1217
+ wmb = self::new
1218
+ wmb.instance_variable_set(:@__was_big, input_big)
1219
+ wmb.__convert(input, output, input_big, output_big)
1220
+
1221
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1222
+ output.close unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1223
+ wmb
1224
+ end
1225
+
1226
+ def self.load(input_name)
1227
+ if input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1228
+ input = input_name
1229
+ else
1230
+ input = File.open(input_name, "rb")
1231
+ end
1232
+ if is_wmb3?(input)
1233
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1234
+ return WMB3File::load(input_name)
1235
+ end
1236
+ input = StringIO::new(input.read, "rb")
1237
+ input_big = validate_endianness(input)
1238
+
1239
+ wmb = self::new
1240
+ wmb.instance_variable_set(:@__was_big, input_big)
1241
+ wmb.__load(input, input_big)
1242
+ input.close unless input_name.respond_to?(:read) && input_name.respond_to?(:seek)
1243
+
1244
+ wmb
1245
+ end
1246
+
1247
+ def was_big?
1248
+ @__was_big
1249
+ end
1250
+
1251
+ def is_bayo2?
1252
+ @header.offset_shader_names != 0 || @header.offset_tex_infos != 0
1253
+ end
1254
+
1255
+ def self.is_wmb3?(input)
1256
+ input.rewind
1257
+ id = input.read(4).unpack("a4").first
1258
+ input.rewind
1259
+ return id == "WMB3".b || id == "3BMW".b
1260
+ end
1261
+
1262
+ def self.validate_endianness(input)
1263
+ input.rewind
1264
+ id = input.read(4).unpack("a4").first
1265
+ case id
1266
+ when "WMB\0".b
1267
+ input_big = false
1268
+ when "\0BMW".b
1269
+ input_big = true
1270
+ else
1271
+ raise "Invalid file type #{id}!"
1272
+ end
1273
+ input.rewind
1274
+ input_big
1275
+ end
1276
+
1277
+ def dump(output_name, output_big = false)
1278
+ if output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1279
+ output = output_name
1280
+ else
1281
+ output = StringIO::new("", "wb")
1282
+ end
1283
+ output.rewind
1284
+
1285
+ if was_big? != output_big
1286
+ if output_big
1287
+ u_a = header.u_a
1288
+ header.u_a = -1
1289
+ else
1290
+ u_a = header.u_a
1291
+ header.u_a = 0
1292
+ end
1293
+ end
1294
+ __set_dump_state(output, output_big, nil, nil)
1295
+ __dump_fields
1296
+ __unset_dump_state
1297
+ if was_big? != output_big
1298
+ header.u_a = u_a
1299
+ end
1300
+
1301
+ sz = output.size
1302
+ sz = align(sz, 0x20)
1303
+ if sz > output.size
1304
+ output.seek(sz-1)
1305
+ output.write("\x00")
1306
+ end
1307
+
1308
+ unless output_name.respond_to?(:write) && output_name.respond_to?(:seek)
1309
+ File.open(output_name, "wb") { |f|
1310
+ f.write output.string
1311
+ }
1312
+ output.close
1313
+ end
1314
+ self
1315
+ end
1316
+
1317
+ def check_normals
1318
+ return self if @__was_big
1319
+ wide_normals = false
1320
+ @vertexes.each { |v|
1321
+ if v.normal.normal_small_orig.bytes.first != 0
1322
+ wide_normals = true
1323
+ end
1324
+ }
1325
+ if wide_normals
1326
+ @vertexes.each { |v|
1327
+ v.normal.wide = true
1328
+ v.normal.redecode_wide
1329
+ }
1330
+ end
1331
+ self
1332
+ end
1333
+
1334
+ def normals_to_wide
1335
+ @vertexes.each { |v|
1336
+ v.normal.wide = true
1337
+ v.normal.normal_big_orig = nil
1338
+ v.normal.normal_small_orig = nil
1339
+ }
1340
+ self
1341
+ end
1342
+
1343
+ def normals_to_narrow
1344
+ @vertexes.each { |v|
1345
+ v.normal.wide = false
1346
+ v.normal.normal_big_orig = nil
1347
+ v.normal.normal_small_orig = nil
1348
+ }
1349
+ self
1350
+ end
1351
+
1352
+ def get_bone_structure
1353
+ bones = @bone_positions.collect { |p|
1354
+ Bone::new(p)
1355
+ }
1356
+ bones.each_with_index { |b, i|
1357
+ if @bone_hierarchy[i] == -1
1358
+ b.parent = nil
1359
+ else
1360
+ b.parent = bones[@bone_hierarchy[i]]
1361
+ bones[@bone_hierarchy[i]].children.push(b)
1362
+ end
1363
+ b.index = i
1364
+ b.relative_position = @bone_relative_positions[i]
1365
+ b.symmetric = @bone_symmetries[i] if @header.offset_bone_symmetries > 0x0
1366
+ b.flag = @bone_flags[i] if @header.offset_bone_flags > 0x0
1367
+ }
1368
+ end
1369
+
1370
+ def recompute_relative_positions
1371
+ @bone_hierarchy.each_with_index { |b, i|
1372
+ if b != -1
1373
+ #puts "bone: #{i} parent: #{b} position: #{@bone_positions[i]} pposition: #{@bone_positions[b]}"
1374
+ @bone_relative_positions[i] = @bone_positions[i] - @bone_positions[b]
1375
+ else
1376
+ @bone_relative_positions[i] = @bone_positions[i]
1377
+ end
1378
+ }
1379
+ self
1380
+ end
1381
+
1382
+ def set_bone_structure(bones)
1383
+ @bone_hierarchy = []
1384
+ @bone_relative_positions = []
1385
+ @bone_positions = []
1386
+ @bone_symmetries = [] if @header.offset_bone_symmetries > 0x0
1387
+ @bone_flags = [] if @header.offset_bone_flags > 0x0
1388
+ bones.each { |b|
1389
+ p_index = -1
1390
+ p_index = b.parent.index if b.parent
1391
+ @bone_hierarchy.push p_index
1392
+ @bone_positions.push b.position
1393
+ rel_position = b.relative_position
1394
+ unless rel_position
1395
+ if b.parent
1396
+ rel_position = b.position - b.parent.position
1397
+ else
1398
+ rel_position = b.position
1399
+ end
1400
+ end
1401
+ @bone_relative_positions.push rel_position
1402
+ @bone_symmetries.push b.symmetric if @header.offset_bone_symmetries > 0x0
1403
+ @bone_flags.push b.flag if @header.offset_bone_flags > 0x0
1404
+ }
1405
+ @header.num_bones = bones.size
1406
+ self
1407
+ end
1408
+
1409
+ def scale(s)
1410
+ if @positions
1411
+ @positions.each { |p|
1412
+ p.x = p.x * s
1413
+ p.y = p.y * s
1414
+ p.z = p.z * s
1415
+ }
1416
+ end
1417
+ if @vertexes && @vertexes.first.respond_to?(:position)
1418
+ @vertexes.each { |v|
1419
+ v.position.x = v.position.x * s
1420
+ v.position.y = v.position.y * s
1421
+ v.position.z = v.position.z * s
1422
+ }
1423
+ end
1424
+ if @vertexes && @vertexes.first.respond_to?(:position2)
1425
+ @vertexes.each { |v|
1426
+ v.position2.x = v.position2.x * s
1427
+ v.position2.y = v.position2.y * s
1428
+ v.position2.z = v.position2.z * s
1429
+ }
1430
+ end
1431
+ if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1432
+ @vertexes_ex_data.each { |v|
1433
+ v.position2.x = v.position2.x * s
1434
+ v.position2.y = v.position2.y * s
1435
+ v.position2.z = v.position2.z * s
1436
+ }
1437
+ end
1438
+ if @bone_positions
1439
+ @bone_positions.each { |p|
1440
+ p.x = p.x * s
1441
+ p.y = p.y * s
1442
+ p.z = p.z * s
1443
+ }
1444
+ recompute_relative_positions
1445
+ end
1446
+ self
1447
+ end
1448
+
1449
+ def shift(x, y, z)
1450
+ if @positions
1451
+ @positions.each { |p|
1452
+ p.x = p.x + x
1453
+ p.y = p.y + y
1454
+ p.z = p.z + z
1455
+ }
1456
+ end
1457
+ if @vertexes && @vertexes.first.respond_to?(:position)
1458
+ @vertexes.each { |v|
1459
+ v.position.x = v.position.x + x
1460
+ v.position.y = v.position.y + y
1461
+ v.position.z = v.position.z + z
1462
+ }
1463
+ end
1464
+ if @vertexes && @vertexes.first.respond_to?(:position2)
1465
+ @vertexes.each { |v|
1466
+ v.position2.x = v.position2.x + x
1467
+ v.position2.y = v.position2.y + y
1468
+ v.position2.z = v.position2.z + z
1469
+ }
1470
+ end
1471
+ if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1472
+ @vertexes_ex_data.each { |v|
1473
+ v.position2.x = v.position2.x + x
1474
+ v.position2.y = v.position2.y + y
1475
+ v.position2.z = v.position2.z + z
1476
+ }
1477
+ end
1478
+ if @bone_positions
1479
+ @bone_positions.each { |p|
1480
+ p.x = p.x + x
1481
+ p.y = p.y + y
1482
+ p.z = p.z + z
1483
+ }
1484
+ recompute_relative_positions
1485
+ end
1486
+ self
1487
+ end
1488
+
1489
+ def rotate(*args)
1490
+ if args.length == 2
1491
+ (rx, ry, rz), center = args
1492
+ elsif args.length == 3
1493
+ rx, ry, rz = args
1494
+ center = nil
1495
+ else
1496
+ raise "Invalid arguments for rotate: #{args.inspect}!"
1497
+ end
1498
+ m = Linalg::get_rotation_matrix(rx, ry, rz, center: center)
1499
+ if @positions
1500
+ @positions.each { |p|
1501
+ r = m * Linalg::Vector::new(p.x, p.y, p.z)
1502
+ p.x = r.x
1503
+ p.y = r.y
1504
+ p.z = r.z
1505
+ }
1506
+ end
1507
+ if @vertexes && @vertexes.first.respond_to?(:position)
1508
+ @vertexes.each { |v|
1509
+ r = m * Linalg::Vector::new(v.position.x, v.position.y, v.position.z)
1510
+ v.position.x = r.x
1511
+ v.position.y = r.y
1512
+ v.position.z = r.z
1513
+ }
1514
+ end
1515
+ if @vertexes && @vertexes.first.respond_to?(:position2)
1516
+ @vertexes.each { |v|
1517
+ r = m * Linalg::Vector::new(v.position2.x, v.position2.y, v.position2.z)
1518
+ v.position2.x = r.x
1519
+ v.position2.y = r.y
1520
+ v.position2.z = r.z
1521
+ }
1522
+ end
1523
+ if @vertexes_ex_data && @vertexes_ex_data.first.respond_to?(:position2)
1524
+ @vertexes_ex_data.each { |v|
1525
+ r = m * Linalg::Vector::new(v.position2.x, v.position2.y, v.position2.z)
1526
+ v.position2.x = r.x
1527
+ v.position2.y = r.y
1528
+ v.position2.z = r.z
1529
+ }
1530
+ end
1531
+ if @bone_positions
1532
+ @bone_positions.each { |p|
1533
+ r = m * Linalg::Vector::new(p.x, p.y, p.z)
1534
+ p.x = r.x
1535
+ p.y = r.y
1536
+ p.z = r.z
1537
+ }
1538
+ recompute_relative_positions
1539
+ end
1540
+ if @vertexes
1541
+ @vertexes.each { |v|
1542
+ r = m * Linalg::Vector::new(v.normal.x, v.normal.y, v.normal.z, 0.0)
1543
+ v.normal.x = r.x
1544
+ v.normal.y = r.y
1545
+ v.normal.z = r.z
1546
+ if v.tangents.data != 0xc0c0c0ff
1547
+ r = m * Linalg::Vector::new(v.tangents.x, v.tangents.y, v.tangents.z, 0.0)
1548
+ v.tangents.x = r.x
1549
+ v.tangents.y = r.y
1550
+ v.tangents.z = r.z
1551
+ end
1552
+ }
1553
+ end
1554
+ self
1555
+ end
1556
+
1557
+ def set_pose(pose, exp)
1558
+ table = @bone_index_translate_table.table
1559
+ bones = get_bone_structure
1560
+ tracks = Hash::new { |h,k| h[k] = {} }
1561
+ tracks = bones.collect { |b|
1562
+ [ b.relative_position.x,
1563
+ b.relative_position.y,
1564
+ b.relative_position.z,
1565
+ 0.0, 0.0, 0.0,
1566
+ 0.0,
1567
+ 1.0, 1.0, 1.0,
1568
+ 1.0, 1.0, 1.0 ]
1569
+ }
1570
+ pose.each { |b, ts|
1571
+ bi = table[b]
1572
+ if bi
1573
+ ts.each { |ti, v|
1574
+ tracks[bi][ti] = v
1575
+ }
1576
+ end
1577
+ }
1578
+ if exp
1579
+ exp.apply(tracks, table)
1580
+ end
1581
+ matrices = tracks.each_with_index.collect { |ts, bi|
1582
+ if @header.offset_bone_flags > 0x0
1583
+ order = @bone_flags[bi]
1584
+ else
1585
+ order = nil
1586
+ end
1587
+ m = Linalg::get_translation_matrix(*ts[0..2])
1588
+ pi = @bone_hierarchy[bi]
1589
+ if pi != -1
1590
+ parent_cumulative_scale = tracks[pi][10..12]
1591
+ m = m * Linalg::get_inverse_scaling_matrix(*parent_cumulative_scale)
1592
+ 3.times { |i| ts[10+i] *= parent_cumulative_scale[i] }
1593
+ end
1594
+ 3.times { |i| ts[10+i] *= ts[7+i] }
1595
+ m = m * Linalg::get_rotation_matrix(*ts[3..5], order: order)
1596
+ m = m * Linalg::get_scaling_matrix(*ts[10..12])
1597
+ }
1598
+ multiplied_matrices = []
1599
+ inverse_bind_pose = bones.collect { |b|
1600
+ Linalg::get_translation_matrix(-1 * b.position.x, -1 * b.position.y, -1 * b.position.z)
1601
+ }
1602
+ bones.each { |b|
1603
+ if b.parent
1604
+ multiplied_matrices[b.index] = multiplied_matrices[b.parent.index] * matrices[b.index]
1605
+ else
1606
+ multiplied_matrices[b.index] = matrices[b.index]
1607
+ end
1608
+ }
1609
+ bones.each { |b|
1610
+ v = multiplied_matrices[b.index] * Linalg::Vector::new( 0.0, 0.0, 0.0 )
1611
+ b.position.x = v.x
1612
+ b.position.y = v.y
1613
+ b.position.z = v.z
1614
+ b.relative_position = nil
1615
+ }
1616
+ set_bone_structure(bones)
1617
+ multiplied_matrices = bones.collect { |b|
1618
+ multiplied_matrices[b.index] * inverse_bind_pose[b.index]
1619
+ }
1620
+ vertex_usage = get_vertex_usage
1621
+ vertex_usage.each { |vi, bs|
1622
+ bone_refs = bs.first.bone_refs
1623
+ bone_infos = get_vertex_field(:bone_infos, vi)
1624
+ indexes_and_weights = bone_infos.get_indexes_and_weights
1625
+ vertex_matrix = Linalg::get_zero_matrix
1626
+ indexes_and_weights.each { |bi, bw|
1627
+ i = bone_refs[bi]
1628
+ vertex_matrix = vertex_matrix + multiplied_matrices[i] * (bw.to_f/255.to_f)
1629
+ }
1630
+ normal_matrix = vertex_matrix.inverse.transpose
1631
+ vp = get_vertex_field(:position, vi)
1632
+ new_vp = vertex_matrix * Linalg::Vector::new(vp.x, vp.y, vp.z)
1633
+ vp.x = new_vp.x
1634
+ vp.y = new_vp.y
1635
+ vp.z = new_vp.z
1636
+ n = get_vertex_field(:normal, vi)
1637
+ new_n = (normal_matrix * Linalg::Vector::new(n.x, n.y, n.z, 0.0)).normalize
1638
+ n.x = new_n.x
1639
+ n.y = new_n.y
1640
+ n.z = new_n.z
1641
+ t = get_vertex_field(:tangents, vi)
1642
+ new_t = (normal_matrix * Linalg::Vector::new(t.x, t.y, t.z, 0.0)).normalize
1643
+ t.x = new_t.x
1644
+ t.y = new_t.y
1645
+ t.z = new_t.z
1646
+ p2 = get_vertex_field(:position2, vi)
1647
+ if p2
1648
+ new_p2 = vertex_matrix * Linalg::Vector::new(p2.x, p2.y, p2.z)
1649
+ p2.x = new_p2.x
1650
+ p2.y = new_p2.y
1651
+ p2.z = new_p2.z
1652
+ end
1653
+ }
1654
+ self
1655
+ end
1656
+
1657
+ def restrict_bones(used_bones)
1658
+ bones = get_bone_structure
1659
+ used_bones_array = used_bones.to_a.sort
1660
+ bone_map = used_bones_array.each_with_index.collect.to_h
1661
+ new_bones = used_bones_array.collect { |bi|
1662
+ b = bones[bi].dup
1663
+ b.index = bone_map[b.index]
1664
+ b
1665
+ }
1666
+ new_bones.each { |b|
1667
+ b.parent = new_bones[bone_map[b.parent.index]] if b.parent
1668
+ }
1669
+ set_bone_structure(new_bones)
1670
+
1671
+ table = @bone_index_translate_table.table
1672
+ new_table = table.select { |k,v|
1673
+ used_bones.include? v
1674
+ }
1675
+ new_table = new_table.collect { |k, v| [k, bone_map[v]] }.to_h
1676
+ @bone_index_translate_table.table = new_table
1677
+ @meshes.each_with_index { |m, i|
1678
+ m.batches.each_with_index { |b, j|
1679
+ b.bone_refs.collect! { |bi|
1680
+ new_bi = bone_map[bi]
1681
+ raise "Bone #{bi} was deleted bu is still used by mesh #{i} batch #{j}!" unless new_bi
1682
+ new_bi
1683
+ }
1684
+ }
1685
+ }
1686
+ self
1687
+ end
1688
+ private :restrict_bones
1689
+
1690
+ def remap_bones(bone_map)
1691
+ raise "Global index specified multiple times!" unless bone_map.values.uniq.size == bone_map.size
1692
+ local_to_global = @bone_index_translate_table.table.invert
1693
+ unknown_bones = bone_map.keys - local_to_global.keys
1694
+ raise "Unknown bones: #{unknown_bones}!" unless unknown_bones.size == 0
1695
+ global_tt = {}
1696
+ bone_map.each { |k, v|
1697
+ global_tt[local_to_global.delete(k)] = v
1698
+ }
1699
+ puts global_tt
1700
+ table = local_to_global.invert
1701
+ new_global_indexes = bone_map.values - table.keys
1702
+ raise "Global indexes: #{bone_map.values - new_global_indexes} still in use!" unless new_global_indexes.size == bone_map.size
1703
+ bone_map.each { |k, v|
1704
+ table[v] = k
1705
+ }
1706
+ @bone_symmetries.collect! { |k|
1707
+ global_tt[k] ? global_tt[k] : k
1708
+ } if @bone_symmetries
1709
+ @bone_index_translate_table.table = table
1710
+ self
1711
+ end
1712
+
1713
+ def order_bones
1714
+ arr = @bone_index_translate_table.table.sort
1715
+ old_local_to_new_local = {}
1716
+ arr.each_with_index { |(_, old_local), new_local|
1717
+ old_local_to_new_local[old_local] = new_local
1718
+ }
1719
+ new_local_to_old_local = old_local_to_new_local.invert
1720
+ bones = get_bone_structure
1721
+ new_bones = bones.size.times.collect { |new_bi|
1722
+ b = bones[new_local_to_old_local[new_bi]]
1723
+ b.index = new_bi
1724
+ b
1725
+ }
1726
+ new_bones.each { |b|
1727
+ raise "Invali hierarchy: #{b.parent.index} >= #{b.index} !" if b.parent && b.parent.index >= b.index
1728
+ }
1729
+ set_bone_structure(new_bones)
1730
+ new_table = @bone_index_translate_table.table.collect { |k, v| [k, old_local_to_new_local[v]] }.to_h
1731
+ @bone_index_translate_table.table = new_table
1732
+ @meshes.each_with_index { |m, i|
1733
+ m.batches.each_with_index { |b, j|
1734
+ b.bone_refs.collect! { |bi|
1735
+ old_local_to_new_local[bi]
1736
+ }
1737
+ }
1738
+ }
1739
+ self
1740
+ end
1741
+
1742
+ def delete_meshes(list)
1743
+ kept_meshes = @meshes.size.times.to_a - list
1744
+ @meshes = kept_meshes.collect { |i|
1745
+ @meshes[i]
1746
+ }
1747
+ @header.num_meshes = @meshes.size
1748
+ self
1749
+ end
1750
+
1751
+ def split_meshes(list)
1752
+ kept_meshes = @meshes.size.times.to_a - list
1753
+ split_meshes = @meshes.size.times.to_a - kept_meshes
1754
+ new_meshes = []
1755
+ split_meshes.each { |i|
1756
+ @meshes[i].batches.each_with_index { |b, j|
1757
+ new_mesh = @meshes[i].dup
1758
+ new_mesh.header = @meshes[i].header.dup
1759
+ new_mesh.header.name = @meshes[i].header.name.tr("\x00","") + ("_%02d" % j)
1760
+ new_mesh.batches = [b]
1761
+ new_meshes.push new_mesh
1762
+ }
1763
+ }
1764
+ @meshes = kept_meshes.collect { |i|
1765
+ @meshes[i]
1766
+ }
1767
+ @meshes += new_meshes
1768
+ @header.num_meshes = @meshes.size
1769
+ self
1770
+ end
1771
+
1772
+ def dummy_meshes(list)
1773
+ list.map { |i| m = @meshes[i]
1774
+ m.header.num_batch = 1
1775
+ m.batches = [m.batches.first]
1776
+ m.batches[0].set_triangles([m.batches[0].triangles.first[0]]*3)
1777
+ }
1778
+ self
1779
+ end
1780
+
1781
+ def duplicate_meshes(list)
1782
+ @meshes += list.collect { |i|
1783
+ @meshes[i].duplicate(@positions, @vertexes, @vertexes_ex_data)
1784
+ }
1785
+ @header.num_meshes = @meshes.size
1786
+ @header.num_vertexes = @vertexes.size
1787
+ self
1788
+ end
1789
+
1790
+ def swap_meshes(hash)
1791
+ hash.each { |k, v|
1792
+ raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1793
+ raise "Mesh #{v} was not found in the model!" unless @meshes[v]
1794
+ tmp = @meshes[k]
1795
+ @meshes[k] = @meshes[v]
1796
+ @meshes[v] = tmp
1797
+ }
1798
+ self
1799
+ end
1800
+
1801
+ def move_meshes(positions)
1802
+ raise "Invalid positions!" unless positions.size > 0
1803
+ positions.each { |k, v|
1804
+ raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1805
+ raise "Invalid target position #{v}!" unless v >= 0 && v < @meshes.length
1806
+ }
1807
+ raise "Duplicate mesh found!" unless positions.keys.uniq.size == positions.size
1808
+ raise "Duplicate target position found!" unless positions.values.uniq.size == positions.size
1809
+ m_p = positions.to_a.sort { |(m1, _), (m2, _)| m2 <=> m1 }
1810
+ m_a = m_p.collect { |m, p|
1811
+ [@meshes.delete_at(m), p]
1812
+ }.sort { |(_, p1), (_, p2)| p1 <=> p2 }
1813
+ m_a.each { |m, p|
1814
+ @meshes.insert(p, m)
1815
+ }
1816
+ self
1817
+ end
1818
+
1819
+ def merge_meshes(hash)
1820
+ hash.each { |k, vs|
1821
+ raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1822
+ vs = [vs].flatten
1823
+ vs.each { |v|
1824
+ raise "Mesh #{v} was not found in the model!" unless @meshes[v]
1825
+ @meshes[k].batches += @meshes[v].batches
1826
+ }
1827
+ @meshes[k].header.num_batch = @meshes[k].batches.length
1828
+ }
1829
+ self
1830
+ end
1831
+
1832
+ def delete_batches(hash)
1833
+ hash.each { |k, list|
1834
+ raise "Mesh #{k} was not found in the model!" unless @meshes[k]
1835
+ list = case list
1836
+ when Enumerable
1837
+ list.to_a
1838
+ else
1839
+ [list]
1840
+ end
1841
+ kept_batches = @meshes[k].batches.length.times.to_a - list
1842
+ @meshes[k].batches = kept_batches.collect { |i|
1843
+ raise "Batch #{i} was not found in mesh #{k}!" unless @meshes[k].batches[i]
1844
+ @meshes[k].batches[i]
1845
+ }
1846
+ @meshes[k].header.num_batch = @meshes[k].batches.length
1847
+ }
1848
+ self
1849
+ end
1850
+
1851
+ def delete_bones(list)
1852
+ used_bones = (@header.num_bones.times.to_a - list)
1853
+ restrict_bones(used_bones)
1854
+ self
1855
+ end
1856
+
1857
+ def cleanup_textures(input_name, overwrite)
1858
+ if File.exist?(input_name.gsub(".wmb",".wtb"))
1859
+ wtb = WTBFile::new(File::new(input_name.gsub(".wmb",".wtb"), "rb"))
1860
+ if overwrite
1861
+ output_name = input_name.gsub(".wmb",".wtb")
1862
+ else
1863
+ output_name = "wtb_output/#{File.basename(input_name, ".wmb")}.wtb"
1864
+ end
1865
+ wtp = false
1866
+ elsif File.exist?(input_name.gsub(".wmb",".wta"))
1867
+ wtb = WTBFile::new(File::new(input_name.gsub(".wmb",".wta"), "rb"), true, File::new(input_name.gsub(".wmb",".wtp"), "rb"))
1868
+ if overwrite
1869
+ output_name = input_name.gsub(".wmb",".wta")
1870
+ else
1871
+ output_name = "wtb_output/#{File.basename(input_name, ".wmb")}.wta"
1872
+ end
1873
+ wtp = true
1874
+ else
1875
+ raise "Could not find texture file!"
1876
+ end
1877
+
1878
+ available_textures = {}
1879
+ digests = []
1880
+ wtb.each.with_index { |(info, t), i|
1881
+ if @tex_info #Bayo 2
1882
+ digest = Digest::SHA1.hexdigest(t.read)
1883
+ available_textures[info[2]] = digest
1884
+ digests.push( digest )
1885
+ else #Bayo 1
1886
+ digest = Digest::SHA1.hexdigest(t.read)
1887
+ available_textures[i] = digest
1888
+ digests.push( digest )
1889
+ end
1890
+ t.rewind
1891
+ }
1892
+ used_textures_digest_map = {}
1893
+ used_texture_digests = Set[]
1894
+ @materials.each { |m|
1895
+ m.material_data[0..4].each { |tex_id|
1896
+ if available_textures.key?(tex_id)
1897
+ digest = available_textures[tex_id]
1898
+ used_textures_digest_map[tex_id] = digest
1899
+ used_texture_digests.add(digest)
1900
+ end
1901
+ }
1902
+ }
1903
+ index_list = digests.each_with_index.collect { |d,i| [i,d] }.select { |i, d|
1904
+ used_texture_digests.delete?(d)
1905
+ }.collect { |i,d| i }
1906
+ new_wtb = WTBFile::new(nil, wtb.big, wtp)
1907
+ j = 0
1908
+ digest_to_tex_id_map = {}
1909
+ wtb.each.with_index { |(info, t), i|
1910
+ if index_list.include?(i)
1911
+ new_wtb.push( t, info[1], info[2])
1912
+ if @tex_info
1913
+ digest_to_tex_id_map[digests[i]] = info[2]
1914
+ else
1915
+ digest_to_tex_id_map[digests[i]] = j
1916
+ end
1917
+ j += 1
1918
+ end
1919
+ }
1920
+ new_wtb.dump(output_name)
1921
+ @materials.each { |m|
1922
+ m.material_data[0..4].each_with_index { |tex_id, i|
1923
+ if available_textures.key?(tex_id)
1924
+ digest = available_textures[tex_id]
1925
+ m.material_data[i] = digest_to_tex_id_map[digest]
1926
+ end
1927
+ }
1928
+ }
1929
+ end
1930
+
1931
+ def advanced_materials
1932
+ if is_bayo2?
1933
+ materials
1934
+ else
1935
+ materials.collect { |m|
1936
+ if $material_db[m.type][:layout]
1937
+ Bayo1Material::new(m)
1938
+ else
1939
+ m
1940
+ end
1941
+ }
1942
+ end
1943
+ end
1944
+
1945
+ def cleanup_materials
1946
+ used_materials = Set[]
1947
+ @meshes.each { |m|
1948
+ m.batches.each { |b|
1949
+ if @tex_infos #Bayo 2
1950
+ used_materials.add(b.header.ex_mat_id)
1951
+ else #Bayo 1
1952
+ used_materials.add(b.header.material_id)
1953
+ end
1954
+ }
1955
+ }
1956
+ materials = @header.num_materials.times.to_a
1957
+ kept_materials = materials & used_materials.to_a
1958
+ correspondance_table = kept_materials.each_with_index.to_h
1959
+ @materials.select!.with_index { |_, i| used_materials.include?(i) }
1960
+ @header.num_materials = used_materials.size
1961
+ if @shader_names
1962
+ @shader_names.select!.with_index { |_, i| used_materials.include?(i) }
1963
+ end
1964
+ @meshes.each { |m|
1965
+ m.batches.each { |b|
1966
+ if @tex_infos
1967
+ b.header.ex_mat_id = correspondance_table[b.header.ex_mat_id]
1968
+ else
1969
+ b.header.material_id = correspondance_table[b.header.material_id]
1970
+ end
1971
+ }
1972
+ }
1973
+ self
1974
+ end
1975
+
1976
+ def cleanup_material_sizes
1977
+ raise "Unsupported for Bayonetta 2!" if @shader_names
1978
+ @materials.each { |m|
1979
+ type = m.type
1980
+ if $material_db.key?(type) && $material_db[type][:size]
1981
+ size = $material_db[type][:size]
1982
+ else
1983
+ warn "Unknown material type #{m.type}!"
1984
+ next
1985
+ end
1986
+ data_number = (size - 4)/4
1987
+ m.material_data = m.material_data.first(data_number)
1988
+ }
1989
+ self
1990
+ end
1991
+
1992
+ def maximize_material_sizes
1993
+ raise "Unsupported for Bayonetta 2!" if @shader_names
1994
+ max_size_mat = $material_db.select { |k, v| v[:size] }.max_by { |k, v|
1995
+ v[:size]
1996
+ }
1997
+ max_data_number = (max_size_mat[1][:size] - 4)/4
1998
+ @materials.each { |m|
1999
+ m.material_data = m.material_data + [0]*(max_data_number - m.material_data.size)
2000
+ }
2001
+ self
2002
+ end
2003
+
2004
+ def cleanup_bone_refs
2005
+ @meshes.each { |m|
2006
+ m.batches.each { |b|
2007
+ b.cleanup_bone_refs(@vertexes)
2008
+ }
2009
+ }
2010
+ self
2011
+ end
2012
+
2013
+ def add_ancestors_bone_refs
2014
+ @meshes.each { |m|
2015
+ m.batches.each { |b|
2016
+ b.add_ancestors_bone_refs(@vertexes, get_bone_structure)
2017
+ }
2018
+ }
2019
+ self
2020
+ end
2021
+
2022
+ def add_previous_bone_refs
2023
+ @meshes.each { |m|
2024
+ m.batches.each { |b|
2025
+ b.add_previous_bone_refs(@vertexes, get_bone_structure)
2026
+ }
2027
+ }
2028
+ self
2029
+ end
2030
+
2031
+ def cleanup_bones
2032
+ used_bones = Set[]
2033
+ @meshes.each { |m|
2034
+ m.batches.each { |b|
2035
+ used_bones.merge b.bone_refs
2036
+ }
2037
+ }
2038
+ bones = get_bone_structure
2039
+ used_bones.to_a.each { |bi|
2040
+ used_bones.merge bones[bi].parents.collect(&:index)
2041
+ }
2042
+ restrict_bones(used_bones)
2043
+ self
2044
+ end
2045
+
2046
+ def dump_bones(list = nil)
2047
+ bone_struct = Struct::new(:index, :parent, :relative_position, :position, :global_index, :symmetric, :flag)
2048
+ table = @bone_index_translate_table.table.invert
2049
+ list = (0...@header.num_bones) unless list
2050
+ list.collect { |bi|
2051
+ bone_struct::new(bi, @bone_hierarchy[bi], @bone_relative_positions[bi], @bone_positions[bi], table[bi], @header.offset_bone_symmetries > 0x0 ? @bone_symmetries[bi] : -1, @header.offset_bone_flags > 0x0 ? @bone_flags[bi] : 5)
2052
+ }
2053
+ end
2054
+
2055
+ def import_bones( list )
2056
+ table = @bone_index_translate_table.table
2057
+ @header.num_bones += list.length
2058
+ list.each { |b|
2059
+ table[b[:global_index]] = b[:index]
2060
+ @bone_hierarchy.push b[:parent]
2061
+ @bone_relative_positions.push b[:relative_position]
2062
+ @bone_positions.push b[:position]
2063
+ @bone_symmetries.push b[:symmetric] if @header.offset_bone_symmetries > 0x0
2064
+ @bone_flags.push b[:flag] if @header.offset_bone_flags > 0x0
2065
+ }
2066
+ @bone_index_translate_table.table = table
2067
+ self
2068
+ end
2069
+
2070
+ def remove_triangle_strips
2071
+ @meshes.each { |m|
2072
+ m.batches.each { |b|
2073
+ b.set_triangles(b.triangles)
2074
+ }
2075
+ }
2076
+ end
2077
+
2078
+ def revert_triangles( mesh_list )
2079
+ mesh_list.each { |mesh_index|
2080
+ @meshes[mesh_index].batches.each { |b|
2081
+ b.set_triangles( b.triangles.collect { |n1, n2, n3|
2082
+ [n1, n3, n2]
2083
+ })
2084
+ }
2085
+ }
2086
+ end
2087
+
2088
+ def cleanup_vertexes
2089
+ used_vertex_indexes = []
2090
+ @meshes.each { |m|
2091
+ m.batches.each { |b|
2092
+ used_vertex_indexes += b.vertex_indices
2093
+ }
2094
+ }
2095
+ used_vertex_indexes = used_vertex_indexes.sort.uniq
2096
+ @vertexes = used_vertex_indexes.collect { |i| @vertexes[i] }
2097
+ @vertexes_ex_data = used_vertex_indexes.collect { |i| @vertexes_ex_data[i] } if @vertexes_ex_data
2098
+ @header.num_vertexes = @vertexes.size
2099
+ vertex_map = used_vertex_indexes.each_with_index.to_h
2100
+ @meshes.each { |m|
2101
+ m.batches.each { |b|
2102
+ b.indices.collect! { |i|
2103
+ vertex_map[i + b.header.vertex_offset]
2104
+ }
2105
+ b.recompute_from_absolute_indices
2106
+ }
2107
+ }
2108
+ self
2109
+ end
2110
+
2111
+ def get_vertex_usage
2112
+ vertex_usage = Hash::new { |h, k| h[k] = [] }
2113
+ @meshes.each { |m|
2114
+ m.batches.each { |b|
2115
+ b.vertex_indices.each { |i|
2116
+ vertex_usage[i].push(b)
2117
+ }
2118
+ }
2119
+ }
2120
+ vertex_usage.each { |k,v| v.uniq! }
2121
+ vertex_usage
2122
+ end
2123
+
2124
+ # Duplicate vertexes used by several batches
2125
+ def normalize_vertex_usage
2126
+ vertex_usage = get_vertex_usage
2127
+ vertex_usage.select! { |k, v| v.length > 1 }
2128
+ batches = Set::new
2129
+ vertex_usage.each { |vi, blist|
2130
+ batches.merge blist[1..-1]
2131
+ }
2132
+ batches.each { |b|
2133
+ new_batch = b.duplicate(@positions, @vertexes, @vertexes_ex_data)
2134
+ b.header = new_batch.header
2135
+ b.indices = new_batch.indices
2136
+ }
2137
+ @header.num_vertexes = @vertexes.size
2138
+ self
2139
+ end
2140
+
2141
+ def remove_duplicate_vertexes
2142
+ vertex_indices = @header.num_vertexes.times
2143
+ equivalent_vertex_indices = vertex_indices.group_by { |indx|
2144
+ data = get_vertex_fields.collect { |f|
2145
+ get_vertex_field(f, indx).to_a
2146
+ }
2147
+ }
2148
+ indx_map = {}
2149
+ equivalent_vertex_indices.each { |k, group|
2150
+ group.each { |indx|
2151
+ indx_map[indx] = group.first
2152
+ }
2153
+ }
2154
+ @meshes.each { |m|
2155
+ m.batches.each { |b|
2156
+ b.indices = b.vertex_indices.collect { |i|
2157
+ indx_map[i]
2158
+ }
2159
+ b.recompute_from_absolute_indices
2160
+ }
2161
+ }
2162
+ end
2163
+
2164
+ def copy_vertex_properties(vertex_hash, **options)
2165
+ vertex_usage = nil
2166
+ vertex_usage = get_vertex_usage if options[:bone_infos]
2167
+ vertex_hash.each { |ivi, ovis|
2168
+ ovis = [ovis].flatten
2169
+ ovis.each { |ovi|
2170
+ iv = @vertexes[ivi]
2171
+ ov = @vertexes[ovi]
2172
+ if options[:position]
2173
+ if @positions
2174
+ @positions[ovi].x = @positions[ivi].x
2175
+ @positions[ovi].y = @positions[ivi].y
2176
+ @positions[ovi].z = @positions[ivi].z
2177
+ end
2178
+ if ov.respond_to?(:position)
2179
+ ov.position.x = iv.position.x
2180
+ ov.position.y = iv.position.y
2181
+ ov.position.z = iv.position.z
2182
+ end
2183
+ if ov.respond_to?(:position2)
2184
+ ov.position2.x = iv.position2.x
2185
+ ov.position2.y = iv.position2.y
2186
+ ov.position2.z = iv.position2.z
2187
+ end
2188
+ if @vertexes_ex_data
2189
+ if @vertexes_ex_data[ovi].respond_to?(:position2)
2190
+ @vertexes_ex_data[ovi].position2.x = @vertexes_ex_data[ivi].position2.x
2191
+ @vertexes_ex_data[ovi].position2.y = @vertexes_ex_data[ivi].position2.y
2192
+ @vertexes_ex_data[ovi].position2.z = @vertexes_ex_data[ivi].position2.z
2193
+ end
2194
+ end
2195
+ end
2196
+ if options[:mapping]
2197
+ if ov.respond_to?(:mapping)
2198
+ ov.mapping.u = iv.mapping.u
2199
+ ov.mapping.v = iv.mapping.v
2200
+ end
2201
+ if ov.respond_to?(:mapping2)
2202
+ ov.mapping2.u = iv.mapping2.u
2203
+ ov.mapping2.v = iv.mapping2.v
2204
+ end
2205
+ if @vertexes_ex_data && @vertexes_ex_data[ovi].respond_to?(:mapping2)
2206
+ @vertexes_ex_data[ovi].mapping2.u = @vertexes_ex_data[ivi].mapping2.u
2207
+ @vertexes_ex_data[ovi].mapping2.v = @vertexes_ex_data[ivi].mapping2.v
2208
+ end
2209
+ end
2210
+ if options[:normal]
2211
+ ov.normal = iv.normal
2212
+ end
2213
+ if options[:tangents]
2214
+ ov.tangents = iv.tangents
2215
+ end
2216
+ if options[:color]
2217
+ if ov.respond_to?(:color)
2218
+ ov.color = iv.color
2219
+ end
2220
+ if @vertexes_ex_data && @vertexes_ex_data[ovi].respond_to?(:color)
2221
+ @vertexes_ex_data[ovi].color = @vertexes_ex_data[ivi].color
2222
+ end
2223
+ end
2224
+ if options[:bone_infos] && ov.respond_to?(:bone_infos)
2225
+ input_batches = vertex_usage[ivi]
2226
+ raise "Unormalized vertex #{ivi} , normalize first, and recompute vertex numbers!" if input_batches.length > 1
2227
+ raise "Unused vertex #{ivi}!" if input_batches.length == 0
2228
+ output_batches = vertex_usage[ovi]
2229
+ raise "Unormalized vertex #{ovi} , normalize first, and recompute vertex numbers!" if output_batches.length > 1
2230
+ raise "Unused vertex #{ovi}!" if output_batches.length == 0
2231
+ input_batch = input_batches.first
2232
+ output_batch = output_batches.first
2233
+ input_bone_indexes_and_weights = iv.bone_infos.get_indexes_and_weights
2234
+ output_bone_indexes_and_weights = input_bone_indexes_and_weights.collect { |bi, bw|
2235
+ [input_batch.bone_refs[bi], bw]
2236
+ }.collect { |bi, bw|
2237
+ new_bi = output_batch.bone_refs.find_index(bi)
2238
+ unless new_bi
2239
+ new_bi = output_batch.bone_refs.length
2240
+ output_batch.bone_refs.push(bi)
2241
+ output_batch.num_bone_ref = output_batch.bone_refs.length
2242
+ end
2243
+ [new_bi, bw]
2244
+ }
2245
+ ov.bone_infos.set_indexes_and_weights( output_bone_indexes_and_weights )
2246
+ end
2247
+ }
2248
+ }
2249
+ self
2250
+ end
2251
+
2252
+ def renumber_batches
2253
+ @meshes.each_with_index { |m, i|
2254
+ m.header.id = i
2255
+ m.batches.each { |b|
2256
+ b.header.mesh_id = i
2257
+ }
2258
+ }
2259
+ self
2260
+ end
2261
+
2262
+ def remove_batch_vertex_offsets
2263
+ @meshes.each { |m|
2264
+ m.batches.each { |b|
2265
+ b.indices = b.vertex_indices
2266
+ b.recompute_from_absolute_indices
2267
+ }
2268
+ }
2269
+ self
2270
+ end
2271
+
2272
+ def fix_ex_data
2273
+ @vertexes.each_with_index { |v, i|
2274
+ if @vertexes_ex_data[i].respond_to?(:color)
2275
+ @vertexes_ex_data[i].color.data = 0xffc0c0c0
2276
+ end
2277
+ if @vertexes_ex_data[i].respond_to?(:mapping2)
2278
+ @vertexes_ex_data[i].mapping2.u = v.mapping.u
2279
+ @vertexes_ex_data[i].mapping2.v = v.mapping.v
2280
+ end
2281
+ }
2282
+ end
2283
+
2284
+ def copy_uv12(mesh_list)
2285
+ raise "No UV2 in model!" unless @vertexes_ex_data[0].respond_to?(:mapping2)
2286
+ mesh_list.each { |i|
2287
+ @meshes[i].batches.each { |b|
2288
+ b.vertex_indices.each { |vi|
2289
+ @vertexes_ex_data[vi].mapping2.u = @vertexes[vi].mapping.u
2290
+ @vertexes_ex_data[vi].mapping2.v = @vertexes[vi].mapping.v
2291
+ }
2292
+ }
2293
+ }
2294
+ end
2295
+
2296
+ def reverse_tangents_byte_order(mesh_list)
2297
+ raise "Vertex don't have tangents information!" unless @vertexes[0].respond_to?(:tangents)
2298
+ vertex_indices = []
2299
+ mesh_list.each { |i|
2300
+ @meshes[i].batches.each { |b|
2301
+ vertex_indices += b.vertex_indices
2302
+ }
2303
+ }
2304
+ vertex_indices.uniq!
2305
+ vertex_indices.each { |i|
2306
+ @vertexes[i].tangents.data = [@vertexes[i].tangents.data].pack("L<").unpack("L>").first
2307
+ }
2308
+ end
2309
+
2310
+ def recompute_batch_tangents(batch)
2311
+ vertex_indices = batch.vertex_indices.uniq
2312
+ tangents = vertex_indices.collect { |i| [i, Linalg::Vector::new(0, 0, 0, 0)] }.to_h
2313
+ indices = batch.triangles.flatten
2314
+ inconsistentuvs = 0
2315
+ # https://stackoverflow.com/a/66918075
2316
+ indices.each_with_index { |i, l|
2317
+ j = indices[(l + 1) % 3 + (l / 3) * 3]
2318
+ k = indices[(l + 2) % 3 + (l / 3) * 3]
2319
+ n = Linalg::Vector::new(*@vertexes[i].normal.to_a, 0.0)
2320
+ pi = Linalg::Vector::new(*@vertexes[i].position.to_a, 0.0)
2321
+ mi = Linalg::Vector::new(*@vertexes[i].mapping.to_a, 0.0, 0.0)
2322
+ pj = Linalg::Vector::new(*@vertexes[j].position.to_a, 0.0)
2323
+ mj = Linalg::Vector::new(*@vertexes[j].mapping.to_a, 0.0, 0.0)
2324
+ pk = Linalg::Vector::new(*@vertexes[k].position.to_a, 0.0)
2325
+ mk = Linalg::Vector::new(*@vertexes[k].mapping.to_a, 0.0, 0.0)
2326
+ v1 = pj - pi
2327
+ v2 = pk - pi
2328
+ t1 = mj - mi
2329
+ t2 = mk - mi
2330
+ uv2xArea = t1.x * t2.y - t1.y * t2.x
2331
+ next if (uv2xArea.abs < 9.5367431640625e-07)
2332
+ flip = uv2xArea > 0.0 ? 1.0 : -1.0
2333
+ inconsistentuvs += 1 if (tangents[i].w != 0 && tangents[i].w != flip)
2334
+ tangents[i].w = flip
2335
+ c = v1.x * n.x + v1.y * n.y + v1.z * n.z
2336
+ v1 -= n * (v1.dot(n));
2337
+ v2 -= n * (v2.dot(n));
2338
+ s = ((v1 * t2.y - v2 * t1.y) * flip).normalize
2339
+ ac = v1.dot(v2) / (v1.length * v2.length)
2340
+ ac = (ac > 1.0 ? 1.0 : (ac < -1.0 ? -1.0 : ac))
2341
+ angle = Math.acos(ac);
2342
+ tangents[i] += s*angle
2343
+ }
2344
+ warn "Found #{inconsistentuvs} inconsistent UVs" if inconsistentuvs > 0
2345
+ tangents.each { |i, t|
2346
+ t.normalize!
2347
+ @vertexes[i].tangents.set(t.x, t.y, t.z, -t.w)
2348
+ }
2349
+ end
2350
+
2351
+ def recompute_tangents(mesh_list)
2352
+ raise "Vertex don't have tangents information!" unless @vertexes[0].respond_to?(:tangents)
2353
+ mesh_list.each { |i|
2354
+ @meshes[i].batches.each { |b|
2355
+ recompute_batch_tangents(b)
2356
+ }
2357
+ }
2358
+ end
2359
+
2360
+ def recompute_layout
2361
+ get_vertex_types
2362
+
2363
+ if is_bayo2?
2364
+ last_offset = 0xc0
2365
+ else
2366
+ last_offset = 0x80
2367
+ end
2368
+
2369
+ @header.num_vertexes = @vertexes.size if @vertexes
2370
+
2371
+ if @header.offset_positions > 0x0
2372
+ last_offset = @header.offset_positions = align(last_offset, 0x20)
2373
+ last_offset += @header.num_vertexes * 12
2374
+ end
2375
+
2376
+ if @header.offset_vertexes > 0x0
2377
+ last_offset = @header.offset_vertexes = align(last_offset, 0x20)
2378
+ last_offset += @header.num_vertexes * @vertex_size
2379
+ end
2380
+ if @header.offset_vertexes_ex_data > 0x0
2381
+ last_offset += 0x20 if is_bayo2?
2382
+ last_offset = @header.offset_vertexes_ex_data = align(last_offset, 0x20)
2383
+ last_offset += @header.num_vertexes * @vertex_ex_size
2384
+ end
2385
+ if @header.offset_bone_relative_position > 0x0
2386
+ last_offset = @header.offset_bone_hierarchy = align(last_offset, 0x20)
2387
+ last_offset += @header.num_bones * 2
2388
+ end
2389
+ if @header.offset_bone_relative_position > 0x0
2390
+ last_offset = @header.offset_bone_relative_position = align(last_offset, 0x20)
2391
+ last_offset += @header.num_bones * 12
2392
+ end
2393
+ if @header.offset_bone_position > 0x0
2394
+ last_offset = @header.offset_bone_position = align(last_offset, 0x20)
2395
+ last_offset += @header.num_bones * 12
2396
+ end
2397
+ if @header.offset_bone_index_translate_table > 0x0
2398
+ last_offset = @header.offset_bone_index_translate_table = align(last_offset, 0x20)
2399
+ last_offset += @bone_index_translate_table.__size
2400
+ end
2401
+ if @header.offset_u_j > 0x0
2402
+ last_offset = @header.offset_u_j = align(last_offset, 0x20)
2403
+ last_offset += @u_j.__size
2404
+ end
2405
+ if @header.offset_bone_symmetries > 0x0
2406
+ last_offset = @header.offset_bone_symmetries = align(last_offset, 0x20)
2407
+ last_offset += @header.num_bones * 2
2408
+ end
2409
+ if @header.offset_bone_flags > 0x0
2410
+ last_offset = @header.offset_bone_flags = align(last_offset, 0x20)
2411
+ last_offset += @header.num_bones
2412
+ end
2413
+ if @header.offset_shader_names > 0x0
2414
+ last_offset = @header.offset_shader_names = align(last_offset, 0x20)
2415
+ last_offset += @header.num_materials * 16
2416
+ end
2417
+ if @header.offset_tex_infos > 0x0
2418
+ last_offset = @header.offset_tex_infos = align(last_offset, 0x20)
2419
+ last_offset += 4 + @tex_infos.num_tex_infos * 8
2420
+ end
2421
+
2422
+ last_offset = @header.offset_materials_offsets = align(last_offset, 0x20)
2423
+ off = 0
2424
+ @materials_offsets = []
2425
+ @header.num_materials.times { |i|
2426
+ @materials_offsets.push off
2427
+ off += @materials[i].__size
2428
+ off = align(off, 0x4)
2429
+ }
2430
+
2431
+ last_offset += 4*@header.num_materials
2432
+ last_offset = @header.offset_materials = align(last_offset, 0x20)
2433
+
2434
+ last_offset += @materials.collect(&:__size).reduce(&:+)
2435
+ # Pad last material with zeros
2436
+ pad_count = (align(last_offset, 0x20) - last_offset)/4
2437
+ @materials.last.material_data += [0]*pad_count
2438
+ last_offset = @header.offset_meshes_offsets = align(last_offset, 0x20)
2439
+
2440
+ off = 0
2441
+ @meshes_offsets = []
2442
+ @header.num_meshes.times { |i|
2443
+ @meshes[i].recompute_layout
2444
+ @meshes_offsets.push off
2445
+ off += @meshes[i].__size
2446
+ off = align(off, 0x20)
2447
+ }
2448
+
2449
+ last_offset += 4*@header.num_meshes
2450
+ last_offset = @header.offset_meshes = align(last_offset, 0x20)
2451
+ end
2452
+
2453
+ end
2454
+
2455
+ end