react-native-shine 0.0.0 → 0.2.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 (70) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +159 -0
  3. package/lib/module/index.js +222 -0
  4. package/lib/module/index.js.map +1 -0
  5. package/lib/module/package.json +1 -0
  6. package/lib/module/roots.js +16 -0
  7. package/lib/module/roots.js.map +1 -0
  8. package/lib/module/shaders/bindGroupLayouts.js +45 -0
  9. package/lib/module/shaders/bindGroupLayouts.js.map +1 -0
  10. package/lib/module/shaders/bindGroupUtils.js +45 -0
  11. package/lib/module/shaders/bindGroupUtils.js.map +1 -0
  12. package/lib/module/shaders/fragmentShaders/bloomFragment.js +66 -0
  13. package/lib/module/shaders/fragmentShaders/bloomFragment.js.map +1 -0
  14. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js +28 -0
  15. package/lib/module/shaders/fragmentShaders/colorMaskFragment.js.map +1 -0
  16. package/lib/module/shaders/pipelineSetups.js +32 -0
  17. package/lib/module/shaders/pipelineSetups.js.map +1 -0
  18. package/lib/module/shaders/resourceManagement.js +19 -0
  19. package/lib/module/shaders/resourceManagement.js.map +1 -0
  20. package/lib/module/shaders/tgpuUtils.js +38 -0
  21. package/lib/module/shaders/tgpuUtils.js.map +1 -0
  22. package/lib/module/shaders/utils.js +65 -0
  23. package/lib/module/shaders/utils.js.map +1 -0
  24. package/lib/module/shaders/vertexShaders/mainVertex.js +35 -0
  25. package/lib/module/shaders/vertexShaders/mainVertex.js.map +1 -0
  26. package/lib/module/types/typeUtils.js +92 -0
  27. package/lib/module/types/typeUtils.js.map +1 -0
  28. package/lib/module/types/types.js +4 -0
  29. package/lib/module/types/types.js.map +1 -0
  30. package/lib/typescript/package.json +1 -0
  31. package/lib/typescript/src/index.d.ts +12 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -0
  33. package/lib/typescript/src/roots.d.ts +4 -0
  34. package/lib/typescript/src/roots.d.ts.map +1 -0
  35. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +57 -0
  36. package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -0
  37. package/lib/typescript/src/shaders/bindGroupUtils.d.ts +53 -0
  38. package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -0
  39. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts +6 -0
  40. package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts.map +1 -0
  41. package/lib/typescript/src/shaders/fragmentShaders/colorMaskFragment.d.ts +6 -0
  42. package/lib/typescript/src/shaders/fragmentShaders/colorMaskFragment.d.ts.map +1 -0
  43. package/lib/typescript/src/shaders/pipelineSetups.d.ts +8 -0
  44. package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -0
  45. package/lib/typescript/src/shaders/resourceManagement.d.ts +3 -0
  46. package/lib/typescript/src/shaders/resourceManagement.d.ts.map +1 -0
  47. package/lib/typescript/src/shaders/tgpuUtils.d.ts +6 -0
  48. package/lib/typescript/src/shaders/tgpuUtils.d.ts.map +1 -0
  49. package/lib/typescript/src/shaders/utils.d.ts +13 -0
  50. package/lib/typescript/src/shaders/utils.d.ts.map +1 -0
  51. package/lib/typescript/src/shaders/vertexShaders/mainVertex.d.ts +6 -0
  52. package/lib/typescript/src/shaders/vertexShaders/mainVertex.d.ts.map +1 -0
  53. package/lib/typescript/src/types/typeUtils.d.ts +17 -0
  54. package/lib/typescript/src/types/typeUtils.d.ts.map +1 -0
  55. package/lib/typescript/src/types/types.d.ts +33 -0
  56. package/lib/typescript/src/types/types.d.ts.map +1 -0
  57. package/package.json +160 -13
  58. package/src/index.tsx +326 -0
  59. package/src/roots.ts +16 -0
  60. package/src/shaders/bindGroupLayouts.ts +40 -0
  61. package/src/shaders/bindGroupUtils.ts +101 -0
  62. package/src/shaders/fragmentShaders/bloomFragment.ts +83 -0
  63. package/src/shaders/fragmentShaders/colorMaskFragment.ts +35 -0
  64. package/src/shaders/pipelineSetups.ts +44 -0
  65. package/src/shaders/resourceManagement.ts +22 -0
  66. package/src/shaders/tgpuUtils.ts +75 -0
  67. package/src/shaders/utils.ts +103 -0
  68. package/src/shaders/vertexShaders/mainVertex.ts +35 -0
  69. package/src/types/typeUtils.ts +122 -0
  70. package/src/types/types.ts +51 -0
@@ -0,0 +1,33 @@
1
+ import type { TgpuBindGroup, TgpuBindGroupLayout } from 'typegpu';
2
+ export type vec2 = [number, number];
3
+ export type vec3 = [number, number, number];
4
+ export type vec4 = [number, number, number, number];
5
+ export type quaternion = vec4;
6
+ export type BloomOptions = {
7
+ glowPower: number;
8
+ hueShiftAngleMax: number;
9
+ hueShiftAngleMin: number;
10
+ hueBlendPower: number;
11
+ lightIntensity: number;
12
+ bloomIntensity: number;
13
+ };
14
+ export type ColorMask = {
15
+ baseColor: vec3;
16
+ rgbToleranceRange: {
17
+ upper: vec3;
18
+ lower: vec3;
19
+ };
20
+ };
21
+ export type PartiallyOptional<T, K extends keyof T> = {
22
+ [P in K]: T[P];
23
+ } & Partial<Omit<T, K>>;
24
+ export type Primitive = string | number | boolean | bigint | symbol | null | undefined;
25
+ export type DeepPartial<T> = {
26
+ [P in keyof T]?: T[P] extends Primitive | any[] ? T[P] : DeepPartial<T[P]>;
27
+ };
28
+ export type DeepPartiallyOptional<T, K extends keyof T> = Required<Pick<T, K>> & DeepPartial<Omit<T, K>>;
29
+ export type BindGroupPair = {
30
+ layout: TgpuBindGroupLayout;
31
+ group: TgpuBindGroup;
32
+ };
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAElE,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACpC,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAC5C,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AACpD,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC;AAE9B,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,iBAAiB,EAAE;QACjB,KAAK,EAAE,IAAI,CAAC;QACZ,KAAK,EAAE,IAAI,CAAC;KACb,CAAC;CACH,CAAC;AAGF,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI;KACnD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAExB,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,IAAI,GACJ,SAAS,CAAC;AAId,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3E,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAC5E,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAE1B,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC"}
package/package.json CHANGED
@@ -1,24 +1,171 @@
1
1
  {
2
2
  "name": "react-native-shine",
3
- "version": "0.0.0",
4
- "description": "Add beautiful shine to your components",
5
- "main": "index.js",
3
+ "version": "0.2.1",
4
+ "description": "Fast and efficient way of adding interactive effects that are run entirely on shaders using typeGPU.",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
6
34
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
35
+ "example": "yarn workspace react-native-shine-example",
36
+ "test": "jest",
37
+ "typecheck": "tsc",
38
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
39
+ "clean": "del-cli lib",
40
+ "prepare": "bob build",
41
+ "release": "release-it --only-version",
42
+ "postinstall": "node ./scripts/postinstall.js",
43
+ "install-peers": "node ./scripts/install-peers.js"
8
44
  },
45
+ "keywords": [
46
+ "react-native",
47
+ "ios",
48
+ "android"
49
+ ],
9
50
  "repository": {
10
51
  "type": "git",
11
- "url": "git+https://github.com/wojtus7/react-native-shine.git"
52
+ "url": "git+https://github.com/wojtus7/react-native-shine.git.git"
12
53
  },
13
- "keywords": [
14
- "glare",
15
- "share",
16
- "react-native"
17
- ],
18
- "author": "Wojciech Stanisz",
54
+ "author": "VoidFrog <shadownn0000@gmail.com> (https://github.com/VoidFrog)",
19
55
  "license": "MIT",
20
56
  "bugs": {
21
- "url": "https://github.com/wojtus7/react-native-shine/issues"
57
+ "url": "https://github.com/wojtus7/react-native-shine.git/issues"
58
+ },
59
+ "homepage": "https://github.com/wojtus7/react-native-shine.git#readme",
60
+ "publishConfig": {
61
+ "registry": "https://registry.npmjs.org/"
62
+ },
63
+ "devDependencies": {
64
+ "@commitlint/config-conventional": "^19.6.0",
65
+ "@eslint/compat": "^1.2.7",
66
+ "@eslint/eslintrc": "^3.3.0",
67
+ "@eslint/js": "^9.22.0",
68
+ "@evilmartians/lefthook": "^1.5.0",
69
+ "@react-native/babel-preset": "0.78.2",
70
+ "@react-native/eslint-config": "^0.78.0",
71
+ "@release-it/conventional-changelog": "^9.0.2",
72
+ "@types/jest": "^29.5.5",
73
+ "@types/react": "^19.0.12",
74
+ "@webgpu/types": "^0.1.64",
75
+ "commitlint": "^19.6.1",
76
+ "del-cli": "^5.1.0",
77
+ "eslint": "^9.22.0",
78
+ "eslint-config-prettier": "^10.1.1",
79
+ "eslint-plugin-prettier": "^5.2.3",
80
+ "jest": "^29.7.0",
81
+ "prettier": "^3.0.3",
82
+ "react": "19.0.0",
83
+ "react-native": "0.79.5",
84
+ "react-native-builder-bob": "^0.40.13",
85
+ "react-native-reanimated": "^4.0.1",
86
+ "react-native-wgpu": "^0.2.1",
87
+ "react-native-worklets": "^0.4.1",
88
+ "release-it": "^17.10.0",
89
+ "typegpu": "^0.7.0",
90
+ "typescript": "^5.8.3",
91
+ "unplugin-typegpu": "^0.2.2"
92
+ },
93
+ "peerDependencies": {
94
+ "react": "*",
95
+ "react-native": "*",
96
+ "react-native-reanimated": "^4.0.1",
97
+ "react-native-wgpu": "^0.2.0",
98
+ "react-native-worklets": "^0.4.1",
99
+ "typegpu": "^0.7.0"
100
+ },
101
+ "workspaces": [
102
+ "example"
103
+ ],
104
+ "packageManager": "yarn@3.6.1",
105
+ "jest": {
106
+ "preset": "react-native",
107
+ "modulePathIgnorePatterns": [
108
+ "<rootDir>/example/node_modules",
109
+ "<rootDir>/lib/"
110
+ ]
111
+ },
112
+ "commitlint": {
113
+ "extends": [
114
+ "@commitlint/config-conventional"
115
+ ]
116
+ },
117
+ "release-it": {
118
+ "git": {
119
+ "commitMessage": "chore: release ${version}",
120
+ "tagName": "v${version}"
121
+ },
122
+ "npm": {
123
+ "publish": true
124
+ },
125
+ "github": {
126
+ "release": true
127
+ },
128
+ "plugins": {
129
+ "@release-it/conventional-changelog": {
130
+ "preset": {
131
+ "name": "angular"
132
+ }
133
+ }
134
+ }
135
+ },
136
+ "prettier": {
137
+ "quoteProps": "consistent",
138
+ "singleQuote": true,
139
+ "tabWidth": 2,
140
+ "trailingComma": "es5",
141
+ "useTabs": false
142
+ },
143
+ "react-native-builder-bob": {
144
+ "source": "src",
145
+ "output": "lib",
146
+ "targets": [
147
+ [
148
+ "module",
149
+ {
150
+ "esm": true
151
+ }
152
+ ],
153
+ [
154
+ "typescript",
155
+ {
156
+ "project": "tsconfig.build.json"
157
+ }
158
+ ]
159
+ ]
160
+ },
161
+ "create-react-native-library": {
162
+ "languages": "js",
163
+ "type": "library",
164
+ "version": "0.52.0"
22
165
  },
23
- "homepage": "https://github.com/wojtus7/react-native-shine#readme"
166
+ "dependencies": {
167
+ "expo-asset": "^11.1.7",
168
+ "react-native-orientation-locker": "^1.7.0",
169
+ "unplugin-typegpu": "^0.2.2"
170
+ }
24
171
  }
package/src/index.tsx ADDED
@@ -0,0 +1,326 @@
1
+ import { useEffect, 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';
6
+ import {
7
+ createTexture,
8
+ loadTexture,
9
+ clamp,
10
+ rotate2D,
11
+ subscribeToOrientationChange,
12
+ getAngleFromDimensions,
13
+ } from './shaders/utils';
14
+ import type { TgpuTexture } from 'typegpu';
15
+ import {
16
+ bloomOptionsBindGroupLayout,
17
+ colorMaskBindGroupLayout,
18
+ rotationValuesBindGroupLayout,
19
+ textureBindGroupLayout,
20
+ } from './shaders/bindGroupLayouts';
21
+ import {
22
+ SensorType,
23
+ useAnimatedSensor,
24
+ useDerivedValue,
25
+ useSharedValue,
26
+ } from 'react-native-reanimated';
27
+ import * as d from 'typegpu/data';
28
+ import { Platform } from 'react-native';
29
+ import bloomFragment from './shaders/fragmentShaders/bloomFragment';
30
+ import {
31
+ createBloomOptionsBindGroup,
32
+ createBloomOptionsBuffer,
33
+ createColorMaskBindGroup,
34
+ createColorMaskBuffer,
35
+ createRotationBuffer,
36
+ createRotationValuesBindGroup,
37
+ } from './shaders/bindGroupUtils';
38
+ import {
39
+ createBindGroupPairs,
40
+ createBloomOptions,
41
+ createColorMask,
42
+ } from './types/typeUtils';
43
+ import type {
44
+ BindGroupPair,
45
+ BloomOptions,
46
+ ColorMask,
47
+ DeepPartiallyOptional,
48
+ } from './types/types';
49
+ import { attachBindGroups, getDefaultTarget } from './shaders/pipelineSetups';
50
+ import colorMaskFragment from './shaders/fragmentShaders/colorMaskFragment';
51
+ interface ShineProps {
52
+ width: number;
53
+ height: number;
54
+ imageURI: string;
55
+ bloomOptions?: Partial<BloomOptions>;
56
+ colorMaskOptions?: DeepPartiallyOptional<ColorMask, 'baseColor'>;
57
+ }
58
+
59
+ export function Shine({
60
+ width,
61
+ height,
62
+ imageURI,
63
+ bloomOptions,
64
+ colorMaskOptions,
65
+ }: ShineProps) {
66
+ const { device = null } = useDevice();
67
+ const root = device ? getOrInitRoot(device) : null;
68
+ const { ref, context } = useGPUContext();
69
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
70
+ const frameRef = useRef<number | null>(null);
71
+
72
+ const [imageTexture, setImageTexture] = useState<TgpuTexture | null>(null);
73
+
74
+ const orientationAngle = useSharedValue<number>(0); // degrees
75
+ const rotationShared = useSharedValue<[number, number, number]>([0, 0, 0]); // final GPU offsets
76
+
77
+ // Calibration shared values (UI thread)
78
+ const initialGravity = useSharedValue<[number, number, number]>([0, 0, 0]);
79
+ const calibSum = useSharedValue<[number, number, number]>([0, 0, 0]);
80
+ const calibCount = useSharedValue<number>(0);
81
+ const calibrated = useSharedValue<boolean>(false);
82
+
83
+ const gravitySensor = useAnimatedSensor(SensorType.GRAVITY, { interval: 20 });
84
+
85
+ console.log('render');
86
+
87
+ // Subscribe to orientation changes and reset calibration on change
88
+ useEffect(() => {
89
+ orientationAngle.value = getAngleFromDimensions();
90
+ const unsubscribe = subscribeToOrientationChange((angleDeg) => {
91
+ orientationAngle.value = angleDeg;
92
+ });
93
+
94
+ return () => unsubscribe();
95
+ }, [orientationAngle]);
96
+
97
+ // Calibration & mapping logic
98
+ useDerivedValue(() => {
99
+ 'worklet';
100
+
101
+ // console.log(orientationAngle.value);
102
+ const v: any = gravitySensor.sensor?.value ??
103
+ gravitySensor.sensor.value ?? { x: 0, y: 0, z: 0 };
104
+ const gx = v.x ?? 0;
105
+ const gy = v.y ?? 0;
106
+ const gz = v.z ?? 0;
107
+
108
+ const CALIBRATION_SAMPLES = 40;
109
+ const alpha = 0.15; // smoothing
110
+ const scale = 0.6;
111
+
112
+ if (!calibrated.value) {
113
+ // accumulate baseline in device coordinates
114
+ const s = calibSum.value;
115
+ const c = calibCount.value + 1;
116
+ calibSum.value = [s[0] + gx, s[1] + gy, s[2] + gz];
117
+ calibCount.value = c;
118
+
119
+ if (c >= CALIBRATION_SAMPLES) {
120
+ const avg = calibSum.value;
121
+ initialGravity.value = [avg[0] / c, avg[1] / c, avg[2] / c];
122
+ calibrated.value = true;
123
+ }
124
+
125
+ rotationShared.value = [0, 0, 0];
126
+ return;
127
+ }
128
+
129
+ const init = initialGravity.value;
130
+ const dx = gx - init[0];
131
+ const dy = gy - init[1];
132
+ const dz = gz - init[2];
133
+
134
+ // Rotate into screen coordinates so offsets auto-swap with orientation
135
+ const [mx, my] = rotate2D([dx, dy], -orientationAngle.value);
136
+ const screenX = mx;
137
+ const screenY = -my;
138
+
139
+ const prev = rotationShared.value;
140
+ const smoothX = prev[0] * (1 - alpha) + screenX * alpha;
141
+ const smoothY = prev[1] * (1 - alpha) + screenY * alpha;
142
+ const smoothZ = prev[2] * (1 - alpha) + dz * alpha;
143
+
144
+ if (orientationAngle.value === 90) {
145
+ rotationShared.value = [
146
+ clamp(smoothY * scale, -1, 1),
147
+ clamp(-smoothX * scale, -1, 1),
148
+ clamp(smoothZ * scale, -1, 1),
149
+ ];
150
+ } else {
151
+ rotationShared.value = [
152
+ clamp(smoothX * scale, -1, 1),
153
+ clamp(smoothY * scale, -1, 1),
154
+ clamp(smoothZ * scale, -1, 1),
155
+ ];
156
+ }
157
+ });
158
+
159
+ // Resource setup
160
+ useEffect(() => {
161
+ if (!root || !device || !context) return;
162
+ (async () => {
163
+ const bitmap = await getBitmapFromURI(imageURI);
164
+ const texture = await createTexture(root, bitmap);
165
+ setImageTexture(texture);
166
+ await loadTexture(root, bitmap, texture);
167
+ })();
168
+ }, [root, device, context, imageURI]);
169
+
170
+ // Render loop
171
+ useEffect(() => {
172
+ if (!root || !device || !context || !imageTexture) return;
173
+
174
+ context.configure({
175
+ device,
176
+ format: presentationFormat,
177
+ alphaMode: 'premultiplied',
178
+ });
179
+
180
+ const sampler = device.createSampler({
181
+ magFilter: 'linear',
182
+ minFilter: 'linear',
183
+ });
184
+ const textureBindGroup = root.createBindGroup(textureBindGroupLayout, {
185
+ texture: root.unwrap(imageTexture).createView(),
186
+ sampler,
187
+ });
188
+
189
+ const rotationBuffer = createRotationBuffer(root);
190
+ const rotationBindGroup = createRotationValuesBindGroup(
191
+ root,
192
+ rotationBuffer
193
+ );
194
+
195
+ const bloomOptionsBuffer = createBloomOptionsBuffer(
196
+ root,
197
+ createBloomOptions(bloomOptions ?? {})
198
+ );
199
+ const bloomOptionsBindGroup = createBloomOptionsBindGroup(
200
+ root,
201
+ bloomOptionsBuffer
202
+ );
203
+
204
+ const colorMaskBuffer = createColorMaskBuffer(
205
+ root,
206
+ createColorMask(colorMaskOptions ?? { baseColor: [-20, -20, -20] })
207
+ );
208
+ const colorMaskBindGroup = createColorMaskBindGroup(root, colorMaskBuffer);
209
+
210
+ const bloomBGP: BindGroupPair[] = createBindGroupPairs(
211
+ [
212
+ textureBindGroupLayout,
213
+ rotationValuesBindGroupLayout,
214
+ bloomOptionsBindGroupLayout,
215
+ colorMaskBindGroupLayout,
216
+ ],
217
+ [
218
+ textureBindGroup,
219
+ rotationBindGroup,
220
+ bloomOptionsBindGroup,
221
+ colorMaskBindGroup,
222
+ ]
223
+ );
224
+
225
+ const maskBGP: BindGroupPair[] = createBindGroupPairs(
226
+ [textureBindGroupLayout, colorMaskBindGroupLayout],
227
+ [textureBindGroup, colorMaskBindGroup]
228
+ );
229
+
230
+ let bloomPipeline = root['~unstable']
231
+ .withVertex(mainVertex, {})
232
+ .withFragment(bloomFragment, getDefaultTarget(presentationFormat))
233
+ .createPipeline();
234
+ bloomPipeline = attachBindGroups(bloomPipeline, bloomBGP);
235
+
236
+ let colorMaskPipeline = root['~unstable']
237
+ .withVertex(mainVertex, {})
238
+ .withFragment(colorMaskFragment, getDefaultTarget(presentationFormat))
239
+ .createPipeline();
240
+ colorMaskPipeline = attachBindGroups(colorMaskPipeline, maskBGP);
241
+
242
+ const rot = d.vec3f(0.0);
243
+ let view: GPUTextureView;
244
+ let bloomAttachment;
245
+ let colorMaskAttachment;
246
+ const render = () => {
247
+ rot[0] = rotationShared.value[0];
248
+ rot[1] = rotationShared.value[1];
249
+ rot[2] = rotationShared.value[2];
250
+ rotationBuffer.write(rot);
251
+
252
+ view = context.getCurrentTexture().createView();
253
+ bloomAttachment = {
254
+ view: view,
255
+ clearValue: [0, 0, 0, 0],
256
+ loadOp: 'clear' as GPULoadOp,
257
+ storeOp: 'store' as GPUStoreOp,
258
+ };
259
+
260
+ colorMaskAttachment = {
261
+ view: view,
262
+ loadOp: 'load' as GPULoadOp,
263
+ storeOp: 'store' as GPUStoreOp,
264
+ };
265
+
266
+ // root['~unstable'].beginRenderPass(
267
+ // {
268
+ // colorAttachments: [
269
+ // {
270
+ // view,
271
+ // clearValue: [0, 0, 0, 0],
272
+ // loadOp: 'clear',
273
+ // storeOp: 'store',
274
+ // },
275
+ // ],
276
+ // },
277
+ // (pass) => {
278
+ // pass.setPipeline(bloomPipeline);
279
+ // // pass = attachBindGroupsToPass(pass, bloomBGP);
280
+ // pass.setBindGroup(textureBindGroupLayout, textureBindGroup);
281
+ // pass.setBindGroup(rotationValuesBindGroupLayout, rotationBindGroup);
282
+ // pass.setBindGroup(bloomOptionsBindGroupLayout, bloomOptionsBindGroup);
283
+ // pass.setBindGroup(colorMaskBindGroupLayout, colorMaskBindGroup);
284
+ // pass.draw(6);
285
+
286
+ // // Mask draw
287
+ // pass.setPipeline(colorMaskPipeline);
288
+ // pass.setBindGroup(textureBindGroupLayout, textureBindGroup);
289
+ // pass.setBindGroup(colorMaskBindGroupLayout, colorMaskBindGroup);
290
+ // pass.draw(6);
291
+ // }
292
+ // );
293
+ // root['~unstable'].flush();
294
+
295
+ bloomPipeline.withColorAttachment(bloomAttachment).draw(6);
296
+ colorMaskPipeline.withColorAttachment(colorMaskAttachment).draw(6);
297
+
298
+ context.present();
299
+ frameRef.current = requestAnimationFrame(render);
300
+ };
301
+ frameRef.current = requestAnimationFrame(render);
302
+
303
+ return () => {
304
+ if (frameRef.current) cancelAnimationFrame(frameRef.current);
305
+ };
306
+ }, [
307
+ device,
308
+ context,
309
+ root,
310
+ presentationFormat,
311
+ imageTexture,
312
+ rotationShared,
313
+ bloomOptions,
314
+ colorMaskOptions,
315
+ ]);
316
+
317
+ return (
318
+ <Canvas
319
+ ref={ref}
320
+ style={{ width, height, aspectRatio: width / height }}
321
+ transparent={Platform.OS === 'ios'}
322
+ />
323
+ );
324
+ }
325
+
326
+ export { subscribeToOrientationChange, getAngleFromDimensions };
package/src/roots.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { tgpu, type TgpuRoot } from 'typegpu';
2
+
3
+ const deviceToRootMap = new WeakMap<GPUDevice, TgpuRoot>();
4
+
5
+ function getOrInitRoot(device: GPUDevice): TgpuRoot {
6
+ let root = deviceToRootMap.get(device);
7
+
8
+ if (!root) {
9
+ root = tgpu.initFromDevice({ device });
10
+ deviceToRootMap.set(device, root);
11
+ }
12
+
13
+ return root;
14
+ }
15
+
16
+ export { getOrInitRoot };
@@ -0,0 +1,40 @@
1
+ import tgpu from 'typegpu';
2
+ import * as d from 'typegpu/data';
3
+
4
+ export const textureBindGroupLayout = tgpu.bindGroupLayout({
5
+ texture: { texture: 'float', dimension: '2d', sampleType: 'float' },
6
+ sampler: { sampler: 'filtering' },
7
+ });
8
+
9
+ export const rotationValuesBindGroupLayout = tgpu.bindGroupLayout({
10
+ vec: { uniform: d.vec3f },
11
+ });
12
+
13
+ export const bloomOptionsSchema = d.struct({
14
+ glowPower: d.f32,
15
+ hueShiftAngleMax: d.f32,
16
+ hueShiftAngleMin: d.f32,
17
+ hueBlendPower: d.f32,
18
+ lightIntensity: d.f32,
19
+ bloomIntensity: d.f32,
20
+ });
21
+
22
+ export type bloomOptionsSchema = typeof bloomOptionsSchema;
23
+
24
+ export const bloomOptionsBindGroupLayout = tgpu.bindGroupLayout({
25
+ bloomOptions: { uniform: bloomOptionsSchema },
26
+ });
27
+
28
+ export const colorMaskSchema = d.struct({
29
+ baseColor: d.vec3f,
30
+ rgbToleranceRange: d.struct({
31
+ upper: d.vec3f,
32
+ lower: d.vec3f,
33
+ }),
34
+ });
35
+
36
+ export type colorMaskSchema = typeof colorMaskSchema;
37
+
38
+ export const colorMaskBindGroupLayout = tgpu.bindGroupLayout({
39
+ mask: { uniform: colorMaskSchema },
40
+ });