├── .gitignore
├── .npmignore
├── README.md
├── code-complete.gif
├── examples
├── anisotropic
│ ├── index.html
│ └── index.ts
├── base-primitives
│ ├── index.html
│ └── index.ts
├── compressed-textures
│ ├── index.html
│ └── index.ts
├── compute-vertex-normal
│ ├── index.html
│ └── index.ts
├── cube-map
│ ├── index.html
│ └── index.ts
├── curves
│ ├── index.html
│ └── index.ts
├── draw-modes
│ ├── index.html
│ └── index.ts
├── flat-shading-matcap
│ ├── index.html
│ └── index.ts
├── fog
│ ├── index.html
│ └── index.ts
├── fresnel
│ ├── index.html
│ └── index.ts
├── frustum-culling
│ ├── index.html
│ └── index.ts
├── gpgpu-particles
│ ├── index.html
│ └── index.ts
├── high-mesh-count
│ ├── index.html
│ └── index.ts
├── indexed-vs-non-indexed
│ ├── index.html
│ └── index.ts
├── instancing-gpu-picking
│ ├── index.html
│ └── index.ts
├── instancing
│ ├── index.html
│ └── index.ts
├── load-gltf
│ ├── index.html
│ └── index.ts
├── load-json
│ ├── index.html
│ └── index.ts
├── mouse-flowmap
│ ├── index.html
│ └── index.ts
├── mrt
│ ├── index.html
│ └── index.ts
├── msdf-text
│ ├── index.html
│ └── index.ts
├── normal-maps
│ ├── index.html
│ └── index.ts
├── orbit-controls
│ ├── index.html
│ └── index.ts
├── particles
│ ├── index.html
│ └── index.ts
├── pbr
│ ├── index.html
│ └── index.ts
├── point-lighting
│ ├── index.html
│ └── index.ts
├── polylines
│ ├── index.html
│ └── index.ts
├── post-bloom
│ ├── index.html
│ └── index.ts
├── post-fluid-distortion
│ ├── index.html
│ └── index.ts
├── post-fxaa
│ ├── index.html
│ └── index.ts
├── raycasting
│ ├── index.html
│ └── index.ts
├── render-to-texture
│ ├── index.html
│ └── index.ts
├── scene-graph
│ ├── index.html
│ └── index.ts
├── shadow-maps
│ ├── index.html
│ └── index.ts
├── skinning
│ ├── index.html
│ └── index.ts
├── skydome
│ ├── index.html
│ └── index.ts
├── sort-transparency
│ ├── index.html
│ └── index.ts
├── template.html
├── textures
│ ├── index.html
│ └── index.ts
├── torus
│ ├── index.html
│ └── index.ts
├── triangle-screen-shader
│ ├── index.html
│ └── index.ts
├── wireframe-shader
│ ├── index.html
│ └── index.ts
└── wireframe
│ ├── index.html
│ └── index.ts
├── index.html
├── package-lock.json
├── package.json
├── public
└── assets
│ ├── acorn.jpg
│ ├── acorn.json
│ ├── airplane.jpg
│ ├── airplane.json
│ ├── anim-format.json
│ ├── compressed
│ ├── astc-m-y.ktx
│ ├── etc-m-y.ktx
│ ├── etc1-m-y.ktx
│ ├── pvrtc-m-y.ktx
│ ├── s3tc-m-y.ktx
│ └── uv.jpg
│ ├── croissant.jpg
│ ├── croissant.json
│ ├── cube
│ ├── negx.jpg
│ ├── negy.jpg
│ ├── negz.jpg
│ ├── posx.jpg
│ ├── posy.jpg
│ └── posz.jpg
│ ├── earth.jpg
│ ├── earth_cloud.jpg
│ ├── earth_specular.jpg
│ ├── favicon.png
│ ├── fonts
│ ├── FiraSans-Bold.json
│ ├── FiraSans-Bold.png
│ ├── FiraSans-Bold.ttf
│ ├── raleway-bold-webfont.woff
│ ├── raleway-bold-webfont.woff2
│ ├── raleway-regular-webfont.woff
│ └── raleway-regular-webfont.woff2
│ ├── forest.jpg
│ ├── forest.json
│ ├── fox.jpg
│ ├── fox.json
│ ├── gltf
│ └── hershel.glb
│ ├── goat.jpg
│ ├── goat.json
│ ├── granite-diffuse.jpg
│ ├── granite-normal.jpg
│ ├── grid.jpg
│ ├── laputa.mp4
│ ├── leaf.jpg
│ ├── macaw.jpg
│ ├── macaw.json
│ ├── main.css
│ ├── matcap.jpg
│ ├── octopus.jpg
│ ├── octopus.json
│ ├── ogl.png
│ ├── pbr
│ ├── black.jpg
│ ├── car-ext-color.jpg
│ ├── car-ext-emissive.jpg
│ ├── car-ext-inner.json
│ ├── car-ext-normal.jpg
│ ├── car-ext-opacity.jpg
│ ├── car-ext-rmo.jpg
│ ├── car-ext.json
│ ├── car-int-color.jpg
│ ├── car-int-normal.jpg
│ ├── car-int-rmo.jpg
│ ├── car-int.json
│ ├── car-shadow.jpg
│ ├── car-shadow.png
│ ├── lut.png
│ ├── waterfall-diffuse-RGBM.png
│ ├── waterfall-specular-RGBM.png
│ └── white.jpg
│ ├── plane.json
│ ├── rounded-cube.json
│ ├── saddle.jpg
│ ├── saddle.json
│ ├── sky.jpg
│ ├── snout-anim.json
│ ├── snout-rig.json
│ ├── snout-shadow.jpg
│ ├── snout.jpg
│ ├── sunset-diffuse-RGBM.png
│ ├── sunset-specular-RGBM.png
│ ├── sunset.exr
│ ├── uv.jpg
│ ├── water.jpg
│ ├── windmill.jpg
│ └── windmill.json
├── src
├── Guards.ts
├── core
│ ├── Camera.ts
│ ├── Geometry.ts
│ ├── Mesh.ts
│ ├── Program.ts
│ ├── RenderTarget.ts
│ ├── Renderer.ts
│ ├── Texture.ts
│ └── Transform.ts
├── extras
│ ├── Animation.ts
│ ├── Box.ts
│ ├── Curve.ts
│ ├── Cylinder.ts
│ ├── Flowmap.ts
│ ├── GLTFAnimation.ts
│ ├── GLTFLoader.ts
│ ├── GLTFSkin.ts
│ ├── GPGPU.ts
│ ├── KTXTexture.ts
│ ├── NormalProgram.ts
│ ├── Orbit.ts
│ ├── Plane.ts
│ ├── Polyline.ts
│ ├── Post.ts
│ ├── Raycast.ts
│ ├── Shadow.ts
│ ├── Skin.ts
│ ├── Sphere.ts
│ ├── Text.ts
│ ├── TextureLoader.ts
│ ├── Torus.ts
│ └── Triangle.ts
├── index.ts
└── math
│ ├── Color.ts
│ ├── Euler.ts
│ ├── Mat3.ts
│ ├── Mat4.ts
│ ├── Quat.ts
│ ├── Vec2.ts
│ ├── Vec3.ts
│ ├── Vec4.ts
│ └── functions
│ ├── ColorFunc.ts
│ ├── EulerFunc.ts
│ ├── Mat3Func.ts
│ ├── Mat4Func.ts
│ ├── QuatFunc.ts
│ ├── Vec2Func.ts
│ ├── Vec3Func.ts
│ └── Vec4Func.ts
├── tea.yaml
├── tsconfig.json
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | lib
4 | dist
5 | yarn-error.log
6 | .vscode
7 | yarn.lock
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
OGL-TypeScript
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Minimal WebGL library.
19 |
20 |
21 |
22 | [See the Examples!](https://nshen.github.io/ogl-typescript/examples)
23 |
24 | OGL-TypeScript is TypeScript port of [OGL Minimal WebGL library](https://github.com/oframe/ogl) , so it add code completion feature to OGL.
25 |
26 |
27 |
28 | ## Install
29 |
30 | ```bash
31 | npm i ogl-typescript # yarn add ogl-typescript
32 | ```
33 |
34 | ## Usage
35 |
36 | Same as OGL, just change
37 |
38 | ```typescript
39 | import { ... } from 'ogl';
40 | ```
41 |
42 | to
43 |
44 | ```typescript
45 | import { ... } from 'ogl-typescript'
46 | ```
47 |
48 | You can find [ogl-typescript-starter](https://github.com/nshen/ogl-typescript-starter) here, it's very easy to get start.
49 |
50 | ## Examples
51 |
52 | [Show me what you got!](https://nshen.github.io/ogl-typescript/examples) - Explore a comprehensive list of examples, with comments in the source code.
53 |
54 | ## Build
55 |
56 | dev mode
57 |
58 | ```bash
59 | yarn dev # npm run dev
60 | ```
61 |
62 | build library
63 |
64 | ```bash
65 | yarn build # npm run build
66 | ```
67 |
68 | build examples
69 |
70 | ```bash
71 | yarn build:examples # npm run build:examples
72 | ```
73 |
74 | ## Versions
75 |
76 | | OGL-TypeScript | OGL | |
77 | | -------------- | ------ | --------------------------------------------- |
78 | | 0.1.40 | 0.0.71 | |
79 | | 0.1.39 | 0.0.70 | |
80 | | 0.1.38 | 0.0.69 | |
81 | | 0.1.37 | 0.0.68 | |
82 | | 0.1.36 | 0.0.67 | |
83 | | 0.1.35 | 0.0.65 | |
84 | | 0.1.34 | 0.0.64 | |
85 | | 0.1.33 | 0.0.63 | |
86 | | 0.1.32 | 0.0.62 | |
87 | | 0.1.31 | 0.0.61 | build with [vite](https://vitejs.dev/) |
88 | | 0.1.20 | 0.0.60 | |
89 | | 0.1.19 | 0.0.59 | |
90 | | 0.1.18 | 0.0.58 | |
91 | | 0.1.17 | 0.0.57 | |
92 | | 0.1.16 | | add computeVertexNormal method to Geometry |
93 | | 0.1.15 | 0.0.56 | |
94 | | 0.1.14 | 0.0.55 | |
95 | | 0.1.13 | 0.0.54 | |
96 | | 0.1.12 | 0.0.53 | |
97 | | 0.1.11 | 0.0.52 | |
98 | | 0.1.10 | 0.0.51 | |
99 | | 0.1.9 | 0.0.50 | |
100 | | 0.1.8 | 0.0.49 | |
101 | | 0.1.7 | 0.0.48 | |
102 | | 0.1.6 | 0.0.47 | |
103 | | 0.1.5 | 0.0.46 | |
104 | | 0.1.4 | 0.0.45 | |
105 | | 0.1.3 | 0.0.43 | |
106 | | 0.1.2 | 0.0.42 | |
107 | | 0.1.1 | 0.0.40 | |
108 |
--------------------------------------------------------------------------------
/code-complete.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/code-complete.gif
--------------------------------------------------------------------------------
/examples/anisotropic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/anisotropic/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Plane } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | attribute vec2 uv;
5 | attribute vec3 position;
6 |
7 | uniform mat4 modelViewMatrix;
8 | uniform mat4 projectionMatrix;
9 |
10 | varying vec2 vUv;
11 |
12 | void main() {
13 | vUv = uv;
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
15 | }
16 | `;
17 |
18 | const fragment = /* glsl */ `
19 | precision highp float;
20 |
21 | uniform sampler2D tMap;
22 | uniform sampler2D tMapA;
23 | uniform float fSlide;
24 |
25 | varying vec2 vUv;
26 |
27 | void main() {
28 | vec3 tex = texture2D(tMap, vUv).rgb;
29 | vec3 texA = texture2D(tMapA, vUv).rgb;
30 |
31 | gl_FragColor.rgb = mix(tex, texA, step(fSlide, vUv.x)) + 0.1;
32 | gl_FragColor.a = 1.0;
33 | }
34 | `;
35 |
36 | const renderer = new Renderer({ dpr: 2 });
37 | const gl = renderer.gl;
38 | document.body.appendChild(gl.canvas);
39 | gl.clearColor(1, 1, 1, 1);
40 |
41 | const camera = new Camera(gl);
42 | camera.position.set(0, 0, 1);
43 |
44 | function resize() {
45 | renderer.setSize(window.innerWidth, window.innerHeight);
46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
47 | }
48 | window.addEventListener('resize', resize, false);
49 | resize();
50 |
51 | const scene = new Transform();
52 |
53 | // By default, anisotropy is not active
54 | const texture = new Texture(gl);
55 |
56 | // Pass in number of anisotropic samples to activate
57 | const textureAnisotropy = new Texture(gl, { anisotropy: 16 });
58 |
59 | const img = new Image();
60 | img.src = '../../assets/grid.jpg';
61 | img.onload = () => {
62 | texture.image = img;
63 | textureAnisotropy.image = img;
64 | };
65 |
66 | const geometry = new Plane(gl);
67 |
68 | const program = new Program(gl, {
69 | vertex,
70 | fragment,
71 | uniforms: {
72 | tMap: { value: texture },
73 | tMapA: { value: textureAnisotropy },
74 | fSlide: { value: 0.5 },
75 | },
76 | });
77 |
78 | const mesh = new Mesh(gl, {
79 | geometry: geometry,
80 | program: program,
81 | });
82 |
83 | mesh.scale.set(1, 2, 1);
84 | mesh.rotation.set(-1.5, 0, 0);
85 |
86 | mesh.setParent(scene);
87 |
88 | gl.canvas.addEventListener('mousemove', (event) => {
89 | const x = (2 * event.x) / gl.canvas.width;
90 | program.uniforms.fSlide.value = x;
91 | });
92 |
93 | requestAnimationFrame(update);
94 | function update(t) {
95 | requestAnimationFrame(update);
96 | renderer.render({ scene, camera });
97 | }
98 |
99 | document.getElementsByClassName('Info')[0].innerHTML = 'Anisotropic';
100 | document.getElementsByClassName('Info split')[0].innerHTML = 'Texture anisotropy: None Texture anisotropy: 16';
101 | document.title = 'OGL • Anisotropic';
102 |
--------------------------------------------------------------------------------
/examples/base-primitives/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/base-primitives/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Program, Mesh, Plane, Sphere, Box, Cylinder, Orbit } from '../../src/index';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 | uniform mat3 normalMatrix;
13 |
14 | varying vec3 vNormal;
15 |
16 | void main() {
17 | vNormal = normalize(normalMatrix * normal);
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 |
26 | varying vec3 vNormal;
27 |
28 | void main() {
29 | vec3 normal = normalize(vNormal);
30 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6)));
31 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1;
32 | gl_FragColor.a = 1.0;
33 | }
34 | `;
35 |
36 | const renderer = new Renderer({ dpr: 2 });
37 | const gl = renderer.gl;
38 | document.body.appendChild(gl.canvas);
39 | gl.clearColor(1, 1, 1, 1);
40 |
41 | const camera = new Camera(gl, { fov: 35 });
42 | camera.position.set(0, 1, 7);
43 | camera.lookAt([0, 0, 0]);
44 | const controls = new Orbit(camera);
45 |
46 | function resize() {
47 | renderer.setSize(window.innerWidth, window.innerHeight);
48 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
49 | }
50 | window.addEventListener('resize', resize, false);
51 | resize();
52 |
53 | const scene = new Transform();
54 |
55 | const planeGeometry = new Plane(gl);
56 | const sphereGeometry = new Sphere(gl);
57 | const cubeGeometry = new Box(gl);
58 | const cylinderGeometry = new Cylinder(gl);
59 |
60 | const program = new Program(gl, {
61 | vertex,
62 | fragment,
63 |
64 | // Don't cull faces so that plane is double sided - default is gl.BACK
65 | cullFace: null,
66 | });
67 |
68 | const plane = new Mesh(gl, { geometry: planeGeometry, program });
69 | plane.position.set(0, 1.3, 0);
70 | plane.setParent(scene);
71 |
72 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program });
73 | sphere.position.set(1.3, 0, 0);
74 | sphere.setParent(scene);
75 |
76 | const cube = new Mesh(gl, { geometry: cubeGeometry, program });
77 | cube.position.set(0, -1.3, 0);
78 | cube.setParent(scene);
79 |
80 | const cylinder = new Mesh(gl, { geometry: cylinderGeometry, program });
81 | cylinder.position.set(-1.3, 0, 0);
82 | cylinder.setParent(scene);
83 |
84 | requestAnimationFrame(update);
85 | function update() {
86 | requestAnimationFrame(update);
87 | controls.update();
88 |
89 | plane.rotation.y -= 0.02;
90 | sphere.rotation.y -= 0.03;
91 | cube.rotation.y -= 0.04;
92 | cylinder.rotation.y -= 0.02;
93 |
94 | renderer.render({ scene, camera });
95 | }
96 |
97 | document.getElementsByClassName('Info')[0].innerHTML = 'Base Primitives - Plane, Cube, Sphere, Cylinder';
98 | document.title = 'OGL • Base Primitives - Plane, Cube, Sphere, Cylinder';
99 |
--------------------------------------------------------------------------------
/examples/compressed-textures/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/compressed-textures/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Plane, Orbit, TextureLoader } from '../../src';
2 |
3 | document.getElementsByClassName('Info')[0].innerHTML = 'Compressed Textures.';
4 | document.title = 'OGL • Compressed Textures';
5 |
6 | const vertex = /* glsl */ `
7 | attribute vec2 uv;
8 | attribute vec3 position;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 |
13 | varying vec2 vUv;
14 |
15 | void main() {
16 | vUv = uv;
17 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
18 | }
19 | `;
20 |
21 | const fragment = /* glsl */ `
22 | precision highp float;
23 |
24 | uniform sampler2D tMap;
25 |
26 | varying vec2 vUv;
27 |
28 | void main() {
29 | gl_FragColor = texture2D(tMap, vUv * 2.0);
30 | }
31 | `;
32 |
33 | const renderer = new Renderer({ dpr: 2 });
34 | const gl = renderer.gl;
35 | document.body.appendChild(gl.canvas);
36 | gl.clearColor(1, 1, 1, 1);
37 |
38 | const camera = new Camera(gl, { fov: 45 });
39 | camera.position.set(-1, 0.5, 2);
40 |
41 | const controls = new Orbit(camera);
42 |
43 | function resize() {
44 | renderer.setSize(window.innerWidth, window.innerHeight);
45 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
46 | }
47 |
48 | window.addEventListener('resize', resize, false);
49 | resize();
50 |
51 | const scene = new Transform();
52 |
53 | // Compressed textures use the KTXTexture (Khronos Texture) class
54 | // For generation, use https://github.com/TimvanScherpenzeel/texture-compressor
55 |
56 | // For a handy method that handles loading for you, use the TextureLoader.load() method.
57 | // Either pass a `src` string to load it (regardless of support)
58 | // const texture = TextureLoader.load(gl, {
59 | // src: 'assets/compressed/s3tc-m-y.ktx',
60 | // });
61 |
62 | // Or pass in an object of extension:src key-values, and the function will use the first
63 | // supported format in the list - so order by preference!
64 | const texture = TextureLoader.load(gl, {
65 | src: {
66 | s3tc: '../../assets/compressed/s3tc-m-y.ktx',
67 | etc: '../../assets/compressed/etc-m-y.ktx',
68 | pvrtc: '../../assets/compressed/pvrtc-m-y.ktx',
69 | jpg: '../../assets/compressed/uv.jpg',
70 | },
71 | wrapS: gl.REPEAT,
72 | wrapT: gl.REPEAT,
73 | });
74 | // A console warning will show when no supported format was supplied
75 |
76 | // `loaded` property is a promise resolved when the file is loaded and processed
77 | // texture.loaded.then(() => console.log('loaded'));
78 |
79 | // You can check which format was applied using the `ext` property
80 | document.body.querySelector('.Info').textContent += ` Supported format chosen: '${texture.ext}'.`;
81 |
82 | // For direct use of the KTXTexture class, you first need to activate the extensions
83 | // TextureLoader.getSupportedExtensions();
84 |
85 | // Then create an empty texture
86 | // const texture = new KTXTexture(gl);
87 |
88 | // Then, when the buffer has loaded, parse it using the parseBuffer method
89 | // fetch(src)
90 | // .then(res => res.arrayBuffer())
91 | // .then(buffer => texture.parseBuffer(buffer));
92 |
93 | const geometry = new Plane(gl);
94 |
95 | const program = new Program(gl, {
96 | vertex,
97 | fragment,
98 | uniforms: {
99 | tMap: { value: texture },
100 | },
101 | cullFace: null,
102 | });
103 |
104 | const mesh = new Mesh(gl, { geometry, program });
105 | mesh.setParent(scene);
106 |
107 | requestAnimationFrame(update);
108 | function update(t) {
109 | requestAnimationFrame(update);
110 |
111 | controls.update();
112 | renderer.render({ scene, camera });
113 | }
114 |
--------------------------------------------------------------------------------
/examples/compute-vertex-normal/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/compute-vertex-normal/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, OGLRenderingContext, Geometry, Camera, Transform, Program, Mesh, Orbit, Torus } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 | uniform mat3 normalMatrix;
13 |
14 | varying vec3 vNormal;
15 |
16 | void main() {
17 | vNormal = normalize(normalMatrix * normal);
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 | varying vec3 vNormal;
26 | void main() {
27 | vec3 normal = normalize(vNormal);
28 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6)));
29 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1;
30 | gl_FragColor.a = 1.0;
31 | }
32 | `;
33 |
34 | let renderer: Renderer;
35 | let camera: Camera;
36 | let gl: OGLRenderingContext;
37 | let scene: Transform;
38 | let controls;
39 |
40 | initOGL();
41 | addTrimesh();
42 | animate();
43 |
44 | function initOGL() {
45 | renderer = new Renderer({ dpr: 2 });
46 | gl = renderer.gl;
47 | document.body.appendChild(gl.canvas);
48 | gl.clearColor(1, 1, 1, 1);
49 | camera = new Camera(gl);
50 | camera.perspective({
51 | fov: 30,
52 | aspect: gl.drawingBufferWidth / gl.drawingBufferHeight,
53 | near: 1,
54 | far: 10000,
55 | });
56 | camera.position.set(0, 3, 20);
57 | camera.up.set(0, 1, 0);
58 | camera.lookAt([0, 0, 0]);
59 | controls = new Orbit(camera);
60 | scene = new Transform();
61 | }
62 |
63 | function addTrimesh() {
64 | const program = new Program(gl, {
65 | vertex,
66 | fragment,
67 | // Don't cull faces so that plane is double sided - default is gl.BACK
68 | cullFace: null,
69 | });
70 |
71 | let triGeo = new Torus(gl, {
72 | radius: 1,
73 | tube: 0.3,
74 | radialSegments: 16,
75 | tubularSegments: 16,
76 | });
77 |
78 | const trimesh = new Mesh(gl, { geometry: triGeo, program });
79 | trimesh.setParent(scene);
80 | trimesh.position.x = -2; // left
81 |
82 | // copy Torus geometry without normal
83 | const vertices = triGeo.attributes.position.data;
84 | const indices = triGeo.attributes.index.data;
85 | let geometry = new Geometry(gl, {
86 | position: { size: 3, data: vertices },
87 | index: { data: indices },
88 | // normal: { size: 3, data: normals }
89 | });
90 |
91 | // runtime compute normal attribute
92 | geometry.computeVertexNormals();
93 |
94 | const trimesh2 = new Mesh(gl, { geometry, program });
95 | trimesh2.setParent(scene);
96 | trimesh2.position.x = 2; // right
97 | }
98 |
99 | function animate(time?) {
100 | requestAnimationFrame(animate);
101 | controls.update();
102 | renderer.render({ scene, camera });
103 | }
104 |
105 | function resize() {
106 | renderer.setSize(window.innerWidth, window.innerHeight);
107 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
108 | }
109 | window.addEventListener('resize', resize, false);
110 | resize();
111 |
112 | document.title = 'OGL • Compute Vertex Normal';
113 |
--------------------------------------------------------------------------------
/examples/cube-map/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/cube-map/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Orbit, Box } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | attribute vec3 position;
5 |
6 | uniform mat4 modelViewMatrix;
7 | uniform mat4 projectionMatrix;
8 |
9 | varying vec3 vDir;
10 |
11 | void main() {
12 | vDir = normalize(position);
13 |
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
15 | }
16 | `;
17 |
18 | const fragment = /* glsl */ `
19 | precision highp float;
20 |
21 | // uniform type is samplerCube rather than sampler2D
22 | uniform samplerCube tMap;
23 |
24 | varying vec3 vDir;
25 |
26 | void main() {
27 |
28 | // sample function is textureCube rather than texture2D
29 | vec3 tex = textureCube(tMap, vDir).rgb;
30 |
31 | gl_FragColor.rgb = tex;
32 | gl_FragColor.a = 1.0;
33 | }
34 | `;
35 |
36 | const renderer = new Renderer({ dpr: 2 });
37 | const gl = renderer.gl;
38 | document.body.appendChild(gl.canvas);
39 | gl.clearColor(1, 1, 1, 1);
40 |
41 | const camera = new Camera(gl, { fov: 45 });
42 | camera.position.set(-2, 1, -3);
43 |
44 | const controls = new Orbit(camera, { ease: 1 });
45 |
46 | function resize() {
47 | renderer.setSize(window.innerWidth, window.innerHeight);
48 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
49 | }
50 | window.addEventListener('resize', resize, false);
51 | resize();
52 |
53 | const scene = new Transform();
54 |
55 | // Create an empty texture using the gl.TEXTURE_CUBE_MAP target
56 | const texture = new Texture(gl, {
57 | target: gl.TEXTURE_CUBE_MAP,
58 | });
59 |
60 | loadImages();
61 | async function loadImages() {
62 | function loadImage(src) {
63 | return new Promise((res) => {
64 | const img = new Image();
65 | img.onload = () => res(img);
66 | img.src = src;
67 | });
68 | }
69 |
70 | // Must be in this order (it's a WebGL thing)
71 | // gl.TEXTURE_CUBE_MAP_POSITIVE_X Right
72 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_X Left
73 | // gl.TEXTURE_CUBE_MAP_POSITIVE_Y Top
74 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_Y Bottom
75 | // gl.TEXTURE_CUBE_MAP_POSITIVE_Z Back
76 | // gl.TEXTURE_CUBE_MAP_NEGATIVE_Z Front
77 |
78 | const images = await Promise.all([
79 | loadImage('../../assets/cube/posx.jpg'),
80 | loadImage('../../assets/cube/negx.jpg'),
81 | loadImage('../../assets/cube/posy.jpg'),
82 | loadImage('../../assets/cube/negy.jpg'),
83 | loadImage('../../assets/cube/posz.jpg'),
84 | loadImage('../../assets/cube/negz.jpg'),
85 | ]);
86 |
87 | // Once all are loaded, we can update the texture image, which will upload them
88 | texture.image = images;
89 | }
90 |
91 | const geometry = new Box(gl);
92 |
93 | const program = new Program(gl, {
94 | vertex,
95 | fragment,
96 | uniforms: {
97 | tMap: { value: texture },
98 | },
99 | cullFace: null,
100 | });
101 |
102 | const skybox = new Mesh(gl, { geometry, program });
103 | skybox.setParent(scene);
104 | skybox.scale.set(20);
105 |
106 | const mesh = new Mesh(gl, { geometry, program });
107 | mesh.setParent(scene);
108 |
109 | requestAnimationFrame(update);
110 | function update(t) {
111 | requestAnimationFrame(update);
112 |
113 | controls.update();
114 |
115 | mesh.rotation.y += 0.003;
116 | renderer.render({ scene, camera });
117 | }
118 |
119 | document.getElementsByClassName('Info')[0].innerHTML = 'Cube Map. Texture by Humus';
120 | document.title = 'OGL • Cube Map';
121 |
--------------------------------------------------------------------------------
/examples/curves/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/curves/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Program, Mesh, Sphere, Polyline, Orbit, Vec3, Color, Curve } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 |
10 | uniform mat3 normalMatrix;
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 |
14 | varying vec3 vNormal;
15 |
16 | void main() {
17 | vNormal = normalize(normalMatrix * normal);
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 |
26 | varying vec3 vNormal;
27 |
28 | void main() {
29 | gl_FragColor.rgb = normalize(vNormal);
30 | gl_FragColor.a = 1.0;
31 | }
32 | `;
33 |
34 | const renderer = new Renderer({ dpr: 2 });
35 | const gl = renderer.gl;
36 | document.body.appendChild(gl.canvas);
37 | gl.clearColor(1, 1, 1, 1);
38 |
39 | const camera = new Camera(gl, { fov: 35 });
40 | camera.position.set(0, 0, 5);
41 |
42 | // Create controls and pass parameters
43 | const controls = new Orbit(camera, {
44 | target: new Vec3(0, 0, 0),
45 | });
46 |
47 | function resize() {
48 | renderer.setSize(window.innerWidth, window.innerHeight);
49 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
50 | }
51 | window.addEventListener('resize', resize, false);
52 | resize();
53 |
54 | const scene = new Transform();
55 |
56 | const sphereGeometry = new Sphere(gl);
57 |
58 | const program = new Program(gl, {
59 | vertex,
60 | fragment,
61 |
62 | // Don't cull faces so that plane is double sided - default is gl.BACK
63 | cullFace: null,
64 | });
65 |
66 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program });
67 | sphere.setParent(scene);
68 |
69 | const curve = new Curve({
70 | points: [new Vec3(0, 0.5, 0), new Vec3(0, 1, 1), new Vec3(0, -1, 1), new Vec3(0, -0.5, 0)],
71 | type: Curve.CUBICBEZIER,
72 | });
73 | const points = curve.getPoints(20);
74 |
75 | curve.type = Curve.CATMULLROM;
76 | const points2 = curve.getPoints(20);
77 |
78 | curve.type = Curve.QUADRATICBEZIER;
79 | const points3 = curve.getPoints(20);
80 |
81 | const polyline = new Polyline(gl, {
82 | points,
83 | uniforms: {
84 | uColor: { value: new Color('#f00') },
85 | uThickness: { value: 3 },
86 | },
87 | });
88 |
89 | const polyline2 = new Polyline(gl, {
90 | points: points2,
91 | uniforms: {
92 | uColor: { value: new Color('#00f') },
93 | uThickness: { value: 2 },
94 | },
95 | });
96 |
97 | const polyline3 = new Polyline(gl, {
98 | points: points3,
99 | uniforms: {
100 | uColor: { value: new Color('#0f0') },
101 | uThickness: { value: 4 },
102 | },
103 | });
104 |
105 | for (let i = 0; i <= 60; i++) {
106 | const p = [polyline, polyline2, polyline3][i % 3];
107 | const mesh = new Mesh(gl, { geometry: p.geometry, program: p.program });
108 | mesh.setParent(sphere);
109 | mesh.rotation.y = (i * Math.PI) / 60;
110 | }
111 |
112 | requestAnimationFrame(update);
113 | function update() {
114 | requestAnimationFrame(update);
115 | sphere.rotation.y -= 0.01;
116 | controls.update();
117 | renderer.render({ scene, camera });
118 | }
119 |
120 | document.getElementsByClassName('Info')[0].innerHTML = 'Curves';
121 | document.title = 'OGL • Curves';
122 |
--------------------------------------------------------------------------------
/examples/draw-modes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/draw-modes/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Geometry, Program, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec2 uv;
8 | attribute vec3 position;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 |
13 | varying vec2 vUv;
14 |
15 | void main() {
16 | vUv = uv;
17 |
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 |
20 | // gl_PointSize only applicable for gl.POINTS draw mode
21 | gl_PointSize = 5.0;
22 | }
23 | `;
24 |
25 | const fragment = /* glsl */ `
26 | precision highp float;
27 | precision highp int;
28 |
29 | uniform float uTime;
30 |
31 | varying vec2 vUv;
32 |
33 | void main() {
34 | gl_FragColor.rgb = 0.5 + 0.3 * sin(vUv.yxx + uTime) + vec3(0.2, 0.0, 0.1);
35 | gl_FragColor.a = 1.0;
36 | }
37 | `;
38 |
39 | const renderer = new Renderer();
40 | const gl = renderer.gl;
41 | document.body.appendChild(gl.canvas);
42 | gl.clearColor(1, 1, 1, 1);
43 |
44 | const camera = new Camera(gl, { fov: 15 });
45 | camera.position.z = 15;
46 |
47 | function resize() {
48 | renderer.setSize(window.innerWidth, window.innerHeight);
49 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
50 | }
51 | window.addEventListener('resize', resize, false);
52 | resize();
53 |
54 | const scene = new Transform();
55 |
56 | // Geometry is an indexed square, comprised of 4 vertices.
57 | const geometry = new Geometry(gl, {
58 | position: { size: 3, data: new Float32Array([-0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, 0.5, 0, 0.5, -0.5, 0]) },
59 | uv: { size: 2, data: new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]) },
60 | index: { data: new Uint16Array([0, 1, 2, 1, 3, 2]) },
61 | });
62 |
63 | const program = new Program(gl, {
64 | vertex,
65 | fragment,
66 | uniforms: {
67 | uTime: { value: 0 },
68 | },
69 | });
70 |
71 | // gl.POINTS: draws 4 points (actually draws 6, with 2 duplicates due to the geometry indices)
72 | const points = new Mesh(gl, { mode: gl.POINTS, geometry, program });
73 | points.setParent(scene);
74 | points.position.set(-1, 1, 0);
75 |
76 | // gl.LINES: draws 3 lines - a line between each pair of vertices.
77 | // Ideal use for separated lines.
78 | const lineStrip = new Mesh(gl, { mode: gl.LINES, geometry, program });
79 | lineStrip.setParent(scene);
80 | lineStrip.position.set(1, 1, 0);
81 |
82 | // gl.LINE_LOOP: draws 6 lines (1 unavoidable overlap for squares).
83 | const lineLoop = new Mesh(gl, { mode: gl.LINE_LOOP, geometry, program });
84 | lineLoop.setParent(scene);
85 | lineLoop.position.set(-1, -1, 0);
86 |
87 | // gl.TRIANGLES: draws a triangle between each set of 3 vertices.
88 | // Used as the default draw mode, so doesn't really need to be passed in as a param.
89 | const triangles = new Mesh(gl, { mode: gl.TRIANGLES, geometry, program });
90 | triangles.setParent(scene);
91 | triangles.position.set(1, -1, 0);
92 |
93 | // OTHER MODES NOT FEATURED:
94 | // gl.LINE_STRIP: Draws a straight line to the next vertex. Does not connect first and last vertices like gl.LINE_LOOP,
95 |
96 | // gl.TRIANGLE_STRIP: draws triangles in a criss-cross pattern. Ideal for ribbons.
97 | // For example, in order to draw a rectangle, only 4 vertices needed (unlike 6 with gl.TRIANGLES).
98 | // The 4 vertices should follow the below pattern.
99 | //
100 | // 0--2
101 | // | /|
102 | // |/ |
103 | // 1--3
104 |
105 | // gl.TRIANGLE_FAN: draws triangles in a fan pattern. Ideal for small polygons.
106 | // For the rectangle example, similarly to gl.TRIANGLE_STRIP, only 4 vertices needed. However the
107 | // pattern differs as per below.
108 | //
109 | // 1--2
110 | // | /|
111 | // |/ |
112 | // 0--3
113 |
114 | requestAnimationFrame(update);
115 | function update(t) {
116 | requestAnimationFrame(update);
117 |
118 | program.uniforms.uTime.value = t * 0.001;
119 | renderer.render({ scene, camera });
120 | }
121 |
122 | document.getElementsByClassName('Info')[0].innerHTML = 'Draw Modes';
123 | document.title = 'OGL • Draw Modes';
124 |
--------------------------------------------------------------------------------
/examples/flat-shading-matcap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/fog/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/fresnel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/fresnel/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Program, Mesh, Sphere, Color } from '../../src';
2 |
3 | const params = {
4 | backgroundColor: new Color('#B6D8F2'),
5 | baseColor: new Color('#B6D8F2'),
6 | fresnelColor: new Color('#F7F6CF'),
7 | fresnelFactor: 1.5,
8 | };
9 |
10 | {
11 | const renderer = new Renderer({ dpr: 2 });
12 | const gl = renderer.gl;
13 | document.body.appendChild(gl.canvas);
14 | gl.clearColor(...params.backgroundColor, 1);
15 |
16 | const camera = new Camera(gl, { fov: 35 });
17 | camera.position.set(0, 1, 7);
18 | camera.lookAt([0, 0, 0]);
19 |
20 | function resize() {
21 | renderer.setSize(window.innerWidth, window.innerHeight);
22 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
23 | }
24 | window.addEventListener('resize', resize, false);
25 | resize();
26 |
27 | const scene = new Transform();
28 |
29 | const sphereGeometry = new Sphere(gl, {
30 | radius: 1,
31 | widthSegments: 128,
32 | });
33 |
34 | const vertex = /* glsl */ `
35 | attribute vec3 position;
36 | attribute vec2 uv;
37 | attribute vec3 normal;
38 |
39 | uniform mat4 modelViewMatrix;
40 | uniform mat4 projectionMatrix;
41 | uniform mat3 normalMatrix;
42 |
43 | varying vec2 vUv;
44 | varying vec3 vNormal;
45 | varying vec3 vPosition;
46 |
47 | void main() {
48 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
49 |
50 | vPosition = position;
51 | vUv = uv;
52 | vNormal = normalMatrix * normal;
53 | }
54 | `;
55 |
56 | const fragment = /* glsl */ `
57 | precision highp float;
58 |
59 | varying vec2 vUv;
60 | varying vec3 vNormal;
61 | varying vec3 vPosition;
62 |
63 | uniform vec3 fresnelColor;
64 | uniform vec3 baseColor;
65 | uniform float powerOfFactor;
66 | uniform vec3 cameraPosition;
67 |
68 | void main() {
69 | vec3 viewDirection = normalize(cameraPosition - vPosition.xyz);
70 | float fresnelFactor = dot(viewDirection, normalize(vNormal));
71 |
72 | float inversefresnelFactor = clamp(1. - fresnelFactor, 0., 1.);
73 |
74 | // Shaping function
75 | fresnelFactor = pow(fresnelFactor, powerOfFactor);
76 | inversefresnelFactor = pow(inversefresnelFactor, powerOfFactor);
77 |
78 | gl_FragColor = vec4(fresnelFactor * baseColor + inversefresnelFactor * fresnelColor, 1.);
79 | }
80 | `;
81 |
82 | const uniforms = {
83 | fresnelColor: { value: params.fresnelColor },
84 | baseColor: { value: params.baseColor },
85 | powerOfFactor: { value: params.fresnelFactor },
86 | };
87 |
88 | const program = new Program(gl, {
89 | vertex: vertex,
90 | fragment: fragment,
91 | uniforms: uniforms,
92 | });
93 |
94 | const sphere = new Mesh(gl, { geometry: sphereGeometry, program });
95 | sphere.setParent(scene);
96 |
97 | requestAnimationFrame(update);
98 | function update() {
99 | requestAnimationFrame(update);
100 | sphere.rotation.x += 0.01;
101 | sphere.rotation.z += 0.02;
102 |
103 | renderer.render({ scene, camera });
104 | }
105 | }
106 |
107 | document.getElementsByClassName('Info')[0].innerHTML = 'Simple Fresnel Shader';
108 | document.title = 'Frustum Culling. Model by Google Poly';
109 |
--------------------------------------------------------------------------------
/examples/frustum-culling/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/gpgpu-particles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/high-mesh-count/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/examples/high-mesh-count/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Program, Mesh, Box } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 | uniform mat3 normalMatrix;
13 |
14 | varying vec3 vNormal;
15 |
16 | void main() {
17 | vNormal = normalize(normalMatrix * normal);
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 |
26 | varying vec3 vNormal;
27 |
28 | void main() {
29 | vec3 normal = normalize(vNormal);
30 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6)));
31 | gl_FragColor.rgb = vec3(0.2, 0.8, 1.0) + lighting * 0.1;
32 | gl_FragColor.a = 1.0;
33 | }
34 | `;
35 |
36 | const renderer = new Renderer({});
37 | const gl = renderer.gl;
38 | document.body.appendChild(gl.canvas);
39 | gl.clearColor(1, 1, 1, 1);
40 |
41 | const camera = new Camera(gl, { fov: 35, far: 3000 });
42 |
43 | function resize() {
44 | renderer.setSize(window.innerWidth, window.innerHeight);
45 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
46 | }
47 | window.addEventListener('resize', resize, false);
48 | resize();
49 |
50 | const scene = new Transform();
51 |
52 | // Create base geometry
53 | const cubeGeometry = new Box(gl);
54 |
55 | // Using the shader from the base primitive example
56 | const program = new Program(gl, {
57 | vertex,
58 | fragment,
59 | });
60 |
61 | // mesh container
62 | let meshes = [];
63 |
64 | function setMeshCount(count) {
65 | // sanitize input
66 | count = parseInt(count) || 1000;
67 |
68 | // remove old meshes
69 | for (let i = 0; i < meshes.length; ++i) scene.removeChild(meshes[i]);
70 | meshes = [];
71 |
72 | // create our meshes according to input
73 | for (let i = 0; i < count; ++i) {
74 | let mesh = new Mesh(gl, { geometry: cubeGeometry, program });
75 |
76 | // position meshes in a random position between -100 / +100 in each dimension
77 | mesh.position.set(-100 + Math.random() * 200, -100 + Math.random() * 200, -100 + Math.random() * 200);
78 | mesh.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
79 | scene.addChild(mesh);
80 | meshes.push(mesh);
81 | }
82 |
83 | // set input counter value to make sure
84 | (document.getElementById('meshCountInput') as any).value = count;
85 | }
86 |
87 | (window as any).setMeshCount = setMeshCount;
88 |
89 | setMeshCount(1000);
90 |
91 | requestAnimationFrame(update);
92 | function update() {
93 | requestAnimationFrame(update);
94 |
95 | // rotate camera
96 | let time = performance.now() / 30000;
97 | camera.position.set(Math.sin(time) * 180, 80, Math.cos(time) * 180);
98 | camera.lookAt([0, 0, 0]);
99 |
100 | // rotate meshes
101 | for (let i = 0; i < meshes.length; ++i) {
102 | meshes[i].rotation.x += 0.01;
103 | meshes[i].rotation.y += 0.01;
104 | }
105 |
106 | renderer.render({ scene, camera });
107 | }
108 |
109 | document.getElementsByClassName('Info')[0].innerHTML = 'Performance - High mesh count';
110 | document.title = 'OGL • Performance - High mesh count';
111 |
--------------------------------------------------------------------------------
/examples/indexed-vs-non-indexed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/indexed-vs-non-indexed/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Geometry, Program, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec2 uv;
8 | attribute vec3 position;
9 |
10 | uniform mat4 modelViewMatrix;
11 | uniform mat4 projectionMatrix;
12 |
13 | varying vec2 vUv;
14 |
15 | void main() {
16 | vUv = uv;
17 |
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 |
26 | uniform float uTime;
27 |
28 | varying vec2 vUv;
29 |
30 | void main() {
31 | gl_FragColor.rgb = 0.5 + 0.3 * sin(vUv.yxx + uTime) + vec3(0.2, 0.0, 0.1);
32 | gl_FragColor.a = 1.0;
33 | }
34 | `;
35 |
36 | const renderer = new Renderer();
37 | const gl = renderer.gl;
38 | document.body.appendChild(gl.canvas);
39 | gl.clearColor(1, 1, 1, 1);
40 |
41 | const camera = new Camera(gl, { fov: 15 });
42 | camera.position.z = 15;
43 |
44 | function resize() {
45 | renderer.setSize(window.innerWidth, window.innerHeight);
46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
47 | }
48 | window.addEventListener('resize', resize, false);
49 | resize();
50 |
51 | const scene = new Transform();
52 |
53 | // 4 vertices, using 6 indices to designate 2 triangles using the default gl.TRIANGLES draw mode
54 | const indexedGeometry = new Geometry(gl, {
55 | position: {
56 | size: 3,
57 | data: new Float32Array([
58 | -0.5,
59 | 0.5,
60 | 0, // vertex 0
61 | -0.5,
62 | -0.5,
63 | 0, // vertex 1
64 | 0.5,
65 | 0.5,
66 | 0, // vertex 2
67 | 0.5,
68 | -0.5,
69 | 0, // vertex 3
70 | ]),
71 | },
72 | uv: {
73 | size: 2,
74 | data: new Float32Array([
75 | 0,
76 | 1, // vertex 0
77 | 1,
78 | 1, // vertex 1
79 | 0,
80 | 0, // vertex 2
81 | 1,
82 | 0, // vertex 3
83 | ]),
84 | },
85 |
86 | // the indices attribute must use the name 'index' to be treated as an ELEMENT_ARRAY_BUFFER
87 | index: { data: new Uint16Array([0, 1, 2, 1, 3, 2]) },
88 | });
89 |
90 | // To recreate the same square using non-indexed geometry, 6 vertices need to be passed in, including 2 duplicates.
91 | // If only the original 4 vertices were supplied, only one triangle would be drawn.
92 | const nonIndexedGeometry = new Geometry(gl, {
93 | position: {
94 | size: 3,
95 | data: new Float32Array([
96 | -0.5,
97 | 0.5,
98 | 0, // vertex 0
99 | -0.5,
100 | -0.5,
101 | 0, // vertex 1
102 | 0.5,
103 | 0.5,
104 | 0, // vertex 2
105 | -0.5,
106 | -0.5,
107 | 0, // vertex 1
108 | 0.5,
109 | -0.5,
110 | 0, // vertex 3
111 | 0.5,
112 | 0.5,
113 | 0, // vertex 2
114 | ]),
115 | },
116 | uv: {
117 | size: 2,
118 | data: new Float32Array([
119 | 0,
120 | 1, // vertex 0
121 | 1,
122 | 1, // vertex 1
123 | 0,
124 | 0, // vertex 2
125 | 1,
126 | 1, // vertex 1
127 | 1,
128 | 0, // vertex 3
129 | 0,
130 | 0, // vertex 2
131 | ]),
132 | },
133 | });
134 |
135 | const program = new Program(gl, {
136 | vertex,
137 | fragment,
138 | uniforms: {
139 | uTime: { value: 0 },
140 | },
141 | });
142 |
143 | const indexedMesh = new Mesh(gl, { geometry: indexedGeometry, program });
144 | indexedMesh.setParent(scene);
145 | indexedMesh.position.y = 0.9;
146 |
147 | const nonIndexedMesh = new Mesh(gl, { geometry: nonIndexedGeometry, program });
148 | nonIndexedMesh.setParent(scene);
149 | nonIndexedMesh.position.y = -0.9;
150 |
151 | requestAnimationFrame(update);
152 | function update(t) {
153 | requestAnimationFrame(update);
154 |
155 | program.uniforms.uTime.value = t * 0.001;
156 | renderer.render({ scene, camera });
157 | }
158 |
159 | document.getElementsByClassName('Info')[0].innerHTML = 'Indexed vs Non-Indexed';
160 | document.title = 'OGL • Indexed vs Non-Indexed';
161 |
--------------------------------------------------------------------------------
/examples/instancing-gpu-picking/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/instancing/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/load-gltf/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/load-json/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/load-json/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Geometry, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec2 uv;
8 | attribute vec3 position;
9 | attribute vec3 normal;
10 |
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 | uniform mat3 normalMatrix;
14 |
15 | varying vec2 vUv;
16 | varying vec3 vNormal;
17 |
18 | void main() {
19 | vUv = uv;
20 | vNormal = normalize(normalMatrix * normal);
21 |
22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
23 | }
24 | `;
25 |
26 | const fragment = /* glsl */ `
27 | precision highp float;
28 | precision highp int;
29 |
30 | uniform float uTime;
31 | uniform sampler2D tMap;
32 |
33 | varying vec2 vUv;
34 | varying vec3 vNormal;
35 |
36 | void main() {
37 | vec3 normal = normalize(vNormal);
38 | vec3 tex = texture2D(tMap, vUv).rgb;
39 |
40 | vec3 light = normalize(vec3(0.5, 1.0, -0.3));
41 | float shading = dot(normal, light) * 0.15;
42 | gl_FragColor.rgb = tex + shading;
43 | gl_FragColor.a = 1.0;
44 | }
45 | `;
46 |
47 | const renderer = new Renderer({ dpr: 2 });
48 | const gl = renderer.gl;
49 | document.body.appendChild(gl.canvas);
50 | gl.clearColor(1, 1, 1, 1);
51 |
52 | const camera = new Camera(gl, { fov: 35 });
53 | camera.position.set(8, 5, 15);
54 | camera.lookAt([0, 1.5, 0]);
55 |
56 | function resize() {
57 | renderer.setSize(window.innerWidth, window.innerHeight);
58 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
59 | }
60 | window.addEventListener('resize', resize, false);
61 | resize();
62 |
63 | const scene = new Transform();
64 |
65 | const texture = new Texture(gl);
66 | const img = new Image();
67 | img.onload = () => (texture.image = img);
68 | img.src = '../../assets/fox.jpg';
69 |
70 | const program = new Program(gl, {
71 | vertex,
72 | fragment,
73 | uniforms: {
74 | tMap: { value: texture },
75 | },
76 | });
77 |
78 | let mesh;
79 | loadModel();
80 | async function loadModel() {
81 | const data = await (await fetch(`../../assets/fox.json`)).json();
82 |
83 | const geometry = new Geometry(gl, {
84 | position: { size: 3, data: new Float32Array(data.position) },
85 | uv: { size: 2, data: new Float32Array(data.uv) },
86 | normal: { size: 3, data: new Float32Array(data.normal) },
87 | });
88 |
89 | mesh = new Mesh(gl, { geometry, program });
90 | mesh.setParent(scene);
91 | }
92 |
93 | requestAnimationFrame(update);
94 | function update() {
95 | requestAnimationFrame(update);
96 |
97 | if (mesh) mesh.rotation.y -= 0.005;
98 | renderer.render({ scene, camera });
99 | }
100 |
101 | document.getElementsByClassName('Info')[0].innerHTML = 'Load JSON (Javascript Object Notation). Model by Google Poly';
102 | document.title = 'OGL • Load JSON';
103 |
--------------------------------------------------------------------------------
/examples/mouse-flowmap/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/mrt/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/msdf-text/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/normal-maps/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/orbit-controls/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/orbit-controls/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Geometry, Mesh, Vec3, Orbit } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec2 uv;
8 | attribute vec3 position;
9 | attribute vec3 normal;
10 |
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 | uniform mat3 normalMatrix;
14 |
15 | varying vec2 vUv;
16 | varying vec3 vNormal;
17 |
18 | void main() {
19 | vUv = uv;
20 | vNormal = normalize(normalMatrix * normal);
21 |
22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
23 | }
24 | `;
25 |
26 | const fragment = /* glsl */ `
27 | precision highp float;
28 | precision highp int;
29 |
30 | uniform float uTime;
31 | uniform sampler2D tMap;
32 |
33 | varying vec2 vUv;
34 | varying vec3 vNormal;
35 |
36 | void main() {
37 | vec3 normal = normalize(vNormal);
38 | vec3 tex = texture2D(tMap, vUv).rgb;
39 |
40 | vec3 light = normalize(vec3(0.5, 1.0, -0.3));
41 | float shading = dot(normal, light) * 0.15;
42 | gl_FragColor.rgb = tex + shading;
43 | gl_FragColor.a = 1.0;
44 | }
45 | `;
46 |
47 | const renderer = new Renderer({ dpr: 2 });
48 | const gl = renderer.gl;
49 | document.body.appendChild(gl.canvas);
50 | gl.clearColor(1, 1, 1, 1);
51 |
52 | const camera = new Camera(gl, { fov: 45 });
53 | camera.position.set(-2, 1, 2);
54 |
55 | // Create controls and pass parameters
56 | const controls = new Orbit(camera, {
57 | target: new Vec3(0, 0.7, 0),
58 | });
59 |
60 | function resize() {
61 | renderer.setSize(window.innerWidth, window.innerHeight);
62 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
63 | }
64 | window.addEventListener('resize', resize, false);
65 | resize();
66 |
67 | const scene = new Transform();
68 |
69 | const texture = new Texture(gl);
70 | const img = new Image();
71 | img.onload = () => (texture.image = img);
72 | img.src = '../../assets/macaw.jpg';
73 |
74 | const program = new Program(gl, {
75 | vertex,
76 | fragment,
77 | uniforms: {
78 | tMap: { value: texture },
79 | },
80 | cullFace: null,
81 | });
82 |
83 | loadModel();
84 | async function loadModel() {
85 | const data = await (await fetch(`../../assets/macaw.json`)).json();
86 |
87 | const geometry = new Geometry(gl, {
88 | position: { size: 3, data: new Float32Array(data.position) },
89 | uv: { size: 2, data: new Float32Array(data.uv) },
90 | normal: { size: 3, data: new Float32Array(data.normal) },
91 | });
92 |
93 | let mesh = new Mesh(gl, { geometry, program });
94 | mesh.setParent(scene);
95 | }
96 |
97 | requestAnimationFrame(update);
98 | function update() {
99 | requestAnimationFrame(update);
100 |
101 | // Need to update controls every frame
102 | controls.update();
103 | renderer.render({ scene, camera });
104 | }
105 |
106 | document.getElementsByClassName('Info')[0].innerHTML = 'Orbit Controls. Model by Google Poly.';
107 | document.title = 'OGL • Orbit Controls';
108 |
--------------------------------------------------------------------------------
/examples/particles/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/particles/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Geometry, Program, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec4 random;
9 |
10 | uniform mat4 modelMatrix;
11 | uniform mat4 viewMatrix;
12 | uniform mat4 projectionMatrix;
13 | uniform float uTime;
14 |
15 | varying vec4 vRandom;
16 |
17 | void main() {
18 | vRandom = random;
19 |
20 | // positions are 0->1, so make -1->1
21 | vec3 pos = position * 2.0 - 1.0;
22 |
23 | // Scale towards camera to be more interesting
24 | pos.z *= 10.0;
25 |
26 | // modelMatrix is one of the automatically attached uniforms when using the Mesh class
27 | vec4 mPos = modelMatrix * vec4(pos, 1.0);
28 |
29 | // add some movement in world space
30 | float t = uTime * 0.6;
31 | mPos.x += sin(t * random.z + 6.28 * random.w) * mix(0.1, 1.5, random.x);
32 | mPos.y += sin(t * random.y + 6.28 * random.x) * mix(0.1, 1.5, random.w);
33 | mPos.z += sin(t * random.w + 6.28 * random.y) * mix(0.1, 1.5, random.z);
34 |
35 | // get the model view position so that we can scale the points off into the distance
36 | vec4 mvPos = viewMatrix * mPos;
37 | gl_PointSize = 300.0 / length(mvPos.xyz) * (random.x + 0.1);
38 | gl_Position = projectionMatrix * mvPos;
39 | }
40 | `;
41 |
42 | const fragment = /* glsl */ `
43 | precision highp float;
44 | precision highp int;
45 |
46 | uniform float uTime;
47 |
48 | varying vec4 vRandom;
49 |
50 | void main() {
51 | vec2 uv = gl_PointCoord.xy;
52 |
53 | float circle = smoothstep(0.5, 0.4, length(uv - 0.5)) * 0.8;
54 |
55 | gl_FragColor.rgb = 0.8 + 0.2 * sin(uv.yxx + uTime + vRandom.y * 6.28) + vec3(0.1, 0.0, 0.3);
56 | gl_FragColor.a = circle;
57 | }
58 | `;
59 |
60 | const renderer = new Renderer({ depth: false });
61 | const gl = renderer.gl;
62 | document.body.appendChild(gl.canvas);
63 | gl.clearColor(1, 1, 1, 1);
64 |
65 | const camera = new Camera(gl, { fov: 15 });
66 | camera.position.z = 15;
67 |
68 | function resize() {
69 | renderer.setSize(window.innerWidth, window.innerHeight);
70 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
71 | }
72 | window.addEventListener('resize', resize, false);
73 | resize();
74 |
75 | const num = 100;
76 | const position = new Float32Array(num * 3);
77 | const random = new Float32Array(num * 4);
78 |
79 | for (let i = 0; i < num; i++) {
80 | position.set([Math.random(), Math.random(), Math.random()], i * 3);
81 | random.set([Math.random(), Math.random(), Math.random(), Math.random()], i * 4);
82 | }
83 |
84 | const geometry = new Geometry(gl, {
85 | position: { size: 3, data: position },
86 | random: { size: 4, data: random },
87 | });
88 |
89 | const program = new Program(gl, {
90 | vertex,
91 | fragment,
92 | uniforms: {
93 | uTime: { value: 0 },
94 | },
95 | transparent: true,
96 | depthTest: false,
97 | });
98 |
99 | // Make sure mode is gl.POINTS
100 | const particles = new Mesh(gl, { mode: gl.POINTS, geometry, program });
101 |
102 | requestAnimationFrame(update);
103 | function update(t) {
104 | requestAnimationFrame(update);
105 |
106 | // add some slight overall movement to be more interesting
107 | particles.rotation.x = Math.sin(t * 0.0002) * 0.1;
108 | particles.rotation.y = Math.cos(t * 0.0005) * 0.15;
109 | particles.rotation.z += 0.01;
110 |
111 | program.uniforms.uTime.value = t * 0.001;
112 | renderer.render({ scene: particles, camera });
113 | }
114 |
115 | document.getElementsByClassName('Info')[0].innerHTML = 'Particles';
116 | document.title = 'OGL • Particles';
117 |
--------------------------------------------------------------------------------
/examples/pbr/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/point-lighting/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/polylines/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/post-bloom/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/post-fluid-distortion/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/post-fxaa/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/post-fxaa/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Program, Mesh, Vec2, Post, Box, NormalProgram } from '../../src';
2 |
3 | const fragment = /* glsl */ `
4 | precision highp float;
5 |
6 | // Default uniform for previous pass is 'tMap'.
7 | // Can change this using the 'textureUniform' property
8 | // when adding a pass.
9 | uniform sampler2D tMap;
10 |
11 | uniform vec2 uResolution;
12 |
13 | varying vec2 vUv;
14 |
15 | vec4 fxaa(sampler2D tex, vec2 uv, vec2 resolution) {
16 | vec2 pixel = vec2(1) / resolution;
17 |
18 | vec3 l = vec3(0.299, 0.587, 0.114);
19 | float lNW = dot(texture2D(tex, uv + vec2(-1, -1) * pixel).rgb, l);
20 | float lNE = dot(texture2D(tex, uv + vec2( 1, -1) * pixel).rgb, l);
21 | float lSW = dot(texture2D(tex, uv + vec2(-1, 1) * pixel).rgb, l);
22 | float lSE = dot(texture2D(tex, uv + vec2( 1, 1) * pixel).rgb, l);
23 | float lM = dot(texture2D(tex, uv).rgb, l);
24 | float lMin = min(lM, min(min(lNW, lNE), min(lSW, lSE)));
25 | float lMax = max(lM, max(max(lNW, lNE), max(lSW, lSE)));
26 |
27 | vec2 dir = vec2(
28 | -((lNW + lNE) - (lSW + lSE)),
29 | ((lNW + lSW) - (lNE + lSE))
30 | );
31 |
32 | float dirReduce = max((lNW + lNE + lSW + lSE) * 0.03125, 0.0078125);
33 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
34 | dir = min(vec2(8, 8), max(vec2(-8, -8), dir * rcpDirMin)) * pixel;
35 |
36 | vec3 rgbA = 0.5 * (
37 | texture2D(tex, uv + dir * (1.0 / 3.0 - 0.5)).rgb +
38 | texture2D(tex, uv + dir * (2.0 / 3.0 - 0.5)).rgb);
39 |
40 | vec3 rgbB = rgbA * 0.5 + 0.25 * (
41 | texture2D(tex, uv + dir * -0.5).rgb +
42 | texture2D(tex, uv + dir * 0.5).rgb);
43 |
44 | float lB = dot(rgbB, l);
45 |
46 | return mix(
47 | vec4(rgbB, 1),
48 | vec4(rgbA, 1),
49 | max(sign(lB - lMin), 0.0) * max(sign(lB - lMax), 0.0)
50 | );
51 | }
52 |
53 | void main() {
54 | vec4 raw = texture2D(tMap, vUv);
55 | vec4 aa = fxaa(tMap, vUv, uResolution);
56 |
57 | // Split screen in half to show side-by-side comparison
58 | gl_FragColor = mix(raw, aa, step(0.5, vUv.x));
59 |
60 | // Darken left side a tad for clarity
61 | gl_FragColor -= step(vUv.x, 0.5) * 0.1;
62 | }
63 | `;
64 |
65 | const renderer = new Renderer({ dpr: 1 });
66 | const gl = renderer.gl;
67 | document.body.appendChild(gl.canvas);
68 | gl.clearColor(1, 1, 1, 1);
69 |
70 | const camera = new Camera(gl, { fov: 35 });
71 | camera.position.set(0, 1, 5);
72 | camera.lookAt([0, 0, 0]);
73 |
74 | // Post copies the current renderer values (width, height, dpr) if none are passed in
75 | const post = new Post(gl);
76 |
77 | // Create uniform for pass
78 | const resolution = { value: new Vec2() };
79 |
80 | function resize() {
81 | renderer.setSize(window.innerWidth, window.innerHeight);
82 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
83 |
84 | // Need to resize post as render targets need to be re-created
85 | post.resize();
86 | resolution.value.set(gl.canvas.width, gl.canvas.height);
87 | }
88 | window.addEventListener('resize', resize, false);
89 | resize();
90 |
91 | const geometry = new Box(gl);
92 | const mesh = new Mesh(gl, { geometry, program: NormalProgram(gl) });
93 |
94 | // Add pass like you're creating a Program. Then use the 'enabled'
95 | // property to toggle the pass.
96 | const pass = post.addPass({
97 | // If not passed in, pass will use the default vertex/fragment
98 | // shaders found within the class.
99 | fragment,
100 | uniforms: {
101 | uResolution: resolution,
102 | },
103 | });
104 |
105 | requestAnimationFrame(update);
106 | function update() {
107 | requestAnimationFrame(update);
108 |
109 | mesh.rotation.y -= 0.005;
110 | mesh.rotation.x -= 0.01;
111 |
112 | // Replace Renderer.render with post.render. Use the same arguments.
113 | post.render({ scene: mesh, camera });
114 | }
115 |
116 | document.getElementsByClassName('Info')[0].innerHTML = 'Post FXAA (Fast Approximate Anti-Aliasing)';
117 | document.title = 'OGL • Post FXAA (Fast Approximate Anti-Aliasing)';
118 |
--------------------------------------------------------------------------------
/examples/raycasting/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/render-to-texture/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/render-to-texture/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, RenderTarget, Program, Mesh, Box } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 | attribute vec2 uv;
10 |
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 | uniform mat3 normalMatrix;
14 |
15 | varying vec2 vUv;
16 | varying vec3 vNormal;
17 |
18 | void main() {
19 | vUv = uv;
20 | vNormal = normalize(normalMatrix * normal);
21 |
22 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
23 | }
24 | `;
25 |
26 | const fragment = /* glsl */ `
27 | precision highp float;
28 | precision highp int;
29 |
30 | uniform sampler2D tMap;
31 |
32 | varying vec2 vUv;
33 | varying vec3 vNormal;
34 |
35 | void main() {
36 | vec3 normal = normalize(vNormal);
37 | float lighting = 0.2 * dot(normal, normalize(vec3(-0.3, 0.8, 0.6)));
38 | vec3 tex = texture2D(tMap, vUv).rgb;
39 | gl_FragColor.rgb = tex + lighting + vec3(vUv - 0.5, 0.0) * 0.1;
40 | gl_FragColor.a = 1.0;
41 | }
42 | `;
43 |
44 | const renderer = new Renderer({ dpr: 2 });
45 | const gl = renderer.gl;
46 | document.body.appendChild(gl.canvas);
47 |
48 | const camera = new Camera(gl, { fov: 35 });
49 | camera.position.set(0, 1, 5);
50 | camera.lookAt([0, 0, 0]);
51 |
52 | const targetCamera = new Camera(gl, { fov: 35 });
53 | targetCamera.position.set(0, 1, 5);
54 | targetCamera.lookAt([0, 0, 0]);
55 |
56 | function resize() {
57 | renderer.setSize(window.innerWidth, window.innerHeight);
58 |
59 | // Only update aspect of target camera, as first scene will be drawn to a square render target
60 | targetCamera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
61 | }
62 | window.addEventListener('resize', resize, false);
63 | resize();
64 |
65 | const geometry = new Box(gl);
66 |
67 | // A little data texture with 4 colors just to keep things interesting
68 | const texture = new Texture(gl, {
69 | image: new Uint8Array([191, 25, 54, 255, 96, 18, 54, 255, 96, 18, 54, 255, 37, 13, 53, 255]),
70 | width: 2,
71 | height: 2,
72 | magFilter: gl.NEAREST,
73 | });
74 |
75 | const program = new Program(gl, {
76 | vertex,
77 | fragment,
78 | uniforms: {
79 | tMap: { value: texture },
80 | },
81 | });
82 |
83 | // Create render target framebuffer.
84 | // Uses canvas size by default and doesn't automatically resize.
85 | // To resize, re-create target
86 | const target = new RenderTarget(gl, {
87 | width: 512,
88 | height: 512,
89 | });
90 |
91 | const targetProgram = new Program(gl, {
92 | vertex,
93 | fragment,
94 | uniforms: {
95 | tMap: { value: target.texture },
96 | },
97 | });
98 |
99 | const mesh = new Mesh(gl, { geometry, program });
100 | const targetMesh = new Mesh(gl, { geometry, program: targetProgram });
101 |
102 | requestAnimationFrame(update);
103 | function update() {
104 | requestAnimationFrame(update);
105 |
106 | mesh.rotation.y -= 0.02;
107 | targetMesh.rotation.y -= 0.005;
108 | targetMesh.rotation.x -= 0.01;
109 |
110 | // Set background for first render to target
111 | gl.clearColor(0.15, 0.05, 0.2, 1);
112 |
113 | // Add target property to render call
114 | renderer.render({ scene: mesh, camera, target });
115 |
116 | // Change to final background
117 | gl.clearColor(1, 1, 1, 1);
118 |
119 | // Omit target to render to canvas
120 | renderer.render({ scene: targetMesh, camera: targetCamera });
121 | }
122 |
123 | document.getElementsByClassName('Info')[0].innerHTML = 'Render to texture';
124 | document.title = 'OGL • Render to texture';
125 |
--------------------------------------------------------------------------------
/examples/scene-graph/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/scene-graph/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Program, Transform, Mesh, Box, Sphere } from '../../src';
2 |
3 | type SpeedMesh = { speed?: number } & Mesh;
4 |
5 | const vertex = /* glsl */ `
6 | precision highp float;
7 | precision highp int;
8 |
9 | attribute vec3 position;
10 | attribute vec3 normal;
11 |
12 | uniform mat4 modelViewMatrix;
13 | uniform mat4 projectionMatrix;
14 | uniform mat3 normalMatrix;
15 |
16 | varying vec3 vNormal;
17 | varying vec4 vMVPos;
18 |
19 | void main() {
20 | vNormal = normalize(normalMatrix * normal);
21 |
22 | vMVPos = modelViewMatrix * vec4(position, 1.0);
23 | gl_Position = projectionMatrix * vMVPos;
24 | }
25 | `;
26 |
27 | const fragment = /* glsl */ `
28 | precision highp float;
29 | precision highp int;
30 |
31 | varying vec3 vNormal;
32 | varying vec4 vMVPos;
33 |
34 | void main() {
35 | vec3 normal = normalize(vNormal);
36 | float lighting = dot(normal, normalize(vec3(-0.3, 0.8, 0.6)));
37 | vec3 color = vec3(1.0, 0.5, 0.2) * (1.0 - 0.5 * lighting) + vMVPos.xzy * 0.1;
38 |
39 | float dist = length(vMVPos);
40 | float fog = smoothstep(4.0, 10.0, dist);
41 | color = mix(color, vec3(1.0), fog);
42 |
43 | gl_FragColor.rgb = color;
44 | gl_FragColor.a = 1.0;
45 | }
46 | `;
47 |
48 | const renderer = new Renderer({ dpr: 2 });
49 | const gl = renderer.gl;
50 | document.body.appendChild(gl.canvas);
51 | gl.clearColor(1, 1, 1, 1);
52 |
53 | // The Camera class extends Transform. See Below for more on Transform.
54 | const camera = new Camera(gl, { fov: 35 });
55 | camera.position.set(0, 1, 7);
56 | camera.lookAt([0, 0, 0]);
57 |
58 | function resize() {
59 | renderer.setSize(window.innerWidth, window.innerHeight);
60 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
61 | }
62 | window.addEventListener('resize', resize, false);
63 | resize();
64 |
65 | const sphereGeometry = new Sphere(gl, { radius: 0.15 });
66 | const cubeGeometry = new Box(gl, { width: 0.3, height: 0.3, depth: 0.3 });
67 |
68 | const program = new Program(gl, {
69 | vertex,
70 | fragment,
71 | });
72 |
73 | // The scene hierarchy is controlled by the Transform class
74 | // To create scenes, groups, null pointers etc, use Transform
75 | const scene = new Transform();
76 |
77 | // The Mesh class extends the Transform class, and so shares the scene graph functionality
78 | const sphere: SpeedMesh = new Mesh(gl, { geometry: sphereGeometry, program }) as SpeedMesh;
79 | sphere.speed = -0.5;
80 |
81 | // Use .setParent to add transform as a child of another transform
82 | sphere.setParent(scene);
83 | // scene.addChild(sphere); // also works
84 |
85 | const shapes = [sphere];
86 |
87 | // Create random array of shapes
88 | for (let i = 0; i < 50; i++) {
89 | const geometry = Math.random() > 0.5 ? cubeGeometry : sphereGeometry;
90 | const shape: SpeedMesh = new Mesh(gl, { geometry, program });
91 | shape.scale.set(Math.random() * 0.3 + 0.7);
92 | shape.position.set((Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3, (Math.random() - 0.5) * 3);
93 | shape.speed = (Math.random() - 0.5) * 0.7;
94 |
95 | // Attach them to a random, previously created shape
96 | shape.setParent(shapes[Math.floor(Math.random() * shapes.length)]);
97 | shapes.push(shape);
98 | }
99 |
100 | requestAnimationFrame(update);
101 | function update(t) {
102 | requestAnimationFrame(update);
103 |
104 | shapes.forEach((shape) => {
105 | shape.rotation.y += 0.03 * shape.speed;
106 | shape.rotation.x += 0.04 * shape.speed;
107 | shape.rotation.z += 0.01 * shape.speed;
108 | });
109 |
110 | // The 'scene' property in the render call expects any Transform.
111 | // Therefore can be a Mesh instance as well.
112 | renderer.render({ scene, camera });
113 | }
114 |
115 | document.getElementsByClassName('Info')[0].innerHTML = 'Scene Graph Hierarchy';
116 | document.title = 'OGL • Scene Graph Hierarchy';
117 |
--------------------------------------------------------------------------------
/examples/shadow-maps/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/skinning/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/skydome/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/skydome/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Geometry, Program, Mesh, Camera, Transform, Texture } from '../../src';
2 | import { Sphere, Orbit } from '../../src';
3 |
4 | const vertex = /* glsl */ `
5 | precision highp float;
6 | precision highp int;
7 |
8 | attribute vec2 uv;
9 | attribute vec3 position;
10 | attribute vec3 normal;
11 |
12 | uniform mat4 modelViewMatrix;
13 | uniform mat4 projectionMatrix;
14 |
15 | varying vec2 vUv;
16 |
17 | void main() {
18 | vUv = uv;
19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
20 | }
21 | `;
22 |
23 | const fragment = /* glsl */ `
24 | precision highp float;
25 | precision highp int;
26 |
27 | uniform sampler2D tMap;
28 |
29 | varying vec2 vUv;
30 |
31 | void main() {
32 | vec3 tex = texture2D(tMap, vUv).rgb;
33 |
34 | gl_FragColor.rgb = tex;
35 | gl_FragColor.a = 1.0;
36 | }
37 | `;
38 |
39 | const renderer = new Renderer({ dpr: 2 });
40 | const gl = renderer.gl;
41 | document.body.appendChild(gl.canvas);
42 | gl.clearColor(1, 1, 1, 1);
43 |
44 | const camera = new Camera(gl, { fov: 45 });
45 | camera.position.set(0, 0, 8);
46 |
47 | const controls = new Orbit(camera, {
48 | enablePan: false,
49 | enableZoom: false,
50 | });
51 |
52 | function resize() {
53 | renderer.setSize(window.innerWidth, window.innerHeight);
54 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
55 | }
56 | window.addEventListener('resize', resize, false);
57 | resize();
58 |
59 | const scene = new Transform();
60 |
61 | // Texture is equirectangular
62 | const texture = new Texture(gl);
63 | const img = new Image();
64 | img.onload = () => (texture.image = img);
65 | img.src = '../../assets/sky.jpg';
66 |
67 | // Use Sphere geometry to render equirectangular textures
68 | const geometry = new Sphere(gl, { radius: 1, widthSegments: 64 });
69 |
70 | const program = new Program(gl, {
71 | vertex,
72 | fragment,
73 | uniforms: {
74 | tMap: { value: texture },
75 | },
76 |
77 | // Need inside of sphere to be visible
78 | cullFace: null,
79 | });
80 |
81 | // A smaller sphere in the center just to help illustrate
82 | const mesh = new Mesh(gl, { geometry, program });
83 | mesh.setParent(scene);
84 |
85 | // Camera will dwell inside skybox
86 | const skybox = new Mesh(gl, { geometry, program });
87 | skybox.scale.set(10);
88 | skybox.setParent(scene);
89 |
90 | requestAnimationFrame(update);
91 | function update() {
92 | requestAnimationFrame(update);
93 |
94 | controls.update();
95 | renderer.render({ scene, camera });
96 | }
97 |
98 | document.getElementsByClassName('Info')[0].innerHTML =
99 | 'Skydome. Image credit charlesashaw.';
100 | document.title = 'OGL • Skydome';
101 |
--------------------------------------------------------------------------------
/examples/sort-transparency/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/sort-transparency/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture, Program, Mesh, Color, Plane } from '../../src';
2 |
3 | type SpeedMesh = Mesh & { speed?: number };
4 |
5 | const vertex = /* glsl */ `
6 | precision highp float;
7 | precision highp int;
8 |
9 | attribute vec2 uv;
10 | attribute vec3 position;
11 |
12 | uniform mat4 modelViewMatrix;
13 | uniform mat4 projectionMatrix;
14 |
15 | varying vec2 vUv;
16 | varying vec4 vMVPos;
17 |
18 | void main() {
19 | vUv = uv;
20 | vec3 pos = position;
21 | float dist = pow(length(vUv - 0.5), 2.0) - 0.25;
22 | pos.z += dist * 0.5;
23 | vMVPos = modelViewMatrix * vec4(pos, 1.0);
24 | gl_Position = projectionMatrix * vMVPos;
25 | }
26 | `;
27 |
28 | const fragment = /* glsl */ `
29 | precision highp float;
30 | precision highp int;
31 |
32 | uniform sampler2D tMap;
33 | uniform vec3 uColor;
34 |
35 | varying vec2 vUv;
36 | varying vec4 vMVPos;
37 |
38 | void main() {
39 | float alpha = texture2D(tMap, vUv).g;
40 |
41 | vec3 color = uColor + vMVPos.xzy * 0.05;
42 |
43 | float dist = length(vMVPos);
44 | float fog = smoothstep(5.0, 10.0, dist);
45 | color = mix(color, vec3(1.0), fog);
46 |
47 | gl_FragColor.rgb = color;
48 | gl_FragColor.a = alpha;
49 | if (alpha < 0.01) discard;
50 | }
51 | `;
52 |
53 | // This demonstrates the default geometry sorting before rendering.
54 | // It does not include sorting between faces/points within a single geometry.
55 |
56 | const renderer = new Renderer({ dpr: 2 });
57 | const gl = renderer.gl;
58 | document.body.appendChild(gl.canvas);
59 | gl.clearColor(1, 1, 1, 1);
60 |
61 | const camera = new Camera(gl, { fov: 35 });
62 | camera.position.set(0, 0, 7);
63 | camera.rotation.z = -0.3;
64 |
65 | function resize() {
66 | renderer.setSize(window.innerWidth, window.innerHeight);
67 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
68 | }
69 | window.addEventListener('resize', resize, false);
70 | resize();
71 |
72 | const scene = new Transform();
73 |
74 | const geometry = new Plane(gl, {
75 | widthSegments: 10,
76 | heightSegments: 10,
77 | });
78 |
79 | const texture = new Texture(gl);
80 | const img = new Image();
81 | img.onload = () => (texture.image = img);
82 | img.src = '../../assets/leaf.jpg';
83 |
84 | const program = new Program(gl, {
85 | vertex,
86 | fragment,
87 | uniforms: {
88 | tMap: { value: texture },
89 | uColor: { value: new Color('#ffc219') },
90 | },
91 | transparent: true,
92 | cullFace: null,
93 | });
94 |
95 | const meshes = [];
96 |
97 | for (let i = 0; i < 50; i++) {
98 | const mesh: SpeedMesh = new Mesh(gl, { geometry, program });
99 | mesh.position.set((Math.random() - 0.5) * 3, (Math.random() - 0.5) * 6, (Math.random() - 0.5) * 3);
100 | mesh.rotation.set(0, (Math.random() - 0.5) * 6.28, (Math.random() - 0.5) * 6.28);
101 | mesh.scale.set(Math.random() * 0.5 + 0.2);
102 | mesh.speed = Math.random() * 1.5 + 0.2;
103 | mesh.setParent(scene);
104 | meshes.push(mesh);
105 | }
106 |
107 | requestAnimationFrame(update);
108 | function update(t) {
109 | requestAnimationFrame(update);
110 |
111 | meshes.forEach((mesh) => {
112 | mesh.rotation.y += 0.05;
113 | mesh.rotation.z += 0.05;
114 | // mesh.position.y -= 0.02 * mesh.speed;
115 | if (mesh.position.y < -3) mesh.position.y += 6;
116 | });
117 |
118 | scene.rotation.y += 0.015;
119 |
120 | // Objects are automatically sorted if renderer.sort === true
121 | renderer.render({ scene, camera });
122 | }
123 |
124 | document.getElementsByClassName('Info')[0].innerHTML = 'Sort Transparency';
125 | document.title = 'OGL • Sort Transparency';
126 |
--------------------------------------------------------------------------------
/examples/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/textures/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/torus/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/torus/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Orbit, Transform, Program, Torus, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | precision highp float;
5 | precision highp int;
6 |
7 | attribute vec3 position;
8 | attribute vec3 normal;
9 |
10 | uniform mat3 normalMatrix;
11 | uniform mat4 modelViewMatrix;
12 | uniform mat4 projectionMatrix;
13 |
14 | varying vec3 vNormal;
15 |
16 | void main() {
17 | vNormal = normalize(normalMatrix * normal);
18 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
19 | }
20 | `;
21 |
22 | const fragment = /* glsl */ `
23 | precision highp float;
24 | precision highp int;
25 |
26 | varying vec3 vNormal;
27 |
28 | void main() {
29 | gl_FragColor.rgb = normalize(vNormal);
30 | gl_FragColor.a = 1.0;
31 | }
32 | `;
33 |
34 | {
35 | const renderer = new Renderer({ dpr: 2 });
36 | const gl = renderer.gl;
37 | document.body.appendChild(gl.canvas);
38 | gl.clearColor(1, 1, 1, 1);
39 |
40 | const camera = new Camera(gl, { fov: 35 });
41 | camera.position.set(5, 3, 6);
42 | camera.lookAt([0, 0, 0]);
43 |
44 | function resize() {
45 | renderer.setSize(window.innerWidth, window.innerHeight);
46 | camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });
47 | }
48 | window.addEventListener('resize', resize, false);
49 | resize();
50 |
51 | const scene = new Transform();
52 | const controls = new Orbit(camera);
53 |
54 | const program = new Program(gl, {
55 | vertex,
56 | fragment,
57 | });
58 |
59 | const torusGeometry = new Torus(gl, {
60 | radius: 1,
61 | tube: 0.4,
62 | radialSegments: 16,
63 | tubularSegments: 32,
64 | });
65 |
66 | const torus = new Mesh(gl, { geometry: torusGeometry, program });
67 | torus.setParent(scene);
68 |
69 | requestAnimationFrame(update);
70 | function update() {
71 | requestAnimationFrame(update);
72 | torus.rotation.x += 0.001;
73 | torus.rotation.y += 0.005;
74 | torus.rotation.z += 0.003;
75 |
76 | controls.update();
77 |
78 | renderer.render({ scene, camera });
79 | }
80 | }
81 |
82 | document.getElementsByClassName('Info')[0].innerHTML = 'Torus';
83 | document.title = 'OGL • Torus';
84 |
--------------------------------------------------------------------------------
/examples/triangle-screen-shader/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/triangle-screen-shader/index.ts:
--------------------------------------------------------------------------------
1 | import { Renderer, Triangle, Program, Color, Mesh } from '../../src';
2 |
3 | const vertex = /* glsl */ `
4 | attribute vec2 uv;
5 | attribute vec2 position;
6 |
7 | varying vec2 vUv;
8 |
9 | void main() {
10 | vUv = uv;
11 | gl_Position = vec4(position, 0, 1);
12 | }
13 | `;
14 |
15 | const fragment = /* glsl */ `
16 | precision highp float;
17 |
18 | uniform float uTime;
19 | uniform vec3 uColor;
20 |
21 | varying vec2 vUv;
22 |
23 | void main() {
24 | gl_FragColor.rgb = 0.5 + 0.3 * cos(vUv.xyx + uTime) + uColor;
25 | gl_FragColor.a = 1.0;
26 | }
27 | `;
28 |
29 | const renderer = new Renderer();
30 | const gl = renderer.gl;
31 | document.body.appendChild(gl.canvas);
32 | gl.clearColor(1, 1, 1, 1);
33 |
34 | function resize() {
35 | renderer.setSize(window.innerWidth, window.innerHeight);
36 | }
37 | window.addEventListener('resize', resize, false);
38 | resize();
39 |
40 | // Rather than using a plane (two triangles) to cover the viewport here is a
41 | // triangle that includes -1 to 1 range for 'position', and 0 to 1 range for 'uv'.
42 | // Excess will be out of the viewport.
43 |
44 | // position uv
45 | // (-1, 3) (0, 2)
46 | // |\ |\
47 | // |__\(1, 1) |__\(1, 1)
48 | // |__|_\ |__|_\
49 | // (-1, -1) (3, -1) (0, 0) (2, 0)
50 |
51 | const geometry = new Triangle(gl);
52 |
53 | const program = new Program(gl, {
54 | vertex,
55 | fragment,
56 | uniforms: {
57 | uTime: { value: 0 },
58 | uColor: { value: new Color(0.3, 0.2, 0.5) },
59 | },
60 | });
61 |
62 | const mesh = new Mesh(gl, { geometry, program });
63 |
64 | requestAnimationFrame(update);
65 | function update(t) {
66 | requestAnimationFrame(update);
67 |
68 | program.uniforms.uTime.value = t * 0.001;
69 |
70 | // Don't need a camera if camera uniforms aren't required
71 | renderer.render({ scene: mesh });
72 | }
73 |
74 | document.getElementsByClassName('Info')[0].innerHTML = 'Triangle Screen Shader';
75 | document.title = 'OGL • Triangle Screen Shader';
76 |
--------------------------------------------------------------------------------
/examples/wireframe-shader/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/examples/wireframe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ogl-typescript",
3 | "version": "0.1.40",
4 | "description": "TypeScript port of OGL WebGL Library",
5 | "module": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "sideEffects": false,
8 | "directories": {
9 | "example": "examples"
10 | },
11 | "files": [
12 | "lib"
13 | ],
14 | "publishConfig": {
15 | "access": "public"
16 | },
17 | "scripts": {
18 | "dev": "vite",
19 | "build": "tsc",
20 | "build:examples": "vite build"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/nshen/ogl-typescript"
25 | },
26 | "keywords": [
27 | "webgl",
28 | "ogl",
29 | "ogl-typescript"
30 | ],
31 | "author": {
32 | "name": "Nathan Gordon",
33 | "email": "gordonnl@gmail.com",
34 | "url": "https://twitter.com/gordonnl"
35 | },
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/nshen/ogl-typescript/issues"
39 | },
40 | "homepage": "https://github.com/nshen/ogl-typescript",
41 | "devDependencies": {
42 | "prettier": "^2.2.1",
43 | "rimraf": "^3.0.2",
44 | "typescript": "^4.2.4",
45 | "vite": "^2.1.5"
46 | },
47 | "prettier": {
48 | "arrowParens": "always",
49 | "bracketSpacing": true,
50 | "endOfLine": "lf",
51 | "htmlWhitespaceSensitivity": "css",
52 | "printWidth": 150,
53 | "quoteProps": "as-needed",
54 | "semi": true,
55 | "singleQuote": true,
56 | "tabWidth": 4,
57 | "trailingComma": "es5",
58 | "useTabs": false
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/public/assets/acorn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/acorn.jpg
--------------------------------------------------------------------------------
/public/assets/airplane.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/airplane.jpg
--------------------------------------------------------------------------------
/public/assets/anim-format.json:
--------------------------------------------------------------------------------
1 | {
2 | frames: [
3 | {
4 | position: [bone1X, bone1Y, bone1Z, bone2X, bone2Y, bone2Z],
5 | quaternion: [2, 3, 1, 5, 6, 2],
6 | scale: [2, 3, 1, 5, 6, 2],
7 | },
8 | {
9 | position: [2, 3, 1, 5, 6, 2],
10 | quaternion: [2, 3, 1, 5, 6, 2],
11 | scale: [2, 3, 1, 5, 6, 2],
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/public/assets/compressed/astc-m-y.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/astc-m-y.ktx
--------------------------------------------------------------------------------
/public/assets/compressed/etc-m-y.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/etc-m-y.ktx
--------------------------------------------------------------------------------
/public/assets/compressed/etc1-m-y.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/etc1-m-y.ktx
--------------------------------------------------------------------------------
/public/assets/compressed/pvrtc-m-y.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/pvrtc-m-y.ktx
--------------------------------------------------------------------------------
/public/assets/compressed/s3tc-m-y.ktx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/s3tc-m-y.ktx
--------------------------------------------------------------------------------
/public/assets/compressed/uv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/compressed/uv.jpg
--------------------------------------------------------------------------------
/public/assets/croissant.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/croissant.jpg
--------------------------------------------------------------------------------
/public/assets/cube/negx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negx.jpg
--------------------------------------------------------------------------------
/public/assets/cube/negy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negy.jpg
--------------------------------------------------------------------------------
/public/assets/cube/negz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/negz.jpg
--------------------------------------------------------------------------------
/public/assets/cube/posx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posx.jpg
--------------------------------------------------------------------------------
/public/assets/cube/posy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posy.jpg
--------------------------------------------------------------------------------
/public/assets/cube/posz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/cube/posz.jpg
--------------------------------------------------------------------------------
/public/assets/earth.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth.jpg
--------------------------------------------------------------------------------
/public/assets/earth_cloud.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth_cloud.jpg
--------------------------------------------------------------------------------
/public/assets/earth_specular.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/earth_specular.jpg
--------------------------------------------------------------------------------
/public/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/favicon.png
--------------------------------------------------------------------------------
/public/assets/fonts/FiraSans-Bold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/FiraSans-Bold.png
--------------------------------------------------------------------------------
/public/assets/fonts/FiraSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/FiraSans-Bold.ttf
--------------------------------------------------------------------------------
/public/assets/fonts/raleway-bold-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-bold-webfont.woff
--------------------------------------------------------------------------------
/public/assets/fonts/raleway-bold-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-bold-webfont.woff2
--------------------------------------------------------------------------------
/public/assets/fonts/raleway-regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-regular-webfont.woff
--------------------------------------------------------------------------------
/public/assets/fonts/raleway-regular-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fonts/raleway-regular-webfont.woff2
--------------------------------------------------------------------------------
/public/assets/forest.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/forest.jpg
--------------------------------------------------------------------------------
/public/assets/fox.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/fox.jpg
--------------------------------------------------------------------------------
/public/assets/gltf/hershel.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/gltf/hershel.glb
--------------------------------------------------------------------------------
/public/assets/goat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/goat.jpg
--------------------------------------------------------------------------------
/public/assets/granite-diffuse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/granite-diffuse.jpg
--------------------------------------------------------------------------------
/public/assets/granite-normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/granite-normal.jpg
--------------------------------------------------------------------------------
/public/assets/grid.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/grid.jpg
--------------------------------------------------------------------------------
/public/assets/laputa.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/laputa.mp4
--------------------------------------------------------------------------------
/public/assets/leaf.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/leaf.jpg
--------------------------------------------------------------------------------
/public/assets/macaw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/macaw.jpg
--------------------------------------------------------------------------------
/public/assets/main.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body,
6 | html {
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | overflow-x: hidden;
10 | height: 100%;
11 | overscroll-behavior-y: contain;
12 | }
13 |
14 | body,
15 | button,
16 | p,
17 | ul {
18 | margin: 0;
19 | padding: 0;
20 | }
21 |
22 | button,
23 | select {
24 | border: none;
25 | outline: none;
26 | background: none;
27 | font-family: inherit;
28 | }
29 |
30 | a,
31 | button,
32 | input,
33 | select,
34 | textarea {
35 | -webkit-tap-highlight-color: transparent;
36 | }
37 |
38 | a {
39 | color: inherit; /* blue colors for links too */
40 | text-decoration: inherit; /* no underline */
41 | }
42 |
43 | :root {
44 | overflow-x: hidden;
45 | height: 100%;
46 | }
47 |
48 | @font-face {
49 | font-family: 'Raleway';
50 | src: url('fonts/raleway-regular-webfont.woff2') format('woff2'),
51 | url('fonts/raleway-regular-webfont.woff') format('woff');
52 | font-weight: 400;
53 | font-style: normal;
54 | }
55 |
56 | @font-face {
57 | font-family: 'Raleway';
58 | src: url('fonts/raleway-bold-webfont.woff2') format('woff2'),
59 | url('fonts/raleway-bold-webfont.woff') format('woff');
60 | font-weight: 700;
61 | font-style: normal;
62 | }
63 |
64 | body {
65 | font-family: 'Raleway', sans-serif;
66 | font-weight: 700;
67 | font-size: 14px;
68 | }
69 |
70 | .Side {
71 | position: relative;
72 | width: 300px;
73 | height: 100%;
74 | overflow: auto;
75 | padding-bottom: 100px;
76 | padding-left: 20px;
77 | padding-right: 40px;
78 | background: #fff;
79 | }
80 | .Iframe {
81 | position: absolute;
82 | left: 300px;
83 | right: 0;
84 | top: 0;
85 | bottom: 0;
86 | height: 100%;
87 | width: calc(100% - 300px);
88 | border: none;
89 | margin: 0;
90 | }
91 | .Title {
92 | font-size: 18px;
93 | margin: 20px 0;
94 | display: block;
95 | }
96 | .SubTitle {
97 | font-weight: 400;
98 | font-size: 17px;
99 | margin: 20px 0 50px;
100 | }
101 | .Section {
102 | font-weight: 400;
103 | margin: 40px 0 20px;
104 | padding-left: 15px;
105 | }
106 | .Example {
107 | margin: 10px 0;
108 | padding-left: 30px;
109 | display: block;
110 | }
111 | div.Example {
112 | opacity: 0.2;
113 | }
114 |
115 | .SideIcon,
116 | .CodeIcon {
117 | position: absolute;
118 | display: block;
119 | z-index: 1;
120 | margin: 30px;
121 | width: 50px;
122 | height: 50px;
123 | line-height: 50px;
124 | text-align: center;
125 | background: #fff;
126 | border-radius: 100%;
127 | font-size: 19px;
128 | letter-spacing: -0.1em;
129 | box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.1);
130 | }
131 | .CodeIcon {
132 | bottom: 0;
133 | right: 0;
134 | }
135 | .SideIcon {
136 | top: 20px;
137 | left: 245px;
138 | display: none;
139 | }
140 |
141 | @media (max-width: 600px) {
142 | .Side {
143 | z-index: 1;
144 | }
145 | .Iframe {
146 | left: 0;
147 | width: 100%;
148 | }
149 | .SideIcon {
150 | display: block;
151 | }
152 | [data-hideSidebar] .Side {
153 | transform: translateX(-100%);
154 | }
155 |
156 | [data-hideSidebar] .SideIcon {
157 | left: 0;
158 | transform: rotate(180deg);
159 | }
160 | }
161 |
162 | .Info {
163 | padding: 20px;
164 | position: relative;
165 | z-index: 1;
166 | }
167 | .Info a {
168 | color: rgb(39, 124, 235);
169 | }
170 | canvas {
171 | position: absolute;
172 | width: 100%;
173 | height: 100%;
174 | top: 0;
175 | left: 0;
176 | }
177 |
--------------------------------------------------------------------------------
/public/assets/matcap.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/matcap.jpg
--------------------------------------------------------------------------------
/public/assets/octopus.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/octopus.jpg
--------------------------------------------------------------------------------
/public/assets/ogl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/ogl.png
--------------------------------------------------------------------------------
/public/assets/pbr/black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/black.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-ext-color.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-color.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-ext-emissive.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-emissive.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-ext-normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-normal.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-ext-opacity.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-opacity.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-ext-rmo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-ext-rmo.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-int-color.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-color.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-int-normal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-normal.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-int-rmo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-int-rmo.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-shadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-shadow.jpg
--------------------------------------------------------------------------------
/public/assets/pbr/car-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/car-shadow.png
--------------------------------------------------------------------------------
/public/assets/pbr/lut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/lut.png
--------------------------------------------------------------------------------
/public/assets/pbr/waterfall-diffuse-RGBM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/waterfall-diffuse-RGBM.png
--------------------------------------------------------------------------------
/public/assets/pbr/waterfall-specular-RGBM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/waterfall-specular-RGBM.png
--------------------------------------------------------------------------------
/public/assets/pbr/white.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/pbr/white.jpg
--------------------------------------------------------------------------------
/public/assets/plane.json:
--------------------------------------------------------------------------------
1 | {
2 | "position": [0.0, -0.149, -1.543, -0.002, -0.288, 0.649, 0.242, 0.149, 0.589, 0.242, 0.149, 0.589, 0.681, -0.055, 0.618, 0.0, -0.149, -1.543, -0.221, 0.149, 0.589, -0.002, -0.288, 0.649, 0.0, -0.149, -1.543, -0.681, -0.055, 0.618, -0.221, 0.149, 0.589, 0.0, -0.149, -1.543],
3 | "normal": [-0.87, 0.491, 0.03, -0.87, 0.491, 0.03, -0.87, 0.491, 0.03, 0.425, 0.889, -0.172, 0.425, 0.889, -0.172, 0.425, 0.889, -0.172, 0.892, 0.451, 0.03, 0.892, 0.451, 0.03, 0.892, 0.451, 0.03, -0.409, 0.897, -0.168, -0.409, 0.897, -0.168, -0.409, 0.897, -0.168],
4 | "uv": [0.5, 1.0, 0.5, 0.0, 0.698, 0.0, 0.698, 0.0, 1.0, 0.0, 0.5, 1.0, 0.312, 0.0, 0.5, 0.0, 0.5, 1.0, 0.0, 0.0, 0.312, 0.0, 0.5, 1.0]
5 | }
--------------------------------------------------------------------------------
/public/assets/saddle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/saddle.jpg
--------------------------------------------------------------------------------
/public/assets/sky.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sky.jpg
--------------------------------------------------------------------------------
/public/assets/snout-shadow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/snout-shadow.jpg
--------------------------------------------------------------------------------
/public/assets/snout.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/snout.jpg
--------------------------------------------------------------------------------
/public/assets/sunset-diffuse-RGBM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset-diffuse-RGBM.png
--------------------------------------------------------------------------------
/public/assets/sunset-specular-RGBM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset-specular-RGBM.png
--------------------------------------------------------------------------------
/public/assets/sunset.exr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/sunset.exr
--------------------------------------------------------------------------------
/public/assets/uv.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/uv.jpg
--------------------------------------------------------------------------------
/public/assets/water.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/water.jpg
--------------------------------------------------------------------------------
/public/assets/windmill.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nshen/ogl-typescript/1638c8b9162381c80942505c6f65490e4126e056/public/assets/windmill.jpg
--------------------------------------------------------------------------------
/src/Guards.ts:
--------------------------------------------------------------------------------
1 | import { Transform } from './core/Transform';
2 | import { Mesh } from './core/Mesh';
3 | import { OGLRenderingContext } from './core/Renderer';
4 |
5 | export const isArrayLike = (term: any): term is ArrayLike => {
6 | if (term.length) return true;
7 | return false;
8 | };
9 |
10 | export const isMesh = (node: Transform | Mesh): node is Mesh => {
11 | return !!(node as any).draw;
12 | };
13 |
14 | export const isWebGl2 = (gl: OGLRenderingContext): gl is OGLRenderingContext & WebGL2RenderingContext => {
15 | return gl.renderer.isWebgl2;
16 | };
17 |
--------------------------------------------------------------------------------
/src/core/Mesh.ts:
--------------------------------------------------------------------------------
1 | import { Transform } from './Transform';
2 | import { Mat3 } from '../math/Mat3';
3 | import { Mat4 } from '../math/Mat4';
4 | import { Geometry } from './Geometry';
5 | import { Program } from './Program';
6 | import { OGLRenderingContext } from './Renderer';
7 | import { Camera } from './Camera';
8 | import { Vec3 } from '../math/Vec3';
9 | import { Vec2 } from '../math/Vec2';
10 |
11 | let ID = 0;
12 |
13 | export interface MeshOptions {
14 | geometry: Geometry;
15 | program: Program;
16 | mode: GLenum;
17 | frustumCulled: boolean;
18 | renderOrder: number;
19 | }
20 |
21 | export interface DrawOptions {
22 | camera: Camera;
23 | }
24 |
25 | export class Mesh extends Transform {
26 | name: string;
27 | numInstances;
28 |
29 | gl: OGLRenderingContext;
30 | id: number;
31 | geometry: Geometry;
32 | program: Program;
33 | mode: GLenum;
34 | frustumCulled: boolean;
35 | renderOrder: number;
36 |
37 | modelViewMatrix: Mat4;
38 | normalMatrix: Mat3;
39 |
40 | beforeRenderCallbacks: Array;
41 | afterRenderCallbacks: Array;
42 |
43 | // raycast.ts
44 | hit: Partial<{
45 | localPoint: Vec3;
46 | distance: number;
47 | point: Vec3;
48 | faceNormal: Vec3;
49 | localFaceNormal: Vec3;
50 | uv: Vec2;
51 | localNormal: Vec3;
52 | normal: Vec3;
53 | }> = null;
54 |
55 | constructor(
56 | gl: OGLRenderingContext,
57 | { geometry, program, mode = gl.TRIANGLES, frustumCulled = true, renderOrder = 0 }: Partial = {}
58 | ) {
59 | super();
60 | if (!gl.canvas) console.error('gl not passed as first argument to Mesh');
61 | this.gl = gl;
62 | this.id = ID++;
63 | this.geometry = geometry;
64 | this.program = program;
65 | this.mode = mode;
66 |
67 | // Used to skip frustum culling
68 | this.frustumCulled = frustumCulled;
69 |
70 | // Override sorting to force an order
71 | this.renderOrder = renderOrder;
72 | this.modelViewMatrix = new Mat4();
73 | this.normalMatrix = new Mat3();
74 | this.beforeRenderCallbacks = [];
75 | this.afterRenderCallbacks = [];
76 | }
77 |
78 | onBeforeRender(f) {
79 | this.beforeRenderCallbacks.push(f);
80 | return this;
81 | }
82 |
83 | onAfterRender(f) {
84 | this.afterRenderCallbacks.push(f);
85 | return this;
86 | }
87 |
88 | draw({ camera }: Partial = {}) {
89 | this.beforeRenderCallbacks.forEach((f) => f && f({ mesh: this, camera }));
90 |
91 | // Set the matrix uniforms
92 | if (camera) {
93 | // Add empty matrix uniforms to program if unset
94 | if (!this.program.uniforms.modelMatrix) {
95 | Object.assign(this.program.uniforms, {
96 | modelMatrix: { value: null },
97 | viewMatrix: { value: null },
98 | modelViewMatrix: { value: null },
99 | normalMatrix: { value: null },
100 | projectionMatrix: { value: null },
101 | cameraPosition: { value: null },
102 | });
103 | }
104 |
105 | // Set the matrix uniforms
106 | this.program.uniforms.projectionMatrix.value = camera.projectionMatrix;
107 | this.program.uniforms.cameraPosition.value = camera.worldPosition;
108 | this.program.uniforms.viewMatrix.value = camera.viewMatrix;
109 | this.modelViewMatrix.multiply(camera.viewMatrix, this.worldMatrix);
110 | this.normalMatrix.getNormalMatrix(this.modelViewMatrix);
111 | this.program.uniforms.modelMatrix.value = this.worldMatrix;
112 | this.program.uniforms.modelViewMatrix.value = this.modelViewMatrix;
113 | this.program.uniforms.normalMatrix.value = this.normalMatrix;
114 | }
115 |
116 | // determine if faces need to be flipped - when mesh scaled negatively
117 | let flipFaces = this.program.cullFace && this.worldMatrix.determinant() < 0;
118 |
119 | this.program.use({ flipFaces });
120 | this.geometry.draw({ mode: this.mode, program: this.program });
121 |
122 | this.afterRenderCallbacks.forEach((f) => f && f({ mesh: this, camera }));
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/core/Transform.ts:
--------------------------------------------------------------------------------
1 | import { Vec3 } from '../math/Vec3';
2 | import { Quat } from '../math/Quat';
3 | import { Mat4 } from '../math/Mat4';
4 | import { Euler } from '../math/Euler';
5 |
6 | export class Transform {
7 | parent: Transform;
8 | children: Transform[];
9 | visible: boolean;
10 |
11 | matrix: Mat4;
12 | worldMatrix: Mat4;
13 | matrixAutoUpdate: boolean;
14 | worldMatrixNeedsUpdate: boolean;
15 |
16 | position: Vec3;
17 | scale: Vec3;
18 | up: Vec3;
19 | quaternion: Quat;
20 | rotation: Euler;
21 |
22 | constructor() {
23 | this.parent = null;
24 | this.children = [];
25 | this.visible = true;
26 |
27 | this.matrix = new Mat4();
28 | this.worldMatrix = new Mat4();
29 | this.matrixAutoUpdate = true;
30 |
31 | this.up = new Vec3(0, 1, 0);
32 | this.position = new Vec3();
33 | this.scale = new Vec3(1);
34 |
35 | this.quaternion = new Quat();
36 | this.rotation = new Euler();
37 |
38 | this.rotation.onChange = () => this.quaternion.fromEuler(this.rotation);
39 | this.quaternion.onChange = () => this.rotation.fromQuaternion(this.quaternion);
40 | }
41 |
42 | setParent(parent, notifyParent = true) {
43 | if (this.parent && parent !== this.parent) this.parent.removeChild(this, false);
44 | this.parent = parent;
45 | if (notifyParent && parent) parent.addChild(this, false);
46 | }
47 |
48 | addChild(child: Transform, notifyChild: boolean = true) {
49 | if (!~this.children.indexOf(child)) this.children.push(child);
50 | if (notifyChild) child.setParent(this, false);
51 | }
52 |
53 | removeChild(child: Transform, notifyChild: boolean = true) {
54 | if (!!~this.children.indexOf(child)) this.children.splice(this.children.indexOf(child), 1);
55 | if (notifyChild) child.setParent(null, false);
56 | }
57 |
58 | updateMatrixWorld(force?: boolean) {
59 | if (this.matrixAutoUpdate) this.updateMatrix();
60 | if (this.worldMatrixNeedsUpdate || force) {
61 | if (this.parent === null) this.worldMatrix.copy(this.matrix);
62 | else this.worldMatrix.multiply(this.parent.worldMatrix, this.matrix);
63 | this.worldMatrixNeedsUpdate = false;
64 | force = true;
65 | }
66 |
67 | for (let i = 0, l = this.children.length; i < l; i++) {
68 | this.children[i].updateMatrixWorld(force);
69 | }
70 | }
71 |
72 | updateMatrix() {
73 | this.matrix.compose(this.quaternion, this.position, this.scale);
74 | this.worldMatrixNeedsUpdate = true;
75 | }
76 |
77 | traverse(callback: (node: Transform) => boolean | void) {
78 | // Return true in callback to stop traversing children
79 | if (callback(this)) return;
80 | for (let i = 0, l = this.children.length; i < l; i++) {
81 | this.children[i].traverse(callback);
82 | }
83 | }
84 |
85 | decompose() {
86 | this.matrix.getTranslation(this.position);
87 | this.matrix.getRotation(this.quaternion);
88 | this.matrix.getScaling(this.scale);
89 | this.rotation.fromQuaternion(this.quaternion);
90 | }
91 |
92 | lookAt(target: T, invert = false) {
93 | if (invert) this.matrix.lookAt(this.position, target, this.up);
94 | else this.matrix.lookAt(target, this.position, this.up);
95 | this.matrix.getRotation(this.quaternion);
96 | this.rotation.fromQuaternion(this.quaternion);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/extras/Animation.ts:
--------------------------------------------------------------------------------
1 | import { Vec3 } from '../math/Vec3';
2 | import { Quat } from '../math/Quat';
3 | import { BoneTransform } from './Skin';
4 |
5 | const prevPos = new Vec3();
6 | const prevRot = new Quat();
7 | const prevScl = new Vec3();
8 |
9 | const nextPos = new Vec3();
10 | const nextRot = new Quat();
11 | const nextScl = new Vec3();
12 |
13 | export interface AnimationOptions {
14 | objects: BoneTransform[];
15 | data: any;
16 | }
17 |
18 | export class Animation {
19 | objects: BoneTransform[];
20 | data: any;
21 | elapsed: number;
22 | weight: number;
23 | duration: number;
24 |
25 | constructor({ objects, data }: AnimationOptions) {
26 | this.objects = objects;
27 | this.data = data;
28 | this.elapsed = 0;
29 | this.weight = 1;
30 | this.duration = data.frames.length - 1;
31 | }
32 |
33 | update(totalWeight = 1, isSet) {
34 | const weight = isSet ? 1 : this.weight / totalWeight;
35 | const elapsed = this.elapsed % this.duration;
36 |
37 | const floorFrame = Math.floor(elapsed);
38 | const blend = elapsed - floorFrame;
39 | const prevKey = this.data.frames[floorFrame];
40 | const nextKey = this.data.frames[(floorFrame + 1) % this.duration];
41 |
42 | this.objects.forEach((object, i) => {
43 | prevPos.fromArray(prevKey.position, i * 3);
44 | prevRot.fromArray(prevKey.quaternion, i * 4);
45 | prevScl.fromArray(prevKey.scale, i * 3);
46 |
47 | nextPos.fromArray(nextKey.position, i * 3);
48 | nextRot.fromArray(nextKey.quaternion, i * 4);
49 | nextScl.fromArray(nextKey.scale, i * 3);
50 |
51 | prevPos.lerp(nextPos, blend);
52 | prevRot.slerp(nextRot, blend);
53 | prevScl.lerp(nextScl, blend);
54 |
55 | object.position.lerp(prevPos, weight);
56 | object.quaternion.slerp(prevRot, weight);
57 | object.scale.lerp(prevScl, weight);
58 | });
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/extras/Box.ts:
--------------------------------------------------------------------------------
1 | import { Geometry, AttributeMap } from '../core/Geometry';
2 | import { Plane } from './Plane';
3 | import { OGLRenderingContext } from '../core/Renderer';
4 |
5 | export type BoxOptions = {
6 | width: number;
7 | height: number;
8 | depth: number;
9 | widthSegments: number;
10 | heightSegments: number;
11 | depthSegments: number;
12 | attributes: AttributeMap;
13 | };
14 |
15 | export class Box extends Geometry {
16 | constructor(
17 | gl: OGLRenderingContext,
18 | { width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1, attributes = {} }: Partial = {}
19 | ) {
20 | const wSegs = widthSegments;
21 | const hSegs = heightSegments;
22 | const dSegs = depthSegments;
23 |
24 | const num = (wSegs + 1) * (hSegs + 1) * 2 + (wSegs + 1) * (dSegs + 1) * 2 + (hSegs + 1) * (dSegs + 1) * 2;
25 | const numIndices = (wSegs * hSegs * 2 + wSegs * dSegs * 2 + hSegs * dSegs * 2) * 6;
26 |
27 | const position = new Float32Array(num * 3);
28 | const normal = new Float32Array(num * 3);
29 | const uv = new Float32Array(num * 2);
30 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices);
31 |
32 | let i = 0;
33 | let ii = 0;
34 |
35 | // left, right
36 | Plane.buildPlane(position, normal, uv, index, depth, height, width, dSegs, hSegs, 2, 1, 0, -1, -1, i, ii);
37 | i += (dSegs + 1) * (hSegs + 1);
38 | ii += dSegs * hSegs;
39 |
40 | Plane.buildPlane(position, normal, uv, index, depth, height, -width, dSegs, hSegs, 2, 1, 0, 1, -1, i, ii);
41 | i += (dSegs + 1) * (hSegs + 1);
42 | ii += dSegs * hSegs;
43 |
44 | // top, bottom
45 | Plane.buildPlane(position, normal, uv, index, width, depth, height, dSegs, wSegs, 0, 2, 1, 1, 1, i, ii);
46 | i += (wSegs + 1) * (dSegs + 1);
47 | ii += wSegs * dSegs;
48 |
49 | Plane.buildPlane(position, normal, uv, index, width, depth, -height, dSegs, wSegs, 0, 2, 1, 1, -1, i, ii);
50 | i += (wSegs + 1) * (dSegs + 1);
51 | ii += wSegs * dSegs;
52 |
53 | // front, back
54 | Plane.buildPlane(position, normal, uv, index, width, height, -depth, wSegs, hSegs, 0, 1, 2, -1, -1, i, ii);
55 | i += (wSegs + 1) * (hSegs + 1);
56 | ii += wSegs * hSegs;
57 |
58 | Plane.buildPlane(position, normal, uv, index, width, height, depth, wSegs, hSegs, 0, 1, 2, 1, -1, i, ii);
59 |
60 | Object.assign(attributes, {
61 | position: { size: 3, data: position },
62 | normal: { size: 3, data: normal },
63 | uv: { size: 2, data: uv },
64 | index: { data: index },
65 | });
66 |
67 | super(gl, attributes);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/extras/GLTFAnimation.ts:
--------------------------------------------------------------------------------
1 | import { Vec3 } from '../math/Vec3';
2 | import { Quat } from '../math/Quat';
3 |
4 | const tmpVec3A = new Vec3();
5 | const tmpVec3B = new Vec3();
6 | const tmpVec3C = new Vec3();
7 | const tmpVec3D = new Vec3();
8 |
9 | const tmpQuatA = new Quat();
10 | const tmpQuatB = new Quat();
11 | const tmpQuatC = new Quat();
12 | const tmpQuatD = new Quat();
13 |
14 | export class GLTFAnimation {
15 | private data;
16 | private elapsed: number;
17 | private weight: number;
18 | private loop: boolean;
19 | private duration: number;
20 | private startTime: number;
21 | private endTime: number;
22 |
23 | constructor(data, weight = 1) {
24 | this.data = data;
25 | this.elapsed = 0;
26 | this.weight = weight;
27 |
28 | // Set to false to not apply modulo to elapsed against duration
29 | this.loop = true;
30 |
31 | // Find starting time as exports from blender (perhaps others too) don't always start from 0
32 | this.startTime = data.reduce((a, { times }) => Math.min(a, times[0]), Infinity);
33 | // Get largest final time in all channels to calculate duration
34 | this.endTime = data.reduce((a, { times }) => Math.max(a, times[times.length - 1]), 0);
35 | this.duration = this.endTime - this.startTime;
36 | }
37 |
38 | update(totalWeight = 1, isSet) {
39 | const weight = isSet ? 1 : this.weight / totalWeight;
40 | const elapsed = (this.loop ? this.elapsed % this.duration : Math.min(this.elapsed, this.duration - 0.001)) + this.startTime;
41 |
42 | this.data.forEach(({ node, transform, interpolation, times, values }) => {
43 | // Get index of two time values elapsed is between
44 | const prevIndex =
45 | Math.max(
46 | 1,
47 | times.findIndex((t) => t > elapsed)
48 | ) - 1;
49 | const nextIndex = prevIndex + 1;
50 |
51 | // Get linear blend/alpha between the two
52 | let alpha = (elapsed - times[prevIndex]) / (times[nextIndex] - times[prevIndex]);
53 | if (interpolation === 'STEP') alpha = 0;
54 |
55 | let prevVal: any = tmpVec3A;
56 | let prevTan: any = tmpVec3B;
57 | let nextTan: any = tmpVec3C;
58 | let nextVal: any = tmpVec3D;
59 | let size = 3;
60 |
61 | if (transform === 'quaternion') {
62 | prevVal = tmpQuatA;
63 | prevTan = tmpQuatB;
64 | nextTan = tmpQuatC;
65 | nextVal = tmpQuatD;
66 | size = 4;
67 | }
68 |
69 | if (interpolation === 'CUBICSPLINE') {
70 | // Get the prev and next values from the indices
71 | prevVal.fromArray(values, prevIndex * size * 3 + size * 1);
72 | prevTan.fromArray(values, prevIndex * size * 3 + size * 2);
73 | nextTan.fromArray(values, nextIndex * size * 3 + size * 0);
74 | nextVal.fromArray(values, nextIndex * size * 3 + size * 1);
75 |
76 | // interpolate for final value
77 | prevVal = this.cubicSplineInterpolate(alpha, prevVal, prevTan, nextTan, nextVal);
78 | if (size === 4) prevVal.normalize();
79 | } else {
80 | // Get the prev and next values from the indices
81 | prevVal.fromArray(values, prevIndex * size);
82 | nextVal.fromArray(values, nextIndex * size);
83 |
84 | // interpolate for final value
85 | if (size === 4) prevVal.slerp(nextVal, alpha);
86 | else prevVal.lerp(nextVal, alpha);
87 | }
88 |
89 | // interpolate between multiple possible animations
90 | if (size === 4) node[transform].slerp(prevVal, weight);
91 | else node[transform].lerp(prevVal, weight);
92 | });
93 | }
94 |
95 | cubicSplineInterpolate(t, prevVal, prevTan, nextTan, nextVal) {
96 | const t2 = t * t;
97 | const t3 = t2 * t;
98 |
99 | const s2 = 3 * t2 - 2 * t3;
100 | const s3 = t3 - t2;
101 | const s0 = 1 - s2;
102 | const s1 = s3 - t2 + t;
103 |
104 | for (let i = 0; i < prevVal.length; i++) {
105 | prevVal[i] = s0 * prevVal[i] + s1 * (1 - t) * prevTan[i] + s2 * nextVal[i] + s3 * t * nextTan[i];
106 | }
107 |
108 | return prevVal;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/extras/GLTFSkin.ts:
--------------------------------------------------------------------------------
1 | import { Mesh } from '../core/Mesh';
2 | import { Mat4 } from '../math/Mat4';
3 | import { Texture } from '../core/Texture';
4 | import { Camera } from '../core/Camera';
5 | import { isWebGl2 } from '../Guards';
6 |
7 | const tempMat4 = new Mat4();
8 | const identity = new Mat4();
9 |
10 | export interface GLTFSkinOptions {
11 | skeleton;
12 | geometry;
13 | program;
14 | mode;
15 | }
16 |
17 | export class GLTFSkin extends Mesh {
18 | skeleton;
19 | animations;
20 | boneMatrices: Float32Array;
21 | boneTextureSize: number;
22 | boneTexture: Texture;
23 |
24 | constructor(gl, { skeleton, geometry, program, mode = gl.TRIANGLES }: Partial = {}) {
25 | super(gl, { geometry, program, mode });
26 | this.skeleton = skeleton;
27 | this.program = program;
28 | this.createBoneTexture();
29 | this.animations = [];
30 | }
31 |
32 | createBoneTexture() {
33 | if (!this.skeleton.joints.length) return;
34 | const size = Math.max(4, Math.pow(2, Math.ceil(Math.log(Math.sqrt(this.skeleton.joints.length * 4)) / Math.LN2)));
35 | this.boneMatrices = new Float32Array(size * size * 4);
36 | this.boneTextureSize = size;
37 | this.boneTexture = new Texture(this.gl, {
38 | image: this.boneMatrices,
39 | generateMipmaps: false,
40 | type: this.gl.FLOAT,
41 | internalFormat: isWebGl2(this.gl) ? this.gl.RGBA32F : this.gl.RGBA,
42 | // internalFormat: this.gl.renderer.isWebgl2 ? this.gl.RGBA32F : this.gl.RGBA,
43 | minFilter: this.gl.NEAREST,
44 | magFilter: this.gl.NEAREST,
45 | flipY: false,
46 | width: size,
47 | });
48 | }
49 |
50 | // addAnimation(data) {
51 | // const animation = new Animation({ objects: this.bones, data });
52 | // this.animations.push(animation);
53 | // return animation;
54 | // }
55 |
56 | // updateAnimations() {
57 | // // Calculate combined animation weight
58 | // let total = 0;
59 | // this.animations.forEach((animation) => (total += animation.weight));
60 |
61 | // this.animations.forEach((animation, i) => {
62 | // // force first animation to set in order to reset frame
63 | // animation.update(total, i === 0);
64 | // });
65 | // }
66 |
67 | updateUniforms() {
68 | // Update bone texture
69 | this.skeleton.joints.forEach((bone, i) => {
70 | // Find difference between current and bind pose
71 | tempMat4.multiply(bone.worldMatrix, bone.bindInverse);
72 | this.boneMatrices.set(tempMat4, i * 16);
73 | });
74 | if (this.boneTexture) this.boneTexture.needsUpdate = true;
75 | }
76 |
77 | draw({ camera }: { camera?: Camera } = {}) {
78 | if (!this.program.uniforms.boneTexture) {
79 | Object.assign(this.program.uniforms, {
80 | boneTexture: { value: this.boneTexture },
81 | boneTextureSize: { value: this.boneTextureSize },
82 | });
83 | }
84 |
85 | this.updateUniforms();
86 |
87 | // Switch the world matrix with identity to ignore any transforms
88 | // on the mesh itself - only use skeleton's transforms
89 | const _worldMatrix = this.worldMatrix;
90 | this.worldMatrix = identity;
91 |
92 | super.draw({ camera });
93 |
94 | // Switch back to leave identity untouched
95 | this.worldMatrix = _worldMatrix;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/extras/KTXTexture.ts:
--------------------------------------------------------------------------------
1 | import { Texture, CompressedImage } from '../core/Texture';
2 |
3 | // TODO: Support cubemaps
4 | // Generate textures using https://github.com/TimvanScherpenzeel/texture-compressor
5 |
6 | export interface KTXTextureOptions {
7 | buffer: ArrayBuffer;
8 | src: string;
9 | wrapS: number;
10 | wrapT: number;
11 | anisotropy: number;
12 | minFilter: number;
13 | magFilter: number;
14 | }
15 |
16 | export class KTXTexture extends Texture {
17 | constructor(
18 | gl,
19 | { buffer, wrapS = gl.CLAMP_TO_EDGE, wrapT = gl.CLAMP_TO_EDGE, anisotropy = 0, minFilter, magFilter }: Partial = {}
20 | ) {
21 | super(gl, {
22 | generateMipmaps: false,
23 | wrapS,
24 | wrapT,
25 | anisotropy,
26 | minFilter,
27 | magFilter,
28 | });
29 |
30 | if (buffer) this.parseBuffer(buffer);
31 | }
32 |
33 | parseBuffer(buffer: ArrayBuffer) {
34 | const ktx = new KhronosTextureContainer(buffer);
35 | ktx.mipmaps.isCompressedTexture = true;
36 |
37 | // Update texture
38 | this.image = ktx.mipmaps;
39 | this.internalFormat = ktx.glInternalFormat;
40 | if (ktx.numberOfMipmapLevels > 1) {
41 | if (this.minFilter === this.gl.LINEAR) this.minFilter = this.gl.NEAREST_MIPMAP_LINEAR;
42 | } else {
43 | if (this.minFilter === this.gl.NEAREST_MIPMAP_LINEAR) this.minFilter = this.gl.LINEAR;
44 | }
45 |
46 | // TODO: support cube maps
47 | // ktx.numberOfFaces
48 | }
49 | }
50 |
51 | class KhronosTextureContainer {
52 | glInternalFormat: number;
53 | numberOfFaces: number;
54 | numberOfMipmapLevels: number;
55 | mipmaps: CompressedImage;
56 | isCompressedTexture: boolean;
57 |
58 | constructor(buffer) {
59 | const idCheck = [0xab, 0x4b, 0x54, 0x58, 0x20, 0x31, 0x31, 0xbb, 0x0d, 0x0a, 0x1a, 0x0a];
60 | const id = new Uint8Array(buffer, 0, 12);
61 | for (let i = 0; i < id.length; i++)
62 | if (id[i] !== idCheck[i]) {
63 | console.error('File missing KTX identifier');
64 | return;
65 | }
66 |
67 | // TODO: Is this always 4? Tested: [android, macos]
68 | const size = Uint32Array.BYTES_PER_ELEMENT;
69 | const head = new DataView(buffer, 12, 13 * size);
70 | const littleEndian = head.getUint32(0, true) === 0x04030201;
71 | const glType = head.getUint32(1 * size, littleEndian);
72 | if (glType !== 0) {
73 | console.warn('only compressed formats currently supported');
74 | return;
75 | }
76 | this.glInternalFormat = head.getUint32(4 * size, littleEndian);
77 | let width = head.getUint32(6 * size, littleEndian);
78 | let height = head.getUint32(7 * size, littleEndian);
79 | this.numberOfFaces = head.getUint32(10 * size, littleEndian);
80 | this.numberOfMipmapLevels = Math.max(1, head.getUint32(11 * size, littleEndian));
81 | const bytesOfKeyValueData = head.getUint32(12 * size, littleEndian);
82 |
83 | this.mipmaps = [];
84 | let offset = 12 + 13 * 4 + bytesOfKeyValueData;
85 | for (let level = 0; level < this.numberOfMipmapLevels; level++) {
86 | const levelSize = new Int32Array(buffer, offset, 1)[0]; // size per face, since not supporting array cubemaps
87 | offset += 4; // levelSize field
88 | for (let face = 0; face < this.numberOfFaces; face++) {
89 | const data = new Uint8Array(buffer, offset, levelSize);
90 | this.mipmaps.push({ data, width, height });
91 | offset += levelSize;
92 | offset += 3 - ((levelSize + 3) % 4); // add padding for odd sized image
93 | }
94 | width = width >> 1;
95 | height = height >> 1;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/extras/NormalProgram.ts:
--------------------------------------------------------------------------------
1 | import { Program, ProgramOptions } from '../core/Program';
2 | import { OGLRenderingContext } from '../core/Renderer';
3 |
4 | const vertex = /* glsl */ `
5 | precision highp float;
6 | precision highp int;
7 |
8 | attribute vec3 position;
9 | attribute vec3 normal;
10 |
11 | uniform mat3 normalMatrix;
12 | uniform mat4 modelViewMatrix;
13 | uniform mat4 projectionMatrix;
14 |
15 | varying vec3 vNormal;
16 |
17 | void main() {
18 | vNormal = normalize(normalMatrix * normal);
19 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
20 | }
21 | `;
22 |
23 | const fragment = /* glsl */ `
24 | precision highp float;
25 | precision highp int;
26 |
27 | varying vec3 vNormal;
28 |
29 | void main() {
30 | gl_FragColor.rgb = normalize(vNormal);
31 | gl_FragColor.a = 1.0;
32 | }
33 | `;
34 |
35 | export function NormalProgram(gl) {
36 | return new Program(gl, {
37 | vertex: vertex,
38 | fragment: fragment,
39 | cullFace: null,
40 | });
41 | }
42 |
43 | // export class NormalProgram extends Program {
44 |
45 | // public gltfMaterial;
46 |
47 | // constructor(gl: OGLRenderingContext, options: Partial = {}) {
48 | // super(gl, options);
49 | // }
50 | // }
51 |
--------------------------------------------------------------------------------
/src/extras/Plane.ts:
--------------------------------------------------------------------------------
1 | import { Geometry, Attribute, AttributeMap } from '../core/Geometry';
2 | import { OGLRenderingContext } from '../core/Renderer';
3 |
4 | export type PlaneOptions = {
5 | width: number;
6 | height: number;
7 | widthSegments: number;
8 | heightSegments: number;
9 | attributes: AttributeMap;
10 | };
11 |
12 | export class Plane extends Geometry {
13 | constructor(
14 | gl: OGLRenderingContext,
15 | { width = 1, height = 1, widthSegments = 1, heightSegments = 1, attributes = {} }: Partial = {}
16 | ) {
17 | const wSegs = widthSegments;
18 | const hSegs = heightSegments;
19 |
20 | // Determine length of arrays
21 | const num = (wSegs + 1) * (hSegs + 1);
22 | const numIndices = wSegs * hSegs * 6;
23 |
24 | // Generate empty arrays once
25 | const position = new Float32Array(num * 3);
26 | const normal = new Float32Array(num * 3);
27 | const uv = new Float32Array(num * 2);
28 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices);
29 |
30 | Plane.buildPlane(position, normal, uv, index, width, height, 0, wSegs, hSegs);
31 |
32 | Object.assign(attributes, {
33 | position: { size: 3, data: position },
34 | normal: { size: 3, data: normal },
35 | uv: { size: 2, data: uv },
36 | index: { data: index },
37 | });
38 |
39 | super(gl, attributes);
40 | }
41 |
42 | static buildPlane(
43 | position: Float32Array,
44 | normal: Float32Array,
45 | uv: Float32Array,
46 | index: Uint32Array | Uint16Array,
47 | width: number,
48 | height: number,
49 | depth: number,
50 | wSegs: number,
51 | hSegs: number,
52 | u = 0,
53 | v = 1,
54 | w = 2,
55 | uDir = 1,
56 | vDir = -1,
57 | i = 0,
58 | ii = 0
59 | ) {
60 | const io = i;
61 | const segW = width / wSegs;
62 | const segH = height / hSegs;
63 |
64 | for (let iy = 0; iy <= hSegs; iy++) {
65 | let y = iy * segH - height / 2;
66 | for (let ix = 0; ix <= wSegs; ix++, i++) {
67 | let x = ix * segW - width / 2;
68 |
69 | position[i * 3 + u] = x * uDir;
70 | position[i * 3 + v] = y * vDir;
71 | position[i * 3 + w] = depth / 2;
72 |
73 | normal[i * 3 + u] = 0;
74 | normal[i * 3 + v] = 0;
75 | normal[i * 3 + w] = depth >= 0 ? 1 : -1;
76 |
77 | uv[i * 2] = ix / wSegs;
78 | uv[i * 2 + 1] = 1 - iy / hSegs;
79 |
80 | if (iy === hSegs || ix === wSegs) continue;
81 | let a = io + ix + iy * (wSegs + 1);
82 | let b = io + ix + (iy + 1) * (wSegs + 1);
83 | let c = io + ix + (iy + 1) * (wSegs + 1) + 1;
84 | let d = io + ix + iy * (wSegs + 1) + 1;
85 |
86 | index[ii * 6] = a;
87 | index[ii * 6 + 1] = b;
88 | index[ii * 6 + 2] = d;
89 | index[ii * 6 + 3] = b;
90 | index[ii * 6 + 4] = c;
91 | index[ii * 6 + 5] = d;
92 | ii++;
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/extras/Shadow.ts:
--------------------------------------------------------------------------------
1 | import { Camera } from '../core/Camera';
2 | import { Program } from '../core/Program';
3 | import { RenderTarget, RenderTargetOptions } from '../core/RenderTarget';
4 | import { OGLRenderingContext } from '../core/Renderer';
5 | import { Mesh } from '../core/Mesh';
6 |
7 | export class Shadow {
8 | gl: OGLRenderingContext;
9 | light: Camera;
10 | target: RenderTarget;
11 | depthProgram: Program;
12 | castMeshes: Mesh[];
13 |
14 | constructor(gl: OGLRenderingContext, { light = new Camera(gl), width = 1024, height = width }) {
15 | this.gl = gl;
16 |
17 | this.light = light;
18 |
19 | this.target = new RenderTarget(gl, { width, height });
20 |
21 | this.depthProgram = new Program(gl, {
22 | vertex: defaultVertex,
23 | fragment: defaultFragment,
24 | cullFace: null,
25 | });
26 |
27 | this.castMeshes = [];
28 | }
29 |
30 | add({
31 | mesh,
32 | receive = true,
33 | cast = true,
34 | vertex = defaultVertex,
35 | fragment = defaultFragment,
36 | uniformProjection = 'shadowProjectionMatrix',
37 | uniformView = 'shadowViewMatrix',
38 | uniformTexture = 'tShadow',
39 | }) {
40 | // Add uniforms to existing program
41 | if (receive && !mesh.program.uniforms[uniformProjection]) {
42 | mesh.program.uniforms[uniformProjection] = { value: this.light.projectionMatrix };
43 | mesh.program.uniforms[uniformView] = { value: this.light.viewMatrix };
44 | mesh.program.uniforms[uniformTexture] = { value: this.target.texture };
45 | }
46 |
47 | if (!cast) return;
48 | this.castMeshes.push(mesh);
49 |
50 | // Store program for when switching between depth override
51 | mesh.colorProgram = mesh.program;
52 |
53 | // Check if depth program already attached
54 | if (mesh.depthProgram) return;
55 |
56 | // Use global depth override if nothing custom passed in
57 | if (vertex === defaultVertex && fragment === defaultFragment) {
58 | mesh.depthProgram = this.depthProgram;
59 | return;
60 | }
61 |
62 | // Create custom override program
63 | mesh.depthProgram = new Program(this.gl, {
64 | vertex,
65 | fragment,
66 | cullFace: null,
67 | });
68 | }
69 |
70 | render({ scene }) {
71 | // For depth render, replace program with depth override.
72 | // Hide meshes not casting shadows.
73 | scene.traverse((node) => {
74 | if (!node.draw) return;
75 | if (!!~this.castMeshes.indexOf(node)) {
76 | node.program = node.depthProgram;
77 | } else {
78 | node.isForceVisibility = node.visible;
79 | node.visible = false;
80 | }
81 | });
82 |
83 | // Render the depth shadow map using the light as the camera
84 | this.gl.renderer.render({
85 | scene,
86 | camera: this.light,
87 | target: this.target,
88 | });
89 |
90 | // Then switch the program back to the normal one
91 | scene.traverse((node) => {
92 | if (!node.draw) return;
93 | if (!!~this.castMeshes.indexOf(node)) {
94 | node.program = node.colorProgram;
95 | } else {
96 | node.visible = node.isForceVisibility;
97 | }
98 | });
99 | }
100 | }
101 |
102 | const defaultVertex = /* glsl */ `
103 | attribute vec3 position;
104 | attribute vec2 uv;
105 |
106 | uniform mat4 modelViewMatrix;
107 | uniform mat4 projectionMatrix;
108 |
109 | void main() {
110 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
111 | }
112 | `;
113 |
114 | const defaultFragment = /* glsl */ `
115 | precision highp float;
116 |
117 | vec4 packRGBA (float v) {
118 | vec4 pack = fract(vec4(1.0, 255.0, 65025.0, 16581375.0) * v);
119 | pack -= pack.yzww * vec2(1.0 / 255.0, 0.0).xxxy;
120 | return pack;
121 | }
122 |
123 | void main() {
124 | gl_FragColor = packRGBA(gl_FragCoord.z);
125 | }
126 | `;
127 |
--------------------------------------------------------------------------------
/src/extras/Sphere.ts:
--------------------------------------------------------------------------------
1 | import { Geometry, AttributeMap } from '../core/Geometry';
2 | import { Vec3 } from '../math/Vec3';
3 | import { OGLRenderingContext } from '../core/Renderer';
4 |
5 | export type SphereOptions = {
6 | radius: number;
7 | widthSegments: number;
8 | heightSegments: number;
9 | phiStart: number;
10 | phiLength: number;
11 | thetaStart: number;
12 | thetaLength: number;
13 | attributes: AttributeMap;
14 | };
15 |
16 | export class Sphere extends Geometry {
17 | constructor(
18 | gl: OGLRenderingContext,
19 | {
20 | radius = 0.5,
21 | widthSegments = 16,
22 | heightSegments = Math.ceil(widthSegments * 0.5),
23 | phiStart = 0,
24 | phiLength = Math.PI * 2,
25 | thetaStart = 0,
26 | thetaLength = Math.PI,
27 | attributes = {},
28 | }: Partial = {}
29 | ) {
30 | const wSegs = widthSegments;
31 | const hSegs = heightSegments;
32 | const pStart = phiStart;
33 | const pLength = phiLength;
34 | const tStart = thetaStart;
35 | const tLength = thetaLength;
36 |
37 | const num = (wSegs + 1) * (hSegs + 1);
38 | const numIndices = wSegs * hSegs * 6;
39 |
40 | const position = new Float32Array(num * 3);
41 | const normal = new Float32Array(num * 3);
42 | const uv = new Float32Array(num * 2);
43 | const index = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices);
44 |
45 | let i = 0;
46 | let iv = 0;
47 | let ii = 0;
48 | let te = tStart + tLength;
49 | const grid = [];
50 |
51 | let n = new Vec3();
52 |
53 | for (let iy = 0; iy <= hSegs; iy++) {
54 | let vRow = [];
55 | let v = iy / hSegs;
56 | for (let ix = 0; ix <= wSegs; ix++, i++) {
57 | let u = ix / wSegs;
58 | let x = -radius * Math.cos(pStart + u * pLength) * Math.sin(tStart + v * tLength);
59 | let y = radius * Math.cos(tStart + v * tLength);
60 | let z = radius * Math.sin(pStart + u * pLength) * Math.sin(tStart + v * tLength);
61 |
62 | position[i * 3] = x;
63 | position[i * 3 + 1] = y;
64 | position[i * 3 + 2] = z;
65 |
66 | n.set(x, y, z).normalize();
67 | normal[i * 3] = n.x;
68 | normal[i * 3 + 1] = n.y;
69 | normal[i * 3 + 2] = n.z;
70 |
71 | uv[i * 2] = u;
72 | uv[i * 2 + 1] = 1 - v;
73 |
74 | vRow.push(iv++);
75 | }
76 |
77 | grid.push(vRow);
78 | }
79 |
80 | for (let iy = 0; iy < hSegs; iy++) {
81 | for (let ix = 0; ix < wSegs; ix++) {
82 | let a = grid[iy][ix + 1];
83 | let b = grid[iy][ix];
84 | let c = grid[iy + 1][ix];
85 | let d = grid[iy + 1][ix + 1];
86 |
87 | if (iy !== 0 || tStart > 0) {
88 | index[ii * 3] = a;
89 | index[ii * 3 + 1] = b;
90 | index[ii * 3 + 2] = d;
91 | ii++;
92 | }
93 | if (iy !== hSegs - 1 || te < Math.PI) {
94 | index[ii * 3] = b;
95 | index[ii * 3 + 1] = c;
96 | index[ii * 3 + 2] = d;
97 | ii++;
98 | }
99 | }
100 | }
101 |
102 | Object.assign(attributes, {
103 | position: { size: 3, data: position },
104 | normal: { size: 3, data: normal },
105 | uv: { size: 2, data: uv },
106 | index: { data: index },
107 | });
108 |
109 | super(gl, attributes);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/extras/Torus.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/mrdoob/three.js/blob/master/src/geometries/TorusGeometry.js
2 |
3 | import { Geometry } from '../core/Geometry';
4 | import { Vec3 } from '../math/Vec3';
5 |
6 | export class Torus extends Geometry {
7 | constructor(gl, { radius = 0.5, tube = 0.2, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2, attributes = {} } = {}) {
8 | const num = (radialSegments + 1) * (tubularSegments + 1);
9 | const numIndices = radialSegments * tubularSegments * 6;
10 |
11 | const vertices = new Float32Array(num * 3);
12 | const normals = new Float32Array(num * 3);
13 | const uvs = new Float32Array(num * 2);
14 | const indices = num > 65536 ? new Uint32Array(numIndices) : new Uint16Array(numIndices);
15 |
16 | const center = new Vec3();
17 | const vertex = new Vec3();
18 | const normal = new Vec3();
19 |
20 | // generate vertices, normals and uvs
21 |
22 | let idx = 0;
23 | for (let j = 0; j <= radialSegments; j++) {
24 | for (let i = 0; i <= tubularSegments; i++, idx++) {
25 | const u = (i / tubularSegments) * arc;
26 | const v = (j / radialSegments) * Math.PI * 2;
27 |
28 | // vertex
29 |
30 | vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u);
31 | vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u);
32 | vertex.z = tube * Math.sin(v);
33 |
34 | vertices.set([vertex.x, vertex.y, vertex.z], idx * 3);
35 |
36 | // normal
37 |
38 | center.x = radius * Math.cos(u);
39 | center.y = radius * Math.sin(u);
40 | normal.sub(vertex, center).normalize();
41 |
42 | normals.set([normal.x, normal.y, normal.z], idx * 3);
43 |
44 | // uv
45 |
46 | uvs.set([i / tubularSegments, j / radialSegments], idx * 2);
47 | }
48 | }
49 |
50 | // generate indices
51 |
52 | idx = 0;
53 | for (let j = 1; j <= radialSegments; j++) {
54 | for (let i = 1; i <= tubularSegments; i++, idx++) {
55 | // indices
56 |
57 | const a = (tubularSegments + 1) * j + i - 1;
58 | const b = (tubularSegments + 1) * (j - 1) + i - 1;
59 | const c = (tubularSegments + 1) * (j - 1) + i;
60 | const d = (tubularSegments + 1) * j + i;
61 |
62 | // faces
63 |
64 | indices.set([a, b, d, b, c, d], idx * 6);
65 | }
66 | }
67 |
68 | Object.assign(attributes, {
69 | position: { size: 3, data: vertices },
70 | normal: { size: 3, data: normals },
71 | uv: { size: 2, data: uvs },
72 | index: { data: indices },
73 | });
74 |
75 | super(gl, attributes);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/extras/Triangle.ts:
--------------------------------------------------------------------------------
1 | import { Geometry } from '../core/Geometry';
2 |
3 | export class Triangle extends Geometry {
4 | constructor(gl, { attributes = {} } = {}) {
5 | // position uv
6 | // (-1, 3) (0, 2)
7 | // |\ |\
8 | // |__\(1, 1) |__\(1, 1)
9 | // |__|_\ |__|_\
10 | // (-1, -1) (3, -1) (0, 0) (2, 0)
11 |
12 | Object.assign(attributes, {
13 | position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },
14 | uv: { size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2]) },
15 | });
16 |
17 | super(gl, attributes);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // Core
2 | export * from './core/Geometry';
3 | export * from './core/Program';
4 | export * from './core/Renderer';
5 | export * from './core/Camera';
6 | export * from './core/Transform';
7 | export * from './core/Mesh';
8 | export * from './core/Texture';
9 | export * from './core/RenderTarget';
10 |
11 | // Maths
12 | export * from './math/Color';
13 | export * from './math/Euler';
14 | export * from './math/Mat3';
15 | export * from './math/Mat4';
16 | export * from './math/Quat';
17 | export * from './math/Vec2';
18 | export * from './math/Vec3';
19 | export * from './math/Vec4';
20 |
21 | // Extras
22 | export * from './extras/Plane';
23 | export * from './extras/Box';
24 | export * from './extras/Sphere';
25 | export * from './extras/Cylinder';
26 | export * from './extras/Triangle';
27 | export * from './extras/Torus';
28 |
29 | export * from './extras/Orbit';
30 | export * from './extras/Raycast';
31 | export * from './extras/Curve';
32 | export * from './extras/Post';
33 | export * from './extras/Skin';
34 | export * from './extras/Animation';
35 | export * from './extras/Text';
36 | export * from './extras/NormalProgram';
37 | export * from './extras/Flowmap';
38 | export * from './extras/GPGPU';
39 | export * from './extras/Polyline';
40 | export * from './extras/Shadow';
41 | export * from './extras/KTXTexture';
42 | export * from './extras/TextureLoader';
43 | export * from './extras/GLTFLoader';
44 | export * from './extras/GLTFAnimation';
45 | export * from './extras/GLTFSkin';
46 |
--------------------------------------------------------------------------------
/src/math/Color.ts:
--------------------------------------------------------------------------------
1 | import * as ColorFunc from './functions/ColorFunc';
2 |
3 | // Color stored as an array of RGB decimal values (between 0 > 1)
4 | // Constructor and set method accept following formats:
5 | // new Color() - Empty (defaults to black)
6 | // new Color([0.2, 0.4, 1.0]) - Decimal Array (or another Color instance)
7 | // new Color(0.7, 0.0, 0.1) - Decimal RGB values
8 | // new Color('#ff0000') - Hex string
9 | // new Color('#ccc') - Short-hand Hex string
10 | // new Color(0x4f27e8) - Number
11 | // new Color('red') - Color name string (short list in ColorFunc.js)
12 |
13 | export class Color extends Array {
14 | constructor();
15 | constructor(color: [number, number, number]);
16 | constructor(color: number, g: number, b: number);
17 | constructor(color: string);
18 | constructor(color: ColorFunc.ColorNames);
19 | constructor(color: number);
20 | constructor(color: any = null) {
21 | if (Array.isArray(color)) {
22 | super(...color);
23 | } else {
24 | super(...ColorFunc.parseColor(...arguments));
25 | }
26 | }
27 |
28 | get r() {
29 | return this[0];
30 | }
31 |
32 | get g() {
33 | return this[1];
34 | }
35 |
36 | get b() {
37 | return this[2];
38 | }
39 |
40 | set r(v) {
41 | this[0] = v;
42 | }
43 |
44 | set g(v) {
45 | this[1] = v;
46 | }
47 |
48 | set b(v) {
49 | this[2] = v;
50 | }
51 |
52 | set(color) {
53 | if (Array.isArray(color)) return this.copy(color);
54 | return this.copy(ColorFunc.parseColor(...arguments));
55 | }
56 |
57 | copy(v) {
58 | this[0] = v[0];
59 | this[1] = v[1];
60 | this[2] = v[2];
61 | return this;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/math/Euler.ts:
--------------------------------------------------------------------------------
1 | import * as EulerFunc from './functions/EulerFunc';
2 | import { Mat4 } from './Mat4';
3 |
4 | const tmpMat4 = new Mat4();
5 |
6 | export class Euler extends Array {
7 | onChange: () => void;
8 | order: string;
9 | constructor(x = 0, y = x, z = x, order = 'YXZ') {
10 | super(x, y, z);
11 | this.order = order;
12 | this.onChange = () => {};
13 | return this;
14 | }
15 |
16 | get x() {
17 | return this[0];
18 | }
19 |
20 | get y() {
21 | return this[1];
22 | }
23 |
24 | get z() {
25 | return this[2];
26 | }
27 |
28 | set x(v) {
29 | this[0] = v;
30 | this.onChange();
31 | }
32 |
33 | set y(v) {
34 | this[1] = v;
35 | this.onChange();
36 | }
37 |
38 | set z(v) {
39 | this[2] = v;
40 | this.onChange();
41 | }
42 |
43 | set(x, y = x, z = x) {
44 | if (x.length) return this.copy(x);
45 | this[0] = x;
46 | this[1] = y;
47 | this[2] = z;
48 | this.onChange();
49 | return this;
50 | }
51 |
52 | copy(v) {
53 | this[0] = v[0];
54 | this[1] = v[1];
55 | this[2] = v[2];
56 | this.onChange();
57 | return this;
58 | }
59 |
60 | reorder(order) {
61 | this.order = order;
62 | this.onChange();
63 | return this;
64 | }
65 |
66 | fromRotationMatrix(m, order = this.order) {
67 | EulerFunc.fromRotationMatrix(this, m, order);
68 | return this;
69 | }
70 |
71 | fromQuaternion(q, order = this.order) {
72 | tmpMat4.fromQuaternion(q);
73 | return this.fromRotationMatrix(tmpMat4, order);
74 | }
75 |
76 | toArray(a = [], o = 0) {
77 | a[o] = this[0];
78 | a[o + 1] = this[1];
79 | a[o + 2] = this[2];
80 | return a;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/math/Mat3.ts:
--------------------------------------------------------------------------------
1 | import * as Mat3Func from './functions/Mat3Func';
2 |
3 | export class Mat3 extends Array {
4 | constructor(m00 = 1, m01 = 0, m02 = 0, m10 = 0, m11 = 1, m12 = 0, m20 = 0, m21 = 0, m22 = 1) {
5 | super(m00, m01, m02, m10, m11, m12, m20, m21, m22);
6 | return this;
7 | }
8 |
9 | set(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
10 | if (m00.length) return this.copy(m00);
11 | Mat3Func.set(this, m00, m01, m02, m10, m11, m12, m20, m21, m22);
12 | return this;
13 | }
14 |
15 | translate(v, m = this) {
16 | Mat3Func.translate(this, m, v);
17 | return this;
18 | }
19 |
20 | rotate(v, m = this) {
21 | Mat3Func.rotate(this, m, v);
22 | return this;
23 | }
24 |
25 | scale(v, m = this) {
26 | Mat3Func.scale(this, m, v);
27 | return this;
28 | }
29 |
30 | multiply(ma, mb) {
31 | if (mb) {
32 | Mat3Func.multiply(this, ma, mb);
33 | } else {
34 | Mat3Func.multiply(this, this, ma);
35 | }
36 | return this;
37 | }
38 |
39 | identity() {
40 | Mat3Func.identity(this);
41 | return this;
42 | }
43 |
44 | copy(m) {
45 | Mat3Func.copy(this, m);
46 | return this;
47 | }
48 |
49 | fromMatrix4(m) {
50 | Mat3Func.fromMat4(this, m);
51 | return this;
52 | }
53 |
54 | fromQuaternion(q) {
55 | Mat3Func.fromQuat(this, q);
56 | return this;
57 | }
58 |
59 | fromBasis(vec3a, vec3b, vec3c) {
60 | this.set(vec3a[0], vec3a[1], vec3a[2], vec3b[0], vec3b[1], vec3b[2], vec3c[0], vec3c[1], vec3c[2]);
61 | return this;
62 | }
63 |
64 | inverse(m = this) {
65 | Mat3Func.invert(this, m);
66 | return this;
67 | }
68 |
69 | getNormalMatrix(m) {
70 | Mat3Func.normalFromMat4(this, m);
71 | return this;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/math/Quat.ts:
--------------------------------------------------------------------------------
1 | import * as QuatFunc from './functions/QuatFunc';
2 |
3 | export class Quat extends Array {
4 | onChange: () => void;
5 | constructor(x = 0, y = 0, z = 0, w = 1) {
6 | super(x, y, z, w);
7 | this.onChange = () => {};
8 | return this;
9 | }
10 |
11 | get x() {
12 | return this[0];
13 | }
14 |
15 | get y() {
16 | return this[1];
17 | }
18 |
19 | get z() {
20 | return this[2];
21 | }
22 |
23 | get w() {
24 | return this[3];
25 | }
26 |
27 | set x(v) {
28 | this[0] = v;
29 | this.onChange();
30 | }
31 |
32 | set y(v) {
33 | this[1] = v;
34 | this.onChange();
35 | }
36 |
37 | set z(v) {
38 | this[2] = v;
39 | this.onChange();
40 | }
41 |
42 | set w(v) {
43 | this[3] = v;
44 | this.onChange();
45 | }
46 |
47 | identity() {
48 | QuatFunc.identity(this);
49 | this.onChange();
50 | return this;
51 | }
52 |
53 | set(x, y, z, w) {
54 | if (x.length) return this.copy(x);
55 | QuatFunc.set(this, x, y, z, w);
56 | this.onChange();
57 | return this;
58 | }
59 |
60 | rotateX(a) {
61 | QuatFunc.rotateX(this, this, a);
62 | this.onChange();
63 | return this;
64 | }
65 |
66 | rotateY(a) {
67 | QuatFunc.rotateY(this, this, a);
68 | this.onChange();
69 | return this;
70 | }
71 |
72 | rotateZ(a) {
73 | QuatFunc.rotateZ(this, this, a);
74 | this.onChange();
75 | return this;
76 | }
77 |
78 | inverse(q = this) {
79 | QuatFunc.invert(this, q);
80 | this.onChange();
81 | return this;
82 | }
83 |
84 | conjugate(q = this) {
85 | QuatFunc.conjugate(this, q);
86 | this.onChange();
87 | return this;
88 | }
89 |
90 | copy(q) {
91 | QuatFunc.copy(this, q);
92 | this.onChange();
93 | return this;
94 | }
95 |
96 | normalize(q = this) {
97 | QuatFunc.normalize(this, q);
98 | this.onChange();
99 | return this;
100 | }
101 |
102 | multiply(qA, qB) {
103 | if (qB) {
104 | QuatFunc.multiply(this, qA, qB);
105 | } else {
106 | QuatFunc.multiply(this, this, qA);
107 | }
108 | this.onChange();
109 | return this;
110 | }
111 |
112 | dot(v) {
113 | return QuatFunc.dot(this, v);
114 | }
115 |
116 | fromMatrix3(matrix3) {
117 | QuatFunc.fromMat3(this, matrix3);
118 | this.onChange();
119 | return this;
120 | }
121 |
122 | fromEuler(euler) {
123 | QuatFunc.fromEuler(this, euler, euler.order);
124 | return this;
125 | }
126 |
127 | fromAxisAngle(axis, a) {
128 | QuatFunc.setAxisAngle(this, axis, a);
129 | return this;
130 | }
131 |
132 | slerp(q, t) {
133 | QuatFunc.slerp(this, this, q, t);
134 | return this;
135 | }
136 |
137 | fromArray(a, o = 0) {
138 | this[0] = a[o];
139 | this[1] = a[o + 1];
140 | this[2] = a[o + 2];
141 | this[3] = a[o + 3];
142 | return this;
143 | }
144 |
145 | toArray(a = [], o = 0) {
146 | a[o] = this[0];
147 | a[o + 1] = this[1];
148 | a[o + 2] = this[2];
149 | a[o + 3] = this[3];
150 | return a;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/math/Vec2.ts:
--------------------------------------------------------------------------------
1 | import * as Vec2Func from './functions/Vec2Func';
2 |
3 | export class Vec2 extends Array {
4 | constructor(x = 0, y = x) {
5 | super(x, y);
6 | return this;
7 | }
8 |
9 | get x() {
10 | return this[0];
11 | }
12 |
13 | get y() {
14 | return this[1];
15 | }
16 |
17 | set x(v) {
18 | this[0] = v;
19 | }
20 |
21 | set y(v) {
22 | this[1] = v;
23 | }
24 |
25 | set(x, y = x) {
26 | if (x.length) return this.copy(x);
27 | Vec2Func.set(this, x, y);
28 | return this;
29 | }
30 |
31 | copy(v) {
32 | Vec2Func.copy(this, v);
33 | return this;
34 | }
35 |
36 | add(va, vb) {
37 | if (vb) Vec2Func.add(this, va, vb);
38 | else Vec2Func.add(this, this, va);
39 | return this;
40 | }
41 |
42 | sub(va, vb) {
43 | if (vb) Vec2Func.subtract(this, va, vb);
44 | else Vec2Func.subtract(this, this, va);
45 | return this;
46 | }
47 |
48 | multiply(v) {
49 | if (v.length) Vec2Func.multiply(this, this, v);
50 | else Vec2Func.scale(this, this, v);
51 | return this;
52 | }
53 |
54 | divide(v) {
55 | if (v.length) Vec2Func.divide(this, this, v);
56 | else Vec2Func.scale(this, this, 1 / v);
57 | return this;
58 | }
59 |
60 | inverse(v = this) {
61 | Vec2Func.inverse(this, v);
62 | return this;
63 | }
64 |
65 | // Can't use 'length' as Array.prototype uses it
66 | len() {
67 | return Vec2Func.length(this);
68 | }
69 |
70 | distance(v) {
71 | if (v) return Vec2Func.distance(this, v);
72 | else return Vec2Func.length(this);
73 | }
74 |
75 | squaredLen() {
76 | return this.squaredDistance();
77 | }
78 |
79 | squaredDistance(v?) {
80 | if (v) return Vec2Func.squaredDistance(this, v);
81 | else return Vec2Func.squaredLength(this);
82 | }
83 |
84 | negate(v = this) {
85 | Vec2Func.negate(this, v);
86 | return this;
87 | }
88 |
89 | cross(va, vb) {
90 | if (vb) return Vec2Func.cross(va, vb);
91 | return Vec2Func.cross(this, va);
92 | }
93 |
94 | scale(v) {
95 | Vec2Func.scale(this, this, v);
96 | return this;
97 | }
98 |
99 | normalize() {
100 | Vec2Func.normalize(this, this);
101 | return this;
102 | }
103 |
104 | dot(v) {
105 | return Vec2Func.dot(this, v);
106 | }
107 |
108 | equals(v) {
109 | return Vec2Func.exactEquals(this, v);
110 | }
111 |
112 | applyMatrix3(mat3) {
113 | Vec2Func.transformMat3(this, this, mat3);
114 | return this;
115 | }
116 |
117 | applyMatrix4(mat4) {
118 | Vec2Func.transformMat4(this, this, mat4);
119 | return this;
120 | }
121 |
122 | lerp(v, a) {
123 | Vec2Func.lerp(this, this, v, a);
124 | }
125 |
126 | clone() {
127 | return new Vec2(this[0], this[1]);
128 | }
129 |
130 | fromArray(a, o = 0) {
131 | this[0] = a[o];
132 | this[1] = a[o + 1];
133 | return this;
134 | }
135 |
136 | toArray(a = [], o = 0) {
137 | a[o] = this[0];
138 | a[o + 1] = this[1];
139 | return a;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/math/Vec3.ts:
--------------------------------------------------------------------------------
1 | import * as Vec3Func from './functions/Vec3Func';
2 | import { isArrayLike } from '../Guards';
3 | export class Vec3 extends Array {
4 | constant: number; // TODO: only be used in Camera class
5 | constructor(x = 0, y = x, z = x) {
6 | super(x, y, z);
7 | return this;
8 | }
9 |
10 | get x() {
11 | return this[0];
12 | }
13 |
14 | get y() {
15 | return this[1];
16 | }
17 |
18 | get z() {
19 | return this[2];
20 | }
21 |
22 | set x(v) {
23 | this[0] = v;
24 | }
25 |
26 | set y(v) {
27 | this[1] = v;
28 | }
29 |
30 | set z(v) {
31 | this[2] = v;
32 | }
33 |
34 | set(x: number | Array, y = x, z = x) {
35 | if (isArrayLike(x)) return this.copy(x);
36 | Vec3Func.set(this, x, y, z);
37 | return this;
38 | }
39 |
40 | copy(v) {
41 | Vec3Func.copy(this, v);
42 | return this;
43 | }
44 |
45 | add(va, vb?: Vec3) {
46 | if (vb) Vec3Func.add(this, va, vb);
47 | else Vec3Func.add(this, this, va);
48 | return this;
49 | }
50 |
51 | sub(va, vb?: Vec3) {
52 | if (vb) Vec3Func.subtract(this, va, vb);
53 | else Vec3Func.subtract(this, this, va);
54 | return this;
55 | }
56 |
57 | multiply(v) {
58 | if (v.length) Vec3Func.multiply(this, this, v);
59 | else Vec3Func.scale(this, this, v);
60 | return this;
61 | }
62 |
63 | divide(v) {
64 | if (v.length) Vec3Func.divide(this, this, v);
65 | else Vec3Func.scale(this, this, 1 / v);
66 | return this;
67 | }
68 |
69 | inverse(v = this) {
70 | Vec3Func.inverse(this, v);
71 | return this;
72 | }
73 |
74 | // Can't use 'length' as Array.prototype uses it
75 | len() {
76 | return Vec3Func.length(this);
77 | }
78 |
79 | distance(v?: Vec3) {
80 | if (v) return Vec3Func.distance(this, v);
81 | else return Vec3Func.length(this);
82 | }
83 |
84 | squaredLen() {
85 | return Vec3Func.squaredLength(this);
86 | }
87 |
88 | squaredDistance(v?: Vec3) {
89 | if (v) return Vec3Func.squaredDistance(this, v);
90 | else return Vec3Func.squaredLength(this);
91 | }
92 |
93 | negate(v = this) {
94 | Vec3Func.negate(this, v);
95 | return this;
96 | }
97 |
98 | cross(va, vb?) {
99 | if (vb) Vec3Func.cross(this, va, vb);
100 | else Vec3Func.cross(this, this, va);
101 | return this;
102 | }
103 |
104 | scale(v) {
105 | Vec3Func.scale(this, this, v);
106 | return this;
107 | }
108 |
109 | normalize() {
110 | Vec3Func.normalize(this, this);
111 | return this;
112 | }
113 |
114 | dot(v) {
115 | return Vec3Func.dot(this, v);
116 | }
117 |
118 | equals(v) {
119 | return Vec3Func.exactEquals(this, v);
120 | }
121 |
122 | applyMatrix4(mat4) {
123 | Vec3Func.transformMat4(this, this, mat4);
124 | return this;
125 | }
126 |
127 | scaleRotateMatrix4(mat4) {
128 | Vec3Func.scaleRotateMat4(this, this, mat4);
129 | return this;
130 | }
131 |
132 | applyQuaternion(q) {
133 | Vec3Func.transformQuat(this, this, q);
134 | return this;
135 | }
136 |
137 | angle(v) {
138 | return Vec3Func.angle(this, v);
139 | }
140 |
141 | lerp(v, t) {
142 | Vec3Func.lerp(this, this, v, t);
143 | return this;
144 | }
145 |
146 | clone() {
147 | return new Vec3(this[0], this[1], this[2]);
148 | }
149 |
150 | fromArray(a, o = 0) {
151 | this[0] = a[o];
152 | this[1] = a[o + 1];
153 | this[2] = a[o + 2];
154 | return this;
155 | }
156 |
157 | toArray(a: Float32Array | Array = [], o = 0) {
158 | a[o] = this[0];
159 | a[o + 1] = this[1];
160 | a[o + 2] = this[2];
161 | return a;
162 | }
163 |
164 | transformDirection(mat4) {
165 | const x = this[0];
166 | const y = this[1];
167 | const z = this[2];
168 |
169 | this[0] = mat4[0] * x + mat4[4] * y + mat4[8] * z;
170 | this[1] = mat4[1] * x + mat4[5] * y + mat4[9] * z;
171 | this[2] = mat4[2] * x + mat4[6] * y + mat4[10] * z;
172 |
173 | return this.normalize();
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/math/Vec4.ts:
--------------------------------------------------------------------------------
1 | import * as Vec4Func from './functions/Vec4Func';
2 |
3 | export class Vec4 extends Array {
4 | constructor(x = 0, y = x, z = x, w = x) {
5 | super(x, y, z, w);
6 | return this;
7 | }
8 |
9 | get x() {
10 | return this[0];
11 | }
12 |
13 | get y() {
14 | return this[1];
15 | }
16 |
17 | get z() {
18 | return this[2];
19 | }
20 |
21 | get w() {
22 | return this[3];
23 | }
24 |
25 | set x(v) {
26 | this[0] = v;
27 | }
28 |
29 | set y(v) {
30 | this[1] = v;
31 | }
32 |
33 | set z(v) {
34 | this[2] = v;
35 | }
36 |
37 | set w(v) {
38 | this[3] = v;
39 | }
40 |
41 | set(x, y, z, w) {
42 | if (x.length) return this.copy(x);
43 | Vec4Func.set(this, x, y, z, w);
44 | return this;
45 | }
46 |
47 | copy(v) {
48 | Vec4Func.copy(this, v);
49 | return this;
50 | }
51 |
52 | normalize() {
53 | Vec4Func.normalize(this, this);
54 | return this;
55 | }
56 |
57 | fromArray(a, o = 0) {
58 | this[0] = a[o];
59 | this[1] = a[o + 1];
60 | this[2] = a[o + 2];
61 | this[3] = a[o + 3];
62 | return this;
63 | }
64 |
65 | toArray(a = [], o = 0) {
66 | a[o] = this[0];
67 | a[o + 1] = this[1];
68 | a[o + 2] = this[2];
69 | a[o + 3] = this[3];
70 | return a;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/math/functions/ColorFunc.ts:
--------------------------------------------------------------------------------
1 | const NAMES = {
2 | black: '#000000',
3 | white: '#ffffff',
4 | red: '#ff0000',
5 | green: '#00ff00',
6 | blue: '#0000ff',
7 | fuchsia: '#ff00ff',
8 | cyan: '#00ffff',
9 | yellow: '#ffff00',
10 | orange: '#ff8000',
11 | };
12 |
13 | export type ColorNames = keyof typeof NAMES;
14 |
15 | export function hexToRGB(hex) {
16 | if (hex.length === 4) hex = hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
17 | const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
18 | if (!rgb) console.warn(`Unable to convert hex string ${hex} to rgb values`);
19 | return [parseInt(rgb[1], 16) / 255, parseInt(rgb[2], 16) / 255, parseInt(rgb[3], 16) / 255];
20 | }
21 |
22 | export function numberToRGB(num) {
23 | num = parseInt(num);
24 | return [((num >> 16) & 255) / 255, ((num >> 8) & 255) / 255, (num & 255) / 255];
25 | }
26 |
27 | export function parseColor(color: any = null) {
28 | // Empty
29 | if (!color) return [0, 0, 0];
30 |
31 | // Decimal
32 | if (arguments.length === 3) return arguments;
33 |
34 | // Number
35 | if (!isNaN(color)) return numberToRGB(color);
36 |
37 | // Hex
38 | if (color[0] === '#') return hexToRGB(color);
39 |
40 | // Names
41 | if (NAMES[color.toLowerCase()]) return hexToRGB(NAMES[color.toLowerCase()]);
42 |
43 | console.warn('Color format not recognised');
44 | return [0, 0, 0];
45 | }
46 |
--------------------------------------------------------------------------------
/src/math/functions/EulerFunc.ts:
--------------------------------------------------------------------------------
1 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
2 | export function fromRotationMatrix(out, m, order = 'YXZ') {
3 | if (order === 'XYZ') {
4 | out[1] = Math.asin(Math.min(Math.max(m[8], -1), 1));
5 | if (Math.abs(m[8]) < 0.99999) {
6 | out[0] = Math.atan2(-m[9], m[10]);
7 | out[2] = Math.atan2(-m[4], m[0]);
8 | } else {
9 | out[0] = Math.atan2(m[6], m[5]);
10 | out[2] = 0;
11 | }
12 | } else if (order === 'YXZ') {
13 | out[0] = Math.asin(-Math.min(Math.max(m[9], -1), 1));
14 | if (Math.abs(m[9]) < 0.99999) {
15 | out[1] = Math.atan2(m[8], m[10]);
16 | out[2] = Math.atan2(m[1], m[5]);
17 | } else {
18 | out[1] = Math.atan2(-m[2], m[0]);
19 | out[2] = 0;
20 | }
21 | } else if (order === 'ZXY') {
22 | out[0] = Math.asin(Math.min(Math.max(m[6], -1), 1));
23 | if (Math.abs(m[6]) < 0.99999) {
24 | out[1] = Math.atan2(-m[2], m[10]);
25 | out[2] = Math.atan2(-m[4], m[5]);
26 | } else {
27 | out[1] = 0;
28 | out[2] = Math.atan2(m[1], m[0]);
29 | }
30 | } else if (order === 'ZYX') {
31 | out[1] = Math.asin(-Math.min(Math.max(m[2], -1), 1));
32 | if (Math.abs(m[2]) < 0.99999) {
33 | out[0] = Math.atan2(m[6], m[10]);
34 | out[2] = Math.atan2(m[1], m[0]);
35 | } else {
36 | out[0] = 0;
37 | out[2] = Math.atan2(-m[4], m[5]);
38 | }
39 | } else if (order === 'YZX') {
40 | out[2] = Math.asin(Math.min(Math.max(m[1], -1), 1));
41 | if (Math.abs(m[1]) < 0.99999) {
42 | out[0] = Math.atan2(-m[9], m[5]);
43 | out[1] = Math.atan2(-m[2], m[0]);
44 | } else {
45 | out[0] = 0;
46 | out[1] = Math.atan2(m[8], m[10]);
47 | }
48 | } else if (order === 'XZY') {
49 | out[2] = Math.asin(-Math.min(Math.max(m[4], -1), 1));
50 | if (Math.abs(m[4]) < 0.99999) {
51 | out[0] = Math.atan2(m[6], m[5]);
52 | out[1] = Math.atan2(m[8], m[0]);
53 | } else {
54 | out[0] = Math.atan2(-m[9], m[10]);
55 | out[1] = 0;
56 | }
57 | }
58 |
59 | return out;
60 | }
61 |
--------------------------------------------------------------------------------
/src/math/functions/Vec4Func.ts:
--------------------------------------------------------------------------------
1 | const EPSILON = 0.000001;
2 |
3 | /**
4 | * Copy the values from one vec4 to another
5 | *
6 | * @param {vec4} out the receiving vector
7 | * @param {vec4} a the source vector
8 | * @returns {vec4} out
9 | */
10 | export function copy(out, a) {
11 | out[0] = a[0];
12 | out[1] = a[1];
13 | out[2] = a[2];
14 | out[3] = a[3];
15 | return out;
16 | }
17 |
18 | /**
19 | * Set the components of a vec4 to the given values
20 | *
21 | * @param {vec4} out the receiving vector
22 | * @param {Number} x X component
23 | * @param {Number} y Y component
24 | * @param {Number} z Z component
25 | * @param {Number} w W component
26 | * @returns {vec4} out
27 | */
28 | export function set(out, x, y, z, w) {
29 | out[0] = x;
30 | out[1] = y;
31 | out[2] = z;
32 | out[3] = w;
33 | return out;
34 | }
35 |
36 | /**
37 | * Adds two vec4's
38 | *
39 | * @param {vec4} out the receiving vector
40 | * @param {vec4} a the first operand
41 | * @param {vec4} b the second operand
42 | * @returns {vec4} out
43 | */
44 | export function add(out, a, b) {
45 | out[0] = a[0] + b[0];
46 | out[1] = a[1] + b[1];
47 | out[2] = a[2] + b[2];
48 | out[3] = a[3] + b[3];
49 | return out;
50 | }
51 |
52 | /**
53 | * Scales a vec4 by a scalar number
54 | *
55 | * @param {vec4} out the receiving vector
56 | * @param {vec4} a the vector to scale
57 | * @param {Number} b amount to scale the vector by
58 | * @returns {vec4} out
59 | */
60 | export function scale(out, a, b) {
61 | out[0] = a[0] * b;
62 | out[1] = a[1] * b;
63 | out[2] = a[2] * b;
64 | out[3] = a[3] * b;
65 | return out;
66 | }
67 |
68 | /**
69 | * Calculates the length of a vec4
70 | *
71 | * @param {vec4} a vector to calculate length of
72 | * @returns {Number} length of a
73 | */
74 | export function length(a) {
75 | let x = a[0];
76 | let y = a[1];
77 | let z = a[2];
78 | let w = a[3];
79 | return Math.sqrt(x * x + y * y + z * z + w * w);
80 | }
81 |
82 | /**
83 | * Normalize a vec4
84 | *
85 | * @param {vec4} out the receiving vector
86 | * @param {vec4} a vector to normalize
87 | * @returns {vec4} out
88 | */
89 | export function normalize(out, a) {
90 | let x = a[0];
91 | let y = a[1];
92 | let z = a[2];
93 | let w = a[3];
94 | let len = x * x + y * y + z * z + w * w;
95 | if (len > 0) {
96 | len = 1 / Math.sqrt(len);
97 | }
98 | out[0] = x * len;
99 | out[1] = y * len;
100 | out[2] = z * len;
101 | out[3] = w * len;
102 | return out;
103 | }
104 |
105 | /**
106 | * Calculates the dot product of two vec4's
107 | *
108 | * @param {vec4} a the first operand
109 | * @param {vec4} b the second operand
110 | * @returns {Number} dot product of a and b
111 | */
112 | export function dot(a, b) {
113 | return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
114 | }
115 |
116 | /**
117 | * Performs a linear interpolation between two vec4's
118 | *
119 | * @param {vec4} out the receiving vector
120 | * @param {vec4} a the first operand
121 | * @param {vec4} b the second operand
122 | * @param {Number} t interpolation amount between the two inputs
123 | * @returns {vec4} out
124 | */
125 | export function lerp(out, a, b, t) {
126 | let ax = a[0];
127 | let ay = a[1];
128 | let az = a[2];
129 | let aw = a[3];
130 | out[0] = ax + t * (b[0] - ax);
131 | out[1] = ay + t * (b[1] - ay);
132 | out[2] = az + t * (b[2] - az);
133 | out[3] = aw + t * (b[3] - aw);
134 | return out;
135 | }
136 |
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | ---
3 | version: 1.0.0
4 | codeOwners:
5 | - '0x5763B690102Cd5167Ad247a598a13018B84fF77f'
6 | quorum: 1
7 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | const { resolve } = require('path');
3 | const fs = require('fs');
4 |
5 | export default ({ command, mode }) => {
6 | if (command === 'build') {
7 | return {
8 | // https://github.com/vitejs/vite/issues/3025
9 | base: 'http://github.nshen.net/ogl-typescript/dist/',
10 | build: {
11 | minify: false,
12 | emptyOutDir: true,
13 | assetsDir: 'chunks',
14 | rollupOptions: {
15 | input: getInput([
16 | // Geometry
17 | 'triangle-screen-shader',
18 | 'draw-modes',
19 | 'indexed-vs-non-indexed',
20 | 'load-json',
21 | 'wireframe',
22 | 'base-primitives',
23 | 'particles',
24 | 'instancing',
25 | 'instancing-gpu-picking',
26 | 'polylines',
27 | 'curves',
28 | 'torus',
29 | 'load-gltf',
30 | 'compute-vertex-normal',
31 |
32 | // Scenes
33 | 'scene-graph',
34 | 'sort-transparency',
35 | 'frustum-culling',
36 |
37 | // Interaction
38 | 'orbit-controls',
39 | 'raycasting',
40 | 'mouse-flowmap',
41 |
42 | //Shading
43 | 'fog',
44 | 'textures',
45 | 'anisotropic',
46 | 'skydome',
47 | 'cube-map',
48 | 'normal-maps',
49 | 'flat-shading-matcap',
50 | 'wireframe-shader',
51 | 'msdf-text',
52 | 'point-lighting',
53 | 'pbr',
54 | 'compressed-textures',
55 |
56 | // Frame Buffer
57 | 'render-to-texture',
58 | 'post-fxaa',
59 | 'post-bloom',
60 | 'mrt',
61 | 'shadow-maps',
62 | 'post-fluid-distortion',
63 | 'gpgpu-particles',
64 |
65 | // Animation
66 | 'skinning',
67 |
68 | // Performance
69 | 'high-mesh-count',
70 | ]),
71 |
72 | output: {
73 | // https://github.com/vitejs/vite/issues/378#issuecomment-716717258
74 | // entryFileNames: `examples/[name]/index.js`, // works
75 | // chunkFileNames: `chunks/[name].js`,
76 | // assetFileNames: `assets/[name].[ext]`
77 | },
78 | },
79 | },
80 | plugins: [copyIndexPlugin()],
81 | };
82 | } else {
83 | return {
84 | // dev specific config
85 | };
86 | }
87 | };
88 |
89 | function copyIndexPlugin() {
90 | return {
91 | name: 'copyIndexPlugin',
92 | async closeBundle() {
93 | fs.copyFileSync('./index.html', './dist/index.html');
94 | },
95 | };
96 | }
97 |
98 | function getInput(arr) {
99 | let input = {};
100 | arr.forEach((example) => {
101 | input[example] = resolve(__dirname, `examples/${example}/index.html`);
102 | });
103 | return input;
104 | }
105 |
--------------------------------------------------------------------------------