| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969 |
- import NodeUniform from './NodeUniform.js';
- import NodeAttribute from './NodeAttribute.js';
- import NodeVarying from './NodeVarying.js';
- import NodeVar from './NodeVar.js';
- import NodeCode from './NodeCode.js';
- import NodeCache from './NodeCache.js';
- import ParameterNode from './ParameterNode.js';
- import StructType from './StructType.js';
- import FunctionNode from '../code/FunctionNode.js';
- import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
- import { getTypeFromLength } from './NodeUtils.js';
- import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
- import {
- NumberNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
- ColorNodeUniform, Matrix2NodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
- } from '../../renderers/common/nodes/NodeUniform.js';
- import { stack } from './StackNode.js';
- import { getCurrentStack, setCurrentStack } from '../tsl/TSLBase.js';
- import CubeRenderTarget from '../../renderers/common/CubeRenderTarget.js';
- import ChainMap from '../../renderers/common/ChainMap.js';
- import BindGroup from '../../renderers/common/BindGroup.js';
- import { REVISION, IntType, UnsignedIntType, LinearFilter, LinearMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapLinearFilter } from '../../constants.js';
- import { RenderTarget } from '../../core/RenderTarget.js';
- import { Color } from '../../math/Color.js';
- import { Vector2 } from '../../math/Vector2.js';
- import { Vector3 } from '../../math/Vector3.js';
- import { Vector4 } from '../../math/Vector4.js';
- import { Float16BufferAttribute } from '../../core/BufferAttribute.js';
- import { warn, error } from '../../utils.js';
- const rendererCache = new WeakMap();
- const typeFromArray = new Map( [
- [ Int8Array, 'int' ],
- [ Int16Array, 'int' ],
- [ Int32Array, 'int' ],
- [ Uint8Array, 'uint' ],
- [ Uint16Array, 'uint' ],
- [ Uint32Array, 'uint' ],
- [ Float32Array, 'float' ]
- ] );
- const toFloat = ( value ) => {
- if ( /e/g.test( value ) ) {
- return String( value ).replace( /\+/g, '' );
- } else {
- value = Number( value );
- return value + ( value % 1 ? '' : '.0' );
- }
- };
- /**
- * Base class for builders which generate a shader program based
- * on a 3D object and its node material definition.
- */
- class NodeBuilder {
- /**
- * Constructs a new node builder.
- *
- * @param {Object3D} object - The 3D object.
- * @param {Renderer} renderer - The current renderer.
- * @param {NodeParser} parser - A reference to a node parser.
- */
- constructor( object, renderer, parser ) {
- /**
- * The 3D object.
- *
- * @type {Object3D}
- */
- this.object = object;
- /**
- * The material of the 3D object.
- *
- * @type {?Material}
- */
- this.material = ( object && object.material ) || null;
- /**
- * The geometry of the 3D object.
- *
- * @type {?BufferGeometry}
- */
- this.geometry = ( object && object.geometry ) || null;
- /**
- * The current renderer.
- *
- * @type {Renderer}
- */
- this.renderer = renderer;
- /**
- * A reference to a node parser.
- *
- * @type {NodeParser}
- */
- this.parser = parser;
- /**
- * The scene the 3D object belongs to.
- *
- * @type {?Scene}
- * @default null
- */
- this.scene = null;
- /**
- * The camera the 3D object is rendered with.
- *
- * @type {?Camera}
- * @default null
- */
- this.camera = null;
- /**
- * A list of all nodes the builder is processing
- * for this 3D object.
- *
- * @type {Array<Node>}
- */
- this.nodes = [];
- /**
- * A list of all sequential nodes.
- *
- * @type {Array<Node>}
- */
- this.sequentialNodes = [];
- /**
- * A list of all nodes which {@link Node#update} method should be executed.
- *
- * @type {Array<Node>}
- */
- this.updateNodes = [];
- /**
- * A list of all nodes which {@link Node#updateBefore} method should be executed.
- *
- * @type {Array<Node>}
- */
- this.updateBeforeNodes = [];
- /**
- * A list of all nodes which {@link Node#updateAfter} method should be executed.
- *
- * @type {Array<Node>}
- */
- this.updateAfterNodes = [];
- /**
- * A dictionary that assigns each node to a unique hash.
- *
- * @type {Object<number,Node>}
- */
- this.hashNodes = {};
- /**
- * A reference to a node material observer.
- *
- * @type {?NodeMaterialObserver}
- * @default null
- */
- this.observer = null;
- /**
- * A reference to the current lights node.
- *
- * @type {?LightsNode}
- * @default null
- */
- this.lightsNode = null;
- /**
- * A reference to the current environment node.
- *
- * @type {?Node}
- * @default null
- */
- this.environmentNode = null;
- /**
- * A reference to the current fog node.
- *
- * @type {?Node}
- * @default null
- */
- this.fogNode = null;
- /**
- * The current clipping context.
- *
- * @type {?ClippingContext}
- */
- this.clippingContext = null;
- /**
- * The generated vertex shader.
- *
- * @type {?string}
- */
- this.vertexShader = null;
- /**
- * The generated fragment shader.
- *
- * @type {?string}
- */
- this.fragmentShader = null;
- /**
- * The generated compute shader.
- *
- * @type {?string}
- */
- this.computeShader = null;
- /**
- * Nodes used in the primary flow of code generation.
- *
- * @type {Object<string,Array<Node>>}
- */
- this.flowNodes = { vertex: [], fragment: [], compute: [] };
- /**
- * Nodes code from `.flowNodes`.
- *
- * @type {Object<string,string>}
- */
- this.flowCode = { vertex: '', fragment: '', compute: '' };
- /**
- * This dictionary holds the node uniforms of the builder.
- * The uniforms are maintained in an array for each shader stage.
- *
- * @type {Object}
- */
- this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
- /**
- * This dictionary holds the output structs of the builder.
- * The structs are maintained in an array for each shader stage.
- *
- * @type {Object}
- */
- this.structs = { vertex: [], fragment: [], compute: [], index: 0 };
- /**
- * This dictionary holds the types of the builder.
- *
- * @type {Object}
- */
- this.types = { vertex: [], fragment: [], compute: [], index: 0 };
- /**
- * This dictionary holds the bindings for each shader stage.
- *
- * @type {Object}
- */
- this.bindings = { vertex: {}, fragment: {}, compute: {} };
- /**
- * This dictionary maintains the binding indices per bind group.
- *
- * @type {Object}
- */
- this.bindingsIndexes = {};
- /**
- * Reference to the array of bind groups.
- *
- * @type {?Array<BindGroup>}
- */
- this.bindGroups = null;
- /**
- * This array holds the node attributes of this builder
- * created via {@link AttributeNode}.
- *
- * @type {Array<NodeAttribute>}
- */
- this.attributes = [];
- /**
- * This array holds the node attributes of this builder
- * created via {@link BufferAttributeNode}.
- *
- * @type {Array<NodeAttribute>}
- */
- this.bufferAttributes = [];
- /**
- * This array holds the node varyings of this builder.
- *
- * @type {Array<NodeVarying>}
- */
- this.varyings = [];
- /**
- * This dictionary holds the (native) node codes of this builder.
- * The codes are maintained in an array for each shader stage.
- *
- * @type {Object<string,Array<NodeCode>>}
- */
- this.codes = {};
- /**
- * This dictionary holds the node variables of this builder.
- * The variables are maintained in an array for each shader stage.
- * This dictionary is also used to count the number of variables
- * according to their type (const, vars).
- *
- * @type {Object<string,Array<NodeVar>|number>}
- */
- this.vars = {};
- /**
- * This dictionary holds the declarations for each shader stage.
- *
- * @type {Object}
- */
- this.declarations = {};
- /**
- * Current code flow.
- * All code generated in this stack will be stored in `.flow`.
- *
- * @type {{code: string}}
- */
- this.flow = { code: '' };
- /**
- * A chain of nodes.
- * Used to check recursive calls in node-graph.
- *
- * @type {Array<Node>}
- */
- this.chaining = [];
- /**
- * The current stack.
- * This reflects the current process in the code block hierarchy,
- * it is useful to know if the current process is inside a conditional for example.
- *
- * @type {StackNode}
- */
- this.stack = stack();
- /**
- * List of stack nodes.
- * The current stack hierarchy is stored in an array.
- *
- * @type {Array<StackNode>}
- */
- this.stacks = [];
- /**
- * A tab value. Used for shader string generation.
- *
- * @type {string}
- * @default '\t'
- */
- this.tab = '\t';
- /**
- * Reference to the current function node.
- *
- * @type {?FunctionNode}
- * @default null
- */
- this.currentFunctionNode = null;
- /**
- * The builder's context.
- *
- * @type {Object}
- */
- this.context = {
- material: this.material
- };
- /**
- * The builder's cache.
- *
- * @type {NodeCache}
- */
- this.cache = new NodeCache();
- /**
- * Since the {@link NodeBuilder#cache} might be temporarily
- * overwritten by other caches, this member retains the reference
- * to the builder's own cache.
- *
- * @type {NodeCache}
- * @default this.cache
- */
- this.globalCache = this.cache;
- this.flowsData = new WeakMap();
- /**
- * The current shader stage.
- *
- * @type {?('vertex'|'fragment'|'compute'|'any')}
- */
- this.shaderStage = null;
- /**
- * The current build stage.
- *
- * @type {?('setup'|'analyze'|'generate')}
- */
- this.buildStage = null;
- /**
- * The sub-build layers.
- *
- * @type {Array<SubBuildNode>}
- * @default []
- */
- this.subBuildLayers = [];
- /**
- * The current stack of nodes.
- *
- * @type {?StackNode}
- * @default null
- */
- this.currentStack = null;
- /**
- * The current sub-build TSL function(Fn).
- *
- * @type {?string}
- * @default null
- */
- this.subBuildFn = null;
- }
- /**
- * Returns the bind groups of the current renderer.
- *
- * @return {ChainMap} The cache.
- */
- getBindGroupsCache() {
- let bindGroupsCache = rendererCache.get( this.renderer );
- if ( bindGroupsCache === undefined ) {
- bindGroupsCache = new ChainMap();
- rendererCache.set( this.renderer, bindGroupsCache );
- }
- return bindGroupsCache;
- }
- /**
- * Factory method for creating an instance of {@link RenderTarget} with the given
- * dimensions and options.
- *
- * @param {number} width - The width of the render target.
- * @param {number} height - The height of the render target.
- * @param {Object} options - The options of the render target.
- * @return {RenderTarget} The render target.
- */
- createRenderTarget( width, height, options ) {
- return new RenderTarget( width, height, options );
- }
- /**
- * Factory method for creating an instance of {@link CubeRenderTarget} with the given
- * dimensions and options.
- *
- * @param {number} size - The size of the cube render target.
- * @param {Object} options - The options of the cube render target.
- * @return {CubeRenderTarget} The cube render target.
- */
- createCubeRenderTarget( size, options ) {
- return new CubeRenderTarget( size, options );
- }
- /**
- * Whether the given node is included in the internal array of nodes or not.
- *
- * @param {Node} node - The node to test.
- * @return {boolean} Whether the given node is included in the internal array of nodes or not.
- */
- includes( node ) {
- return this.nodes.includes( node );
- }
- /**
- * Returns the output struct name which is required by
- * {@link OutputStructNode}.
- *
- * @abstract
- * @return {string} The name of the output struct.
- */
- getOutputStructName() {}
- /**
- * Returns a bind group for the given group name and binding.
- *
- * @private
- * @param {string} groupName - The group name.
- * @param {Array<NodeUniformsGroup>} bindings - List of bindings.
- * @return {BindGroup} The bind group
- */
- _getBindGroup( groupName, bindings ) {
- const bindGroupsCache = this.getBindGroupsCache();
- //
- const bindingsArray = [];
- let sharedGroup = true;
- for ( const binding of bindings ) {
- bindingsArray.push( binding );
- sharedGroup = sharedGroup && binding.groupNode.shared !== true;
- }
- //
- let bindGroup;
- if ( sharedGroup ) {
- bindGroup = bindGroupsCache.get( bindingsArray );
- if ( bindGroup === undefined ) {
- bindGroup = new BindGroup( groupName, bindingsArray, this.bindingsIndexes[ groupName ].group, bindingsArray );
- bindGroupsCache.set( bindingsArray, bindGroup );
- }
- } else {
- bindGroup = new BindGroup( groupName, bindingsArray, this.bindingsIndexes[ groupName ].group, bindingsArray );
- }
- return bindGroup;
- }
- /**
- * Returns an array of node uniform groups for the given group name and shader stage.
- *
- * @param {string} groupName - The group name.
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {Array<NodeUniformsGroup>} The array of node uniform groups.
- */
- getBindGroupArray( groupName, shaderStage ) {
- const bindings = this.bindings[ shaderStage ];
- let bindGroup = bindings[ groupName ];
- if ( bindGroup === undefined ) {
- if ( this.bindingsIndexes[ groupName ] === undefined ) {
- this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length };
- }
- bindings[ groupName ] = bindGroup = [];
- }
- return bindGroup;
- }
- /**
- * Returns a list bindings of all shader stages separated by groups.
- *
- * @return {Array<BindGroup>} The list of bindings.
- */
- getBindings() {
- let bindingsGroups = this.bindGroups;
- if ( bindingsGroups === null ) {
- const groups = {};
- const bindings = this.bindings;
- for ( const shaderStage of shaderStages ) {
- for ( const groupName in bindings[ shaderStage ] ) {
- const uniforms = bindings[ shaderStage ][ groupName ];
- const groupUniforms = groups[ groupName ] || ( groups[ groupName ] = [] );
- groupUniforms.push( ...uniforms );
- }
- }
- bindingsGroups = [];
- for ( const groupName in groups ) {
- const group = groups[ groupName ];
- const bindingsGroup = this._getBindGroup( groupName, group );
- bindingsGroups.push( bindingsGroup );
- }
- this.bindGroups = bindingsGroups;
- }
- return bindingsGroups;
- }
- /**
- * Sorts the bind groups and updates {@link NodeBuilder#bindingsIndexes}.
- */
- sortBindingGroups() {
- const bindingsGroups = this.getBindings();
- bindingsGroups.sort( ( a, b ) => ( a.bindings[ 0 ].groupNode.order - b.bindings[ 0 ].groupNode.order ) );
- for ( let i = 0; i < bindingsGroups.length; i ++ ) {
- const bindingGroup = bindingsGroups[ i ];
- this.bindingsIndexes[ bindingGroup.name ].group = i;
- bindingGroup.index = i;
- }
- }
- /**
- * The builder maintains each node in a hash-based dictionary.
- * This method sets the given node (value) with the given hash (key) into this dictionary.
- *
- * @param {Node} node - The node to add.
- * @param {number} hash - The hash of the node.
- */
- setHashNode( node, hash ) {
- this.hashNodes[ hash ] = node;
- }
- /**
- * Adds a node to this builder.
- *
- * @param {Node} node - The node to add.
- */
- addNode( node ) {
- if ( this.nodes.includes( node ) === false ) {
- this.nodes.push( node );
- this.setHashNode( node, node.getHash( this ) );
- }
- }
- /**
- * It is used to add Nodes that will be used as FRAME and RENDER events,
- * and need to follow a certain sequence in the calls to work correctly.
- * This function should be called after 'setup()' in the 'build()' process to ensure that the child nodes are processed first.
- *
- * @param {Node} node - The node to add.
- */
- addSequentialNode( node ) {
- if ( this.sequentialNodes.includes( node ) === false ) {
- this.sequentialNodes.push( node );
- }
- }
- /**
- * Checks the update types of nodes
- */
- buildUpdateNodes() {
- for ( const node of this.nodes ) {
- const updateType = node.getUpdateType();
- if ( updateType !== NodeUpdateType.NONE ) {
- this.updateNodes.push( node );
- }
- }
- for ( const node of this.sequentialNodes ) {
- const updateBeforeType = node.getUpdateBeforeType();
- const updateAfterType = node.getUpdateAfterType();
- if ( updateBeforeType !== NodeUpdateType.NONE ) {
- this.updateBeforeNodes.push( node );
- }
- if ( updateAfterType !== NodeUpdateType.NONE ) {
- this.updateAfterNodes.push( node );
- }
- }
- }
- /**
- * A reference the current node which is the
- * last node in the chain of nodes.
- *
- * @type {Node}
- */
- get currentNode() {
- return this.chaining[ this.chaining.length - 1 ];
- }
- /**
- * Whether the given texture is filtered or not.
- *
- * @param {Texture} texture - The texture to check.
- * @return {boolean} Whether the given texture is filtered or not.
- */
- isFilteredTexture( texture ) {
- return ( texture.magFilter === LinearFilter || texture.magFilter === LinearMipmapNearestFilter || texture.magFilter === NearestMipmapLinearFilter || texture.magFilter === LinearMipmapLinearFilter ||
- texture.minFilter === LinearFilter || texture.minFilter === LinearMipmapNearestFilter || texture.minFilter === NearestMipmapLinearFilter || texture.minFilter === LinearMipmapLinearFilter );
- }
- /**
- * Adds the given node to the internal node chain.
- * This is used to check recursive calls in node-graph.
- *
- * @param {Node} node - The node to add.
- */
- addChain( node ) {
- /*
- if ( this.chaining.indexOf( node ) !== - 1 ) {
- warn( 'Recursive node: ', node );
- }
- */
- this.chaining.push( node );
- }
- /**
- * Removes the given node from the internal node chain.
- *
- * @param {Node} node - The node to remove.
- */
- removeChain( node ) {
- const lastChain = this.chaining.pop();
- if ( lastChain !== node ) {
- throw new Error( 'NodeBuilder: Invalid node chaining!' );
- }
- }
- /**
- * Returns the native shader method name for a given generic name. E.g.
- * the method name `textureDimensions` matches the WGSL name but must be
- * resolved to `textureSize` in GLSL.
- *
- * @abstract
- * @param {string} method - The method name to resolve.
- * @return {string} The resolved method name.
- */
- getMethod( method ) {
- return method;
- }
- /**
- * Returns the native snippet for a ternary operation. E.g. GLSL would output
- * a ternary op as `cond ? x : y` whereas WGSL would output it as `select(y, x, cond)`
- *
- * @abstract
- * @param {string} condSnippet - The condition determining which expression gets resolved.
- * @param {string} ifSnippet - The expression to resolve to if the condition is true.
- * @param {string} elseSnippet - The expression to resolve to if the condition is false.
- * @return {string} The resolved method name.
- */
- getTernary( /* condSnippet, ifSnippet, elseSnippet*/ ) {
- return null;
- }
- /**
- * Returns a node for the given hash, see {@link NodeBuilder#setHashNode}.
- *
- * @param {number} hash - The hash of the node.
- * @return {Node} The found node.
- */
- getNodeFromHash( hash ) {
- return this.hashNodes[ hash ];
- }
- /**
- * Adds the Node to a target flow so that it can generate code in the 'generate' process.
- *
- * @param {('vertex'|'fragment'|'compute')} shaderStage - The shader stage.
- * @param {Node} node - The node to add.
- * @return {Node} The node.
- */
- addFlow( shaderStage, node ) {
- this.flowNodes[ shaderStage ].push( node );
- return node;
- }
- /**
- * Sets builder's context.
- *
- * @param {Object} context - The context to set.
- */
- setContext( context ) {
- this.context = context;
- }
- /**
- * Returns the builder's current context.
- *
- * @return {Object} The builder's current context.
- */
- getContext() {
- return this.context;
- }
- /**
- * Gets a context used in shader construction that can be shared across different materials.
- * This is necessary since the renderer cache can reuse shaders generated in one material and use them in another.
- *
- * @return {Object} The builder's current context without material.
- */
- getSharedContext() {
- const context = { ...this.context };
- delete context.material;
- return this.context;
- }
- /**
- * Sets builder's cache.
- *
- * @param {NodeCache} cache - The cache to set.
- */
- setCache( cache ) {
- this.cache = cache;
- }
- /**
- * Returns the builder's current cache.
- *
- * @return {NodeCache} The builder's current cache.
- */
- getCache() {
- return this.cache;
- }
- /**
- * Returns a cache for the given node.
- *
- * @param {Node} node - The node.
- * @param {boolean} [parent=true] - Whether this node refers to a shared parent cache or not.
- * @return {NodeCache} The cache.
- */
- getCacheFromNode( node, parent = true ) {
- const data = this.getDataFromNode( node );
- if ( data.cache === undefined ) data.cache = new NodeCache( parent ? this.getCache() : null );
- return data.cache;
- }
- /**
- * Whether the requested feature is available or not.
- *
- * @abstract
- * @param {string} name - The requested feature.
- * @return {boolean} Whether the requested feature is supported or not.
- */
- isAvailable( /*name*/ ) {
- return false;
- }
- /**
- * Returns the vertexIndex input variable as a native shader string.
- *
- * @abstract
- * @return {string} The instanceIndex shader string.
- */
- getVertexIndex() {
- warn( 'Abstract function.' );
- }
- /**
- * Contextually returns either the vertex stage instance index builtin
- * or the linearized index of an compute invocation within a grid of workgroups.
- *
- * @abstract
- * @return {string} The instanceIndex shader string.
- */
- getInstanceIndex() {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the drawIndex input variable as a native shader string.
- * Only relevant for WebGL and its `WEBGL_multi_draw` extension.
- *
- * @abstract
- * @return {?string} The drawIndex shader string.
- */
- getDrawIndex() {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the frontFacing input variable as a native shader string.
- *
- * @abstract
- * @return {string} The frontFacing shader string.
- */
- getFrontFacing() {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the fragCoord input variable as a native shader string.
- *
- * @abstract
- * @return {string} The fragCoord shader string.
- */
- getFragCoord() {
- warn( 'Abstract function.' );
- }
- /**
- * Whether to flip texture data along its vertical axis or not. WebGL needs
- * this method evaluate to `true`, WebGPU to `false`.
- *
- * @abstract
- * @return {boolean} Whether to flip texture data along its vertical axis or not.
- */
- isFlipY() {
- return false;
- }
- /**
- * Calling this method increases the usage count for the given node by one.
- *
- * @param {Node} node - The node to increase the usage count for.
- * @return {number} The updated usage count.
- */
- increaseUsage( node ) {
- const nodeData = this.getDataFromNode( node );
- nodeData.usageCount = nodeData.usageCount === undefined ? 1 : nodeData.usageCount + 1;
- return nodeData.usageCount;
- }
- /**
- * Generates a texture sample shader string for the given texture data.
- *
- * @abstract
- * @param {Texture} texture - The texture.
- * @param {string} textureProperty - The texture property name.
- * @param {string} uvSnippet - Snippet defining the texture coordinates.
- * @return {string} The generated shader string.
- */
- generateTexture( /* texture, textureProperty, uvSnippet */ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Generates a texture LOD shader string for the given texture data.
- *
- * @abstract
- * @param {Texture} texture - The texture.
- * @param {string} textureProperty - The texture property name.
- * @param {string} uvSnippet - Snippet defining the texture coordinates.
- * @param {?string} depthSnippet - Snippet defining the 0-based texture array index to sample.
- * @param {string} levelSnippet - Snippet defining the mip level.
- * @return {string} The generated shader string.
- */
- generateTextureLod( /* texture, textureProperty, uvSnippet, depthSnippet, levelSnippet */ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Generates the array declaration string.
- *
- * @param {string} type - The type.
- * @param {?number} [count] - The count.
- * @return {string} The generated value as a shader string.
- */
- generateArrayDeclaration( type, count ) {
- return this.getType( type ) + '[ ' + count + ' ]';
- }
- /**
- * Generates the array shader string for the given type and value.
- *
- * @param {string} type - The type.
- * @param {?number} [count] - The count.
- * @param {?Array<Node>} [values=null] - The default values.
- * @return {string} The generated value as a shader string.
- */
- generateArray( type, count, values = null ) {
- let snippet = this.generateArrayDeclaration( type, count ) + '( ';
- for ( let i = 0; i < count; i ++ ) {
- const value = values ? values[ i ] : null;
- if ( value !== null ) {
- snippet += value.build( this, type );
- } else {
- snippet += this.generateConst( type );
- }
- if ( i < count - 1 ) snippet += ', ';
- }
- snippet += ' )';
- return snippet;
- }
- /**
- * Generates the struct shader string.
- *
- * @param {string} type - The type.
- * @param {Array<Object>} [membersLayout] - The count.
- * @param {?Array<Node>} [values=null] - The default values.
- * @return {string} The generated value as a shader string.
- */
- generateStruct( type, membersLayout, values = null ) {
- const snippets = [];
- for ( const member of membersLayout ) {
- const { name, type } = member;
- if ( values && values[ name ] && values[ name ].isNode ) {
- snippets.push( values[ name ].build( this, type ) );
- } else {
- snippets.push( this.generateConst( type ) );
- }
- }
- return type + '( ' + snippets.join( ', ' ) + ' )';
- }
- /**
- * Generates the shader string for the given type and value.
- *
- * @param {string} type - The type.
- * @param {?any} [value=null] - The value.
- * @return {string} The generated value as a shader string.
- */
- generateConst( type, value = null ) {
- if ( value === null ) {
- if ( type === 'float' || type === 'int' || type === 'uint' ) value = 0;
- else if ( type === 'bool' ) value = false;
- else if ( type === 'color' ) value = new Color();
- else if ( type === 'vec2' ) value = new Vector2();
- else if ( type === 'vec3' ) value = new Vector3();
- else if ( type === 'vec4' ) value = new Vector4();
- }
- if ( type === 'float' ) return toFloat( value );
- if ( type === 'int' ) return `${ Math.round( value ) }`;
- if ( type === 'uint' ) return value >= 0 ? `${ Math.round( value ) }u` : '0u';
- if ( type === 'bool' ) return value ? 'true' : 'false';
- if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${ toFloat( value.r ) }, ${ toFloat( value.g ) }, ${ toFloat( value.b ) } )`;
- const typeLength = this.getTypeLength( type );
- const componentType = this.getComponentType( type );
- const generateConst = value => this.generateConst( componentType, value );
- if ( typeLength === 2 ) {
- return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) } )`;
- } else if ( typeLength === 3 ) {
- return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) }, ${ generateConst( value.z ) } )`;
- } else if ( typeLength === 4 && type !== 'mat2' ) {
- return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) }, ${ generateConst( value.z ) }, ${ generateConst( value.w ) } )`;
- } else if ( typeLength >= 4 && value && ( value.isMatrix2 || value.isMatrix3 || value.isMatrix4 ) ) {
- return `${ this.getType( type ) }( ${ value.elements.map( generateConst ).join( ', ' ) } )`;
- } else if ( typeLength > 4 ) {
- return `${ this.getType( type ) }()`;
- }
- throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
- }
- /**
- * It might be necessary to convert certain data types to different ones
- * so this method can be used to hide the conversion.
- *
- * @param {string} type - The type.
- * @return {string} The updated type.
- */
- getType( type ) {
- if ( type === 'color' ) return 'vec3';
- return type;
- }
- /**
- * Whether the given attribute name is defined in the geometry or not.
- *
- * @param {string} name - The attribute name.
- * @return {boolean} Whether the given attribute name is defined in the geometry.
- */
- hasGeometryAttribute( name ) {
- return this.geometry && this.geometry.getAttribute( name ) !== undefined;
- }
- /**
- * Returns a node attribute for the given name and type.
- *
- * @param {string} name - The attribute's name.
- * @param {string} type - The attribute's type.
- * @return {NodeAttribute} The node attribute.
- */
- getAttribute( name, type ) {
- const attributes = this.attributes;
- // find attribute
- for ( const attribute of attributes ) {
- if ( attribute.name === name ) {
- return attribute;
- }
- }
- // create a new if no exist
- const attribute = new NodeAttribute( name, type );
- this.registerDeclaration( attribute );
- attributes.push( attribute );
- return attribute;
- }
- /**
- * Returns for the given node and shader stage the property name for the shader.
- *
- * @param {Node} node - The node.
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The property name.
- */
- getPropertyName( node/*, shaderStage*/ ) {
- return node.name;
- }
- /**
- * Whether the given type is a vector type or not.
- *
- * @param {string} type - The type to check.
- * @return {boolean} Whether the given type is a vector type or not.
- */
- isVector( type ) {
- return /vec\d/.test( type );
- }
- /**
- * Whether the given type is a matrix type or not.
- *
- * @param {string} type - The type to check.
- * @return {boolean} Whether the given type is a matrix type or not.
- */
- isMatrix( type ) {
- return /mat\d/.test( type );
- }
- /**
- * Whether the given type is a reference type or not.
- *
- * @param {string} type - The type to check.
- * @return {boolean} Whether the given type is a reference type or not.
- */
- isReference( type ) {
- return type === 'void' || type === 'property' || type === 'sampler' || type === 'samplerComparison' || type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'depthTexture' || type === 'texture3D';
- }
- /**
- * Checks if the given texture requires a manual conversion to the working color space.
- *
- * @abstract
- * @param {Texture} texture - The texture to check.
- * @return {boolean} Whether the given texture requires a conversion to working color space or not.
- */
- needsToWorkingColorSpace( /*texture*/ ) {
- return false;
- }
- /**
- * Returns the component type of a given texture.
- *
- * @param {Texture} texture - The texture.
- * @return {string} The component type.
- */
- getComponentTypeFromTexture( texture ) {
- const type = texture.type;
- if ( texture.isDataTexture ) {
- if ( type === IntType ) return 'int';
- if ( type === UnsignedIntType ) return 'uint';
- }
- return 'float';
- }
- /**
- * Returns the element type for a given type.
- *
- * @param {string} type - The type.
- * @return {string} The element type.
- */
- getElementType( type ) {
- if ( type === 'mat2' ) return 'vec2';
- if ( type === 'mat3' ) return 'vec3';
- if ( type === 'mat4' ) return 'vec4';
- return this.getComponentType( type );
- }
- /**
- * Returns the component type for a given type.
- *
- * @param {string} type - The type.
- * @return {string} The component type.
- */
- getComponentType( type ) {
- type = this.getVectorType( type );
- if ( type === 'float' || type === 'bool' || type === 'int' || type === 'uint' ) return type;
- const componentType = /(b|i|u|)(vec|mat)([2-4])/.exec( type );
- if ( componentType === null ) return null;
- if ( componentType[ 1 ] === 'b' ) return 'bool';
- if ( componentType[ 1 ] === 'i' ) return 'int';
- if ( componentType[ 1 ] === 'u' ) return 'uint';
- return 'float';
- }
- /**
- * Returns the vector type for a given type.
- *
- * @param {string} type - The type.
- * @return {string} The vector type.
- */
- getVectorType( type ) {
- if ( type === 'color' ) return 'vec3';
- if ( type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'texture3D' ) return 'vec4';
- return type;
- }
- /**
- * Returns the data type for the given the length and component type.
- *
- * @param {number} length - The length.
- * @param {string} [componentType='float'] - The component type.
- * @return {string} The type.
- */
- getTypeFromLength( length, componentType = 'float' ) {
- if ( length === 1 ) return componentType;
- let baseType = getTypeFromLength( length );
- const prefix = componentType === 'float' ? '' : componentType[ 0 ];
- // fix edge case for mat2x2 being same size as vec4
- if ( /mat2/.test( componentType ) === true ) {
- baseType = baseType.replace( 'vec', 'mat' );
- }
- return prefix + baseType;
- }
- /**
- * Returns the type for a given typed array.
- *
- * @param {TypedArray} array - The typed array.
- * @return {string} The type.
- */
- getTypeFromArray( array ) {
- return typeFromArray.get( array.constructor );
- }
- /**
- * Returns the type is an integer type.
- *
- * @param {string} type - The type.
- * @return {boolean} Whether the type is an integer type or not.
- */
- isInteger( type ) {
- return /int|uint|(i|u)vec/.test( type );
- }
- /**
- * Returns the type for a given buffer attribute.
- *
- * @param {BufferAttribute} attribute - The buffer attribute.
- * @return {string} The type.
- */
- getTypeFromAttribute( attribute ) {
- let dataAttribute = attribute;
- if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data;
- const array = dataAttribute.array;
- const itemSize = attribute.itemSize;
- const normalized = attribute.normalized;
- let arrayType;
- if ( ! ( attribute instanceof Float16BufferAttribute ) && normalized !== true ) {
- arrayType = this.getTypeFromArray( array );
- }
- return this.getTypeFromLength( itemSize, arrayType );
- }
- /**
- * Returns the length for the given data type.
- *
- * @param {string} type - The data type.
- * @return {number} The length.
- */
- getTypeLength( type ) {
- const vecType = this.getVectorType( type );
- const vecNum = /vec([2-4])/.exec( vecType );
- if ( vecNum !== null ) return Number( vecNum[ 1 ] );
- if ( vecType === 'float' || vecType === 'bool' || vecType === 'int' || vecType === 'uint' ) return 1;
- if ( /mat2/.test( type ) === true ) return 4;
- if ( /mat3/.test( type ) === true ) return 9;
- if ( /mat4/.test( type ) === true ) return 16;
- return 0;
- }
- /**
- * Returns the vector type for a given matrix type.
- *
- * @param {string} type - The matrix type.
- * @return {string} The vector type.
- */
- getVectorFromMatrix( type ) {
- return type.replace( 'mat', 'vec' );
- }
- /**
- * For a given type this method changes the component type to the
- * given value. E.g. `vec4` should be changed to the new component type
- * `uint` which results in `uvec4`.
- *
- * @param {string} type - The type.
- * @param {string} newComponentType - The new component type.
- * @return {string} The new type.
- */
- changeComponentType( type, newComponentType ) {
- return this.getTypeFromLength( this.getTypeLength( type ), newComponentType );
- }
- /**
- * Returns the integer type pendant for the given type.
- *
- * @param {string} type - The type.
- * @return {string} The integer type.
- */
- getIntegerType( type ) {
- const componentType = this.getComponentType( type );
- if ( componentType === 'int' || componentType === 'uint' ) return type;
- return this.changeComponentType( type, 'int' );
- }
- /**
- * Adds a stack node to the internal stack.
- *
- * @return {StackNode} The added stack node.
- */
- addStack() {
- this.stack = stack( this.stack );
- const previousStack = getCurrentStack();
- this.stacks.push( previousStack );
- setCurrentStack( this.stack );
- return this.stack;
- }
- /**
- * Removes the last stack node from the internal stack.
- *
- * @return {StackNode} The removed stack node.
- */
- removeStack() {
- const lastStack = this.stack;
- this.stack = lastStack.parent;
- setCurrentStack( this.stacks.pop() );
- return lastStack;
- }
- /**
- * The builder maintains (cached) data for each node during the building process. This method
- * can be used to get these data for a specific shader stage and cache.
- *
- * @param {Node} node - The node to get the data for.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @param {?NodeCache} cache - An optional cache.
- * @return {Object} The node data.
- */
- getDataFromNode( node, shaderStage = this.shaderStage, cache = null ) {
- cache = cache === null ? ( node.isGlobal( this ) ? this.globalCache : this.cache ) : cache;
- let nodeData = cache.getData( node );
- if ( nodeData === undefined ) {
- nodeData = {};
- cache.setData( node, nodeData );
- }
- if ( nodeData[ shaderStage ] === undefined ) nodeData[ shaderStage ] = {};
- //
- let data = nodeData[ shaderStage ];
- const subBuilds = nodeData.any ? nodeData.any.subBuilds : null;
- const subBuild = this.getClosestSubBuild( subBuilds );
- if ( subBuild ) {
- if ( data.subBuildsCache === undefined ) data.subBuildsCache = {};
- data = data.subBuildsCache[ subBuild ] || ( data.subBuildsCache[ subBuild ] = {} );
- data.subBuilds = subBuilds;
- }
- return data;
- }
- /**
- * Returns the properties for the given node and shader stage.
- *
- * @param {Node} node - The node to get the properties for.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage='any'] - The shader stage.
- * @return {Object} The node properties.
- */
- getNodeProperties( node, shaderStage = 'any' ) {
- const nodeData = this.getDataFromNode( node, shaderStage );
- return nodeData.properties || ( nodeData.properties = { outputNode: null } );
- }
- /**
- * Returns an instance of {@link NodeAttribute} for the given buffer attribute node.
- *
- * @param {BufferAttributeNode} node - The buffer attribute node.
- * @param {string} type - The node type.
- * @return {NodeAttribute} The node attribute.
- */
- getBufferAttributeFromNode( node, type ) {
- const nodeData = this.getDataFromNode( node );
- let bufferAttribute = nodeData.bufferAttribute;
- if ( bufferAttribute === undefined ) {
- const index = this.uniforms.index ++;
- bufferAttribute = new NodeAttribute( 'nodeAttribute' + index, type, node );
- this.bufferAttributes.push( bufferAttribute );
- nodeData.bufferAttribute = bufferAttribute;
- }
- return bufferAttribute;
- }
- /**
- * Returns an instance of {@link StructType} for the given struct name and shader stage
- * or null if not found.
- *
- * @param {string} name - The name of the struct.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @return {?StructType} The struct type or null if not found.
- */
- getStructTypeNode( name, shaderStage = this.shaderStage ) {
- return this.types[ shaderStage ][ name ] || null;
- }
- /**
- * Returns an instance of {@link StructType} for the given output struct node.
- *
- * @param {OutputStructNode} node - The output struct node.
- * @param {Array<Object>} membersLayout - The output struct types.
- * @param {?string} [name=null] - The name of the struct.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @return {StructType} The struct type attribute.
- */
- getStructTypeFromNode( node, membersLayout, name = null, shaderStage = this.shaderStage ) {
- const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );
- let structType = nodeData.structType;
- if ( structType === undefined ) {
- const index = this.structs.index ++;
- if ( name === null ) name = 'StructType' + index;
- structType = new StructType( name, membersLayout );
- this.structs[ shaderStage ].push( structType );
- this.types[ shaderStage ][ name ] = node;
- nodeData.structType = structType;
- }
- return structType;
- }
- /**
- * Returns an instance of {@link StructType} for the given output struct node.
- *
- * @param {OutputStructNode} node - The output struct node.
- * @param {Array<Object>} membersLayout - The output struct types.
- * @return {StructType} The struct type attribute.
- */
- getOutputStructTypeFromNode( node, membersLayout ) {
- const structType = this.getStructTypeFromNode( node, membersLayout, 'OutputType', 'fragment' );
- structType.output = true;
- return structType;
- }
- /**
- * Returns an instance of {@link NodeUniform} for the given uniform node.
- *
- * @param {UniformNode} node - The uniform node.
- * @param {string} type - The uniform type.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @param {?string} name - The name of the uniform.
- * @return {NodeUniform} The node uniform.
- */
- getUniformFromNode( node, type, shaderStage = this.shaderStage, name = null ) {
- const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );
- let nodeUniform = nodeData.uniform;
- if ( nodeUniform === undefined ) {
- const index = this.uniforms.index ++;
- nodeUniform = new NodeUniform( name || ( 'nodeUniform' + index ), type, node );
- this.uniforms[ shaderStage ].push( nodeUniform );
- this.registerDeclaration( nodeUniform );
- nodeData.uniform = nodeUniform;
- }
- return nodeUniform;
- }
- /**
- * Returns an instance of {@link NodeVar} for the given variable node.
- *
- * @param {VarNode} node - The variable node.
- * @param {?string} name - The variable's name.
- * @param {string} [type=node.getNodeType( this )] - The variable's type.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @param {boolean} [readOnly=false] - Whether the variable is read-only or not.
- *
- * @return {NodeVar} The node variable.
- */
- getVarFromNode( node, name = null, type = node.getNodeType( this ), shaderStage = this.shaderStage, readOnly = false ) {
- const nodeData = this.getDataFromNode( node, shaderStage );
- const subBuildVariable = this.getSubBuildProperty( 'variable', nodeData.subBuilds );
- let nodeVar = nodeData[ subBuildVariable ];
- if ( nodeVar === undefined ) {
- const idNS = readOnly ? '_const' : '_var';
- const vars = this.vars[ shaderStage ] || ( this.vars[ shaderStage ] = [] );
- const id = this.vars[ idNS ] || ( this.vars[ idNS ] = 0 );
- if ( name === null ) {
- name = ( readOnly ? 'nodeConst' : 'nodeVar' ) + id;
- this.vars[ idNS ] ++;
- }
- //
- if ( subBuildVariable !== 'variable' ) {
- name = this.getSubBuildProperty( name, nodeData.subBuilds );
- }
- //
- const count = node.getArrayCount( this );
- nodeVar = new NodeVar( name, type, readOnly, count );
- if ( ! readOnly ) {
- vars.push( nodeVar );
- }
- this.registerDeclaration( nodeVar );
- nodeData[ subBuildVariable ] = nodeVar;
- }
- return nodeVar;
- }
- /**
- * Returns whether a Node or its flow is deterministic, useful for use in `const`.
- *
- * @param {Node} node - The varying node.
- * @return {boolean} Returns true if deterministic.
- */
- isDeterministic( node ) {
- if ( node.isMathNode ) {
- return this.isDeterministic( node.aNode ) &&
- ( node.bNode ? this.isDeterministic( node.bNode ) : true ) &&
- ( node.cNode ? this.isDeterministic( node.cNode ) : true );
- } else if ( node.isOperatorNode ) {
- return this.isDeterministic( node.aNode ) &&
- ( node.bNode ? this.isDeterministic( node.bNode ) : true );
- } else if ( node.isArrayNode ) {
- if ( node.values !== null ) {
- for ( const n of node.values ) {
- if ( ! this.isDeterministic( n ) ) {
- return false;
- }
- }
- }
- return true;
- } else if ( node.isConstNode ) {
- return true;
- }
- return false;
- }
- /**
- * Returns an instance of {@link NodeVarying} for the given varying node.
- *
- * @param {(VaryingNode|PropertyNode)} node - The varying node.
- * @param {?string} name - The varying's name.
- * @param {string} [type=node.getNodeType( this )] - The varying's type.
- * @param {?string} interpolationType - The interpolation type of the varying.
- * @param {?string} interpolationSampling - The interpolation sampling type of the varying.
- * @return {NodeVar} The node varying.
- */
- getVaryingFromNode( node, name = null, type = node.getNodeType( this ), interpolationType = null, interpolationSampling = null ) {
- const nodeData = this.getDataFromNode( node, 'any' );
- const subBuildVarying = this.getSubBuildProperty( 'varying', nodeData.subBuilds );
- let nodeVarying = nodeData[ subBuildVarying ];
- if ( nodeVarying === undefined ) {
- const varyings = this.varyings;
- const index = varyings.length;
- if ( name === null ) name = 'nodeVarying' + index;
- //
- if ( subBuildVarying !== 'varying' ) {
- name = this.getSubBuildProperty( name, nodeData.subBuilds );
- }
- //
- nodeVarying = new NodeVarying( name, type, interpolationType, interpolationSampling );
- varyings.push( nodeVarying );
- this.registerDeclaration( nodeVarying );
- nodeData[ subBuildVarying ] = nodeVarying;
- }
- return nodeVarying;
- }
- /**
- * Registers a node declaration in the current shader stage.
- *
- * @param {Object} node - The node to be registered.
- */
- registerDeclaration( node ) {
- const shaderStage = this.shaderStage;
- const declarations = this.declarations[ shaderStage ] || ( this.declarations[ shaderStage ] = {} );
- const property = this.getPropertyName( node );
- let index = 1;
- let name = property;
- // Automatically renames the property if the name is already in use.
- while ( declarations[ name ] !== undefined ) {
- name = property + '_' + index ++;
- }
- if ( index > 1 ) {
- node.name = name;
- warn( `TSL: Declaration name '${ property }' of '${ node.type }' already in use. Renamed to '${ name }'.` );
- }
- declarations[ name ] = node;
- }
- /**
- * Returns an instance of {@link NodeCode} for the given code node.
- *
- * @param {CodeNode} node - The code node.
- * @param {string} type - The node type.
- * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
- * @return {NodeCode} The node code.
- */
- getCodeFromNode( node, type, shaderStage = this.shaderStage ) {
- const nodeData = this.getDataFromNode( node );
- let nodeCode = nodeData.code;
- if ( nodeCode === undefined ) {
- const codes = this.codes[ shaderStage ] || ( this.codes[ shaderStage ] = [] );
- const index = codes.length;
- nodeCode = new NodeCode( 'nodeCode' + index, type );
- codes.push( nodeCode );
- nodeData.code = nodeCode;
- }
- return nodeCode;
- }
- /**
- * Adds a code flow based on the code-block hierarchy.
- * This is used so that code-blocks like If,Else create their variables locally if the Node
- * is only used inside one of these conditionals in the current shader stage.
- *
- * @param {Node} node - The node to add.
- * @param {Node} nodeBlock - Node-based code-block. Usually 'ConditionalNode'.
- */
- addFlowCodeHierarchy( node, nodeBlock ) {
- const { flowCodes, flowCodeBlock } = this.getDataFromNode( node );
- let needsFlowCode = true;
- let nodeBlockHierarchy = nodeBlock;
- while ( nodeBlockHierarchy ) {
- if ( flowCodeBlock.get( nodeBlockHierarchy ) === true ) {
- needsFlowCode = false;
- break;
- }
- nodeBlockHierarchy = this.getDataFromNode( nodeBlockHierarchy ).parentNodeBlock;
- }
- if ( needsFlowCode ) {
- for ( const flowCode of flowCodes ) {
- this.addLineFlowCode( flowCode );
- }
- }
- }
- /**
- * Add a inline-code to the current flow code-block.
- *
- * @param {Node} node - The node to add.
- * @param {string} code - The code to add.
- * @param {Node} nodeBlock - Current ConditionalNode
- */
- addLineFlowCodeBlock( node, code, nodeBlock ) {
- const nodeData = this.getDataFromNode( node );
- const flowCodes = nodeData.flowCodes || ( nodeData.flowCodes = [] );
- const codeBlock = nodeData.flowCodeBlock || ( nodeData.flowCodeBlock = new WeakMap() );
- flowCodes.push( code );
- codeBlock.set( nodeBlock, true );
- }
- /**
- * Add a inline-code to the current flow.
- *
- * @param {string} code - The code to add.
- * @param {?Node} [node= null] - Optional Node, can help the system understand if the Node is part of a code-block.
- * @return {NodeBuilder} A reference to this node builder.
- */
- addLineFlowCode( code, node = null ) {
- if ( code === '' ) return this;
- if ( node !== null && this.context.nodeBlock ) {
- this.addLineFlowCodeBlock( node, code, this.context.nodeBlock );
- }
- code = this.tab + code;
- if ( ! /;\s*$/.test( code ) ) {
- code = code + ';\n';
- }
- this.flow.code += code;
- return this;
- }
- /**
- * Adds a code to the current code flow.
- *
- * @param {string} code - Shader code.
- * @return {NodeBuilder} A reference to this node builder.
- */
- addFlowCode( code ) {
- this.flow.code += code;
- return this;
- }
- /**
- * Add tab in the code that will be generated so that other snippets respect the current tabulation.
- * Typically used in codes with If,Else.
- *
- * @return {NodeBuilder} A reference to this node builder.
- */
- addFlowTab() {
- this.tab += '\t';
- return this;
- }
- /**
- * Removes a tab.
- *
- * @return {NodeBuilder} A reference to this node builder.
- */
- removeFlowTab() {
- this.tab = this.tab.slice( 0, - 1 );
- return this;
- }
- /**
- * Gets the current flow data based on a Node.
- *
- * @param {Node} node - Node that the flow was started.
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {Object} The flow data.
- */
- getFlowData( node/*, shaderStage*/ ) {
- return this.flowsData.get( node );
- }
- /**
- * Executes the node flow based on a root node to generate the final shader code.
- *
- * @param {Node} node - The node to execute.
- * @return {Object} The code flow.
- */
- flowNode( node ) {
- const output = node.getNodeType( this );
- const flowData = this.flowChildNode( node, output );
- this.flowsData.set( node, flowData );
- return flowData;
- }
- /**
- * Includes a node in the current function node.
- *
- * @param {Node} node - The node to include.
- * @returns {void}
- */
- addInclude( node ) {
- if ( this.currentFunctionNode !== null ) {
- this.currentFunctionNode.includes.push( node );
- }
- }
- /**
- * Returns the native shader operator name for a given generic name.
- * It is a similar type of method like {@link NodeBuilder#getMethod}.
- *
- * @param {ShaderNodeInternal} shaderNode - The shader node to build the function node with.
- * @return {FunctionNode} The build function node.
- */
- buildFunctionNode( shaderNode ) {
- const fn = new FunctionNode();
- const previous = this.currentFunctionNode;
- this.currentFunctionNode = fn;
- fn.code = this.buildFunctionCode( shaderNode );
- this.currentFunctionNode = previous;
- return fn;
- }
- /**
- * Generates a code flow based on a TSL function: Fn().
- *
- * @param {ShaderNodeInternal} shaderNode - A function code will be generated based on the input.
- * @return {Object}
- */
- flowShaderNode( shaderNode ) {
- const layout = shaderNode.layout;
- const inputs = {
- [ Symbol.iterator ]() {
- let index = 0;
- const values = Object.values( this );
- return {
- next: () => ( {
- value: values[ index ],
- done: index ++ >= values.length
- } )
- };
- }
- };
- for ( const input of layout.inputs ) {
- inputs[ input.name ] = new ParameterNode( input.type, input.name );
- }
- //
- shaderNode.layout = null;
- const callNode = shaderNode.call( inputs );
- const flowData = this.flowStagesNode( callNode, layout.type );
- shaderNode.layout = layout;
- return flowData;
- }
- /**
- * Executes the node in a specific build stage.
- *
- * @param {Node} node - The node to execute.
- * @param {string} buildStage - The build stage to execute the node in.
- * @param {?(Node|string)} [output=null] - Expected output type. For example 'vec3'.
- * @return {?(Node|string)} The result of the node build.
- */
- flowBuildStage( node, buildStage, output = null ) {
- const previousBuildStage = this.getBuildStage();
- this.setBuildStage( buildStage );
- const result = node.build( this, output );
- this.setBuildStage( previousBuildStage );
- return result;
- }
- /**
- * Runs the node flow through all the steps of creation, 'setup', 'analyze', 'generate'.
- *
- * @param {Node} node - The node to execute.
- * @param {?string} output - Expected output type. For example 'vec3'.
- * @return {Object}
- */
- flowStagesNode( node, output = null ) {
- const previousFlow = this.flow;
- const previousVars = this.vars;
- const previousDeclarations = this.declarations;
- const previousCache = this.cache;
- const previousBuildStage = this.buildStage;
- const previousStack = this.stack;
- const flow = {
- code: ''
- };
- this.flow = flow;
- this.vars = {};
- this.declarations = {};
- this.cache = new NodeCache();
- this.stack = stack();
- for ( const buildStage of defaultBuildStages ) {
- this.setBuildStage( buildStage );
- flow.result = node.build( this, output );
- }
- flow.vars = this.getVars( this.shaderStage );
- this.flow = previousFlow;
- this.vars = previousVars;
- this.declarations = previousDeclarations;
- this.cache = previousCache;
- this.stack = previousStack;
- this.setBuildStage( previousBuildStage );
- return flow;
- }
- /**
- * Returns the native shader operator name for a given generic name.
- * It is a similar type of method like {@link NodeBuilder#getMethod}.
- *
- * @abstract
- * @param {string} op - The operator name to resolve.
- * @return {?string} The resolved operator name.
- */
- getFunctionOperator( /* op */ ) {
- return null;
- }
- /**
- * Builds the given shader node.
- *
- * @abstract
- * @param {ShaderNodeInternal} shaderNode - The shader node.
- * @return {string} The function code.
- */
- buildFunctionCode( /* shaderNode */ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Generates a code flow based on a child Node.
- *
- * @param {Node} node - The node to execute.
- * @param {?string} output - Expected output type. For example 'vec3'.
- * @return {Object} The code flow.
- */
- flowChildNode( node, output = null ) {
- const previousFlow = this.flow;
- const flow = {
- code: ''
- };
- this.flow = flow;
- flow.result = node.build( this, output );
- this.flow = previousFlow;
- return flow;
- }
- /**
- * Executes a flow of code in a different stage.
- *
- * Some nodes like `varying()` have the ability to compute code in vertex-stage and
- * return the value in fragment-stage even if it is being executed in an input fragment.
- *
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @param {Node} node - The node to execute.
- * @param {?string} output - Expected output type. For example 'vec3'.
- * @param {?string} propertyName - The property name to assign the result.
- * @return {?(Object|Node)} The code flow or node.build() result.
- */
- flowNodeFromShaderStage( shaderStage, node, output = null, propertyName = null ) {
- const previousTab = this.tab;
- const previousCache = this.cache;
- const previousShaderStage = this.shaderStage;
- const previousContext = this.context;
- this.setShaderStage( shaderStage );
- const context = { ...this.context };
- delete context.nodeBlock;
- this.cache = this.globalCache;
- this.tab = '\t';
- this.context = context;
- let result = null;
- if ( this.buildStage === 'generate' ) {
- const flowData = this.flowChildNode( node, output );
- if ( propertyName !== null ) {
- flowData.code += `${ this.tab + propertyName } = ${ flowData.result };\n`;
- }
- this.flowCode[ shaderStage ] = this.flowCode[ shaderStage ] + flowData.code;
- result = flowData;
- } else {
- result = node.build( this );
- }
- this.setShaderStage( previousShaderStage );
- this.cache = previousCache;
- this.tab = previousTab;
- this.context = previousContext;
- return result;
- }
- /**
- * Returns an array holding all node attributes of this node builder.
- *
- * @return {Array<NodeAttribute>} The node attributes of this builder.
- */
- getAttributesArray() {
- return this.attributes.concat( this.bufferAttributes );
- }
- /**
- * Returns the attribute definitions as a shader string for the given shader stage.
- *
- * @abstract
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The attribute code section.
- */
- getAttributes( /*shaderStage*/ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the varying definitions as a shader string for the given shader stage.
- *
- * @abstract
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The varying code section.
- */
- getVaryings( /*shaderStage*/ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Returns a single variable definition as a shader string for the given variable type and name.
- *
- * @param {string} type - The variable's type.
- * @param {string} name - The variable's name.
- * @param {?number} [count=null] - The array length.
- * @return {string} The shader string.
- */
- getVar( type, name, count = null ) {
- return `${ count !== null ? this.generateArrayDeclaration( type, count ) : this.getType( type ) } ${ name }`;
- }
- /**
- * Returns the variable definitions as a shader string for the given shader stage.
- *
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The variable code section.
- */
- getVars( shaderStage ) {
- let snippet = '';
- const vars = this.vars[ shaderStage ];
- if ( vars !== undefined ) {
- for ( const variable of vars ) {
- snippet += `${ this.getVar( variable.type, variable.name ) }; `;
- }
- }
- return snippet;
- }
- /**
- * Returns the uniform definitions as a shader string for the given shader stage.
- *
- * @abstract
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The uniform code section.
- */
- getUniforms( /*shaderStage*/ ) {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the native code definitions as a shader string for the given shader stage.
- *
- * @param {('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage.
- * @return {string} The native code section.
- */
- getCodes( shaderStage ) {
- const codes = this.codes[ shaderStage ];
- let code = '';
- if ( codes !== undefined ) {
- for ( const nodeCode of codes ) {
- code += nodeCode.code + '\n';
- }
- }
- return code;
- }
- /**
- * Returns the hash of this node builder.
- *
- * @return {string} The hash.
- */
- getHash() {
- return this.vertexShader + this.fragmentShader + this.computeShader;
- }
- /**
- * Sets the current shader stage.
- *
- * @param {?('vertex'|'fragment'|'compute'|'any')} shaderStage - The shader stage to set.
- */
- setShaderStage( shaderStage ) {
- this.shaderStage = shaderStage;
- }
- /**
- * Returns the current shader stage.
- *
- * @return {?('vertex'|'fragment'|'compute'|'any')} The current shader stage.
- */
- getShaderStage() {
- return this.shaderStage;
- }
- /**
- * Sets the current build stage.
- *
- * @param {?('setup'|'analyze'|'generate')} buildStage - The build stage to set.
- */
- setBuildStage( buildStage ) {
- this.buildStage = buildStage;
- }
- /**
- * Returns the current build stage.
- *
- * @return {?('setup'|'analyze'|'generate')} The current build stage.
- */
- getBuildStage() {
- return this.buildStage;
- }
- /**
- * Controls the code build of the shader stages.
- *
- * @abstract
- */
- buildCode() {
- warn( 'Abstract function.' );
- }
- /**
- * Returns the current sub-build layer.
- *
- * @return {SubBuildNode} The current sub-build layers.
- */
- get subBuild() {
- return this.subBuildLayers[ this.subBuildLayers.length - 1 ] || null;
- }
- /**
- * Adds a sub-build layer to the node builder.
- *
- * @param {SubBuildNode} subBuild - The sub-build layer to add.
- */
- addSubBuild( subBuild ) {
- this.subBuildLayers.push( subBuild );
- }
- /**
- * Removes the last sub-build layer from the node builder.
- *
- * @return {SubBuildNode} The removed sub-build layer.
- */
- removeSubBuild() {
- return this.subBuildLayers.pop();
- }
- /**
- * Returns the closest sub-build layer for the given data.
- *
- * @param {Node|Set<string>|Array<string>} data - The data to get the closest sub-build layer from.
- * @return {?string} The closest sub-build name or null if none found.
- */
- getClosestSubBuild( data ) {
- let subBuilds;
- if ( data && data.isNode ) {
- if ( data.isShaderCallNodeInternal ) {
- subBuilds = data.shaderNode.subBuilds;
- } else if ( data.isStackNode ) {
- subBuilds = [ data.subBuild ];
- } else {
- subBuilds = this.getDataFromNode( data, 'any' ).subBuilds;
- }
- } else if ( data instanceof Set ) {
- subBuilds = [ ...data ];
- } else {
- subBuilds = data;
- }
- if ( ! subBuilds ) return null;
- const subBuildLayers = this.subBuildLayers;
- for ( let i = subBuilds.length - 1; i >= 0; i -- ) {
- const subBuild = subBuilds[ i ];
- if ( subBuildLayers.includes( subBuild ) ) {
- return subBuild;
- }
- }
- return null;
- }
- /**
- * Returns the output node of a sub-build layer.
- *
- * @param {Node} node - The node to get the output from.
- * @return {string} The output node name.
- */
- getSubBuildOutput( node ) {
- return this.getSubBuildProperty( 'outputNode', node );
- }
- /**
- * Returns the sub-build property name for the given property and node.
- *
- * @param {string} [property=''] - The property name.
- * @param {?Node} [node=null] - The node to get the sub-build from.
- * @return {string} The sub-build property name.
- */
- getSubBuildProperty( property = '', node = null ) {
- let subBuild;
- if ( node !== null ) {
- subBuild = this.getClosestSubBuild( node );
- } else {
- subBuild = this.subBuildFn;
- }
- let result;
- if ( subBuild ) {
- result = property ? ( subBuild + '_' + property ) : subBuild;
- } else {
- result = property;
- }
- return result;
- }
- /**
- * Central build method which controls the build for the given object.
- *
- * @return {NodeBuilder} A reference to this node builder.
- */
- build() {
- const { object, material, renderer } = this;
- if ( material !== null ) {
- let nodeMaterial = renderer.library.fromMaterial( material );
- if ( nodeMaterial === null ) {
- error( `NodeMaterial: Material "${ material.type }" is not compatible.` );
- nodeMaterial = new NodeMaterial();
- }
- nodeMaterial.build( this );
- } else {
- this.addFlow( 'compute', object );
- }
- // setup() -> stage 1: create possible new nodes and/or return an output reference node
- // analyze() -> stage 2: analyze nodes to possible optimization and validation
- // generate() -> stage 3: generate shader
- for ( const buildStage of defaultBuildStages ) {
- this.setBuildStage( buildStage );
- if ( this.context.vertex && this.context.vertex.isNode ) {
- this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
- }
- for ( const shaderStage of shaderStages ) {
- this.setShaderStage( shaderStage );
- const flowNodes = this.flowNodes[ shaderStage ];
- for ( const node of flowNodes ) {
- if ( buildStage === 'generate' ) {
- this.flowNode( node );
- } else {
- node.build( this );
- }
- }
- }
- }
- this.setBuildStage( null );
- this.setShaderStage( null );
- // stage 4: build code for a specific output
- this.buildCode();
- this.buildUpdateNodes();
- return this;
- }
- /**
- * Returns a uniform representation which is later used for UBO generation and rendering.
- *
- * @param {NodeUniform} uniformNode - The uniform node.
- * @param {string} type - The requested type.
- * @return {Uniform} The uniform.
- */
- getNodeUniform( uniformNode, type ) {
- if ( type === 'float' || type === 'int' || type === 'uint' ) return new NumberNodeUniform( uniformNode );
- if ( type === 'vec2' || type === 'ivec2' || type === 'uvec2' ) return new Vector2NodeUniform( uniformNode );
- if ( type === 'vec3' || type === 'ivec3' || type === 'uvec3' ) return new Vector3NodeUniform( uniformNode );
- if ( type === 'vec4' || type === 'ivec4' || type === 'uvec4' ) return new Vector4NodeUniform( uniformNode );
- if ( type === 'color' ) return new ColorNodeUniform( uniformNode );
- if ( type === 'mat2' ) return new Matrix2NodeUniform( uniformNode );
- if ( type === 'mat3' ) return new Matrix3NodeUniform( uniformNode );
- if ( type === 'mat4' ) return new Matrix4NodeUniform( uniformNode );
- throw new Error( `Uniform "${type}" not declared.` );
- }
- /**
- * Formats the given shader snippet from a given type into another one. E.g.
- * this method might be used to convert a simple float string `"1.0"` into a
- * `vec3` representation: `"vec3<f32>( 1.0 )"`.
- *
- * @param {string} snippet - The shader snippet.
- * @param {string} fromType - The source type.
- * @param {string} toType - The target type.
- * @return {string} The updated shader string.
- */
- format( snippet, fromType, toType ) {
- fromType = this.getVectorType( fromType );
- toType = this.getVectorType( toType );
- if ( fromType === toType || toType === null || this.isReference( toType ) ) {
- return snippet;
- }
- const fromTypeLength = this.getTypeLength( fromType );
- const toTypeLength = this.getTypeLength( toType );
- if ( fromTypeLength === 16 && toTypeLength === 9 ) {
- return `${ this.getType( toType ) }( ${ snippet }[ 0 ].xyz, ${ snippet }[ 1 ].xyz, ${ snippet }[ 2 ].xyz )`;
- }
- if ( fromTypeLength === 9 && toTypeLength === 4 ) {
- return `${ this.getType( toType ) }( ${ snippet }[ 0 ].xy, ${ snippet }[ 1 ].xy )`;
- }
- if ( fromTypeLength > 4 ) { // fromType is matrix-like
- // @TODO: ignore for now
- return snippet;
- }
- if ( toTypeLength > 4 || toTypeLength === 0 ) { // toType is matrix-like or unknown
- // @TODO: ignore for now
- return snippet;
- }
- if ( fromTypeLength === toTypeLength ) {
- return `${ this.getType( toType ) }( ${ snippet } )`;
- }
- if ( fromTypeLength > toTypeLength ) {
- snippet = toType === 'bool' ? `all( ${ snippet } )` : `${ snippet }.${ 'xyz'.slice( 0, toTypeLength ) }`;
- return this.format( snippet, this.getTypeFromLength( toTypeLength, this.getComponentType( fromType ) ), toType );
- }
- if ( toTypeLength === 4 && fromTypeLength > 1 ) { // toType is vec4-like
- return `${ this.getType( toType ) }( ${ this.format( snippet, fromType, 'vec3' ) }, 1.0 )`;
- }
- if ( fromTypeLength === 2 ) { // fromType is vec2-like and toType is vec3-like
- return `${ this.getType( toType ) }( ${ this.format( snippet, fromType, 'vec2' ) }, 0.0 )`;
- }
- if ( fromTypeLength === 1 && toTypeLength > 1 && fromType !== this.getComponentType( toType ) ) { // fromType is float-like
- // convert a number value to vector type, e.g:
- // vec3( 1u ) -> vec3( float( 1u ) )
- snippet = `${ this.getType( this.getComponentType( toType ) ) }( ${ snippet } )`;
- }
- return `${ this.getType( toType ) }( ${ snippet } )`; // fromType is float-like
- }
- /**
- * Returns a signature with the engine's current revision.
- *
- * @return {string} The signature.
- */
- getSignature() {
- return `// Three.js r${ REVISION } - Node System\n`;
- }
- }
- export default NodeBuilder;
|