@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.
Files changed (2) hide show
  1. package/README.md +44 -44
  2. 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 ../sdk
25
+ pnpm add @ssafy-mhk/e-ver
24
26
 
25
27
  # ๋˜๋Š” ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €์— ๋”ฐ๋ผ
26
- npm install ../sdk
27
- yarn add file:../sdk
28
+ npm install @ssafy-mhk/e-ver
29
+ yarn add @ssafy-mhk/e-ver
28
30
  ```
29
31
 
30
- *(์˜์กด์„ฑ ์„ค์น˜๊ฐ€ ์ž˜ ๋˜์—ˆ๋Š”์ง€ ๊ผญ `package.json` ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.)*
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** | `useEverStore` | ์›น์บ  ํ™œ์„ฑํ™” ์—ฌ๋ถ€, ๋ Œ๋”๋ง ์ค‘์ธ ์˜ท/์•„๋ฐ”ํƒ€ ์ฃผ์†Œ, ์ตœ์ข… ํ• ๊ฒฐ๊ณผ(์ฒดํ˜• ์ธก์ •) ๊ด€๋ฆฌ |
41
- | **Renderer** | `EverCanvas` | AR/3D ํ™˜๊ฒฝ์— ์ตœ์ ํ™”๋œ ์กฐ๋ช…๊ณผ ์นด๋ฉ”๋ผ ์„ธํŒ…์„ ๋‚ด์žฅํ•œ R3F ์บ”๋ฒ„์Šค ๋ž˜ํผ |
42
- | **Renderer** | `AvatarModel` | ์‚ฌ์šฉ์ž์˜ AI ๊ธฐ๋ฐ˜ ์ƒ์„ฑ 3D ์•„๋ฐ”ํƒ€(GLB)๋ฅผ ๋กœ๋”ฉํ•˜๊ณ , ๋ผˆ๋Œ€(Bone) ๊ตฌ์กฐ๋ฅผ ์ถ”์ถœ |
43
- | **Renderer** | `GarmentModel` | ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ์˜ท(GLB)์„ ๋กœ๋”ฉํ•˜๊ณ , ์˜ท์˜ SkinnedMesh๋ฅผ ๋Œ€์ƒ ์•„๋ฐ”ํƒ€์— ์ž…ํž˜ |
44
- | **Tracker** | `usePoseTracker` | ๋ธŒ๋ผ์šฐ์ € ๋‚ด์—์„œ MediaPipe๋ฅผ ๊ฐ€๋™์‹œ์ผœ ์‹ค์‹œ๊ฐ„ 30-60Hz ์›น์บ  ์ถ”์  |
45
- | **Tracker** | `usePoseMapper` | ์›น์บ ์—์„œ ์–ป์€ 2D/3D ์ (Landmarks)์„ ํ•„์ˆ˜ 13๋ณธ(Bone)์˜ ํšŒ์ „๊ฐ’(Quaternion)์œผ๋กœ ๋ณ€ํ™˜ |
46
- | **Tracker** | `useBodyMeasurer` | AR ์นด๋ฉ”๋ผ ์ƒ์—์„œ ์‚ฌ์šฉ์ž ์‹ค๋ฃจ์—ฃ ๊ธฐ๋ฐ˜ ์‚ฌ์ด์ฆˆ(์–ด๊นจ, ๊ฐ€์Šด, ํ—ˆ๋ฆฌ)๋ฅผ ์—ฐ์‚ฐํ•˜์—ฌ ์˜ท์˜ ํ• ํ‰๊ฐ€ |
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('/path/to/clothes.glb')}>
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
- <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>}
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
@@ -54,5 +54,5 @@
54
54
  },
55
55
  "type": "module",
56
56
  "types": "./dist/index.d.ts",
57
- "version": "1.0.1"
57
+ "version": "1.0.2"
58
58
  }