UNPKG

mdx-m3-viewer

Version:

A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.

165 lines (129 loc) 5.13 kB
import { Animation } from '../../parsers/mdlx/animations'; import SanityTestData from './data'; import EventObject from '../../parsers/mdlx/eventobject'; function getSequenceFromFrame(data: SanityTestData, frame: number, globalSequenceId: number) { if (globalSequenceId === -1) { let sequences = data.model.sequences; for (let i = 0, l = sequences.length; i < l; i++) { let interval = sequences[i].interval; if (frame >= interval[0] && frame <= interval[1]) { return i; } } } else { let end = data.model.globalSequences[globalSequenceId]; if (frame >= 0 && frame <= end) { return globalSequenceId; } } return -1; } function seprateTracks(data: SanityTestData, object: Animation | EventObject, separated: number[][]) { let frames; let globalSequenceId = object.globalSequenceId; if (object instanceof Animation) { frames = object.frames; } else { frames = object.tracks; } for (let i = 0, l = frames.length; i < l; i++) { let frame = frames[i]; data.assertWarning(frame >= 0, `Track ${i}: Negative frame`); let 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}: Frame ${frame} is not in any sequence`); } else { data.addUnused(`Track ${i}: Frame ${frame} is not in global sequence ${globalSequenceId}`); } } } } } function getSequenceName(data: SanityTestData, sequence: number, globalSequenceId: number) { if (globalSequenceId === -1) { return `sequence "${data.model.sequences[sequence].name}"`; } else { return `global sequence ${globalSequenceId}`; } } const EPSILON = 0.001; function compareValues(a: Uint32Array | Float32Array, b: Uint32Array | Float32Array, c: Uint32Array | Float32Array) { for (let i = 0, l = a.length; i < l; i++) { let ai = a[i]; if (Math.abs(ai - b[i]) > EPSILON || Math.abs(ai - c[i]) > EPSILON) { return false; } } return true; } function testSequenceTracks(data: SanityTestData, object: Animation | EventObject, indices: number[], sequence: number) { let frames; let interpolationType = 0; let globalSequenceId = object.globalSequenceId; if (object instanceof Animation) { frames = object.frames; interpolationType = object.interpolationType; } else { frames = object.tracks; } let start = 0; let end; if (globalSequenceId === -1) { let interval = data.model.sequences[sequence].interval; start = interval[0]; end = interval[1]; } else { end = data.model.globalSequences[globalSequenceId]; } let first = frames[indices[0]]; let last = frames[indices[indices.length - 1]]; if (object instanceof Animation) { // Missing the opening/closing tracks for a specific sequence can sometimes cause weird animations in the game. // Generally speaking these warnings can be ignored though. data.assertWarning(first === start, `No opening track for ${getSequenceName(data, sequence, globalSequenceId)} at frame ${start}`); // If there is no interpolation, then it doesn't matter if there's a closing track or not. data.assertWarning(last === end || interpolationType === 0, `No closing track for ${getSequenceName(data, sequence, globalSequenceId)} at frame ${end}`); // Check for consecutive tracks with the same values. if (indices.length > 2) { let values = object.values; let a = values[indices[0]]; let b = values[indices[1]]; for (let i = 2, l = indices.length; i < l; i++) { let c = values[indices[i]]; if (compareValues(a, b, c)) { let index = indices[i - 1]; data.addUnused(`Track ${index} at frame ${frames[index]} has the same-ish value as tracks ${index - 1} and ${index + 1}`); } a = b; b = c; } } } } export default function testTracks(data: SanityTestData, object: Animation | EventObject) { let globalSequenceId = object.globalSequenceId; let separated: number[][] = []; if (globalSequenceId === -1) { let sequences = data.model.sequences; data.assertWarning(frames.length === 0 || sequences.length !== 0, 'Tracks used without sequences'); for (let i = 0, l = sequences.length; i < l; i++) { separated.push([]); } } else if (globalSequenceId >= 0 && globalSequenceId < data.model.globalSequences.length) { separated[globalSequenceId] = []; } seprateTracks(data, object, separated); for (let i = 0, l = separated.length; i < l; i++) { let indices = separated[i]; if (indices && indices.length > 1) { testSequenceTracks(data, object, indices, i); } } }