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,101 @@
1
+ import { type TgpuBuffer, type TgpuRoot, type UniformFlag } from 'typegpu';
2
+ import * as d from 'typegpu/data';
3
+ import {
4
+ bloomOptionsBindGroupLayout,
5
+ bloomOptionsSchema,
6
+ colorMaskBindGroupLayout,
7
+ colorMaskSchema,
8
+ rotationValuesBindGroupLayout,
9
+ } from './bindGroupLayouts';
10
+ import type {
11
+ BloomOptions,
12
+ ColorMask,
13
+ PartiallyOptional,
14
+ } from '../types/types';
15
+ import {
16
+ colorMaskToTyped,
17
+ createBloomOptions,
18
+ createColorMask,
19
+ mapToF32,
20
+ } from '../types/typeUtils';
21
+
22
+ export const createRotationBuffer = (
23
+ root: TgpuRoot,
24
+ initValues?: { x: number; y: number; z: number }
25
+ ) => {
26
+ const init = initValues
27
+ ? d.vec3f(initValues.x, initValues.y, initValues.z)
28
+ : d.vec3f(0.0);
29
+ const rotationValuesBuffer = root
30
+ .createBuffer(d.vec3f, init)
31
+ .$usage('uniform');
32
+
33
+ return rotationValuesBuffer;
34
+ };
35
+
36
+ export const createRotationValuesBindGroup = (
37
+ root: TgpuRoot,
38
+ buffer: TgpuBuffer<d.Vec3f> & UniformFlag
39
+ ) => {
40
+ const rotationValuesBindGroup = root.createBindGroup(
41
+ rotationValuesBindGroupLayout,
42
+ {
43
+ vec: buffer,
44
+ }
45
+ );
46
+
47
+ return rotationValuesBindGroup;
48
+ };
49
+
50
+ export const createBloomOptionsBuffer = (
51
+ root: TgpuRoot,
52
+ initValues?: Partial<BloomOptions>
53
+ ) => {
54
+ const bloomOptions: BloomOptions = createBloomOptions({ ...initValues });
55
+ const bloomOptionsTyped = mapToF32(bloomOptions);
56
+
57
+ const bloomOptionsBuffer = root
58
+ .createBuffer(bloomOptionsSchema, bloomOptionsTyped)
59
+ .$usage('uniform');
60
+
61
+ return bloomOptionsBuffer;
62
+ };
63
+
64
+ export const createBloomOptionsBindGroup = (
65
+ root: TgpuRoot,
66
+ buffer: TgpuBuffer<bloomOptionsSchema> & UniformFlag
67
+ ) => {
68
+ const bloomOptionsBindGroup = root.createBindGroup(
69
+ bloomOptionsBindGroupLayout,
70
+ {
71
+ bloomOptions: buffer,
72
+ }
73
+ );
74
+
75
+ return bloomOptionsBindGroup;
76
+ };
77
+
78
+ export const createColorMaskBuffer = (
79
+ root: TgpuRoot,
80
+ initValues: PartiallyOptional<ColorMask, 'baseColor'>
81
+ ) => {
82
+ const colorMask: ColorMask = createColorMask({ ...initValues });
83
+ const colorMaskTyped = colorMaskToTyped(colorMask);
84
+
85
+ const colorMaskBuffer = root
86
+ .createBuffer(colorMaskSchema, colorMaskTyped)
87
+ .$usage('uniform');
88
+
89
+ return colorMaskBuffer;
90
+ };
91
+
92
+ export const createColorMaskBindGroup = (
93
+ root: TgpuRoot,
94
+ buffer: TgpuBuffer<colorMaskSchema> & UniformFlag
95
+ ) => {
96
+ const colorMaskBindGroup = root.createBindGroup(colorMaskBindGroupLayout, {
97
+ mask: buffer,
98
+ });
99
+
100
+ return colorMaskBindGroup;
101
+ };
@@ -0,0 +1,83 @@
1
+ import tgpu from 'typegpu';
2
+ import * as d from 'typegpu/data';
3
+ import * as std from 'typegpu/std';
4
+ import {
5
+ rotationValuesBindGroupLayout,
6
+ textureBindGroupLayout,
7
+ bloomOptionsBindGroupLayout,
8
+ colorMaskBindGroupLayout,
9
+ } from '../bindGroupLayouts';
10
+ import { bloomColorShift, hueShift, overlayChannels } from '../tgpuUtils';
11
+
12
+ const bloomFragment = tgpu['~unstable'].fragmentFn({
13
+ in: { uv: d.vec2f },
14
+ out: d.vec4f,
15
+ })((input) => {
16
+ const texcoord = d.vec2f(input.uv.x, 1.0 - input.uv.y);
17
+ const uv = d.vec2f(input.uv.x, 1.0 - input.uv.y);
18
+ const centeredCoords = std.sub(std.mul(uv, 2.0), 1); //-1 to 1
19
+
20
+ const rot = rotationValuesBindGroupLayout.$.vec;
21
+ const center = std.add(d.vec2f(0.0), d.vec2f(rot.x, rot.y));
22
+
23
+ const bloomOptions = bloomOptionsBindGroupLayout.$.bloomOptions;
24
+ const bloomIntensity = bloomOptions.bloomIntensity;
25
+ const glowPower = bloomOptions.glowPower;
26
+ const hueBlendPower = bloomOptions.hueBlendPower;
27
+ const hueShiftAngleMax = bloomOptions.hueShiftAngleMax;
28
+ const hueShiftAngleMin = bloomOptions.hueShiftAngleMin;
29
+ const lightIntensity = bloomOptions.lightIntensity;
30
+
31
+ const mask = colorMaskBindGroupLayout.$.mask;
32
+ const maskedColor = mask.baseColor;
33
+ const rgbToleranceRange = mask.rgbToleranceRange;
34
+
35
+ let color = std.textureSample(
36
+ textureBindGroupLayout.$.texture,
37
+ textureBindGroupLayout.$.sampler,
38
+ texcoord
39
+ );
40
+
41
+ const maskedColorLower = std.sub(maskedColor, rgbToleranceRange.lower);
42
+ const maskedColorUpper = std.add(maskedColor, rgbToleranceRange.upper);
43
+ const upperCheck = std.all(std.le(color.xyz, maskedColorUpper));
44
+ const lowerCheck = std.all(std.ge(color.xyz, maskedColorLower));
45
+ if (upperCheck && lowerCheck) {
46
+ return color;
47
+ }
48
+
49
+ //bloomIntensity
50
+ const dst = std.exp(-std.distance(center, centeredCoords));
51
+ const distToCenter = std.smoothstep(0.0, 1 / bloomIntensity, dst);
52
+
53
+ //glowPower
54
+ let glow = d.vec3f(distToCenter);
55
+ glow = std.mul(glow, glowPower * color.w);
56
+
57
+ //hueBlend
58
+ const hueBlend = (d.f32(hueBlendPower) * dst) / 10.0;
59
+
60
+ //lightIntensity
61
+ glow = std.add(glow, lightIntensity / 10.0);
62
+ let shiftedRGB = bloomColorShift(color.xyz, dst / (lightIntensity * 2));
63
+
64
+ //hueShiftAngleMin/Max
65
+ const hueShiftAngle = std.smoothstep(
66
+ hueShiftAngleMin,
67
+ hueShiftAngleMax,
68
+ distToCenter
69
+ );
70
+ const shiftedHue = hueShift(shiftedRGB, hueShiftAngle);
71
+ shiftedRGB = overlayChannels(shiftedRGB, shiftedHue);
72
+
73
+ color = d.vec4f(std.mix(color.xyz, shiftedRGB, hueBlend), color.w);
74
+ const baseColor = color;
75
+ const blendColor = glow;
76
+
77
+ const combined = overlayChannels(baseColor.xyz, blendColor);
78
+ color = d.vec4f(std.mix(color.xyz, combined, glow), color.w);
79
+
80
+ return color;
81
+ });
82
+
83
+ export default bloomFragment;
@@ -0,0 +1,35 @@
1
+ import tgpu from 'typegpu';
2
+ import * as d from 'typegpu/data';
3
+ import * as std from 'typegpu/std';
4
+ import {
5
+ textureBindGroupLayout,
6
+ colorMaskBindGroupLayout,
7
+ } from '../bindGroupLayouts';
8
+
9
+ const colorMaskFragment = tgpu['~unstable'].fragmentFn({
10
+ in: { uv: d.vec2f },
11
+ out: d.vec4f,
12
+ })((input) => {
13
+ const texcoord = d.vec2f(input.uv.x, 1.0 - input.uv.y);
14
+
15
+ const mask = colorMaskBindGroupLayout.$.mask;
16
+ const maskedColor = mask.baseColor;
17
+ const rgbToleranceRange = mask.rgbToleranceRange;
18
+
19
+ let color = std.textureSample(
20
+ textureBindGroupLayout.$.texture,
21
+ textureBindGroupLayout.$.sampler,
22
+ texcoord
23
+ );
24
+
25
+ const maskedColorLower = std.sub(maskedColor, rgbToleranceRange.lower);
26
+ const maskedColorUpper = std.add(maskedColor, rgbToleranceRange.upper);
27
+ const upperCheck = std.all(std.le(color.xyz, maskedColorUpper));
28
+ const lowerCheck = std.all(std.ge(color.xyz, maskedColorLower));
29
+ if (upperCheck && lowerCheck) {
30
+ return color;
31
+ }
32
+ std.discard();
33
+ });
34
+
35
+ export default colorMaskFragment;
@@ -0,0 +1,44 @@
1
+ import type { TgpuRenderPipeline } from 'typegpu';
2
+ import type { BindGroupPair } from '../types/types';
3
+
4
+ export const attachBindGroups = (
5
+ pipeline: TgpuRenderPipeline,
6
+ bindGroupPairs: BindGroupPair[]
7
+ ) => {
8
+ for (const pair of bindGroupPairs) {
9
+ pipeline = pipeline.with(pair.layout, pair.group);
10
+ }
11
+
12
+ return pipeline;
13
+ };
14
+
15
+ export const getDefaultTarget = (
16
+ presentationFormat: GPUTextureFormat
17
+ ): GPUColorTargetState => {
18
+ return {
19
+ format: presentationFormat,
20
+ blend: {
21
+ color: {
22
+ srcFactor: 'src-alpha',
23
+ dstFactor: 'one-minus-src-alpha',
24
+ operation: 'add',
25
+ },
26
+ alpha: {
27
+ srcFactor: 'one',
28
+ dstFactor: 'one-minus-src-alpha',
29
+ operation: 'add',
30
+ },
31
+ },
32
+ };
33
+ };
34
+
35
+ export const attachBindGroupsToPass = (
36
+ pass: any,
37
+ bindGroupPairs: BindGroupPair[]
38
+ ) => {
39
+ for (const pair of bindGroupPairs) {
40
+ pass.setBindGroup(pair.layout, pair.group);
41
+ }
42
+
43
+ return pass;
44
+ };
@@ -0,0 +1,22 @@
1
+ import { Asset } from 'expo-asset';
2
+
3
+ const getBitmapFromURI = async (uri: string): Promise<ImageBitmap> => {
4
+ if (uriToBitmapMap.has(uri)) return uriToBitmapMap.get(uri)!;
5
+ console.log('bitmap not found in cache, fetching from URI');
6
+
7
+ const ast = Asset.fromURI(uri);
8
+ await ast.downloadAsync();
9
+ const fileURI = ast.localUri || ast.uri;
10
+
11
+ console.log('fetch completed, creating ImageBitmap');
12
+ const response = await fetch(fileURI);
13
+ const blob = await response.blob();
14
+ const imageBitmap = await createImageBitmap(blob);
15
+
16
+ uriToBitmapMap.set(uri, imageBitmap);
17
+ return imageBitmap;
18
+ };
19
+
20
+ const uriToBitmapMap = new Map<string, ImageBitmap>();
21
+
22
+ export default getBitmapFromURI;
@@ -0,0 +1,75 @@
1
+ import tgpu from 'typegpu';
2
+ import * as std from 'typegpu/std';
3
+ import * as d from 'typegpu/data';
4
+
5
+ export const hueShift = tgpu.fn(
6
+ [d.vec3f, d.f32],
7
+ d.vec3f
8
+ )((rgb, angle) => {
9
+ const yiqY = std.add(
10
+ std.mul(rgb.x, 0.299),
11
+ std.add(std.mul(rgb.y, 0.587), std.mul(rgb.z, 0.114))
12
+ );
13
+ const yiqI = std.add(
14
+ std.mul(rgb.x, 0.596),
15
+ std.sub(std.mul(rgb.y, -0.274), std.mul(rgb.z, 0.322))
16
+ );
17
+ const yiqQ = std.add(
18
+ std.mul(rgb.x, 0.211),
19
+ std.sub(std.mul(rgb.y, -0.523), std.mul(rgb.z, 0.311))
20
+ );
21
+
22
+ // Rotate hue
23
+ const cosA = std.cos(angle);
24
+ const sinA = std.sin(angle);
25
+ const i = std.sub(std.mul(yiqI, cosA), std.mul(yiqQ, sinA));
26
+ const q = std.add(std.mul(yiqI, sinA), std.mul(yiqQ, cosA));
27
+
28
+ // Convert back to RGB
29
+ const r = std.add(std.add(yiqY, std.mul(i, 0.956)), std.mul(q, 0.621));
30
+ const g = std.add(std.add(yiqY, std.mul(i, -0.272)), std.mul(q, -0.647));
31
+ const b = std.add(std.add(yiqY, std.mul(i, -1.105)), std.mul(q, 1.702));
32
+
33
+ return d.vec3f(r, g, b);
34
+ });
35
+
36
+ export const bloomColorShift = tgpu.fn(
37
+ [d.vec3f, d.f32],
38
+ d.vec3f
39
+ )((color, power) => {
40
+ const maxValue = std.max(std.max(color.x, color.y), color.z);
41
+ const scale = std.mix(
42
+ d.f32(1.0),
43
+ d.f32(1.0) / std.max(maxValue, d.f32(0.001)),
44
+ power
45
+ );
46
+ const boosted = std.mul(color, scale);
47
+ const saturated = std.mix(color, boosted, power);
48
+
49
+ return saturated;
50
+ });
51
+
52
+ export const overlayChannel = tgpu.fn(
53
+ [d.f32, d.f32],
54
+ d.f32
55
+ )((base, blend) => {
56
+ const mult = std.mul(2.0, std.mul(base, blend));
57
+
58
+ const screen = std.sub(
59
+ 1.0,
60
+ std.mul(2.0, std.mul(std.sub(1.0, base), std.sub(1.0, blend)))
61
+ );
62
+
63
+ return std.select(screen, mult, base < d.f32(0.5));
64
+ });
65
+
66
+ export const overlayChannels = tgpu.fn(
67
+ [d.vec3f, d.vec3f],
68
+ d.vec3f
69
+ )((base, blend) => {
70
+ return d.vec3f(
71
+ overlayChannel(base.x, blend.x),
72
+ overlayChannel(base.y, blend.y),
73
+ overlayChannel(base.z, blend.z)
74
+ );
75
+ });
@@ -0,0 +1,103 @@
1
+ import { type TgpuRoot, type TgpuTexture } from 'typegpu';
2
+ import type { quaternion, vec3 } from '../types/types';
3
+ import { Dimensions } from 'react-native';
4
+
5
+ export const createTexture = async (
6
+ root: TgpuRoot,
7
+ size: {
8
+ width: number;
9
+ height: number;
10
+ }
11
+ ): Promise<TgpuTexture> => {
12
+ const texture = root['~unstable']
13
+ .createTexture({
14
+ size: [size.width, size.height],
15
+ format: 'rgba8unorm',
16
+ })
17
+ .$usage('sampled', 'render');
18
+
19
+ return texture;
20
+ };
21
+
22
+ export const loadTexture = async (
23
+ root: TgpuRoot,
24
+ imageBitmap: ImageBitmap,
25
+ texture: TgpuTexture
26
+ ) => {
27
+ root.device.queue.copyExternalImageToTexture(
28
+ { source: imageBitmap },
29
+ { texture: root.unwrap(texture) },
30
+ [imageBitmap.width, imageBitmap.height]
31
+ );
32
+ };
33
+
34
+ export const rotateVectorByQuaternion = (
35
+ vec: vec3,
36
+ quaternion: quaternion
37
+ ): vec3 => {
38
+ 'worklet';
39
+ const t: vec3 = [
40
+ quaternion[1] * vec[2] - quaternion[2] * vec[1],
41
+ quaternion[2] * vec[0] - quaternion[0] * vec[2],
42
+ quaternion[0] * vec[1] - quaternion[1] * vec[0],
43
+ ];
44
+
45
+ // t2 = qw * forward + t
46
+ const t2: vec3 = [
47
+ quaternion[3] * vec[0] + t[0],
48
+ quaternion[3] * vec[1] + t[1],
49
+ quaternion[3] * vec[2] + t[2],
50
+ ];
51
+
52
+ const crossQT2: vec3 = [
53
+ quaternion[1] * t2[2] - quaternion[2] * t2[1],
54
+ quaternion[2] * t2[0] - quaternion[0] * t2[2],
55
+ quaternion[0] * t2[1] - quaternion[1] * t2[0],
56
+ ];
57
+
58
+ // rotated = vec + 2.0 * cross(q, t2)
59
+ const rotated: vec3 = [
60
+ vec[0] + 2.0 * crossQT2[0],
61
+ vec[1] + 2.0 * crossQT2[1],
62
+ vec[2] + 2.0 * crossQT2[2],
63
+ ];
64
+
65
+ return [rotated[0], rotated[1], rotated[2]];
66
+ };
67
+
68
+ export const clamp = (v: number, min = -1, max = 1) => {
69
+ 'worklet';
70
+ return Math.max(min, Math.min(max, v));
71
+ };
72
+
73
+ export const rotate2D = (
74
+ [x, y]: [number, number],
75
+ angleDeg: number
76
+ ): [number, number] => {
77
+ 'worklet';
78
+ const rad = (angleDeg * Math.PI) / 180;
79
+ const c = Math.cos(rad);
80
+ const s = Math.sin(rad);
81
+ return [x * c - y * s, x * s + y * c];
82
+ };
83
+
84
+ // Simple helper to get angle from dimensions (0 or 90)
85
+ export function getAngleFromDimensions() {
86
+ const { width, height } = Dimensions.get('window');
87
+ return width >= height ? 90 : 0;
88
+ }
89
+
90
+ // Subscribe to orientation change via Dimensions API only
91
+ export function subscribeToOrientationChange(
92
+ callback: (angleDeg: number) => void
93
+ ) {
94
+ callback(getAngleFromDimensions());
95
+ const handler = () => {
96
+ callback(getAngleFromDimensions());
97
+ };
98
+
99
+ const dimSub = Dimensions.addEventListener('change', handler);
100
+ return () => {
101
+ dimSub.remove();
102
+ };
103
+ }
@@ -0,0 +1,35 @@
1
+ import tgpu from 'typegpu';
2
+ import * as d from 'typegpu/data';
3
+
4
+ const mainVertex = tgpu['~unstable'].vertexFn({
5
+ in: { vertexIndex: d.builtin.vertexIndex },
6
+ out: { position: d.builtin.position, uv: d.vec2f },
7
+ })((input) => {
8
+ const position: d.v2f[] = [
9
+ d.vec2f(-1.0, -1.0), // bottom left
10
+ d.vec2f(1.0, 1.0), // top right
11
+ d.vec2f(1.0, -1.0), // bottom right
12
+ d.vec2f(-1.0, -1.0), // bottom left
13
+ d.vec2f(-1.0, 1.0), // top left
14
+ d.vec2f(1.0, 1.0), // top right
15
+ ];
16
+
17
+ const uv: d.v2f[] = [
18
+ d.vec2f(0.0, 0.0),
19
+ d.vec2f(1.0, 1.0),
20
+ d.vec2f(1.0, 0.0),
21
+ d.vec2f(0.0, 0.0),
22
+ d.vec2f(0.0, 1.0),
23
+ d.vec2f(1.0, 1.0),
24
+ ];
25
+
26
+ const index = input.vertexIndex;
27
+ const pos = position[index] as d.v2f;
28
+
29
+ return {
30
+ position: d.vec4f(pos.xy, 0.0, 1.0),
31
+ uv: uv[index] as d.v2f,
32
+ };
33
+ });
34
+
35
+ export default mainVertex;
@@ -0,0 +1,122 @@
1
+ import { f32, vec2f, vec3f, vec4f } from 'typegpu/data';
2
+ import type {
3
+ BindGroupPair,
4
+ BloomOptions,
5
+ ColorMask,
6
+ DeepPartiallyOptional,
7
+ vec3,
8
+ } from './types';
9
+ import { div } from 'typegpu/std';
10
+ import type { TgpuBindGroup, TgpuBindGroupLayout } from 'typegpu';
11
+
12
+ export const createBloomOptions = (
13
+ options: Partial<BloomOptions>
14
+ ): BloomOptions => {
15
+ const {
16
+ glowPower,
17
+ hueShiftAngleMax,
18
+ hueShiftAngleMin,
19
+ hueBlendPower,
20
+ lightIntensity,
21
+ bloomIntensity,
22
+ } = options;
23
+
24
+ const bloomOp = {
25
+ glowPower: glowPower ?? 1.0,
26
+ hueShiftAngleMax: hueShiftAngleMax ?? 1.0,
27
+ hueShiftAngleMin: hueShiftAngleMin ?? 0.0,
28
+ hueBlendPower: hueBlendPower ?? 1.0,
29
+ lightIntensity: lightIntensity ?? 1.0,
30
+ bloomIntensity: bloomIntensity ?? 1.0,
31
+ };
32
+
33
+ return bloomOp;
34
+ };
35
+
36
+ export const mapToF32 = <T extends Record<string, number>>(
37
+ obj: T
38
+ ): {
39
+ [K in keyof T]: ReturnType<typeof f32>;
40
+ } => {
41
+ const result = {} as any;
42
+ for (const key in obj) {
43
+ result[key] = f32(obj[key]);
44
+ }
45
+
46
+ return result;
47
+ };
48
+
49
+ export const createColorMask = (
50
+ colorMask: DeepPartiallyOptional<ColorMask, 'baseColor'>
51
+ ): ColorMask => {
52
+ const { baseColor, rgbToleranceRange } = colorMask;
53
+ const baseTolerance = {
54
+ upper: [20, 20, 20] as vec3,
55
+ lower: [20, 20, 20] as vec3,
56
+ };
57
+ const tolerance = { ...baseTolerance, ...rgbToleranceRange };
58
+
59
+ const mask: ColorMask = {
60
+ baseColor: baseColor,
61
+ rgbToleranceRange: tolerance,
62
+ };
63
+
64
+ return mask;
65
+ };
66
+
67
+ export const colorMaskToTyped = (colorMask: ColorMask) => {
68
+ const result = {
69
+ baseColor: div(numberArrToTyped(colorMask.baseColor), 255),
70
+ rgbToleranceRange: {
71
+ upper: div(numberArrToTyped(colorMask.rgbToleranceRange.upper), 255),
72
+ lower: div(numberArrToTyped(colorMask.rgbToleranceRange.lower), 255),
73
+ },
74
+ };
75
+ return result;
76
+ };
77
+
78
+ export const numberArrToTyped = (vec: number[]) => {
79
+ let convFn: ((...args: number[]) => any) | null = null;
80
+ switch (vec.length) {
81
+ case 2:
82
+ convFn = vec2f;
83
+ break;
84
+ case 3:
85
+ convFn = vec3f;
86
+ break;
87
+ case 4:
88
+ convFn = vec4f;
89
+ break;
90
+ default:
91
+ throw new Error('numberArrToTyped: Vector must be of length [2-4]');
92
+ }
93
+
94
+ const typed = convFn(...vec);
95
+ return typed;
96
+ };
97
+
98
+ export const createBindGroupPair = (
99
+ bindGroupLayout: TgpuBindGroupLayout,
100
+ bindGroup: TgpuBindGroup
101
+ ): BindGroupPair => {
102
+ return { layout: bindGroupLayout, group: bindGroup };
103
+ };
104
+
105
+ export const createBindGroupPairs = (
106
+ bindGroupLayouts: TgpuBindGroupLayout[],
107
+ bindGroups: TgpuBindGroup[]
108
+ ): BindGroupPair[] => {
109
+ if (
110
+ bindGroupLayouts.length > 0 &&
111
+ bindGroupLayouts.length !== bindGroups.length
112
+ )
113
+ throw new Error(
114
+ 'createBindGroups: bindGroupLayout and bindGroup arrrays must be of the same length'
115
+ );
116
+ const pairs: BindGroupPair[] = [];
117
+ for (let i = 0; i < bindGroupLayouts.length; i++) {
118
+ pairs.push(createBindGroupPair(bindGroupLayouts[i]!, bindGroups[i]!));
119
+ }
120
+
121
+ return pairs;
122
+ };
@@ -0,0 +1,51 @@
1
+ import type { TgpuBindGroup, TgpuBindGroupLayout } from 'typegpu';
2
+
3
+ export type vec2 = [number, number];
4
+ export type vec3 = [number, number, number];
5
+ export type vec4 = [number, number, number, number];
6
+ export type quaternion = vec4;
7
+
8
+ export type BloomOptions = {
9
+ glowPower: number;
10
+ hueShiftAngleMax: number;
11
+ hueShiftAngleMin: number;
12
+ hueBlendPower: number;
13
+ lightIntensity: number;
14
+ bloomIntensity: number;
15
+ };
16
+
17
+ export type ColorMask = {
18
+ baseColor: vec3;
19
+ rgbToleranceRange: {
20
+ upper: vec3;
21
+ lower: vec3;
22
+ };
23
+ };
24
+
25
+ //makes all keys besides specified optional
26
+ export type PartiallyOptional<T, K extends keyof T> = {
27
+ [P in K]: T[P];
28
+ } & Partial<Omit<T, K>>;
29
+
30
+ export type Primitive =
31
+ | string
32
+ | number
33
+ | boolean
34
+ | bigint
35
+ | symbol
36
+ | null
37
+ | undefined;
38
+
39
+ //makes every object and its' properties optional
40
+ //unless the objects are contained in any kind of array
41
+ export type DeepPartial<T> = {
42
+ [P in keyof T]?: T[P] extends Primitive | any[] ? T[P] : DeepPartial<T[P]>;
43
+ };
44
+
45
+ export type DeepPartiallyOptional<T, K extends keyof T> = Required<Pick<T, K>> &
46
+ DeepPartial<Omit<T, K>>;
47
+
48
+ export type BindGroupPair = {
49
+ layout: TgpuBindGroupLayout;
50
+ group: TgpuBindGroup;
51
+ };