-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmerger.cpp
272 lines (234 loc) · 9.96 KB
/
merger.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include "merger.h"
#define COMPRESSED
#include <algorithm>
#include <string>
#include <vector>
#include <fstream>
const float fps = 30.0f;
const float frame_delta = 1.0f / fps;
const bool enable_test = false;
void process(int argc, char* argv[])
{
// Arg parsing
if (!enable_test && (argc != 4))
{
LOG("Wrong parameter count. Format: in_a.hkx in_b.hkx out.hkx");
return;
}
std::string dir_a, dir_v, dir_out;
if (enable_test)
{
dir_a = "E:/dev/projects/HkxMerge/hkx/a.hkx";
dir_v = "E:/dev/projects/HkxMerge/hkx/v.hkx";
dir_out = "E:/dev/projects/HkxMerge/hkx/out.hkx";
}
else
{
dir_a = argv[1];
dir_v = argv[2];
dir_out = argv[3];
}
LOG("Input file 1: " << dir_a << std::endl
<< "Input file 2: " << dir_v << std::endl
<< "Output file: " << dir_out);
// Raeading files and checking
hkResource* data_a = loadData(dir_a);
hkResource* data_v = loadData(dir_v);
if (!(data_a && data_v))
return;
auto anim_a = static_cast<hkaInterleavedUncompressedAnimation*>(reinterpret_cast<hkaAnimationContainer*>(data_a->getContents<hkRootLevelContainer>()->findObjectByType(hkaAnimationContainerClass.getName()))->m_animations[0].val());
auto anim_v = static_cast<hkaInterleavedUncompressedAnimation*>(reinterpret_cast<hkaAnimationContainer*>(data_v->getContents<hkRootLevelContainer>()->findObjectByType(hkaAnimationContainerClass.getName()))->m_animations[0].val());
if (!checkSame(anim_a, anim_v))
{
LOG("Two animations duration not matched!");
return;
}
// - Crafting the new animation
LOG("Start merging animation!");
int ntracks = anim_a->m_numberOfTransformTracks + anim_v->m_numberOfTransformTracks + 3;
float duration = anim_a->m_duration;
int nframes = (int)roundf(duration / frame_delta);
// Uncompressed Animation init
hkRefPtr<hkaInterleavedUncompressedAnimation> temp_anim = new hkaInterleavedUncompressedAnimation();
temp_anim->m_duration = anim_a->m_duration;
temp_anim->m_numberOfTransformTracks = ntracks;
temp_anim->m_numberOfFloatTracks = 0;
temp_anim->m_transforms.setSize(ntracks * nframes, hkQsTransform::getIdentity());
temp_anim->m_floats.setSize(temp_anim->m_numberOfFloatTracks);
temp_anim->m_annotationTracks.setSize(ntracks);
// Annotations
LOG("Merging annotations!");
hkArray<hkaAnnotationTrack>& anno_tracks = temp_anim->m_annotationTracks;
int com_npc = -1;
int com_2 = -1;
anno_tracks[0].m_trackName = "PairedRoot";
anno_tracks[1].m_trackName = "2_";
for (auto i = 0; i < anim_v->m_numberOfTransformTracks; i++)
{
auto idx = i + 2;
anno_tracks[idx] = anim_v->m_annotationTracks[i];
std::string temp_str = "2_";
if (anim_v->m_annotationTracks[i].m_trackName.cString())
{
temp_str += anim_v->m_annotationTracks[i].m_trackName.cString();
}
anno_tracks[idx].m_trackName = temp_str.c_str();
if (temp_str.find("NPC COM") != temp_str.npos)
com_2 = idx;
}
anno_tracks[anim_v->m_numberOfTransformTracks + 2].m_trackName = "NPC";
for (auto i = 0; i < anim_a->m_numberOfTransformTracks; i++)
{
auto idx = i + anim_v->m_numberOfTransformTracks + 3;
anno_tracks[idx] = anim_a->m_annotationTracks[i];
std::string name = anim_a->m_annotationTracks[i].m_trackName;
if (name.find("NPC COM") != name.npos)
com_npc = idx;
}
if ((com_2 == -1) || (com_npc == -1))
LOG("ERROR: Cannot find NPC COM bone! Abort!");
// Fill in transform
LOG("Merging transforms! If programs stops here without any output, please try a few more times.");
hkArray<hkQsTransform>& transforms = temp_anim->m_transforms;
int idx = 0;
for (int frame = 0; frame < nframes; frame++)
{
float curr_time = frame * duration / nframes;
// Paired root
idx++;
#ifndef COMPRESSED
// UNCOMPRESSED
// Victim anim
idx++;
memcpy(transforms.begin() + idx,
anim_v->m_transforms.begin() + (frame * anim_v->m_numberOfTransformTracks),
sizeof(hkQsTransform) * (anim_v->m_numberOfTransformTracks));
idx += anim_v->m_numberOfTransformTracks;
// Attacker anim
idx++;
memcpy(transforms.begin() + idx,
anim_a->m_transforms.begin() + (frame * anim_a->m_numberOfTransformTracks),
sizeof(hkQsTransform) * (anim_a->m_numberOfTransformTracks));
idx += anim_a->m_numberOfTransformTracks;
#else
// COMPRESSED
// Victim anim
idx++;
anim_v->sampleTracks(curr_time, transforms.begin() + idx, nullptr, nullptr); // Causes access violation for some reason
idx += anim_v->m_numberOfTransformTracks;
// Attacker anim
idx++;
anim_a->sampleTracks(curr_time, transforms.begin() + idx, nullptr, nullptr);
idx += anim_a->m_numberOfTransformTracks;
#endif
}
// swap
hkQsTransform init_trans_vic, init_trans_att;
hkQsTransform init_trans_vic_com, init_trans_att_com;
for (int frame = 0; frame < nframes; frame++)
{
auto frame_begin = transforms.begin() + frame * ntracks;
auto& vic_root = *(frame_begin + 1);
auto& vic_root_bone = *(frame_begin + 2);
auto& vic_com = *(frame_begin + com_2);
auto& att_root = *(frame_begin + anim_v->m_numberOfTransformTracks + 2);
auto& att_root_bone = *(frame_begin + anim_v->m_numberOfTransformTracks + 3);
auto& att_com = *(frame_begin + com_npc);
std::swap(vic_root, vic_root_bone);
std::swap(att_root, att_root_bone);
// inverse rotation?
vic_root_bone.m_rotation.setInverse(vic_root.m_rotation);
vic_com.m_rotation.setMul(vic_com.m_rotation, vic_root.m_rotation);
att_root_bone.m_rotation.setInverse(att_root.m_rotation);
att_com.m_rotation.setMul(att_com.m_rotation, att_root.m_rotation);
}
hkaSkeletonUtils::normalizeRotations(transforms.begin(), transforms.getSize());
// Shit compressor
LOG("Compressing!");
hkaSplineCompressedAnimation::TrackCompressionParams tparams;
hkaSplineCompressedAnimation::AnimationCompressionParams aparams;
tparams.m_rotationTolerance = 0.0001f;
tparams.m_translationTolerance = 0.0001f;
tparams.m_rotationQuantizationType = hkaSplineCompressedAnimation::TrackCompressionParams::THREECOMP40;
aparams.m_enableSampleSingleTracks = true;
hkRefPtr<hkaSplineCompressedAnimation> compressed_anim = new hkaSplineCompressedAnimation(*temp_anim.val(), tparams, aparams);
// - Animation Creation Done
// Binding
LOG("Create binding!");
hkRefPtr<hkaAnimationBinding> binding = new hkaAnimationBinding();
binding->m_originalSkeletonName = "PairedRoot";
binding->m_animation = compressed_anim;
binding->m_blendHint = hkaAnimationBinding::BlendHint::NORMAL;
// Anim container
LOG("Create anim container!");
hkRefPtr<hkaAnimationContainer> anim_cont = new hkaAnimationContainer();
anim_cont->m_bindings.append(&binding, 1);
anim_cont->m_animations.pushBack(binding->m_animation);
anim_cont->m_skeletons.clear();
anim_cont->m_attachments.clear();
anim_cont->m_skins.clear();
// Root & Export
hkRootLevelContainer root_cont;
root_cont.m_namedVariants.pushBack(
hkRootLevelContainer::NamedVariant("Merged Animation Container", anim_cont.val(), &anim_cont->staticClass()));
hkOstream stream(dir_out.c_str());
hkVariant root = {&root_cont, &root_cont.staticClass()};
hkPackfileWriter::Options options;
options.m_layout = hkStructureLayout::MsvcWin32LayoutRules;
hkSerializeUtil::SaveOptionBits flags = hkSerializeUtil::SAVE_DEFAULT;
hkResult res = hkSerializeUtil::savePackfile(root.m_object, *root.m_class, stream.getStreamWriter(), options, HK_NULL, flags);
// hkResult res = hkSerializeUtil::saveTagfile(root.m_object, *root.m_class, stream.getStreamWriter(), HK_NULL, flags);
if (res == HK_SUCCESS)
{
LOG("Successfully exported!");
}
else
{
LOG("Failed to save export file!");
}
hkOstream stream_xml((dir_out + ".xml").c_str());
hkXmlPackfileWriter writer;
writer.setContents(&root_cont, *root.m_class);
res = writer.save(stream_xml.getStreamWriter(), options);
if (res == HK_SUCCESS)
{
LOG("XML HKX file Successfully exported!");
}
else
{
LOG("Failed to save export file!");
}
}
hkResource* loadData(std::string path)
{
hkSerializeUtil::ErrorDetails loadError;
hkResource* data = hkSerializeUtil::load(path.c_str(), &loadError);
if (!data)
{
LOG("Could not load " << path << std::endl
<< " Error: " << loadError.defaultMessage.cString());
return nullptr;
}
hkRootLevelContainer* container = data->getContents<hkRootLevelContainer>();
if (container == HK_NULL)
{
LOG("Could not load root level object for" << path);
return nullptr;
}
hkaAnimationContainer* ac = reinterpret_cast<hkaAnimationContainer*>(container->findObjectByType(hkaAnimationContainerClass.getName()));
if (!ac || (ac->m_animations.getSize() == 0))
{
LOG("No animation loaded for " << path);
return nullptr;
}
if (ac->m_animations.getSize() > 1)
{
LOG("Multiple animations found in " << path << "! Abort!");
return nullptr;
}
return data;
}
bool checkSame(hkaAnimation* a, hkaAnimation* b)
{
return compareFloat(a->m_duration, b->m_duration);
}