@fonsecabarreto/genesis-gl-react 0.1.0 → 0.1.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.
@@ -0,0 +1,66 @@
1
+ // src/hooks/useModel.ts
2
+ import { useEffect, useRef, useState } from "react";
3
+ function useModel(context, options) {
4
+ const [result, setResult] = useState({
5
+ model: null,
6
+ loading: false,
7
+ error: null
8
+ });
9
+ const optionsRef = useRef(options);
10
+ optionsRef.current = options;
11
+ useEffect(() => {
12
+ if (!context?.scene || !context?.loadOBJ || !context?.webglCore) return;
13
+ const { scene, loadOBJ, webglCore } = context;
14
+ const { name, objUrl, postLoad, ...loadOpts } = optionsRef.current;
15
+ let cancelled = false;
16
+ setResult({ model: null, loading: true, error: null });
17
+ loadOBJ(objUrl, loadOpts).then(async (model) => {
18
+ if (cancelled) return;
19
+ if (postLoad) await postLoad(model, webglCore);
20
+ scene.add(name, model);
21
+ setResult({ model, loading: false, error: null });
22
+ }).catch((err) => {
23
+ if (cancelled) return;
24
+ const error = err instanceof Error ? err : new Error(String(err));
25
+ setResult({ model: null, loading: false, error });
26
+ });
27
+ return () => {
28
+ cancelled = true;
29
+ };
30
+ }, [context?.scene, context?.loadOBJ, context?.webglCore]);
31
+ return result;
32
+ }
33
+
34
+ // src/hooks/useModelRotation.ts
35
+ import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
36
+ function useModelRotation(model) {
37
+ const [rotation, setRotationState] = useState2([0, 0, 0]);
38
+ const modelRef = useRef2(model);
39
+ modelRef.current = model;
40
+ useEffect2(() => {
41
+ if (model) setRotationState([...model.rotation]);
42
+ }, [model]);
43
+ const setRotation = useCallback((x, y, z) => {
44
+ const m = modelRef.current;
45
+ if (!m) return;
46
+ m.setRotation(x, y, z);
47
+ setRotationState([x, y, z]);
48
+ }, []);
49
+ const rotate = useCallback((dx, dy, dz) => {
50
+ const m = modelRef.current;
51
+ if (!m) return;
52
+ const [cx, cy, cz] = m.rotation;
53
+ const nx = cx + dx;
54
+ const ny = cy + dy;
55
+ const nz = cz + dz;
56
+ m.setRotation(nx, ny, nz);
57
+ setRotationState([nx, ny, nz]);
58
+ }, []);
59
+ return { rotation, setRotation, rotate };
60
+ }
61
+
62
+ export {
63
+ useModel,
64
+ useModelRotation
65
+ };
66
+ //# sourceMappingURL=chunk-QBJQURWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useModel.ts","../src/hooks/useModelRotation.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { Model, WebGLCore } from '../../../GenesisGL/src/Core';\nimport type { GenesisGLContext, LoadOBJOptions } from './useGenesisGL';\n\nexport interface UseModelOptions extends LoadOBJOptions {\n name: string;\n objUrl: string;\n postLoad?: (model: Model, webglCore: WebGLCore) => Promise<void> | void;\n}\n\nexport interface UseModelResult {\n model: Model | null;\n loading: boolean;\n error: Error | null;\n}\n\nexport function useModel(\n context: GenesisGLContext | null,\n options: UseModelOptions,\n): UseModelResult {\n const [result, setResult] = useState<UseModelResult>({\n model: null,\n loading: false,\n error: null,\n });\n\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n useEffect(() => {\n if (!context?.scene || !context?.loadOBJ || !context?.webglCore) return;\n\n const { scene, loadOBJ, webglCore } = context;\n const { name, objUrl, postLoad, ...loadOpts } = optionsRef.current;\n let cancelled = false;\n\n setResult({ model: null, loading: true, error: null });\n\n loadOBJ(objUrl, loadOpts)\n .then(async (model) => {\n if (cancelled) return;\n if (postLoad) await postLoad(model, webglCore);\n scene.add(name, model);\n setResult({ model, loading: false, error: null });\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const error = err instanceof Error ? err : new Error(String(err));\n setResult({ model: null, loading: false, error });\n });\n\n return () => {\n cancelled = true;\n };\n }, [context?.scene, context?.loadOBJ, context?.webglCore]);\n\n return result;\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport type { Model } from '@fonsecabarreto/genesis-gl-core/Core';\n\ntype Rotation = [number, number, number];\n\nexport interface UseModelRotationResult {\n rotation: Rotation;\n setRotation: (x: number, y: number, z: number) => void;\n rotate: (dx: number, dy: number, dz: number) => void;\n}\n\nexport function useModelRotation(model: Model | null): UseModelRotationResult {\n const [rotation, setRotationState] = useState<Rotation>([0, 0, 0]);\n const modelRef = useRef(model);\n modelRef.current = model;\n\n useEffect(() => {\n if (model) setRotationState([...model.rotation]);\n }, [model]);\n\n const setRotation = useCallback((x: number, y: number, z: number) => {\n const m = modelRef.current;\n if (!m) return;\n m.setRotation(x, y, z);\n setRotationState([x, y, z]);\n }, []);\n\n const rotate = useCallback((dx: number, dy: number, dz: number) => {\n const m = modelRef.current;\n if (!m) return;\n const [cx, cy, cz] = m.rotation;\n const nx = cx + dx;\n const ny = cy + dy;\n const nz = cz + dz;\n m.setRotation(nx, ny, nz);\n setRotationState([nx, ny, nz]);\n }, []);\n\n return { rotation, setRotation, rotate };\n}\n"],"mappings":";AAAA,SAAS,WAAW,QAAQ,gBAAgB;AAgBrC,SAAS,SACd,SACA,SACgB;AAChB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAyB;AAAA,IACnD,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AAErB,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,SAAS,CAAC,SAAS,WAAW,CAAC,SAAS,UAAW;AAEjE,UAAM,EAAE,OAAO,SAAS,UAAU,IAAI;AACtC,UAAM,EAAE,MAAM,QAAQ,UAAU,GAAG,SAAS,IAAI,WAAW;AAC3D,QAAI,YAAY;AAEhB,cAAU,EAAE,OAAO,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AAErD,YAAQ,QAAQ,QAAQ,EACrB,KAAK,OAAO,UAAU;AACrB,UAAI,UAAW;AACf,UAAI,SAAU,OAAM,SAAS,OAAO,SAAS;AAC7C,YAAM,IAAI,MAAM,KAAK;AACrB,gBAAU,EAAE,OAAO,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IAClD,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,UAAI,UAAW;AACf,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,gBAAU,EAAE,OAAO,MAAM,SAAS,OAAO,MAAM,CAAC;AAAA,IAClD,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,SAAS,SAAS,SAAS,SAAS,CAAC;AAEzD,SAAO;AACT;;;ACzDA,SAAS,aAAa,aAAAA,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAWlD,SAAS,iBAAiB,OAA6C;AAC5E,QAAM,CAAC,UAAU,gBAAgB,IAAIA,UAAmB,CAAC,GAAG,GAAG,CAAC,CAAC;AACjE,QAAM,WAAWD,QAAO,KAAK;AAC7B,WAAS,UAAU;AAEnB,EAAAD,WAAU,MAAM;AACd,QAAI,MAAO,kBAAiB,CAAC,GAAG,MAAM,QAAQ,CAAC;AAAA,EACjD,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,cAAc,YAAY,CAAC,GAAW,GAAW,MAAc;AACnE,UAAM,IAAI,SAAS;AACnB,QAAI,CAAC,EAAG;AACR,MAAE,YAAY,GAAG,GAAG,CAAC;AACrB,qBAAiB,CAAC,GAAG,GAAG,CAAC,CAAC;AAAA,EAC5B,GAAG,CAAC,CAAC;AAEL,QAAM,SAAS,YAAY,CAAC,IAAY,IAAY,OAAe;AACjE,UAAM,IAAI,SAAS;AACnB,QAAI,CAAC,EAAG;AACR,UAAM,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE;AACvB,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,MAAE,YAAY,IAAI,IAAI,EAAE;AACxB,qBAAiB,CAAC,IAAI,IAAI,EAAE,CAAC;AAAA,EAC/B,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,UAAU,aAAa,OAAO;AACzC;","names":["useEffect","useRef","useState"]}
@@ -0,0 +1,351 @@
1
+ import {
2
+ useCameraControls,
3
+ useGenesisGL,
4
+ useRenderer,
5
+ useWorldToScreen
6
+ } from "./chunk-G55QEKXA.js";
7
+
8
+ // src/components/GenesisGLCanvas.tsx
9
+ import React, { useRef, useCallback } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var GenesisGLCanvas = React.forwardRef(
12
+ ({
13
+ onReady,
14
+ onFrame,
15
+ width,
16
+ height,
17
+ initialYaw,
18
+ initialPitch,
19
+ initialZoom,
20
+ style,
21
+ className
22
+ }, ref) => {
23
+ const canvasRef = useRef(null);
24
+ const setRef = useCallback(
25
+ (el) => {
26
+ canvasRef.current = el;
27
+ if (typeof ref === "function") ref(el);
28
+ else if (ref) ref.current = el;
29
+ },
30
+ [ref]
31
+ );
32
+ const onFrameRef = useRef(onFrame);
33
+ onFrameRef.current = onFrame;
34
+ const genesisContext = useGenesisGL({
35
+ canvasRef,
36
+ width,
37
+ height,
38
+ initialYaw,
39
+ initialPitch,
40
+ initialZoom,
41
+ onReady
42
+ });
43
+ const genesisContextRef = useRef(genesisContext);
44
+ genesisContextRef.current = genesisContext;
45
+ const stableOnFrame = useCallback((time) => {
46
+ const { renderer, scene } = genesisContextRef.current;
47
+ if (renderer && scene) {
48
+ renderer.render(scene);
49
+ onFrameRef.current?.(time);
50
+ }
51
+ }, []);
52
+ useRenderer({ renderer: genesisContext.renderer, onFrame: stableOnFrame });
53
+ return /* @__PURE__ */ jsx(
54
+ "canvas",
55
+ {
56
+ ref: setRef,
57
+ style: { display: "block", width: "100%", height: "100%", ...style },
58
+ className
59
+ }
60
+ );
61
+ }
62
+ );
63
+ GenesisGLCanvas.displayName = "GenesisGLCanvas";
64
+
65
+ // src/components/RotationControls.tsx
66
+ import { useState } from "react";
67
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
68
+ var STEP = 0.05;
69
+ function IconBtn({
70
+ children,
71
+ title,
72
+ onClick,
73
+ color
74
+ }) {
75
+ const [active, setActive] = useState(false);
76
+ const [hover, setHover] = useState(false);
77
+ return /* @__PURE__ */ jsx2(
78
+ "button",
79
+ {
80
+ title,
81
+ onClick,
82
+ onMouseEnter: () => setHover(true),
83
+ onMouseLeave: () => {
84
+ setHover(false);
85
+ setActive(false);
86
+ },
87
+ onMouseDown: () => setActive(true),
88
+ onMouseUp: () => setActive(false),
89
+ style: {
90
+ display: "flex",
91
+ alignItems: "center",
92
+ justifyContent: "center",
93
+ width: "22px",
94
+ height: "22px",
95
+ border: `1px solid ${hover ? color + "88" : color + "33"}`,
96
+ borderRadius: "5px",
97
+ background: active ? color + "30" : hover ? color + "18" : color + "0c",
98
+ color: hover ? color : color + "aa",
99
+ cursor: "pointer",
100
+ fontSize: "11px",
101
+ fontWeight: 700,
102
+ lineHeight: 1,
103
+ padding: 0,
104
+ transition: "all 0.12s",
105
+ transform: active ? "scale(0.88)" : "scale(1)",
106
+ flexShrink: 0
107
+ },
108
+ children
109
+ }
110
+ );
111
+ }
112
+ function AxisPill({
113
+ label,
114
+ color,
115
+ value,
116
+ onDec,
117
+ onInc,
118
+ onReset
119
+ }) {
120
+ const deg = (value * 180 / Math.PI).toFixed(1);
121
+ const normalized = (value % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2);
122
+ const pct = normalized / (Math.PI * 2) * 100;
123
+ return /* @__PURE__ */ jsxs(
124
+ "div",
125
+ {
126
+ style: {
127
+ display: "flex",
128
+ alignItems: "center",
129
+ gap: "5px",
130
+ background: color + "0a",
131
+ border: `1px solid ${color}22`,
132
+ borderRadius: "8px",
133
+ padding: "5px 7px",
134
+ flex: 1,
135
+ minWidth: 0
136
+ },
137
+ children: [
138
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "18px", height: "18px", flexShrink: 0 }, children: [
139
+ /* @__PURE__ */ jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", children: [
140
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "7", fill: "none", stroke: color + "22", strokeWidth: "2" }),
141
+ /* @__PURE__ */ jsx2(
142
+ "circle",
143
+ {
144
+ cx: "9",
145
+ cy: "9",
146
+ r: "7",
147
+ fill: "none",
148
+ stroke: color,
149
+ strokeWidth: "2",
150
+ strokeDasharray: `${pct / 100 * 44} 44`,
151
+ strokeLinecap: "round",
152
+ transform: "rotate(-90 9 9)",
153
+ style: { transition: "stroke-dasharray 0.1s ease" }
154
+ }
155
+ )
156
+ ] }),
157
+ /* @__PURE__ */ jsx2(
158
+ "span",
159
+ {
160
+ style: {
161
+ position: "absolute",
162
+ inset: 0,
163
+ display: "flex",
164
+ alignItems: "center",
165
+ justifyContent: "center",
166
+ fontSize: "7px",
167
+ fontWeight: 800,
168
+ color,
169
+ letterSpacing: "-0.02em"
170
+ },
171
+ children: label
172
+ }
173
+ )
174
+ ] }),
175
+ /* @__PURE__ */ jsxs(
176
+ "span",
177
+ {
178
+ style: {
179
+ fontSize: "10px",
180
+ color: color + "cc",
181
+ fontVariantNumeric: "tabular-nums",
182
+ width: "36px",
183
+ textAlign: "right",
184
+ flexShrink: 0
185
+ },
186
+ children: [
187
+ deg,
188
+ "\xB0"
189
+ ]
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "3px", marginLeft: "2px" }, children: [
193
+ /* @__PURE__ */ jsx2(IconBtn, { color, title: `\u2212${label}`, onClick: onDec, children: "\u2212" }),
194
+ /* @__PURE__ */ jsx2(IconBtn, { color, title: `+${label}`, onClick: onInc, children: "+" }),
195
+ /* @__PURE__ */ jsx2(IconBtn, { color: color + "88", title: "Reset", onClick: onReset, children: "\u21BA" })
196
+ ] })
197
+ ]
198
+ }
199
+ );
200
+ }
201
+ function RotationControls({ context, style }) {
202
+ const { camera, rotate, setYaw, setPitch, reset } = useCameraControls(context);
203
+ return /* @__PURE__ */ jsxs(
204
+ "div",
205
+ {
206
+ style: {
207
+ display: "flex",
208
+ alignItems: "center",
209
+ gap: "6px",
210
+ background: "rgba(8,8,12,0.78)",
211
+ border: "1px solid rgba(255,255,255,0.07)",
212
+ borderRadius: "12px",
213
+ padding: "6px 8px",
214
+ backdropFilter: "blur(16px)",
215
+ boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
216
+ fontFamily: '"Inter", system-ui, monospace',
217
+ ...style
218
+ },
219
+ children: [
220
+ /* @__PURE__ */ jsx2(
221
+ "div",
222
+ {
223
+ title: context ? "Camera ready" : "Waiting for context\u2026",
224
+ style: {
225
+ width: "6px",
226
+ height: "6px",
227
+ borderRadius: "50%",
228
+ background: context ? "#c3e88d" : "#555",
229
+ boxShadow: context ? "0 0 6px #c3e88daa" : "none",
230
+ flexShrink: 0,
231
+ transition: "all 0.3s"
232
+ }
233
+ }
234
+ ),
235
+ /* @__PURE__ */ jsx2(
236
+ AxisPill,
237
+ {
238
+ label: "Yaw",
239
+ color: "#ff5370",
240
+ value: camera.yaw,
241
+ onDec: () => rotate(-STEP, 0),
242
+ onInc: () => rotate(STEP, 0),
243
+ onReset: () => setYaw(0)
244
+ }
245
+ ),
246
+ /* @__PURE__ */ jsx2(
247
+ AxisPill,
248
+ {
249
+ label: "Pitch",
250
+ color: "#82aaff",
251
+ value: camera.pitch,
252
+ onDec: () => rotate(0, -STEP),
253
+ onInc: () => rotate(0, STEP),
254
+ onReset: () => setPitch(0)
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsx2(IconBtn, { color: "#ffffff", title: "Reset all", onClick: reset, children: "\u2298" })
258
+ ]
259
+ }
260
+ );
261
+ }
262
+
263
+ // src/components/FaceLabel.tsx
264
+ import { jsx as jsx3 } from "react/jsx-runtime";
265
+ function transformLocalToWorld(m, x, y, z) {
266
+ return {
267
+ x: m[0] * x + m[4] * y + m[8] * z + m[12],
268
+ y: m[1] * x + m[5] * y + m[9] * z + m[13],
269
+ z: m[2] * x + m[6] * y + m[10] * z + m[14]
270
+ };
271
+ }
272
+ function cross(ax, ay, az, bx, by, bz) {
273
+ return {
274
+ x: ay * bz - az * by,
275
+ y: az * bx - ax * bz,
276
+ z: ax * by - ay * bx
277
+ };
278
+ }
279
+ function FaceSkin({
280
+ context,
281
+ model,
282
+ corners,
283
+ children,
284
+ style,
285
+ className
286
+ }) {
287
+ const { project } = useWorldToScreen(context);
288
+ if (!model || !context) return null;
289
+ const mm = model.getModelMatrix();
290
+ const worldPts = corners.map(
291
+ ([lx, ly, lz]) => transformLocalToWorld(mm, lx, ly, lz)
292
+ );
293
+ const e1x = worldPts[1].x - worldPts[0].x;
294
+ const e1y = worldPts[1].y - worldPts[0].y;
295
+ const e1z = worldPts[1].z - worldPts[0].z;
296
+ const e2x = worldPts[3].x - worldPts[0].x;
297
+ const e2y = worldPts[3].y - worldPts[0].y;
298
+ const e2z = worldPts[3].z - worldPts[0].z;
299
+ const normal = cross(e1x, e1y, e1z, e2x, e2y, e2z);
300
+ const camera = context.viewport.camera;
301
+ const radius = Math.sqrt(
302
+ (camera.position[0] - camera.target[0]) ** 2 + (camera.position[1] - camera.target[1]) ** 2 + (camera.position[2] - camera.target[2]) ** 2
303
+ );
304
+ const eyeX = camera.target[0] + radius * Math.cos(camera.pitch) * Math.sin(camera.yaw);
305
+ const eyeY = camera.target[1] + radius * Math.sin(camera.pitch);
306
+ const eyeZ = camera.target[2] + radius * Math.cos(camera.pitch) * Math.cos(camera.yaw);
307
+ const toCamera = {
308
+ x: eyeX - worldPts[0].x,
309
+ y: eyeY - worldPts[0].y,
310
+ z: eyeZ - worldPts[0].z
311
+ };
312
+ const dot = normal.x * toCamera.x + normal.y * toCamera.y + normal.z * toCamera.z;
313
+ if (dot <= 0) return null;
314
+ const screenPts = worldPts.map((w) => project(w.x, w.y, w.z));
315
+ if (screenPts.some((p) => !p.visible)) return null;
316
+ const clip = screenPts.map((p) => `${p.x.toFixed(1)}px ${p.y.toFixed(1)}px`).join(", ");
317
+ const cx = screenPts.reduce((s, p) => s + p.x, 0) / 4;
318
+ const cy = screenPts.reduce((s, p) => s + p.y, 0) / 4;
319
+ return /* @__PURE__ */ jsx3(
320
+ "div",
321
+ {
322
+ className,
323
+ style: {
324
+ position: "absolute",
325
+ inset: 0,
326
+ pointerEvents: "none",
327
+ clipPath: `polygon(${clip})`,
328
+ ...style
329
+ },
330
+ children: children && /* @__PURE__ */ jsx3(
331
+ "div",
332
+ {
333
+ style: {
334
+ position: "absolute",
335
+ left: cx,
336
+ top: cy,
337
+ transform: "translate(-50%,-50%)"
338
+ },
339
+ children
340
+ }
341
+ )
342
+ }
343
+ );
344
+ }
345
+
346
+ export {
347
+ GenesisGLCanvas,
348
+ RotationControls,
349
+ FaceSkin
350
+ };
351
+ //# sourceMappingURL=chunk-SP2L7A72.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/GenesisGLCanvas.tsx","../src/components/RotationControls.tsx","../src/components/FaceLabel.tsx"],"sourcesContent":["import React, { useRef, useCallback } from 'react';\nimport { useGenesisGL } from '../hooks/useGenesisGL';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\nimport { useRenderer } from '../hooks/useRenderer';\n\nexport interface GenesisGLCanvasProps {\n onReady?: (context: GenesisGLContext) => void;\n onFrame?: (time: number) => void;\n width?: number;\n height?: number;\n initialYaw?: number;\n initialPitch?: number;\n initialZoom?: number;\n style?: React.CSSProperties;\n className?: string;\n}\n\nexport const GenesisGLCanvas = React.forwardRef<\n HTMLCanvasElement,\n GenesisGLCanvasProps\n>(\n (\n {\n onReady,\n onFrame,\n width,\n height,\n initialYaw,\n initialPitch,\n initialZoom,\n style,\n className,\n },\n ref,\n ) => {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n\n // Always use the internal ref for GL init; expose to parent via callback ref\n const setRef = useCallback(\n (el: HTMLCanvasElement | null) => {\n (\n canvasRef as React.MutableRefObject<HTMLCanvasElement | null>\n ).current = el;\n if (typeof ref === 'function') ref(el);\n else if (ref) ref.current = el;\n },\n [ref],\n );\n\n const onFrameRef = useRef(onFrame);\n onFrameRef.current = onFrame;\n\n const genesisContext = useGenesisGL({\n canvasRef,\n width,\n height,\n initialYaw,\n initialPitch,\n initialZoom,\n onReady,\n });\n\n const genesisContextRef = useRef(genesisContext);\n genesisContextRef.current = genesisContext;\n\n const stableOnFrame = useCallback((time: number) => {\n const { renderer, scene } = genesisContextRef.current;\n if (renderer && scene) {\n renderer.render(scene);\n onFrameRef.current?.(time);\n }\n }, []);\n\n useRenderer({ renderer: genesisContext.renderer, onFrame: stableOnFrame });\n\n return (\n <canvas\n ref={setRef}\n style={{ display: 'block', width: '100%', height: '100%', ...style }}\n className={className}\n />\n );\n },\n);\n\nGenesisGLCanvas.displayName = 'GenesisGLCanvas';\n","import React, { useState } from 'react';\nimport { useCameraControls } from '../hooks/useCameraControls';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\n\nconst STEP = 0.05;\n\nfunction IconBtn({\n children,\n title,\n onClick,\n color,\n}: {\n children: React.ReactNode;\n title?: string;\n onClick: () => void;\n color: string;\n}) {\n const [active, setActive] = useState(false);\n const [hover, setHover] = useState(false);\n\n return (\n <button\n title={title}\n onClick={onClick}\n onMouseEnter={() => setHover(true)}\n onMouseLeave={() => { setHover(false); setActive(false); }}\n onMouseDown={() => setActive(true)}\n onMouseUp={() => setActive(false)}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '22px',\n height: '22px',\n border: `1px solid ${hover ? color + '88' : color + '33'}`,\n borderRadius: '5px',\n background: active ? color + '30' : hover ? color + '18' : color + '0c',\n color: hover ? color : color + 'aa',\n cursor: 'pointer',\n fontSize: '11px',\n fontWeight: 700,\n lineHeight: 1,\n padding: 0,\n transition: 'all 0.12s',\n transform: active ? 'scale(0.88)' : 'scale(1)',\n flexShrink: 0,\n }}\n >\n {children}\n </button>\n );\n}\n\nfunction AxisPill({\n label,\n color,\n value,\n onDec,\n onInc,\n onReset,\n}: {\n label: string;\n color: string;\n value: number;\n onDec: () => void;\n onInc: () => void;\n onReset: () => void;\n}) {\n const deg = ((value * 180) / Math.PI).toFixed(1);\n const normalized = ((value % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);\n const pct = (normalized / (Math.PI * 2)) * 100;\n\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '5px',\n background: color + '0a',\n border: `1px solid ${color}22`,\n borderRadius: '8px',\n padding: '5px 7px',\n flex: 1,\n minWidth: 0,\n }}\n >\n <div style={{ position: 'relative', width: '18px', height: '18px', flexShrink: 0 }}>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\">\n <circle cx=\"9\" cy=\"9\" r=\"7\" fill=\"none\" stroke={color + '22'} strokeWidth=\"2\" />\n <circle\n cx=\"9\" cy=\"9\" r=\"7\"\n fill=\"none\"\n stroke={color}\n strokeWidth=\"2\"\n strokeDasharray={`${(pct / 100) * 44} 44`}\n strokeLinecap=\"round\"\n transform=\"rotate(-90 9 9)\"\n style={{ transition: 'stroke-dasharray 0.1s ease' }}\n />\n </svg>\n <span\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '7px',\n fontWeight: 800,\n color,\n letterSpacing: '-0.02em',\n }}\n >\n {label}\n </span>\n </div>\n\n <span\n style={{\n fontSize: '10px',\n color: color + 'cc',\n fontVariantNumeric: 'tabular-nums',\n width: '36px',\n textAlign: 'right',\n flexShrink: 0,\n }}\n >\n {deg}°\n </span>\n\n <div style={{ display: 'flex', gap: '3px', marginLeft: '2px' }}>\n <IconBtn color={color} title={`−${label}`} onClick={onDec}>−</IconBtn>\n <IconBtn color={color} title={`+${label}`} onClick={onInc}>+</IconBtn>\n <IconBtn color={color + '88'} title=\"Reset\" onClick={onReset}>↺</IconBtn>\n </div>\n </div>\n );\n}\n\nexport interface RotationControlsProps {\n context: GenesisGLContext | null;\n style?: React.CSSProperties;\n}\n\nexport function RotationControls({ context, style }: RotationControlsProps) {\n const { camera, rotate, setYaw, setPitch, reset } = useCameraControls(context);\n\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n background: 'rgba(8,8,12,0.78)',\n border: '1px solid rgba(255,255,255,0.07)',\n borderRadius: '12px',\n padding: '6px 8px',\n backdropFilter: 'blur(16px)',\n boxShadow: '0 4px 24px rgba(0,0,0,0.5)',\n fontFamily: '\"Inter\", system-ui, monospace',\n ...style,\n }}\n >\n <div\n title={context ? 'Camera ready' : 'Waiting for context…'}\n style={{\n width: '6px',\n height: '6px',\n borderRadius: '50%',\n background: context ? '#c3e88d' : '#555',\n boxShadow: context ? '0 0 6px #c3e88daa' : 'none',\n flexShrink: 0,\n transition: 'all 0.3s',\n }}\n />\n\n <AxisPill\n label=\"Yaw\"\n color=\"#ff5370\"\n value={camera.yaw}\n onDec={() => rotate(-STEP, 0)}\n onInc={() => rotate(STEP, 0)}\n onReset={() => setYaw(0)}\n />\n\n <AxisPill\n label=\"Pitch\"\n color=\"#82aaff\"\n value={camera.pitch}\n onDec={() => rotate(0, -STEP)}\n onInc={() => rotate(0, STEP)}\n onReset={() => setPitch(0)}\n />\n\n <IconBtn color=\"#ffffff\" title=\"Reset all\" onClick={reset}>⊘</IconBtn>\n </div>\n );\n}\n","import React from 'react';\nimport type { Model } from '@fonsecabarreto/genesis-gl-core/Core';\nimport type { GenesisGLContext } from '../hooks/useGenesisGL';\nimport { useWorldToScreen } from '../hooks/useWorldToScreen';\n\nexport interface FaceSkinProps {\n context: GenesisGLContext | null;\n model: Model | null;\n /**\n * Four corners of the face in model local-space, wound consistently\n * (counter-clockwise when facing outward). E.g. front face of a unit cube:\n * [[-0.5,-0.5,0.5],[0.5,-0.5,0.5],[0.5,0.5,0.5],[-0.5,0.5,0.5]]\n */\n corners: [\n [number, number, number],\n [number, number, number],\n [number, number, number],\n [number, number, number],\n ];\n children?: React.ReactNode;\n style?: React.CSSProperties;\n className?: string;\n}\n\nfunction transformLocalToWorld(\n m: Float32Array,\n x: number,\n y: number,\n z: number,\n) {\n return {\n x: m[0] * x + m[4] * y + m[8] * z + m[12],\n y: m[1] * x + m[5] * y + m[9] * z + m[13],\n z: m[2] * x + m[6] * y + m[10] * z + m[14],\n };\n}\n\n/** Cross product of two 3-D vectors. */\nfunction cross(\n ax: number, ay: number, az: number,\n bx: number, by: number, bz: number,\n) {\n return {\n x: ay * bz - az * by,\n y: az * bx - ax * bz,\n z: ax * by - ay * bx,\n };\n}\n\nexport function FaceSkin({\n context,\n model,\n corners,\n children,\n style,\n className,\n}: FaceSkinProps) {\n const { project } = useWorldToScreen(context);\n\n if (!model || !context) return null;\n\n const mm = model.getModelMatrix() as unknown as Float32Array;\n\n // Transform all four corners to world space\n const worldPts = corners.map(([lx, ly, lz]) =>\n transformLocalToWorld(mm, lx, ly, lz),\n );\n\n // Back-face culling in world space: compute face normal via cross product of\n // two edges, then check if it points toward the camera.\n const e1x = worldPts[1].x - worldPts[0].x;\n const e1y = worldPts[1].y - worldPts[0].y;\n const e1z = worldPts[1].z - worldPts[0].z;\n const e2x = worldPts[3].x - worldPts[0].x;\n const e2y = worldPts[3].y - worldPts[0].y;\n const e2z = worldPts[3].z - worldPts[0].z;\n const normal = cross(e1x, e1y, e1z, e2x, e2y, e2z);\n\n const camera = context.viewport!.camera;\n\n // Recompute the actual eye position from orbit params (mirrors Camera.getComputedPosition)\n const radius = Math.sqrt(\n (camera.position[0] - camera.target[0]) ** 2 +\n (camera.position[1] - camera.target[1]) ** 2 +\n (camera.position[2] - camera.target[2]) ** 2,\n );\n const eyeX = camera.target[0] + radius * Math.cos(camera.pitch) * Math.sin(camera.yaw);\n const eyeY = camera.target[1] + radius * Math.sin(camera.pitch);\n const eyeZ = camera.target[2] + radius * Math.cos(camera.pitch) * Math.cos(camera.yaw);\n\n const toCamera = {\n x: eyeX - worldPts[0].x,\n y: eyeY - worldPts[0].y,\n z: eyeZ - worldPts[0].z,\n };\n const dot = normal.x * toCamera.x + normal.y * toCamera.y + normal.z * toCamera.z;\n\n if (dot <= 0) return null;\n\n const screenPts = worldPts.map((w) => project(w.x, w.y, w.z));\n\n // Hide if any corner is clipped\n if (screenPts.some((p) => !p.visible)) return null;\n\n const clip = screenPts.map((p) => `${p.x.toFixed(1)}px ${p.y.toFixed(1)}px`).join(', ');\n\n // Centre of the quad for positioning children\n const cx = screenPts.reduce((s, p) => s + p.x, 0) / 4;\n const cy = screenPts.reduce((s, p) => s + p.y, 0) / 4;\n\n return (\n <div\n className={className}\n style={{\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n clipPath: `polygon(${clip})`,\n ...style,\n }}\n >\n {/* children are centred inside the face */}\n {children && (\n <div\n style={{\n position: 'absolute',\n left: cx,\n top: cy,\n transform: 'translate(-50%,-50%)',\n }}\n >\n {children}\n </div>\n )}\n </div>\n );\n}\n\n// Keep the old name as an alias so existing imports don't break\nexport { FaceSkin as FaceLabel };\nexport type { FaceSkinProps as FaceLabelProps };\n"],"mappings":"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","names":["jsx","jsx"]}
@@ -1,8 +1,13 @@
1
1
  import {
2
- GenesisGLCanvas
3
- } from "../chunk-6ZAB2GPA.js";
4
- import "../chunk-4H2GKYMT.js";
2
+ FaceSkin,
3
+ GenesisGLCanvas,
4
+ RotationControls
5
+ } from "../chunk-SP2L7A72.js";
6
+ import "../chunk-G55QEKXA.js";
5
7
  export {
6
- GenesisGLCanvas
8
+ FaceSkin as FaceLabel,
9
+ FaceSkin,
10
+ GenesisGLCanvas,
11
+ RotationControls
7
12
  };
8
13
  //# sourceMappingURL=index.js.map
@@ -1,10 +1,19 @@
1
- import "../chunk-OJF67RNM.js";
2
1
  import {
2
+ useModel,
3
+ useModelRotation
4
+ } from "../chunk-QBJQURWX.js";
5
+ import {
6
+ useCameraControls,
3
7
  useGenesisGL,
4
- useRenderer
5
- } from "../chunk-4H2GKYMT.js";
8
+ useRenderer,
9
+ useWorldToScreen
10
+ } from "../chunk-G55QEKXA.js";
6
11
  export {
12
+ useCameraControls,
7
13
  useGenesisGL,
8
- useRenderer
14
+ useModel,
15
+ useModelRotation,
16
+ useRenderer,
17
+ useWorldToScreen
9
18
  };
10
19
  //# sourceMappingURL=index.js.map
package/dist/index.js CHANGED
@@ -1,14 +1,34 @@
1
1
  import {
2
- GenesisGLCanvas
3
- } from "./chunk-6ZAB2GPA.js";
4
- import "./chunk-OJF67RNM.js";
2
+ FaceSkin,
3
+ GenesisGLCanvas,
4
+ RotationControls
5
+ } from "./chunk-SP2L7A72.js";
6
+ import {
7
+ useModel,
8
+ useModelRotation
9
+ } from "./chunk-QBJQURWX.js";
5
10
  import {
11
+ useCameraControls,
6
12
  useGenesisGL,
7
- useRenderer
8
- } from "./chunk-4H2GKYMT.js";
13
+ useRenderer,
14
+ useWorldToScreen
15
+ } from "./chunk-G55QEKXA.js";
16
+
17
+ // src/index.ts
18
+ import { Material } from "@fonsecabarreto/genesis-gl-core/Core";
19
+ import { loadOBJWithMTL } from "@fonsecabarreto/genesis-gl-core/Core/utils/parse-obj";
9
20
  export {
21
+ FaceSkin as FaceLabel,
22
+ FaceSkin,
10
23
  GenesisGLCanvas,
24
+ Material,
25
+ RotationControls,
26
+ loadOBJWithMTL,
27
+ useCameraControls,
11
28
  useGenesisGL,
12
- useRenderer
29
+ useModel,
30
+ useModelRotation,
31
+ useRenderer,
32
+ useWorldToScreen
13
33
  };
14
34
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './hooks';\nexport * from './components';\nexport type {\n Scene,\n Viewport,\n Renderer,\n Model,\n WebGLCore,\n} from '@fonsecabarreto/genesis-gl-core/Core';\nexport { Material } from '@fonsecabarreto/genesis-gl-core/Core';\nexport { loadOBJWithMTL } from '@fonsecabarreto/genesis-gl-core/Core/utils/parse-obj';\n"],"mappings":";;;;;;;;;;;;;;;;;AASA,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fonsecabarreto/genesis-gl-react",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "React integration for GenesisGL — WebGL 3D engine",
5
5
  "author": "Lucas Fonseca Barreto",
6
6
  "license": "MIT",