react-native-shine 0.2.1 → 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.
- package/README.md +1 -1
- package/lib/module/components/Shine.js +269 -0
- package/lib/module/components/Shine.js.map +1 -0
- package/lib/module/components/ShineGroup.js +104 -0
- package/lib/module/components/ShineGroup.js.map +1 -0
- package/lib/module/enums/waveCallback.js +19 -0
- package/lib/module/enums/waveCallback.js.map +1 -0
- package/lib/module/hooks/useOrientation.js +16 -0
- package/lib/module/hooks/useOrientation.js.map +1 -0
- package/lib/module/index.js +5 -219
- package/lib/module/index.js.map +1 -1
- package/lib/module/shaders/bindGroupLayouts.js +40 -5
- package/lib/module/shaders/bindGroupLayouts.js.map +1 -1
- package/lib/module/shaders/bindGroupUtils.js +27 -12
- package/lib/module/shaders/bindGroupUtils.js.map +1 -1
- package/lib/module/shaders/fragmentShaders/colorMaskFragment.js +2 -2
- package/lib/module/shaders/fragmentShaders/colorMaskFragment.js.map +1 -1
- package/lib/module/shaders/fragmentShaders/glareFragment.js +114 -0
- package/lib/module/shaders/fragmentShaders/glareFragment.js.map +1 -0
- package/lib/module/shaders/fragmentShaders/holoFragment.js +33 -0
- package/lib/module/shaders/fragmentShaders/holoFragment.js.map +1 -0
- package/lib/module/shaders/fragmentShaders/maskFragment.js +20 -0
- package/lib/module/shaders/fragmentShaders/maskFragment.js.map +1 -0
- package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js +46 -0
- package/lib/module/shaders/fragmentShaders/reverseHoloFragment.js.map +1 -0
- package/lib/module/shaders/pipelineSetups.js +82 -13
- package/lib/module/shaders/pipelineSetups.js.map +1 -1
- package/lib/module/shaders/{resourceManagement.js → resourceManagement/bitmaps.js} +2 -1
- package/lib/module/shaders/resourceManagement/bitmaps.js.map +1 -0
- package/lib/module/shaders/resourceManagement/bufferManager.js +46 -0
- package/lib/module/shaders/resourceManagement/bufferManager.js.map +1 -0
- package/lib/module/shaders/resourceManagement/textures.js +17 -0
- package/lib/module/shaders/resourceManagement/textures.js.map +1 -0
- package/lib/module/shaders/tgpuUtils.js +19 -1
- package/lib/module/shaders/tgpuUtils.js.map +1 -1
- package/lib/module/shaders/utils.js +0 -14
- package/lib/module/shaders/utils.js.map +1 -1
- package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js +47 -0
- package/lib/module/shaders/vertexShaders/mainRotationEffectVertex.js.map +1 -0
- package/lib/module/types/typeUtils.js +17 -5
- package/lib/module/types/typeUtils.js.map +1 -1
- package/lib/typescript/src/components/Shine.d.ts +17 -0
- package/lib/typescript/src/components/Shine.d.ts.map +1 -0
- package/lib/typescript/src/components/ShineGroup.d.ts +8 -0
- package/lib/typescript/src/components/ShineGroup.d.ts.map +1 -0
- package/lib/typescript/src/enums/waveCallback.d.ts +9 -0
- package/lib/typescript/src/enums/waveCallback.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useOrientation.d.ts +2 -0
- package/lib/typescript/src/hooks/useOrientation.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -11
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +44 -6
- package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -1
- package/lib/typescript/src/shaders/bindGroupUtils.d.ts +8 -8
- package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -1
- package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts +8 -0
- package/lib/typescript/src/shaders/fragmentShaders/glareFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts +5 -0
- package/lib/typescript/src/shaders/fragmentShaders/holoFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts +6 -0
- package/lib/typescript/src/shaders/fragmentShaders/maskFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts +5 -0
- package/lib/typescript/src/shaders/fragmentShaders/reverseHoloFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/pipelineSetups.d.ts +9 -4
- package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -1
- package/lib/typescript/src/shaders/{resourceManagement.d.ts → resourceManagement/bitmaps.d.ts} +1 -1
- package/lib/typescript/src/shaders/resourceManagement/bitmaps.d.ts.map +1 -0
- package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts +28 -0
- package/lib/typescript/src/shaders/resourceManagement/bufferManager.d.ts.map +1 -0
- package/lib/typescript/src/shaders/resourceManagement/textures.d.ts +7 -0
- package/lib/typescript/src/shaders/resourceManagement/textures.d.ts.map +1 -0
- package/lib/typescript/src/shaders/tgpuUtils.d.ts +5 -1
- package/lib/typescript/src/shaders/tgpuUtils.d.ts.map +1 -1
- package/lib/typescript/src/shaders/utils.d.ts +1 -7
- package/lib/typescript/src/shaders/utils.d.ts.map +1 -1
- package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts +6 -0
- package/lib/typescript/src/shaders/vertexShaders/mainRotationEffectVertex.d.ts.map +1 -0
- package/lib/typescript/src/types/typeUtils.d.ts +3 -2
- package/lib/typescript/src/types/typeUtils.d.ts.map +1 -1
- package/lib/typescript/src/types/types.d.ts +7 -2
- package/lib/typescript/src/types/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/scripts/install-peers.js +21 -0
- package/scripts/postinstall.js +18 -0
- package/src/components/Shine.tsx +480 -0
- package/src/components/ShineGroup.tsx +107 -0
- package/src/enums/waveCallback.ts +22 -0
- package/src/hooks/useOrientation.ts +20 -0
- package/src/index.tsx +6 -322
- package/src/shaders/bindGroupLayouts.ts +43 -6
- package/src/shaders/bindGroupUtils.ts +34 -19
- package/src/shaders/fragmentShaders/colorMaskFragment.ts +2 -2
- package/src/shaders/fragmentShaders/glareFragment.ts +142 -0
- package/src/shaders/fragmentShaders/holoFragment.ts +43 -0
- package/src/shaders/fragmentShaders/maskFragment.ts +31 -0
- package/src/shaders/fragmentShaders/reverseHoloFragment.ts +71 -0
- package/src/shaders/pipelineSetups.ts +161 -14
- package/src/shaders/{resourceManagement.ts → resourceManagement/bitmaps.ts} +1 -0
- package/src/shaders/resourceManagement/bufferManager.ts +82 -0
- package/src/shaders/resourceManagement/textures.ts +30 -0
- package/src/shaders/tgpuUtils.ts +36 -1
- package/src/shaders/utils.ts +0 -30
- package/src/shaders/vertexShaders/mainRotationEffectVertex.ts +76 -0
- package/src/types/typeUtils.ts +22 -8
- package/src/types/types.ts +8 -2
- package/lib/module/shaders/fragmentShaders/bloomFragment.js +0 -66
- package/lib/module/shaders/fragmentShaders/bloomFragment.js.map +0 -1
- package/lib/module/shaders/resourceManagement.js.map +0 -1
- package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts +0 -6
- package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts.map +0 -1
- package/lib/typescript/src/shaders/resourceManagement.d.ts.map +0 -1
- 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
|
+
};
|