// VVVV.js -- Visual Web Client Programming
// (c) 2011 Matthias Zauner
// VVVV.js is freely distributable under the MIT license.
// Additional authors of sub components are mentioned at the specific code locations.
/** A hash table of {@VVVV.Types.ShaderCodeResource} objects, indexed with the name/path of the shader code resource */
VVVV.ShaderCodeResources = {};
/**
* Stores and caches shader code, which comes from loaded .vvvvjs.fx files or a DefineEffect node
* @class
* @constructor
*/
VVVV.Types.ShaderCodeResource = function() {
var sourceCode = '';
/** An array of all nodes which utilize this shader code */
this.relatedNodes = [];
/** the DefineNode node which defines this shader code; undefined if the shader code comes from a .vvvvjs.fx file */
this.definingNode = undefined;
/**
* Sets the source code
* @param {String} str the shader code as string
*/
this.setSourceCode = function(src) {
sourceCode = src;
for (var i=0; i<this.relatedNodes.length; i++) {
this.relatedNodes[i].shaderSourceUpdated(sourceCode);
}
}
/**
* registers a shader node with this shader code resource
* @param {VVVV.Core.Node} the shader node
*/
this.addRelatedNode = function(node) {
this.relatedNodes.push(node);
if (sourceCode!='')
node.shaderSourceUpdated(sourceCode);
}
}
var identity = mat4.identity(mat4.create());
/**
* A data structure which contains all render state attributes that can be set in VVVV.js
* This is the data object which flows between WebGlRenderState pins
* @class
* @constructor
*/
VVVV.Types.WebGlRenderState = function() {
/** @member */
this.alphaBlending = true;
/** @member */
this.srcBlendMode = "SRC_ALPHA";
/** @member */
this.destBlendMode = "ONE_MINUS_SRC_ALPHA";
/** @member */
this.enableZWrite = true;
/** @member */
this.depthFunc = "LEQUAL";
/** @member */
this.depthOffset = 0.0;
/** @member */
this.polygonDrawMode = "TRIANGLES";
/**
* Used to create a copy of a WebGlRenderState object. Heavily used by the (EX9.RenderState) nodes to create altered versions
* of the incoming render state
* @param {VVVV.Types.WebGlRenderState} other the source render state
*/
this.copy_attributes = function(other) {
this.alphaBlending = other.alphaBlending;
this.alphaFunc = other.alphaFunc;
this.srcBlendMode = other.srcBlendMode;
this.destBlendMode = other.destBlendMode;
this.enableZwrite = other.enableZWrite;
this.depthFunc = other.depthFunc;
this.depthOffset = other.depthOffset;
this.polygonDrawMode = other.polygonDrawMode;
}
/**
* makes the WebGL calls to establish the render state
* @param {WebGlContext} gl the WebGL context
*/
this.apply = function(gl) {
if (this.alphaBlending)
gl.enable(gl.BLEND);
else
gl.disable(gl.BLEND);
gl.blendFunc(gl[this.srcBlendMode], gl[this.destBlendMode]);
gl.depthMask(this.enableZWrite);
//gl.depthFunc(gl[this.depthFunc]);
}
}
/**
* The VertexBuffer class holds vertex data and provides methods to create vertex buffer objects in a given WebGL context;
* VVVV.Types.VertexBuffer objects mainly are used in (EX9.Geometry) nodes, and
* ultimately are parts of a {@link VVVV.Types.Mesh} object
* @class
* @constructor
* @param {WebGlContext} gl the WebGL context
* @param {Array} p an array of vertex positions
*/
VVVV.Types.VertexBuffer = function(gl, p) {
/** the WebGL Vertex Buffer Object */
this.vbo = undefined;
/** @member */
this.subBuffers = {};
/** total buffer length */
this.length = 0;
/**
* sets sub buffer data
* @param {String} u the buffer usage (e.g. POSITION, NORMAL, TEXCOORD0, TEXCOORD1, ...)
* @param {Integer} s the sub buffer size
* @param {Array} d the sub buffer data
*/
this.setSubBuffer = function(u, s, d) {
this.subBuffers[u] = {
usage: u,
data: new Float32Array(d),
size: s,
offset: this.length
};
this.length += this.subBuffers[u].data.byteLength;
}
this.setSubBuffer('POSITION', 3, p);
/**
* Creates the VBO in the WebGL context and stores the vertex data
*/
this.create = function() {
this.vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.bufferData(gl.ARRAY_BUFFER, this.length, gl.STATIC_DRAW);
_(this.subBuffers).each(function(b) {
gl.bufferSubData(gl.ARRAY_BUFFER, b.offset, b.data);
});
}
}
/**
* A Mesh consists of a {@link VVVV.Types.VertexBuffer} object and a list of indices. It creates a new index buffer
* in the given WebGL context. Mesh objects are usually created by (EX9.Geometry) nodes and flow into a shader node's
* Mesh input pin
* @class
* @constructor
* @param {WebGlContext} gl the WebGL context
* @param {VVVV.Core.VertexBuffer} the vertex data
* @param {Array} indices the list of indices
*/
VVVV.Types.Mesh = function(gl, vertexBuffer, indices) {
/** @member */
this.vertexBuffer = vertexBuffer;
/** @member */
this.indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
/** @member */
this.numIndices = indices.length;
}
/**
* A Layer is the sum of a mesh, textures, render state, shaders and it parameters. Usually, a Layer object is the output
* of a shader node and flows into a Renderer (EX9) or Group (EX9) node.
* @class
* @constructor
*/
VVVV.Types.Layer = function() {
/** @member */
this.mesh = null;
/** An array of WebGlTexture objects */
this.textures = [];
/** @type VVVV.Types.ShaderProgram */
this.shader = null;
/** @member */
this.uniforms = {};
/** @member */
this.uniformNames = []; // to help iterate through this.uniforms
/** @type VVVV.Types.WebGlRenderState */
this.renderState = defaultWebGlRenderState;
/** returns "Layer" */
this.toString = function() {
return "Layer";
}
}
/**
* The WebGlResource Pin Type. Its connectionChangedHandler finds a downstream Renderer (EX9) node, gets its WebGL context, and
* sets it to all upstream WebGL nodes' renderContext members.
* @mixin
* @property {String} typeName "WebGlResource"
* @property {Boolean} reset_on_disconnect true
* @property {Object} connectionChangedHandlers "webglresource" => function
*/
VVVV.PinTypes.WebGlResource = {
typeName: "WebGlResource",
reset_on_disconnect: true,
connectionChangedHandlers: {
"webglresource": function() {
if (this.direction==PinDirection.Input)
return;
var that = this.node
var renderers = that.findDownstreamNodes('Renderer (EX9)');
if (!that.renderContexts)
that.renderContexts = []; // this 'public property' should actually go to the top, right above this.setAsWebGlResourcePin. However, that doesnt work, values got overwritte by nodes of the same type.
if (that.contextChanged==undefined)
that.contextChanged = false;
for (var i=0; i<renderers.length; i++) {
that.contextChanged |= (!that.renderContexts[i] || that.renderContexts[i].canvas.id!=renderers[i].ctxt.id)
that.renderContexts[i] = renderers[i].ctxt;
that.dirty = true;
}
if (that.renderContexts.length!=renderers.length) {
that.renderContexts.length = renderers.length;
that.contextChanged = true;
that.dirty = true;
}
function isWebGlPinType(typeName) {
return typeName=="WebGlResource" || typeName=="WebGlTexture";
}
if (!that.isSubpatch) {
_(that.inputPins).each(function(p) {
var fromPin;
p.markPinAsChanged();
if (that.nodename!="Renderer (EX9)") {
if (p.isConnected()) {
if (p.links.length>0)
fromPin = p.links[0].fromPin
else if (p.masterPin.links[0].fromPin.links.length>0)
fromPin = p.masterPin.links[0].fromPin;
if (fromPin && isWebGlPinType(fromPin.typeName))
fromPin.connectionChanged();
}
}
});
}
if (this.masterPin && isWebGlPinType(this.masterPin.typeName))
this.masterPin.connectionChanged();
}
},
defaultValue: function() {
return new VVVV.Types.Layer();
}
}
/**
* The WebGLTexture Pin Type, has the same connectionChangedHandler as {@link VVVV.PinTypes.WebGlResource}.
* @mixin
* @property {String} typeName "WebGlTexture"
* @property {Boolean} reset_on_disconnect true
* @property {Object} connectionChangedHandlers "webglresource" => function
* @property {Function} defaultValue a function returning {@link VVVV.DefaultTexture}
*/
VVVV.PinTypes.WebGlTexture = {
typeName: "WebGlTexture",
reset_on_disconnect: true,
connectionChangedHandlers: {
"webglresource": VVVV.PinTypes.WebGlResource.connectionChangedHandlers["webglresource"]
},
defaultValue: function() {
return VVVV.DefaultTexture;
}
}
var defaultWebGlRenderState = new VVVV.Types.WebGlRenderState();
/**
* The WebGlRenderState Pin Type
* @mixin
* @property {String} typeName "WebGlRenderState"
* @property {Boolean} reset_on_disconnect true
* @property {Function} defaultValue a function returning the default {@link VVVV.Types.WebGlRenderState} object
*/
VVVV.PinTypes.WebGlRenderState = {
typeName: "WebGlRenderState",
reset_on_disconnect: true,
defaultValue: function() {
return defaultWebGlRenderState;
}
}
/**
* Constant representing a WebGl context's default texture
* @const
*/
VVVV.DefaultTexture = "Empty Texture";
/**
* The ShaderProgram class holds vertex shader and fragment shader code and provides methods to extract uniform/attribute positions
* and to create the shader program in the WebGl context
*/
VVVV.Types.ShaderProgram = function() {
this.uniformSpecs = {};
this.attributeSpecs = {};
this.attribSemanticMap = {};
this.uniformSemanticMap = {};
var vertexShaderCode = '';
var fragmentShaderCode = '';
var vertexShader;
var fragmentShader;
this.isSetup = false;
this.shaderProgram = undefined;
this.log = '';
var thatShader = this;
this.extractSemantics = function(code) {
thatShader.attributeSpecs = {};
thatShader.attribSemanticMap = {};
thatShader.uniformSpecs = {};
thatShader.uniformSemanticMap = {};
var pattern = /(uniform|attribute) ([a-zA-Z]+)([0-9xD]*) ([a-zA-Z0-9_]+)( : ([A-Z0-9]+))?( = \{?([^;\}]+)\}?)?;/g;
var match;
while ((match = pattern.exec(code))) {
if (match[1]=='attribute' && !(thatShader.attributeSpecs[match[4]])) {
thatShader.attributeSpecs[match[4]] = {
varname: match[4],
semantic: match[6],
position: 0
};
if (match[6]!=undefined)
thatShader.attribSemanticMap[match[6]] = match[4];
}
else if (match[1]=='uniform' && !thatShader.uniformSpecs[match[4]]) {
var dimension = match[3]=='' ? 1 : match[3];
var uniformSpec = {
varname: match[4],
semantic: match[6],
position: 0,
type: match[2],
defaultValue: match[8],
dimension: dimension
}
thatShader.uniformSpecs[match[4]] = uniformSpec;
if (match[6]!=undefined)
thatShader.uniformSemanticMap[match[6]] = match[4];
}
}
}
this.setVertexShader = function(code) {
vertexShaderCode = code;
//extractSemantics(code);
}
this.setFragmentShader =function(code) {
fragmentShaderCode = code;
//extractSemantics(code);
}
this.setup = function(gl) {
this.log = '';
vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderCode.replace(/((uniform|attribute) [a-zA-Z0-9]+ [a-zA-Z0-9_]+)[^;]*/g, '$1'));
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
this.log = gl.getShaderInfoLog(vertexShader);
console.log(this.log);
}
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderCode.replace(/((uniform|attribute) [a-zA-Z0-9]+ [a-zA-Z0-9_]+)[^;]*/g, '$1'));
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
this.log = gl.getShaderInfoLog(fragmentShader);
console.log(this.log);
}
this.shaderProgram = gl.createProgram();
gl.attachShader(this.shaderProgram, vertexShader);
gl.attachShader(this.shaderProgram, fragmentShader);
gl.linkProgram(this.shaderProgram);
if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
console.log("Could not initialise shaders");
}
_(this.attributeSpecs).each(function(aSpec) {
aSpec.position = gl.getAttribLocation(thatShader.shaderProgram, aSpec.varname);
});
_(this.uniformSpecs).each(function(uSpec) {
uSpec.position = gl.getUniformLocation(thatShader.shaderProgram, uSpec.varname);
});
this.isSetup = true;
return this.log=='';
}
}
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: FileTexture (EX9.Texture)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.FileTexture = function(id, graph) {
this.constructor(id, "FileTexture (EX9.Texture)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['Always loads in background', 'No reload pin', 'No preload pin (preloading handled by browser)', 'No up and running pin', 'No texture info outputs']
};
this.auto_evaluate = false;
var filenamePin = this.addInputPin("Filename", [""], VVVV.PinTypes.String);
var outputPin = this.addOutputPin("Texture Out", [], VVVV.PinTypes.WebGlTexture);
var typeIn = this.addInvisiblePin("Type", ["Texture"], VVVV.PinTypes.Enum);
typeIn.enumOptions = ["Texture", "Cube Texture"];
var textures = [];
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
if (this.contextChanged) {
for (var i=0; i<textures.length; i++) {
textures[i].context.deleteTexture(textures[i]);
}
textures = [];
}
if (filenamePin.pinIsChanged() || typeIn.pinIsChanged() || this.contextChanged) {
var type = typeIn.getValue(0);
var maxSize = this.getMaxInputSliceCount();
for (var i=0; i<maxSize; i++) {
var filename = VVVV.Helpers.prepareFilePath(filenamePin.getValue(i), this.parentPatch);
if (filename.indexOf('http://')===0 && VVVV.ImageProxyPrefix!==undefined)
filename = VVVV.ImageProxyPrefix+encodeURI(filename);
textures[i] = gl.createTexture();
textures[i].context = gl;
if (type=="Texture") {
textures[i].image = new Image();
textures[i].image.onload = (function(j) {
return function() { // this is to create a new scope within the loop. see "javascript closure in for loops" http://www.mennovanslooten.nl/blog/post/62
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
gl.bindTexture(gl.TEXTURE_2D, textures[j]);
//gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[j].image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
outputPin.setValue(j, textures[j]);
}
})(i);
textures[i].image.src = filename;
}
else if (type=="Cube Texture") {
textures[i].image = new Image();
textures[i].image.onload = (function(j) {
return function() {
var faces = [
{face: gl.TEXTURE_CUBE_MAP_POSITIVE_X, offset: [2, 1]},
{face: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, offset: [0, 1]},
{face: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, offset: [1, 0]},
{face: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, offset: [1, 2]},
{face: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, offset: [1, 1]},
{face: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, offset: [3, 1]}];
gl.bindTexture(gl.TEXTURE_CUBE_MAP, textures[j]);
var $texcanvas = $('<canvas style="display:none" width="'+(this.width/4)+'" height="'+(this.height/3)+'"></canvas>');
$('body').append($texcanvas);
var ctx = $texcanvas.get(0).getContext("2d");
for (var k=0; k<6; k++) {
ctx.save();
ctx.translate(-this.width/4 * faces[k].offset[0], -this.height/3 * faces[k].offset[1]);
ctx.drawImage(this, 0, 0);
gl.texImage2D(faces[k].face, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, $texcanvas.get(0));
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
ctx.restore();
}
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
$texcanvas.remove();
outputPin.setValue(j, textures[j]);
}
})(i);
textures[i].image.src = filename;
}
outputPin.setValue(i, VVVV.defaultTexture);
}
outputPin.setSliceCount(maxSize);
}
this.contextChanged = false;
}
this.destroy = function() {
for (var i=0; i<textures.length; i++) {
textures[i].context.deleteTexture(textures[i]);
}
}
}
VVVV.Nodes.FileTexture.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: DX9Texture (EX9.Texture)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.DX9Texture = function(id, graph) {
this.constructor(id, "DX9Texture (EX9.Texture)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['Using WebGL renderer as source doesnt work correctly in Chrome.']
};
var sourceIn = this.addInputPin("Source", [], VVVV.PinTypes.WebGlResource);
var outputOut = this.addOutputPin("Texture Out", [], VVVV.PinTypes.WebGlTexture);
var texture;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
if (this.contextChanged && texture) {
texture.context.deleteTexture(texture);
texture = undefined;
}
if (sourceIn.isConnected()) {
var source = sourceIn.getValue(0);
if (!source)
return;
if ( (source.width & (source.width-1)) != 0 || (source.height & (source.height-1)) != 0)
console.log("Warning: Source renderer's width/height is not a power of 2. DX9Texture will most likely not work.");
if (source instanceof WebGLTexture) {
outputOut.setValue(0, source);
}
else {
if (texture==undefined) {
texture = gl.createTexture();
texture.context = gl;
}
gl.bindTexture(gl.TEXTURE_2D, texture);
//gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.bindTexture(gl.TEXTURE_2D, null);
outputOut.setValue(0, texture);
}
}
else {
delete texture;
gl.deleteTexture(texture);
outputOut.setValue(0, undefined);
}
this.contextChanged = false;
}
this.destroy = function() {
if (texture)
texture.context.deleteTexture(texture);
}
}
VVVV.Nodes.DX9Texture.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: VideoTexture (EX9.Texture VMR9)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.VideoTexture = function(id, graph) {
this.constructor(id, "VideoTexture (EX9.Texture VMR9)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['Only supports power-of-2 sized videos', 'Has no output pins for meta data']
};
var sourceIn = this.addInputPin("Video", [], this);
var outputOut = this.addOutputPin("Texture Out", [], VVVV.PinTypes.WebGlTexture);
var texture;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
if (sourceIn.isConnected()) {
var source = sourceIn.getValue(0);
if ( (source.videoWidth & (source.videoWidth-1)) != 0 || (source.videoHeight & (source.videoHeight-1)) != 0)
console.log("Warning: Video width/height is not a power of 2. VideoTexture will most likely not work.");
if (texture==undefined || this.contextChanged)
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
//gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
outputOut.setValue(0, texture);
this.contextChanged = false;
}
else {
delete texture;
gl.deleteTexture(texture);
outputOut.setValue(0, undefined);
}
}
}
VVVV.Nodes.VideoTexture.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: VertexBuffer(EX9.Geometry Join)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.VertexBufferJoin = function(id, graph) {
this.constructor(id, "VertexBuffer (EX9.Geometry Join)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var posIn = this.addInputPin("Position XYZ", [0.0, 0.0, 0.0], VVVV.PinTypes.Value);
var normalIn = this.addInputPin("Normal XYZ", [0.0, 0.0, 0.0], VVVV.PinTypes.Value);
var texCoord0In = this.addInputPin("Texture Coordinate 0 XY", [0.0, 0.0], VVVV.PinTypes.Value);
var applyIn = this.addInputPin("Apply", [1], VVVV.PinTypes.Value);
var vbOut = this.addOutputPin("Vertex Buffer", [], VVVV.PinTypes.WebGlResource);
var vertexBuffer = null;
this.evaluate = function() {
var gl = this.renderContexts[0];
if (!gl)
return;
if (applyIn.getValue(0)>=.5) {
var positions = [];
var texCoords0 = [];
var normals = [];
for (var i=0; i<this.getMaxInputSliceCount(); i++) { // this is most likely wrong, because texcoord only has 2 elements, which might cause some shift glitch
positions[i] = parseFloat(posIn.getValue(i));
texCoords0[i] = parseFloat(texCoord0In.getValue(i));
normals[i] = parseFloat(normalIn.getValue(i));
}
vertexBuffer = new VVVV.Types.VertexBuffer(gl, positions);
vertexBuffer.setSubBuffer('TEXCOORD0', 2, texCoords0);
vertexBuffer.setSubBuffer('NORMAL', 3, normals);
vertexBuffer.create();
vbOut.setValue(0, vertexBuffer);
}
}
}
VVVV.Nodes.VertexBufferJoin.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Mesh (EX9.Geometry Join)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.MeshJoin = function(id, graph) {
this.constructor(id, "Mesh (EX9.Geometry Join)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var vbIn = this.addInputPin("Vertex Buffer", [], VVVV.PinTypes.WebGlResource);
var indicesIn = this.addInputPin("Indices", [0], VVVV.PinTypes.Value);
var applyIn = this.addInputPin("Apply", [1], VVVV.PinTypes.Value);
var meshOut = this.addOutputPin("Mesh", [], VVVV.PinTypes.WebGlResource);
var mesh = null;
this.evaluate = function() {
var gl = this.renderContexts[0];
if (!gl)
return;
if (applyIn.getValue(0)>=.5) {
if (vbIn.isConnected()) {
mesh = new VVVV.Types.Mesh(gl, vbIn.getValue(0), indicesIn.values);
meshOut.setValue(0, mesh);
}
else {
meshOut.setValue(0, undefined);
delete mesh;
}
}
}
}
VVVV.Nodes.MeshJoin.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Grid (EX9.Geometry)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.Grid = function(id, graph) {
this.constructor(id, "Grid (EX9.Geometry)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var xIn = this.addInputPin("Resolution X", [2], VVVV.PinTypes.Value);
var yIn = this.addInputPin("Resolution Y", [2], VVVV.PinTypes.Value);
var meshOut = this.addOutputPin("Mesh", [], VVVV.PinTypes.WebGlResource);
var mesh = null;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
var xRes = parseInt(xIn.getValue(0));
var yRes = parseInt(yIn.getValue(0));
var vertices = [];
var normals = [];
var texCoords = [];
var index = 0;
for (var y=0; y<yRes; y++) {
for (var x=0; x<xRes; x++) {
vertices.push(parseFloat(x)/(xRes-1)-0.5);
vertices.push(0.5-parseFloat(y)/(yRes-1));
vertices.push(0.0);
index++;
normals.push(0);
normals.push(0);
normals.push(1);
texCoords.push(parseFloat(x)/(xRes-1));
texCoords.push(parseFloat(y)/(yRes-1));
}
}
var vertexBuffer = new VVVV.Types.VertexBuffer(gl, vertices);
vertexBuffer.setSubBuffer('TEXCOORD0', 2, texCoords);
vertexBuffer.setSubBuffer('NORMAL', 3, normals);
vertexBuffer.create();
var indices = [];
for (var y=0; y<yRes-1; y++) {
for (var x=0; x<xRes-1; x++) {
var refP = x+xRes*y;
indices.push(refP);
indices.push(refP+1);
indices.push(refP+xRes+1);
indices.push(refP+xRes+1);
indices.push(refP+xRes);
indices.push(refP);
}
}
mesh = new VVVV.Types.Mesh(gl, vertexBuffer, indices);
meshOut.setValue(0, mesh);
}
}
VVVV.Nodes.Grid.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Sphere (EX9.Geometry)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.Sphere = function(id, graph) {
this.constructor(id, "Sphere (EX9.Geometry)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var rIn = this.addInputPin("Radius", [0.5], VVVV.PinTypes.Value);
var xIn = this.addInputPin("Resolution X", [15], VVVV.PinTypes.Value);
var yIn = this.addInputPin("Resolution Y", [15], VVVV.PinTypes.Value);
var meshOut = this.addOutputPin("Mesh", [], VVVV.PinTypes.WebGlResource);
var mesh = null;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
var xRes = parseInt(xIn.getValue(0));
var yRes = parseInt(yIn.getValue(0));
var radius = parseFloat(rIn.getValue(0));
var vertices = [];
var normals = [];
var texCoords = [];
for (var y=0; y<yRes+1; y++) {
var yPos = Math.cos(-parseFloat(y)/yRes*Math.PI);
for (var x=0; x<xRes; x++) {
var xPos = Math.cos(parseFloat(x)/xRes*2*Math.PI)*Math.cos(Math.asin(yPos));
var zPos = Math.sin(parseFloat(x)/xRes*2*Math.PI)*Math.cos(Math.asin(yPos));
vertices.push(xPos*radius);
vertices.push(yPos*radius);
vertices.push(zPos*radius);
normals.push(xPos);
normals.push(yPos);
normals.push(zPos);
texCoords.push(parseFloat(x)/(xRes));
texCoords.push(parseFloat(y)/(yRes));
}
}
var vertexBuffer = new VVVV.Types.VertexBuffer(gl, vertices);
vertexBuffer.setSubBuffer('TEXCOORD0', 2, texCoords);
vertexBuffer.setSubBuffer('NORMAL', 3, normals);
vertexBuffer.create();
var indices = [];
for (var y=0; y<yRes; y++) {
for (var x=0; x<xRes; x++) {
var yOff = xRes*y;
var refP = x+yOff;
indices.push(refP);
indices.push((refP+1)%xRes+yOff);
indices.push((refP+1)%xRes+xRes+yOff);
indices.push((refP+1)%xRes+xRes+yOff);
indices.push(refP+xRes);
indices.push(refP);
}
}
mesh = new VVVV.Types.Mesh(gl, vertexBuffer, indices);
meshOut.setValue(0, mesh);
}
}
VVVV.Nodes.Sphere.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Cylinder (EX9.Geometry)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.Cylinder = function(id, graph) {
this.constructor(id, "Cylinder (EX9.Geometry)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var r1In = this.addInputPin("Radius 1", [0.5], VVVV.PinTypes.Value);
var r2In = this.addInputPin("Radius 2", [0.5], VVVV.PinTypes.Value);
var lIn = this.addInputPin("Length", [1.0], VVVV.PinTypes.Value);
var cyclesIn = this.addInputPin("Cycles", [1.0], VVVV.PinTypes.Value);
var capsIn = this.addInputPin("Caps", [1], VVVV.PinTypes.Value);
var xIn = this.addInputPin("Resolution X", [15], VVVV.PinTypes.Value);
var yIn = this.addInputPin("Resolution Y", [1], VVVV.PinTypes.Value);
var meshOut = this.addOutputPin("Mesh", [], VVVV.PinTypes.WebGlResource);
var mesh = null;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
var xRes = parseInt(xIn.getValue(0));
var yRes = parseInt(yIn.getValue(0));
var radius1 = parseFloat(r1In.getValue(0));
var radius2 = parseFloat(r2In.getValue(0));
var length = parseFloat(lIn.getValue(0));
var cycles = parseFloat(cyclesIn.getValue(0));
var vertices = [];
var normals = [];
var texCoords = [];
// cap vertices ...
vertices.push(0.0);
vertices.push(length/2);
vertices.push(0.0);
normals.push(0.0);
normals.push(1.0);
normals.push(0.0);
texCoords.push(0.0);
texCoords.push(0.0);
vertices.push(0.0);
vertices.push(-length/2);
vertices.push(0.0);
normals.push(0.0);
normals.push(-1.0);
normals.push(0.0);
texCoords.push(0.0);
texCoords.push(0.0);
// other vertices ...
for (var y=0; y<yRes+1; y++) {
var n = parseFloat(y)/yRes
var yPos = (n - 0.5) * -length;
for (var x=0; x<xRes+1; x++) {
var xPos = Math.cos((parseFloat(x)/xRes*2*Math.PI * cycles - Math.PI*cycles -Math.PI/2));
var zPos = Math.sin((parseFloat(x)/xRes*2*Math.PI * cycles - Math.PI*cycles -Math.PI/2));
var r = n*radius2 + (1-n)*radius1;
vertices.push(xPos*r);
vertices.push(yPos);
vertices.push(zPos*r);
normals.push(xPos);
normals.push(0.0);
normals.push(zPos);
texCoords.push(parseFloat(x)/(xRes));
texCoords.push(parseFloat(y)/(yRes));
}
}
var vertexBuffer = new VVVV.Types.VertexBuffer(gl, vertices);
vertexBuffer.setSubBuffer('TEXCOORD0', 2, texCoords);
vertexBuffer.setSubBuffer('NORMAL', 3, normals);
vertexBuffer.create();
var indices = [];
// caps indices ...
if (capsIn.getValue(0)>.5) {
for (var n=0; n<2; n++) {
for (var x=0; x<xRes; x++) {
indices.push(n);
indices.push(2+x+n+(n*yRes*(xRes+1)));
indices.push(2+x+(1-n)+(n*yRes*(xRes+1)));
}
}
}
// other indices ...
for (var y=0; y<yRes; y++) {
for (var x=0; x<xRes; x++) {
var refP = x+(xRes+1)*y + 2;
indices.push(refP);
indices.push(refP+1);
indices.push(refP+xRes+2);
indices.push(refP+xRes+2);
indices.push(refP+xRes+1);
indices.push(refP);
}
}
mesh = new VVVV.Types.Mesh(gl, vertexBuffer, indices);
meshOut.setValue(0, mesh);
}
}
VVVV.Nodes.Cylinder.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Blend (EX9.RenderState Advanced)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.BlendWebGLAdvanced = function(id, graph) {
this.constructor(id, "Blend (EX9.RenderState Advanced)", graph);
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var renderStateIn = this.addInputPin("Render State In", [], VVVV.PinTypes.WebGlRenderState);
var alphaBlendingIn = this.addInputPin("Alpha Blending", [1], VVVV.PinTypes.Value);
var srcModeIn = this.addInputPin("Source Blend Mode", ['SrcAlpha'], VVVV.PinTypes.Enum);
var destModeIn = this.addInputPin("Destination Blend Mode", ['SrcAlpha'], VVVV.PinTypes.Enum);
srcModeIn.enumOptions = destModeIn.enumOptions = ['One', 'Zero', 'SrcAlpha', 'InvSrcAlpha', 'DestAlpha', 'InvDestAlpha', 'SrcColor', 'InvSrcColor', 'DestColor', 'InvDestColor'];
var renderStateOut = this.addOutputPin("Render State Out", [], VVVV.PinTypes.WebGlRenderState);
var renderStates = [];
function convertToWebGLBlendFactor(VVVVFactor) {
switch (VVVVFactor) {
case 'One': return "ONE";
case 'Zero': return "ZERO";
case 'SrcAlpha': return "SRC_ALPHA";
case 'InvSrcAlpha': return "ONE_MINUS_SRC_ALPHA";
case 'DestAlpha': return "DST_ALPHA";
case 'InvDestAlpha': return "ONE_MINUS_DST_ALPHA";
case 'SrcColor': return "SRC_COLOR";
case 'InvSrcColor': return "ONE_MINUS_SRC_COLOR";
case 'DestColor': return "DST_COLOR";
case 'InvDestColor': return "ONE_MINUS_DST_COLOR";
}
return null;
}
this.evaluate = function() {
var maxSpreadSize = this.getMaxInputSliceCount();
for (var i=0; i<maxSpreadSize; i++) {
if (renderStates[i]==undefined) {
renderStates[i] = new VVVV.Types.WebGlRenderState();
}
renderStates[i].copy_attributes(renderStateIn.getValue(i));
renderStates[i].alphaBlending = parseFloat(alphaBlendingIn.getValue(i))>.5;
renderStates[i].srcBlendMode = convertToWebGLBlendFactor(srcModeIn.getValue(i));
renderStates[i].destBlendMode = convertToWebGLBlendFactor(destModeIn.getValue(i));
renderStateOut.setValue(i, renderStates[i]);
}
renderStateOut.setSliceCount(maxSpreadSize);
}
}
VVVV.Nodes.BlendWebGLAdvanced.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Blend (EX9.RenderState)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.BlendWebGL = function(id, graph) {
this.constructor(id, "Blend (EX9.RenderState)", graph);
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['results differ from VVVV', 'Multiply mode not supported']
};
var renderStateIn = this.addInputPin("Render State In", [], VVVV.PinTypes.WebGlRenderState);
var drawModeIn = this.addInputPin("Draw Mode", ["Blend"], VVVV.PinTypes.Enum);
drawModeIn.enumOptions = ['Add', 'Multiply', 'Blend', 'ColorAsAlphaAdd', 'ColorAsAlphaBlend'];
var renderStateOut = this.addOutputPin("Render State Out", [], VVVV.PinTypes.WebGlRenderState);
var renderStates = [];
this.evaluate = function() {
var maxSpreadSize = this.getMaxInputSliceCount();
for (var i=0; i<maxSpreadSize; i++) {
if (renderStates[i]==undefined) {
renderStates[i] = new VVVV.Types.WebGlRenderState();
}
renderStates[i].copy_attributes(renderStateIn.getValue(i));
switch (drawModeIn.getValue(i)) {
case "Add":
renderStates[i].srcBlendMode = "SRC_ALPHA";
renderStates[i].destBlendMode = "ONE";
break;
case "Multiply":
console.log("Multiply Blend Mode not supported (or we just missed it)");
case "Blend":
renderStates[i].srcBlendMode = "SRC_ALPHA";
renderStates[i].destBlendMode = "ONE_MINUS_SRC_ALPHA";
break;
case "ColorAsAlphaAdd":
renderStates[i].srcBlendMode = "SRC_COLOR";
renderStates[i].destBlendMode = "ONE";
break;
case "ColorAsAlphaBlend":
renderStates[i].srcBlendMode = "SRC_COLOR";
renderStates[i].destBlendMode = "ONE_MINUS_SRC_COLOR";
break;
}
renderStateOut.setValue(i, renderStates[i]);
}
renderStateOut.setSliceCount(maxSpreadSize);
}
}
VVVV.Nodes.BlendWebGL.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Fill (EX9.RenderState)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.FillWebGL = function(id, graph) {
this.constructor(id, "Fill (EX9.RenderState)", graph);
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['does not actually draw wireframe, because this is not supported in WebGL, but makes renderer use gl.LINE instead of gl.TRIANGLES when drawing']
};
var renderStateIn = this.addInputPin("Render State In", [], VVVV.PinTypes.WebGlRenderState);
var fillModeIn = this.addInputPin("Fill Mode", ["Solid"], VVVV.PinTypes.Enum);
fillModeIn.enumOptions = ['Point', 'Solid', 'WireFrame'];
var renderStateOut = this.addOutputPin("Render State Out", [], VVVV.PinTypes.WebGlRenderState);
var renderStates = [];
this.evaluate = function() {
var maxSpreadSize = this.getMaxInputSliceCount();
for (var i=0; i<maxSpreadSize; i++) {
if (renderStates[i]==undefined) {
renderStates[i] = new VVVV.Types.WebGlRenderState();
}
renderStates[i].copy_attributes(renderStateIn.getValue(i));
switch (fillModeIn.getValue(i)) {
case 'Point':
renderStates[i].polygonDrawMode = "POINTS";
break;
case 'Solid':
renderStates[i].polygonDrawMode = "TRIANGLES";
break;
case 'WireFrame':
renderStates[i].polygonDrawMode = "LINES";
}
renderStateOut.setValue(i, renderStates[i]);
}
renderStateOut.setSliceCount(maxSpreadSize);
}
}
VVVV.Nodes.FillWebGL.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: ZWriteEnable (EX9.RenderState)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.ZWriteEnableWebGL = function(id, graph) {
this.constructor(id, "ZWriteEnable (EX9.RenderState)", graph);
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var renderStateIn = this.addInputPin("Render State In", [], VVVV.PinTypes.WebGlRenderState);
var enableZWriteIn = this.addInputPin("ZWrite Enable", [1], VVVV.PinTypes.Value);
var depthFuncIn = this.addInputPin("Compare Function", ['Always'], VVVV.PinTypes.Enum);
depthFuncIn.enumOptions = ['Never', 'Less', 'LessEqual', 'Equal', 'NotEqual', 'Greater', 'GreaterEqual', 'Always'];
var biasIn = this.addInputPin("Depth Bias", [0.0], VVVV.PinTypes.Value);
var renderStateOut = this.addOutputPin("Render State Out", [], VVVV.PinTypes.WebGlRenderState);
var renderStates = [];
function convertToWebGLDepthFunc(VVVVFunc) {
switch (VVVVFunc) {
case 'Never': return "NEVER";
case 'Less': return "LESS";
case 'LessEqual': return "LEQUAL";
case 'Equal': return "EQUAL";
case 'NotEqual': return "NOTEQUAL";
case 'Greater': return "GREATER";
case 'GreaterEqual': return "GEQUAL";
case 'Always': return "ALWAYS";
}
return null;
}
this.evaluate = function() {
var maxSpreadSize = this.getMaxInputSliceCount();
for (var i=0; i<maxSpreadSize; i++) {
if (renderStates[i]==undefined) {
renderStates[i] = new VVVV.Types.WebGlRenderState();
}
renderStates[i].copy_attributes(renderStateIn.getValue(i));
renderStates[i].enableZWrite = parseFloat(enableZWriteIn.getValue(i))>.5;
renderStates[i].depthFunc = convertToWebGLDepthFunc(depthFuncIn.getValue(i));
renderStates[i].depthOffset = parseFloat(biasIn.getValue(0));
renderStateOut.setValue(i, renderStates[i]);
}
renderStateOut.setSliceCount(maxSpreadSize);
}
}
VVVV.Nodes.ZWriteEnableWebGL.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: GenericShader (EX9.Effect)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.GenericShader = function(id, graph) {
this.constructor(id, "GenericShader (EX9.Effect)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
this.shaderFile = '';
var renderStateIn = this.addInputPin("Render State", [], VVVV.PinTypes.WebGlRenderState);
var meshIn = this.addInputPin("Mesh", [], VVVV.PinTypes.WebGlResource);
var transformIn = this.addInputPin("Transform", [], VVVV.PinTypes.Transform);
var techniqueIn = this.addInputPin("Technique", [''], VVVV.PinTypes.Enum);
techniqueIn.enumOptions = [''];
var layerOut = this.addOutputPin("Layer", [], VVVV.PinTypes.WebGlResource);
var layers = [];
var mesh = null;
var shader = null;
var shaderCode;
var shaderCodeChanged = false;
var shaderPins = [];
var initialized = false;
var thatNode = this;
this.initialize = function() {
// add the pins which have already been added (by the patch XML) to the shaderPins array
var defaultPins = ["Render State", "Mesh", "Transform", "Technique"];
_(thatNode.inputPins).each(function(p) {
if (defaultPins.indexOf(p.pinname)<0) {
p.unvalidated = true;
shaderPins.push(p);
}
})
if (VVVV.ShaderCodeResources[thatNode.shaderFile]==undefined) {
VVVV.ShaderCodeResources[thatNode.shaderFile] = new VVVV.Types.ShaderCodeResource();
VVVV.ShaderCodeResources[thatNode.shaderFile].addRelatedNode(thatNode);
$.ajax({
url: VVVV.Helpers.prepareFilePath(thatNode.shaderFile, thatNode.parentPatch),
async: false,
dataType: 'text',
success: function(response) {
VVVV.ShaderCodeResources[thatNode.shaderFile].setSourceCode(response);
},
error: function() {
console.log('ERROR: Could not load shader file '+thatNode.shaderFile.replace('%VVVV%', VVVV.Root));
VVVV.onNotImplemented('Could not load shader file '+thatNode.shaderFile.replace('%VVVV%', VVVV.Root));
}
});
}
else {
VVVV.ShaderCodeResources[thatNode.shaderFile].addRelatedNode(thatNode);
}
}
this.shaderSourceUpdated = function(sc) {
shaderCode = sc;
if (!shader)
shader = new VVVV.Types.ShaderProgram();
thatNode.addUniformPins();
thatNode.setupShader();
_(thatNode.inputPins).each(function(p) {
p.markPinAsChanged();
})
shaderCodeChanged = true;
this.parentPatch.afterUpdate();
}
this.addUniformPins = function() {
shader.extractSemantics(shaderCode);
// delete pins which have been removed from shader code or where the type changed
var deletables = [];
for (var i=0; i<shaderPins.length; i++) {
if (!shader.uniformSpecs[shaderPins[i].pinname.replace(/ /g, '_')]) {
thatNode.removeInputPin(shaderPins[i].pinname);
deletables.push(i);
}
}
for (var i=0; i<deletables.length; i++) {
shaderPins.splice(deletables[i], 1);
}
// add pins
_(shader.uniformSpecs).each(function(u) {
if (u.semantic=="VIEW" || u.semantic=="PROJECTION" || u.semantic=="WORLD")
return;
var pinType = VVVV.PinTypes.Value;
var defaultValue = [];
switch (u.type) {
case 'mat':
pinType = VVVV.PinTypes.Transform;
break;
case 'samplerCube':
case 'sampler':
pinType = VVVV.PinTypes.WebGlTexture;
break;
default:
if (u.semantic == 'COLOR') {
pinType = VVVV.PinTypes.Color;
defaultValue = ['1.0, 1.0, 1.0, 1.0'];
}
else
defaultValue = [0.0];
if (u.defaultValue) {
if (u.semantic != 'COLOR')
defaultValue = _(u.defaultValue.split(',')).map(function(e) { return parseFloat(e); });
else
defaultValue = [u.defaultValue];
}
}
for (var i=0; i<shaderPins.length; i++) {
if (shaderPins[i].pinname==u.varname.replace(/_/g,' ')) {
shaderPins[i].dimensions = u.dimension;
if (shaderPins[i].unvalidated && !shaderPins[i].isConnected())
shaderPins[i].values = thatNode.defaultPinValues[shaderPins[i].pinname] ? thatNode.defaultPinValues[shaderPins[i].pinname].slice() : defaultValue;
if (shaderPins[i].typeName!=pinType.typeName) {
var values = shaderPins[i].values.slice();
shaderPins[i].setType(pinType);
if (shaderPins[i].unvalidated && (pinType.primitive || shaderPins[i].isConnected()))
shaderPins[i].values = values;
if (shaderPins[i].isConnected() && !shaderPins[i].unvalidated) {
shaderPins[i].connectionChanged();
shaderPins[i].links[0].destroy();
}
}
shaderPins[i].unvalidated = false;
return;
}
}
var pin = thatNode.addInputPin(u.varname.replace(/_/g,' '), defaultValue, pinType);
pin.dimensions = u.dimension;
shaderPins.push(pin);
});
}
this.setupShader = function() {
var technique = techniqueIn.getValue(0);
technique = technique.replace(/^\s*/, '').replace(/\s*$/, '');
var rx = new RegExp(/(vertex_shader|fragment_shader)\{([^\}]+)\}/g);
techniqueIn.enumOptions = [];
var match;
while ((match = rx.exec(shaderCode))!=null) {
techniqueIn.enumOptions = techniqueIn.enumOptions.concat(match[2].replace(/\s/g, '').split(','));
}
techniqueIn.enumOptions = techniqueIn.enumOptions.filter(function(e, index, self) { return self.indexOf(e)===index })
if (techniqueIn.enumOptions.length==0)
techniqueIn.enumOptions.push('');
if (technique=="" || techniqueIn.enumOptions.indexOf(technique)<0) {
technique = techniqueIn.enumOptions[0];
techniqueIn.setValue(0, technique);
}
var vsRegEx = new RegExp('vertex_shader(\\{([a-zA-Z0-9]+,\\s*)*'+technique+'(,\\s*[a-zA-Z0-9]+)*\\})?:([\\s\\S]*?)(vertex_shader|fragment_shader)');
var psRegEx = new RegExp('fragment_shader(\\{([a-zA-Z0-9]+,\\s*)*'+technique+'(,\\s*[a-zA-Z0-9]+)*\\})?:([\\s\\S]*?)(vertex_shader|fragment_shader)');
var match;
match = /STARTOFSTRING((\r?\n|.)*?)(vertex_shader|fragment_shader)/.exec('STARTOFSTRING'+shaderCode);
var varDefs = match[1];
if ((match = vsRegEx.exec(shaderCode+'\nfragment_shader'))==undefined) {
console.log('ERROR: No vertex shader code for technique '+technique+' found');
return;
}
var vertexShaderCode = match[4];
if ((match = psRegEx.exec(shaderCode+'\nfragment_shader'))==undefined) {
console.log('ERROR: No fragment shader code for technique '+technique+' found');
return;
}
var fragmentShaderCode = match[4];
shader.setFragmentShader(varDefs+fragmentShaderCode);
shader.setVertexShader(varDefs+vertexShaderCode);
}
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl || !shader)
return;
if (!shader.isSetup || this.contextChanged || techniqueIn.pinIsChanged()) {
this.setupShader();
if (shader.setup(gl)) {
if (VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode)
VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode.showStatus('success', "Successfully compiled");
}
else {
if (VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode)
VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode.showStatus('error', shader.log);
}
}
// find out input slice count with respect to the input pin dimension, defined by the shader code
var maxSize = 0;
_(this.inputPins).each(function(p) {
var sliceCount = p.getSliceCount();
var pinname = p.pinname.replace(/ /g,'_')
if (shader.uniformSpecs[pinname] && shader.uniformSpecs[pinname].type=='vec' && shader.uniformSpecs[pinname].semantic!='COLOR') {
sliceCount = parseInt(sliceCount/shader.uniformSpecs[pinname].dimension);
}
if (sliceCount > maxSize)
maxSize = sliceCount;
});
if (!meshIn.isConnected() || meshIn.getValue(0)==undefined)
maxSize = 0;
var currentLayerCount = layers.length;
if (this.contextChanged || shaderCodeChanged)
currentLayerCount = 0;
// shorten layers array, if input slice count decreases
if (maxSize<currentLayerCount) {
layers.splice(maxSize, currentLayerCount-maxSize);
}
for (var j=currentLayerCount; j<maxSize; j++) {
layers[j] = new VVVV.Types.Layer();
layers[j].mesh = meshIn.getValue(0);
layers[j].shader = shader;
_(shader.uniformSpecs).each(function(u) {
layers[j].uniformNames.push(u.varname);
layers[j].uniforms[u.varname] = { uniformSpec: u, value: undefined };
});
}
if (meshIn.pinIsChanged()) {
for (var j=0; j<maxSize; j++) {
layers[j].mesh = meshIn.getValue(0);
}
}
for (var j=0; j<maxSize; j++) {
layers[j].shader = shader;
}
for (var i=0; i<shaderPins.length; i++) {
var pinname = shaderPins[i].pinname.replace(/ /g, '_');
if (shaderPins[i].pinIsChanged() || currentLayerCount<maxSize) {
for (var j=0; j<maxSize; j++) {
if (shader.uniformSpecs[pinname].type=='vec') {
if (shader.uniformSpecs[pinname].semantic=='COLOR') {
var rgba = _(shaderPins[i].getValue(j).split(',')).map(function(x) { return parseFloat(x) });
layers[j].uniforms[pinname].value = new Float32Array(rgba);
}
else {
var arr = shaderPins[i].getValue(j, shaderPins[i].dimensions);
layers[j].uniforms[pinname].value = new Float32Array(arr);
}
}
else {
var v = shaderPins[i].getValue(j);
layers[j].uniforms[pinname].value = v;
}
}
}
}
if (renderStateIn.pinIsChanged() || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
if (renderStateIn.isConnected())
layers[i].renderState = renderStateIn.getValue(i);
else
layers[i].renderState = VVVV.DefaultRenderState;
}
}
if (transformIn.pinIsChanged() || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
var transform = this.inputPins["Transform"].getValue(i);
layers[i].uniforms[layers[i].shader.uniformSemanticMap['WORLD']].value = transform;
}
}
this.outputPins["Layer"].setSliceCount(maxSize);
for (var i=0; i<maxSize; i++) {
this.outputPins["Layer"].setValue(i, layers[i]);
}
this.contextChanged = false;
shaderCodeChanged = false;
}
this.openUIWindow = function() {
if (VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode)
VVVV.ShaderCodeResources[thatNode.shaderFile].definingNode.openUIWindow();
}
}
VVVV.Nodes.GenericShader.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Quad (DX9)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.Quad = function(id, graph) {
this.constructor(id, "Quad (DX9)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['No Sampler States', 'No texture coord mapping', 'No enable pin', 'Transprent pixels are discarded by default']
};
this.auto_evaluate = false;
var renderStateIn = this.addInputPin("Render State", [], VVVV.PinTypes.WebGlRenderState);
this.addInputPin("Transform", [], VVVV.PinTypes.Transform);
this.addInputPin("Texture", [], VVVV.PinTypes.WebGlTexture);
this.addInputPin("Texture Transform", [], VVVV.PinTypes.Transform);
this.addInputPin("Color", ["1.0, 1.0, 1.0, 1.0"], VVVV.PinTypes.Color);
var layerOut = this.addOutputPin("Layer", [], VVVV.PinTypes.WebGlResource);
var initialized = false;
var layers = [];
var mesh = null;
var shader = null;
this.evaluate = function() {
if (!this.renderContexts) return;
var gl = this.renderContexts[0];
if (!gl)
return;
if (this.contextChanged) {
var vertices = [
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0
];
var texCoords = [
1.0, 0.0,
0.0, 0.0,
1.0, 1.0,
0.0, 1.0
];
var vertexBuffer = new VVVV.Types.VertexBuffer(gl, vertices);
vertexBuffer.setSubBuffer('TEXCOORD0', 2, texCoords);
vertexBuffer.create();
mesh = new VVVV.Types.Mesh(gl, vertexBuffer, [ 0, 1, 2, 1, 3, 2 ]);
// shaders
var fragmentShaderCode = "#ifdef GL_ES\n";
fragmentShaderCode += "precision highp float;\n";
fragmentShaderCode += "#endif\n";
fragmentShaderCode += "uniform vec4 col : COLOR = {1.0, 1.0, 1.0, 1.0}; varying vec2 vs2psTexCd; uniform sampler2D Samp0; void main(void) { gl_FragColor = col*texture2D(Samp0, vs2psTexCd); if (gl_FragColor.a==0.0) discard; }";
var vertexShaderCode = "attribute vec3 PosO : POSITION; attribute vec2 TexCd : TEXCOORD0; uniform mat4 tW : WORLD; uniform mat4 tV : VIEW; uniform mat4 tP : PROJECTION; uniform mat4 tTex; varying vec2 vs2psTexCd; void main(void) { gl_Position = tP * tV * tW * vec4(PosO, 1.0); vs2psTexCd = (tTex * vec4(TexCd.xy-.5, 0.0, 1.0)).xy+.5; }";
shader = new VVVV.Types.ShaderProgram();
shader.extractSemantics(fragmentShaderCode + vertexShaderCode);
shader.setFragmentShader(fragmentShaderCode);
shader.setVertexShader(vertexShaderCode);
shader.setup(gl);
}
var maxSize = this.getMaxInputSliceCount();
var currentLayerCount = layers.length;
if (this.contextChanged)
currentLayerCount = 0;
// shorten layers array, if input slice count decreases
if (maxSize<currentLayerCount) {
layers.splice(maxSize, currentLayerCount-maxSize);
}
for (var j=currentLayerCount; j<maxSize; j++) {
layers[j] = new VVVV.Types.Layer();
layers[j].mesh = mesh;
layers[j].shader = shader;
_(shader.uniformSpecs).each(function(u) {
layers[j].uniformNames.push(u.varname);
layers[j].uniforms[u.varname] = { uniformSpec: u, value: undefined };
});
}
var colorChanged = this.inputPins["Color"].pinIsChanged();
var transformChanged = this.inputPins["Transform"].pinIsChanged();
var textureChanged = this.inputPins["Texture"].pinIsChanged();
var textureTransformChanged = this.inputPins["Texture Transform"].pinIsChanged();
if (colorChanged || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
var color = this.inputPins["Color"].getValue(i);
var rgba = _(color.split(',')).map(function(x) { return parseFloat(x) });
layers[i].uniforms['col'].value = new Float32Array(rgba);
}
}
if (renderStateIn.pinIsChanged() || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
if (renderStateIn.isConnected())
layers[i].renderState = renderStateIn.getValue(i);
else
layers[i].renderState = VVVV.DefaultRenderState;
}
}
if (transformChanged || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
var transform = this.inputPins["Transform"].getValue(i);
layers[i].uniforms[layers[i].shader.uniformSemanticMap['WORLD']].value = transform;
}
}
if (textureChanged || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
layers[i].uniforms["Samp0"].value = this.inputPins["Texture"].getValue(i);
}
}
if (textureTransformChanged || currentLayerCount<maxSize) {
for (var i=0; i<maxSize; i++) {
var transform = this.inputPins["Texture Transform"].getValue(i);
layers[i].uniforms["tTex"].value = transform;
}
}
this.outputPins["Layer"].setSliceCount(maxSize);
for (var i=0; i<maxSize; i++) {
this.outputPins["Layer"].setValue(i, layers[i]);
}
this.contextChanged = false;
}
}
VVVV.Nodes.Quad.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Group (EX9)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.Group = function(id, graph) {
this.constructor(id, "Group (EX9)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: []
};
var layerIns = [];
var enableIn = this.addInputPin("Enabled", [1], VVVV.PinTypes.Value);
var layerCountIn = this.addInvisiblePin("Layer Template Count", [2], VVVV.PinTypes.Value);
var layerOut = this.addOutputPin("Layer", [], VVVV.PinTypes.WebGlResource);
this.initialize = function() {
var layerCount = Math.max(2, layerCountIn.getValue(0));
VVVV.Helpers.dynamicPins(this, layerIns, layerCount, function(i) {
return this.addInputPin("Layer "+(i+1), [], VVVV.PinTypes.WebGlResource);
});
}
this.evaluate = function() {
if (layerCountIn.pinIsChanged()) {
this.initialize();
}
var outSliceIdx = 0;
if(enableIn.getValue(0) > .5) {
for(var i = 0; i < layerIns.length; i++) {
for(var j = 0; j < layerIns[i].getSliceCount(); j++) {
layerOut.setValue(outSliceIdx++, layerIns[i].getValue(j));
}
}
}
layerOut.setSliceCount(outSliceIdx);
}
}
VVVV.Nodes.Group.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: Renderer (EX9)
Author(s): Matthias Zauner
Original Node Author(s): VVVV Group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.RendererWebGL = function(id, graph) {
this.constructor(id, "Renderer (EX9)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['VVVV Group'],
credits: [],
compatibility_issues: ['Disabling Clear doesn\'t work in Chrome', 'No Fullscreen', 'No Enable Pin', 'No Aspect Ration and Viewport transform', 'No mouse output']
};
this.addInputPin("Layers", [], VVVV.PinTypes.WebGlResource);
var clearIn = this.addInputPin("Clear", [1], VVVV.PinTypes.Value);
var bgColIn = this.addInputPin("Background Color", ['0.0, 0.0, 0.0, 1.0'], VVVV.PinTypes.Color);
var bufferWidthIn = this.addInputPin("Backbuffer Width", [0], VVVV.PinTypes.Value);
var bufferHeightIn = this.addInputPin("Backbuffer Height", [0], VVVV.PinTypes.Value);
var viewIn = this.addInputPin("View", [], VVVV.PinTypes.Transform);
var projIn = this.addInputPin("Projection", [], VVVV.PinTypes.Transform);
var enableDepthBufIn = this.addInvisiblePin("Windowed Depthbuffer Format", ['NONE'], VVVV.PinTypes.Enum);
enableDepthBufIn.enumOptions = ['NONE', 'DX16'];
var bufferWidthOut = this.addOutputPin("Actual Backbuffer Width", [0.0], VVVV.PinTypes.Value);
var bufferHeightOut = this.addOutputPin("Actual Backbuffer Height", [0.0], VVVV.PinTypes.Value);
var ex9Out = this.addOutputPin("EX9 Out", [], VVVV.PinTypes.WebGlResource);
var width = 0.0;
var height = 0.0;
var pMatrix;
var vMatrix;
var canvas;
this.ctxt = undefined; // the renderer's active context. might be the canvas context, or the context of a connected downstream renderer
var canvasCtxt = undefined; // the context of the canvas which is connected to the renderer
var gl; // just a convenience variable for keeping the lines short
var bbufFramebuffer;
var bbufTexture;
function attachMouseEvents() {
$(canvas).detach('mousemove');
$(canvas).detach('mousedown');
$(canvas).detach('mouseup');
VVVV.MousePositions[canvas.id] = {'x': 0.0, 'y': 0.0, 'wheel': 0.0, 'lb': 0.0, 'mb': 0.0, 'rb': 0.0};
$(canvas).mousemove(function(e) {
var x = (e.pageX - $(this).offset().left) * 2 / $(this).width() - 1;
var y = -((e.pageY - $(this).offset().top) * 2 / $(this).height() - 1);
VVVV.MousePositions['_all'].x = x;
VVVV.MousePositions['_all'].y = y;
VVVV.MousePositions[canvas.id].x = x;
VVVV.MousePositions[canvas.id].y = y;
});
$(canvas).bind('mousewheel', function(e) {
var delta = e.originalEvent.wheelDelta/120;
VVVV.MousePositions[canvas.id].wheel += delta;
VVVV.MousePositions['_all'].wheel += delta;
});
$(canvas).bind('DOMMouseScroll', function(e) {
var delta = -e.originalEvent.detail/3;
VVVV.MousePositions[canvas.id].wheel += delta;
VVVV.MousePositions['_all'].wheel += delta;
})
$(canvas).mousedown(function(e) {
switch (e.which) {
case 1: VVVV.MousePositions['_all'].lb = 1; VVVV.MousePositions[canvas.id].lb = 1; break;
case 2: VVVV.MousePositions['_all'].mb = 1; VVVV.MousePositions[canvas.id].mb = 1; break;
case 3: VVVV.MousePositions['_all'].rb = 1; VVVV.MousePositions[canvas.id].rb = 1; break;
}
});
$(canvas).mouseup(function(e) {
switch (e.which) {
case 1: VVVV.MousePositions['_all'].lb = 0; VVVV.MousePositions[canvas.id].lb = 0; break;
case 2: VVVV.MousePositions['_all'].mb = 0; VVVV.MousePositions[canvas.id].mb = 0; break;
case 3: VVVV.MousePositions['_all'].rb = 0; VVVV.MousePositions[canvas.id].rb = 0; break;
}
});
}
this.getContexts = function() {
if (!this.invisiblePins["Descriptive Name"])
return;
var selector = this.invisiblePins["Descriptive Name"].getValue(0);
var targetElement = $(selector).get(0);
if (!targetElement || targetElement.nodeName!='CANVAS') {
var w = parseInt(bufferWidthIn.getValue(0));
var h = parseInt(bufferHeightIn.getValue(0));
w = w > 0 ? w : 512;
h = h > 0 ? h : 512;
canvas = $('<canvas width="'+w+'" height="'+h+'" id="vvvv-js-generated-renderer-'+(new Date().getTime())+'" class="vvvv-js-generated-renderer"></canvas>');
if (!targetElement) targetElement = 'body';
$(targetElement).append(canvas);
}
else
canvas = $(targetElement);
if (!canvas)
return;
attachMouseEvents();
try {
canvasCtxt = canvas.get(0).getContext("experimental-webgl", {preserveDrawingBuffer: true});
canvasCtxt.viewportWidth = parseInt(canvas.get(0).width);
canvasCtxt.viewportHeight = parseInt(canvas.get(0).height);
} catch (e) {
console.log(e);
}
this.ctxt = canvasCtxt;
if (ex9Out.isConnected() && this.renderContexts && this.renderContexts[0]) {
this.ctxt = this.renderContexts[0];
gl = this.ctxt;
bbufFramebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, bbufFramebuffer);
bbufFramebuffer.width = canvas.get(0).width;
bbufFramebuffer.height = canvas.get(0).height;
bbufTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, bbufTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, bbufFramebuffer.width, bbufFramebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.generateMipmap(gl.TEXTURE_2D);
var renderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, bbufFramebuffer.width, bbufFramebuffer.height);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, bbufTexture, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
else {
if (this.renderContexts && this.renderContexts[0]) {
this.renderContexts[0].deleteTexture(bbufTexture);
bbufTexture = undefined;
// TODO: destroy framebuffer resources ...
}
}
if (!this.ctxt)
return;
// doing this afterwards, so we can use these values in the patch for checking, if webgl context was set up correctly
width = parseInt(canvas.get(0).width);
height = parseInt(canvas.get(0).height);
// create default white texture
gl = this.ctxt;
var pixels = new Uint8Array([255, 255, 255]);
gl.DefaultTexture = {};
gl.DefaultTexture['2D'] = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, gl.DefaultTexture['2D']);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.DefaultTexture['CUBE'] = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, gl.DefaultTexture['CUBE']);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, pixels);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
// this is to ensure that all the input pins get evaluated, if the gl context has been set after the node creation
this.inputPins["Layers"].markPinAsChanged();
clearIn.markPinAsChanged();
bgColIn.markPinAsChanged();
viewIn.markPinAsChanged();
projIn.markPinAsChanged();
}
this.destroy = function() {
$(canvas).remove();
}
var initialized = false;
this.evaluate = function() {
gl = this.ctxt;
if (this.invisiblePins["Descriptive Name"].pinIsChanged() || this.contextChanged) {
if (canvasCtxt && $(canvasCtxt.canvas).hasClass('vvvv-js-generated-renderer'))
$(canvasCtxt.canvas).remove();
this.getContexts();
if (this.inputPins["Layers"].isConnected())
this.inputPins["Layers"].links[0].fromPin.connectionChanged();
}
if (!initialized) {
bufferWidthOut.setValue(0, width);
bufferHeightOut.setValue(0, height);
initialized = true;
}
if (gl==undefined)
return;
if (bufferWidthIn.pinIsChanged() && !(this.renderContexts && this.renderContexts[0])) {
var w = parseInt(bufferWidthIn.getValue(0));
if (w>0) {
width = w;
$(canvasCtxt.canvas).attr('width', width);
bufferWidthOut.setValue(0, width);
}
}
if (bufferHeightIn.pinIsChanged() && !(this.renderContexts && this.renderContexts[0])) {
var h = parseInt(bufferHeightIn.getValue(0));
if (h>0) {
height = h;
$(canvasCtxt.canvas).attr('height', height);
bufferHeightOut.setValue(0, height);
}
}
if (this.renderContexts && this.renderContexts[0] && gl==this.renderContexts[0]) {
gl.bindFramebuffer(gl.FRAMEBUFFER, bbufFramebuffer);
}
else {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
if (true) {//bgColIn.pinIsChanged()) {
var col = _(bgColIn.getValue(0).split(',')).map(function(e) {
return parseFloat(e);
});
gl.clearColor(col[0], col[1], col[2], col[3]);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}
if (true) {//enableDepthBufIn.pinIsChanged()) {
if (enableDepthBufIn.getValue(0)=='NONE')
gl.disable(gl.DEPTH_TEST);
else
gl.enable(gl.DEPTH_TEST);
}
if (projIn.pinIsChanged()) {
if (projIn.isConnected()) {
pMatrix = mat4.create();
mat4.set(projIn.getValue(0), pMatrix);
mat4.scale(pMatrix, [1, 1, -1]);
}
else {
pMatrix = mat4.create();
mat4.ortho(-1, 1, -1, 1, -100, 100, pMatrix);
mat4.scale(pMatrix, [1, 1, -1]);
}
if (this.renderContexts && this.renderContexts[0]) // flip the output texture, if connected to downstream renderer
mat4.scale(pMatrix, [1, -1, 1]);
}
if (viewIn.pinIsChanged()) {
vMatrix = viewIn.getValue(0);
}
if (this.contextChanged) { // don't render anything, if the context changed in this frame. will only give warnings...
this.contextChanged = false;
return
}
gl.viewport(0, 0, width, height);
var currentShaderProgram = null;
var currentRenderState = null;
var currentMesh = null;
if (this.inputPins["Layers"].isConnected()) {
var layers = this.inputPins["Layers"].values;
for (var i=0; i<layers.length; i++) {
layer = layers[i];
if (layer.shader==undefined) // if it's an empty layer (e.g. created by IOBox (Node))
continue;
if (currentShaderProgram!=layer.shader.shaderProgram) {
gl.useProgram(layer.shader.shaderProgram);
gl.uniformMatrix4fv(layer.shader.uniformSpecs[layer.shader.uniformSemanticMap["PROJECTION"]].position, false, pMatrix);
gl.uniformMatrix4fv(layer.shader.uniformSpecs[layer.shader.uniformSemanticMap["VIEW"]].position, false, vMatrix);
}
var renderState = layer.renderState;
if (!renderState)
renderState = defaultWebGlRenderState;
if (renderState!=currentRenderState)
renderState.apply(gl);
if (layer.mesh != currentMesh || layer.shader.shaderProgram != currentShaderProgram) {
gl.bindBuffer(gl.ARRAY_BUFFER, layer.mesh.vertexBuffer.vbo);
_(layer.mesh.vertexBuffer.subBuffers).each(function(b) {
if (!layer.shader.attributeSpecs[layer.shader.attribSemanticMap[b.usage]] || layer.shader.attributeSpecs[layer.shader.attribSemanticMap[b.usage]].position==-1)
return;
gl.enableVertexAttribArray(layer.shader.attributeSpecs[layer.shader.attribSemanticMap[b.usage]].position);
gl.vertexAttribPointer(layer.shader.attributeSpecs[layer.shader.attribSemanticMap[b.usage]].position, b.size, gl.FLOAT, false, 0, b.offset);
});
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, layer.mesh.indexBuffer);
}
var uniformCount = layer.uniformNames.length;
var textureIdx = 0;
for (var j=0; j<uniformCount; j++) {
var u = layer.uniforms[layer.uniformNames[j]];
if (u.value==undefined)
continue;
if (i>0 && layer.shader.shaderProgram==currentShaderProgram && layers[i-1].uniforms[layer.uniformNames[j]] && u.value==layers[i-1].uniforms[layer.uniformNames[j]].value)
continue;
start = new Date().getTime();
switch (u.uniformSpec.type) {
case "mat": gl['uniformMatrix'+u.uniformSpec.dimension+'fv'](u.uniformSpec.position, false, u.value); break;
case "vec": gl['uniform'+u.uniformSpec.dimension+'fv'](u.uniformSpec.position, u.value); break;
case "int": gl['uniform'+u.uniformSpec.dimension+'i'](u.uniformSpec.position, u.value); break;
case "float": gl['uniform'+u.uniformSpec.dimension+'f'](u.uniformSpec.position, u.value); break;
case "sampler":
var tex = u.value;
if (tex==VVVV.DefaultTexture)
tex = gl.DefaultTexture['2D'];
gl.activeTexture(gl['TEXTURE'+textureIdx]);
gl.bindTexture(gl['TEXTURE_'+u.uniformSpec.dimension], tex);
gl.uniform1i(u.uniformSpec.position, textureIdx);
textureIdx++;
break;
case "samplerCube":
var tex = u.value;
if (tex==VVVV.DefaultTexture)
tex = gl.DefaultTexture['CUBE'];
gl.activeTexture(gl['TEXTURE'+textureIdx]);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
gl.uniform1i(u.uniformSpec.position, textureIdx);
textureIdx++;
break;
}
loopstart = new Date().getTime();
}
gl.drawElements(gl[renderState.polygonDrawMode], layer.mesh.numIndices, gl.UNSIGNED_SHORT, 0);
// save current states
currentShaderProgram = layer.shader.shaderProgram;
currentRenderState = renderState;
currentMesh = layer.mesh;
}
gl.bindTexture(gl.TEXTURE_2D, null);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
}
if (this.renderContexts && this.renderContexts[0]) {
gl.bindTexture(gl.TEXTURE_2D, bbufTexture);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(this.renderContexts[0].TEXTURE_2D, null);
}
ex9Out.setValue(0, bbufTexture);
}
}
VVVV.Nodes.RendererWebGL.prototype = new VVVV.Core.Node();
/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NODE: DefineEffect (DX9)
Author(s): Matthias Zauner
Original Node Author(s): Matthias Zauner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
VVVV.Nodes.DefineEffect = function(id, graph) {
this.constructor(id, "DefineEffect (DX9)", graph);
this.auto_nil = false;
this.meta = {
authors: ['Matthias Zauner'],
original_authors: ['Matthias Zauner'],
credits: [],
compatibility_issues: ['Not available in classic VVVV']
};
this.auto_evaluate = false;
var nameIn = this.addInputPin("Effect Descriptor", [''], VVVV.PinTypes.String);
var sourceCodeIn = this.addInvisiblePin("Source Code", ['#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform mat4 tW : WORLD;\nuniform mat4 tV : VIEW;\nuniform mat4 tP : PROJECTION;\n\nuniform vec4 Color : COLOR = {1.0, 1.0, 1.0, 1.0};\nuniform sampler2D Texture;\nuniform mat4 Texture_Transform;\nuniform float Alpha = 1.0;\n\nvarying vec2 vs2psTexCd;\n\nvertex_shader:\n\nattribute vec3 PosO : POSITION;\nattribute vec2 TexCd : TEXCOORD0;\n\nvoid main(void) {\n gl_Position = tP * tV * tW * vec4(PosO, 1.0);\n vs2psTexCd = (Texture_Transform * vec4(TexCd, 0, 1)).xy;\n}\n\n\nfragment_shader:\n\nvoid main(void) {\n gl_FragColor = Color * texture2D(Texture, vs2psTexCd) * vec4(1.0, 1.0, 1.0, Alpha);\n}'], VVVV.PinTypes.String);
var currentName = '';
var w; // the UI window
this.evaluate = function() {
if (nameIn.getValue(0)!='') {
if (nameIn.pinIsChanged()) {
var descriptor = nameIn.getValue(0);
if (descriptor=='')
return;
if (currentName=='') { // if is set for the first time
if (VVVV.ShaderCodeResources["./"+descriptor+'.vvvvjs.fx']==undefined)
VVVV.ShaderCodeResources["./"+descriptor+'.vvvvjs.fx'] = new VVVV.Types.ShaderCodeResource();
}
else
VVVV.ShaderCodeResources["./"+descriptor+'.vvvvjs.fx'] = VVVV.ShaderCodeResources[currentName];
currentName = "./"+descriptor+'.vvvvjs.fx';
VVVV.ShaderCodeResources[currentName].definingNode = this;
VVVV.ShaderCodeResources[currentName].setSourceCode(sourceCodeIn.getValue(0));
if (w)
$('#path', w.document).text((this.parentPatch.nodename || 'root')+' / '+(currentName!='' ? currentName : 'Untitled'));
}
if (sourceCodeIn.pinIsChanged()) {
if (VVVV.ShaderCodeResources[currentName])
VVVV.ShaderCodeResources[currentName].setSourceCode(sourceCodeIn.getValue(0));
}
}
}
this.openUIWindow = function() {
w = window.open("code_editor.html", currentName+" / VVVV.js Effect Editor", "location=no, width=800, height=800, toolbar=no");
var thatNode = this;
window.setTimeout(function() {
w.document.title = currentName+" / VVVV.js Effect Editor";
var definingNodeName = thatNode.parentPatch.nodename || 'root';
var shaderName = currentName!='' ? currentName : 'Untitled';
$('#path', w.document).text(definingNodeName+' / '+shaderName);
$('textarea', w.document).text(sourceCodeIn.getValue(0));
$('#compile_button', w.document).click(function() {
if (currentName=='') {
thatNode.showStatus('error', 'Please provide a name for this shader first');
return;
}
sourceCodeIn.setValue(0, $('textarea', w.document).val());
if (VVVV.ShaderCodeResources[currentName].relatedNodes.length>0)
thatNode.showStatus('notice', 'Compiling ...');
else
thatNode.showStatus('notice', 'No instance of this shader found. Create a node (./'+currentName+') and connect it to a Renderer (EX9) to compile.');
});
w.focus();
}, 500);
}
this.showStatus = function(type, message) {
if (w) {
$('#status', w.document).text(message);
$('#status', w.document).attr('class', type);
}
}
}
VVVV.Nodes.DefineEffect.prototype = new VVVV.Core.Node();