2920 lines
60 KiB
HTML
2920 lines
60 KiB
HTML
<!doctype html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>LeeSori Reaction Sequencer — reactions.html</title>
|
||
<style>
|
||
:root { color-scheme: dark; }
|
||
body { margin:0; font-family: system-ui, "Segoe UI", sans-serif; background:#0e1014; color:#e6e8ee; }
|
||
header { padding:10px 16px; border-bottom:1px solid #232733; display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
|
||
h1 { font-size:15px; margin:0; font-weight:600; color:#9ff0e0; }
|
||
.wrap { display:flex; gap:16px; padding:16px; flex-wrap:wrap; }
|
||
.stage { background:
|
||
linear-gradient(45deg,#1a1d24 25%,transparent 25%,transparent 75%,#1a1d24 75%),
|
||
linear-gradient(45deg,#1a1d24 25%,#15171d 25%,#15171d 75%,#1a1d24 75%);
|
||
background-size:24px 24px; background-position:0 0,12px 12px; border:1px solid #232733; border-radius:8px; }
|
||
canvas { display:block; }
|
||
.panel { min-width:250px; max-width:360px; font-size:13px; line-height:1.6; }
|
||
.row { display:flex; align-items:center; gap:8px; margin:6px 0; flex-wrap:wrap; }
|
||
button { background:#1d2a3a; color:#cfe6ff; border:1px solid #2c4a63; border-radius:6px; padding:6px 12px; cursor:pointer; font-size:13px; }
|
||
button:hover { background:#26405a; }
|
||
button.on { background:#2e9e8b; color:#04231d; border-color:#2e9e8b; }
|
||
button.trig { background:#2a2536; border-color:#4a3d63; color:#e2d6ff; }
|
||
button.trig:hover { background:#39304d; }
|
||
label.file { display:inline-block; background:#232733; border:1px solid #37414f; border-radius:6px; padding:6px 10px; cursor:pointer; }
|
||
input[type=file]{ display:none; } input[type=range]{ width:130px; }
|
||
.muted { color:#8a93a6; font-size:12px; } code { background:#191c23; padding:1px 5px; border-radius:4px; color:#9ff0e0; }
|
||
.tag { font-size:11px; color:#9ff0e0; background:#12241f; border:1px solid #1f4238; border-radius:10px; padding:1px 8px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>이소리 Reaction Sequencer — <span id="stateName">dance_idle</span> <span class="muted">(520×900 / file://)</span></h1>
|
||
<div class="row">
|
||
<button id="playBtn" class="on">⏸ 일시정지</button>
|
||
<span class="muted">속도</span><input type="range" id="speed" min="0" max="2" step="0.05" value="1"><span id="speedV" class="muted">1.0×</span>
|
||
<button id="capBtn" class="on">💬 말풍선</button>
|
||
</div>
|
||
</header>
|
||
<div class="wrap">
|
||
<div class="stage"><canvas id="cv" width="520" height="900"></canvas></div>
|
||
<div class="panel">
|
||
<div class="row" id="trigRow"></div>
|
||
<p class="muted" id="status">상황 트리거를 누르면 반응 클립을 재생하고 종료 후 <code>dance_idle</code> 로 복귀합니다.</p>
|
||
<div class="row">
|
||
<label class="file">🖼 이미지 로드(다중)<input type="file" id="imgFiles" accept="image/png" multiple></label>
|
||
</div>
|
||
<p class="muted">• 리그 idle 은 풀캔버스 파츠를 원점에 그리고 관절 피벗 기준 회전.<br>
|
||
• baked 반응은 headless 바디 + 표정 머리를 <b>목(neck) 부착점</b>에 합성 (Python <code>reactions_layout_render.py</code> 와 동일 어파인).<br>
|
||
• 상대경로 로드가 막히면 <b>이미지 로드(다중)</b> 로 PNG 를 직접 지정(파일명 매칭).</p>
|
||
<p class="muted" id="loadInfo"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* ============================================================
|
||
Embedded JSON (inlined so it runs from file:// with zero fetch)
|
||
============================================================ */
|
||
const RIG = {
|
||
"name": "LeeSori",
|
||
"canvas": {
|
||
"width": 520,
|
||
"height": 900
|
||
},
|
||
"imageBase": "../03_Assets/Parts/Images/",
|
||
"mode": "fullcanvas",
|
||
"note": "Full-canvas parts: each PNG is 520x900 with the part already at its master position. Renderer draws each image at the ORIGIN (0,0) and rotates it about its pivot (the joint). pivot = auto-derived centroid of the opaque overlap between the part and its parent. At rest (all rot/tx/ty = 0) every world matrix is identity, so stacking reproduces the master.",
|
||
"bones": [
|
||
{
|
||
"name": "pelvis",
|
||
"parent": null,
|
||
"pivot": [
|
||
259.6,
|
||
363.4
|
||
],
|
||
"z": 6,
|
||
"image": "sori_part_pelvis.png"
|
||
},
|
||
{
|
||
"name": "chest",
|
||
"parent": "pelvis",
|
||
"pivot": [
|
||
259.6,
|
||
363.4
|
||
],
|
||
"z": 8,
|
||
"image": "sori_part_chest.png"
|
||
},
|
||
{
|
||
"name": "neck",
|
||
"parent": "chest",
|
||
"pivot": [
|
||
262,
|
||
229.3
|
||
],
|
||
"z": 9,
|
||
"image": "sori_part_neck.png"
|
||
},
|
||
{
|
||
"name": "head",
|
||
"parent": "neck",
|
||
"pivot": [
|
||
259.8,
|
||
209.3
|
||
],
|
||
"z": 10,
|
||
"image": "sori_part_head.png"
|
||
},
|
||
{
|
||
"name": "upperarm_r",
|
||
"parent": "chest",
|
||
"pivot": [
|
||
174.2,
|
||
287.2
|
||
],
|
||
"z": 5,
|
||
"image": "sori_part_upperarm_r.png"
|
||
},
|
||
{
|
||
"name": "forearm_r",
|
||
"parent": "upperarm_r",
|
||
"pivot": [
|
||
137.3,
|
||
358
|
||
],
|
||
"z": 5,
|
||
"image": "sori_part_forearm_r.png"
|
||
},
|
||
{
|
||
"name": "hand_r",
|
||
"parent": "forearm_r",
|
||
"pivot": [
|
||
134.2,
|
||
400.8
|
||
],
|
||
"z": 5,
|
||
"image": "sori_part_hand_r.png"
|
||
},
|
||
{
|
||
"name": "upperarm_l",
|
||
"parent": "chest",
|
||
"pivot": [
|
||
346,
|
||
286.5
|
||
],
|
||
"z": 12,
|
||
"image": "sori_part_upperarm_l.png"
|
||
},
|
||
{
|
||
"name": "forearm_l",
|
||
"parent": "upperarm_l",
|
||
"pivot": [
|
||
382.9,
|
||
357.6
|
||
],
|
||
"z": 12,
|
||
"image": "sori_part_forearm_l.png"
|
||
},
|
||
{
|
||
"name": "hand_l",
|
||
"parent": "forearm_l",
|
||
"pivot": [
|
||
389.6,
|
||
400.6
|
||
],
|
||
"z": 13,
|
||
"image": "sori_part_hand_l.png"
|
||
},
|
||
{
|
||
"name": "thigh_r",
|
||
"parent": "pelvis",
|
||
"pivot": [
|
||
226.3,
|
||
455
|
||
],
|
||
"z": 4,
|
||
"image": "sori_part_thigh_r.png"
|
||
},
|
||
{
|
||
"name": "shin_r",
|
||
"parent": "thigh_r",
|
||
"pivot": [
|
||
233.3,
|
||
609.1
|
||
],
|
||
"z": 3,
|
||
"image": "sori_part_shin_r.png"
|
||
},
|
||
{
|
||
"name": "foot_r",
|
||
"parent": "shin_r",
|
||
"pivot": [
|
||
236.5,
|
||
729.4
|
||
],
|
||
"z": 2,
|
||
"image": "sori_part_foot_r.png"
|
||
},
|
||
{
|
||
"name": "thigh_l",
|
||
"parent": "pelvis",
|
||
"pivot": [
|
||
294.1,
|
||
455
|
||
],
|
||
"z": 4,
|
||
"image": "sori_part_thigh_l.png"
|
||
},
|
||
{
|
||
"name": "shin_l",
|
||
"parent": "thigh_l",
|
||
"pivot": [
|
||
286.9,
|
||
609.1
|
||
],
|
||
"z": 3,
|
||
"image": "sori_part_shin_l.png"
|
||
},
|
||
{
|
||
"name": "foot_l",
|
||
"parent": "shin_l",
|
||
"pivot": [
|
||
283.9,
|
||
729.5
|
||
],
|
||
"z": 2,
|
||
"image": "sori_part_foot_l.png"
|
||
}
|
||
]
|
||
};
|
||
const DANCE_IDLE = {
|
||
"name": "dance_idle",
|
||
"duration": 2,
|
||
"loop": true,
|
||
"fpsHint": 60,
|
||
"defaultEase": "sine",
|
||
"note": "이소리 전용 튜닝 — 크롭 상의로 드러난 미드리프(맨살)의 이음새가 안 벌어지도록 CHEST는 트랙 없음(=골반에 리지드). 스웨이는 pelvis가 상체 전체를 통째로 기울여서 냄. 모션은 옷/머리로 가려지는 관절(어깨·무릎·목)에만.",
|
||
"tracks": {
|
||
"pelvis": {
|
||
"ty": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 9
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 9
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
],
|
||
"tx": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 7
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -7
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
],
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 3
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -3
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"neck": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 2
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -2
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"head": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 4
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -4
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
],
|
||
"ty": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": -2
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -2
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"upperarm_r": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 8
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -4
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"forearm_r": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 12
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -6
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"hand_r": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 6
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -3
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"upperarm_l": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": -4
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 8
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"forearm_l": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": -6
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 12
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"hand_l": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": -3
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 6
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"thigh_r": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 2
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": -1
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"shin_r": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 6
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"thigh_l": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": -1
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 2
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"shin_l": {
|
||
"rot": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.5,
|
||
"v": 6
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
}
|
||
}
|
||
};
|
||
const REACTIONS = {
|
||
"name": "LeeSori reactions map",
|
||
"note": "상황키(app event) → 반응 클립 이름(clips/<name>.json). idle은 배경 기본 루프.",
|
||
"idleDefault": "dance_idle",
|
||
"map": {
|
||
"idle": "dance_idle",
|
||
"error": "gesture_no",
|
||
"success": "gesture_heart"
|
||
},
|
||
"plannedExpansion": {
|
||
"greet": "gesture_wave",
|
||
"explain": "gesture_present",
|
||
"thinking": "gesture_think"
|
||
}
|
||
};
|
||
const LAYOUT = {
|
||
"stage": {
|
||
"w": 520,
|
||
"h": 900
|
||
},
|
||
"neck": [
|
||
260,
|
||
250
|
||
],
|
||
"overlap": 6,
|
||
"headTargetW": 150,
|
||
"bodies": {
|
||
"sori_body_campus_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_campus_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_campus_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_campus_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_campus_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_campus_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_campus_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_campus_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_campus_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_campus_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_campus_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_campus_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_campus_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_campus_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_campus_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_campus_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_campus_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_campus_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_ceo_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_ceo_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_ceo_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_ceo_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_ceo_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_ceo_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_ceo_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_ceo_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_ceo_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_ceo_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_ceo_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_ceo_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_ceo_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_ceo_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_ceo_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_ceo_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_ceo_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_ceo_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_dressL_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_dressL_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_dressL_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_dressL_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_dressL_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_dressL_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_dressL_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_dressL_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_dressL_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_dressL_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_dressL_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_dressL_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_dressL_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_dressL_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_dressL_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_dressL_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_dressL_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_dressL_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_dressS_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_dressS_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_dressS_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_dressS_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_dressS_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_dressS_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_dressS_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_dressS_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_dressS_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_dressS_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_dressS_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_dressS_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_dressS_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_dressS_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_dressS_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_dressS_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_dressS_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_dressS_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_jeans_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_jeans_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_jeans_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_jeans_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_jeans_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_jeans_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_jeans_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_jeans_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_jeans_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_jeans_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_jeans_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_jeans_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_jeans_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_jeans_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_jeans_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_jeans_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_jeans_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_jeans_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_track_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_track_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_track_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_track_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_track_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_track_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_track_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_track_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_track_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_track_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_track_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_track_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_track_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_track_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_track_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_track_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_track_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_track_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
},
|
||
"sori_body_tee_armscross": {
|
||
"scale": 0.3657,
|
||
"ox": 4,
|
||
"oy": 229.5
|
||
},
|
||
"sori_body_tee_cheer": {
|
||
"scale": 0.225,
|
||
"ox": 186.7,
|
||
"oy": 239.6
|
||
},
|
||
"sori_body_tee_clap": {
|
||
"scale": 0.3872,
|
||
"ox": -19.9,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_tee_control": {
|
||
"scale": 0.3172,
|
||
"ox": 29.4,
|
||
"oy": 225.3
|
||
},
|
||
"sori_body_tee_dj": {
|
||
"scale": 0.3177,
|
||
"ox": 15.1,
|
||
"oy": 225.2
|
||
},
|
||
"sori_body_tee_handwave": {
|
||
"scale": 0.3018,
|
||
"ox": 27.9,
|
||
"oy": 230.7
|
||
},
|
||
"sori_body_tee_heart": {
|
||
"scale": 0.3397,
|
||
"ox": 15.9,
|
||
"oy": 225.9
|
||
},
|
||
"sori_body_tee_idle_full": {
|
||
"scale": 0.4894,
|
||
"ox": 48.4,
|
||
"oy": 203.5
|
||
},
|
||
"sori_body_tee_idle_upper": {
|
||
"scale": 0.3611,
|
||
"ox": -4.3,
|
||
"oy": 231.2
|
||
},
|
||
"sori_body_tee_joy": {
|
||
"scale": 0.3075,
|
||
"ox": 30.3,
|
||
"oy": 230.9
|
||
},
|
||
"sori_body_tee_listen": {
|
||
"scale": 0.323,
|
||
"ox": 80.6,
|
||
"oy": 233.2
|
||
},
|
||
"sori_body_tee_peace": {
|
||
"scale": 0.3662,
|
||
"ox": 53.1,
|
||
"oy": 230.6
|
||
},
|
||
"sori_body_tee_piano": {
|
||
"scale": 0.3952,
|
||
"ox": -26.1,
|
||
"oy": 229.1
|
||
},
|
||
"sori_body_tee_point": {
|
||
"scale": 0.3239,
|
||
"ox": 23.2,
|
||
"oy": 227.3
|
||
},
|
||
"sori_body_tee_present": {
|
||
"scale": 0.2641,
|
||
"ox": 54.2,
|
||
"oy": 234.9
|
||
},
|
||
"sori_body_tee_shrug": {
|
||
"scale": 0.1888,
|
||
"ox": 123.3,
|
||
"oy": 236.6
|
||
},
|
||
"sori_body_tee_thumbsup": {
|
||
"scale": 0.3392,
|
||
"ox": 14.2,
|
||
"oy": 229.6
|
||
},
|
||
"sori_body_tee_wave": {
|
||
"scale": 0.2991,
|
||
"ox": 103,
|
||
"oy": 224.9
|
||
}
|
||
},
|
||
"heads": {
|
||
"sori_head_long": {
|
||
"w": 681,
|
||
"neckNorm": [
|
||
0.4984,
|
||
0.9522
|
||
]
|
||
},
|
||
"sori_head_longneat": {
|
||
"w": 744,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9753
|
||
]
|
||
},
|
||
"sori_head_longneat_blink": {
|
||
"w": 730,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9713
|
||
]
|
||
},
|
||
"sori_head_longneat_confused": {
|
||
"w": 737,
|
||
"neckNorm": [
|
||
0.5032,
|
||
0.9801
|
||
]
|
||
},
|
||
"sori_head_longneat_cool": {
|
||
"w": 722,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.9729
|
||
]
|
||
},
|
||
"sori_head_longneat_laugh": {
|
||
"w": 715,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_longneat_love": {
|
||
"w": 720,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_longneat_negative": {
|
||
"w": 725,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9729
|
||
]
|
||
},
|
||
"sori_head_longneat_neutral": {
|
||
"w": 744,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9753
|
||
]
|
||
},
|
||
"sori_head_longneat_playful": {
|
||
"w": 725,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9737
|
||
]
|
||
},
|
||
"sori_head_longneat_positive": {
|
||
"w": 696,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.9585
|
||
]
|
||
},
|
||
"sori_head_longneat_pout": {
|
||
"w": 725,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9697
|
||
]
|
||
},
|
||
"sori_head_longneat_proud": {
|
||
"w": 729,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9745
|
||
]
|
||
},
|
||
"sori_head_longneat_sad": {
|
||
"w": 724,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.9729
|
||
]
|
||
},
|
||
"sori_head_longneat_shy": {
|
||
"w": 724,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_longneat_sleepy": {
|
||
"w": 726,
|
||
"neckNorm": [
|
||
0.5012,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_longneat_smile": {
|
||
"w": 732,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.9761
|
||
]
|
||
},
|
||
"sori_head_longneat_surprised": {
|
||
"w": 716,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.9689
|
||
]
|
||
},
|
||
"sori_head_longneat_talk": {
|
||
"w": 721,
|
||
"neckNorm": [
|
||
0.504,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_longneat_talk_wide": {
|
||
"w": 708,
|
||
"neckNorm": [
|
||
0.498,
|
||
0.9561
|
||
]
|
||
},
|
||
"sori_head_longneat_thinking": {
|
||
"w": 727,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9713
|
||
]
|
||
},
|
||
"sori_head_longneat_wink": {
|
||
"w": 728,
|
||
"neckNorm": [
|
||
0.5028,
|
||
0.9745
|
||
]
|
||
},
|
||
"sori_head_long_blink": {
|
||
"w": 797,
|
||
"neckNorm": [
|
||
0.4888,
|
||
0.9522
|
||
]
|
||
},
|
||
"sori_head_long_confused": {
|
||
"w": 804,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9553
|
||
]
|
||
},
|
||
"sori_head_long_cool": {
|
||
"w": 806,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9545
|
||
]
|
||
},
|
||
"sori_head_long_laugh": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4936,
|
||
0.9665
|
||
]
|
||
},
|
||
"sori_head_long_love": {
|
||
"w": 805,
|
||
"neckNorm": [
|
||
0.4872,
|
||
0.9553
|
||
]
|
||
},
|
||
"sori_head_long_negative": {
|
||
"w": 802,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9514
|
||
]
|
||
},
|
||
"sori_head_long_neutral": {
|
||
"w": 681,
|
||
"neckNorm": [
|
||
0.4984,
|
||
0.9522
|
||
]
|
||
},
|
||
"sori_head_long_playful": {
|
||
"w": 808,
|
||
"neckNorm": [
|
||
0.486,
|
||
0.9561
|
||
]
|
||
},
|
||
"sori_head_long_positive": {
|
||
"w": 801,
|
||
"neckNorm": [
|
||
0.488,
|
||
0.9514
|
||
]
|
||
},
|
||
"sori_head_long_pout": {
|
||
"w": 808,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9561
|
||
]
|
||
},
|
||
"sori_head_long_proud": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.4864,
|
||
0.9545
|
||
]
|
||
},
|
||
"sori_head_long_sad": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.4872,
|
||
0.9545
|
||
]
|
||
},
|
||
"sori_head_long_shy": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.4864,
|
||
0.9553
|
||
]
|
||
},
|
||
"sori_head_long_sleepy": {
|
||
"w": 802,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9545
|
||
]
|
||
},
|
||
"sori_head_long_smile": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.488,
|
||
0.9498
|
||
]
|
||
},
|
||
"sori_head_long_surprised": {
|
||
"w": 805,
|
||
"neckNorm": [
|
||
0.4864,
|
||
0.9545
|
||
]
|
||
},
|
||
"sori_head_long_talk": {
|
||
"w": 793,
|
||
"neckNorm": [
|
||
0.4888,
|
||
0.9514
|
||
]
|
||
},
|
||
"sori_head_long_talk_wide": {
|
||
"w": 799,
|
||
"neckNorm": [
|
||
0.488,
|
||
0.9537
|
||
]
|
||
},
|
||
"sori_head_long_thinking": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.4864,
|
||
0.9537
|
||
]
|
||
},
|
||
"sori_head_long_wink": {
|
||
"w": 807,
|
||
"neckNorm": [
|
||
0.4864,
|
||
0.9553
|
||
]
|
||
},
|
||
"sori_head_short": {
|
||
"w": 906,
|
||
"neckNorm": [
|
||
0.4837,
|
||
0.8931
|
||
]
|
||
},
|
||
"sori_head_shortneat": {
|
||
"w": 725,
|
||
"neckNorm": [
|
||
0.4976,
|
||
0.8493
|
||
]
|
||
},
|
||
"sori_head_shortneat_blink": {
|
||
"w": 877,
|
||
"neckNorm": [
|
||
0.4848,
|
||
0.8852
|
||
]
|
||
},
|
||
"sori_head_shortneat_confused": {
|
||
"w": 829,
|
||
"neckNorm": [
|
||
0.4968,
|
||
0.8437
|
||
]
|
||
},
|
||
"sori_head_shortneat_cool": {
|
||
"w": 772,
|
||
"neckNorm": [
|
||
0.5044,
|
||
0.8469
|
||
]
|
||
},
|
||
"sori_head_shortneat_laugh": {
|
||
"w": 818,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.8692
|
||
]
|
||
},
|
||
"sori_head_shortneat_love": {
|
||
"w": 817,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.8684
|
||
]
|
||
},
|
||
"sori_head_shortneat_negative": {
|
||
"w": 781,
|
||
"neckNorm": [
|
||
0.5032,
|
||
0.8581
|
||
]
|
||
},
|
||
"sori_head_shortneat_neutral": {
|
||
"w": 725,
|
||
"neckNorm": [
|
||
0.4976,
|
||
0.8493
|
||
]
|
||
},
|
||
"sori_head_shortneat_playful": {
|
||
"w": 837,
|
||
"neckNorm": [
|
||
0.492,
|
||
0.8596
|
||
]
|
||
},
|
||
"sori_head_shortneat_positive": {
|
||
"w": 819,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.8541
|
||
]
|
||
},
|
||
"sori_head_shortneat_pout": {
|
||
"w": 808,
|
||
"neckNorm": [
|
||
0.5012,
|
||
0.8557
|
||
]
|
||
},
|
||
"sori_head_shortneat_proud": {
|
||
"w": 813,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8405
|
||
]
|
||
},
|
||
"sori_head_shortneat_sad": {
|
||
"w": 778,
|
||
"neckNorm": [
|
||
0.5052,
|
||
0.8589
|
||
]
|
||
},
|
||
"sori_head_shortneat_shy": {
|
||
"w": 798,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.8596
|
||
]
|
||
},
|
||
"sori_head_shortneat_sleepy": {
|
||
"w": 831,
|
||
"neckNorm": [
|
||
0.4944,
|
||
0.8565
|
||
]
|
||
},
|
||
"sori_head_shortneat_smile": {
|
||
"w": 834,
|
||
"neckNorm": [
|
||
0.502,
|
||
0.8844
|
||
]
|
||
},
|
||
"sori_head_shortneat_surprised": {
|
||
"w": 792,
|
||
"neckNorm": [
|
||
0.5044,
|
||
0.8684
|
||
]
|
||
},
|
||
"sori_head_shortneat_talk": {
|
||
"w": 762,
|
||
"neckNorm": [
|
||
0.5012,
|
||
0.8501
|
||
]
|
||
},
|
||
"sori_head_shortneat_talk_wide": {
|
||
"w": 810,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8573
|
||
]
|
||
},
|
||
"sori_head_shortneat_thinking": {
|
||
"w": 819,
|
||
"neckNorm": [
|
||
0.5072,
|
||
0.8581
|
||
]
|
||
},
|
||
"sori_head_shortneat_wink": {
|
||
"w": 801,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.8628
|
||
]
|
||
},
|
||
"sori_head_short_blink": {
|
||
"w": 936,
|
||
"neckNorm": [
|
||
0.4932,
|
||
0.9043
|
||
]
|
||
},
|
||
"sori_head_short_confused": {
|
||
"w": 903,
|
||
"neckNorm": [
|
||
0.4825,
|
||
0.8979
|
||
]
|
||
},
|
||
"sori_head_short_cool": {
|
||
"w": 939,
|
||
"neckNorm": [
|
||
0.4912,
|
||
0.9043
|
||
]
|
||
},
|
||
"sori_head_short_laugh": {
|
||
"w": 899,
|
||
"neckNorm": [
|
||
0.4833,
|
||
0.8987
|
||
]
|
||
},
|
||
"sori_head_short_love": {
|
||
"w": 902,
|
||
"neckNorm": [
|
||
0.4813,
|
||
0.8963
|
||
]
|
||
},
|
||
"sori_head_short_negative": {
|
||
"w": 904,
|
||
"neckNorm": [
|
||
0.4844,
|
||
0.8995
|
||
]
|
||
},
|
||
"sori_head_short_neutral": {
|
||
"w": 906,
|
||
"neckNorm": [
|
||
0.4837,
|
||
0.8931
|
||
]
|
||
},
|
||
"sori_head_short_playful": {
|
||
"w": 910,
|
||
"neckNorm": [
|
||
0.486,
|
||
0.9043
|
||
]
|
||
},
|
||
"sori_head_short_positive": {
|
||
"w": 906,
|
||
"neckNorm": [
|
||
0.4837,
|
||
0.8987
|
||
]
|
||
},
|
||
"sori_head_short_pout": {
|
||
"w": 919,
|
||
"neckNorm": [
|
||
0.4888,
|
||
0.9019
|
||
]
|
||
},
|
||
"sori_head_short_proud": {
|
||
"w": 920,
|
||
"neckNorm": [
|
||
0.4908,
|
||
0.8971
|
||
]
|
||
},
|
||
"sori_head_short_sad": {
|
||
"w": 918,
|
||
"neckNorm": [
|
||
0.4868,
|
||
0.9091
|
||
]
|
||
},
|
||
"sori_head_short_shy": {
|
||
"w": 900,
|
||
"neckNorm": [
|
||
0.4829,
|
||
0.8963
|
||
]
|
||
},
|
||
"sori_head_short_sleepy": {
|
||
"w": 925,
|
||
"neckNorm": [
|
||
0.4888,
|
||
0.9035
|
||
]
|
||
},
|
||
"sori_head_short_smile": {
|
||
"w": 897,
|
||
"neckNorm": [
|
||
0.4896,
|
||
0.9011
|
||
]
|
||
},
|
||
"sori_head_short_surprised": {
|
||
"w": 901,
|
||
"neckNorm": [
|
||
0.4817,
|
||
0.8987
|
||
]
|
||
},
|
||
"sori_head_short_talk": {
|
||
"w": 908,
|
||
"neckNorm": [
|
||
0.4884,
|
||
0.8963
|
||
]
|
||
},
|
||
"sori_head_short_talk_wide": {
|
||
"w": 934,
|
||
"neckNorm": [
|
||
0.49,
|
||
0.9043
|
||
]
|
||
},
|
||
"sori_head_short_thinking": {
|
||
"w": 902,
|
||
"neckNorm": [
|
||
0.4844,
|
||
0.8987
|
||
]
|
||
},
|
||
"sori_head_short_wink": {
|
||
"w": 902,
|
||
"neckNorm": [
|
||
0.4821,
|
||
0.8995
|
||
]
|
||
},
|
||
"sori_head_waveL": {
|
||
"w": 810,
|
||
"neckNorm": [
|
||
0.494,
|
||
0.9498
|
||
]
|
||
},
|
||
"sori_head_waveLneat": {
|
||
"w": 841,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveLneat_blink": {
|
||
"w": 766,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9298
|
||
]
|
||
},
|
||
"sori_head_waveLneat_confused": {
|
||
"w": 838,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.9745
|
||
]
|
||
},
|
||
"sori_head_waveLneat_cool": {
|
||
"w": 835,
|
||
"neckNorm": [
|
||
0.496,
|
||
0.9633
|
||
]
|
||
},
|
||
"sori_head_waveLneat_laugh": {
|
||
"w": 822,
|
||
"neckNorm": [
|
||
0.4964,
|
||
0.9498
|
||
]
|
||
},
|
||
"sori_head_waveLneat_love": {
|
||
"w": 825,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.9697
|
||
]
|
||
},
|
||
"sori_head_waveLneat_negative": {
|
||
"w": 874,
|
||
"neckNorm": [
|
||
0.4964,
|
||
0.9856
|
||
]
|
||
},
|
||
"sori_head_waveLneat_neutral": {
|
||
"w": 841,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveLneat_playful": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4976,
|
||
0.9617
|
||
]
|
||
},
|
||
"sori_head_waveLneat_positive": {
|
||
"w": 819,
|
||
"neckNorm": [
|
||
0.4952,
|
||
0.9514
|
||
]
|
||
},
|
||
"sori_head_waveLneat_pout": {
|
||
"w": 822,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveLneat_proud": {
|
||
"w": 824,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveLneat_sad": {
|
||
"w": 827,
|
||
"neckNorm": [
|
||
0.4984,
|
||
0.9649
|
||
]
|
||
},
|
||
"sori_head_waveLneat_shy": {
|
||
"w": 811,
|
||
"neckNorm": [
|
||
0.4968,
|
||
0.9577
|
||
]
|
||
},
|
||
"sori_head_waveLneat_sleepy": {
|
||
"w": 819,
|
||
"neckNorm": [
|
||
0.4968,
|
||
0.9657
|
||
]
|
||
},
|
||
"sori_head_waveLneat_smile": {
|
||
"w": 806,
|
||
"neckNorm": [
|
||
0.4972,
|
||
0.9434
|
||
]
|
||
},
|
||
"sori_head_waveLneat_surprised": {
|
||
"w": 832,
|
||
"neckNorm": [
|
||
0.4924,
|
||
0.9514
|
||
]
|
||
},
|
||
"sori_head_waveLneat_talk": {
|
||
"w": 812,
|
||
"neckNorm": [
|
||
0.49,
|
||
0.9553
|
||
]
|
||
},
|
||
"sori_head_waveLneat_talk_wide": {
|
||
"w": 841,
|
||
"neckNorm": [
|
||
0.5056,
|
||
0.9785
|
||
]
|
||
},
|
||
"sori_head_waveLneat_thinking": {
|
||
"w": 836,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.9697
|
||
]
|
||
},
|
||
"sori_head_waveLneat_wink": {
|
||
"w": 836,
|
||
"neckNorm": [
|
||
0.4988,
|
||
0.9729
|
||
]
|
||
},
|
||
"sori_head_waveL_blink": {
|
||
"w": 820,
|
||
"neckNorm": [
|
||
0.498,
|
||
0.9522
|
||
]
|
||
},
|
||
"sori_head_waveL_confused": {
|
||
"w": 840,
|
||
"neckNorm": [
|
||
0.506,
|
||
0.9689
|
||
]
|
||
},
|
||
"sori_head_waveL_cool": {
|
||
"w": 839,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.9665
|
||
]
|
||
},
|
||
"sori_head_waveL_laugh": {
|
||
"w": 831,
|
||
"neckNorm": [
|
||
0.5008,
|
||
0.9633
|
||
]
|
||
},
|
||
"sori_head_waveL_love": {
|
||
"w": 840,
|
||
"neckNorm": [
|
||
0.502,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveL_negative": {
|
||
"w": 833,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.9617
|
||
]
|
||
},
|
||
"sori_head_waveL_neutral": {
|
||
"w": 810,
|
||
"neckNorm": [
|
||
0.494,
|
||
0.9498
|
||
]
|
||
},
|
||
"sori_head_waveL_playful": {
|
||
"w": 837,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.9609
|
||
]
|
||
},
|
||
"sori_head_waveL_positive": {
|
||
"w": 838,
|
||
"neckNorm": [
|
||
0.502,
|
||
0.9617
|
||
]
|
||
},
|
||
"sori_head_waveL_pout": {
|
||
"w": 832,
|
||
"neckNorm": [
|
||
0.5012,
|
||
0.9601
|
||
]
|
||
},
|
||
"sori_head_waveL_proud": {
|
||
"w": 839,
|
||
"neckNorm": [
|
||
0.5032,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveL_sad": {
|
||
"w": 833,
|
||
"neckNorm": [
|
||
0.5,
|
||
0.9617
|
||
]
|
||
},
|
||
"sori_head_waveL_shy": {
|
||
"w": 834,
|
||
"neckNorm": [
|
||
0.5012,
|
||
0.9609
|
||
]
|
||
},
|
||
"sori_head_waveL_sleepy": {
|
||
"w": 833,
|
||
"neckNorm": [
|
||
0.5024,
|
||
0.9617
|
||
]
|
||
},
|
||
"sori_head_waveL_smile": {
|
||
"w": 831,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.9609
|
||
]
|
||
},
|
||
"sori_head_waveL_surprised": {
|
||
"w": 837,
|
||
"neckNorm": [
|
||
0.5016,
|
||
0.9641
|
||
]
|
||
},
|
||
"sori_head_waveL_talk": {
|
||
"w": 822,
|
||
"neckNorm": [
|
||
0.4972,
|
||
0.9609
|
||
]
|
||
},
|
||
"sori_head_waveL_talk_wide": {
|
||
"w": 826,
|
||
"neckNorm": [
|
||
0.498,
|
||
0.9609
|
||
]
|
||
},
|
||
"sori_head_waveL_thinking": {
|
||
"w": 833,
|
||
"neckNorm": [
|
||
0.5032,
|
||
0.9721
|
||
]
|
||
},
|
||
"sori_head_waveL_wink": {
|
||
"w": 838,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.9649
|
||
]
|
||
},
|
||
"sori_head_waveS": {
|
||
"w": 820,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat": {
|
||
"w": 820,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_blink": {
|
||
"w": 829,
|
||
"neckNorm": [
|
||
0.5096,
|
||
0.8214
|
||
]
|
||
},
|
||
"sori_head_waveSneat_confused": {
|
||
"w": 866,
|
||
"neckNorm": [
|
||
0.506,
|
||
0.862
|
||
]
|
||
},
|
||
"sori_head_waveSneat_cool": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_laugh": {
|
||
"w": 837,
|
||
"neckNorm": [
|
||
0.4976,
|
||
0.8341
|
||
]
|
||
},
|
||
"sori_head_waveSneat_love": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_negative": {
|
||
"w": 877,
|
||
"neckNorm": [
|
||
0.5,
|
||
0.8517
|
||
]
|
||
},
|
||
"sori_head_waveSneat_neutral": {
|
||
"w": 820,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_playful": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_positive": {
|
||
"w": 892,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.8581
|
||
]
|
||
},
|
||
"sori_head_waveSneat_pout": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_proud": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_sad": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_shy": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8262
|
||
]
|
||
},
|
||
"sori_head_waveSneat_sleepy": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_smile": {
|
||
"w": 897,
|
||
"neckNorm": [
|
||
0.4984,
|
||
0.8533
|
||
]
|
||
},
|
||
"sori_head_waveSneat_surprised": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_talk": {
|
||
"w": 868,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8541
|
||
]
|
||
},
|
||
"sori_head_waveSneat_talk_wide": {
|
||
"w": 888,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8525
|
||
]
|
||
},
|
||
"sori_head_waveSneat_thinking": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveSneat_wink": {
|
||
"w": 876,
|
||
"neckNorm": [
|
||
0.498,
|
||
0.8581
|
||
]
|
||
},
|
||
"sori_head_waveS_blink": {
|
||
"w": 829,
|
||
"neckNorm": [
|
||
0.5096,
|
||
0.8214
|
||
]
|
||
},
|
||
"sori_head_waveS_confused": {
|
||
"w": 866,
|
||
"neckNorm": [
|
||
0.506,
|
||
0.862
|
||
]
|
||
},
|
||
"sori_head_waveS_cool": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_laugh": {
|
||
"w": 837,
|
||
"neckNorm": [
|
||
0.4976,
|
||
0.8341
|
||
]
|
||
},
|
||
"sori_head_waveS_love": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_negative": {
|
||
"w": 877,
|
||
"neckNorm": [
|
||
0.5,
|
||
0.8517
|
||
]
|
||
},
|
||
"sori_head_waveS_neutral": {
|
||
"w": 820,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_playful": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_positive": {
|
||
"w": 892,
|
||
"neckNorm": [
|
||
0.5004,
|
||
0.8581
|
||
]
|
||
},
|
||
"sori_head_waveS_pout": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_proud": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_sad": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_shy": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8262
|
||
]
|
||
},
|
||
"sori_head_waveS_sleepy": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_smile": {
|
||
"w": 897,
|
||
"neckNorm": [
|
||
0.4984,
|
||
0.8533
|
||
]
|
||
},
|
||
"sori_head_waveS_surprised": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_talk": {
|
||
"w": 868,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8541
|
||
]
|
||
},
|
||
"sori_head_waveS_talk_wide": {
|
||
"w": 888,
|
||
"neckNorm": [
|
||
0.4996,
|
||
0.8525
|
||
]
|
||
},
|
||
"sori_head_waveS_thinking": {
|
||
"w": 821,
|
||
"neckNorm": [
|
||
0.4992,
|
||
0.8254
|
||
]
|
||
},
|
||
"sori_head_waveS_wink": {
|
||
"w": 876,
|
||
"neckNorm": [
|
||
0.498,
|
||
0.8581
|
||
]
|
||
}
|
||
}
|
||
};
|
||
const CLIPS = {
|
||
"gesture_heart": {
|
||
"name": "gesture_heart",
|
||
"desc": "손 하트를 그리며 밝게 '잘됐어요'",
|
||
"duration": 2.2,
|
||
"return": "idle",
|
||
"layers": {
|
||
"body": [
|
||
{
|
||
"t": 0,
|
||
"mode": "rig",
|
||
"clip": "idle"
|
||
},
|
||
{
|
||
"t": 0.15,
|
||
"mode": "baked",
|
||
"image": "sori_body_track_heart",
|
||
"fade": 0.2
|
||
}
|
||
],
|
||
"face": [
|
||
{
|
||
"t": 0,
|
||
"expr": "smile"
|
||
},
|
||
{
|
||
"t": 0.25,
|
||
"expr": "love"
|
||
}
|
||
],
|
||
"mouth": [
|
||
{
|
||
"t": 0.5,
|
||
"say": "잘됐어요",
|
||
"dur": 1.1,
|
||
"pattern": "talk"
|
||
}
|
||
],
|
||
"transform": {
|
||
"pelvis": {
|
||
"ty": [
|
||
{
|
||
"t": 0.4,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.7,
|
||
"v": 8
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 1.3,
|
||
"v": 8
|
||
},
|
||
{
|
||
"t": 1.6,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"head": {
|
||
"rot": [
|
||
{
|
||
"t": 0.4,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.7,
|
||
"v": 4
|
||
},
|
||
{
|
||
"t": 1,
|
||
"v": -4
|
||
},
|
||
{
|
||
"t": 1.3,
|
||
"v": 4
|
||
},
|
||
{
|
||
"t": 1.6,
|
||
"v": 0
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"caption": [
|
||
{
|
||
"t": 0.5,
|
||
"text": "잘됐어요",
|
||
"dur": 1.5
|
||
}
|
||
],
|
||
"sfx": [
|
||
{
|
||
"t": 0.45,
|
||
"id": "success"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"gesture_no": {
|
||
"name": "gesture_no",
|
||
"desc": "서있다 → 팔짱 끼고 인상 쓰며 고개 저으며 '안돼요'",
|
||
"duration": 2.4,
|
||
"return": "idle",
|
||
"layers": {
|
||
"body": [
|
||
{
|
||
"t": 0,
|
||
"mode": "rig",
|
||
"clip": "idle"
|
||
},
|
||
{
|
||
"t": 0.15,
|
||
"mode": "baked",
|
||
"image": "sori_body_track_armscross",
|
||
"fade": 0.2
|
||
}
|
||
],
|
||
"face": [
|
||
{
|
||
"t": 0,
|
||
"expr": "neutral"
|
||
},
|
||
{
|
||
"t": 0.3,
|
||
"expr": "negative"
|
||
}
|
||
],
|
||
"mouth": [
|
||
{
|
||
"t": 0.55,
|
||
"say": "안돼요",
|
||
"dur": 1.2,
|
||
"pattern": "talk"
|
||
}
|
||
],
|
||
"transform": {
|
||
"chest": {
|
||
"ty": [
|
||
{
|
||
"t": 0,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.2,
|
||
"v": -4
|
||
},
|
||
{
|
||
"t": 0.5,
|
||
"v": 0
|
||
}
|
||
]
|
||
},
|
||
"head": {
|
||
"rot": [
|
||
{
|
||
"t": 0.5,
|
||
"v": 0
|
||
},
|
||
{
|
||
"t": 0.8,
|
||
"v": 9
|
||
},
|
||
{
|
||
"t": 1.1,
|
||
"v": -9
|
||
},
|
||
{
|
||
"t": 1.4,
|
||
"v": 9
|
||
},
|
||
{
|
||
"t": 1.7,
|
||
"v": -9
|
||
},
|
||
{
|
||
"t": 2,
|
||
"v": 0
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"caption": [
|
||
{
|
||
"t": 0.55,
|
||
"text": "안돼요",
|
||
"dur": 1.6
|
||
}
|
||
],
|
||
"sfx": [
|
||
{
|
||
"t": 0.5,
|
||
"id": "nope"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
};
|
||
|
||
/* ============================================================
|
||
Asset paths
|
||
============================================================ */
|
||
const PARTS_DIR = RIG.imageBase || "../03_Assets/Parts/Images/";
|
||
const HEAD_DIR = "../03_Assets/Library/Heads/";
|
||
const BAKED_DIRS = ["../03_Assets/Library/BakedPoses/Track/","../03_Assets/Library/BakedPoses/Campus/","../03_Assets/Library/BakedPoses/CeoPantsuit/","../03_Assets/Library/BakedPoses/DressLong/","../03_Assets/Library/BakedPoses/DressShort/","../03_Assets/Library/BakedPoses/Jeans/","../03_Assets/Library/BakedPoses/Tshirt/"];
|
||
|
||
/* images keyed by basename (no extension) */
|
||
const images = {}; // key -> HTMLImageElement (loaded)
|
||
let loadTotal = 0, loadOk = 0;
|
||
|
||
function tryLoad(key, dirs){
|
||
loadTotal++;
|
||
let i = 0;
|
||
const attempt = () => {
|
||
if (i >= dirs.length) { refreshInfo(); return; } // give up (leave undefined)
|
||
const im = new Image();
|
||
im.onload = () => { images[key] = im; loadOk++; refreshInfo(); };
|
||
im.onerror = () => { i++; attempt(); };
|
||
im.src = dirs[i] + key + ".png";
|
||
};
|
||
attempt();
|
||
}
|
||
|
||
function loadAllAssets(){
|
||
// rig parts
|
||
RIG.bones.forEach(b => { if (b.image) tryLoad(b.image.replace(/\.png$/i,""), [PARTS_DIR]); });
|
||
// heads (all expressions + talk frames)
|
||
Object.keys(LAYOUT.heads).forEach(k => tryLoad(k, [HEAD_DIR]));
|
||
// baked bodies referenced by clips (+ common ones); resolve across Cozy/Day/Night
|
||
const bakedNeeded = new Set();
|
||
Object.values(CLIPS).forEach(c => (c.layers.body||[]).forEach(e => { if (e.mode==="baked" && e.image) bakedNeeded.add(e.image); }));
|
||
bakedNeeded.forEach(k => tryLoad(k, BAKED_DIRS));
|
||
}
|
||
function refreshInfo(){
|
||
document.getElementById('loadInfo').textContent = `이미지 로드: ${loadOk} / ${loadTotal}` + (loadOk===0?' (없음 → 이미지 로드(다중) 사용)':'');
|
||
}
|
||
|
||
/* ============================================================
|
||
Matrix + sampling helpers (same convention as index.html)
|
||
mul(A,B) = A·B (B applied first, then A); tp = A·point
|
||
============================================================ */
|
||
const mul=(m,n)=>({a:m.a*n.a+m.c*n.b,b:m.b*n.a+m.d*n.b,c:m.a*n.c+m.c*n.d,d:m.b*n.c+m.d*n.d,e:m.a*n.e+m.c*n.f+m.e,f:m.b*n.e+m.d*n.f+m.f});
|
||
const T=(x,y)=>({a:1,b:0,c:0,d:1,e:x,f:y});
|
||
const R=deg=>{const r=deg*Math.PI/180,c=Math.cos(r),s=Math.sin(r);return{a:c,b:s,c:-s,d:c,e:0,f:0};};
|
||
const Sc=k=>({a:k,b:0,c:0,d:k,e:0,f:0});
|
||
function ease(ty,x){return ty==='linear'?x:-(Math.cos(Math.PI*x)-1)/2;}
|
||
function sample(keys,t,defEase){ if(!keys||!keys.length)return 0; if(t<=keys[0].t)return keys[0].v; const n=keys.length; if(t>=keys[n-1].t)return keys[n-1].v;
|
||
for(let i=0;i<n-1;i++){ if(t>=keys[i].t&&t<=keys[i+1].t){ const k0=keys[i],k1=keys[i+1],sp=(k1.t-k0.t)||1e-6; return k0.v+(k1.v-k0.v)*ease(k0.e||defEase||'sine',(t-k0.t)/sp);} } return keys[n-1].v; }
|
||
function sT(track,sub,t){ return (track && track[sub]) ? sample(track[sub],t) : 0; }
|
||
// active timeline entry: last with t<=now (or first)
|
||
function activeEntry(arr,now){ if(!arr||!arr.length)return null; let e=arr[0]; for(let i=0;i<arr.length;i++){ if(arr[i].t<=now) e=arr[i]; else break; } return e; }
|
||
function activeIndex(arr,now){ let idx=0; for(let i=0;i<arr.length;i++){ if(arr[i].t<=now) idx=i; else break; } return idx; }
|
||
|
||
/* ============================================================
|
||
Rig FK render (copied engine from index.html) with optional
|
||
per-bone extra deltas and whole-stage translate.
|
||
============================================================ */
|
||
const cv=document.getElementById('cv'), ctx=cv.getContext('2d');
|
||
cv.width=RIG.canvas.width; cv.height=RIG.canvas.height;
|
||
const bones=RIG.bones.map(b=>Object.assign({},b));
|
||
const boneMap={}; bones.forEach(b=>boneMap[b.name]=b);
|
||
|
||
function rigWorlds(time, extra){
|
||
extra = extra||{};
|
||
bones.forEach(b=>{
|
||
const tr=DANCE_IDLE.tracks[b.name]||{};
|
||
const ex=extra[b.name]||{};
|
||
const rot=sample(tr.rot,time,DANCE_IDLE.defaultEase)+(ex.rot||0);
|
||
const tx =sample(tr.tx ,time,DANCE_IDLE.defaultEase)+(ex.tx ||0);
|
||
const ty =sample(tr.ty ,time,DANCE_IDLE.defaultEase)+(ex.ty ||0);
|
||
const jx=b.pivot[0],jy=b.pivot[1];
|
||
const local=mul(T(tx,ty),mul(T(jx,jy),mul(R(rot),T(-jx,-jy))));
|
||
b._world=b.parent?mul(boneMap[b.parent]._world,local):local;
|
||
});
|
||
}
|
||
function drawRig(time, extra, stageTx, stageTy, alpha){
|
||
rigWorlds(time, extra);
|
||
const base=T(stageTx||0, stageTy||0);
|
||
ctx.globalAlpha = alpha==null?1:alpha;
|
||
bones.slice().sort((a,b)=>(a.z||0)-(b.z||0)).forEach(b=>{
|
||
const im=images[b.image.replace(/\.png$/i,"")]; if(!im)return;
|
||
const w=mul(base,b._world);
|
||
ctx.setTransform(w.a,w.b,w.c,w.d,w.e,w.f); ctx.drawImage(im,0,0);
|
||
});
|
||
ctx.setTransform(1,0,0,1,0,0); ctx.globalAlpha=1;
|
||
}
|
||
|
||
/* ============================================================
|
||
Baked composition — mirrors reactions_layout_render.py compose()
|
||
body: M = T(stage) · T(ox,oy) · Sc(scale)
|
||
head: M = T(stage) · T(NECK_X, NECK_Y+OVERLAP+headTy) · R(rot) · Sc(hs) · T(-ax,-ay)
|
||
============================================================ */
|
||
const NECK_X = LAYOUT.neck[0], NECK_Y = LAYOUT.neck[1], OVERLAP = LAYOUT.overlap, HEAD_TARGET_W = LAYOUT.headTargetW;
|
||
|
||
function drawBakedBody(imgKey, stageTx, stageTy, alpha){
|
||
const bl=LAYOUT.bodies[imgKey]; const im=images[imgKey];
|
||
if(!bl||!im)return;
|
||
const M=mul(T(stageTx||0,stageTy||0), mul(T(bl.ox,bl.oy), Sc(bl.scale)));
|
||
ctx.globalAlpha=alpha==null?1:alpha;
|
||
ctx.setTransform(M.a,M.b,M.c,M.d,M.e,M.f); ctx.drawImage(im,0,0);
|
||
ctx.setTransform(1,0,0,1,0,0); ctx.globalAlpha=1;
|
||
}
|
||
function headKeyFallback(key){
|
||
return images[key] ? key : (images['sori_head_short_neutral'] ? 'sori_head_short_neutral' : (images['sori_head_short'] ? 'sori_head_short' : null));
|
||
}
|
||
function drawHead(headKey, rot, headTy, stageTx, stageTy, alpha){
|
||
const key=headKeyFallback(headKey); if(!key)return;
|
||
const hl=LAYOUT.heads[headKey]||LAYOUT.heads[key]; const im=images[key]; if(!hl||!im)return;
|
||
const hs=HEAD_TARGET_W/hl.w;
|
||
const ax=hl.neckNorm[0]*im.naturalWidth, ay=hl.neckNorm[1]*im.naturalHeight;
|
||
const M=mul(T(stageTx||0,stageTy||0),
|
||
mul(T(NECK_X, NECK_Y+OVERLAP+(headTy||0)),
|
||
mul(R(rot||0), mul(Sc(hs), T(-ax,-ay)))));
|
||
ctx.globalAlpha=alpha==null?1:alpha;
|
||
ctx.setTransform(M.a,M.b,M.c,M.d,M.e,M.f); ctx.drawImage(im,0,0);
|
||
ctx.setTransform(1,0,0,1,0,0); ctx.globalAlpha=1;
|
||
}
|
||
|
||
/* current expression + mouth lip-sync frame */
|
||
function currentHeadKey(clip, now){
|
||
const face=clip.layers.face; const fe=activeEntry(face,now);
|
||
const expr=fe?fe.expr:'neutral';
|
||
const exprKey='sori_head_short_'+expr;
|
||
const mouth=clip.layers.mouth||[];
|
||
for(const m of mouth){
|
||
if(now>=m.t && now < m.t+(m.dur||0)){
|
||
// ~7Hz swap between talk / expr / talk_wide to approximate lip-sync
|
||
const seq=['sori_head_short_talk', exprKey, 'sori_head_short_talk_wide', exprKey];
|
||
return seq[Math.floor((now-m.t)*7)%seq.length];
|
||
}
|
||
}
|
||
return exprKey;
|
||
}
|
||
|
||
/* baked body layer draw (body + head) at given alpha */
|
||
function drawBakedLayer(clip, entry, now, stageTx, stageTy, headRot, headTy, alpha){
|
||
drawBakedBody(entry.image, stageTx, stageTy, alpha);
|
||
drawHead(currentHeadKey(clip, now), headRot, headTy, stageTx, stageTy, alpha);
|
||
}
|
||
/* rig body layer draw (rig already includes head; add head rot/ty delta) */
|
||
function drawRigLayer(clip, now, stageTx, stageTy, headRot, headTy, alpha){
|
||
drawRig(now, { head:{ rot:headRot, ty:headTy } }, stageTx, stageTy, alpha);
|
||
}
|
||
function drawBodyEntry(clip, entry, now, stageTx, stageTy, headRot, headTy, alpha){
|
||
if(entry.mode==='baked') drawBakedLayer(clip, entry, now, stageTx, stageTy, headRot, headTy, alpha);
|
||
else drawRigLayer(clip, now, stageTx, stageTy, headRot, headTy, alpha);
|
||
}
|
||
|
||
/* speech bubble caption near the head */
|
||
let showCaption=true;
|
||
function drawCaption(clip, now, stageTx, stageTy){
|
||
if(!showCaption)return;
|
||
const cap=(clip.layers.caption||[]).find(c=>now>=c.t && now<c.t+(c.dur||0));
|
||
if(!cap)return;
|
||
const cx=NECK_X+(stageTx||0), topY=NECK_Y-190+(stageTy||0);
|
||
ctx.save(); ctx.setTransform(1,0,0,1,0,0);
|
||
ctx.font='600 20px system-ui, "Segoe UI", sans-serif';
|
||
const tw=ctx.measureText(cap.text).width;
|
||
const padX=14, padY=10, bw=tw+padX*2, bh=34, bx=cx-bw/2, by=topY-bh;
|
||
ctx.fillStyle='rgba(20,24,30,0.92)'; ctx.strokeStyle='#2e9e8b'; ctx.lineWidth=2;
|
||
roundRect(bx,by,bw,bh,10); ctx.fill(); ctx.stroke();
|
||
// tail
|
||
ctx.beginPath(); ctx.moveTo(cx-8,by+bh); ctx.lineTo(cx+8,by+bh); ctx.lineTo(cx,by+bh+12); ctx.closePath();
|
||
ctx.fillStyle='rgba(20,24,30,0.92)'; ctx.fill();
|
||
ctx.fillStyle='#e6f7f2'; ctx.textAlign='center'; ctx.textBaseline='middle';
|
||
ctx.fillText(cap.text, cx, by+bh/2);
|
||
ctx.restore();
|
||
}
|
||
function roundRect(x,y,w,h,r){ ctx.beginPath(); ctx.moveTo(x+r,y); ctx.arcTo(x+w,y,x+w,y+h,r); ctx.arcTo(x+w,y+h,x,y+h,r); ctx.arcTo(x,y+h,x,y,r); ctx.arcTo(x,y,x+w,y,r); ctx.closePath(); }
|
||
|
||
/* ============================================================
|
||
Reaction render (full frame)
|
||
============================================================ */
|
||
function renderReaction(clip, now){
|
||
ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,cv.width,cv.height);
|
||
const tr=clip.layers.transform||{};
|
||
const stageTx=sT(tr.pelvis,'tx',now)+sT(tr.chest,'tx',now);
|
||
const stageTy=sT(tr.pelvis,'ty',now)+sT(tr.chest,'ty',now);
|
||
const headRot=sT(tr.head,'rot',now);
|
||
const headTy =sT(tr.head,'ty',now);
|
||
|
||
const entries=clip.layers.body||[];
|
||
const ai=activeIndex(entries,now);
|
||
const cur=entries[ai];
|
||
const fade=cur?cur.fade||0:0;
|
||
if(cur && fade>0 && ai>0 && now < cur.t+fade){
|
||
const p=Math.max(0,Math.min(1,(now-cur.t)/fade));
|
||
drawBodyEntry(clip, entries[ai-1], now, stageTx, stageTy, headRot, headTy, 1-p); // outgoing
|
||
drawBodyEntry(clip, cur, now, stageTx, stageTy, headRot, headTy, p); // incoming
|
||
} else if(cur){
|
||
drawBodyEntry(clip, cur, now, stageTx, stageTy, headRot, headTy, 1);
|
||
}
|
||
drawCaption(clip, now, stageTx, stageTy);
|
||
}
|
||
function renderIdle(time){
|
||
ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,cv.width,cv.height);
|
||
drawRig(time, null, 0, 0, 1);
|
||
}
|
||
|
||
/* ============================================================
|
||
Playback loop / state machine
|
||
============================================================ */
|
||
let stateMode='idle'; // 'idle' | 'reaction'
|
||
let curClip=null, reactTime=0, idleTime=0;
|
||
let playing=true, speed=1, last=performance.now();
|
||
|
||
function playClip(name){
|
||
const clip=CLIPS[name];
|
||
if(!clip){ stateMode='idle'; setStateName('dance_idle'); return; }
|
||
curClip=clip; reactTime=0; stateMode='reaction'; setStateName(clip.name);
|
||
}
|
||
function goIdle(){ stateMode='idle'; curClip=null; setStateName('dance_idle'); }
|
||
function setStateName(n){ document.getElementById('stateName').textContent=n; }
|
||
|
||
function frame(now){
|
||
const dt=(now-last)/1000; last=now;
|
||
if(playing){
|
||
if(stateMode==='reaction' && curClip){
|
||
reactTime+=dt*speed;
|
||
if(reactTime>=curClip.duration){ goIdle(); }
|
||
} else {
|
||
idleTime+=dt*speed; if(idleTime>=DANCE_IDLE.duration) idleTime%=DANCE_IDLE.duration;
|
||
}
|
||
}
|
||
if(stateMode==='reaction' && curClip) renderReaction(curClip, reactTime);
|
||
else renderIdle(idleTime);
|
||
requestAnimationFrame(frame);
|
||
}
|
||
|
||
/* ============================================================
|
||
UI
|
||
============================================================ */
|
||
const trigRow=document.getElementById('trigRow');
|
||
Object.keys(REACTIONS.map).forEach(key=>{
|
||
const clipName=REACTIONS.map[key];
|
||
const btn=document.createElement('button');
|
||
btn.className='trig';
|
||
btn.innerHTML=`▶ ${key} <span class="tag">${clipName}</span>`;
|
||
btn.onclick=()=>{
|
||
if(key==='idle' || clipName===REACTIONS.idleDefault){ idleTime=0; goIdle(); }
|
||
else playClip(clipName);
|
||
};
|
||
trigRow.appendChild(btn);
|
||
});
|
||
|
||
const playBtn=document.getElementById('playBtn'), capBtn=document.getElementById('capBtn');
|
||
playBtn.onclick=()=>{ playing=!playing; playBtn.textContent=playing?'⏸ 일시정지':'▶ 재생'; playBtn.classList.toggle('on',playing); };
|
||
capBtn.onclick=()=>{ showCaption=!showCaption; capBtn.classList.toggle('on',showCaption); };
|
||
document.getElementById('speed').oninput=e=>{ speed=parseFloat(e.target.value); document.getElementById('speedV').textContent=speed.toFixed(2)+'×'; };
|
||
document.getElementById('imgFiles').onchange=e=>{
|
||
[...e.target.files].forEach(f=>{
|
||
const key=f.name.replace(/\.png$/i,"");
|
||
const im=new Image(); im.onload=()=>{ if(!images[key])loadOk++; images[key]=im; refreshInfo(); }; im.src=URL.createObjectURL(f);
|
||
});
|
||
};
|
||
|
||
loadAllAssets(); refreshInfo(); requestAnimationFrame(frame);
|
||
</script>
|
||
</body>
|
||
</html>
|