@ssafy-mhk/e-ver 1.0.1 โ 1.0.3
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 +61 -47
- package/dist/api/EverClient.test.d.ts +1 -0
- package/dist/components/EverProvider.d.ts +0 -8
- package/dist/components/ever-context.d.ts +9 -0
- package/dist/hooks/useBodyMeasurer.test.d.ts +1 -0
- package/dist/hooks/useEverGeneration.test.d.ts +1 -0
- package/dist/hooks/usePoseMapper.d.ts +7 -1
- package/dist/hooks/usePoseTracker.test.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +9 -5
- package/dist/index.umd.js +3 -3
- package/dist/store/useEverStore.d.ts +10 -3
- package/package.json +4 -2
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)
|
|
@@ -13,21 +14,36 @@ S14P21M104 ๊ฐ์ ํผํ
(WebAR) ํ๋ก์ ํธ๋ฅผ ์ํ ๊ณต์ ํ๋ก ํธ์๋ Re
|
|
|
13
14
|
|
|
14
15
|
## ๐ ์ค์น ๋ฐฉ๋ฒ (Installation)
|
|
15
16
|
|
|
16
|
-
์ด ํจํค์ง๋
|
|
17
|
+
์ด ํจํค์ง๋ ๋ฐฐํฌ ์ ๋ก์ปฌ ๊ฒ์ฆ ์ tarball๋ก ํจํค์งํ ๋ค ์๋น ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์นํด์ ํ์ธํ๋ ๋ฐฉ์์ ๊ถ์ฅํฉ๋๋ค.
|
|
18
|
+
|
|
19
|
+
### ๋ฐฐํฌ๋ ํจํค์ง ์ค์น
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
์ฌ๋ฌ๋ถ์ Next.js ํ๋ก์ ํธ ์ต์์ ํด๋(`frontend`)์์ ์๋ ๋ช
๋ น์ด๋ฅผ ์คํํ์ธ์.
|
|
21
|
+
์ฌ๋ฌ๋ถ์ Next.js ํ๋ก์ ํธ ์ต์์ ํด๋์์ ์๋ ๋ช
๋ น์ด๋ฅผ ์คํํ์ธ์.
|
|
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
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
_(์์กด์ฑ ์ค์น๊ฐ ์ ๋์๋์ง ๊ผญ `package.json` ์ฝ๋๋ฅผ ํ์ธํ์ธ์.)_
|
|
33
|
+
|
|
34
|
+
### ์ด ๋ ํฌ์์ `frontend`๋ก ๋ก์ปฌ ๊ฒ์ฆํ ๊ฒฝ์ฐ
|
|
35
|
+
|
|
36
|
+
`/ever/frontend`์์ ์๋ ๋ช
๋ น์ด๋ฅผ ์คํํ๋ฉด `/ever/sdk`๋ฅผ ๋น๋ํ๊ณ tarball๋ก ํจํค์งํ ๋ค ํ์ฌ ํ๋ก ํธ์๋์ ์ค์นํฉ๋๋ค.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm run sdk:use-tarball
|
|
28
40
|
```
|
|
29
41
|
|
|
30
|
-
|
|
42
|
+
๊ฒ์ฆ ํ ํผ๋ธ๋ฆฌ์๋ ๋ฒ์ ์ผ๋ก ๋๋๋ฆด ๋๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm run sdk:use-version -- 1.0.3
|
|
46
|
+
```
|
|
31
47
|
|
|
32
48
|
---
|
|
33
49
|
|
|
@@ -35,15 +51,15 @@ yarn add file:../sdk
|
|
|
35
51
|
|
|
36
52
|
SDK๋ ํฌ๊ฒ **State(์ํ ๊ด๋ฆฌ)**, **Renderer(๋ ๋๋ง UI)**, **Tracker(์ถ์ /์ธก์ Hook)** ์ด๋ ๊ฒ 3๊ฐ์ง ํต์ฌ ์ถ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
|
|
37
53
|
|
|
38
|
-
| ๋ถ๋ฅ
|
|
39
|
-
|
|
40
|
-
| **State**
|
|
41
|
-
| **Renderer** | `EverCanvas`
|
|
42
|
-
| **Renderer** | `AvatarModel`
|
|
43
|
-
| **Renderer** | `GarmentModel`
|
|
44
|
-
| **Tracker**
|
|
45
|
-
| **Tracker**
|
|
46
|
-
| **Tracker**
|
|
54
|
+
| ๋ถ๋ฅ | ๋ชจ๋๋ช
| ์ญํ |
|
|
55
|
+
| ------------ | ----------------- | ------------------------------------------------------------------------------------ |
|
|
56
|
+
| **State** | `useEverStore` | ์น์บ ํ์ฑํ ์ฌ๋ถ, ๋ ๋๋ง ์ค์ธ ์ท/์๋ฐํ ์ฃผ์, ์ต์ข
ํ ๊ฒฐ๊ณผ(์ฒดํ ์ธก์ ) ๊ด๋ฆฌ |
|
|
57
|
+
| **Renderer** | `EverCanvas` | AR/3D ํ๊ฒฝ์ ์ต์ ํ๋ ์กฐ๋ช
๊ณผ ์นด๋ฉ๋ผ ์ธํ
์ ๋ด์ฅํ R3F ์บ๋ฒ์ค ๋ํผ |
|
|
58
|
+
| **Renderer** | `AvatarModel` | ์ฌ์ฉ์์ AI ๊ธฐ๋ฐ ์์ฑ 3D ์๋ฐํ(GLB)๋ฅผ ๋ก๋ฉํ๊ณ , ๋ผ๋(Bone) ๊ตฌ์กฐ๋ฅผ ์ถ์ถ |
|
|
59
|
+
| **Renderer** | `GarmentModel` | ์ฌ์ฉ์๊ฐ ์ ํํ ์ท(GLB)์ ๋ก๋ฉํ๊ณ , ์ท์ SkinnedMesh๋ฅผ ๋์ ์๋ฐํ์ ์
ํ |
|
|
60
|
+
| **Tracker** | `usePoseTracker` | ๋ธ๋ผ์ฐ์ ๋ด์์ MediaPipe๋ฅผ ๊ฐ๋์์ผ ์ค์๊ฐ 30-60Hz ์น์บ ์ถ์ |
|
|
61
|
+
| **Tracker** | `usePoseMapper` | ์น์บ ์์ ์ป์ 2D/3D ์ (Landmarks)์ ํ์ 13๋ณธ(Bone)์ ํ์ ๊ฐ(Quaternion)์ผ๋ก ๋ณํ |
|
|
62
|
+
| **Tracker** | `useBodyMeasurer` | AR ์นด๋ฉ๋ผ ์์์ ์ฌ์ฉ์ ์ค๋ฃจ์ฃ ๊ธฐ๋ฐ ์ฌ์ด์ฆ(์ด๊นจ, ๊ฐ์ด, ํ๋ฆฌ)๋ฅผ ์ฐ์ฐํ์ฌ ์ท์ ํ ํ๊ฐ |
|
|
47
63
|
|
|
48
64
|
---
|
|
49
65
|
|
|
@@ -53,7 +69,7 @@ SDK๋ ํฌ๊ฒ **State(์ํ ๊ด๋ฆฌ)**, **Renderer(๋ ๋๋ง UI)**, **Tracker(
|
|
|
53
69
|
|
|
54
70
|
### Step 1. Zustand๋ก ๊ธ๋ก๋ฒ ์ํ ์ธํ
ํ๊ธฐ
|
|
55
71
|
|
|
56
|
-
์ฐ์ , ์ ํ๋ฆฌ์ผ์ด์
์ธ๋ถ ๋ฒํผ ์กฐ์์ ์ํด ์คํ ์ด๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
|
|
72
|
+
์ฐ์ , ์ ํ๋ฆฌ์ผ์ด์
์ธ๋ถ ๋ฒํผ ์กฐ์์ ์ํด ์คํ ์ด๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
|
|
57
73
|
|
|
58
74
|
```tsx
|
|
59
75
|
// app/fitting/page.tsx
|
|
@@ -67,7 +83,7 @@ export default function FittingControls() {
|
|
|
67
83
|
return (
|
|
68
84
|
<div className="absolute top-4 left-4 z-10 flex flex-col gap-2">
|
|
69
85
|
<button onClick={() => setWebcamActive(true)}>์น์บ ๊ฐ๋ ์์ ๐ฅ</button>
|
|
70
|
-
<button onClick={() => setActiveGarmentUrl(
|
|
86
|
+
<button onClick={() => setActiveGarmentUrl("/path/to/clothes.glb")}>
|
|
71
87
|
์๋ก์ด ์ท ์
์ด๋ณด๊ธฐ ๐
|
|
72
88
|
</button>
|
|
73
89
|
|
|
@@ -83,46 +99,44 @@ export default function FittingControls() {
|
|
|
83
99
|
|
|
84
100
|
### Step 2. 3D AR ์บ๋ฒ์ค ๋ฐ ๋ชจ๋ธ ๊ตฌ์ถํ๊ธฐ
|
|
85
101
|
|
|
86
|
-
SDK์์ ์ ๊ณตํ๋ `EverCanvas`, `AvatarModel`, `GarmentModel`์ ํ์ฉํ์ฌ ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค.
|
|
102
|
+
SDK์์ ์ ๊ณตํ๋ `EverCanvas`, `AvatarModel`, `GarmentModel`์ ํ์ฉํ์ฌ ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค.
|
|
87
103
|
|
|
88
104
|
```tsx
|
|
89
105
|
// components/VirtualFittingScene.tsx
|
|
90
106
|
"use client";
|
|
91
107
|
|
|
92
108
|
import { useState } from "react";
|
|
93
|
-
import {
|
|
94
|
-
EverCanvas,
|
|
95
|
-
AvatarModel,
|
|
96
|
-
GarmentModel,
|
|
97
|
-
useEverStore
|
|
109
|
+
import {
|
|
110
|
+
EverCanvas,
|
|
111
|
+
AvatarModel,
|
|
112
|
+
GarmentModel,
|
|
113
|
+
useEverStore,
|
|
98
114
|
} from "@ssafy-mhk/e-ver";
|
|
99
115
|
|
|
100
116
|
export default function VirtualFittingScene() {
|
|
101
117
|
const [avatarBones, setAvatarBones] = useState<Record<string, THREE.Bone>>();
|
|
102
118
|
const { activeGarmentUrl } = useEverStore();
|
|
103
|
-
|
|
119
|
+
|
|
104
120
|
// ๋ด ๊ณ์ ์ 3D ์๋ฐํ ํ์ผ ๊ฒฝ๋ก
|
|
105
|
-
const AVATAR_URL = "/models/my-avatar.glb";
|
|
121
|
+
const AVATAR_URL = "/models/my-avatar.glb";
|
|
106
122
|
|
|
107
123
|
return (
|
|
108
124
|
<div className="w-full h-screen bg-transparent relative">
|
|
109
125
|
{/* 1. ๊ธฐ๋ณธ AR ์กฐ๋ช
์ด ์ธํ
๋ ์บ๋ฒ์ค ํธ์ถ */}
|
|
110
126
|
<EverCanvas showEnvironment={true}>
|
|
111
|
-
|
|
112
127
|
{/* 2. ์ฌ์ฉ์์ ๋ฒ ์ด์ค ์๋ฐํ ๋ถ๋ฌ์ค๊ธฐ */}
|
|
113
|
-
<AvatarModel
|
|
114
|
-
url={AVATAR_URL}
|
|
115
|
-
onBonesReady={(bones) => setAvatarBones(bones)}
|
|
128
|
+
<AvatarModel
|
|
129
|
+
url={AVATAR_URL}
|
|
130
|
+
onBonesReady={(bones) => setAvatarBones(bones)}
|
|
116
131
|
/>
|
|
117
|
-
|
|
132
|
+
|
|
118
133
|
{/* 3. ํ์ฑํ๋ ์ท์ด ์๋ค๋ฉด ์๋ฐํ ์์ ๋ง์
ํ๊ธฐ */}
|
|
119
134
|
{activeGarmentUrl && avatarBones && (
|
|
120
|
-
<GarmentModel
|
|
121
|
-
url={activeGarmentUrl}
|
|
122
|
-
targetAvatarBones={avatarBones}
|
|
135
|
+
<GarmentModel
|
|
136
|
+
url={activeGarmentUrl}
|
|
137
|
+
targetAvatarBones={avatarBones}
|
|
123
138
|
/>
|
|
124
139
|
)}
|
|
125
|
-
|
|
126
140
|
</EverCanvas>
|
|
127
141
|
</div>
|
|
128
142
|
);
|
|
@@ -142,23 +156,23 @@ import { usePoseTracker, useEverStore } from "@ssafy-mhk/e-ver";
|
|
|
142
156
|
|
|
143
157
|
export default function ArCameraView() {
|
|
144
158
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
145
|
-
|
|
159
|
+
|
|
146
160
|
// SDK ๋ด๋ถ์ ๋ก์ง์ ํตํด ์ค์๊ฐ์ผ๋ก ํ๋ ์์ MediaPipe์ ์ ์ก
|
|
147
161
|
const { isTrackerReady } = usePoseTracker(videoRef);
|
|
148
162
|
const { isWebcamActive } = useEverStore();
|
|
149
163
|
|
|
150
164
|
return (
|
|
151
165
|
<div className="absolute inset-0 z-0">
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
<video
|
|
167
|
+
ref={videoRef}
|
|
168
|
+
autoPlay
|
|
169
|
+
playsInline
|
|
170
|
+
muted
|
|
171
|
+
className="w-full h-full object-cover"
|
|
172
|
+
style={{ display: isWebcamActive ? "block" : "none" }}
|
|
173
|
+
/>
|
|
174
|
+
{/* Tracker๊ฐ ๋ก๋ฉ ์ค์ผ ๋ ๋ก๋ฉ ์ค๋ฒ๋ ์ด ํ์ ๊ฐ๋ฅ */}
|
|
175
|
+
{!isTrackerReady && isWebcamActive && <p>AI ๋ชจ๋ธ ๋ก๋ฉ ์ค...</p>}
|
|
162
176
|
</div>
|
|
163
177
|
);
|
|
164
178
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import * as THREE from "three";
|
|
3
|
-
export interface EverContextType {
|
|
4
|
-
videoRef: React.RefObject<HTMLVideoElement | null>;
|
|
5
|
-
avatarBones: Record<string, THREE.Bone> | null;
|
|
6
|
-
setAvatarBones: (bones: Record<string, THREE.Bone> | null) => void;
|
|
7
|
-
isProviderReady: boolean;
|
|
8
|
-
}
|
|
9
|
-
export declare const useEver: () => EverContextType;
|
|
10
2
|
export interface EverProviderProps {
|
|
11
3
|
children: React.ReactNode;
|
|
12
4
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
export interface EverContextType {
|
|
3
|
+
videoRef: React.RefObject<HTMLVideoElement | null>;
|
|
4
|
+
avatarBones: Record<string, THREE.Bone> | null;
|
|
5
|
+
setAvatarBones: (bones: Record<string, THREE.Bone> | null) => void;
|
|
6
|
+
isProviderReady: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const EverContext: import("react").Context<EverContextType | undefined>;
|
|
9
|
+
export declare function useEver(): EverContextType;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
|
+
interface PoseLandmark {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
z: number;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* Custom hook to map MediaPipe 2D/3D landmarks to 3D bones (Quaternions).
|
|
4
9
|
*/
|
|
5
10
|
export declare const usePoseMapper: () => {
|
|
6
|
-
mapPoseToBones: (landmarks:
|
|
11
|
+
mapPoseToBones: (landmarks: PoseLandmark[], targetBones: Record<string, THREE.Bone>) => void;
|
|
7
12
|
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.es.js
CHANGED
|
@@ -17837,11 +17837,15 @@ var mg = class {
|
|
|
17837
17837
|
let t = this.getContext();
|
|
17838
17838
|
t.drawingBufferColorSpace = $n._getDrawingBufferColorSpace(e), t.unpackColorSpace = $n._getUnpackColorSpace();
|
|
17839
17839
|
}
|
|
17840
|
-
}, hg = i(void 0)
|
|
17840
|
+
}, hg = i(void 0);
|
|
17841
|
+
function gg() {
|
|
17841
17842
|
let e = o(hg);
|
|
17842
17843
|
if (!e) throw Error("useEver must be used within an EverProvider");
|
|
17843
17844
|
return e;
|
|
17844
|
-
}
|
|
17845
|
+
}
|
|
17846
|
+
//#endregion
|
|
17847
|
+
//#region src/components/EverProvider.tsx
|
|
17848
|
+
var _g = ({ children: e }) => {
|
|
17845
17849
|
let t = u(null), [n, r] = d(null), i = {
|
|
17846
17850
|
videoRef: t,
|
|
17847
17851
|
avatarBones: n,
|
|
@@ -35941,7 +35945,7 @@ var Ux = ({ children: e, showEnvironment: t = !0 }) => /* @__PURE__ */ p(_g, { c
|
|
|
35941
35945
|
if (!n) return;
|
|
35942
35946
|
let e = {};
|
|
35943
35947
|
return n.traverse((t) => {
|
|
35944
|
-
t
|
|
35948
|
+
t instanceof oo && (e[t.name] = t);
|
|
35945
35949
|
}), i(e), t && t(e), () => i(null);
|
|
35946
35950
|
}, [
|
|
35947
35951
|
n,
|
|
@@ -35959,7 +35963,7 @@ var Ux = ({ children: e, showEnvironment: t = !0 }) => /* @__PURE__ */ p(_g, { c
|
|
|
35959
35963
|
let { scene: n } = ax(e), r = u(null), { avatarBones: i } = gg(), a = t || i;
|
|
35960
35964
|
return s(() => {
|
|
35961
35965
|
!n || !a || n.traverse((e) => {
|
|
35962
|
-
if (e
|
|
35966
|
+
if (e instanceof ao) {
|
|
35963
35967
|
let t = e, n = [];
|
|
35964
35968
|
t.skeleton.bones.forEach((e) => {
|
|
35965
35969
|
a[e.name] ? n.push(a[e.name]) : n.push(e);
|
|
@@ -40789,4 +40793,4 @@ function iM(e) {
|
|
|
40789
40793
|
};
|
|
40790
40794
|
}
|
|
40791
40795
|
//#endregion
|
|
40792
|
-
export { Gx as AvatarModel, Wx as CameraView, Ux as EverCanvas, tM as EverClient, _g as EverProvider, qx as GarmentModel, Qj as useBodyMeasurer, gg as useEver, iM as useEverGeneration, E as useEverStore, Zj as usePoseMapper, Xj as usePoseTracker };
|
|
40796
|
+
export { Gx as AvatarModel, Wx as CameraView, Ux as EverCanvas, tM as EverClient, hg as EverContext, _g as EverProvider, qx as GarmentModel, Qj as useBodyMeasurer, gg as useEver, iM as useEverGeneration, E as useEverStore, Zj as usePoseMapper, Xj as usePoseTracker };
|