@kitware/vtk.js
Version:
Visualization Toolkit for the Web
663 lines (512 loc) • 18.6 kB
JavaScript
import macro from '../../macros.js';
import vtkShader from './Shader.js';
var vtkErrorMacro = macro.vtkErrorMacro; // perform in place string substitutions, indicate if a substitution was done
// this is useful for building up shader strings which typically involve
// lots of string substitutions. Return true if a substitution was done.
function substitute(source, search, replace, all) {
// We only accept strings or array of strings, typeof is faster than Array.isArray
var replaceStr = typeof replace === 'string' ? replace : replace.join('\n'); // We don't need to instantiate a RegExp if we don't want a global substitution.
// In all other cases, we need to take the provided string or RegExp and
// instantiate a new one to add the `g` flag.
// Argument defaults are transpiled to slow `arguments`-based operations
// better assume undefined as flag to know if the value is set or not
var replaceSearch = all === false ? search : new RegExp(search, 'g');
var resultstr = source.replace(replaceSearch, replaceStr);
return {
// If the result is different than the input, we did perform a replacement
replace: resultstr !== replaceStr,
result: resultstr
};
} // ----------------------------------------------------------------------------
// vtkShaderProgram methods
// ----------------------------------------------------------------------------
function vtkShaderProgram(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkShaderProgram');
publicAPI.compileShader = function () {
if (!model.vertexShader.compile()) {
vtkErrorMacro(model.vertexShader.getSource().split('\n').map(function (line, index) {
return "".concat(index, ": ").concat(line);
}).join('\n'));
vtkErrorMacro(model.vertexShader.getError());
return 0;
}
if (!model.fragmentShader.compile()) {
vtkErrorMacro(model.fragmentShader.getSource().split('\n').map(function (line, index) {
return "".concat(index, ": ").concat(line);
}).join('\n'));
vtkErrorMacro(model.fragmentShader.getError());
return 0;
} // skip geometry for now
if (!publicAPI.attachShader(model.vertexShader)) {
vtkErrorMacro(model.error);
return 0;
}
if (!publicAPI.attachShader(model.fragmentShader)) {
vtkErrorMacro(model.error);
return 0;
}
if (!publicAPI.link()) {
vtkErrorMacro("Links failed: ".concat(model.error));
return 0;
}
publicAPI.setCompiled(true);
return 1;
};
publicAPI.cleanup = function () {
if (model.shaderType === 'Unknown' || model.handle === 0) {
return;
}
model.context.deleteShader(model.handle);
model.handle = 0;
};
publicAPI.bind = function () {
if (!model.linked && !publicAPI.link()) {
return false;
}
model.context.useProgram(model.handle);
publicAPI.setBound(true);
return true;
};
publicAPI.isBound = function () {
return !!model.bound;
};
publicAPI.release = function () {
model.context.useProgram(null);
publicAPI.setBound(false);
};
publicAPI.setContext = function (ctx) {
model.vertexShader.setContext(ctx);
model.fragmentShader.setContext(ctx);
model.geometryShader.setContext(ctx);
};
publicAPI.link = function () {
if (model.linked) {
return true;
}
if (model.handle === 0) {
model.error = 'Program has not been initialized, and/or does not have shaders.';
return false;
} // clear out the list of uniforms used
model.uniformLocs = {};
model.context.linkProgram(model.handle);
var isCompiled = model.context.getProgramParameter(model.handle, model.context.LINK_STATUS);
if (!isCompiled) {
var lastError = model.context.getProgramInfoLog(model.handle);
vtkErrorMacro("Error linking shader ".concat(lastError));
model.handle = 0;
return false;
}
publicAPI.setLinked(true);
model.attributeLocs = {};
return true;
};
publicAPI.setUniformMatrix = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
var f32 = new Float32Array(v);
model.context.uniformMatrix4fv(location, false, f32);
return true;
};
publicAPI.setUniformMatrix3x3 = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
var f32 = new Float32Array(v);
model.context.uniformMatrix3fv(location, false, f32);
return true;
};
publicAPI.setUniformf = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform1f(location, v);
return true;
};
publicAPI.setUniformfv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform1fv(location, v);
return true;
};
publicAPI.setUniformi = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform1i(location, v);
return true;
};
publicAPI.setUniformiv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform1iv(location, v);
return true;
};
publicAPI.setUniform2f = function (name, v1, v2) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
if (v2 === undefined) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform2f(location, v1, v2);
return true;
};
publicAPI.setUniform2fv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform2fv(location, v);
return true;
};
publicAPI.setUniform2i = function (name, v1, v2) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
if (v2 === undefined) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform2i(location, v1, v2);
return true;
};
publicAPI.setUniform2iv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform2iv(location, v);
return true;
};
publicAPI.setUniform3f = function (name, a1, a2, a3) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
if (a3 === undefined) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform3f(location, a1, a2, a3);
return true;
};
publicAPI.setUniform3fArray = function (name, a) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
if (!Array.isArray(a) || a.length !== 3) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform3f(location, a[0], a[1], a[2]);
return true;
};
publicAPI.setUniform3fv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform3fv(location, v);
return true;
};
publicAPI.setUniform3i = function (name) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var array = args; // allow an array passed as a single argument
if (array.length === 1 && Array.isArray(array[0])) {
array = array[0];
}
if (array.length !== 3) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform3i(location, array[0], array[1], array[2]);
return true;
};
publicAPI.setUniform3iv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform3iv(location, v);
return true;
};
publicAPI.setUniform4f = function (name) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
var array = args; // allow an array passed as a single argument
if (array.length === 1 && Array.isArray(array[0])) {
array = array[0];
}
if (array.length !== 4) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform4f(location, array[0], array[1], array[2], array[3]);
return true;
};
publicAPI.setUniform4fv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform4fv(location, v);
return true;
};
publicAPI.setUniform4i = function (name) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
var array = args; // allow an array passed as a single argument
if (array.length === 1 && Array.isArray(array[0])) {
array = array[0];
}
if (array.length !== 4) {
throw new RangeError('Invalid number of values for array');
}
model.context.uniform4i(location, array[0], array[1], array[2], array[3]);
return true;
};
publicAPI.setUniform4iv = function (name, v) {
var location = publicAPI.findUniform(name);
if (location === -1) {
model.error = "Could not set uniform ".concat(name, " . No such uniform.");
return false;
}
model.context.uniform4iv(location, v);
return true;
};
publicAPI.findUniform = function (name) {
if (!name || !model.linked) {
return -1;
} // see if we have cached the result
var loc = model.uniformLocs[name];
if (loc !== undefined) {
return loc;
}
loc = model.context.getUniformLocation(model.handle, name);
if (loc === null) {
model.error = "Uniform ".concat(name, " not found in current shader program.");
model.uniformLocs[name] = -1;
return -1;
}
model.uniformLocs[name] = loc;
return loc;
};
publicAPI.isUniformUsed = function (name) {
if (!name) {
return false;
} // see if we have cached the result
var loc = model.uniformLocs[name];
if (loc !== undefined) {
return loc !== null;
}
if (!model.linked) {
vtkErrorMacro('attempt to find uniform when the shader program is not linked');
return false;
}
loc = model.context.getUniformLocation(model.handle, name);
model.uniformLocs[name] = loc;
if (loc === null) {
return false;
}
return true;
};
publicAPI.isAttributeUsed = function (name) {
if (!name) {
return false;
} // see if we have cached the result
if (name in model.attributeLocs) {
return true;
}
if (!model.linked) {
vtkErrorMacro('attempt to find uniform when the shader program is not linked');
return false;
}
var loc = model.context.getAttribLocation(model.handle, name);
if (loc === -1) {
return false;
}
model.attributeLocs[name] = loc;
return true;
};
publicAPI.attachShader = function (shader) {
if (shader.getHandle() === 0) {
model.error = 'Shader object was not initialized, cannot attach it.';
return false;
}
if (shader.getShaderType() === 'Unknown') {
model.error = 'Shader object is of type Unknown and cannot be used.';
return false;
}
if (model.handle === 0) {
var thandle = model.context.createProgram();
if (thandle === 0) {
model.error = 'Could not create shader program.';
return false;
}
model.handle = thandle;
model.linked = false;
}
if (shader.getShaderType() === 'Vertex') {
if (model.vertexShaderHandle !== 0) {
model.comntext.detachShader(model.handle, model.vertexShaderHandle);
}
model.vertexShaderHandle = shader.getHandle();
}
if (shader.getShaderType() === 'Fragment') {
if (model.fragmentShaderHandle !== 0) {
model.context.detachShader(model.handle, model.fragmentShaderHandle);
}
model.fragmentShaderHandle = shader.getHandle();
}
model.context.attachShader(model.handle, shader.getHandle());
publicAPI.setLinked(false);
return true;
};
publicAPI.detachShader = function (shader) {
if (shader.getHandle() === 0) {
model.error = 'shader object was not initialized, cannot attach it.';
return false;
}
if (shader.getShaderType() === 'Unknown') {
model.error = 'Shader object is of type Unknown and cannot be used.';
return false;
}
if (model.handle === 0) {
model.error = 'This shader program has not been initialized yet.';
}
switch (shader.getShaderType()) {
case 'Vertex':
if (model.vertexShaderHandle !== shader.getHandle()) {
model.error = 'The supplied shader was not attached to this program.';
return false;
}
model.context.detachShader(model.handle, shader.getHandle());
model.vertexShaderHandle = 0;
model.linked = false;
return true;
case 'Fragment':
if (model.fragmentShaderHandle !== shader.getHandle()) {
model.error = 'The supplied shader was not attached to this program.';
return false;
}
model.context.detachShader(model.handle, shader.getHandle());
model.fragmentShaderHandle = 0;
model.linked = false;
return true;
default:
return false;
}
};
publicAPI.setContext = function (ctx) {
model.context = ctx;
model.vertexShader.setContext(ctx);
model.fragmentShader.setContext(ctx);
model.geometryShader.setContext(ctx);
};
publicAPI.setLastCameraMTime = function (mtime) {
model.lastCameraMTime = mtime;
}; // publicAPI.enableAttributeArray = (name) => {
// const location = publicAPI.findAttributeArray(name);
// if (location === -1) {
// model.error = `Could not enable attribute ${name} No such attribute.`;
// return false;
// }
// model.context.enableVertexAttribArray(location);
// return true;
// };
// publicAPI.disableAttributeArray = (name) => {
// const location = publicAPI.findAttributeArray(name);
// if (location === -1) {
// model.error = `Could not enable attribute ${name} No such attribute.`;
// return false;
// }
// model.context.disableVertexAttribArray(location);
// return true;
// };
} // ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
var DEFAULT_VALUES = {
vertexShaderHandle: 0,
fragmentShaderHandle: 0,
geometryShaderHandle: 0,
vertexShader: null,
fragmentShader: null,
geometryShader: null,
linked: false,
bound: false,
compiled: false,
error: '',
handle: 0,
numberOfOutputs: 0,
attributesLocs: null,
uniformLocs: null,
md5Hash: 0,
context: null,
lastCameraMTime: null
}; // ----------------------------------------------------------------------------
function extend(publicAPI, model) {
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
Object.assign(model, DEFAULT_VALUES, initialValues); // Instantiate internal objects
model.attributesLocs = {};
model.uniformLocs = {};
model.vertexShader = vtkShader.newInstance();
model.vertexShader.setShaderType('Vertex');
model.fragmentShader = vtkShader.newInstance();
model.fragmentShader.setShaderType('Fragment');
model.geometryShader = vtkShader.newInstance();
model.geometryShader.setShaderType('Geometry'); // Build VTK API
macro.obj(publicAPI, model);
macro.get(publicAPI, model, ['lastCameraMTime']);
macro.setGet(publicAPI, model, ['error', 'handle', 'compiled', 'bound', 'md5Hash', 'vertexShader', 'fragmentShader', 'geometryShader', 'linked']); // Object methods
vtkShaderProgram(publicAPI, model);
} // ----------------------------------------------------------------------------
var newInstance = macro.newInstance(extend, 'vtkShaderProgram'); // ----------------------------------------------------------------------------
var vtkShaderProgram$1 = {
newInstance: newInstance,
extend: extend,
substitute: substitute
};
export { vtkShaderProgram$1 as default, substitute };