import Base from '../core/Base';
// PENDING
// Use topological sort ?
/**
* Node of graph based post processing.
*
* @constructor clay.compositor.CompositorNode
* @extends clay.core.Base
*
*/
var CompositorNode = Base.extend(function () {
return /** @lends clay.compositor.CompositorNode# */ {
/**
* @type {string}
*/
name: '',
/**
* Input links, will be updated by the graph
* @example:
* inputName: {
* node: someNode,
* pin: 'xxxx'
* }
* @type {Object}
*/
inputLinks: {},
/**
* Output links, will be updated by the graph
* @example:
* outputName: {
* node: someNode,
* pin: 'xxxx'
* }
* @type {Object}
*/
outputLinks: {},
// Save the output texture of previous frame
// Will be used when there exist a circular reference
_prevOutputTextures: {},
_outputTextures: {},
// Example: { name: 2 }
_outputReferences: {},
_rendering: false,
// If rendered in this frame
_rendered: false,
_compositor: null
};
},
/** @lends clay.compositor.CompositorNode.prototype */
{
// TODO Remove parameter function callback
updateParameter: function (outputName, renderer) {
var outputInfo = this.outputs[outputName];
var parameters = outputInfo.parameters;
var parametersCopy = outputInfo._parametersCopy;
if (!parametersCopy) {
parametersCopy = outputInfo._parametersCopy = {};
}
if (parameters) {
for (var key in parameters) {
if (key !== 'width' && key !== 'height') {
parametersCopy[key] = parameters[key];
}
}
}
var width, height;
if (parameters.width instanceof Function) {
width = parameters.width.call(this, renderer);
}
else {
width = parameters.width;
}
if (parameters.height instanceof Function) {
height = parameters.height.call(this, renderer);
}
else {
height = parameters.height;
}
if (
parametersCopy.width !== width
|| parametersCopy.height !== height
) {
if (this._outputTextures[outputName]) {
this._outputTextures[outputName].dispose(renderer.gl);
}
}
parametersCopy.width = width;
parametersCopy.height = height;
return parametersCopy;
},
/**
* Set parameter
* @param {string} name
* @param {} value
*/
setParameter: function (name, value) {},
/**
* Get parameter value
* @param {string} name
* @return {}
*/
getParameter: function (name) {},
/**
* Set parameters
* @param {Object} obj
*/
setParameters: function (obj) {
for (var name in obj) {
this.setParameter(name, obj[name]);
}
},
render: function () {},
getOutput: function (renderer /*optional*/, name) {
if (name == null) {
// Return the output texture without rendering
name = renderer;
return this._outputTextures[name];
}
var outputInfo = this.outputs[name];
if (!outputInfo) {
return ;
}
// Already been rendered in this frame
if (this._rendered) {
// Force return texture in last frame
if (outputInfo.outputLastFrame) {
return this._prevOutputTextures[name];
}
else {
return this._outputTextures[name];
}
}
else if (
// TODO
this._rendering // Solve Circular Reference
) {
if (!this._prevOutputTextures[name]) {
// Create a blank texture at first pass
this._prevOutputTextures[name] = this._compositor.allocateTexture(outputInfo.parameters || {});
}
return this._prevOutputTextures[name];
}
this.render(renderer);
return this._outputTextures[name];
},
removeReference: function (outputName) {
this._outputReferences[outputName]--;
if (this._outputReferences[outputName] === 0) {
var outputInfo = this.outputs[outputName];
if (outputInfo.keepLastFrame) {
if (this._prevOutputTextures[outputName]) {
this._compositor.releaseTexture(this._prevOutputTextures[outputName]);
}
this._prevOutputTextures[outputName] = this._outputTextures[outputName];
}
else {
// Output of this node have alreay been used by all other nodes
// Put the texture back to the pool.
this._compositor.releaseTexture(this._outputTextures[outputName]);
}
}
},
link: function (inputPinName, fromNode, fromPinName) {
// The relationship from output pin to input pin is one-on-multiple
this.inputLinks[inputPinName] = {
node: fromNode,
pin: fromPinName
};
if (!fromNode.outputLinks[fromPinName]) {
fromNode.outputLinks[fromPinName] = [];
}
fromNode.outputLinks[fromPinName].push({
node: this,
pin: inputPinName
});
// Enabled the pin texture in shader
this.pass.material.enableTexture(inputPinName);
},
clear: function () {
this.inputLinks = {};
this.outputLinks = {};
},
updateReference: function (outputName) {
if (!this._rendering) {
this._rendering = true;
for (var inputName in this.inputLinks) {
var link = this.inputLinks[inputName];
link.node.updateReference(link.pin);
}
this._rendering = false;
}
if (outputName) {
this._outputReferences[outputName] ++;
}
},
beforeFrame: function () {
this._rendered = false;
for (var name in this.outputLinks) {
this._outputReferences[name] = 0;
}
},
afterFrame: function () {
// Put back all the textures to pool
for (var name in this.outputLinks) {
if (this._outputReferences[name] > 0) {
var outputInfo = this.outputs[name];
if (outputInfo.keepLastFrame) {
if (this._prevOutputTextures[name]) {
this._compositor.releaseTexture(this._prevOutputTextures[name]);
}
this._prevOutputTextures[name] = this._outputTextures[name];
}
else {
this._compositor.releaseTexture(this._outputTextures[name]);
}
}
}
}
});
export default CompositorNode;