import assert from "node:assert/strict"; import { describe, it } from "node:test"; import * as THREE from "three"; import { CROW_METRICS, DOG_METRICS, HUMANOID_METRICS, buildCrow, buildDog, buildHumanoid, cloneCrow, cloneDog, cloneHumanoid, disposeCrow, disposeDog, disposeHumanoid, poseCrowFlight, poseDogAttention, poseDogWalk, poseHumanoid, } from "../assets/actors/index.ts"; function meshes(root: THREE.Object3D): THREE.Mesh[] { const result: THREE.Mesh[] = []; root.traverse((object) => { if (object instanceof THREE.Mesh) result.push(object); }); return result; } function assertSharedResources(source: THREE.Group, clone: THREE.Group): void { const a = meshes(source); const b = meshes(clone); assert.equal(b.length, a.length); for (let i = 0; i < a.length; i++) { assert.equal(b[i]!.geometry, a[i]!.geometry); assert.equal(b[i]!.material, a[i]!.material); } } describe("procedural actor assets", () => { it("builds a metre-scale customizable humanoid with a front face surface", () => { const rig = buildHumanoid({ bodyShape: "broad", outfitColor: 0x224466 }); const box = new THREE.Box3().setFromObject(rig.root); const size = box.getSize(new THREE.Vector3()); assert.ok(Math.abs(size.y - HUMANOID_METRICS.height) < 0.08, `height ${size.y}`); assert.ok(box.min.y > -0.015, `floor ${box.min.y}`); assert.equal(rig.root.userData.forwardAxis, "-Z"); assert.ok(rig.face.getWorldPosition(new THREE.Vector3()).z < 0); const clone = cloneHumanoid(rig); assertSharedResources(rig.root, clone.root); poseHumanoid(clone, { walkPhase: Math.PI / 2, stride: 0.5, headYaw: 0.3 }); assert.notEqual(clone.joints.hipLeft.rotation.x, rig.joints.hipLeft.rotation.x); assert.equal(clone.joints.head.rotation.y, 0.3); assert.equal(clone.ownsMaterials, false); disposeHumanoid(rig); }); it("builds an anonymous office dog with independent attention and gait joints", () => { const rig = buildDog(); const box = new THREE.Box3().setFromObject(rig.root); const size = box.getSize(new THREE.Vector3()); assert.ok(size.y <= DOG_METRICS.height + 0.08, `height ${size.y}`); assert.ok(size.z >= DOG_METRICS.length - 0.1, `length ${size.z}`); assert.equal(rig.root.userData.actorType, "anonymous-dog"); for (const side of ["left", "right"] as const) { const eye = rig.root.getObjectByName(`dog.eye.${side}`); const catchlight = rig.root.getObjectByName(`dog.eye-catchlight.${side}`); assert.ok(eye instanceof THREE.Mesh); assert.ok(catchlight instanceof THREE.Mesh); assert.ok(eye.getWorldPosition(new THREE.Vector3()).z < 0, `${side} eye faces -Z`); assert.ok(catchlight.getWorldPosition(new THREE.Vector3()).z < eye.getWorldPosition(new THREE.Vector3()).z); } const clone = cloneDog(rig); assertSharedResources(rig.root, clone.root); poseDogWalk(clone, Math.PI / 2); poseDogAttention(clone, 0.4, Math.PI / 2); assert.notEqual(clone.joints.legFrontLeft.rotation.x, rig.joints.legFrontLeft.rotation.x); assert.equal(clone.joints.head.rotation.y, 0.4); assert.notEqual(clone.joints.tail.rotation.z, 0); disposeDog(rig); }); it("builds a Tera crow with mirrored independent wing joints", () => { const rig = buildCrow(); const box = new THREE.Box3().setFromObject(rig.root); assert.ok(box.max.y <= CROW_METRICS.perchedHeight + 0.1, `height ${box.max.y}`); assert.equal(rig.root.userData.actorType, "anonymous-crow"); const beak = rig.root.getObjectByName("crow.beak"); assert.ok(beak); assert.ok(beak.getWorldPosition(new THREE.Vector3()).z < 0); const clone = cloneCrow(rig); assertSharedResources(rig.root, clone.root); poseCrowFlight(clone, Math.PI / 2, 1); assert.equal(clone.joints.wingLeft.rotation.z, -clone.joints.wingRight.rotation.z); assert.notEqual(clone.joints.wingLeft.rotation.z, rig.joints.wingLeft.rotation.z); disposeCrow(rig); }); it("never disposes caller-owned materials unless explicitly requested", () => { const source = buildCrow(); const shared = meshes(source.root)[0]!.material as THREE.Material; let disposals = 0; shared.addEventListener("dispose", () => disposals++); const external = { feather: shared, sheen: shared, beak: shared, eye: shared, foot: shared, }; const rig = buildCrow({ materials: external }); disposeCrow(rig); assert.equal(disposals, 0); disposeCrow(source); assert.equal(disposals, 1); }); });