├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── build └── shadergraph.js ├── editor ├── .DS_Store ├── css │ ├── bootstrap.css │ └── style.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── images │ ├── chesterfield-ao.png │ ├── chesterfield-color.png │ ├── chesterfield-normal.png │ ├── chesterfield-specular.png │ └── chesterfield.png ├── index.html └── js │ ├── bootstrap.min.js │ ├── browser.min.js │ ├── goo.js │ ├── jquery.min.js │ ├── jsPlumb-2.0.2-min.js │ ├── jsplumb.js │ ├── react-dom.js │ └── react.js ├── examples ├── lib │ └── goo.js └── simple.html ├── index.js ├── package.json ├── previewScript.js ├── src ├── Attribute.js ├── Connection.js ├── FragmentGraph.js ├── Graph.js ├── GraphShader.js ├── Uniform.js ├── Utils.js ├── Varying.js ├── VertexGraph.js └── nodes │ ├── AbsNode.js │ ├── AddNode.js │ ├── AppendNode.js │ ├── ArcCosNode.js │ ├── ArcSinNode.js │ ├── ArcTanNode.js │ ├── CeilNode.js │ ├── Clamp01Node.js │ ├── ClampNode.js │ ├── ComponentMaskNode.js │ ├── CosNode.js │ ├── DistanceNode.js │ ├── DivideNode.js │ ├── FloorNode.js │ ├── FracNode.js │ ├── FragColorNode.js │ ├── IfNode.js │ ├── LengthNode.js │ ├── LogNode.js │ ├── MathFunctionNode.js │ ├── Matrix4Node.js │ ├── MaxNode.js │ ├── MinNode.js │ ├── MixNode.js │ ├── ModNode.js │ ├── MultiplyNode.js │ ├── Node.js │ ├── NormalizeNode.js │ ├── OperatorNode.js │ ├── PositionNode.js │ ├── PowNode.js │ ├── ReciprocalNode.js │ ├── RelayNode.js │ ├── RoundNode.js │ ├── SignNode.js │ ├── SineNode.js │ ├── SmoothStepNode.js │ ├── SqrtNode.js │ ├── StepNode.js │ ├── SubtractNode.js │ ├── TanNode.js │ ├── TextureNode.js │ ├── TimeNode.js │ ├── TruncNode.js │ ├── UVNode.js │ ├── UberFragNode.js │ ├── UberVertNode.js │ ├── ValueNode.js │ ├── Vector2Node.js │ ├── Vector3Node.js │ ├── Vector4Node.js │ └── Vector4PropertyNode.js ├── test └── index.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.DS_Store -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function(grunt) { 4 | 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | 8 | // browserify : { 9 | // core : { 10 | // src : ["src/p2.js"], 11 | // dest : 'build/p2.js', 12 | // options:{ 13 | // bundleOptions : { 14 | // standalone : "p2" 15 | // } 16 | // } 17 | // } 18 | // }, 19 | 20 | // uglify : { 21 | // build: { 22 | // src : ['build/p2.js'], 23 | // dest : 'build/p2.min.js' 24 | // }, 25 | // demo: { 26 | // src : ['build/p2.renderer.js'], 27 | // dest : 'build/p2.renderer.min.js' 28 | // } 29 | // }, 30 | 31 | nodeunit: { 32 | all: ['test/**/*.js'], 33 | }, 34 | 35 | // jshint: { 36 | // all: ['src/**/*.js'], 37 | // options:{ 38 | // jshintrc: '.jshintrc', 39 | // force: true // Do not fail the task 40 | // } 41 | // }, 42 | 43 | watch: { 44 | options: { 45 | nospawn: false 46 | }, 47 | source: { 48 | files: 'src/**/*', 49 | tasks: [ 50 | 'default' 51 | ] 52 | }, 53 | renderer: { 54 | files: 'demos/js/*Renderer.js', 55 | tasks: [ 56 | 'concat:renderer' 57 | ] 58 | }, 59 | test: { 60 | files: ['src/**/*', 'test/**/*'], 61 | tasks: [ 62 | 'test' 63 | ] 64 | }, 65 | }, 66 | 67 | // concat: { 68 | // renderer: { 69 | // src: ['demos/js/pixi.js', 'demos/js/dat.gui.js', 'demos/js/Renderer.js', 'demos/js/WebGLRenderer.js'], 70 | // dest: 'build/p2.renderer.js', 71 | // } 72 | // } 73 | }); 74 | 75 | // grunt.loadNpmTasks('grunt-contrib-uglify'); 76 | // grunt.loadNpmTasks('grunt-contrib-jshint'); 77 | // grunt.loadNpmTasks('grunt-browserify'); 78 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 79 | grunt.loadNpmTasks('grunt-contrib-watch'); 80 | 81 | grunt.registerTask('default', ['test']); 82 | grunt.registerTask('test', ['nodeunit']); 83 | }; 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Goo Technologies 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Goo Shader Graph 2 | 3 | API (and experimental editor) for constructing shaders for Goo Engine. 4 | 5 | ![it_s_now_possible_to_remove_nodes _can_t_concentrate_on_coding_this _it_s_too_fun_to_play_with_it _reactjs_webgl_goocreate](https://cloud.githubusercontent.com/assets/1063152/14169679/8bc769ea-f729-11e5-8b6b-446fffcb327a.gif) 6 | -------------------------------------------------------------------------------- /editor/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/.DS_Store -------------------------------------------------------------------------------- /editor/css/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | margin: auto; 7 | height: 100%; 8 | } 9 | 10 | .list-group-item { 11 | padding: 3px 9px; 12 | } 13 | 14 | .node-type-vec2 input { 15 | width: 50px; 16 | } 17 | 18 | .node-type-vec3 input { 19 | width: 35px; 20 | } 21 | 22 | .node-type-vec4 input { 23 | width: 30px; 24 | } 25 | 26 | .remove-button, .add-node-button { 27 | cursor: pointer; 28 | } 29 | 30 | /* Bootstrap */ 31 | .container-full { 32 | padding: 0 15px; 33 | margin: 0 auto; 34 | width: 100%; 35 | min-height: 100%; 36 | } 37 | 38 | /* jsplumb start */ 39 | #canvas { 40 | width: 100%; 41 | max-height: 100%; 42 | height: 600px; 43 | margin: auto; 44 | position: relative; 45 | -moz-user-select: none; 46 | -webkit-user-select: none; 47 | -ms-user-select: none; 48 | } 49 | 50 | #goo { 51 | width: 100%; 52 | } 53 | .preview { 54 | padding-left: 15px; 55 | padding-right: 15px; 56 | padding-bottom: 15px; 57 | } 58 | 59 | .demo { 60 | touch-action:none; 61 | } 62 | 63 | .w { 64 | padding: 0; 65 | position: absolute; 66 | z-index: 4; 67 | border: 1px solid black; 68 | background-color: white; 69 | font-size: 11px; 70 | min-width: 100px; 71 | border-radius: 4px; 72 | color: #555; 73 | } 74 | 75 | .w .out, .w .in { 76 | cursor: pointer; 77 | margin: 3px; 78 | } 79 | 80 | .w .out { 81 | text-align: right; 82 | } 83 | 84 | .w .title { 85 | cursor: move; 86 | text-align: center; 87 | padding:5px; 88 | background-color: #222; 89 | color: #9d9d9d; 90 | } 91 | 92 | .w:hover { 93 | background-color: #ddd; 94 | } 95 | 96 | .aLabel.jsplumb-hover, .jsplumb-source-hover, .jsplumb-target-hover { 97 | background-color: #ddd; 98 | } 99 | 100 | .aLabel { 101 | background-color: white; 102 | padding: 3px; 103 | border: 1px solid black; 104 | cursor: pointer; 105 | } 106 | 107 | .ep { 108 | position: absolute; 109 | top: 10px; 110 | right: -30px; 111 | width: 30px; 112 | height: 20px; 113 | background-color: gray; 114 | cursor: pointer; 115 | } 116 | 117 | .statemachine-demo .jsplumb-endpoint { 118 | z-index: 3; 119 | } 120 | 121 | #opened { 122 | left: 10em; 123 | top: 10em; 124 | } 125 | 126 | #phone1 { 127 | left: 35em; 128 | top: 12em; 129 | width: 7em; 130 | } 131 | 132 | #inperson { 133 | left: 12em; 134 | top: 23em; 135 | } 136 | 137 | #phone2 { 138 | left: 28em; 139 | top: 24em; 140 | } 141 | 142 | .dragHover { 143 | border: 2px solid orange; 144 | } 145 | 146 | path, .jsplumb-endpoint { 147 | cursor: pointer; 148 | } 149 | 150 | /* jsplumb end */ 151 | -------------------------------------------------------------------------------- /editor/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /editor/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /editor/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /editor/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /editor/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /editor/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /editor/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /editor/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /editor/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /editor/images/chesterfield-ao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/images/chesterfield-ao.png -------------------------------------------------------------------------------- /editor/images/chesterfield-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/images/chesterfield-color.png -------------------------------------------------------------------------------- /editor/images/chesterfield-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/images/chesterfield-normal.png -------------------------------------------------------------------------------- /editor/images/chesterfield-specular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/images/chesterfield-specular.png -------------------------------------------------------------------------------- /editor/images/chesterfield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GooTechnologies/shader-graph/8f80e014ef26910baff4d2fb04b548468c7c8c2c/editor/images/chesterfield.png -------------------------------------------------------------------------------- /editor/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Shader Graph Editor 6 | 7 | 8 | 9 | 10 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 668 | 669 | -------------------------------------------------------------------------------- /editor/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /editor/js/react-dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOM v0.14.0 3 | * 4 | * Copyright 2013-2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | // Based off https://github.com/ForbesLindesay/umd/blob/master/template.js 13 | ;(function(f) { 14 | // CommonJS 15 | if (typeof exports === "object" && typeof module !== "undefined") { 16 | module.exports = f(require('react')); 17 | 18 | // RequireJS 19 | } else if (typeof define === "function" && define.amd) { 20 | define(['react'], f); 21 | 22 | // 15 | 16 | 86 | 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | AbsNode: require('./src/nodes/AbsNode'), 4 | AddNode: require('./src/nodes/AddNode'), 5 | AppendNode: require('./src/nodes/AppendNode'), 6 | Attribute: require('./src/Attribute'), 7 | CeilNode: require('./src/nodes/CeilNode'), 8 | Clamp01Node: require('./src/nodes/Clamp01Node'), 9 | ClampNode: require('./src/nodes/ClampNode'), 10 | Connection: require('./src/Connection'), 11 | ComponentMaskNode: require('./src/nodes/ComponentMaskNode'), 12 | CosNode: require('./src/nodes/CosNode'), 13 | DistanceNode: require('./src/nodes/DistanceNode'), 14 | DivideNode: require('./src/nodes/DivideNode'), 15 | FloorNode: require('./src/nodes/FloorNode'), 16 | FracNode: require('./src/nodes/FracNode'), 17 | FragColorNode: require('./src/nodes/FragColorNode'), 18 | FragmentGraph: require('./src/FragmentGraph'), 19 | Graph: require('./src/Graph'), 20 | GraphShader: require('./src/GraphShader'), 21 | IfNode: require('./src/nodes/IfNode'), 22 | LogNode: require('./src/nodes/LogNode'), 23 | MathFunctionNode: require('./src/nodes/MathFunctionNode'), 24 | Matrix4Node: require('./src/nodes/Matrix4Node'), 25 | MaxNode: require('./src/nodes/MaxNode'), 26 | MinNode: require('./src/nodes/MinNode'), 27 | MixNode: require('./src/nodes/MixNode'), 28 | ModNode: require('./src/nodes/ModNode'), 29 | LengthNode: require('./src/nodes/LengthNode'), 30 | MultiplyNode: require('./src/nodes/MultiplyNode'), 31 | Node: require('./src/nodes/Node'), 32 | NormalizeNode: require('./src/nodes/NormalizeNode'), 33 | OperatorNode: require('./src/nodes/OperatorNode'), 34 | PositionNode: require('./src/nodes/PositionNode'), 35 | PowNode: require('./src/nodes/PowNode'), 36 | RelayNode: require('./src/nodes/RelayNode'), 37 | ReciprocalNode: require('./src/nodes/ReciprocalNode'), 38 | RoundNode: require('./src/nodes/RoundNode'), 39 | SignNode: require('./src/nodes/SignNode'), 40 | SineNode: require('./src/nodes/SineNode'), 41 | SmoothStepNode: require('./src/nodes/SmoothStepNode'), 42 | SqrtNode: require('./src/nodes/SqrtNode'), 43 | StepNode: require('./src/nodes/StepNode'), 44 | SubtractNode: require('./src/nodes/SubtractNode'), 45 | TextureNode: require('./src/nodes/TextureNode'), 46 | TimeNode: require('./src/nodes/TimeNode'), 47 | TruncNode: require('./src/nodes/TruncNode'), 48 | UberFragNode: require('./src/nodes/UberFragNode'), 49 | UberVertNode: require('./src/nodes/UberVertNode'), 50 | Uniform: require('./src/Uniform'), 51 | Utils: require('./src/Utils'), 52 | UVNode: require('./src/nodes/UVNode'), 53 | ValueNode: require('./src/nodes/ValueNode'), 54 | Varying: require('./src/Varying'), 55 | Vector2Node: require('./src/nodes/Vector2Node'), 56 | Vector3Node: require('./src/nodes/Vector3Node'), 57 | Vector4Node: require('./src/nodes/Vector4Node'), 58 | 59 | }; 60 | 61 | if(typeof(window) !== 'undefined'){ 62 | window.ShaderGraph = module.exports; 63 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shader-graph", 3 | "version": "1.0.0", 4 | "description": "## new Graph(options) ```.connections``` ```.nodes```", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "Stefan Hedman", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "grunt": "^0.4.5", 16 | "grunt-contrib-nodeunit": "^0.4.1", 17 | "grunt-contrib-watch": "^0.6.1", 18 | "webpack": "^1.12.2" 19 | }, 20 | "dependencies": { 21 | "toposort": "^0.2.12" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /previewScript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global goo */ 4 | 5 | var setup = function (args, ctx) { 6 | var RenderTarget = goo.RenderTarget; 7 | var MeshData = goo.MeshData; 8 | var Shader = goo.Shader; 9 | var Quad = goo.Quad; 10 | var MeshDataComponent = goo.MeshDataComponent; 11 | var MeshRendererComponent = goo.MeshRendererComponent; 12 | var Material = goo.Material; 13 | var FullscreenUtils = goo.FullscreenUtils; 14 | var Transform = goo.Transform; 15 | 16 | var shader = { 17 | attributes: { 18 | vertexPosition: 'POSITION', 19 | vertexUV0: 'TEXCOORD0' 20 | }, 21 | uniforms: {}, 22 | vshader: [ 23 | 'attribute vec3 vertexPosition;', 24 | 'attribute vec2 vertexUV0;', 25 | 'varying vec2 textureCoord;', 26 | 27 | 'void main(void) {', 28 | ' textureCoord = vertexUV0;', 29 | ' gl_Position = vec4(vertexPosition.xy, 0.0, 1.0);', 30 | '}' 31 | ].join('\n'), 32 | fshader: [ 33 | 'varying vec2 textureCoord;', 34 | 35 | 'void main(void){', 36 | ' gl_FragColor = vec4(textureCoord.xy, 0.0, 1.0);', 37 | '}' 38 | ].join('\n') 39 | }; 40 | 41 | var renderable = { 42 | meshData: FullscreenUtils.quad, 43 | materials: [new Material(shader)], 44 | transform: new Transform() 45 | }; 46 | 47 | var size = 64; 48 | var renderer = ctx.world.gooRunner.renderer; 49 | 50 | ctx.renderTarget = new RenderTarget(size, size); 51 | renderer.render(renderable, FullscreenUtils.camera, [], ctx.renderTarget, true); 52 | var pixels = new Uint8Array(size * size * 4); 53 | renderer.readPixels(0,0,size,size,pixels); 54 | 55 | // Draw the pixels to a canvas element 56 | var canvas = document.createElement('CANVAS'); 57 | canvas.width = canvas.height = size; 58 | var context = canvas.getContext('2d'); 59 | var imgData = context.createImageData(size, size); 60 | imgData.data.set(pixels); 61 | context.putImageData(imgData,0,0); 62 | 63 | var texture = new goo.Texture(canvas, { 64 | wrapS: 'EdgeClamp', 65 | wrapT: 'EdgeClamp' 66 | }, size, size); 67 | ctx.entity.meshRendererComponent.materials[0].setTexture('DIFFUSE_MAP', texture); 68 | }; 69 | 70 | var cleanup = function (args, ctx) { 71 | ctx.entity.meshRendererComponent.materials[0].removeTexture('DIFFUSE_MAP'); 72 | }; 73 | 74 | var update = function (args, ctx) { 75 | 76 | }; 77 | 78 | var parameters = []; -------------------------------------------------------------------------------- /src/Attribute.js: -------------------------------------------------------------------------------- 1 | module.exports = Attribute; 2 | 3 | function Attribute(options){ 4 | options = options || {}; 5 | this.type = options.type || 'float'; 6 | this.name = options.name || 'aUntitled'; 7 | this.key = options.key || 'POSITION'; 8 | this.ifdef = options.ifdef || ''; // A define name or empty string to indicate no define dependency 9 | } -------------------------------------------------------------------------------- /src/Connection.js: -------------------------------------------------------------------------------- 1 | module.exports = Connection; 2 | 3 | function Connection(options){ 4 | options = options || {}; 5 | 6 | this.fromNode = options.fromNode || null; 7 | this.fromPortKey = options.fromPortKey || null; 8 | this.toNode = options.toNode || null; 9 | this.toPortKey = options.toPortKey || null; 10 | this.graph = options.graph || null; 11 | } 12 | 13 | /** 14 | * Check if the connection is still valid. The connection can become invalid if some upstream dependency types change. 15 | * @return {Boolean} 16 | */ 17 | Connection.prototype.isValid = function(){ 18 | var fromNode = this.fromNode; 19 | var toNode = this.toNode; 20 | var toPortKey = this.toPortKey; 21 | var fromPortKey = this.fromPortKey; 22 | 23 | if(!this.graph) return false; 24 | if(this.fromNode === this.toNode) return false; 25 | if(toNode.getInputPorts().indexOf(this.toPortKey) === -1) return false; 26 | if(fromNode.getInputPorts().indexOf(this.fromPortKey) === -1) return false; 27 | 28 | // Check if they have a type in common 29 | var outputTypes = fromNode.getOutputTypes(fromPortKey); 30 | var inputTypes = toNode.getInputTypes(toPortKey); 31 | var hasSharedType = outputTypes.some(function(type){ 32 | return inputTypes.indexOf(type) !== -1; 33 | }); 34 | if(!outputTypes.length || !inputTypes.length || !hasSharedType) return false; 35 | if(fromNode.getOutputPorts().indexOf(fromPortKey) === -1) return false; 36 | return true; 37 | }; -------------------------------------------------------------------------------- /src/FragmentGraph.js: -------------------------------------------------------------------------------- 1 | var Graph = require('./Graph'); 2 | var FragColorNode = require('./nodes/FragColorNode'); 3 | 4 | module.exports = FragmentGraph; 5 | 6 | function FragmentGraph(options){ 7 | options = options || {}; 8 | options.mainNode = options.mainNode || new FragColorNode(); 9 | Graph.call(this, options); 10 | } 11 | FragmentGraph.prototype = Object.create(Graph.prototype); -------------------------------------------------------------------------------- /src/Graph.js: -------------------------------------------------------------------------------- 1 | var toposort = require('toposort'); 2 | 3 | module.exports = Graph; 4 | 5 | function Graph(options){ 6 | options = options || {}; 7 | 8 | this.shader = options.shader || null; 9 | this.nodes = []; 10 | this.connections = []; 11 | this.mainNode = options.mainNode || null; 12 | if(this.mainNode){ 13 | this.addNode(this.mainNode); 14 | } 15 | } 16 | 17 | Graph.prototype.addNode = function(node){ 18 | if(!node) throw new Error('Node not given'); 19 | if(node.graph) throw new Error('Node was already added to a graph'); 20 | this.nodes.push(node); 21 | node.graph = this; 22 | }; 23 | 24 | Graph.prototype.removeNode = function(node){ 25 | var index = this.nodes.indexOf(node); 26 | if(index !== -1){ 27 | this.nodes.splice(index, 1); 28 | node.graph = null; 29 | } 30 | }; 31 | 32 | Graph.prototype.getNodeById = function(id){ 33 | return this.nodes.find(function(node){ 34 | return node.id == id; 35 | }); 36 | }; 37 | 38 | Graph.prototype.addConnection = function(conn){ 39 | if(conn.graph) throw new Error('Connection was already added to a graph'); 40 | this.connections.push(conn); 41 | conn.graph = this; 42 | this.sortNodes(); 43 | }; 44 | 45 | Graph.prototype.removeConnection = function(conn){ 46 | var index = this.connections.indexOf(conn); 47 | if(index !== -1){ 48 | this.connections.splice(index, 1); 49 | } 50 | }; 51 | 52 | Graph.prototype.inputPortIsConnected = function(node, inputPort){ 53 | return this.connections.some(function (conn){ 54 | return conn.toNode === node && conn.toPortKey === inputPort; 55 | }); 56 | }; 57 | 58 | Graph.prototype.outputPortIsConnected = function(node, outputPort){ 59 | return this.connections.some(function (conn){ 60 | return conn.fromNode === node && conn.fromPortKey === outputPort; 61 | }); 62 | }; 63 | 64 | Graph.prototype.getNodeConnectedToInputPort = function(node, inputPort){ 65 | var connection = this.connections.filter(function (conn){ 66 | return conn.toNode === node && conn.toPortKey === inputPort; 67 | })[0]; 68 | return connection && connection.fromNode; 69 | }; 70 | 71 | Graph.prototype.getPortKeyConnectedToInputPort = function(node, inputPort){ 72 | var connection = this.connections.filter(function (conn){ 73 | return conn.toNode === node && conn.toPortKey === inputPort; 74 | })[0]; 75 | return connection && connection.fromPortKey; 76 | }; 77 | 78 | Graph.prototype.getUniforms = function(){ 79 | var uniforms = []; 80 | this.nodes.forEach(function (node){ 81 | uniforms = uniforms.concat(node.getUniforms()); 82 | }); 83 | return uniforms; 84 | }; 85 | 86 | Graph.prototype.getAttributes = function(){ 87 | var attributes = []; 88 | this.shader.getNodes().forEach(function (node){ 89 | attributes = attributes.concat(node.getAttributes()); 90 | }); 91 | return attributes; 92 | }; 93 | 94 | Graph.prototype.getVaryings = function(){ 95 | var varyings = []; 96 | this.shader.getNodes().forEach(function (node){ 97 | varyings = varyings.concat(node.getVaryings()); 98 | }); 99 | return varyings; 100 | }; 101 | 102 | Graph.prototype.getProcessors = function(){ 103 | var processors = []; 104 | this.nodes.forEach(function (node){ 105 | processors = processors.concat(node.getProcessors()); 106 | }); 107 | return processors; 108 | }; 109 | 110 | function sortByName(a1, a2){ 111 | if(a1.name === a2.name){ 112 | return 0; 113 | } else if(a1.name > a2.name){ 114 | return 1; 115 | } else { 116 | return -1; 117 | } 118 | } 119 | 120 | Graph.prototype.renderNodeCodes = function(){ 121 | var shaderSource = []; 122 | var nodes = this.nodes; 123 | for (var i = 0; i < nodes.length; i++) { 124 | node = nodes[i]; 125 | if(node !== this.mainNode){ // Save main node until last 126 | var nodeSource = node.render(); 127 | if(nodeSource){ 128 | shaderSource.push('{ // node ' + node.id + ', ' + node.constructor.type, nodeSource, '}'); 129 | } 130 | } 131 | } 132 | return shaderSource.join('\n'); 133 | }; 134 | 135 | Graph.prototype.renderAttributeToVaryingAssignments = function(){ 136 | var shaderSource = []; 137 | var keyToAttributeMap = {}; 138 | this.getAttributes().forEach(function(attribute){ 139 | keyToAttributeMap[attribute.key] = attribute; 140 | }); 141 | this.getVaryings().sort(sortByName).forEach(function(varying){ 142 | var attribute = keyToAttributeMap[varying.attributeKey]; 143 | if(attribute){ 144 | shaderSource.push(varying.name + ' = ' + attribute.name + ';'); 145 | } 146 | }); 147 | return shaderSource.join('\n'); 148 | }; 149 | 150 | Graph.prototype.renderConnectionVariableDeclarations = function(){ 151 | var shaderSource = []; 152 | var nodes = this.nodes; 153 | for (var i = 0; i < nodes.length; i++) { 154 | node = nodes[i]; 155 | var outputPorts = node.getOutputPorts(); 156 | for (var k = 0; k < outputPorts.length; k++) { 157 | var key = outputPorts[k]; 158 | // is the output port connected? 159 | if(this.outputPortIsConnected(node, key)){ 160 | var types = node.getOutputTypes(key); 161 | var names = node.getOutputVariableNames(key); 162 | for (j = 0; j < names.length; j++) { 163 | shaderSource.push(types[j] + ' ' + names[j] + ';'); 164 | } 165 | } 166 | } 167 | } 168 | return shaderSource.join('\n'); 169 | }; 170 | 171 | Graph.prototype.renderUniformDeclarations = function(){ 172 | var shaderSource = []; 173 | this.getUniforms().sort(sortByName).forEach(function(uniform){ 174 | shaderSource.push('uniform ' + uniform.type + ' ' + uniform.name + ';'); 175 | }); 176 | return shaderSource.join('\n'); 177 | }; 178 | 179 | Graph.prototype.renderAttrubuteDeclarations = function(){ 180 | var shaderSource = []; 181 | var declarations = {}; // Only unique declarations 182 | this.getAttributes().sort(sortByName).forEach(function(attribute){ 183 | declarations['attribute ' + attribute.type + ' ' + attribute.name + ';'] = true; 184 | }); 185 | return Object.keys(declarations).join('\n'); 186 | }; 187 | 188 | Graph.prototype.renderVaryingDeclarations = function(){ 189 | var shaderSource = []; 190 | var declarations = {}; // Only unique declarations 191 | this.getVaryings().sort(sortByName).forEach(function(varying){ 192 | declarations['varying ' + varying.type + ' ' + varying.name + ';'] = true; 193 | }); 194 | return Object.keys(declarations).join('\n'); 195 | }; 196 | 197 | // Topology sort the nodes 198 | Graph.prototype.sortNodes = function(){ 199 | var edges = this.connections.map(function (connection) { 200 | return [ 201 | connection.fromNode.id, 202 | connection.toNode.id 203 | ]; 204 | }); 205 | var nodeIds = toposort(edges); 206 | var nodes = this.nodes.slice(0); 207 | this.nodes = nodeIds.map(function (nodeId) { 208 | for (var i = nodes.length - 1; i >= 0; i--) { 209 | var node = nodes[i]; 210 | if(nodeId === node.id) return nodes.splice(i, 1)[0]; 211 | } 212 | throw new Error('Node id not found: ' + nodeId); 213 | }); 214 | 215 | // add any left overs (needed?) 216 | while(nodes.length){ 217 | this.nodes.push(nodes.pop()) 218 | } 219 | }; -------------------------------------------------------------------------------- /src/GraphShader.js: -------------------------------------------------------------------------------- 1 | var FragmentGraph = require('./FragmentGraph'); 2 | var VertexGraph = require('./VertexGraph'); 3 | 4 | module.exports = GraphShader; 5 | 6 | function GraphShader(options){ 7 | options = options || {}; 8 | 9 | this.fragmentGraph = new FragmentGraph({ 10 | shader: this, 11 | mainNode: options.fragMainNode 12 | }); 13 | this.vertexGraph = new VertexGraph({ 14 | shader: this, 15 | mainNode: options.vertexMainNode 16 | }); 17 | } 18 | 19 | GraphShader.prototype.getNodes = function(){ 20 | return this.vertexGraph.nodes.concat(this.fragmentGraph.nodes); 21 | }; 22 | 23 | GraphShader.prototype.buildShader = function(){ 24 | var shaderDef = { 25 | processors: [], 26 | defines: {}, 27 | attributes : {}, 28 | uniforms : {}, 29 | vshader: '', 30 | fshader : '' 31 | }; 32 | 33 | // Uniforms and attributes 34 | [this.fragmentGraph, this.vertexGraph].forEach(function (graph){ 35 | 36 | // Uniforms 37 | graph.getUniforms().forEach(function(uniform){ 38 | shaderDef.uniforms[uniform.name] = uniform.defaultValue; 39 | }); 40 | 41 | // Attributes 42 | graph.getAttributes().forEach(function(attribute){ 43 | shaderDef.attributes[attribute.name] = attribute.key; 44 | }); 45 | }); 46 | 47 | // Source 48 | shaderDef.fshader = this.fragmentGraph.mainNode.buildShader(); 49 | shaderDef.vshader = this.vertexGraph.mainNode.buildShader(); 50 | 51 | shaderDef.builder = this.fragmentGraph.mainNode.getBuilder() || this.vertexGraph.mainNode.getBuilder(); 52 | 53 | shaderDef.processors = shaderDef.processors.concat(this.fragmentGraph.getProcessors(), this.vertexGraph.getProcessors()); 54 | 55 | return shaderDef; 56 | }; -------------------------------------------------------------------------------- /src/Uniform.js: -------------------------------------------------------------------------------- 1 | module.exports = Uniform; 2 | 3 | function Uniform(options){ 4 | options = options || {}; 5 | 6 | this.type = options.type || 'float'; 7 | this.name = options.name || 'uUntitled'; 8 | this.defaultValue = options.defaultValue || 1; 9 | } -------------------------------------------------------------------------------- /src/Utils.js: -------------------------------------------------------------------------------- 1 | module.exports = Utils; 2 | 3 | function Utils(){} 4 | 5 | // Utils.arrayIntersect(['a', 'b', 'c'], ['a', 'c']) => ['a', 'c'] 6 | Utils.arrayIntersect = function(a, b){ 7 | return a.filter(function (item){ 8 | return b.indexOf(item) !== -1; 9 | }); 10 | }; 11 | 12 | // Utils.getHighestDimensionVectorType(['float', 'vec2', 'vec3']) => 'vec3' 13 | Utils.getHighestDimensionVectorType = function(array){ 14 | return array.sort().reverse()[0]; 15 | }; 16 | 17 | // Utils.numberToGlslFloat(2) => '2.0' 18 | Utils.numberToGlslFloat = function(n){ 19 | return (n+'').indexOf('.') === -1 ? n+'.0' : n+''; 20 | }; 21 | 22 | var expressionTable = { 23 | 'float': { 24 | 'float': 'X', 25 | 'vec2': 'vec2(X)', 26 | 'vec3': 'vec3(X)', 27 | 'vec4': 'vec4(X)' 28 | }, 29 | 'vec2': { 30 | 'float': 'X.x', 31 | 'vec2': 'X', 32 | 'vec3': 'vec3(X,0)', 33 | 'vec4': 'vec4(X,0,0)' 34 | }, 35 | 'vec3': { 36 | 'float': 'X.x', 37 | 'vec2': 'X.xy', 38 | 'vec3': 'X', 39 | 'vec4': 'vec4(X,0)' 40 | }, 41 | 'vec4': { 42 | 'float': 'X.x', 43 | 'vec2': 'X.xy', 44 | 'vec3': 'X.xyz', 45 | 'vec4': 'X' 46 | } 47 | }; 48 | 49 | /** 50 | * Manually cast a GLSL type to another one. For example, a float can be casted to a vec3: convertGlslType('myFloatVar', 'float', 'vec3') => 'vec3(myFloatVar)' 51 | * @param {string} expression 52 | * @param {string} type 53 | * @param {string} newType 54 | * @return {string} 55 | */ 56 | Utils.convertGlslType = function(expression, type, newType){ 57 | return expressionTable[type][newType].replace('X', expression); 58 | }; -------------------------------------------------------------------------------- /src/Varying.js: -------------------------------------------------------------------------------- 1 | module.exports = Varying; 2 | 3 | function Varying(options){ 4 | options = options || {}; 5 | 6 | this.type = options.type || 'float'; 7 | this.name = options.name || 'vUntitled'; 8 | this.attributeKey = options.attributeKey || ''; // e.g. COLOR 9 | this.ifdef = options.ifdef || ''; // A define name or empty string to indicate no define dependency 10 | } -------------------------------------------------------------------------------- /src/VertexGraph.js: -------------------------------------------------------------------------------- 1 | var PositionNode = require('./nodes/PositionNode'); 2 | var Graph = require('./Graph'); 3 | var Uniform = require('./Uniform'); 4 | var Attribute = require('./Attribute'); 5 | 6 | module.exports = VertexGraph; 7 | 8 | function VertexGraph(options){ 9 | options.mainNode = options.mainNode || new PositionNode(); 10 | Graph.call(this, options); 11 | } 12 | VertexGraph.prototype = Object.create(Graph.prototype); 13 | 14 | VertexGraph.prototype.getUniforms = function(){ 15 | var uniforms = Graph.prototype.getUniforms.apply(this); 16 | uniforms.push( 17 | new Uniform({ 18 | name: 'viewProjectionMatrix', 19 | type: 'mat4', 20 | defaultValue: 'VIEW_PROJECTION_MATRIX' 21 | }), 22 | new Uniform({ 23 | name: 'worldMatrix', 24 | type: 'mat4', 25 | defaultValue: 'WORLD_MATRIX' 26 | }) 27 | ); 28 | return uniforms; 29 | }; 30 | 31 | VertexGraph.prototype.getAttributes = function(){ 32 | var attributes = Graph.prototype.getAttributes.apply(this); 33 | attributes.push(new Attribute({ 34 | name: 'vertexPosition', 35 | defaultValue: 'POSITION', 36 | type: 'vec3' 37 | })); 38 | return attributes; 39 | }; -------------------------------------------------------------------------------- /src/nodes/AbsNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = AbsNode; 5 | 6 | function AbsNode(options){ 7 | options = options || {}; 8 | options.functionName = 'abs'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | AbsNode.prototype = Object.create(MathFunctionNode.prototype); 12 | AbsNode.prototype.constructor = AbsNode; 13 | 14 | Node.registerClass('abs', AbsNode); -------------------------------------------------------------------------------- /src/nodes/AddNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var OperatorNode = require('./OperatorNode'); 3 | var Utils = require('../Utils'); 4 | 5 | module.exports = AddNode; 6 | 7 | function AddNode(options){ 8 | options = options || {}; 9 | options.operator = '+'; 10 | OperatorNode.call(this, options); 11 | } 12 | AddNode.prototype = Object.create(OperatorNode.prototype); 13 | AddNode.prototype.constructor = AddNode; 14 | 15 | Node.registerClass('add', AddNode); -------------------------------------------------------------------------------- /src/nodes/AppendNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | 4 | module.exports = AppendNode; 5 | 6 | // A vector with four components/values. 7 | function AppendNode(options){ 8 | options = options || {}; 9 | Node.call(this, options); 10 | } 11 | AppendNode.prototype = Object.create(Node.prototype); 12 | AppendNode.prototype.constructor = AppendNode; 13 | 14 | Node.registerClass('append', AppendNode); 15 | 16 | AppendNode.supportedTypes = [ 17 | 'float', 18 | 'vec2', 19 | 'vec3', 20 | 'vec4' 21 | ]; 22 | 23 | AppendNode.prototype.getInputPorts = function(){ 24 | // var sum = this.getComponentSum(); 25 | 26 | // var a = this.inputPortIsConnected('a'); 27 | // var b = this.inputPortIsConnected('b'); 28 | // var c = this.inputPortIsConnected('c'); 29 | // var d = this.inputPortIsConnected('d'); 30 | 31 | // if(!a && !b && !c && !d) 32 | // return ['a']; 33 | // else if(a && !b && !c && !d && sum < 4) 34 | // return ['a', 'b']; 35 | // else if(a && b && !c && !d && sum < 4) 36 | // return ['a', 'b', 'c']; 37 | // else if(a && b && c && !d && sum < 4) 38 | // return ['a', 'b', 'c', 'd']; 39 | // else 40 | return ['a', 'b', 'c', 'd']; 41 | }; 42 | 43 | AppendNode.prototype.getOutputPorts = function(){ 44 | return ['out']; 45 | }; 46 | 47 | AppendNode.prototype.getInputTypes = function(key){ 48 | var types; 49 | switch(key){ 50 | case 'a': 51 | case 'b': 52 | case 'c': 53 | case 'd': 54 | types = AppendNode.supportedTypes.slice(0/*, 4 - sum*/); 55 | break; 56 | } 57 | return types; 58 | }; 59 | 60 | AppendNode.prototype.getComponentSum = function(){ 61 | var ports = 'abcd'; 62 | var weights = { 63 | 'float': 1, 64 | 'vec2': 2, 65 | 'vec3': 3, 66 | 'vec4': 4 67 | }; 68 | var sum = 0; 69 | for(var i=0; ib']; 24 | }; 25 | 26 | IfNode.prototype.getOutputPorts = function(key){ 27 | return ['out']; 28 | }; 29 | 30 | IfNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'out'){ 33 | if(this.anyInputPortIsConnected()){ 34 | // Something is connected to the input - choose the vector type of largest dimension 35 | types = [ 36 | Utils.getHighestDimensionVectorType( 37 | this.getInputVariableTypes('a') 38 | .concat(this.getInputVariableTypes('b')) 39 | .concat(this.getInputVariableTypes('ab')) 42 | ) 43 | ]; 44 | } else { 45 | // Nothing connected - use default float type 46 | types = ['float']; 47 | } 48 | } 49 | return types; 50 | }; 51 | 52 | IfNode.prototype.getInputTypes = function(key){ 53 | return this.getInputPorts().indexOf(key) !== -1 ? IfNode.supportedTypes : []; 54 | }; 55 | 56 | IfNode.prototype.render = function(){ 57 | var inVarNameA = this.getInputVariableName('a'); 58 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 59 | 60 | var inVarNameB = this.getInputVariableName('b'); 61 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 62 | 63 | var inVarNameLess = this.getInputVariableName('ab'); 70 | var inVarTypeLarger = this.getInputVariableTypes('a>b')[0]; 71 | 72 | var outVarName = this.getOutputVariableNames('out')[0]; 73 | var outVarType = this.getOutputTypes('out')[0]; 74 | 75 | // TODO: compare components one by one 76 | if(inVarNameA && inVarNameB && inVarNameEqual && inVarNameLess && inVarNameLarger && outVarName){ 77 | return ( 78 | 'if(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + '<' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + '){' + 79 | outVarName + '=' + inVarNameLess + ';' + 80 | '}else if(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + '>' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + '){' + 81 | outVarName + '=' + inVarNameLarger + ';' + 82 | '}else{' + 83 | outVarName + '=' + inVarNameEqual + ';' + 84 | '}' 85 | ); 86 | } else if(outVarName){ 87 | var outType = this.getOutputTypes('out')[0]; 88 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 89 | } else { 90 | return ''; 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/nodes/LengthNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | var Utils = require('../Utils'); 4 | 5 | module.exports = LengthNode; 6 | 7 | function LengthNode(options){ 8 | options = options || {}; 9 | MathFunctionNode.call(this, options); 10 | } 11 | LengthNode.prototype = Object.create(MathFunctionNode.prototype); 12 | LengthNode.prototype.constructor = LengthNode; 13 | 14 | Node.registerClass('length', LengthNode); 15 | 16 | // Output type is always float 17 | LengthNode.prototype.getOutputTypes = function(key){ 18 | return key === 'y' ? ['float'] : []; 19 | }; 20 | 21 | LengthNode.prototype.render = function(){ 22 | var inVarNameA = this.getInputVariableName('x'); 23 | var inVarTypeA = this.getInputVariableTypes('x')[0]; 24 | 25 | var outVarName = this.getOutputVariableNames('y')[0]; 26 | var outVarType = this.getOutputTypes('y')[0]; 27 | 28 | if(inVarNameA && outVarName){ 29 | return outVarName + ' = length(' + inVarNameA + ');'; 30 | } else if(outVarName){ 31 | var outType = this.getOutputTypes('y')[0]; 32 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 33 | } else { 34 | return ''; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/nodes/LogNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = LogNode; 5 | 6 | function LogNode(options){ 7 | options = options || {}; 8 | options.functionName = 'log'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | LogNode.prototype = Object.create(MathFunctionNode.prototype); 12 | LogNode.prototype.constructor = LogNode; 13 | 14 | Node.registerClass('log', LogNode); 15 | 16 | LogNode.BASE_E = 'log'; 17 | LogNode.BASE_2 = 'log2'; 18 | 19 | LogNode.prototype.setBase = function(base){ 20 | this.functionName = base; 21 | }; -------------------------------------------------------------------------------- /src/nodes/MathFunctionNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = MathFunctionNode; 5 | 6 | function MathFunctionNode(options){ 7 | options = options || {}; 8 | this.functionName = options.functionName || 'f'; 9 | Node.call(this, options); 10 | } 11 | MathFunctionNode.prototype = Object.create(Node.prototype); 12 | MathFunctionNode.prototype.constructor = MathFunctionNode; 13 | 14 | MathFunctionNode.supportedTypes = [ 15 | 'float', 16 | 'vec2', 17 | 'vec3', 18 | 'vec4' 19 | ]; 20 | 21 | MathFunctionNode.prototype.getInputPorts = function(key){ 22 | return ['x']; 23 | }; 24 | 25 | MathFunctionNode.prototype.getOutputPorts = function(key){ 26 | return ['y']; 27 | }; 28 | 29 | // Output type is same as what we get in. 30 | MathFunctionNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'y'){ 33 | types = this.inputPortIsConnected('x') ? this.getInputVariableTypes('x') : ['float']; 34 | } 35 | return types; 36 | }; 37 | 38 | MathFunctionNode.prototype.getInputTypes = function(key){ 39 | return key === 'x' ? MathFunctionNode.supportedTypes : []; 40 | }; 41 | 42 | MathFunctionNode.prototype.render = function(){ 43 | var outVarName = this.getOutputVariableNames('y')[0]; 44 | var outVarType = this.getOutputTypes('y')[0]; 45 | 46 | var inVarName = this.getInputVariableName('x'); 47 | var inVarType = this.getInputVariableTypes('x')[0]; 48 | 49 | if(outVarName && inVarName){ 50 | return outVarName + ' = ' + this.functionName + '(' + Utils.convertGlslType(inVarName, inVarType, outVarType) + ');'; 51 | } else if(outVarName){ 52 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outVarType) + ';'; 53 | } else { 54 | return ''; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/nodes/Matrix4Node.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = Matrix4Node; 4 | 5 | // A vector with four components/values. 6 | function Matrix4Node(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | this.value = options.value ? options.value.slice(0) : [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; 10 | } 11 | Matrix4Node.prototype = Object.create(Node.prototype); 12 | Matrix4Node.prototype.constructor = Matrix4Node; 13 | 14 | Node.registerClass('mat4', Matrix4Node); 15 | 16 | Matrix4Node.prototype.getInputPorts = function(){ 17 | return []; 18 | }; 19 | 20 | Matrix4Node.prototype.getOutputPorts = function(){ 21 | return ['value']; 22 | }; 23 | 24 | Matrix4Node.prototype.getOutputTypes = function(key){ 25 | return key === 'value' ? ['mat4'] : []; 26 | }; 27 | 28 | Matrix4Node.prototype.render = function(){ 29 | var outVarName = this.getOutputVariableNames('value')[0]; 30 | return outVarName ? outVarName + ' = mat4(' + this.value.join(',') + ');' : ''; 31 | }; 32 | -------------------------------------------------------------------------------- /src/nodes/MaxNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = MaxNode; 5 | 6 | function MaxNode(options){ 7 | options = options || {}; 8 | options.functionName = 'max'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | MaxNode.prototype = Object.create(MathFunctionNode.prototype); 12 | MaxNode.prototype.constructor = MaxNode; 13 | 14 | Node.registerClass('max', MaxNode); -------------------------------------------------------------------------------- /src/nodes/MinNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = MinNode; 5 | 6 | function MinNode(options){ 7 | options = options || {}; 8 | options.functionName = 'min'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | MinNode.prototype = Object.create(MathFunctionNode.prototype); 12 | MinNode.prototype.constructor = MinNode; 13 | 14 | Node.registerClass('min', MinNode); -------------------------------------------------------------------------------- /src/nodes/MixNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = MixNode; 5 | 6 | function MixNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | MixNode.prototype = Object.create(Node.prototype); 11 | MixNode.prototype.constructor = MixNode; 12 | 13 | Node.registerClass('mix', MixNode); 14 | 15 | MixNode.supportedTypes = [ 16 | 'float', 17 | 'vec2', 18 | 'vec3', 19 | 'vec4' 20 | ]; 21 | 22 | MixNode.prototype.getInputPorts = function(key){ 23 | return ['a', 'b', 't']; 24 | }; 25 | 26 | MixNode.prototype.getOutputPorts = function(key){ 27 | return ['out']; 28 | }; 29 | 30 | MixNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'out'){ 33 | if(this.anyInputPortIsConnected()){ 34 | // Something is connected to the input - choose the vector type of largest dimension 35 | types = [ 36 | Utils.getHighestDimensionVectorType( 37 | this.getInputVariableTypes('a') 38 | .concat(this.getInputVariableTypes('b')) 39 | .concat(this.getInputVariableTypes('t')) 40 | ) 41 | ]; 42 | } else { 43 | // Nothing connected - use default float type 44 | types = ['float']; 45 | } 46 | } 47 | return types; 48 | }; 49 | 50 | MixNode.prototype.getInputTypes = function(key){ 51 | return this.getInputPorts().indexOf(key) !== -1 ? MixNode.supportedTypes : []; 52 | }; 53 | 54 | MixNode.prototype.render = function(){ 55 | var inVarNameA = this.getInputVariableName('a'); 56 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 57 | 58 | var inVarNameB = this.getInputVariableName('b'); 59 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 60 | 61 | var inVarNameX = this.getInputVariableName('t'); 62 | var inVarTypeX = this.getInputVariableTypes('t')[0]; 63 | 64 | var outVarName = this.getOutputVariableNames('out')[0]; 65 | var outVarType = this.getOutputTypes('out')[0]; 66 | 67 | if(inVarNameA && inVarNameB && inVarNameX && outVarName){ 68 | return outVarName + ' = mix(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + ',' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + ',' + Utils.convertGlslType(inVarNameX, inVarTypeX, outVarType) + ');'; 69 | } else if(outVarName){ 70 | var outType = this.getOutputTypes('out')[0]; 71 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 72 | } else { 73 | return ''; 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/nodes/ModNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var OperatorNode = require('./OperatorNode'); 3 | var Utils = require('../Utils'); 4 | 5 | module.exports = ModNode; 6 | 7 | function ModNode(options){ 8 | options = options || {}; 9 | options.functionName = 'mod'; 10 | OperatorNode.call(this, options); 11 | } 12 | ModNode.prototype = Object.create(OperatorNode.prototype); 13 | ModNode.prototype.constructor = ModNode; 14 | 15 | Node.registerClass('mod', ModNode); 16 | 17 | ModNode.prototype.render = function(){ 18 | var inVarNameA = this.getInputVariableName('a'); 19 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 20 | 21 | var inVarNameB = this.getInputVariableName('b'); 22 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 23 | 24 | var outVarName = this.getOutputVariableNames('out')[0]; 25 | var outVarType = this.getOutputTypes('out')[0]; 26 | 27 | if(inVarNameA && inVarNameB && outVarName){ 28 | return outVarName + ' = mod(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + ',' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + ');'; 29 | } else if(outVarName){ 30 | var outType = this.getOutputTypes('out')[0]; 31 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 32 | } else { 33 | return ''; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/nodes/MultiplyNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var OperatorNode = require('./OperatorNode'); 3 | var Utils = require('../Utils'); 4 | 5 | module.exports = MultiplyNode; 6 | 7 | function MultiplyNode(options){ 8 | options = options || {}; 9 | options.operator = '*'; 10 | OperatorNode.call(this, options); 11 | } 12 | MultiplyNode.prototype = Object.create(OperatorNode.prototype); 13 | MultiplyNode.prototype.constructor = MultiplyNode; 14 | 15 | Node.registerClass('multiply', MultiplyNode); -------------------------------------------------------------------------------- /src/nodes/Node.js: -------------------------------------------------------------------------------- 1 | var Connection = require('../Connection'); 2 | 3 | module.exports = Node; 4 | 5 | function Node(options){ 6 | options = options || {}; 7 | if(options.id){ 8 | Node._idCounter = Math.max(options.id + 1, Node._idCounter); 9 | this.id = options.id; 10 | } else { 11 | this.id = Node._idCounter++; 12 | } 13 | this.name = options.name || 'Unnamed node'; 14 | } 15 | 16 | Node._idCounter = 1; 17 | 18 | Node.classes = {}; 19 | 20 | Node.registerClass = function(key, constructor){ 21 | constructor.type = key; 22 | Node.classes[key] = constructor; 23 | }; 24 | 25 | Node.prototype.getInputPorts = function(key){ 26 | return []; 27 | }; 28 | 29 | Node.prototype.getOutputPorts = function(key){ 30 | return []; 31 | }; 32 | 33 | Node.prototype.getInputTypes = function(key){ 34 | return []; 35 | }; 36 | 37 | Node.prototype.getOutputTypes = function(key){ 38 | return []; 39 | }; 40 | 41 | Node.prototype.inputPortIsValid = function(key){ 42 | return true; 43 | }; 44 | 45 | Node.prototype.outputPortIsValid = function(key){ 46 | return true; 47 | }; 48 | 49 | Node.prototype.canBuildShader = function(){ 50 | return false; 51 | }; 52 | 53 | Node.prototype.outputPortIsConnected = function(key){ 54 | return this.graph.outputPortIsConnected(this, key); 55 | }; 56 | 57 | Node.prototype.inputPortIsConnected = function(key){ 58 | return this.graph.inputPortIsConnected(this, key); 59 | }; 60 | 61 | Node.prototype.anyInputPortIsConnected = function(){ 62 | return this.getInputPorts().some(function(key){ 63 | return this.graph.inputPortIsConnected(this, key); 64 | }, this); 65 | }; 66 | 67 | Node.prototype.getOutputVariableNames = function(key){ 68 | return this.outputPortIsConnected(key) ? [key + this.id] : []; // todo really an array? 69 | }; 70 | 71 | Node.prototype.getInputVariableName = function(key){ 72 | var connectedNode = this.graph.getNodeConnectedToInputPort(this, key); 73 | if(connectedNode){ 74 | var portKey = this.graph.getPortKeyConnectedToInputPort(this, key); 75 | return portKey + connectedNode.id; 76 | } 77 | }; 78 | 79 | Node.prototype.getInputVariableTypes = function(key){ 80 | var connectedNode = this.graph.getNodeConnectedToInputPort(this, key); 81 | if(connectedNode){ 82 | var portKey = this.graph.getPortKeyConnectedToInputPort(this, key); 83 | return connectedNode.getOutputTypes(portKey); 84 | } 85 | return []; 86 | }; 87 | 88 | Node.prototype.buildShader = function(){ 89 | return this.graph.buildShader(this); 90 | }; 91 | 92 | Node.prototype._getConnectError = function(key, targetNode, targetPortKey){ 93 | if(!this.graph){ 94 | return 'Node must be added to a Graph to be connected.'; 95 | } 96 | 97 | if(targetNode === this){ 98 | return 'Cannot connect the node to itself'; 99 | } 100 | 101 | if(this.getInputPorts().indexOf(key) === -1){ 102 | return this.name + ' does not have input port ' + key; 103 | } 104 | 105 | // Check if they have a type in common 106 | var outputTypes = targetNode.getOutputTypes(targetPortKey); 107 | var inputTypes = this.getInputTypes(key); 108 | var hasSharedType = outputTypes.some(function(type){ 109 | return inputTypes.indexOf(type) !== -1; 110 | }); 111 | if(!outputTypes.length || !inputTypes.length || !hasSharedType){ 112 | return 'the ports do not have a shared type. InputTypes: ' + inputTypes.join(',') + ', Outputtypes: ' + outputTypes.join(','); 113 | } 114 | 115 | if(targetNode.getOutputPorts().indexOf(targetPortKey) === -1){ 116 | return targetNode.name + ' does not have output port ' + targetPortKey; 117 | } 118 | }; 119 | 120 | Node.prototype.canConnect = function(key, targetNode, targetPortKey){ 121 | var errorMessage = this._getConnectError(key, targetNode, targetPortKey); 122 | this.errorMessage = errorMessage; 123 | return errorMessage ? false : true; 124 | }; 125 | 126 | Node.prototype.connect = function(key, targetNode, targetPortKey){ 127 | var errorMessage = this._getConnectError(key, targetNode, targetPortKey); 128 | 129 | if(errorMessage){ 130 | throw new Error(errorMessage); 131 | } 132 | 133 | this.graph.addConnection(new Connection({ 134 | fromNode: targetNode, 135 | fromPortKey: targetPortKey, 136 | toNode: this, 137 | toPortKey: key 138 | })); 139 | }; 140 | 141 | // todo 142 | Node.prototype.disconnect = function(key, targetNode, targetPortKey){ 143 | var conn = this.graph.connections.find(function(c){ 144 | return ( 145 | c.fromNode === targetNode && 146 | c.fromPortKey === targetPortKey && 147 | c.toNode === this && 148 | c.toPortKey === key 149 | ); 150 | }, this); 151 | if(conn) 152 | this.graph.removeConnection(conn); 153 | }; 154 | 155 | Node.prototype.getAttributes = function(){ 156 | return []; 157 | }; 158 | 159 | Node.prototype.getUniforms = function(){ 160 | return []; 161 | }; 162 | 163 | Node.prototype.getUniforms = function(){ 164 | return []; 165 | }; 166 | 167 | Node.prototype.getVaryings = function(){ 168 | return []; 169 | }; 170 | 171 | Node.prototype.getProcessors = function(){ 172 | return []; 173 | }; 174 | 175 | Node.prototype.render = function(){ 176 | return ''; 177 | }; 178 | 179 | Node.prototype.getBuilder = function(){}; 180 | 181 | Node.prototype.buildShader = function(){ 182 | return function(){ 183 | this.graph.sortNodes(); 184 | return [ 185 | this.graph.renderAttrubuteDeclarations(), 186 | this.graph.renderUniformDeclarations(), 187 | 'void main(void){', 188 | this.graph.renderConnectionVariableDeclarations(), 189 | this.graph.renderNodeCodes(), 190 | '{', 191 | //this.mainNode.render(), 192 | '}', 193 | '}' 194 | ].join('\n'); 195 | 196 | }.bind(this); 197 | }; -------------------------------------------------------------------------------- /src/nodes/NormalizeNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = NormalizeNode; 5 | 6 | function NormalizeNode(options){ 7 | options = options || {}; 8 | options.functionName = 'normalize'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | NormalizeNode.prototype = Object.create(MathFunctionNode.prototype); 12 | NormalizeNode.prototype.constructor = NormalizeNode; 13 | 14 | Node.registerClass('normalize', NormalizeNode); -------------------------------------------------------------------------------- /src/nodes/OperatorNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = OperatorNode; 5 | 6 | function OperatorNode(options){ 7 | options = options || {}; 8 | this.operator = options.operator || '*'; 9 | Node.call(this, options); 10 | } 11 | OperatorNode.prototype = Object.create(Node.prototype); 12 | OperatorNode.prototype.constructor = OperatorNode; 13 | 14 | OperatorNode.supportedTypes = [ 15 | 'float', 16 | 'vec2', 17 | 'vec3', 18 | 'vec4' 19 | ]; 20 | 21 | OperatorNode.prototype.getInputPorts = function(key){ 22 | return ['a', 'b']; 23 | }; 24 | 25 | OperatorNode.prototype.getOutputPorts = function(key){ 26 | return ['out']; 27 | }; 28 | 29 | OperatorNode.prototype.getOutputTypes = function(key){ 30 | var types = []; 31 | if(key === 'out'){ 32 | if(this.inputPortIsConnected('a') || this.inputPortIsConnected('b')){ 33 | // Something is connected to the input - choose the vector type of largest dimension 34 | types = [ 35 | Utils.getHighestDimensionVectorType( 36 | this.getInputVariableTypes('a').concat(this.getInputVariableTypes('b')) 37 | ) 38 | ]; 39 | } else { 40 | // Nothing connected - use default float type 41 | types = ['float']; 42 | } 43 | } 44 | return types; 45 | }; 46 | 47 | OperatorNode.prototype.getInputTypes = function(key){ 48 | return (key === 'a' || key === 'b') ? OperatorNode.supportedTypes : []; 49 | }; 50 | 51 | OperatorNode.prototype.render = function(){ 52 | var inVarNameA = this.getInputVariableName('a'); 53 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 54 | 55 | var inVarNameB = this.getInputVariableName('b'); 56 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 57 | 58 | var outVarName = this.getOutputVariableNames('out')[0]; 59 | var outVarType = this.getOutputTypes('out')[0]; 60 | 61 | if(inVarNameA && inVarNameB && outVarName){ 62 | return outVarName + ' = ' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + this.operator + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + ';'; 63 | } else if(outVarName){ 64 | var outType = this.getOutputTypes('out')[0]; 65 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 66 | } else { 67 | return ''; 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /src/nodes/PositionNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = PositionNode; 4 | 5 | function PositionNode(options){ 6 | options = options || {}; 7 | Node.call(this, options); 8 | } 9 | PositionNode.prototype = Object.create(Node.prototype); 10 | PositionNode.prototype.constructor = PositionNode; 11 | 12 | Node.registerClass('position', PositionNode); 13 | 14 | PositionNode.prototype.buildShader = function(){ 15 | return function(){ 16 | this.graph.sortNodes(); 17 | return [ 18 | this.graph.renderVaryingDeclarations(), 19 | this.graph.renderAttrubuteDeclarations(), 20 | this.graph.renderUniformDeclarations(), 21 | 'void main(void){', 22 | this.graph.renderConnectionVariableDeclarations(), 23 | this.graph.renderNodeCodes(), 24 | this.graph.renderAttributeToVaryingAssignments(), 25 | '{', 26 | 'gl_Position = viewProjectionMatrix * worldMatrix * vec4(vertexPosition, 1.0);', 27 | '}', 28 | '}' 29 | ].join('\n'); 30 | 31 | }.bind(this); 32 | }; -------------------------------------------------------------------------------- /src/nodes/PowNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = PowNode; 5 | 6 | function PowNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | PowNode.prototype = Object.create(Node.prototype); 11 | PowNode.prototype.constructor = PowNode; 12 | 13 | Node.registerClass('pow', PowNode); 14 | 15 | PowNode.supportedTypes = [ 16 | 'float', 17 | 'vec2', 18 | 'vec3', 19 | 'vec4' 20 | ]; 21 | 22 | PowNode.prototype.getInputPorts = function(key){ 23 | return ['val', 'exp']; 24 | }; 25 | 26 | PowNode.prototype.getOutputPorts = function(key){ 27 | return ['out']; 28 | }; 29 | 30 | PowNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'out'){ 33 | if(this.inputPortIsConnected('val') || this.inputPortIsConnected('exp')){ 34 | // Something is connected to the input - choose the vector type of largest dimension 35 | types = [ 36 | Utils.getHighestDimensionVectorType( 37 | this.getInputVariableTypes('val').concat(this.getInputVariableTypes('exp')) 38 | ) 39 | ]; 40 | } else { 41 | // Nothing connected - use default float type 42 | types = ['float']; 43 | } 44 | } 45 | return types; 46 | }; 47 | 48 | PowNode.prototype.getInputTypes = function(key){ 49 | return (key === 'val' || key === 'exp') ? PowNode.supportedTypes : []; 50 | }; 51 | 52 | PowNode.prototype.render = function(){ 53 | var inVarNameA = this.getInputVariableName('val'); 54 | var inVarTypeA = this.getInputVariableTypes('val')[0]; 55 | 56 | var inVarNameB = this.getInputVariableName('exp'); 57 | var inVarTypeB = this.getInputVariableTypes('exp')[0]; 58 | 59 | var outVarName = this.getOutputVariableNames('out')[0]; 60 | var outVarType = this.getOutputTypes('out')[0]; 61 | 62 | if(inVarNameA && inVarNameB && outVarName){ 63 | return outVarName + ' = pow(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + ',' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + ');'; 64 | } else if(outVarName){ 65 | var outType = this.getOutputTypes('out')[0]; 66 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 67 | } else { 68 | return ''; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/nodes/ReciprocalNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = ReciprocalNode; 5 | 6 | function ReciprocalNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | ReciprocalNode.prototype = Object.create(Node.prototype); 11 | ReciprocalNode.prototype.constructor = ReciprocalNode; 12 | 13 | Node.registerClass('reciprocal', ReciprocalNode); 14 | 15 | ReciprocalNode.supportedTypes = [ 16 | 'float', 17 | 'vec2', 18 | 'vec3', 19 | 'vec4' 20 | ]; 21 | 22 | ReciprocalNode.prototype.getInputPorts = function(key){ 23 | return ['x']; 24 | }; 25 | 26 | ReciprocalNode.prototype.getOutputPorts = function(key){ 27 | return ['y']; 28 | }; 29 | 30 | // Output type is same as what we get in. 31 | ReciprocalNode.prototype.getOutputTypes = function(key){ 32 | var types = []; 33 | if(key === 'y'){ 34 | types = this.inputPortIsConnected('x') ? this.getInputVariableTypes('x') : ['float']; 35 | } 36 | return types; 37 | }; 38 | 39 | ReciprocalNode.prototype.getInputTypes = function(key){ 40 | return key === 'x' ? ReciprocalNode.supportedTypes : []; 41 | }; 42 | 43 | ReciprocalNode.prototype.render = function(){ 44 | var outVarName = this.getOutputVariableNames('y')[0]; 45 | var outVarType = this.getOutputTypes('y')[0]; 46 | 47 | var inVarName = this.getInputVariableName('x'); 48 | var inVarType = this.getInputVariableTypes('x')[0]; 49 | 50 | if(outVarName && inVarName){ 51 | return outVarName + ' = ' + inVarType + '(1) / (' + Utils.convertGlslType(inVarName, inVarType, outVarType) + ');'; 52 | } else if(outVarName){ 53 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outVarType) + ';'; 54 | } else { 55 | return ''; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/nodes/RelayNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = RelayNode; 4 | 5 | // A vector with four components/values. 6 | function RelayNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | RelayNode.prototype = Object.create(Node.prototype); 11 | RelayNode.prototype.constructor = RelayNode; 12 | 13 | RelayNode.supportedTypes = [ 14 | 'float', 15 | 'vec2', 16 | 'vec3', 17 | 'vec4' 18 | ]; 19 | 20 | Node.registerClass('relay', RelayNode); 21 | 22 | RelayNode.prototype.getInputPorts = function(){ 23 | return ['in']; 24 | }; 25 | 26 | RelayNode.prototype.getOutputPorts = function(){ 27 | return ['out']; 28 | }; 29 | 30 | // Output type is same as what we get in. 31 | RelayNode.prototype.getOutputTypes = function(key){ 32 | var types = []; 33 | if(key === 'out'){ 34 | types = this.inputPortIsConnected('in') ? this.getInputVariableTypes('in') : ['float']; 35 | } 36 | return types; 37 | }; 38 | 39 | RelayNode.prototype.getInputTypes = function(key){ 40 | return key === 'in' ? RelayNode.supportedTypes : []; 41 | }; 42 | 43 | 44 | RelayNode.prototype.render = function(){ 45 | var outVarName = this.getOutputVariableNames('out')[0]; 46 | var inVarName = this.getInputVariableName('in'); 47 | return outVarName ? outVarName + ' = ' + inVarName + ';' : ''; 48 | }; 49 | -------------------------------------------------------------------------------- /src/nodes/RoundNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = RoundNode; 5 | 6 | function RoundNode(options){ 7 | options = options || {}; 8 | options.functionName = 'round'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | RoundNode.prototype = Object.create(MathFunctionNode.prototype); 12 | RoundNode.prototype.constructor = RoundNode; 13 | 14 | Node.registerClass('round', RoundNode); -------------------------------------------------------------------------------- /src/nodes/SignNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = SignNode; 5 | 6 | function SignNode(options){ 7 | options = options || {}; 8 | options.functionName = 'sign'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | SignNode.prototype = Object.create(MathFunctionNode.prototype); 12 | SignNode.prototype.constructor = SignNode; 13 | 14 | Node.registerClass('sign', SignNode); -------------------------------------------------------------------------------- /src/nodes/SineNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = SineNode; 5 | 6 | function SineNode(options){ 7 | options = options || {}; 8 | options.functionName = 'sin'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | SineNode.prototype = Object.create(MathFunctionNode.prototype); 12 | SineNode.prototype.constructor = SineNode; 13 | 14 | Node.registerClass('sine', SineNode); -------------------------------------------------------------------------------- /src/nodes/SmoothStepNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = SmoothStepNode; 5 | 6 | function SmoothStepNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | SmoothStepNode.prototype = Object.create(Node.prototype); 11 | SmoothStepNode.prototype.constructor = SmoothStepNode; 12 | 13 | Node.registerClass('smoothstep', SmoothStepNode); 14 | 15 | SmoothStepNode.supportedTypes = [ 16 | 'float', 17 | 'vec2', 18 | 'vec3', 19 | 'vec4' 20 | ]; 21 | 22 | SmoothStepNode.prototype.getInputPorts = function(key){ 23 | return ['a', 'b', 'x']; 24 | }; 25 | 26 | SmoothStepNode.prototype.getOutputPorts = function(key){ 27 | return ['out']; 28 | }; 29 | 30 | SmoothStepNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'out'){ 33 | if(this.inputPortIsConnected('a') || this.inputPortIsConnected('b') || this.inputPortIsConnected('x')){ 34 | // Something is connected to the input - choose the vector type of largest dimension 35 | types = [ 36 | Utils.getHighestDimensionVectorType( 37 | this.getInputVariableTypes('a').concat(this.getInputVariableTypes('b')).concat(this.getInputVariableTypes('x')) 38 | ) 39 | ]; 40 | } else { 41 | // Nothing connected - use default float type 42 | types = ['float']; 43 | } 44 | } 45 | return types; 46 | }; 47 | 48 | SmoothStepNode.prototype.getInputTypes = function(key){ 49 | return (key === 'a' || key === 'b' || key === 'x') ? SmoothStepNode.supportedTypes : []; 50 | }; 51 | 52 | SmoothStepNode.prototype.render = function(){ 53 | var inVarNameA = this.getInputVariableName('a'); 54 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 55 | 56 | var inVarNameB = this.getInputVariableName('b'); 57 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 58 | 59 | var inVarNameX = this.getInputVariableName('x'); 60 | var inVarTypeX = this.getInputVariableTypes('x')[0]; 61 | 62 | var outVarName = this.getOutputVariableNames('out')[0]; 63 | var outVarType = this.getOutputTypes('out')[0]; 64 | 65 | if(inVarNameA && inVarNameB && inVarNameX && outVarName){ 66 | return outVarName + ' = smoothstep(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + ',' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType)+ ',' + Utils.convertGlslType(inVarNameX, inVarTypeX, outVarType) + ');'; 67 | } else if(outVarName){ 68 | var outType = this.getOutputTypes('out')[0]; 69 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 70 | } else { 71 | return ''; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/nodes/SqrtNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = SqrtNode; 5 | 6 | function SqrtNode(options){ 7 | options = options || {}; 8 | options.functionName = 'sqrt'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | SqrtNode.prototype = Object.create(MathFunctionNode.prototype); 12 | SqrtNode.prototype.constructor = SqrtNode; 13 | 14 | Node.registerClass('sqrt', SqrtNode); -------------------------------------------------------------------------------- /src/nodes/StepNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = StepNode; 5 | 6 | function StepNode(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | } 10 | StepNode.prototype = Object.create(Node.prototype); 11 | StepNode.prototype.constructor = StepNode; 12 | 13 | Node.registerClass('step', StepNode); 14 | 15 | StepNode.supportedTypes = [ 16 | 'float', 17 | 'vec2', 18 | 'vec3', 19 | 'vec4' 20 | ]; 21 | 22 | StepNode.prototype.getInputPorts = function(key){ 23 | return ['a', 'b']; 24 | }; 25 | 26 | StepNode.prototype.getOutputPorts = function(key){ 27 | return ['out']; 28 | }; 29 | 30 | StepNode.prototype.getOutputTypes = function(key){ 31 | var types = []; 32 | if(key === 'out'){ 33 | if(this.inputPortIsConnected('a') || this.inputPortIsConnected('b')){ 34 | // Something is connected to the input - choose the vector type of largest dimension 35 | types = [ 36 | Utils.getHighestDimensionVectorType( 37 | this.getInputVariableTypes('a').concat(this.getInputVariableTypes('b')) 38 | ) 39 | ]; 40 | } else { 41 | // Nothing connected - use default float type 42 | types = ['float']; 43 | } 44 | } 45 | return types; 46 | }; 47 | 48 | StepNode.prototype.getInputTypes = function(key){ 49 | return (key === 'a' || key === 'b') ? StepNode.supportedTypes : []; 50 | }; 51 | 52 | StepNode.prototype.render = function(){ 53 | var inVarNameA = this.getInputVariableName('a'); 54 | var inVarTypeA = this.getInputVariableTypes('a')[0]; 55 | 56 | var inVarNameB = this.getInputVariableName('b'); 57 | var inVarTypeB = this.getInputVariableTypes('b')[0]; 58 | 59 | var outVarName = this.getOutputVariableNames('out')[0]; 60 | var outVarType = this.getOutputTypes('out')[0]; 61 | 62 | if(inVarNameA && inVarNameB && outVarName){ 63 | return outVarName + ' = step(' + Utils.convertGlslType(inVarNameA, inVarTypeA, outVarType) + ',' + Utils.convertGlslType(inVarNameB, inVarTypeB, outVarType) + ');'; 64 | } else if(outVarName){ 65 | var outType = this.getOutputTypes('out')[0]; 66 | return outVarName + ' = ' + Utils.convertGlslType('0.0', 'float', outType) + ';'; 67 | } else { 68 | return ''; 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/nodes/SubtractNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var OperatorNode = require('./OperatorNode'); 3 | var Utils = require('../Utils'); 4 | 5 | module.exports = SubtractNode; 6 | 7 | function SubtractNode(options){ 8 | options = options || {}; 9 | options.operator = '-'; 10 | OperatorNode.call(this, options); 11 | } 12 | SubtractNode.prototype = Object.create(OperatorNode.prototype); 13 | SubtractNode.prototype.constructor = SubtractNode; 14 | 15 | Node.registerClass('subtract', SubtractNode); -------------------------------------------------------------------------------- /src/nodes/TanNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = TanNode; 5 | 6 | function TanNode(options){ 7 | options = options || {}; 8 | options.functionName = 'tan'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | TanNode.prototype = Object.create(MathFunctionNode.prototype); 12 | TanNode.prototype.constructor = TanNode; 13 | 14 | Node.registerClass('tan', TanNode); -------------------------------------------------------------------------------- /src/nodes/TextureNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | var Attribute = require('../Attribute'); 4 | var Varying = require('../Varying'); 5 | 6 | module.exports = TextureNode; 7 | 8 | function TextureNode(options){ 9 | options = options || {}; 10 | Node.call(this, options); 11 | } 12 | TextureNode.prototype = Object.create(Node.prototype); 13 | TextureNode.prototype.constructor = TextureNode; 14 | 15 | Node.registerClass('texture', TextureNode); 16 | 17 | TextureNode.prototype.getInputPorts = function(key){ 18 | return ['uv']; 19 | }; 20 | 21 | TextureNode.prototype.getInputTypes = function(key){ 22 | var types = []; 23 | switch(key){ 24 | case 'uv': 25 | types = ['vec2']; 26 | break; 27 | } 28 | return types; 29 | }; 30 | 31 | TextureNode.prototype.getOutputPorts = function(key){ 32 | return ['rgba']; 33 | }; 34 | 35 | TextureNode.prototype.getOutputTypes = function(key){ 36 | var types = []; 37 | switch(key){ 38 | case 'rgba': 39 | types = ['vec4']; 40 | break; 41 | } 42 | return types; 43 | }; 44 | 45 | TextureNode.prototype.getUniforms = function(){ 46 | return [ 47 | new Uniform({ 48 | name: 'texture' + this.id, 49 | type: 'sampler2D', 50 | defaultValue: 'TEXTURE' + this.id 51 | }) 52 | ]; 53 | }; 54 | 55 | TextureNode.prototype.render = function(){ 56 | var source = []; 57 | var outName = this.getOutputVariableNames('rgba')[0]; 58 | var inName = this.getInputVariableName('uv'); 59 | if(outName && inName){ 60 | source.push(outName + ' = texture2D(texture' + this.id + ', vec2(' + inName + '));'); 61 | } else if(outName){ 62 | source.push(outName + ' = vec4(0,0,0,1);'); 63 | } 64 | 65 | return source.join('\n'); 66 | }; 67 | -------------------------------------------------------------------------------- /src/nodes/TimeNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | 4 | module.exports = TimeNode; 5 | 6 | // Adds a vec4 uniform to the shader. 7 | function TimeNode(options){ 8 | options = options || {}; 9 | Node.call(this, options); 10 | } 11 | TimeNode.prototype = Object.create(Node.prototype); 12 | TimeNode.prototype.constructor = TimeNode; 13 | 14 | Node.registerClass('time', TimeNode); 15 | 16 | TimeNode.prototype.getOutputPorts = function(key){ 17 | return ['time']; 18 | }; 19 | 20 | TimeNode.prototype.getOutputTypes = function(key){ 21 | return key === 'time' ? ['float'] : []; 22 | }; 23 | 24 | TimeNode.prototype.getUniforms = function(){ 25 | var uniforms = [ 26 | new Uniform({ 27 | name: 'uTime' + this.id, 28 | defaultValue: 'TIME', 29 | type: 'float' 30 | }) 31 | ]; 32 | return uniforms; 33 | }; 34 | 35 | TimeNode.prototype.render = function(){ 36 | var outVarName = this.getOutputVariableNames('time')[0]; 37 | if(outVarName){ 38 | return outVarName + ' = ' + this.getUniforms()[0].name + ';'; 39 | } else { 40 | return ''; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/nodes/TruncNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var MathFunctionNode = require('./MathFunctionNode'); 3 | 4 | module.exports = TruncNode; 5 | 6 | function TruncNode(options){ 7 | options = options || {}; 8 | options.functionName = 'trunc'; 9 | MathFunctionNode.call(this, options); 10 | } 11 | TruncNode.prototype = Object.create(MathFunctionNode.prototype); 12 | TruncNode.prototype.constructor = TruncNode; 13 | 14 | Node.registerClass('trunc', TruncNode); -------------------------------------------------------------------------------- /src/nodes/UVNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | var Attribute = require('../Attribute'); 4 | var Varying = require('../Varying'); 5 | 6 | module.exports = UVNode; 7 | 8 | function UVNode(options){ 9 | options = options || {}; 10 | Node.call(this, options); 11 | } 12 | UVNode.prototype = Object.create(Node.prototype); 13 | UVNode.prototype.constructor = UVNode; 14 | 15 | Node.registerClass('uv', UVNode); 16 | 17 | UVNode.prototype.getOutputPorts = function(key){ 18 | return [ 19 | 'uv', 20 | 'u', 21 | 'v' 22 | ]; 23 | }; 24 | 25 | UVNode.prototype.getAttributes = function(){ 26 | return [ 27 | new Attribute({ 28 | name: 'vertexUV0', 29 | key: 'TEXCOORD0', 30 | type: 'vec2', 31 | ifdef: 'TEXCOORD0' 32 | }) 33 | ]; 34 | }; 35 | 36 | UVNode.prototype.getVaryings = function(){ 37 | return [ 38 | new Varying({ 39 | type: 'vec2', 40 | name: 'texCoord0', 41 | attributeKey: 'TEXCOORD0' 42 | }) 43 | ]; 44 | }; 45 | 46 | UVNode.prototype.getOutputTypes = function(key){ 47 | var types = []; 48 | switch(key){ 49 | case 'uv': 50 | types = ['vec2']; 51 | break; 52 | case 'u': 53 | case 'v': 54 | types = ['float']; 55 | break; 56 | } 57 | return types; 58 | }; 59 | 60 | UVNode.prototype.render = function(){ 61 | var source = []; 62 | 63 | var uvVarName = this.getOutputVariableNames('uv')[0]; 64 | if(uvVarName){ 65 | source.push(uvVarName + ' = texCoord0;'); 66 | } 67 | 68 | var uVarName = this.getOutputVariableNames('u')[0]; 69 | if(uVarName){ 70 | source.push(uVarName + ' = texCoord0.x;'); 71 | } 72 | 73 | var vVarName = this.getOutputVariableNames('v')[0]; 74 | if(vVarName){ 75 | source.push(vVarName + ' = texCoord0.y;'); 76 | } 77 | 78 | return source.join('\n'); 79 | }; 80 | -------------------------------------------------------------------------------- /src/nodes/UberFragNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = UberFragNode; 4 | 5 | function UberFragNode(){ 6 | Node.call(this, options); 7 | } 8 | UberFragNode.prototype = Object.create(Node.prototype); 9 | UberFragNode.prototype.constructor = UberFragNode; 10 | 11 | UberFragNode.prototype.getInputPorts = function(key){ 12 | return [ 13 | 'diffuse', 14 | 'normal', 15 | 'specular', 16 | 'emissive', 17 | 'alpha', 18 | 'alphakill' 19 | ]; 20 | }; 21 | 22 | UberFragNode.prototype.canBuildShader = function(){ 23 | return true; 24 | }; 25 | 26 | UberFragNode.prototype.canConnect = function(key, targetNode, targetPortKey){ 27 | return Node.prototype.canConnect.apply(this, arguments); 28 | }; 29 | 30 | UberFragNode.prototype.getInputTypes = function(key){ 31 | var types = []; 32 | switch(key){ 33 | case 'diffuse': 34 | types = ['vec4']; 35 | break; 36 | case 'normal': 37 | types = ['vec3']; 38 | break; 39 | case 'specular': 40 | types = ['vec3']; 41 | break; 42 | case 'emissive': 43 | types = ['vec3']; 44 | break; 45 | case 'alpha': 46 | types = ['float']; 47 | break; 48 | case 'alphakill': 49 | types = ['float']; 50 | break; 51 | } 52 | return types; 53 | }; 54 | 55 | UberFragNode.prototype.getProcessors = function(){ 56 | return [ 57 | ShaderBuilder.uber.processor, 58 | ShaderBuilder.light.processor, 59 | ShaderBuilder.animation.processor 60 | ]; 61 | }; 62 | 63 | UberFragNode.prototype.getBuilder = function(){ 64 | return function (shader, shaderInfo) { 65 | ShaderBuilder.light.builder(shader, shaderInfo); 66 | }; 67 | }; 68 | 69 | UberFragNode.prototype.getUniforms = function(){ 70 | var uniforms = [ 71 | // new Uniform({ 72 | // name: 'color' + this.id, 73 | // defaultValue: value.slice(0), 74 | // type: 'vec4' 75 | // }) 76 | ]; 77 | return uniforms; 78 | 79 | // viewProjectionMatrix: Shader.VIEW_PROJECTION_MATRIX, 80 | // worldMatrix: Shader.WORLD_MATRIX, 81 | // normalMatrix: Shader.NORMAL_MATRIX, 82 | // cameraPosition: Shader.CAMERA, 83 | // diffuseMap: Shader.DIFFUSE_MAP, 84 | // offsetRepeat: [0, 0, 1, 1], 85 | // normalMap: Shader.NORMAL_MAP, 86 | // normalMultiplier: 1.0, 87 | // specularMap: Shader.SPECULAR_MAP, 88 | // emissiveMap: Shader.EMISSIVE_MAP, 89 | // aoMap: Shader.AO_MAP, 90 | // lightMap: Shader.LIGHT_MAP, 91 | // environmentCube: 'ENVIRONMENT_CUBE', 92 | // environmentSphere: 'ENVIRONMENT_SPHERE', 93 | // reflectionMap: 'REFLECTION_MAP', 94 | // transparencyMap: 'TRANSPARENCY_MAP', 95 | // opacity: 1.0, 96 | // reflectivity: 0.0, 97 | // refractivity: 0.0, 98 | // etaRatio: -0.5, 99 | // fresnel: 0.0, 100 | // discardThreshold: -0.01, 101 | // fogSettings: [0, 10000], 102 | // fogColor: [1, 1, 1], 103 | // shadowDarkness: 0.5, 104 | // vertexColorAmount: 1.0, 105 | // lodBias: 0.0, 106 | // wrapSettings: [0.5, 0.0] 107 | }; 108 | 109 | UberFragNode.prototype.buildShader = function(){ 110 | return function(){ 111 | this.graph.sortNodes(); 112 | return [ 113 | this.graph.renderAttrubuteDeclarations(), 114 | this.graph.renderUniformDeclarations(), 115 | 'void main(void){', 116 | this.graph.renderConnectionVariableDeclarations(), 117 | this.graph.renderNodeCodes(), 118 | '{', 119 | 'vec4 final_color = vec4(1.0);', 120 | 121 | '#if defined(DIFFUSE_MAP) && defined(TEXCOORD0)', 122 | 'final_color *= texture2D(diffuseMap, texCoord0, lodBias);', 123 | '#endif', 124 | 125 | '#ifdef COLOR', 126 | 'final_color *= mix(vec4(1.0), color, vertexColorAmount);', 127 | '#endif', 128 | 129 | '#if defined(TRANSPARENCY_MAP) && defined(TEXCOORD0)', 130 | '#ifdef TRANSPARENCY_BW', 131 | 'final_color.a = texture2D(transparencyMap, texCoord0).r;', 132 | '#else', 133 | 'final_color.a = texture2D(transparencyMap, texCoord0).a;', 134 | '#endif', 135 | '#endif', 136 | '#ifdef OPACITY', 137 | 'final_color.a *= opacity;', 138 | '#endif', 139 | 140 | '#ifdef DISCARD', 141 | 'if (final_color.a < discardThreshold) discard;', 142 | '#endif', 143 | 144 | '#ifdef AO_MAP', 145 | '#ifdef TEXCOORD1', 146 | 'final_color.rgb *= texture2D(aoMap, texCoord1).rgb;', 147 | '#elif defined(TEXCOORD0)', 148 | 'final_color.rgb *= texture2D(aoMap, texCoord0).rgb;', 149 | '#endif', 150 | '#endif', 151 | 152 | '#ifdef LIGHT_MAP', 153 | '#ifdef TEXCOORD1', 154 | 'final_color.rgb *= texture2D(lightMap, texCoord1).rgb * 2.0;', 155 | '#elif defined(TEXCOORD0)', 156 | 'final_color.rgb *= texture2D(lightMap, texCoord0).rgb * 2.0;', 157 | '#endif', 158 | '#else', 159 | 'vec3 N = vec3(0.0, 1.0, 0.0);', 160 | '#if defined(NORMAL)', // Do nasty doublework for IE compliance 161 | 'N = normalize(normal);', 162 | '#endif', 163 | '#if defined(TANGENT) && defined(NORMAL_MAP) && defined(TEXCOORD0)', 164 | 'mat3 tangentToWorld = mat3(tangent, binormal, normal);', 165 | 'vec3 tangentNormal = texture2D(normalMap, texCoord0, lodBias).xyz * vec3(2.0) - vec3(1.0);', 166 | 'tangentNormal.xy *= normalMultiplier;', 167 | 'vec3 worldNormal = (tangentToWorld * tangentNormal);', 168 | 'N = normalize(worldNormal);', 169 | // '#elif defined(NORMAL)', 170 | // 'N = normalize(normal);', 171 | // '#endif', 172 | '#endif', 173 | 174 | ShaderBuilder.light.fragment, 175 | '#endif', 176 | 177 | '#ifdef REFLECTIVE', 178 | 'if (refractivity > 0.0) {', 179 | 'vec4 environment = vec4(0.0);', 180 | '#ifdef ENVIRONMENT_CUBE', 181 | 'vec3 refractionVector = refract(normalize(viewPosition), N, etaRatio);', 182 | 'refractionVector.x = -refractionVector.x;', 183 | 'environment = textureCube(environmentCube, refractionVector);', 184 | '#elif defined(ENVIRONMENT_SPHERE)', 185 | 'vec3 refractionVector = refract(normalize(viewPosition), N, etaRatio);', 186 | 'refractionVector = -refractionVector;', 187 | 'float xx = (atan(refractionVector.z, refractionVector.x) + M_PI) / (2.0 * M_PI);', 188 | 'float yy = refractionVector.y * 0.5 + 0.5;', 189 | 'environment = texture2D(environmentSphere, vec2(xx, yy));', 190 | '#endif', 191 | 'environment.rgb = mix(clearColor.rgb, environment.rgb, environment.a);', 192 | 193 | 'final_color.rgb = mix(final_color.rgb, environment.rgb, refractivity);', 194 | '}', 195 | 196 | 'if (reflectivity > 0.0) {', 197 | 'vec4 environment = vec4(0.0);', 198 | '#ifdef ENVIRONMENT_CUBE', 199 | 'vec3 reflectionVector = reflect(normalize(viewPosition), N);', 200 | 'reflectionVector.yz = -reflectionVector.yz;', 201 | 'environment = textureCube(environmentCube, reflectionVector);', 202 | '#elif defined(ENVIRONMENT_SPHERE)', 203 | 'vec3 reflectionVector = reflect(normalize(viewPosition), N);', 204 | 'float xx = (atan(reflectionVector.z, reflectionVector.x) + M_PI) / (2.0 * M_PI);', 205 | 'float yy = reflectionVector.y * 0.5 + 0.5;', 206 | 'environment = texture2D(environmentSphere, vec2(xx, yy));', 207 | '#endif', 208 | 'environment.rgb = mix(clearColor.rgb, environment.rgb, environment.a);', 209 | 210 | 'float reflectionAmount = reflectivity;', 211 | '#if defined(REFLECTION_MAP) && defined(TEXCOORD0)', 212 | 'reflectionAmount *= texture2D(reflectionMap, texCoord0).r;', 213 | '#endif', 214 | 215 | 'float fresnelVal = pow(1.0 - abs(dot(normalize(viewPosition), N)), fresnel * 4.0);', 216 | 'reflectionAmount *= fresnelVal;', 217 | 218 | '#if REFLECTION_TYPE == 0', 219 | 'final_color.rgb = mix(final_color.rgb, environment.rgb, reflectionAmount);', 220 | '#elif REFLECTION_TYPE == 1', 221 | 'final_color.rgb += environment.rgb * reflectionAmount;', 222 | '#endif', 223 | 'final_color.a = min(final_color.a + reflectionAmount, 1.0);', 224 | '}', 225 | '#endif', 226 | 227 | '#ifndef LIGHT_MAP', 228 | 'final_color.rgb += totalSpecular;', 229 | 'final_color.a = min(final_color.a + length(totalSpecular) / 3.0, 1.0);', 230 | '#endif', 231 | 232 | '#ifdef FOG', 233 | 'float d = pow(smoothstep(fogSettings.x, fogSettings.y, length(viewPosition)), 1.0);', 234 | 'final_color.rgb = mix(final_color.rgb, fogColor, d);', 235 | '#endif', 236 | 237 | 'gl_FragColor = final_color;', 238 | '}', 239 | '}' 240 | ].join('\n'); 241 | 242 | }.bind(this); 243 | }; -------------------------------------------------------------------------------- /src/nodes/UberVertNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | 4 | module.exports = UberVertNode; 5 | 6 | function UberVertNode(){ 7 | Node.call(this, options); 8 | } 9 | UberVertNode.prototype = Object.create(Node.prototype); 10 | UberVertNode.prototype.constructor = UberVertNode; 11 | 12 | UberVertNode.prototype.canBuildShader = function(){ 13 | return true; 14 | }; 15 | 16 | UberVertNode.prototype.getUniforms = function(){ 17 | var uniforms = [ 18 | new Uniform({ 19 | name: 'viewProjectionMatrix', 20 | defaultValue: 'VIEW_PROJECTION_MATRIX', 21 | type: 'vec3' 22 | }), 23 | new Uniform({ 24 | name: 'worldMatrix', 25 | defaultValue: 'WORLD_MATRIX', 26 | type: 'mat4' 27 | }), 28 | new Uniform({ 29 | name: 'normalMatrix', 30 | defaultValue: 'NORMAL_MATRIX', 31 | type: 'mat3' 32 | }), 33 | new Uniform({ 34 | name: 'cameraPosition', 35 | defaultValue: 'CAMERA', 36 | type: 'vec3' 37 | }) 38 | ]; 39 | return uniforms; 40 | // diffuseMap: Shader.DIFFUSE_MAP, 41 | // offsetRepeat: [0, 0, 1, 1], 42 | // normalMap: Shader.NORMAL_MAP, 43 | // normalMultiplier: 1.0, 44 | // specularMap: Shader.SPECULAR_MAP, 45 | // emissiveMap: Shader.EMISSIVE_MAP, 46 | // aoMap: Shader.AO_MAP, 47 | // lightMap: Shader.LIGHT_MAP, 48 | // environmentCube: 'ENVIRONMENT_CUBE', 49 | // environmentSphere: 'ENVIRONMENT_SPHERE', 50 | // reflectionMap: 'REFLECTION_MAP', 51 | // transparencyMap: 'TRANSPARENCY_MAP', 52 | // opacity: 1.0, 53 | // reflectivity: 0.0, 54 | // refractivity: 0.0, 55 | // etaRatio: -0.5, 56 | // fresnel: 0.0, 57 | // discardThreshold: -0.01, 58 | // fogSettings: [0, 10000], 59 | // fogColor: [1, 1, 1], 60 | // shadowDarkness: 0.5, 61 | // vertexColorAmount: 1.0, 62 | // lodBias: 0.0, 63 | // wrapSettings: [0.5, 0.0] 64 | }; 65 | 66 | UberVertNode.prototype.getAttributes = function(){ 67 | var attribute = [ 68 | new Attribute({ 69 | name: 'vertexPosition', 70 | key: 'POSITION', 71 | type: 'vec3' 72 | }), 73 | new Attribute({ 74 | name: 'vertexNormal', 75 | key: 'NORMAL', 76 | type: 'vec3', 77 | ifdef: 'NORMAL' 78 | }), 79 | new Attribute({ 80 | name: 'vertexTangent', 81 | key: 'TANGENT', 82 | type: 'vec4', 83 | ifdef: 'NORMAL' 84 | }), 85 | new Attribute({ 86 | name: 'vertexColor', 87 | key: 'COLOR', 88 | type: 'vec4', 89 | ifdef: 'COLOR' 90 | }), 91 | new Attribute({ 92 | name: 'vertexUV0', 93 | key: 'TEXCOORD0', 94 | type: 'vec2', 95 | ifdef: 'TEXCOORD0' 96 | }), 97 | new Attribute({ 98 | name: 'vertexUV1', 99 | key: 'TEXCOORD1', 100 | type: 'vec2', 101 | ifdef: 'TEXCOORD1' 102 | }), 103 | new Attribute({ 104 | name: 'vertexJointIDs', 105 | key: 'JOINTIDS', 106 | type: 'vec4', 107 | ifdef: 'JOINTIDS' 108 | }), 109 | new Attribute({ 110 | name: 'vertexWeights', 111 | key: 'WEIGHTS', 112 | type: 'vec4', 113 | ifdef: 'WEIGHTS' 114 | }) 115 | ]; 116 | return attribute; 117 | }; 118 | 119 | UberVertNode.prototype.buildShader = function(){ 120 | return function(){ 121 | this.graph.sortNodes(); 122 | return [ 123 | this.graph.renderAttrubuteDeclarations(), 124 | this.graph.renderUniformDeclarations(), 125 | 'void main(void){', 126 | this.graph.renderConnectionVariableDeclarations(), 127 | this.graph.renderNodeCodes(), 128 | '{', 129 | 'mat4 wMatrix = worldMatrix;', 130 | '#ifdef NORMAL', 131 | 'mat3 nMatrix = normalMatrix;', 132 | '#endif', 133 | ShaderBuilder.animation.vertex, 134 | 'vec4 worldPos = wMatrix * vec4(vertexPosition, 1.0);', 135 | 'vWorldPos = worldPos.xyz;', 136 | 'gl_Position = viewProjectionMatrix * worldPos;', 137 | 138 | 'viewPosition = cameraPosition - worldPos.xyz;', 139 | 140 | '#ifdef NORMAL', 141 | ' normal = normalize(nMatrix * vertexNormal);', 142 | '#endif', 143 | '#ifdef TANGENT', 144 | ' tangent = normalize(nMatrix * vertexTangent.xyz);', 145 | ' binormal = cross(normal, tangent) * vec3(vertexTangent.w);', 146 | '#endif', 147 | '#ifdef COLOR', 148 | ' color = vertexColor;', 149 | '#endif', 150 | '#ifdef TEXCOORD0', 151 | ' texCoord0 = vertexUV0 * offsetRepeat.zw + offsetRepeat.xy;', 152 | '#endif', 153 | '#ifdef TEXCOORD1', 154 | ' texCoord1 = vertexUV1;', 155 | '#endif', 156 | 157 | ShaderBuilder.light.vertex, 158 | '}', 159 | '}' 160 | ].join('\n'); 161 | 162 | }.bind(this); 163 | }; -------------------------------------------------------------------------------- /src/nodes/ValueNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Utils = require('../Utils'); 3 | 4 | module.exports = ValueNode; 5 | 6 | // A vector with four components/values. 7 | function ValueNode(options){ 8 | options = options || {}; 9 | Node.call(this, options); 10 | this.value = options.value || 0; 11 | } 12 | ValueNode.prototype = Object.create(Node.prototype); 13 | ValueNode.prototype.constructor = ValueNode; 14 | 15 | Node.registerClass('value', ValueNode); 16 | 17 | ValueNode.prototype.getOutputPorts = function(key){ 18 | return ['value']; 19 | }; 20 | 21 | ValueNode.prototype.getOutputTypes = function(key){ 22 | return key === 'value' ? ['float'] : []; 23 | }; 24 | 25 | ValueNode.prototype.render = function(){ 26 | var outVarName = this.getOutputVariableNames('value')[0]; 27 | return outVarName ? outVarName + ' = ' + Utils.numberToGlslFloat(this.value) + ';' : ''; 28 | }; 29 | -------------------------------------------------------------------------------- /src/nodes/Vector2Node.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = Vector2Node; 4 | 5 | function Vector2Node(options){ 6 | options = options || {}; 7 | Node.call(this, options); 8 | this.value = options.value ? options.value.slice(0) : [0,0]; 9 | } 10 | Vector2Node.prototype = Object.create(Node.prototype); 11 | Vector2Node.prototype.constructor = Vector2Node; 12 | 13 | Node.registerClass('vec2', Vector2Node); 14 | 15 | Vector2Node.prototype.getInputPorts = function(){ 16 | return []; 17 | }; 18 | 19 | Vector2Node.prototype.getOutputPorts = function(){ 20 | return ['rg']; 21 | }; 22 | 23 | Vector2Node.prototype.getOutputTypes = function(key){ 24 | return key === 'rg' ? ['vec2'] : []; 25 | }; 26 | 27 | Vector2Node.prototype.render = function(){ 28 | var outVarName = this.getOutputVariableNames('rg')[0]; 29 | return outVarName ? outVarName + ' = vec2(' + this.value.join(',') + ');' : ''; 30 | }; 31 | -------------------------------------------------------------------------------- /src/nodes/Vector3Node.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = Vector3Node; 4 | 5 | function Vector3Node(options){ 6 | options = options || {}; 7 | Node.call(this, options); 8 | this.value = options.value ? options.value.slice(0) : [0,0,0]; 9 | } 10 | Vector3Node.prototype = Object.create(Node.prototype); 11 | Vector3Node.prototype.constructor = Vector3Node; 12 | 13 | Node.registerClass('vec3', Vector3Node); 14 | 15 | Vector3Node.prototype.getInputPorts = function(){ 16 | return []; 17 | }; 18 | 19 | Vector3Node.prototype.getOutputPorts = function(){ 20 | return ['rgb']; 21 | }; 22 | 23 | Vector3Node.prototype.getOutputTypes = function(key){ 24 | return key === 'rgb' ? ['vec3'] : []; 25 | }; 26 | 27 | Vector3Node.prototype.render = function(){ 28 | var outVarName = this.getOutputVariableNames('rgb')[0]; 29 | return outVarName ? outVarName + ' = vec3(' + this.value.join(',') + ');' : ''; 30 | }; 31 | -------------------------------------------------------------------------------- /src/nodes/Vector4Node.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | 3 | module.exports = Vector4Node; 4 | 5 | // A vector with four components/values. 6 | function Vector4Node(options){ 7 | options = options || {}; 8 | Node.call(this, options); 9 | this.value = options.value ? options.value.slice(0) : [0,0,0,0]; 10 | } 11 | Vector4Node.prototype = Object.create(Node.prototype); 12 | Vector4Node.prototype.constructor = Vector4Node; 13 | 14 | Node.registerClass('vec4', Vector4Node); 15 | 16 | Vector4Node.prototype.getInputPorts = function(){ 17 | return []; 18 | }; 19 | 20 | Vector4Node.prototype.getOutputPorts = function(){ 21 | return ['rgba']; 22 | }; 23 | 24 | Vector4Node.prototype.getOutputTypes = function(key){ 25 | return key === 'rgba' ? ['vec4'] : []; 26 | }; 27 | 28 | Vector4Node.prototype.render = function(){ 29 | var outVarName = this.getOutputVariableNames('rgba')[0]; 30 | return outVarName ? outVarName + ' = vec4(' + this.value.join(',') + ');' : ''; 31 | }; 32 | -------------------------------------------------------------------------------- /src/nodes/Vector4PropertyNode.js: -------------------------------------------------------------------------------- 1 | var Node = require('./Node'); 2 | var Uniform = require('../Uniform'); 3 | 4 | module.exports = Vector4PropertyNode; 5 | 6 | // Adds a vec4 uniform to the shader. 7 | function Vector4PropertyNode(options){ 8 | options = options || {}; 9 | Node.call(this, options); 10 | this.defaultValue = options.defaultValue ? options.defaultValue.slice(0) : [0,0,0,0]; 11 | } 12 | Vector4PropertyNode.prototype = Object.create(Node.prototype); 13 | Vector4PropertyNode.prototype.constructor = Vector4PropertyNode; 14 | 15 | Node.registerClass('vec4prop', Vector4PropertyNode); 16 | 17 | Vector4PropertyNode.prototype.getOutputPorts = function(key){ 18 | return [ 19 | 'rgba' 20 | ]; 21 | }; 22 | 23 | Vector4PropertyNode.prototype.getOutputTypes = function(key){ 24 | return key === 'rgba' ? ['vec4'] : []; 25 | }; 26 | 27 | Vector4PropertyNode.prototype.getOutputVarNames = function(key){ 28 | return key === 'rgba' ? ['rgba' + this.id] : []; 29 | }; 30 | 31 | Vector4PropertyNode.prototype.getUniforms = function(){ 32 | var value = this.defaultValue; 33 | var uniforms = [ 34 | new Uniform({ 35 | name: 'color' + this.id, 36 | defaultValue: value.slice(0), 37 | type: 'vec4' 38 | }) 39 | ]; 40 | return uniforms; 41 | }; 42 | 43 | Vector4PropertyNode.prototype.render = function(){ 44 | return this.getOutputVarNames('rgba')[0] + ' = ' + this.getUniforms()[0].name + ';'; 45 | }; 46 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var GraphShader = require('../src/GraphShader'); 2 | var Vector4Node = require('../src/nodes/Vector4Node'); 3 | 4 | module.exports = { 5 | GraphShader: { 6 | fragColor: function (test) { 7 | var shader = new GraphShader(); 8 | var fragmentGraph = shader.fragmentGraph; 9 | var vertexGraph = shader.vertexGraph; 10 | 11 | var vectorNode = new Vector4Node({ 12 | defaultValue: [1,1,1,1] 13 | }); 14 | shader.fragmentGraph.addNode(vectorNode); 15 | 16 | test.ok(fragmentGraph.mainNode.canConnect('rgba', vectorNode, 'rgba')); 17 | fragmentGraph.mainNode.connect('rgba', vectorNode, 'rgba'); 18 | 19 | var shaderDef = shader.buildShader(); 20 | 21 | var expectedUniforms = { 22 | viewProjectionMatrix: 'VIEW_PROJECTION_MATRIX', 23 | worldMatrix: 'WORLD_MATRIX' 24 | }; 25 | expectedUniforms['color' + vectorNode.id] = [1, 1, 1, 1]; 26 | test.deepEqual(shaderDef.uniforms, expectedUniforms); 27 | 28 | test.deepEqual(shaderDef.attributes, { 29 | vertexPosition: 'POSITION' 30 | }); 31 | 32 | test.deepEqual(shaderDef.vshader(), [ 33 | 'attribute vec3 vertexPosition;', 34 | 'uniform mat4 viewProjectionMatrix;', 35 | 'uniform mat4 worldMatrix;', 36 | 'void main(void){', 37 | '', 38 | '', 39 | '{', 40 | 'gl_Position = viewProjectionMatrix * worldMatrix * vec4(vertexPosition, 1.0);', 41 | '}', 42 | '}' 43 | ].join('\n')); 44 | 45 | test.deepEqual(shaderDef.fshader(), [ 46 | '', 47 | 'uniform vec4 color' + vectorNode.id + ';', 48 | 'void main(void){', 49 | 'vec4 rgba' + vectorNode.id + ';', 50 | '{', 51 | 'rgba' + vectorNode.id + ' = color' + vectorNode.id + ';', 52 | '}', 53 | '{', 54 | 'gl_FragColor = rgba' + vectorNode.id + ';', 55 | '}', 56 | '}' 57 | ].join('\n')); 58 | 59 | test.done(); 60 | } 61 | } 62 | }; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | 3 | module.exports = { 4 | entry: "./index.js", 5 | output: { 6 | path: __dirname + '/build', 7 | filename: "shadergraph.js" 8 | }, 9 | plugins: [ 10 | //new webpack.optimize.UglifyJsPlugin({ minimize: true }) 11 | ], 12 | module: { 13 | loaders: [ 14 | //{ test: /\.css$/, loader: "style!css" } 15 | ] 16 | } 17 | }; --------------------------------------------------------------------------------