California gets roads, traffic, and a car to follow
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
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");
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user