SOURCE / PINNED RELEASE
Made of little things.
Mini Samurai
- Release
- a7beb798adbc…
- Author-recorded commit
- 4f97be1515b8…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-3b28f0efc5b
Archive hash verified: 401ec9347ab8f8cd…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Development-only pose viewer: ?poses=<clip>&t=<seconds>. Renders a samurai from three angles.
import * as THREE from 'three';
import { Rig } from './game/rig.js';
import * as A from './game/anims.js';
import { copyPose, emptyPose, type Pose } from './game/pose.js';
import { KATANA, itemById } from './game/items.js';
export function runPoseViewer(params: URLSearchParams): void {
document.body.innerHTML = '';
const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
renderer.setSize(1200, 420);
renderer.shadowMap.enabled = true;
document.body.append(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xd9cbb4);
scene.add(new THREE.HemisphereLight(0xfff4e0, 0x6b5a48, 1.2));
const sun = new THREE.DirectionalLight(0xffffff, 2);
sun.position.set(2, 4, 3);
sun.castShadow = true;
scene.add(sun);
const floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({ color: 0xb08a5a }));
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
const name = params.get('poses') ?? 'stance';
const t = Number(params.get('t') ?? '0');
const item = params.get('item');
const def = item ? itemById(item) : KATANA;
const pose = emptyPose();
const g: A.Grip = params.get('grip') === 'fists' ? 'fists' : def.twoHanded ? 'katana' : 'one';
const set = g === 'fists' ? A.FIST_CLIPS : g === 'one' ? A.ONE_CLIPS : A.KATANA_CLIPS;
const sample = (p: Pose): void => {
const clips: Record<string, () => void> = {
stance: () => A.locomotion(p, { phase: 0, speed: 0, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
walk: () => A.locomotion(p, { phase: t, speed: 1.5, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
run: () => A.locomotion(p, { phase: t, speed: 5, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 1 }),
crouch: () => A.locomotion(p, { phase: t, speed: 0, crouch: 1, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
air: () => A.airPose(p, Number(params.get('vy') ?? '4'), g, t),
hang: () => A.hangPose(p, 1.0, 0.2, t, 0, 0),
climb: () => A.climbPose(p, t, 1.0 - t * 1.0, 0.2 - t * 0.4),
block: () => copyPose(p, A.blockPose(g)),
slide: () => Object.assign(p, A.SLIDE),
l1: () => set.light[0]!.sample(t, p),
l2: () => set.light[1]!.sample(t, p),
l3: () => set.light[2]!.sample(t, p),
heavy: () => set.heavy.sample(t, p),
dash: () => set.dash.sample(t, p),
low: () => set.low.sample(t, p),
airatk: () => set.air.sample(t, p),
airland: () => set.airLand.sample(t, p),
knock: () => A.KNOCKDOWN.sample(t, p),
getup: () => A.GETUP(A.STANCE_KATANA).sample(t, p),
pickup: () => A.PICKUP(A.STANCE_FISTS).sample(t, p),
victory: () => A.VICTORY.sample(t, p),
flinch: () => A.FLINCH(A.STANCE_KATANA, 1).sample(t, p),
};
(clips[name] ?? clips.stance!)();
};
const views = [
{ x: 0, z: 2.6, label: 'front' },
{ x: 2.6, z: 0, label: 'side' },
{ x: -1.6, z: -1.9, label: 'back' },
];
const rigs = views.map(() => {
const rig = new Rig({ armor: 0xc8372d, trim: 0xe3b23c, cloth: 0x2b2d42, ribbon: 0xf2efe6 }, scene);
if (g !== 'fists') rig.setWeapon(def.build(), def.style === 'katana' ? -0.12 : 0.18);
scene.add(rig.root);
return rig;
});
sample(pose);
rigs.forEach((rig, i) => {
rig.root.position.set(i * 6 - 6, 0, 0);
rig.apply(pose, 0, new THREE.Vector3());
rig.resetSecondary();
for (let k = 0; k < 30; k++) rig.apply(pose, 1 / 60, new THREE.Vector3());
});
const w = 400;
renderer.setScissorTest(true);
views.forEach((view, i) => {
const cam = new THREE.PerspectiveCamera(35, 1 / 1.05, 0.05, 50);
const cx = i * 6 - 6;
cam.position.set(cx + view.x, 0.75, view.z);
cam.lookAt(cx, 0.55, 0);
renderer.setViewport(i * w, 0, w, 420);
renderer.setScissor(i * w, 0, w, 420);
renderer.render(scene, cam);
});
document.title = 'ready';
}
