├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── examples
├── cube.html
├── cube.ts
├── math.ts
├── offscreen.html
├── offscreen.ts
├── triangle.html
└── triangle.ts
├── src
├── altai.ts
├── enums.ts
├── options.ts
└── types.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | *.js
4 | *.map
5 |
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Chrome against localhost, with sourcemaps",
6 | "type": "chrome",
7 | "request": "launch",
8 | "url": "http://localhost:8080",
9 | "sourceMaps": true,
10 | "webRoot": "${workspaceRoot}"
11 | },
12 | {
13 | "name": "Attach to Chrome, with sourcemaps",
14 | "type": "chrome",
15 | "request": "attach",
16 | "port": 9222,
17 | "sourceMaps": true,
18 | "webRoot": "${workspaceRoot}"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "**/*.js": true,
5 | "**/*.map": true
6 | }
7 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "command": "tsc",
6 | "args": [
7 | "-p",
8 | "."
9 | ],
10 | "problemMatcher": "$tsc",
11 | "tasks": [
12 | {
13 | "label": "tsc",
14 | "type": "shell",
15 | "command": "tsc",
16 | "args": [
17 | "-p",
18 | "."
19 | ],
20 | "problemMatcher": "$tsc",
21 | "group": {
22 | "_id": "build",
23 | "isDefault": false
24 | }
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Andre Weissflog
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Altai
2 |
3 | Work In Progress!
4 |
5 | A 'Modern 3D-API' wrapper for WebGL in TypeScript, or:
6 |
7 | Like the Oryol Gfx module, but in TS.
8 |
--------------------------------------------------------------------------------
/examples/cube.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/cube.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | function cube() {
5 | const WIDTH = 600;
6 | const HEIGHT = 400;
7 |
8 | let gfx = new altai.Gfx({
9 | UseWebGL2: true,
10 | Width: WIDTH,
11 | Height: HEIGHT,
12 | Depth: true,
13 | Canvas: "canvas",
14 | AntiAlias: true
15 | });
16 |
17 | let pass = gfx.makePass({
18 | ColorAttachments: [ { ClearColor: [ 0.3, 0.4, 0.5, 1.0 ] } ]
19 | })
20 |
21 | let vertexBuffer = gfx.makeBuffer({
22 | Type: altai.BufferType.VertexBuffer,
23 | Data: new Float32Array([
24 | -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
25 | 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
26 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
27 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
28 |
29 | -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
30 | 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
31 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
32 | -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
33 |
34 | -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
35 | -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
36 | -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
37 | -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
38 |
39 | 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
40 | 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
41 | 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
42 | 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
43 |
44 | -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
45 | -1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
46 | 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
47 | 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
48 |
49 | -1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0,
50 | -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
51 | 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
52 | 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0
53 | ])
54 | })
55 | let indexBuffer = gfx.makeBuffer({
56 | Type: altai.BufferType.IndexBuffer,
57 | Data: new Uint16Array([
58 | 0, 1, 2, 0, 2, 3,
59 | 4, 5, 6, 4, 6, 7,
60 | 8, 9, 10, 8, 10, 11,
61 | 12, 13, 14, 12, 14, 15,
62 | 16, 17, 18, 16, 18, 19,
63 | 20, 21, 22, 20, 22, 23
64 | ])
65 | })
66 |
67 | let shader = gfx.makeShader({
68 | VertexShader: `
69 | uniform mat4 mvp;
70 | attribute vec4 position;
71 | attribute vec4 color;
72 | varying lowp vec4 vColor;
73 | void main(void) {
74 | gl_Position = mvp * position;
75 | vColor = color;
76 | }`,
77 | FragmentShader: `
78 | varying lowp vec4 vColor;
79 | void main(void) {
80 | gl_FragColor = vColor;
81 | }`
82 | });
83 |
84 | let pipeline = gfx.makePipeline({
85 | Shader: shader,
86 | VertexLayouts: [{
87 | Components: [
88 | [ "position", altai.VertexFormat.Float3 ],
89 | [ "color", altai.VertexFormat.Float4 ],
90 | ]
91 | }],
92 | IndexFormat: altai.IndexFormat.UInt16,
93 | DepthCmpFunc: altai.CompareFunc.LessEqual,
94 | DepthWriteEnabled: true,
95 | CullFaceEnabled: false
96 | });
97 |
98 | let drawState = gfx.makeDrawState({
99 | Pipeline: pipeline,
100 | IndexBuffer: indexBuffer,
101 | VertexBuffers: [ vertexBuffer ],
102 | });
103 |
104 | let angleX = 0.0;
105 | let angleY = 0.0;
106 | const proj = mat4.perspective_fov(deg2rad(45.0), WIDTH, HEIGHT, 0.01, 100.0);
107 | const view = mat4.identity();
108 |
109 | function draw() {
110 | angleX += 0.01;
111 | angleY += 0.02;
112 | let model = mat4.translate(mat4.identity(), 0.0, 0.0, -6.0);
113 | model = mat4.rotate(model, angleX, 1.0, 0.0, 0.0);
114 | model = mat4.rotate(model, angleY, 0.0, 1.0, 0.0);
115 | let mvp = mat4.mul(proj, mat4.mul(view, model));
116 | gfx.beginPass(pass);
117 | gfx.applyDrawState(drawState);
118 | gfx.applyUniforms({
119 | "mvp": mvp.values(),
120 | })
121 | gfx.draw(0, 36);
122 | gfx.endPass();
123 | gfx.commitFrame(draw);
124 | }
125 | draw();
126 | }
--------------------------------------------------------------------------------
/examples/math.ts:
--------------------------------------------------------------------------------
1 | function deg2rad(deg: number) {
2 | return (deg * Math.PI)/180.0;
3 | }
4 |
5 | function rad2deg(rad: number) {
6 | return (rad * 180.0) / Math.PI;
7 | }
8 |
9 | class vec4 {
10 | public x: number;
11 | public y: number;
12 | public z: number;
13 | public w: number;
14 |
15 | public constructor(x: number, y: number, z: number, w: number) {
16 | this.x=x; this.y=y; this.z=z; this.w=w;
17 | }
18 |
19 | public copy(): vec4 {
20 | return new vec4(this.x, this.y, this.z, this.w);
21 | }
22 |
23 | public values(): number[] {
24 | return [this.x, this.y, this.z, this.w];
25 | }
26 |
27 | public len(): number {
28 | return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);
29 | }
30 |
31 | public static normalize(v: vec4): vec4 {
32 | const l = v.len();
33 | if (l > 0.0) {
34 | return new vec4(v.x/l, v.y/l, v.z/l, v.w/l);
35 | }
36 | else {
37 | return new vec4(0.0, 0.0, 0.0, 0.0);
38 | }
39 | }
40 |
41 | public static add(v0: vec4, v1: vec4): vec4 {
42 | return new vec4(v0.x+v1.x, v0.y+v1.y, v0.z+v1.z, v0.w+v1.w);
43 | }
44 |
45 | public static add3(v0: vec4, v1: vec4, v2: vec4): vec4 {
46 | return new vec4(v0.x+v1.x+v2.x, v0.y+v1.y+v2.y, v0.z+v1.z+v2.z, v0.w+v1.w+v2.w);
47 | }
48 |
49 | public static add4(v0: vec4, v1: vec4, v2: vec4, v3: vec4): vec4 {
50 | return new vec4(v0.x+v1.x+v2.x+v3.x, v0.y+v1.y+v2.y+v3.y, v0.z+v1.z+v2.z+v3.z, v0.w+v1.w+v2.w+v3.w);
51 | }
52 | public static muls(v0: vec4, s: number): vec4 {
53 | return new vec4(v0.x*s, v0.y*s, v0.z*s, v0.w*s);
54 | }
55 | }
56 |
57 | class mat4 {
58 | public v: vec4[];
59 |
60 | /** construct identity matrix */
61 | public constructor() {
62 | this.v = [
63 | new vec4(1.0, 0.0, 0.0, 0.0),
64 | new vec4(0.0, 1.0, 0.0, 0.0),
65 | new vec4(0.0, 0.0, 1.0, 0.0),
66 | new vec4(0.0, 0.0, 0.0, 1.0)
67 | ];
68 | }
69 |
70 | /** return content as array of numbers */
71 | public values(): number[] {
72 | return [
73 | this.v[0].x, this.v[0].y, this.v[0].z, this.v[0].w,
74 | this.v[1].x, this.v[1].y, this.v[1].z, this.v[1].w,
75 | this.v[2].x, this.v[2].y, this.v[2].z, this.v[2].w,
76 | this.v[3].x, this.v[3].y, this.v[3].z, this.v[3].w,
77 | ];
78 | }
79 |
80 | /** return a new identity matrix */
81 | public static identity(): mat4 {
82 | return new mat4();
83 | }
84 |
85 | /** create a new matrix by translating an existing one */
86 | public static translate(m: mat4, x: number, y: number, z: number): mat4 {
87 | let r = new mat4();
88 | let t0 = vec4.muls(m.v[0], x);
89 | let t1 = vec4.muls(m.v[1], y);
90 | let t2 = vec4.muls(m.v[2], z);
91 | let t3 = m.v[3];
92 | r.v[3] = vec4.add(t0, vec4.add(t1, vec4.add(t2, t3)));
93 | return r;
94 | }
95 |
96 | /** create a new matrix by rotating an existing one around an axis */
97 | public static rotate(m: mat4, a: number, x: number, y: number, z: number): mat4 {
98 | const c = Math.cos(a);
99 | const s = Math.sin(a);
100 | const axis = vec4.normalize(new vec4(x, y, z, 0.0));
101 | const t = vec4.muls(axis, (1.0 - c));
102 |
103 | let rot = new mat4();
104 | rot.v[0].x = c + t.x*axis.x;
105 | rot.v[0].y = t.x*axis.y + s*axis.z;
106 | rot.v[0].z = t.x*axis.z - s*axis.y;
107 |
108 | rot.v[1].x = t.y*axis.x - s*axis.z;
109 | rot.v[1].y = c + t.y*axis.y;
110 | rot.v[1].z = t.y*axis.z + s*axis.x;
111 |
112 | rot.v[2].x = t.z*axis.x + s*axis.y;
113 | rot.v[2].y = t.z*axis.y - s*axis.x;
114 | rot.v[2].z = c + t.z*axis.z;
115 |
116 | let res = new mat4();
117 | res.v[0] = vec4.add3(vec4.muls(m.v[0],rot.v[0].x), vec4.muls(m.v[1], rot.v[0].y), vec4.muls(m.v[2], rot.v[0].z));
118 | res.v[1] = vec4.add3(vec4.muls(m.v[0],rot.v[1].x), vec4.muls(m.v[1], rot.v[1].y), vec4.muls(m.v[2], rot.v[1].z));
119 | res.v[2] = vec4.add3(vec4.muls(m.v[0],rot.v[2].x), vec4.muls(m.v[1], rot.v[2].y), vec4.muls(m.v[2], rot.v[2].z));
120 | res.v[3] = m.v[3];
121 | return res;
122 | }
123 |
124 |
125 | /** matrix multiplication */
126 | public static mul(m1: mat4, m2: mat4): mat4 {
127 | const [a0,a1,a2,a3] = m1.v;
128 | const [b0,b1,b2,b3] = m2.v;
129 | let res = new mat4();
130 | res.v[0] = vec4.add4(vec4.muls(a0,b0.x), vec4.muls(a1,b0.y), vec4.muls(a2,b0.z), vec4.muls(a3,b0.w));
131 | res.v[1] = vec4.add4(vec4.muls(a0,b1.x), vec4.muls(a1,b1.y), vec4.muls(a2,b1.z), vec4.muls(a3,b1.w));
132 | res.v[2] = vec4.add4(vec4.muls(a0,b2.x), vec4.muls(a1,b2.y), vec4.muls(a2,b2.z), vec4.muls(a3,b2.w));
133 | res.v[3] = vec4.add4(vec4.muls(a0,b3.x), vec4.muls(a1,b3.y), vec4.muls(a2,b3.z), vec4.muls(a3,b3.w));
134 | return res;
135 | }
136 |
137 | /**
138 | * Return new perspective matrix
139 | *
140 | * @param {number} fov - field-of-view angle in radians
141 | * @param {number} width - width (to compute aspect ratio)
142 | * @param {number} height - height (to compute aspect ratio)
143 | * @param {number} znear - near-plane distance
144 | * @param {number} zfar - far-plane distance
145 | */
146 | public static perspective_fov(fov: number, width: number, height: number, znear: number, zfar: number): mat4 {
147 | let m = new mat4();
148 | const rad = fov;
149 | const h = Math.cos(rad * 0.5) / Math.sin(rad * 0.5);
150 | const w = h * height / width;
151 | m.v[0].x = w;
152 | m.v[1].y = h;
153 | m.v[2].z = -(zfar + znear) / (zfar - znear);
154 | m.v[2].w = -1.0;
155 | m.v[3].z = -(2.0 * zfar * znear) / (zfar - znear);
156 | m.v[3].w = 0.0;
157 | return m;
158 | }
159 | }
--------------------------------------------------------------------------------
/examples/offscreen.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/offscreen.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | function offscreen() {
5 | // setup the GL canvas
6 | const WIDTH = 600;
7 | const HEIGHT = 400;
8 | let gfx = new altai.Gfx({
9 | UseWebGL2: true,
10 | Width: WIDTH,
11 | Height: HEIGHT,
12 | Depth: true,
13 | Canvas: "canvas",
14 | });
15 |
16 | // create texture and render-pass for offscreen-rendering
17 | let offTex = gfx.makeTexture({
18 | Type: altai.TextureType.Texture2D,
19 | Width: 256,
20 | Height: 256,
21 | ColorFormat: altai.PixelFormat.RGBA8,
22 | DepthFormat: altai.DepthStencilFormat.DEPTH,
23 | WrapU: altai.Wrap.Repeat,
24 | WrapV: altai.Wrap.Repeat,
25 | MinFilter: altai.Filter.Linear,
26 | MagFilter: altai.Filter.Linear,
27 | });
28 | let offPass = gfx.makePass({
29 | ColorAttachments: [{ Texture: offTex, ClearColor: [0.25, 0.25, 0.25, 1.0] }],
30 | DepthAttachment: { Texture: offTex }
31 | });
32 |
33 | // a vertex and index buffer for a cube
34 | let cubeVB = gfx.makeBuffer({
35 | Type: altai.BufferType.VertexBuffer,
36 | Data: new Float32Array([
37 | -1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
38 | 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
39 | 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
40 | -1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
41 |
42 | -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
43 | 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
44 | 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
45 | -1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
46 |
47 | -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
48 | -1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
49 | -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
50 | -1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
51 |
52 | 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
53 | 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
54 | 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
55 | 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
56 |
57 | -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
58 | -1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
59 | 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, 1.0,
60 | 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0,
61 |
62 | -1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0,
63 | -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
64 | 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0,
65 | 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0
66 | ])
67 | })
68 | let cubeIB = gfx.makeBuffer({
69 | Type: altai.BufferType.IndexBuffer,
70 | Data: new Uint16Array([
71 | 0, 1, 2, 0, 2, 3,
72 | 4, 5, 6, 4, 6, 7,
73 | 8, 9, 10, 8, 10, 11,
74 | 12, 13, 14, 12, 14, 15,
75 | 16, 17, 18, 16, 18, 19,
76 | 20, 21, 22, 20, 22, 23
77 | ])
78 | })
79 |
80 | // a shader to render a vertex-colored cube
81 | let cubeShader = gfx.makeShader({
82 | VertexShader: `
83 | uniform mat4 mvp;
84 | attribute vec4 position;
85 | attribute vec4 color;
86 | varying lowp vec4 vColor;
87 | void main(void) {
88 | gl_Position = mvp * position;
89 | vColor = color;
90 | }`,
91 | FragmentShader: `
92 | varying lowp vec4 vColor;
93 | void main(void) {
94 | gl_FragColor = vColor;
95 | }`
96 | });
97 |
98 | // a pipeline object to render the cube
99 | let cubePipeline = gfx.makePipeline({
100 | Shader: cubeShader,
101 | VertexLayouts: [{
102 | Components: [
103 | [ "position", altai.VertexFormat.Float3 ],
104 | [ "color", altai.VertexFormat.Float4 ],
105 | ]
106 | }],
107 | IndexFormat: altai.IndexFormat.UInt16,
108 | DepthCmpFunc: altai.CompareFunc.LessEqual,
109 | DepthWriteEnabled: true,
110 | CullFaceEnabled: false
111 | });
112 |
113 | // draw state with resource bindings for the cube
114 | let cubeDrawState = gfx.makeDrawState({
115 | Pipeline: cubePipeline,
116 | IndexBuffer: cubeIB,
117 | VertexBuffers: [ cubeVB ],
118 | });
119 |
120 | // default render pass
121 | let pass = gfx.makePass({
122 | ColorAttachments: [{ ClearColor: [0.75, 0.75, 0.75, 1.0] }]
123 | });
124 |
125 | // a vertex buffer for a textured quad
126 | let quadVB = gfx.makeBuffer({
127 | Type: altai.BufferType.VertexBuffer,
128 | Data: new Float32Array([
129 | // positions texcoords
130 | -0.5, -0.5, 0.5, 0.0, 0.0, // first triangle
131 | +0.5, -0.5, 0.5, 2.0, 0.0,
132 | +0.5, +0.5, 0.5, 2.0, 2.0,
133 |
134 | -0.5, -0.5, 0.5, 0.0, 0.0, // second triangle
135 | +0.5, +0.5, 0.5, 2.0, 2.0,
136 | -0.5, +0.5, 0.5, 0.0, 2.0,
137 | ]),
138 | });
139 |
140 | // shader to render a textured quad into the default framebuffer
141 | let shader = gfx.makeShader({
142 | VertexShader: `
143 | attribute vec4 position;
144 | attribute vec2 texcoord0;
145 | varying vec2 uv;
146 | void main(void) {
147 | gl_Position = position;
148 | uv = texcoord0;
149 | }`,
150 | FragmentShader: `
151 | precision mediump float;
152 | uniform sampler2D texture;
153 | varying vec2 uv;
154 | void main(void) {
155 | gl_FragColor = texture2D(texture, uv);
156 | }`
157 | });
158 |
159 | // the pipeline-state object for the textured quad
160 | let pipeline = gfx.makePipeline({
161 | VertexLayouts: [{
162 | Components: [
163 | [ "position", altai.VertexFormat.Float3 ],
164 | [ "texcoord0", altai.VertexFormat.Float2 ],
165 | ]
166 | }],
167 | Shader: shader,
168 | DepthCmpFunc: altai.CompareFunc.Always,
169 | DepthWriteEnabled: false,
170 | CullFaceEnabled: false,
171 | });
172 |
173 | // DrawState object for the texture quad
174 | let drawState = gfx.makeDrawState({
175 | Pipeline: pipeline,
176 | VertexBuffers: [ quadVB ],
177 | Textures: {
178 | "texture": offTex,
179 | }
180 | });
181 |
182 | // rotation angles and projection matrix
183 | let angleX = 0.0;
184 | let angleY = 0.0;
185 | const proj = mat4.perspective_fov(deg2rad(45.0), WIDTH, HEIGHT, 0.01, 100.0);
186 | function draw() {
187 |
188 | // model-view-projection matrix for rotating cube
189 | angleX += 0.01;
190 | angleY += 0.02;
191 | let model = mat4.translate(mat4.identity(), 0.0, 0.0, -6.0);
192 | model = mat4.rotate(model, angleX, 1.0, 0.0, 0.0);
193 | model = mat4.rotate(model, angleY, 0.0, 1.0, 0.0);
194 | let mvp = mat4.mul(proj, model);
195 |
196 | // render rotating cube to offscreen render target
197 | gfx.beginPass(offPass);
198 | gfx.applyDrawState(cubeDrawState);
199 | gfx.applyUniforms({
200 | "mvp": mvp.values()
201 | });
202 | gfx.draw(0, 36);
203 | gfx.endPass();
204 |
205 | // render to default framebuffer
206 | gfx.beginPass(pass);
207 | gfx.applyDrawState(drawState);
208 | gfx.draw(0, 6);
209 | gfx.endPass();
210 | gfx.commitFrame(draw);
211 | }
212 | draw();
213 | }
214 |
--------------------------------------------------------------------------------
/examples/triangle.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/triangle.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | function triangle() {
4 |
5 | // create the Altai Gfx object which initializes
6 | // a WebGL context and the default render state
7 | let gfx = new altai.Gfx({ UseWebGL2: true, Width: 400, Height: 300, Canvas: "triangle-canvas" });
8 |
9 | // create a render-pass
10 | let pass = gfx.makePass({
11 | ColorAttachments: [ { ClearColor: [0.5, 0.5, 0.5, 1.0] } ]
12 | });
13 |
14 | // a vertex buffer with positions and colors for a triangle
15 | let vertexBuffer = gfx.makeBuffer({
16 | Type: altai.BufferType.VertexBuffer,
17 | Data: new Float32Array([
18 | // positions colors
19 | 0.0, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0,
20 | 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 1.0,
21 | -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0,
22 | ]),
23 | });
24 |
25 | // create a vertex/fragment shader pair
26 | let shader = gfx.makeShader({
27 | VertexShader: `
28 | attribute vec4 position;
29 | attribute vec4 color;
30 | varying lowp vec4 vColor;
31 | void main(void) {
32 | gl_Position = position;
33 | vColor = color;
34 | }`,
35 | FragmentShader: `
36 | varying lowp vec4 vColor;
37 | void main(void) {
38 | gl_FragColor = vColor;
39 | }`
40 | });
41 |
42 | // the pipeline-state object
43 | let pipeline = gfx.makePipeline({
44 | VertexLayouts: [{
45 | Components: [
46 | [ "position", altai.VertexFormat.Float3 ],
47 | [ "color", altai.VertexFormat.Float4 ],
48 | ]
49 | }],
50 | Shader: shader,
51 | DepthCmpFunc: altai.CompareFunc.Always,
52 | DepthWriteEnabled: false,
53 | CullFaceEnabled: false,
54 | });
55 |
56 | // putting it all together in a draw-state object
57 | let drawState = gfx.makeDrawState({
58 | Pipeline: pipeline,
59 | VertexBuffers: [ vertexBuffer ],
60 | });
61 |
62 | function draw() {
63 | gfx.beginPass(pass);
64 | gfx.applyDrawState(drawState);
65 | gfx.draw(0, 3);
66 | gfx.endPass();
67 | gfx.commitFrame(draw);
68 | }
69 | draw();
70 | }
71 |
--------------------------------------------------------------------------------
/src/altai.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | 'use strict'
4 |
5 | namespace altai {
6 |
7 | const vertexFormatMap = [
8 | [ 1, WebGLRenderingContext.FLOAT, false ],
9 | [ 2, WebGLRenderingContext.FLOAT, false ],
10 | [ 3, WebGLRenderingContext.FLOAT, false ],
11 | [ 4, WebGLRenderingContext.FLOAT, false ],
12 | [ 4, WebGLRenderingContext.BYTE, false ],
13 | [ 4, WebGLRenderingContext.BYTE, true ],
14 | [ 4, WebGLRenderingContext.UNSIGNED_BYTE, false ],
15 | [ 4, WebGLRenderingContext.UNSIGNED_BYTE, true ],
16 | [ 2, WebGLRenderingContext.SHORT, false ],
17 | [ 2, WebGLRenderingContext.SHORT, true ],
18 | [ 4, WebGLRenderingContext.SHORT, false ],
19 | [ 4, WebGLRenderingContext.SHORT, true ]
20 | ];
21 |
22 | const cubeFaceMap = [
23 | WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_X,
24 | WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_X,
25 | WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Y,
26 | WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Y,
27 | WebGLRenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Z,
28 | WebGLRenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Z,
29 | ];
30 |
31 | /**
32 | * Altai's main interface for resource creation and rendering.
33 | */
34 | export class Gfx {
35 | private gl: WebGL2RenderingContext | WebGLRenderingContext;
36 | private webgl2: boolean = false;
37 | private cache: PipelineState;
38 | private curProgram: WebGLProgram;
39 | private curIndexFormat: GLenum;
40 | private curIndexSize: number = 0;
41 | private curPrimType: PrimitiveType;
42 |
43 | /**
44 | * Create the Altai interface object.
45 | *
46 | * @param {GfxOptions} options - WebGL context and HTML canvas intialization options
47 | */
48 | constructor(options: GfxOptions) {
49 | const glContextAttrs = {
50 | alpha: some(options.Alpha, true),
51 | depth: some(options.Depth, true),
52 | stencil: some(options.Stencil, false),
53 | antialias: some(options.AntiAlias, true),
54 | premultipliedAlpha: some(options.PreMultipliedAlpha, true),
55 | preserveDrawingBuffer: some(options.PreserveDrawingBuffer, false),
56 | preferLowPowerToHighPerformance: some(options.PreferLowPowerToHighPerformance, false),
57 | failIfMajorPerformanceCaveat: some(options.FailIfMajorPerformanceCaveat, false)
58 | };
59 | const canvas = document.getElementById(some(options.Canvas, "canvas")) as HTMLCanvasElement;
60 | if (options.Width != null) {
61 | canvas.width = options.Width;
62 | }
63 | if (options.Height != null) {
64 | canvas.height = options.Height;
65 | }
66 | if (some(options.UseWebGL2, false)) {
67 | this.gl = (canvas.getContext("webgl2", glContextAttrs) ||
68 | canvas.getContext("webgl2-experimental", glContextAttrs)) as WebGL2RenderingContext;
69 | if (this.gl != null) {
70 | this.webgl2 = true;
71 | console.log("altai: using webgl2");
72 | }
73 | }
74 | if (this.gl == null) {
75 | this.gl = (canvas.getContext("webgl", glContextAttrs) ||
76 | canvas.getContext("experimental-webgl", glContextAttrs)) as WebGLRenderingContext;
77 | console.log("altai: using webgl1");
78 | }
79 | this.gl.viewport(0, 0, canvas.width, canvas.height);
80 | this.gl.enable(this.gl.DEPTH_TEST);
81 |
82 | // FIXME: HighDPI handling
83 |
84 | // apply default state
85 | this.cache = new PipelineState({ VertexLayouts: [], Shader: null });
86 | this.applyState(this.cache, true);
87 | }
88 |
89 | /**
90 | * Create a new Pass object.
91 | *
92 | * @param {PassOptions} options - Pass creation options
93 | */
94 | public makePass(options: PassOptions): Pass {
95 | // special handling for default pass
96 | if (null == options.ColorAttachments[0].Texture) {
97 | return new Pass(options, null, null);
98 | }
99 |
100 | // an offscreen pass, need to create a framebuffer with color- and depth attachments
101 | const gl = this.gl;
102 | const gl2 = this.gl as WebGL2RenderingContext;
103 | const isMSAA = options.ColorAttachments[0].Texture.sampleCount > 1;
104 | const glFb = gl.createFramebuffer();
105 | gl.bindFramebuffer(gl.FRAMEBUFFER, glFb);
106 | if (isMSAA) {
107 | // MSAA offscreen rendering, attach the MSAA renderbuffers from texture objects
108 | for (let i = 0; i < options.ColorAttachments.length; i++) {
109 | const glMsaaFb = options.ColorAttachments[i].Texture.glMSAARenderBuffer;
110 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.RENDERBUFFER, glMsaaFb);
111 | }
112 | }
113 | else {
114 | // non-MSAA rendering, attach texture objects
115 | for (let i = 0; i < options.ColorAttachments.length; i++) {
116 | const att = options.ColorAttachments[i];
117 | const tex = att.Texture;
118 | switch (tex.type) {
119 | case TextureType.Texture2D:
120 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
121 | gl.TEXTURE_2D, tex.glTexture, att.MipLevel);
122 | break;
123 | case TextureType.TextureCube:
124 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
125 | cubeFaceMap[att.Slice], tex.glTexture, att.MipLevel);
126 | break;
127 | default:
128 | // 3D and 2D-array textures
129 | gl2.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i,
130 | tex.glTexture, att.MipLevel, att.Slice);
131 | break;
132 | }
133 | }
134 | }
135 | // attach optional depth-stencil buffer to framebuffer
136 | if (options.DepthAttachment.Texture) {
137 | const glDSRenderBuffer = options.DepthAttachment.Texture.glDepthRenderBuffer;
138 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, glDSRenderBuffer);
139 | if (options.DepthAttachment.Texture.depthFormat === DepthStencilFormat.DEPTHSTENCIL) {
140 | gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, glDSRenderBuffer);
141 | }
142 | }
143 | if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
144 | console.warn('altai.makePass(): framebuffer completeness check failed!');
145 | }
146 |
147 | // for MSAA, create resolve-framebuffers
148 | const glMsaaFbs = [];
149 | if (isMSAA) {
150 | for (let i = 0; i < options.ColorAttachments.length; i++) {
151 | glMsaaFbs[i] = gl.createFramebuffer();
152 | gl.bindFramebuffer(gl.FRAMEBUFFER, glMsaaFbs[i]);
153 | const att = options.ColorAttachments[i];
154 | const glTex = att.Texture.glTexture;
155 | switch (att.Texture.type) {
156 | case TextureType.Texture2D:
157 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glTex, att.MipLevel);
158 | break;
159 | case TextureType.TextureCube:
160 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, cubeFaceMap[att.Slice], glTex, att.MipLevel);
161 | break;
162 | default:
163 | gl2.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glTex, att.MipLevel, att.Slice);
164 | break;
165 | }
166 | if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
167 | console.warn('altai.makePass(): framebuffer completeness check failed (for MSAA resolve buffers)');
168 | }
169 | }
170 | }
171 | return new Pass(options, glFb, glMsaaFbs);
172 | }
173 |
174 | /**
175 | * Create a new Buffer object.
176 | *
177 | * @param {BufferOptions} options - Buffer creation options
178 | */
179 | public makeBuffer(options: BufferOptions): Buffer {
180 | const gl: WebGLRenderingContext = this.gl;
181 | const buf = new Buffer(options, gl.createBuffer());
182 | gl.bindBuffer(buf.type, buf.glBuffer);
183 | if (options.Data) {
184 | gl.bufferData(buf.type, options.Data as ArrayBuffer, buf.usage);
185 | }
186 | else if (options.LengthInBytes) {
187 | gl.bufferData(buf.type, options.LengthInBytes, buf.usage);
188 | }
189 | return buf;
190 | }
191 |
192 | private asGLTexImgFormat(p: PixelFormat): number {
193 | const gl = this.gl;
194 | switch (p) {
195 | case PixelFormat.RGBA8:
196 | case PixelFormat.RGBA4:
197 | case PixelFormat.RGB5_A1:
198 | case PixelFormat.RGB10_A2:
199 | case PixelFormat.RGBA32F:
200 | case PixelFormat.RGBA16F:
201 | return gl.RGBA;
202 | case PixelFormat.RGB8:
203 | case PixelFormat.RGB565:
204 | return gl.RGB;
205 | case PixelFormat.R32F:
206 | case PixelFormat.R16F:
207 | return gl.LUMINANCE;
208 | default:
209 | return 0;
210 | }
211 | }
212 |
213 | private asGLDepthTexImgFormat(d: DepthStencilFormat): number {
214 | switch (d) {
215 | case DepthStencilFormat.DEPTH: return this.gl.DEPTH_COMPONENT16;
216 | case DepthStencilFormat.DEPTHSTENCIL: return this.gl.DEPTH_STENCIL;
217 | default: return 0;
218 | }
219 | }
220 |
221 | private asGLTexImgType(p: PixelFormat): number {
222 | const gl = this.gl;
223 | switch (p) {
224 | case PixelFormat.RGBA32F:
225 | case PixelFormat.R32F:
226 | return gl.FLOAT;
227 | case PixelFormat.RGBA16F:
228 | case PixelFormat.R16F:
229 | return WebGL2RenderingContext.HALF_FLOAT;
230 | case PixelFormat.RGBA8:
231 | case PixelFormat.RGB8:
232 | return gl.UNSIGNED_BYTE;
233 | case PixelFormat.RGB5_A1:
234 | return gl.UNSIGNED_SHORT_5_5_5_1;
235 | case PixelFormat.RGB565:
236 | return gl.UNSIGNED_SHORT_5_6_5;
237 | case PixelFormat.RGBA4:
238 | return gl.UNSIGNED_SHORT_4_4_4_4;
239 | }
240 | }
241 |
242 | /**
243 | * Create a new Texture object
244 | *
245 | * @param {TextureOptions} options - Texture creation options
246 | */
247 | public makeTexture(options: TextureOptions): Texture {
248 | const gl = this.gl;
249 | const tex = new Texture(options, gl);
250 | gl.activeTexture(gl.TEXTURE0);
251 | gl.bindTexture(tex.type, tex.glTexture);
252 | gl.texParameteri(tex.type, gl.TEXTURE_MIN_FILTER, tex.minFilter);
253 | gl.texParameteri(tex.type, gl.TEXTURE_MAG_FILTER, tex.magFilter);
254 | gl.texParameteri(tex.type, gl.TEXTURE_WRAP_S, tex.wrapU);
255 | gl.texParameteri(tex.type, gl.TEXTURE_WRAP_T, tex.wrapV);
256 | if (tex.type === WebGL2RenderingContext.TEXTURE_3D) {
257 | gl.texParameteri(tex.type, WebGL2RenderingContext.TEXTURE_WRAP_R, tex.wrapW);
258 | }
259 | const numFaces = tex.type === TextureType.TextureCube ? 6 : 1;
260 | const imgFmt = this.asGLTexImgFormat(tex.colorFormat);
261 | const imgType = this.asGLTexImgType(tex.colorFormat);
262 | for (let faceIndex = 0; faceIndex < numFaces; faceIndex++) {
263 | const imgTgt = tex.type === TextureType.TextureCube ? cubeFaceMap[faceIndex] : tex.type;
264 | for (let mipIndex = 0; mipIndex < tex.numMipMaps; mipIndex++) {
265 | // FIXME: data!
266 | let mipWidth = tex.width >> mipIndex;
267 | if (mipWidth === 0) {
268 | mipWidth = 1;
269 | }
270 | let mipHeight = tex.height >> mipIndex;
271 | if (mipHeight === 0) {
272 | mipHeight = 1;
273 | }
274 | if ((TextureType.Texture2D === tex.type) || (TextureType.TextureCube === tex.type)) {
275 | // FIXME: compressed formats + data
276 | gl.texImage2D(imgTgt, mipIndex, imgFmt, mipWidth, mipHeight, 0, imgFmt, imgType, null);
277 | }
278 | }
279 | }
280 |
281 | // MSAA render buffer?
282 | const isMSAA = tex.sampleCount > 1;
283 | if (isMSAA) {
284 | const gl2 = gl as WebGL2RenderingContext;
285 | gl2.bindRenderbuffer(gl.RENDERBUFFER, tex.glMSAARenderBuffer);
286 | gl2.renderbufferStorageMultisample(gl.RENDERBUFFER, tex.sampleCount, imgFmt, tex.width, tex.height);
287 | }
288 | // depth render buffer?
289 | if (tex.depthFormat != DepthStencilFormat.NONE) {
290 | const depthFmt = this.asGLDepthTexImgFormat(tex.depthFormat);
291 | gl.bindRenderbuffer(gl.RENDERBUFFER, tex.glDepthRenderBuffer);
292 | if (isMSAA) {
293 | const gl2 = gl as WebGL2RenderingContext;
294 | gl2.renderbufferStorageMultisample(gl.RENDERBUFFER, tex.sampleCount, depthFmt, tex.width, tex.height);
295 | } else {
296 | gl.renderbufferStorage(gl.RENDERBUFFER, depthFmt, tex.width, tex.height);
297 | }
298 | }
299 | return tex;
300 | }
301 |
302 | /**
303 | * Create a new Shader object.
304 | *
305 | * @param {ShaderOptions} options - Shader creation options
306 | */
307 | public makeShader(options: ShaderOptions): Shader {
308 | const gl = this.gl;
309 | const vs = gl.createShader(gl.VERTEX_SHADER);
310 | gl.shaderSource(vs, options.VertexShader);
311 | gl.compileShader(vs);
312 | if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
313 | console.error("Failed to compile vertex shader:\n" + gl.getShaderInfoLog(vs));
314 | }
315 |
316 | const fs = gl.createShader(gl.FRAGMENT_SHADER);
317 | gl.shaderSource(fs, options.FragmentShader);
318 | gl.compileShader(fs);
319 | if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
320 | console.error("Failed to compile fragment shader:\n" + gl.getShaderInfoLog(fs));
321 | }
322 |
323 | const prog = gl.createProgram();
324 | gl.attachShader(prog, vs);
325 | gl.attachShader(prog, fs);
326 | gl.linkProgram(prog);
327 | if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
328 | console.error("Failed to link shader program!");
329 | }
330 | const shd = new Shader(prog);
331 | gl.deleteShader(vs);
332 | gl.deleteShader(fs);
333 |
334 | return shd;
335 | }
336 |
337 | /**
338 | * Create a new Pipeline object.
339 | *
340 | * @param {PipelineOptions} options - Pipeline creation options
341 | */
342 | public makePipeline(options: PipelineOptions): Pipeline {
343 | const gl = this.gl;
344 | const pip = new Pipeline(options);
345 |
346 | // resolve vertex attributes
347 | for (let layoutIndex = 0; layoutIndex < pip.vertexLayouts.length; layoutIndex++) {
348 | const layout = pip.vertexLayouts[layoutIndex];
349 | const layoutByteSize = layout.byteSize();
350 | for (let compIndex = 0; compIndex < layout.components.length; compIndex++) {
351 | const comp = layout.components[compIndex];
352 | const attrName = comp[0];
353 | const attrFormat = comp[1];
354 | const attrIndex = gl.getAttribLocation(pip.shader.glProgram, attrName);
355 | if (attrIndex != -1) {
356 | const attrib = pip.glAttribs[attrIndex];
357 | attrib.enabled = true;
358 | attrib.vbIndex = layoutIndex;
359 | attrib.divisor = layout.stepFunc == StepFunc.PerVertex ? 0 : layout.stepRate;
360 | attrib.stride = layoutByteSize;
361 | attrib.offset = layout.componentByteOffset(compIndex);
362 | attrib.size = vertexFormatMap[attrFormat][0] as number;
363 | attrib.type = vertexFormatMap[attrFormat][1] as number;
364 | attrib.normalized = vertexFormatMap[attrFormat][2] as boolean;
365 | }
366 | else {
367 | console.warn("Attribute '", attrName, "' not found in shader!");
368 | }
369 | }
370 | }
371 | return pip;
372 | }
373 |
374 | /**
375 | * Create a new DrawState object.
376 | *
377 | * @param {DrawStateOptions} options - DrawState creation options
378 | */
379 | public makeDrawState(options: DrawStateOptions): DrawState {
380 | return new DrawState(options);
381 | }
382 |
383 | /**
384 | * Begin a render-pass.
385 | *
386 | * @param {Pass} pass - a Pass object which describes what happens at the start and end of the render pass
387 | */
388 | public beginPass(pass: Pass) {
389 | const gl = this.gl;
390 | const gl2 = this.gl as WebGL2RenderingContext;
391 | const isDefaultPass: boolean = !pass.ColorAttachments[0].texture;
392 | const width = isDefaultPass ? gl.canvas.width : pass.ColorAttachments[0].texture.width;
393 | const height = isDefaultPass ? gl.canvas.height : pass.ColorAttachments[0].texture.height;
394 | if (isDefaultPass) {
395 | gl.bindFramebuffer(gl.FRAMEBUFFER, null);
396 | } else {
397 | gl.bindFramebuffer(gl.FRAMEBUFFER, pass.glFramebuffer);
398 | if (this.webgl2) {
399 | const drawBuffers: number[] = [];
400 | for (let i = 0; i < pass.ColorAttachments.length; i++) {
401 | if (pass.ColorAttachments[i].texture) {
402 | drawBuffers[i] = gl.COLOR_ATTACHMENT0 + i;
403 | }
404 | }
405 | gl2.drawBuffers(drawBuffers);
406 | }
407 | }
408 |
409 | // prepare clear operations
410 | gl.viewport(0, 0, width, height);
411 | gl.disable(WebGLRenderingContext.SCISSOR_TEST);
412 | gl.colorMask(true, true, true, true);
413 | gl.depthMask(true);
414 | gl.stencilMask(0xFF);
415 |
416 | // update cache
417 | this.cache.scissorTestEnabled = false;
418 | this.cache.colorWriteMask[0] = true;
419 | this.cache.colorWriteMask[1] = true;
420 | this.cache.colorWriteMask[2] = true;
421 | this.cache.colorWriteMask[3] = true;
422 | this.cache.depthWriteEnabled = true;
423 | this.cache.frontStencilWriteMask = 0xFF;
424 | this.cache.backStencilWriteMask = 0xFF;
425 |
426 | if (isDefaultPass || !this.webgl2) {
427 | let clearMask = 0;
428 | const col = pass.ColorAttachments[0];
429 | const dep = pass.DepthAttachment;
430 | if (col.loadAction === LoadAction.Clear) {
431 | clearMask |= WebGLRenderingContext.COLOR_BUFFER_BIT;
432 | gl.clearColor(col.clearColor[0], col.clearColor[1], col.clearColor[2], col.clearColor[3]);
433 | }
434 | if (dep.loadAction === LoadAction.Clear) {
435 | clearMask |= WebGLRenderingContext.DEPTH_BUFFER_BIT | WebGLRenderingContext.STENCIL_BUFFER_BIT;
436 | gl.clearDepth(dep.clearDepth);
437 | gl.clearStencil(dep.clearStencil);
438 | }
439 | if (0 !== clearMask) {
440 | gl.clear(clearMask);
441 | }
442 | } else {
443 | // offscreen WebGL2 (could be MRT)
444 | for (let i = 0; i < pass.ColorAttachments.length; i++) {
445 | const col = pass.ColorAttachments[i];
446 | if (col.texture && (LoadAction.Clear == col.loadAction)) {
447 | gl2.clearBufferfv(gl2.COLOR, i, col.clearColor);
448 | }
449 | }
450 | const dep = pass.DepthAttachment;
451 | if (LoadAction.Clear === dep.loadAction) {
452 | gl2.clearBufferfi(gl2.DEPTH_STENCIL, 0, dep.clearDepth, dep.clearStencil);
453 | }
454 | }
455 | }
456 | /**
457 | * Finish current render-pass.
458 | */
459 | public endPass() {
460 | // FIXME: perform MSAA resolve
461 | }
462 | /**
463 | * Apply a new viewport area.
464 | *
465 | * @param {number} x - horizontal pixel position of viewport area
466 | * @param {number} y - vertical pixel position of viewport area
467 | * @param {number} width - width in pixels of viewport area
468 | * @param {number} height - height in pixels of viewport area
469 | */
470 | public applyViewPort(x: number, y: number, width: number, height: number) {
471 | this.gl.viewport(x, y, width, height);
472 | }
473 | /**
474 | * Apply new scissor rectangle.
475 | *
476 | * @param {number} x - horizontal pixel position of scissor rect
477 | * @param {number} y - vertical pixel position of scissor rect
478 | * @param {number} width - width in pixels of viewport area
479 | * @param {number} height - height in pixels of viewport area
480 | */
481 | public applyScissorRect(x: number, y: number, width: number, height: number) {
482 | this.gl.scissor(x, y, width, height);
483 | }
484 | /**
485 | * Apply new resource bindings.
486 | *
487 | * @param {DrawState} drawState - a DrawState object with the new resource bindings
488 | */
489 | public applyDrawState(drawState: DrawState) {
490 | const gl = this.gl;
491 |
492 | // some validity checks
493 | if ((drawState.IndexBuffer != null) && (drawState.Pipeline.indexFormat === IndexFormat.None)) {
494 | console.warn("altai.applyDrawState(): index buffer bound but pipeline.indexFormat is none!");
495 | }
496 | if ((drawState.IndexBuffer == null) && (drawState.Pipeline.indexFormat !== IndexFormat.None)) {
497 | console.warn("altai.applyDrawState(): pipeline.indexFormat is not none, but no index buffer bound!");
498 | }
499 |
500 | this.curPrimType = drawState.Pipeline.primitiveType;
501 |
502 | // update render state
503 | this.applyState(drawState.Pipeline.state, false);
504 |
505 | // apply shader program
506 | if (this.curProgram !== drawState.Pipeline.shader.glProgram) {
507 | this.curProgram = drawState.Pipeline.shader.glProgram;
508 | gl.useProgram(this.curProgram);
509 | }
510 |
511 | // apply index and vertex data
512 | this.curIndexFormat = drawState.Pipeline.indexFormat;
513 | this.curIndexSize = drawState.Pipeline.indexSize;
514 | if (drawState.IndexBuffer != null) {
515 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, drawState.IndexBuffer.glBuffer);
516 | }
517 | else {
518 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
519 | }
520 | let curVB: WebGLBuffer = null;
521 | for (let attrIndex = 0; attrIndex < MaxNumVertexAttribs; attrIndex++) {
522 | const attrib = drawState.Pipeline.glAttribs[attrIndex];
523 | // FIXME: implement a state cache for vertex attrib bindings
524 | if (attrib.enabled) {
525 | if (drawState.VertexBuffers[attrib.vbIndex].glBuffer !== curVB) {
526 | curVB = drawState.VertexBuffers[attrib.vbIndex].glBuffer;
527 | gl.bindBuffer(gl.ARRAY_BUFFER, curVB);
528 | }
529 | gl.vertexAttribPointer(attrIndex, attrib.size, attrib.type, attrib.normalized, attrib.stride, attrib.offset);
530 | gl.enableVertexAttribArray(attrIndex);
531 | // FIMXE: WebGL2 vertex attrib divisor!
532 | }
533 | else {
534 | gl.disableVertexAttribArray(attrIndex);
535 | }
536 | }
537 |
538 | // apply texture uniforms
539 | let texSlot = 0;
540 | for (const key in drawState.Textures) {
541 | const tex = drawState.Textures[key];
542 | const loc = gl.getUniformLocation(this.curProgram, key);
543 | gl.activeTexture(gl.TEXTURE0+texSlot);
544 | gl.bindTexture(tex.type, tex.glTexture);
545 | gl.uniform1i(loc, texSlot);
546 | texSlot++;
547 | }
548 | }
549 |
550 | /**
551 | * Apply shader uniforms by name and value. Only the following
552 | * types are allowed: float, vec2, vec3, vec4, mat4. Textures
553 | * are applied via applyDrawState.
554 | *
555 | * @param uniforms - uniform name/value pairs
556 | */
557 | applyUniforms(uniforms: {[key: string]: number[] | number}) {
558 | let gl: WebGLRenderingContext = this.gl;
559 | for (let key in uniforms) {
560 | const val = uniforms[key];
561 | const loc = gl.getUniformLocation(this.curProgram, key);
562 | if (loc !== null) {
563 | if (typeof val === "number") {
564 | gl.uniform1f(loc, val);
565 | }
566 | else {
567 | switch (val.length) {
568 | case 1: gl.uniform1fv(loc, val); break;
569 | case 2: gl.uniform2fv(loc, val); break;
570 | case 3: gl.uniform3fv(loc, val); break;
571 | case 4: gl.uniform4fv(loc, val); break;
572 | case 16: gl.uniformMatrix4fv(loc, false, val); break;
573 | default: console.warn('altai.applyUniforms: invalid parameter type!');
574 | }
575 | }
576 | }
577 | }
578 | }
579 |
580 | /**
581 | * Draw primitive range with current draw settings.
582 | *
583 | * @param {number} baseElement - index of first vertex or index
584 | * @param {number} numElements - number of vertices or indices
585 | * @param {number} numInstances - number of instances (default: 1)
586 | */
587 | draw(baseElement: number, numElements: number, numInstances: number = 1) {
588 | if (IndexFormat.None == this.curIndexFormat) {
589 | // non-indexed rendering
590 | if (1 == numInstances) {
591 | this.gl.drawArrays(this.curPrimType, baseElement, numElements);
592 | }
593 | else {
594 | // FIXME: instanced rendering!
595 | }
596 | }
597 | else {
598 | // indexed rendering
599 | let indexOffset = baseElement * this.curIndexSize;
600 | if (1 == numInstances) {
601 | this.gl.drawElements(this.curPrimType, numElements, this.curIndexFormat, indexOffset);
602 | }
603 | else {
604 | // FIXME: instanced rendering!
605 | }
606 | }
607 | }
608 | /**
609 | * Finish current frame, pass function pointer of next frame's draw function.
610 | *
611 | * @param {() => void} drawFunc - the next frame's draw function
612 | */
613 | commitFrame(drawFunc: () => void) {
614 | requestAnimationFrame(drawFunc);
615 | }
616 |
617 | private applyState(state: PipelineState, force: boolean) {
618 | let gl = this.gl;
619 | // apply depth-stencil state changes
620 | if (force || (this.cache.depthCmpFunc != state.depthCmpFunc)) {
621 | this.cache.depthCmpFunc = state.depthCmpFunc;
622 | gl.depthFunc(state.depthCmpFunc);
623 | }
624 | if (force || (this.cache.depthWriteEnabled != state.depthWriteEnabled)) {
625 | this.cache.depthWriteEnabled = state.depthWriteEnabled;
626 | gl.depthMask(state.depthWriteEnabled);
627 | }
628 | if (force || (this.cache.stencilEnabled != state.stencilEnabled)) {
629 | this.cache.stencilEnabled = state.stencilEnabled;
630 | if (state.stencilEnabled) gl.enable(gl.STENCIL_TEST);
631 | else gl.disable(gl.STENCIL_TEST);
632 | }
633 | let sCmpFunc = state.frontStencilCmpFunc;
634 | let sReadMask = state.frontStencilReadMask;
635 | let sRef = state.frontStencilRef;
636 | if (force ||
637 | (this.cache.frontStencilCmpFunc != sCmpFunc) ||
638 | (this.cache.frontStencilReadMask != sReadMask) ||
639 | (this.cache.frontStencilRef != sRef))
640 | {
641 | this.cache.frontStencilCmpFunc = sCmpFunc;
642 | this.cache.frontStencilReadMask = sReadMask;
643 | this.cache.frontStencilRef = sRef;
644 | gl.stencilFuncSeparate(gl.FRONT, sCmpFunc, sRef, sReadMask);
645 | }
646 | sCmpFunc = state.backStencilCmpFunc;
647 | sReadMask = state.backStencilReadMask;
648 | sRef = state.backStencilRef;
649 | if (force ||
650 | (this.cache.backStencilCmpFunc != sCmpFunc) ||
651 | (this.cache.backStencilReadMask != sReadMask) ||
652 | (this.cache.backStencilRef != sRef))
653 | {
654 | this.cache.backStencilCmpFunc = sCmpFunc;
655 | this.cache.backStencilReadMask = sReadMask;
656 | this.cache.backStencilRef = sRef;
657 | gl.stencilFuncSeparate(gl.BACK, sCmpFunc, sRef, sReadMask);
658 | }
659 | let sFailOp = state.frontStencilFailOp;
660 | let sDepthFailOp = state.frontStencilDepthFailOp;
661 | let sPassOp = state.frontStencilPassOp;
662 | if (force ||
663 | (this.cache.frontStencilFailOp != sFailOp) ||
664 | (this.cache.frontStencilDepthFailOp != sDepthFailOp) ||
665 | (this.cache.frontStencilPassOp != sPassOp))
666 | {
667 | this.cache.frontStencilFailOp = sFailOp;
668 | this.cache.frontStencilDepthFailOp = sDepthFailOp;
669 | this.cache.frontStencilPassOp = sPassOp;
670 | gl.stencilOpSeparate(gl.FRONT, sFailOp, sDepthFailOp, sPassOp);
671 | }
672 | sFailOp = state.backStencilFailOp;
673 | sDepthFailOp = state.backStencilDepthFailOp;
674 | sPassOp = state.backStencilPassOp;
675 | if (force ||
676 | (this.cache.backStencilFailOp != sFailOp) ||
677 | (this.cache.backStencilDepthFailOp != sDepthFailOp) ||
678 | (this.cache.backStencilPassOp != sPassOp))
679 | {
680 | this.cache.backStencilFailOp = sFailOp;
681 | this.cache.backStencilDepthFailOp = sDepthFailOp;
682 | this.cache.backStencilPassOp = sPassOp;
683 | gl.stencilOpSeparate(gl.BACK, sFailOp, sDepthFailOp, sPassOp);
684 | }
685 | if (force || (this.cache.frontStencilWriteMask != state.frontStencilWriteMask)) {
686 | this.cache.frontStencilWriteMask = state.frontStencilWriteMask;
687 | gl.stencilMaskSeparate(gl.FRONT, state.frontStencilWriteMask)
688 | }
689 | if (force || (this.cache.backStencilWriteMask != state.backStencilWriteMask)) {
690 | this.cache.backStencilWriteMask = state.backStencilWriteMask;
691 | gl.stencilMaskSeparate(gl.BACK, state.backStencilWriteMask);
692 | }
693 |
694 | // apply blend state changes
695 | if (force || (this.cache.blendEnabled != state.blendEnabled)) {
696 | this.cache.blendEnabled = state.blendEnabled;
697 | gl.enable(gl.BLEND);
698 | }
699 | if (force ||
700 | (this.cache.blendSrcFactorRGB != state.blendSrcFactorRGB) ||
701 | (this.cache.blendDstFactorRGB != state.blendDstFactorRGB) ||
702 | (this.cache.blendSrcFactorAlpha != state.blendSrcFactorAlpha) ||
703 | (this.cache.blendDstFactorAlpha != state.blendDstFactorAlpha))
704 | {
705 | this.cache.blendSrcFactorRGB = state.blendSrcFactorRGB;
706 | this.cache.blendDstFactorRGB = state.blendDstFactorRGB;
707 | this.cache.blendSrcFactorAlpha = state.blendSrcFactorAlpha;
708 | this.cache.blendDstFactorAlpha = state.blendDstFactorAlpha;
709 | gl.blendFuncSeparate(state.blendSrcFactorRGB,
710 | state.blendDstFactorRGB,
711 | state.blendSrcFactorAlpha,
712 | state.blendDstFactorAlpha);
713 | }
714 | if (force ||
715 | (this.cache.blendOpRGB != state.blendOpRGB) ||
716 | (this.cache.blendOpAlpha != state.blendOpAlpha))
717 | {
718 | this.cache.blendOpRGB = state.blendOpRGB;
719 | this.cache.blendOpAlpha = state.blendOpAlpha;
720 | gl.blendEquationSeparate(state.blendOpRGB, state.blendOpAlpha);
721 | }
722 | if (force ||
723 | (this.cache.colorWriteMask[0] != state.colorWriteMask[0]) ||
724 | (this.cache.colorWriteMask[1] != state.colorWriteMask[1]) ||
725 | (this.cache.colorWriteMask[2] != state.colorWriteMask[2]) ||
726 | (this.cache.colorWriteMask[3] != state.colorWriteMask[3]))
727 | {
728 | this.cache.colorWriteMask[0] = state.colorWriteMask[0];
729 | this.cache.colorWriteMask[1] = state.colorWriteMask[1];
730 | this.cache.colorWriteMask[2] = state.colorWriteMask[2];
731 | this.cache.colorWriteMask[3] = state.colorWriteMask[3];
732 | gl.colorMask(state.colorWriteMask[0],
733 | state.colorWriteMask[1],
734 | state.colorWriteMask[2],
735 | state.colorWriteMask[3]);
736 | }
737 | if (force ||
738 | (this.cache.blendColor[0] != state.blendColor[0]) ||
739 | (this.cache.blendColor[1] != state.blendColor[1]) ||
740 | (this.cache.blendColor[2] != state.blendColor[2]) ||
741 | (this.cache.blendColor[3] != state.blendColor[3]))
742 | {
743 | this.cache.blendColor[0] = state.blendColor[0];
744 | this.cache.blendColor[1] = state.blendColor[1];
745 | this.cache.blendColor[2] = state.blendColor[2];
746 | this.cache.blendColor[3] = state.blendColor[3];
747 | gl.blendColor(state.blendColor[0],
748 | state.blendColor[1],
749 | state.blendColor[2],
750 | state.blendColor[3]);
751 | }
752 |
753 | // apply rasterizer state
754 | if (force || (this.cache.cullFaceEnabled != state.cullFaceEnabled)) {
755 | this.cache.cullFaceEnabled = state.cullFaceEnabled;
756 | if (state.cullFaceEnabled) gl.enable(gl.CULL_FACE);
757 | else gl.disable(gl.CULL_FACE);
758 | }
759 | if (force || (this.cache.cullFace != state.cullFace)) {
760 | this.cache.cullFace = state.cullFace;
761 | gl.cullFace(state.cullFace);
762 | }
763 | if (force || (this.cache.scissorTestEnabled != state.scissorTestEnabled)) {
764 | this.cache.scissorTestEnabled = state.scissorTestEnabled;
765 | if (state.scissorTestEnabled) gl.enable(gl.SCISSOR_TEST);
766 | else gl.disable(gl.SCISSOR_TEST);
767 | }
768 | }
769 |
770 | }
771 |
772 | }
--------------------------------------------------------------------------------
/src/enums.ts:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module altai {
4 |
5 | /**
6 | * Buffer types (vertex or index buffers).
7 | */
8 | export enum BufferType {
9 | VertexBuffer = WebGLRenderingContext.ARRAY_BUFFER,
10 | IndexBuffer = WebGLRenderingContext.ELEMENT_ARRAY_BUFFER,
11 | }
12 |
13 |
14 | /**
15 | * Vertex index formats.
16 | */
17 | export enum IndexFormat {
18 | /** no vertex indices */
19 | None = WebGLRenderingContext.NONE,
20 | /** 16-bit indices */
21 | UInt16 = WebGLRenderingContext.UNSIGNED_SHORT,
22 | /** 32-bit indices */
23 | UInt32 = WebGLRenderingContext.UNSIGNED_INT,
24 | }
25 |
26 | /**
27 | * Texture pixel formats.
28 | */
29 | export enum PixelFormat {
30 | /** undefined/none/unused */
31 | NONE,
32 | /** RGBA with 8 bits per channel */
33 | RGBA8,
34 | /** RGB with 8 bits per channel */
35 | RGB8,
36 | /** RGBA with 4 bits per channel */
37 | RGBA4,
38 | /** RGB with 5/6/5 bits per channel */
39 | RGB565,
40 | /** RGBA with 5-bit color channels, and 1-bit alpha */
41 | RGB5_A1,
42 | /** RGBA with 10-bits color channels and 1-bit alpha */
43 | RGB10_A2,
44 | /** RGBA with 32-bit floating point channels */
45 | RGBA32F,
46 | /** RGBA with 16-bit floating point channels */
47 | RGBA16F,
48 | /** R component only, 32-bit floating point */
49 | R32F,
50 | /** R component only, 16-bit floating point */
51 | R16F,
52 | }
53 |
54 | /**
55 | * Depth/stencil surface formats.
56 | */
57 | export enum DepthStencilFormat {
58 | /** depth-only */
59 | DEPTH,
60 | /** combined depth-stencil */
61 | DEPTHSTENCIL,
62 | /** no depth/stencil buffer */
63 | NONE,
64 | }
65 |
66 | /**
67 | * Vertex component formats.
68 | */
69 | export enum VertexFormat {
70 | /** 32-bit float, single component in X */
71 | Float,
72 | /** 32-bit floats, 2 components in XY */
73 | Float2,
74 | /** 32-bit floats, 3 components in XYZ */
75 | Float3,
76 | /** 32-bit floats, 4 components in XYZW */
77 | Float4,
78 | /** 4 packed bytes, signed (-128 .. 127) */
79 | Byte4,
80 | /** 4 packed bytes, signed, normalized (-1.0 .. +1.0) */
81 | Byte4N,
82 | /** 4 packed bytes, unsigned (0 .. 255) */
83 | UByte4,
84 | /** 4 packed bytes, unsigned, normalized (0.0 .. 1.0) */
85 | UByte4N,
86 | /** 2 packed 16-bit shorts, signed (-32767 .. +32768) */
87 | Short2,
88 | /** 2 packed 16-bit shorts, signed (-1.0 .. +1.0) */
89 | Short2N,
90 | /** 4 packed 16-bit shorts, signed (-32767 .. +32768) */
91 | Short4,
92 | /** 4 packed 16-bit shorts, signed (-1.0 .. +1.0) */
93 | Short4N,
94 | }
95 |
96 | /**
97 | * 3D primitive types.
98 | */
99 | export enum PrimitiveType {
100 | /** point list */
101 | Points = WebGLRenderingContext.POINTS,
102 | /** line list */
103 | Lines = WebGLRenderingContext.LINES,
104 | /** line strip */
105 | LineStrip = WebGLRenderingContext.LINE_STRIP,
106 | /** triangle list */
107 | Triangles = WebGLRenderingContext.TRIANGLES,
108 | /** triangle strip */
109 | TriangleStrip = WebGLRenderingContext.TRIANGLE_STRIP,
110 | }
111 |
112 | /**
113 | * texture sampling filters (minification/magnification and mipmapping)
114 | */
115 | export enum Filter {
116 | /** use nearest-filtering (aka point-filtering) */
117 | Nearest = WebGLRenderingContext.NEAREST,
118 | /** use linear filtering */
119 | Linear = WebGLRenderingContext.LINEAR,
120 | /** nearest within mipmap and between mipmaps */
121 | NearestMipmapNearest = WebGLRenderingContext.NEAREST_MIPMAP_NEAREST,
122 | /** nearest within mipmap, linear between mipmaps */
123 | NearestMipmapLinear = WebGLRenderingContext.NEAREST_MIPMAP_LINEAR,
124 | /** linear within mipmap, nearest between mipmaps */
125 | LinearMipmapNearest = WebGLRenderingContext.LINEAR_MIPMAP_NEAREST,
126 | /** linear within and between mipmaps */
127 | LinearMipmapLinear = WebGLRenderingContext.LINEAR_MIPMAP_LINEAR
128 | }
129 |
130 | /**
131 | * texture addressing wrap mode (aka UV wrap)
132 | */
133 | export enum Wrap {
134 | /** clamp texture coords to (0.0 .. 1.0) */
135 | ClampToEdge = WebGLRenderingContext.CLAMP_TO_EDGE,
136 | /** repeat texture coords within (0.0 .. 1.0) */
137 | Repeat = WebGLRenderingContext.REPEAT,
138 | /** mirror-repeat texture coords (0.0 .. 1.0 .. 0.0) */
139 | MirroredRepeat = WebGLRenderingContext.MIRRORED_REPEAT,
140 | }
141 |
142 | /**
143 | * texture object types
144 | */
145 | export enum TextureType {
146 | /** 2D texture */
147 | Texture2D = WebGLRenderingContext.TEXTURE_2D,
148 | /** cubemap texture */
149 | TextureCube = WebGLRenderingContext.TEXTURE_CUBE_MAP,
150 | /** 3D texture */
151 | Texture3D = WebGL2RenderingContext.TEXTURE_3D,
152 | /** 2D-array texture */
153 | TextureArray2D = WebGL2RenderingContext.TEXTURE_2D_ARRAY
154 | }
155 |
156 | /**
157 | * buffer and texture data usage hint
158 | */
159 | export enum Usage {
160 | /** data is immutable, cannot be modified after creation */
161 | Immutable = WebGLRenderingContext.STATIC_DRAW,
162 | /** data is updated infrequently */
163 | Dynamic = WebGLRenderingContext.DYNAMIC_DRAW,
164 | /** data is overwritten each frame */
165 | Stream = WebGLRenderingContext.STREAM_DRAW,
166 | }
167 |
168 | /**
169 | * identify front/back sides for face culling.
170 | */
171 | export enum Face {
172 | /** cull front side */
173 | Front = WebGLRenderingContext.FRONT,
174 | /** cull back side */
175 | Back = WebGLRenderingContext.BACK,
176 | /** cull both sides */
177 | Both = WebGLRenderingContext.FRONT_AND_BACK,
178 | }
179 |
180 | /**
181 | * Comparision functions for depth and stencil checks.
182 | */
183 | export enum CompareFunc {
184 | /** new value never passes comparion test */
185 | Never = WebGLRenderingContext.NEVER,
186 | /** new value passses if it is less than the existing value */
187 | Less = WebGLRenderingContext.LESS,
188 | /** new value passes if it is equal to existing value */
189 | Equal = WebGLRenderingContext.EQUAL,
190 | /** new value passes if it is less than or equal to existing value */
191 | LessEqual = WebGLRenderingContext.LEQUAL,
192 | /** new value passes if it is greater than existing value */
193 | Greater = WebGLRenderingContext.GREATER,
194 | /** new value passes if it is not equal to existing value */
195 | NotEqual = WebGLRenderingContext.NOTEQUAL,
196 | /** new value passes if it is greater than or equal to existing value */
197 | GreaterEqual = WebGLRenderingContext.GEQUAL,
198 | /** new value always passes */
199 | Always = WebGLRenderingContext.ALWAYS,
200 | }
201 |
202 | /**
203 | * Stencil-buffer operations.
204 | */
205 | export enum StencilOp {
206 | /** keep the current stencil value */
207 | Keep = WebGLRenderingContext.KEEP,
208 | /** set the stencil value to zero */
209 | Zero = WebGLRenderingContext.ZERO,
210 | /** replace the stencil value with stencil reference value */
211 | Replace = WebGLRenderingContext.REPLACE,
212 | /** increment the current stencil value, clamp to max */
213 | IncrClamp = WebGLRenderingContext.INCR,
214 | /** decrement the current stencil value, clamp to zero */
215 | DecrClamp = WebGLRenderingContext.DECR,
216 | /** perform a logical bitwise invert operation on the stencil value */
217 | Invert = WebGLRenderingContext.INVERT,
218 | /** increment the current stencil value, with wrap-around */
219 | IncrWrap = WebGLRenderingContext.INCR_WRAP,
220 | /** decrement the current stencil value, with wrap-around */
221 | DecrWrap = WebGLRenderingContext.DECR_WRAP,
222 | }
223 |
224 | /**
225 | * Alpha-blending factors.
226 | */
227 | export enum BlendFactor {
228 | /** blend factor of zero */
229 | Zero = WebGLRenderingContext.ZERO,
230 | /** blend factor of one */
231 | One = WebGLRenderingContext.ONE,
232 | /** blend factor of source color */
233 | SrcColor = WebGLRenderingContext.SRC_COLOR,
234 | /** blend factor of one minus source color */
235 | OneMinusSrcColor = WebGLRenderingContext.ONE_MINUS_SRC_COLOR,
236 | /** blend factor of source alpha */
237 | SrcAlpha = WebGLRenderingContext.SRC_ALPHA,
238 | /** blend factor of one minus source alpha */
239 | OneMinusSrcAlpha = WebGLRenderingContext.ONE_MINUS_SRC_ALPHA,
240 | /** blend factor of destination color */
241 | DstColor = WebGLRenderingContext.DST_COLOR,
242 | /** blend factor of one minus destination alpha */
243 | OneMinusDstColor = WebGLRenderingContext.ONE_MINUS_DST_COLOR,
244 | /** blend factor of destination alpha */
245 | DstAlpha = WebGLRenderingContext.DST_ALPHA,
246 | /** blend factor of one minus destination alpha */
247 | OneMinusDstAlpha = WebGLRenderingContext.ONE_MINUS_DST_ALPHA,
248 | /** blend factor of the minimum of either source alpha or one minus destination alpha */
249 | SrcAlphaSaturated = WebGLRenderingContext.SRC_ALPHA_SATURATE,
250 | /** blend factor of constant color */
251 | BlendColor = WebGLRenderingContext.CONSTANT_COLOR,
252 | /** blend factor of one minus constant color */
253 | OneMinusBlendColor = WebGLRenderingContext.ONE_MINUS_CONSTANT_COLOR,
254 | /** blend factor of constant alpha */
255 | BlendAlpha = WebGLRenderingContext.CONSTANT_ALPHA,
256 | /** blend factor of one minus destination alpha */
257 | OneMinusBlendAlpha = WebGLRenderingContext.ONE_MINUS_CONSTANT_ALPHA,
258 | }
259 |
260 | export enum BlendOp {
261 | /** add source and destination pixel values */
262 | Add = WebGLRenderingContext.FUNC_ADD,
263 | /** subtract destination from source pixel values
264 | Subtract = WebGLRenderingContext.FUNC_SUBTRACT,
265 | /** subtract source from destination pixel values */
266 | ReverseSubtract = WebGLRenderingContext.FUNC_REVERSE_SUBTRACT,
267 | }
268 |
269 | export enum StepFunc {
270 | PerVertex,
271 | PerInstance,
272 | }
273 |
274 | export enum LoadAction {
275 | DontCare,
276 | Load,
277 | Clear,
278 | }
279 |
280 | }
--------------------------------------------------------------------------------
/src/options.ts:
--------------------------------------------------------------------------------
1 | ///
2 | 'use strict'
3 |
4 | module altai {
5 |
6 | /**
7 | * WebGL and canvas initialization options.
8 | */
9 | export interface GfxOptions {
10 | /** use WebGL2? (default: false) */
11 | UseWebGL2?: boolean;
12 | /** name of existing HTML canvas (default: 'canvas') */
13 | Canvas?: string;
14 | /** new width of canvas (default: don't change canvas width) */
15 | Width?: number;
16 | /** new height of canvas (default: don't change canvas height) */
17 | Height?: number;
18 | /** whether drawing buffer should have alpha channel (default: true) */
19 | Alpha?: boolean;
20 | /** whether drawing buffer should have a depth buffer (default: true) */
21 | Depth?: boolean;
22 | /** whether drawing buffer should have a stencil buffer (default: false) */
23 | Stencil?: boolean;
24 | /** whether drawing buffer should be anti-aliased (default: true) */
25 | AntiAlias?: boolean;
26 | /** whether drawing buffer contains pre-multiplied-alpha colors (default: true) */
27 | PreMultipliedAlpha?: boolean;
28 | /** whether content of drawing buffer should be preserved (default: false) */
29 | PreserveDrawingBuffer?: boolean;
30 | /** whether to create a context for low-power-consumption (default: false) */
31 | PreferLowPowerToHighPerformance?: boolean;
32 | /** whether context creation fails if performance would be low (default: false) */
33 | FailIfMajorPerformanceCaveat?: boolean;
34 | /** whether to create a high-resolution context on Retina-type displays (default: false) */
35 | HighDPI?: boolean;
36 | }
37 |
38 | /**
39 | * Buffer creation options.
40 | */
41 | export interface BufferOptions {
42 | /** whether the buffer contains vertex- or index-data */
43 | Type: BufferType;
44 | /** whether the buffer is immutable or can be updated after creation */
45 | Usage?: Usage;
46 | /** optional content initialization data */
47 | Data?: ArrayBufferView | ArrayBuffer;
48 | /** buffer size in bytes if no init data provided */
49 | LengthInBytes?: number;
50 | }
51 |
52 | /**
53 | * Texture creation options
54 | */
55 | export interface TextureOptions {
56 | /** the textue type (2D, 3D, Cube...) */
57 | Type: TextureType;
58 | /** texture usage (immutable, , ..) */
59 | Usage?: Usage;
60 | /** width of the texture */
61 | Width: number;
62 | /** height of the texture */
63 | Height: number;
64 | /** optional depth of the texture (for 3D or Array textures) */
65 | Depth?: number;
66 | /** optional number of mipmaps */
67 | NumMipMaps?: number;
68 | /** pixel format of the texture */
69 | ColorFormat: PixelFormat;
70 | /** optional depth-buffer format (for render target textures) */
71 | DepthFormat?: DepthStencilFormat;
72 | /** optional sample-count (for MSAA render targets) */
73 | SampleCount?: number;
74 | /** optional texture wrap mode for U dimension */
75 | WrapU?: Wrap;
76 | /** optional texture wrap mode for V dimension */
77 | WrapV?: Wrap;
78 | /** optional texture wrap mode for W dimension */
79 | WrapW?: Wrap;
80 | /** optional texture filter mode for minimifaction */
81 | MinFilter?: Filter;
82 | /** optional texture filter mode for magnification */
83 | MagFilter?: Filter;
84 | // FIXME: initialization data
85 | }
86 |
87 | /**
88 | * Vertex input layout description.
89 | */
90 | export interface VertexLayoutOptions {
91 | /** vertex component names and formats */
92 | Components: [ string, VertexFormat ][];
93 | /** advance per-vertex or per-instance */
94 | StepFunc?: StepFunc;
95 | /** the vertex step-rate (divisor) for instancing */
96 | StepRate?: number;
97 | }
98 |
99 | /**
100 | * Options for creating a Pipeline object.
101 | */
102 | export interface PipelineOptions {
103 |
104 | /** described the structure of input vertex data */
105 | VertexLayouts: VertexLayoutOptions[];
106 | /** the shader object, with matching vertex inputs */
107 | Shader: Shader;
108 | /** rendering primitive type (triangle, triangle strip, lines, ..)*/
109 | PrimitiveType?: PrimitiveType;
110 | /** index data format (none, 16- or 32-bit) */
111 | IndexFormat?: IndexFormat;
112 |
113 | /** is alpha-blending enabled? (default: false) */
114 | BlendEnabled?: boolean;
115 | /** the blend source factor (both RGB and Alpha, default: One) */
116 | BlendSrcFactor?: BlendFactor;
117 | /** the blend destination factor (both RGB and Alpha, default: Zero) */
118 | BlendDstFactor?: BlendFactor;
119 | /** the blend operation (both RGB and Alpha, default: Add) */
120 | BlendOp?: BlendOp;
121 | /** what color-channels to write (default: all true) */
122 | ColorWriteMask?: [boolean, boolean, boolean, boolean];
123 | /** blend-constant color (default: all 1.0) */
124 | BlendColor?: [number, number, number, number];
125 |
126 | /** separate RGB blend source factor (default: One) */
127 | BlendSrcFactorRGB?: BlendFactor;
128 | /** separate RGB blend destination factor (default: Zero) */
129 | BlendDstFactorRGB?: BlendFactor;
130 | /** separate RGB blend operation (default: Add) */
131 | BlendOpRGB?: BlendOp;
132 |
133 | /** separate Alpha blend source factor (default: One) */
134 | BlendSrcFactorAlpha?: BlendFactor;
135 | /** separate Alpha blend destination factor (default: Zero) */
136 | BlendDstFactorAlpha?: BlendFactor;
137 | /** separate Alpha blend operation (default: Add) */
138 | BlendOpAlpha?: BlendOp;
139 |
140 | /** stencil operations enabled? (default: false) */
141 | StencilEnabled?: boolean;
142 | /** common front/back stencil-fail operation (default: Keep) */
143 | StencilFailOp?: StencilOp;
144 | /** common front/back stencil-depth-fail operation (default: Keep) */
145 | StencilDepthFailOp?: StencilOp;
146 | /** common front/back stencil-pass operation (default: Keep) */
147 | StencilPassOp?: StencilOp;
148 | /** common front/back stencil-compare function (default: Always) */
149 | StencilCmpFunc?: CompareFunc;
150 | /** common front/back stencil read mask (default: 0xFF) */
151 | StencilReadMask?: number;
152 | /** common front/back stencil write mask (default: 0xFF) */
153 | StencilWriteMask?: number;
154 | /** common front/back stencil ref value (default: 0) */
155 | StencilRef?: number;
156 |
157 | /** separate front stencil-fail operation (default: Keep) */
158 | FrontStencilFailOp?: StencilOp;
159 | /** separate front stencil-depth-fail operation (default: Keep) */
160 | FrontStencilDepthFailOp?: StencilOp;
161 | /** separate front stencil-pass operation (default: Keep) */
162 | FrontStencilPassOp?: StencilOp;
163 | /** separate front stencil-compare function (default: Always) */
164 | FrontStencilCmpFunc?: CompareFunc;
165 | /** separate front stencil read mask (default: 0xFF) */
166 | FrontStencilReadMask?: number;
167 | /** separate front stencil write mask (default: 0xFF) */
168 | FrontStencilWriteMask?: number;
169 | /** separate front stencil ref value (default: 0) */
170 | FrontStencilRef?: number;
171 |
172 | /** separate back stencil-fail operation (default: Keep) */
173 | BackStencilFailOp?: StencilOp;
174 | /** separate back stencil-depth-fail operation (default: Keep) */
175 | BackStencilDepthFailOp?: StencilOp;
176 | /** separate back stencil-pass operation (default: Keep) */
177 | BackStencilPassOp?: StencilOp;
178 | /** separate back stencil-compare function (default: Always) */
179 | BackStencilCmpFunc?: CompareFunc;
180 | /** separate back stencil read mask (default: 0xFF) */
181 | BackStencilReadMask?: number;
182 | /** separate back stencil write mask (default: 0xFF) */
183 | BackStencilWriteMask?: number;
184 | /** separate back stencil ref value (default: 0) */
185 | BackStencilRef?: number;
186 |
187 | /** depth-compare function (default: Always) */
188 | DepthCmpFunc?: CompareFunc;
189 | /** depth-writes enabled? (default: false) */
190 | DepthWriteEnabled?: boolean;
191 |
192 | /** face-culling enabled? (default: false) */
193 | CullFaceEnabled?: boolean;
194 | /** face side to be culled (default: Back) */
195 | CullFace?: Face;
196 | /** scissor test enabled? (default: false) */
197 | ScissorTestEnabled?: boolean;
198 | }
199 |
200 | /**
201 | * Options for creating a Shader object.
202 | */
203 | export interface ShaderOptions {
204 | /** GLSL vertex shader source code */
205 | VertexShader: string,
206 | /** GLSL fragment shader source code */
207 | FragmentShader: string,
208 | }
209 |
210 | /**
211 | * Options for creating a DrawState object.
212 | */
213 | export interface DrawStateOptions {
214 | /** a Pipeline object */
215 | Pipeline: Pipeline;
216 | /** one or multiple VertexBuffer objects */
217 | VertexBuffers: Buffer[];
218 | /** an optional index buffer object */
219 | IndexBuffer?: Buffer;
220 | /** optional texture objects */
221 | Textures?: {[key: string]: Texture; };
222 | }
223 |
224 | /**
225 | * Options for initializing pass color-attachments
226 | */
227 | export interface ColorAttachmentOptions {
228 | /** the backing texture of the color attachment */
229 | Texture?: Texture;
230 | /** optional rendering mip level (default: 0) */
231 | MipLevel?: number;
232 | /** optional rendering texture slice (default: 0) */
233 | Slice?: number;
234 | LoadAction?: LoadAction;
235 | ClearColor?: [number, number, number, number];
236 | }
237 |
238 | export interface DepthAttachmentOptions {
239 | Texture?: Texture,
240 | LoadAction?: LoadAction,
241 | ClearDepth?: number,
242 | ClearStencil?: number,
243 | }
244 |
245 | export interface PassOptions {
246 | ColorAttachments?: ColorAttachmentOptions[];
247 | DepthAttachment?: DepthAttachmentOptions;
248 | }
249 |
250 | }
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | 'use strict'
4 |
5 | module altai {
6 |
7 | export const MaxNumColorAttachments = 4;
8 | export const MaxNumVertexAttribs = 16;
9 |
10 | export function some(opt0: T, opt1: T): T {
11 | return opt0 != null ? opt0 : opt1;
12 | }
13 | export function some3(opt0: T, opt1: T, opt2: T): T {
14 | return opt0 != null ? opt0 : (opt1 != null ? opt1 : opt2);
15 | }
16 |
17 | /**
18 | * A Buffer object for vertex- or index-data.
19 | */
20 | export class Buffer {
21 | readonly type: BufferType;
22 | readonly usage: Usage;
23 | readonly glBuffer: WebGLBuffer;
24 |
25 | constructor(o: BufferOptions, glBuffer: WebGLBuffer) {
26 | this.type = o.Type;
27 | this.usage = some(o.Usage, Usage.Immutable);
28 | this.glBuffer = glBuffer;
29 | }
30 | }
31 |
32 | /**
33 | * A Texture object.
34 | */
35 | export class Texture {
36 | readonly type: TextureType;
37 | readonly usage: Usage;
38 | readonly width: number;
39 | readonly height: number;
40 | readonly depth: number;
41 | readonly numMipMaps: number;
42 | readonly colorFormat: PixelFormat;
43 | readonly depthFormat: DepthStencilFormat;
44 | readonly sampleCount: number;
45 | readonly wrapU: Wrap;
46 | readonly wrapV: Wrap;
47 | readonly wrapW: Wrap;
48 | readonly minFilter: Filter;
49 | readonly magFilter: Filter;
50 | readonly glTexture: WebGLTexture;
51 | readonly glMSAARenderBuffer: WebGLRenderbuffer;
52 | readonly glDepthRenderBuffer: WebGLRenderbuffer;
53 |
54 | constructor(o: TextureOptions, gl: WebGLRenderingContext|WebGL2RenderingContext) {
55 | this.type = o.Type;
56 | this.usage = some(o.Usage, Usage.Immutable);
57 | this.width = o.Width;
58 | this.height = o.Height;
59 | this.depth = some(o.Depth, 1);
60 | this.numMipMaps = some(o.NumMipMaps, 1);
61 | this.colorFormat = o.ColorFormat;
62 | this.depthFormat = some(o.DepthFormat, DepthStencilFormat.NONE);
63 | this.sampleCount = some(o.SampleCount, 1);
64 | this.wrapU = some(o.WrapU, Wrap.ClampToEdge);
65 | this.wrapV = some(o.WrapV, Wrap.ClampToEdge);
66 | this.wrapW = some(o.WrapW, Wrap.ClampToEdge);
67 | this.minFilter = some(o.MinFilter, Filter.Nearest);
68 | this.magFilter = some(o.MagFilter, Filter.Nearest);
69 | this.glTexture = gl.createTexture();
70 | if (this.sampleCount > 1) {
71 | this.glMSAARenderBuffer = gl.createRenderbuffer();
72 | }
73 | else {
74 | this.glMSAARenderBuffer = null;
75 | }
76 | if (this.depthFormat != DepthStencilFormat.NONE) {
77 | this.glDepthRenderBuffer = gl.createRenderbuffer();
78 | }
79 | else {
80 | this.glDepthRenderBuffer = null;
81 | }
82 | }
83 | }
84 |
85 | export class VertexLayout {
86 | components: [string, VertexFormat][];
87 | stepFunc?: StepFunc;
88 | stepRate?: number;
89 |
90 | constructor(o: VertexLayoutOptions) {
91 | this.components = o.Components;
92 | this.stepFunc = some(o.StepFunc, StepFunc.PerVertex);
93 | this.stepRate = some(o.StepRate, 1);
94 | }
95 |
96 | static vertexFormatByteSize(fmt: VertexFormat): number {
97 | switch (fmt) {
98 | case VertexFormat.Float:
99 | case VertexFormat.Byte4:
100 | case VertexFormat.Byte4N:
101 | case VertexFormat.UByte4:
102 | case VertexFormat.UByte4N:
103 | case VertexFormat.Short2:
104 | case VertexFormat.Short2N:
105 | return 4;
106 | case VertexFormat.Float2:
107 | case VertexFormat.Short4:
108 | case VertexFormat.Short4N:
109 | return 8;
110 | case VertexFormat.Float3:
111 | return 12;
112 | case VertexFormat.Float4:
113 | return 16;
114 | }
115 | }
116 |
117 | byteSize(): number {
118 | let size = 0;
119 | for (let comp of this.components) {
120 | size += VertexLayout.vertexFormatByteSize(comp[1]);
121 | }
122 | return size;
123 | }
124 |
125 | componentByteOffset(compIndex: number): number {
126 | let offset = 0;
127 | for (let i = 0; i < compIndex; i++) {
128 | offset += VertexLayout.vertexFormatByteSize(this.components[i][1]);
129 | }
130 | return offset;
131 | }
132 | }
133 |
134 | export class PipelineState {
135 | blendEnabled: boolean;
136 | blendSrcFactorRGB: BlendFactor;
137 | blendDstFactorRGB: BlendFactor;
138 | blendOpRGB: BlendOp;
139 | blendSrcFactorAlpha: BlendFactor;
140 | blendDstFactorAlpha: BlendFactor;
141 | blendOpAlpha: BlendOp;
142 | colorWriteMask: [boolean, boolean, boolean, boolean];
143 | blendColor: [number, number, number, number];
144 |
145 | stencilEnabled: boolean;
146 |
147 | frontStencilFailOp: StencilOp;
148 | frontStencilDepthFailOp: StencilOp;
149 | frontStencilPassOp: StencilOp;
150 | frontStencilCmpFunc: CompareFunc;
151 | frontStencilReadMask: number;
152 | frontStencilWriteMask: number;
153 | frontStencilRef: number;
154 |
155 | backStencilFailOp: StencilOp;
156 | backStencilDepthFailOp: StencilOp;
157 | backStencilPassOp: StencilOp;
158 | backStencilCmpFunc: CompareFunc;
159 | backStencilReadMask: number;
160 | backStencilWriteMask: number;
161 | backStencilRef: number;
162 |
163 | depthCmpFunc: CompareFunc;
164 | depthWriteEnabled: boolean;
165 |
166 | cullFaceEnabled: boolean;
167 | cullFace: Face;
168 | scissorTestEnabled: boolean;
169 |
170 | constructor(o: PipelineOptions) {
171 | this.blendEnabled = some(o.BlendEnabled, false);
172 | this.blendSrcFactorRGB = some3(o.BlendSrcFactorRGB, o.BlendSrcFactor, BlendFactor.One);
173 | this.blendDstFactorRGB = some3(o.BlendDstFactorRGB, o.BlendDstFactor, BlendFactor.Zero);
174 | this.blendOpRGB = some3(o.BlendOpRGB, o.BlendOp, BlendOp.Add);
175 | this.blendSrcFactorAlpha = some3(o.BlendSrcFactorAlpha, o.BlendSrcFactor, BlendFactor.One);
176 | this.blendDstFactorAlpha = some3(o.BlendDstFactorAlpha, o.BlendDstFactor, BlendFactor.Zero);
177 | this.blendOpAlpha = some3(o.BlendOpAlpha, o.BlendOp, BlendOp.Add);
178 | this.colorWriteMask = some(o.ColorWriteMask, [true, true, true, true] as [boolean, boolean, boolean, boolean]);
179 | this.blendColor = some(o.BlendColor, [1.0, 1.0, 1.0, 1.0] as [number, number, number, number]);
180 |
181 | this.stencilEnabled = some(o.StencilEnabled, false);
182 |
183 | this.frontStencilFailOp = some3(o.FrontStencilFailOp, o.StencilFailOp, StencilOp.Keep);
184 | this.frontStencilDepthFailOp = some3(o.FrontStencilDepthFailOp, o.StencilDepthFailOp, StencilOp.Keep);
185 | this.frontStencilPassOp = some3(o.FrontStencilPassOp, o.StencilPassOp, StencilOp.Keep);
186 | this.frontStencilCmpFunc = some3(o.FrontStencilCmpFunc, o.StencilCmpFunc, CompareFunc.Always);
187 | this.frontStencilReadMask = some3(o.FrontStencilReadMask, o.StencilReadMask, 0xFF);
188 | this.frontStencilWriteMask = some3(o.FrontStencilWriteMask, o.StencilWriteMask, 0xFF);
189 | this.frontStencilRef = some3(o.FrontStencilRef, o.StencilRef, 0);
190 |
191 | this.backStencilFailOp = some3(o.BackStencilFailOp, o.StencilFailOp, StencilOp.Keep);
192 | this.backStencilDepthFailOp = some3(o.BackStencilDepthFailOp, o.StencilDepthFailOp, StencilOp.Keep);
193 | this.backStencilPassOp = some3(o.BackStencilPassOp, o.StencilPassOp, StencilOp.Keep);
194 | this.backStencilCmpFunc = some3(o.BackStencilCmpFunc, o.StencilCmpFunc, CompareFunc.Always);
195 | this.backStencilReadMask = some3(o.BackStencilReadMask, o.StencilReadMask, 0xFF);
196 | this.backStencilWriteMask = some3(o.BackStencilWriteMask, o.StencilWriteMask, 0xFF);
197 | this.backStencilRef = some3(o.BackStencilRef, o.StencilRef, 0);
198 |
199 | this.depthCmpFunc = some(o.DepthCmpFunc, CompareFunc.Always);
200 | this.depthWriteEnabled = some(o.DepthWriteEnabled, false);
201 |
202 | this.cullFaceEnabled = some(o.CullFaceEnabled, false);
203 | this.cullFace = some(o.CullFace, Face.Back);
204 | this.scissorTestEnabled = some(o.ScissorTestEnabled, false);
205 | }
206 | }
207 |
208 | class glAttrib {
209 | enabled: boolean = false;
210 | vbIndex: number = 0;
211 | divisor: number = 0;
212 | stride: number = 0;
213 | size: number = 0;
214 | normalized: boolean = false;
215 | offset: number = 0;
216 | type: GLenum = 0;
217 | }
218 |
219 | /**
220 | * Opaque pipeline-state-object.
221 | */
222 | export class Pipeline {
223 | readonly vertexLayouts: VertexLayout[];
224 | readonly shader: Shader;
225 | readonly primitiveType: PrimitiveType;
226 | readonly state: PipelineState;
227 | readonly glAttribs: glAttrib[];
228 | readonly indexFormat: IndexFormat;
229 | readonly indexSize: number;
230 |
231 | constructor(o: PipelineOptions) {
232 | this.vertexLayouts = [];
233 | for (let vlOpt of o.VertexLayouts) {
234 | this.vertexLayouts.push(new VertexLayout(vlOpt));
235 | }
236 | this.shader = o.Shader;
237 | this.primitiveType = some(o.PrimitiveType, PrimitiveType.Triangles);
238 | this.state = new PipelineState(o);
239 | this.glAttribs = [];
240 | for (let i = 0; i < MaxNumVertexAttribs; i++) {
241 | this.glAttribs.push(new glAttrib());
242 | }
243 | this.indexFormat = some(o.IndexFormat, IndexFormat.None);
244 | switch (this.indexFormat) {
245 | case IndexFormat.UInt16: this.indexSize = 2; break;
246 | case IndexFormat.UInt32: this.indexSize = 4; break;
247 | default: this.indexSize = 0; break;
248 | }
249 | }
250 | }
251 |
252 | /**
253 | * Opaque shader object.
254 | */
255 | export class Shader {
256 | readonly glProgram: WebGLProgram;
257 |
258 | constructor(glProgram: WebGLProgram) {
259 | this.glProgram = glProgram;
260 | }
261 | }
262 |
263 | /**
264 | * A DrawState object is a bundle of resource binding slots,
265 | * create with Gfx.makePass(). DrawState objects area
266 | * mutable, the resource binding slots can be
267 | * reconfigured on existing DrawState objects.
268 | */
269 | export class DrawState {
270 | Pipeline: Pipeline;
271 | VertexBuffers: Buffer[];
272 | IndexBuffer: Buffer;
273 | Textures: {[key: string]: Texture; };
274 |
275 | constructor(o: DrawStateOptions) {
276 | this.Pipeline = o.Pipeline;
277 | this.VertexBuffers = o.VertexBuffers;
278 | this.IndexBuffer = some(o.IndexBuffer, null);
279 | this.Textures = some(o.Textures, null);
280 | }
281 | }
282 |
283 | export class ColorAttachment {
284 | texture: Texture;
285 | mipLevel: number;
286 | slice: number;
287 | loadAction: LoadAction;
288 | clearColor: [number, number, number, number];
289 | readonly glMSAAResolveFramebuffer: WebGLFramebuffer;
290 |
291 | constructor(o: ColorAttachmentOptions, glMsaaFb: WebGLFramebuffer) {
292 | this.texture = some(o.Texture, null);
293 | this.mipLevel = some(o.MipLevel, 0);
294 | this.slice = some(o.Slice, 0);
295 | this.loadAction = some(o.LoadAction, LoadAction.Clear);
296 | this.clearColor = some(o.ClearColor, [0.0, 0.0, 0.0, 1.0] as [number, number, number, number]);
297 | this.glMSAAResolveFramebuffer = glMsaaFb;
298 | }
299 | }
300 |
301 | export class DepthAttachment {
302 | texture: Texture;
303 | loadAction: LoadAction;
304 | clearDepth: number;
305 | clearStencil: number;
306 |
307 | constructor(o: DepthAttachmentOptions) {
308 | this.texture = some(o.Texture, null);
309 | this.loadAction = some(o.LoadAction, LoadAction.Clear);
310 | this.clearDepth = some(o.ClearDepth, 1.0);
311 | this.clearStencil = some(o.ClearStencil, 0);
312 | }
313 | }
314 |
315 | export class Pass {
316 | ColorAttachments: ColorAttachment[];
317 | DepthAttachment: DepthAttachment;
318 | readonly glFramebuffer: WebGLFramebuffer;
319 |
320 | constructor(o: PassOptions, glFb: WebGLFramebuffer, glMsaaFbs: WebGLFramebuffer[]) {
321 | this.glFramebuffer = glFb;
322 | this.ColorAttachments = [];
323 | if (o.ColorAttachments == null) {
324 | this.ColorAttachments.push(new ColorAttachment({}, null));
325 | }
326 | else {
327 | for (let i = 0; i < o.ColorAttachments.length; i++) {
328 | const glMsaaFb = (glMsaaFbs && glMsaaFbs[i]) ? glMsaaFbs[i]:null;
329 | this.ColorAttachments.push(new ColorAttachment(o.ColorAttachments[i], glMsaaFb))
330 | }
331 | }
332 | this.DepthAttachment = new DepthAttachment(some(o.DepthAttachment, {}));
333 | }
334 | }
335 |
336 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "sourceMap": true,
5 | "outFile": "examples/examples.js"
6 | },
7 | "include": [
8 | "src/altai.ts",
9 | "examples/*.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "jsRules": {},
7 | "rules": {
8 | "no-reference": false,
9 | "no-console": false,
10 | "variable-name": {
11 | "options": [
12 | "allow-leading-underscore",
13 | "allow-snake-case",
14 | "allow-pascal-case"
15 | ]
16 | },
17 | "max-classes-per-file": false,
18 | "no-trailing-whitespace": false,
19 | "no-bitwise": false,
20 | "no-namespace": false,
21 | "object-literal-sort-keys": false,
22 | "max-line-length": false,
23 | "quotemark": false,
24 | "member-ordering": false,
25 | "array-type": false,
26 | "interface-name": false,
27 | },
28 | "rulesDirectory": []
29 | }
--------------------------------------------------------------------------------