pgtools 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +25 -25
- data/bin/bxm_decoder +2 -2
- data/bin/bxm_encoder +2 -2
- data/bin/clh_convert +2 -2
- data/bin/clp_convert +2 -2
- data/bin/clw_convert +2 -2
- data/bin/dat_creator +2 -2
- data/bin/dat_extractor +2 -2
- data/bin/dat_ls +2 -2
- data/bin/eff_idd_creator +2 -2
- data/bin/eff_idd_extractor +2 -2
- data/bin/exp_convert_wiiu_pc +2 -2
- data/bin/exp_tool +2 -2
- data/bin/mot_convert_wiiu_pc +2 -2
- data/bin/mot_tool +2 -2
- data/bin/pkz_extractor +2 -2
- data/bin/scr_creator +2 -2
- data/bin/scr_extractor +2 -2
- data/bin/wmb_cleanup +2 -2
- data/bin/wmb_common_bones +2 -2
- data/bin/wmb_convert_pc_switch +2 -2
- data/bin/wmb_convert_wiiu_pc +2 -2
- data/bin/wmb_export_assimp +2 -2
- data/bin/wmb_get_bone_map +2 -2
- data/bin/wmb_import_assimp +2 -2
- data/bin/wmb_import_nier +2 -2
- data/bin/wmb_import_wiiu +2 -2
- data/bin/wtb_convert_wiiu_pc +2 -2
- data/bin/wtx_creator +2 -2
- data/bin/wtx_extractor +2 -2
- data/lib/bayonetta/alignment.rb +0 -0
- data/lib/bayonetta/bone.rb +0 -0
- data/lib/bayonetta/bxm.rb +180 -180
- data/lib/bayonetta/clh.rb +159 -159
- data/lib/bayonetta/clp.rb +212 -212
- data/lib/bayonetta/clw.rb +166 -166
- data/lib/bayonetta/dat.rb +261 -261
- data/lib/bayonetta/eff.rb +314 -314
- data/lib/bayonetta/endianness.rb +0 -0
- data/lib/bayonetta/exp.rb +768 -768
- data/lib/bayonetta/linalg.rb +416 -416
- data/lib/bayonetta/material_database.yaml +2581 -2581
- data/lib/bayonetta/mot.rb +763 -763
- data/lib/bayonetta/pkz.rb +63 -63
- data/lib/bayonetta/scr.rb +0 -0
- data/lib/bayonetta/tools/bxm_decoder.rb +23 -23
- data/lib/bayonetta/tools/bxm_encoder.rb +37 -37
- data/lib/bayonetta/tools/clh_convert.rb +60 -60
- data/lib/bayonetta/tools/clp_convert.rb +70 -70
- data/lib/bayonetta/tools/clw_convert.rb +60 -60
- data/lib/bayonetta/tools/dat_creator.rb +57 -57
- data/lib/bayonetta/tools/dat_extractor.rb +94 -94
- data/lib/bayonetta/tools/dat_ls.rb +106 -106
- data/lib/bayonetta/tools/eff_idd_creator.rb +66 -66
- data/lib/bayonetta/tools/eff_idd_extractor.rb +73 -73
- data/lib/bayonetta/tools/exp_convert_wiiu_pc.rb +33 -33
- data/lib/bayonetta/tools/exp_tool.rb +48 -48
- data/lib/bayonetta/tools/mot_convert_wiiu_pc.rb +33 -33
- data/lib/bayonetta/tools/mot_tool.rb +0 -0
- data/lib/bayonetta/tools/pkz_extractor.rb +75 -75
- data/lib/bayonetta/tools/scr_creator.rb +63 -63
- data/lib/bayonetta/tools/scr_extractor.rb +78 -78
- data/lib/bayonetta/tools/wmb_cleanup.rb +250 -250
- data/lib/bayonetta/tools/wmb_common_bones.rb +45 -45
- data/lib/bayonetta/tools/wmb_convert_pc_switch.rb +35 -35
- data/lib/bayonetta/tools/wmb_convert_wiiu_pc.rb +33 -33
- data/lib/bayonetta/tools/wmb_export_assimp.rb +479 -479
- data/lib/bayonetta/tools/wmb_get_bone_map.rb +50 -50
- data/lib/bayonetta/tools/wmb_import_assimp.rb +735 -735
- data/lib/bayonetta/tools/wmb_import_geometry_wiiu_pc.rb +472 -472
- data/lib/bayonetta/tools/wmb_import_nier.rb +309 -309
- data/lib/bayonetta/tools/wtb_convert_wiiu_pc.rb +95 -95
- data/lib/bayonetta/tools/wtb_import_textures.rb +103 -103
- data/lib/bayonetta/tools/wtx_creator.rb +69 -69
- data/lib/bayonetta/tools/wtx_extractor.rb +85 -85
- data/lib/bayonetta/vertex_types.yaml +0 -0
- data/lib/bayonetta/vertex_types2.yaml +0 -0
- data/lib/bayonetta/vertex_types_nier.yaml +145 -145
- data/lib/bayonetta/wmb.rb +2455 -2443
- data/lib/bayonetta/wmb3.rb +759 -759
- data/lib/bayonetta/wtb.rb +481 -481
- data/lib/bayonetta.rb +60 -60
- metadata +2 -2
@@ -1,735 +1,735 @@
|
|
1
|
-
require 'assimp-ffi'
|
2
|
-
require 'rbconfig'
|
3
|
-
require 'optparse'
|
4
|
-
require 'pathname'
|
5
|
-
require 'set'
|
6
|
-
require_relative '../../bayonetta.rb'
|
7
|
-
require 'yaml'
|
8
|
-
require 'shellwords'
|
9
|
-
include Bayonetta
|
10
|
-
|
11
|
-
$is_win = (RbConfig::CONFIG['host_os'] =~ /mswin/)
|
12
|
-
|
13
|
-
$options = {}
|
14
|
-
|
15
|
-
OptionParser.new do |opts|
|
16
|
-
opts.banner = "Usage: wmb_import_assim.rb target_file source_file [options]"
|
17
|
-
|
18
|
-
opts.on("-bFILE", "--bone-map=FILE", "Bone map") do |bone_map|
|
19
|
-
$options[:bone_map] = bone_map
|
20
|
-
end
|
21
|
-
|
22
|
-
opts.on("-u", "--update-bones", "Update recognized bone positions") do |update_bones|
|
23
|
-
$options[:update_bones] = update_bones
|
24
|
-
end
|
25
|
-
|
26
|
-
# opts.on("-t", "--[no-]import-textures", "Import textures also") do |import_textures|
|
27
|
-
# $options[:import_textures] = import_textures
|
28
|
-
# end
|
29
|
-
|
30
|
-
opts.on("-t", "--[no-]transform-meshes", "Apply node transformation to meshes, conflicts with --group-batch-by-name") do |transform|
|
31
|
-
$options[:transform] = transform
|
32
|
-
$options[:group] = false
|
33
|
-
end
|
34
|
-
|
35
|
-
opts.on("--swap-mesh-y-z", "Use to swap the mesh if it is not aligned to the skeleton") do |swap|
|
36
|
-
$options[:swap] = swap
|
37
|
-
end
|
38
|
-
|
39
|
-
opts.on("--[no-]sort-bones", "Sorts the bone alphanumerically, WARNING: may cause issue if the order doesn't respect the hierarchy!") do |sort|
|
40
|
-
$options[:sort] = sort
|
41
|
-
end
|
42
|
-
|
43
|
-
opts.on("-o", "--[no-]overwrite", "Overwrite destination files") do |overwrite|
|
44
|
-
$options[:overwrite] = overwrite
|
45
|
-
end
|
46
|
-
|
47
|
-
opts.on("-g", "--[no-]group-batch-by-name", "Try grouping batches using their names, conflicts with --transform-meshes") do |group|
|
48
|
-
$options[:group] = group
|
49
|
-
$options[:transform] = false
|
50
|
-
end
|
51
|
-
|
52
|
-
opts.on("-f", "--filter-bones=REJECT_LIST", "Don't import all bones") do |filter_bones|
|
53
|
-
$options[:filter_bones] = eval(filter_bones)
|
54
|
-
end
|
55
|
-
|
56
|
-
opts.on("-a", "--[no-]auto-map", "Auto map bones") do |auto_map|
|
57
|
-
$options[:auto_map] = auto_map
|
58
|
-
end
|
59
|
-
|
60
|
-
opts.on("-s", "--[no-]list-skeleton", "Display a the skeleton") do |skeleton|
|
61
|
-
$options[:skeleton] = skeleton
|
62
|
-
end
|
63
|
-
|
64
|
-
opts.on("-l", "--[no-]list", "List source content") do |list|
|
65
|
-
$options[:list] = list
|
66
|
-
end
|
67
|
-
|
68
|
-
opts.on("-v", "--[no-]verbose", "Enable logging") do |verbose|
|
69
|
-
$options[:verbose] = verbose
|
70
|
-
end
|
71
|
-
|
72
|
-
opts.on("-r", "--[no-]use-root-bone", "Use the skeleton root as a bone") do |root|
|
73
|
-
$options[:root] = root
|
74
|
-
end
|
75
|
-
|
76
|
-
opts.on("--[no-]print-transform", "Print transform matrix when listing") do |print|
|
77
|
-
$options[:print_transform] = print
|
78
|
-
end
|
79
|
-
|
80
|
-
opts.on("--bone-prefix=STRING", "Change the bone prefix") do |prefix|
|
81
|
-
$options[:bone_prefix] = prefix
|
82
|
-
end
|
83
|
-
|
84
|
-
opts.on("-h", "--help", "Prints this help") do
|
85
|
-
puts opts
|
86
|
-
exit
|
87
|
-
end
|
88
|
-
|
89
|
-
end.parse!
|
90
|
-
|
91
|
-
$mesh_prefix = /mesh_(\d\d)_/
|
92
|
-
$batch_prefix = /batch_(\d\d)_/
|
93
|
-
if $options[:bone_prefix]
|
94
|
-
$bone_prefix = /#{$options[:bone_prefix]}(\d\d\d)/
|
95
|
-
else
|
96
|
-
$bone_prefix = /bone_(\d\d\d)/
|
97
|
-
end
|
98
|
-
$skeleton_prefix = /skeleton/
|
99
|
-
|
100
|
-
target = ARGV[0]
|
101
|
-
source = ARGV[1]
|
102
|
-
|
103
|
-
if $options[:list]
|
104
|
-
|
105
|
-
source = target unless source
|
106
|
-
|
107
|
-
property_store = Assimp::PropertyStore::new
|
108
|
-
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
109
|
-
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :CalcTangentSpace], props: property_store)
|
110
|
-
|
111
|
-
puts "Found #{scene.num_meshes} meshes."
|
112
|
-
puts "Found #{scene.num_textures} embedded textures."
|
113
|
-
puts "Found #{scene.num_materials} materials."
|
114
|
-
|
115
|
-
scene.root_node.each_node_with_depth { |n, d|
|
116
|
-
puts " "*d + n.name + (n.num_meshes > 0 ? " (#{n.num_meshes} mesh#{n.num_meshes > 1 ? "es" : ""})" : "")
|
117
|
-
puts n.transformation if $options[:print_transform]
|
118
|
-
}
|
119
|
-
puts "-----------------------------------------------"
|
120
|
-
scene.meshes.each { |m|
|
121
|
-
puts m.name
|
122
|
-
puts "num_vertices: #{m.num_vertices}"
|
123
|
-
puts "num_faces: #{m.num_faces}"
|
124
|
-
puts "num_bones: #{m.num_bones}"
|
125
|
-
m.bones.each { |b|
|
126
|
-
puts " "+b.name
|
127
|
-
}
|
128
|
-
puts "normals: #{!m[:normals].null?}"
|
129
|
-
puts "tangents: #{!m[:tangents].null?}"
|
130
|
-
puts "bitangents: #{!m[:bitangents].null?}"
|
131
|
-
m.colors.each_with_index { |c, i|
|
132
|
-
puts "color #{i}" if c
|
133
|
-
}
|
134
|
-
m.texture_coords.each_with_index { |t, i|
|
135
|
-
puts "uv #{i} (#{m.num_uv_components[i]})" if t
|
136
|
-
}
|
137
|
-
}
|
138
|
-
exit
|
139
|
-
end
|
140
|
-
|
141
|
-
def get_used_bone_set(scene)
|
142
|
-
known_bones = Set::new
|
143
|
-
scene.meshes.each { |m|
|
144
|
-
m.bones.each { |b|
|
145
|
-
known_bones.add(b.name)
|
146
|
-
}
|
147
|
-
}
|
148
|
-
known_bones
|
149
|
-
end
|
150
|
-
|
151
|
-
def find_skeleton(scene)
|
152
|
-
|
153
|
-
known_bones = get_used_bone_set(scene)
|
154
|
-
|
155
|
-
raise "Model uses no bones!" if known_bones.size == 0
|
156
|
-
|
157
|
-
skeleton = nil
|
158
|
-
potential_roots = nil
|
159
|
-
|
160
|
-
Assimp::Node.define_method(:eql?) do |other|
|
161
|
-
self.class == other.class &&
|
162
|
-
self.pointer == other.pointer
|
163
|
-
end
|
164
|
-
|
165
|
-
Assimp::Node.define_method(:hash) do
|
166
|
-
self.pointer.address.hash
|
167
|
-
end
|
168
|
-
|
169
|
-
scene.root_node.each_node { |n|
|
170
|
-
if known_bones.include?(n.name)
|
171
|
-
potential_roots = n.ancestors unless potential_roots
|
172
|
-
potential_roots &= n.ancestors
|
173
|
-
end
|
174
|
-
}
|
175
|
-
|
176
|
-
Assimp::Node.remove_method :eql?
|
177
|
-
Assimp::Node.remove_method :hash
|
178
|
-
|
179
|
-
skeleton = potential_roots.find { |n| n.name.match($skeleton_prefix) }
|
180
|
-
|
181
|
-
if !skeleton
|
182
|
-
potential_roots.reverse.each { |n|
|
183
|
-
if n.children.find { |c| c.name.match($bone_prefix) }
|
184
|
-
skeleton = n
|
185
|
-
break
|
186
|
-
end
|
187
|
-
}
|
188
|
-
end
|
189
|
-
|
190
|
-
if !skeleton
|
191
|
-
skeleton = potential_roots.first
|
192
|
-
end
|
193
|
-
|
194
|
-
skeleton
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
if $options[:skeleton]
|
199
|
-
|
200
|
-
source = target unless source
|
201
|
-
|
202
|
-
property_store = Assimp::PropertyStore::new
|
203
|
-
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
204
|
-
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :CalcTangentSpace], props: property_store)
|
205
|
-
|
206
|
-
skeleton = find_skeleton(scene)
|
207
|
-
skeleton.each_node_with_depth { |n, d|
|
208
|
-
puts " "*d + n.name
|
209
|
-
}
|
210
|
-
exit
|
211
|
-
end
|
212
|
-
|
213
|
-
def scene_bones(scene)
|
214
|
-
skeleton = find_skeleton(scene)
|
215
|
-
bones = []
|
216
|
-
skeleton.children.each { |c|
|
217
|
-
bones += c.each_node_with_depth.collect.to_a
|
218
|
-
}
|
219
|
-
#this doesn't always work
|
220
|
-
bones.sort! { |(n1, d1), (n2, d2)|
|
221
|
-
n1.name <=> n2.name
|
222
|
-
} if $options[:sort]
|
223
|
-
bones.collect! { |n, d| n }
|
224
|
-
bones = [skeleton] + bones if $options[:root]
|
225
|
-
bones
|
226
|
-
end
|
227
|
-
|
228
|
-
def find_bone_mapping(wmb, scene)
|
229
|
-
tt1 = wmb.bone_index_translate_table.table
|
230
|
-
|
231
|
-
common_mapping = {}
|
232
|
-
|
233
|
-
global_bone_names = tt1.keys
|
234
|
-
scene_bones = scene_bones(scene)
|
235
|
-
|
236
|
-
if $options[:auto_map]
|
237
|
-
scene_bones.each { |n|
|
238
|
-
data = n.name.match($bone_prefix)
|
239
|
-
if data
|
240
|
-
bone_number = data[1].to_i
|
241
|
-
if global_bone_names.include?(bone_number)
|
242
|
-
common_mapping[n.name] = bone_number
|
243
|
-
end
|
244
|
-
end
|
245
|
-
}
|
246
|
-
elsif $options[:bone_map]
|
247
|
-
common_mapping.merge! YAML::load_file( $options[:bone_map] )
|
248
|
-
else
|
249
|
-
common_mapping = {}
|
250
|
-
end
|
251
|
-
|
252
|
-
mapping = {}
|
253
|
-
scene_bones.each { |n|
|
254
|
-
mapping[n.name] = tt1[common_mapping[n.name]]
|
255
|
-
}
|
256
|
-
node_mapping = scene_bones.collect { |n| [n.name, n] }.to_h
|
257
|
-
[mapping, common_mapping, node_mapping]
|
258
|
-
end
|
259
|
-
|
260
|
-
def get_new_bones(wmb, mapping, node_mapping)
|
261
|
-
bones_wmb = wmb.get_bone_structure
|
262
|
-
if $options[:update_bones]
|
263
|
-
common_bones = mapping.reject { |k,v| v.nil? }
|
264
|
-
common_bones.each { |k, v|
|
265
|
-
p = node_mapping[k].world_transformation * Assimp::Vector3D::new
|
266
|
-
pos = bones_wmb[v].position
|
267
|
-
pos.x, pos.y, pos.z = p.x, p.y, p.z
|
268
|
-
}
|
269
|
-
end
|
270
|
-
|
271
|
-
mapping[-1] = -1
|
272
|
-
missing_bones = mapping.select { |k,v| v.nil? }.collect { |k,v| k }
|
273
|
-
|
274
|
-
if $options[:filter_bones]
|
275
|
-
missing_bones -= $options[:filter_bones]
|
276
|
-
end
|
277
|
-
|
278
|
-
new_bone_index = bones_wmb.size
|
279
|
-
new_bone_indexes = []
|
280
|
-
|
281
|
-
missing_bones.each { |bi|
|
282
|
-
mapping[bi] = new_bone_index
|
283
|
-
new_bone_indexes.push(new_bone_index)
|
284
|
-
p = node_mapping[bi].world_transformation * Assimp::Vector3D::new
|
285
|
-
pos = Position::new
|
286
|
-
pos.x, pos.y, pos.z = p.x, p.y, p.z
|
287
|
-
b = Bone::new(pos)
|
288
|
-
b.index = new_bone_index
|
289
|
-
parent_name = nil
|
290
|
-
if node_mapping[bi].parent
|
291
|
-
parent_name = node_mapping[bi].parent.name
|
292
|
-
parent_name = nil unless node_mapping.include?(parent_name)
|
293
|
-
end
|
294
|
-
b.parent = bones_wmb[mapping[parent_name]] if parent_name
|
295
|
-
b.symmetric = -1
|
296
|
-
b.flag = 5
|
297
|
-
bones_wmb.push b
|
298
|
-
new_bone_index += 1
|
299
|
-
}
|
300
|
-
[bones_wmb, missing_bones, new_bone_indexes]
|
301
|
-
end
|
302
|
-
|
303
|
-
def update_translate_table(wmb, common_mapping, missing_bones, new_bone_indexes)
|
304
|
-
missing_bones_count = new_bone_indexes.length
|
305
|
-
raise "Too many bones to add: #{missing_bones.inspect}!" if missing_bones_count > 0x100
|
306
|
-
(align(missing_bones_count, 0x10) - missing_bones_count).times {
|
307
|
-
new_bone_indexes.push(0xfff)
|
308
|
-
}
|
309
|
-
|
310
|
-
tt = wmb.bone_index_translate_table.table
|
311
|
-
used_indexes = tt.keys
|
312
|
-
start_index = nil
|
313
|
-
|
314
|
-
(0x250..(0x1000-new_bone_indexes.size)).step(0x10) { |s_index|
|
315
|
-
if (used_indexes & (s_index..(s_index+new_bone_indexes.size)).to_a) == []
|
316
|
-
start_index = s_index
|
317
|
-
break
|
318
|
-
end
|
319
|
-
}
|
320
|
-
raise "No room available in translate table!" unless start_index
|
321
|
-
new_tt = wmb.bone_index_translate_table.table.dup
|
322
|
-
new_bone_indexes.each_with_index { |ind, i|
|
323
|
-
new_tt[i+start_index] = ind
|
324
|
-
common_mapping[missing_bones[i]] = i + start_index if i < missing_bones.length && missing_bones[i]
|
325
|
-
}
|
326
|
-
wmb.bone_index_translate_table.table = new_tt
|
327
|
-
if wmb.bone_symmetries
|
328
|
-
(-missing_bones_count..-1).each { |i|
|
329
|
-
symmetric = common_mapping[wmb.bone_symmetries[i]]
|
330
|
-
symmetric = -1 unless symmetric
|
331
|
-
wmb.bone_symmetries[i] = symmetric
|
332
|
-
}
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
def merge_bones(wmb, scene)
|
337
|
-
mapping, common_mapping, node_mapping = find_bone_mapping(wmb, scene)
|
338
|
-
|
339
|
-
bones_wmb, missing_bones, new_bone_indexes = get_new_bones(wmb, mapping, node_mapping)
|
340
|
-
|
341
|
-
wmb.set_bone_structure(bones_wmb)
|
342
|
-
|
343
|
-
update_translate_table(wmb, common_mapping, missing_bones, new_bone_indexes)
|
344
|
-
|
345
|
-
[common_mapping, mapping]
|
346
|
-
end
|
347
|
-
|
348
|
-
def get_mesh_mapping(scene)
|
349
|
-
if $options[:group]
|
350
|
-
mesh_mapping = Hash::new { |h, k| h[k] = [] }
|
351
|
-
scene.meshes.sort { |m1, m2| m1.name <=> m2.name }.each { |m|
|
352
|
-
data = m.name.match($batch_prefix)
|
353
|
-
if data
|
354
|
-
mesh_name = m.name.gsub(data[0], "")
|
355
|
-
else
|
356
|
-
mesh_name = m.name
|
357
|
-
end
|
358
|
-
data = mesh_name.match(/_(\d\d)/)
|
359
|
-
if data
|
360
|
-
mesh_name = mesh_name.gsub(data[0], "")
|
361
|
-
end
|
362
|
-
mesh_mapping[mesh_name].push(m)
|
363
|
-
}
|
364
|
-
else
|
365
|
-
mesh_nodes = scene.root_node.each_node.select{ |n| n.children.find { |c| c.num_meshes > 0 } }.to_a
|
366
|
-
mesh_mapping = mesh_nodes.collect { |n|
|
367
|
-
batches = []
|
368
|
-
n.children.each { |c|
|
369
|
-
batches += c.meshes
|
370
|
-
}
|
371
|
-
[n, batches.collect{ |num| scene.meshes[num] }]
|
372
|
-
}.sort { |(n1, _), (n2, _)| n1.name <=> n2.name }.to_h
|
373
|
-
end
|
374
|
-
mesh_mapping
|
375
|
-
end
|
376
|
-
|
377
|
-
def create_new_meshes(wmb, mesh_mapping)
|
378
|
-
new_meshes = mesh_mapping.each_with_index.collect { |(m, _), i|
|
379
|
-
new_mesh = WMBFile::Mesh::new
|
380
|
-
if $options[:group]
|
381
|
-
mesh_name = m
|
382
|
-
else
|
383
|
-
mesh_name = m.name
|
384
|
-
end
|
385
|
-
data = mesh_name.match($mesh_prefix)
|
386
|
-
if data
|
387
|
-
name = mesh_name.gsub(data[0], "")
|
388
|
-
mesh_name = name if name != ""
|
389
|
-
end
|
390
|
-
new_mesh.header.name = mesh_name
|
391
|
-
new_mesh.header.id = i + wmb.header.num_meshes
|
392
|
-
new_mesh
|
393
|
-
}
|
394
|
-
end
|
395
|
-
|
396
|
-
def set_fields(wmb, bone_mapping, batch, new_indices, transform_matrix)
|
397
|
-
recompute_tangents = false
|
398
|
-
bone_refs = {}
|
399
|
-
bone_refs = batch.bones.sort { |b1, b2|
|
400
|
-
b1.name <=> b2.name
|
401
|
-
}.collect(&:name).uniq.each_with_index.collect { |b, i|
|
402
|
-
[b, i]
|
403
|
-
}.to_h
|
404
|
-
fields = wmb.get_vertex_fields
|
405
|
-
|
406
|
-
_, rotation, _ = transform_matrix.decompose
|
407
|
-
|
408
|
-
fields.each do |field|
|
409
|
-
case field
|
410
|
-
when :position
|
411
|
-
vertices = batch.vertices
|
412
|
-
new_indices.each_with_index { |target_index, index|
|
413
|
-
p = Position::new
|
414
|
-
o_p = vertices[index]
|
415
|
-
o_p = transform_matrix * o_p
|
416
|
-
p.x = o_p.x
|
417
|
-
p.y = o_p.y
|
418
|
-
p.z = o_p.z
|
419
|
-
wmb.set_vertex_field(field, target_index, p)
|
420
|
-
}
|
421
|
-
when :normal
|
422
|
-
normals = batch.normals
|
423
|
-
new_indices.each_with_index { |target_index, index|
|
424
|
-
n = Normal::new
|
425
|
-
o_n = normals[index]
|
426
|
-
o_n = rotation * o_n
|
427
|
-
n.x = o_n.x
|
428
|
-
n.y = o_n.y
|
429
|
-
n.z = o_n.z
|
430
|
-
wmb.set_vertex_field(field, target_index, n)
|
431
|
-
}
|
432
|
-
when :tangents
|
433
|
-
tangents = batch.tangents
|
434
|
-
bitangents = batch.bitangents
|
435
|
-
normals = batch.normals
|
436
|
-
new_indices.each_with_index { |target_index, index|
|
437
|
-
t = Tangents::new
|
438
|
-
o_t = tangents[index]
|
439
|
-
if o_t
|
440
|
-
o_t = rotation * o_t
|
441
|
-
o_n = normals[index]
|
442
|
-
o_n = rotation * o_n
|
443
|
-
o_b = bitangents[index]
|
444
|
-
o_b = rotation * o_b
|
445
|
-
n_o_b = (o_n ^ o_t)
|
446
|
-
if (n_o_b + o_b).length > 1
|
447
|
-
s = -1.0
|
448
|
-
else
|
449
|
-
s = 1.0
|
450
|
-
end
|
451
|
-
if o_t.x.nan? || o_t.y.nan? || o_t.z.nan?
|
452
|
-
t.set(0, 0, 0, 1)
|
453
|
-
else
|
454
|
-
t.set(o_t.x, o_t.y, o_t.z, s)
|
455
|
-
end
|
456
|
-
else
|
457
|
-
warn "Invalid mapping for batch: #{batch.name}, tangents will be recomputed!" unless recompute_tangents
|
458
|
-
recompute_tangents = true
|
459
|
-
t.set(0, 0, 0, 1)
|
460
|
-
end
|
461
|
-
wmb.set_vertex_field(field, target_index, t)
|
462
|
-
}
|
463
|
-
when :mapping, :mapping2, :mapping3, :mapping4, :mapping5
|
464
|
-
mapping_index = 0
|
465
|
-
mapping_index = 1 if field == :mapping2
|
466
|
-
mapping_index = 2 if field == :mapping3
|
467
|
-
mapping_index = 3 if field == :mapping4
|
468
|
-
mapping_index = 4 if field == :mapping5
|
469
|
-
mapping_index = 0 if batch.num_uv_components[mapping_index] < 2
|
470
|
-
texture_coords = batch.texture_coords[mapping_index]
|
471
|
-
raise "No texture coordinate found!" unless texture_coords
|
472
|
-
new_indices.each_with_index { |target_index, index|
|
473
|
-
m = Mapping::new
|
474
|
-
o_m = texture_coords[index]
|
475
|
-
m.u = o_m.x
|
476
|
-
m.v = o_m.y
|
477
|
-
wmb.set_vertex_field(field, target_index, m)
|
478
|
-
}
|
479
|
-
when :color, :color2
|
480
|
-
color_index = 0
|
481
|
-
color_index = 1 if field == :color2
|
482
|
-
colors = batch.colors[color_index]
|
483
|
-
if colors
|
484
|
-
new_indices.each_with_index { |target_index, index|
|
485
|
-
c = Color::new
|
486
|
-
o_c = colors[index]
|
487
|
-
c.r = (o_c.r * 255.0).round.clamp(0, 255)
|
488
|
-
c.g = (o_c.g * 255.0).round.clamp(0, 255)
|
489
|
-
c.b = (o_c.b * 255.0).round.clamp(0, 255)
|
490
|
-
c.a = (o_c.a < 0 ? 255 : (c.a * 255.0).round.clamp(0, 255))
|
491
|
-
wmb.set_vertex_field(field, target_index, c)
|
492
|
-
}
|
493
|
-
else
|
494
|
-
c = Color::new
|
495
|
-
c.r = 0xc0
|
496
|
-
c.g = 0xc0
|
497
|
-
c.b = 0xc0
|
498
|
-
c.a = 0xff
|
499
|
-
new_indices.each_with_index { |target_index, _|
|
500
|
-
wmb.set_vertex_field(field, target_index, c)
|
501
|
-
}
|
502
|
-
end
|
503
|
-
when :bone_infos
|
504
|
-
bone_infos = new_indices.size.times.collect {
|
505
|
-
[]
|
506
|
-
}
|
507
|
-
batch.bones.each { |bone|
|
508
|
-
bone_index = bone_refs[bone.name]
|
509
|
-
raise "Missing bone: #{bone.name}!" unless bone_index
|
510
|
-
bone.weights.each { |vtxweight|
|
511
|
-
vertex_id = vtxweight.vertex_id
|
512
|
-
weight = vtxweight.weight
|
513
|
-
bone_infos[vertex_id].push [bone_index, weight]
|
514
|
-
}
|
515
|
-
}
|
516
|
-
bone_infos = bone_infos.collect { |bone_info|
|
517
|
-
b_i = bone_info.sort { |(_, w1), (_, w2)| w1 <=> w2 }.reverse.first(4).reject { |_, w| w <= 0.0 }
|
518
|
-
if b_i.length == 0
|
519
|
-
warn "Invalid rigging for batch: #{batch.name}, orphan vertex!"
|
520
|
-
else
|
521
|
-
sum = b_i.reduce(0.0) { |memo, (_, w)| memo + w }
|
522
|
-
b_i.collect! { |ind, w| [ind, (w*255.0/sum).round.clamp(0, 255)] }
|
523
|
-
sum = b_i.reduce(0) { |memo, (_, w)| memo + w }
|
524
|
-
if sum != 255
|
525
|
-
diff = 255 - sum
|
526
|
-
b_i.first[1] += diff
|
527
|
-
end
|
528
|
-
end
|
529
|
-
b_i
|
530
|
-
}
|
531
|
-
|
532
|
-
new_indices.each_with_index { |target_index, index|
|
533
|
-
bi = BoneInfos::new
|
534
|
-
bi.set_indexes_and_weights(bone_infos[index])
|
535
|
-
wmb.set_vertex_field(field, target_index, bi)
|
536
|
-
}
|
537
|
-
else
|
538
|
-
raise "Unknow field in wmb file #{field.inspect}!"
|
539
|
-
end
|
540
|
-
end
|
541
|
-
[bone_refs, recompute_tangents]
|
542
|
-
end
|
543
|
-
|
544
|
-
def merge_geometry(wmb, scene, bone_mapping)
|
545
|
-
mesh_mapping = get_mesh_mapping(scene)
|
546
|
-
|
547
|
-
new_meshes = create_new_meshes(wmb, mesh_mapping)
|
548
|
-
|
549
|
-
vertex_types = wmb.get_vertex_types
|
550
|
-
|
551
|
-
mesh_mapping.each_with_index { |(n, batches), i|
|
552
|
-
if $options[:transform]
|
553
|
-
matrix = n.world_transformation
|
554
|
-
else
|
555
|
-
matrix = Assimp::Matrix4x4::identity
|
556
|
-
end
|
557
|
-
if $options[:swap]
|
558
|
-
rot = Assimp::Matrix4x4::new
|
559
|
-
rot.a1 = 1.0
|
560
|
-
rot.b3 = -1.0
|
561
|
-
rot.c2 = 1.0
|
562
|
-
rot.d4 = 1.0
|
563
|
-
matrix = rot * matrix
|
564
|
-
end
|
565
|
-
|
566
|
-
batches.each_with_index { |batch, j|
|
567
|
-
first_vertex_index = wmb.vertexes.length
|
568
|
-
|
569
|
-
num_vertices = batch.num_vertices
|
570
|
-
indices = (0...num_vertices)
|
571
|
-
new_indices = (first_vertex_index...(first_vertex_index + num_vertices)).to_a
|
572
|
-
|
573
|
-
wmb.vertexes += num_vertices.times.collect {
|
574
|
-
vertex_types[0]::new
|
575
|
-
}
|
576
|
-
if wmb.vertexes_ex_data
|
577
|
-
wmb.vertexes_ex_data += num_vertices.times.collect {
|
578
|
-
vertex_types[1]::new
|
579
|
-
}
|
580
|
-
end
|
581
|
-
|
582
|
-
wmb.header.num_vertexes += num_vertices
|
583
|
-
|
584
|
-
bone_refs, recompute_tangents = set_fields(wmb, bone_mapping, batch, new_indices, matrix)
|
585
|
-
|
586
|
-
b = WMBFile::Batch::new
|
587
|
-
b.header.material_id = wmb.header.num_materials + batch.material_index
|
588
|
-
b.header.mesh_id = new_meshes[i].header.id
|
589
|
-
indice_array = []
|
590
|
-
batch.faces.each { |f|
|
591
|
-
indice_array += f.indices.collect { |ind| new_indices[ind] }
|
592
|
-
}
|
593
|
-
b.header.num_indices = indice_array.length
|
594
|
-
b.indices = indice_array
|
595
|
-
b.recompute_from_absolute_indices
|
596
|
-
b.bone_refs = bone_refs.collect { |name, _| bone_mapping[name] }
|
597
|
-
b.num_bone_ref = b.bone_refs.length
|
598
|
-
new_meshes[i].batches.push b
|
599
|
-
new_meshes[i].header.num_batch += 1
|
600
|
-
if recompute_tangents
|
601
|
-
wmb.recompute_batch_tangents(b)
|
602
|
-
end
|
603
|
-
}
|
604
|
-
}
|
605
|
-
wmb.meshes += new_meshes
|
606
|
-
wmb.header.num_meshes += new_meshes.length
|
607
|
-
|
608
|
-
end
|
609
|
-
|
610
|
-
def get_new_tex_list(scene)
|
611
|
-
texture_set = Set::new
|
612
|
-
scene.materials.each { |m|
|
613
|
-
m.properties.each { |p|
|
614
|
-
if p.key == Assimp::MATKEY_TEXTURE
|
615
|
-
texture_set.add p.data
|
616
|
-
end
|
617
|
-
}
|
618
|
-
}
|
619
|
-
texture_set.to_a.sort
|
620
|
-
end
|
621
|
-
|
622
|
-
def merge_materials(wmb, scene, tex)
|
623
|
-
old_tex_count = tex.each.count
|
624
|
-
new_tex_list = get_new_tex_list(scene)
|
625
|
-
new_tex_map = new_tex_list.each_with_index.collect { |t, i| [t, i+old_tex_count] }.to_h
|
626
|
-
|
627
|
-
mat_offset = wmb.materials_offsets.last + wmb.materials.last.__size
|
628
|
-
new_materials = []
|
629
|
-
new_materials_offsets = []
|
630
|
-
scene.materials.each_with_index { |mat, i|
|
631
|
-
new_materials_offsets.push(mat_offset + i*0x124)
|
632
|
-
m = WMBFile::Material::new
|
633
|
-
m.type = 0x0
|
634
|
-
m.flag = 0x0
|
635
|
-
m.material_data = [0x0]*(0x120/4)
|
636
|
-
mat.properties.select { |p|
|
637
|
-
p.key == Assimp::MATKEY_TEXTURE
|
638
|
-
}.collect { |p|
|
639
|
-
new_tex_map[p.data]
|
640
|
-
}.each_with_index { |tex, j|
|
641
|
-
m.material_data[j] = tex
|
642
|
-
}
|
643
|
-
m.material_data[0] = (m.material_data[0] ? m.material_data[0] : 0x80000000)
|
644
|
-
m.material_data[1] = (m.material_data[1] ? m.material_data[1] : 0x80000000)
|
645
|
-
new_materials.push(m)
|
646
|
-
}
|
647
|
-
wmb.header.num_materials += new_materials.size
|
648
|
-
wmb.materials += new_materials
|
649
|
-
wmb.materials_offsets += new_materials_offsets
|
650
|
-
|
651
|
-
new_tex_list
|
652
|
-
end
|
653
|
-
|
654
|
-
def convert_windows_path(path)
|
655
|
-
# we are on linux but were given a windows path, hypothesis: linux on windows
|
656
|
-
if path.include?("\\")
|
657
|
-
res = ""
|
658
|
-
copy = path.dup
|
659
|
-
if path.start_with?("\\\\")
|
660
|
-
res << "//"
|
661
|
-
copy = copy[2..-1]
|
662
|
-
elsif path.start_with?("\\")
|
663
|
-
res << "/"
|
664
|
-
copy = copy[2..-1]
|
665
|
-
elsif m = path.match(/([A-Za-z]):\\/) #linux on windows
|
666
|
-
res << "/mnt/#{m[1].downcase}/"
|
667
|
-
copy = copy[3..-1]
|
668
|
-
end
|
669
|
-
res << copy.gsub("\\", "/")
|
670
|
-
else
|
671
|
-
res = path
|
672
|
-
end
|
673
|
-
res
|
674
|
-
end
|
675
|
-
|
676
|
-
def add_textures(tex, path, new_tex_list)
|
677
|
-
new_tex_list.each { |tex_path|
|
678
|
-
extension = File.extname(tex_path)
|
679
|
-
tex_path = convert_windows_path(tex_path) unless $is_win
|
680
|
-
old_tex_path = Pathname.new(tex_path).absolute? ? tex_path : File.join(path, tex_path)
|
681
|
-
if extension.downcase != ".dds"
|
682
|
-
tex_path = Pathname.new(tex_path).absolute? ? File.join(File.dirname(tex_path), File.basename(tex_path,extension)) + ".dds" :
|
683
|
-
File.join(path, File.join(File.dirname(tex_path), File.basename(tex_path,extension))) + ".dds"
|
684
|
-
`convert -define dds:compression=dxt5 #{Shellwords.escape old_tex_path} #{Shellwords.escape tex_path}`
|
685
|
-
else
|
686
|
-
tex_path = old_tex_path
|
687
|
-
end
|
688
|
-
tex.push File::new(tex_path, "rb")
|
689
|
-
}
|
690
|
-
end
|
691
|
-
|
692
|
-
raise "Invalid file: #{target}" unless target && File::file?(target)
|
693
|
-
raise "Invalid file: #{source}" unless source && File::file?(source)
|
694
|
-
|
695
|
-
Dir.mkdir("wmb_output") unless Dir.exist?("wmb_output")
|
696
|
-
Dir.mkdir("wtb_output") unless Dir.exist?("wtb_output")
|
697
|
-
|
698
|
-
wmb = WMBFile::load(target)
|
699
|
-
|
700
|
-
tex_file_name = target.gsub(/wmb\z/,"wtb")
|
701
|
-
tex = WTBFile::new(File::new(tex_file_name, "rb"))
|
702
|
-
|
703
|
-
if $options[:verbose]
|
704
|
-
log = Assimp::LogStream::stderr
|
705
|
-
log.attach
|
706
|
-
Assimp::LogStream::verbose(1)
|
707
|
-
end
|
708
|
-
|
709
|
-
property_store = Assimp::PropertyStore::new
|
710
|
-
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
711
|
-
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :FlipWindingOrder, :Triangulate, :FlipUVs], props: property_store)
|
712
|
-
|
713
|
-
|
714
|
-
common_mapping, bone_mapping = merge_bones(wmb, scene)
|
715
|
-
|
716
|
-
merge_geometry(wmb, scene, bone_mapping)
|
717
|
-
|
718
|
-
new_tex_list = merge_materials(wmb, scene, tex)
|
719
|
-
|
720
|
-
add_textures(tex, File.dirname(source), new_tex_list)
|
721
|
-
|
722
|
-
wmb.recompute_relative_positions
|
723
|
-
wmb.recompute_layout
|
724
|
-
|
725
|
-
File::open(File.join("wmb_output", "#{File::basename(source,File::extname(source))}_#{File::basename(target,".wmb")}_bone_map.yaml"), "w") { |f|
|
726
|
-
f.write YAML::dump(common_mapping)
|
727
|
-
}
|
728
|
-
|
729
|
-
if $options[:overwrite]
|
730
|
-
wmb.dump(target)
|
731
|
-
tex.dump(tex_file_name)
|
732
|
-
else
|
733
|
-
wmb.dump(File.join("wmb_output", File.basename(target)))
|
734
|
-
tex.dump(File.join("wtb_output", File.basename(tex_file_name)))
|
735
|
-
end
|
1
|
+
require 'assimp-ffi'
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'optparse'
|
4
|
+
require 'pathname'
|
5
|
+
require 'set'
|
6
|
+
require_relative '../../bayonetta.rb'
|
7
|
+
require 'yaml'
|
8
|
+
require 'shellwords'
|
9
|
+
include Bayonetta
|
10
|
+
|
11
|
+
$is_win = (RbConfig::CONFIG['host_os'] =~ /mswin/)
|
12
|
+
|
13
|
+
$options = {}
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: wmb_import_assim.rb target_file source_file [options]"
|
17
|
+
|
18
|
+
opts.on("-bFILE", "--bone-map=FILE", "Bone map") do |bone_map|
|
19
|
+
$options[:bone_map] = bone_map
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-u", "--update-bones", "Update recognized bone positions") do |update_bones|
|
23
|
+
$options[:update_bones] = update_bones
|
24
|
+
end
|
25
|
+
|
26
|
+
# opts.on("-t", "--[no-]import-textures", "Import textures also") do |import_textures|
|
27
|
+
# $options[:import_textures] = import_textures
|
28
|
+
# end
|
29
|
+
|
30
|
+
opts.on("-t", "--[no-]transform-meshes", "Apply node transformation to meshes, conflicts with --group-batch-by-name") do |transform|
|
31
|
+
$options[:transform] = transform
|
32
|
+
$options[:group] = false
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("--swap-mesh-y-z", "Use to swap the mesh if it is not aligned to the skeleton") do |swap|
|
36
|
+
$options[:swap] = swap
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--[no-]sort-bones", "Sorts the bone alphanumerically, WARNING: may cause issue if the order doesn't respect the hierarchy!") do |sort|
|
40
|
+
$options[:sort] = sort
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-o", "--[no-]overwrite", "Overwrite destination files") do |overwrite|
|
44
|
+
$options[:overwrite] = overwrite
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-g", "--[no-]group-batch-by-name", "Try grouping batches using their names, conflicts with --transform-meshes") do |group|
|
48
|
+
$options[:group] = group
|
49
|
+
$options[:transform] = false
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on("-f", "--filter-bones=REJECT_LIST", "Don't import all bones") do |filter_bones|
|
53
|
+
$options[:filter_bones] = eval(filter_bones)
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on("-a", "--[no-]auto-map", "Auto map bones") do |auto_map|
|
57
|
+
$options[:auto_map] = auto_map
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on("-s", "--[no-]list-skeleton", "Display a the skeleton") do |skeleton|
|
61
|
+
$options[:skeleton] = skeleton
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on("-l", "--[no-]list", "List source content") do |list|
|
65
|
+
$options[:list] = list
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on("-v", "--[no-]verbose", "Enable logging") do |verbose|
|
69
|
+
$options[:verbose] = verbose
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on("-r", "--[no-]use-root-bone", "Use the skeleton root as a bone") do |root|
|
73
|
+
$options[:root] = root
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on("--[no-]print-transform", "Print transform matrix when listing") do |print|
|
77
|
+
$options[:print_transform] = print
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on("--bone-prefix=STRING", "Change the bone prefix") do |prefix|
|
81
|
+
$options[:bone_prefix] = prefix
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on("-h", "--help", "Prints this help") do
|
85
|
+
puts opts
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
|
89
|
+
end.parse!
|
90
|
+
|
91
|
+
$mesh_prefix = /mesh_(\d\d)_/
|
92
|
+
$batch_prefix = /batch_(\d\d)_/
|
93
|
+
if $options[:bone_prefix]
|
94
|
+
$bone_prefix = /#{$options[:bone_prefix]}(\d\d\d)/
|
95
|
+
else
|
96
|
+
$bone_prefix = /bone_(\d\d\d)/
|
97
|
+
end
|
98
|
+
$skeleton_prefix = /skeleton/
|
99
|
+
|
100
|
+
target = ARGV[0]
|
101
|
+
source = ARGV[1]
|
102
|
+
|
103
|
+
if $options[:list]
|
104
|
+
|
105
|
+
source = target unless source
|
106
|
+
|
107
|
+
property_store = Assimp::PropertyStore::new
|
108
|
+
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
109
|
+
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :CalcTangentSpace], props: property_store)
|
110
|
+
|
111
|
+
puts "Found #{scene.num_meshes} meshes."
|
112
|
+
puts "Found #{scene.num_textures} embedded textures."
|
113
|
+
puts "Found #{scene.num_materials} materials."
|
114
|
+
|
115
|
+
scene.root_node.each_node_with_depth { |n, d|
|
116
|
+
puts " "*d + n.name + (n.num_meshes > 0 ? " (#{n.num_meshes} mesh#{n.num_meshes > 1 ? "es" : ""})" : "")
|
117
|
+
puts n.transformation if $options[:print_transform]
|
118
|
+
}
|
119
|
+
puts "-----------------------------------------------"
|
120
|
+
scene.meshes.each { |m|
|
121
|
+
puts m.name
|
122
|
+
puts "num_vertices: #{m.num_vertices}"
|
123
|
+
puts "num_faces: #{m.num_faces}"
|
124
|
+
puts "num_bones: #{m.num_bones}"
|
125
|
+
m.bones.each { |b|
|
126
|
+
puts " "+b.name
|
127
|
+
}
|
128
|
+
puts "normals: #{!m[:normals].null?}"
|
129
|
+
puts "tangents: #{!m[:tangents].null?}"
|
130
|
+
puts "bitangents: #{!m[:bitangents].null?}"
|
131
|
+
m.colors.each_with_index { |c, i|
|
132
|
+
puts "color #{i}" if c
|
133
|
+
}
|
134
|
+
m.texture_coords.each_with_index { |t, i|
|
135
|
+
puts "uv #{i} (#{m.num_uv_components[i]})" if t
|
136
|
+
}
|
137
|
+
}
|
138
|
+
exit
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_used_bone_set(scene)
|
142
|
+
known_bones = Set::new
|
143
|
+
scene.meshes.each { |m|
|
144
|
+
m.bones.each { |b|
|
145
|
+
known_bones.add(b.name)
|
146
|
+
}
|
147
|
+
}
|
148
|
+
known_bones
|
149
|
+
end
|
150
|
+
|
151
|
+
def find_skeleton(scene)
|
152
|
+
|
153
|
+
known_bones = get_used_bone_set(scene)
|
154
|
+
|
155
|
+
raise "Model uses no bones!" if known_bones.size == 0
|
156
|
+
|
157
|
+
skeleton = nil
|
158
|
+
potential_roots = nil
|
159
|
+
|
160
|
+
Assimp::Node.define_method(:eql?) do |other|
|
161
|
+
self.class == other.class &&
|
162
|
+
self.pointer == other.pointer
|
163
|
+
end
|
164
|
+
|
165
|
+
Assimp::Node.define_method(:hash) do
|
166
|
+
self.pointer.address.hash
|
167
|
+
end
|
168
|
+
|
169
|
+
scene.root_node.each_node { |n|
|
170
|
+
if known_bones.include?(n.name)
|
171
|
+
potential_roots = n.ancestors unless potential_roots
|
172
|
+
potential_roots &= n.ancestors
|
173
|
+
end
|
174
|
+
}
|
175
|
+
|
176
|
+
Assimp::Node.remove_method :eql?
|
177
|
+
Assimp::Node.remove_method :hash
|
178
|
+
|
179
|
+
skeleton = potential_roots.find { |n| n.name.match($skeleton_prefix) }
|
180
|
+
|
181
|
+
if !skeleton
|
182
|
+
potential_roots.reverse.each { |n|
|
183
|
+
if n.children.find { |c| c.name.match($bone_prefix) }
|
184
|
+
skeleton = n
|
185
|
+
break
|
186
|
+
end
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
if !skeleton
|
191
|
+
skeleton = potential_roots.first
|
192
|
+
end
|
193
|
+
|
194
|
+
skeleton
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
if $options[:skeleton]
|
199
|
+
|
200
|
+
source = target unless source
|
201
|
+
|
202
|
+
property_store = Assimp::PropertyStore::new
|
203
|
+
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
204
|
+
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :CalcTangentSpace], props: property_store)
|
205
|
+
|
206
|
+
skeleton = find_skeleton(scene)
|
207
|
+
skeleton.each_node_with_depth { |n, d|
|
208
|
+
puts " "*d + n.name
|
209
|
+
}
|
210
|
+
exit
|
211
|
+
end
|
212
|
+
|
213
|
+
def scene_bones(scene)
|
214
|
+
skeleton = find_skeleton(scene)
|
215
|
+
bones = []
|
216
|
+
skeleton.children.each { |c|
|
217
|
+
bones += c.each_node_with_depth.collect.to_a
|
218
|
+
}
|
219
|
+
#this doesn't always work
|
220
|
+
bones.sort! { |(n1, d1), (n2, d2)|
|
221
|
+
n1.name <=> n2.name
|
222
|
+
} if $options[:sort]
|
223
|
+
bones.collect! { |n, d| n }
|
224
|
+
bones = [skeleton] + bones if $options[:root]
|
225
|
+
bones
|
226
|
+
end
|
227
|
+
|
228
|
+
def find_bone_mapping(wmb, scene)
|
229
|
+
tt1 = wmb.bone_index_translate_table.table
|
230
|
+
|
231
|
+
common_mapping = {}
|
232
|
+
|
233
|
+
global_bone_names = tt1.keys
|
234
|
+
scene_bones = scene_bones(scene)
|
235
|
+
|
236
|
+
if $options[:auto_map]
|
237
|
+
scene_bones.each { |n|
|
238
|
+
data = n.name.match($bone_prefix)
|
239
|
+
if data
|
240
|
+
bone_number = data[1].to_i
|
241
|
+
if global_bone_names.include?(bone_number)
|
242
|
+
common_mapping[n.name] = bone_number
|
243
|
+
end
|
244
|
+
end
|
245
|
+
}
|
246
|
+
elsif $options[:bone_map]
|
247
|
+
common_mapping.merge! YAML::load_file( $options[:bone_map] )
|
248
|
+
else
|
249
|
+
common_mapping = {}
|
250
|
+
end
|
251
|
+
|
252
|
+
mapping = {}
|
253
|
+
scene_bones.each { |n|
|
254
|
+
mapping[n.name] = tt1[common_mapping[n.name]]
|
255
|
+
}
|
256
|
+
node_mapping = scene_bones.collect { |n| [n.name, n] }.to_h
|
257
|
+
[mapping, common_mapping, node_mapping]
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_new_bones(wmb, mapping, node_mapping)
|
261
|
+
bones_wmb = wmb.get_bone_structure
|
262
|
+
if $options[:update_bones]
|
263
|
+
common_bones = mapping.reject { |k,v| v.nil? }
|
264
|
+
common_bones.each { |k, v|
|
265
|
+
p = node_mapping[k].world_transformation * Assimp::Vector3D::new
|
266
|
+
pos = bones_wmb[v].position
|
267
|
+
pos.x, pos.y, pos.z = p.x, p.y, p.z
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
mapping[-1] = -1
|
272
|
+
missing_bones = mapping.select { |k,v| v.nil? }.collect { |k,v| k }
|
273
|
+
|
274
|
+
if $options[:filter_bones]
|
275
|
+
missing_bones -= $options[:filter_bones]
|
276
|
+
end
|
277
|
+
|
278
|
+
new_bone_index = bones_wmb.size
|
279
|
+
new_bone_indexes = []
|
280
|
+
|
281
|
+
missing_bones.each { |bi|
|
282
|
+
mapping[bi] = new_bone_index
|
283
|
+
new_bone_indexes.push(new_bone_index)
|
284
|
+
p = node_mapping[bi].world_transformation * Assimp::Vector3D::new
|
285
|
+
pos = Position::new
|
286
|
+
pos.x, pos.y, pos.z = p.x, p.y, p.z
|
287
|
+
b = Bone::new(pos)
|
288
|
+
b.index = new_bone_index
|
289
|
+
parent_name = nil
|
290
|
+
if node_mapping[bi].parent
|
291
|
+
parent_name = node_mapping[bi].parent.name
|
292
|
+
parent_name = nil unless node_mapping.include?(parent_name)
|
293
|
+
end
|
294
|
+
b.parent = bones_wmb[mapping[parent_name]] if parent_name
|
295
|
+
b.symmetric = -1
|
296
|
+
b.flag = 5
|
297
|
+
bones_wmb.push b
|
298
|
+
new_bone_index += 1
|
299
|
+
}
|
300
|
+
[bones_wmb, missing_bones, new_bone_indexes]
|
301
|
+
end
|
302
|
+
|
303
|
+
def update_translate_table(wmb, common_mapping, missing_bones, new_bone_indexes)
|
304
|
+
missing_bones_count = new_bone_indexes.length
|
305
|
+
raise "Too many bones to add: #{missing_bones.inspect}!" if missing_bones_count > 0x100
|
306
|
+
(align(missing_bones_count, 0x10) - missing_bones_count).times {
|
307
|
+
new_bone_indexes.push(0xfff)
|
308
|
+
}
|
309
|
+
|
310
|
+
tt = wmb.bone_index_translate_table.table
|
311
|
+
used_indexes = tt.keys
|
312
|
+
start_index = nil
|
313
|
+
|
314
|
+
(0x250..(0x1000-new_bone_indexes.size)).step(0x10) { |s_index|
|
315
|
+
if (used_indexes & (s_index..(s_index+new_bone_indexes.size)).to_a) == []
|
316
|
+
start_index = s_index
|
317
|
+
break
|
318
|
+
end
|
319
|
+
}
|
320
|
+
raise "No room available in translate table!" unless start_index
|
321
|
+
new_tt = wmb.bone_index_translate_table.table.dup
|
322
|
+
new_bone_indexes.each_with_index { |ind, i|
|
323
|
+
new_tt[i+start_index] = ind
|
324
|
+
common_mapping[missing_bones[i]] = i + start_index if i < missing_bones.length && missing_bones[i]
|
325
|
+
}
|
326
|
+
wmb.bone_index_translate_table.table = new_tt
|
327
|
+
if wmb.bone_symmetries
|
328
|
+
(-missing_bones_count..-1).each { |i|
|
329
|
+
symmetric = common_mapping[wmb.bone_symmetries[i]]
|
330
|
+
symmetric = -1 unless symmetric
|
331
|
+
wmb.bone_symmetries[i] = symmetric
|
332
|
+
}
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def merge_bones(wmb, scene)
|
337
|
+
mapping, common_mapping, node_mapping = find_bone_mapping(wmb, scene)
|
338
|
+
|
339
|
+
bones_wmb, missing_bones, new_bone_indexes = get_new_bones(wmb, mapping, node_mapping)
|
340
|
+
|
341
|
+
wmb.set_bone_structure(bones_wmb)
|
342
|
+
|
343
|
+
update_translate_table(wmb, common_mapping, missing_bones, new_bone_indexes)
|
344
|
+
|
345
|
+
[common_mapping, mapping]
|
346
|
+
end
|
347
|
+
|
348
|
+
def get_mesh_mapping(scene)
|
349
|
+
if $options[:group]
|
350
|
+
mesh_mapping = Hash::new { |h, k| h[k] = [] }
|
351
|
+
scene.meshes.sort { |m1, m2| m1.name <=> m2.name }.each { |m|
|
352
|
+
data = m.name.match($batch_prefix)
|
353
|
+
if data
|
354
|
+
mesh_name = m.name.gsub(data[0], "")
|
355
|
+
else
|
356
|
+
mesh_name = m.name
|
357
|
+
end
|
358
|
+
data = mesh_name.match(/_(\d\d)/)
|
359
|
+
if data
|
360
|
+
mesh_name = mesh_name.gsub(data[0], "")
|
361
|
+
end
|
362
|
+
mesh_mapping[mesh_name].push(m)
|
363
|
+
}
|
364
|
+
else
|
365
|
+
mesh_nodes = scene.root_node.each_node.select{ |n| n.children.find { |c| c.num_meshes > 0 } }.to_a
|
366
|
+
mesh_mapping = mesh_nodes.collect { |n|
|
367
|
+
batches = []
|
368
|
+
n.children.each { |c|
|
369
|
+
batches += c.meshes
|
370
|
+
}
|
371
|
+
[n, batches.collect{ |num| scene.meshes[num] }]
|
372
|
+
}.sort { |(n1, _), (n2, _)| n1.name <=> n2.name }.to_h
|
373
|
+
end
|
374
|
+
mesh_mapping
|
375
|
+
end
|
376
|
+
|
377
|
+
def create_new_meshes(wmb, mesh_mapping)
|
378
|
+
new_meshes = mesh_mapping.each_with_index.collect { |(m, _), i|
|
379
|
+
new_mesh = WMBFile::Mesh::new
|
380
|
+
if $options[:group]
|
381
|
+
mesh_name = m
|
382
|
+
else
|
383
|
+
mesh_name = m.name
|
384
|
+
end
|
385
|
+
data = mesh_name.match($mesh_prefix)
|
386
|
+
if data
|
387
|
+
name = mesh_name.gsub(data[0], "")
|
388
|
+
mesh_name = name if name != ""
|
389
|
+
end
|
390
|
+
new_mesh.header.name = mesh_name
|
391
|
+
new_mesh.header.id = i + wmb.header.num_meshes
|
392
|
+
new_mesh
|
393
|
+
}
|
394
|
+
end
|
395
|
+
|
396
|
+
def set_fields(wmb, bone_mapping, batch, new_indices, transform_matrix)
|
397
|
+
recompute_tangents = false
|
398
|
+
bone_refs = {}
|
399
|
+
bone_refs = batch.bones.sort { |b1, b2|
|
400
|
+
b1.name <=> b2.name
|
401
|
+
}.collect(&:name).uniq.each_with_index.collect { |b, i|
|
402
|
+
[b, i]
|
403
|
+
}.to_h
|
404
|
+
fields = wmb.get_vertex_fields
|
405
|
+
|
406
|
+
_, rotation, _ = transform_matrix.decompose
|
407
|
+
|
408
|
+
fields.each do |field|
|
409
|
+
case field
|
410
|
+
when :position
|
411
|
+
vertices = batch.vertices
|
412
|
+
new_indices.each_with_index { |target_index, index|
|
413
|
+
p = Position::new
|
414
|
+
o_p = vertices[index]
|
415
|
+
o_p = transform_matrix * o_p
|
416
|
+
p.x = o_p.x
|
417
|
+
p.y = o_p.y
|
418
|
+
p.z = o_p.z
|
419
|
+
wmb.set_vertex_field(field, target_index, p)
|
420
|
+
}
|
421
|
+
when :normal
|
422
|
+
normals = batch.normals
|
423
|
+
new_indices.each_with_index { |target_index, index|
|
424
|
+
n = Normal::new
|
425
|
+
o_n = normals[index]
|
426
|
+
o_n = rotation * o_n
|
427
|
+
n.x = o_n.x
|
428
|
+
n.y = o_n.y
|
429
|
+
n.z = o_n.z
|
430
|
+
wmb.set_vertex_field(field, target_index, n)
|
431
|
+
}
|
432
|
+
when :tangents
|
433
|
+
tangents = batch.tangents
|
434
|
+
bitangents = batch.bitangents
|
435
|
+
normals = batch.normals
|
436
|
+
new_indices.each_with_index { |target_index, index|
|
437
|
+
t = Tangents::new
|
438
|
+
o_t = tangents[index]
|
439
|
+
if o_t
|
440
|
+
o_t = rotation * o_t
|
441
|
+
o_n = normals[index]
|
442
|
+
o_n = rotation * o_n
|
443
|
+
o_b = bitangents[index]
|
444
|
+
o_b = rotation * o_b
|
445
|
+
n_o_b = (o_n ^ o_t)
|
446
|
+
if (n_o_b + o_b).length > 1
|
447
|
+
s = -1.0
|
448
|
+
else
|
449
|
+
s = 1.0
|
450
|
+
end
|
451
|
+
if o_t.x.nan? || o_t.y.nan? || o_t.z.nan?
|
452
|
+
t.set(0, 0, 0, 1)
|
453
|
+
else
|
454
|
+
t.set(o_t.x, o_t.y, o_t.z, s)
|
455
|
+
end
|
456
|
+
else
|
457
|
+
warn "Invalid mapping for batch: #{batch.name}, tangents will be recomputed!" unless recompute_tangents
|
458
|
+
recompute_tangents = true
|
459
|
+
t.set(0, 0, 0, 1)
|
460
|
+
end
|
461
|
+
wmb.set_vertex_field(field, target_index, t)
|
462
|
+
}
|
463
|
+
when :mapping, :mapping2, :mapping3, :mapping4, :mapping5
|
464
|
+
mapping_index = 0
|
465
|
+
mapping_index = 1 if field == :mapping2
|
466
|
+
mapping_index = 2 if field == :mapping3
|
467
|
+
mapping_index = 3 if field == :mapping4
|
468
|
+
mapping_index = 4 if field == :mapping5
|
469
|
+
mapping_index = 0 if batch.num_uv_components[mapping_index] < 2
|
470
|
+
texture_coords = batch.texture_coords[mapping_index]
|
471
|
+
raise "No texture coordinate found!" unless texture_coords
|
472
|
+
new_indices.each_with_index { |target_index, index|
|
473
|
+
m = Mapping::new
|
474
|
+
o_m = texture_coords[index]
|
475
|
+
m.u = o_m.x
|
476
|
+
m.v = o_m.y
|
477
|
+
wmb.set_vertex_field(field, target_index, m)
|
478
|
+
}
|
479
|
+
when :color, :color2
|
480
|
+
color_index = 0
|
481
|
+
color_index = 1 if field == :color2
|
482
|
+
colors = batch.colors[color_index]
|
483
|
+
if colors
|
484
|
+
new_indices.each_with_index { |target_index, index|
|
485
|
+
c = Color::new
|
486
|
+
o_c = colors[index]
|
487
|
+
c.r = (o_c.r * 255.0).round.clamp(0, 255)
|
488
|
+
c.g = (o_c.g * 255.0).round.clamp(0, 255)
|
489
|
+
c.b = (o_c.b * 255.0).round.clamp(0, 255)
|
490
|
+
c.a = (o_c.a < 0 ? 255 : (c.a * 255.0).round.clamp(0, 255))
|
491
|
+
wmb.set_vertex_field(field, target_index, c)
|
492
|
+
}
|
493
|
+
else
|
494
|
+
c = Color::new
|
495
|
+
c.r = 0xc0
|
496
|
+
c.g = 0xc0
|
497
|
+
c.b = 0xc0
|
498
|
+
c.a = 0xff
|
499
|
+
new_indices.each_with_index { |target_index, _|
|
500
|
+
wmb.set_vertex_field(field, target_index, c)
|
501
|
+
}
|
502
|
+
end
|
503
|
+
when :bone_infos
|
504
|
+
bone_infos = new_indices.size.times.collect {
|
505
|
+
[]
|
506
|
+
}
|
507
|
+
batch.bones.each { |bone|
|
508
|
+
bone_index = bone_refs[bone.name]
|
509
|
+
raise "Missing bone: #{bone.name}!" unless bone_index
|
510
|
+
bone.weights.each { |vtxweight|
|
511
|
+
vertex_id = vtxweight.vertex_id
|
512
|
+
weight = vtxweight.weight
|
513
|
+
bone_infos[vertex_id].push [bone_index, weight]
|
514
|
+
}
|
515
|
+
}
|
516
|
+
bone_infos = bone_infos.collect { |bone_info|
|
517
|
+
b_i = bone_info.sort { |(_, w1), (_, w2)| w1 <=> w2 }.reverse.first(4).reject { |_, w| w <= 0.0 }
|
518
|
+
if b_i.length == 0
|
519
|
+
warn "Invalid rigging for batch: #{batch.name}, orphan vertex!"
|
520
|
+
else
|
521
|
+
sum = b_i.reduce(0.0) { |memo, (_, w)| memo + w }
|
522
|
+
b_i.collect! { |ind, w| [ind, (w*255.0/sum).round.clamp(0, 255)] }
|
523
|
+
sum = b_i.reduce(0) { |memo, (_, w)| memo + w }
|
524
|
+
if sum != 255
|
525
|
+
diff = 255 - sum
|
526
|
+
b_i.first[1] += diff
|
527
|
+
end
|
528
|
+
end
|
529
|
+
b_i
|
530
|
+
}
|
531
|
+
|
532
|
+
new_indices.each_with_index { |target_index, index|
|
533
|
+
bi = BoneInfos::new
|
534
|
+
bi.set_indexes_and_weights(bone_infos[index])
|
535
|
+
wmb.set_vertex_field(field, target_index, bi)
|
536
|
+
}
|
537
|
+
else
|
538
|
+
raise "Unknow field in wmb file #{field.inspect}!"
|
539
|
+
end
|
540
|
+
end
|
541
|
+
[bone_refs, recompute_tangents]
|
542
|
+
end
|
543
|
+
|
544
|
+
def merge_geometry(wmb, scene, bone_mapping)
|
545
|
+
mesh_mapping = get_mesh_mapping(scene)
|
546
|
+
|
547
|
+
new_meshes = create_new_meshes(wmb, mesh_mapping)
|
548
|
+
|
549
|
+
vertex_types = wmb.get_vertex_types
|
550
|
+
|
551
|
+
mesh_mapping.each_with_index { |(n, batches), i|
|
552
|
+
if $options[:transform]
|
553
|
+
matrix = n.world_transformation
|
554
|
+
else
|
555
|
+
matrix = Assimp::Matrix4x4::identity
|
556
|
+
end
|
557
|
+
if $options[:swap]
|
558
|
+
rot = Assimp::Matrix4x4::new
|
559
|
+
rot.a1 = 1.0
|
560
|
+
rot.b3 = -1.0
|
561
|
+
rot.c2 = 1.0
|
562
|
+
rot.d4 = 1.0
|
563
|
+
matrix = rot * matrix
|
564
|
+
end
|
565
|
+
|
566
|
+
batches.each_with_index { |batch, j|
|
567
|
+
first_vertex_index = wmb.vertexes.length
|
568
|
+
|
569
|
+
num_vertices = batch.num_vertices
|
570
|
+
indices = (0...num_vertices)
|
571
|
+
new_indices = (first_vertex_index...(first_vertex_index + num_vertices)).to_a
|
572
|
+
|
573
|
+
wmb.vertexes += num_vertices.times.collect {
|
574
|
+
vertex_types[0]::new
|
575
|
+
}
|
576
|
+
if wmb.vertexes_ex_data
|
577
|
+
wmb.vertexes_ex_data += num_vertices.times.collect {
|
578
|
+
vertex_types[1]::new
|
579
|
+
}
|
580
|
+
end
|
581
|
+
|
582
|
+
wmb.header.num_vertexes += num_vertices
|
583
|
+
|
584
|
+
bone_refs, recompute_tangents = set_fields(wmb, bone_mapping, batch, new_indices, matrix)
|
585
|
+
|
586
|
+
b = WMBFile::Batch::new
|
587
|
+
b.header.material_id = wmb.header.num_materials + batch.material_index
|
588
|
+
b.header.mesh_id = new_meshes[i].header.id
|
589
|
+
indice_array = []
|
590
|
+
batch.faces.each { |f|
|
591
|
+
indice_array += f.indices.collect { |ind| new_indices[ind] }
|
592
|
+
}
|
593
|
+
b.header.num_indices = indice_array.length
|
594
|
+
b.indices = indice_array
|
595
|
+
b.recompute_from_absolute_indices
|
596
|
+
b.bone_refs = bone_refs.collect { |name, _| bone_mapping[name] }
|
597
|
+
b.num_bone_ref = b.bone_refs.length
|
598
|
+
new_meshes[i].batches.push b
|
599
|
+
new_meshes[i].header.num_batch += 1
|
600
|
+
if recompute_tangents
|
601
|
+
wmb.recompute_batch_tangents(b)
|
602
|
+
end
|
603
|
+
}
|
604
|
+
}
|
605
|
+
wmb.meshes += new_meshes
|
606
|
+
wmb.header.num_meshes += new_meshes.length
|
607
|
+
|
608
|
+
end
|
609
|
+
|
610
|
+
def get_new_tex_list(scene)
|
611
|
+
texture_set = Set::new
|
612
|
+
scene.materials.each { |m|
|
613
|
+
m.properties.each { |p|
|
614
|
+
if p.key == Assimp::MATKEY_TEXTURE
|
615
|
+
texture_set.add p.data
|
616
|
+
end
|
617
|
+
}
|
618
|
+
}
|
619
|
+
texture_set.to_a.sort
|
620
|
+
end
|
621
|
+
|
622
|
+
def merge_materials(wmb, scene, tex)
|
623
|
+
old_tex_count = tex.each.count
|
624
|
+
new_tex_list = get_new_tex_list(scene)
|
625
|
+
new_tex_map = new_tex_list.each_with_index.collect { |t, i| [t, i+old_tex_count] }.to_h
|
626
|
+
|
627
|
+
mat_offset = wmb.materials_offsets.last + wmb.materials.last.__size
|
628
|
+
new_materials = []
|
629
|
+
new_materials_offsets = []
|
630
|
+
scene.materials.each_with_index { |mat, i|
|
631
|
+
new_materials_offsets.push(mat_offset + i*0x124)
|
632
|
+
m = WMBFile::Material::new
|
633
|
+
m.type = 0x0
|
634
|
+
m.flag = 0x0
|
635
|
+
m.material_data = [0x0]*(0x120/4)
|
636
|
+
mat.properties.select { |p|
|
637
|
+
p.key == Assimp::MATKEY_TEXTURE
|
638
|
+
}.collect { |p|
|
639
|
+
new_tex_map[p.data]
|
640
|
+
}.each_with_index { |tex, j|
|
641
|
+
m.material_data[j] = tex
|
642
|
+
}
|
643
|
+
m.material_data[0] = (m.material_data[0] ? m.material_data[0] : 0x80000000)
|
644
|
+
m.material_data[1] = (m.material_data[1] ? m.material_data[1] : 0x80000000)
|
645
|
+
new_materials.push(m)
|
646
|
+
}
|
647
|
+
wmb.header.num_materials += new_materials.size
|
648
|
+
wmb.materials += new_materials
|
649
|
+
wmb.materials_offsets += new_materials_offsets
|
650
|
+
|
651
|
+
new_tex_list
|
652
|
+
end
|
653
|
+
|
654
|
+
def convert_windows_path(path)
|
655
|
+
# we are on linux but were given a windows path, hypothesis: linux on windows
|
656
|
+
if path.include?("\\")
|
657
|
+
res = ""
|
658
|
+
copy = path.dup
|
659
|
+
if path.start_with?("\\\\")
|
660
|
+
res << "//"
|
661
|
+
copy = copy[2..-1]
|
662
|
+
elsif path.start_with?("\\")
|
663
|
+
res << "/"
|
664
|
+
copy = copy[2..-1]
|
665
|
+
elsif m = path.match(/([A-Za-z]):\\/) #linux on windows
|
666
|
+
res << "/mnt/#{m[1].downcase}/"
|
667
|
+
copy = copy[3..-1]
|
668
|
+
end
|
669
|
+
res << copy.gsub("\\", "/")
|
670
|
+
else
|
671
|
+
res = path
|
672
|
+
end
|
673
|
+
res
|
674
|
+
end
|
675
|
+
|
676
|
+
def add_textures(tex, path, new_tex_list)
|
677
|
+
new_tex_list.each { |tex_path|
|
678
|
+
extension = File.extname(tex_path)
|
679
|
+
tex_path = convert_windows_path(tex_path) unless $is_win
|
680
|
+
old_tex_path = Pathname.new(tex_path).absolute? ? tex_path : File.join(path, tex_path)
|
681
|
+
if extension.downcase != ".dds"
|
682
|
+
tex_path = Pathname.new(tex_path).absolute? ? File.join(File.dirname(tex_path), File.basename(tex_path,extension)) + ".dds" :
|
683
|
+
File.join(path, File.join(File.dirname(tex_path), File.basename(tex_path,extension))) + ".dds"
|
684
|
+
`convert -define dds:compression=dxt5 #{Shellwords.escape old_tex_path} #{Shellwords.escape tex_path}`
|
685
|
+
else
|
686
|
+
tex_path = old_tex_path
|
687
|
+
end
|
688
|
+
tex.push File::new(tex_path, "rb")
|
689
|
+
}
|
690
|
+
end
|
691
|
+
|
692
|
+
raise "Invalid file: #{target}" unless target && File::file?(target)
|
693
|
+
raise "Invalid file: #{source}" unless source && File::file?(source)
|
694
|
+
|
695
|
+
Dir.mkdir("wmb_output") unless Dir.exist?("wmb_output")
|
696
|
+
Dir.mkdir("wtb_output") unless Dir.exist?("wtb_output")
|
697
|
+
|
698
|
+
wmb = WMBFile::load(target)
|
699
|
+
|
700
|
+
tex_file_name = target.gsub(/wmb\z/,"wtb")
|
701
|
+
tex = WTBFile::new(File::new(tex_file_name, "rb"))
|
702
|
+
|
703
|
+
if $options[:verbose]
|
704
|
+
log = Assimp::LogStream::stderr
|
705
|
+
log.attach
|
706
|
+
Assimp::LogStream::verbose(1)
|
707
|
+
end
|
708
|
+
|
709
|
+
property_store = Assimp::PropertyStore::new
|
710
|
+
property_store.import_fbx_preserve_pivots = Assimp::FALSE
|
711
|
+
scene = Assimp::import_file(source, flags: [:JoinIdenticalVertices, :FlipWindingOrder, :Triangulate, :FlipUVs], props: property_store)
|
712
|
+
|
713
|
+
|
714
|
+
common_mapping, bone_mapping = merge_bones(wmb, scene)
|
715
|
+
|
716
|
+
merge_geometry(wmb, scene, bone_mapping)
|
717
|
+
|
718
|
+
new_tex_list = merge_materials(wmb, scene, tex)
|
719
|
+
|
720
|
+
add_textures(tex, File.dirname(source), new_tex_list)
|
721
|
+
|
722
|
+
wmb.recompute_relative_positions
|
723
|
+
wmb.recompute_layout
|
724
|
+
|
725
|
+
File::open(File.join("wmb_output", "#{File::basename(source,File::extname(source))}_#{File::basename(target,".wmb")}_bone_map.yaml"), "w") { |f|
|
726
|
+
f.write YAML::dump(common_mapping)
|
727
|
+
}
|
728
|
+
|
729
|
+
if $options[:overwrite]
|
730
|
+
wmb.dump(target)
|
731
|
+
tex.dump(tex_file_name)
|
732
|
+
else
|
733
|
+
wmb.dump(File.join("wmb_output", File.basename(target)))
|
734
|
+
tex.dump(File.join("wtb_output", File.basename(tex_file_name)))
|
735
|
+
end
|