@ssafy-mhk/e-ver 1.0.1 โ 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# `@ssafy-mhk/e-ver` - Virtual Fitting React SDK ๐๐
|
|
2
2
|
|
|
3
|
-
S14P21M104 ๊ฐ์ ํผํ
(WebAR) ํ๋ก์ ํธ๋ฅผ ์ํ ๊ณต์ ํ๋ก ํธ์๋ React SDK์
๋๋ค.
|
|
3
|
+
S14P21M104 ๊ฐ์ ํผํ
(WebAR) ํ๋ก์ ํธ๋ฅผ ์ํ ๊ณต์ ํ๋ก ํธ์๋ React SDK์
๋๋ค.
|
|
4
4
|
๋ณต์กํ Three.js ์นด๋ฉ๋ผ ๋ฐ ์กฐ๋ช
์ค์ , MediaPipe AI ๋ฐ๋ ํธ๋ํน ์ฐ๋, SkinnedMesh ๊ธฐ๋ฐ ์๋ฅ ๋ ๋๋ง ๋ก์ง์ ์ถ์ํํ์ฌ, **๋จ์ํ React ์ปดํฌ๋ํธ์ Hook**์ผ๋ก ์ฆ๊ฐํ์ค(AR) ๊ฐ์ ํผํ
๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ๊ตฌํํ ์ ์๋๋ก ์ง์ํฉ๋๋ค.
|
|
5
5
|
|
|
6
6
|
## ๊ฐ๋ฐ ์คํ (Technology Stack)
|
|
7
|
+
|
|
7
8
|
- **Framework**: React / Next.js ํธํ
|
|
8
9
|
- **3D & AR Rendering**: Three.js, React Three Fiber (R3F), Drei, three-vrm
|
|
9
10
|
- **AI Tracking**: MediaPipe Pose Landmarker (Lite)
|
|
@@ -16,18 +17,19 @@ S14P21M104 ๊ฐ์ ํผํ
(WebAR) ํ๋ก์ ํธ๋ฅผ ์ํ ๊ณต์ ํ๋ก ํธ์๋ Re
|
|
|
16
17
|
์ด ํจํค์ง๋ ์ฌ๋ด ๋๋ ์ธ๋ถ NPM์ ์ง์ ๋ฐฐํฌ๋๊ธฐ ์ ๋ก์ปฌ ์ ์ฅ์๋ฅผ ํตํ ์ฌ๋ณผ๋ฆญ ๋งํฌ(Symbolic Link) ๋ฐฉ์์ผ๋ก ์ฐ๊ฒฐํ๊ฑฐ๋ ํ์ผ ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐธ์กฐํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
|
|
17
18
|
|
|
18
19
|
### ๋ก์ปฌ ํ๋ก์ ํธ์์ ์ฐธ์กฐํ์ฌ ์ค์นํ ๊ฒฝ์ฐ:
|
|
20
|
+
|
|
19
21
|
์ฌ๋ฌ๋ถ์ Next.js ํ๋ก์ ํธ ์ต์์ ํด๋(`frontend`)์์ ์๋ ๋ช
๋ น์ด๋ฅผ ์คํํ์ธ์.
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
# pnpm ์ฌ์ฉ ์
|
|
23
|
-
pnpm add
|
|
25
|
+
pnpm add @ssafy-mhk/e-ver
|
|
24
26
|
|
|
25
27
|
# ๋๋ ํจํค์ง ๋งค๋์ ์ ๋ฐ๋ผ
|
|
26
|
-
npm install
|
|
27
|
-
yarn add
|
|
28
|
+
npm install @ssafy-mhk/e-ver
|
|
29
|
+
yarn add @ssafy-mhk/e-ver
|
|
28
30
|
```
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
_(์์กด์ฑ ์ค์น๊ฐ ์ ๋์๋์ง ๊ผญ `package.json` ์ฝ๋๋ฅผ ํ์ธํ์ธ์.)_
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
@@ -35,15 +37,15 @@ yarn add file:../sdk
|
|
|
35
37
|
|
|
36
38
|
SDK๋ ํฌ๊ฒ **State(์ํ ๊ด๋ฆฌ)**, **Renderer(๋ ๋๋ง UI)**, **Tracker(์ถ์ /์ธก์ Hook)** ์ด๋ ๊ฒ 3๊ฐ์ง ํต์ฌ ์ถ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
|
|
37
39
|
|
|
38
|
-
| ๋ถ๋ฅ
|
|
39
|
-
|
|
40
|
-
| **State**
|
|
41
|
-
| **Renderer** | `EverCanvas`
|
|
42
|
-
| **Renderer** | `AvatarModel`
|
|
43
|
-
| **Renderer** | `GarmentModel`
|
|
44
|
-
| **Tracker**
|
|
45
|
-
| **Tracker**
|
|
46
|
-
| **Tracker**
|
|
40
|
+
| ๋ถ๋ฅ | ๋ชจ๋๋ช
| ์ญํ |
|
|
41
|
+
| ------------ | ----------------- | ------------------------------------------------------------------------------------ |
|
|
42
|
+
| **State** | `useEverStore` | ์น์บ ํ์ฑํ ์ฌ๋ถ, ๋ ๋๋ง ์ค์ธ ์ท/์๋ฐํ ์ฃผ์, ์ต์ข
ํ ๊ฒฐ๊ณผ(์ฒดํ ์ธก์ ) ๊ด๋ฆฌ |
|
|
43
|
+
| **Renderer** | `EverCanvas` | AR/3D ํ๊ฒฝ์ ์ต์ ํ๋ ์กฐ๋ช
๊ณผ ์นด๋ฉ๋ผ ์ธํ
์ ๋ด์ฅํ R3F ์บ๋ฒ์ค ๋ํผ |
|
|
44
|
+
| **Renderer** | `AvatarModel` | ์ฌ์ฉ์์ AI ๊ธฐ๋ฐ ์์ฑ 3D ์๋ฐํ(GLB)๋ฅผ ๋ก๋ฉํ๊ณ , ๋ผ๋(Bone) ๊ตฌ์กฐ๋ฅผ ์ถ์ถ |
|
|
45
|
+
| **Renderer** | `GarmentModel` | ์ฌ์ฉ์๊ฐ ์ ํํ ์ท(GLB)์ ๋ก๋ฉํ๊ณ , ์ท์ SkinnedMesh๋ฅผ ๋์ ์๋ฐํ์ ์
ํ |
|
|
46
|
+
| **Tracker** | `usePoseTracker` | ๋ธ๋ผ์ฐ์ ๋ด์์ MediaPipe๋ฅผ ๊ฐ๋์์ผ ์ค์๊ฐ 30-60Hz ์น์บ ์ถ์ |
|
|
47
|
+
| **Tracker** | `usePoseMapper` | ์น์บ ์์ ์ป์ 2D/3D ์ (Landmarks)์ ํ์ 13๋ณธ(Bone)์ ํ์ ๊ฐ(Quaternion)์ผ๋ก ๋ณํ |
|
|
48
|
+
| **Tracker** | `useBodyMeasurer` | AR ์นด๋ฉ๋ผ ์์์ ์ฌ์ฉ์ ์ค๋ฃจ์ฃ ๊ธฐ๋ฐ ์ฌ์ด์ฆ(์ด๊นจ, ๊ฐ์ด, ํ๋ฆฌ)๋ฅผ ์ฐ์ฐํ์ฌ ์ท์ ํ ํ๊ฐ |
|
|
47
49
|
|
|
48
50
|
---
|
|
49
51
|
|
|
@@ -53,7 +55,7 @@ SDK๋ ํฌ๊ฒ **State(์ํ ๊ด๋ฆฌ)**, **Renderer(๋ ๋๋ง UI)**, **Tracker(
|
|
|
53
55
|
|
|
54
56
|
### Step 1. Zustand๋ก ๊ธ๋ก๋ฒ ์ํ ์ธํ
ํ๊ธฐ
|
|
55
57
|
|
|
56
|
-
์ฐ์ , ์ ํ๋ฆฌ์ผ์ด์
์ธ๋ถ ๋ฒํผ ์กฐ์์ ์ํด ์คํ ์ด๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
|
|
58
|
+
์ฐ์ , ์ ํ๋ฆฌ์ผ์ด์
์ธ๋ถ ๋ฒํผ ์กฐ์์ ์ํด ์คํ ์ด๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
|
|
57
59
|
|
|
58
60
|
```tsx
|
|
59
61
|
// app/fitting/page.tsx
|
|
@@ -67,7 +69,7 @@ export default function FittingControls() {
|
|
|
67
69
|
return (
|
|
68
70
|
<div className="absolute top-4 left-4 z-10 flex flex-col gap-2">
|
|
69
71
|
<button onClick={() => setWebcamActive(true)}>์น์บ ๊ฐ๋ ์์ ๐ฅ</button>
|
|
70
|
-
<button onClick={() => setActiveGarmentUrl(
|
|
72
|
+
<button onClick={() => setActiveGarmentUrl("/path/to/clothes.glb")}>
|
|
71
73
|
์๋ก์ด ์ท ์
์ด๋ณด๊ธฐ ๐
|
|
72
74
|
</button>
|
|
73
75
|
|
|
@@ -83,46 +85,44 @@ export default function FittingControls() {
|
|
|
83
85
|
|
|
84
86
|
### Step 2. 3D AR ์บ๋ฒ์ค ๋ฐ ๋ชจ๋ธ ๊ตฌ์ถํ๊ธฐ
|
|
85
87
|
|
|
86
|
-
SDK์์ ์ ๊ณตํ๋ `EverCanvas`, `AvatarModel`, `GarmentModel`์ ํ์ฉํ์ฌ ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค.
|
|
88
|
+
SDK์์ ์ ๊ณตํ๋ `EverCanvas`, `AvatarModel`, `GarmentModel`์ ํ์ฉํ์ฌ ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค.
|
|
87
89
|
|
|
88
90
|
```tsx
|
|
89
91
|
// components/VirtualFittingScene.tsx
|
|
90
92
|
"use client";
|
|
91
93
|
|
|
92
94
|
import { useState } from "react";
|
|
93
|
-
import {
|
|
94
|
-
EverCanvas,
|
|
95
|
-
AvatarModel,
|
|
96
|
-
GarmentModel,
|
|
97
|
-
useEverStore
|
|
95
|
+
import {
|
|
96
|
+
EverCanvas,
|
|
97
|
+
AvatarModel,
|
|
98
|
+
GarmentModel,
|
|
99
|
+
useEverStore,
|
|
98
100
|
} from "@ssafy-mhk/e-ver";
|
|
99
101
|
|
|
100
102
|
export default function VirtualFittingScene() {
|
|
101
103
|
const [avatarBones, setAvatarBones] = useState<Record<string, THREE.Bone>>();
|
|
102
104
|
const { activeGarmentUrl } = useEverStore();
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
// ๋ด ๊ณ์ ์ 3D ์๋ฐํ ํ์ผ ๊ฒฝ๋ก
|
|
105
|
-
const AVATAR_URL = "/models/my-avatar.glb";
|
|
107
|
+
const AVATAR_URL = "/models/my-avatar.glb";
|
|
106
108
|
|
|
107
109
|
return (
|
|
108
110
|
<div className="w-full h-screen bg-transparent relative">
|
|
109
111
|
{/* 1. ๊ธฐ๋ณธ AR ์กฐ๋ช
์ด ์ธํ
๋ ์บ๋ฒ์ค ํธ์ถ */}
|
|
110
112
|
<EverCanvas showEnvironment={true}>
|
|
111
|
-
|
|
112
113
|
{/* 2. ์ฌ์ฉ์์ ๋ฒ ์ด์ค ์๋ฐํ ๋ถ๋ฌ์ค๊ธฐ */}
|
|
113
|
-
<AvatarModel
|
|
114
|
-
url={AVATAR_URL}
|
|
115
|
-
onBonesReady={(bones) => setAvatarBones(bones)}
|
|
114
|
+
<AvatarModel
|
|
115
|
+
url={AVATAR_URL}
|
|
116
|
+
onBonesReady={(bones) => setAvatarBones(bones)}
|
|
116
117
|
/>
|
|
117
|
-
|
|
118
|
+
|
|
118
119
|
{/* 3. ํ์ฑํ๋ ์ท์ด ์๋ค๋ฉด ์๋ฐํ ์์ ๋ง์
ํ๊ธฐ */}
|
|
119
120
|
{activeGarmentUrl && avatarBones && (
|
|
120
|
-
<GarmentModel
|
|
121
|
-
url={activeGarmentUrl}
|
|
122
|
-
targetAvatarBones={avatarBones}
|
|
121
|
+
<GarmentModel
|
|
122
|
+
url={activeGarmentUrl}
|
|
123
|
+
targetAvatarBones={avatarBones}
|
|
123
124
|
/>
|
|
124
125
|
)}
|
|
125
|
-
|
|
126
126
|
</EverCanvas>
|
|
127
127
|
</div>
|
|
128
128
|
);
|
|
@@ -142,23 +142,23 @@ import { usePoseTracker, useEverStore } from "@ssafy-mhk/e-ver";
|
|
|
142
142
|
|
|
143
143
|
export default function ArCameraView() {
|
|
144
144
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
// SDK ๋ด๋ถ์ ๋ก์ง์ ํตํด ์ค์๊ฐ์ผ๋ก ํ๋ ์์ MediaPipe์ ์ ์ก
|
|
147
147
|
const { isTrackerReady } = usePoseTracker(videoRef);
|
|
148
148
|
const { isWebcamActive } = useEverStore();
|
|
149
149
|
|
|
150
150
|
return (
|
|
151
151
|
<div className="absolute inset-0 z-0">
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
<video
|
|
153
|
+
ref={videoRef}
|
|
154
|
+
autoPlay
|
|
155
|
+
playsInline
|
|
156
|
+
muted
|
|
157
|
+
className="w-full h-full object-cover"
|
|
158
|
+
style={{ display: isWebcamActive ? "block" : "none" }}
|
|
159
|
+
/>
|
|
160
|
+
{/* Tracker๊ฐ ๋ก๋ฉ ์ค์ผ ๋ ๋ก๋ฉ ์ค๋ฒ๋ ์ด ํ์ ๊ฐ๋ฅ */}
|
|
161
|
+
{!isTrackerReady && isWebcamActive && <p>AI ๋ชจ๋ธ ๋ก๋ฉ ์ค...</p>}
|
|
162
162
|
</div>
|
|
163
163
|
);
|
|
164
164
|
}
|
package/package.json
CHANGED