| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239 | ( function () {	class VRMLLoader extends THREE.Loader {		constructor( manager ) {			super( manager ); // dependency check			if ( typeof chevrotain === 'undefined' ) {				// eslint-disable-line no-undef				throw Error( 'THREE.VRMLLoader: External library chevrotain.min.js required.' );			}		}		load( url, onLoad, onProgress, onError ) {			const scope = this;			const path = scope.path === '' ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;			const loader = new THREE.FileLoader( scope.manager );			loader.setPath( scope.path );			loader.setRequestHeader( scope.requestHeader );			loader.setWithCredentials( scope.withCredentials );			loader.load( url, function ( text ) {				try {					onLoad( scope.parse( text, path ) );				} catch ( e ) {					if ( onError ) {						onError( e );					} else {						console.error( e );					}					scope.manager.itemError( url );				}			}, onProgress, onError );		}		parse( data, path ) {			const nodeMap = {};			function generateVRMLTree( data ) {				// create lexer, parser and visitor				const tokenData = createTokens();				const lexer = new VRMLLexer( tokenData.tokens );				const parser = new VRMLParser( tokenData.tokenVocabulary );				const visitor = createVisitor( parser.getBaseCstVisitorConstructor() ); // lexing				const lexingResult = lexer.lex( data );				parser.input = lexingResult.tokens; // parsing				const cstOutput = parser.vrml();				if ( parser.errors.length > 0 ) {					console.error( parser.errors );					throw Error( 'THREE.VRMLLoader: Parsing errors detected.' );				} // actions				const ast = visitor.visit( cstOutput );				return ast;			}			function createTokens() {				const createToken = chevrotain.createToken; // eslint-disable-line no-undef				// from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics				const RouteIdentifier = createToken( {					name: 'RouteIdentifier',					pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/				} );				const Identifier = createToken( {					name: 'Identifier',					pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/,					longer_alt: RouteIdentifier				} ); // from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html				const nodeTypes = [ 'Anchor', 'Billboard', 'Collision', 'Group', 'Transform', // grouping nodes					'Inline', 'LOD', 'Switch', // special groups					'AudioClip', 'DirectionalLight', 'PointLight', 'Script', 'Shape', 'Sound', 'SpotLight', 'WorldInfo', // common nodes					'CylinderSensor', 'PlaneSensor', 'ProximitySensor', 'SphereSensor', 'TimeSensor', 'TouchSensor', 'VisibilitySensor', // sensors					'Box', 'Cone', 'Cylinder', 'ElevationGrid', 'Extrusion', 'IndexedFaceSet', 'IndexedLineSet', 'PointSet', 'Sphere', // geometries					'Color', 'Coordinate', 'Normal', 'TextureCoordinate', // geometric properties					'Appearance', 'FontStyle', 'ImageTexture', 'Material', 'MovieTexture', 'PixelTexture', 'TextureTransform', // appearance					'ColorInterpolator', 'CoordinateInterpolator', 'NormalInterpolator', 'OrientationInterpolator', 'PositionInterpolator', 'ScalarInterpolator', // interpolators					'Background', 'Fog', 'NavigationInfo', 'Viewpoint', // bindable nodes					'Text' // Text must be placed at the end of the regex so there are no matches for TextureTransform and TextureCoordinate				]; //				const Version = createToken( {					name: 'Version',					pattern: /#VRML.*/,					longer_alt: Identifier				} );				const NodeName = createToken( {					name: 'NodeName',					pattern: new RegExp( nodeTypes.join( '|' ) ),					longer_alt: Identifier				} );				const DEF = createToken( {					name: 'DEF',					pattern: /DEF/,					longer_alt: Identifier				} );				const USE = createToken( {					name: 'USE',					pattern: /USE/,					longer_alt: Identifier				} );				const ROUTE = createToken( {					name: 'ROUTE',					pattern: /ROUTE/,					longer_alt: Identifier				} );				const TO = createToken( {					name: 'TO',					pattern: /TO/,					longer_alt: Identifier				} ); //				const StringLiteral = createToken( {					name: 'StringLiteral',					pattern: /"(:?[^\\"\n\r]+|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/				} );				const HexLiteral = createToken( {					name: 'HexLiteral',					pattern: /0[xX][0-9a-fA-F]+/				} );				const NumberLiteral = createToken( {					name: 'NumberLiteral',					pattern: /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/				} );				const TrueLiteral = createToken( {					name: 'TrueLiteral',					pattern: /TRUE/				} );				const FalseLiteral = createToken( {					name: 'FalseLiteral',					pattern: /FALSE/				} );				const NullLiteral = createToken( {					name: 'NullLiteral',					pattern: /NULL/				} );				const LSquare = createToken( {					name: 'LSquare',					pattern: /\[/				} );				const RSquare = createToken( {					name: 'RSquare',					pattern: /]/				} );				const LCurly = createToken( {					name: 'LCurly',					pattern: /{/				} );				const RCurly = createToken( {					name: 'RCurly',					pattern: /}/				} );				const Comment = createToken( {					name: 'Comment',					pattern: /#.*/,					group: chevrotain.Lexer.SKIPPED // eslint-disable-line no-undef				} ); // commas, blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields				const WhiteSpace = createToken( {					name: 'WhiteSpace',					pattern: /[ ,\s]/,					group: chevrotain.Lexer.SKIPPED // eslint-disable-line no-undef				} );				const tokens = [ WhiteSpace, // keywords appear before the Identifier					NodeName, DEF, USE, ROUTE, TO, TrueLiteral, FalseLiteral, NullLiteral, // the Identifier must appear after the keywords because all keywords are valid identifiers					Version, Identifier, RouteIdentifier, StringLiteral, HexLiteral, NumberLiteral, LSquare, RSquare, LCurly, RCurly, Comment ];				const tokenVocabulary = {};				for ( let i = 0, l = tokens.length; i < l; i ++ ) {					const token = tokens[ i ];					tokenVocabulary[ token.name ] = token;				}				return {					tokens: tokens,					tokenVocabulary: tokenVocabulary				};			}			function createVisitor( BaseVRMLVisitor ) {				// the visitor is created dynmaically based on the given base class				function VRMLToASTVisitor() {					BaseVRMLVisitor.call( this );					this.validateVisitor();				}				VRMLToASTVisitor.prototype = Object.assign( Object.create( BaseVRMLVisitor.prototype ), {					constructor: VRMLToASTVisitor,					vrml: function ( ctx ) {						const data = {							version: this.visit( ctx.version ),							nodes: [],							routes: []						};						for ( let i = 0, l = ctx.node.length; i < l; i ++ ) {							const node = ctx.node[ i ];							data.nodes.push( this.visit( node ) );						}						if ( ctx.route ) {							for ( let i = 0, l = ctx.route.length; i < l; i ++ ) {								const route = ctx.route[ i ];								data.routes.push( this.visit( route ) );							}						}						return data;					},					version: function ( ctx ) {						return ctx.Version[ 0 ].image;					},					node: function ( ctx ) {						const data = {							name: ctx.NodeName[ 0 ].image,							fields: []						};						if ( ctx.field ) {							for ( let i = 0, l = ctx.field.length; i < l; i ++ ) {								const field = ctx.field[ i ];								data.fields.push( this.visit( field ) );							}						} // DEF						if ( ctx.def ) {							data.DEF = this.visit( ctx.def[ 0 ] );						}						return data;					},					field: function ( ctx ) {						const data = {							name: ctx.Identifier[ 0 ].image,							type: null,							values: null						};						let result; // SFValue						if ( ctx.singleFieldValue ) {							result = this.visit( ctx.singleFieldValue[ 0 ] );						} // MFValue						if ( ctx.multiFieldValue ) {							result = this.visit( ctx.multiFieldValue[ 0 ] );						}						data.type = result.type;						data.values = result.values;						return data;					},					def: function ( ctx ) {						return ( ctx.Identifier || ctx.NodeName )[ 0 ].image;					},					use: function ( ctx ) {						return {							USE: ( ctx.Identifier || ctx.NodeName )[ 0 ].image						};					},					singleFieldValue: function ( ctx ) {						return processField( this, ctx );					},					multiFieldValue: function ( ctx ) {						return processField( this, ctx );					},					route: function ( ctx ) {						const data = {							FROM: ctx.RouteIdentifier[ 0 ].image,							TO: ctx.RouteIdentifier[ 1 ].image						};						return data;					}				} );				function processField( scope, ctx ) {					const field = {						type: null,						values: []					};					if ( ctx.node ) {						field.type = 'node';						for ( let i = 0, l = ctx.node.length; i < l; i ++ ) {							const node = ctx.node[ i ];							field.values.push( scope.visit( node ) );						}					}					if ( ctx.use ) {						field.type = 'use';						for ( let i = 0, l = ctx.use.length; i < l; i ++ ) {							const use = ctx.use[ i ];							field.values.push( scope.visit( use ) );						}					}					if ( ctx.StringLiteral ) {						field.type = 'string';						for ( let i = 0, l = ctx.StringLiteral.length; i < l; i ++ ) {							const stringLiteral = ctx.StringLiteral[ i ];							field.values.push( stringLiteral.image.replace( /'|"/g, '' ) );						}					}					if ( ctx.NumberLiteral ) {						field.type = 'number';						for ( let i = 0, l = ctx.NumberLiteral.length; i < l; i ++ ) {							const numberLiteral = ctx.NumberLiteral[ i ];							field.values.push( parseFloat( numberLiteral.image ) );						}					}					if ( ctx.HexLiteral ) {						field.type = 'hex';						for ( let i = 0, l = ctx.HexLiteral.length; i < l; i ++ ) {							const hexLiteral = ctx.HexLiteral[ i ];							field.values.push( hexLiteral.image );						}					}					if ( ctx.TrueLiteral ) {						field.type = 'boolean';						for ( let i = 0, l = ctx.TrueLiteral.length; i < l; i ++ ) {							const trueLiteral = ctx.TrueLiteral[ i ];							if ( trueLiteral.image === 'TRUE' ) field.values.push( true );						}					}					if ( ctx.FalseLiteral ) {						field.type = 'boolean';						for ( let i = 0, l = ctx.FalseLiteral.length; i < l; i ++ ) {							const falseLiteral = ctx.FalseLiteral[ i ];							if ( falseLiteral.image === 'FALSE' ) field.values.push( false );						}					}					if ( ctx.NullLiteral ) {						field.type = 'null';						ctx.NullLiteral.forEach( function () {							field.values.push( null );						} );					}					return field;				}				return new VRMLToASTVisitor();			}			function parseTree( tree ) {				// console.log( JSON.stringify( tree, null, 2 ) );				const nodes = tree.nodes;				const scene = new THREE.Scene(); // first iteration: build nodemap based on DEF statements				for ( let i = 0, l = nodes.length; i < l; i ++ ) {					const node = nodes[ i ];					buildNodeMap( node );				} // second iteration: build nodes				for ( let i = 0, l = nodes.length; i < l; i ++ ) {					const node = nodes[ i ];					const object = getNode( node );					if ( object instanceof THREE.Object3D ) scene.add( object );					if ( node.name === 'WorldInfo' ) scene.userData.worldInfo = object;				}				return scene;			}			function buildNodeMap( node ) {				if ( node.DEF ) {					nodeMap[ node.DEF ] = node;				}				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					if ( field.type === 'node' ) {						const fieldValues = field.values;						for ( let j = 0, jl = fieldValues.length; j < jl; j ++ ) {							buildNodeMap( fieldValues[ j ] );						}					}				}			}			function getNode( node ) {				// handle case where a node refers to a different one				if ( node.USE ) {					return resolveUSE( node.USE );				}				if ( node.build !== undefined ) return node.build;				node.build = buildNode( node );				return node.build;			} // node builder			function buildNode( node ) {				const nodeName = node.name;				let build;				switch ( nodeName ) {					case 'Group':					case 'Transform':					case 'Collision':						build = buildGroupingNode( node );						break;					case 'Background':						build = buildBackgroundNode( node );						break;					case 'Shape':						build = buildShapeNode( node );						break;					case 'Appearance':						build = buildAppearanceNode( node );						break;					case 'Material':						build = buildMaterialNode( node );						break;					case 'ImageTexture':						build = buildImageTextureNode( node );						break;					case 'PixelTexture':						build = buildPixelTextureNode( node );						break;					case 'TextureTransform':						build = buildTextureTransformNode( node );						break;					case 'IndexedFaceSet':						build = buildIndexedFaceSetNode( node );						break;					case 'IndexedLineSet':						build = buildIndexedLineSetNode( node );						break;					case 'PointSet':						build = buildPointSetNode( node );						break;					case 'Box':						build = buildBoxNode( node );						break;					case 'Cone':						build = buildConeNode( node );						break;					case 'Cylinder':						build = buildCylinderNode( node );						break;					case 'Sphere':						build = buildSphereNode( node );						break;					case 'ElevationGrid':						build = buildElevationGridNode( node );						break;					case 'Extrusion':						build = buildExtrusionNode( node );						break;					case 'Color':					case 'Coordinate':					case 'Normal':					case 'TextureCoordinate':						build = buildGeometricNode( node );						break;					case 'WorldInfo':						build = buildWorldInfoNode( node );						break;					case 'Anchor':					case 'Billboard':					case 'Inline':					case 'LOD':					case 'Switch':					case 'AudioClip':					case 'DirectionalLight':					case 'PointLight':					case 'Script':					case 'Sound':					case 'SpotLight':					case 'CylinderSensor':					case 'PlaneSensor':					case 'ProximitySensor':					case 'SphereSensor':					case 'TimeSensor':					case 'TouchSensor':					case 'VisibilitySensor':					case 'Text':					case 'FontStyle':					case 'MovieTexture':					case 'ColorInterpolator':					case 'CoordinateInterpolator':					case 'NormalInterpolator':					case 'OrientationInterpolator':					case 'PositionInterpolator':					case 'ScalarInterpolator':					case 'Fog':					case 'NavigationInfo':					case 'Viewpoint':						// node not supported yet						break;					default:						console.warn( 'THREE.VRMLLoader: Unknown node:', nodeName );						break;				}				if ( build !== undefined && node.DEF !== undefined && build.hasOwnProperty( 'name' ) === true ) {					build.name = node.DEF;				}				return build;			}			function buildGroupingNode( node ) {				const object = new THREE.Group(); //				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'bboxCenter':							// field not supported							break;						case 'bboxSize':							// field not supported							break;						case 'center':							// field not supported							break;						case 'children':							parseFieldChildren( fieldValues, object );							break;						case 'collide':							// field not supported							break;						case 'rotation':							const axis = new THREE.Vector3( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							const angle = fieldValues[ 3 ];							object.quaternion.setFromAxisAngle( axis, angle );							break;						case 'scale':							object.scale.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							break;						case 'scaleOrientation':							// field not supported							break;						case 'translation':							object.position.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							break;						case 'proxy':							// field not supported							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				return object;			}			function buildBackgroundNode( node ) {				const group = new THREE.Group();				let groundAngle, groundColor;				let skyAngle, skyColor;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'groundAngle':							groundAngle = fieldValues;							break;						case 'groundColor':							groundColor = fieldValues;							break;						case 'backUrl':							// field not supported							break;						case 'bottomUrl':							// field not supported							break;						case 'frontUrl':							// field not supported							break;						case 'leftUrl':							// field not supported							break;						case 'rightUrl':							// field not supported							break;						case 'topUrl':							// field not supported							break;						case 'skyAngle':							skyAngle = fieldValues;							break;						case 'skyColor':							skyColor = fieldValues;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const radius = 10000; // sky				if ( skyColor ) {					const skyGeometry = new THREE.SphereGeometry( radius, 32, 16 );					const skyMaterial = new THREE.MeshBasicMaterial( {						fog: false,						side: THREE.BackSide,						depthWrite: false,						depthTest: false					} );					if ( skyColor.length > 3 ) {						paintFaces( skyGeometry, radius, skyAngle, toColorArray( skyColor ), true );						skyMaterial.vertexColors = true;					} else {						skyMaterial.color.setRGB( skyColor[ 0 ], skyColor[ 1 ], skyColor[ 2 ] );					}					const sky = new THREE.Mesh( skyGeometry, skyMaterial );					group.add( sky );				} // ground				if ( groundColor ) {					if ( groundColor.length > 0 ) {						const groundGeometry = new THREE.SphereGeometry( radius, 32, 16, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );						const groundMaterial = new THREE.MeshBasicMaterial( {							fog: false,							side: THREE.BackSide,							vertexColors: true,							depthWrite: false,							depthTest: false						} );						paintFaces( groundGeometry, radius, groundAngle, toColorArray( groundColor ), false );						const ground = new THREE.Mesh( groundGeometry, groundMaterial );						group.add( ground );					}				} // render background group first				group.renderOrder = - Infinity;				return group;			}			function buildShapeNode( node ) {				const fields = node.fields; // if the appearance field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)				let material = new THREE.MeshBasicMaterial( {					color: 0x000000				} );				let geometry;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'appearance':							if ( fieldValues[ 0 ] !== null ) {								material = getNode( fieldValues[ 0 ] );							}							break;						case 'geometry':							if ( fieldValues[ 0 ] !== null ) {								geometry = getNode( fieldValues[ 0 ] );							}							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				} // build 3D object				let object;				if ( geometry && geometry.attributes.position ) {					const type = geometry._type;					if ( type === 'points' ) {						// points						const pointsMaterial = new THREE.PointsMaterial( {							color: 0xffffff						} );						if ( geometry.attributes.color !== undefined ) {							pointsMaterial.vertexColors = true;						} else {							// if the color field is NULL and there is a material defined for the appearance affecting this PointSet, then use the emissiveColor of the material to draw the points							if ( material.isMeshPhongMaterial ) {								pointsMaterial.color.copy( material.emissive );							}						}						object = new THREE.Points( geometry, pointsMaterial );					} else if ( type === 'line' ) {						// lines						const lineMaterial = new THREE.LineBasicMaterial( {							color: 0xffffff						} );						if ( geometry.attributes.color !== undefined ) {							lineMaterial.vertexColors = true;						} else {							// if the color field is NULL and there is a material defined for the appearance affecting this IndexedLineSet, then use the emissiveColor of the material to draw the lines							if ( material.isMeshPhongMaterial ) {								lineMaterial.color.copy( material.emissive );							}						}						object = new THREE.LineSegments( geometry, lineMaterial );					} else {						// consider meshes						// check "solid" hint (it's placed in the geometry but affects the material)						if ( geometry._solid !== undefined ) {							material.side = geometry._solid ? THREE.FrontSide : THREE.DoubleSide;						} // check for vertex colors						if ( geometry.attributes.color !== undefined ) {							material.vertexColors = true;						}						object = new THREE.Mesh( geometry, material );					}				} else {					object = new THREE.Object3D(); // if the geometry field is NULL or no vertices are defined the object is not drawn					object.visible = false;				}				return object;			}			function buildAppearanceNode( node ) {				let material = new THREE.MeshPhongMaterial();				let transformData;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'material':							if ( fieldValues[ 0 ] !== null ) {								const materialData = getNode( fieldValues[ 0 ] );								if ( materialData.diffuseColor ) material.color.copy( materialData.diffuseColor );								if ( materialData.emissiveColor ) material.emissive.copy( materialData.emissiveColor );								if ( materialData.shininess ) material.shininess = materialData.shininess;								if ( materialData.specularColor ) material.specular.copy( materialData.specularColor );								if ( materialData.transparency ) material.opacity = 1 - materialData.transparency;								if ( materialData.transparency > 0 ) material.transparent = true;							} else {								// if the material field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)								material = new THREE.MeshBasicMaterial( {									color: 0x000000								} );							}							break;						case 'texture':							const textureNode = fieldValues[ 0 ];							if ( textureNode !== null ) {								if ( textureNode.name === 'ImageTexture' || textureNode.name === 'PixelTexture' ) {									material.map = getNode( textureNode );								} else { // MovieTexture not supported yet								}							}							break;						case 'textureTransform':							if ( fieldValues[ 0 ] !== null ) {								transformData = getNode( fieldValues[ 0 ] );							}							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				} // only apply texture transform data if a texture was defined				if ( material.map ) {					// respect VRML lighting model					if ( material.map.__type ) {						switch ( material.map.__type ) {							case TEXTURE_TYPE.INTENSITY_ALPHA:								material.opacity = 1; // ignore transparency								break;							case TEXTURE_TYPE.RGB:								material.color.set( 0xffffff ); // ignore material color								break;							case TEXTURE_TYPE.RGBA:								material.color.set( 0xffffff ); // ignore material color								material.opacity = 1; // ignore transparency								break;							default:						}						delete material.map.__type;					} // apply texture transform					if ( transformData ) {						material.map.center.copy( transformData.center );						material.map.rotation = transformData.rotation;						material.map.repeat.copy( transformData.scale );						material.map.offset.copy( transformData.translation );					}				}				return material;			}			function buildMaterialNode( node ) {				const materialData = {};				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'ambientIntensity':							// field not supported							break;						case 'diffuseColor':							materialData.diffuseColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							break;						case 'emissiveColor':							materialData.emissiveColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							break;						case 'shininess':							materialData.shininess = fieldValues[ 0 ];							break;						case 'specularColor':							materialData.emissiveColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );							break;						case 'transparency':							materialData.transparency = fieldValues[ 0 ];							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				return materialData;			}			function parseHexColor( hex, textureType, color ) {				let value;				switch ( textureType ) {					case TEXTURE_TYPE.INTENSITY:						// Intensity texture: A one-component image specifies one-byte hexadecimal or integer values representing the intensity of the image						value = parseInt( hex );						color.r = value;						color.g = value;						color.b = value;						break;					case TEXTURE_TYPE.INTENSITY_ALPHA:						// Intensity+Alpha texture: A two-component image specifies the intensity in the first (high) byte and the alpha opacity in the second (low) byte.						value = parseInt( '0x' + hex.substring( 2, 4 ) );						color.r = value;						color.g = value;						color.b = value;						color.a = parseInt( '0x' + hex.substring( 4, 6 ) );						break;					case TEXTURE_TYPE.RGB:						// RGB texture: Pixels in a three-component image specify the red component in the first (high) byte, followed by the green and blue components						color.r = parseInt( '0x' + hex.substring( 2, 4 ) );						color.g = parseInt( '0x' + hex.substring( 4, 6 ) );						color.b = parseInt( '0x' + hex.substring( 6, 8 ) );						break;					case TEXTURE_TYPE.RGBA:						// RGBA texture: Four-component images specify the alpha opacity byte after red/green/blue						color.r = parseInt( '0x' + hex.substring( 2, 4 ) );						color.g = parseInt( '0x' + hex.substring( 4, 6 ) );						color.b = parseInt( '0x' + hex.substring( 6, 8 ) );						color.a = parseInt( '0x' + hex.substring( 8, 10 ) );						break;					default:				}			}			function getTextureType( num_components ) {				let type;				switch ( num_components ) {					case 1:						type = TEXTURE_TYPE.INTENSITY;						break;					case 2:						type = TEXTURE_TYPE.INTENSITY_ALPHA;						break;					case 3:						type = TEXTURE_TYPE.RGB;						break;					case 4:						type = TEXTURE_TYPE.RGBA;						break;					default:				}				return type;			}			function buildPixelTextureNode( node ) {				let texture;				let wrapS = THREE.RepeatWrapping;				let wrapT = THREE.RepeatWrapping;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'image':							const width = fieldValues[ 0 ];							const height = fieldValues[ 1 ];							const num_components = fieldValues[ 2 ];							const useAlpha = num_components === 2 || num_components === 4;							const textureType = getTextureType( num_components );							const size = ( useAlpha === true ? 4 : 3 ) * ( width * height );							const data = new Uint8Array( size );							const color = {								r: 0,								g: 0,								b: 0,								a: 0							};							for ( let j = 3, k = 0, jl = fieldValues.length; j < jl; j ++, k ++ ) {								parseHexColor( fieldValues[ j ], textureType, color );								if ( useAlpha === true ) {									const stride = k * 4;									data[ stride + 0 ] = color.r;									data[ stride + 1 ] = color.g;									data[ stride + 2 ] = color.b;									data[ stride + 3 ] = color.a;								} else {									const stride = k * 3;									data[ stride + 0 ] = color.r;									data[ stride + 1 ] = color.g;									data[ stride + 2 ] = color.b;								}							}							texture = new THREE.DataTexture( data, width, height, useAlpha === true ? THREE.RGBAFormat : THREE.RGBFormat );							texture.__type = textureType; // needed for material modifications							break;						case 'repeatS':							if ( fieldValues[ 0 ] === false ) wrapS = THREE.ClampToEdgeWrapping;							break;						case 'repeatT':							if ( fieldValues[ 0 ] === false ) wrapT = THREE.ClampToEdgeWrapping;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				if ( texture ) {					texture.wrapS = wrapS;					texture.wrapT = wrapT;				}				return texture;			}			function buildImageTextureNode( node ) {				let texture;				let wrapS = THREE.RepeatWrapping;				let wrapT = THREE.RepeatWrapping;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'url':							const url = fieldValues[ 0 ];							if ( url ) texture = textureLoader.load( url );							break;						case 'repeatS':							if ( fieldValues[ 0 ] === false ) wrapS = THREE.ClampToEdgeWrapping;							break;						case 'repeatT':							if ( fieldValues[ 0 ] === false ) wrapT = THREE.ClampToEdgeWrapping;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				if ( texture ) {					texture.wrapS = wrapS;					texture.wrapT = wrapT;				}				return texture;			}			function buildTextureTransformNode( node ) {				const transformData = {					center: new THREE.Vector2(),					rotation: new THREE.Vector2(),					scale: new THREE.Vector2(),					translation: new THREE.Vector2()				};				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'center':							transformData.center.set( fieldValues[ 0 ], fieldValues[ 1 ] );							break;						case 'rotation':							transformData.rotation = fieldValues[ 0 ];							break;						case 'scale':							transformData.scale.set( fieldValues[ 0 ], fieldValues[ 1 ] );							break;						case 'translation':							transformData.translation.set( fieldValues[ 0 ], fieldValues[ 1 ] );							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				return transformData;			}			function buildGeometricNode( node ) {				return node.fields[ 0 ].values;			}			function buildWorldInfoNode( node ) {				const worldInfo = {};				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'title':							worldInfo.title = fieldValues[ 0 ];							break;						case 'info':							worldInfo.info = fieldValues;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				return worldInfo;			}			function buildIndexedFaceSetNode( node ) {				let color, coord, normal, texCoord;				let ccw = true,					solid = true,					creaseAngle = 0;				let colorIndex, coordIndex, normalIndex, texCoordIndex;				let colorPerVertex = true,					normalPerVertex = true;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'color':							const colorNode = fieldValues[ 0 ];							if ( colorNode !== null ) {								color = getNode( colorNode );							}							break;						case 'coord':							const coordNode = fieldValues[ 0 ];							if ( coordNode !== null ) {								coord = getNode( coordNode );							}							break;						case 'normal':							const normalNode = fieldValues[ 0 ];							if ( normalNode !== null ) {								normal = getNode( normalNode );							}							break;						case 'texCoord':							const texCoordNode = fieldValues[ 0 ];							if ( texCoordNode !== null ) {								texCoord = getNode( texCoordNode );							}							break;						case 'ccw':							ccw = fieldValues[ 0 ];							break;						case 'colorIndex':							colorIndex = fieldValues;							break;						case 'colorPerVertex':							colorPerVertex = fieldValues[ 0 ];							break;						case 'convex':							// field not supported							break;						case 'coordIndex':							coordIndex = fieldValues;							break;						case 'creaseAngle':							creaseAngle = fieldValues[ 0 ];							break;						case 'normalIndex':							normalIndex = fieldValues;							break;						case 'normalPerVertex':							normalPerVertex = fieldValues[ 0 ];							break;						case 'solid':							solid = fieldValues[ 0 ];							break;						case 'texCoordIndex':							texCoordIndex = fieldValues;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				if ( coordIndex === undefined ) {					console.warn( 'THREE.VRMLLoader: Missing coordIndex.' );					return new THREE.BufferGeometry(); // handle VRML files with incomplete geometry definition				}				const triangulatedCoordIndex = triangulateFaceIndex( coordIndex, ccw );				let colorAttribute;				let normalAttribute;				let uvAttribute;				if ( color ) {					if ( colorPerVertex === true ) {						if ( colorIndex && colorIndex.length > 0 ) {							// if the colorIndex field is not empty, then it is used to choose colors for each vertex of the IndexedFaceSet.							const triangulatedColorIndex = triangulateFaceIndex( colorIndex, ccw );							colorAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedColorIndex, color, 3 );						} else {							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the THREE.Color node							colorAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( color, 3 ) );						}					} else {						if ( colorIndex && colorIndex.length > 0 ) {							// if the colorIndex field is not empty, then they are used to choose one color for each face of the IndexedFaceSet							const flattenFaceColors = flattenData( color, colorIndex );							const triangulatedFaceColors = triangulateFaceData( flattenFaceColors, coordIndex );							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );						} else {							// if the colorIndex field is empty, then the color are applied to each face of the IndexedFaceSet in order							const triangulatedFaceColors = triangulateFaceData( color, coordIndex );							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );						}					}				}				if ( normal ) {					if ( normalPerVertex === true ) {						// consider vertex normals						if ( normalIndex && normalIndex.length > 0 ) {							// if the normalIndex field is not empty, then it is used to choose normals for each vertex of the IndexedFaceSet.							const triangulatedNormalIndex = triangulateFaceIndex( normalIndex, ccw );							normalAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedNormalIndex, normal, 3 );						} else {							// if the normalIndex field is empty, then the coordIndex field is used to choose normals from the Normal node							normalAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( normal, 3 ) );						}					} else {						// consider face normals						if ( normalIndex && normalIndex.length > 0 ) {							// if the normalIndex field is not empty, then they are used to choose one normal for each face of the IndexedFaceSet							const flattenFaceNormals = flattenData( normal, normalIndex );							const triangulatedFaceNormals = triangulateFaceData( flattenFaceNormals, coordIndex );							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );						} else {							// if the normalIndex field is empty, then the normals are applied to each face of the IndexedFaceSet in order							const triangulatedFaceNormals = triangulateFaceData( normal, coordIndex );							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );						}					}				} else {					// if the normal field is NULL, then the loader should automatically generate normals, using creaseAngle to determine if and how normals are smoothed across shared vertices					normalAttribute = computeNormalAttribute( triangulatedCoordIndex, coord, creaseAngle );				}				if ( texCoord ) {					// texture coordinates are always defined on vertex level					if ( texCoordIndex && texCoordIndex.length > 0 ) {						// if the texCoordIndex field is not empty, then it is used to choose texture coordinates for each vertex of the IndexedFaceSet.						const triangulatedTexCoordIndex = triangulateFaceIndex( texCoordIndex, ccw );						uvAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedTexCoordIndex, texCoord, 2 );					} else {						// if the texCoordIndex field is empty, then the coordIndex array is used to choose texture coordinates from the TextureCoordinate node						uvAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( texCoord, 2 ) );					}				}				const geometry = new THREE.BufferGeometry();				const positionAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( coord, 3 ) );				geometry.setAttribute( 'position', positionAttribute );				geometry.setAttribute( 'normal', normalAttribute ); // optional attributes				if ( colorAttribute ) geometry.setAttribute( 'color', colorAttribute );				if ( uvAttribute ) geometry.setAttribute( 'uv', uvAttribute ); // "solid" influences the material so let's store it for later use				geometry._solid = solid;				geometry._type = 'mesh';				return geometry;			}			function buildIndexedLineSetNode( node ) {				let color, coord;				let colorIndex, coordIndex;				let colorPerVertex = true;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'color':							const colorNode = fieldValues[ 0 ];							if ( colorNode !== null ) {								color = getNode( colorNode );							}							break;						case 'coord':							const coordNode = fieldValues[ 0 ];							if ( coordNode !== null ) {								coord = getNode( coordNode );							}							break;						case 'colorIndex':							colorIndex = fieldValues;							break;						case 'colorPerVertex':							colorPerVertex = fieldValues[ 0 ];							break;						case 'coordIndex':							coordIndex = fieldValues;							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				} // build lines				let colorAttribute;				const expandedLineIndex = expandLineIndex( coordIndex ); // create an index for three.js's linesegment primitive				if ( color ) {					if ( colorPerVertex === true ) {						if ( colorIndex.length > 0 ) {							// if the colorIndex field is not empty, then one color is used for each polyline of the IndexedLineSet.							const expandedColorIndex = expandLineIndex( colorIndex ); // compute colors for each line segment (rendering primitve)							colorAttribute = computeAttributeFromIndexedData( expandedLineIndex, expandedColorIndex, color, 3 ); // compute data on vertex level						} else {							// if the colorIndex field is empty, then the colors are applied to each polyline of the IndexedLineSet in order.							colorAttribute = toNonIndexedAttribute( expandedLineIndex, new THREE.Float32BufferAttribute( color, 3 ) );						}					} else {						if ( colorIndex.length > 0 ) {							// if the colorIndex field is not empty, then colors are applied to each vertex of the IndexedLineSet							const flattenLineColors = flattenData( color, colorIndex ); // compute colors for each VRML primitve							const expandedLineColors = expandLineData( flattenLineColors, coordIndex ); // compute colors for each line segment (rendering primitve)							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level						} else {							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the THREE.Color node							const expandedLineColors = expandLineData( color, coordIndex ); // compute colors for each line segment (rendering primitve)							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level						}					}				} //				const geometry = new THREE.BufferGeometry();				const positionAttribute = toNonIndexedAttribute( expandedLineIndex, new THREE.Float32BufferAttribute( coord, 3 ) );				geometry.setAttribute( 'position', positionAttribute );				if ( colorAttribute ) geometry.setAttribute( 'color', colorAttribute );				geometry._type = 'line';				return geometry;			}			function buildPointSetNode( node ) {				let color, coord;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'color':							const colorNode = fieldValues[ 0 ];							if ( colorNode !== null ) {								color = getNode( colorNode );							}							break;						case 'coord':							const coordNode = fieldValues[ 0 ];							if ( coordNode !== null ) {								coord = getNode( coordNode );							}							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const geometry = new THREE.BufferGeometry();				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( coord, 3 ) );				if ( color ) geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( color, 3 ) );				geometry._type = 'points';				return geometry;			}			function buildBoxNode( node ) {				const size = new THREE.Vector3( 2, 2, 2 );				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'size':							size.x = fieldValues[ 0 ];							size.y = fieldValues[ 1 ];							size.z = fieldValues[ 2 ];							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const geometry = new THREE.BoxGeometry( size.x, size.y, size.z );				return geometry;			}			function buildConeNode( node ) {				let radius = 1,					height = 2,					openEnded = false;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'bottom':							openEnded = ! fieldValues[ 0 ];							break;						case 'bottomRadius':							radius = fieldValues[ 0 ];							break;						case 'height':							height = fieldValues[ 0 ];							break;						case 'side':							// field not supported							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const geometry = new THREE.ConeGeometry( radius, height, 16, 1, openEnded );				return geometry;			}			function buildCylinderNode( node ) {				let radius = 1,					height = 2;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'bottom':							// field not supported							break;						case 'radius':							radius = fieldValues[ 0 ];							break;						case 'height':							height = fieldValues[ 0 ];							break;						case 'side':							// field not supported							break;						case 'top':							// field not supported							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const geometry = new THREE.CylinderGeometry( radius, radius, height, 16, 1 );				return geometry;			}			function buildSphereNode( node ) {				let radius = 1;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'radius':							radius = fieldValues[ 0 ];							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const geometry = new THREE.SphereGeometry( radius, 16, 16 );				return geometry;			}			function buildElevationGridNode( node ) {				let color;				let normal;				let texCoord;				let height;				let colorPerVertex = true;				let normalPerVertex = true;				let solid = true;				let ccw = true;				let creaseAngle = 0;				let xDimension = 2;				let zDimension = 2;				let xSpacing = 1;				let zSpacing = 1;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'color':							const colorNode = fieldValues[ 0 ];							if ( colorNode !== null ) {								color = getNode( colorNode );							}							break;						case 'normal':							const normalNode = fieldValues[ 0 ];							if ( normalNode !== null ) {								normal = getNode( normalNode );							}							break;						case 'texCoord':							const texCoordNode = fieldValues[ 0 ];							if ( texCoordNode !== null ) {								texCoord = getNode( texCoordNode );							}							break;						case 'height':							height = fieldValues;							break;						case 'ccw':							ccw = fieldValues[ 0 ];							break;						case 'colorPerVertex':							colorPerVertex = fieldValues[ 0 ];							break;						case 'creaseAngle':							creaseAngle = fieldValues[ 0 ];							break;						case 'normalPerVertex':							normalPerVertex = fieldValues[ 0 ];							break;						case 'solid':							solid = fieldValues[ 0 ];							break;						case 'xDimension':							xDimension = fieldValues[ 0 ];							break;						case 'xSpacing':							xSpacing = fieldValues[ 0 ];							break;						case 'zDimension':							zDimension = fieldValues[ 0 ];							break;						case 'zSpacing':							zSpacing = fieldValues[ 0 ];							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				} // vertex data				const vertices = [];				const normals = [];				const colors = [];				const uvs = [];				for ( let i = 0; i < zDimension; i ++ ) {					for ( let j = 0; j < xDimension; j ++ ) {						// compute a row major index						const index = i * xDimension + j; // vertices						const x = xSpacing * i;						const y = height[ index ];						const z = zSpacing * j;						vertices.push( x, y, z ); // colors						if ( color && colorPerVertex === true ) {							const r = color[ index * 3 + 0 ];							const g = color[ index * 3 + 1 ];							const b = color[ index * 3 + 2 ];							colors.push( r, g, b );						} // normals						if ( normal && normalPerVertex === true ) {							const xn = normal[ index * 3 + 0 ];							const yn = normal[ index * 3 + 1 ];							const zn = normal[ index * 3 + 2 ];							normals.push( xn, yn, zn );						} // uvs						if ( texCoord ) {							const s = texCoord[ index * 2 + 0 ];							const t = texCoord[ index * 2 + 1 ];							uvs.push( s, t );						} else {							uvs.push( i / ( xDimension - 1 ), j / ( zDimension - 1 ) );						}					}				} // indices				const indices = [];				for ( let i = 0; i < xDimension - 1; i ++ ) {					for ( let j = 0; j < zDimension - 1; j ++ ) {						// from https://tecfa.unige.ch/guides/vrml/vrml97/spec/part1/nodesRef.html#ElevationGrid						const a = i + j * xDimension;						const b = i + ( j + 1 ) * xDimension;						const c = i + 1 + ( j + 1 ) * xDimension;						const d = i + 1 + j * xDimension; // faces						if ( ccw === true ) {							indices.push( a, c, b );							indices.push( c, a, d );						} else {							indices.push( a, b, c );							indices.push( c, d, a );						}					}				} //				const positionAttribute = toNonIndexedAttribute( indices, new THREE.Float32BufferAttribute( vertices, 3 ) );				const uvAttribute = toNonIndexedAttribute( indices, new THREE.Float32BufferAttribute( uvs, 2 ) );				let colorAttribute;				let normalAttribute; // color attribute				if ( color ) {					if ( colorPerVertex === false ) {						for ( let i = 0; i < xDimension - 1; i ++ ) {							for ( let j = 0; j < zDimension - 1; j ++ ) {								const index = i + j * ( xDimension - 1 );								const r = color[ index * 3 + 0 ];								const g = color[ index * 3 + 1 ];								const b = color[ index * 3 + 2 ]; // one color per quad								colors.push( r, g, b );								colors.push( r, g, b );								colors.push( r, g, b );								colors.push( r, g, b );								colors.push( r, g, b );								colors.push( r, g, b );							}						}						colorAttribute = new THREE.Float32BufferAttribute( colors, 3 );					} else {						colorAttribute = toNonIndexedAttribute( indices, new THREE.Float32BufferAttribute( colors, 3 ) );					}				} // normal attribute				if ( normal ) {					if ( normalPerVertex === false ) {						for ( let i = 0; i < xDimension - 1; i ++ ) {							for ( let j = 0; j < zDimension - 1; j ++ ) {								const index = i + j * ( xDimension - 1 );								const xn = normal[ index * 3 + 0 ];								const yn = normal[ index * 3 + 1 ];								const zn = normal[ index * 3 + 2 ]; // one normal per quad								normals.push( xn, yn, zn );								normals.push( xn, yn, zn );								normals.push( xn, yn, zn );								normals.push( xn, yn, zn );								normals.push( xn, yn, zn );								normals.push( xn, yn, zn );							}						}						normalAttribute = new THREE.Float32BufferAttribute( normals, 3 );					} else {						normalAttribute = toNonIndexedAttribute( indices, new THREE.Float32BufferAttribute( normals, 3 ) );					}				} else {					normalAttribute = computeNormalAttribute( indices, vertices, creaseAngle );				} // build geometry				const geometry = new THREE.BufferGeometry();				geometry.setAttribute( 'position', positionAttribute );				geometry.setAttribute( 'normal', normalAttribute );				geometry.setAttribute( 'uv', uvAttribute );				if ( colorAttribute ) geometry.setAttribute( 'color', colorAttribute ); // "solid" influences the material so let's store it for later use				geometry._solid = solid;				geometry._type = 'mesh';				return geometry;			}			function buildExtrusionNode( node ) {				let crossSection = [ 1, 1, 1, - 1, - 1, - 1, - 1, 1, 1, 1 ];				let spine = [ 0, 0, 0, 0, 1, 0 ];				let scale;				let orientation;				let beginCap = true;				let ccw = true;				let creaseAngle = 0;				let endCap = true;				let solid = true;				const fields = node.fields;				for ( let i = 0, l = fields.length; i < l; i ++ ) {					const field = fields[ i ];					const fieldName = field.name;					const fieldValues = field.values;					switch ( fieldName ) {						case 'beginCap':							beginCap = fieldValues[ 0 ];							break;						case 'ccw':							ccw = fieldValues[ 0 ];							break;						case 'convex':							// field not supported							break;						case 'creaseAngle':							creaseAngle = fieldValues[ 0 ];							break;						case 'crossSection':							crossSection = fieldValues;							break;						case 'endCap':							endCap = fieldValues[ 0 ];							break;						case 'orientation':							orientation = fieldValues;							break;						case 'scale':							scale = fieldValues;							break;						case 'solid':							solid = fieldValues[ 0 ];							break;						case 'spine':							spine = fieldValues; // only extrusion along the Y-axis are supported so far							break;						default:							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );							break;					}				}				const crossSectionClosed = crossSection[ 0 ] === crossSection[ crossSection.length - 2 ] && crossSection[ 1 ] === crossSection[ crossSection.length - 1 ]; // vertices				const vertices = [];				const spineVector = new THREE.Vector3();				const scaling = new THREE.Vector3();				const axis = new THREE.Vector3();				const vertex = new THREE.Vector3();				const quaternion = new THREE.Quaternion();				for ( let i = 0, j = 0, o = 0, il = spine.length; i < il; i += 3, j += 2, o += 4 ) {					spineVector.fromArray( spine, i );					scaling.x = scale ? scale[ j + 0 ] : 1;					scaling.y = 1;					scaling.z = scale ? scale[ j + 1 ] : 1;					axis.x = orientation ? orientation[ o + 0 ] : 0;					axis.y = orientation ? orientation[ o + 1 ] : 0;					axis.z = orientation ? orientation[ o + 2 ] : 1;					const angle = orientation ? orientation[ o + 3 ] : 0;					for ( let k = 0, kl = crossSection.length; k < kl; k += 2 ) {						vertex.x = crossSection[ k + 0 ];						vertex.y = 0;						vertex.z = crossSection[ k + 1 ]; // scale						vertex.multiply( scaling ); // rotate						quaternion.setFromAxisAngle( axis, angle );						vertex.applyQuaternion( quaternion ); // translate						vertex.add( spineVector );						vertices.push( vertex.x, vertex.y, vertex.z );					}				} // indices				const indices = [];				const spineCount = spine.length / 3;				const crossSectionCount = crossSection.length / 2;				for ( let i = 0; i < spineCount - 1; i ++ ) {					for ( let j = 0; j < crossSectionCount - 1; j ++ ) {						const a = j + i * crossSectionCount;						let b = j + 1 + i * crossSectionCount;						const c = j + ( i + 1 ) * crossSectionCount;						let d = j + 1 + ( i + 1 ) * crossSectionCount;						if ( j === crossSectionCount - 2 && crossSectionClosed === true ) {							b = i * crossSectionCount;							d = ( i + 1 ) * crossSectionCount;						}						if ( ccw === true ) {							indices.push( a, b, c );							indices.push( c, b, d );						} else {							indices.push( a, c, b );							indices.push( c, d, b );						}					}				} // triangulate cap				if ( beginCap === true || endCap === true ) {					const contour = [];					for ( let i = 0, l = crossSection.length; i < l; i += 2 ) {						contour.push( new THREE.Vector2( crossSection[ i ], crossSection[ i + 1 ] ) );					}					const faces = THREE.ShapeUtils.triangulateShape( contour, [] );					const capIndices = [];					for ( let i = 0, l = faces.length; i < l; i ++ ) {						const face = faces[ i ];						capIndices.push( face[ 0 ], face[ 1 ], face[ 2 ] );					} // begin cap					if ( beginCap === true ) {						for ( let i = 0, l = capIndices.length; i < l; i += 3 ) {							if ( ccw === true ) {								indices.push( capIndices[ i + 0 ], capIndices[ i + 1 ], capIndices[ i + 2 ] );							} else {								indices.push( capIndices[ i + 0 ], capIndices[ i + 2 ], capIndices[ i + 1 ] );							}						}					} // end cap					if ( endCap === true ) {						const indexOffset = crossSectionCount * ( spineCount - 1 ); // references to the first vertex of the last cross section						for ( let i = 0, l = capIndices.length; i < l; i += 3 ) {							if ( ccw === true ) {								indices.push( indexOffset + capIndices[ i + 0 ], indexOffset + capIndices[ i + 2 ], indexOffset + capIndices[ i + 1 ] );							} else {								indices.push( indexOffset + capIndices[ i + 0 ], indexOffset + capIndices[ i + 1 ], indexOffset + capIndices[ i + 2 ] );							}						}					}				}				const positionAttribute = toNonIndexedAttribute( indices, new THREE.Float32BufferAttribute( vertices, 3 ) );				const normalAttribute = computeNormalAttribute( indices, vertices, creaseAngle );				const geometry = new THREE.BufferGeometry();				geometry.setAttribute( 'position', positionAttribute );				geometry.setAttribute( 'normal', normalAttribute ); // no uvs yet				// "solid" influences the material so let's store it for later use				geometry._solid = solid;				geometry._type = 'mesh';				return geometry;			} // helper functions			function resolveUSE( identifier ) {				const node = nodeMap[ identifier ];				const build = getNode( node ); // because the same 3D objects can have different transformations, it's necessary to clone them.				// materials can be influenced by the geometry (e.g. vertex normals). cloning is necessary to avoid				// any side effects				return build.isObject3D || build.isMaterial ? build.clone() : build;			}			function parseFieldChildren( children, owner ) {				for ( let i = 0, l = children.length; i < l; i ++ ) {					const object = getNode( children[ i ] );					if ( object instanceof THREE.Object3D ) owner.add( object );				}			}			function triangulateFaceIndex( index, ccw ) {				const indices = []; // since face defintions can have more than three vertices, it's necessary to				// perform a simple triangulation				let start = 0;				for ( let i = 0, l = index.length; i < l; i ++ ) {					const i1 = index[ start ];					const i2 = index[ i + ( ccw ? 1 : 2 ) ];					const i3 = index[ i + ( ccw ? 2 : 1 ) ];					indices.push( i1, i2, i3 ); // an index of -1 indicates that the current face has ended and the next one begins					if ( index[ i + 3 ] === - 1 || i + 3 >= l ) {						i += 3;						start = i + 1;					}				}				return indices;			}			function triangulateFaceData( data, index ) {				const triangulatedData = [];				let start = 0;				for ( let i = 0, l = index.length; i < l; i ++ ) {					const stride = start * 3;					const x = data[ stride ];					const y = data[ stride + 1 ];					const z = data[ stride + 2 ];					triangulatedData.push( x, y, z ); // an index of -1 indicates that the current face has ended and the next one begins					if ( index[ i + 3 ] === - 1 || i + 3 >= l ) {						i += 3;						start ++;					}				}				return triangulatedData;			}			function flattenData( data, index ) {				const flattenData = [];				for ( let i = 0, l = index.length; i < l; i ++ ) {					const i1 = index[ i ];					const stride = i1 * 3;					const x = data[ stride ];					const y = data[ stride + 1 ];					const z = data[ stride + 2 ];					flattenData.push( x, y, z );				}				return flattenData;			}			function expandLineIndex( index ) {				const indices = [];				for ( let i = 0, l = index.length; i < l; i ++ ) {					const i1 = index[ i ];					const i2 = index[ i + 1 ];					indices.push( i1, i2 ); // an index of -1 indicates that the current line has ended and the next one begins					if ( index[ i + 2 ] === - 1 || i + 2 >= l ) {						i += 2;					}				}				return indices;			}			function expandLineData( data, index ) {				const triangulatedData = [];				let start = 0;				for ( let i = 0, l = index.length; i < l; i ++ ) {					const stride = start * 3;					const x = data[ stride ];					const y = data[ stride + 1 ];					const z = data[ stride + 2 ];					triangulatedData.push( x, y, z ); // an index of -1 indicates that the current line has ended and the next one begins					if ( index[ i + 2 ] === - 1 || i + 2 >= l ) {						i += 2;						start ++;					}				}				return triangulatedData;			}			const vA = new THREE.Vector3();			const vB = new THREE.Vector3();			const vC = new THREE.Vector3();			const uvA = new THREE.Vector2();			const uvB = new THREE.Vector2();			const uvC = new THREE.Vector2();			function computeAttributeFromIndexedData( coordIndex, index, data, itemSize ) {				const array = []; // we use the coordIndex.length as delimiter since normalIndex must contain at least as many indices				for ( let i = 0, l = coordIndex.length; i < l; i += 3 ) {					const a = index[ i ];					const b = index[ i + 1 ];					const c = index[ i + 2 ];					if ( itemSize === 2 ) {						uvA.fromArray( data, a * itemSize );						uvB.fromArray( data, b * itemSize );						uvC.fromArray( data, c * itemSize );						array.push( uvA.x, uvA.y );						array.push( uvB.x, uvB.y );						array.push( uvC.x, uvC.y );					} else {						vA.fromArray( data, a * itemSize );						vB.fromArray( data, b * itemSize );						vC.fromArray( data, c * itemSize );						array.push( vA.x, vA.y, vA.z );						array.push( vB.x, vB.y, vB.z );						array.push( vC.x, vC.y, vC.z );					}				}				return new THREE.Float32BufferAttribute( array, itemSize );			}			function computeAttributeFromFaceData( index, faceData ) {				const array = [];				for ( let i = 0, j = 0, l = index.length; i < l; i += 3, j ++ ) {					vA.fromArray( faceData, j * 3 );					array.push( vA.x, vA.y, vA.z );					array.push( vA.x, vA.y, vA.z );					array.push( vA.x, vA.y, vA.z );				}				return new THREE.Float32BufferAttribute( array, 3 );			}			function computeAttributeFromLineData( index, lineData ) {				const array = [];				for ( let i = 0, j = 0, l = index.length; i < l; i += 2, j ++ ) {					vA.fromArray( lineData, j * 3 );					array.push( vA.x, vA.y, vA.z );					array.push( vA.x, vA.y, vA.z );				}				return new THREE.Float32BufferAttribute( array, 3 );			}			function toNonIndexedAttribute( indices, attribute ) {				const array = attribute.array;				const itemSize = attribute.itemSize;				const array2 = new array.constructor( indices.length * itemSize );				let index = 0,					index2 = 0;				for ( let i = 0, l = indices.length; i < l; i ++ ) {					index = indices[ i ] * itemSize;					for ( let j = 0; j < itemSize; j ++ ) {						array2[ index2 ++ ] = array[ index ++ ];					}				}				return new THREE.Float32BufferAttribute( array2, itemSize );			}			const ab = new THREE.Vector3();			const cb = new THREE.Vector3();			function computeNormalAttribute( index, coord, creaseAngle ) {				const faces = [];				const vertexNormals = {}; // prepare face and raw vertex normals				for ( let i = 0, l = index.length; i < l; i += 3 ) {					const a = index[ i ];					const b = index[ i + 1 ];					const c = index[ i + 2 ];					const face = new Face( a, b, c );					vA.fromArray( coord, a * 3 );					vB.fromArray( coord, b * 3 );					vC.fromArray( coord, c * 3 );					cb.subVectors( vC, vB );					ab.subVectors( vA, vB );					cb.cross( ab );					cb.normalize();					face.normal.copy( cb );					if ( vertexNormals[ a ] === undefined ) vertexNormals[ a ] = [];					if ( vertexNormals[ b ] === undefined ) vertexNormals[ b ] = [];					if ( vertexNormals[ c ] === undefined ) vertexNormals[ c ] = [];					vertexNormals[ a ].push( face.normal );					vertexNormals[ b ].push( face.normal );					vertexNormals[ c ].push( face.normal );					faces.push( face );				} // compute vertex normals and build final geometry				const normals = [];				for ( let i = 0, l = faces.length; i < l; i ++ ) {					const face = faces[ i ];					const nA = weightedNormal( vertexNormals[ face.a ], face.normal, creaseAngle );					const nB = weightedNormal( vertexNormals[ face.b ], face.normal, creaseAngle );					const nC = weightedNormal( vertexNormals[ face.c ], face.normal, creaseAngle );					vA.fromArray( coord, face.a * 3 );					vB.fromArray( coord, face.b * 3 );					vC.fromArray( coord, face.c * 3 );					normals.push( nA.x, nA.y, nA.z );					normals.push( nB.x, nB.y, nB.z );					normals.push( nC.x, nC.y, nC.z );				}				return new THREE.Float32BufferAttribute( normals, 3 );			}			function weightedNormal( normals, vector, creaseAngle ) {				const normal = new THREE.Vector3();				if ( creaseAngle === 0 ) {					normal.copy( vector );				} else {					for ( let i = 0, l = normals.length; i < l; i ++ ) {						if ( normals[ i ].angleTo( vector ) < creaseAngle ) {							normal.add( normals[ i ] );						}					}				}				return normal.normalize();			}			function toColorArray( colors ) {				const array = [];				for ( let i = 0, l = colors.length; i < l; i += 3 ) {					array.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );				}				return array;			}			/**     * Vertically paints the faces interpolating between the     * specified colors at the specified angels. This is used for the Background     * node, but could be applied to other nodes with multiple faces as well.     *     * When used with the Background node, default is directionIsDown is true if     * interpolating the skyColor down from the Zenith. When interpolationg up from     * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.     *     * The first angle is never specified, it is the Zenith (0 rad). Angles are specified     * in radians. The geometry is thought a sphere, but could be anything. The color interpolation     * is linear along the Y axis in any case.     *     * You must specify one more color than you have angles at the beginning of the colors array.     * This is the color of the Zenith (the top of the shape).     *     * @param {BufferGeometry} geometry     * @param {number} radius     * @param {array} angles     * @param {array} colors     * @param {boolean} topDown - Whether to work top down or bottom up.     */			function paintFaces( geometry, radius, angles, colors, topDown ) {				// compute threshold values				const thresholds = [];				const startAngle = topDown === true ? 0 : Math.PI;				for ( let i = 0, l = colors.length; i < l; i ++ ) {					let angle = i === 0 ? 0 : angles[ i - 1 ];					angle = topDown === true ? angle : startAngle - angle;					const point = new THREE.Vector3();					point.setFromSphericalCoords( radius, angle, 0 );					thresholds.push( point );				} // generate vertex colors				const indices = geometry.index;				const positionAttribute = geometry.attributes.position;				const colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );				const position = new THREE.Vector3();				const color = new THREE.Color();				for ( let i = 0; i < indices.count; i ++ ) {					const index = indices.getX( i );					position.fromBufferAttribute( positionAttribute, index );					let thresholdIndexA, thresholdIndexB;					let t = 1;					for ( let j = 1; j < thresholds.length; j ++ ) {						thresholdIndexA = j - 1;						thresholdIndexB = j;						const thresholdA = thresholds[ thresholdIndexA ];						const thresholdB = thresholds[ thresholdIndexB ];						if ( topDown === true ) {							// interpolation for sky color							if ( position.y <= thresholdA.y && position.y > thresholdB.y ) {								t = Math.abs( thresholdA.y - position.y ) / Math.abs( thresholdA.y - thresholdB.y );								break;							}						} else {							// interpolation for ground color							if ( position.y >= thresholdA.y && position.y < thresholdB.y ) {								t = Math.abs( thresholdA.y - position.y ) / Math.abs( thresholdA.y - thresholdB.y );								break;							}						}					}					const colorA = colors[ thresholdIndexA ];					const colorB = colors[ thresholdIndexB ];					color.copy( colorA ).lerp( colorB, t );					colorAttribute.setXYZ( index, color.r, color.g, color.b );				}				geometry.setAttribute( 'color', colorAttribute );			} //			const textureLoader = new THREE.TextureLoader( this.manager );			textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin ); // check version (only 2.0 is supported)			if ( data.indexOf( '#VRML V2.0' ) === - 1 ) {				throw Error( 'THREE.VRMLLexer: Version of VRML asset not supported.' );			} // create JSON representing the tree structure of the VRML asset			const tree = generateVRMLTree( data ); // parse the tree structure to a three.js scene			const scene = parseTree( tree );			return scene;		}	}	class VRMLLexer {		constructor( tokens ) {			this.lexer = new chevrotain.Lexer( tokens ); // eslint-disable-line no-undef		}		lex( inputText ) {			const lexingResult = this.lexer.tokenize( inputText );			if ( lexingResult.errors.length > 0 ) {				console.error( lexingResult.errors );				throw Error( 'THREE.VRMLLexer: Lexing errors detected.' );			}			return lexingResult;		}	}	const CstParser = chevrotain.CstParser; // eslint-disable-line no-undef	class VRMLParser extends CstParser {		constructor( tokenVocabulary ) {			super( tokenVocabulary );			const $ = this;			const Version = tokenVocabulary[ 'Version' ];			const LCurly = tokenVocabulary[ 'LCurly' ];			const RCurly = tokenVocabulary[ 'RCurly' ];			const LSquare = tokenVocabulary[ 'LSquare' ];			const RSquare = tokenVocabulary[ 'RSquare' ];			const Identifier = tokenVocabulary[ 'Identifier' ];			const RouteIdentifier = tokenVocabulary[ 'RouteIdentifier' ];			const StringLiteral = tokenVocabulary[ 'StringLiteral' ];			const HexLiteral = tokenVocabulary[ 'HexLiteral' ];			const NumberLiteral = tokenVocabulary[ 'NumberLiteral' ];			const TrueLiteral = tokenVocabulary[ 'TrueLiteral' ];			const FalseLiteral = tokenVocabulary[ 'FalseLiteral' ];			const NullLiteral = tokenVocabulary[ 'NullLiteral' ];			const DEF = tokenVocabulary[ 'DEF' ];			const USE = tokenVocabulary[ 'USE' ];			const ROUTE = tokenVocabulary[ 'ROUTE' ];			const TO = tokenVocabulary[ 'TO' ];			const NodeName = tokenVocabulary[ 'NodeName' ];			$.RULE( 'vrml', function () {				$.SUBRULE( $.version );				$.AT_LEAST_ONE( function () {					$.SUBRULE( $.node );				} );				$.MANY( function () {					$.SUBRULE( $.route );				} );			} );			$.RULE( 'version', function () {				$.CONSUME( Version );			} );			$.RULE( 'node', function () {				$.OPTION( function () {					$.SUBRULE( $.def );				} );				$.CONSUME( NodeName );				$.CONSUME( LCurly );				$.MANY( function () {					$.SUBRULE( $.field );				} );				$.CONSUME( RCurly );			} );			$.RULE( 'field', function () {				$.CONSUME( Identifier );				$.OR2( [ {					ALT: function () {						$.SUBRULE( $.singleFieldValue );					}				}, {					ALT: function () {						$.SUBRULE( $.multiFieldValue );					}				} ] );			} );			$.RULE( 'def', function () {				$.CONSUME( DEF );				$.OR( [ {					ALT: function () {						$.CONSUME( Identifier );					}				}, {					ALT: function () {						$.CONSUME( NodeName );					}				} ] );			} );			$.RULE( 'use', function () {				$.CONSUME( USE );				$.OR( [ {					ALT: function () {						$.CONSUME( Identifier );					}				}, {					ALT: function () {						$.CONSUME( NodeName );					}				} ] );			} );			$.RULE( 'singleFieldValue', function () {				$.AT_LEAST_ONE( function () {					$.OR( [ {						ALT: function () {							$.SUBRULE( $.node );						}					}, {						ALT: function () {							$.SUBRULE( $.use );						}					}, {						ALT: function () {							$.CONSUME( StringLiteral );						}					}, {						ALT: function () {							$.CONSUME( HexLiteral );						}					}, {						ALT: function () {							$.CONSUME( NumberLiteral );						}					}, {						ALT: function () {							$.CONSUME( TrueLiteral );						}					}, {						ALT: function () {							$.CONSUME( FalseLiteral );						}					}, {						ALT: function () {							$.CONSUME( NullLiteral );						}					} ] );				} );			} );			$.RULE( 'multiFieldValue', function () {				$.CONSUME( LSquare );				$.MANY( function () {					$.OR( [ {						ALT: function () {							$.SUBRULE( $.node );						}					}, {						ALT: function () {							$.SUBRULE( $.use );						}					}, {						ALT: function () {							$.CONSUME( StringLiteral );						}					}, {						ALT: function () {							$.CONSUME( HexLiteral );						}					}, {						ALT: function () {							$.CONSUME( NumberLiteral );						}					}, {						ALT: function () {							$.CONSUME( NullLiteral );						}					} ] );				} );				$.CONSUME( RSquare );			} );			$.RULE( 'route', function () {				$.CONSUME( ROUTE );				$.CONSUME( RouteIdentifier );				$.CONSUME( TO );				$.CONSUME2( RouteIdentifier );			} );			this.performSelfAnalysis();		}	}	class Face {		constructor( a, b, c ) {			this.a = a;			this.b = b;			this.c = c;			this.normal = new THREE.Vector3();		}	}	const TEXTURE_TYPE = {		INTENSITY: 1,		INTENSITY_ALPHA: 2,		RGB: 3,		RGBA: 4	};	THREE.VRMLLoader = VRMLLoader;} )();
 |