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;
- } )();
|