Release Healer Man 0.1.30

This commit is contained in:
phenom committed 2026-09-11 18:49:33 -04:00
1 parent 544c1bee91
commit 7d7fd567f0
845 files changed
+2040 -87

No files matched your search

+45 -1
View File
@@ -7,6 +7,17 @@ const SKIN_SUBMESH_SIZE = 48;
const SKIN_TEXTURE_UNIT_SIZE = 24;
const VALUE_TYPES = {
floatQuaternion: {
byteLength: 16,
read: (buffer, offset) => [0, 4, 8, 12].map((component) => buffer.readFloatLE(offset + component)),
},
quaternion: {
byteLength: 8,
read: (buffer, offset) => [0, 2, 4, 6].map((component) => {
const value = buffer.readInt16LE(offset + component);
return (value < 0 ? value + 32768 : value - 32767) / 32767;
}),
},
uint8x4: {
byteLength: 4,
read: (buffer, offset) => Array.from(buffer.subarray(offset, offset + 4)),
@@ -211,6 +222,39 @@ function decodeTextureWeights(buffer, name) {
));
}
/** Source-space bone and UV animation, shared by meshes, particles and ribbons. */
export function decodeWotlkM2Animation(buffer, name = "M2") {
const bones = readArrayHeader(buffer, 44, `${name} bones`);
if (bones.count) assertRange(buffer, bones.offset, bones.count * 88, `${name} bones`);
const transforms = readArrayHeader(buffer, 96, `${name} texture transforms`);
if (transforms.count) assertRange(buffer, transforms.offset, transforms.count * 60, `${name} texture transforms`);
return {
globalSequences: decodeArray(buffer, 20, VALUE_TYPES.uint32, `${name} global sequences`).values,
bones: Array.from({ length: bones.count }, (_, index) => {
const offset = bones.offset + index * 88;
const parent = buffer.readInt16LE(offset + 8);
if (parent >= index || parent < -1) throw new Error(`${name} bone ${index} has an invalid parent.`);
return {
parent,
flags: buffer.readUInt32LE(offset + 4),
pivot: vector3(buffer, offset + 76),
translation: decodeTrack(buffer, offset + 16, VALUE_TYPES.vector3, `${name} bone ${index} translation`),
rotation: decodeTrack(buffer, offset + 36, VALUE_TYPES.quaternion, `${name} bone ${index} rotation`),
scale: decodeTrack(buffer, offset + 56, VALUE_TYPES.vector3, `${name} bone ${index} scale`),
};
}),
textureTransforms: Array.from({ length: transforms.count }, (_, index) => {
const offset = transforms.offset + index * 60;
return {
translation: decodeTrack(buffer, offset, VALUE_TYPES.vector3, `${name} UV ${index} translation`),
rotation: decodeTrack(buffer, offset + 20, VALUE_TYPES.floatQuaternion, `${name} UV ${index} rotation`),
scale: decodeTrack(buffer, offset + 40, VALUE_TYPES.vector3, `${name} UV ${index} scale`),
};
}),
materials: decodeMaterials(buffer, name),
};
}
function decodeSkinSubmeshes(buffer, count, offset, triangleCount, vertexLookupCount, name) {
const byteLength = count * SKIN_SUBMESH_SIZE;
if (!Number.isSafeInteger(byteLength)) throw new Error(`${name} submeshes are too large to decode.`);
@@ -303,7 +347,7 @@ function decodeSkinTextureUnits(buffer, count, offset, context, name) {
textureWeightIndices: lookupSlice(
context.lookups.textureWeights,
textureWeightComboIndex,
textureCount,
1,
`${name} texture unit ${index} texture weights`,
),
textureTransformIndices: lookupSlice(