VRMLLoader.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. */
  4. THREE.VRMLLoader = function ( manager ) {
  5. this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
  6. };
  7. THREE.VRMLLoader.prototype = {
  8. constructor: THREE.VRMLLoader,
  9. // for IndexedFaceSet support
  10. isRecordingPoints: false,
  11. isRecordingFaces: false,
  12. points: [],
  13. indexes: [],
  14. // for Background support
  15. isRecordingAngles: false,
  16. isRecordingColors: false,
  17. angles: [],
  18. colors: [],
  19. recordingFieldname: null,
  20. crossOrigin: 'anonymous',
  21. load: function ( url, onLoad, onProgress, onError ) {
  22. var scope = this;
  23. var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
  24. var loader = new THREE.FileLoader( this.manager );
  25. loader.setPath( scope.path );
  26. loader.load( url, function ( text ) {
  27. onLoad( scope.parse( text, path ) );
  28. }, onProgress, onError );
  29. },
  30. setPath: function ( value ) {
  31. this.path = value;
  32. return this;
  33. },
  34. setResourcePath: function ( value ) {
  35. this.resourcePath = value;
  36. return this;
  37. },
  38. setCrossOrigin: function ( value ) {
  39. this.crossOrigin = value;
  40. return this;
  41. },
  42. parse: function ( data, path ) {
  43. var scope = this;
  44. var textureLoader = new THREE.TextureLoader( this.manager );
  45. textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
  46. function parseV2( lines, scene ) {
  47. var defines = {};
  48. var float_pattern = /(\b|\-|\+)([\d\.e]+)/;
  49. var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
  50. var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
  51. /**
  52. * Vertically paints the faces interpolating between the
  53. * specified colors at the specified angels. This is used for the Background
  54. * node, but could be applied to other nodes with multiple faces as well.
  55. *
  56. * When used with the Background node, default is directionIsDown is true if
  57. * interpolating the skyColor down from the Zenith. When interpolationg up from
  58. * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
  59. *
  60. * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
  61. * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
  62. * is linear along the Y axis in any case.
  63. *
  64. * You must specify one more color than you have angles at the beginning of the colors array.
  65. * This is the color of the Zenith (the top of the shape).
  66. *
  67. * @param geometry
  68. * @param radius
  69. * @param angles
  70. * @param colors
  71. * @param boolean topDown Whether to work top down or bottom up.
  72. */
  73. function paintFaces( geometry, radius, angles, colors, topDown ) {
  74. var direction = ( topDown === true ) ? 1 : - 1;
  75. var coord = [], A = {}, B = {}, applyColor = false;
  76. for ( var k = 0; k < angles.length; k ++ ) {
  77. // push the vector at which the color changes
  78. var vec = {
  79. x: direction * ( Math.cos( angles[ k ] ) * radius ),
  80. y: direction * ( Math.sin( angles[ k ] ) * radius )
  81. };
  82. coord.push( vec );
  83. }
  84. var index = geometry.index;
  85. var positionAttribute = geometry.attributes.position;
  86. var colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
  87. var position = new THREE.Vector3();
  88. var color = new THREE.Color();
  89. for ( var i = 0; i < index.count; i ++ ) {
  90. var vertexIndex = index.getX( i );
  91. position.fromBufferAttribute( positionAttribute, vertexIndex );
  92. for ( var j = 0; j < colors.length; j ++ ) {
  93. // linear interpolation between aColor and bColor, calculate proportion
  94. // A is previous point (angle)
  95. if ( j === 0 ) {
  96. A.x = 0;
  97. A.y = ( topDown === true ) ? radius : - 1 * radius;
  98. } else {
  99. A.x = coord[ j - 1 ].x;
  100. A.y = coord[ j - 1 ].y;
  101. }
  102. // B is current point (angle)
  103. B = coord[ j ];
  104. if ( B !== undefined ) {
  105. // p has to be between the points A and B which we interpolate
  106. applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
  107. if ( applyColor === true ) {
  108. var aColor = colors[ j ];
  109. var bColor = colors[ j + 1 ];
  110. // below is simple linear interpolation
  111. var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
  112. // to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
  113. color.copy( aColor ).lerp( bColor, t );
  114. colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
  115. } else {
  116. var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
  117. var c = colors[ colorIndex ];
  118. colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
  119. }
  120. }
  121. }
  122. }
  123. geometry.addAttribute( 'color', colorAttribute );
  124. }
  125. var index = [];
  126. function parseProperty( node, line ) {
  127. var parts = [], part, property = {}, fieldName;
  128. /**
  129. * Expression for matching relevant information, such as a name or value, but not the separators
  130. * @type {RegExp}
  131. */
  132. var regex = /[^\s,\[\]]+/g;
  133. var point;
  134. while ( null !== ( part = regex.exec( line ) ) ) {
  135. parts.push( part[ 0 ] );
  136. }
  137. fieldName = parts[ 0 ];
  138. // trigger several recorders
  139. switch ( fieldName ) {
  140. case 'skyAngle':
  141. case 'groundAngle':
  142. scope.recordingFieldname = fieldName;
  143. scope.isRecordingAngles = true;
  144. scope.angles = [];
  145. break;
  146. case 'color':
  147. case 'skyColor':
  148. case 'groundColor':
  149. scope.recordingFieldname = fieldName;
  150. scope.isRecordingColors = true;
  151. scope.colors = [];
  152. break;
  153. case 'point':
  154. case 'vector':
  155. scope.recordingFieldname = fieldName;
  156. scope.isRecordingPoints = true;
  157. scope.points = [];
  158. break;
  159. case 'colorIndex':
  160. case 'coordIndex':
  161. case 'normalIndex':
  162. case 'texCoordIndex':
  163. scope.recordingFieldname = fieldName;
  164. scope.isRecordingFaces = true;
  165. scope.indexes = [];
  166. break;
  167. }
  168. if ( scope.isRecordingFaces ) {
  169. // the parts hold the indexes as strings
  170. if ( parts.length > 0 ) {
  171. for ( var ind = 0; ind < parts.length; ind ++ ) {
  172. // the part should either be positive integer or -1
  173. if ( ! /(-?\d+)/.test( parts[ ind ] ) ) {
  174. continue;
  175. }
  176. // end of current face
  177. if ( parts[ ind ] === '-1' ) {
  178. if ( index.length > 0 ) {
  179. scope.indexes.push( index );
  180. }
  181. // start new one
  182. index = [];
  183. } else {
  184. index.push( parseInt( parts[ ind ] ) );
  185. }
  186. }
  187. }
  188. // end
  189. if ( /]/.exec( line ) ) {
  190. if ( index.length > 0 ) {
  191. scope.indexes.push( index );
  192. }
  193. // start new one
  194. index = [];
  195. scope.isRecordingFaces = false;
  196. node[ scope.recordingFieldname ] = scope.indexes;
  197. }
  198. } else if ( scope.isRecordingPoints ) {
  199. if ( node.nodeType == 'Coordinate' ) {
  200. while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
  201. point = {
  202. x: parseFloat( parts[ 1 ] ),
  203. y: parseFloat( parts[ 2 ] ),
  204. z: parseFloat( parts[ 3 ] )
  205. };
  206. scope.points.push( point );
  207. }
  208. }
  209. if ( node.nodeType == 'Normal' ) {
  210. while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
  211. point = {
  212. x: parseFloat( parts[ 1 ] ),
  213. y: parseFloat( parts[ 2 ] ),
  214. z: parseFloat( parts[ 3 ] )
  215. };
  216. scope.points.push( point );
  217. }
  218. }
  219. if ( node.nodeType == 'TextureCoordinate' ) {
  220. while ( null !== ( parts = float2_pattern.exec( line ) ) ) {
  221. point = {
  222. x: parseFloat( parts[ 1 ] ),
  223. y: parseFloat( parts[ 2 ] )
  224. };
  225. scope.points.push( point );
  226. }
  227. }
  228. // end
  229. if ( /]/.exec( line ) ) {
  230. scope.isRecordingPoints = false;
  231. node.points = scope.points;
  232. }
  233. } else if ( scope.isRecordingAngles ) {
  234. // the parts hold the angles as strings
  235. if ( parts.length > 0 ) {
  236. for ( var ind = 0; ind < parts.length; ind ++ ) {
  237. // the part should be a float
  238. if ( ! float_pattern.test( parts[ ind ] ) ) {
  239. continue;
  240. }
  241. scope.angles.push( parseFloat( parts[ ind ] ) );
  242. }
  243. }
  244. // end
  245. if ( /]/.exec( line ) ) {
  246. scope.isRecordingAngles = false;
  247. node[ scope.recordingFieldname ] = scope.angles;
  248. }
  249. } else if ( scope.isRecordingColors ) {
  250. while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
  251. var color = {
  252. r: parseFloat( parts[ 1 ] ),
  253. g: parseFloat( parts[ 2 ] ),
  254. b: parseFloat( parts[ 3 ] )
  255. };
  256. scope.colors.push( color );
  257. }
  258. // end
  259. if ( /]/.exec( line ) ) {
  260. scope.isRecordingColors = false;
  261. node[ scope.recordingFieldname ] = scope.colors;
  262. }
  263. } else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) {
  264. switch ( fieldName ) {
  265. case 'diffuseColor':
  266. case 'emissiveColor':
  267. case 'specularColor':
  268. case 'color':
  269. if ( parts.length !== 4 ) {
  270. console.warn( 'THREE.VRMLLoader: Invalid color format detected for %s.', fieldName );
  271. break;
  272. }
  273. property = {
  274. r: parseFloat( parts[ 1 ] ),
  275. g: parseFloat( parts[ 2 ] ),
  276. b: parseFloat( parts[ 3 ] )
  277. };
  278. break;
  279. case 'location':
  280. case 'direction':
  281. case 'translation':
  282. case 'scale':
  283. case 'size':
  284. if ( parts.length !== 4 ) {
  285. console.warn( 'THREE.VRMLLoader: Invalid vector format detected for %s.', fieldName );
  286. break;
  287. }
  288. property = {
  289. x: parseFloat( parts[ 1 ] ),
  290. y: parseFloat( parts[ 2 ] ),
  291. z: parseFloat( parts[ 3 ] )
  292. };
  293. break;
  294. case 'intensity':
  295. case 'cutOffAngle':
  296. case 'radius':
  297. case 'topRadius':
  298. case 'bottomRadius':
  299. case 'height':
  300. case 'transparency':
  301. case 'shininess':
  302. case 'ambientIntensity':
  303. case 'creaseAngle':
  304. if ( parts.length !== 2 ) {
  305. console.warn( 'THREE.VRMLLoader: Invalid single float value specification detected for %s.', fieldName );
  306. break;
  307. }
  308. property = parseFloat( parts[ 1 ] );
  309. break;
  310. case 'rotation':
  311. if ( parts.length !== 5 ) {
  312. console.warn( 'THREE.VRMLLoader: Invalid quaternion format detected for %s.', fieldName );
  313. break;
  314. }
  315. property = {
  316. x: parseFloat( parts[ 1 ] ),
  317. y: parseFloat( parts[ 2 ] ),
  318. z: parseFloat( parts[ 3 ] ),
  319. w: parseFloat( parts[ 4 ] )
  320. };
  321. break;
  322. case 'on':
  323. case 'ccw':
  324. case 'solid':
  325. case 'colorPerVertex':
  326. case 'convex':
  327. if ( parts.length !== 2 ) {
  328. console.warn( 'THREE.VRMLLoader: Invalid format detected for %s.', fieldName );
  329. break;
  330. }
  331. property = parts[ 1 ] === 'TRUE' ? true : false;
  332. break;
  333. }
  334. // VRMLLoader does not support text so it can't process the "string" property yet
  335. if ( fieldName !== 'string' ) node[ fieldName ] = property;
  336. }
  337. return property;
  338. }
  339. function getTree( lines ) {
  340. var tree = { 'string': 'Scene', children: [] };
  341. var current = tree;
  342. var matches;
  343. var specification;
  344. for ( var i = 0; i < lines.length; i ++ ) {
  345. var comment = '';
  346. var line = lines[ i ];
  347. // omit whitespace only lines
  348. if ( null !== ( /^\s+?$/g.exec( line ) ) ) {
  349. continue;
  350. }
  351. line = line.trim();
  352. // skip empty lines
  353. if ( line === '' ) {
  354. continue;
  355. }
  356. if ( /#/.exec( line ) ) {
  357. var parts = line.split( '#' );
  358. // discard everything after the #, it is a comment
  359. line = parts[ 0 ];
  360. // well, let's also keep the comment
  361. comment = parts[ 1 ];
  362. }
  363. if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) {
  364. // first subpattern should match the Node name
  365. var block = { 'nodeType': matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment': comment };
  366. current.children.push( block );
  367. current = block;
  368. if ( /}/.exec( line ) ) {
  369. // example: geometry Box { size 1 1 1 } # all on the same line
  370. specification = /{(.*)}/.exec( line )[ 1 ];
  371. // todo: remove once new parsing is complete?
  372. block.children.push( specification );
  373. parseProperty( current, specification );
  374. current = current.parent;
  375. }
  376. } else if ( /}/.exec( line ) ) {
  377. current = current.parent;
  378. } else if ( line !== '' ) {
  379. parseProperty( current, line );
  380. // todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way
  381. current.children.push( line );
  382. }
  383. }
  384. return tree;
  385. }
  386. function parseNode( data, parent ) {
  387. var object;
  388. if ( typeof data === 'string' ) {
  389. if ( /USE/.exec( data ) ) {
  390. var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ];
  391. if ( undefined == defines[ defineKey ] ) {
  392. console.warn( 'THREE.VRMLLoader: %s is not defined.', defineKey );
  393. } else {
  394. if ( /appearance/.exec( data ) && defineKey ) {
  395. parent.material = defines[ defineKey ].clone();
  396. } else if ( /geometry/.exec( data ) && defineKey ) {
  397. parent.geometry = defines[ defineKey ].clone();
  398. // the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it
  399. if ( defines[ defineKey ].solid !== undefined && defines[ defineKey ].solid === false ) {
  400. parent.geometry.solid = false;
  401. parent.material.side = THREE.DoubleSide;
  402. }
  403. } else if ( defineKey ) {
  404. object = defines[ defineKey ].clone();
  405. parent.add( object );
  406. }
  407. }
  408. }
  409. return;
  410. }
  411. object = parent;
  412. if ( data.string.indexOf( 'AmbientLight' ) > - 1 && data.nodeType === 'PointLight' ) {
  413. data.nodeType = 'AmbientLight';
  414. }
  415. var l_visible = data.on !== undefined ? data.on : true;
  416. var l_intensity = data.intensity !== undefined ? data.intensity : 1;
  417. var l_color = new THREE.Color();
  418. if ( data.color ) {
  419. l_color.copy( data.color );
  420. }
  421. if ( data.nodeType === 'AmbientLight' ) {
  422. object = new THREE.AmbientLight( l_color, l_intensity );
  423. object.visible = l_visible;
  424. parent.add( object );
  425. } else if ( data.nodeType === 'PointLight' ) {
  426. var l_distance = 0;
  427. if ( data.radius !== undefined && data.radius < 1000 ) {
  428. l_distance = data.radius;
  429. }
  430. object = new THREE.PointLight( l_color, l_intensity, l_distance );
  431. object.visible = l_visible;
  432. parent.add( object );
  433. } else if ( data.nodeType === 'SpotLight' ) {
  434. var l_intensity = 1;
  435. var l_distance = 0;
  436. var l_angle = Math.PI / 3;
  437. var l_penumbra = 0;
  438. var l_visible = true;
  439. if ( data.radius !== undefined && data.radius < 1000 ) {
  440. l_distance = data.radius;
  441. }
  442. if ( data.cutOffAngle !== undefined ) {
  443. l_angle = data.cutOffAngle;
  444. }
  445. object = new THREE.SpotLight( l_color, l_intensity, l_distance, l_angle, l_penumbra );
  446. object.visible = l_visible;
  447. parent.add( object );
  448. } else if ( data.nodeType === 'Transform' || data.nodeType === 'Group' ) {
  449. object = new THREE.Object3D();
  450. if ( /DEF/.exec( data.string ) ) {
  451. object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
  452. defines[ object.name ] = object;
  453. }
  454. if ( data.translation !== undefined ) {
  455. var t = data.translation;
  456. object.position.set( t.x, t.y, t.z );
  457. }
  458. if ( data.rotation !== undefined ) {
  459. var r = data.rotation;
  460. object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w );
  461. }
  462. if ( data.scale !== undefined ) {
  463. var s = data.scale;
  464. object.scale.set( s.x, s.y, s.z );
  465. }
  466. parent.add( object );
  467. } else if ( data.nodeType === 'Shape' ) {
  468. object = new THREE.Mesh();
  469. if ( /DEF/.exec( data.string ) ) {
  470. object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
  471. defines[ object.name ] = object;
  472. }
  473. parent.add( object );
  474. } else if ( data.nodeType === 'Background' ) {
  475. var segments = 20;
  476. // sky (full sphere):
  477. var radius = 2e4;
  478. var skyGeometry = new THREE.SphereBufferGeometry( radius, segments, segments );
  479. var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide } );
  480. if ( data.skyColor.length > 1 ) {
  481. paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true );
  482. skyMaterial.vertexColors = THREE.VertexColors;
  483. } else {
  484. var color = data.skyColor[ 0 ];
  485. skyMaterial.color.setRGB( color.r, color.b, color.g );
  486. }
  487. scene.add( new THREE.Mesh( skyGeometry, skyMaterial ) );
  488. // ground (half sphere):
  489. if ( data.groundColor !== undefined ) {
  490. radius = 1.2e4;
  491. var groundGeometry = new THREE.SphereBufferGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
  492. var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors } );
  493. paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false );
  494. scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) );
  495. }
  496. } else if ( /geometry/.exec( data.string ) ) {
  497. if ( data.nodeType === 'Box' ) {
  498. var s = data.size;
  499. parent.geometry = new THREE.BoxBufferGeometry( s.x, s.y, s.z );
  500. } else if ( data.nodeType === 'Cylinder' ) {
  501. parent.geometry = new THREE.CylinderBufferGeometry( data.radius, data.radius, data.height );
  502. } else if ( data.nodeType === 'Cone' ) {
  503. parent.geometry = new THREE.CylinderBufferGeometry( data.topRadius, data.bottomRadius, data.height );
  504. } else if ( data.nodeType === 'Sphere' ) {
  505. parent.geometry = new THREE.SphereBufferGeometry( data.radius );
  506. } else if ( data.nodeType === 'IndexedLineSet' ) {
  507. console.warn( 'THREE.VRMLLoader: IndexedLineSet not supported yet.' );
  508. parent.parent.remove( parent ); // since the loader is not able to parse the geometry, remove the respective 3D object
  509. } else if ( data.nodeType === 'Text' ) {
  510. console.warn( 'THREE.VRMLLoader: Text not supported yet.' );
  511. parent.parent.remove( parent );
  512. } else if ( data.nodeType === 'IndexedFaceSet' ) {
  513. var geometry = new THREE.BufferGeometry();
  514. var positions = [];
  515. var colors = [];
  516. var normals = [];
  517. var uvs = [];
  518. var position, color, normal, uv;
  519. var i, il, j, jl;
  520. for ( i = 0, il = data.children.length; i < il; i ++ ) {
  521. var child = data.children[ i ];
  522. // uvs
  523. if ( child.nodeType === 'TextureCoordinate' ) {
  524. if ( child.points ) {
  525. for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
  526. uv = child.points[ j ];
  527. uvs.push( uv.x, uv.y );
  528. }
  529. }
  530. }
  531. // normals
  532. if ( child.nodeType === 'Normal' ) {
  533. if ( child.points ) {
  534. for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
  535. normal = child.points[ j ];
  536. normals.push( normal.x, normal.y, normal.z );
  537. }
  538. }
  539. }
  540. // colors
  541. if ( child.nodeType === 'Color' ) {
  542. if ( child.color ) {
  543. for ( j = 0, jl = child.color.length; j < jl; j ++ ) {
  544. color = child.color[ j ];
  545. colors.push( color.r, color.g, color.b );
  546. }
  547. }
  548. }
  549. // positions
  550. if ( child.nodeType === 'Coordinate' ) {
  551. if ( child.points ) {
  552. for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
  553. position = child.points[ j ];
  554. positions.push( position.x, position.y, position.z );
  555. }
  556. }
  557. if ( child.string.indexOf( 'DEF' ) > - 1 ) {
  558. var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ];
  559. defines[ name ] = positions.slice( 0 );
  560. }
  561. if ( child.string.indexOf( 'USE' ) > - 1 ) {
  562. var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ];
  563. positions = defines[ defineKey ];
  564. }
  565. }
  566. }
  567. // some shapes only have vertices for use in other shapes
  568. if ( data.coordIndex ) {
  569. function triangulateIndexArray( indexArray, ccw, colorPerVertex ) {
  570. if ( ccw === undefined ) {
  571. // ccw is true by default
  572. ccw = true;
  573. }
  574. var triangulatedIndexArray = [];
  575. var skip = 0;
  576. for ( i = 0, il = indexArray.length; i < il; i ++ ) {
  577. if ( colorPerVertex === false ) {
  578. var colorIndices = indexArray[ i ];
  579. for ( j = 0, jl = colorIndices.length; j < jl; j ++ ) {
  580. var index = colorIndices[ j ];
  581. triangulatedIndexArray.push( index, index, index );
  582. }
  583. } else {
  584. var indexedFace = indexArray[ i ];
  585. // VRML support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
  586. skip = 0;
  587. while ( indexedFace.length >= 3 && skip < ( indexedFace.length - 2 ) ) {
  588. var i1 = indexedFace[ 0 ];
  589. var i2 = indexedFace[ skip + ( ccw ? 1 : 2 ) ];
  590. var i3 = indexedFace[ skip + ( ccw ? 2 : 1 ) ];
  591. triangulatedIndexArray.push( i1, i2, i3 );
  592. skip ++;
  593. }
  594. }
  595. }
  596. return triangulatedIndexArray;
  597. }
  598. var positionIndexes = data.coordIndex ? triangulateIndexArray( data.coordIndex, data.ccw ) : [];
  599. var normalIndexes = data.normalIndex ? triangulateIndexArray( data.normalIndex, data.ccw ) : positionIndexes;
  600. var colorIndexes = data.colorIndex ? triangulateIndexArray( data.colorIndex, data.ccw, data.colorPerVertex ) : [];
  601. var uvIndexes = data.texCoordIndex ? triangulateIndexArray( data.texCoordIndex, data.ccw ) : positionIndexes;
  602. var newIndexes = [];
  603. var newPositions = [];
  604. var newNormals = [];
  605. var newColors = [];
  606. var newUvs = [];
  607. // if any other index array does not match the coordinate indexes, split any points that differ
  608. var pointMap = Object.create( null );
  609. for ( i = 0; i < positionIndexes.length; i ++ ) {
  610. var pointAttributes = [];
  611. var positionIndex = positionIndexes[ i ];
  612. var normalIndex = normalIndexes[ i ];
  613. var colorIndex = colorIndexes[ i ];
  614. var uvIndex = uvIndexes[ i ];
  615. var base = 10; // which base to use to represent each value
  616. pointAttributes.push( positionIndex.toString( base ) );
  617. if ( normalIndex !== undefined ) {
  618. pointAttributes.push( normalIndex.toString( base ) );
  619. }
  620. if ( colorIndex !== undefined ) {
  621. pointAttributes.push( colorIndex.toString( base ) );
  622. }
  623. if ( uvIndex !== undefined ) {
  624. pointAttributes.push( uvIndex.toString( base ) );
  625. }
  626. var pointId = pointAttributes.join( ',' );
  627. var newIndex = pointMap[ pointId ];
  628. if ( newIndex === undefined ) {
  629. newIndex = newPositions.length / 3;
  630. pointMap[ pointId ] = newIndex;
  631. newPositions.push(
  632. positions[ positionIndex * 3 ],
  633. positions[ positionIndex * 3 + 1 ],
  634. positions[ positionIndex * 3 + 2 ]
  635. );
  636. if ( normalIndex !== undefined && normals.length > 0 ) {
  637. newNormals.push(
  638. normals[ normalIndex * 3 ],
  639. normals[ normalIndex * 3 + 1 ],
  640. normals[ normalIndex * 3 + 2 ]
  641. );
  642. }
  643. if ( colorIndex !== undefined && colors.length > 0 ) {
  644. newColors.push(
  645. colors[ colorIndex * 3 ],
  646. colors[ colorIndex * 3 + 1 ],
  647. colors[ colorIndex * 3 + 2 ]
  648. );
  649. }
  650. if ( uvIndex !== undefined && uvs.length > 0 ) {
  651. newUvs.push(
  652. uvs[ uvIndex * 2 ],
  653. uvs[ uvIndex * 2 + 1 ]
  654. );
  655. }
  656. }
  657. newIndexes.push( newIndex );
  658. }
  659. positions = newPositions;
  660. normals = newNormals;
  661. colors = newColors;
  662. uvs = newUvs;
  663. geometry.setIndex( newIndexes );
  664. } else {
  665. // do not add dummy mesh to the scene
  666. parent.parent.remove( parent );
  667. }
  668. if ( false === data.solid ) {
  669. parent.material.side = THREE.DoubleSide;
  670. }
  671. // we need to store it on the geometry for use with defines
  672. geometry.solid = data.solid;
  673. geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
  674. if ( colors.length > 0 ) {
  675. geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
  676. parent.material.vertexColors = THREE.VertexColors;
  677. }
  678. if ( uvs.length > 0 ) {
  679. geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
  680. }
  681. if ( normals.length > 0 ) {
  682. geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
  683. } else {
  684. // convert geometry to non-indexed to get sharp normals
  685. geometry = geometry.toNonIndexed();
  686. geometry.computeVertexNormals();
  687. }
  688. geometry.computeBoundingSphere();
  689. // see if it's a define
  690. if ( /DEF/.exec( data.string ) ) {
  691. geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
  692. defines[ geometry.name ] = geometry;
  693. }
  694. parent.geometry = geometry;
  695. }
  696. return;
  697. } else if ( /appearance/.exec( data.string ) ) {
  698. for ( var i = 0; i < data.children.length; i ++ ) {
  699. var child = data.children[ i ];
  700. if ( child.nodeType === 'Material' ) {
  701. var material = new THREE.MeshPhongMaterial();
  702. if ( child.diffuseColor !== undefined ) {
  703. var d = child.diffuseColor;
  704. material.color.setRGB( d.r, d.g, d.b );
  705. }
  706. if ( child.emissiveColor !== undefined ) {
  707. var e = child.emissiveColor;
  708. material.emissive.setRGB( e.r, e.g, e.b );
  709. }
  710. if ( child.specularColor !== undefined ) {
  711. var s = child.specularColor;
  712. material.specular.setRGB( s.r, s.g, s.b );
  713. }
  714. if ( child.transparency !== undefined ) {
  715. var t = child.transparency;
  716. // transparency is opposite of opacity
  717. material.opacity = Math.abs( 1 - t );
  718. material.transparent = true;
  719. }
  720. if ( /DEF/.exec( data.string ) ) {
  721. material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
  722. defines[ material.name ] = material;
  723. }
  724. parent.material = material;
  725. }
  726. if ( child.nodeType === 'ImageTexture' ) {
  727. var textureName = /"([^"]+)"/.exec( child.children[ 0 ] );
  728. if ( textureName ) {
  729. parent.material.name = textureName[ 1 ];
  730. parent.material.map = textureLoader.load( textureName[ 1 ] );
  731. }
  732. }
  733. }
  734. return;
  735. }
  736. for ( var i = 0, l = data.children.length; i < l; i ++ ) {
  737. parseNode( data.children[ i ], object );
  738. }
  739. }
  740. parseNode( getTree( lines ), scene );
  741. }
  742. var scene = new THREE.Scene();
  743. var lines = data.split( '\n' );
  744. // some lines do not have breaks
  745. for ( var i = lines.length - 1; i > 0; i -- ) {
  746. // The # symbol indicates that all subsequent text, until the end of the line is a comment,
  747. // and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html)
  748. lines[ i ] = lines[ i ].replace( /(#.*)/, '' );
  749. var line = lines[ i ];
  750. // split lines with {..{ or {..[ - some have both
  751. if ( /{.*[{\[]/.test( line ) ) {
  752. var parts = line.split( '{' ).join( '{\n' ).split( '\n' );
  753. parts.unshift( 1 );
  754. parts.unshift( i );
  755. lines.splice.apply( lines, parts );
  756. } else if ( /\].*}/.test( line ) ) {
  757. // split lines with ]..}
  758. var parts = line.split( ']' ).join( ']\n' ).split( '\n' );
  759. parts.unshift( 1 );
  760. parts.unshift( i );
  761. lines.splice.apply( lines, parts );
  762. }
  763. line = lines[ i ];
  764. if ( /}.*}/.test( line ) ) {
  765. // split lines with }..}
  766. var parts = line.split( '}' ).join( '}\n' ).split( '\n' );
  767. parts.unshift( 1 );
  768. parts.unshift( i );
  769. lines.splice.apply( lines, parts );
  770. }
  771. line = lines[ i ];
  772. if ( /^\b[^\s]+\b$/.test( line.trim() ) ) {
  773. // prevent lines with single words like "coord" or "geometry", see #12209
  774. lines[ i + 1 ] = line + ' ' + lines[ i + 1 ].trim();
  775. lines.splice( i, 1 );
  776. } else if ( ( line.indexOf( 'coord' ) > - 1 ) && ( line.indexOf( '[' ) < 0 ) && ( line.indexOf( '{' ) < 0 ) ) {
  777. // force the parser to create Coordinate node for empty coords
  778. // coord USE something -> coord USE something Coordinate {}
  779. lines[ i ] += ' Coordinate {}';
  780. }
  781. }
  782. var header = lines.shift();
  783. if ( /V1.0/.exec( header ) ) {
  784. console.warn( 'THREE.VRMLLoader: V1.0 not supported yet.' );
  785. } else if ( /V2.0/.exec( header ) ) {
  786. parseV2( lines, scene );
  787. }
  788. return scene;
  789. }
  790. };