@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
80 lines (71 loc) • 3.63 kB
JavaScript
import { quat_decode_from_uint32 } from "../../../core/geom/3d/quaternion/quat_decode_from_uint32.js";
import { quat_encode_to_uint32 } from "../../../core/geom/3d/quaternion/quat_encode_to_uint32.js";
import { v3_binary_equality_decode } from "../../../core/geom/vec3/serialization/v3_binary_equality_decode.js";
import { v3_binary_equality_encode } from "../../../core/geom/vec3/serialization/v3_binary_equality_encode.js";
import { BinaryInterpolationAdapter } from "../sim/BinaryInterpolationAdapter.js";
/**
* Module-level scratch. Layout: `[aq.xyzw, bq.xyzw, as.xyz, bs.xyz]`.
* Safe because `interpolate` is synchronous and non-reentrant.
* @type {Float32Array}
*/
const scratch = new Float32Array(16);
/**
* Linear-blend interpolation for {@link Transform}, matching the wire
* layout of {@link TransformReplicationAdapter}: 3×Float32 position +
* uint32 packed quaternion + equality-encoded scale.
*
* Position lerps componentwise; rotation nlerps with shortest-path
* correction; scale decodes/lerps/re-encodes (output is still valid
* equality-encoded).
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class TransformInterpolationAdapter extends BinaryInterpolationAdapter {
/** @inheritdoc */
interpolate(out_buffer, source, first_offset, second_offset, t) {
// ---- Decode sample A ----
source.position = first_offset;
const ax = source.readFloat32();
const ay = source.readFloat32();
const az = source.readFloat32();
const a_quat_packed = source.readUint32();
quat_decode_from_uint32(scratch, 0, a_quat_packed); // → scratch[0..3]
v3_binary_equality_decode(source, scratch, 8); // → scratch[8..10]
// ---- Decode sample B ----
source.position = second_offset;
const bx = source.readFloat32();
const by = source.readFloat32();
const bz = source.readFloat32();
const b_quat_packed = source.readUint32();
quat_decode_from_uint32(scratch, 4, b_quat_packed); // → scratch[4..7]
v3_binary_equality_decode(source, scratch, 11); // → scratch[11..13]
// ---- Position lerp ----
const out_x = ax + (bx - ax) * t;
const out_y = ay + (by - ay) * t;
const out_z = az + (bz - az) * t;
// ---- Rotation nlerp with shortest-path correction ----
const aqx = scratch[0], aqy = scratch[1], aqz = scratch[2], aqw = scratch[3];
let bqx = scratch[4], bqy = scratch[5], bqz = scratch[6], bqw = scratch[7];
if (aqx * bqx + aqy * bqy + aqz * bqz + aqw * bqw < 0) {
bqx = -bqx; bqy = -bqy; bqz = -bqz; bqw = -bqw;
}
let qx = aqx + (bqx - aqx) * t;
let qy = aqy + (bqy - aqy) * t;
let qz = aqz + (bqz - aqz) * t;
let qw = aqw + (bqw - aqw) * t;
const len2 = qx * qx + qy * qy + qz * qz + qw * qw;
const inv_len = len2 > 0 ? 1 / Math.sqrt(len2) : 1;
qx *= inv_len; qy *= inv_len; qz *= inv_len; qw *= inv_len;
// ---- Scale lerp ----
const out_sx = scratch[8] + (scratch[11] - scratch[8]) * t;
const out_sy = scratch[9] + (scratch[12] - scratch[9]) * t;
const out_sz = scratch[10] + (scratch[13] - scratch[10]) * t;
// ---- Re-encode to TransformReplicationAdapter wire layout ----
out_buffer.writeFloat32(out_x);
out_buffer.writeFloat32(out_y);
out_buffer.writeFloat32(out_z);
out_buffer.writeUint32(quat_encode_to_uint32(qx, qy, qz, qw));
v3_binary_equality_encode(out_buffer, out_sx, out_sy, out_sz);
}
}