mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
165 lines • 6.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.areValuesEqual = exports.getValuesDiff = void 0;
const animations_1 = require("../../../parsers/mdlx/animations");
const utils_1 = require("./utils");
function getSequenceFromFrame(data, frame, globalSequenceId) {
if (globalSequenceId === -1) {
const sequences = data.model.sequences;
for (let i = 0, l = sequences.length; i < l; i++) {
const interval = sequences[i].interval;
if (frame >= interval[0] && frame <= interval[1]) {
return i;
}
}
}
else {
const end = data.model.globalSequences[globalSequenceId];
if (frame >= 0 && frame <= end) {
return globalSequenceId;
}
}
return -1;
}
function seprateTracks(data, frames, globalSequenceId, separated) {
let lastFrame = -Infinity;
for (let i = 0, l = frames.length; i < l; i++) {
const frame = frames[i];
data.assertWarning(frame >= 0, `Track ${i} has a negative frame ${frame}`);
if (frame === lastFrame) {
data.addWarning(`Track ${i} has the same frame ${frame} as track ${i - 1}`);
}
else if (frame < lastFrame) {
data.addSevere(`Track ${i} at frame ${frame} is lower than the track before it at ${lastFrame}`);
}
const sequence = getSequenceFromFrame(data, frame, globalSequenceId);
if (sequence !== -1) {
separated[sequence].push(i);
}
else {
// Frame 0 seems to be special.
// Or maybe it's the first keyframe regardless of the frame.
// Who knows.
if (frame !== 0 && frames.length > 1) {
if (globalSequenceId === -1) {
data.addUnused(`Track ${i} at frame ${frame} is not in any sequence`);
}
else {
data.addUnused(`Track ${i} at frame ${frame} is not in global sequence ${globalSequenceId}`);
}
}
}
lastFrame = frame;
}
}
function getSequenceName(data, sequence, globalSequenceId) {
if (globalSequenceId === -1) {
return `sequence "${data.model.sequences[sequence].name}"`;
}
else {
return `global sequence ${globalSequenceId}`;
}
}
const EPSILON = 0.001;
function getValuesDiff(a, b, c) {
let d = 0;
for (let i = 0, l = a.length; i < l; i++) {
const ai = a[i];
const d1 = Math.abs(ai - b[i]);
const d2 = Math.abs(ai - c[i]);
if (d1 > d) {
d = d1;
}
if (d2 > d) {
d = d2;
}
}
return d;
}
exports.getValuesDiff = getValuesDiff;
function areValuesEqual(a, b) {
for (let i = 0, l = a.length; i < l; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
exports.areValuesEqual = areValuesEqual;
function testSequenceTracks(data, indices, sequence, object, frames) {
const { globalSequenceId, interpolationType, values } = object;
let start;
if (globalSequenceId === -1) {
start = data.model.sequences[sequence].interval[0];
}
else {
start = 0;
}
const firstIndex = indices[0];
const lastIndex = indices[indices.length - 1];
const firstFrame = frames[firstIndex];
// Missing the opening track for a specific sequence can cause bugged warping with negative interpolations.
if (interpolationType !== 0 /* DontInterp */ && firstFrame !== start) {
const firstValue = values[firstIndex];
const lastValue = values[lastIndex];
// If the first and last values are equal, even though there's warping, it won't do anything.
if (!areValuesEqual(firstValue, lastValue)) {
data.addSevere(`Missing opening track for ${getSequenceName(data, sequence, globalSequenceId)} at frame ${start} where it is needed`);
}
}
// Check for consecutive tracks with the same values.
if (indices.length > 2) {
let a = values[indices[0]];
let b = values[indices[1]];
for (let i = 2, l = indices.length; i < l; i++) {
const c = values[indices[i]];
const index = indices[i - 1];
const d = getValuesDiff(a, b, c);
if (d === 0) {
data.addUnused(`Track ${index} at frame ${frames[index]} has exactly the same value as tracks ${index - 1} and ${index + 1}`);
}
else if (d < EPSILON) {
data.addUnused(`Track ${index} at frame ${frames[index]} has roughly the same value as tracks ${index - 1} and ${index + 1}`);
}
a = b;
b = c;
}
}
}
function testTracks(data, object) {
let framesOrTracks;
if (object instanceof animations_1.Animation) {
data.assertWarning(object.frames.length > 0, 'Zero tracks');
framesOrTracks = object.frames;
}
else {
data.assertError(object.tracks.length > 0, 'Zero tracks');
framesOrTracks = object.tracks;
}
const globalSequenceId = object.globalSequenceId;
const separated = [];
if (globalSequenceId === -1) {
data.assertWarning(data.model.sequences.length > 0, 'This animation exists, but the model has no sequences');
for (let i = 0, l = data.model.sequences.length; i < l; i++) {
separated.push([]);
}
}
else if ((0, utils_1.testReference)(data, data.model.globalSequences, globalSequenceId, 'global sequence')) {
separated[globalSequenceId] = [];
}
else {
return;
}
seprateTracks(data, framesOrTracks, globalSequenceId, separated);
// Since event objects work on the concept of notes, where a keyframe denotes emission, check just animation keyframe values.
if (object instanceof animations_1.Animation) {
for (let i = 0, l = separated.length; i < l; i++) {
const indices = separated[i];
if (indices && indices.length > 1) {
testSequenceTracks(data, indices, i, object, framesOrTracks);
}
}
}
}
exports.default = testTracks;
//# sourceMappingURL=tracks.js.map