├── .gitignore
├── .gitattributes
├── screenshot.png
├── worker.js
├── controls
├── debounce.js
└── OrbitControls.svelte
├── scene
├── Layer.svelte
├── Mesh
│ ├── shaders
│ │ ├── builtin
│ │ │ ├── vert.glsl
│ │ │ └── frag.glsl
│ │ └── default
│ │ │ ├── vert.glsl
│ │ │ └── frag.glsl
│ ├── index.svelte
│ ├── Material.mjs
│ └── program.mjs
├── lights
│ ├── AmbientLight.svelte
│ ├── DirectionalLight.svelte
│ └── PointLight.svelte
├── Overlay.svelte
├── Target.svelte
├── cameras
│ ├── OrthoCamera.svelte
│ └── PerspectiveCamera.svelte
├── Group.svelte
├── Point.svelte
└── Scene.svelte
├── geometry
├── plane.mjs
├── dodecahedron.mjs
├── sphere.mjs
├── cone.mjs
├── box.mjs
├── Geometry.mjs
├── icosphere.mjs
└── polyhedron.mjs
├── package.json
├── LICENSE
├── internal
├── utils.mjs
├── image.mjs
├── index.mjs
└── constants.mjs
├── API.md
├── index.mjs
├── scratch
└── TAXONOMY.md
├── README.md
├── abstract
└── Texture.mjs
└── CHANGELOG.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.svelte linguist-language=HTML
2 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sveltejs/gl/master/screenshot.png
--------------------------------------------------------------------------------
/worker.js:
--------------------------------------------------------------------------------
1 | self.onmessage = e => {
2 | self.onmessage = null;
3 | eval(e.data);
4 | };
--------------------------------------------------------------------------------
/controls/debounce.js:
--------------------------------------------------------------------------------
1 | export function debounce(fn) {
2 | let scheduled = false;
3 | let event;
4 |
5 | function release() {
6 | fn(event);
7 | scheduled = false;
8 | }
9 |
10 | return function(e) {
11 | if (!scheduled) {
12 | requestAnimationFrame(release);
13 | scheduled = true;
14 | }
15 |
16 | event = e;
17 | };
18 | }
--------------------------------------------------------------------------------
/scene/Layer.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/scene/Mesh/shaders/builtin/vert.glsl:
--------------------------------------------------------------------------------
1 | /* start builtins */
2 | precision highp float;
3 |
4 | uniform mat4 MODEL;
5 | uniform mat4 PROJECTION;
6 | uniform mat4 VIEW;
7 | uniform mat4 MODEL_INVERSE_TRANSPOSE;
8 |
9 | uniform vec3 CAMERA_WORLD_POSITION;
10 |
11 | struct PointLight {
12 | vec3 location;
13 | vec3 color;
14 | float intensity;
15 | // TODO fall-off etc
16 | };
17 |
18 | uniform PointLight POINT_LIGHTS[NUM_LIGHTS];
19 | /* end builtins */
--------------------------------------------------------------------------------
/scene/lights/AmbientLight.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scene/Mesh/shaders/builtin/frag.glsl:
--------------------------------------------------------------------------------
1 | #extension GL_OES_standard_derivatives : enable
2 |
3 | /* start builtins */
4 | precision highp float;
5 |
6 | struct DirectionalLight {
7 | vec3 direction;
8 | vec3 color;
9 | float intensity;
10 | };
11 |
12 | struct PointLight {
13 | vec3 location;
14 | vec3 color;
15 | float intensity;
16 | // TODO fall-off etc
17 | };
18 |
19 | uniform vec3 AMBIENT_LIGHT;
20 | uniform DirectionalLight DIRECTIONAL_LIGHTS[NUM_LIGHTS];
21 | uniform PointLight POINT_LIGHTS[NUM_LIGHTS];
22 | /* end builtins */
--------------------------------------------------------------------------------
/scene/Overlay.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/geometry/plane.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize } from '../internal/utils.mjs';
3 |
4 | export default memoize(() => {
5 | return new Geometry({
6 | position: {
7 | data: new Float32Array([
8 | 1, 1, 0,
9 | -1, 1, 0,
10 | 1, -1, 0,
11 | -1, -1, 0,
12 | ]),
13 | size: 3
14 | },
15 |
16 | normal: {
17 | data: new Float32Array([
18 | 0, 0, 1,
19 | 0, 0, 1,
20 | 0, 0, 1,
21 | 0, 0, 1
22 | ]),
23 | size: 3
24 | },
25 |
26 | uv: {
27 | data: new Float32Array([
28 | 1, 0,
29 | 0, 0,
30 | 1, 1,
31 | 0, 1
32 | ]),
33 | size: 2
34 | }
35 | }, {
36 | index: new Uint32Array([
37 | 0, 1, 2,
38 | 3, 2, 1
39 | ])
40 | });
41 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sveltejs/gl",
3 | "version": "0.0.37",
4 | "description": "A (very experimental) project to bring WebGL to Svelte",
5 | "main": "index.mjs",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/Rich-Harris/gl.git"
12 | },
13 | "keywords": [
14 | "Svelte",
15 | "WebGL"
16 | ],
17 | "author": "Rich Harris",
18 | "license": "MIT",
19 | "bugs": {
20 | "url": "https://github.com/Rich-Harris/gl/issues"
21 | },
22 | "homepage": "https://github.com/Rich-Harris/gl#readme",
23 | "devDependencies": {
24 | "tiny-glob": "^0.2.6"
25 | },
26 | "dependencies": {
27 | "gl-matrix": "^3.0.0",
28 | "yootils": "0.0.16"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/scene/lights/DirectionalLight.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scene/lights/PointLight.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scene/Target.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-19 [these people](https://github.com/sveltejs/gl/graphs/contributors)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/internal/utils.mjs:
--------------------------------------------------------------------------------
1 | export function process_color(color) {
2 | if (typeof color === 'number') {
3 | const r = (color & 0xff0000) >> 16;
4 | const g = (color & 0x00ff00) >> 8;
5 | const b = (color & 0x0000ff);
6 |
7 | return new Float32Array([
8 | r / 255,
9 | g / 255,
10 | b / 255
11 | ]);
12 | }
13 |
14 | return color;
15 | }
16 |
17 | export function normalize(out, vector = out) {
18 | let total = 0;
19 | for (let i = 0; i < vector.length; i += 1) {
20 | total += vector[i] * vector[i];
21 | }
22 |
23 | const mag = Math.sqrt(total);
24 |
25 | out[0] = vector[0] / mag;
26 | out[1] = vector[1] / mag;
27 | out[2] = vector[2] / mag;
28 |
29 | return out;
30 | }
31 |
32 | export function create_worker(url, fn) {
33 | const worker = new Worker(url);
34 | const code = fn.toString().replace(/^(function.+?|.+?=>\s*)\{/g, '').slice(0, -1);
35 |
36 | worker.postMessage(code);
37 |
38 | return worker;
39 | }
40 |
41 | export function memoize(fn) {
42 | const cache = new Map();
43 | return (...args) => {
44 | const hash = JSON.stringify(args);
45 | if (!cache.has(hash)) cache.set(hash, fn(...args));
46 | return cache.get(hash);
47 | };
48 | }
--------------------------------------------------------------------------------
/API.md:
--------------------------------------------------------------------------------
1 | # Using Svelte GL
2 |
3 | You'll need a bundler plugin that recognises .glsl files and turns them into JavaScript strings, e.g. https://github.com/glslify/rollup-plugin-glslify.
4 |
5 |
6 | ## Creating a basic scene
7 |
8 | ```html
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
28 |
29 |
30 |
37 |
38 | ```
39 |
40 | Each scene needs:
41 |
42 | * a top-level `` component
43 | * one or more lights — currently we have ``, `` and ``
44 | * one or more `` components — the objects in the scene
45 |
46 |
--------------------------------------------------------------------------------
/geometry/dodecahedron.mjs:
--------------------------------------------------------------------------------
1 | import polyhedron from './polyhedron.mjs';
2 |
3 | // adapted from https://github.com/mrdoob/three.js/blob/master/src/geometries/DodecahedronGeometry.js
4 | // MIT licensed https://github.com/mrdoob/three.js/blob/dev/LICENSE
5 |
6 | const t = ( 1 + Math.sqrt( 5 ) ) / 2;
7 | const r = 1 / t;
8 |
9 | const vertices = [
10 | // (±1, ±1, ±1)
11 | -1, -1, -1, -1, -1, +1,
12 | -1, +1, -1, -1, +1, +1,
13 | +1, -1, -1, +1, -1, +1,
14 | +1, +1, -1, +1, +1, +1,
15 |
16 | // (0, ±1/φ, ±φ)
17 | 0, -r, -t, 0, -r, +t,
18 | 0, +r, -t, 0, +r, +t,
19 |
20 | // (±1/φ, ±φ, 0)
21 | -r, -t, 0, -r, +t, 0,
22 | +r, -t, 0, +r, +t, 0,
23 |
24 | // (±φ, 0, ±1/φ)
25 | -t, 0, -r, +t, 0, -r,
26 | -t, 0, +r, +t, 0, +r
27 | ];
28 |
29 | const indices = [
30 | 3, 11, 7, 3, 7, 15, 3, 15, 13,
31 | 7, 19, 17, 7, 17, 6, 7, 6, 15,
32 | 17, 4, 8, 17, 8, 10, 17, 10, 6,
33 | 8, 0, 16, 8, 16, 2, 8, 2, 10,
34 | 0, 12, 1, 0, 1, 18, 0, 18, 16,
35 | 6, 10, 2, 6, 2, 13, 6, 13, 15,
36 | 2, 16, 18, 2, 18, 3, 2, 3, 13,
37 | 18, 1, 9, 18, 9, 11, 18, 11, 3,
38 | 4, 14, 12, 4, 12, 0, 4, 0, 8,
39 | 11, 9, 5, 11, 5, 19, 11, 19, 7,
40 | 19, 5, 14, 19, 14, 4, 19, 4, 17,
41 | 1, 12, 14, 1, 14, 5, 1, 5, 9
42 | ];
43 |
44 | export default function({ subdivisions, shading } = {}) {
45 | return polyhedron(vertices, indices, subdivisions, shading);
46 | }
--------------------------------------------------------------------------------
/scene/cameras/OrthoCamera.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scene/cameras/PerspectiveCamera.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/scene/Mesh/shaders/default/vert.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 | attribute vec3 normal;
3 |
4 | varying vec3 v_normal;
5 |
6 | #if defined(has_colormap) || defined(has_specularitymap) || defined(has_normalmap) || defined(has_bumpmap)
7 | #define has_textures true
8 | #endif
9 |
10 | #ifdef has_textures
11 | attribute vec2 uv;
12 | varying vec2 v_uv;
13 | #endif
14 |
15 | #if defined(has_normalmap) || defined(has_bumpmap)
16 | varying vec3 v_view_position;
17 | #endif
18 |
19 | varying vec3 v_surface_to_light[NUM_LIGHTS];
20 |
21 | #ifdef has_specularity
22 | varying vec3 v_surface_to_view[NUM_LIGHTS];
23 | #endif
24 |
25 | #ifdef USE_FOG
26 | varying float v_fog_depth;
27 | #endif
28 |
29 | void main() {
30 | vec4 pos = vec4(position, 1.0);
31 | vec4 model_view_pos = VIEW * MODEL * pos;
32 |
33 | v_normal = (MODEL_INVERSE_TRANSPOSE * vec4(normal, 0.0)).xyz;
34 |
35 | #ifdef has_textures
36 | v_uv = uv;
37 | #endif
38 |
39 | #if defined(has_normalmap) || defined(has_bumpmap)
40 | v_view_position = model_view_pos.xyz;
41 | #endif
42 |
43 | #ifdef USE_FOG
44 | v_fog_depth = -model_view_pos.z;
45 | #endif
46 |
47 | for (int i = 0; i < NUM_LIGHTS; i += 1) {
48 | PointLight light = POINT_LIGHTS[i];
49 |
50 | vec3 surface_world_position = (MODEL * pos).xyz;
51 | v_surface_to_light[i] = light.location - surface_world_position;
52 |
53 | #ifdef has_specularity
54 | v_surface_to_view[i] = CAMERA_WORLD_POSITION - surface_world_position;
55 | #endif
56 | }
57 |
58 | gl_Position = PROJECTION * model_view_pos;
59 | }
--------------------------------------------------------------------------------
/index.mjs:
--------------------------------------------------------------------------------
1 | // scene
2 | export { default as Scene } from './scene/Scene.svelte';
3 | export { default as Group } from './scene/Group.svelte';
4 | export { default as Layer } from './scene/Layer.svelte';
5 | export { default as Mesh } from './scene/Mesh/index.svelte';
6 | export { default as Overlay } from './scene/Overlay.svelte';
7 | export { default as Point } from './scene/Point.svelte';
8 | export { default as Target } from './scene/Target.svelte';
9 |
10 | // lights
11 | export { default as AmbientLight } from './scene/lights/AmbientLight.svelte';
12 | export { default as DirectionalLight } from './scene/lights/DirectionalLight.svelte';
13 | export { default as PointLight } from './scene/lights/PointLight.svelte';
14 |
15 | // controls
16 | export { default as OrbitControls } from './controls/OrbitControls.svelte';
17 |
18 | // cameras
19 | export { default as PerspectiveCamera } from './scene/cameras/PerspectiveCamera.svelte';
20 | export { default as OrthoCamera } from './scene/cameras/OrthoCamera.svelte';
21 |
22 | // geometry
23 | export { default as Geometry } from './geometry/Geometry.mjs';
24 | export { default as box } from './geometry/box.mjs';
25 | export { default as cone } from './geometry/cone.mjs';
26 | export { default as dodecahedron } from './geometry/dodecahedron.mjs';
27 | export { default as plane } from './geometry/plane.mjs';
28 | export { default as icosphere } from './geometry/icosphere.mjs';
29 | export { default as sphere } from './geometry/sphere.mjs';
30 |
31 | // textures
32 | export { default as Texture } from './abstract/Texture.mjs';
--------------------------------------------------------------------------------
/scene/Group.svelte:
--------------------------------------------------------------------------------
1 |
46 |
47 |
--------------------------------------------------------------------------------
/scene/Point.svelte:
--------------------------------------------------------------------------------
1 |
48 |
49 |
--------------------------------------------------------------------------------
/internal/image.mjs:
--------------------------------------------------------------------------------
1 | import { create_worker } from './utils.mjs';
2 |
3 | const worker_url = (typeof Blob !== 'undefined' && URL.createObjectURL(new Blob(
4 | [`self.onmessage = e => { self.onmessage = null; eval(e.data); };`],
5 | { type: 'application/javascript' }
6 | ))) || typeof window !== 'undefined' && window.SVELTE_GL_WORKER_URL;
7 |
8 | const image_cache = new Map();
9 |
10 | export function load_image(src) {
11 | if (!worker_url) {
12 | throw new Error(`Workers cannot be created from Blob URLs in this browser. Please set SVELTE_GL_WORKER_URL`);
13 | }
14 |
15 | if (!image_cache.has(src)) {
16 | image_cache.set(src, new Promise((fulfil, reject) => {
17 | if (typeof createImageBitmap !== 'undefined') {
18 | // TODO pool workers?
19 | const worker = create_worker(worker_url, () => {
20 | self.onmessage = e => {
21 | fetch(e.data, { mode: 'cors' })
22 | .then(response => response.blob())
23 | .then(blobData => createImageBitmap(blobData))
24 | .then(bitmap => {
25 | self.postMessage({ bitmap }, [bitmap]);
26 | })
27 | .catch(error => {
28 | self.postMessage({
29 | error: {
30 | message: error.message,
31 | stack: error.stack
32 | }
33 | });
34 | });
35 | };
36 | });
37 |
38 | worker.onmessage = e => {
39 | if (e.data.error) {
40 | image_cache.delete(src);
41 | reject(e.data.error);
42 | }
43 |
44 | else fulfil(e.data.bitmap);
45 | };
46 |
47 | worker.postMessage(new URL(src, location.href).href);
48 | } else {
49 | const img = new Image();
50 | img.crossOrigin = '';
51 | img.onload = () => fulfil(img);
52 | img.onerror = e => {
53 | image_cache.delete(src);
54 | reject(e);
55 | };
56 | img.src = src;
57 | }
58 | }));
59 | }
60 |
61 | return image_cache.get(src);
62 | }
--------------------------------------------------------------------------------
/internal/index.mjs:
--------------------------------------------------------------------------------
1 | import { getContext, setContext, onDestroy } from 'svelte';
2 |
3 | export const RENDERER = {};
4 | export const LAYER = {};
5 | export const PARENT = {};
6 | export const CAMERA = {};
7 |
8 | export function get_scene() {
9 | return getContext(RENDERER);
10 | }
11 |
12 | export function get_layer() {
13 | return getContext(LAYER);
14 | }
15 |
16 | export function get_parent() {
17 | return getContext(PARENT);
18 | }
19 |
20 | export function get_camera() {
21 | return getContext(CAMERA);
22 | }
23 |
24 | export function set_layer(layer) {
25 | setContext(LAYER, layer);
26 | }
27 |
28 | export function set_parent(parent) {
29 | setContext(PARENT, parent);
30 | }
31 |
32 | function remove_index(array, index) {
33 | array[index] = array[array.length - 1];
34 | array.pop();
35 | }
36 |
37 | function remove_item(array, item) {
38 | const index = array.indexOf(item);
39 | if (~index) remove_index(array, index);
40 | }
41 |
42 | export function create_layer(index, invalidate) {
43 | let child_index = 0;
44 |
45 | const meshes = [];
46 | const transparent_meshes = [];
47 | const child_layers = [];
48 |
49 | const layer = {
50 | index: 0,
51 | meshes,
52 | transparent_meshes,
53 | child_layers,
54 | needs_sort: false,
55 | needs_transparency_sort: true,
56 | add_mesh: (mesh, existing) => {
57 | if (existing) {
58 | remove_item(mesh.transparent ? meshes : transparent_meshes, mesh);
59 | }
60 |
61 | if (mesh.transparent) {
62 | transparent_meshes.push(mesh);
63 | layer.needs_transparency_sort = true;
64 | } else {
65 | meshes.push(mesh);
66 | }
67 |
68 | onDestroy(() => {
69 | remove_item(meshes, mesh);
70 | remove_item(transparent_meshes, mesh);
71 | invalidate();
72 | });
73 | },
74 | add_child: (index = child_index++) => {
75 | const child_layer = create_layer(index, invalidate);
76 | child_layers.push(child_layer);
77 |
78 | layer.needs_sort = true;
79 |
80 | onDestroy(() => {
81 | remove_item(child_layers, child_layer);
82 |
83 | layer.needs_sort = true;
84 | invalidate();
85 | });
86 |
87 | return child_layer;
88 | }
89 | };
90 |
91 | return layer;
92 | }
--------------------------------------------------------------------------------
/geometry/sphere.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize } from '../internal/utils.mjs';
3 |
4 | const PI = Math.PI;
5 | const PI2 = PI * 2;
6 |
7 | function create_smooth_geometry(turns, bands) {
8 | const num_vertices = (turns + 1) * (bands + 1);
9 | const num_faces_per_turn = 2 * (bands - 1);
10 | const num_faces = num_faces_per_turn * turns;
11 |
12 | const position = new Float32Array(num_vertices * 3); // doubles as normal
13 | const uv = new Float32Array(num_vertices * 2);
14 | const index = new Uint32Array(num_faces * 3);
15 |
16 | let position_index = 0;
17 | let uv_index = 0;
18 |
19 | for (let i = 0; i <= turns; i += 1) {
20 | const u = i / turns;
21 |
22 | for (let j = 0; j <= bands; j += 1) {
23 | const v = j / bands;
24 |
25 | const x = -Math.cos(u * PI2) * Math.sin(v * PI);
26 | const y = Math.cos(v * PI);
27 | const z = Math.sin(u * PI2) * Math.sin(v * PI);
28 |
29 | position[position_index++] = x;
30 | position[position_index++] = y;
31 | position[position_index++] = z;
32 |
33 | uv[uv_index++] = u;
34 | uv[uv_index++] = v;
35 | }
36 | }
37 |
38 | let face_index = 0;
39 |
40 | for (let i = 0; i < turns; i += 1) {
41 | const offset = i * (bands + 1);
42 |
43 | // north pole face
44 | index[face_index++] = offset + 0;
45 | index[face_index++] = offset + 1;
46 | index[face_index++] = offset + bands + 2;
47 |
48 | for (let j = 1; j < bands - 1; j += 1) {
49 | index[face_index++] = offset + j;
50 | index[face_index++] = offset + j + 1;
51 | index[face_index++] = offset + j + bands + 1;
52 |
53 | index[face_index++] = offset + j + bands + 1;
54 | index[face_index++] = offset + j + 1;
55 | index[face_index++] = offset + j + bands + 2;
56 | }
57 |
58 | index[face_index++] = offset + bands - 1;
59 | index[face_index++] = offset + bands;
60 | index[face_index++] = offset + bands * 2;
61 | }
62 |
63 | return new Geometry({
64 | position: {
65 | data: position,
66 | size: 3
67 | },
68 | normal: {
69 | data: position,
70 | size: 3
71 | },
72 | uv: {
73 | data: uv,
74 | size: 2
75 | }
76 | }, {
77 | index
78 | });
79 | }
80 |
81 | function create_flat_geometry(turns, bands) {
82 | throw new Error('TODO implement flat geometry');
83 | }
84 |
85 | export default memoize(({ turns = 8, bands = 6, shading = 'smooth' } = {}) => {
86 | return shading === 'smooth'
87 | ? create_smooth_geometry(turns, bands)
88 | : create_flat_geometry(turns, bands);
89 | });
--------------------------------------------------------------------------------
/scene/Mesh/index.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/geometry/cone.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize } from '../internal/utils.mjs';
3 |
4 | function create_flat_geometry(radius, height, sides) {
5 | const num_vertices = sides * 3;
6 |
7 | const position_data = new Float32Array(num_vertices * 3 * 2);
8 | const normal_data = new Float32Array(num_vertices * 3 * 2);
9 |
10 | const ny = radius / height;
11 |
12 | for (let i = 0; i < sides; i += 1) {
13 | const start_angle = (i / sides) * Math.PI * 2;
14 | const end_angle = ((i + 1) / sides) * Math.PI * 2;
15 | const half_angle = (start_angle + end_angle) / 2;
16 |
17 | let o = i * 3 * 3 * 2;
18 |
19 | const x1 = Math.sin(start_angle) * radius;
20 | const z1 = Math.cos(start_angle) * radius;
21 | const x2 = Math.sin(end_angle) * radius;
22 | const z2 = Math.cos(end_angle) * radius;
23 |
24 | // top face
25 | position_data[o + 0] = x1;
26 | position_data[o + 1] = 0;
27 | position_data[o + 2] = z1;
28 |
29 | position_data[o + 3] = x2;
30 | position_data[o + 4] = 0;
31 | position_data[o + 5] = z2;
32 |
33 | position_data[o + 6] = 0;
34 | position_data[o + 7] = height;
35 | position_data[o + 8] = 0;
36 |
37 | const nx = Math.sin(half_angle);
38 | const nz = Math.cos(half_angle);
39 |
40 | const mag = Math.sqrt(nx * nx + ny * ny + nz * nz);
41 |
42 | const nnx = nx / mag;
43 | const nny = ny / mag;
44 | const nnz = nz / mag;
45 |
46 | normal_data[o + 0] = normal_data[o + 3] = normal_data[o + 6] = nnx;
47 | normal_data[o + 1] = normal_data[o + 4] = normal_data[o + 7] = nny;
48 | normal_data[o + 2] = normal_data[o + 5] = normal_data[o + 8] = nnz;
49 |
50 | o += 9;
51 |
52 | // bottom face
53 | position_data[o + 0] = x2;
54 | position_data[o + 1] = 0;
55 | position_data[o + 2] = z2;
56 |
57 | position_data[o + 3] = x1;
58 | position_data[o + 4] = 0;
59 | position_data[o + 5] = z1;
60 |
61 | position_data[o + 6] = 0;
62 | position_data[o + 7] = 0;
63 | position_data[o + 8] = 0;
64 |
65 | normal_data[o + 0] = normal_data[o + 3] = normal_data[o + 6] = 0;
66 | normal_data[o + 1] = normal_data[o + 4] = normal_data[o + 7] = -1;
67 | normal_data[o + 2] = normal_data[o + 5] = normal_data[o + 8] = 0;
68 | }
69 |
70 | return new Geometry({
71 | position: {
72 | data: position_data,
73 | size: 3
74 | },
75 |
76 | normal: {
77 | data: normal_data,
78 | size: 3
79 | }
80 | });
81 | }
82 |
83 | function create_smooth_geometry(radius, height, sides) {
84 | throw new Error('TODO');
85 | }
86 |
87 | export default memoize(({ radius = 1, height = 1, sides = 12, shading = 'flat' } = {}) => {
88 | return shading === 'flat'
89 | ? create_flat_geometry(radius, height, sides)
90 | : create_smooth_geometry(radius, height, sides);
91 | });
--------------------------------------------------------------------------------
/scene/Mesh/Material.mjs:
--------------------------------------------------------------------------------
1 | import vert_builtin from './shaders/builtin/vert.glsl';
2 | import frag_builtin from './shaders/builtin/frag.glsl';
3 | import { compile } from './program.mjs';
4 | import { process_color } from '../../internal/utils.mjs';
5 |
6 | function deep_set(obj, path, value) {
7 | const parts = path.replace(/\]$/, '').split(/\[|\]\.|\./);
8 |
9 | while (parts.length > 1) {
10 | const part = parts.shift();
11 | const next = parts[0];
12 |
13 | if (!obj[part]) obj[part] = /^\d+$/.test(next) ? [] : {};
14 | obj = obj[part];
15 | }
16 |
17 | obj[parts[0]] = value;
18 | }
19 |
20 | export default class Material {
21 | constructor(scene, vert, frag, defines) {
22 | this.scene = scene;
23 |
24 | const gl = scene.gl;
25 | this.gl = gl;
26 |
27 | const { program, uniforms, attributes } = compile(
28 | gl,
29 | scene.defines + defines + '\n\n' + vert_builtin + '\n\n' + vert,
30 | scene.defines + defines + '\n\n' + frag_builtin + '\n\n' + frag
31 | );
32 |
33 | this.program = program;
34 | this.uniforms = uniforms;
35 | this.attributes = attributes;
36 |
37 | this.uniform_locations = {};
38 | this.uniforms.forEach(uniform => {
39 | deep_set(this.uniform_locations, uniform.name, gl.getUniformLocation(this.program, uniform.name));
40 | });
41 |
42 | this.attribute_locations = {};
43 | this.attributes.forEach(attribute => {
44 | this.attribute_locations[attribute.name] = gl.getAttribLocation(this.program, attribute.name);
45 | });
46 |
47 | this.raw_values = {};
48 | this.values = {};
49 | }
50 |
51 | set_uniforms(raw_values) {
52 | let texture_index = 0;
53 |
54 | this.uniforms.forEach(({ name, type, loc, setter, processor }) => {
55 | if (name in raw_values) {
56 | let data = raw_values[name];
57 |
58 | if (data === this.raw_values[name]) return;
59 |
60 | if (type === 35678) {
61 | // texture
62 | this.values[name] = {
63 | texture: data.instantiate(this.scene)._,
64 | index: texture_index++
65 | };
66 |
67 | return;
68 | }
69 |
70 | if (typeof data === 'number' && type !== 5126) {
71 | // data provided was a number like 0x123456,
72 | // but it needs to be an array. for now,
73 | // assume it's a color, i.e. vec3
74 | data = process_color(data);
75 | }
76 |
77 | this.values[name] = data;
78 | }
79 | });
80 |
81 | this.raw_values = raw_values;
82 | }
83 |
84 | apply_uniforms(gl, builtins) {
85 | // TODO if this is the only program, maybe
86 | // we don't need to re-run this each time
87 | this.uniforms.forEach(uniform => {
88 | if (uniform.name in this.values) {
89 | uniform.setter(gl, uniform.loc, this.values[uniform.name]);
90 | }
91 | });
92 | }
93 |
94 | destroy() {
95 | // TODO
96 | }
97 | }
--------------------------------------------------------------------------------
/geometry/box.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize } from '../internal/utils.mjs';
3 |
4 | export default memoize(() => {
5 | let pos = 0.5;
6 | let neg = -0.5;
7 |
8 | return new Geometry({
9 | position: {
10 | data: new Float32Array([
11 | // front
12 | pos, pos, pos,
13 | neg, pos, pos,
14 | pos, neg, pos,
15 | neg, neg, pos,
16 |
17 | // left
18 | neg, pos, pos,
19 | neg, pos, neg,
20 | neg, neg, pos,
21 | neg, neg, neg,
22 |
23 | // back
24 | neg, pos, neg,
25 | pos, pos, neg,
26 | neg, neg, neg,
27 | pos, neg, neg,
28 |
29 | // right
30 | pos, pos, neg,
31 | pos, pos, pos,
32 | pos, neg, neg,
33 | pos, neg, pos,
34 |
35 | // top
36 | neg, pos, neg,
37 | neg, pos, pos,
38 | pos, pos, neg,
39 | pos, pos, pos,
40 |
41 | // bottom
42 | neg, neg, pos,
43 | neg, neg, neg,
44 | pos, neg, pos,
45 | pos, neg, neg
46 | ]),
47 | size: 3
48 | },
49 |
50 | normal: {
51 | data: new Float32Array([
52 | // front
53 | 0, 0, 1,
54 | 0, 0, 1,
55 | 0, 0, 1,
56 | 0, 0, 1,
57 |
58 | // left
59 | -1, 0, 0,
60 | -1, 0, 0,
61 | -1, 0, 0,
62 | -1, 0, 0,
63 |
64 | // back
65 | 0, 0, -1,
66 | 0, 0, -1,
67 | 0, 0, -1,
68 | 0, 0, -1,
69 |
70 | // right
71 | 1, 0, 0,
72 | 1, 0, 0,
73 | 1, 0, 0,
74 | 1, 0, 0,
75 |
76 | // top
77 | 0, 1, 0,
78 | 0, 1, 0,
79 | 0, 1, 0,
80 | 0, 1, 0,
81 |
82 | // bottom
83 | 0, -1, 0,
84 | 0, -1, 0,
85 | 0, -1, 0,
86 | 0, -1, 0
87 | ]),
88 | size: 3
89 | },
90 |
91 | uv: {
92 | data: new Float32Array([
93 | // front
94 | 2/4, 1/4,
95 | 1/4, 1/4,
96 | 2/4, 2/4,
97 | 1/4, 2/4,
98 |
99 | // left
100 | 1/4, 1/4,
101 | 0/4, 1/4,
102 | 1/4, 2/4,
103 | 0/4, 2/4,
104 |
105 | // back
106 | 4/4, 1/4,
107 | 3/4, 1/4,
108 | 4/4, 2/4,
109 | 3/4, 2/4,
110 |
111 | // right
112 | 3/4, 1/4,
113 | 2/4, 1/4,
114 | 3/4, 2/4,
115 | 2/4, 2/4,
116 |
117 | // top
118 | 1/4, 0/4,
119 | 1/4, 1/4,
120 | 2/4, 0/4,
121 | 2/4, 1/4,
122 |
123 | // bottom
124 | 1/4, 2/4,
125 | 1/4, 3/4,
126 | 2/4, 2/4,
127 | 2/4, 3/4
128 | ]),
129 | size: 2
130 | }
131 | }, {
132 | index: new Uint32Array([
133 | // front
134 | 0, 1, 2,
135 | 3, 2, 1,
136 |
137 | // left
138 | 4, 5, 6,
139 | 7, 6, 5,
140 |
141 | // back
142 | 8, 9, 10,
143 | 11, 10, 9,
144 |
145 | // right
146 | 12, 13, 14,
147 | 15, 14, 13,
148 |
149 | // top
150 | 16, 17, 18,
151 | 19, 18, 17,
152 |
153 | // bottom
154 | 20, 21, 22,
155 | 23, 22, 21
156 | ])
157 | });
158 | });
--------------------------------------------------------------------------------
/scratch/TAXONOMY.md:
--------------------------------------------------------------------------------
1 | # Taxonomy
2 |
3 | This is a rough attempt to organise the various concepts in Svelte GL. It is subject to change.
4 |
5 |
6 | ## Scene
7 |
8 | The root component. A `` creates a container element and a canvas, and manages all the entities inside, determining when to redraw.
9 |
10 | A scene contains one or more *layers*, and exactly one *camera*.
11 |
12 | > It's possible to imagine that we might want to decouple the scene from the canvas — e.g. we might want to view the same scene from different angles (as in a 3D editor) or render a scene to a texture.
13 |
14 |
15 | ## Layer
16 |
17 | Clears the depth buffer, guaranteeing that objects contained within are drawn on top of whatever is on lower layers. Particularly useful in situations where you'd otherwise experience z-fighting.
18 |
19 | The first layer is 'implicit' — you don't need to create a `` component inside your scene if everything will be on the same layer.
20 |
21 | Layers contain zero or more *objects*.
22 |
23 |
24 | ## Object
25 |
26 | An object is something with a transform (a location and rotation and a scale).
27 |
28 | ### Group
29 |
30 | A logical grouping of objects that can be used to apply transformations to all of them simultaneously.
31 |
32 | ### Camera
33 |
34 | Cameras are objects, whose sole purpose is to generate the view matrix, which manifests as a scene-wide *uniform*.
35 |
36 | ### Light
37 |
38 | Lights also manifest as scene-wide uniforms.
39 |
40 | ### Mesh
41 |
42 | Meshes are objects with a *geometry* and a *material*. They are the only things that actually get drawn, other than overlays.
43 |
44 |
45 | ## Geometry
46 |
47 | A geometry is a collection of *attributes*, a primitive (e.g. triangle), and an (optional) index.
48 |
49 | An attribute is a typed array (normally a Float32Array, I think?) plus a size.
50 |
51 | > There are also `normalized` and `dynamic` properties, which don't currently do anything?
52 |
53 | ## Material
54 |
55 | A material is a combination of a WebGL *program* and a number of uniforms that pertain to that program. Some of those uniforms are 'well known', such as alpha, specularity, colorMap and so on. Some may be specific to the program's shader, if it's not using a default shader.
56 |
57 | Each mesh has its own material. Many materials can share the same program.
58 |
59 |
60 | ## Open questions
61 |
62 | * How can we make it easy to share materials between meshes while also making it easy to define materials in an ad-hoc way as props on meshes (and their wrappers)?
63 |
64 | * Geometries and materials need to belong to a scene (or more accurately, to the `gl` context associated with the scene, though it's useful if they have a reference to the scene so that they can invalidate it). How can we do that in a way that isn't onerous for scene authors?
65 |
66 | * In the medium term, is there a way to design the API such that a compiler could omit built-in shader chunks that weren't used?
67 |
68 | * How can we make shader chunks composable?
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This project is deprecated — we recommend https://threlte.xyz/
2 |
3 | ---
4 |
5 | # @sveltejs/gl
6 |
7 | Declarative WebGL scene graphs inside [Svelte](https://svelte.dev).
8 |
9 |
10 |
11 |
12 |
13 | ## Here be dragons
14 |
15 | This is not even *close* to production-ready. It's missing many essential features, and isn't optimised. The API is certain to change in substantial ways. You have been warned.
16 |
17 | If you're not fazed by that sort of thing, and would like to help figure out how to make this all work, contributions are very welcome.
18 |
19 |
20 | ## Why are you doing this?
21 |
22 | Someone once described the DOM as 'an API that only a mother could love'. Clearly that person had never tried to build anything with WebGL. Hand-written WebGL code is *phenomenally* ugly and confusing.
23 |
24 | Because of that, people typically reach for libraries like [Three.js](https://threejs.org/) and [regl](http://regl.party/). I use and love both, but they each have characteristics that make them unsuitable for certain tasks:
25 |
26 | * Three is comfortably over half a megabyte minified, making it less than idea for *casual* or *whimsical* graphics — especially on low-powered devices where your JS budget is small. Typically, when you encounter Three in the wild, it's used for a standalone set-piece interactive
27 | * regl is intentionally low-level. I've found it to be difficult to use for quickly iterating on an idea, because you're thinking in terms of *draw calls* rather than a *scene graph*
28 |
29 | Meanwhile, [A-Frame](https://aframe.io/) — which is a web component wrapper around Three — has demonstrated that a declarative markup-driven approach provides a great authoring experience, particularly for certain kinds of scenes. Since it builds on top of Three, it inherits its strengths and weaknesses.
30 |
31 | @sveltejs/gl is several things:
32 |
33 | * An experiment to test a hypothesis that building scene graphs 'the Svelte way' is satisfying and productive, and needn't cost much in terms of JavaScript
34 | * A way to figure out what the right APIs are
35 | * A test to see how much of this can be built in Svelte already (and still perform well) without needing to extend the compiler
36 | * A thing I need at work
37 |
38 |
39 | ## How can I use it?
40 |
41 | Check out [this demo](https://svelte.dev/repl/8d6d139a3d634c2fb1e1ff107c123dd5) to get started. There's not much point writing docs until things are a bit more settled.
42 |
43 |
44 | ## Ideas for future development
45 |
46 | Some of these may be a bit pie-in-the-sky:
47 |
48 | * Post-processing
49 | * Scene graph editor
50 | * WebVR (this would require Svelte to be able to use the VR display's `requestAnimationFrame` instead of the default one, since the refresh rate on VR devices can be 90 or 120fps)
51 | * Interactivity (i.e. raycasting)
52 | * Physics
53 | * Interleaving DOM and GL content (i.e. part of the scene renders behind the DOM, some in front)
54 | * What's the analog of server-side rendering for a GL scene — precomputing high fidelity global illumination maps etc?
55 |
56 |
57 | ## License
58 |
59 | MIT
60 |
--------------------------------------------------------------------------------
/abstract/Texture.mjs:
--------------------------------------------------------------------------------
1 | import * as constants from '../internal/constants.mjs';
2 | import { load_image } from '../internal/image.mjs';
3 |
4 | const is_power_of_two = n => (n & (n - 1)) === 0;
5 |
6 | const black_pixel = new Uint8Array([0, 0, 0, 255]);
7 |
8 | class TextureInstance {
9 | constructor(scene, texture) {
10 | const { gl } = scene;
11 |
12 | this._ = gl.createTexture();
13 |
14 | if (typeof texture.data === 'string') {
15 | this.bind(gl, texture, black_pixel);
16 |
17 | texture.ready.then(() => {
18 | this.bind(gl, texture, texture.data);
19 | scene.invalidate();
20 | });
21 | } else {
22 | this.bind(gl, texture, texture.data);
23 | }
24 | }
25 |
26 | bind(gl, texture, data) {
27 | gl.bindTexture(constants.TEXTURE_2D, this._);
28 |
29 | if (ArrayBuffer.isView(data)) {
30 | // TODO figure out where this goes
31 | const width = 1;
32 | const height = 1;
33 |
34 | gl.texImage2D(constants.TEXTURE_2D, 0, constants.RGBA, width, height, 0, constants.RGBA, constants.UNSIGNED_BYTE, data);
35 | } else {
36 | gl.texImage2D(constants.TEXTURE_2D, 0, constants.RGBA, constants.RGBA, constants.UNSIGNED_BYTE, data);
37 | }
38 |
39 | const width = 'naturalWidth' in data ? data.naturalWidth : data.width;
40 | const height = 'naturalHeight' in data ? data.naturalHeight : data.height;
41 |
42 | if (is_power_of_two(width) && is_power_of_two(height)) {
43 | gl.generateMipmap(constants.TEXTURE_2D);
44 |
45 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_WRAP_S, texture.opts.wrapS);
46 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_WRAP_T, texture.opts.wrapT);
47 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_MIN_FILTER, texture.opts.minFilter);
48 | } else {
49 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_WRAP_S, constants.CLAMP_TO_EDGE);
50 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_WRAP_T, constants.CLAMP_TO_EDGE);
51 | gl.texParameteri(constants.TEXTURE_2D, constants.TEXTURE_MIN_FILTER, constants.LINEAR);
52 | }
53 | }
54 | }
55 |
56 | const caches = new Map();
57 | let resolved;
58 |
59 | export default class Texture {
60 | constructor(data, opts = {}) {
61 | this.data = data;
62 |
63 | this.opts = {
64 | width: opts.width || 1,
65 | height: opts.height || 1,
66 | internalFormat: opts.format || constants.RGBA,
67 | srcFormat: opts.srcFormat || constants.RGBA,
68 | srcType: opts.srcType || constants.UNSIGNED_BYTE,
69 | wrapS: opts.wrapS || constants.CLAMP_TO_EDGE,
70 | wrapT: opts.wrapT || constants.CLAMP_TO_EDGE,
71 | minFilter: opts.minFilter || constants.LINEAR
72 | };
73 |
74 | // TODO clamp, mipmaps, etc
75 |
76 | this.hash = JSON.stringify(this.opts);
77 |
78 | this.ready = typeof data === 'string'
79 | ? load_image(data).then(img => {
80 | this.data = img;
81 | })
82 | : resolved || (resolved = Promise.resolve());
83 | }
84 |
85 | instantiate(scene, index) {
86 | if (!caches.has(scene)) caches.set(scene, new Map());
87 | const a = caches.get(scene);
88 |
89 | if (!a.has(this.data)) a.set(this.data, new Map());
90 | const b = a.get(this.data);
91 |
92 | if (!b.has(this.hash)) b.set(this.hash, new TextureInstance(scene, this, index));
93 | return b.get(this.hash);
94 | }
95 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @sveltejs/gl changelog
2 |
3 | ## 0.0.37
4 |
5 | * Fix `geometry.update(...)` when length changes
6 |
7 | ## 0.0.36
8 |
9 | * Squelch missing prop warning
10 |
11 | ## 0.0.35
12 |
13 | * Use `translate3d` with overlays, add `will-change: transform`
14 |
15 | ## 0.0.34
16 |
17 | * Add `doubleSided` property to meshes
18 |
19 | ## 0.0.33
20 |
21 | * Use `requestAnimationFrame` to batch renders
22 |
23 | ## 0.0.32
24 |
25 | * Add `geometry.update(key, data)` method
26 |
27 | ## 0.0.31
28 |
29 | * Squelch some prop warnings
30 | * Add `GL.dodecahedron` geometry
31 |
32 | ## 0.0.30
33 |
34 | * Add `snap` prop to `` components, to snap to pixel
35 |
36 | ## 0.0.29
37 |
38 | * Default to sensible blending
39 |
40 | ## 0.0.28
41 |
42 | * Fix `alpha` uniform behaviour in default shader when using `colormap`
43 | * Fix rendering of non-indexed geometry
44 |
45 | ## 0.0.27
46 |
47 | * Add `` component
48 | * Add `emissive` and `emissivemap` uniforms to default material
49 |
50 | ## 0.0.26
51 |
52 | * Fix sorting of transparent meshes ([#16](https://github.com/sveltejs/gl/issues/16))
53 |
54 | ## 0.0.25
55 |
56 | * Lose context when scene is destroyed
57 |
58 | ## 0.0.24
59 |
60 | * Work around apparent IntersectionObserver bug
61 |
62 | ## 0.0.23
63 |
64 | * Render on entering viewport
65 |
66 | ## 0.0.22
67 |
68 | * Add an orthographic camera
69 | * Pass width and height into scene
70 | * Sort transparent objects
71 |
72 | ## 0.0.21
73 |
74 | * Make image loading scene-independent
75 |
76 | ## 0.0.20
77 |
78 | * Add `fog` property to ``
79 | * `background` property on `` now takes an `[r,g,b]` array or `0xrrggbb` value
80 | * `backgroundOpacity` property controls background opacity
81 |
82 | ## 0.0.19
83 |
84 | * Pass through `depthTest` and `blend`
85 | * Allow more than one texture
86 |
87 | ## 0.0.18
88 |
89 | * Overhaul ([#22](https://github.com/sveltejs/gl/pull/22))
90 |
91 | ## 0.0.17
92 |
93 | * Default to crossOrigin images
94 |
95 | ## 0.0.16
96 |
97 | * Allow meshes to receive frag, vert, blend properties
98 |
99 | ## 0.0.15
100 |
101 | * Declare extensions first, due to change in Chrome 75
102 |
103 | ## 0.0.14
104 |
105 | * Allow `pixelRatio` to be specified
106 |
107 | ## 0.0.13
108 |
109 | * Fetch image data in `cors` mode
110 |
111 | ## 0.0.12
112 |
113 | * Only update when canvas is visible
114 |
115 | ## 0.0.10-11
116 |
117 | * Fix resize glitches
118 |
119 | ## 0.0.9
120 |
121 | * Support primitives other than lines
122 |
123 | ## 0.0.8
124 |
125 | * Fix blending formula
126 |
127 | ## 0.0.7
128 |
129 | * Default to `highp` floats
130 | * Work around some shader bug I don't understand
131 | * Depth test by default
132 |
133 | ## 0.0.6
134 |
135 | * Fix serialization of minified code sent to workers
136 |
137 | ## 0.0.5
138 |
139 | * Add `` component
140 | * Add `user-select: none` on overlays
141 | * Load and decode images off the main thread
142 | * Allow depth testing to be disabled per-material
143 |
144 | ## 0.0.4
145 |
146 | * Bump mapping
147 | * New `Sphere` implementation
148 |
149 | ## 0.0.3
150 |
151 | * Don't reinitialise element buffers unnecessarily
152 | * Enable `minDistance` and `maxDistance` on OrbitControls
153 |
154 | ## 0.0.2
155 |
156 | * Throttle OrbitControls events
157 | * Add touch support to OrbitControls
158 |
159 | ## 0.0.1
160 |
161 | * First experimental release
--------------------------------------------------------------------------------
/geometry/Geometry.mjs:
--------------------------------------------------------------------------------
1 | class GeometryInstance {
2 | constructor(scene, program, attributes, index, primitive, count) {
3 | this.scene = scene;
4 | const gl = scene.gl;
5 |
6 | this.attributes = attributes;
7 | this.index = index;
8 | this.primitive = primitive;
9 | this.count = count;
10 |
11 | this.locations = {};
12 | this.buffers = {};
13 |
14 | for (const key in attributes) {
15 | const attribute = attributes[key];
16 |
17 | this.locations[key] = gl.getAttribLocation(program, key);
18 |
19 | const buffer = gl.createBuffer();
20 | this.buffers[key] = buffer;
21 |
22 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
23 | gl.bufferData(gl.ARRAY_BUFFER, attribute.data, attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
24 | }
25 |
26 | if (index) {
27 | const buffer = gl.createBuffer();
28 | this.buffers.__index = buffer;
29 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
30 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index, gl.STATIC_DRAW);
31 | }
32 | }
33 |
34 | set_attributes(gl) {
35 | for (const key in this.attributes) {
36 | const attribute = this.attributes[key];
37 |
38 | const loc = this.locations[key];
39 | if (loc < 0) continue; // attribute is unused by current program
40 |
41 | const {
42 | size = 3,
43 | type = gl.FLOAT,
44 | normalized = false,
45 | stride = 0,
46 | offset = 0
47 | } = attribute;
48 |
49 | // Bind the position buffer.
50 | const buffer = this.buffers[key];
51 |
52 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
53 |
54 | // Turn on the attribute
55 | gl.enableVertexAttribArray(loc);
56 |
57 | gl.vertexAttribPointer(
58 | loc,
59 | size,
60 | type,
61 | normalized,
62 | stride,
63 | offset
64 | );
65 | }
66 | }
67 |
68 | update(k, data, count) {
69 | const scene = this.scene;
70 | const { gl } = scene;
71 |
72 | const attribute = this.attributes[k];
73 | const buffer = this.buffers[k];
74 |
75 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
76 | gl.bufferData(gl.ARRAY_BUFFER, attribute.data = data, attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW);
77 |
78 | this.count = count;
79 |
80 | if (count === Infinity) {
81 | throw new Error(`GL.Geometry must be instantiated with one or more { data, size } attributes`);
82 | }
83 |
84 | scene.invalidate();
85 | }
86 | }
87 |
88 | export default class Geometry {
89 | constructor(attributes = {}, opts = {}) {
90 | this.attributes = attributes;
91 |
92 | const { index, primitive = 'TRIANGLES' } = opts;
93 | this.index = index;
94 | this.primitive = primitive.toUpperCase();
95 | this.count = get_count(attributes);
96 |
97 | this.instances = new Map();
98 | }
99 |
100 | instantiate(scene, program) {
101 | if (!this.instances.has(program)) {
102 | this.instances.set(program, new GeometryInstance(
103 | scene,
104 | program,
105 | this.attributes,
106 | this.index,
107 | this.primitive,
108 | this.count
109 | ));
110 | }
111 |
112 | return this.instances.get(program);
113 | }
114 |
115 | update(k, data) {
116 | this.attributes[k].data = data;
117 | this.count = get_count(this.attributes);
118 |
119 | this.instances.forEach(instance => {
120 | instance.update(k, data, this.count);
121 | });
122 | }
123 | }
124 |
125 | function get_count(attributes) {
126 | let min = Infinity;
127 |
128 | for (const k in attributes) {
129 | const count = attributes[k].data.length / attributes[k].size;
130 | if (count < min) min = count;
131 | }
132 |
133 | return min;
134 | }
--------------------------------------------------------------------------------
/geometry/icosphere.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize, normalize } from '../internal/utils.mjs';
3 |
4 | const p = 0.85065080835204;
5 | const q = 0.5257311121191336;
6 |
7 | const position = new Float32Array([
8 | -q, +p, 0,
9 | +q, +p, 0,
10 | -q, -p, 0,
11 | +q, -p, 0,
12 | 0, -q, +p,
13 | 0, +q, +p,
14 | 0, -q, -p,
15 | 0, +q, -p,
16 | +p, 0, -q,
17 | +p, 0, +q,
18 | -p, 0, -q,
19 | -p, 0, +q
20 | ]);
21 |
22 | const index = new Uint16Array([
23 | 0, 11, 5,
24 | 0, 5, 1,
25 | 0, 1, 7,
26 | 0, 7, 10,
27 | 0, 10, 11,
28 | 1, 5, 9,
29 | 5, 11, 4,
30 | 11, 10, 2,
31 | 10, 7, 6,
32 | 7, 1, 8,
33 | 3, 9, 4,
34 | 3, 4, 2,
35 | 3, 2, 6,
36 | 3, 6, 8,
37 | 3, 8, 9,
38 | 4, 9, 5,
39 | 2, 4, 11,
40 | 6, 2, 10,
41 | 8, 6, 7,
42 | 9, 8, 1
43 | ]);
44 |
45 | const smooth_geometry = [
46 | new Geometry({
47 | position: { data: position, size: 3 },
48 | normal: { data: position, size: 3 }
49 | }, { index })
50 | ];
51 |
52 | function subdivide(geometry) {
53 | const index = new Uint32Array(geometry.index.length * 4);
54 |
55 | const old_position = geometry.attributes.position.data;
56 | const new_positions = [];
57 | const lookup = new Map();
58 |
59 | function get_index(point) {
60 | const hash = `${point[0].toPrecision(6)},${point[1].toPrecision(6)},${point[2].toPrecision(6)}`;
61 |
62 | if (lookup.has(hash)) {
63 | return lookup.get(hash);
64 | }
65 |
66 | const index = new_positions.length;
67 | lookup.set(hash, index);
68 | new_positions[index] = point;
69 | return index;
70 | }
71 |
72 | function mid(a, b) {
73 | return get_index([
74 | (a[0] + b[0]) / 2,
75 | (a[1] + b[1]) / 2,
76 | (a[2] + b[2]) / 2
77 | ]);
78 | }
79 |
80 | for (let i = 0; i < geometry.index.length; i += 3) {
81 | const c0 = geometry.index[i + 0];
82 | const c1 = geometry.index[i + 1];
83 | const c2 = geometry.index[i + 2];
84 |
85 | const v0 = [
86 | old_position[c0 * 3 + 0],
87 | old_position[c0 * 3 + 1],
88 | old_position[c0 * 3 + 2]
89 | ];
90 |
91 | const v1 = [
92 | old_position[c1 * 3 + 0],
93 | old_position[c1 * 3 + 1],
94 | old_position[c1 * 3 + 2]
95 | ];
96 |
97 | const v2 = [
98 | old_position[c2 * 3 + 0],
99 | old_position[c2 * 3 + 1],
100 | old_position[c2 * 3 + 2]
101 | ];
102 |
103 | const a = mid(v0, v1);
104 | const b = mid(v1, v2);
105 | const c = mid(v2, v0);
106 |
107 | // four new faces
108 | const j = i * 4;
109 |
110 | index[j + 0] = get_index(v0);
111 | index[j + 1] = a;
112 | index[j + 2] = c;
113 |
114 | index[j + 3] = get_index(v1);
115 | index[j + 4] = b;
116 | index[j + 5] = a;
117 |
118 | index[j + 6] = get_index(v2);
119 | index[j + 7] = c;
120 | index[j + 8] = b;
121 |
122 | index[j + 9] = a
123 | index[j + 10] = b;
124 | index[j + 11] = c;
125 | }
126 |
127 | const position = new Float32Array(new_positions.length * 3);
128 | for (let i = 0; i < new_positions.length; i += 1) {
129 | const vector = normalize(new_positions[i]);
130 |
131 | position[i * 3 + 0] = vector[0];
132 | position[i * 3 + 1] = vector[1];
133 | position[i * 3 + 2] = vector[2];
134 | }
135 |
136 | return new Geometry({
137 | position: { data: position, size: 3 },
138 | normal: { data: position, size: 3 }
139 | }, { index })
140 | }
141 |
142 | function create_smooth_geometry(subdivisions = 0) {
143 | if (!smooth_geometry[subdivisions]) {
144 | const geometry = create_smooth_geometry(subdivisions - 1);
145 | smooth_geometry[subdivisions] = subdivide(geometry);
146 | }
147 |
148 | return smooth_geometry[subdivisions];
149 | }
150 |
151 | function create_flat_geometry(subdivisions) {
152 | throw new Error(`TODO implement flat sphere geometry`);
153 | }
154 |
155 | export default memoize(({ subdivisions = 0, shading = 'smooth' } = {}) => {
156 | return shading === 'smooth'
157 | ? create_smooth_geometry(subdivisions)
158 | : create_flat_geometry(subdivisions);
159 | });
--------------------------------------------------------------------------------
/scene/Mesh/program.mjs:
--------------------------------------------------------------------------------
1 | const caches = new Map();
2 |
3 | const setters = {
4 | [5126]: (gl, loc, data) => gl.uniform1f(loc, data),
5 | [35664]: (gl, loc, data) => gl.uniform2fv(loc, data),
6 | [35665]: (gl, loc, data) => gl.uniform3fv(loc, data),
7 | [35666]: (gl, loc, data) => gl.uniform4fv(loc, data),
8 |
9 | [35674]: (gl, loc, data) => gl.uniformMatrix2fv(loc, false, data),
10 | [35675]: (gl, loc, data) => gl.uniformMatrix3fv(loc, false, data),
11 | [35676]: (gl, loc, data) => gl.uniformMatrix4fv(loc, false, data),
12 |
13 | [35678]: (gl, loc, data) => {
14 | gl.activeTexture(gl[`TEXTURE${data.index}`]);
15 | gl.bindTexture(gl.TEXTURE_2D, data.texture);
16 | gl.uniform1i(loc, data.index);
17 | }
18 | };
19 |
20 | export function compile(gl, vert, frag) {
21 | if (!caches.has(gl)) caches.set(gl, new Map());
22 | const cache = caches.get(gl);
23 |
24 | const hash = vert + frag;
25 | if (!cache.has(hash)) {
26 | const program = create_program(gl, vert, frag);
27 | const uniforms = get_uniforms(gl, program);
28 | const attributes = get_attributes(gl, program);
29 |
30 | cache.set(hash, { program, uniforms, attributes });
31 | }
32 |
33 | return cache.get(hash);
34 | }
35 |
36 | export function remove_program(info) {
37 | const cache = caches.get(info.gl);
38 |
39 | if (--info.users === 0) {
40 | info.gl.deleteProgram(info.program);
41 | cache.delete(info.hash);
42 | }
43 | }
44 |
45 | function pad(num, len = 4) {
46 | num = String(num);
47 | while (num.length < len) num = ` ${num}`;
48 | return num;
49 | }
50 |
51 | function repeat(str, i) {
52 | let result = '';
53 | while (i--) result += str;
54 | return result;
55 | }
56 |
57 | function create_shader(gl, type, source, label) {
58 | const shader = gl.createShader(type);
59 | gl.shaderSource(shader, source);
60 | gl.compileShader(shader);
61 |
62 | if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
63 | return shader;
64 | }
65 |
66 | const log = gl.getShaderInfoLog(shader);
67 | const match = /ERROR: (\d+):(\d+): (.+)/.exec(log);
68 |
69 | if (match) {
70 | const c = +match[1];
71 | const l = +match[2] - 1;
72 |
73 | console.log('%c' + match[3], 'font-weight: bold; color: red');
74 |
75 | const lines = source.split('\n');
76 | for (let i = 0; i < lines.length; i += 1) {
77 | if (Math.abs(l - i) > 5) continue;
78 |
79 | const line = lines[i].replace(/^\t+/gm, tabs => repeat(' ', tabs.length * 4));
80 | const indent = /^\s+/.exec(line);
81 |
82 | const str = `${pad(i)}: ${line}`;
83 |
84 | if (i === l) {
85 | console.log('%c' + str, 'font-weight: bold; color: red');
86 | console.log('%c' + (indent && indent[0] || '') + repeat(' ', c + 6) + '^', 'color: red');
87 | } else {
88 | console.log(str);
89 | }
90 | }
91 |
92 | throw new Error(`Failed to compile ${label} shader`);
93 | }
94 |
95 | throw new Error(`Failed to compile ${label} shader:\n${log}`);
96 | }
97 |
98 | function create_program(gl, vert, frag) {
99 | const program = gl.createProgram();
100 |
101 | gl.attachShader(program, create_shader(gl, gl.VERTEX_SHADER, vert, 'vertex'));
102 | gl.attachShader(program, create_shader(gl, gl.FRAGMENT_SHADER, frag, 'fragment'));
103 | gl.linkProgram(program);
104 |
105 | const success = gl.getProgramParameter(program, gl.LINK_STATUS);
106 | if (!success) {
107 | console.log(gl.getProgramInfoLog(program));
108 | throw new Error(`Failed to compile program:\n${gl.getProgramInfoLog(program)}`);
109 | }
110 |
111 | return program;
112 | }
113 |
114 | function get_uniforms(gl, program) {
115 | const uniforms = [];
116 |
117 | const n = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
118 |
119 | for (let i = 0; i < n; i += 1) {
120 | let { size, type, name } = gl.getActiveUniform(program, i);
121 | const loc = gl.getUniformLocation(program, name);
122 | const setter = setters[type];
123 |
124 | if (!setter) {
125 | throw new Error(`not implemented ${type} (${name})`);
126 | }
127 |
128 | uniforms.push({ size, type, name, setter, loc });
129 | }
130 |
131 | return uniforms;
132 | }
133 |
134 | function get_attributes(gl, program) {
135 | const attributes = [];
136 |
137 | const n = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
138 |
139 | for (let i = 0; i < n; i += 1) {
140 | let { size, type, name } = gl.getActiveAttrib(program, i);
141 | name = name.replace('[0]', '');
142 | const loc = gl.getAttribLocation(program, name);
143 |
144 | attributes.push({ size, type, name, loc });
145 | }
146 |
147 | return attributes;
148 | }
--------------------------------------------------------------------------------
/scene/Mesh/shaders/default/frag.glsl:
--------------------------------------------------------------------------------
1 | // mesh uniforms
2 | #if defined(has_colormap) || defined(has_specularitymap) || defined(has_normalmap) || defined(has_bumpmap) || defined(has_emissivemap)
3 | #define has_textures true
4 | #endif
5 |
6 | #ifdef has_textures
7 | varying vec2 v_uv;
8 | #endif
9 |
10 | #ifdef has_specularity
11 | uniform float specularity;
12 | #endif
13 |
14 | #ifdef has_colormap
15 | uniform sampler2D colormap;
16 | #endif
17 |
18 | #ifdef has_emissivemap
19 | uniform sampler2D emissivemap;
20 | #endif
21 |
22 | #ifdef has_specularitymap
23 | uniform sampler2D specularitymap;
24 | #endif
25 |
26 | #ifdef has_bumpmap
27 | uniform sampler2D bumpmap;
28 |
29 | // adapted from https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl.js
30 | // https://github.com/mrdoob/three.js/blob/dev/LICENSE
31 | vec2 dHdxy_fwd() {
32 | vec2 dSTdx = dFdx(v_uv);
33 | vec2 dSTdy = dFdy(v_uv);
34 |
35 | float Hll = texture2D(bumpmap, v_uv).x;
36 | float dBx = texture2D(bumpmap, v_uv + dSTdx).x - Hll;
37 | float dBy = texture2D(bumpmap, v_uv + dSTdy).x - Hll;
38 |
39 | #ifdef has_bumpscale
40 | Hll *= bumpscale;
41 | dBx *= bumpscale;
42 | dBy *= bumpscale;
43 | #endif
44 |
45 | return vec2(dBx, dBy);
46 | }
47 |
48 | vec3 perturbNormalArb(vec3 surf_pos, vec3 surface_normal, vec2 dHdxy) {
49 | // Workaround for Adreno 3XX dFd*(vec3) bug. See #9988
50 | vec3 vSigmaX = vec3(dFdx(surf_pos.x), dFdx(surf_pos.y), dFdx(surf_pos.z));
51 | vec3 vSigmaY = vec3(dFdy(surf_pos.x), dFdy(surf_pos.y), dFdy(surf_pos.z));
52 | vec3 vN = surface_normal;
53 |
54 | vec3 R1 = cross(vSigmaY, vN);
55 | vec3 R2 = cross(vN, vSigmaX);
56 |
57 | float fDet = dot(vSigmaX, R1);
58 |
59 | fDet *= (float(gl_FrontFacing) * 2.0 - 1.0);
60 |
61 | vec3 vGrad = sign(fDet) * (dHdxy.x * R1 + dHdxy.y * R2);
62 | return normalize(abs(fDet) * surface_normal - vGrad);
63 | }
64 | #endif
65 |
66 | #ifdef has_bumpscale
67 | uniform float bumpscale;
68 | #endif
69 |
70 | #ifdef has_normalmap
71 | uniform sampler2D normalmap;
72 |
73 | vec3 perturbNormal2Arb(vec3 eye_pos, vec3 surface_normal) {
74 | // Workaround for Adreno 3XX dFd*(vec3) bug. See https://github.com/mrdoob/three.js/issues/9988
75 | vec3 q0 = vec3(dFdx(eye_pos.x), dFdx(eye_pos.y), dFdx(eye_pos.z));
76 | vec3 q1 = vec3(dFdy(eye_pos.x), dFdy(eye_pos.y), dFdy(eye_pos.z));
77 |
78 | vec2 st0 = dFdx(v_uv.st);
79 | vec2 st1 = dFdy(v_uv.st);
80 |
81 | // TODO derivative functions don't seem to work on some
82 | // mobile phones - need to investigate
83 | if (length(q0) == 0.0) {
84 | return surface_normal;
85 | }
86 |
87 | float scale = sign(st1.t * st0.s - st0.t * st1.s); // we do not care about the magnitude
88 |
89 | vec3 S = normalize((q0 * st1.t - q1 * st0.t) * scale);
90 | vec3 T = normalize((-q0 * st1.s + q1 * st0.s) * scale);
91 | vec3 N = normalize(surface_normal);
92 | mat3 tsn = mat3(S, T, N);
93 | vec3 mapN = texture2D(normalmap, v_uv).xyz * 2.0 - 1.0;
94 |
95 | // TODO
96 | // mapN.xy *= NORMAL_SCALE;
97 |
98 | mapN.xy *= (float(gl_FrontFacing) * 2.0 - 1.0);
99 | return normalize(tsn * mapN);
100 | }
101 | #endif
102 |
103 | #ifdef has_color
104 | uniform vec3 color;
105 | #endif
106 |
107 | #ifdef has_emissive
108 | uniform vec3 emissive;
109 | #endif
110 |
111 | #ifdef has_alpha
112 | uniform float alpha;
113 | #endif
114 |
115 | #ifdef USE_FOG
116 | uniform vec3 FOG_COLOR;
117 | uniform float FOG_DENSITY;
118 | varying float v_fog_depth;
119 | #endif
120 |
121 | varying vec3 v_normal;
122 |
123 | #if defined(has_normalmap) || defined(has_bumpmap)
124 | varying vec3 v_view_position;
125 | #endif
126 |
127 | varying vec3 v_surface_to_light[NUM_LIGHTS];
128 | varying vec3 v_surface_to_view[NUM_LIGHTS];
129 |
130 | void main () {
131 | vec3 normal = normalize(v_normal);
132 |
133 | #ifdef has_bumpmap
134 | normal = perturbNormalArb(-v_view_position, normal, dHdxy_fwd());
135 | #elif defined(has_normalmap)
136 | normal = perturbNormal2Arb(-v_view_position, normal);
137 | #endif
138 |
139 | vec3 lighting = vec3(0.0);
140 | vec3 spec_amount = vec3(0.0);
141 |
142 | // directional lights
143 | for (int i = 0; i < NUM_LIGHTS; i += 1) {
144 | DirectionalLight light = DIRECTIONAL_LIGHTS[i];
145 |
146 | float multiplier = clamp(dot(normal, -light.direction), 0.0, 1.0);
147 | lighting += multiplier * light.color * light.intensity;
148 | }
149 |
150 | // point lights
151 | for (int i = 0; i < NUM_LIGHTS; i += 1) {
152 | PointLight light = POINT_LIGHTS[i];
153 |
154 | vec3 surface_to_light = normalize(v_surface_to_light[i]);
155 |
156 | float multiplier = clamp(dot(normal, surface_to_light), 0.0, 1.0); // TODO is clamp necessary?
157 | lighting += multiplier * light.color * light.intensity;
158 |
159 | #ifdef has_specularity
160 | vec3 surface_to_view = normalize(v_surface_to_view[i]);
161 | vec3 half_vector = normalize(surface_to_light + surface_to_view);
162 | float spec = clamp(dot(normal, half_vector), 0.0, 1.0);
163 |
164 | #ifdef has_specularitymap
165 | spec *= texture2D(specularitymap, v_uv).r;
166 | #endif
167 |
168 | spec_amount += specularity * spec * light.color * light.intensity;
169 | #endif
170 | }
171 |
172 | #if defined(has_colormap)
173 | gl_FragColor = texture2D(colormap, v_uv);
174 | #elif defined(has_color)
175 | gl_FragColor = vec4(color, 1.0);
176 | #endif
177 |
178 | #ifdef has_alpha
179 | gl_FragColor.a *= alpha;
180 | #endif
181 |
182 | gl_FragColor.rgb *= mix(AMBIENT_LIGHT, vec3(1.0, 1.0, 1.0), lighting);
183 | gl_FragColor.rgb += spec_amount;
184 |
185 | #if defined(has_emissivemap)
186 | gl_FragColor.rgb += texture2D(emissivemap, v_uv);
187 | #elif defined(has_emissive)
188 | gl_FragColor.rgb += emissive;
189 | #endif
190 |
191 | #ifdef USE_FOG
192 | gl_FragColor.rgb = mix(
193 | gl_FragColor.rgb,
194 | FOG_COLOR,
195 | 1.0 - exp(-FOG_DENSITY * FOG_DENSITY * v_fog_depth * v_fog_depth)
196 | );
197 | #endif
198 | }
--------------------------------------------------------------------------------
/geometry/polyhedron.mjs:
--------------------------------------------------------------------------------
1 | import Geometry from './Geometry.mjs';
2 | import { memoize, normalize } from '../internal/utils.mjs';
3 |
4 | // adapted from https://github.com/mrdoob/three.js/blob/master/src/geometries/PolyhedronGeometry.js
5 | // MIT licensed https://github.com/mrdoob/three.js/blob/dev/LICENSE
6 |
7 | function lerp(a, b, t) {
8 | return a.map((aa, i) => {
9 | const bb = b[i];
10 | return aa + (bb - aa) * t;
11 | });
12 | }
13 |
14 | function set2(vector, a, b) {
15 | vector[0] = a;
16 | vector[1] = b;
17 | }
18 |
19 | function set3(vector, a, b, c) {
20 | vector[0] = a;
21 | vector[1] = b;
22 | vector[2] = c;
23 | }
24 |
25 | function correct_uvs(vertex_buffer, uv_buffer) {
26 | const a = new Float32Array(3);
27 | const b = new Float32Array(3);
28 | const c = new Float32Array(3);
29 |
30 | const centroid = new Float32Array(3);
31 |
32 | const uv_a = new Float32Array(2);
33 | const uv_b = new Float32Array(2);
34 | const uv_c = new Float32Array(2);
35 |
36 | for (let i = 0, j = 0; i < vertex_buffer.length; i += 9, j += 6) {
37 | set3(a, vertex_buffer[i + 0], vertex_buffer[i + 1], vertex_buffer[i + 2]);
38 | set3(b, vertex_buffer[i + 3], vertex_buffer[i + 4], vertex_buffer[i + 5]);
39 | set3(c, vertex_buffer[i + 6], vertex_buffer[i + 7], vertex_buffer[i + 8]);
40 |
41 | set2(uv_a, uv_buffer[j + 0], uv_buffer[j + 1]);
42 | set2(uv_b, uv_buffer[j + 2], uv_buffer[j + 3]);
43 | set2(uv_c, uv_buffer[j + 4], uv_buffer[j + 5]);
44 |
45 | centroid[0] = (a[0] + b[0] + c[0]) / 3;
46 | centroid[1] = (a[1] + b[1] + c[1]) / 3;
47 | centroid[2] = (a[2] + b[2] + c[2]) / 3;
48 |
49 | const azi = azimuth(centroid);
50 |
51 | correct_uv(uv_buffer, uv_a, j + 0, a, azi);
52 | correct_uv(uv_buffer, uv_b, j + 2, b, azi);
53 | correct_uv(uv_buffer, uv_c, j + 4, c, azi);
54 | }
55 | }
56 |
57 | function correct_uv(uv_buffer, uv, stride, vector, azimuth) {
58 | if ((azimuth < 0) && (uv[0] === 1)) {
59 | uv_buffer[stride] = uv[0] - 1;
60 | }
61 |
62 | if ((vector[0] === 0) && (vector[2] === 0)) {
63 | uv_buffer[stride] = azimuth / 2 / Math.PI + 0.5;
64 | }
65 | }
66 |
67 | function correct_seam(uv_buffer) {
68 | // handle case when face straddles the seam
69 | for (var i = 0; i < uv_buffer.length; i += 6) {
70 | // uv data of a single face
71 | var x0 = uv_buffer[i + 0];
72 | var x1 = uv_buffer[i + 2];
73 | var x2 = uv_buffer[i + 4];
74 |
75 | var max = Math.max(x0, x1, x2);
76 | var min = Math.min(x0, x1, x2);
77 |
78 | // 0.9 is somewhat arbitrary
79 | if (max > 0.9 && min < 0.1) {
80 | if (x0 < 0.2) uv_buffer[i + 0] += 1;
81 | if (x1 < 0.2) uv_buffer[i + 2] += 1;
82 | if (x2 < 0.2) uv_buffer[i + 4] += 1;
83 | }
84 | }
85 | }
86 |
87 | // Angle around the Y axis, counter-clockwise when looking from above.
88 | function azimuth(vector) {
89 | return Math.atan2(vector[2], - vector[0]);
90 | }
91 |
92 | // Angle above the XZ plane.
93 | function inclination(vector) {
94 | return Math.atan2(-vector[1], Math.sqrt((vector[0] * vector[0]) + (vector[2] * vector[2])));
95 | }
96 |
97 | function compute_vertex_normals(position) {
98 | const cb = new Float32Array(3);
99 | const ab = new Float32Array(3);
100 |
101 | const normals = new Float32Array(position.length);
102 |
103 | for (let i = 0; i < position.length; i += 9 ) {
104 | const pa = position.subarray(i + 0, i + 3);
105 | const pb = position.subarray(i + 3, i + 6);
106 | const pc = position.subarray(i + 6, i + 9);
107 |
108 | set3(cb, pc[0] - pb[0], pc[1] - pb[1], pc[2] - pb[2]);
109 | set3(ab, pa[0] - pb[0], pa[1] - pb[1], pa[2] - pb[2]);
110 |
111 | // cb x ab
112 | const x = cb[1] * ab[2] - cb[2] * ab[1];
113 | const y = cb[2] * ab[0] - cb[0] * ab[2];
114 | const z = cb[0] * ab[1] - cb[1] * ab[0];
115 |
116 | normals[i + 0] = normals[i + 3] = normals[i + 6] = x;
117 | normals[i + 1] = normals[i + 4] = normals[i + 7] = y;
118 | normals[i + 2] = normals[i + 5] = normals[i + 8] = z;
119 | }
120 |
121 | return normals;
122 | }
123 |
124 | function create_vertex_buffer(vertices, indices, subdivisions) {
125 | const vertex_buffer = [];
126 |
127 | const a = new Float32Array(3);
128 | const b = new Float32Array(3);
129 | const c = new Float32Array(3);
130 |
131 | for (let i = 0; i < indices.length; i += 3) {
132 | // get the vertices of the face
133 | get_vertex_data(indices[i + 0], a);
134 | get_vertex_data(indices[i + 1], b);
135 | get_vertex_data(indices[i + 2], c);
136 |
137 | // perform subdivision
138 | subdivide_face(a, b, c, subdivisions);
139 | }
140 |
141 | function get_vertex_data(index, out) {
142 | const offset = index * 3;
143 |
144 | out[0] = vertices[offset + 0];
145 | out[1] = vertices[offset + 1];
146 | out[2] = vertices[offset + 2];
147 | }
148 |
149 | function push_vertex(vertex) {
150 | vertex_buffer.push(vertex[0], vertex[1], vertex[2]);
151 | }
152 |
153 | function subdivide_face(a, b, c, subdivisions) {
154 | const cols = Math.pow(2, subdivisions);
155 |
156 | // we use this multidimensional array as a data structure for creating the subdivision
157 | const v = [];
158 |
159 | // construct all of the vertices for this subdivision
160 | for (let i = 0; i <= cols; i++) {
161 | v[i] = [];
162 |
163 | const aj = lerp(a, c, i / cols);
164 | const bj = lerp(b, c, i / cols);
165 |
166 | const rows = cols - i;
167 |
168 | for (let j = 0; j <= rows; j++) {
169 | if (j === 0 && i === cols) {
170 | v[i][j] = aj;
171 | } else {
172 | v[i][j] = lerp(aj, bj, j / rows);
173 | }
174 | }
175 | }
176 |
177 | // construct all of the faces
178 | for (let i = 0; i < cols; i++) {
179 | for (let j = 0; j < 2 * (cols - i) - 1; j++) {
180 | const k = Math.floor(j / 2);
181 |
182 | if (j % 2 === 0) {
183 | push_vertex(v[i][k + 1]);
184 | push_vertex(v[i + 1][k]);
185 | push_vertex(v[i][k]);
186 | } else {
187 | push_vertex(v[i][k + 1]);
188 | push_vertex(v[i + 1][k + 1]);
189 | push_vertex(v[i + 1][k]);
190 | }
191 | }
192 | }
193 | }
194 |
195 | return new Float32Array(vertex_buffer);
196 | }
197 |
198 | export default memoize((vertices, indices, subdivisions = 0, shading = 'flat') => {
199 | var uv_buffer = [];
200 |
201 | // the subdivision creates the vertex buffer data
202 | const vertex_buffer = create_vertex_buffer(vertices, indices, subdivisions);
203 |
204 | for (let i = 0; i < vertex_buffer.length; i += 3) {
205 | const vertex = new Float32Array(vertex_buffer.buffer, i * 4, 3);
206 |
207 | // all vertices should lie on a conceptual sphere with a given radius
208 | normalize(vertex);
209 |
210 | var u = azimuth(vertex) / 2 / Math.PI + 0.5;
211 | var v = inclination(vertex) / Math.PI + 0.5;
212 | uv_buffer.push(u, 1 - v);
213 | }
214 |
215 | correct_uvs(vertex_buffer, uv_buffer);
216 | correct_seam(uv_buffer);
217 |
218 | const position_buffer = new Float32Array(vertex_buffer);
219 |
220 | return new Geometry({
221 | position: {
222 | data: position_buffer,
223 | size: 3
224 | },
225 |
226 | normal: {
227 | data: shading === 'smooth' ? position_buffer : compute_vertex_normals(position_buffer),
228 | size: 3
229 | },
230 |
231 | uv: {
232 | data: new Float32Array(uv_buffer),
233 | size: 2
234 | }
235 | });
236 | });
--------------------------------------------------------------------------------
/controls/OrbitControls.svelte:
--------------------------------------------------------------------------------
1 |
277 |
278 |
--------------------------------------------------------------------------------
/scene/Scene.svelte:
--------------------------------------------------------------------------------
1 |
47 |
48 |
411 |
412 |
421 |
422 |
423 |
424 |
425 | {#if gl}
426 |
427 | {/if}
428 |
--------------------------------------------------------------------------------
/internal/constants.mjs:
--------------------------------------------------------------------------------
1 | // https://www.khronos.org/registry/webgl/specs/1.0/
2 |
3 | /* ClearBufferMask */
4 | export const DEPTH_BUFFER_BIT = 0x00000100;
5 | export const STENCIL_BUFFER_BIT = 0x00000400;
6 | export const COLOR_BUFFER_BIT = 0x00004000;
7 |
8 | /* BeginMode */
9 | export const POINTS = 0x0000;
10 | export const LINES = 0x0001;
11 | export const LINE_LOOP = 0x0002;
12 | export const LINE_STRIP = 0x0003;
13 | export const TRIANGLES = 0x0004;
14 | export const TRIANGLE_STRIP = 0x0005;
15 | export const TRIANGLE_FAN = 0x0006;
16 |
17 | /* BlendingFactorDest */
18 | export const ZERO = 0;
19 | export const ONE = 1;
20 | export const SRC_COLOR = 0x0300;
21 | export const ONE_MINUS_SRC_COLOR = 0x0301;
22 | export const SRC_ALPHA = 0x0302;
23 | export const ONE_MINUS_SRC_ALPHA = 0x0303;
24 | export const DST_ALPHA = 0x0304;
25 | export const ONE_MINUS_DST_ALPHA = 0x0305;
26 |
27 | /* BlendingFactorSrc */
28 | export const DST_COLOR = 0x0306;
29 | export const ONE_MINUS_DST_COLOR = 0x0307;
30 | export const SRC_ALPHA_SATURATE = 0x0308;
31 |
32 | /* BlendEquationSeparate */
33 | export const FUNC_ADD = 0x8006;
34 | export const BLEND_EQUATION = 0x8009;
35 | export const BLEND_EQUATION_RGB = 0x8009; /* same as BLEND_EQUATION */
36 | export const BLEND_EQUATION_ALPHA = 0x883D;
37 |
38 | /* BlendSubtract */
39 | export const FUNC_SUBTRACT = 0x800A;
40 | export const FUNC_REVERSE_SUBTRACT = 0x800B;
41 |
42 | /* Separate Blend Functions */
43 | export const BLEND_DST_RGB = 0x80C8;
44 | export const BLEND_SRC_RGB = 0x80C9;
45 | export const BLEND_DST_ALPHA = 0x80CA;
46 | export const BLEND_SRC_ALPHA = 0x80CB;
47 | export const CONSTANT_COLOR = 0x8001;
48 | export const ONE_MINUS_CONSTANT_COLOR = 0x8002;
49 | export const CONSTANT_ALPHA = 0x8003;
50 | export const ONE_MINUS_CONSTANT_ALPHA = 0x8004;
51 | export const BLEND_COLOR = 0x8005;
52 |
53 | /* Buffer Objects */
54 | export const ARRAY_BUFFER = 0x8892;
55 | export const ELEMENT_ARRAY_BUFFER = 0x8893;
56 | export const ARRAY_BUFFER_BINDING = 0x8894;
57 | export const ELEMENT_ARRAY_BUFFER_BINDING = 0x8895;
58 |
59 | export const STREAM_DRAW = 0x88E0;
60 | export const STATIC_DRAW = 0x88E4;
61 | export const DYNAMIC_DRAW = 0x88E8;
62 |
63 | export const BUFFER_SIZE = 0x8764;
64 | export const BUFFER_USAGE = 0x8765;
65 |
66 | export const CURRENT_VERTEX_ATTRIB = 0x8626;
67 |
68 | /* CullFaceMode */
69 | export const FRONT = 0x0404;
70 | export const BACK = 0x0405;
71 | export const FRONT_AND_BACK = 0x0408;
72 |
73 | /* EnableCap */
74 | /* TEXTURE_2D */
75 | export const CULL_FACE = 0x0B44;
76 | export const BLEND = 0x0BE2;
77 | export const DITHER = 0x0BD0;
78 | export const STENCIL_TEST = 0x0B90;
79 | export const DEPTH_TEST = 0x0B71;
80 | export const SCISSOR_TEST = 0x0C11;
81 | export const POLYGON_OFFSET_FILL = 0x8037;
82 | export const SAMPLE_ALPHA_TO_COVERAGE = 0x809E;
83 | export const SAMPLE_COVERAGE = 0x80A0;
84 |
85 | /* ErrorCode */
86 | export const NO_ERROR = 0;
87 | export const INVALID_ENUM = 0x0500;
88 | export const INVALID_VALUE = 0x0501;
89 | export const INVALID_OPERATION = 0x0502;
90 | export const OUT_OF_MEMORY = 0x0505;
91 |
92 | /* FrontFaceDirection */
93 | export const CW = 0x0900;
94 | export const CCW = 0x0901;
95 |
96 | /* GetPName */
97 | export const LINE_WIDTH = 0x0B21;
98 | export const ALIASED_POINT_SIZE_RANGE = 0x846D;
99 | export const ALIASED_LINE_WIDTH_RANGE = 0x846E;
100 | export const CULL_FACE_MODE = 0x0B45;
101 | export const FRONT_FACE = 0x0B46;
102 | export const DEPTH_RANGE = 0x0B70;
103 | export const DEPTH_WRITEMASK = 0x0B72;
104 | export const DEPTH_CLEAR_VALUE = 0x0B73;
105 | export const DEPTH_FUNC = 0x0B74;
106 | export const STENCIL_CLEAR_VALUE = 0x0B91;
107 | export const STENCIL_FUNC = 0x0B92;
108 | export const STENCIL_FAIL = 0x0B94;
109 | export const STENCIL_PASS_DEPTH_FAIL = 0x0B95;
110 | export const STENCIL_PASS_DEPTH_PASS = 0x0B96;
111 | export const STENCIL_REF = 0x0B97;
112 | export const STENCIL_VALUE_MASK = 0x0B93;
113 | export const STENCIL_WRITEMASK = 0x0B98;
114 | export const STENCIL_BACK_FUNC = 0x8800;
115 | export const STENCIL_BACK_FAIL = 0x8801;
116 | export const STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802;
117 | export const STENCIL_BACK_PASS_DEPTH_PASS = 0x8803;
118 | export const STENCIL_BACK_REF = 0x8CA3;
119 | export const STENCIL_BACK_VALUE_MASK = 0x8CA4;
120 | export const STENCIL_BACK_WRITEMASK = 0x8CA5;
121 | export const VIEWPORT = 0x0BA2;
122 | export const SCISSOR_BOX = 0x0C10;
123 | /* SCISSOR_TEST */
124 | export const COLOR_CLEAR_VALUE = 0x0C22;
125 | export const COLOR_WRITEMASK = 0x0C23;
126 | export const UNPACK_ALIGNMENT = 0x0CF5;
127 | export const PACK_ALIGNMENT = 0x0D05;
128 | export const MAX_TEXTURE_SIZE = 0x0D33;
129 | export const MAX_VIEWPORT_DIMS = 0x0D3A;
130 | export const SUBPIXEL_BITS = 0x0D50;
131 | export const RED_BITS = 0x0D52;
132 | export const GREEN_BITS = 0x0D53;
133 | export const BLUE_BITS = 0x0D54;
134 | export const ALPHA_BITS = 0x0D55;
135 | export const DEPTH_BITS = 0x0D56;
136 | export const STENCIL_BITS = 0x0D57;
137 | export const POLYGON_OFFSET_UNITS = 0x2A00;
138 | /* POLYGON_OFFSET_FILL */
139 | export const POLYGON_OFFSET_FACTOR = 0x8038;
140 | export const TEXTURE_BINDING_2D = 0x8069;
141 | export const SAMPLE_BUFFERS = 0x80A8;
142 | export const SAMPLES = 0x80A9;
143 | export const SAMPLE_COVERAGE_VALUE = 0x80AA;
144 | export const SAMPLE_COVERAGE_INVERT = 0x80AB;
145 |
146 | export const COMPRESSED_TEXTURE_FORMATS = 0x86A3;
147 |
148 | /* HintMode */
149 | export const DONT_CARE = 0x1100;
150 | export const FASTEST = 0x1101;
151 | export const NICEST = 0x1102;
152 |
153 | /* HintTarget */
154 | export const GENERATE_MIPMAP_HINT = 0x8192;
155 |
156 | /* DataType */
157 | export const BYTE = 0x1400;
158 | export const UNSIGNED_BYTE = 0x1401;
159 | export const SHORT = 0x1402;
160 | export const UNSIGNED_SHORT = 0x1403;
161 | export const INT = 0x1404;
162 | export const UNSIGNED_INT = 0x1405;
163 | export const FLOAT = 0x1406;
164 |
165 | /* PixelFormat */
166 | export const DEPTH_COMPONENT = 0x1902;
167 | export const ALPHA = 0x1906;
168 | export const RGB = 0x1907;
169 | export const RGBA = 0x1908;
170 | export const LUMINANCE = 0x1909;
171 | export const LUMINANCE_ALPHA = 0x190A;
172 |
173 | /* PixelType */
174 | /* UNSIGNED_BYTE */
175 | export const UNSIGNED_SHORT_4_4_4_4 = 0x8033;
176 | export const UNSIGNED_SHORT_5_5_5_1 = 0x8034;
177 | export const UNSIGNED_SHORT_5_6_5 = 0x8363;
178 |
179 | /* Shaders */
180 | export const FRAGMENT_SHADER = 0x8B30;
181 | export const VERTEX_SHADER = 0x8B31;
182 | export const MAX_VERTEX_ATTRIBS = 0x8869;
183 | export const MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
184 | export const MAX_VARYING_VECTORS = 0x8DFC;
185 | export const MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D;
186 | export const MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C;
187 | export const MAX_TEXTURE_IMAGE_UNITS = 0x8872;
188 | export const MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD;
189 | export const SHADER_TYPE = 0x8B4F;
190 | export const DELETE_STATUS = 0x8B80;
191 | export const LINK_STATUS = 0x8B82;
192 | export const VALIDATE_STATUS = 0x8B83;
193 | export const ATTACHED_SHADERS = 0x8B85;
194 | export const ACTIVE_UNIFORMS = 0x8B86;
195 | export const ACTIVE_ATTRIBUTES = 0x8B89;
196 | export const SHADING_LANGUAGE_VERSION = 0x8B8C;
197 | export const CURRENT_PROGRAM = 0x8B8D;
198 |
199 | /* StencilFunction */
200 | export const NEVER = 0x0200;
201 | export const LESS = 0x0201;
202 | export const EQUAL = 0x0202;
203 | export const LEQUAL = 0x0203;
204 | export const GREATER = 0x0204;
205 | export const NOTEQUAL = 0x0205;
206 | export const GEQUAL = 0x0206;
207 | export const ALWAYS = 0x0207;
208 |
209 | /* StencilOp */
210 | export const KEEP = 0x1E00;
211 | export const REPLACE = 0x1E01;
212 | export const INCR = 0x1E02;
213 | export const DECR = 0x1E03;
214 | export const INVERT = 0x150A;
215 | export const INCR_WRAP = 0x8507;
216 | export const DECR_WRAP = 0x8508;
217 |
218 | /* StringName */
219 | export const VENDOR = 0x1F00;
220 | export const RENDERER = 0x1F01;
221 | export const VERSION = 0x1F02;
222 |
223 | /* TextureMagFilter */
224 | export const NEAREST = 0x2600;
225 | export const LINEAR = 0x2601;
226 |
227 | /* TextureMinFilter */
228 | export const NEAREST_MIPMAP_NEAREST = 0x2700;
229 | export const LINEAR_MIPMAP_NEAREST = 0x2701;
230 | export const NEAREST_MIPMAP_LINEAR = 0x2702;
231 | export const LINEAR_MIPMAP_LINEAR = 0x2703;
232 |
233 | /* TextureParameterName */
234 | export const TEXTURE_MAG_FILTER = 0x2800;
235 | export const TEXTURE_MIN_FILTER = 0x2801;
236 | export const TEXTURE_WRAP_S = 0x2802;
237 | export const TEXTURE_WRAP_T = 0x2803;
238 |
239 | /* TextureTarget */
240 | export const TEXTURE_2D = 0x0DE1;
241 | export const TEXTURE = 0x1702;
242 |
243 | export const TEXTURE_CUBE_MAP = 0x8513;
244 | export const TEXTURE_BINDING_CUBE_MAP = 0x8514;
245 | export const TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
246 | export const TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
247 | export const TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
248 | export const TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
249 | export const TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
250 | export const TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
251 | export const MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C;
252 |
253 | /* TextureUnit */
254 | export const TEXTURE0 = 0x84C0;
255 | export const TEXTURE1 = 0x84C1;
256 | export const TEXTURE2 = 0x84C2;
257 | export const TEXTURE3 = 0x84C3;
258 | export const TEXTURE4 = 0x84C4;
259 | export const TEXTURE5 = 0x84C5;
260 | export const TEXTURE6 = 0x84C6;
261 | export const TEXTURE7 = 0x84C7;
262 | export const TEXTURE8 = 0x84C8;
263 | export const TEXTURE9 = 0x84C9;
264 | export const TEXTURE10 = 0x84CA;
265 | export const TEXTURE11 = 0x84CB;
266 | export const TEXTURE12 = 0x84CC;
267 | export const TEXTURE13 = 0x84CD;
268 | export const TEXTURE14 = 0x84CE;
269 | export const TEXTURE15 = 0x84CF;
270 | export const TEXTURE16 = 0x84D0;
271 | export const TEXTURE17 = 0x84D1;
272 | export const TEXTURE18 = 0x84D2;
273 | export const TEXTURE19 = 0x84D3;
274 | export const TEXTURE20 = 0x84D4;
275 | export const TEXTURE21 = 0x84D5;
276 | export const TEXTURE22 = 0x84D6;
277 | export const TEXTURE23 = 0x84D7;
278 | export const TEXTURE24 = 0x84D8;
279 | export const TEXTURE25 = 0x84D9;
280 | export const TEXTURE26 = 0x84DA;
281 | export const TEXTURE27 = 0x84DB;
282 | export const TEXTURE28 = 0x84DC;
283 | export const TEXTURE29 = 0x84DD;
284 | export const TEXTURE30 = 0x84DE;
285 | export const TEXTURE31 = 0x84DF;
286 | export const ACTIVE_TEXTURE = 0x84E0;
287 |
288 | /* TextureWrapMode */
289 | export const REPEAT = 0x2901;
290 | export const CLAMP_TO_EDGE = 0x812F;
291 | export const MIRRORED_REPEAT = 0x8370;
292 |
293 | /* Uniform Types */
294 | export const FLOAT_VEC2 = 0x8B50;
295 | export const FLOAT_VEC3 = 0x8B51;
296 | export const FLOAT_VEC4 = 0x8B52;
297 | export const INT_VEC2 = 0x8B53;
298 | export const INT_VEC3 = 0x8B54;
299 | export const INT_VEC4 = 0x8B55;
300 | export const BOOL = 0x8B56;
301 | export const BOOL_VEC2 = 0x8B57;
302 | export const BOOL_VEC3 = 0x8B58;
303 | export const BOOL_VEC4 = 0x8B59;
304 | export const FLOAT_MAT2 = 0x8B5A;
305 | export const FLOAT_MAT3 = 0x8B5B;
306 | export const FLOAT_MAT4 = 0x8B5C;
307 | export const SAMPLER_2D = 0x8B5E;
308 | export const SAMPLER_CUBE = 0x8B60;
309 |
310 | /* Vertex Arrays */
311 | export const VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622;
312 | export const VERTEX_ATTRIB_ARRAY_SIZE = 0x8623;
313 | export const VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624;
314 | export const VERTEX_ATTRIB_ARRAY_TYPE = 0x8625;
315 | export const VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A;
316 | export const VERTEX_ATTRIB_ARRAY_POINTER = 0x8645;
317 | export const VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F;
318 |
319 | /* Read Format */
320 | export const IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A;
321 | export const IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B;
322 |
323 | /* Shader Source */
324 | export const COMPILE_STATUS = 0x8B81;
325 |
326 | /* Shader Precision-Specified Types */
327 | export const LOW_FLOAT = 0x8DF0;
328 | export const MEDIUM_FLOAT = 0x8DF1;
329 | export const HIGH_FLOAT = 0x8DF2;
330 | export const LOW_INT = 0x8DF3;
331 | export const MEDIUM_INT = 0x8DF4;
332 | export const HIGH_INT = 0x8DF5;
333 |
334 | /* Framebuffer Object. */
335 | export const FRAMEBUFFER = 0x8D40;
336 | export const RENDERBUFFER = 0x8D41;
337 |
338 | export const RGBA4 = 0x8056;
339 | export const RGB5_A1 = 0x8057;
340 | export const RGB565 = 0x8D62;
341 | export const DEPTH_COMPONENT16 = 0x81A5;
342 | export const STENCIL_INDEX = 0x1901;
343 | export const STENCIL_INDEX8 = 0x8D48;
344 | export const DEPTH_STENCIL = 0x84F9;
345 |
346 | export const RENDERBUFFER_WIDTH = 0x8D42;
347 | export const RENDERBUFFER_HEIGHT = 0x8D43;
348 | export const RENDERBUFFER_INTERNAL_FORMAT = 0x8D44;
349 | export const RENDERBUFFER_RED_SIZE = 0x8D50;
350 | export const RENDERBUFFER_GREEN_SIZE = 0x8D51;
351 | export const RENDERBUFFER_BLUE_SIZE = 0x8D52;
352 | export const RENDERBUFFER_ALPHA_SIZE = 0x8D53;
353 | export const RENDERBUFFER_DEPTH_SIZE = 0x8D54;
354 | export const RENDERBUFFER_STENCIL_SIZE = 0x8D55;
355 |
356 | export const FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0;
357 | export const FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1;
358 | export const FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2;
359 | export const FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3;
360 |
361 | export const COLOR_ATTACHMENT0 = 0x8CE0;
362 | export const DEPTH_ATTACHMENT = 0x8D00;
363 | export const STENCIL_ATTACHMENT = 0x8D20;
364 | export const DEPTH_STENCIL_ATTACHMENT = 0x821A;
365 |
366 | export const NONE = 0;
367 |
368 | export const FRAMEBUFFER_COMPLETE = 0x8CD5;
369 | export const FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6;
370 | export const FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7;
371 | export const FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9;
372 | export const FRAMEBUFFER_UNSUPPORTED = 0x8CDD;
373 |
374 | export const FRAMEBUFFER_BINDING = 0x8CA6;
375 | export const RENDERBUFFER_BINDING = 0x8CA7;
376 | export const MAX_RENDERBUFFER_SIZE = 0x84E8;
377 |
378 | export const INVALID_FRAMEBUFFER_OPERATION = 0x0506;
379 |
380 | /* WebGL-specific enums */
381 | export const UNPACK_FLIP_Y_WEBGL = 0x9240;
382 | export const UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
383 | export const CONTEXT_LOST_WEBGL = 0x9242;
384 | export const UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
385 | export const BROWSER_DEFAULT_WEBGL = 0x9244;
--------------------------------------------------------------------------------