1import * as GL from 'expo-gl';
2import mat4 from 'gl-mat4';
3import hsv2rgb from 'hsv2rgb';
4import React from 'react';
5import { ActivityIndicator, InteractionManager, StyleSheet, View } from 'react-native';
6import REGL from 'regl';
7
8const NUM_POINTS = 1e4;
9const VERT_SIZE = 4 * (4 + 4 + 3);
10
11export default class BasicScene extends React.Component {
12  static title = 'GLView example';
13  static navigationOptions = {
14    title: 'GLView example',
15  };
16
17  state = {
18    transitionIsComplete: false,
19  };
20
21  componentDidMount() {
22    InteractionManager.runAfterInteractions(() => {
23      this.setState({ transitionIsComplete: true });
24    });
25  }
26
27  render() {
28    if (!this.state.transitionIsComplete) {
29      return (
30        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
31          <ActivityIndicator />
32        </View>
33      );
34    }
35
36    return <GL.GLView style={StyleSheet.absoluteFill} onContextCreate={this._onContextCreate} />;
37  }
38
39  _onContextCreate = (gl: GL.ExpoWebGLRenderingContext) => {
40    const regl = REGL({ gl });
41
42    const pointBuffer = regl.buffer(
43      Array(NUM_POINTS)
44        .fill(0)
45        .map(() => {
46          const color = hsv2rgb(Math.random() * 360, 0.6, 1);
47          return [
48            // freq
49            Math.random() * 10,
50            Math.random() * 10,
51            Math.random() * 10,
52            Math.random() * 10,
53            // phase
54            2.0 * Math.PI * Math.random(),
55            2.0 * Math.PI * Math.random(),
56            2.0 * Math.PI * Math.random(),
57            2.0 * Math.PI * Math.random(),
58            // color
59            color[0] / 255,
60            color[1] / 255,
61            color[2] / 255,
62          ];
63        })
64    );
65
66    const drawParticles = regl({
67      vert: `
68  precision highp float;
69  attribute vec4 freq, phase;
70  attribute vec3 color;
71  uniform float time;
72  uniform mat4 view, projection;
73  varying vec3 fragColor;
74  void main() {
75    vec3 position = 8.0 * cos(freq.xyz * time + phase.xyz);
76    gl_PointSize = 10.0 * (1.0 + cos(freq.w * time + phase.w));
77    gl_Position = projection * view * vec4(position, 1);
78    fragColor = color;
79  }`,
80
81      frag: `
82  precision lowp float;
83  varying vec3 fragColor;
84  void main() {
85    if (length(gl_PointCoord.xy - 0.5) > 0.5) {
86      discard;
87    }
88    gl_FragColor = vec4(fragColor, 1);
89  }`,
90
91      attributes: {
92        freq: {
93          buffer: pointBuffer,
94          stride: VERT_SIZE,
95          offset: 0,
96        },
97        phase: {
98          buffer: pointBuffer,
99          stride: VERT_SIZE,
100          offset: 16,
101        },
102        color: {
103          buffer: pointBuffer,
104          stride: VERT_SIZE,
105          offset: 32,
106        },
107      },
108
109      uniforms: {
110        view: ({ time: t }: { time: number }) => {
111          t = t * 0.1;
112          return mat4.lookAt([], [30 * Math.cos(t), 2.5, 30 * Math.sin(t)], [0, 0, 0], [0, 1, 0]);
113        },
114        projection: mat4.perspective(
115          [],
116          Math.PI / 4,
117          gl.drawingBufferWidth / gl.drawingBufferHeight,
118          0.01,
119          1000
120        ),
121        time: ({ time }: { time: number }) => time * 0.1,
122      },
123
124      count: NUM_POINTS,
125
126      primitive: 'points',
127    });
128
129    const frame = () => {
130      regl.poll();
131      regl.clear({
132        color: [0, 0, 0, 1],
133        depth: 1,
134      });
135
136      drawParticles();
137
138      gl.flush();
139      gl.endFrameEXP();
140      requestAnimationFrame(frame);
141    };
142    frame();
143  };
144}
145