P2~P7 리노컷 캐스트 원화·리그와 세션 활성화

- 추천 원화(P2 b·P3 v2b·P4 v2b·P5 v2a·P6 v2b·P7 v2b)로 얼굴 없는 기본형·몸 원본 codex 편집, 정렬·정규화 후 공통 파이프라인 실행
- 게시 자산 apps/web/public/avatar/v3/p2~p7, 리그 rigs/p2Rig~p7Rig
- 세션 레지스트리 P1~P7 전원 활성, Lab은 생성된 리그 자동 수집
- P3·P6 기본 눈 감김을 줄여 중립 렌더를 원화 눈매에 맞춤
- 표정 E2E를 v3 렌더러 기대로 갱신(검사 의도 유지)
This commit is contained in:
Yun Chan 2026-10-01 22:38:45 +09:00
parent 98577cf4ae
commit ded312acdc
258 changed files with 18463 additions and 32 deletions

View file

@ -8,7 +8,7 @@ import { useEffect, useMemo, useRef, useState, type ChangeEvent } from "react";
import DebugFace from "../components/avatar/v3/DebugFace";
import LinocutAvatar from "../components/avatar/v3/LinocutAvatar";
import LinocutMotif from "../components/avatar/v3/LinocutMotif";
import { P1_LINOCUT_RIG } from "../components/avatar/v3/rigs/p1Rig";
import { labLinocutRigs } from "../components/avatar/v3/rigs";
import type { LinocutRig } from "../components/avatar/v3/linocutRig";
import { CHANNEL_IDS } from "../components/avatar/engine/channels";
import { createAvatarEngine, type AvatarEngine, type DebugSnapshot } from "../components/avatar/engine/engine";
@ -30,8 +30,10 @@ const PERSONA_OPTIONS: Array<{ code: string; label: string }> = [
{ code: "P7", label: "P7 도현" },
];
/** 리노컷 리그가 있는 페르소나만. 없는 페르소나는 debug 렌더러를 강제한다. */
const LINOCUT_RIGS: Partial<Record<string, LinocutRig>> = { P1: P1_LINOCUT_RIG };
/** 리노컷 리그가 있는 페르소나만. 없는 페르소나는 debug 렌더러를 강제한다.
공통 파이프라인이 만든 pNRig.ts를 전부 모은 값이라, 새 페르소나가 빌드되는
즉시(재빌드 없이) 이 Lab에서 보인다 — v3/rigs/index.ts labLinocutRigs() 참고. */
const LINOCUT_RIGS: Partial<Record<string, LinocutRig>> = labLinocutRigs();
type RendererMode = "linocut" | "debug";
type FeedbackMode = "immersive" | "ambient" | "coached";
@ -80,11 +82,19 @@ function seedFromQuery(): number {
return Number.isFinite(parsed) ? parsed : 1;
}
/** ?persona=P3 같은 쿼리로 처음 고를 페르소나를 정한다(캡처 도구용). 모르는 코드거나
없으면 기본 P1. */
function personaFromQuery(): string {
const raw = new URLSearchParams(window.location.search).get("persona");
const upper = raw?.toUpperCase() ?? "";
return PERSONA_OPTIONS.some((p) => p.code === upper) ? upper : "P1";
}
export default function AvatarLab() {
const seed = useMemo(seedFromQuery, []);
const clock = useRef<Clock>(createClock()).current;
const [personaCode, setPersonaCode] = useState("P1");
const [personaCode, setPersonaCode] = useState(personaFromQuery);
const [avatarState, setAvatarState] = useState<AvatarState>("idle");
const [openness, setOpenness] = useState(0.4);
const [reducedMotion, setReducedMotion] = useState(false);