pgtools 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 +472 -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