cione-comp-lib
Version:
`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`
265 lines (264 loc) • 7.22 kB
JavaScript
import util from "./core/util.mjs";
import Compositor from "./compositor/Compositor.mjs";
import CompoSceneNode from "./compositor/SceneNode.mjs";
import CompoTextureNode from "./compositor/TextureNode.mjs";
import CompoFilterNode from "./compositor/FilterNode.mjs";
import Shader from "./Shader.mjs";
import Texture from "./Texture.mjs";
import Texture2D from "./Texture2D.mjs";
import TextureCube from "./TextureCube.mjs";
import register from "./shader/registerBuiltinCompositor.mjs";
register(Shader);
var shaderSourceReg = /^#source\((.*?)\)/;
function createCompositor(json, opts) {
var compositor = new Compositor();
opts = opts || {};
var lib = {
textures: {},
parameters: {}
};
var afterLoad = function(shaderLib, textureLib) {
for (var i = 0; i < json.nodes.length; i++) {
var nodeInfo = json.nodes[i];
var node = createNode(nodeInfo, lib, opts);
if (node) {
compositor.addNode(node);
}
}
};
for (var name in json.parameters) {
var paramInfo = json.parameters[name];
lib.parameters[name] = convertParameter(paramInfo);
}
loadTextures(json, lib, opts, function(textureLib) {
lib.textures = textureLib;
afterLoad();
});
return compositor;
}
function createNode(nodeInfo, lib, opts) {
var type = nodeInfo.type || "filter";
var shaderSource;
var inputs;
var outputs;
if (type === "filter") {
var shaderExp = nodeInfo.shader.trim();
var res = shaderSourceReg.exec(shaderExp);
if (res) {
shaderSource = Shader.source(res[1].trim());
} else if (shaderExp.charAt(0) === "#") {
shaderSource = lib.shaders[shaderExp.substr(1)];
}
if (!shaderSource) {
shaderSource = shaderExp;
}
if (!shaderSource) {
return;
}
}
if (nodeInfo.inputs) {
inputs = {};
for (var name in nodeInfo.inputs) {
if (typeof nodeInfo.inputs[name] === "string") {
inputs[name] = nodeInfo.inputs[name];
} else {
inputs[name] = {
node: nodeInfo.inputs[name].node,
pin: nodeInfo.inputs[name].pin
};
}
}
}
if (nodeInfo.outputs) {
outputs = {};
for (var name in nodeInfo.outputs) {
var outputInfo = nodeInfo.outputs[name];
outputs[name] = {};
if (outputInfo.attachment != null) {
outputs[name].attachment = outputInfo.attachment;
}
if (outputInfo.keepLastFrame != null) {
outputs[name].keepLastFrame = outputInfo.keepLastFrame;
}
if (outputInfo.outputLastFrame != null) {
outputs[name].outputLastFrame = outputInfo.outputLastFrame;
}
if (outputInfo.parameters) {
outputs[name].parameters = convertParameter(outputInfo.parameters);
}
}
}
var node;
if (type === "scene") {
node = new CompoSceneNode({
name: nodeInfo.name,
scene: opts.scene,
camera: opts.camera,
outputs
});
} else if (type === "texture") {
node = new CompoTextureNode({
name: nodeInfo.name,
outputs
});
} else {
node = new CompoFilterNode({
name: nodeInfo.name,
shader: shaderSource,
inputs,
outputs
});
}
if (node) {
if (nodeInfo.parameters) {
for (var name in nodeInfo.parameters) {
var val = nodeInfo.parameters[name];
if (typeof val === "string") {
val = val.trim();
if (val.charAt(0) === "#") {
val = lib.textures[val.substr(1)];
} else {
node.on(
"beforerender",
createSizeSetHandler(
name,
tryConvertExpr(val)
)
);
}
} else if (typeof val === "function") {
node.on("beforerender", val);
}
node.setParameter(name, val);
}
}
if (nodeInfo.defines && node.pass) {
for (var name in nodeInfo.defines) {
var val = nodeInfo.defines[name];
node.pass.material.define("fragment", name, val);
}
}
}
return node;
}
function defaultWidthFunc(width, height) {
return width;
}
function defaultHeightFunc(width, height) {
return height;
}
function convertParameter(paramInfo) {
var param = {};
if (!paramInfo) {
return param;
}
["type", "minFilter", "magFilter", "wrapS", "wrapT", "flipY", "useMipmap"].forEach(function(name) {
var val = paramInfo[name];
if (val != null) {
if (typeof val === "string") {
val = Texture[val];
}
param[name] = val;
}
});
var sizeScale = paramInfo.scale || 1;
["width", "height"].forEach(function(name) {
if (paramInfo[name] != null) {
var val = paramInfo[name];
if (typeof val === "string") {
val = val.trim();
param[name] = createSizeParser(
name,
tryConvertExpr(val),
sizeScale
);
} else {
param[name] = val;
}
}
});
if (!param.width) {
param.width = defaultWidthFunc;
}
if (!param.height) {
param.height = defaultHeightFunc;
}
if (paramInfo.useMipmap != null) {
param.useMipmap = paramInfo.useMipmap;
}
return param;
}
function loadTextures(json, lib, opts, callback) {
if (!json.textures) {
callback({});
return;
}
var textures = {};
var loading = 0;
var cbd = false;
var textureRootPath = opts.textureRootPath;
util.each(json.textures, function(textureInfo, name) {
var texture;
var path = textureInfo.path;
var parameters = convertParameter(textureInfo.parameters);
if (Array.isArray(path) && path.length === 6) {
if (textureRootPath) {
path = path.map(function(item) {
return util.relative2absolute(item, textureRootPath);
});
}
texture = new TextureCube(parameters);
} else if (typeof path === "string") {
if (textureRootPath) {
path = util.relative2absolute(path, textureRootPath);
}
texture = new Texture2D(parameters);
} else {
return;
}
texture.load(path);
loading++;
texture.once("success", function() {
textures[name] = texture;
loading--;
if (loading === 0) {
callback(textures);
cbd = true;
}
});
});
if (loading === 0 && !cbd) {
callback(textures);
}
}
function createSizeSetHandler(name, exprFunc) {
return function(renderer) {
var dpr = renderer.getDevicePixelRatio();
var width = renderer.getWidth();
var height = renderer.getHeight();
var result = exprFunc(width, height, dpr);
this.setParameter(name, result);
};
}
function createSizeParser(name, exprFunc, scale) {
scale = scale || 1;
return function(renderer) {
var dpr = renderer.getDevicePixelRatio();
var width = renderer.getWidth() * scale;
var height = renderer.getHeight() * scale;
return exprFunc(width, height, dpr);
};
}
function tryConvertExpr(string) {
var exprRes = /^expr\((.*)\)$/.exec(string);
if (exprRes) {
try {
var func = new Function("width", "height", "dpr", "return " + exprRes[1]);
func(1, 1);
return func;
} catch (e) {
throw new Error("Invalid expression.");
}
}
}
export { createCompositor as default };