Source: shaderHelper.js

/*jshint esversion: 11 */

// @ts-check

//import THREE from "three";

/**
 * Simplified creation of Shader Material for CS559 Framework
 *
 * The "shaderMaterial" function creates a THREE shader material
 * given URLs for the vertex and fragment shader.
 *
 * It work asynchronously - it gives simple shaders until the
 * real ones load.
 *
 * There are potentially issues if the fragment shader
 * ends up getting compiled before the vertex shader.
 * 
 * @module shaderHelper
 */

import * as T from "../CS559-Three/build/three.module.js";

// this takes an object that describes a shader material and adds the
// shader code (if provided) to it.
// if the shader code is not there, it gives a default shader
const defaultVertexShader = `
    void main()
    {
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
`;
const defaultFragmentShader = `
    void main()
    {
        gl_FragColor = vec4(0.4,0.4,0.6,1);
    }
`;
const errorFragmentShader = `
    void main()
    {
        gl_FragColor = vec4(0.8,0.4,0.4,1);
    }
`;

/**
 *
 * @param {string} url
 * @param {THREE.ShaderMaterial} material
 */
function loadFragmentShader(url, material) {
  const loader = new T.FileLoader();

  loader.load(
    url,
    /* onload = */ function(data) {
      material.fragmentShader = data.toString();
      material.needsUpdate = true;
    },
    /* onprogress = */ function(xhr) {},
    /* onerror = */ function(err) {
      console.log(`Failed to Load Shader (file:${url})`);
      console.log(`Error: ${err}`);
      material.fragmentShader = errorFragmentShader;
      material.needsUpdate = true;
    }
  );
}

/**
 *
 * @param {string} url
 * @param {THREE.ShaderMaterial} material
 */
function loadVertexShader(url, material) {
  const loader = new T.FileLoader();

  loader.load(
    url,
    /* onload = */ function(data) {
      material.vertexShader = data.toString();
      material.needsUpdate = true;
    },
    /* onprogress = */ function(xhr) {},
    /* onerror = */ function(err) {
      console.log(`Failed to Load Shader (file:${url})`);
      console.log(`Error: ${err}`);
      material.fragmentShader = errorFragmentShader;
      material.needsUpdate = true;
    }
  );
}

/**
 * Create a Shader Material from a set of shader files
 * Creates the material with default shaders, and async loads the
 * shaders from file and swaps them in when they are ready.
 *
 * @param {string} vertexShaderURL
 * @param {string} fragmentShaderURL
 * @param {THREE.ShaderMaterialParameters} [properties]
 * @returns {THREE.ShaderMaterial}
 */
export function shaderMaterial(vertexShaderURL, fragmentShaderURL, properties = {}) {
  const sm = new T.ShaderMaterial(properties);
  // create a default shader until the real ones load
  sm.vertexShader = defaultVertexShader;
  sm.fragmentShader = defaultFragmentShader;
  sm.needsUpdate = true;
  // these will be loaded asynchronously
  loadVertexShader(vertexShaderURL, sm);
  loadFragmentShader(fragmentShaderURL, sm);
  // the material is ready for use, even if it has the default shader
  return sm;
}