NodeMaterial.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /**
  2. * @author sunag / http://www.sunag.com.br/
  3. */
  4. THREE.NodeMaterial = function ( vertex, fragment ) {
  5. THREE.ShaderMaterial.call( this );
  6. this.defines.UUID = THREE.Math.generateUUID();
  7. this.vertex = vertex || new THREE.RawNode( new THREE.PositionNode( THREE.PositionNode.PROJECTION ) );
  8. this.fragment = fragment || new THREE.RawNode( new THREE.ColorNode( 0xFF0000 ) );
  9. this.updaters = [];
  10. };
  11. THREE.NodeMaterial.types = {
  12. t: 'sampler2D',
  13. tc: 'samplerCube',
  14. bv1: 'bool',
  15. iv1: 'int',
  16. fv1: 'float',
  17. c: 'vec3',
  18. v2: 'vec2',
  19. v3: 'vec3',
  20. v4: 'vec4',
  21. m3: 'mat3',
  22. m4: 'mat4'
  23. };
  24. THREE.NodeMaterial.addShortcuts = function ( proto, prop, list ) {
  25. function applyShortcut( prop, name ) {
  26. return {
  27. get: function () {
  28. return this[ prop ][ name ];
  29. },
  30. set: function ( val ) {
  31. this[ prop ][ name ] = val;
  32. }
  33. };
  34. }
  35. return ( function () {
  36. var shortcuts = {};
  37. for ( var i = 0; i < list.length; ++ i ) {
  38. var name = list[ i ];
  39. shortcuts[ name ] = applyShortcut( prop, name );
  40. }
  41. Object.defineProperties( proto, shortcuts );
  42. } )();
  43. };
  44. THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
  45. THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
  46. THREE.NodeMaterial.prototype.type = "NodeMaterial";
  47. THREE.NodeMaterial.prototype.updateFrame = function ( frame ) {
  48. for ( var i = 0; i < this.updaters.length; ++ i ) {
  49. frame.updateNode( this.updaters[ i ] );
  50. }
  51. };
  52. THREE.NodeMaterial.prototype.onBeforeCompile = function ( shader, renderer ) {
  53. if ( this.needsUpdate ) {
  54. this.build( { dispose: false } );
  55. shader.uniforms = this.uniforms;
  56. shader.vertexShader = this.vertexShader;
  57. shader.fragmentShader = this.fragmentShader;
  58. }
  59. };
  60. THREE.NodeMaterial.prototype.build = function ( params ) {
  61. params = params || {};
  62. params.dispose = params.dispose !== undefined ? params.dispose : true;
  63. var vertex, fragment;
  64. this.nodes = [];
  65. this.defines = { UUID: this.defines.UUID };
  66. this.uniforms = {};
  67. this.attributes = {};
  68. this.extensions = {};
  69. this.nodeData = {};
  70. this.vertexUniform = [];
  71. this.fragmentUniform = [];
  72. this.vars = [];
  73. this.vertexTemps = [];
  74. this.fragmentTemps = [];
  75. this.uniformList = [];
  76. this.consts = [];
  77. this.functions = [];
  78. this.updaters = [];
  79. this.requires = {
  80. uv: [],
  81. color: [],
  82. lights: this.lights,
  83. fog: this.fog
  84. };
  85. this.vertexPars = '';
  86. this.fragmentPars = '';
  87. this.vertexCode = '';
  88. this.fragmentCode = '';
  89. this.vertexNode = '';
  90. this.fragmentNode = '';
  91. this.prefixCode = [
  92. "#ifdef GL_EXT_shader_texture_lod",
  93. " #define texCube(a, b) textureCube(a, b)",
  94. " #define texCubeBias(a, b, c) textureCubeLodEXT(a, b, c)",
  95. " #define tex2D(a, b) texture2D(a, b)",
  96. " #define tex2DBias(a, b, c) texture2DLodEXT(a, b, c)",
  97. "#else",
  98. " #define texCube(a, b) textureCube(a, b)",
  99. " #define texCubeBias(a, b, c) textureCube(a, b, c)",
  100. " #define tex2D(a, b) texture2D(a, b)",
  101. " #define tex2DBias(a, b, c) texture2D(a, b, c)",
  102. "#endif",
  103. "#include <packing>"
  104. ].join( "\n" );
  105. var builder = new THREE.NodeBuilder( this );
  106. vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
  107. fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
  108. if ( this.requires.uv[ 0 ] ) {
  109. this.addVertexPars( 'varying vec2 vUv;' );
  110. this.addFragmentPars( 'varying vec2 vUv;' );
  111. this.addVertexCode( 'vUv = uv;' );
  112. }
  113. if ( this.requires.uv[ 1 ] ) {
  114. this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
  115. this.addFragmentPars( 'varying vec2 vUv2;' );
  116. this.addVertexCode( 'vUv2 = uv2;' );
  117. }
  118. if ( this.requires.color[ 0 ] ) {
  119. this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
  120. this.addFragmentPars( 'varying vec4 vColor;' );
  121. this.addVertexCode( 'vColor = color;' );
  122. }
  123. if ( this.requires.color[ 1 ] ) {
  124. this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
  125. this.addFragmentPars( 'varying vec4 vColor2;' );
  126. this.addVertexCode( 'vColor2 = color2;' );
  127. }
  128. if ( this.requires.position ) {
  129. this.addVertexPars( 'varying vec3 vPosition;' );
  130. this.addFragmentPars( 'varying vec3 vPosition;' );
  131. this.addVertexCode( 'vPosition = transformed;' );
  132. }
  133. if ( this.requires.worldPosition ) {
  134. this.addVertexPars( 'varying vec3 vWPosition;' );
  135. this.addFragmentPars( 'varying vec3 vWPosition;' );
  136. this.addVertexCode( 'vWPosition = ( modelMatrix * vec4( transformed, 1.0 ) ).xyz;' );
  137. }
  138. if ( this.requires.normal ) {
  139. this.addVertexPars( 'varying vec3 vObjectNormal;' );
  140. this.addFragmentPars( 'varying vec3 vObjectNormal;' );
  141. this.addVertexCode( 'vObjectNormal = normal;' );
  142. }
  143. if ( this.requires.worldNormal ) {
  144. this.addVertexPars( 'varying vec3 vWNormal;' );
  145. this.addFragmentPars( 'varying vec3 vWNormal;' );
  146. this.addVertexCode( 'vWNormal = ( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz;' );
  147. }
  148. this.fog = this.requires.fog;
  149. this.lights = this.requires.lights;
  150. this.transparent = this.requires.transparent || this.blending > THREE.NormalBlending;
  151. this.vertexShader = [
  152. this.prefixCode,
  153. this.vertexPars,
  154. this.getCodePars( this.vertexUniform, 'uniform' ),
  155. this.getIncludes( this.consts[ 'vertex' ] ),
  156. this.getIncludes( this.functions[ 'vertex' ] ),
  157. 'void main(){',
  158. this.getCodePars( this.vertexTemps ),
  159. vertex,
  160. this.vertexCode,
  161. '}'
  162. ].join( "\n" );
  163. this.fragmentShader = [
  164. this.prefixCode,
  165. this.fragmentPars,
  166. this.getCodePars( this.fragmentUniform, 'uniform' ),
  167. this.getIncludes( this.consts[ 'fragment' ] ),
  168. this.getIncludes( this.functions[ 'fragment' ] ),
  169. 'void main(){',
  170. this.getCodePars( this.fragmentTemps ),
  171. this.fragmentCode,
  172. fragment,
  173. '}'
  174. ].join( "\n" );
  175. if ( params.dispose ) {
  176. // force update
  177. this.dispose();
  178. }
  179. return this;
  180. };
  181. THREE.NodeMaterial.prototype.define = function ( name, value ) {
  182. this.defines[ name ] = value == undefined ? 1 : value;
  183. };
  184. THREE.NodeMaterial.prototype.isDefined = function ( name ) {
  185. return this.defines[ name ] != undefined;
  186. };
  187. THREE.NodeMaterial.prototype.mergeUniform = function ( uniforms ) {
  188. for ( var name in uniforms ) {
  189. this.uniforms[ name ] = uniforms[ name ];
  190. }
  191. };
  192. THREE.NodeMaterial.prototype.createUniform = function ( type, node, ns, needsUpdate ) {
  193. var index = this.uniformList.length;
  194. var uniform = new THREE.NodeUniform( {
  195. type: type,
  196. name: ns ? ns : 'nVu' + index + '_' + THREE.Math.generateUUID().substr(0, 8),
  197. node: node,
  198. needsUpdate: needsUpdate
  199. } );
  200. this.uniformList.push( uniform );
  201. return uniform;
  202. };
  203. THREE.NodeMaterial.prototype.getVertexTemp = function ( uuid, type, ns ) {
  204. var data = this.vertexTemps[ uuid ];
  205. if ( ! data ) {
  206. var index = this.vertexTemps.length,
  207. name = ns ? ns : 'nVt' + index;
  208. data = { name: name, type: type };
  209. this.vertexTemps.push( data );
  210. this.vertexTemps[ uuid ] = data;
  211. }
  212. return data;
  213. };
  214. THREE.NodeMaterial.prototype.getFragmentTemp = function ( uuid, type, ns ) {
  215. var data = this.fragmentTemps[ uuid ];
  216. if ( ! data ) {
  217. var index = this.fragmentTemps.length,
  218. name = ns ? ns : 'nVt' + index;
  219. data = { name: name, type: type };
  220. this.fragmentTemps.push( data );
  221. this.fragmentTemps[ uuid ] = data;
  222. }
  223. return data;
  224. };
  225. THREE.NodeMaterial.prototype.getVar = function ( uuid, type, ns ) {
  226. var data = this.vars[ uuid ];
  227. if ( ! data ) {
  228. var index = this.vars.length,
  229. name = ns ? ns : 'nVv' + index;
  230. data = { name: name, type: type };
  231. this.vars.push( data );
  232. this.vars[ uuid ] = data;
  233. this.addVertexPars( 'varying ' + type + ' ' + name + ';' );
  234. this.addFragmentPars( 'varying ' + type + ' ' + name + ';' );
  235. }
  236. return data;
  237. };
  238. THREE.NodeMaterial.prototype.getAttribute = function ( name, type ) {
  239. if ( ! this.attributes[ name ] ) {
  240. var varying = this.getVar( name, type );
  241. this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
  242. this.addVertexCode( varying.name + ' = ' + name + ';' );
  243. this.attributes[ name ] = { varying: varying, name: name, type: type };
  244. }
  245. return this.attributes[ name ];
  246. };
  247. THREE.NodeMaterial.prototype.getIncludes = function () {
  248. function sortByPosition( a, b ) {
  249. return a.deps.length - b.deps.length;
  250. }
  251. return function ( incs ) {
  252. if ( ! incs ) return '';
  253. var code = '', incs = incs.sort( sortByPosition );
  254. for ( var i = 0; i < incs.length; i ++ ) {
  255. if ( incs[ i ].src ) code += incs[ i ].src + '\n';
  256. }
  257. return code;
  258. };
  259. }();
  260. THREE.NodeMaterial.prototype.addVertexPars = function ( code ) {
  261. this.vertexPars += code + '\n';
  262. };
  263. THREE.NodeMaterial.prototype.addFragmentPars = function ( code ) {
  264. this.fragmentPars += code + '\n';
  265. };
  266. THREE.NodeMaterial.prototype.addVertexCode = function ( code ) {
  267. this.vertexCode += code + '\n';
  268. };
  269. THREE.NodeMaterial.prototype.addFragmentCode = function ( code ) {
  270. this.fragmentCode += code + '\n';
  271. };
  272. THREE.NodeMaterial.prototype.addVertexNode = function ( code ) {
  273. this.vertexNode += code + '\n';
  274. };
  275. THREE.NodeMaterial.prototype.clearVertexNode = function () {
  276. var code = this.vertexNode;
  277. this.vertexNode = '';
  278. return code;
  279. };
  280. THREE.NodeMaterial.prototype.addFragmentNode = function ( code ) {
  281. this.fragmentNode += code + '\n';
  282. };
  283. THREE.NodeMaterial.prototype.clearFragmentNode = function () {
  284. var code = this.fragmentNode;
  285. this.fragmentNode = '';
  286. return code;
  287. };
  288. THREE.NodeMaterial.prototype.getCodePars = function ( pars, prefix ) {
  289. prefix = prefix || '';
  290. var code = '';
  291. for ( var i = 0, l = pars.length; i < l; ++ i ) {
  292. var parsType = pars[ i ].type;
  293. var parsName = pars[ i ].name;
  294. var parsValue = pars[ i ].value;
  295. if ( parsType == 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
  296. var type = THREE.NodeMaterial.types[ parsType ];
  297. if ( type == undefined ) throw new Error( "Node pars " + parsType + " not found." );
  298. code += prefix + ' ' + type + ' ' + parsName + ';\n';
  299. }
  300. return code;
  301. };
  302. THREE.NodeMaterial.prototype.createVertexUniform = function ( type, node, ns, needsUpdate ) {
  303. var uniform = this.createUniform( type, node, ns, needsUpdate );
  304. this.vertexUniform.push( uniform );
  305. this.vertexUniform[ uniform.name ] = uniform;
  306. this.uniforms[ uniform.name ] = uniform;
  307. return uniform;
  308. };
  309. THREE.NodeMaterial.prototype.createFragmentUniform = function ( type, node, ns, needsUpdate ) {
  310. var uniform = this.createUniform( type, node, ns, needsUpdate );
  311. this.fragmentUniform.push( uniform );
  312. this.fragmentUniform[ uniform.name ] = uniform;
  313. this.uniforms[ uniform.name ] = uniform;
  314. return uniform;
  315. };
  316. THREE.NodeMaterial.prototype.getDataNode = function ( uuid ) {
  317. return this.nodeData[ uuid ] = this.nodeData[ uuid ] || {};
  318. };
  319. THREE.NodeMaterial.prototype.include = function ( builder, node, parent, source ) {
  320. var includes;
  321. node = typeof node === 'string' ? THREE.NodeLib.get( node ) : node;
  322. if ( node instanceof THREE.FunctionNode ) {
  323. includes = this.functions[ builder.shader ] = this.functions[ builder.shader ] || [];
  324. } else if ( node instanceof THREE.ConstNode ) {
  325. includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
  326. }
  327. var included = includes[ node.name ];
  328. if ( ! included ) {
  329. included = includes[ node.name ] = {
  330. node: node,
  331. deps: []
  332. };
  333. includes.push( included );
  334. included.src = node.build( builder, 'source' );
  335. }
  336. if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ] && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
  337. includes[ parent.name ].deps.push( node );
  338. if ( node.includes && node.includes.length ) {
  339. var i = 0;
  340. do {
  341. this.include( builder, node.includes[ i ++ ], parent );
  342. } while ( i < node.includes.length );
  343. }
  344. }
  345. if ( source ) {
  346. included.src = source;
  347. }
  348. };
  349. THREE.NodeMaterial.prototype.toJSON = function ( meta ) {
  350. var isRootObject = ( meta === undefined || typeof meta === 'string' );
  351. if ( isRootObject ) {
  352. meta = {
  353. nodes: {}
  354. };
  355. }
  356. if ( meta && ! meta.materials ) meta.materials = {};
  357. if ( ! meta.materials[ this.uuid ] ) {
  358. var data = {};
  359. data.uuid = this.uuid;
  360. data.type = this.type;
  361. meta.materials[ data.uuid ] = data;
  362. if ( this.name !== "" ) data.name = this.name;
  363. if ( this.blending !== THREE.NormalBlending ) data.blending = this.blending;
  364. if ( this.flatShading === true ) data.flatShading = this.flatShading;
  365. if ( this.side !== THREE.FrontSide ) data.side = this.side;
  366. if ( this.transparent === true ) data.transparent = this.transparent;
  367. data.depthFunc = this.depthFunc;
  368. data.depthTest = this.depthTest;
  369. data.depthWrite = this.depthWrite;
  370. if ( this.wireframe === true ) data.wireframe = this.wireframe;
  371. if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
  372. if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
  373. if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
  374. if ( this.morphTargets === true ) data.morphTargets = true;
  375. if ( this.skinning === true ) data.skinning = true;
  376. data.fog = this.fog;
  377. data.lights = this.lights;
  378. if ( this.visible === false ) data.visible = false;
  379. if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
  380. data.vertex = this.vertex.toJSON( meta ).uuid;
  381. data.fragment = this.fragment.toJSON( meta ).uuid;
  382. }
  383. meta.material = this.uuid;
  384. return meta;
  385. };