File size: 3,361 Bytes
c0dce6d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import Attribute from './Attribute.js';

export default class Material {

    /**
     * The parent MiniGL controller.
     *
     * @type {MiniGL}
     * @private
     */
    gl;

    uniformInstances = [];

    /**
     *
     * @param {MiniGL} minigl
     * @param {object} properties
     */
    constructor(minigl, vertexShaders, fragments, uniforms = {}, properties = {}) {

        // Add additional properties.
        Object.assign(this, properties);

        // Set required properties.
        this.gl = minigl;
        this.uniforms = uniforms;

        const context = this.gl.getContext();

        const prefix = `
            precision highp float;
        `;

        this.vertexSource = `
            ${prefix}
            attribute vec4 position;
            attribute vec2 uv;
            attribute vec2 uvNorm;
            ${this._getUniformVariableDeclarations(this.gl.commonUniforms,"vertex")}
            ${this._getUniformVariableDeclarations(uniforms,"vertex")}
            ${vertexShaders}
        `;

        this.Source = `
            ${prefix}
            ${this._getUniformVariableDeclarations(this.gl.commonUniforms,"fragment")}
            ${this._getUniformVariableDeclarations(uniforms,"fragment")}
            ${fragments}
        `;

        this.vertexShader = this._getShaderByType(context.VERTEX_SHADER, this.vertexSource);
        this.fragmentShader = this._getShaderByType(context.FRAGMENT_SHADER, this.Source);
        this.program = context.createProgram();

        context.attachShader(this.program, this.vertexShader);
        context.attachShader(this.program, this.fragmentShader);
        context.linkProgram(this.program);
        context.getProgramParameter(this.program, context.LINK_STATUS) || console.error(context.getProgramInfoLog(this.program));
        context.useProgram(this.program);

        this.attachUniforms(void 0, this.gl.commonUniforms);
        this.attachUniforms(void 0, this.uniforms);

    }

    _getShaderByType(type, source) {
        const context = this.gl.getContext();
        const shader = context.createShader(type);
        context.shaderSource(shader, source);
        context.compileShader(shader);

        if (!context.getShaderParameter(shader, context.COMPILE_STATUS)) {
            console.error(context.getShaderInfoLog(shader));
        }

        return shader;
    }

    _getUniformVariableDeclarations(uniforms, type) {
        return Object.entries(uniforms).map(([uniform, value]) => {
            return value.getDeclaration(uniform, type);
        }).join("\n");
    }

    attachUniforms(name, uniforms) {
        if (!name) {
            Object.entries(uniforms).forEach(([name, uniform]) => {
                this.attachUniforms(name, uniform);
            });
        } else if (uniforms.type === 'array') {
            uniforms.value.forEach((uniform, i) => {
                this.attachUniforms(`${name}[${i}]`, uniform);
            });
        } else if (uniforms.type === 'struct') {
            Object.entries(uniforms.value).forEach(([uniform, i]) => {
                this.attachUniforms(`${name}.${uniform}`, i);
            });
        } else {
            this.uniformInstances.push({
                uniform: uniforms,
                location: this.gl.getContext().getUniformLocation(this.program, name)
            });
        }
    }

}