#pragma once
#include <string>
#include <vector>
#include <whiteout/models/mdx/structures.h>
#include <whiteout/models/mdx/types.h>
#include <whiteout/models/wem/anim/clip.h>
#include <whiteout/models/wem/document.h>
namespace whiteout {
namespace models {
namespace wem {
namespace mdx_slice {
constexpr u32 kNoGlobalSequence = mdx::Track<f32>::kNoGlobalSequence;
constexpr f32 Seconds(f32 milliseconds) {
return milliseconds / 1000.0f;
}
template <class T>
struct ValueTrait;
template <>
struct ValueTrait<f32> {
static constexpr geom::AttrType kType = geom::AttrType::F32;
};
template <>
struct ValueTrait<u32> {
static constexpr geom::AttrType kType = geom::AttrType::U32;
};
template <>
struct ValueTrait<Vector3f> {
static constexpr geom::AttrType kType = geom::AttrType::F32x3;
};
template <>
struct ValueTrait<Quaternion> {
static constexpr geom::AttrType kType = geom::AttrType::Quat;
};
inline Interpolation InterpOf(mdx::InterpolationType type, geom::AttrType valueType) {
switch (type) {
case mdx::InterpolationType::None:
return Interpolation::Step;
case mdx::InterpolationType::Linear:
return valueType == geom::AttrType::Quat ? Interpolation::Slerp : Interpolation::Linear;
case mdx::InterpolationType::Hermite:
return Interpolation::Hermite;
case mdx::InterpolationType::Bezier:
return Interpolation::Bezier;
}
return Interpolation::Linear;
}
struct Window {
f32 start = 0; f32 end = 0;
u32 clip = kInvalidIndex;
};
template <class T>
bool WellFormed(const mdx::Track<T>& track) {
const u32 perKey = ValuesPerKey(InterpOf(track.interpolationType, ValueTrait<T>::kType));
return track.keys_data.size() >= track.timestamps.size() * perKey;
}
template <class T>
void AppendKey(const mdx::Track<T>& track, std::size_t key, u32 perKey, std::vector<u8>& values) {
const std::size_t base = key * perKey;
for (u32 v = 0; v < perKey; ++v) {
const T& value = track.keys_data[base + v];
const u8* bytes = reinterpret_cast<const u8*>(&value);
values.insert(values.end(), bytes, bytes + sizeof(T));
}
}
template <class T>
bool SliceWindow(const mdx::Track<T>& track, u32 channel, const Window& window, SubTrack& out) {
const Interpolation interp = InterpOf(track.interpolationType, ValueTrait<T>::kType);
const u32 perKey = ValuesPerKey(interp);
const std::vector<u32>& times = track.timestamps;
std::size_t first = 0;
while (first < times.size() && static_cast<f32>(times[first]) < window.start) {
++first;
}
std::size_t last = first;
while (last < times.size() && static_cast<f32>(times[last]) <= window.end) {
++last;
}
const std::size_t lo = first > 0 ? first - 1 : first;
const std::size_t hi = last < times.size() ? last + 1 : last;
if (lo >= hi) {
return false;
}
out = SubTrack{};
out.channel = channel;
out.interp = interp;
for (std::size_t k = lo; k < hi; ++k) {
out.times.push_back(Seconds(static_cast<f32>(times[k]) - window.start));
AppendKey(track, k, perKey, out.values);
}
return true;
}
template <class T>
SubTrack WholeTrack(const mdx::Track<T>& track, u32 channel) {
const Interpolation interp = InterpOf(track.interpolationType, ValueTrait<T>::kType);
const u32 perKey = ValuesPerKey(interp);
SubTrack out;
out.channel = channel;
out.interp = interp;
for (std::size_t k = 0; k < track.timestamps.size(); ++k) {
out.times.push_back(Seconds(static_cast<f32>(track.timestamps[k])));
AppendKey(track, k, perKey, out.values);
}
return out;
}
struct ClipCut {
u32 clip = kInvalidIndex;
SubTrack track;
};
template <class T>
std::vector<ClipCut> CutForClips(const mdx::Track<T>& track, const Document& document, u32 model,
u32 channel = 0) {
std::vector<ClipCut> cuts;
if (!track.isUsed || track.timestamps.empty() || !WellFormed(track)) {
return cuts;
}
for (std::size_t c = 0; c < document.clips.size(); ++c) {
const Clip& clip = document.clips[c];
if (clip.model != model) {
continue;
}
if (track.globalSequenceId != kNoGlobalSequence) {
if (clip.native.value("globalSequenceId", -1) ==
static_cast<i64>(track.globalSequenceId)) {
cuts.push_back({static_cast<u32>(c), WholeTrack(track, channel)});
}
continue;
}
const i64 start = clip.native.value("intervalStart", -1);
const i64 end = clip.native.value("intervalEnd", -1);
if (start < 0 || end < start) {
continue;
}
Window window;
window.start = static_cast<f32>(start);
window.end = static_cast<f32>(end);
window.clip = static_cast<u32>(c);
ClipCut cut;
cut.clip = window.clip;
if (SliceWindow(track, channel, window, cut.track)) {
cuts.push_back(std::move(cut));
}
}
return cuts;
}
} } } }