UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

80 lines (71 loc) 3.63 kB
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); } }