pgtools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +25 -0
  3. data/bin/bxm_decoder +2 -0
  4. data/bin/bxm_encoder +2 -0
  5. data/bin/clh_convert +2 -0
  6. data/bin/clp_convert +2 -0
  7. data/bin/clw_convert +2 -0
  8. data/bin/dat_creator +2 -0
  9. data/bin/dat_extractor +2 -0
  10. data/bin/dat_ls +2 -0
  11. data/bin/eff_idd_creator +2 -0
  12. data/bin/eff_idd_extractor +2 -0
  13. data/bin/exp_convert_wiiu_pc +2 -0
  14. data/bin/exp_tool +2 -0
  15. data/bin/mot_convert_wiiu_pc +2 -0
  16. data/bin/mot_tool +2 -0
  17. data/bin/pkz_extractor +2 -0
  18. data/bin/scr_creator +2 -0
  19. data/bin/scr_extractor +2 -0
  20. data/bin/wmb_cleanup +2 -0
  21. data/bin/wmb_common_bones +2 -0
  22. data/bin/wmb_convert_pc_switch +2 -0
  23. data/bin/wmb_convert_wiiu_pc +2 -0
  24. data/bin/wmb_export_assimp +2 -0
  25. data/bin/wmb_get_bone_map +2 -0
  26. data/bin/wmb_import_assimp +2 -0
  27. data/bin/wmb_import_nier +2 -0
  28. data/bin/wmb_import_wiiu +2 -0
  29. data/bin/wtb_convert_wiiu_pc +2 -0
  30. data/bin/wtx_creator +2 -0
  31. data/bin/wtx_extractor +2 -0
  32. data/lib/bayonetta/alignment.rb +14 -0
  33. data/lib/bayonetta/bone.rb +55 -0
  34. data/lib/bayonetta/bxm.rb +180 -0
  35. data/lib/bayonetta/clh.rb +159 -0
  36. data/lib/bayonetta/clp.rb +212 -0
  37. data/lib/bayonetta/clw.rb +166 -0
  38. data/lib/bayonetta/dat.rb +261 -0
  39. data/lib/bayonetta/eff.rb +314 -0
  40. data/lib/bayonetta/endianness.rb +53 -0
  41. data/lib/bayonetta/exp.rb +768 -0
  42. data/lib/bayonetta/linalg.rb +416 -0
  43. data/lib/bayonetta/material_database.yaml +2581 -0
  44. data/lib/bayonetta/mot.rb +763 -0
  45. data/lib/bayonetta/pkz.rb +63 -0
  46. data/lib/bayonetta/scr.rb +393 -0
  47. data/lib/bayonetta/tools/bxm_decoder.rb +23 -0
  48. data/lib/bayonetta/tools/bxm_encoder.rb +37 -0
  49. data/lib/bayonetta/tools/clh_convert.rb +60 -0
  50. data/lib/bayonetta/tools/clp_convert.rb +70 -0
  51. data/lib/bayonetta/tools/clw_convert.rb +60 -0
  52. data/lib/bayonetta/tools/dat_creator.rb +57 -0
  53. data/lib/bayonetta/tools/dat_extractor.rb +94 -0
  54. data/lib/bayonetta/tools/dat_ls.rb +106 -0
  55. data/lib/bayonetta/tools/eff_idd_creator.rb +66 -0
  56. data/lib/bayonetta/tools/eff_idd_extractor.rb +73 -0
  57. data/lib/bayonetta/tools/exp_convert_wiiu_pc.rb +33 -0
  58. data/lib/bayonetta/tools/exp_tool.rb +48 -0
  59. data/lib/bayonetta/tools/mot_convert_wiiu_pc.rb +33 -0
  60. data/lib/bayonetta/tools/mot_tool.rb +60 -0
  61. data/lib/bayonetta/tools/pkz_extractor.rb +75 -0
  62. data/lib/bayonetta/tools/scr_creator.rb +63 -0
  63. data/lib/bayonetta/tools/scr_extractor.rb +78 -0
  64. data/lib/bayonetta/tools/wmb_cleanup.rb +250 -0
  65. data/lib/bayonetta/tools/wmb_common_bones.rb +45 -0
  66. data/lib/bayonetta/tools/wmb_convert_pc_switch.rb +35 -0
  67. data/lib/bayonetta/tools/wmb_convert_wiiu_pc.rb +33 -0
  68. data/lib/bayonetta/tools/wmb_export_assimp.rb +479 -0
  69. data/lib/bayonetta/tools/wmb_get_bone_map.rb +50 -0
  70. data/lib/bayonetta/tools/wmb_import_assimp.rb +735 -0
  71. data/lib/bayonetta/tools/wmb_import_geometry_wiiu_pc.rb +472 -0
  72. data/lib/bayonetta/tools/wmb_import_nier.rb +309 -0
  73. data/lib/bayonetta/tools/wtb_convert_wiiu_pc.rb +95 -0
  74. data/lib/bayonetta/tools/wtb_import_textures.rb +103 -0
  75. data/lib/bayonetta/tools/wtx_creator.rb +69 -0
  76. data/lib/bayonetta/tools/wtx_extractor.rb +85 -0
  77. data/lib/bayonetta/vertex_types.yaml +213 -0
  78. data/lib/bayonetta/vertex_types2.yaml +164 -0
  79. data/lib/bayonetta/vertex_types_nier.yaml +145 -0
  80. data/lib/bayonetta/wmb.rb +2443 -0
  81. data/lib/bayonetta/wmb3.rb +759 -0
  82. data/lib/bayonetta/wtb.rb +481 -0
  83. data/lib/bayonetta.rb +60 -0
  84. metadata +254 -0
@@ -0,0 +1,472 @@
1
+ require 'optparse'
2
+ require 'set'
3
+ require_relative '../../bayonetta.rb'
4
+ require 'yaml'
5
+ require 'shellwords'
6
+ include Bayonetta
7
+
8
+ def get_bone_mapping(source, target)
9
+ mapping = source.each.collect { |p|
10
+ distance = [Float::INFINITY, Float::INFINITY]
11
+ min_index = nil
12
+ target.each { |q|
13
+ d = p.distance(q)
14
+ if ( d[0] <= distance[0] && d[1] < distance[1] ) || d[0] < distance[0]
15
+ distance = d
16
+ min_index = q.index
17
+ end
18
+ }
19
+ [p.index, min_index]
20
+ }.to_h
21
+ end
22
+
23
+ def apply_mapping(mapping, meshes)
24
+ meshes.each { |m|
25
+ m.batches.each { |b|
26
+ b.bone_refs.collect! { |i|
27
+ mapping[i]
28
+ }
29
+ }
30
+ }
31
+ end
32
+
33
+ def get_bone_indexes(vertexes)
34
+ s = Set::new
35
+ vertexes.each { |v|
36
+ bi = v.bone_index
37
+ nbi = 0x0
38
+ ia = 4.times.collect { |i|
39
+ ni = bi & 0xff
40
+ bi >>= 8
41
+ s.add(ni)
42
+ }
43
+ }
44
+ s
45
+ end
46
+
47
+ def merge_bones(wmb1, wmb2)
48
+
49
+ tt1 = wmb1.bone_index_translate_table.table
50
+ tt2_orig = wmb2.bone_index_translate_table.table
51
+ tt2 = tt2_orig.invert
52
+
53
+ bones1 = wmb1.get_bone_structure
54
+ bones2 = wmb2.get_bone_structure
55
+
56
+ #works but for the arms
57
+ #F..ing subtree isomorphism problem
58
+ #mapping = get_bone_mapping(bones2, bones1)
59
+
60
+ if $options[:bone_map]
61
+ if $options[:bone_map] == "same"
62
+ common_mapping = tt2_orig.keys.collect { |k| [k,k] }.to_h
63
+ else
64
+ common_mapping = YAML::load_file( $options[:bone_map] )
65
+ end
66
+ else
67
+ common_mapping = {}
68
+ end
69
+ #common_bones = YAML::load_file("Bayonetta2_common_bones.yaml")
70
+ #mapping = YAML::load_file("Bayo2_pl0010_Bayo_pl0010_bone_mapping.yaml")
71
+
72
+ #mapping in local indexes
73
+ mapping = {}
74
+
75
+ tt2.each { |key, val|
76
+ mapping[key] = tt1[common_mapping[val]]
77
+ }
78
+ mapping = mapping.to_a.sort { |e1, e2| e1.first <=> e2.first }.to_h
79
+
80
+ #update bone positions to meet imported model's ones.
81
+ if $options[:update_bones]
82
+ mapping.select { |k,v| v }.each { |k,v|
83
+ bones1[v].position = bones2[k].position
84
+ }
85
+ end
86
+ #missing_bones = mapping.select { |k,v| v.nil? }.collect { |k,v| bones2[k] }
87
+ #missing_mapping = get_bone_mapping(missing_bones, bones1)
88
+ #p missing_mapping
89
+ #mapping.update(missing_mapping)
90
+ #p mapping
91
+
92
+ mapping[-1] = -1
93
+ missing_bones = mapping.select { |k,v| v.nil? }.collect { |k,v| k }
94
+ new_bone_index = bones1.size
95
+ new_bone_indexes = []
96
+ missing_bones.each { |bi|
97
+ mapping[bi] = new_bone_index
98
+ new_bone_indexes.push(new_bone_index)
99
+
100
+ b = bones2[bi].dup
101
+ b.index = new_bone_index
102
+ b.parent = bones1[mapping[b.parent.index]] if b.parent
103
+ b.symmetric = b.symmetric ? b.symmetric : -1
104
+ b.flag = b.flag ? b.flag : 5
105
+
106
+ bones1.push b
107
+ new_bone_index += 1
108
+ }
109
+ wmb1.set_bone_structure(bones1)
110
+
111
+ missing_bones_count = missing_bones.length
112
+
113
+ raise "Too many bones to add!" if missing_bones_count > 0x100
114
+
115
+ #missing_bones_slots = align(missing_bones_count, 0x10)/0x10
116
+ (align(missing_bones_count, 0x10) - missing_bones_count).times {
117
+ new_bone_indexes.push(0xfff)
118
+ }
119
+
120
+ used_indexes = tt1.keys
121
+ start_index = nil
122
+ #find a free range to add new bone data
123
+ (0x250..(0x1000-new_bone_indexes.size)).step(0x10) { |s_index|
124
+ if (used_indexes & (s_index..(s_index+new_bone_indexes.size)).to_a) == []
125
+ start_index = s_index
126
+ break
127
+ end
128
+ }
129
+ raise "No room available in translate table!" unless start_index
130
+ new_tt = wmb1.bone_index_translate_table.table.dup
131
+ new_bone_indexes.each_with_index { |ind, i|
132
+ new_tt[i+start_index] = ind
133
+ common_mapping[tt2[missing_bones[i]]] = i + start_index if i < missing_bones.length && missing_bones[i]
134
+ }
135
+ wmb1.bone_index_translate_table.table = new_tt
136
+
137
+ #update bone symmetries
138
+ if wmb1.bone_symmetries
139
+ (-missing_bones_count..-1).each { |i|
140
+ symmetric = common_mapping[wmb1.bone_symmetries[i]]
141
+ symmetric = -1 unless symmetric
142
+ wmb1.bone_symmetries[i] = symmetric
143
+ }
144
+ end
145
+ #mapping.each_with_index { |i, j|
146
+ # p = bones2[j]
147
+ # q = bones1[i]
148
+ # puts "#{j} -> #{i} : #{p.distance(q)}"
149
+ #}
150
+
151
+ apply_mapping(mapping, wmb2.meshes)
152
+
153
+ common_mapping
154
+ end
155
+
156
+ def merge_vertexes(wmb1, wmb2)
157
+ num_vertex1 = wmb1.header.num_vertexes
158
+ num_vertex2 = wmb2.header.num_vertexes
159
+
160
+ vertex_types = wmb1.get_vertex_types
161
+
162
+ wmb1.vertexes += num_vertex2.times.collect {
163
+ vertex_types[0]::new
164
+ }
165
+
166
+ if wmb1.vertexes_ex_data
167
+ wmb1.vertexes_ex_data += num_vertex2.times.collect {
168
+ vertex_types[1]::new
169
+ }
170
+ end
171
+
172
+ wmb1.header.num_vertexes += num_vertex2
173
+
174
+ wmb1.get_vertex_fields.each { |field|
175
+ unless wmb2.get_vertex_field(field, 0)
176
+ warn "Couldn't find vertex field #{field} in model 2"
177
+ if field == :color
178
+ warn "Using default value 0xc0 0xc0 0xc0 0xff"
179
+ c = Color::new
180
+ c.r = 0xc0
181
+ c.g = 0xc0
182
+ c.b = 0xc0
183
+ c.a = 0xff
184
+ num_vertex2.times { |i|
185
+ wmb1.set_vertex_field(field, num_vertex1 + i, c)
186
+ }
187
+ elsif field == :mapping2
188
+ warn "Using mapping as default"
189
+ num_vertex2.times { |i|
190
+ wmb1.set_vertex_field(field, num_vertex1 + i, wmb2.get_vertex_field(:mapping, i))
191
+ }
192
+ else
193
+ warn "No suitable default found..."
194
+ end
195
+ else
196
+ num_vertex2.times { |i|
197
+ wmb1.set_vertex_field(field, num_vertex1 + i, wmb2.get_vertex_field(field, i))
198
+ }
199
+ end
200
+ }
201
+ return num_vertex1
202
+ end
203
+
204
+ def merge_meshes(wmb1, wmb2)
205
+ new_vertex_offset = wmb1.header.num_vertexes - wmb2.header.num_vertexes
206
+ mesh_offset = align(wmb1.meshes_offsets.last + wmb1.meshes.last.__size, 0x20)
207
+ new_meshes_offset = wmb2.meshes_offsets.collect { |e|
208
+ e + mesh_offset
209
+ }
210
+ wmb2.meshes.each_with_index { |m, i|
211
+ m.header.id = i + wmb1.header.num_meshes
212
+ m.batches.each { |b|
213
+ b.header.mesh_id = m.header.id
214
+ if !wmb1.is_bayo2? && wmb2.is_bayo2?
215
+ b.header.batch_id = 0x0
216
+ b.header.flags = 0x8001
217
+ if b.header.u_e1 == 0x10
218
+ b.header.u_e1 = 0x0
219
+ elsif b.header.u_e1 == 0x30
220
+ b.header.u_e1 = 0x20
221
+ b.header.u_e2 = 0x0f
222
+ end
223
+ end
224
+ b.header.vertex_start += new_vertex_offset
225
+ b.header.vertex_end += new_vertex_offset
226
+ b.header.vertex_offset += new_vertex_offset
227
+ }
228
+ }
229
+
230
+ wmb1.header.num_meshes += wmb2.header.num_meshes
231
+ wmb1.meshes += wmb2.meshes
232
+ wmb1.meshes_offsets += new_meshes_offset
233
+ end
234
+
235
+ def merge_materials(wmb1, wmb2, tex_map)
236
+ new_mat_offset = wmb1.header.num_materials
237
+ mat_offset = wmb1.materials_offsets.last + wmb1.materials.last.__size
238
+ new_materials_offsets = []
239
+ new_materials = []
240
+ if wmb2.tex_infos then #Bayo 2
241
+ wmb2.materials.each_with_index { |e, i|
242
+ #biggest known material( in fact biggset is 0x174)
243
+ new_materials_offsets.push(mat_offset + i*0x124)
244
+ m = WMBFile::Material::new
245
+ m.type = 0x0
246
+ m.flag = 0x0
247
+ m.material_data = [0x0]*(0x120/4)
248
+ m.material_data[0] = (tex_map[e.material_data[0]] ? tex_map[e.material_data[0]] : 0x80000000)
249
+ m.material_data[1] = (tex_map[e.material_data[3]] ? tex_map[e.material_data[3]] : 0x80000000)
250
+ new_materials.push(m)
251
+ }
252
+ wmb2.meshes.each { |m|
253
+ m.batches.each { |b|
254
+ b.header.material_id = b.header.ex_mat_id + new_mat_offset
255
+ b.header.ex_mat_id = 0x0
256
+ }
257
+ }
258
+ else #Bayo 1
259
+ wmb2.materials.each_with_index { |e, i|
260
+ new_materials_offsets.push(mat_offset)
261
+ mat_offset += e.__size
262
+ m = WMBFile::Material::new
263
+ m.type = e.type
264
+ m.flag = e.flag
265
+ m.material_data = e.material_data.dup
266
+ if bayo_mat_properties[m.type]
267
+ bayomat = bayo_mat_properties[m.type]
268
+ bayomat.tex_num.times { |j|
269
+ m.material_data[j] = ( tex_map[e.material_data[j]] ? tex_map[e.material_data[j]] : e.material_data[j] )
270
+ }
271
+ else
272
+ warn "Unknow material type 0x#{m.type.to_s(16)}!"
273
+ 5.times { |j|
274
+ m.material_data[j] = ( tex_map[e.material_data[j]] ? tex_map[e.material_data[j]] : e.material_data[j] )
275
+ }
276
+ end
277
+ new_materials.push(m)
278
+ }
279
+ wmb2.meshes.each { |m|
280
+ m.batches.each { |b|
281
+ b.header.material_id = b.header.material_id + new_mat_offset
282
+ }
283
+ }
284
+ end
285
+
286
+
287
+ wmb1.header.num_materials += wmb2.header.num_materials
288
+ wmb1.materials += new_materials
289
+ wmb1.materials_offsets += new_materials_offsets
290
+ end
291
+
292
+ def get_texture_map(tex1, tex2)
293
+ offset = tex1.each.count
294
+ tex_map = {}
295
+ tex2.each.each_with_index { |t,i|
296
+ info, _ = t
297
+ _, _, idx = info
298
+ idx = i unless idx
299
+ tex_map[idx] = i+offset
300
+ }
301
+ tex_map
302
+ end
303
+
304
+ class BayoMat
305
+ attr_reader :code
306
+ attr_reader :size
307
+ attr_reader :tex_num
308
+ attr_reader :lightmap_index
309
+ attr_reader :normalmap_index
310
+ attr_reader :second_diffuse_index
311
+ attr_reader :reflection_index
312
+ def initialize(code, size, tex_num, lightmap_index, normalmap_index, second_diffuse_index, reflection_index)
313
+ @code = code
314
+ @size = size
315
+ @tex_num = tex_num
316
+ @lightmap_index = lightmap_index
317
+ @normalmap_index = normalmap_index
318
+ @second_diffuse_index = second_diffuse_index
319
+ @reflexion_index = reflection_index
320
+ end
321
+ end
322
+
323
+ @material_database = YAML::load_file(File.join(__dir__,"..", "material_database.yaml"))
324
+ @mat_properties = @material_database.select { |k, h|
325
+ h[:layout]
326
+ }.collect { |k, h|
327
+ tex_num = h[:layout].count { |k, v| v.match("sampler") }
328
+ lightmap_index = h[:layout].find_index { |k, v| k == "lightmap" && v.match("sampler") }
329
+ lightmap_index = -1 unless lightmap_index
330
+ normalmap_index = h[:layout].find_index { |k, v| k == "reliefmap" && v.match("sampler") }
331
+ normalmap_index = -1 unless normalmap_index
332
+ second_diffuse_index = h[:layout].find_index { |k, v| k == "Color_2" && v.match("sampler") }
333
+ second_diffuse_index = -1 unless second_diffuse_index
334
+ reflection_index = h[:layout].find_index { |k, v| k == "envmap" && v.match("sampler") }
335
+ reflection_index = -1 unless reflection_index
336
+ mat_info = BayoMat.new(k, h[:size], tex_num, lightmap_index, normalmap_index, second_diffuse_index, reflection_index)
337
+ [k, mat_info]
338
+ }.to_h
339
+
340
+ def bayo_mat_properties
341
+ return @mat_properties
342
+ {
343
+ 0x31 => BayoMat::new(0x31, 0xC0, 3, 1, -1, -1, -1),
344
+ 0x32 => BayoMat::new(0x32, 0xE4, 4, 1, -1, -1, 3),
345
+ 0x33 => BayoMat::new(0x33, 0xD4, 4, 2, -1, 1, -1),
346
+ 0x34 => BayoMat::new(0x34, 0xF8, 5, 2, -1, 1, 4),
347
+ 0x38 => BayoMat::new(0x38, 0xD4, 4, -1, 2, -1, -1),
348
+ 0x3A => BayoMat::new(0x3A, 0xD4, 4, 1, 2, -1, -1),
349
+ 0x3C => BayoMat::new(0x3C, 0xD4, 4, -1, -1, -1, -1),
350
+ 0x40 => BayoMat::new(0x40, 0xC4, 4, -1, -1, -1, -1),
351
+ 0x42 => BayoMat::new(0x42, 0xAC, 2, -1, -1, -1, -1),
352
+ 0x44 => BayoMat::new(0x44, 0xE4, 4, 1, -1, -1, -1),
353
+ 0x47 => BayoMat::new(0x47, 0x68, 1, -1, -1, -1, -1),
354
+ 0x48 => BayoMat::new(0x48, 0xC0, 3, 1, -1, 2, -1),
355
+ 0x4A => BayoMat::new(0x4A, 0xD4, 4, 2, -1, 1, -1),
356
+ 0x4B => BayoMat::new(0x4B, 0xD4, 4, -1, 2, -1, -1),
357
+ 0x4C => BayoMat::new(0x4C, 0xAC, 2, -1, -1, -1, -1),
358
+ 0x53 => BayoMat::new(0x53, 0x68, 1, -1, -1, -1, -1),
359
+ 0x54 => BayoMat::new(0x54, 0xD4, 4, 1, -1, -1, -1),
360
+ 0x59 => BayoMat::new(0x59, 0xD4, 4, 1, -1, -1, -1),
361
+ 0x60 => BayoMat::new(0x60, 0x68, 1, -1, -1, -1, -1),
362
+ 0x68 => BayoMat::new(0x68, 0xAC, 2, -1, -1, -1, -1),
363
+ 0x6B => BayoMat::new(0x6B, 0xD0, 3, -1, 1, -1, -1),
364
+ 0x6D => BayoMat::new(0x6D, 0xD0, 3, -1, 1, -1, -1),
365
+ 0x6E => BayoMat::new(0x6E, 0xD4, 4, -1, 1, -1, -1),
366
+ 0x71 => BayoMat::new(0x71, 0xE4, 4, 1, -1, -1, -1),
367
+ 0x72 => BayoMat::new(0x72, 0xD4, 4, -1, 1, -1, -1),
368
+ 0x75 => BayoMat::new(0x75, 0xAC, 2, -1, -1, -1, -1),
369
+ 0x7C => BayoMat::new(0x7C, 0xEA, 4, 1, -1, -1, 3),
370
+ 0x7F => BayoMat::new(0x7F, 0x124,4, -1, 1, -1, -1),
371
+ 0x81 => BayoMat::new(0x81, 0x120,3, -1, -1, -1, -1),
372
+ 0x83 => BayoMat::new(0x83, 0xAC, 2, -1, -1, -1, -1),
373
+ 0x87 => BayoMat::new(0x87, 0xD4, 4, -1, 1, -1, -1),
374
+ 0x89 => BayoMat::new(0x89, 0xC0, 3, 1, -1, -1, 2),
375
+ 0x8F => BayoMat::new(0x8F, 0xD4, 4, 1, -1, 2, 3),
376
+ 0x97 => BayoMat::new(0x97, 0x114,4, -1, -1, -1, -1),
377
+ 0xA1 => BayoMat::new(0xA1, 0xB0, 3, 1, -1, -1, -1),
378
+ 0xA3 => BayoMat::new(0xA3, 0xE4, 4, -1, 1, -1, -1),
379
+ 0xB2 => BayoMat::new(0xB2, 0xD4, 4, -1, 1, -1, -1),
380
+ 0xB3 => BayoMat::new(0xB3, 0x124,4, -1, 1, -1, -1)
381
+ }
382
+ end
383
+
384
+ def get_shader_map
385
+ {
386
+ "ois00_xbceX" => 0xB3,
387
+ "ois01_xbweX" => 0x7f,
388
+ "ois20_xbceX" => 0xB2,
389
+ "skn03_xbXXX" => 0x87,
390
+ "alp03_sbXXX" => 0x42,
391
+ "har01_sbXtX" => 0x84
392
+ }
393
+ end
394
+
395
+ $options = {}
396
+
397
+ OptionParser.new do |opts|
398
+ opts.banner = "Usage: wmb_import_geometry_wiiu_pc.rb target_file source_file [options]"
399
+
400
+ opts.on("-bFILE", "--bone-map=FILE", "Bone map") do |bone_map|
401
+ $options[:bone_map] = bone_map
402
+ end
403
+
404
+ opts.on("-u", "--update-bones", "Update recognized bone positions") do |update_bones|
405
+ $options[:update_bones] = update_bones
406
+ end
407
+
408
+ opts.on("-t", "--[no-]import-textures", "Import textures also") do |import_textures|
409
+ $options[:import_textures] = import_textures
410
+ end
411
+
412
+ opts.on("-o", "--[no-]overwrite", "Overwrite destination files") do |overwrite|
413
+ $options[:overwrite] = overwrite
414
+ end
415
+
416
+ opts.on("-h", "--help", "Prints this help") do
417
+ puts opts
418
+ exit
419
+ end
420
+
421
+ end.parse!
422
+
423
+ input_file1 = ARGV[0]
424
+ input_file2 = ARGV[1]
425
+
426
+ raise "Invalid file #{input_file1}" unless File::file?(input_file1)
427
+ raise "Invalid file #{input_file2}" unless File::file?(input_file2)
428
+
429
+ Dir.mkdir("wmb_output") unless Dir.exist?("wmb_output")
430
+
431
+ wmb1 = WMBFile::load(input_file1)
432
+ wmb2 = WMBFile::load(input_file2)
433
+
434
+ if $options[:import_textures]
435
+ tex1_file_name = input_file1.gsub(/wmb\z/,"wtb")
436
+ tex1 = WTBFile::new(File::new(tex1_file_name, "rb"))
437
+ begin
438
+ tex2_file_name = input_file2.gsub(/wmb\z/,"wta")
439
+ tex2 = WTBFile::new(File::new(tex2_file_name, "rb"), true, File::new(input_file2.gsub(/wmb\z/,"wtp"), "rb"))
440
+ rescue
441
+ tex2_file_name = input_file2.gsub(/wmb\z/,"wtb")
442
+ tex2 = WTBFile::new(File::new(tex2_file_name, "rb"))
443
+ end
444
+ end
445
+
446
+ tex_map = get_texture_map(tex1, tex2)
447
+
448
+ merge_vertexes(wmb1, wmb2)
449
+
450
+ common_mapping = merge_bones(wmb1, wmb2)
451
+
452
+ merge_materials(wmb1, wmb2, tex_map)
453
+
454
+ merge_meshes(wmb1, wmb2)
455
+
456
+ wmb1.recompute_relative_positions if $options[:update_bones]
457
+
458
+ wmb1.recompute_layout
459
+
460
+ File::open(File.join("wmb_output","#{File::basename(input_file2,".wmb")}_#{File::basename(input_file1,".wmb")}_bone_map.yaml"), "w") { |f|
461
+ f.write YAML::dump(common_mapping)
462
+ }
463
+
464
+ if $options[:overwrite]
465
+ wmb1.dump(input_file1)
466
+ else
467
+ wmb1.dump(File.join("wmb_output",File.basename(input_file1)))
468
+ end
469
+
470
+ if $options[:import_textures]
471
+ `ruby #{Shellwords.escape File.join(__dir__,"wtb_import_textures.rb")} #{Shellwords.escape tex1_file_name} #{Shellwords.escape tex2_file_name}#{$options[:overwrite] ? " --overwrite" : ""}`
472
+ end