diff --git a/HANDOFF.md b/HANDOFF.md deleted file mode 100644 index 314e66f..0000000 --- a/HANDOFF.md +++ /dev/null @@ -1,87 +0,0 @@ -> ⚠️ **아카이브** (소스 자산 생성 시기 기록, 이동된 소스 폴더 참조 포함). **현재 베이스 = `INTERACTIVE_RIG_HANDOFF.md` + `*_Profile/` + `README.md`.** - -# HANDOFF — Dansori 캐릭터 자산 & Character Builder (세션 인수인계) - -> 작성: 2026-07-02 (같은 날 저녁 갱신). 다른 세션에서 이어서 진행하기 위한 요약. -> **최신**: NewImage 88장을 각 `Images/`로 **이동 완료** · `Characters`/`Characters.Old` **삭제 예정** · 이미지 생성 계속 후 **내일(2026-07-03) 이어서**. -> 이 문서 = **캐릭터 자산 시스템(`Characters_Build_Docs`) + 조합 앱(`Character_Builder`)** 진행/계획. -> DansoriEQ **본체 앱 UI** 작업(메인화면 캐릭터 배치·전 창 네온·부유 동기화 등)은 `../docs/HANDOFF.md §8` 참조. - ---- - -## 0. 한 줄 요약 -Dansori 브랜드 마스코트(이소리·이단·듀오)를 **페이퍼돌 조립 방식**으로 관리하는 자산 체계와, -이를 **직접 보고 조합/저장(.md)** 하는 WPF 앱을 구축했다. **이미지 대량 생성은 진행 중**이며, -빌더 테스트는 **이미지가 충분히 생성된 뒤** 진행 예정. - -## 1. 자산 시스템 구조 (`Characters_Build_Docs/`) -``` -README.md # 브랜드 자산 규칙 + "새 캐릭터 추가 Playbook"(STEP 1~7) ← 단일 진실원 -HANDOFF.md # (이 문서) -LeeSori/ # 이소리(민트/여) - LeeSori.md # 토대(정체성·조립규칙·네이밍·§8 새 베리에이션 절차·23 표준 바디 목록) - USAGE_FOR_DEV.md # 앱 통합 가이드(캐릭터 공통) — 조립/앵커/포터블 규칙 - Reference/sori_sheet.png - Base/Base.md (+Images/) # 트랙슈트 헤드리스 바디: 파츠5 + 제스처18 = 23블록 - Hair/Hair.md (+Images/) # 4모양(short/long/waveS/waveL) 개별블록 88 (머리+표정20+hairmask ×4) - Hair/Hair_{Long,Short,WaveS,WaveL}Neat.md # 단정·윤기 추가본 각 22 → 헤어 토큰 총 8종(원본4 + neat4) - Accessories/Accessories.md (+Images/) # 오버레이 11(착용7+소품4) - Variations/{DressShort,DressLong,Jeans,Tshirt,CeoPantsuit}/*.md (+Images/) # 각 23블록 -LeeDan/ # 이단(시안/남) — LeeSori와 동일 구조 - LeeDan.md · Reference/dan_sheet.png - Base/Base.md(23) · Hair/Hair.md(88: short/buzz/messy/slick ×22) · Accessories(3: headphones/smartwatch/cap) - Variations/{Suit,Casual,Club}/*.md(각 23) -LeeSori_and_LeeDan/ # 듀오(베이크드 장면, 리그 아님) - Duo.md · Reference/{combined_master_sheet,sori_sheet,dan_sheet}.png - Scenes/Scenes.md(+Images/) # 7장: backtoback·sidebyside·fistbump·present·wave·success·earbud_share -Character_Builder/ # WPF 조합 앱 (아래 §4) -``` -- 구버전 레이어식 요청서는 `../docs/IMAGE_GEN_*`(및 `*.old`)에 **보존**(다른 세션 작업중, 건드리지 않음). 이 폴더는 그 내용을 캐릭터 중심으로 재정리한 독립본. - -## 2. 페이퍼돌 조립 규칙 (모든 캐릭터 공통) — 상세 `README.md` -- 완성 캐릭터 = **헤드리스 바디(복장)** + **머리(헤어모양)** + **표정 프레임** + **악세서리(오버레이)** + **색상(코드 틴트)**. -- **정합 앵커**: 목(바디↔머리)·어깨·정수리/귀·목선·손목·발. -- **동작은 코드**(끄덕/갸웃/호흡/blink/talk = 트랜스폼+프레임 교체) — 이미지 아님. -- **색상은 코드**(hairmask로 hue-shift) — 색 변형 이미지 생성 안 함. -- 생성용 md = **영어 프롬프트 위주**, `Reference/_sheet.png` 첨부 명시, 결과는 **각 md 옆 `Images/`**에 저장. - -## 3. 이미지 생성 현황 (갱신 2026-07-02 저녁) -- **목표 총량 ≈ 516장**(md 이미지 블록 기준): 이소리 ~326 · 이단 183 · 듀오 7. -- **완료·배치됨: 88장(이소리)** — `Characters/NewImage`에서 각 `Images/`로 **이동 완료**(미일치 0): - - `LeeSori/Base/Images` **23**(트랙 바디 전부) · `LeeSori/Accessories/Images` **11**(전부) · `LeeSori/Hair/Images` **54**(short·long 완성 + waveS/waveL 일부) -- `Characters/NewImage` **잔여 0**. -- **미생성 ≈430**: 이소리 헤어 나머지(waveS/waveL 잔여 + **neat 4종 88**) + 복장변형 116 · **이단 183 전부** · **듀오 7**. -- **앞으로**: 생성물은 임시폴더 말고 **각 md 옆 `Images/`에 바로 저장**(분류 불필요). 순서 = 각 캐릭터 `_RUN_ORDER.md`. - -## 4. Character Builder (WPF 앱) — `Character_Builder/` -- **상태**: **빌드 성공(오류0)**. `Character_Builder/bin/Debug/net8.0-windows/Character_Builder.exe`. - - 빌드: `dotnet build Characters_Build_Docs/Character_Builder/Character_Builder.csproj` (외부 패키지 없음, 오프라인 OK). -- **기능**: 캐릭터 리스트(시트 썸네일) → 선택 → 파츠 조합(바디/헤어모양/표정/헤어색/악세서리) → - **미리보기 합성** → **.md 저장(파일명 지정)/불러오기** → **제스처 미리보기**(포즈순환+깜빡임+부유). -- **자동 정렬**: `AlphaTools.cs` 가 알파(불투명) 분석으로 목 위치 검출 → 머리를 바디 목에 스냅 + 비율 스케일. - 캐릭터/바디/헤어 변경 시 자동, [🎯 머리 자동 정렬] 버튼으로 재실행, 슬라이더로 미세조정. -- **.md(빌드파일) = Dansori 앱 개발용**: 좌표 규약(stage 520×680, 각 레이어 Height=680 Uniform·중심·center scale·(x,y)px)을 - .md 헤더에 명시. 앱은 트랜스폼 직접 사용 또는 동일 정렬 알고리즘 재사용(`AlphaTools` 재활용). -- **테스트 보류**: 사용자가 **이미지가 충분히 생성된 뒤 테스트** 예정(내일). - - 현재 `LeeSori/{Base,Accessories,Hair}/Images`에 88장 배치됨 → **이소리 + short/long 헤어 + 트랙 바디 + 악세서리** 조합은 지금도 미리보기 가능. - -## 5. DansoriEQ 본체 앱 (요약 — 상세 `../docs/HANDOFF.md §8`) -- 메인화면 EQ 그래프 좌측 배경에 **이소리(단독)** 크게 배치, 그래프 좌측이동/축소, 창 1600×980. -- 배경 이소리 **부유/말하기**를 아바타와 동기(`MainWindow.SetBgMascotTalking`). -- **전 팝업 창 모던 타이틀(FluentWindow+ui:TitleBar) + 파란 네온 프레임**, MainWindow 네온. -- 다음 코드작업: 배경 이소리 **통짜→헤드리스 리그 교체**(파츠 합성+표정프레임+고개회전+hairmask 틴트+악세 오버레이). - -## 6. 앞으로 할 일 (우선순위) -1. **이미지 생성 계속** (사용자, 외부 이미지 AI): 각 `_RUN_ORDER.md` 순서로 **이소리 나머지 → 이단 전체 → 듀오**. 결과는 **각 md 옆 `Images/`에 바로 저장**(분류 불필요). -2. ✅ **NewImage → 각 `Images/` 분류 완료** (88장 이동, 미일치 0). -3. **구 자료 삭제**(사용자 직접, 예정): `../Characters`, `../Characters.Old`. - - 안전성 확인됨: 앱은 자체 `src/DansoriEQ.App/Assets/Characters`(13장) 사용, 시트는 Build_Docs에 백업. - - 유일본 손실 주의: Characters.Old의 구 파츠·흰배경 백업·추가포즈(새 파이프라인이 대체 예정). -4. **Character Builder 테스트**(이미지 후) → 자동정렬 정확도 튜닝(머리 스케일 비율 현재 0.32, 목밴드 6%), - 필요 시 **드래그 이동**·색상 커스텀 추가. -5. **앱 통합**: `LeeSori/USAGE_FOR_DEV.md` 규칙대로 DansoriEQ(및 향후 Dansori 앱)에 리그 통합. -6. **폴더 이동**: `Characters_Build_Docs`를 Dansori 브랜드 루트로 이동(포터블 규칙 — 절대경로 없음, 통째 이동 가능). - -## 7. 유지할 결정/제약 -- 색상=코드(이미지X) · 절대경로 하드코딩 금지 · 헤드리스 바디+목 앵커 · 듀오=베이크드 장면 · - 생성 md=영어 프롬프트+시트 첨부 · 빌드 .md=앱 개발용(좌표규약 명시) · 모든 신규 이소리 베리에이션은 기본 리깅 상속. diff --git a/HARUKA_IMAGE_PROGRESS_HANDOFF.md b/HARUKA_IMAGE_PROGRESS_HANDOFF.md deleted file mode 100644 index 574401b..0000000 --- a/HARUKA_IMAGE_PROGRESS_HANDOFF.md +++ /dev/null @@ -1,47 +0,0 @@ -> ⚠️ **아카이브** (소스 이미지 생성 기록). 하루카는 완성됨 — 현재 베이스 = `INTERACTIVE_RIG_HANDOFF.md` + `Haruka_Profile/`. - -# Haruka Image Generation Progress Handoff - -Updated: 2026-07-03 16:32 KST - -## Scope -- This session handled **Haruka only**. -- `Isabel/` was intentionally excluded because it is being processed in another parallel session. - -## Result -- Haruka `_RUN_ORDER.md` target set is complete. -- Total Haruka targets: **100** -- Verified OK: **100** -- Missing: **0** -- Bad alpha / bad format: **0** - -## Completed Groups -- `Haruka/Reference`: 1 / 1 -- `Haruka/Hair/Images`: 22 / 22 -- `Haruka/Base/Images`: 23 / 23 -- `Haruka/Accessories/Images`: 7 / 7 -- `Haruka/Variations/Idol/Images`: 23 / 23 -- `Haruka/Variations/Witch/Images`: 24 / 24 - -## Alpha Verification -All Haruka target PNGs were checked with `System.Drawing.Bitmap`. - -Required condition: -- `PixelFormat == Format32bppArgb` -- Four corner pixels have `alpha == 0` - -Verification summary: -```text -Haruka target total=100 ok=100 missing=0 bad=0 -Haruka\Accessories\Images: total=7 ok=7 missing=0 bad=0 -Haruka\Base\Images: total=23 ok=23 missing=0 bad=0 -Haruka\Hair\Images: total=22 ok=22 missing=0 bad=0 -Haruka\Reference: total=1 ok=1 missing=0 bad=0 -Haruka\Variations\Idol\Images: total=23 ok=23 missing=0 bad=0 -Haruka\Variations\Witch\Images: total=24 ok=24 missing=0 bad=0 -``` - -## Notes -- Generated sources were produced with a flat chroma-key background, then converted locally with the installed `remove_chroma_key.py` helper. -- Existing images were not AI-edited for transparency; alpha was produced by local pixel-based chroma-key removal. -- The final assets are saved beside their source md files under each `Images/` folder, following `_RUN_ORDER.md`. diff --git a/Haruka_Live2D/01_Overview/Decisions.md b/Haruka_Live2D/01_Overview/Decisions.md deleted file mode 100644 index b0cfef8..0000000 --- a/Haruka_Live2D/01_Overview/Decisions.md +++ /dev/null @@ -1,50 +0,0 @@ -# 확정 결정 로그 (Decisions) - -> 논의를 통해 확정된 결정과 근거. 뒤집을 땐 여기에 사유와 함께 갱신. - -## D1. 표현 방식 = 하이브리드 (확정) -리그 + 베이크드 포즈 + 표정 프레임 스왑을 상황별로 조합. -- **근거**: 리그는 앰비언트/열린 제스처에 강하고, 팔짱·하트 같은 자기-가림 포즈는 베이크드 이미지가 자연스럽다. 각자의 강점만 사용. - -## D2. 구현 레벨 = 코드 네이티브 경량 리그 (확정, Live2D/Spine 배제) -- **근거**: Live2D/Spine의 리깅은 **독점 GUI 에디터에서 사람이** 하는 작업 → **AI 자동화 목적과 상충**. 우리 코드로 리그/모션/반응을 소유하면 데이터(JSON)만으로 자동화·반복이 가능. - -## D3. 분절 = 완전 해부학 16파츠 (확정) -head·neck·chest·pelvis + (상완·전완·손)×2 + (허벅지·종아리·발)×2. -- **근거**: 팔꿈치·무릎·손목·목·허리가 실제로 접혀야 제스처/춤이 자연스럽다. - -## D4. 얼굴 = 표정 프레임 스왑 (확정) -20종 표정 이미지 교체 + 말하기 = talk 프레임 순환(유사 립싱크). -- **한계 인지**: 눈+입이 세트로 고정 → "감정+정밀 립싱크 동시"는 불가. 필요 시 D7로 승급. - -## D5. 자기-가림 포즈 = 베이크드 이미지 (확정) -팔짱(armscross)·하트(heart) 등은 리그 보간 대신 **통짜 포즈 이미지**로. (기존 표준 18제스처 자산 재사용.) - -## D6. 투명 알파 필수 (확정) -모든 파츠/프레임 = 32-bit RGBA(`Format32bppArgb`), 배경 alpha=0. 24-bit·매트 배경 금지. - -## D7. mesh-warp(그리드 변형) = 옵션·후속 (보류) -목/얼굴 국소 mesh-warp(WebGL)로 목 이음새·정밀 립싱크·중간 각도 고개돌림을 승급. -- **승급 조건**: 강체 리그로 목/얼굴이 실제로 부족할 때, 그 부위에만 국소 도입. 전신 적용 안 함. - -## D8. 이미지 = ChatGPT 자동생성 (확정) -사람이 안 그림. 생성용 `.md` 스펙을 우리가 제공. - -## D9. 색상·모션 = 코드/데이터 (확정) -색 변형 = hairmask hue-shift. 모션 = 리그 클립. 반응 = 시퀀서 데이터. - -## D10. 프로필 구조 (확정) -`Haruka_Profile` 구조를 복제해 **`Haruka_Profile`** 로 운용(캐릭터별 자료 구조 표준). 시트 표준 위치 = `03_Assets/Reference/haruka_sheet.png`. - -## D11. 리그 파츠 생성 = 마스터-슬라이스 우선, 개별생성 폴백/attachment (확정) -- 핵심 16파츠는 **마스터 1장 → 로컬 슬라이스**(같은 좌표계 → 관절 자동 정합, 접합 오차↓)가 **1순위**. 파츠 개별 생성은 그 **폴백**(같은 16파츠를 만드는 대체 방법 — 둘 다 만들 필요 없음). -- **슬라이스 출력 = 풀캔버스**: 각 파츠는 **크롭 없이 마스터와 동일한 520×900 캔버스에 제자리 배치**(그 외 투명). 16장 스택 시 마스터 복원 → 위치정보 보존, 앵커 튜닝 불필요. (타이트 크롭하면 위치정보가 사라져 정합이 깨짐.) -- **단 마스터에 없는 변형 파츠**(핑거하트·주먹·가리킴 등 대체 손 attachment)는 **개별 생성으로만** 가능 → 그 용도엔 개별 생성이 별도로 필요. -- **근거**: 슬라이스는 좌표 정합에 강함(반복 수정 원인 제거). 생성 AI는 픽셀 좌표를 못 맞추므로 접합 좌표는 **생성 후 이미지에서 측정**(정규화 앵커 `imgAnchor`)해 `rig.json`에 저장. - -## 열린 결정 (미확정) -- **O1. 최종 런타임 호스트**: 프로토타입=웹(Canvas). 본체=WPF. WPF에 동일 리그/시퀀서를 이식(C#) 할지, 아니면 WebView2로 웹 런타임을 임베드할지 → `../08_Roadmap/App_Integration.md` 에서 결정 예정. -- **O2. 대사 표시**: 말풍선 캡션 vs TTS 음성 vs 둘 다. - - - diff --git a/Haruka_Live2D/01_Overview/Purpose_and_Direction.md b/Haruka_Live2D/01_Overview/Purpose_and_Direction.md deleted file mode 100644 index 3881e79..0000000 --- a/Haruka_Live2D/01_Overview/Purpose_and_Direction.md +++ /dev/null @@ -1,29 +0,0 @@ -# 목적과 방향 (Purpose & Direction) - -## 최종 목적 -하루카를 **앱에 탑재된 인터랙티브 마스코트**로 만든다. 사용자의 행동·앱 상태(상황)에 따라 캐릭터가 **적절한 제스처·표정·대사로 반응**해 살아있는 느낌을 준다. - -## 대표 사용 시나리오 (상황 → 반응) -| 상황(트리거) | 반응 | -|---|---| -| 오류/금지된 동작 | 팔짱 끼고 인상 쓰며 고개 저으며 **"안돼요"** | -| 성공/완료/칭찬 | 손 하트 그리며 밝게 **"잘됐어요"** | -| 대기/유휴(배경) | 가볍게 **춤추는** 루프(앰비언트) | -| (확장) 인사 | 손 흔들며 "안녕하세요" | -| (확장) 안내/설명 | 한 손 제시(present) + 말하기 | -| (확장) 생각중/로딩 | 갸웃 + thinking 표정 | -> 확장 반응은 같은 프레임워크로 계속 추가한다(`../06_Reactions/Reactions.md`). - -## 방향성 (핵심 원칙) -1. **AI 자동화**: 모든 캐릭터 이미지는 **ChatGPT로 생성**(각 생성용 `.md` 스펙 제공). 사람이 그리지 않는다. -2. **동작·색상은 코드/데이터**: 모션(리그 클립)·반응 시퀀스·색 변형(hairmask hue-shift)은 이미지가 아니라 코드/데이터. → 재사용·자동화·경량. -3. **하이브리드 표현**: 상황에 맞춰 **리그(앰비언트/열린 제스처)** + **베이크드 포즈(자기-가림 포즈)** + **표정 프레임 스왑(감정/말하기)** 을 조합. -4. **경량·포터블**: 에디터/외주 없이 **우리 코드**로 리그·모션·반응을 소유. 데이터(JSON)는 뷰어(웹)와 WPF 앱이 동일하게 사용. -5. **투명 알파 필수**: 모든 파츠/프레임은 32-bit RGBA(`Format32bppArgb`), 배경 alpha=0. -6. **점진적 품질 상향**: 강체 리그로 시작 → 필요한 곳(목/얼굴)만 **mesh-warp** 국소 승급(옵션). - -## 범위 밖(당분간 안 함) -- Live2D/Spine 도입(GUI 리깅=자동화와 상충). 전신 mesh-warp. 3D. 정밀 음소 립싱크(얼굴 mesh-warp 승급 시 재검토). - - - diff --git a/Haruka_Live2D/02_Architecture/Architecture.md b/Haruka_Live2D/02_Architecture/Architecture.md deleted file mode 100644 index a499d01..0000000 --- a/Haruka_Live2D/02_Architecture/Architecture.md +++ /dev/null @@ -1,53 +0,0 @@ -# 아키텍처 (Architecture) - -## 레이어 모델 -``` -[상황 이벤트] 예: "error" / "success" / "idle" - │ - ▼ -[트리거 매퍼] reactions.json 상황키 → 반응 클립 이름 - │ - ▼ -[반응 시퀀서] clips/.json 타임라인으로 아래 레이어들을 조율 - ├─ Body 레이어 ── 리그 클립(rig.json+track) │ 또는 │ 베이크드 포즈 이미지 - ├─ Face 레이어 ── 표정 프레임 스왑(neutral/negative/love/…) - ├─ Mouth 레이어 ── 말하기(talk 프레임 순환, 유사 립싱크) - ├─ Transform 레이어 ── 리그 위에 덧입히는 잔모션(고개젓기·바운스) - └─ FX/Caption 레이어 ── 말풍선·효과음(옵션) - │ - ▼ -[컴포지터] 파츠 합성 + 표정 오버레이 + 앵커 정렬(AlphaTools 재활용) - │ - ▼ -[렌더러] Canvas(웹 프로토타입) / WPF(본체) — 60fps -``` - -## 레이어 책임 -- **Body**: 캐릭터 몸의 자세/모션. 두 모드. - - `rig` 모드 = 16파츠 리그를 클립으로 구동(앰비언트·열린 제스처·전환). - - `baked` 모드 = 통짜 포즈 이미지 1장(자기-가림 포즈: 팔짱·하트). -- **Face**: 감정 = 표정 프레임 교체(머리 이미지 스왑). -- **Mouth**: 말하기 = talk/neutral 프레임 순환(유사 립싱크). *정밀 립싱크는 mesh-warp 승급 시.* -- **Transform**: 리그 본에 delta를 더하는 잔모션(고개 좌우·호흡·바운스). Body가 baked여도 전체 트랜스폼은 적용 가능. -- **FX/Caption**: 말풍선 텍스트·효과음(옵션). - -## 하이브리드 선택 규칙 (언제 무엇을) -| 상황 | Body 모드 | 근거 | -|---|---|---| -| 배경춤·유휴·호흡 | **rig** | 부드러운 앰비언트, 자산 최소 | -| 손 흔들기·가리키기·제시·박수 | **rig** | 열린 자세 → 리그로 자연스러움 | -| 고개 끄덕/젓기 | **rig**(Transform) | 작은 각도 + 가림 | -| 팔짱·핑거하트·볼하트 | **baked** | 손이 몸에 겹침 → 리그는 뚫림/어색 | -| 큰 감정 포즈(만세·좌절) | **baked** 또는 rig(joy/cheer) | 필요 정밀도에 따라 | - -## 전환 (transition) -- rig→rig: 트랜스폼 보간(부드럽게). -- rig↔baked: 짧은 **크로스페이드**(150~250ms) 또는 리그로 근사 진입 후 스냅. -- 반응 종료 후 `return` 지정 클립(보통 `idle`/`dance_idle`)으로 복귀. - -## 데이터 재사용 -- `rig.json`·클립·`reactions.json`·표정/포즈 이미지는 **웹 뷰어와 WPF가 동일하게** 사용(플랫폼 독립 데이터). -- 앵커 정렬은 기존 `Character_Builder/AlphaTools.cs`(알파 기반 목/어깨 검출) 로직을 재활용. - - - diff --git a/Haruka_Live2D/02_Architecture/Limits_and_Mitigations.md b/Haruka_Live2D/02_Architecture/Limits_and_Mitigations.md deleted file mode 100644 index 639d9c0..0000000 --- a/Haruka_Live2D/02_Architecture/Limits_and_Mitigations.md +++ /dev/null @@ -1,29 +0,0 @@ -# 한계와 완화 (Limits & Mitigations) - -강체 컷아웃 + 프레임 스왑의 본질적 한계와, 우리가 쓰는 완화책. 그리고 mesh-warp 승급 기준. - -## L1. 관절 이음새 (강체 회전) -- **문제**: 파츠를 크게 회전하면 관절 경계가 벌어지거나 겹침(특히 목). 분절을 늘려도 **근육 연속성은 해결 안 됨**(강체의 본질). -- **완화**: ① 회전 각도 작게 ② 피벗 낮게 ③ **근위단 오버랩 스텁**(부모가 틈을 덮음) ④ 옷깃/머리/초커로 **가림** ⑤ z-순서. -- **잔여 위험**: 큰 각도 고개돌림·큰 팔동작은 여전히 티남 → baked 포즈로 우회 또는 L4 승급. - -## L2. 립싱크 (프레임 스왑 얼굴) -- **문제**: 표정이 눈+입 세트 고정 → "특정 감정 + 정밀 입모양" 동시 불가. -- **완화**: 감정 표정 + 고개짓 + talk/neutral 프레임 순환으로 **뉘앙스 전달**. 필요하면 **감정+talk 조합 머리**를 소수 추가 생성(`../03_Assets/Expressions_and_Poses.md`). -- **잔여 위험**: 음소 단위 정밀 립싱크는 불가 → L4 승급. - -## L3. 자기-가림 포즈 -- **문제**: 팔짱·하트 등 손이 몸/반대팔에 겹치는 포즈는 리그로 뚫림. -- **완화**: **baked 포즈 이미지**로 대체(기존 18제스처 자산). 진입은 크로스페이드. - -## L4. mesh-warp 승급 (옵션·후속) -- **무엇**: 목/얼굴에 그리드 메시를 씌워 **피부 늘어남·입/눈썹 독립 변형·중간 각도 고개돌림**을 구현(WebGL). 우리 런타임 내 구현 → 자동화 유지. -- **승급 조건(하나라도 강하게 필요할 때)**: (a) 목 이음새가 baked/가림으로도 부족 (b) 감정+정밀 립싱크 동시 필요 (c) 중간 각도 고개돌림 필요. -- **한계(승급해도)**: 큰 각도(대략 30°↑) 고개돌림은 **가려진 반대면이 없어** 여전히 각도별 머리 아트 추가가 필요. 자동 워프가 없는 면을 만들지는 못함. -- **원칙**: 전신 아님, **국소(neck/head)만**. 품질 튜닝은 스크린샷 피드백 루프. - -## 요약 -> 강체+프레임스왑으로 **대부분의 상황 반응은 충분히 자연스럽게** 만든다(가림·baked·잔모션 조합). 정밀 립싱크/큰 고개돌림만 별도 승급(L4/각도 아트) 대상. - - - diff --git a/Haruka_Live2D/03_Assets/Assets_Overview.md b/Haruka_Live2D/03_Assets/Assets_Overview.md deleted file mode 100644 index c49a455..0000000 --- a/Haruka_Live2D/03_Assets/Assets_Overview.md +++ /dev/null @@ -1,30 +0,0 @@ -# 자산 전체 맵 (Assets Overview) — Haruka - -> ✅ **완성** — 리그·소스 이미지·Library·반응 런타임 모두 완비. 프로필 자립(소스 원본은 별도 아카이브). - -## 자산 (전부 프로필 내) -| 종류 | 개수 | 위치 | -|---|---|---| -| 시트 | 1 | `Reference/haruka_sheet.png` | -| 리그 파츠(마스터+16 풀캔버스) | 17 | `Parts/Images/` — 피벗 산출·배경춤 검증 완료 | -| 베이크드 포즈 | 54 | `Library/BakedPoses/` (Sailor·Idol·Witch 각 18) | -| 레거시 파츠 | 15 | `Library/CoarseParts/` (의상별 5) | -| 표정 프레임 | 20 (twin) | `Library/Heads/` (반응 head base `haruka_head_twin`) | -| hairmask / 악세서리 | 1 / 8 | `Library/Hairmasks/` · `Accessories/`(마녀모자 포함) | -| `_layout.json` | — | `06_Reactions/` (반응 목 정합) | - -## 뷰어 (더블클릭 재생) -- 배경춤: `07_Viewer/index.html` -- 반응: `07_Viewer/reactions.html` — 트리거 idle / error / success - -## dance 튜닝 (occlusion-aware) -세일러 **블라우스↔스커트가 별개 의상** → `dance_idle`에서 **chest 리지드**(허리 봉인). 블라우스가 얇아 어깨 소켓 틈이 나기 쉬워 **팔 진폭 축소**(upperarm ±4), 생동감은 소매 안 forearm/hand로. - -## 반응 매핑 -| 상황 | Body(baked) | Face | -|---|---|---| -| error | `haruka_body_sailor_armscross` | `haruka_head_twin_negative` | -| success | `haruka_body_sailor_heart` | `haruka_head_twin_love` | - - - diff --git a/Haruka_Live2D/03_Assets/Expressions_and_Poses.md b/Haruka_Live2D/03_Assets/Expressions_and_Poses.md deleted file mode 100644 index e8c53b1..0000000 --- a/Haruka_Live2D/03_Assets/Expressions_and_Poses.md +++ /dev/null @@ -1,39 +0,0 @@ -# 표정 & 베이크드 포즈 (Expressions & Poses) - -반응 시퀀서의 **Face 레이어**(표정)와 **Body baked 모드**(포즈)가 쓰는 기존 자산 목록. 출처는 기존 Haruka 생성 md. - -## 표정 프레임 20종 (Face 레이어) -출처: `(소스 아카이브)`(및 neat 변형). 헤어모양별로 동일 세트. -``` -neutral · blink · talk · talk_wide · smile · positive · negative · confused · wink · -surprised · laugh · thinking · cool · love · shy · sad · pout · sleepy · proud · playful -``` -- **말하기**: `talk` ↔ `talk_wide` ↔ (해당 감정 프레임) 순환으로 입 움직임 근사. -- **감정 표현**: 위 목록에서 상황에 맞는 프레임 선택. - -## 베이크드 포즈 18종 (Body baked 모드) -출처: `(소스 아카이브)` (표준 제스처 바디, 헤드리스). 파일 `haruka_body_sailor_`. -``` -idle_full · idle_upper · wave · handwave · listen · present · dj · piano · control · -thumbsup · heart · clap · peace · armscross · shrug · point · cheer · joy -``` -+ 파츠 5(apose·torso·arm_r·arm_l·legs) = 표준 23. - -## 반응 3종이 쓰는 조합 -| 반응 | Body | Face(감정) | Mouth | -|---|---|---|---| -| gesture_no | baked `armscross` | `negative` (또는 `pout`) | "안돼요" (negative↔talk 순환) | -| gesture_heart | baked `heart` | `love`/`positive`/`smile` | "잘됐어요" (love↔talk 순환) | -| dance_idle | rig 16파츠 | `smile`/`neutral` | — | - -## (옵션) 감정+talk 조합 머리 — 립싱크 강화용 -프레임 스왑 한계(L2)로 "감정 유지 + 입만 움직임"이 안 될 때만 소수 신규 생성. -- 후보: `haruka_head__negative_talk`, `haruka_head__love_talk` (+ positive_talk) -- **판단**: 먼저 표정↔talk 순환으로 충분한지 확인 후 결정(불충분하면 생성 또는 mesh-warp L4). - -## 주의 -- Face 프레임은 **선택한 헤어모양 세트**와 일치해야 함(예: short 바디엔 short 표정). -- 모든 프레임/포즈는 투명 알파 32-bit(`Format32bppArgb`). - - - diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_boots.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_boots.png deleted file mode 100644 index 84946b1..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_boots.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_catear.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_catear.png deleted file mode 100644 index fa08013..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_catear.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_glowstick.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_glowstick.png deleted file mode 100644 index b964ec8..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_glowstick.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_hairclip.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_hairclip.png deleted file mode 100644 index ff0ee46..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_hairclip.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_headphones.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_headphones.png deleted file mode 100644 index e1c7cdb..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_headphones.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_ribbon.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_ribbon.png deleted file mode 100644 index 281aee6..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_ribbon.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_sneakers.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_sneakers.png deleted file mode 100644 index c39ec02..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_haruka_sneakers.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Accessories/acc_hat_witch.png b/Haruka_Live2D/03_Assets/Library/Accessories/acc_hat_witch.png deleted file mode 100644 index 2cb9d96..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Accessories/acc_hat_witch.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_armscross.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_armscross.png deleted file mode 100644 index 603d8d1..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_armscross.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_cheer.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_cheer.png deleted file mode 100644 index 8e78f71..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_cheer.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_clap.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_clap.png deleted file mode 100644 index 23e5b2a..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_clap.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_control.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_control.png deleted file mode 100644 index a2395d2..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_control.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_dj.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_dj.png deleted file mode 100644 index 9f18cca..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_dj.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_handwave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_handwave.png deleted file mode 100644 index 9513795..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_handwave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_heart.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_heart.png deleted file mode 100644 index ba42990..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_heart.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_full.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_full.png deleted file mode 100644 index 1feac96..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_full.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_upper.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_upper.png deleted file mode 100644 index ccb3ec5..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_idle_upper.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_joy.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_joy.png deleted file mode 100644 index 447fe67..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_joy.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_listen.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_listen.png deleted file mode 100644 index 7ba3a2c..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_listen.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_peace.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_peace.png deleted file mode 100644 index a0ffd53..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_peace.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_piano.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_piano.png deleted file mode 100644 index b754c84..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_piano.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_point.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_point.png deleted file mode 100644 index ca2cbd6..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_point.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_present.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_present.png deleted file mode 100644 index 1ef2dfd..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_present.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_shrug.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_shrug.png deleted file mode 100644 index 27234dd..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_shrug.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_thumbsup.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_thumbsup.png deleted file mode 100644 index 105b083..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_thumbsup.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_wave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_wave.png deleted file mode 100644 index efdea33..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Idol/haruka_body_idol_wave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_armscross.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_armscross.png deleted file mode 100644 index 9d12973..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_armscross.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_cheer.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_cheer.png deleted file mode 100644 index 5437c78..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_cheer.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_clap.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_clap.png deleted file mode 100644 index 6360f56..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_clap.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_control.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_control.png deleted file mode 100644 index d30105c..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_control.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_dj.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_dj.png deleted file mode 100644 index 5220efb..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_dj.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_handwave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_handwave.png deleted file mode 100644 index 0f31118..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_handwave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_heart.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_heart.png deleted file mode 100644 index acc9abe..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_heart.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_full.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_full.png deleted file mode 100644 index 4784513..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_full.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_upper.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_upper.png deleted file mode 100644 index 030a08e..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_idle_upper.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_joy.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_joy.png deleted file mode 100644 index 1db4f56..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_joy.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_listen.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_listen.png deleted file mode 100644 index 00523a2..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_listen.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_peace.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_peace.png deleted file mode 100644 index 2c24dba..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_peace.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_piano.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_piano.png deleted file mode 100644 index 3c62868..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_piano.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_point.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_point.png deleted file mode 100644 index 49ec11f..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_point.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_present.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_present.png deleted file mode 100644 index 1e00f4e..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_present.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_shrug.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_shrug.png deleted file mode 100644 index 71dee1f..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_shrug.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_thumbsup.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_thumbsup.png deleted file mode 100644 index e5fef94..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_thumbsup.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_wave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_wave.png deleted file mode 100644 index 904bba5..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Sailor/haruka_body_sailor_wave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_armscross.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_armscross.png deleted file mode 100644 index 51c50fe..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_armscross.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_cheer.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_cheer.png deleted file mode 100644 index 367e812..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_cheer.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_clap.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_clap.png deleted file mode 100644 index 86f3600..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_clap.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_control.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_control.png deleted file mode 100644 index ceb3338..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_control.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_dj.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_dj.png deleted file mode 100644 index 88e4034..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_dj.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_handwave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_handwave.png deleted file mode 100644 index da6e868..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_handwave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_heart.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_heart.png deleted file mode 100644 index 8f6ba18..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_heart.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_full.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_full.png deleted file mode 100644 index 2f003b4..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_full.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_upper.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_upper.png deleted file mode 100644 index f8dc7b2..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_idle_upper.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_joy.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_joy.png deleted file mode 100644 index 2934f85..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_joy.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_listen.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_listen.png deleted file mode 100644 index 8cda319..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_listen.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_peace.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_peace.png deleted file mode 100644 index 0b5c69d..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_peace.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_piano.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_piano.png deleted file mode 100644 index 9eea692..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_piano.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_point.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_point.png deleted file mode 100644 index 21d01ca..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_point.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_present.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_present.png deleted file mode 100644 index 59a2566..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_present.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_shrug.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_shrug.png deleted file mode 100644 index 5121b34..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_shrug.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_thumbsup.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_thumbsup.png deleted file mode 100644 index fd8d259..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_thumbsup.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_wave.png b/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_wave.png deleted file mode 100644 index e3352fc..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/BakedPoses/Witch/haruka_body_witch_wave.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_apose.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_apose.png deleted file mode 100644 index 7ce54d7..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_apose.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_l.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_l.png deleted file mode 100644 index a2ac2eb..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_r.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_r.png deleted file mode 100644 index af2bf79..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_arm_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_legs.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_legs.png deleted file mode 100644 index fd67e89..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_legs.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_torso.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_torso.png deleted file mode 100644 index 692643b..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Idol/haruka_body_idol_torso.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_apose.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_apose.png deleted file mode 100644 index 88c21e6..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_apose.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_l.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_l.png deleted file mode 100644 index ff2455b..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_r.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_r.png deleted file mode 100644 index 7cc0c98..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_arm_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_legs.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_legs.png deleted file mode 100644 index 34eaece..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_legs.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_torso.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_torso.png deleted file mode 100644 index 07f2613..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Sailor/haruka_body_sailor_torso.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_apose.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_apose.png deleted file mode 100644 index d492962..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_apose.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_l.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_l.png deleted file mode 100644 index 8daf394..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_r.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_r.png deleted file mode 100644 index ac2a18a..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_arm_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_legs.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_legs.png deleted file mode 100644 index 30909ff..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_legs.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_torso.png b/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_torso.png deleted file mode 100644 index 32891b8..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/CoarseParts/Witch/haruka_body_witch_torso.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Hairmasks/haruka_hairmask_twin.png b/Haruka_Live2D/03_Assets/Library/Hairmasks/haruka_hairmask_twin.png deleted file mode 100644 index cf1eee9..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Hairmasks/haruka_hairmask_twin.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin.png deleted file mode 100644 index c3d4473..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_blink.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_blink.png deleted file mode 100644 index 1fb33c9..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_blink.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_confused.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_confused.png deleted file mode 100644 index 4316ab8..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_confused.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_cool.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_cool.png deleted file mode 100644 index 809725a..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_cool.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_laugh.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_laugh.png deleted file mode 100644 index 1e47f2a..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_laugh.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_love.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_love.png deleted file mode 100644 index b61b171..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_love.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_negative.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_negative.png deleted file mode 100644 index 974b656..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_negative.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_neutral.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_neutral.png deleted file mode 100644 index ecbe1dd..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_neutral.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_playful.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_playful.png deleted file mode 100644 index 5aaa6eb..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_playful.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_positive.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_positive.png deleted file mode 100644 index a968d63..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_positive.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_pout.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_pout.png deleted file mode 100644 index 8e92766..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_pout.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_proud.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_proud.png deleted file mode 100644 index 3d6972c..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_proud.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sad.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sad.png deleted file mode 100644 index 6555966..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sad.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_shy.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_shy.png deleted file mode 100644 index b11cb05..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_shy.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sleepy.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sleepy.png deleted file mode 100644 index d2e3d61..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_sleepy.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_smile.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_smile.png deleted file mode 100644 index 41a2fd6..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_smile.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_surprised.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_surprised.png deleted file mode 100644 index 03111a3..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_surprised.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk.png deleted file mode 100644 index 299df43..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk_wide.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk_wide.png deleted file mode 100644 index 8849c47..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_talk_wide.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_thinking.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_thinking.png deleted file mode 100644 index 56c6e6f..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_thinking.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_wink.png b/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_wink.png deleted file mode 100644 index cb2c2bf..0000000 Binary files a/Haruka_Live2D/03_Assets/Library/Heads/haruka_head_twin_wink.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Library/_README_분류.md b/Haruka_Live2D/03_Assets/Library/_README_분류.md deleted file mode 100644 index 38650c5..0000000 --- a/Haruka_Live2D/03_Assets/Library/_README_분류.md +++ /dev/null @@ -1,36 +0,0 @@ -# 이미지 라이브러리 — 용도별 분류 (Haruka) - -기존 `Haruka/` 의 완성 이미지(OLD 제외, **99장**)를 용도별로 복사해 통합 관리(복사본, 원본은 `Haruka/`). - -## 폴더 구성 -``` -03_Assets/ - Reference/ # haruka_sheet.png (표준 위치) - Parts/Images/ # ① 관절 분할 파츠 16 (신규·미생성) — RIG - Library/ - BakedPoses/ # ② 통짜 포즈 — Body baked - Sailor/ Idol/ Witch/ - CoarseParts/ # (레거시) 부분통짜 파츠 5/의상 - Sailor/ Idol/ Witch/ - Heads/ # 머리+표정 (twin) — Face - Hairmasks/ # hairmask (twin) - Accessories/ # 악세서리 오버레이(마녀모자 포함) -``` - -## 개수 (Library 99 + 시트 1 = 100) -| 분류 | 개수 | 비고 | -|---|---|---| -| BakedPoses | 54 | Sailor/Idol/Witch 각 18 포즈 | -| CoarseParts | 15 | 각 의상 5 파츠(레거시) | -| Heads | 21 | twin: head + 20 표정 | -| Hairmasks | 1 | twin | -| Accessories | 8 | 착용/소품 7 + `acc_hat_witch` | -| *(시트)* | 1 | `../Reference/haruka_sheet.png` | - -## 참고 -- 3의상 완성: **Sailor**(세일러 캐주얼·기본) · **Idol**(아이돌 무대) · **Witch**(할로윈 꼬마마녀·마녀모자 악세). -- 헤어는 **twin(트윈테일) 1모양** 완성(표정 21). -- 반응 baked는 `Sailor` 세트 사용(`haruka_body_sailor_armscross/heart` 등). - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/AI_PSD_Workflow.md b/Haruka_Live2D/03_Assets/Live2D/AI_PSD_Workflow.md deleted file mode 100644 index 5f560d4..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/AI_PSD_Workflow.md +++ /dev/null @@ -1,56 +0,0 @@ -# AI PSD 제작 워크플로 - -## 1. 입력 자료 지정 - -입력 자료는 다음 순서로 사용한다. - -1. `03_Assets/Reference/haruka_sheet.png` -2. `03_Assets/Parts/Images/haruka_part_master_apose.png` -3. `03_Assets/Library/Heads/` -4. `03_Assets/Library/BakedPoses/Track/` - -## 2. AI 생성 방식 - -AI 이미지 도구가 layered PSD를 만들 수 있으면 다음 파일을 만든다. - -- `haruka_live2d_material_separation.psd` -- `haruka_live2d_import.psd` - -PNG 레이어 방식으로 작업할 경우 다음 순서를 따른다. - -1. `layer_manifest.json`의 각 `file` 이름대로 투명 PNG 레이어를 생성한다. -2. 모든 PNG는 같은 캔버스 크기와 같은 원점 좌표를 유지한다. -3. Photoshop 또는 Clip Studio에서 PNG를 레이어로 쌓는다. -4. `Guide` 레이어는 숨김 처리한다. -5. import PSD는 각 파츠를 단일 레이어로 정리해 저장한다. - -## 3. 생성 프롬프트 핵심 문구 - -```text -Create Live2D Cubism-ready separated character art layers for the same adult woman Haruka. -Keep identical face identity, mint teal short hair, white headphones, black choker with teal pendant, -white cropped hoodie, mint/black track jacket, black track pants, black/mint sneakers. -Each layer must be transparent PNG, same artboard, same registration, no background, no white halo. -Paint hidden areas underneath overlaps so the model can rotate and deform in Live2D. -Use the exact layer id and file name from the manifest. -``` - -## 4. PSD import 전 검수 - -- RGB, 8bit/channel, sRGB. -- 레이어 이름 중복 없음. -- 불필요한 먼지 픽셀 없음. -- layer mask, clipping mask 잔여 없음. -- import PSD의 각 파츠는 단일 레이어. -- 눈, 입, 눈썹 레이어가 독립적으로 보인다. -- 머리카락 레이어가 앞, 옆, 뒤로 분리된다. - -## 5. Cubism import 후 검수 - -- ArtMesh가 각 파츠에 생성된다. -- texture atlas가 과도하게 낭비되지 않는다. -- `ParamEyeLOpen/ROpen`, `ParamMouthOpenY`, `ParamAngleX/Y/Z`, `ParamBodyAngleX/Y/Z`, `ParamBreath`가 정상 동작한다. -- 눈깜빡임, 입열림, 고개 좌우, 호흡이 자연스럽다. - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/Asset_Audit.md b/Haruka_Live2D/03_Assets/Live2D/Asset_Audit.md deleted file mode 100644 index 7ee747e..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/Asset_Audit.md +++ /dev/null @@ -1,34 +0,0 @@ -# Live2D 자산 체크리스트 - -## 입력 자료 - -| 항목 | 위치 | 용도 | -|---|---|---| -| 정체성 시트 | `03_Assets/Reference/haruka_sheet.png` | 얼굴, 헤어, 의상, 색상 기준 | -| A-pose 이미지 | `03_Assets/Parts/Images/haruka_part_master_apose.png` | 비율과 관절 위치 기준 | -| 표정 이미지 | `03_Assets/Library/Heads/*.png` | expression 목표 설정 | -| 포즈 이미지 | `03_Assets/Library/BakedPoses/*.png` | motion 키포즈 설정 | -| 머리 영역 이미지 | `03_Assets/Library/Hairmasks/*.png` | hair layer 분리 | -| 액세서리 이미지 | `03_Assets/Library/Accessories/*.png` | 소품 레이어 분리 | - -## 필수 산출물 - -1. `03_Assets/Live2D/LayerPNGs/*.png` -2. `03_Assets/Live2D/haruka_live2d_material_separation.psd` -3. `03_Assets/Live2D/haruka_live2d_import.psd` -4. `04_Rig/live2d_parameters.json` 기준 Cubism model -5. `05_Animation/live2d_motion_plan.json` 기준 motions -6. `06_Reactions/reactions.json` 기준 runtime map - -## 검수 항목 - -- required 레이어 전체 생성. -- PSD import 조건 충족: RGB, 8bit/channel, sRGB. -- 투명 배경 유지. -- 레이어명 중복 없음. -- 눈, 눈썹, 입, 머리카락, 손, 의상 겹침 부위 분리. -- 숨은 밑그림 채색. -- Cubism import 성공. - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_apose_current.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_apose_current.png deleted file mode 100644 index 145649f..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_apose_current.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_haruka_sheet.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_haruka_sheet.png deleted file mode 100644 index 0cb1137..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_haruka_sheet.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_sori_sheet.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_sori_sheet.png deleted file mode 100644 index 7c7d5dc..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/00_Guide/guide_sori_sheet.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_base.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_base.png deleted file mode 100644 index c6a88ca..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_base.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_shadow.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_shadow.png deleted file mode 100644 index 78ebbba..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_shadow.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_L01.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_L01.png deleted file mode 100644 index ae3b094..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_L01.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_R01.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_R01.png deleted file mode 100644 index 562056e..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_strand_R01.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_L.png deleted file mode 100644 index 2542ac5..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_R.png deleted file mode 100644 index 60bb90a..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/10_BackHair/back_hair_tip_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_L.png deleted file mode 100644 index 646059b..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_R.png deleted file mode 100644 index 00d9d10..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_fore_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_L.png deleted file mode 100644 index 24ac0a8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_R.png deleted file mode 100644 index a82b444..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/arm_upper_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_L_base.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_L_base.png deleted file mode 100644 index 3957db3..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_L_base.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_R_base.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_R_base.png deleted file mode 100644 index defcfc0..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/hand_R_base.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_L.png deleted file mode 100644 index 9351720..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_R.png deleted file mode 100644 index acfc2b9..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_lower_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_L.png deleted file mode 100644 index edfc74d..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_R.png deleted file mode 100644 index 8ea07a8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/leg_upper_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_back_fill.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_back_fill.png deleted file mode 100644 index e3557a4..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_back_fill.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_front.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_front.png deleted file mode 100644 index bfc5edb..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/neck_front.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/torso_skin.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/torso_skin.png deleted file mode 100644 index c0ab4c8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/20_Body/torso_skin.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_back.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_back.png deleted file mode 100644 index 9d7c71b..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_back.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_L.png deleted file mode 100644 index 246f732..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_R.png deleted file mode 100644 index eac401e..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hood_front_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_front.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_front.png deleted file mode 100644 index 991e482..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_front.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_L.png deleted file mode 100644 index ea2ec63..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_R.png deleted file mode 100644 index bf83cbf..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/hoodie_string_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_body.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_body.png deleted file mode 100644 index 90e64a0..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_body.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_L.png deleted file mode 100644 index 4dc781f..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_R.png deleted file mode 100644 index b76ef8c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/jacket_sleeve_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/pants_base.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/pants_base.png deleted file mode 100644 index f93059f..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/pants_base.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_L.png deleted file mode 100644 index 021332b..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_R.png deleted file mode 100644 index 385c5cb..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/30_Clothes/shoe_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_L.png deleted file mode 100644 index 66d044c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_R.png deleted file mode 100644 index 67e7a52..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/cheek_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_L.png deleted file mode 100644 index 8c5ee71..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_R.png deleted file mode 100644 index 03aa9bf..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/ear_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_base.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_base.png deleted file mode 100644 index ce0ec08..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_base.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_shadow.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_shadow.png deleted file mode 100644 index de0d569..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/face_shadow.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/nose.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/nose.png deleted file mode 100644 index df959a2..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/40_Head/nose.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_highlight.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_highlight.png deleted file mode 100644 index d26cd12..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_highlight.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_iris.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_iris.png deleted file mode 100644 index f54ec1e..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_iris.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lid.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lid.png deleted file mode 100644 index bcdd42c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lid.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lower_lash.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lower_lash.png deleted file mode 100644 index 1c4fe79..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_lower_lash.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_pupil.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_pupil.png deleted file mode 100644 index 0039f8e..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_pupil.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_upper_lash.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_upper_lash.png deleted file mode 100644 index f8f77b3..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_upper_lash.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_white.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_white.png deleted file mode 100644 index b132d06..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_L_white.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_highlight.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_highlight.png deleted file mode 100644 index f6ce8fa..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_highlight.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_iris.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_iris.png deleted file mode 100644 index c04a726..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_iris.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lid.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lid.png deleted file mode 100644 index 9bb3148..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lid.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lower_lash.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lower_lash.png deleted file mode 100644 index 3259fd8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_lower_lash.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_pupil.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_pupil.png deleted file mode 100644 index 5826605..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_pupil.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_upper_lash.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_upper_lash.png deleted file mode 100644 index d4353fe..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_upper_lash.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_white.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_white.png deleted file mode 100644 index e9f5e59..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/50_Eyes/eye_R_white.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_L.png deleted file mode 100644 index b225709..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_R.png deleted file mode 100644 index 43f6d7b..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/55_Brows/brow_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/lip_highlight.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/lip_highlight.png deleted file mode 100644 index 3abee55..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/lip_highlight.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_inside.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_inside.png deleted file mode 100644 index 4dc2972..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_inside.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_lower.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_lower.png deleted file mode 100644 index c189359..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_lower.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_upper.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_upper.png deleted file mode 100644 index 8808770..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/mouth_line_upper.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_lower.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_lower.png deleted file mode 100644 index 8ebcffa..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_lower.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_upper.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_upper.png deleted file mode 100644 index cf6f033..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/teeth_upper.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/tongue.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/tongue.png deleted file mode 100644 index 4b1fdc7..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/60_Mouth/tongue.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_L.png deleted file mode 100644 index e29cb3c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_R.png deleted file mode 100644 index 1d86942..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_center.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_center.png deleted file mode 100644 index 9e425f8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/front_hair_center.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/hair_highlight_front.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/hair_highlight_front.png deleted file mode 100644 index 5ad8975..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/hair_highlight_front.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_L.png deleted file mode 100644 index e279c39..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_R.png deleted file mode 100644 index 7b527a8..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/70_FrontHair/side_hair_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/choker_band.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/choker_band.png deleted file mode 100644 index 076f3f0..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/choker_band.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_L.png deleted file mode 100644 index f7382a2..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_R.png deleted file mode 100644 index a712c2c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_band.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_band.png deleted file mode 100644 index 203b149..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/headphone_band.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/pendant.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/pendant.png deleted file mode 100644 index c5e9679..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/80_Accessories/pendant.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_L.png deleted file mode 100644 index 3508059..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_R.png deleted file mode 100644 index 22b4c64..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_arm_cross_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_L.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_L.png deleted file mode 100644 index 5c582c9..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_L.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_R.png b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_R.png deleted file mode 100644 index 7af967f..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs/90_SwapParts/swap_hand_heart_R.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs_README.md b/Haruka_Live2D/03_Assets/Live2D/LayerPNGs_README.md deleted file mode 100644 index 8193ef5..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/LayerPNGs_README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Live2D Layer PNG Bundle - -- Generated: 2026-07-03T19:23:29 -- Canvas: 1600x2800, transparent RGBA -- Layers: 78 -- Required non-empty: 67/67 -- PSD note: layered PSD was not written here; assemble these PNGs in manifest order in Photoshop/Clip Studio/Cubism workflow. - -## Files - -| Group | ID | File | Required | Non-empty | -|---|---|---|---:|---:| -| Guide | `guide_haruka_sheet` | `00_Guide/guide_haruka_sheet.png` | true | true | -| Guide | `guide_apose_current` | `00_Guide/guide_apose_current.png` | true | true | -| BackHair | `back_hair_base` | `10_BackHair/back_hair_base.png` | true | true | -| BackHair | `back_hair_shadow` | `10_BackHair/back_hair_shadow.png` | true | true | -| BackHair | `back_hair_tip_L` | `10_BackHair/back_hair_tip_L.png` | true | true | -| BackHair | `back_hair_tip_R` | `10_BackHair/back_hair_tip_R.png` | true | true | -| BackHair | `back_hair_strand_L01` | `10_BackHair/back_hair_strand_L01.png` | true | true | -| BackHair | `back_hair_strand_R01` | `10_BackHair/back_hair_strand_R01.png` | true | true | -| Body | `neck_back_fill` | `20_Body/neck_back_fill.png` | true | true | -| Body | `neck_front` | `20_Body/neck_front.png` | true | true | -| Body | `torso_skin` | `20_Body/torso_skin.png` | true | true | -| Body | `arm_upper_L` | `20_Body/arm_upper_L.png` | true | true | -| Body | `arm_fore_L` | `20_Body/arm_fore_L.png` | true | true | -| Body | `hand_L_base` | `20_Body/hand_L_base.png` | true | true | -| Body | `arm_upper_R` | `20_Body/arm_upper_R.png` | true | true | -| Body | `arm_fore_R` | `20_Body/arm_fore_R.png` | true | true | -| Body | `hand_R_base` | `20_Body/hand_R_base.png` | true | true | -| Body | `leg_upper_L` | `20_Body/leg_upper_L.png` | false | true | -| Body | `leg_lower_L` | `20_Body/leg_lower_L.png` | false | true | -| Body | `leg_upper_R` | `20_Body/leg_upper_R.png` | false | true | -| Body | `leg_lower_R` | `20_Body/leg_lower_R.png` | false | true | -| Clothes | `hood_back` | `30_Clothes/hood_back.png` | true | true | -| Clothes | `hood_front_L` | `30_Clothes/hood_front_L.png` | true | true | -| Clothes | `hood_front_R` | `30_Clothes/hood_front_R.png` | true | true | -| Clothes | `jacket_body` | `30_Clothes/jacket_body.png` | true | true | -| Clothes | `jacket_sleeve_L` | `30_Clothes/jacket_sleeve_L.png` | true | true | -| Clothes | `jacket_sleeve_R` | `30_Clothes/jacket_sleeve_R.png` | true | true | -| Clothes | `hoodie_front` | `30_Clothes/hoodie_front.png` | true | true | -| Clothes | `hoodie_string_L` | `30_Clothes/hoodie_string_L.png` | true | true | -| Clothes | `hoodie_string_R` | `30_Clothes/hoodie_string_R.png` | true | true | -| Clothes | `pants_base` | `30_Clothes/pants_base.png` | false | true | -| Clothes | `shoe_L` | `30_Clothes/shoe_L.png` | false | true | -| Clothes | `shoe_R` | `30_Clothes/shoe_R.png` | false | true | -| Head | `face_base` | `40_Head/face_base.png` | true | true | -| Head | `face_shadow` | `40_Head/face_shadow.png` | true | true | -| Head | `ear_L` | `40_Head/ear_L.png` | true | true | -| Head | `ear_R` | `40_Head/ear_R.png` | true | true | -| Head | `nose` | `40_Head/nose.png` | true | true | -| Head | `cheek_L` | `40_Head/cheek_L.png` | true | true | -| Head | `cheek_R` | `40_Head/cheek_R.png` | true | true | -| Eyes | `eye_L_white` | `50_Eyes/eye_L_white.png` | true | true | -| Eyes | `eye_L_iris` | `50_Eyes/eye_L_iris.png` | true | true | -| Eyes | `eye_L_pupil` | `50_Eyes/eye_L_pupil.png` | true | true | -| Eyes | `eye_L_highlight` | `50_Eyes/eye_L_highlight.png` | true | true | -| Eyes | `eye_L_upper_lash` | `50_Eyes/eye_L_upper_lash.png` | true | true | -| Eyes | `eye_L_lower_lash` | `50_Eyes/eye_L_lower_lash.png` | true | true | -| Eyes | `eye_L_lid` | `50_Eyes/eye_L_lid.png` | true | true | -| Eyes | `eye_R_white` | `50_Eyes/eye_R_white.png` | true | true | -| Eyes | `eye_R_iris` | `50_Eyes/eye_R_iris.png` | true | true | -| Eyes | `eye_R_pupil` | `50_Eyes/eye_R_pupil.png` | true | true | -| Eyes | `eye_R_highlight` | `50_Eyes/eye_R_highlight.png` | true | true | -| Eyes | `eye_R_upper_lash` | `50_Eyes/eye_R_upper_lash.png` | true | true | -| Eyes | `eye_R_lower_lash` | `50_Eyes/eye_R_lower_lash.png` | true | true | -| Eyes | `eye_R_lid` | `50_Eyes/eye_R_lid.png` | true | true | -| Brows | `brow_L` | `55_Brows/brow_L.png` | true | true | -| Brows | `brow_R` | `55_Brows/brow_R.png` | true | true | -| Mouth | `mouth_inside` | `60_Mouth/mouth_inside.png` | true | true | -| Mouth | `teeth_upper` | `60_Mouth/teeth_upper.png` | true | true | -| Mouth | `teeth_lower` | `60_Mouth/teeth_lower.png` | true | true | -| Mouth | `tongue` | `60_Mouth/tongue.png` | true | true | -| Mouth | `mouth_line_upper` | `60_Mouth/mouth_line_upper.png` | true | true | -| Mouth | `mouth_line_lower` | `60_Mouth/mouth_line_lower.png` | true | true | -| Mouth | `lip_highlight` | `60_Mouth/lip_highlight.png` | true | true | -| FrontHair | `front_hair_center` | `70_FrontHair/front_hair_center.png` | true | true | -| FrontHair | `front_hair_L` | `70_FrontHair/front_hair_L.png` | true | true | -| FrontHair | `front_hair_R` | `70_FrontHair/front_hair_R.png` | true | true | -| FrontHair | `side_hair_L` | `70_FrontHair/side_hair_L.png` | true | true | -| FrontHair | `side_hair_R` | `70_FrontHair/side_hair_R.png` | true | true | -| FrontHair | `hair_highlight_front` | `70_FrontHair/hair_highlight_front.png` | true | true | -| Accessories | `headphone_band` | `80_Accessories/headphone_band.png` | true | true | -| Accessories | `headphone_L` | `80_Accessories/headphone_L.png` | true | true | -| Accessories | `headphone_R` | `80_Accessories/headphone_R.png` | true | true | -| Accessories | `choker_band` | `80_Accessories/choker_band.png` | true | true | -| Accessories | `pendant` | `80_Accessories/pendant.png` | true | true | -| SwapParts | `swap_hand_heart_L` | `90_SwapParts/swap_hand_heart_L.png` | false | true | -| SwapParts | `swap_hand_heart_R` | `90_SwapParts/swap_hand_heart_R.png` | false | true | -| SwapParts | `swap_arm_cross_L` | `90_SwapParts/swap_arm_cross_L.png` | false | true | -| SwapParts | `swap_arm_cross_R` | `90_SwapParts/swap_arm_cross_R.png` | false | true | diff --git a/Haruka_Live2D/03_Assets/Live2D/Layer_Manifest.md b/Haruka_Live2D/03_Assets/Live2D/Layer_Manifest.md deleted file mode 100644 index 0bcf8fa..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/Layer_Manifest.md +++ /dev/null @@ -1,54 +0,0 @@ -# Live2D 레이어 Manifest - -이 문서는 AI 또는 작업자가 만들어야 할 Live2D 원화 레이어 목록이다. 기계 처리용 목록은 `layer_manifest.json`을 따른다. - -## 기본 규격 - -- 권장 캔버스: 1600x2800. -- 배경: 완전 투명. -- 색공간: sRGB. -- PSD import: RGB, 8bit/channel. -- 좌표: 모든 PNG 레이어는 같은 캔버스와 원점. -- 좌우: `L/R`은 캐릭터 기준이다. `L`은 화면 오른쪽, `R`은 화면 왼쪽이다. - -## 레이어 그룹 - -| 그룹 | 목적 | -|---|---| -| `Guide` | 기준 이미지. Cubism import 때 숨김 | -| `BackHair` | 뒤쪽 머리와 목 뒤 머리카락 | -| `Body` | 피부, 목, 팔, 손, 다리 | -| `Clothes` | 후디, 재킷, 팬츠, 신발 | -| `Head` | 얼굴 베이스, 귀, 코, 볼 | -| `Eyes` | 눈 흰자, 홍채, 동공, 하이라이트, 속눈썹, 눈꺼풀 | -| `Brows` | 좌우 눈썹 | -| `Mouth` | 입 안, 치아, 혀, 입선, 입술 | -| `FrontHair` | 앞머리, 옆머리, 잔머리 | -| `Accessories` | 헤드폰, 초커, 펜던트 | -| `SwapParts` | 하트 손, 팔짱 손 등 후속 교체 파츠 | - -## MVP 필수 레이어 - -MVP는 전신 정밀 댄스보다 **WPF 앱 마스코트의 상반신 반응 품질**을 우선한다. - -1. 얼굴 회전: face, ear, nose, cheek, front/side/back hair. -2. 눈깜빡임: upper/lower lash, eyelid, eyeball, highlight. -3. 말하기: mouth inside, teeth, tongue, mouth line, lips. -4. 호흡/상체: neck, torso, hoodie, jacket. -5. 손 제스처: upperarm, forearm, hand. -6. 물리: front hair, side hair, back hair, pendant. - -## 산출 방식 - -AI가 PSD를 만들 수 없으면 `LayerPNGs/` 아래에 manifest의 `file` 이름으로 투명 PNG를 만든다. 이후 Photoshop/Clip Studio에서 레이어로 쌓아 `haruka_live2d_import.psd`를 만든다. - -## 검수 키포인트 - -- 눈꺼풀을 닫아도 눈동자/하이라이트가 이상하게 남지 않는다. -- 입을 열었을 때 입 안, 치아, 혀가 자연스럽게 보인다. -- 고개를 좌우로 돌릴 때 귀와 옆머리의 앞뒤 관계가 맞다. -- 머리카락 물리 적용 시 빈 구멍이 보이지 않는다. -- 팔을 움직일 때 어깨/소매 밑그림이 드러나도 자연스럽다. - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/PSD_ASSEMBLY_GUIDE.md b/Haruka_Live2D/03_Assets/Live2D/PSD_ASSEMBLY_GUIDE.md deleted file mode 100644 index 135bf23..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/PSD_ASSEMBLY_GUIDE.md +++ /dev/null @@ -1,39 +0,0 @@ -# PSD Assembly Guide - -현재 세션에서는 layered PSD를 직접 저장할 수 있는 라이브러리나 ImageMagick/Krita가 없어 PSD 파일을 바로 생성하지 않았다. 대신 Cubism 조립에 필요한 투명 PNG 레이어 번들을 완료했고, Photoshop 조립용 JSX를 함께 생성했다. - -## 생성된 이미지 산출물 - -- `LayerPNGs/`: `layer_manifest.json`의 `file` 경로와 동일한 78개 PNG. -- `haruka_live2d_layer_preview.png`: import 레이어 기본 합성 프리뷰. -- `haruka_live2d_layer_preview_checker.png`: 체크 배경 검수용 프리뷰. -- `haruka_live2d_swap_parts_preview_checker.png`: swap part 포함 프리뷰. -- `layer_generation_report.json`: 파일 존재, bbox, required 여부 검수 결과. -- `LayerPNGs_README.md`: 사람이 읽는 PNG 목록. - -## PSD 조립 방법 - -Photoshop에서 다음 파일을 실행한다. - -`photoshop_assemble_live2d_psd.jsx` - -실행하면 프로젝트 루트 폴더를 선택하라는 창이 뜬다. `Haruka_Live2D` 폴더를 선택하면 다음 PSD를 저장한다. - -- `haruka_live2d_material_separation.psd` -- `haruka_live2d_import.psd` - -## 조립 규칙 - -- 캔버스: 1600x2800 px. -- 모드: RGB, 8bit/channel, sRGB. -- PNG는 모두 같은 원점과 같은 캔버스를 유지한다. -- import PSD에는 `import: true` 레이어만 포함한다. -- Guide 레이어는 작업 PSD에만 들어가며 숨김 처리한다. -- SwapParts 레이어는 포함하지만 기본 visibility는 꺼 둔다. - -## 현재 한계 - -이 번들은 Live2D 제작을 이어가기 위한 1차 분리 원화다. 기존 A-pose 파츠를 기반으로 마스크/영역/보강 드로잉을 적용했으므로, Cubism import 전 Photoshop 또는 Clip Studio에서 눈/입/머리카락 경계와 숨은 밑그림을 한 번 더 수작업 보정하는 것을 권장한다. - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/_parts_contact_sheet.png b/Haruka_Live2D/03_Assets/Live2D/_parts_contact_sheet.png deleted file mode 100644 index bc831bf..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/_parts_contact_sheet.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview.png b/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview.png deleted file mode 100644 index b94d421..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview_checker.png b/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview_checker.png deleted file mode 100644 index 69bffba..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_layer_preview_checker.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_swap_parts_preview_checker.png b/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_swap_parts_preview_checker.png deleted file mode 100644 index 801535c..0000000 Binary files a/Haruka_Live2D/03_Assets/Live2D/haruka_live2d_swap_parts_preview_checker.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Live2D/layer_generation_report.json b/Haruka_Live2D/03_Assets/Live2D/layer_generation_report.json deleted file mode 100644 index 7025148..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/layer_generation_report.json +++ /dev/null @@ -1,1507 +0,0 @@ -{ - "generatedAt": "2026-07-03T19:23:29", - "canvas": { - "width": 1600, - "height": 2800 - }, - "scaleFromSource": { - "source": [ - 520, - 900 - ], - "scale": 3, - "offset": [ - 20, - 50 - ] - }, - "layerCount": 78, - "requiredLayerCount": 67, - "nonemptyRequiredLayerCount": 67, - "missingFiles": [], - "rows": [ - { - "id": "guide_haruka_sheet", - "file": "00_Guide/guide_haruka_sheet.png", - "group": "Guide", - "required": true, - "import": false, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 60, - 90, - 1539, - 1082 - ], - "note": "guide layer, not for Cubism import" - }, - { - "id": "guide_apose_current", - "file": "00_Guide/guide_apose_current.png", - "group": "Guide", - "required": true, - "import": false, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 124, - 103, - 1473, - 2697 - ], - "note": "guide layer, not for Cubism import" - }, - { - "id": "back_hair_base", - "file": "10_BackHair/back_hair_base.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 448, - 178, - 1155, - 1026 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "back_hair_shadow", - "file": "10_BackHair/back_hair_shadow.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 449, - 359, - 1155, - 1026 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "back_hair_tip_L", - "file": "10_BackHair/back_hair_tip_L.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 926, - 551, - 1175, - 1026 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "back_hair_tip_R", - "file": "10_BackHair/back_hair_tip_R.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 425, - 551, - 674, - 969 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "back_hair_strand_L01", - "file": "10_BackHair/back_hair_strand_L01.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 986, - 251, - 1175, - 969 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "back_hair_strand_R01", - "file": "10_BackHair/back_hair_strand_R01.png", - "group": "BackHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 425, - 254, - 614, - 969 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "neck_back_fill", - "file": "20_Body/neck_back_fill.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 673, - 763, - 927, - 984 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "neck_front", - "file": "20_Body/neck_front.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 673, - 763, - 927, - 984 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "torso_skin", - "file": "20_Body/torso_skin.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 592, - 955, - 1008, - 1265 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "arm_upper_L", - "file": "20_Body/arm_upper_L.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 1033, - 805, - 1242, - 1152 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "arm_fore_L", - "file": "20_Body/arm_fore_L.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 1138, - 1060, - 1275, - 1308 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hand_L_base", - "file": "20_Body/hand_L_base.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 1126, - 1279, - 1470, - 1509 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "arm_upper_R", - "file": "20_Body/arm_upper_R.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 355, - 805, - 570, - 1152 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "arm_fore_R", - "file": "20_Body/arm_fore_R.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 328, - 1060, - 465, - 1308 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hand_R_base", - "file": "20_Body/hand_R_base.png", - "group": "Body", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 124, - 1279, - 474, - 1503 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "leg_upper_L", - "file": "20_Body/leg_upper_L.png", - "group": "Body", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 826, - 1204, - 993, - 1959 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "leg_lower_L", - "file": "20_Body/leg_lower_L.png", - "group": "Body", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 823, - 1777, - 957, - 2331 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "leg_upper_R", - "file": "20_Body/leg_upper_R.png", - "group": "Body", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 610, - 1204, - 777, - 1959 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "leg_lower_R", - "file": "20_Body/leg_lower_R.png", - "group": "Body", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 646, - 1777, - 780, - 2331 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hood_back", - "file": "30_Clothes/hood_back.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 547, - 703, - 1053, - 918 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hood_front_L", - "file": "30_Clothes/hood_front_L.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 791, - 955, - 975, - 1034 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hood_front_R", - "file": "30_Clothes/hood_front_R.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 622, - 955, - 809, - 1034 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "jacket_body", - "file": "30_Clothes/jacket_body.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 593, - 1091, - 1008, - 1422 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "jacket_sleeve_L", - "file": "30_Clothes/jacket_sleeve_L.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 994, - 826, - 1473, - 1560 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "jacket_sleeve_R", - "file": "30_Clothes/jacket_sleeve_R.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 124, - 826, - 606, - 1557 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hoodie_front", - "file": "30_Clothes/hoodie_front.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 619, - 955, - 978, - 1109 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hoodie_string_L", - "file": "30_Clothes/hoodie_string_L.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 838, - 838, - 882, - 1155 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hoodie_string_R", - "file": "30_Clothes/hoodie_string_R.png", - "group": "Clothes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 721, - 835, - 765, - 1155 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "pants_base", - "file": "30_Clothes/pants_base.png", - "group": "Clothes", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 488, - 1408, - 1110, - 2427 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "shoe_L", - "file": "30_Clothes/shoe_L.png", - "group": "Clothes", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 979, - 2413, - 1152, - 2697 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "shoe_R", - "file": "30_Clothes/shoe_R.png", - "group": "Clothes", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 445, - 2413, - 618, - 2697 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "face_base", - "file": "40_Head/face_base.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 566, - 226, - 1034, - 816 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "face_shadow", - "file": "40_Head/face_shadow.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 596, - 242, - 1001, - 773 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "ear_L", - "file": "40_Head/ear_L.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 956, - 356, - 1139, - 689 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "ear_R", - "file": "40_Head/ear_R.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 461, - 356, - 644, - 689 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "nose", - "file": "40_Head/nose.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 781, - 475, - 825, - 561 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "cheek_L", - "file": "40_Head/cheek_L.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 866, - 497, - 1007, - 599 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "cheek_R", - "file": "40_Head/cheek_R.png", - "group": "Head", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 593, - 497, - 734, - 599 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_white", - "file": "50_Eyes/eye_L_white.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 829, - 436, - 977, - 504 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_iris", - "file": "50_Eyes/eye_L_iris.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 868, - 430, - 936, - 510 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_pupil", - "file": "50_Eyes/eye_L_pupil.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 880, - 442, - 924, - 498 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_highlight", - "file": "50_Eyes/eye_L_highlight.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 886, - 439, - 930, - 489 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_upper_lash", - "file": "50_Eyes/eye_L_upper_lash.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 823, - 430, - 981, - 474 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_lower_lash", - "file": "50_Eyes/eye_L_lower_lash.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 836, - 475, - 969, - 507 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_L_lid", - "file": "50_Eyes/eye_L_lid.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 833, - 412, - 972, - 444 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_white", - "file": "50_Eyes/eye_R_white.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 625, - 436, - 773, - 504 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_iris", - "file": "50_Eyes/eye_R_iris.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 664, - 430, - 732, - 510 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_pupil", - "file": "50_Eyes/eye_R_pupil.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 676, - 442, - 720, - 498 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_highlight", - "file": "50_Eyes/eye_R_highlight.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 682, - 439, - 726, - 489 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_upper_lash", - "file": "50_Eyes/eye_R_upper_lash.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 619, - 430, - 777, - 474 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_lower_lash", - "file": "50_Eyes/eye_R_lower_lash.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 632, - 475, - 765, - 507 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "eye_R_lid", - "file": "50_Eyes/eye_R_lid.png", - "group": "Eyes", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 629, - 412, - 768, - 444 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "brow_L", - "file": "55_Brows/brow_L.png", - "group": "Brows", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 832, - 370, - 960, - 411 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "brow_R", - "file": "55_Brows/brow_R.png", - "group": "Brows", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 640, - 370, - 768, - 411 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "mouth_inside", - "file": "60_Mouth/mouth_inside.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 757, - 562, - 843, - 624 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "teeth_upper", - "file": "60_Mouth/teeth_upper.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 769, - 565, - 834, - 600 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "teeth_lower", - "file": "60_Mouth/teeth_lower.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 775, - 592, - 828, - 621 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "tongue", - "file": "60_Mouth/tongue.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 766, - 583, - 834, - 630 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "mouth_line_upper", - "file": "60_Mouth/mouth_line_upper.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 739, - 553, - 861, - 585 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "mouth_line_lower", - "file": "60_Mouth/mouth_line_lower.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 766, - 601, - 837, - 633 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "lip_highlight", - "file": "60_Mouth/lip_highlight.png", - "group": "Mouth", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 767, - 544, - 834, - 570 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "front_hair_center", - "file": "70_FrontHair/front_hair_center.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 628, - 103, - 972, - 531 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "front_hair_L", - "file": "70_FrontHair/front_hair_L.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 821, - 128, - 1133, - 629 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "front_hair_R", - "file": "70_FrontHair/front_hair_R.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 467, - 127, - 779, - 627 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "side_hair_L", - "file": "70_FrontHair/side_hair_L.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 911, - 386, - 1175, - 1026 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "side_hair_R", - "file": "70_FrontHair/side_hair_R.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 425, - 386, - 689, - 969 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "hair_highlight_front", - "file": "70_FrontHair/hair_highlight_front.png", - "group": "FrontHair", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 494, - 104, - 1124, - 792 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "headphone_band", - "file": "80_Accessories/headphone_band.png", - "group": "Accessories", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 596, - 103, - 1019, - 323 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "headphone_L", - "file": "80_Accessories/headphone_L.png", - "group": "Accessories", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 941, - 176, - 1157, - 764 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "headphone_R", - "file": "80_Accessories/headphone_R.png", - "group": "Accessories", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 427, - 176, - 659, - 764 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "choker_band", - "file": "80_Accessories/choker_band.png", - "group": "Accessories", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 664, - 694, - 936, - 744 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "pendant", - "file": "80_Accessories/pendant.png", - "group": "Accessories", - "required": true, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 737, - 709, - 869, - 804 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "swap_hand_heart_L", - "file": "90_SwapParts/swap_hand_heart_L.png", - "group": "SwapParts", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 844, - 940, - 1103, - 1215 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "swap_hand_heart_R", - "file": "90_SwapParts/swap_hand_heart_R.png", - "group": "SwapParts", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 652, - 950, - 912, - 1217 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "swap_arm_cross_L", - "file": "90_SwapParts/swap_arm_cross_L.png", - "group": "SwapParts", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 706, - 919, - 1209, - 1518 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - }, - { - "id": "swap_arm_cross_R", - "file": "90_SwapParts/swap_arm_cross_R.png", - "group": "SwapParts", - "required": false, - "import": true, - "exists": true, - "size": [ - 1600, - 2800 - ], - "bbox": [ - 704, - 907, - 1202, - 1506 - ], - "note": "generated from existing Haruka A-pose assets and manifest mapping" - } - ], - "psdNote": "Layered PSD was not written in this environment. Use LayerPNGs in manifest order to assemble the Cubism import PSD." -} \ No newline at end of file diff --git a/Haruka_Live2D/03_Assets/Live2D/layer_manifest.json b/Haruka_Live2D/03_Assets/Live2D/layer_manifest.json deleted file mode 100644 index 7725846..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/layer_manifest.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "name": "Haruka Live2D Layer Manifest", - "version": 1, - "canvas": { - "width": 1600, - "height": 2800, - "background": "transparent", - "colorProfile": "sRGB", - "psdMode": "RGB 8bit/channel" - }, - "coordinateRule": "All layer PNGs use the same canvas and origin. L/R are from the character's point of view.", - "output": { - "materialPsd": "03_Assets/Live2D/haruka_live2d_material_separation.psd", - "importPsd": "03_Assets/Live2D/haruka_live2d_import.psd", - "layerPngBase": "03_Assets/Live2D/LayerPNGs/" - }, - "layers": [ - { "id": "guide_haruka_sheet", "group": "Guide", "file": "00_Guide/guide_haruka_sheet.png", "import": false, "required": true }, - { "id": "guide_apose_current", "group": "Guide", "file": "00_Guide/guide_apose_current.png", "import": false, "required": true }, - - { "id": "back_hair_base", "group": "BackHair", "file": "10_BackHair/back_hair_base.png", "import": true, "required": true, "physics": "ParamHairBack" }, - { "id": "back_hair_shadow", "group": "BackHair", "file": "10_BackHair/back_hair_shadow.png", "import": true, "required": true, "physics": "ParamHairBack" }, - { "id": "back_hair_tip_L", "group": "BackHair", "file": "10_BackHair/back_hair_tip_L.png", "import": true, "required": true, "physics": "ParamHairSide" }, - { "id": "back_hair_tip_R", "group": "BackHair", "file": "10_BackHair/back_hair_tip_R.png", "import": true, "required": true, "physics": "ParamHairSide" }, - { "id": "back_hair_strand_L01", "group": "BackHair", "file": "10_BackHair/back_hair_strand_L01.png", "import": true, "required": true, "physics": "ParamHairSide" }, - { "id": "back_hair_strand_R01", "group": "BackHair", "file": "10_BackHair/back_hair_strand_R01.png", "import": true, "required": true, "physics": "ParamHairSide" }, - - { "id": "neck_back_fill", "group": "Body", "file": "20_Body/neck_back_fill.png", "import": true, "required": true }, - { "id": "neck_front", "group": "Body", "file": "20_Body/neck_front.png", "import": true, "required": true, "deformer": "D_Neck" }, - { "id": "torso_skin", "group": "Body", "file": "20_Body/torso_skin.png", "import": true, "required": true, "deformer": "D_Body" }, - { "id": "arm_upper_L", "group": "Body", "file": "20_Body/arm_upper_L.png", "import": true, "required": true, "deformer": "D_Arm_L" }, - { "id": "arm_fore_L", "group": "Body", "file": "20_Body/arm_fore_L.png", "import": true, "required": true, "deformer": "D_Arm_L" }, - { "id": "hand_L_base", "group": "Body", "file": "20_Body/hand_L_base.png", "import": true, "required": true, "deformer": "D_Hand_L" }, - { "id": "arm_upper_R", "group": "Body", "file": "20_Body/arm_upper_R.png", "import": true, "required": true, "deformer": "D_Arm_R" }, - { "id": "arm_fore_R", "group": "Body", "file": "20_Body/arm_fore_R.png", "import": true, "required": true, "deformer": "D_Arm_R" }, - { "id": "hand_R_base", "group": "Body", "file": "20_Body/hand_R_base.png", "import": true, "required": true, "deformer": "D_Hand_R" }, - { "id": "leg_upper_L", "group": "Body", "file": "20_Body/leg_upper_L.png", "import": true, "required": false, "deformer": "D_Leg_L" }, - { "id": "leg_lower_L", "group": "Body", "file": "20_Body/leg_lower_L.png", "import": true, "required": false, "deformer": "D_Leg_L" }, - { "id": "leg_upper_R", "group": "Body", "file": "20_Body/leg_upper_R.png", "import": true, "required": false, "deformer": "D_Leg_R" }, - { "id": "leg_lower_R", "group": "Body", "file": "20_Body/leg_lower_R.png", "import": true, "required": false, "deformer": "D_Leg_R" }, - - { "id": "hood_back", "group": "Clothes", "file": "30_Clothes/hood_back.png", "import": true, "required": true }, - { "id": "hood_front_L", "group": "Clothes", "file": "30_Clothes/hood_front_L.png", "import": true, "required": true }, - { "id": "hood_front_R", "group": "Clothes", "file": "30_Clothes/hood_front_R.png", "import": true, "required": true }, - { "id": "jacket_body", "group": "Clothes", "file": "30_Clothes/jacket_body.png", "import": true, "required": true, "deformer": "D_Body" }, - { "id": "jacket_sleeve_L", "group": "Clothes", "file": "30_Clothes/jacket_sleeve_L.png", "import": true, "required": true, "deformer": "D_Arm_L" }, - { "id": "jacket_sleeve_R", "group": "Clothes", "file": "30_Clothes/jacket_sleeve_R.png", "import": true, "required": true, "deformer": "D_Arm_R" }, - { "id": "hoodie_front", "group": "Clothes", "file": "30_Clothes/hoodie_front.png", "import": true, "required": true, "deformer": "D_Body" }, - { "id": "hoodie_string_L", "group": "Clothes", "file": "30_Clothes/hoodie_string_L.png", "import": true, "required": true, "physics": "ParamBreath" }, - { "id": "hoodie_string_R", "group": "Clothes", "file": "30_Clothes/hoodie_string_R.png", "import": true, "required": true, "physics": "ParamBreath" }, - { "id": "pants_base", "group": "Clothes", "file": "30_Clothes/pants_base.png", "import": true, "required": false, "deformer": "D_Body" }, - { "id": "shoe_L", "group": "Clothes", "file": "30_Clothes/shoe_L.png", "import": true, "required": false }, - { "id": "shoe_R", "group": "Clothes", "file": "30_Clothes/shoe_R.png", "import": true, "required": false }, - - { "id": "face_base", "group": "Head", "file": "40_Head/face_base.png", "import": true, "required": true, "deformer": "D_Face" }, - { "id": "face_shadow", "group": "Head", "file": "40_Head/face_shadow.png", "import": true, "required": true, "deformer": "D_Face" }, - { "id": "ear_L", "group": "Head", "file": "40_Head/ear_L.png", "import": true, "required": true, "deformer": "D_Head" }, - { "id": "ear_R", "group": "Head", "file": "40_Head/ear_R.png", "import": true, "required": true, "deformer": "D_Head" }, - { "id": "nose", "group": "Head", "file": "40_Head/nose.png", "import": true, "required": true, "deformer": "D_Face" }, - { "id": "cheek_L", "group": "Head", "file": "40_Head/cheek_L.png", "import": true, "required": true, "parameter": "ParamCheek" }, - { "id": "cheek_R", "group": "Head", "file": "40_Head/cheek_R.png", "import": true, "required": true, "parameter": "ParamCheek" }, - - { "id": "eye_L_white", "group": "Eyes", "file": "50_Eyes/eye_L_white.png", "import": true, "required": true, "parameter": "ParamEyeLOpen" }, - { "id": "eye_L_iris", "group": "Eyes", "file": "50_Eyes/eye_L_iris.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_L_pupil", "group": "Eyes", "file": "50_Eyes/eye_L_pupil.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_L_highlight", "group": "Eyes", "file": "50_Eyes/eye_L_highlight.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_L_upper_lash", "group": "Eyes", "file": "50_Eyes/eye_L_upper_lash.png", "import": true, "required": true, "parameter": "ParamEyeLOpen" }, - { "id": "eye_L_lower_lash", "group": "Eyes", "file": "50_Eyes/eye_L_lower_lash.png", "import": true, "required": true, "parameter": "ParamEyeLOpen" }, - { "id": "eye_L_lid", "group": "Eyes", "file": "50_Eyes/eye_L_lid.png", "import": true, "required": true, "parameter": "ParamEyeLOpen" }, - { "id": "eye_R_white", "group": "Eyes", "file": "50_Eyes/eye_R_white.png", "import": true, "required": true, "parameter": "ParamEyeROpen" }, - { "id": "eye_R_iris", "group": "Eyes", "file": "50_Eyes/eye_R_iris.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_R_pupil", "group": "Eyes", "file": "50_Eyes/eye_R_pupil.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_R_highlight", "group": "Eyes", "file": "50_Eyes/eye_R_highlight.png", "import": true, "required": true, "parameter": "ParamEyeBallX/Y" }, - { "id": "eye_R_upper_lash", "group": "Eyes", "file": "50_Eyes/eye_R_upper_lash.png", "import": true, "required": true, "parameter": "ParamEyeROpen" }, - { "id": "eye_R_lower_lash", "group": "Eyes", "file": "50_Eyes/eye_R_lower_lash.png", "import": true, "required": true, "parameter": "ParamEyeROpen" }, - { "id": "eye_R_lid", "group": "Eyes", "file": "50_Eyes/eye_R_lid.png", "import": true, "required": true, "parameter": "ParamEyeROpen" }, - - { "id": "brow_L", "group": "Brows", "file": "55_Brows/brow_L.png", "import": true, "required": true, "parameter": "ParamBrowL*" }, - { "id": "brow_R", "group": "Brows", "file": "55_Brows/brow_R.png", "import": true, "required": true, "parameter": "ParamBrowR*" }, - - { "id": "mouth_inside", "group": "Mouth", "file": "60_Mouth/mouth_inside.png", "import": true, "required": true, "parameter": "ParamMouthOpenY" }, - { "id": "teeth_upper", "group": "Mouth", "file": "60_Mouth/teeth_upper.png", "import": true, "required": true, "parameter": "ParamMouthOpenY" }, - { "id": "teeth_lower", "group": "Mouth", "file": "60_Mouth/teeth_lower.png", "import": true, "required": true, "parameter": "ParamMouthOpenY" }, - { "id": "tongue", "group": "Mouth", "file": "60_Mouth/tongue.png", "import": true, "required": true, "parameter": "ParamMouthOpenY" }, - { "id": "mouth_line_upper", "group": "Mouth", "file": "60_Mouth/mouth_line_upper.png", "import": true, "required": true, "parameter": "ParamMouthForm" }, - { "id": "mouth_line_lower", "group": "Mouth", "file": "60_Mouth/mouth_line_lower.png", "import": true, "required": true, "parameter": "ParamMouthForm" }, - { "id": "lip_highlight", "group": "Mouth", "file": "60_Mouth/lip_highlight.png", "import": true, "required": true, "parameter": "ParamMouthForm" }, - - { "id": "front_hair_center", "group": "FrontHair", "file": "70_FrontHair/front_hair_center.png", "import": true, "required": true, "physics": "ParamHairFront" }, - { "id": "front_hair_L", "group": "FrontHair", "file": "70_FrontHair/front_hair_L.png", "import": true, "required": true, "physics": "ParamHairFront" }, - { "id": "front_hair_R", "group": "FrontHair", "file": "70_FrontHair/front_hair_R.png", "import": true, "required": true, "physics": "ParamHairFront" }, - { "id": "side_hair_L", "group": "FrontHair", "file": "70_FrontHair/side_hair_L.png", "import": true, "required": true, "physics": "ParamHairSide" }, - { "id": "side_hair_R", "group": "FrontHair", "file": "70_FrontHair/side_hair_R.png", "import": true, "required": true, "physics": "ParamHairSide" }, - { "id": "hair_highlight_front", "group": "FrontHair", "file": "70_FrontHair/hair_highlight_front.png", "import": true, "required": true, "physics": "ParamHairFront" }, - - { "id": "headphone_band", "group": "Accessories", "file": "80_Accessories/headphone_band.png", "import": true, "required": true, "deformer": "D_Head" }, - { "id": "headphone_L", "group": "Accessories", "file": "80_Accessories/headphone_L.png", "import": true, "required": true, "deformer": "D_Head" }, - { "id": "headphone_R", "group": "Accessories", "file": "80_Accessories/headphone_R.png", "import": true, "required": true, "deformer": "D_Head" }, - { "id": "choker_band", "group": "Accessories", "file": "80_Accessories/choker_band.png", "import": true, "required": true, "deformer": "D_Neck" }, - { "id": "pendant", "group": "Accessories", "file": "80_Accessories/pendant.png", "import": true, "required": true, "physics": "ParamBreath" }, - - { "id": "swap_hand_heart_L", "group": "SwapParts", "file": "90_SwapParts/swap_hand_heart_L.png", "import": true, "required": false, "parameter": "ParamHandL" }, - { "id": "swap_hand_heart_R", "group": "SwapParts", "file": "90_SwapParts/swap_hand_heart_R.png", "import": true, "required": false, "parameter": "ParamHandR" }, - { "id": "swap_arm_cross_L", "group": "SwapParts", "file": "90_SwapParts/swap_arm_cross_L.png", "import": true, "required": false, "parameter": "ParamArmLA/LB" }, - { "id": "swap_arm_cross_R", "group": "SwapParts", "file": "90_SwapParts/swap_arm_cross_R.png", "import": true, "required": false, "parameter": "ParamArmRA/RB" } - ] -} - - - diff --git a/Haruka_Live2D/03_Assets/Live2D/photoshop_assemble_live2d_psd.jsx b/Haruka_Live2D/03_Assets/Live2D/photoshop_assemble_live2d_psd.jsx deleted file mode 100644 index 77d496c..0000000 --- a/Haruka_Live2D/03_Assets/Live2D/photoshop_assemble_live2d_psd.jsx +++ /dev/null @@ -1,85 +0,0 @@ -#target photoshop -app.displayDialogs = DialogModes.NO; - -var CANVAS_W = 1600; -var CANVAS_H = 2800; -var LAYERS = [ - {id:"guide_haruka_sheet",file:"00_Guide/guide_haruka_sheet.png",group:"Guide",importLayer:false,guide:true}, {id:"guide_apose_current",file:"00_Guide/guide_apose_current.png",group:"Guide",importLayer:false,guide:true}, {id:"back_hair_base",file:"10_BackHair/back_hair_base.png",group:"BackHair",importLayer:true,guide:false}, {id:"back_hair_shadow",file:"10_BackHair/back_hair_shadow.png",group:"BackHair",importLayer:true,guide:false}, {id:"back_hair_tip_L",file:"10_BackHair/back_hair_tip_L.png",group:"BackHair",importLayer:true,guide:false}, {id:"back_hair_tip_R",file:"10_BackHair/back_hair_tip_R.png",group:"BackHair",importLayer:true,guide:false}, {id:"back_hair_strand_L01",file:"10_BackHair/back_hair_strand_L01.png",group:"BackHair",importLayer:true,guide:false}, {id:"back_hair_strand_R01",file:"10_BackHair/back_hair_strand_R01.png",group:"BackHair",importLayer:true,guide:false}, {id:"neck_back_fill",file:"20_Body/neck_back_fill.png",group:"Body",importLayer:true,guide:false}, {id:"neck_front",file:"20_Body/neck_front.png",group:"Body",importLayer:true,guide:false}, {id:"torso_skin",file:"20_Body/torso_skin.png",group:"Body",importLayer:true,guide:false}, {id:"arm_upper_L",file:"20_Body/arm_upper_L.png",group:"Body",importLayer:true,guide:false}, {id:"arm_fore_L",file:"20_Body/arm_fore_L.png",group:"Body",importLayer:true,guide:false}, {id:"hand_L_base",file:"20_Body/hand_L_base.png",group:"Body",importLayer:true,guide:false}, {id:"arm_upper_R",file:"20_Body/arm_upper_R.png",group:"Body",importLayer:true,guide:false}, {id:"arm_fore_R",file:"20_Body/arm_fore_R.png",group:"Body",importLayer:true,guide:false}, {id:"hand_R_base",file:"20_Body/hand_R_base.png",group:"Body",importLayer:true,guide:false}, {id:"leg_upper_L",file:"20_Body/leg_upper_L.png",group:"Body",importLayer:true,guide:false}, {id:"leg_lower_L",file:"20_Body/leg_lower_L.png",group:"Body",importLayer:true,guide:false}, {id:"leg_upper_R",file:"20_Body/leg_upper_R.png",group:"Body",importLayer:true,guide:false}, {id:"leg_lower_R",file:"20_Body/leg_lower_R.png",group:"Body",importLayer:true,guide:false}, {id:"hood_back",file:"30_Clothes/hood_back.png",group:"Clothes",importLayer:true,guide:false}, {id:"hood_front_L",file:"30_Clothes/hood_front_L.png",group:"Clothes",importLayer:true,guide:false}, {id:"hood_front_R",file:"30_Clothes/hood_front_R.png",group:"Clothes",importLayer:true,guide:false}, {id:"jacket_body",file:"30_Clothes/jacket_body.png",group:"Clothes",importLayer:true,guide:false}, {id:"jacket_sleeve_L",file:"30_Clothes/jacket_sleeve_L.png",group:"Clothes",importLayer:true,guide:false}, {id:"jacket_sleeve_R",file:"30_Clothes/jacket_sleeve_R.png",group:"Clothes",importLayer:true,guide:false}, {id:"hoodie_front",file:"30_Clothes/hoodie_front.png",group:"Clothes",importLayer:true,guide:false}, {id:"hoodie_string_L",file:"30_Clothes/hoodie_string_L.png",group:"Clothes",importLayer:true,guide:false}, {id:"hoodie_string_R",file:"30_Clothes/hoodie_string_R.png",group:"Clothes",importLayer:true,guide:false}, {id:"pants_base",file:"30_Clothes/pants_base.png",group:"Clothes",importLayer:true,guide:false}, {id:"shoe_L",file:"30_Clothes/shoe_L.png",group:"Clothes",importLayer:true,guide:false}, {id:"shoe_R",file:"30_Clothes/shoe_R.png",group:"Clothes",importLayer:true,guide:false}, {id:"face_base",file:"40_Head/face_base.png",group:"Head",importLayer:true,guide:false}, {id:"face_shadow",file:"40_Head/face_shadow.png",group:"Head",importLayer:true,guide:false}, {id:"ear_L",file:"40_Head/ear_L.png",group:"Head",importLayer:true,guide:false}, {id:"ear_R",file:"40_Head/ear_R.png",group:"Head",importLayer:true,guide:false}, {id:"nose",file:"40_Head/nose.png",group:"Head",importLayer:true,guide:false}, {id:"cheek_L",file:"40_Head/cheek_L.png",group:"Head",importLayer:true,guide:false}, {id:"cheek_R",file:"40_Head/cheek_R.png",group:"Head",importLayer:true,guide:false}, {id:"eye_L_white",file:"50_Eyes/eye_L_white.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_iris",file:"50_Eyes/eye_L_iris.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_pupil",file:"50_Eyes/eye_L_pupil.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_highlight",file:"50_Eyes/eye_L_highlight.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_upper_lash",file:"50_Eyes/eye_L_upper_lash.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_lower_lash",file:"50_Eyes/eye_L_lower_lash.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_L_lid",file:"50_Eyes/eye_L_lid.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_white",file:"50_Eyes/eye_R_white.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_iris",file:"50_Eyes/eye_R_iris.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_pupil",file:"50_Eyes/eye_R_pupil.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_highlight",file:"50_Eyes/eye_R_highlight.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_upper_lash",file:"50_Eyes/eye_R_upper_lash.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_lower_lash",file:"50_Eyes/eye_R_lower_lash.png",group:"Eyes",importLayer:true,guide:false}, {id:"eye_R_lid",file:"50_Eyes/eye_R_lid.png",group:"Eyes",importLayer:true,guide:false}, {id:"brow_L",file:"55_Brows/brow_L.png",group:"Brows",importLayer:true,guide:false}, {id:"brow_R",file:"55_Brows/brow_R.png",group:"Brows",importLayer:true,guide:false}, {id:"mouth_inside",file:"60_Mouth/mouth_inside.png",group:"Mouth",importLayer:true,guide:false}, {id:"teeth_upper",file:"60_Mouth/teeth_upper.png",group:"Mouth",importLayer:true,guide:false}, {id:"teeth_lower",file:"60_Mouth/teeth_lower.png",group:"Mouth",importLayer:true,guide:false}, {id:"tongue",file:"60_Mouth/tongue.png",group:"Mouth",importLayer:true,guide:false}, {id:"mouth_line_upper",file:"60_Mouth/mouth_line_upper.png",group:"Mouth",importLayer:true,guide:false}, {id:"mouth_line_lower",file:"60_Mouth/mouth_line_lower.png",group:"Mouth",importLayer:true,guide:false}, {id:"lip_highlight",file:"60_Mouth/lip_highlight.png",group:"Mouth",importLayer:true,guide:false}, {id:"front_hair_center",file:"70_FrontHair/front_hair_center.png",group:"FrontHair",importLayer:true,guide:false}, {id:"front_hair_L",file:"70_FrontHair/front_hair_L.png",group:"FrontHair",importLayer:true,guide:false}, {id:"front_hair_R",file:"70_FrontHair/front_hair_R.png",group:"FrontHair",importLayer:true,guide:false}, {id:"side_hair_L",file:"70_FrontHair/side_hair_L.png",group:"FrontHair",importLayer:true,guide:false}, {id:"side_hair_R",file:"70_FrontHair/side_hair_R.png",group:"FrontHair",importLayer:true,guide:false}, {id:"hair_highlight_front",file:"70_FrontHair/hair_highlight_front.png",group:"FrontHair",importLayer:true,guide:false}, {id:"headphone_band",file:"80_Accessories/headphone_band.png",group:"Accessories",importLayer:true,guide:false}, {id:"headphone_L",file:"80_Accessories/headphone_L.png",group:"Accessories",importLayer:true,guide:false}, {id:"headphone_R",file:"80_Accessories/headphone_R.png",group:"Accessories",importLayer:true,guide:false}, {id:"choker_band",file:"80_Accessories/choker_band.png",group:"Accessories",importLayer:true,guide:false}, {id:"pendant",file:"80_Accessories/pendant.png",group:"Accessories",importLayer:true,guide:false}, {id:"swap_hand_heart_L",file:"90_SwapParts/swap_hand_heart_L.png",group:"SwapParts",importLayer:true,guide:false}, {id:"swap_hand_heart_R",file:"90_SwapParts/swap_hand_heart_R.png",group:"SwapParts",importLayer:true,guide:false}, {id:"swap_arm_cross_L",file:"90_SwapParts/swap_arm_cross_L.png",group:"SwapParts",importLayer:true,guide:false}, {id:"swap_arm_cross_R",file:"90_SwapParts/swap_arm_cross_R.png",group:"SwapParts",importLayer:true,guide:false} -]; - -function requireFolder(path) { - var f = new Folder(path); - if (!f.exists) { - throw new Error("Missing folder: " + path); - } - return f; -} - -function copyPngIntoDoc(doc, pngFile, layerName, visible) { - var src = app.open(pngFile); - src.selection.selectAll(); - src.selection.copy(); - src.close(SaveOptions.DONOTSAVECHANGES); - app.activeDocument = doc; - doc.paste(); - doc.activeLayer.name = layerName; - doc.activeLayer.visible = visible; -} - -function makeDoc(name) { - return app.documents.add( - UnitValue(CANVAS_W, "px"), - UnitValue(CANVAS_H, "px"), - 72, - name, - NewDocumentMode.RGB, - DocumentFill.TRANSPARENT, - 1, - BitsPerChannelType.EIGHT, - "sRGB IEC61966-2.1" - ); -} - -function savePsd(doc, outFile) { - app.activeDocument = doc; - var opts = new PhotoshopSaveOptions(); - opts.layers = true; - opts.embedColorProfile = true; - opts.alphaChannels = true; - doc.saveAs(outFile, opts, true, Extension.LOWERCASE); -} - -var repo = Folder.selectDialog("Select Haruka_Live2D project folder"); -if (repo == null) { - throw new Error("Cancelled"); -} - -var layerBase = requireFolder(repo.fsName + "/03_Assets/Live2D/LayerPNGs"); -var live2dBase = requireFolder(repo.fsName + "/03_Assets/Live2D"); - -var materialDoc = makeDoc("haruka_live2d_material_separation"); -for (var i = 0; i < LAYERS.length; i++) { - var layer = LAYERS[i]; - var file = new File(layerBase.fsName + "/" + layer.file); - if (!file.exists) { - throw new Error("Missing PNG: " + file.fsName); - } - copyPngIntoDoc(materialDoc, file, layer.id, !layer.guide && layer.group != "SwapParts"); -} -savePsd(materialDoc, new File(live2dBase.fsName + "/haruka_live2d_material_separation.psd")); - -var importDoc = makeDoc("haruka_live2d_import"); -for (var j = 0; j < LAYERS.length; j++) { - var importLayer = LAYERS[j]; - if (!importLayer.importLayer) { - continue; - } - var importFile = new File(layerBase.fsName + "/" + importLayer.file); - if (!importFile.exists) { - throw new Error("Missing PNG: " + importFile.fsName); - } - copyPngIntoDoc(importDoc, importFile, importLayer.id, importLayer.group != "SwapParts"); -} -savePsd(importDoc, new File(live2dBase.fsName + "/haruka_live2d_import.psd")); - -alert("Saved Live2D PSD files in " + live2dBase.fsName); diff --git a/Haruka_Live2D/03_Assets/Parts/Images/PUT_IMAGES_HERE.md b/Haruka_Live2D/03_Assets/Parts/Images/PUT_IMAGES_HERE.md deleted file mode 100644 index 527bde8..0000000 --- a/Haruka_Live2D/03_Assets/Parts/Images/PUT_IMAGES_HERE.md +++ /dev/null @@ -1,13 +0,0 @@ -# 여기에 리그 파츠 PNG를 저장하세요 - -상위 `../../../이미지작업_의뢰서.md` 대로 만든 결과(총 17장)를 이 폴더에 정확한 파일명으로 저장. - -- **마스터**: haruka_part_master_apose.png -- **파츠 16**: haruka_part_head · neck · chest · pelvis · upperarm_r/l · forearm_r/l · hand_r/l · thigh_r/l · shin_r/l · foot_r/l - -**필수**: 전부 **520×900 풀캔버스 · 32-bit RGBA · 배경 alpha=0**, 파츠는 마스터 제자리 배치(크롭 금지 — 16장 겹치면 마스터 복원). - -저장 후 `../../../07_Viewer/index.html` 에서 **🖼 아트 사용** 으로 확인. - - - diff --git a/Haruka_Live2D/03_Assets/Parts/Images/_validation.txt b/Haruka_Live2D/03_Assets/Parts/Images/_validation.txt deleted file mode 100644 index b6a3860..0000000 --- a/Haruka_Live2D/03_Assets/Parts/Images/_validation.txt +++ /dev/null @@ -1,23 +0,0 @@ -Haruka rig part build validation -Canvas: 520x900 -Master: haruka_part_master_apose.png -haruka_part_head.png | 520x900 | Format32bppArgb | bbox=53,20,413,455 -haruka_part_neck.png | 520x900 | Format32bppArgb | bbox=220,240,80,69 -haruka_part_chest.png | 520x900 | Format32bppArgb | bbox=193,304,134,151 -haruka_part_pelvis.png | 520x900 | Format32bppArgb | bbox=180,455,160,103 -haruka_part_upperarm_r.png | 520x900 | Format32bppArgb | bbox=83,261,110,134 -haruka_part_forearm_r.png | 520x900 | Format32bppArgb | bbox=37,390,156,110 -haruka_part_hand_r.png | 520x900 | Format32bppArgb | bbox=37,412,112,70 -haruka_part_upperarm_l.png | 520x900 | Format32bppArgb | bbox=327,261,111,134 -haruka_part_forearm_l.png | 520x900 | Format32bppArgb | bbox=327,390,155,111 -haruka_part_hand_l.png | 520x900 | Format32bppArgb | bbox=371,412,110,72 -haruka_part_thigh_r.png | 520x900 | Format32bppArgb | bbox=163,558,67,112 -haruka_part_shin_r.png | 520x900 | Format32bppArgb | bbox=158,670,50,120 -haruka_part_foot_r.png | 520x900 | Format32bppArgb | bbox=144,790,53,90 -haruka_part_thigh_l.png | 520x900 | Format32bppArgb | bbox=290,558,67,112 -haruka_part_shin_l.png | 520x900 | Format32bppArgb | bbox=311,670,50,120 -haruka_part_foot_l.png | 520x900 | Format32bppArgb | bbox=322,790,53,90 -Direct coverage check | missing=0 | extra=0 | differing=0 | multi_covered=0 - - - diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_chest.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_chest.png deleted file mode 100644 index 4a1ac72..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_chest.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_l.png deleted file mode 100644 index d2ea665..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_r.png deleted file mode 100644 index a560b66..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_foot_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_l.png deleted file mode 100644 index 3c43121..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_r.png deleted file mode 100644 index 0455146..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_forearm_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_l.png deleted file mode 100644 index 37f0eb7..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_r.png deleted file mode 100644 index 01687be..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_hand_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_head.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_head.png deleted file mode 100644 index b9f10e2..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_head.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_master_apose.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_master_apose.png deleted file mode 100644 index 5af6cdf..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_master_apose.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_neck.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_neck.png deleted file mode 100644 index 79e29cb..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_neck.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_pelvis.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_pelvis.png deleted file mode 100644 index 31039e9..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_pelvis.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_l.png deleted file mode 100644 index eeffcf3..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_r.png deleted file mode 100644 index 41af43a..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_shin_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_l.png deleted file mode 100644 index 5279c8a..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_r.png deleted file mode 100644 index 294934f..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_thigh_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_l.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_l.png deleted file mode 100644 index c388b34..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_l.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_r.png b/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_r.png deleted file mode 100644 index feaf37e..0000000 Binary files a/Haruka_Live2D/03_Assets/Parts/Images/haruka_part_upperarm_r.png and /dev/null differ diff --git a/Haruka_Live2D/03_Assets/Parts/Parts.md b/Haruka_Live2D/03_Assets/Parts/Parts.md deleted file mode 100644 index 9be2c8f..0000000 --- a/Haruka_Live2D/03_Assets/Parts/Parts.md +++ /dev/null @@ -1,37 +0,0 @@ -# 하루카 리그 파츠 — 정의 & 슬라이스 스펙 - -> 실무 의뢰서(마스터 프롬프트·컷·검수)는 상위 `../../이미지작업_의뢰서.md`. 이 문서는 **파츠 정의·규칙 요약**. - -## 결과물 -`haruka_part_master_apose.png` (마스터) + 관절 파츠 16장. **모두 520×900 풀캔버스 · 진짜 투명 알파(32-bit RGBA, 배경 alpha=0).** - -## 방법: 마스터-슬라이스 (풀캔버스) -마스터 1장 → 16조각 슬라이스 → 각 조각을 **520×900 제자리 배치**로 저장(그 외 투명). **16장 겹치면 마스터 복원 = 같은 좌표계 → 관절 자동 정합.** (타이트 크롭 금지.) - -## 파츠 정의 (범위 · 근위단 = 부모쪽 오버랩) -| 파일 | 범위 | 근위단(오버랩) | -|---|---|---| -| head | 두개골·얼굴·귀·코랄브라운 트윈테일·시스루뱅 (+목 살짝) | 목(가슴 밑) | -| neck | 턱~쇄골 목기둥 | 양끝 | -| chest | 어깨~허리 (세일러 블라우스+칼라+리본) | 허리·양 어깨 | -| pelvis | 허리~허벅지 상단 (**플리츠 스커트 포함**) | 허리(가슴 밑) | -| upperarm_r/l | 어깨~팔꿈치 (블라우스 소매) | 어깨(가슴 밑) | -| forearm_r/l | 팔꿈치~손목 | 팔꿈치 | -| hand_r/l | 손목~손끝 (펼친 손) | 손목 | -| thigh_r/l | 스커트 밑단~무릎 (드러난 다리) | 고관절(골반/스커트 밑) | -| shin_r/l | 무릎~발목 (양말) | 무릎 | -| foot_r/l | 발목~발끝 (스니커즈) | 발목 | - -> **스커트**: `pelvis`에 포함. `thigh`는 스커트 밑단 아래 노출부만. 스커트 애니는 후속 옵션. - -## 규칙 -- 캔버스 520×900 고정, 배경 alpha=0, 흰 후광 금지, 안티에일리어스. modest(10대). 좌우: `_r`=화면왼쪽, `_l`=화면오른쪽. 저장: `Images/`. - -## 리그 연동 (참고) -풀캔버스 파츠 → 위치 제자리. 리그는 각 파츠를 원점에 그리고 회전 피벗 = 관절 좌표(`../../04_Rig/rig.json`). 하루카는 **~6.5-7등신(틴)** 이라 Haruka 대비 rig 관절 좌표를 조금 낮춰 튜닝(파츠 도착 후). - -## (선택 · 후속) 대체 손 attachment -- 하트·주먹·가리킴 등 마스터에 없는 손은 범위 밖. 필요 시 별도 개별 생성. - - - diff --git a/Haruka_Live2D/03_Assets/Reference/haruka_sheet.png b/Haruka_Live2D/03_Assets/Reference/haruka_sheet.png deleted file mode 100644 index 5850dde..0000000 Binary files a/Haruka_Live2D/03_Assets/Reference/haruka_sheet.png and /dev/null differ diff --git a/Haruka_Live2D/04_Rig/Rig.md b/Haruka_Live2D/04_Rig/Rig.md deleted file mode 100644 index 6af7ebf..0000000 --- a/Haruka_Live2D/04_Rig/Rig.md +++ /dev/null @@ -1,42 +0,0 @@ -# Rig.md — 스켈레톤 정의 (`rig.json`) 설명 - -경량 리그의 뼈대. 뷰어(`../07_Viewer/index.html`)와 WPF 앱이 **동일하게** 읽는다. - -## 본 계층 (부모 → 자식) -``` -pelvis (root) -├─ chest -│ ├─ neck ─ head -│ ├─ upperarm_r ─ forearm_r ─ hand_r (캐릭터 오른팔 = 화면 왼쪽) -│ └─ upperarm_l ─ forearm_l ─ hand_l (캐릭터 왼팔 = 화면 오른쪽) -├─ thigh_r ─ shin_r ─ foot_r -└─ thigh_l ─ shin_l ─ foot_l -``` -16 파츠. 각 관절이 실제로 접힌다(팔꿈치·무릎·손목·발목·목·허리). - -## 필드 스키마 (bones[]) -| 필드 | 의미 | -|---|---| -| `name` | 본 이름(= 애니메이션 트랙 키, = 파츠 파일 접두) | -| `parent` | 부모 본 이름(root는 null). **배열은 부모가 먼저** 오도록 정렬됨 | -| `pos` `[x,y]` | **부모 관절 기준** 이 본 관절의 오프셋(휴지 자세, px) | -| `angle` | 휴지 각도(deg, **+ = 시계방향**). 팔은 살짝 벌린 춤-대기 자세로 프리셋 | -| `z` | 그리기 순서(작을수록 뒤). 화면 왼팔(캐릭터 오른팔)=뒤, 화면 오른팔=앞 | -| `image` | 파츠 PNG 파일명(`imageBase` + 이 값). 없으면 플레이스홀더 | -| `imgAnchor` `[ax,ay]` | **파츠 이미지 안에서 관절이 위치한 정규화 좌표**(0~1) | -| `imgScale` | 이미지 배율(기본 1) | -| `col` / `ph` / `phW` | 플레이스홀더 색/도형(실제 아트 로드 전까지 사용) | - -## 좌표계 -- 캔버스 520×900, y-아래(+y = 화면 아래). 회전 + = 시계방향. -- 본의 **로컬 원점 = 그 본의 관절**. 자식 `pos`는 이 원점 기준. - -## 튜닝 가이드 (실제 아트가 오면) -1. 뷰어에서 **스켈레톤 오버레이 ON** → 관절 점(분홍)이 아트 관절 위에 오도록: - - 위치 어긋남 → `pos` 조정 / 파츠가 관절에서 어긋나 회전 → `imgAnchor` 조정 / 크기 → `imgScale`. -2. 겹침 이상 → `z`. 목 이음새 벌어짐 → 머리 회전 폭↓ 또는 `neck` 피벗 내림. - -> 강체 회전 한계상 큰 각도에서 관절이 벌어질 수 있음. 오버랩 파츠 + z가림으로 완화. 더 필요하면 `../02_Architecture/Limits_and_Mitigations.md` 의 mesh-warp 승급. - - - diff --git a/Haruka_Live2D/04_Rig/_dance_preview.png b/Haruka_Live2D/04_Rig/_dance_preview.png deleted file mode 100644 index 7c51966..0000000 Binary files a/Haruka_Live2D/04_Rig/_dance_preview.png and /dev/null differ diff --git a/Haruka_Live2D/04_Rig/_pivots.json b/Haruka_Live2D/04_Rig/_pivots.json deleted file mode 100644 index d8fb756..0000000 --- a/Haruka_Live2D/04_Rig/_pivots.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "pelvis": [ - 259.4, - 455.0 - ], - "chest": [ - 259.3, - 304.0 - ], - "neck": [ - 259.5, - 240.0 - ], - "head": [ - 260.4, - 20.0 - ], - "upperarm_r": [ - 143.0, - 261.0 - ], - "forearm_r": [ - 151.6, - 390.0 - ], - "hand_r": [ - 77.2, - 412.0 - ], - "upperarm_l": [ - 375.0, - 261.0 - ], - "forearm_l": [ - 367.5, - 390.0 - ], - "hand_l": [ - 440.0, - 412.0 - ], - "thigh_r": [ - 195.3, - 558.0 - ], - "shin_r": [ - 179.8, - 670.0 - ], - "foot_r": [ - 171.8, - 790.0 - ], - "thigh_l": [ - 323.3, - 558.0 - ], - "shin_l": [ - 338.6, - 670.0 - ], - "foot_l": [ - 346.4, - 790.0 - ] -} - - diff --git a/Haruka_Live2D/04_Rig/rig.json b/Haruka_Live2D/04_Rig/rig.json deleted file mode 100644 index 01829f1..0000000 --- a/Haruka_Live2D/04_Rig/rig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "Haruka", - "canvas": { "width": 520, "height": 900 }, - "imageBase": "../03_Assets/Parts/Images/", - "mode": "fullcanvas", - "note": "Full-canvas parts (520x900, part at master position). Draw at origin; rotate about pivot (joint). pivot = auto-derived overlap centroid from Haruka's parts (_tools/rig_pivots_render.py).", - "bones": [ - { "name": "pelvis", "parent": null, "pivot": [259.4, 455.0], "z": 6, "image": "haruka_part_pelvis.png" }, - { "name": "chest", "parent": "pelvis", "pivot": [259.3, 304.0], "z": 8, "image": "haruka_part_chest.png" }, - { "name": "neck", "parent": "chest", "pivot": [259.5, 240.0], "z": 9, "image": "haruka_part_neck.png" }, - { "name": "head", "parent": "neck", "pivot": [260.4, 20.0], "z": 10, "image": "haruka_part_head.png" }, - - { "name": "upperarm_r", "parent": "chest", "pivot": [143.0, 261.0], "z": 5, "image": "haruka_part_upperarm_r.png" }, - { "name": "forearm_r", "parent": "upperarm_r", "pivot": [151.6, 390.0], "z": 5, "image": "haruka_part_forearm_r.png" }, - { "name": "hand_r", "parent": "forearm_r", "pivot": [77.2, 412.0], "z": 5, "image": "haruka_part_hand_r.png" }, - - { "name": "upperarm_l", "parent": "chest", "pivot": [375.0, 261.0], "z": 12, "image": "haruka_part_upperarm_l.png" }, - { "name": "forearm_l", "parent": "upperarm_l", "pivot": [367.5, 390.0], "z": 12, "image": "haruka_part_forearm_l.png" }, - { "name": "hand_l", "parent": "forearm_l", "pivot": [440.0, 412.0], "z": 13, "image": "haruka_part_hand_l.png" }, - - { "name": "thigh_r", "parent": "pelvis", "pivot": [195.3, 558.0], "z": 4, "image": "haruka_part_thigh_r.png" }, - { "name": "shin_r", "parent": "thigh_r", "pivot": [179.8, 670.0], "z": 3, "image": "haruka_part_shin_r.png" }, - { "name": "foot_r", "parent": "shin_r", "pivot": [171.8, 790.0], "z": 2, "image": "haruka_part_foot_r.png" }, - - { "name": "thigh_l", "parent": "pelvis", "pivot": [323.3, 558.0], "z": 4, "image": "haruka_part_thigh_l.png" }, - { "name": "shin_l", "parent": "thigh_l", "pivot": [338.6, 670.0], "z": 3, "image": "haruka_part_shin_l.png" }, - { "name": "foot_l", "parent": "shin_l", "pivot": [346.4, 790.0], "z": 2, "image": "haruka_part_foot_l.png" } - ] -} - - - diff --git a/Haruka_Live2D/05_Animation/Animation.md b/Haruka_Live2D/05_Animation/Animation.md deleted file mode 100644 index 3b622fa..0000000 --- a/Haruka_Live2D/05_Animation/Animation.md +++ /dev/null @@ -1,33 +0,0 @@ -# Animation.md — 리그 클립 (`dance_idle.json`) 설명 - -리그(16파츠)를 움직이는 **본 단위 키프레임** 클립. 뷰어가 매 프레임 샘플링해 60fps 재생. 이 스키마는 반응 시퀀서(`../06_Reactions/`)의 `transform` 레이어에도 쓰인다. - -## 스키마 -```jsonc -{ - "duration": 2.0, "loop": true, - "defaultEase": "sine", // "linear"도 가능 - "tracks": { - "": { - "rot": [ {"t":0,"v":0}, {"t":0.5,"v":7}, ... ], // 회전 delta(deg, +=시계) - "tx": [...], "ty": [...], // 부모 프레임 이동 delta(px, +y=아래) - "sx": [...], "sy": [...] // 스케일 delta(0=변화없음→배율1) - } - } -} -``` -- 값은 **리그 휴지 자세에 더해진다**(`rest + delta`). -- **각 트랙 첫 키 = 마지막 키** 로 두면 루프가 이음매 없이 반복. - -## 현재 클립: `dance_idle` (가벼운 2박 그루브, 2초 루프) -- pelvis: 바운스+스웨이 / chest: 반대 카운터 / head: 좌우 ±7°(+neck 지연) / 팔: 좌우 교대 펌핑 / 다리: 무릎 교대 굽힘. - -## 강도 조절 -- 과하면 모든 `v`×0.6~0.8 / 목 벌어지면 `head.rot` 폭↓ / 신나게 pelvis `ty`↑ / 빠르게 `duration`↓. - -## 새 클립 -- 이 파일 복제 → 원하는 본 트랙만 작성(첫=끝) → 뷰어 "애니메이션 불러오기"로 확인. -- 상시 포즈 오프셋은 클립이 아니라 `../04_Rig/rig.json`의 `angle`로 주는 게 깔끔(클립은 그 위 흔들림만). - - - diff --git a/Haruka_Live2D/05_Animation/dance_idle.json b/Haruka_Live2D/05_Animation/dance_idle.json deleted file mode 100644 index c01aa19..0000000 --- a/Haruka_Live2D/05_Animation/dance_idle.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "dance_idle", - "duration": 2.0, - "loop": true, - "fpsHint": 60, - "defaultEase": "sine", - "note": "하루카 전용 튜닝 — 세일러 블라우스↔스커트가 별개라 CHEST 트랙 없음(허리 봉인). 블라우스가 재킷처럼 두껍지 않아 어깨 소켓 틈이 나기 쉬워 upperarm 진폭을 작게(±4), 팔 생동감은 forearm/hand(소매 안)로. 스웨이는 pelvis가 상체 통째로.", - "tracks": { - "pelvis": { - "ty": [ {"t":0,"v":0}, {"t":0.5,"v":9}, {"t":1.0,"v":0}, {"t":1.5,"v":9}, {"t":2.0,"v":0} ], - "tx": [ {"t":0,"v":0}, {"t":0.5,"v":6}, {"t":1.0,"v":0}, {"t":1.5,"v":-6}, {"t":2.0,"v":0} ], - "rot": [ {"t":0,"v":0}, {"t":0.5,"v":3}, {"t":1.0,"v":0}, {"t":1.5,"v":-3}, {"t":2.0,"v":0} ] - }, - - "neck": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":2}, {"t":1.0,"v":0}, {"t":1.5,"v":-2}, {"t":2.0,"v":0} ] }, - "head": { - "rot": [ {"t":0,"v":0}, {"t":0.5,"v":5}, {"t":1.0,"v":0}, {"t":1.5,"v":-5}, {"t":2.0,"v":0} ], - "ty": [ {"t":0,"v":0}, {"t":0.5,"v":-2}, {"t":1.0,"v":0}, {"t":1.5,"v":-2}, {"t":2.0,"v":0} ] - }, - - "upperarm_r": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":4}, {"t":1.0,"v":0}, {"t":1.5,"v":-2}, {"t":2.0,"v":0} ] }, - "forearm_r": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":9}, {"t":1.0,"v":0}, {"t":1.5,"v":-5}, {"t":2.0,"v":0} ] }, - "hand_r": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":5}, {"t":1.0,"v":0}, {"t":1.5,"v":-3}, {"t":2.0,"v":0} ] }, - - "upperarm_l": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":-2}, {"t":1.0,"v":0}, {"t":1.5,"v":4}, {"t":2.0,"v":0} ] }, - "forearm_l": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":-5}, {"t":1.0,"v":0}, {"t":1.5,"v":9}, {"t":2.0,"v":0} ] }, - "hand_l": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":-3}, {"t":1.0,"v":0}, {"t":1.5,"v":5}, {"t":2.0,"v":0} ] }, - - "thigh_r": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":2}, {"t":1.0,"v":0}, {"t":1.5,"v":-1}, {"t":2.0,"v":0} ] }, - "shin_r": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":5}, {"t":1.0,"v":0}, {"t":1.5,"v":0}, {"t":2.0,"v":0} ] }, - "thigh_l": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":-1}, {"t":1.0,"v":0}, {"t":1.5,"v":2}, {"t":2.0,"v":0} ] }, - "shin_l": { "rot": [ {"t":0,"v":0}, {"t":0.5,"v":0}, {"t":1.0,"v":0}, {"t":1.5,"v":5}, {"t":2.0,"v":0} ] } - } -} - - - diff --git a/Haruka_Live2D/06_Reactions/Reactions.md b/Haruka_Live2D/06_Reactions/Reactions.md deleted file mode 100644 index d9fba10..0000000 --- a/Haruka_Live2D/06_Reactions/Reactions.md +++ /dev/null @@ -1,66 +0,0 @@ -# 반응 시퀀서 & 트리거 (Reactions) - -상황 → 반응을 정의하는 레이어. **트리거 매퍼**(`reactions.json`) + **반응 클립**(`clips/*.json`)으로 구성. -> ✅ **Phase 2 런타임 구현됨**: `../07_Viewer/reactions.html`(더블클릭). 트리거(idle/error/success). baked 바디 + 표정 머리(목 정합·회전)는 `_layout.json` 사용. head base=`haruka_head_twin`. **리그 파츠 완성 → idle=배경춤도 동작**(허리봉인 튜닝). idle 배경춤 단독은 `../07_Viewer/index.html`. - -## 트리거 매퍼 (`reactions.json`) -상황키 → 반응 클립 이름. 앱은 상황키만 던지면 된다. -```json -{ "error": "gesture_no", "success": "gesture_heart", "idle": "dance_idle" } -``` - -## 반응 클립 스키마 (`clips/.json`) -하나의 반응 = 레이어드 타임라인. -```jsonc -{ - "name": "gesture_no", - "duration": 2.4, // 초 - "return": "idle", // 종료 후 복귀 클립(보통 배경 idle) - "layers": { - "body": [ // Body 트랙: 시간에 따라 rig 클립 or baked 포즈 - { "t":0.0, "mode":"rig", "clip":"idle" }, - { "t":0.15,"mode":"baked", "image":"haruka_body_sailor_armscross", "fade":0.2 } - ], - "face": [ // 표정 프레임 트랙 - { "t":0.0, "expr":"neutral" }, - { "t":0.3, "expr":"negative" } - ], - "mouth": [ // 말하기(유사 립싱크): expr↔talk 순환 - { "t":0.5, "say":"안돼요", "dur":1.2, "pattern":"talk" } - ], - "transform": { // 리그 위 잔모션(본별 delta) — Animation.md 스키마와 동일 - "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":0} ] }, - "chest":{ "ty":[ {"t":0.0,"v":0},{"t":0.2,"v":-4},{"t":0.5,"v":0} ] } - }, - "caption": [ { "t":0.5, "text":"안돼요", "dur":1.6 } ], // 옵션 말풍선 - "sfx": [ { "t":0.5, "id":"nope" } ] // 옵션 효과음 - } -} -``` -### 필드 규칙 -- `body[]`: 시간순. `mode:"rig"`+`clip` 또는 `mode:"baked"`+`image`(파일명, 확장자 생략 가능). `fade`=크로스페이드 초. -- `face[]`: `expr` = 표정 프레임 키(20종 중). 시간에 스냅. -- `mouth[]`: `say` 대사, `dur` 길이, `pattern:"talk"`(talk/talk_wide/현재 감정 프레임 순환). 립싱크는 근사. -- `transform`: 본별 키프레임(리그 delta). Body가 baked여도 head/chest 등 트랜스폼은 적용(단 baked는 통짜라 파츠 분리 트랜스폼은 제한적 → 주로 전체/머리에 적용). -- `caption`/`sfx`: 옵션. 앱 설정(말풍선/TTS)에 따라 사용. - -## 상황 → 반응 카탈로그 -| 상황키 | 클립 | Body | Face | Mouth | 잔모션 | -|---|---|---|---|---|---| -| `error` | `gesture_no` | baked armscross | negative | "안돼요" | 고개 젓기 | -| `success` | `gesture_heart` | baked heart | love/positive | "잘됐어요" | 통통 바운스 | -| `idle` | `dance_idle` | rig | smile/neutral | — | 그루브 루프 | -| *(확장)* `greet` | `gesture_wave` | rig wave | smile | "안녕하세요" | 손 흔들기 | -| *(확장)* `explain` | `gesture_present` | rig present | neutral | 안내 대사 | 제시 | -| *(확장)* `thinking` | `gesture_think` | rig idle_upper | thinking | — | 갸웃 | - -## 트리거 API(개념) -``` -Mascot.React("success") // reactions.json으로 클립 결정 → 시퀀서 재생 → 종료 후 return 클립 -Mascot.SetIdle("dance_idle")// 배경 기본 루프 -Mascot.Say("...", expr) // 임시 대사(mouth+face)만 -``` -상세 앱 연동: `../08_Roadmap/App_Integration.md`. - - - diff --git a/Haruka_Live2D/06_Reactions/_layout.json b/Haruka_Live2D/06_Reactions/_layout.json deleted file mode 100644 index e284f32..0000000 --- a/Haruka_Live2D/06_Reactions/_layout.json +++ /dev/null @@ -1,435 +0,0 @@ -{ - "stage": { - "w": 520, - "h": 900 - }, - "neck": [ - 260, - 250 - ], - "overlap": 6, - "headTargetW": 150, - "bodies": { - "haruka_body_idol_armscross": { - "scale": 0.329, - "ox": 28.5, - "oy": 228.9 - }, - "haruka_body_idol_cheer": { - "scale": 0.2516, - "ox": 194.7, - "oy": 242.7 - }, - "haruka_body_idol_clap": { - "scale": 0.319, - "ox": 36.9, - "oy": 227.0 - }, - "haruka_body_idol_control": { - "scale": 0.2725, - "ox": 41.4, - "oy": 218.4 - }, - "haruka_body_idol_dj": { - "scale": 0.3042, - "ox": 2.0, - "oy": 237.2 - }, - "haruka_body_idol_handwave": { - "scale": 0.3329, - "ox": 150.8, - "oy": 212.4 - }, - "haruka_body_idol_heart": { - "scale": 0.3464, - "ox": -5.3, - "oy": 225.8 - }, - "haruka_body_idol_idle_full": { - "scale": 0.5361, - "ox": -24.7, - "oy": 217.3 - }, - "haruka_body_idol_idle_upper": { - "scale": 0.3392, - "ox": -10.4, - "oy": 216.1 - }, - "haruka_body_idol_joy": { - "scale": 0.2641, - "ox": 218.4, - "oy": 244.2 - }, - "haruka_body_idol_listen": { - "scale": 0.3464, - "ox": 72.6, - "oy": 230.9 - }, - "haruka_body_idol_peace": { - "scale": 0.3686, - "ox": 82.7, - "oy": 226.8 - }, - "haruka_body_idol_piano": { - "scale": 0.3397, - "ox": 35.3, - "oy": 229.6 - }, - "haruka_body_idol_point": { - "scale": 0.3142, - "ox": 172.0, - "oy": 233.0 - }, - "haruka_body_idol_present": { - "scale": 0.2593, - "ox": 82.5, - "oy": 222.3 - }, - "haruka_body_idol_shrug": { - "scale": 0.1738, - "ox": 126.1, - "oy": 232.3 - }, - "haruka_body_idol_thumbsup": { - "scale": 0.356, - "ox": 13.8, - "oy": 216.2 - }, - "haruka_body_idol_wave": { - "scale": 0.3397, - "ox": 151.3, - "oy": 234.4 - }, - "haruka_body_sailor_armscross": { - "scale": 0.3199, - "ox": 64.4, - "oy": 221.8 - }, - "haruka_body_sailor_cheer": { - "scale": 0.213, - "ox": 210.3, - "oy": 233.6 - }, - "haruka_body_sailor_clap": { - "scale": 0.3423, - "ox": 5.2, - "oy": 214.7 - }, - "haruka_body_sailor_control": { - "scale": 0.2369, - "ox": 100.7, - "oy": 229.4 - }, - "haruka_body_sailor_dj": { - "scale": 0.293, - "ox": 17.0, - "oy": 234.5 - }, - "haruka_body_sailor_handwave": { - "scale": 0.29, - "ox": 68.9, - "oy": 219.8 - }, - "haruka_body_sailor_heart": { - "scale": 0.3662, - "ox": 4.0, - "oy": 218.9 - }, - "haruka_body_sailor_idle_full": { - "scale": 0.5263, - "ox": -36.6, - "oy": 196.8 - }, - "haruka_body_sailor_idle_upper": { - "scale": 0.2879, - "ox": 83.5, - "oy": 222.1 - }, - "haruka_body_sailor_joy": { - "scale": 0.2712, - "ox": 25.5, - "oy": 242.7 - }, - "haruka_body_sailor_listen": { - "scale": 0.3722, - "ox": -58.6, - "oy": 232.1 - }, - "haruka_body_sailor_peace": { - "scale": 0.3258, - "ox": 107.4, - "oy": 222.3 - }, - "haruka_body_sailor_piano": { - "scale": 0.3083, - "ox": 68.4, - "oy": 219.5 - }, - "haruka_body_sailor_point": { - "scale": 0.3046, - "ox": 173.9, - "oy": 222.6 - }, - "haruka_body_sailor_present": { - "scale": 0.2486, - "ox": 43.2, - "oy": 220.7 - }, - "haruka_body_sailor_shrug": { - "scale": 0.1697, - "ox": 150.2, - "oy": 234.0 - }, - "haruka_body_sailor_thumbsup": { - "scale": 0.3506, - "ox": 27.4, - "oy": 218.4 - }, - "haruka_body_sailor_wave": { - "scale": 0.319, - "ox": 186.6, - "oy": 231.8 - }, - "haruka_body_witch_armscross": { - "scale": 0.3925, - "ox": -49.1, - "oy": 233.1 - }, - "haruka_body_witch_cheer": { - "scale": 0.2255, - "ox": 188.5, - "oy": 240.3 - }, - "haruka_body_witch_clap": { - "scale": 0.4236, - "ox": -62.8, - "oy": 230.5 - }, - "haruka_body_witch_control": { - "scale": 0.2897, - "ox": 12.8, - "oy": 239.0 - }, - "haruka_body_witch_dj": { - "scale": 0.3199, - "ox": 47.6, - "oy": 238.8 - }, - "haruka_body_witch_handwave": { - "scale": 0.3464, - "ox": 9.6, - "oy": 225.8 - }, - "haruka_body_witch_heart": { - "scale": 0.3945, - "ox": -16.7, - "oy": 231.1 - }, - "haruka_body_witch_idle_full": { - "scale": 0.4684, - "ox": 16.2, - "oy": 191.4 - }, - "haruka_body_witch_idle_upper": { - "scale": 0.3814, - "ox": -61.9, - "oy": 222.5 - }, - "haruka_body_witch_joy": { - "scale": 0.2678, - "ox": 216.6, - "oy": 240.4 - }, - "haruka_body_witch_listen": { - "scale": 0.3918, - "ox": 26.1, - "oy": 245.3 - }, - "haruka_body_witch_peace": { - "scale": 0.371, - "ox": 92.9, - "oy": 234.8 - }, - "haruka_body_witch_piano": { - "scale": 0.3758, - "ox": -50.8, - "oy": 229.3 - }, - "haruka_body_witch_point": { - "scale": 0.29, - "ox": 54.4, - "oy": 237.8 - }, - "haruka_body_witch_present": { - "scale": 0.2671, - "ox": 37.2, - "oy": 237.7 - }, - "haruka_body_witch_shrug": { - "scale": 0.1896, - "ox": 125.0, - "oy": 239.4 - }, - "haruka_body_witch_thumbsup": { - "scale": 0.3674, - "ox": 2.6, - "oy": 236.0 - }, - "haruka_body_witch_wave": { - "scale": 0.3272, - "ox": 186.7, - "oy": 235.3 - } - }, - "heads": { - "haruka_head_twin": { - "w": 1046, - "neckNorm": [ - 0.4972, - 0.9537 - ] - }, - "haruka_head_twin_blink": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9537 - ] - }, - "haruka_head_twin_confused": { - "w": 1046, - "neckNorm": [ - 0.4972, - 0.9553 - ] - }, - "haruka_head_twin_cool": { - "w": 1043, - "neckNorm": [ - 0.4976, - 0.9553 - ] - }, - "haruka_head_twin_laugh": { - "w": 1059, - "neckNorm": [ - 0.5, - 0.9641 - ] - }, - "haruka_head_twin_love": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9553 - ] - }, - "haruka_head_twin_negative": { - "w": 1044, - "neckNorm": [ - 0.4964, - 0.953 - ] - }, - "haruka_head_twin_neutral": { - "w": 1045, - "neckNorm": [ - 0.4976, - 0.9545 - ] - }, - "haruka_head_twin_playful": { - "w": 1042, - "neckNorm": [ - 0.4972, - 0.9553 - ] - }, - "haruka_head_twin_positive": { - "w": 1040, - "neckNorm": [ - 0.4972, - 0.9537 - ] - }, - "haruka_head_twin_pout": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9553 - ] - }, - "haruka_head_twin_proud": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9553 - ] - }, - "haruka_head_twin_sad": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9569 - ] - }, - "haruka_head_twin_shy": { - "w": 1043, - "neckNorm": [ - 0.4968, - 0.9553 - ] - }, - "haruka_head_twin_sleepy": { - "w": 1045, - "neckNorm": [ - 0.4976, - 0.9561 - ] - }, - "haruka_head_twin_smile": { - "w": 1045, - "neckNorm": [ - 0.4976, - 0.9537 - ] - }, - "haruka_head_twin_surprised": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9545 - ] - }, - "haruka_head_twin_talk": { - "w": 1044, - "neckNorm": [ - 0.4964, - 0.953 - ] - }, - "haruka_head_twin_talk_wide": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9537 - ] - }, - "haruka_head_twin_thinking": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9537 - ] - }, - "haruka_head_twin_wink": { - "w": 1044, - "neckNorm": [ - 0.4972, - 0.9545 - ] - } - } -} - - diff --git a/Haruka_Live2D/06_Reactions/_reaction_preview.png b/Haruka_Live2D/06_Reactions/_reaction_preview.png deleted file mode 100644 index 447dc32..0000000 Binary files a/Haruka_Live2D/06_Reactions/_reaction_preview.png and /dev/null differ diff --git a/Haruka_Live2D/06_Reactions/clips/gesture_heart.json b/Haruka_Live2D/06_Reactions/clips/gesture_heart.json deleted file mode 100644 index fec5010..0000000 --- a/Haruka_Live2D/06_Reactions/clips/gesture_heart.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "gesture_heart", - "desc": "손 하트를 그리며 밝게 '잘됐어요'", - "duration": 2.2, - "return": "idle", - "layers": { - "body": [ - { "t": 0.0, "mode": "rig", "clip": "idle" }, - { "t": 0.15, "mode": "baked", "image": "haruka_body_sailor_heart", "fade": 0.2 } - ], - "face": [ - { "t": 0.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.0,"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.0,"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" } ] - } -} - - - diff --git a/Haruka_Live2D/06_Reactions/clips/gesture_no.json b/Haruka_Live2D/06_Reactions/clips/gesture_no.json deleted file mode 100644 index 6d7d140..0000000 --- a/Haruka_Live2D/06_Reactions/clips/gesture_no.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "gesture_no", - "desc": "서있다 → 팔짱 끼고 인상 쓰며 고개 저으며 '안돼요'", - "duration": 2.4, - "return": "idle", - "layers": { - "body": [ - { "t": 0.0, "mode": "rig", "clip": "idle" }, - { "t": 0.15, "mode": "baked", "image": "haruka_body_sailor_armscross", "fade": 0.2 } - ], - "face": [ - { "t": 0.0, "expr": "neutral" }, - { "t": 0.3, "expr": "negative" } - ], - "mouth": [ - { "t": 0.55, "say": "안돼요", "dur": 1.2, "pattern": "talk" } - ], - "transform": { - "chest": { "ty": [ {"t":0.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.0,"v":0} ] } - }, - "caption": [ { "t": 0.55, "text": "안돼요", "dur": 1.6 } ], - "sfx": [ { "t": 0.5, "id": "nope" } ] - } -} - - - diff --git a/Haruka_Live2D/06_Reactions/reactions.json b/Haruka_Live2D/06_Reactions/reactions.json deleted file mode 100644 index c149c4b..0000000 --- a/Haruka_Live2D/06_Reactions/reactions.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Haruka reactions map", - "note": "상황키(app event) → 반응 클립 이름(clips/.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" - } -} - - - diff --git a/Haruka_Live2D/07_Viewer/Viewer.md b/Haruka_Live2D/07_Viewer/Viewer.md deleted file mode 100644 index 45c8aca..0000000 --- a/Haruka_Live2D/07_Viewer/Viewer.md +++ /dev/null @@ -1,30 +0,0 @@ -# Viewer.md — 리그 뷰어 (`index.html`) 사용법 - -자립형 캔버스 런타임(프로토타입). **더블클릭만으로** 하루카 리그가 60fps로 춤춘다(이미지 없이 플레이스홀더로). -> 현 단계 뷰어는 **리그 클립 재생기**(Phase 1 검증용). 반응 시퀀서(베이크드+표정 레이어 합성)는 Phase 2에서 확장 → `../08_Roadmap/Roadmap.md`. - -## 실행 -- `index.html` 를 브라우저로 열기(더블클릭). 서버·빌드 불필요. 기본 리그/애니메이션 내장. - -## 컨트롤 -| 버튼 | 기능 | -|---|---| -| ⏸/▶ | 재생 토글 · 속도 슬라이더 0~2배 | -| 🖼 아트 사용 | 파츠 PNG로 렌더 ↔ 플레이스홀더 | -| 🦴 스켈레톤 | 관절점·본 라인 오버레이(튜닝용) | -| 📂 rig.json / animation.json | 외부 파일 로드(수정본 반영) | -| 🖼 파츠 PNG(다중) | 파츠 이미지 직접 지정(파일명 매칭) | - -## 이미지 붙이기 -1. **자동**: ChatGPT 결과를 `../03_Assets/Parts/Images/` 에 정확한 파일명으로 저장 → 뷰어 자동 로드 → 🖼 아트 사용 ON. -2. **수동**(상대경로 차단 시): 🖼 파츠 PNG(다중)로 직접 선택. 우측 패널 `파츠 이미지 로드: N/16` 확인. - -## 튜닝 -- 🦴+🖼 켜고 분홍 관절점이 아트 관절에 오도록 `../04_Rig/rig.json`의 `imgAnchor/pos` 수정 → 📂 rig.json 재로드. -- 모션은 `../05_Animation/dance_idle.json` 수정 → 📂 animation.json 재로드. - -## 참고 -- 내장 리그/클립은 `../04_Rig/rig.json`·`../05_Animation/dance_idle.json` 의 사본. 파일 수정 후 📂로 로드하거나 index.html 상단 `DEFAULT_RIG`/`DEFAULT_ANIM` 갱신. - - - diff --git a/Haruka_Live2D/07_Viewer/index.html b/Haruka_Live2D/07_Viewer/index.html deleted file mode 100644 index 78c705d..0000000 --- a/Haruka_Live2D/07_Viewer/index.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Haruka Rig Viewer — dance_idle (full-canvas) - - - -
-

하루카 Rig Viewer — dance_idle (full-canvas)

-
- - 속도1.0× - -
-
- - - -
-
-
-
-
-

풀캔버스 파츠를 ../03_Assets/Parts/Images/ 에서 자동 로드합니다.

-

• 파츠는 520×900 풀캔버스라 원점에 그리고 관절 피벗을 중심으로 회전합니다.
- • 상대경로 자동 로드가 막히면(브라우저 보안) 파츠 PNG(다중)으로 직접 지정.
- • 스켈레톤: 분홍 점 = 자동 산출한 관절 피벗.

-

-
-
- - - - - - - diff --git a/Haruka_Live2D/07_Viewer/reactions.html b/Haruka_Live2D/07_Viewer/reactions.html deleted file mode 100644 index 833b6ff..0000000 --- a/Haruka_Live2D/07_Viewer/reactions.html +++ /dev/null @@ -1,886 +0,0 @@ - - - - - -Haruka Reaction Sequencer — reactions.html - - - -
-

하루카 Reaction Sequencer — dance_idle (520×900 / file://)

-
- - 속도1.0× - -
-
-
-
-
-
-

상황 트리거를 누르면 반응 클립을 재생하고 종료 후 dance_idle 로 복귀합니다.

-
- -
-

• 리그 idle 은 풀캔버스 파츠를 원점에 그리고 관절 피벗 기준 회전.
- • baked 반응은 headless 바디 + 표정 머리를 목(neck) 부착점에 합성 (Python reactions_layout_render.py 와 동일 어파인).
- • 상대경로 로드가 막히면 이미지 로드(다중) 로 PNG 를 직접 지정(파일명 매칭).

-

-
-
- - - - - - - diff --git a/Haruka_Live2D/08_Roadmap/App_Integration.md b/Haruka_Live2D/08_Roadmap/App_Integration.md deleted file mode 100644 index 6c8a9ee..0000000 --- a/Haruka_Live2D/08_Roadmap/App_Integration.md +++ /dev/null @@ -1,39 +0,0 @@ -# 앱 통합 (App Integration) - -하루카 반응 시스템을 **DanHarukaEQ(및 향후 DanHaruka 앱)** 에 탑재하는 방식. - -## 런타임 호스트 두 선택지 (O1 — 결정 예정) -| 옵션 | 방식 | 장점 | 단점 | -|---|---|---|---| -| **A. WPF-C# 이식** | 리그·시퀀서를 C#로 포팅, WPF 캔버스에 렌더 | 네이티브·경량·기존 `AlphaTools` 재활용 | 런타임을 두 벌(웹/C#) 유지 | -| **B. WebView2 임베드** | 웹 런타임(뷰어 진화형)을 WPF WebView2에 임베드 | 런타임 1벌(데이터·코드 공유) | WebView2 의존·상호작용 브리지 필요 | -> 프로토타입은 웹으로 계속. Phase 3에서 A/B 결정. 어느 쪽이든 **데이터(`rig.json`·클립·`reactions.json`·이미지)는 그대로 재사용**. - -## 트리거 API (앱 ↔ 마스코트) -``` -Mascot.SetIdle("dance_idle") // 배경 기본 루프 지정 -Mascot.React("success") // 상황키 → reactions.json → 클립 재생 → 끝나면 return(idle) -Mascot.React("error") -Mascot.Say("환영합니다", "smile") // 임시 대사(mouth+face)만, 포즈 유지 -Mascot.Stop() / Mascot.SetVisible(b) -``` -- 앱 이벤트(버튼 실패/성공, 화면 진입 등)에서 상황키만 던진다. 표현 방법은 마스코트가 데이터로 결정. - -## 리소스 번들 -- **필요한 PNG만** 앱 리소스로 복사(전부 아님): 리그 16파츠 + 사용하는 표정 세트 + 사용하는 baked 포즈 + hairmask. -- 데이터 파일(`rig.json`·클립·`reactions.json`)은 소스로 번들. - -## 좌표·정렬 규약 -- 스테이지 캔버스 기준(현재 리그 520×900). 앱 배치 시 전체 스케일/위치만 트랜스폼. -- 파츠 앵커 정렬은 기존 `Character_Builder/AlphaTools.cs`(알파 기반 목/어깨 검출)와 동일 알고리즘 재활용. -- 색 변형은 런타임 hairmask hue-shift(이미지 추가 없음). - -## 배경 마스코트 연동(DanHarukaEQ 예시) -- 메인화면 배경 하루카를 **통짜 → 리그**로 교체(부유+말하기 동기화는 기존 `MainWindow.SetBgMascotTalking` 훅 활용). -- 참고: `../../HANDOFF.md`, DanHarukaEQ `docs/HANDOFF.md §8`. - -## 포터블 원칙 -- 절대경로 금지. `Haruka_Profile`은 통째 이동 가능하게 상대경로만 사용. - - - diff --git a/Haruka_Live2D/08_Roadmap/Roadmap.md b/Haruka_Live2D/08_Roadmap/Roadmap.md deleted file mode 100644 index d9a65e3..0000000 --- a/Haruka_Live2D/08_Roadmap/Roadmap.md +++ /dev/null @@ -1,39 +0,0 @@ -# 로드맵 (Roadmap) - -단계별 구현 계획. 각 Phase는 **완료조건**을 만족하면 다음으로. - -## Phase 0 — 방향·설계 (완료) -- 하이브리드 방식·구현레벨 확정(`../01_Overview/Decisions.md`). -- 리그 스키마(`../04_Rig/rig.json`) + 배경춤 프로토타입(`../05_Animation/dance_idle.json`) + 뷰어(`../07_Viewer/`). -- **완료조건**: 뷰어에서 플레이스홀더 춤 재생 확인. ✅ - -## Phase 1 — 자산 생성 & 리그 실아트 검증 -1. 하루카 **시트 투명알파 재확정**(`(소스 아카이브)`). -2. **리그 16파츠 확보** — **방법 A(마스터-슬라이스) 우선**: 슬라이스용 마스터 1장 생성 → 로컬에서 16조각(관절 자동 정합). 어려우면 **방법 B(개별 생성)** 폴백. (대체 손 attachment는 B로.) 스펙: `../이미지작업_의뢰서.md` · `../03_Assets/Parts/Parts.md` → `Parts/Images/`. -3. 뷰어에서 실아트 로드 → `rig.json`의 `imgAnchor/pos` 튜닝(관절 정합). -4. 배경춤(`dance_idle`)이 실제 하루카로 자연스러운지 확인. -- **완료조건**: 실제 파츠로 배경춤이 이음새 없이 자연스럽게 루프. - -## Phase 2 — 반응 시퀀서 런타임 -1. 시퀀서 구현: `reactions.json` + `clips/*.json`의 **Body/Face/Mouth/Transform/Caption** 레이어 합성·전환(크로스페이드). -2. 뷰어 확장(또는 별도 러너)으로 `gesture_no`·`gesture_heart`·`dance_idle` 재생. -3. 베이크드 포즈(armscross/heart) + 표정 프레임 연동. 말하기 근사 립싱크. -- **완료조건**: 상황키 3종(error/success/idle)으로 반응 재생·복귀 동작. - -## Phase 3 — 앱 통합 (WPF 본체) -1. 런타임 호스트 결정(WPF-C# 이식 vs WebView2 임베드 — `App_Integration.md` 참고). -2. 트리거 API(`Mascot.React/SetIdle/Say`)로 앱 이벤트 연결. -3. 리소스 번들(필요한 PNG만) + 좌표규약. -- **완료조건**: 앱에서 실제 상황에 캐릭터가 반응. - -## Phase 4 — (옵션) 얼굴 mesh-warp 승급 -- 조건 충족 시(정밀 립싱크/중간 각도 고개돌림 — `../02_Architecture/Limits_and_Mitigations.md` L4) neck/head 국소 WebGL mesh-warp 도입. - -## 반응 확장 (상시) -- 같은 프레임워크로 greet/explain/thinking 등 반응을 계속 추가(`../06_Reactions/`). - -## 현재 위치 -> **Phase 1 진입 지점.** 다음 액션 = 시트 재확정 + 리그 파츠 생성 의뢰. - - - diff --git a/Haruka_Live2D/Build-HarukaRigParts.ps1 b/Haruka_Live2D/Build-HarukaRigParts.ps1 deleted file mode 100644 index 8aac621..0000000 --- a/Haruka_Live2D/Build-HarukaRigParts.ps1 +++ /dev/null @@ -1,418 +0,0 @@ -param( - [Parameter(Mandatory=$true)] - [string]$Source, - - [string]$OutputDir = "03_Assets\Parts\Images", - [int]$Width = 520, - [int]$Height = 900 -) - -$ErrorActionPreference = "Stop" -Add-Type -AssemblyName System.Drawing - -$code = @" -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.IO; - -public static class HarukaRigBuilder -{ - static readonly string[] PartNames = new string[] { - "head", "neck", "chest", "pelvis", - "upperarm_r", "forearm_r", "hand_r", - "upperarm_l", "forearm_l", "hand_l", - "thigh_r", "shin_r", "foot_r", - "thigh_l", "shin_l", "foot_l" - }; - - public static void Build(string sourcePath, string outputDir, int width, int height) - { - Directory.CreateDirectory(outputDir); - using (var src = new Bitmap(sourcePath)) - using (var keyed = RemoveGreenKey(src)) - { - Rectangle bbox = FindAlphaBounds(keyed, 8); - using (var master = NormalizeToCanvas(keyed, bbox, width, height)) - { - RemoveTinyComponents(master, 24); - string masterPath = Path.Combine(outputDir, "haruka_part_master_apose.png"); - SavePng(master, masterPath); - SliceParts(master, outputDir); - Validate(outputDir, master, width, height); - } - } - } - - static Bitmap RemoveGreenKey(Bitmap src) - { - var dst = new Bitmap(src.Width, src.Height, PixelFormat.Format32bppArgb); - for (int y = 0; y < src.Height; y++) - { - for (int x = 0; x < src.Width; x++) - { - Color c = src.GetPixel(x, y); - int r = c.R, g = c.G, b = c.B; - int maxRB = Math.Max(r, b); - int delta = g - maxRB; - int alpha = 255; - - if (g > 90 && delta > 35) - { - if (delta >= 120) alpha = 0; - else alpha = Clamp((120 - delta) * 255 / 85, 0, 255); - - if (alpha < 255) - { - int despill = Math.Max(r, b); - g = Math.Min(g, despill + 8); - } - } - - if (alpha <= 2) - { - dst.SetPixel(x, y, Color.FromArgb(0, 0, 0, 0)); - } - else - { - dst.SetPixel(x, y, Color.FromArgb(alpha, r, g, b)); - } - } - } - return dst; - } - - static Rectangle FindAlphaBounds(Bitmap bmp, int threshold) - { - int minX = bmp.Width, minY = bmp.Height, maxX = -1, maxY = -1; - for (int y = 0; y < bmp.Height; y++) - { - for (int x = 0; x < bmp.Width; x++) - { - if (bmp.GetPixel(x, y).A > threshold) - { - if (x < minX) minX = x; - if (x > maxX) maxX = x; - if (y < minY) minY = y; - if (y > maxY) maxY = y; - } - } - } - if (maxX < minX || maxY < minY) return Rectangle.Empty; - return Rectangle.FromLTRB(minX, minY, maxX + 1, maxY + 1); - } - - static Bitmap NormalizeToCanvas(Bitmap src, Rectangle bbox, int width, int height) - { - var dst = new Bitmap(width, height, PixelFormat.Format32bppArgb); - using (var g = Graphics.FromImage(dst)) - { - g.Clear(Color.Transparent); - g.CompositingMode = CompositingMode.SourceOver; - g.CompositingQuality = CompositingQuality.HighQuality; - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.SmoothingMode = SmoothingMode.HighQuality; - g.PixelOffsetMode = PixelOffsetMode.HighQuality; - - float maxW = width - 40f; - float maxH = height - 40f; - float scale = Math.Min(maxW / bbox.Width, maxH / bbox.Height); - int drawW = (int)Math.Round(bbox.Width * scale); - int drawH = (int)Math.Round(bbox.Height * scale); - int drawX = (width - drawW) / 2; - int drawY = 20; - if (drawY + drawH > height - 20) drawY = (height - drawH) / 2; - g.DrawImage(src, new Rectangle(drawX, drawY, drawW, drawH), bbox, GraphicsUnit.Pixel); - } - HardClearGreen(dst); - DespillGreenBias(dst); - return dst; - } - - static void HardClearGreen(Bitmap bmp) - { - for (int y = 0; y < bmp.Height; y++) - { - for (int x = 0; x < bmp.Width; x++) - { - Color c = bmp.GetPixel(x, y); - if (c.A == 0) continue; - int maxRB = Math.Max(c.R, c.B); - if (c.G > 70 && c.G - maxRB > 30) - { - int a = c.G - maxRB > 80 ? 0 : Math.Max(0, c.A - 140); - int g = Math.Min(c.G, maxRB + 5); - bmp.SetPixel(x, y, a == 0 ? Color.FromArgb(0, 0, 0, 0) : Color.FromArgb(a, c.R, g, c.B)); - } - } - } - } - - static void DespillGreenBias(Bitmap bmp) - { - for (int y = 0; y < bmp.Height; y++) - { - for (int x = 0; x < bmp.Width; x++) - { - Color c = bmp.GetPixel(x, y); - if (c.A == 0) continue; - if (c.G > c.R + 6 && c.G > c.B + 10) - { - int g = Math.Min(c.G, Math.Max(c.R, c.B) + 4); - int a = c.A; - if (c.A < 235 && c.G > Math.Max(c.R, c.B) + 18) a = Math.Max(0, c.A - 35); - bmp.SetPixel(x, y, Color.FromArgb(a, c.R, g, c.B)); - } - } - } - } - - static void SliceParts(Bitmap master, string outputDir) - { - Rectangle bb = FindAlphaBounds(master, 8); - var parts = new Dictionary(); - foreach (string name in PartNames) - parts[name] = new Bitmap(master.Width, master.Height, PixelFormat.Format32bppArgb); - - for (int y = 0; y < master.Height; y++) - { - for (int x = 0; x < master.Width; x++) - { - Color c = master.GetPixel(x, y); - if (c.A == 0) continue; - string part = ClassifyPixel(c, x, y, bb); - parts[part].SetPixel(x, y, c); - } - } - - foreach (var kv in parts) - { - string path = Path.Combine(outputDir, "haruka_part_" + kv.Key + ".png"); - SavePng(kv.Value, path); - kv.Value.Dispose(); - } - } - - static void RemoveTinyComponents(Bitmap bmp, int minPixels) - { - int w = bmp.Width, h = bmp.Height; - bool[] seen = new bool[w * h]; - int[] stack = new int[w * h]; - int[] comp = new int[w * h]; - int[] dx = new int[] { 1, -1, 0, 0 }; - int[] dy = new int[] { 0, 0, 1, -1 }; - - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - int start = y * w + x; - if (seen[start] || bmp.GetPixel(x, y).A == 0) continue; - - int top = 0, count = 0; - stack[top++] = start; - seen[start] = true; - - while (top > 0) - { - int id = stack[--top]; - comp[count++] = id; - int cx = id % w; - int cy = id / w; - for (int i = 0; i < 4; i++) - { - int nx = cx + dx[i], ny = cy + dy[i]; - if (nx < 0 || nx >= w || ny < 0 || ny >= h) continue; - int nid = ny * w + nx; - if (seen[nid]) continue; - seen[nid] = true; - if (bmp.GetPixel(nx, ny).A > 0) stack[top++] = nid; - } - } - - if (count < minPixels) - { - for (int i = 0; i < count; i++) - { - int id = comp[i]; - bmp.SetPixel(id % w, id / w, Color.FromArgb(0, 0, 0, 0)); - } - } - } - } - } - - static string ClassifyPixel(Color c, int x, int y, Rectangle bb) - { - double nx = (x - bb.Left) / (double)Math.Max(1, bb.Width); - double ny = (y - bb.Top) / (double)Math.Max(1, bb.Height); - double cx = bb.Left + bb.Width * 0.5; - bool screenLeft = x < cx; - double dx = Math.Abs((x - cx) / Math.Max(1.0, bb.Width)); - - bool hair = IsHair(c); - bool skin = IsSkin(c); - - if (hair && ny < 0.54) return "head"; - if (ny < 0.255) return "head"; - if (hair && dx > 0.18 && ny < 0.58) return "head"; - - if (skin && dx < 0.09 && ny >= 0.235 && ny < 0.335) return "neck"; - - bool sideArmZone = dx > 0.18 && ny >= 0.28 && ny < 0.65; - if (sideArmZone) - { - string side = screenLeft ? "_r" : "_l"; - bool farHand = (screenLeft && nx < 0.25 && ny >= 0.455) || (!screenLeft && nx > 0.75 && ny >= 0.455); - if (skin && farHand) return "hand" + side; - if (ny < 0.435) return "upperarm" + side; - if (ny < 0.595) return "forearm" + side; - return "hand" + side; - } - - if (ny < 0.33) - { - if (dx < 0.085) return "neck"; - return "head"; - } - - if (ny < 0.505) - { - if (dx > 0.15) - { - string side = screenLeft ? "_r" : "_l"; - return ny < 0.43 ? "upperarm" + side : "forearm" + side; - } - return "chest"; - } - - if (ny < 0.625) - { - if (dx > 0.24 && skin) - { - string side = screenLeft ? "_r" : "_l"; - return "hand" + side; - } - return "pelvis"; - } - - if (ny < 0.755) - return screenLeft ? "thigh_r" : "thigh_l"; - - if (ny < 0.895) - return screenLeft ? "shin_r" : "shin_l"; - - return screenLeft ? "foot_r" : "foot_l"; - } - - static bool IsHair(Color c) - { - return c.R > 95 && c.R > c.G + 22 && c.G > c.B - 8 && c.B < 155; - } - - static bool IsSkin(Color c) - { - return c.R > 190 && c.G > 125 && c.B > 95 && c.R >= c.G && c.G >= c.B - 18; - } - - static void Validate(string outputDir, Bitmap master, int width, int height) - { - string reportPath = Path.Combine(outputDir, "_validation.txt"); - using (var sw = new StreamWriter(reportPath, false)) - { - sw.WriteLine("Haruka rig part build validation"); - sw.WriteLine("Canvas: " + width + "x" + height); - sw.WriteLine("Master: haruka_part_master_apose.png"); - foreach (string name in PartNames) - { - string path = Path.Combine(outputDir, "haruka_part_" + name + ".png"); - using (var img = new Bitmap(path)) - { - Rectangle b = FindAlphaBounds(img, 8); - sw.WriteLine(Path.GetFileName(path) + " | " + img.Width + "x" + img.Height + " | " + img.PixelFormat + " | bbox=" + RectText(b)); - } - } - int missing, extra, differing, multiCovered; - DirectCoverageCheck(outputDir, master, out missing, out extra, out differing, out multiCovered); - sw.WriteLine("Direct coverage check | missing=" + missing + " | extra=" + extra + " | differing=" + differing + " | multi_covered=" + multiCovered); - } - } - - static void DirectCoverageCheck(string outputDir, Bitmap master, out int missing, out int extra, out int differing, out int multiCovered) - { - missing = 0; - extra = 0; - differing = 0; - multiCovered = 0; - - var loaded = new List(); - try - { - foreach (string name in PartNames) - loaded.Add(new Bitmap(Path.Combine(outputDir, "haruka_part_" + name + ".png"))); - - for (int y = 0; y < master.Height; y++) - { - for (int x = 0; x < master.Width; x++) - { - Color m = master.GetPixel(x, y); - int hits = 0; - Color last = Color.Transparent; - foreach (var part in loaded) - { - Color c = part.GetPixel(x, y); - if (c.A > 0) - { - hits++; - last = c; - } - } - - if (m.A > 0 && hits == 0) missing++; - if (m.A == 0 && hits > 0) extra++; - if (hits > 1) multiCovered++; - if (m.A > 0 && hits > 0) - { - int d = Math.Abs(m.A - last.A) + Math.Abs(m.R - last.R) + Math.Abs(m.G - last.G) + Math.Abs(m.B - last.B); - if (d != 0) differing++; - } - } - } - } - finally - { - foreach (var bmp in loaded) bmp.Dispose(); - } - } - - static string RectText(Rectangle r) - { - return r.Left + "," + r.Top + "," + r.Width + "," + r.Height; - } - - static void SavePng(Bitmap bmp, string path) - { - bmp.Save(path, ImageFormat.Png); - } - - static int Clamp(int v, int min, int max) - { - return v < min ? min : (v > max ? max : v); - } -} -"@ - -Add-Type -TypeDefinition $code -ReferencedAssemblies "System.Drawing" - -$resolvedSource = (Resolve-Path -LiteralPath $Source).Path -$resolvedOut = if (Test-Path -LiteralPath $OutputDir) { - (Resolve-Path -LiteralPath $OutputDir).Path -} else { - New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null - (Resolve-Path -LiteralPath $OutputDir).Path -} - -[HarukaRigBuilder]::Build($resolvedSource, $resolvedOut, $Width, $Height) -Write-Output "Generated Haruka rig parts in $resolvedOut" diff --git a/Haruka_Live2D/Cubism_Editor_AI_Automation_Note.txt b/Haruka_Live2D/Cubism_Editor_AI_Automation_Note.txt deleted file mode 100644 index 43b97b9..0000000 --- a/Haruka_Live2D/Cubism_Editor_AI_Automation_Note.txt +++ /dev/null @@ -1,67 +0,0 @@ -Cubism Editor 리깅/모션/익스포트 단계의 AI 자동화 가능성 - -완전 자동화는 어렵고, 현실적으로는 AI 반자동화 + Cubism Editor 검수가 맞습니다. - -가능한 자동화 범위: - -단계: PSD 레이어 명세 작성 -AI 자동화 가능성: 높음 -판단: manifest, 파일명, 레이어 구조 생성 가능 - -단계: PSD/PNG 레이어 검수 -AI 자동화 가능성: 높음 -판단: 누락 레이어, 해상도, 파일명, 투명도 검사 가능 - -단계: ArtMesh 생성 -AI 자동화 가능성: 중간 -판단: Cubism의 자동 Mesh 기능 활용 가능, 품질 검수 필요 - -단계: Deformer 구성 -AI 자동화 가능성: 중간 -판단: 자동 생성/템플릿 보조 가능, 캐릭터별 튜닝 필요 - -단계: 파라미터 키폼 -AI 자동화 가능성: 낮음~중간 -판단: AI가 설계값은 줄 수 있지만 실제 변형 품질은 수동 조정 필요 - -단계: Physics 설정 -AI 자동화 가능성: 중간 -판단: 기본값 제안 가능, 최종 느낌은 Cubism에서 조정 필요 - -단계: Motion 제작 -AI 자동화 가능성: 중간~높음 -판단: motion3.json 곡선 설계는 AI가 도울 수 있음 - -단계: Expression 제작 -AI 자동화 가능성: 높음 -판단: 파라미터 조합으로 exp3.json 생성 가능 - -단계: .moc3/.model3.json export -AI 자동화 가능성: 낮음~중간 -판단: Cubism Editor 작업이 필요. 공식 API는 export 알림은 제공하지만 완전한 일괄 export 명령 자동화로 보긴 어려움 - -핵심 판단: - -리깅 자체, 특히 얼굴 회전, 입 모양, 눈깜빡임, 머리카락 물리, 손 제스처는 AI가 설계하고 Cubism에서 사람이 확인/수정해야 품질이 나옵니다. - -공식 문서상 Cubism Editor 제작 흐름도 원화 분리 -> Modeling -> Animation -> Export로 설명되어 있고, Modeling 단계에서 Deformer, Parameter, Glue 등을 사용합니다. 또한 Template 기능으로 일부 움직임을 자동화할 수 있다고 되어 있습니다. - -출처: -Production Flow -https://docs.live2d.com/en/cubism-editor-manual/workflow/ - -Cubism Editor에는 외부 API도 있습니다. WebSocket/JSON 방식이고, 파라미터 조회/설정 같은 작업은 가능합니다. 다만 사용자 허용이 필요하고, API 목록은 주로 파라미터 조작/조회와 export 완료 알림 중심입니다. - -출처: -External API Integration -https://docs.live2d.com/en/cubism-editor-manual/external-application-integration-api/ - -API Function List -https://docs.live2d.com/en/cubism-editor-manual/external-application-integration-api-list/ - -정리: - -AI로 70% 정도의 설계, 데이터, 검수 자동화는 가능하지만, Cubism Editor에서의 최종 리깅 품질 조정은 수동 검수가 필요합니다. 특히 .moc3까지 원클릭 완전 자동 생성하는 계획은 위험합니다. - - - diff --git a/Haruka_Live2D/README.md b/Haruka_Live2D/README.md deleted file mode 100644 index cf8f335..0000000 --- a/Haruka_Live2D/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Haruka_Profile — 하루카 인터랙티브 캐릭터 시스템 (단일 진실원) - -> **최종 목적**: 하루카를 **앱에 탑재**해 **상황별로 반응하는** 살아있는 마스코트로 만든다. -> (예: 오류 → 팔짱+인상+"안돼요", 성공 → 하트+"잘됐어요", 대기 → 배경 가벼운 춤 …) -> **원칙**: 이미지는 **ChatGPT 자동생성**, 리그·모션·반응 시퀀스·색상은 **코드/데이터**. 방식은 **하이브리드**(리그 + 베이크드 포즈 + 표정 프레임 스왑). - -이 폴더는 목적·방향·구현레벨·방법·자산·로드맵을 한곳에 모은 **source of truth**다. (구 `Haruka_Rigging`는 이 폴더로 통합·폐기됨.) - -## 폴더 안내 -| 폴더 | 내용 | -|---|---| -| `01_Overview/` | 목적·방향(`Purpose_and_Direction.md`), 확정 결정 로그(`Decisions.md`) | -| `02_Architecture/` | 레이어 아키텍처·하이브리드 규칙(`Architecture.md`), 한계·완화·mesh-warp 승급(`Limits_and_Mitigations.md`) | -| `03_Assets/` | 자산 전체 맵(`Assets_Overview.md`), 리그 파츠 생성 스펙(`Parts/`), 표정·베이크드 포즈(`Expressions_and_Poses.md`) | -| `04_Rig/` | 스켈레톤 정의 `rig.json` + `Rig.md` | -| `05_Animation/` | 리그 클립(배경춤) `dance_idle.json` + `Animation.md` | -| `06_Reactions/` | 반응 시퀀서·트리거 설계(`Reactions.md`), 매핑 `reactions.json`, 샘플 클립 `clips/` | -| `07_Viewer/` | 프로토타입 뷰어 `index.html`(더블클릭 재생) + `Viewer.md` | -| `08_Roadmap/` | 단계별 구현 계획(`Roadmap.md`), 앱 통합(`App_Integration.md`) | - -## 한 눈에 (현재 확정 상태) -- **구현 레벨**: 코드 네이티브 경량 리그(강체 컷아웃) + 하이브리드. Live2D/Spine 미사용(자동화 위해). mesh-warp는 **옵션/후속**. -- **분절**: 해부학 16파츠(head·neck·chest·pelvis + 팔3×2 + 다리3×2). -- **얼굴**: 표정 프레임 스왑 20종 + 말하기 talk 프레임(유사 립싱크). -- **완료**: 방향 확정 · 리그 스키마 · 배경춤 프로토타입(뷰어에서 플레이스홀더로 재생 확인 가능). -- **다음**: 시트 투명알파 재확정 → 리그 파츠 생성(ChatGPT) → 배경춤 실아트 검증 → 반응 시퀀서 → 앱 통합. (상세 `08_Roadmap/Roadmap.md`) - -## 지금 바로 -`07_Viewer/index.html` 더블클릭 → 하루카 스켈레톤이 가볍게 춤추는 것을 확인. - - - diff --git a/Haruka_Live2D/tools/generate_live2d_layers.py b/Haruka_Live2D/tools/generate_live2d_layers.py deleted file mode 100644 index 9849155..0000000 --- a/Haruka_Live2D/tools/generate_live2d_layers.py +++ /dev/null @@ -1,544 +0,0 @@ -from __future__ import annotations - -import json -from datetime import datetime -from pathlib import Path -from typing import Callable - -import numpy as np -from PIL import Image, ImageDraw, ImageFilter - - -ROOT = Path(__file__).resolve().parents[1] -ASSETS = ROOT / "03_Assets" -LIVE2D = ASSETS / "Live2D" -PARTS = ASSETS / "Parts" / "Images" -REFERENCE = ASSETS / "Reference" -MANIFEST = LIVE2D / "layer_manifest.json" -OUT_BASE = LIVE2D / "LayerPNGs" -PREVIEW = LIVE2D / "haruka_live2d_layer_preview.png" -PREVIEW_CHECKER = LIVE2D / "haruka_live2d_layer_preview_checker.png" -SWAP_PREVIEW_CHECKER = LIVE2D / "haruka_live2d_swap_parts_preview_checker.png" -REPORT_JSON = LIVE2D / "layer_generation_report.json" -REPORT_MD = LIVE2D / "LayerPNGs_README.md" - -SRC_W, SRC_H = 520, 900 -OUT_W, OUT_H = 1600, 2800 -SCALE = 3 -OFFSET_X, OFFSET_Y = 20, 50 - - -def load_rgba(path: Path) -> Image.Image: - return Image.open(path).convert("RGBA") - - -def blank(size: tuple[int, int] = (SRC_W, SRC_H)) -> Image.Image: - return Image.new("RGBA", size, (0, 0, 0, 0)) - - -def arr(img: Image.Image) -> np.ndarray: - return np.array(img.convert("RGBA")) - - -def alpha(img: Image.Image, min_alpha: int = 8) -> np.ndarray: - return arr(img)[:, :, 3] > min_alpha - - -def rect(x0: int, y0: int, x1: int, y1: int) -> np.ndarray: - mask = np.zeros((SRC_H, SRC_W), dtype=bool) - mask[max(0, y0) : min(SRC_H, y1), max(0, x0) : min(SRC_W, x1)] = True - return mask - - -def soften_mask(mask: np.ndarray, radius: float = 0.6) -> Image.Image: - img = Image.fromarray((mask.astype(np.uint8) * 255), "L") - if radius: - img = img.filter(ImageFilter.GaussianBlur(radius)) - return img - - -def masked(src: Image.Image, mask: np.ndarray, radius: float = 0.45, opacity: float = 1.0) -> Image.Image: - out = src.copy().convert("RGBA") - source_alpha = out.getchannel("A") - mask_img = soften_mask(mask, radius) - if opacity != 1.0: - mask_img = mask_img.point(lambda p: int(p * opacity)) - new_alpha = Image.composite(source_alpha, Image.new("L", source_alpha.size, 0), mask_img) - out.putalpha(new_alpha) - return out - - -def merge_source_layers(*imgs: Image.Image) -> Image.Image: - out = blank() - for img in imgs: - out.alpha_composite(img.convert("RGBA")) - return out - - -def source_to_output(img: Image.Image) -> Image.Image: - scaled = img.resize((SRC_W * SCALE, SRC_H * SCALE), Image.Resampling.LANCZOS) - out = Image.new("RGBA", (OUT_W, OUT_H), (0, 0, 0, 0)) - out.alpha_composite(scaled, (OFFSET_X, OFFSET_Y)) - return out - - -def fit_to_output(img: Image.Image, max_w: int, max_h: int, y: int) -> Image.Image: - src = img.convert("RGBA") - ratio = min(max_w / src.width, max_h / src.height) - size = (int(src.width * ratio), int(src.height * ratio)) - scaled = src.resize(size, Image.Resampling.LANCZOS) - out = Image.new("RGBA", (OUT_W, OUT_H), (0, 0, 0, 0)) - out.alpha_composite(scaled, ((OUT_W - size[0]) // 2, y)) - return out - - -def anti_alias_draw(draw_fn: Callable[[ImageDraw.ImageDraw, int], None]) -> Image.Image: - factor = 4 - img = Image.new("RGBA", (SRC_W * factor, SRC_H * factor), (0, 0, 0, 0)) - draw = ImageDraw.Draw(img) - draw_fn(draw, factor) - return img.resize((SRC_W, SRC_H), Image.Resampling.LANCZOS) - - -def ellipse_layer(box: tuple[int, int, int, int], fill: tuple[int, int, int, int], blur: float = 0.0) -> Image.Image: - def draw_fn(draw: ImageDraw.ImageDraw, f: int) -> None: - draw.ellipse(tuple(v * f for v in box), fill=fill) - - img = anti_alias_draw(draw_fn) - if blur: - img = img.filter(ImageFilter.GaussianBlur(blur)) - return img - - -def line_layer( - points: list[tuple[int, int]], - fill: tuple[int, int, int, int], - width: int = 2, - joint: str = "curve", -) -> Image.Image: - def draw_fn(draw: ImageDraw.ImageDraw, f: int) -> None: - pts = [(x * f, y * f) for x, y in points] - draw.line(pts, fill=fill, width=width * f, joint=joint) - - return anti_alias_draw(draw_fn) - - -def polygon_layer(points: list[tuple[int, int]], fill: tuple[int, int, int, int]) -> Image.Image: - def draw_fn(draw: ImageDraw.ImageDraw, f: int) -> None: - draw.polygon([(x * f, y * f) for x, y in points], fill=fill) - - return anti_alias_draw(draw_fn) - - -def face_underpaint_layer() -> Image.Image: - base = merge_source_layers( - ellipse_layer((196, 62, 324, 229), (238, 184, 156, 245), 0.3), - polygon_layer([(210, 168), (310, 168), (289, 235), (260, 252), (231, 235)], (236, 178, 151, 245)), - ellipse_layer((218, 80, 302, 198), (248, 199, 174, 125), 5.0), - ) - return base - - -def draw_capsule(layer: Image.Image, p0: tuple[int, int], p1: tuple[int, int], width: int, fill: tuple[int, int, int, int]) -> None: - draw = ImageDraw.Draw(layer) - draw.line([p0, p1], fill=fill, width=width) - r = width // 2 - for x, y in (p0, p1): - draw.ellipse((x - r, y - r, x + r, y + r), fill=fill) - - -def body_limb_layer(kind: str) -> Image.Image: - layer = blank() - skin = (238, 184, 156, 220) - if kind == "arm_upper_L": - draw_capsule(layer, (354, 268), (390, 350), 28, skin) - elif kind == "arm_fore_L": - draw_capsule(layer, (386, 350), (404, 405), 23, skin) - elif kind == "arm_upper_R": - draw_capsule(layer, (166, 268), (128, 350), 28, skin) - elif kind == "arm_fore_R": - draw_capsule(layer, (134, 350), (116, 405), 23, skin) - elif kind == "leg_upper_L": - draw_capsule(layer, (294, 410), (298, 610), 46, skin) - elif kind == "leg_lower_L": - draw_capsule(layer, (292, 595), (287, 740), 34, skin) - elif kind == "leg_upper_R": - draw_capsule(layer, (226, 410), (222, 610), 46, skin) - elif kind == "leg_lower_R": - draw_capsule(layer, (228, 595), (233, 740), 34, skin) - return layer.filter(ImageFilter.GaussianBlur(0.25)) - - -def source_color_masks(sources: dict[str, Image.Image]) -> dict[str, np.ndarray]: - masks: dict[str, np.ndarray] = {} - for name, img in sources.items(): - a = arr(img) - r = a[:, :, 0].astype(np.int16) - g = a[:, :, 1].astype(np.int16) - b = a[:, :, 2].astype(np.int16) - al = a[:, :, 3] > 8 - masks[f"{name}:alpha"] = al - masks[f"{name}:skin"] = al & (r > 125) & (g > 75) & (b > 55) & (r > g - 5) & (r > b + 10) - masks[f"{name}:hair"] = al & (g > 65) & (b > 60) & (r < 135) & ((g - r) > 12) & ((b - r) > 3) - masks[f"{name}:hair_hi"] = al & (g > 120) & (b > 105) & (r < 125) & ((g - r) > 30) - masks[f"{name}:white"] = al & (r > 170) & (g > 170) & (b > 168) & (np.abs(r - g) < 45) & (np.abs(g - b) < 55) - masks[f"{name}:black"] = al & (r < 90) & (g < 95) & (b < 100) - masks[f"{name}:mint"] = al & (g > 115) & (b > 105) & (r < 165) & ((g - r) > 20) - masks[f"{name}:dark_teal"] = al & (g > 55) & (b > 55) & (r < 85) & ((g - r) > 8) - return masks - - -def facial_layers() -> dict[str, Image.Image]: - layers: dict[str, Image.Image] = {} - eye_specs = { - "L": {"cx": 294, "cy": 140, "tilt": -2}, - "R": {"cx": 226, "cy": 140, "tilt": 2}, - } - for side, spec in eye_specs.items(): - cx, cy = spec["cx"], spec["cy"] - white = polygon_layer( - [(cx - 22, cy), (cx - 14, cy - 8), (cx + 13, cy - 8), (cx + 22, cy - 1), (cx + 13, cy + 8), (cx - 13, cy + 7)], - (255, 246, 236, 232), - ) - iris = ellipse_layer((cx - 8, cy - 10, cx + 8, cy + 10), (139, 89, 47, 240)) - iris.alpha_composite(ellipse_layer((cx - 6, cy - 7, cx + 6, cy + 8), (180, 121, 62, 195))) - pupil = ellipse_layer((cx - 4, cy - 6, cx + 4, cy + 6), (38, 26, 19, 240)) - highlight = merge_source_layers( - ellipse_layer((cx - 2, cy - 7, cx + 3, cy - 2), (255, 255, 255, 230)), - ellipse_layer((cx + 4, cy + 1, cx + 6, cy + 3), (255, 255, 255, 170)), - ) - upper = line_layer([(cx - 23, cy - 3), (cx - 11, cy - 10), (cx + 10, cy - 10), (cx + 23, cy - 3)], (50, 28, 24, 235), 2) - lower = line_layer([(cx - 19, cy + 6), (cx - 6, cy + 9), (cx + 11, cy + 8), (cx + 19, cy + 5)], (82, 44, 38, 165), 1) - lid = line_layer([(cx - 20, cy - 13), (cx - 6, cy - 16), (cx + 11, cy - 15), (cx + 20, cy - 12)], (226, 160, 139, 125), 1) - layers[f"eye_{side}_white"] = white - layers[f"eye_{side}_iris"] = iris - layers[f"eye_{side}_pupil"] = pupil - layers[f"eye_{side}_highlight"] = highlight - layers[f"eye_{side}_upper_lash"] = upper - layers[f"eye_{side}_lower_lash"] = lower - layers[f"eye_{side}_lid"] = lid - - layers["brow_L"] = line_layer([(274, 116), (288, 111), (310, 114)], (66, 55, 48, 210), 3) - layers["brow_R"] = line_layer([(210, 114), (232, 111), (246, 116)], (66, 55, 48, 210), 3) - layers["mouth_inside"] = ellipse_layer((249, 174, 271, 188), (85, 22, 28, 220)) - layers["teeth_upper"] = polygon_layer([(252, 175), (268, 175), (266, 180), (254, 180)], (250, 245, 232, 220)) - layers["teeth_lower"] = polygon_layer([(254, 184), (266, 184), (265, 187), (255, 187)], (242, 232, 220, 165)) - layers["tongue"] = ellipse_layer((252, 181, 268, 190), (214, 98, 105, 195)) - layers["mouth_line_upper"] = line_layer([(243, 174), (253, 171), (260, 173), (267, 171), (277, 174)], (94, 42, 36, 225), 2) - layers["mouth_line_lower"] = line_layer([(251, 187), (260, 191), (269, 187)], (120, 65, 60, 145), 1) - layers["lip_highlight"] = line_layer([(252, 170), (260, 168), (268, 170)], (255, 216, 205, 120), 1) - layers["nose"] = merge_source_layers( - line_layer([(260, 144), (257, 158), (261, 164)], (154, 91, 76, 135), 1), - ellipse_layer((257, 161, 265, 167), (124, 70, 62, 70), 0.4), - ) - layers["cheek_L"] = ellipse_layer((288, 155, 323, 177), (255, 112, 130, 60), 2.5) - layers["cheek_R"] = ellipse_layer((197, 155, 232, 177), (255, 112, 130, 60), 2.5) - layers["face_shadow"] = merge_source_layers( - ellipse_layer((201, 185, 318, 232), (140, 81, 65, 36), 4.0), - ellipse_layer((220, 70, 300, 102), (190, 125, 100, 35), 3.0), - ) - return layers - - -def string_and_accessory_primitives() -> dict[str, Image.Image]: - layers: dict[str, Image.Image] = {} - layers["hoodie_string_L"] = merge_source_layers( - line_layer([(276, 265), (282, 305), (281, 356)], (54, 54, 54, 220), 2), - line_layer([(281, 354), (283, 365)], (65, 207, 184, 230), 2), - ) - layers["hoodie_string_R"] = merge_source_layers( - line_layer([(244, 265), (238, 305), (239, 356)], (54, 54, 54, 220), 2), - line_layer([(239, 354), (237, 365)], (65, 207, 184, 230), 2), - ) - layers["choker_band_draw"] = merge_source_layers( - line_layer([(218, 220), (245, 226), (275, 226), (302, 220)], (31, 28, 28, 235), 5), - line_layer([(220, 217), (246, 222), (274, 222), (300, 217)], (72, 64, 63, 85), 1), - ) - layers["pendant_draw"] = merge_source_layers( - ellipse_layer((253, 231, 267, 248), (38, 203, 188, 225)), - ellipse_layer((257, 233, 262, 238), (180, 255, 247, 155)), - line_layer([(260, 223), (260, 230)], (35, 35, 35, 230), 1), - ) - return layers - - -def swap_layers(sources: dict[str, Image.Image]) -> dict[str, Image.Image]: - layers: dict[str, Image.Image] = {} - for side, hand_name, angle, xy in ( - ("L", "hand_l", -32, (276, 292)), - ("R", "hand_r", 32, (190, 292)), - ): - hand = sources[hand_name].crop(sources[hand_name].getbbox()) - hand = hand.resize((64, 94), Image.Resampling.LANCZOS).rotate(angle, resample=Image.Resampling.BICUBIC, expand=True) - layer = blank() - layer.alpha_composite(hand, xy) - layers[f"swap_hand_heart_{side}"] = layer - - sleeve_l = merge_source_layers(sources["upperarm_l"], sources["forearm_l"], sources["hand_l"]) - sleeve_r = merge_source_layers(sources["upperarm_r"], sources["forearm_r"], sources["hand_r"]) - for side, src, angle, xy in ( - ("L", sleeve_l, -47, (220, 280)), - ("R", sleeve_r, 47, (185, 276)), - ): - crop = src.crop(src.getbbox()) - crop = crop.resize((190, 120), Image.Resampling.LANCZOS).rotate(angle, resample=Image.Resampling.BICUBIC, expand=True) - layer = blank() - layer.alpha_composite(crop, xy) - layers[f"swap_arm_cross_{side}"] = layer - return layers - - -def build_source_layers() -> tuple[dict[str, Image.Image], dict[str, str]]: - sources = { - "master": load_rgba(PARTS / "haruka_part_master_apose.png"), - "head": load_rgba(PARTS / "haruka_part_head.png"), - "chest": load_rgba(PARTS / "haruka_part_chest.png"), - "neck": load_rgba(PARTS / "haruka_part_neck.png"), - "pelvis": load_rgba(PARTS / "haruka_part_pelvis.png"), - "upperarm_l": load_rgba(PARTS / "haruka_part_upperarm_l.png"), - "upperarm_r": load_rgba(PARTS / "haruka_part_upperarm_r.png"), - "forearm_l": load_rgba(PARTS / "haruka_part_forearm_l.png"), - "forearm_r": load_rgba(PARTS / "haruka_part_forearm_r.png"), - "hand_l": load_rgba(PARTS / "haruka_part_hand_l.png"), - "hand_r": load_rgba(PARTS / "haruka_part_hand_r.png"), - "thigh_l": load_rgba(PARTS / "haruka_part_thigh_l.png"), - "thigh_r": load_rgba(PARTS / "haruka_part_thigh_r.png"), - "shin_l": load_rgba(PARTS / "haruka_part_shin_l.png"), - "shin_r": load_rgba(PARTS / "haruka_part_shin_r.png"), - "foot_l": load_rgba(PARTS / "haruka_part_foot_l.png"), - "foot_r": load_rgba(PARTS / "haruka_part_foot_r.png"), - } - masks = source_color_masks(sources) - layers: dict[str, Image.Image] = {} - notes: dict[str, str] = {} - - head = sources["head"] - chest = sources["chest"] - neck = sources["neck"] - pelvis = sources["pelvis"] - - # Hair split. L/R are from the character's point of view; L is screen right. - hair = masks["head:hair"] | (alpha(head) & ~masks["head:skin"] & rect(120, 0, 400, 385)) - layers["back_hair_base"] = masked(head, hair & rect(145, 45, 376, 370), 0.35, 0.9) - layers["back_hair_shadow"] = masked(head, (hair & (masks["head:dark_teal"] | rect(145, 105, 376, 370))) & rect(145, 105, 376, 370), 0.45, 0.35) - layers["back_hair_tip_L"] = masked(head, hair & rect(305, 170, 382, 370), 0.55) - layers["back_hair_tip_R"] = masked(head, hair & rect(138, 170, 215, 370), 0.55) - layers["back_hair_strand_L01"] = masked(head, hair & rect(325, 70, 382, 305), 0.5) - layers["back_hair_strand_R01"] = masked(head, hair & rect(138, 70, 195, 305), 0.5) - layers["front_hair_center"] = masked(head, hair & rect(205, 18, 315, 158), 0.35) - layers["front_hair_L"] = masked(head, hair & rect(270, 28, 368, 190), 0.4) - layers["front_hair_R"] = masked(head, hair & rect(152, 28, 250, 190), 0.4) - layers["side_hair_L"] = masked(head, hair & rect(300, 115, 382, 350), 0.45) - layers["side_hair_R"] = masked(head, hair & rect(138, 115, 220, 350), 0.45) - layers["hair_highlight_front"] = masked(head, (masks["head:hair_hi"] | (hair & rect(160, 20, 365, 245))) & rect(160, 20, 365, 245), 0.8, 0.45) - - # Body and hidden under-paint. - layers["neck_back_fill"] = merge_source_layers(masked(neck, masks["neck:skin"] | alpha(neck), 0.5, 0.55), body_limb_layer("leg_upper_L").crop((0, 0, 1, 1))) - layers["neck_front"] = masked(neck, alpha(neck), 0.35) - torso_mask = masks["chest:skin"] & rect(190, 240, 330, 402) - layers["torso_skin"] = masked(chest, torso_mask, 0.6) - for layer_id in ("arm_upper_L", "arm_fore_L", "arm_upper_R", "arm_fore_R", "leg_upper_L", "leg_lower_L", "leg_upper_R", "leg_lower_R"): - layers[layer_id] = body_limb_layer(layer_id) - layers["hand_L_base"] = masked(sources["hand_l"], alpha(sources["hand_l"]), 0.35) - layers["hand_R_base"] = masked(sources["hand_r"], alpha(sources["hand_r"]), 0.35) - - # Clothes. - white_chest = masks["chest:white"] | (alpha(chest) & ~masks["chest:skin"] & rect(150, 210, 370, 360)) - jacket_chest = alpha(chest) & ~masks["chest:skin"] & ~(white_chest & rect(195, 230, 330, 350)) - layers["hood_back"] = merge_source_layers(masked(chest, (white_chest | (alpha(chest) & ~masks["chest:skin"])) & rect(160, 222, 362, 292), 0.55, 0.45), polygon_layer([(178, 244), (232, 220), (288, 220), (342, 244), (315, 286), (205, 286)], (42, 45, 52, 72))) - layers["hood_front_L"] = masked(chest, white_chest & rect(260, 225, 365, 325), 0.45) - layers["hood_front_R"] = masked(chest, white_chest & rect(155, 225, 260, 325), 0.45) - layers["jacket_body"] = masked(chest, jacket_chest, 0.45) - layers["jacket_sleeve_L"] = merge_source_layers( - masked(sources["upperarm_l"], alpha(sources["upperarm_l"]), 0.35), - masked(sources["forearm_l"], alpha(sources["forearm_l"]), 0.35), - ) - layers["jacket_sleeve_R"] = merge_source_layers( - masked(sources["upperarm_r"], alpha(sources["upperarm_r"]), 0.35), - masked(sources["forearm_r"], alpha(sources["forearm_r"]), 0.35), - ) - layers["hoodie_front"] = masked(chest, white_chest & rect(198, 235, 324, 350), 0.4) - layers["pants_base"] = merge_source_layers( - masked(pelvis, alpha(pelvis), 0.35), - masked(sources["thigh_l"], alpha(sources["thigh_l"]), 0.35), - masked(sources["thigh_r"], alpha(sources["thigh_r"]), 0.35), - masked(sources["shin_l"], alpha(sources["shin_l"]), 0.35), - masked(sources["shin_r"], alpha(sources["shin_r"]), 0.35), - ) - layers["shoe_L"] = masked(sources["foot_l"], alpha(sources["foot_l"]), 0.35) - layers["shoe_R"] = masked(sources["foot_r"], alpha(sources["foot_r"]), 0.35) - - # Face and Accessories from source masks plus primitives. - face_mask = masks["head:skin"] & rect(185, 72, 335, 232) - layers["face_base"] = merge_source_layers(face_underpaint_layer(), masked(head, face_mask, 0.5)) - layers["ear_L"] = masked(head, masks["head:skin"] & rect(315, 105, 370, 210), 0.5) - layers["ear_R"] = masked(head, masks["head:skin"] & rect(150, 105, 205, 210), 0.5) - layers.update(facial_layers()) - - headphone_l = (masks["head:white"] | masks["head:mint"] | (alpha(head) & rect(322, 58, 376, 220))) & rect(310, 45, 382, 235) - headphone_r = (masks["head:white"] | masks["head:mint"] | (alpha(head) & rect(145, 58, 198, 220))) & rect(138, 45, 210, 235) - band = (masks["head:white"] | masks["head:mint"] | alpha(head)) & rect(195, 0, 330, 88) - layers["headphone_band"] = masked(head, band, 0.45) - layers["headphone_L"] = masked(head, headphone_l, 0.45) - layers["headphone_R"] = masked(head, headphone_r, 0.45) - primitive_Accessories = string_and_accessory_primitives() - layers["hoodie_string_L"] = primitive_Accessories["hoodie_string_L"] - layers["hoodie_string_R"] = primitive_Accessories["hoodie_string_R"] - choker_src = masked(head, masks["head:black"] & rect(205, 205, 315, 240), 0.45) - layers["choker_band"] = merge_source_layers(choker_src, primitive_Accessories["choker_band_draw"]) - pendant_src = masked(head, (masks["head:mint"] | masks["head:white"]) & rect(242, 225, 280, 260), 0.45) - layers["pendant"] = merge_source_layers(pendant_src, primitive_Accessories["pendant_draw"]) - - layers.update(swap_layers(sources)) - - for key in layers: - notes[key] = "generated from existing Haruka A-pose assets and manifest mapping" - return layers, notes - - -def checker(size: tuple[int, int]) -> Image.Image: - w, h = size - img = Image.new("RGBA", size, (255, 255, 255, 255)) - draw = ImageDraw.Draw(img) - step = 40 - for y in range(0, h, step): - for x in range(0, w, step): - if (x // step + y // step) % 2: - draw.rectangle((x, y, x + step - 1, y + step - 1), fill=(220, 220, 220, 255)) - return img - - -def write_guides(manifest: dict) -> dict[str, Image.Image]: - guide_sheet = fit_to_output(load_rgba(REFERENCE / "haruka_sheet.png"), 1520, 1140, 80) - guide_apose = source_to_output(load_rgba(PARTS / "haruka_part_master_apose.png")) - return { - "guide_haruka_sheet": guide_sheet, - "guide_apose_current": guide_apose, - } - - -def bbox_of(img: Image.Image) -> list[int] | None: - box = img.getbbox() - return list(box) if box else None - - -def save_report(manifest: dict, layer_outputs: dict[str, Image.Image], notes: dict[str, str]) -> None: - rows = [] - missing: list[str] = [] - nonempty_required = 0 - for layer in manifest["layers"]: - layer_id = layer["id"] - file_rel = layer["file"] - path = OUT_BASE / file_rel - img = layer_outputs.get(layer_id) - bbox = bbox_of(img) if img else None - if not path.exists(): - missing.append(file_rel) - if layer.get("required") and bbox: - nonempty_required += 1 - rows.append( - { - "id": layer_id, - "file": file_rel, - "group": layer["group"], - "required": bool(layer.get("required")), - "import": bool(layer.get("import")), - "exists": path.exists(), - "size": list(img.size) if img else None, - "bbox": bbox, - "note": notes.get(layer_id, ""), - } - ) - - report = { - "generatedAt": datetime.now().isoformat(timespec="seconds"), - "canvas": {"width": OUT_W, "height": OUT_H}, - "scaleFromSource": {"source": [SRC_W, SRC_H], "scale": SCALE, "offset": [OFFSET_X, OFFSET_Y]}, - "layerCount": len(rows), - "requiredLayerCount": sum(1 for layer in manifest["layers"] if layer.get("required")), - "nonemptyRequiredLayerCount": nonempty_required, - "missingFiles": missing, - "rows": rows, - "psdNote": "Layered PSD was not written in this environment. Use LayerPNGs in manifest order to assemble the Cubism import PSD.", - } - REPORT_JSON.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") - - lines = [ - "# Live2D Layer PNG Bundle", - "", - f"- Generated: {report['generatedAt']}", - f"- Canvas: {OUT_W}x{OUT_H}, transparent RGBA", - f"- Layers: {report['layerCount']}", - f"- Required non-empty: {nonempty_required}/{report['requiredLayerCount']}", - "- PSD note: layered PSD was not written here; assemble these PNGs in manifest order in Photoshop/Clip Studio/Cubism workflow.", - "", - "## Files", - "", - "| Group | ID | File | Required | Non-empty |", - "|---|---|---|---:|---:|", - ] - for row in rows: - lines.append( - f"| {row['group']} | `{row['id']}` | `{row['file']}` | {str(row['required']).lower()} | {str(row['bbox'] is not None).lower()} |" - ) - REPORT_MD.write_text("\n".join(lines) + "\n", encoding="utf-8") - - -def main() -> None: - manifest = json.loads(MANIFEST.read_text(encoding="utf-8-sig")) - OUT_BASE.mkdir(parents=True, exist_ok=True) - layer_outputs: dict[str, Image.Image] = {} - notes: dict[str, str] = {} - guide_layers = write_guides(manifest) - source_layers, source_notes = build_source_layers() - notes.update({key: "guide layer, not for Cubism import" for key in guide_layers}) - notes.update(source_notes) - - for layer in manifest["layers"]: - layer_id = layer["id"] - rel = layer["file"] - out_path = OUT_BASE / rel - out_path.parent.mkdir(parents=True, exist_ok=True) - if layer_id in guide_layers: - out = guide_layers[layer_id] - elif layer_id in source_layers: - out = source_to_output(source_layers[layer_id]) - else: - raise KeyError(f"No generator for {layer_id}") - out.save(out_path) - layer_outputs[layer_id] = out - - composite = Image.new("RGBA", (OUT_W, OUT_H), (0, 0, 0, 0)) - for layer in manifest["layers"]: - if not layer.get("import"): - continue - if layer.get("group") == "SwapParts": - continue - composite.alpha_composite(layer_outputs[layer["id"]]) - composite.save(PREVIEW) - checker_bg = checker((OUT_W, OUT_H)) - checker_bg.alpha_composite(composite) - checker_bg.convert("RGB").save(PREVIEW_CHECKER) - - swap_composite = composite.copy() - for layer in manifest["layers"]: - if layer.get("group") == "SwapParts": - swap_composite.alpha_composite(layer_outputs[layer["id"]]) - swap_checker = checker((OUT_W, OUT_H)) - swap_checker.alpha_composite(swap_composite) - swap_checker.convert("RGB").save(SWAP_PREVIEW_CHECKER) - save_report(manifest, layer_outputs, notes) - print(f"wrote {len(layer_outputs)} layer PNGs to {OUT_BASE}") - print(f"preview: {PREVIEW}") - print(f"report: {REPORT_JSON}") - - -if __name__ == "__main__": - main() - - - - - - - diff --git a/Haruka_Live2D/tools/make_parts_contact_sheet.py b/Haruka_Live2D/tools/make_parts_contact_sheet.py deleted file mode 100644 index 1c40201..0000000 --- a/Haruka_Live2D/tools/make_parts_contact_sheet.py +++ /dev/null @@ -1,47 +0,0 @@ -from pathlib import Path - -from PIL import Image, ImageDraw, ImageFont - - -ROOT = Path(__file__).resolve().parents[1] -SRC = ROOT / "03_Assets" / "Parts" / "Images" -OUT = ROOT / "03_Assets" / "Live2D" / "_parts_contact_sheet.png" - - -def main() -> None: - files = sorted(p for p in SRC.glob("*.png") if p.name != "haruka_part_master_apose.png") - thumb_w, thumb_h = 220, 380 - label_h = 34 - cols = 4 - rows = (len(files) + cols - 1) // cols - sheet = Image.new("RGBA", (cols * thumb_w, rows * (thumb_h + label_h)), (32, 32, 32, 255)) - draw = ImageDraw.Draw(sheet) - font = ImageFont.load_default() - - for i, path in enumerate(files): - img = Image.open(path).convert("RGBA") - img.thumbnail((thumb_w - 16, thumb_h - 16), Image.Resampling.LANCZOS) - col = i % cols - row = i // cols - x = col * thumb_w + (thumb_w - img.width) // 2 - y = row * (thumb_h + label_h) + 8 - checker = Image.new("RGBA", (thumb_w, thumb_h), (255, 255, 255, 255)) - cd = ImageDraw.Draw(checker) - for yy in range(0, thumb_h, 20): - for xx in range(0, thumb_w, 20): - if (xx // 20 + yy // 20) % 2: - cd.rectangle((xx, yy, xx + 19, yy + 19), fill=(218, 218, 218, 255)) - sheet.alpha_composite(checker, (col * thumb_w, row * (thumb_h + label_h))) - sheet.alpha_composite(img, (x, y)) - draw.text((col * thumb_w + 8, row * (thumb_h + label_h) + thumb_h + 8), path.stem, fill=(255, 255, 255, 255), font=font) - - OUT.parent.mkdir(parents=True, exist_ok=True) - sheet.convert("RGB").save(OUT) - print(OUT) - - -if __name__ == "__main__": - main() - - - diff --git a/Haruka_Live2D/tools/write_photoshop_assembler.py b/Haruka_Live2D/tools/write_photoshop_assembler.py deleted file mode 100644 index b033ae1..0000000 --- a/Haruka_Live2D/tools/write_photoshop_assembler.py +++ /dev/null @@ -1,127 +0,0 @@ -from __future__ import annotations - -import json -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[1] -MANIFEST = ROOT / "03_Assets" / "Live2D" / "layer_manifest.json" -OUT = ROOT / "03_Assets" / "Live2D" / "photoshop_assemble_live2d_psd.jsx" - - -def js_string(value: str) -> str: - return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"' - - -def main() -> None: - manifest = json.loads(MANIFEST.read_text(encoding="utf-8-sig")) - layers = manifest["layers"] - layer_rows = [] - for layer in layers: - layer_rows.append( - "{id:%s,file:%s,group:%s,importLayer:%s,guide:%s}" - % ( - js_string(layer["id"]), - js_string(layer["file"].replace("\\", "/")), - js_string(layer["group"]), - "true" if layer.get("import") else "false", - "true" if layer["group"] == "Guide" else "false", - ) - ) - - jsx = f"""#target photoshop -app.displayDialogs = DialogModes.NO; - -var CANVAS_W = 1600; -var CANVAS_H = 2800; -var LAYERS = [ - {', '.join(layer_rows)} -]; - -function requireFolder(path) {{ - var f = new Folder(path); - if (!f.exists) {{ - throw new Error("Missing folder: " + path); - }} - return f; -}} - -function copyPngIntoDoc(doc, pngFile, layerName, visible) {{ - var src = app.open(pngFile); - src.selection.selectAll(); - src.selection.copy(); - src.close(SaveOptions.DONOTSAVECHANGES); - app.activeDocument = doc; - doc.paste(); - doc.activeLayer.name = layerName; - doc.activeLayer.visible = visible; -}} - -function makeDoc(name) {{ - return app.documents.add( - UnitValue(CANVAS_W, "px"), - UnitValue(CANVAS_H, "px"), - 72, - name, - NewDocumentMode.RGB, - DocumentFill.TRANSPARENT, - 1, - BitsPerChannelType.EIGHT, - "sRGB IEC61966-2.1" - ); -}} - -function savePsd(doc, outFile) {{ - app.activeDocument = doc; - var opts = new PhotoshopSaveOptions(); - opts.layers = true; - opts.embedColorProfile = true; - opts.alphaChannels = true; - doc.saveAs(outFile, opts, true, Extension.LOWERCASE); -}} - -var repo = Folder.selectDialog("Select Haruka_Live2D project folder"); -if (repo == null) {{ - throw new Error("Cancelled"); -}} - -var layerBase = requireFolder(repo.fsName + "/03_Assets/Live2D/LayerPNGs"); -var live2dBase = requireFolder(repo.fsName + "/03_Assets/Live2D"); - -var materialDoc = makeDoc("haruka_live2d_material_separation"); -for (var i = 0; i < LAYERS.length; i++) {{ - var layer = LAYERS[i]; - var file = new File(layerBase.fsName + "/" + layer.file); - if (!file.exists) {{ - throw new Error("Missing PNG: " + file.fsName); - }} - copyPngIntoDoc(materialDoc, file, layer.id, !layer.guide && layer.group != "SwapParts"); -}} -savePsd(materialDoc, new File(live2dBase.fsName + "/haruka_live2d_material_separation.psd")); - -var importDoc = makeDoc("haruka_live2d_import"); -for (var j = 0; j < LAYERS.length; j++) {{ - var importLayer = LAYERS[j]; - if (!importLayer.importLayer) {{ - continue; - }} - var importFile = new File(layerBase.fsName + "/" + importLayer.file); - if (!importFile.exists) {{ - throw new Error("Missing PNG: " + importFile.fsName); - }} - copyPngIntoDoc(importDoc, importFile, importLayer.id, importLayer.group != "SwapParts"); -}} -savePsd(importDoc, new File(live2dBase.fsName + "/haruka_live2d_import.psd")); - -alert("Saved Live2D PSD files in " + live2dBase.fsName); -""" - OUT.write_text(jsx, encoding="utf-8") - print(OUT) - - -if __name__ == "__main__": - main() - - - - diff --git a/Haruka_Live2D/이미지작업_의뢰서.md b/Haruka_Live2D/이미지작업_의뢰서.md deleted file mode 100644 index baafd4c..0000000 --- a/Haruka_Live2D/이미지작업_의뢰서.md +++ /dev/null @@ -1,79 +0,0 @@ -# 하루카(Haruka) 리그 파츠 이미지 작업 의뢰서 — 마스터-슬라이스 · 풀캔버스 - -> **이 문서 하나만 작업자(ChatGPT)에게 주면 됩니다.** 상세 파츠 정의: `03_Assets/Parts/Parts.md`. -> 목적: 하루카를 코드 리그로 춤·제스처 시키기 위한 **관절 파츠 PNG**. - -## 만들 것 (총 17장) -- **마스터 1장**: `haruka_part_master_apose.png` -- **관절 파츠 16장**: `haruka_part_head` · `neck` · `chest` · `pelvis` · `upperarm_r/l` · `forearm_r/l` · `hand_r/l` · `thigh_r/l` · `shin_r/l` · `foot_r/l` - -## 첨부 -- 정체성 시트: **`03_Assets/Reference/haruka_sheet.png`** (동일 인물 유지). - -## 방법: 마스터-슬라이스 (풀캔버스 출력) -1. 아래 프롬프트로 **마스터 A-포즈 1장**을 만든다. -2. 마스터를 16조각(관절 단위)으로 **슬라이스**한다. -3. **★핵심 — 크롭 금지.** 각 조각을 **마스터와 동일한 520×900 캔버스에, 마스터에서의 원위치 그대로** 배치해 저장(그 외 완전 투명). → 16장을 겹치면 마스터가 복원(= **같은 좌표계 → 관절 자동 정합**). - -## 공통 규칙 -- **진짜 투명 알파 PNG — 32-bit RGBA, 배경 alpha = 0.** 흰/회색/체커보드/매트·불투명 24-bit 금지. -- **캔버스 = 520×900 고정**, 파츠는 제자리, 나머지 투명. -- **관절 오버랩**: 근위단(부모쪽)은 관절 뒤로 조금 더 남겨 부모 밑에 들어가게. 원위단 라운드. -- **가장자리**: 안티에일리어스, 흰 후광·프린지 금지. -- **의상 = 세일러 캐주얼**(화이트 블라우스 + 사쿠라핑크 세일러 칼라·리본 + 파스텔 플리츠 스커트 + 양말·로우 스니커즈). 액세서리는 별도 — 파츠에 그리지 말 것. -- **modest·wholesome (10대), chibi/toddler·노출 금지.** -- **좌우**: `_r` = 캐릭터 오른쪽(**화면 왼쪽**), `_l` = 캐릭터 왼쪽(**화면 오른쪽**). -- **저장**: `03_Assets/Parts/Images/`. - ---- - -## 마스터 프롬프트 -## haruka_part_master_apose.png -``` -Draw the SAME girl 하루카 (from the attached reference sheet) as ONE clean full-body FRONT NEUTRAL A-POSE on a -520x900 PORTRAIT canvas, designed to be sliced into rig parts. CUTE Japanese-anime TEEN (about 15-16), slim -youthful proportions about 6.5-7 heads (NOT chibi, NOT toddler), LARGE round sparkling eyes with big -highlights, small nose/mouth; coral-brown TWIN-TAILS with soft see-through bangs. Outfit: sailor-style casual — -white blouse with a sakura-pink sailor collar + ribbon, a light pastel pleated skirt (modest length), plain -socks and low sneakers. Modest and wholesome. POSE FOR SLICING: standing straight, front view, BOTH ARMS held -clearly AWAY from the torso (a wide A-pose) so the arms do NOT overlap the body; elbows straight; palms facing -forward, fingers slightly spread; legs straight and APART; EVERY joint (shoulders, elbows, wrists, hips, knees, -ankles, neck) clearly visible. Flat even lighting, clean anime linework matching the sheet. FULLY TRANSPARENT -background, 32-bit RGBA, background alpha = 0 — no white, no shadow, no rim light. Clean anti-aliased edges, no -white halo/fringe. No text. Avoid: white/opaque background, arms touching the torso, legs touching, bent/crossed -limbs, dynamic pose, extra fingers, deformed hands, chibi, toddler, revealing outfit. -``` - ---- - -## 슬라이스 컷 & 16 파일 (각각 520×900 풀캔버스, 제자리) -- **컷 라인(관절)**: 목(위·아래) · 어깨 · 팔꿈치 · 손목 · 허리 · 골반(양 고관절) · 무릎 · 발목. - -| 파일 | 범위 | 근위단(오버랩) | -|---|---|---| -| `haruka_part_head.png` | 두개골·얼굴·귀·코랄브라운 트윈테일·시스루뱅 (+목 살짝) | 목(가슴 밑) | -| `haruka_part_neck.png` | 턱~쇄골 목기둥 | 양끝 | -| `haruka_part_chest.png` | 어깨~허리 (세일러 블라우스+칼라+리본) | 허리·양 어깨 | -| `haruka_part_pelvis.png` | 허리~허벅지 상단 (**플리츠 스커트 포함**) | 허리(가슴 밑) | -| `haruka_part_upperarm_r/l.png` | 어깨~팔꿈치 (블라우스 소매) | 어깨(가슴 밑) | -| `haruka_part_forearm_r/l.png` | 팔꿈치~손목 | 팔꿈치 | -| `haruka_part_hand_r/l.png` | 손목~손끝 (펼친 손) | 손목 | -| `haruka_part_thigh_r/l.png` | 스커트 밑단~무릎 (드러난 다리) | 고관절(골반/스커트 밑) | -| `haruka_part_shin_r/l.png` | 무릎~발목 (양말) | 무릎 | -| `haruka_part_foot_r/l.png` | 발목~발끝 (스니커즈) | 발목 | - -> **스커트 주의**: 플리츠 스커트는 `pelvis` 파츠에 포함(엉덩이에 붙임). `thigh`는 **스커트 밑단 아래로 드러난 허벅지**만. (스커트 자체 애니메이션은 후속 옵션.) - -## 검수 (제출 전) -1. 마스터 + 16파츠 = **17장**, 파일명 정확. -2. 전부 **520×900, 32-bit RGBA, 배경 alpha=0.** -3. **16장을 (0,0)에 겹치면 마스터 복원**(제자리 배치 확인). -4. 가장자리 흰 후광 없음, 근위단 오버랩 있음. - ---- - -## (선택 · 후속) 대체 손 attachment -- 하트·주먹·가리킴 등 마스터에 없는 손 모양은 범위 밖. 필요 시 별도 개별 생성. - - - diff --git a/Haruka_Live2D/작업_진행상황_2026-07-03.md b/Haruka_Live2D/작업_진행상황_2026-07-03.md deleted file mode 100644 index db3d2a8..0000000 --- a/Haruka_Live2D/작업_진행상황_2026-07-03.md +++ /dev/null @@ -1,70 +0,0 @@ -# 작업 진행상황 - 2026-07-03 - -작성 시각: 2026-07-03 15:48:47 +09:00 -사용자 지정 중단 시각: 2026-07-03 17:40:00 +09:00 - -## 요청 - -`이미지작업_의뢰서.md` 기준으로 이소리 Live2D 제작용 이미지를 모두 제작한다. 사용자가 언급한 파일명은 `이미지제작_의뢰서.md`였지만, 실제 repo에는 `이미지작업_의뢰서.md`가 존재하여 이 파일을 기준으로 진행했다. - -## 완료된 작업 - -1. `이미지작업_의뢰서.md`, `03_Assets/Live2D/Layer_Manifest.md`, `03_Assets/Live2D/layer_manifest.json` 확인. -2. 입력 이미지 확인: - - `03_Assets/Reference/haruka_sheet.png` - - `03_Assets/Parts/Images/haruka_part_master_apose.png` - - `03_Assets/Parts/Images/*.png` -3. manifest 기준 PNG 레이어 번들 생성: - - 위치: `03_Assets/Live2D/LayerPNGs/` - - PNG 수: 78개 - - 캔버스: 1600x2800 - - 모드: RGBA - - 필수 레이어: 67/67 non-empty - - 누락 파일: 없음 -4. 프리뷰와 리포트 생성: - - `03_Assets/Live2D/haruka_live2d_layer_preview.png` - - `03_Assets/Live2D/haruka_live2d_layer_preview_checker.png` - - `03_Assets/Live2D/haruka_live2d_swap_parts_preview_checker.png` - - `03_Assets/Live2D/layer_generation_report.json` - - `03_Assets/Live2D/LayerPNGs_README.md` -5. Photoshop PSD 조립 보조 파일 생성: - - `03_Assets/Live2D/photoshop_assemble_live2d_psd.jsx` - - `03_Assets/Live2D/PSD_ASSEMBLY_GUIDE.md` -6. 생성/보조 스크립트 추가: - - `tools/generate_live2d_layers.py` - - `tools/write_photoshop_assembler.py` - - `tools/make_parts_contact_sheet.py` - -## 검수 결과 - -- `layer_generation_report.json` 기준: - - total layers: 78 - - required layers: 67 - - non-empty required layers: 67 - - missing files: 0 -- 전체 `LayerPNGs/**/*.png` 검사 결과: - - 78개 모두 1600x2800 - - 78개 모두 RGBA - -## PSD 상태 - -현재 환경에는 layered PSD를 직접 저장할 수 있는 `psd_tools`, ImageMagick `magick`, Krita가 없다. 잘못된 평면 PSD를 목표 파일명으로 만들지 않기 위해 `haruka_live2d_material_separation.psd`와 `haruka_live2d_import.psd`는 직접 생성하지 않았다. - -대신 `photoshop_assemble_live2d_psd.jsx`를 생성했다. Photoshop에서 이 JSX를 실행하고 프로젝트 루트 `Haruka_Live2D` 폴더를 선택하면 다음 파일을 저장하도록 구성되어 있다. - -- `03_Assets/Live2D/haruka_live2d_material_separation.psd` -- `03_Assets/Live2D/haruka_live2d_import.psd` - -## 다음 세션에서 이어갈 일 - -1. 필요하면 `03_Assets/Live2D/haruka_live2d_layer_preview_checker.png`를 보고 얼굴, 눈, 입, 머리카락 경계를 추가 보정한다. -2. Photoshop 사용 가능 환경에서 `03_Assets/Live2D/photoshop_assemble_live2d_psd.jsx`를 실행해 PSD 2종을 조립한다. -3. Cubism Editor에 `haruka_live2d_import.psd`를 import하고 레이어명/ArtMesh 생성 상태를 확인한다. -4. 수작업 품질 보정이 필요하면 `tools/generate_live2d_layers.py`의 마스크 좌표 또는 생성된 PNG를 직접 수정한다. - -## 참고 - -현재 PNG 번들은 기존 A-pose 파츠를 기반으로 자동 분리한 1차 제작물이다. Cubism rigging 전에 Photoshop 또는 Clip Studio에서 눈/입/머리카락의 세부 경계와 숨은 밑그림을 보정하는 것이 좋다. - - - diff --git a/Haruka_Profile/10_ImageProduction/00_Common/CHARACTER_IDENTITY.md b/Haruka_Profile/10_ImageProduction/00_Common/CHARACTER_IDENTITY.md new file mode 100644 index 0000000..d36f269 --- /dev/null +++ b/Haruka_Profile/10_ImageProduction/00_Common/CHARACTER_IDENTITY.md @@ -0,0 +1,26 @@ +# Haruka Character Identity + +Haruka는 귀엽고 밝은 아이돌형 캐릭터다. 낮에는 사랑스러운 아이돌/세일러 감성을 중심으로, 밤이나 이벤트에서는 귀여운 할로윈 분장 마녀 컨셉을 유지한다. 표정은 밝고 친근하며, 동작은 작고 탄력 있게 시작해 무대형 제스처로 확장된다. + +## 공통 유지 요소 + +- 모든 컨셉에서 같은 인물로 보여야 한다. +- 얼굴형, 눈매, 기본 체형, 표정 방향성은 컨셉에 따라 바뀌지 않는다. +- 컨셉 의상과 분위기는 달라져도 캐릭터의 핵심 성격은 유지한다. +- 앱 우측 패널에서는 무릎 위부터 머리끝까지 크게 보이는 상체 중심 프레이밍을 우선한다. +- 제스처는 사용자의 명령을 기다리거나 반응하는 상황에 자연스럽게 맞아야 한다. + +## 컨셉별 정체성 + +- **Idol**: 밝고 귀여운 아이돌 무대 컨셉. 낮/공연/응원 반응에 어울리는 활기와 사랑스러움을 유지한다. +- **Sailor**: 귀여운 세일러 캐주얼 컨셉. 학생풍, 밝은 인사, 장난스러운 반응에 적합하다. +- **Witch**: 귀여운 할로윈 분장 마녀 컨셉. 밤/이벤트/장난스러운 마법소녀 분위기를 유지한다. + + +## 금지 사항 + +- 컨셉 변경 때문에 다른 캐릭터처럼 보이는 얼굴 변화 +- 기존 컨셉을 삭제하거나 다른 캐릭터 컨셉으로 덮어쓰기 +- 정체성보다 의상 요소만 강조하는 이미지 +- 앱 대기 상황과 맞지 않는 과도한 팔 벌림 기본 포즈 + diff --git a/Haruka_Profile/10_ImageProduction/00_Common/DANCE_REFERENCE_SPEC.md b/Haruka_Profile/10_ImageProduction/00_Common/DANCE_REFERENCE_SPEC.md new file mode 100644 index 0000000..8e24542 --- /dev/null +++ b/Haruka_Profile/10_ImageProduction/00_Common/DANCE_REFERENCE_SPEC.md @@ -0,0 +1,79 @@ +# Dance Reference Specification + +Haruka 댄스 이미지는 Solo Dance 3를 주 기준으로 한다. 목표는 실제 영상처럼 팔, 손, 손가락, 상체, 하체, 머리카락이 서로 다른 타이밍으로 자연스럽게 이어지는 Live2D 제작 자료를 만드는 것이다. + +## 필수 모션 요소 + +- root sway: 몸 중심 좌우 이동 +- root bounce: 리듬에 맞는 상하 움직임 +- pelvis counter: 골반이 흉곽과 반대로 움직임 +- chest lead/follow: 가슴/상체가 리듬을 따라 늦게 반응 +- shoulder lift: 팔이 올라갈 때 어깨가 같이 올라감 +- elbow bend: 팔꿈치가 단계적으로 굽혀짐 +- wrist roll: 손목이 카메라 방향과 측면 방향을 오감 +- palm direction: 손바닥, 손등, 손날 방향 변화 +- finger curl: 손가락이 펴짐/굽힘/강조 제스처로 변함 +- head follow: 머리가 상체보다 약간 늦게 따라옴 +- hair follow-through: 머리카락이 머리보다 늦게 흔들림 +- torso bend: 상체가 앞/좌/우로 굽혀짐 +- lower body rhythm: 무릎과 골반이 미세하게 리듬을 만듦 + +## Solo Dance 3 키포즈 그룹 + +최소 24개, 권장 32개 키포즈를 제작한다. + +1. ready smile +2. relaxed downbeat +3. hand forward prep +4. palm push front +5. wrist roll front +6. wrist roll side +7. shoulder bounce +8. one arm lift half +9. one arm lift high +10. both arms lift prep +11. both arms high +12. hair touch +13. chest bounce down +14. chest bounce up +15. side turn left prep +16. side turn left +17. back/three-quarter hair swing +18. return from turn +19. hand near face +20. finger accent +21. hip left +22. hip right +23. settle wave +24. loop return + +추가 권장: + +25. open palm close-up +26. wrist crossed high +27. elbow tucked +28. arm behind torso +29. arm in front of torso +30. laugh bounce +31. hair follow-through extreme +32. final idle groove + +## 루프 분해 + +- Loop A: idle groove, 2.5~3.0s +- Loop B: hand forward / palm push, 2.0~2.5s +- Loop C: wrist roll, 1.5~2.0s +- Loop D: arms up, 2.5~3.5s +- Loop E: side turn / hair swing, 2.5~4.0s +- Loop F: return settle, 2.0~3.0s + +## 산출물 + +- key pose PNG set +- key pose contact sheet +- motion curve draft json +- z-order event draft json +- hand/finger close-up sheet +- torso corrective sheet +- hair sway sheet + diff --git a/Haruka_Profile/10_ImageProduction/00_Common/OUTPUT_SPEC.md b/Haruka_Profile/10_ImageProduction/00_Common/OUTPUT_SPEC.md new file mode 100644 index 0000000..0b91606 Binary files /dev/null and b/Haruka_Profile/10_ImageProduction/00_Common/OUTPUT_SPEC.md differ diff --git a/Haruka_Profile/10_ImageProduction/00_Common/POSE_AND_GESTURE_SET.md b/Haruka_Profile/10_ImageProduction/00_Common/POSE_AND_GESTURE_SET.md new file mode 100644 index 0000000..19e67bb --- /dev/null +++ b/Haruka_Profile/10_ImageProduction/00_Common/POSE_AND_GESTURE_SET.md @@ -0,0 +1,102 @@ +# Pose and Gesture Set + +모든 컨셉은 같은 제스처 이름을 사용한다. 컨셉별 의상과 분위기는 다르지만 제스처 파일명과 의미는 통일한다. + +## 기본 포즈 + +| ID | 설명 | +|---|---| +| idle_full | 전신 기본 대기 | +| idle_upper | 앱 우측 패널용 상체 대기 | +| neutral_apose | Live2D 파츠 제작용 A-pose | +| neutral_tpose_soft | 팔 분리 확인용 부드러운 T/A 중간 포즈 | +| app_wait | 사용자 명령 대기, 차분한 손 위치 | +| headset_touch | 헤드셋에 손을 올린 대기/청취 포즈 | +| armscross | 팔짱 대기 포즈 | + +## 앱 제스처 + +| ID | 설명 | +|---|---| +| listen | 사용자 입력을 듣는 상태 | +| thinking | 처리 중, 턱/손/시선 중심 | +| present | 정보를 보여주는 손동작 | +| point | 특정 위치를 가리키는 손동작 | +| control | EQ/슬라이더를 조작하는 손동작 | +| handwave | 가벼운 손 흔들기 | +| wave | 큰 인사 손동작 | +| peace | 브이 포즈 | +| thumbsup | 긍정/완료 포즈 | +| clap | 박수 포즈 | +| cheer | 양손 응원 포즈 | +| joy | 기쁜 반응 | +| heart | 하트 제스처 | +| shrug | 모름/중립 반응 | +| piano | 피아노/키보드 연주 제스처 | +| dj | DJ/믹서 조작 제스처 | +| error_soft | 부드러운 오류/미안함 표현 | +| muted | 조용한 상태, 손가락/입 주변 제스처 | + +## 표정 세트 + +- neutral +- smile +- positive +- proud +- laugh +- talk +- talk_wide +- blink +- wink +- thinking +- confused +- surprised +- negative +- sad +- shy +- sleepy +- pout +- love +- cool +- playful + +## 손 세트 + +각 손은 좌우 모두 필요하다. + +- relaxed +- open_palm_front +- open_palm_side +- backhand +- soft_fist +- point_index +- thumbsup +- peace +- heart_half +- piano_curve +- dj_touch +- headset_touch +- clap_contact +- wrist_roll_A +- wrist_roll_B + +## Live2D용 파츠 확인 포즈 + +- arm_down_relaxed +- arm_forward +- arm_side +- arm_up_half +- arm_up_full +- elbow_bent_front +- elbow_bent_side +- hand_near_face +- hand_over_chest +- hand_behind_body +- torso_left_bend +- torso_right_bend +- torso_forward_bend +- pelvis_left +- pelvis_right +- side_turn_left +- side_turn_right + diff --git a/Haruka_Profile/10_ImageProduction/01_Concepts/Idol.md b/Haruka_Profile/10_ImageProduction/01_Concepts/Idol.md new file mode 100644 index 0000000..1bd2c31 Binary files /dev/null and b/Haruka_Profile/10_ImageProduction/01_Concepts/Idol.md differ diff --git a/Haruka_Profile/10_ImageProduction/01_Concepts/Sailor.md b/Haruka_Profile/10_ImageProduction/01_Concepts/Sailor.md new file mode 100644 index 0000000..d593c6d Binary files /dev/null and b/Haruka_Profile/10_ImageProduction/01_Concepts/Sailor.md differ diff --git a/Haruka_Profile/10_ImageProduction/01_Concepts/Witch.md b/Haruka_Profile/10_ImageProduction/01_Concepts/Witch.md new file mode 100644 index 0000000..2acff50 Binary files /dev/null and b/Haruka_Profile/10_ImageProduction/01_Concepts/Witch.md differ diff --git a/Haruka_Profile/10_ImageProduction/02_Gestures/DANCE_IMAGE_SPEC.md b/Haruka_Profile/10_ImageProduction/02_Gestures/DANCE_IMAGE_SPEC.md new file mode 100644 index 0000000..ae9fad0 --- /dev/null +++ b/Haruka_Profile/10_ImageProduction/02_Gestures/DANCE_IMAGE_SPEC.md @@ -0,0 +1,94 @@ +# Dance Image Specification + +Solo Dance 3를 모든 컨셉 의상으로 재구성한다. 결과물은 정지 이미지 감상이 아니라 Live2D 리그와 모션 제작을 위한 단계별 자료여야 한다. + +## 필수 키포즈 + +각 컨셉마다 24~32개 키포즈 PNG를 제작한다. + +파일명: + +`haruka__dance_solo3_key_###_