react-native-shine 0.2.2 → 0.3.0

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 (111) hide show
  1. package/README.md +1 -1
  2. package/lib/module/components/Shine.js +269 -0
  3. package/lib/module/components/Shine.js.map +1 -0
  4. package/lib/module/components/ShineGroup.js +104 -0
  5. package/lib/module/components/ShineGroup.js.map +1 -0
  6. package/lib/module/enums/waveCallback.js +19 -0
  7. package/lib/module/enums/waveCallback.js.map +1 -0
  8. package/lib/module/hooks/useOrientation.js +16 -0
  9. package/lib/module/hooks/useOrientation.js.map +1 -0
  10. package/lib/module/index.js +5 -219
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/shaders/bindGroupLayouts.js +40 -5
  13. package/lib/module/shaders/bindGroupLayouts.js.map +1 -1
  14. package/lib/module/shaders/bindGroupUtils.js +27 -12
  15. package/lib/module/shaders/bindGroupUtils.js.map +1 -1
  16. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js +2 -2
  17. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js.map +1 -1
  18. package/lib/module/shaders/fragmentShaders/glareFragment.js +114 -0
  19. package/lib/module/shaders/fragmentShaders/glareFragment.js.map +1 -0
  20. package/lib/module/shaders/fragmentShaders/holoFragment.js +33 -0
  21. package/lib/module/shaders/fragmentShaders/holoFragment.js.map +1 -0
  22. package/lib/module/shaders/fragmentShaders/maskFragment.js +20 -0
  23. package/lib/module/shaders/fragmentShaders/maskFragment.js.map +1 -0
  24. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js +46 -0
  25. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js.map +1 -0
  26. package/lib/module/shaders/pipelineSetups.js +82 -13
  27. package/lib/module/shaders/pipelineSetups.js.map +1 -1
  28. package/lib/module/shaders/{resourceManagement.js → resourceManagement/bitmaps.js} +2 -1
  29. package/lib/module/shaders/resourceManagement/bitmaps.js.map +1 -0
  30. package/lib/module/shaders/resourceManagement/bufferManager.js +46 -0
  31. package/lib/module/shaders/resourceManagement/bufferManager.js.map +1 -0
  32. package/lib/module/shaders/resourceManagement/textures.js +17 -0
  33. package/lib/module/shaders/resourceManagement/textures.js.map +1 -0
  34. package/lib/module/shaders/tgpuUtils.js +19 -1
  35. package/lib/module/shaders/tgpuUtils.js.map +1 -1
  36. package/lib/module/shaders/utils.js +0 -14
  37. package/lib/module/shaders/utils.js.map +1 -1
  38. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js +47 -0
  39. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js.map +1 -0
  40. package/lib/module/types/typeUtils.js +17 -5
  41. package/lib/module/types/typeUtils.js.map +1 -1
  42. package/lib/typescript/src/components/Shine.d.ts +17 -0
  43. package/lib/typescript/src/components/Shine.d.ts.map +1 -0
  44. package/lib/typescript/src/components/ShineGroup.d.ts +8 -0
  45. package/lib/typescript/src/components/ShineGroup.d.ts.map +1 -0
  46. package/lib/typescript/src/enums/waveCallback.d.ts +9 -0
  47. package/lib/typescript/src/enums/waveCallback.d.ts.map +1 -0
  48. package/lib/typescript/src/hooks/useOrientation.d.ts +2 -0
  49. package/lib/typescript/src/hooks/useOrientation.d.ts.map +1 -0
  50. package/lib/typescript/src/index.d.ts +6 -11
  51. package/lib/typescript/src/index.d.ts.map +1 -1
  52. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +44 -6
  53. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -1
  54. package/lib/typescript/src/shaders/bindGroupUtils.d.ts +8 -8
  55. package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -1
  56. package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts +8 -0
  57. package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts.map +1 -0
  58. package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts +5 -0
  59. package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts.map +1 -0
  60. package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts +6 -0
  61. package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts.map +1 -0
  62. package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts +5 -0
  63. package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts.map +1 -0
  64. package/lib/typescript/src/shaders/pipelineSetups.d.ts +9 -4
  65. package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -1
  66. package/lib/typescript/src/shaders/{resourceManagement.d.ts → resourceManagement/bitmaps.d.ts} +1 -1
  67. package/lib/typescript/src/shaders/resourceManagement/bitmaps.d.ts.map +1 -0
  68. package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts +28 -0
  69. package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts.map +1 -0
  70. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts +7 -0
  71. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts.map +1 -0
  72. package/lib/typescript/src/shaders/tgpuUtils.d.ts +5 -1
  73. package/lib/typescript/src/shaders/tgpuUtils.d.ts.map +1 -1
  74. package/lib/typescript/src/shaders/utils.d.ts +1 -7
  75. package/lib/typescript/src/shaders/utils.d.ts.map +1 -1
  76. package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts +6 -0
  77. package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts.map +1 -0
  78. package/lib/typescript/src/types/typeUtils.d.ts +3 -2
  79. package/lib/typescript/src/types/typeUtils.d.ts.map +1 -1
  80. package/lib/typescript/src/types/types.d.ts +7 -2
  81. package/lib/typescript/src/types/types.d.ts.map +1 -1
  82. package/package.json +4 -3
  83. package/scripts/postinstall.js +16 -17
  84. package/src/components/Shine.tsx +480 -0
  85. package/src/components/ShineGroup.tsx +107 -0
  86. package/src/enums/waveCallback.ts +22 -0
  87. package/src/hooks/useOrientation.ts +20 -0
  88. package/src/index.tsx +6 -322
  89. package/src/shaders/bindGroupLayouts.ts +43 -6
  90. package/src/shaders/bindGroupUtils.ts +34 -19
  91. package/src/shaders/fragmentShaders/colorMaskFragment.ts +2 -2
  92. package/src/shaders/fragmentShaders/glareFragment.ts +142 -0
  93. package/src/shaders/fragmentShaders/holoFragment.ts +43 -0
  94. package/src/shaders/fragmentShaders/maskFragment.ts +31 -0
  95. package/src/shaders/fragmentShaders/reverseHoloFragment.ts +71 -0
  96. package/src/shaders/pipelineSetups.ts +161 -14
  97. package/src/shaders/{resourceManagement.ts → resourceManagement/bitmaps.ts} +1 -0
  98. package/src/shaders/resourceManagement/bufferManager.ts +82 -0
  99. package/src/shaders/resourceManagement/textures.ts +30 -0
  100. package/src/shaders/tgpuUtils.ts +36 -1
  101. package/src/shaders/utils.ts +0 -30
  102. package/src/shaders/vertexShaders/mainRotationEffectVertex.ts +76 -0
  103. package/src/types/typeUtils.ts +22 -8
  104. package/src/types/types.ts +8 -2
  105. package/lib/module/shaders/fragmentShaders/bloomFragment.js +0 -66
  106. package/lib/module/shaders/fragmentShaders/bloomFragment.js.map +0 -1
  107. package/lib/module/shaders/resourceManagement.js.map +0 -1
  108. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts +0 -6
  109. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts.map +0 -1
  110. package/lib/typescript/src/shaders/resourceManagement.d.ts.map +0 -1
  111. package/src/shaders/fragmentShaders/bloomFragment.ts +0 -83
@@ -0,0 +1,480 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { Canvas, useDevice, useGPUContext } from 'react-native-wgpu';
3
+ import { getOrInitRoot } from '../roots';
4
+ import mainVertex from '../shaders/vertexShaders/mainVertex';
5
+ import getBitmapFromURI from '../shaders/resourceManagement/bitmaps';
6
+ import {
7
+ clamp,
8
+ rotate2D,
9
+ subscribeToOrientationChange,
10
+ getAngleFromDimensions,
11
+ } from '../shaders/utils';
12
+ import type { TgpuRenderPipeline, TgpuTexture } from 'typegpu';
13
+ import {
14
+ glareOptionsBindGroupLayout,
15
+ colorMaskBindGroupLayout,
16
+ rotationValuesBindGroupLayout,
17
+ textureBindGroupLayout,
18
+ type BufferDataMap,
19
+ bufferData,
20
+ } from '../shaders/bindGroupLayouts';
21
+ import Animated, {
22
+ SensorType,
23
+ useAnimatedSensor,
24
+ useAnimatedStyle,
25
+ useDerivedValue,
26
+ useSharedValue,
27
+ type SharedValue,
28
+ } from 'react-native-reanimated';
29
+ import * as d from 'typegpu/data';
30
+ import { PixelRatio, Platform, View } from 'react-native';
31
+ import {
32
+ createGlareOptionsBindGroup,
33
+ createColorMaskBindGroup,
34
+ createRotationValuesBindGroup,
35
+ } from '../shaders/bindGroupUtils';
36
+ import {
37
+ createBindGroupPairs,
38
+ createGlareOptions,
39
+ createColorMask,
40
+ colorMaskToTyped,
41
+ } from '../types/typeUtils';
42
+ import type {
43
+ BindGroupPair,
44
+ GlareOptions,
45
+ ColorMask,
46
+ DeepPartiallyOptional,
47
+ } from '../types/types';
48
+ import {
49
+ attachBindGroups,
50
+ blend,
51
+ createReverseHoloPipeline,
52
+ createMaskPipeline,
53
+ getDefaultTarget,
54
+ pipelineRenderFunction,
55
+ createRainbowHoloPipeline as createHoloPipeline,
56
+ } from '../shaders/pipelineSetups';
57
+ import colorMaskFragment from '../shaders/fragmentShaders/colorMaskFragment';
58
+ import {
59
+ createTexture,
60
+ loadTexture,
61
+ } from '../shaders/resourceManagement/textures';
62
+ import { newGlareFragment } from '../shaders/fragmentShaders/glareFragment';
63
+ import { TypedBufferMap } from '../shaders/resourceManagement/bufferManager';
64
+
65
+ export interface ShineProps {
66
+ width: number;
67
+ height: number;
68
+ imageURI: string;
69
+ glareOptions?: Partial<GlareOptions>;
70
+ colorMaskOptions?: DeepPartiallyOptional<ColorMask, 'baseColor'>;
71
+ maskURI?: string;
72
+ useTouchControl?: boolean;
73
+ touchPosition?: SharedValue<[number, number]>;
74
+ addTextureMask?: boolean;
75
+ addReverseHolo?: boolean;
76
+ addHolo?: boolean;
77
+ }
78
+
79
+ export function Shine({
80
+ width,
81
+ height,
82
+ imageURI,
83
+ glareOptions: glareOptions,
84
+ colorMaskOptions,
85
+ maskURI,
86
+ touchPosition,
87
+ useTouchControl = false,
88
+ addTextureMask = false,
89
+ addHolo = false,
90
+ addReverseHolo = false,
91
+ }: ShineProps) {
92
+ const { device = null } = useDevice();
93
+ const root = device ? getOrInitRoot(device) : null;
94
+ const { ref, context } = useGPUContext();
95
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
96
+ const frameRef = useRef<number | null>(null);
97
+
98
+ //changing canvas size to prevent blur
99
+ const dpr = PixelRatio.get();
100
+ const logicalWidth = width;
101
+ const logicalHeight = height;
102
+ const pixelWidth = Math.max(1, Math.round(logicalWidth * dpr));
103
+ const pixelHeight = Math.max(1, Math.round(logicalHeight * dpr));
104
+
105
+ const [imageTexture, setImageTexture] = useState<TgpuTexture | null>(null);
106
+ const [maskTexture, setMaskTexture] = useState<TgpuTexture | null>(null);
107
+
108
+ const orientationAngle = useSharedValue<number>(0); // degrees
109
+ const rotationShared = useSharedValue<[number, number, number]>([0, 0, 0]); // final GPU offsets
110
+
111
+ // Calibration shared values (UI thread)
112
+ const initialGravity = useSharedValue<[number, number, number]>([0, 0, 0]);
113
+ const calibSum = useSharedValue<[number, number, number]>([0, 0, 0]);
114
+ const calibCount = useSharedValue<number>(0);
115
+ const calibrated = useSharedValue<boolean>(false);
116
+ const gravitySensor = useAnimatedSensor(SensorType.GRAVITY, { interval: 20 });
117
+
118
+ const bufferManager = useMemo(
119
+ () => new TypedBufferMap(bufferData as BufferDataMap),
120
+ []
121
+ );
122
+
123
+ //TODO: add once again, when the wgpu issues are fixed :3
124
+
125
+ const animatedStyle = useAnimatedStyle(() => {
126
+ // const rotX = rotationShared.value[0] * 10;
127
+ // const rotY = rotationShared.value[1] * 10;
128
+
129
+ return {
130
+ transform: [
131
+ { perspective: 300 },
132
+ // { rotateX: `${-rotX}deg` },
133
+ // { rotateY: `${rotY}deg` },
134
+ // { rotateZ: `${rotX * 5}deg` },
135
+ ],
136
+ };
137
+ });
138
+ // Subscribe to orientation changes and reset calibration on change
139
+ useEffect(() => {
140
+ orientationAngle.value = getAngleFromDimensions();
141
+ const unsubscribe = subscribeToOrientationChange((angleDeg) => {
142
+ orientationAngle.value = angleDeg;
143
+ });
144
+
145
+ return () => unsubscribe();
146
+ }, [orientationAngle]);
147
+
148
+ // Calibration & mapping logic
149
+ useDerivedValue(() => {
150
+ 'worklet';
151
+
152
+ if (useTouchControl) {
153
+ rotationShared.value = touchPosition
154
+ ? [...touchPosition.value, 0]
155
+ : [0, 0, 0];
156
+
157
+ return;
158
+ }
159
+
160
+ // console.log(orientationAngle.value);
161
+ const v: { x: number; y: number; z: number } = gravitySensor.sensor
162
+ ?.value ??
163
+ gravitySensor.sensor.value ?? { x: 0, y: 0, z: 0 };
164
+
165
+ const gx = v.x ?? 0;
166
+ const gy = v.y ?? 0;
167
+ const gz = v.z ?? 0;
168
+
169
+ const CALIBRATION_SAMPLES = 40;
170
+ const alpha = 0.15; // smoothing
171
+ const scale = 0.6;
172
+
173
+ if (!calibrated.value) {
174
+ // accumulate baseline in device coordinates
175
+ const s = calibSum.value;
176
+ const c = calibCount.value + 1;
177
+ calibSum.value = [s[0] + gx, s[1] + gy, s[2] + gz];
178
+ calibCount.value = c;
179
+
180
+ if (c >= CALIBRATION_SAMPLES) {
181
+ const avg = calibSum.value;
182
+ initialGravity.value = [avg[0] / c, avg[1] / c, avg[2] / c];
183
+ calibrated.value = true;
184
+ }
185
+
186
+ rotationShared.value = [0, 0, 0];
187
+ return;
188
+ }
189
+
190
+ const init = initialGravity.value;
191
+ const dx = gx - init[0];
192
+ const dy = gy - init[1];
193
+ const dz = gz - init[2];
194
+
195
+ // Rotate into screen coordinates so offsets auto-swap with orientation
196
+ const [mx, my] = rotate2D([dx, dy], -orientationAngle.value);
197
+ const screenX = mx;
198
+ const screenY = -my;
199
+
200
+ const prev = rotationShared.value;
201
+ const smoothX = prev[0] * (1 - alpha) + screenX * alpha;
202
+ const smoothY = prev[1] * (1 - alpha) + screenY * alpha;
203
+ const smoothZ = prev[2] * (1 - alpha) + dz * alpha;
204
+
205
+ if (orientationAngle.value === 90) {
206
+ rotationShared.value = [
207
+ clamp(smoothY * scale, -1, 1),
208
+ clamp(-smoothX * scale, -1, 1),
209
+ clamp(smoothZ * scale, -1, 1),
210
+ ];
211
+ } else {
212
+ rotationShared.value = [
213
+ clamp(smoothX * scale, -1, 1),
214
+ clamp(smoothY * scale, -1, 1),
215
+ clamp(smoothZ * scale, -1, 1),
216
+ ];
217
+ }
218
+ });
219
+
220
+ // Resource setup
221
+ useEffect(() => {
222
+ if (!root || !device || !context) return;
223
+
224
+ (async () => {
225
+ const bitmap = await getBitmapFromURI(imageURI);
226
+ const texture = await createTexture(root, bitmap);
227
+ setImageTexture(texture);
228
+ await loadTexture(root, bitmap, texture);
229
+
230
+ if (!maskURI) return;
231
+ const maskBitmap = await getBitmapFromURI(maskURI);
232
+ const maskTex = await createTexture(root, maskBitmap);
233
+ setMaskTexture(maskTex);
234
+ await loadTexture(root, maskBitmap, maskTex);
235
+ })();
236
+ }, [root, device, context, imageURI, maskURI]);
237
+
238
+ // Render loop
239
+ useEffect(() => {
240
+ if (!root || !device || !context || !imageTexture) return;
241
+
242
+ //this sets the underlying resolution of the canvas to prevent blurriness
243
+ const canvasElement = context.canvas;
244
+ if (
245
+ canvasElement &&
246
+ canvasElement.width !== pixelWidth &&
247
+ canvasElement.height !== pixelHeight
248
+ ) {
249
+ canvasElement.width = pixelWidth;
250
+ canvasElement.height = pixelHeight;
251
+ }
252
+
253
+ context.configure({
254
+ device,
255
+ format: presentationFormat,
256
+ alphaMode: 'premultiplied',
257
+ });
258
+
259
+ const sampler = device.createSampler({
260
+ magFilter: 'linear',
261
+ minFilter: 'linear',
262
+ });
263
+
264
+ const imageTextureBindGroup = root.createBindGroup(textureBindGroupLayout, {
265
+ texture: root.unwrap(imageTexture).createView(),
266
+ sampler: sampler,
267
+ });
268
+
269
+ const rotationBuffer = bufferManager.addBuffer(
270
+ root,
271
+ 'rotationBuffer',
272
+ d.vec3f(0.0)
273
+ );
274
+
275
+ const rotationBindGroup = createRotationValuesBindGroup(
276
+ root,
277
+ rotationBuffer
278
+ );
279
+
280
+ const glareOptionsBuffer = bufferManager.addBuffer(
281
+ root,
282
+ 'glareBuffer',
283
+ createGlareOptions(glareOptions ?? {})
284
+ );
285
+ const glareOptionsBindGroup = createGlareOptionsBindGroup(
286
+ root,
287
+ glareOptionsBuffer
288
+ );
289
+
290
+ const colorMaskBuffer = bufferManager.addBuffer(
291
+ root,
292
+ 'colorMaskBuffer',
293
+ colorMaskToTyped(
294
+ createColorMask(colorMaskOptions ?? { baseColor: [-20, -20, -20] })
295
+ )
296
+ );
297
+ const colorMaskBindGroup = createColorMaskBindGroup(root, colorMaskBuffer);
298
+
299
+ const glareBGP: BindGroupPair[] = createBindGroupPairs(
300
+ [
301
+ textureBindGroupLayout,
302
+ rotationValuesBindGroupLayout,
303
+ glareOptionsBindGroupLayout,
304
+ colorMaskBindGroupLayout,
305
+ ],
306
+ [
307
+ imageTextureBindGroup,
308
+ rotationBindGroup,
309
+ glareOptionsBindGroup,
310
+ colorMaskBindGroup,
311
+ ]
312
+ );
313
+
314
+ const colorMaskBGP: BindGroupPair[] = createBindGroupPairs(
315
+ [
316
+ textureBindGroupLayout,
317
+ colorMaskBindGroupLayout,
318
+ rotationValuesBindGroupLayout,
319
+ ],
320
+ [imageTextureBindGroup, colorMaskBindGroup, rotationBindGroup]
321
+ );
322
+
323
+ let glarePipeline = root['~unstable']
324
+ .withVertex(mainVertex, {})
325
+ .withFragment(newGlareFragment, getDefaultTarget(presentationFormat))
326
+ .createPipeline();
327
+ glarePipeline = attachBindGroups(glarePipeline, glareBGP);
328
+
329
+ let colorMaskPipeline = root['~unstable']
330
+ .withVertex(mainVertex, {})
331
+ .withFragment(
332
+ colorMaskFragment,
333
+ getDefaultTarget(presentationFormat, blend)
334
+ )
335
+ .createPipeline();
336
+ colorMaskPipeline = attachBindGroups(colorMaskPipeline, colorMaskBGP);
337
+
338
+ //optional pipeline - mask
339
+ const maskPipeline = createMaskPipeline(
340
+ root,
341
+ maskTexture,
342
+ createBindGroupPairs(
343
+ [textureBindGroupLayout, rotationValuesBindGroupLayout],
344
+ [imageTextureBindGroup, rotationBindGroup]
345
+ ),
346
+ sampler,
347
+ presentationFormat
348
+ );
349
+
350
+ const reverseHoloBGP: BindGroupPair[] = createBindGroupPairs(
351
+ [
352
+ textureBindGroupLayout,
353
+ rotationValuesBindGroupLayout,
354
+ glareOptionsBindGroupLayout,
355
+ ],
356
+ [imageTextureBindGroup, rotationBindGroup, glareOptionsBindGroup]
357
+ );
358
+
359
+ const reverseHoloPipeline = createReverseHoloPipeline(
360
+ root,
361
+ maskTexture,
362
+ reverseHoloBGP,
363
+ sampler,
364
+ presentationFormat
365
+ );
366
+
367
+ const holoBGP: BindGroupPair[] = createBindGroupPairs(
368
+ [rotationValuesBindGroupLayout],
369
+ [rotationBindGroup]
370
+ );
371
+
372
+ const holoPipeline = createHoloPipeline(
373
+ root,
374
+ imageTexture,
375
+ holoBGP,
376
+ sampler,
377
+ presentationFormat
378
+ );
379
+
380
+ const pipelines: TgpuRenderPipeline[] = [glarePipeline];
381
+ if (addTextureMask && maskPipeline) pipelines.push(maskPipeline);
382
+ if (addReverseHolo && reverseHoloPipeline)
383
+ pipelines.push(reverseHoloPipeline);
384
+ if (addHolo && holoPipeline) pipelines.push(holoPipeline);
385
+ if (colorMaskOptions) pipelines.push(colorMaskPipeline);
386
+
387
+ const rot = d.vec3f(0.0);
388
+ let view: GPUTextureView;
389
+ let initialAttachment;
390
+ let loadingAttachment;
391
+ const isInSinglePass = false;
392
+ const render = () => {
393
+ rot[0] = rotationShared.value[0];
394
+ rot[1] = rotationShared.value[1];
395
+ rot[2] = rotationShared.value[2];
396
+ rotationBuffer.write(rot);
397
+
398
+ view = context.getCurrentTexture().createView();
399
+ initialAttachment = {
400
+ view: view,
401
+ clearValue: [0, 0, 0, 0],
402
+ loadOp: 'clear' as GPULoadOp,
403
+ storeOp: 'store' as GPUStoreOp,
404
+ };
405
+ loadingAttachment = {
406
+ view: view,
407
+ clearValue: [0, 0, 0, 0],
408
+ loadOp: 'load' as GPULoadOp,
409
+ storeOp: 'store' as GPUStoreOp,
410
+ };
411
+
412
+ pipelineRenderFunction(
413
+ root,
414
+ pipelines,
415
+ [
416
+ initialAttachment,
417
+ loadingAttachment,
418
+ loadingAttachment,
419
+ loadingAttachment,
420
+ loadingAttachment,
421
+ ],
422
+ view,
423
+ isInSinglePass
424
+ );
425
+
426
+ context.present();
427
+ frameRef.current = requestAnimationFrame(render);
428
+ };
429
+ frameRef.current = requestAnimationFrame(render);
430
+
431
+ return () => {
432
+ if (frameRef.current) cancelAnimationFrame(frameRef.current);
433
+ };
434
+ }, [
435
+ device,
436
+ context,
437
+ root,
438
+ presentationFormat,
439
+ imageTexture,
440
+ maskTexture,
441
+ rotationShared,
442
+ bufferManager,
443
+ glareOptions,
444
+ colorMaskOptions,
445
+ maskURI,
446
+ addHolo,
447
+ addReverseHolo,
448
+ addTextureMask,
449
+ pixelWidth,
450
+ pixelHeight,
451
+ ]);
452
+
453
+ return (
454
+ <Animated.View style={[animatedStyle]}>
455
+ <View
456
+ style={
457
+ [
458
+ // styles.container,
459
+ // { width: logicalWidth, height: logicalHeight },
460
+ ]
461
+ }
462
+ >
463
+ <Canvas
464
+ ref={ref}
465
+ style={[
466
+ { width: logicalWidth, height: logicalHeight },
467
+ // aspectRatio: pixelWidth / pixelHeight,
468
+ // { transform: [{ scaleX: 1 / dpr }, { scaleY: 1 / dpr }] },
469
+ ]}
470
+ transparent={Platform.OS === 'ios'}
471
+ // transparent={true}
472
+ />
473
+ </View>
474
+ </Animated.View>
475
+ );
476
+ }
477
+
478
+ // const styles = StyleSheet.create({
479
+ // container: { overflow: 'hidden' },
480
+ // });
@@ -0,0 +1,107 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+ type LayoutChangeEvent,
6
+ Image,
7
+ PixelRatio,
8
+ } from 'react-native';
9
+ import ViewShot, { captureRef } from 'react-native-view-shot';
10
+ import { Shine, type ShineProps } from './Shine';
11
+
12
+ interface ShineGroupProps {
13
+ children: React.ReactNode;
14
+ }
15
+
16
+ export function ShineGroup({
17
+ children,
18
+ glareOptions,
19
+ colorMaskOptions,
20
+ maskURI,
21
+ touchPosition,
22
+ useTouchControl = false,
23
+ addTextureMask = false,
24
+ addHolo = false,
25
+ addReverseHolo = false,
26
+ }: ShineGroupProps & Partial<ShineProps>) {
27
+ const viewShotRef = useRef<any>(null);
28
+ const [capturedURI, setCapturedURI] = useState<string | null>(null);
29
+ const [size, setSize] = useState<{ width: number; height: number } | null>(
30
+ null
31
+ );
32
+
33
+ const onInnerLayout = (e: LayoutChangeEvent) => {
34
+ const { width, height } = e.nativeEvent.layout;
35
+ if (width > 0 && height > 0) {
36
+ if (!size || size.width !== width || size.height !== height) {
37
+ setSize({ width, height });
38
+ }
39
+ }
40
+ console.log('onInnerLayout', width, height);
41
+ };
42
+
43
+ // When we have a valid measured size, take a snapshot (after a short tick)
44
+ // Short timeout helps when children include images that finish layout a few ms later
45
+ useEffect(() => {
46
+ if (!viewShotRef.current || !size) return;
47
+
48
+ let mounted = true;
49
+ const t = setTimeout(async () => {
50
+ try {
51
+ const dpr = PixelRatio.get();
52
+ const pixelW = Math.round(size.width * dpr);
53
+ const pixelH = Math.round(size.height * dpr);
54
+
55
+ const uri = await captureRef(viewShotRef.current, {
56
+ format: 'png',
57
+ quality: 1,
58
+ width: pixelW,
59
+ height: pixelH,
60
+ });
61
+ if (mounted) setCapturedURI(uri);
62
+ } catch (err) {
63
+ console.warn('ShineGroup capture failed', err);
64
+ }
65
+ }, 50);
66
+
67
+ return () => {
68
+ mounted = false;
69
+ clearTimeout(t);
70
+ };
71
+ }, [size, children]);
72
+
73
+ return (
74
+ <View style={styles.container}>
75
+ <ViewShot ref={viewShotRef} options={{ format: 'png', quality: 1 }}>
76
+ <View onLayout={onInnerLayout} style={styles.inner}>
77
+ {children}
78
+ </View>
79
+ </ViewShot>
80
+
81
+ {capturedURI && size && (
82
+ <Image src={capturedURI} width={size.width} height={size.height} />
83
+ )}
84
+
85
+ {capturedURI && size && (
86
+ <Shine
87
+ width={size.width}
88
+ height={size.height}
89
+ imageURI={capturedURI}
90
+ glareOptions={glareOptions}
91
+ colorMaskOptions={colorMaskOptions}
92
+ maskURI={maskURI}
93
+ touchPosition={touchPosition}
94
+ useTouchControl={useTouchControl}
95
+ addTextureMask={addTextureMask}
96
+ addReverseHolo={addReverseHolo}
97
+ addHolo={addHolo}
98
+ />
99
+ )}
100
+ </View>
101
+ );
102
+ }
103
+
104
+ const styles = StyleSheet.create({
105
+ container: { display: 'flex', flexDirection: 'column', gap: 10 },
106
+ inner: {},
107
+ });
@@ -0,0 +1,22 @@
1
+ import tgpu, { type TgpuFn } from 'typegpu';
2
+ import * as std from 'typegpu/std';
3
+ import * as d from 'typegpu/data';
4
+
5
+ export const WAVE_CALLBACKS = {
6
+ default: (pos: d.v2f) => {
7
+ 'kernel';
8
+
9
+ const x = pos.x;
10
+ const y = pos.y;
11
+
12
+ const waveX = std.sin(x * 2.0);
13
+ const waveY = std.cos(y * 2.0);
14
+
15
+ return d.vec2f(waveX, waveY);
16
+ },
17
+ } as const;
18
+
19
+ export const waveCallbackSlot = tgpu.slot<TgpuFn<(pos: d.Vec2f) => d.Vec2f>>();
20
+
21
+ export type WaveCallbackFn = (pos: d.v2f) => d.v2f;
22
+ export const waveCallbackFn = tgpu.fn([d.vec2f], d.vec2f);
@@ -0,0 +1,20 @@
1
+ import { useEffect, useState } from 'react';
2
+ import {
3
+ getAngleFromDimensions,
4
+ subscribeToOrientationChange,
5
+ } from '../shaders/utils';
6
+
7
+ export const useOrientation = () => {
8
+ const [orientation, setOrientation] = useState<string>();
9
+
10
+ useEffect(() => {
11
+ setOrientation(getAngleFromDimensions() === 0 ? 'PORTRAIT' : 'LANDSCAPE');
12
+ const unsubscribe = subscribeToOrientationChange((angleDeg) => {
13
+ setOrientation(angleDeg === 0 ? 'PORTRAIT' : 'LANDSCAPE');
14
+ });
15
+
16
+ return () => unsubscribe();
17
+ }, []);
18
+
19
+ return orientation;
20
+ };