react-native-shine 0.2.2 → 0.3.1

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 (141) hide show
  1. package/README.md +22 -7
  2. package/lib/module/components/Content.js +232 -0
  3. package/lib/module/components/Content.js.map +1 -0
  4. package/lib/module/components/Shine.js +33 -0
  5. package/lib/module/components/Shine.js.map +1 -0
  6. package/lib/module/components/ShineGroup.js +92 -0
  7. package/lib/module/components/ShineGroup.js.map +1 -0
  8. package/lib/module/enums/waveCallback.js +19 -0
  9. package/lib/module/enums/waveCallback.js.map +1 -0
  10. package/lib/module/hooks/useAnimationFrame.js +17 -0
  11. package/lib/module/hooks/useAnimationFrame.js.map +1 -0
  12. package/lib/module/hooks/useOrientation.js +10 -0
  13. package/lib/module/hooks/useOrientation.js.map +1 -0
  14. package/lib/module/index.js +6 -219
  15. package/lib/module/index.js.map +1 -1
  16. package/lib/module/shaders/bindGroupLayouts.js +41 -6
  17. package/lib/module/shaders/bindGroupLayouts.js.map +1 -1
  18. package/lib/module/shaders/bindGroupUtils.js +34 -41
  19. package/lib/module/shaders/bindGroupUtils.js.map +1 -1
  20. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js +2 -2
  21. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js.map +1 -1
  22. package/lib/module/shaders/fragmentShaders/glareFragment.js +115 -0
  23. package/lib/module/shaders/fragmentShaders/glareFragment.js.map +1 -0
  24. package/lib/module/shaders/fragmentShaders/holoFragment.js +28 -0
  25. package/lib/module/shaders/fragmentShaders/holoFragment.js.map +1 -0
  26. package/lib/module/shaders/fragmentShaders/maskFragment.js +20 -0
  27. package/lib/module/shaders/fragmentShaders/maskFragment.js.map +1 -0
  28. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js +46 -0
  29. package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js.map +1 -0
  30. package/lib/module/shaders/pipelineSetups.js +72 -17
  31. package/lib/module/shaders/pipelineSetups.js.map +1 -1
  32. package/lib/module/shaders/{resourceManagement.js → resourceManagement/bitmaps.js} +2 -1
  33. package/lib/module/shaders/resourceManagement/bitmaps.js.map +1 -0
  34. package/lib/module/shaders/resourceManagement/bufferManager.js +46 -0
  35. package/lib/module/shaders/resourceManagement/bufferManager.js.map +1 -0
  36. package/lib/module/shaders/resourceManagement/textures.js +24 -0
  37. package/lib/module/shaders/resourceManagement/textures.js.map +1 -0
  38. package/lib/module/shaders/tgpuUtils.js +19 -1
  39. package/lib/module/shaders/tgpuUtils.js.map +1 -1
  40. package/lib/module/shaders/utils.js +8 -36
  41. package/lib/module/shaders/utils.js.map +1 -1
  42. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js +47 -0
  43. package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js.map +1 -0
  44. package/lib/module/types/size.js +2 -0
  45. package/lib/module/types/size.js.map +1 -0
  46. package/lib/module/types/typeUtils.js +17 -19
  47. package/lib/module/types/typeUtils.js.map +1 -1
  48. package/lib/module/types/vector.js +2 -0
  49. package/lib/module/types/vector.js.map +1 -0
  50. package/lib/module/utils/size.js +25 -0
  51. package/lib/module/utils/size.js.map +1 -0
  52. package/lib/module/utils/vector.js +168 -0
  53. package/lib/module/utils/vector.js.map +1 -0
  54. package/lib/typescript/src/components/Content.d.ts +23 -0
  55. package/lib/typescript/src/components/Content.d.ts.map +1 -0
  56. package/lib/typescript/src/components/Shine.d.ts +7 -0
  57. package/lib/typescript/src/components/Shine.d.ts.map +1 -0
  58. package/lib/typescript/src/components/ShineGroup.d.ts +6 -0
  59. package/lib/typescript/src/components/ShineGroup.d.ts.map +1 -0
  60. package/lib/typescript/src/enums/waveCallback.d.ts +9 -0
  61. package/lib/typescript/src/enums/waveCallback.d.ts.map +1 -0
  62. package/lib/typescript/src/hooks/useAnimationFrame.d.ts +2 -0
  63. package/lib/typescript/src/hooks/useAnimationFrame.d.ts.map +1 -0
  64. package/lib/typescript/src/hooks/useOrientation.d.ts +4 -0
  65. package/lib/typescript/src/hooks/useOrientation.d.ts.map +1 -0
  66. package/lib/typescript/src/index.d.ts +8 -11
  67. package/lib/typescript/src/index.d.ts.map +1 -1
  68. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +46 -8
  69. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -1
  70. package/lib/typescript/src/shaders/bindGroupUtils.d.ts +9 -9
  71. package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -1
  72. package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts +8 -0
  73. package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts.map +1 -0
  74. package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts +5 -0
  75. package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts.map +1 -0
  76. package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts +6 -0
  77. package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts.map +1 -0
  78. package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts +5 -0
  79. package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts.map +1 -0
  80. package/lib/typescript/src/shaders/pipelineSetups.d.ts +9 -4
  81. package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -1
  82. package/lib/typescript/src/shaders/{resourceManagement.d.ts → resourceManagement/bitmaps.d.ts} +1 -1
  83. package/lib/typescript/src/shaders/resourceManagement/bitmaps.d.ts.map +1 -0
  84. package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts +28 -0
  85. package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts.map +1 -0
  86. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts +8 -0
  87. package/lib/typescript/src/shaders/resourceManagement/textures.d.ts.map +1 -0
  88. package/lib/typescript/src/shaders/tgpuUtils.d.ts +5 -1
  89. package/lib/typescript/src/shaders/tgpuUtils.d.ts.map +1 -1
  90. package/lib/typescript/src/shaders/utils.d.ts +3 -10
  91. package/lib/typescript/src/shaders/utils.d.ts.map +1 -1
  92. package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts +6 -0
  93. package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts.map +1 -0
  94. package/lib/typescript/src/types/size.d.ts +5 -0
  95. package/lib/typescript/src/types/size.d.ts.map +1 -0
  96. package/lib/typescript/src/types/typeUtils.d.ts +3 -5
  97. package/lib/typescript/src/types/typeUtils.d.ts.map +1 -1
  98. package/lib/typescript/src/types/types.d.ts +10 -3
  99. package/lib/typescript/src/types/types.d.ts.map +1 -1
  100. package/lib/typescript/src/types/vector.d.ts +11 -0
  101. package/lib/typescript/src/types/vector.d.ts.map +1 -0
  102. package/lib/typescript/src/utils/size.d.ts +5 -0
  103. package/lib/typescript/src/utils/size.d.ts.map +1 -0
  104. package/lib/typescript/src/utils/vector.d.ts +33 -0
  105. package/lib/typescript/src/utils/vector.d.ts.map +1 -0
  106. package/package.json +7 -5
  107. package/scripts/postinstall.js +16 -17
  108. package/src/components/Content.tsx +403 -0
  109. package/src/components/Shine.tsx +38 -0
  110. package/src/components/ShineGroup.tsx +100 -0
  111. package/src/enums/waveCallback.ts +22 -0
  112. package/src/hooks/useAnimationFrame.ts +21 -0
  113. package/src/hooks/useOrientation.ts +18 -0
  114. package/src/index.tsx +14 -322
  115. package/src/shaders/bindGroupLayouts.ts +45 -8
  116. package/src/shaders/bindGroupUtils.ts +50 -65
  117. package/src/shaders/fragmentShaders/colorMaskFragment.ts +2 -2
  118. package/src/shaders/fragmentShaders/glareFragment.ts +143 -0
  119. package/src/shaders/fragmentShaders/holoFragment.ts +35 -0
  120. package/src/shaders/fragmentShaders/maskFragment.ts +31 -0
  121. package/src/shaders/fragmentShaders/reverseHoloFragment.ts +71 -0
  122. package/src/shaders/pipelineSetups.ts +152 -20
  123. package/src/shaders/{resourceManagement.ts → resourceManagement/bitmaps.ts} +1 -0
  124. package/src/shaders/resourceManagement/bufferManager.ts +82 -0
  125. package/src/shaders/resourceManagement/textures.ts +42 -0
  126. package/src/shaders/tgpuUtils.ts +36 -1
  127. package/src/shaders/utils.ts +13 -57
  128. package/src/shaders/vertexShaders/mainRotationEffectVertex.ts +76 -0
  129. package/src/types/size.ts +4 -0
  130. package/src/types/typeUtils.ts +22 -36
  131. package/src/types/types.ts +19 -3
  132. package/src/types/vector.ts +13 -0
  133. package/src/utils/size.ts +12 -0
  134. package/src/utils/vector.ts +132 -0
  135. package/lib/module/shaders/fragmentShaders/bloomFragment.js +0 -66
  136. package/lib/module/shaders/fragmentShaders/bloomFragment.js.map +0 -1
  137. package/lib/module/shaders/resourceManagement.js.map +0 -1
  138. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts +0 -6
  139. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts.map +0 -1
  140. package/lib/typescript/src/shaders/resourceManagement.d.ts.map +0 -1
  141. package/src/shaders/fragmentShaders/bloomFragment.ts +0 -83
@@ -0,0 +1,403 @@
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { PixelRatio, Platform, View } from 'react-native';
3
+ import Animated, {
4
+ SensorType,
5
+ type SharedValue,
6
+ useAnimatedSensor,
7
+ useAnimatedStyle,
8
+ useDerivedValue,
9
+ useSharedValue,
10
+ } from 'react-native-reanimated';
11
+ import { Canvas, useGPUContext } from 'react-native-wgpu';
12
+ import * as d from 'typegpu/data';
13
+ import type {
14
+ TextureProps,
15
+ TgpuRenderPipeline,
16
+ TgpuRoot,
17
+ TgpuTexture,
18
+ } from 'typegpu';
19
+ import {
20
+ bufferData,
21
+ type BufferData,
22
+ textureBindGroupLayout,
23
+ } from '../shaders/bindGroupLayouts';
24
+ import useAnimationFrame from '../hooks/useAnimationFrame';
25
+ import { TypedBufferMap } from '../shaders/resourceManagement/bufferManager';
26
+ import {
27
+ createColorMaskBindGroup,
28
+ createGlareBindGroup,
29
+ createRotationValuesBindGroup,
30
+ } from '../shaders/bindGroupUtils';
31
+ import colorMaskFragment from '../shaders/fragmentShaders/colorMaskFragment';
32
+ import { newGlareFragment } from '../shaders/fragmentShaders/glareFragment';
33
+ import {
34
+ attachBindGroups,
35
+ blend,
36
+ createMaskPipeline,
37
+ createRainbowHoloPipeline as createHoloPipeline,
38
+ createReverseHoloPipeline,
39
+ getDefaultTarget,
40
+ } from '../shaders/pipelineSetups';
41
+ import mainVertex from '../shaders/vertexShaders/mainVertex';
42
+ import { subscribeToOrientationChange } from '../shaders/utils';
43
+ import type {
44
+ ColorAttachment,
45
+ ColorMask,
46
+ DeepPartiallyOptional,
47
+ GlareOptions,
48
+ PipelineAttachmentPair,
49
+ } from '../types/types';
50
+ import {
51
+ colorMaskToTyped,
52
+ createColorMask,
53
+ createGlareOptions,
54
+ } from '../types/typeUtils';
55
+ import type { V2d, V3d } from '../types/vector';
56
+ import {
57
+ addV3d,
58
+ clampV3d,
59
+ componentsFromV3d,
60
+ degToRad,
61
+ divV3d,
62
+ negateV2dY,
63
+ rotateV2d,
64
+ scaleV2d,
65
+ scaleV3d,
66
+ subtractV3d,
67
+ transformV2d,
68
+ zeroV3d,
69
+ } from '../utils/vector';
70
+
71
+ export interface SharedProps {
72
+ width: number;
73
+ height: number;
74
+ glareOptions?: Partial<GlareOptions>;
75
+ colorMaskOptions?: DeepPartiallyOptional<ColorMask, 'baseColor'>;
76
+ useTouchControl?: boolean;
77
+ touchPosition?: SharedValue<V2d>;
78
+ addTextureMask?: boolean;
79
+ addReverseHolo?: boolean;
80
+ addHolo?: boolean;
81
+ }
82
+
83
+ interface ContentProps extends SharedProps {
84
+ root: TgpuRoot;
85
+ imageTexture: TgpuTexture<TextureProps>;
86
+ maskTexture?: TgpuTexture<TextureProps>;
87
+ }
88
+
89
+ interface PipelineMap {
90
+ glare: TgpuRenderPipeline;
91
+ colorMask: TgpuRenderPipeline;
92
+ mask: TgpuRenderPipeline | void;
93
+ reverseHolo: TgpuRenderPipeline | void;
94
+ holo: TgpuRenderPipeline | void;
95
+ }
96
+
97
+ export default function Content({
98
+ addHolo,
99
+ addReverseHolo,
100
+ addTextureMask,
101
+ colorMaskOptions,
102
+ glareOptions,
103
+ height,
104
+ imageTexture,
105
+ maskTexture,
106
+ root,
107
+ touchPosition,
108
+ useTouchControl,
109
+ width,
110
+ }: ContentProps) {
111
+ const { device } = root;
112
+ const { ref, context } = useGPUContext();
113
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
114
+ const renderRef = useRef<() => void>(null);
115
+
116
+ //changing canvas size to prevent blur
117
+ const pixelRatio = PixelRatio.get();
118
+ const size = { x: width, y: height };
119
+ const pixelSize = transformV2d(scaleV2d(size, pixelRatio), (v) =>
120
+ Math.max(1, Math.round(v))
121
+ );
122
+
123
+ const landscape = useSharedValue<boolean>(false);
124
+ const rotation = useSharedValue<V3d>(zeroV3d); // final GPU offsets
125
+
126
+ // Calibration shared values (UI thread)
127
+ const initialGravity = useSharedValue<V3d>(zeroV3d);
128
+ const calibSum = useSharedValue<V3d>(zeroV3d);
129
+ const calibCount = useSharedValue<number>(0);
130
+ const calibrated = useSharedValue<boolean>(false);
131
+ const gravitySensor = useAnimatedSensor(SensorType.GRAVITY, { interval: 20 });
132
+
133
+ const bufferMap = useMemo(
134
+ () => new TypedBufferMap<BufferData>(bufferData),
135
+ []
136
+ );
137
+
138
+ //TODO: add once again, when the wgpu issues are fixed :3
139
+
140
+ const animatedStyle = useAnimatedStyle(() => {
141
+ const rotX = rotation.value.x * 10;
142
+ const rotY = rotation.value.y * 10;
143
+
144
+ return {
145
+ transform: [
146
+ { perspective: 300 },
147
+ { rotateX: `${-rotY}deg` },
148
+ { rotateY: `${rotX}deg` },
149
+ // { rotateZ: `${rotX * 5}deg` },
150
+ ],
151
+ };
152
+ });
153
+ // Subscribe to orientation changes and reset calibration on change
154
+ useEffect(
155
+ () =>
156
+ subscribeToOrientationChange((isLandscape) => {
157
+ landscape.value = isLandscape;
158
+ }),
159
+ [landscape]
160
+ );
161
+
162
+ // Calibration & mapping logic
163
+ useDerivedValue(() => {
164
+ 'worklet';
165
+
166
+ if (useTouchControl) {
167
+ rotation.value = touchPosition
168
+ ? { x: touchPosition.value.x, y: touchPosition.value.y, z: 0 }
169
+ : zeroV3d;
170
+
171
+ return;
172
+ }
173
+
174
+ const g = gravitySensor.sensor.value;
175
+ const CALIBRATION_SAMPLES = 40;
176
+ const alpha = 0.15; // smoothing
177
+ const scale = 0.6;
178
+
179
+ if (!calibrated.value) {
180
+ // accumulate baseline in device coordinates
181
+ calibSum.value = addV3d(calibSum.value, g);
182
+
183
+ if (++calibCount.value >= CALIBRATION_SAMPLES) {
184
+ initialGravity.value = divV3d(calibSum.value, calibCount.value);
185
+ calibrated.value = true;
186
+ }
187
+
188
+ rotation.value = zeroV3d;
189
+ return;
190
+ }
191
+
192
+ const init = initialGravity.value;
193
+ const dg = subtractV3d(g, init);
194
+
195
+ // Rotate into screen coordinates so offsets auto-swap with orientation
196
+ const m = rotateV2d(dg, degToRad(-90 * Number(landscape.value)));
197
+ const screen = negateV2dY(m);
198
+ const smoothOffset = { ...scaleV2d(screen, alpha), z: dg.z * alpha };
199
+ const smooth = scaleV3d(
200
+ addV3d(scaleV3d(rotation.value, 1 - alpha), smoothOffset),
201
+ scale
202
+ );
203
+
204
+ rotation.value = clampV3d(
205
+ landscape.value
206
+ ? {
207
+ x: smooth.y,
208
+ y: -smooth.x,
209
+ z: smooth.z,
210
+ }
211
+ : smooth,
212
+ -1,
213
+ 1
214
+ );
215
+ });
216
+ // Render loop
217
+ useEffect(() => {
218
+ if (!context) return;
219
+
220
+ //this sets the underlying resolution of the canvas to prevent blurriness
221
+ const canvasElement = context.canvas;
222
+
223
+ if (
224
+ canvasElement.width !== pixelSize.x &&
225
+ canvasElement.height !== pixelSize.y
226
+ ) {
227
+ canvasElement.width = pixelSize.x;
228
+ canvasElement.height = pixelSize.y;
229
+ }
230
+
231
+ context.configure({
232
+ device,
233
+ format: presentationFormat,
234
+ alphaMode: 'premultiplied',
235
+ });
236
+
237
+ const sampler = device.createSampler({
238
+ magFilter: 'linear',
239
+ minFilter: 'linear',
240
+ });
241
+
242
+ const imageTextureBindGroup = root.createBindGroup(textureBindGroupLayout, {
243
+ texture: root.unwrap(imageTexture).createView(),
244
+ sampler,
245
+ });
246
+
247
+ const rotationBuffer = bufferMap.addBuffer(root, 'rotation', d.vec3f(0.0));
248
+
249
+ const rotationBindGroup = createRotationValuesBindGroup(
250
+ root,
251
+ rotationBuffer
252
+ );
253
+
254
+ const glareBuffer = bufferMap.addBuffer(
255
+ root,
256
+ 'glare',
257
+ createGlareOptions(glareOptions ?? {})
258
+ );
259
+ const glareBindGroup = createGlareBindGroup(root, glareBuffer);
260
+
261
+ const colorMaskBuffer = bufferMap.addBuffer(
262
+ root,
263
+ 'colorMask',
264
+ colorMaskToTyped(
265
+ createColorMask(colorMaskOptions ?? { baseColor: [-20, -20, -20] })
266
+ )
267
+ );
268
+ const colorMaskBindGroup = createColorMaskBindGroup(root, colorMaskBuffer);
269
+
270
+ const pipelineMap: PipelineMap = {
271
+ glare: attachBindGroups(
272
+ root['~unstable']
273
+ .withVertex(mainVertex, {})
274
+ .withFragment(newGlareFragment, getDefaultTarget(presentationFormat))
275
+ .createPipeline(),
276
+ [
277
+ imageTextureBindGroup,
278
+ rotationBindGroup,
279
+ glareBindGroup,
280
+ colorMaskBindGroup,
281
+ ]
282
+ ),
283
+ colorMask: attachBindGroups(
284
+ root['~unstable']
285
+ .withVertex(mainVertex, {})
286
+ .withFragment(
287
+ colorMaskFragment,
288
+ getDefaultTarget(presentationFormat, blend)
289
+ )
290
+ .createPipeline(),
291
+ [imageTextureBindGroup, colorMaskBindGroup, rotationBindGroup]
292
+ ),
293
+ mask: createMaskPipeline(
294
+ root,
295
+ maskTexture,
296
+ [imageTextureBindGroup, rotationBindGroup],
297
+ sampler,
298
+ presentationFormat
299
+ ),
300
+ reverseHolo: createReverseHoloPipeline(
301
+ root,
302
+ maskTexture,
303
+ [imageTextureBindGroup, rotationBindGroup, glareBindGroup],
304
+ sampler,
305
+ presentationFormat
306
+ ),
307
+ holo: createHoloPipeline(
308
+ root,
309
+ imageTexture,
310
+ [rotationBindGroup],
311
+ sampler,
312
+ presentationFormat
313
+ ),
314
+ };
315
+
316
+ const modifyBuffers = () => {
317
+ rotationBuffer.write(d.vec3f(...componentsFromV3d(rotation.value)));
318
+ };
319
+
320
+ const renderPipelines = () => {
321
+ const view = context.getCurrentTexture().createView();
322
+ const initialAttachment: ColorAttachment = {
323
+ view,
324
+ clearValue: [0, 0, 0, 0],
325
+ loadOp: 'clear',
326
+ storeOp: 'store',
327
+ };
328
+ const loadingAttachment: ColorAttachment = {
329
+ view,
330
+ clearValue: [0, 0, 0, 0],
331
+ loadOp: 'load',
332
+ storeOp: 'store',
333
+ };
334
+
335
+ const { glare, mask, colorMask, holo, reverseHolo } = pipelineMap;
336
+
337
+ const pairs: PipelineAttachmentPair[] = [[glare, initialAttachment]];
338
+
339
+ if (addTextureMask && mask) pairs.push([mask, loadingAttachment]);
340
+ if (addReverseHolo && reverseHolo)
341
+ pairs.push([reverseHolo, loadingAttachment]);
342
+ if (addHolo && holo) pairs.push([holo, loadingAttachment]);
343
+ pairs.push([colorMask, loadingAttachment]);
344
+
345
+ pairs.forEach(([pipeline, attachment]) =>
346
+ pipeline.withColorAttachment(attachment).draw(6)
347
+ );
348
+ };
349
+
350
+ const presentContext = () => context.present();
351
+
352
+ renderRef.current = () => {
353
+ modifyBuffers();
354
+ renderPipelines();
355
+ presentContext();
356
+ };
357
+ }, [
358
+ device,
359
+ context,
360
+ root,
361
+ presentationFormat,
362
+ imageTexture,
363
+ maskTexture,
364
+ rotation,
365
+ bufferMap,
366
+ glareOptions,
367
+ colorMaskOptions,
368
+ addHolo,
369
+ addReverseHolo,
370
+ addTextureMask,
371
+ pixelSize,
372
+ ]);
373
+
374
+ useAnimationFrame(() => renderRef.current?.());
375
+
376
+ return (
377
+ <Animated.View style={[animatedStyle]}>
378
+ <View
379
+ style={
380
+ [
381
+ // styles.container,
382
+ // { width, height },
383
+ ]
384
+ }
385
+ >
386
+ <Canvas
387
+ ref={ref}
388
+ style={[
389
+ { width, height },
390
+ // aspectRatio: pixelWidth / pixelHeight,
391
+ // { transform: [{ scaleX: 1 / dpr }, { scaleY: 1 / dpr }] },
392
+ ]}
393
+ transparent={Platform.OS === 'ios'}
394
+ // transparent={true}
395
+ />
396
+ </View>
397
+ </Animated.View>
398
+ );
399
+ }
400
+
401
+ // const styles = StyleSheet.create({
402
+ // container: { overflow: 'hidden' },
403
+ // });
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useDevice } from 'react-native-wgpu';
3
+ import type { TgpuTexture } from 'typegpu';
4
+ import { getOrInitRoot } from '../roots';
5
+ import { loadBitmap } from '../shaders/resourceManagement/textures';
6
+ import Content, { type SharedProps } from './Content';
7
+
8
+ export interface ShineProps extends SharedProps {
9
+ imageURI: string;
10
+ maskURI?: string;
11
+ }
12
+
13
+ export function Shine({ imageURI, maskURI, ...props }: ShineProps) {
14
+ const { device } = useDevice();
15
+ const root = device && getOrInitRoot(device);
16
+ const [imageTexture, setImageTexture] = useState<TgpuTexture>();
17
+ const [maskTexture, setMaskTexture] = useState<TgpuTexture>();
18
+
19
+ useEffect(() => {
20
+ if (root) loadBitmap(root, imageURI, setImageTexture);
21
+ }, [root, imageURI]);
22
+
23
+ useEffect(() => {
24
+ if (root && maskURI) loadBitmap(root, maskURI, setMaskTexture);
25
+ }, [root, imageURI, maskURI]);
26
+
27
+ return (
28
+ root &&
29
+ imageTexture && (
30
+ <Content
31
+ {...props}
32
+ root={root}
33
+ imageTexture={imageTexture}
34
+ maskTexture={maskTexture}
35
+ />
36
+ )
37
+ );
38
+ }
@@ -0,0 +1,100 @@
1
+ import { type PropsWithChildren, 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 type { V2d } from '../types/vector';
11
+ import { sizeFromV2d, sizeToV2d } from '../utils/size';
12
+ import { areV2dEqual, multiplyV2d, round2D } from '../utils/vector';
13
+ import { Shine, type ShineProps } from './Shine';
14
+
15
+ type ShineGroupProps = PropsWithChildren<Partial<ShineProps>>;
16
+
17
+ export function ShineGroup({
18
+ children,
19
+ glareOptions,
20
+ colorMaskOptions,
21
+ maskURI,
22
+ touchPosition,
23
+ useTouchControl = false,
24
+ addTextureMask = false,
25
+ addHolo = false,
26
+ addReverseHolo = false,
27
+ }: ShineGroupProps) {
28
+ const viewShotRef = useRef<ViewShot>(null);
29
+ const [capturedURI, setCapturedURI] = useState<string | null>(null);
30
+ const [size, setSize] = useState<V2d | null>(null);
31
+
32
+ const onInnerLayout = (e: LayoutChangeEvent) => {
33
+ const layoutV2d = sizeToV2d(e.nativeEvent.layout);
34
+
35
+ if (!size || !areV2dEqual(size, layoutV2d)) {
36
+ setSize(layoutV2d);
37
+ }
38
+ };
39
+
40
+ // When we have a valid measured size, take a snapshot (after a short tick)
41
+ // Short timeout helps when children include images that finish layout a few ms later
42
+ useEffect(() => {
43
+ if (!viewShotRef.current || !size) return;
44
+
45
+ let mounted = true;
46
+ const t = setTimeout(async () => {
47
+ try {
48
+ const pixel = round2D(multiplyV2d(size, PixelRatio.get()));
49
+
50
+ const uri = await captureRef(viewShotRef, {
51
+ format: 'png',
52
+ quality: 1,
53
+ ...sizeFromV2d(pixel),
54
+ });
55
+ if (mounted) setCapturedURI(uri);
56
+ } catch (err) {
57
+ console.warn('ShineGroup capture failed', err);
58
+ }
59
+ }, 50);
60
+
61
+ return () => {
62
+ mounted = false;
63
+ clearTimeout(t);
64
+ };
65
+ }, [size, children]);
66
+
67
+ return (
68
+ <View style={styles.container}>
69
+ <ViewShot ref={viewShotRef} options={{ format: 'png', quality: 1 }}>
70
+ <View onLayout={onInnerLayout} style={styles.inner}>
71
+ {children}
72
+ </View>
73
+ </ViewShot>
74
+
75
+ {capturedURI && size && (
76
+ <Image src={capturedURI} {...sizeFromV2d(size)} />
77
+ )}
78
+
79
+ {capturedURI && size && (
80
+ <Shine
81
+ {...sizeFromV2d(size)}
82
+ imageURI={capturedURI}
83
+ glareOptions={glareOptions}
84
+ colorMaskOptions={colorMaskOptions}
85
+ maskURI={maskURI}
86
+ touchPosition={touchPosition}
87
+ useTouchControl={useTouchControl}
88
+ addTextureMask={addTextureMask}
89
+ addReverseHolo={addReverseHolo}
90
+ addHolo={addHolo}
91
+ />
92
+ )}
93
+ </View>
94
+ );
95
+ }
96
+
97
+ const styles = StyleSheet.create({
98
+ container: { display: 'flex', flexDirection: 'column', gap: 10 },
99
+ inner: {},
100
+ });
@@ -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,21 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+
3
+ export default function useAnimationFrame(cb: () => void) {
4
+ const requestId = useRef<number>(null);
5
+
6
+ const onFrame = useCallback(
7
+ function () {
8
+ cb();
9
+ requestId.current = requestAnimationFrame(onFrame);
10
+ },
11
+ [cb]
12
+ );
13
+
14
+ useEffect(() => {
15
+ requestId.current = requestAnimationFrame(onFrame);
16
+
17
+ return () => {
18
+ requestId.current === null || cancelAnimationFrame(requestId.current);
19
+ };
20
+ }, [onFrame]);
21
+ }
@@ -0,0 +1,18 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { subscribeToOrientationChange } from '../shaders/utils';
3
+
4
+ type Orientation = 'LANDSCAPE' | 'PORTRAIT';
5
+
6
+ export const useOrientation = () => {
7
+ const [orientation, setOrientation] = useState<Orientation>();
8
+
9
+ useEffect(
10
+ () =>
11
+ subscribeToOrientationChange((isLandscape) =>
12
+ setOrientation(isLandscape ? 'LANDSCAPE' : 'PORTRAIT')
13
+ ),
14
+ []
15
+ );
16
+
17
+ return orientation;
18
+ };