|
@@ -1,1332 +1,2566 @@
|
|
/**
|
|
/**
|
|
- * @author mrdoob / http://mrdoob.com/
|
|
|
|
|
|
+ * @author Mugen87 / https://github.com/Mugen87
|
|
*/
|
|
*/
|
|
|
|
|
|
-THREE.VRMLLoader = function ( manager ) {
|
|
|
|
|
|
+/* global chevrotain */
|
|
|
|
|
|
- this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
|
|
|
|
|
+THREE.VRMLLoader = ( function () {
|
|
|
|
|
|
-};
|
|
|
|
|
|
+ // dependency check
|
|
|
|
|
|
-THREE.VRMLLoader.prototype = {
|
|
|
|
|
|
+ if ( typeof chevrotain === 'undefined' ) {
|
|
|
|
|
|
- constructor: THREE.VRMLLoader,
|
|
|
|
|
|
+ throw Error( 'THREE.VRMLLoader: External library chevrotain.min.js required.' );
|
|
|
|
|
|
- // for IndexedFaceSet support
|
|
|
|
- isRecordingPoints: false,
|
|
|
|
- isRecordingFaces: false,
|
|
|
|
- points: [],
|
|
|
|
- indexes: [],
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // for Background support
|
|
|
|
- isRecordingAngles: false,
|
|
|
|
- isRecordingColors: false,
|
|
|
|
- angles: [],
|
|
|
|
- colors: [],
|
|
|
|
|
|
+ // class definitions
|
|
|
|
|
|
- recordingFieldname: null,
|
|
|
|
|
|
+ function VRMLLoader( manager ) {
|
|
|
|
|
|
- crossOrigin: 'anonymous',
|
|
|
|
|
|
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
|
|
|
|
|
- load: function ( url, onLoad, onProgress, onError ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var scope = this;
|
|
|
|
|
|
+ VRMLLoader.prototype = {
|
|
|
|
|
|
- var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
|
|
|
|
|
|
+ constructor: VRMLLoader,
|
|
|
|
|
|
- var loader = new THREE.FileLoader( this.manager );
|
|
|
|
- loader.setPath( scope.path );
|
|
|
|
- loader.load( url, function ( text ) {
|
|
|
|
|
|
+ crossOrigin: 'anonymous',
|
|
|
|
|
|
- onLoad( scope.parse( text, path ) );
|
|
|
|
|
|
+ load: function ( url, onLoad, onProgress, onError ) {
|
|
|
|
|
|
- }, onProgress, onError );
|
|
|
|
|
|
+ var scope = this;
|
|
|
|
|
|
- },
|
|
|
|
|
|
+ var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
|
|
|
|
|
|
- setPath: function ( value ) {
|
|
|
|
|
|
+ var loader = new THREE.FileLoader( this.manager );
|
|
|
|
+ loader.setPath( scope.path );
|
|
|
|
+ loader.load( url, function ( text ) {
|
|
|
|
|
|
- this.path = value;
|
|
|
|
- return this;
|
|
|
|
|
|
+ onLoad( scope.parse( text, path ) );
|
|
|
|
|
|
- },
|
|
|
|
|
|
+ }, onProgress, onError );
|
|
|
|
|
|
- setResourcePath: function ( value ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- this.resourcePath = value;
|
|
|
|
- return this;
|
|
|
|
|
|
+ setPath: function ( value ) {
|
|
|
|
|
|
- },
|
|
|
|
|
|
+ this.path = value;
|
|
|
|
+ return this;
|
|
|
|
|
|
- setCrossOrigin: function ( value ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- this.crossOrigin = value;
|
|
|
|
- return this;
|
|
|
|
|
|
+ setResourcePath: function ( value ) {
|
|
|
|
|
|
- },
|
|
|
|
|
|
+ this.resourcePath = value;
|
|
|
|
+ return this;
|
|
|
|
|
|
- parse: function ( data, path ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- var scope = this;
|
|
|
|
|
|
+ setCrossOrigin: function ( value ) {
|
|
|
|
|
|
- var textureLoader = new THREE.TextureLoader( this.manager );
|
|
|
|
- textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
|
|
|
|
|
|
+ this.crossOrigin = value;
|
|
|
|
+ return this;
|
|
|
|
|
|
- function parseV2( lines, scene ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- var defines = {};
|
|
|
|
- var float_pattern = /(\b|\-|\+)([\d\.e]+)/;
|
|
|
|
- var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
|
|
|
|
- var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
|
|
|
|
|
|
+ parse: function ( data, path ) {
|
|
|
|
|
|
- /**
|
|
|
|
- * 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 geometry
|
|
|
|
- * @param radius
|
|
|
|
- * @param angles
|
|
|
|
- * @param colors
|
|
|
|
- * @param boolean topDown Whether to work top down or bottom up.
|
|
|
|
- */
|
|
|
|
- function paintFaces( geometry, radius, angles, colors, topDown ) {
|
|
|
|
|
|
+ var nodeMap = {};
|
|
|
|
|
|
- var direction = ( topDown === true ) ? 1 : - 1;
|
|
|
|
|
|
+ function generateVRMLTree( data ) {
|
|
|
|
|
|
- var coord = [], A = {}, B = {}, applyColor = false;
|
|
|
|
|
|
+ // create lexer, parser and visitor
|
|
|
|
|
|
- for ( var k = 0; k < angles.length; k ++ ) {
|
|
|
|
|
|
+ var tokenData = createTokens();
|
|
|
|
|
|
- // push the vector at which the color changes
|
|
|
|
|
|
+ var lexer = new VRMLLexer( tokenData.tokens );
|
|
|
|
+ var parser = new VRMLParser( tokenData.tokenVocabulary );
|
|
|
|
+ var visitor = createVisitor( parser.getBaseCstVisitorConstructor() );
|
|
|
|
|
|
- var vec = {
|
|
|
|
- x: direction * ( Math.cos( angles[ k ] ) * radius ),
|
|
|
|
- y: direction * ( Math.sin( angles[ k ] ) * radius )
|
|
|
|
- };
|
|
|
|
|
|
+ // lexing
|
|
|
|
|
|
- coord.push( vec );
|
|
|
|
|
|
+ var lexingResult = lexer.lex( data );
|
|
|
|
+ parser.input = lexingResult.tokens;
|
|
|
|
+
|
|
|
|
+ // parsing
|
|
|
|
+
|
|
|
|
+ var cstOutput = parser.vrml();
|
|
|
|
+
|
|
|
|
+ if ( parser.errors.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ console.error( parser.errors );
|
|
|
|
+
|
|
|
|
+ throw Error( 'THREE.VRMLLoader: Parsing errors detected.' );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- var index = geometry.index;
|
|
|
|
- var positionAttribute = geometry.attributes.position;
|
|
|
|
- var colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
|
|
|
|
|
|
+ // actions
|
|
|
|
|
|
- var position = new THREE.Vector3();
|
|
|
|
- var color = new THREE.Color();
|
|
|
|
|
|
+ var ast = visitor.visit( cstOutput );
|
|
|
|
|
|
- for ( var i = 0; i < index.count; i ++ ) {
|
|
|
|
|
|
+ return ast;
|
|
|
|
|
|
- var vertexIndex = index.getX( i );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- position.fromBufferAttribute( positionAttribute, vertexIndex );
|
|
|
|
|
|
+ function createTokens() {
|
|
|
|
+
|
|
|
|
+ var createToken = chevrotain.createToken;
|
|
|
|
+
|
|
|
|
+ // from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics
|
|
|
|
+
|
|
|
|
+ var 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]*/ } );
|
|
|
|
+ var 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
|
|
|
|
+
|
|
|
|
+ var 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
|
|
|
|
+ ];
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+
|
|
|
|
+ var Version = createToken( {
|
|
|
|
+ name: 'Version',
|
|
|
|
+ pattern: /#VRML.*/,
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var NodeName = createToken( {
|
|
|
|
+ name: 'NodeName',
|
|
|
|
+ pattern: new RegExp( nodeTypes.join( '|' ) ),
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var DEF = createToken( {
|
|
|
|
+ name: 'DEF',
|
|
|
|
+ pattern: /DEF/,
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var USE = createToken( {
|
|
|
|
+ name: 'USE',
|
|
|
|
+ pattern: /USE/,
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var ROUTE = createToken( {
|
|
|
|
+ name: 'ROUTE',
|
|
|
|
+ pattern: /ROUTE/,
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var TO = createToken( {
|
|
|
|
+ name: 'TO',
|
|
|
|
+ pattern: /TO/,
|
|
|
|
+ longer_alt: Identifier
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+
|
|
|
|
+ var StringLiteral = createToken( { name: "StringLiteral", pattern: /"(:?[^\\"\n\r]+|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/ } );
|
|
|
|
+ var NumberLiteral = createToken( { name: 'NumberLiteral', pattern: /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ } );
|
|
|
|
+ var BooleanLiteral = createToken( { name: 'BooleanLiteral', pattern: /TRUE|FALSE/ } );
|
|
|
|
+ var NullLiteral = createToken( { name: 'NullLiteral', pattern: /NULL/ } );
|
|
|
|
+ var LSquare = createToken( { name: 'LSquare', pattern: /\[/ } );
|
|
|
|
+ var RSquare = createToken( { name: 'RSquare', pattern: /]/ } );
|
|
|
|
+ var LCurly = createToken( { name: 'LCurly', pattern: /{/ } );
|
|
|
|
+ var RCurly = createToken( { name: 'RCurly', pattern: /}/ } );
|
|
|
|
+ var Comment = createToken( {
|
|
|
|
+ name: 'Comment',
|
|
|
|
+ pattern: /#.*/,
|
|
|
|
+ group: chevrotain.Lexer.SKIPPED
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ // commas, blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields
|
|
|
|
+
|
|
|
|
+ var WhiteSpace = createToken( {
|
|
|
|
+ name: 'WhiteSpace',
|
|
|
|
+ pattern: /[ ,\s]/,
|
|
|
|
+ group: chevrotain.Lexer.SKIPPED
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ var tokens = [
|
|
|
|
+ WhiteSpace,
|
|
|
|
+ // keywords appear before the Identifier
|
|
|
|
+ NodeName,
|
|
|
|
+ DEF,
|
|
|
|
+ USE,
|
|
|
|
+ ROUTE,
|
|
|
|
+ TO,
|
|
|
|
+ BooleanLiteral,
|
|
|
|
+ NullLiteral,
|
|
|
|
+ // the Identifier must appear after the keywords because all keywords are valid identifiers
|
|
|
|
+ Version,
|
|
|
|
+ Identifier,
|
|
|
|
+ RouteIdentifier,
|
|
|
|
+ StringLiteral,
|
|
|
|
+ NumberLiteral,
|
|
|
|
+ LSquare,
|
|
|
|
+ RSquare,
|
|
|
|
+ LCurly,
|
|
|
|
+ RCurly,
|
|
|
|
+ Comment
|
|
|
|
+ ];
|
|
|
|
+
|
|
|
|
+ var tokenVocabulary = {};
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = tokens.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var token = tokens[ i ];
|
|
|
|
+
|
|
|
|
+ tokenVocabulary[ token.name ] = token;
|
|
|
|
|
|
- for ( var j = 0; j < colors.length; j ++ ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // linear interpolation between aColor and bColor, calculate proportion
|
|
|
|
- // A is previous point (angle)
|
|
|
|
|
|
+ return { tokens: tokens, tokenVocabulary: tokenVocabulary };
|
|
|
|
|
|
- if ( j === 0 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- A.x = 0;
|
|
|
|
- A.y = ( topDown === true ) ? radius : - 1 * radius;
|
|
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ function createVisitor( BaseVRMLVisitor ) {
|
|
|
|
|
|
- A.x = coord[ j - 1 ].x;
|
|
|
|
- A.y = coord[ j - 1 ].y;
|
|
|
|
|
|
+ // 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 ) {
|
|
|
|
+
|
|
|
|
+ var data = {
|
|
|
|
+ version: this.visit( ctx.version ),
|
|
|
|
+ nodes: [],
|
|
|
|
+ routes: []
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var node = ctx.node[ i ];
|
|
|
|
+
|
|
|
|
+ data.nodes.push( this.visit( node ) );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // B is current point (angle)
|
|
|
|
|
|
+ if ( ctx.route ) {
|
|
|
|
|
|
- B = coord[ j ];
|
|
|
|
|
|
+ for ( var i = 0, l = ctx.route.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( B !== undefined ) {
|
|
|
|
|
|
+ var route = ctx.route[ i ];
|
|
|
|
|
|
- // p has to be between the points A and B which we interpolate
|
|
|
|
|
|
+ data.routes.push( this.visit( route ) );
|
|
|
|
|
|
- applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( applyColor === true ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var aColor = colors[ j ];
|
|
|
|
- var bColor = colors[ j + 1 ];
|
|
|
|
|
|
+ return data;
|
|
|
|
|
|
- // below is simple linear interpolation
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
|
|
|
|
|
|
+ version: function ( ctx ) {
|
|
|
|
|
|
- // 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
|
|
|
|
|
|
+ return ctx.Version[ 0 ].image;
|
|
|
|
|
|
- color.copy( aColor ).lerp( bColor, t );
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
|
|
|
|
|
|
+ node: function ( ctx ) {
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ var data = {
|
|
|
|
+ name: ctx.NodeName[ 0 ].image,
|
|
|
|
+ fields: []
|
|
|
|
+ };
|
|
|
|
|
|
- var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
|
|
|
|
- var c = colors[ colorIndex ];
|
|
|
|
- colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
|
|
|
|
|
|
+ if ( ctx.field ) {
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = ctx.field.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = ctx.field[ i ];
|
|
|
|
+
|
|
|
|
+ data.fields.push( this.visit( field ) );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // DEF
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( ctx.def ) {
|
|
|
|
|
|
- geometry.addAttribute( 'color', colorAttribute );
|
|
|
|
|
|
+ data.DEF = this.visit( ctx.def[ 0 ] );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return data;
|
|
|
|
|
|
- var index = [];
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- function parseProperty( node, line ) {
|
|
|
|
|
|
+ field: function ( ctx ) {
|
|
|
|
|
|
- var parts = [], part, property = {}, fieldName;
|
|
|
|
|
|
+ var data = {
|
|
|
|
+ name: ctx.Identifier[ 0 ].image,
|
|
|
|
+ type: null,
|
|
|
|
+ values: null
|
|
|
|
+ };
|
|
|
|
|
|
- /**
|
|
|
|
- * Expression for matching relevant information, such as a name or value, but not the separators
|
|
|
|
- * @type {RegExp}
|
|
|
|
- */
|
|
|
|
- var regex = /[^\s,\[\]]+/g;
|
|
|
|
|
|
+ var result;
|
|
|
|
|
|
- var point;
|
|
|
|
|
|
+ // SFValue
|
|
|
|
|
|
- while ( null !== ( part = regex.exec( line ) ) ) {
|
|
|
|
|
|
+ if ( ctx.singleFieldValue ) {
|
|
|
|
|
|
- parts.push( part[ 0 ] );
|
|
|
|
|
|
+ result = this.visit( ctx.singleFieldValue[ 0 ] );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- fieldName = parts[ 0 ];
|
|
|
|
|
|
+ // MFValue
|
|
|
|
|
|
|
|
+ if ( ctx.multiFieldValue ) {
|
|
|
|
|
|
- // trigger several recorders
|
|
|
|
- switch ( fieldName ) {
|
|
|
|
|
|
+ result = this.visit( ctx.multiFieldValue[ 0 ] );
|
|
|
|
|
|
- case 'skyAngle':
|
|
|
|
- case 'groundAngle':
|
|
|
|
- scope.recordingFieldname = fieldName;
|
|
|
|
- scope.isRecordingAngles = true;
|
|
|
|
- scope.angles = [];
|
|
|
|
- break;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- case 'color':
|
|
|
|
- case 'skyColor':
|
|
|
|
- case 'groundColor':
|
|
|
|
- scope.recordingFieldname = fieldName;
|
|
|
|
- scope.isRecordingColors = true;
|
|
|
|
- scope.colors = [];
|
|
|
|
- break;
|
|
|
|
|
|
+ data.type = result.type;
|
|
|
|
+ data.values = result.values;
|
|
|
|
|
|
- case 'point':
|
|
|
|
- case 'vector':
|
|
|
|
- scope.recordingFieldname = fieldName;
|
|
|
|
- scope.isRecordingPoints = true;
|
|
|
|
- scope.points = [];
|
|
|
|
- break;
|
|
|
|
|
|
+ return data;
|
|
|
|
|
|
- case 'colorIndex':
|
|
|
|
- case 'coordIndex':
|
|
|
|
- case 'normalIndex':
|
|
|
|
- case 'texCoordIndex':
|
|
|
|
- scope.recordingFieldname = fieldName;
|
|
|
|
- scope.isRecordingFaces = true;
|
|
|
|
- scope.indexes = [];
|
|
|
|
- break;
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ def: function ( ctx ) {
|
|
|
|
|
|
- if ( scope.isRecordingFaces ) {
|
|
|
|
|
|
+ return ctx.Identifier[ 0 ].image;
|
|
|
|
|
|
- // the parts hold the indexes as strings
|
|
|
|
- if ( parts.length > 0 ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- for ( var ind = 0; ind < parts.length; ind ++ ) {
|
|
|
|
|
|
+ use: function ( ctx ) {
|
|
|
|
|
|
- // the part should either be positive integer or -1
|
|
|
|
- if ( ! /(-?\d+)/.test( parts[ ind ] ) ) {
|
|
|
|
|
|
+ return { USE: ctx.Identifier[ 0 ].image };
|
|
|
|
|
|
- continue;
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ singleFieldValue: function ( ctx ) {
|
|
|
|
|
|
- // end of current face
|
|
|
|
- if ( parts[ ind ] === '-1' ) {
|
|
|
|
|
|
+ return processField( this, ctx );
|
|
|
|
|
|
- if ( index.length > 0 ) {
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- scope.indexes.push( index );
|
|
|
|
|
|
+ multiFieldValue: function ( ctx ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ return processField( this, ctx );
|
|
|
|
|
|
- // start new one
|
|
|
|
- index = [];
|
|
|
|
|
|
+ },
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ route: function ( ctx ) {
|
|
|
|
|
|
- index.push( parseInt( parts[ ind ] ) );
|
|
|
|
|
|
+ var data = {
|
|
|
|
+ FROM: ctx.RouteIdentifier[ 0 ].image,
|
|
|
|
+ TO: ctx.RouteIdentifier[ 1 ].image
|
|
|
|
+ };
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ return data;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ function processField( scope, ctx ) {
|
|
|
|
+
|
|
|
|
+ var field = {
|
|
|
|
+ type: null,
|
|
|
|
+ values: []
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if ( ctx.node ) {
|
|
|
|
+
|
|
|
|
+ field.type = 'node';
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var node = ctx.node[ i ];
|
|
|
|
+
|
|
|
|
+ field.values.push( scope.visit( node ) );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // end
|
|
|
|
- if ( /]/.exec( line ) ) {
|
|
|
|
|
|
+ if ( ctx.use ) {
|
|
|
|
+
|
|
|
|
+ field.type = 'use';
|
|
|
|
|
|
- if ( index.length > 0 ) {
|
|
|
|
|
|
+ for ( var i = 0, l = ctx.use.length; i < l; i ++ ) {
|
|
|
|
|
|
- scope.indexes.push( index );
|
|
|
|
|
|
+ var use = ctx.use[ i ];
|
|
|
|
+
|
|
|
|
+ field.values.push( scope.visit( use ) );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // start new one
|
|
|
|
- index = [];
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ( ctx.StringLiteral ) {
|
|
|
|
+
|
|
|
|
+ field.type = 'string';
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = ctx.StringLiteral.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var stringLiteral = ctx.StringLiteral[ i ];
|
|
|
|
|
|
- scope.isRecordingFaces = false;
|
|
|
|
- node[ scope.recordingFieldname ] = scope.indexes;
|
|
|
|
|
|
+ field.values.push( stringLiteral.image.replace( /'|"/g, '' ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- } else if ( scope.isRecordingPoints ) {
|
|
|
|
|
|
+ if ( ctx.NumberLiteral ) {
|
|
|
|
|
|
- if ( node.nodeType == 'Coordinate' ) {
|
|
|
|
|
|
+ field.type = 'number';
|
|
|
|
|
|
- while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
|
|
|
|
|
|
+ for ( var i = 0, l = ctx.NumberLiteral.length; i < l; i ++ ) {
|
|
|
|
|
|
- point = {
|
|
|
|
- x: parseFloat( parts[ 1 ] ),
|
|
|
|
- y: parseFloat( parts[ 2 ] ),
|
|
|
|
- z: parseFloat( parts[ 3 ] )
|
|
|
|
- };
|
|
|
|
|
|
+ var numberLiteral = ctx.NumberLiteral[ i ];
|
|
|
|
|
|
- scope.points.push( point );
|
|
|
|
|
|
+ field.values.push( parseFloat( numberLiteral.image ) );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- if ( node.nodeType == 'Normal' ) {
|
|
|
|
|
|
+ if ( ctx.BooleanLiteral ) {
|
|
|
|
|
|
- while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
|
|
|
|
|
|
+ field.type = 'boolean';
|
|
|
|
|
|
- point = {
|
|
|
|
- x: parseFloat( parts[ 1 ] ),
|
|
|
|
- y: parseFloat( parts[ 2 ] ),
|
|
|
|
- z: parseFloat( parts[ 3 ] )
|
|
|
|
- };
|
|
|
|
|
|
+ for ( var i = 0, l = ctx.BooleanLiteral.length; i < l; i ++ ) {
|
|
|
|
|
|
- scope.points.push( point );
|
|
|
|
|
|
+ var booleanLiteral = ctx.BooleanLiteral[ i ];
|
|
|
|
+
|
|
|
|
+ field.values.push( booleanLiteral.image === 'TRUE' );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- if ( node.nodeType == 'TextureCoordinate' ) {
|
|
|
|
|
|
+ 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 ) );
|
|
|
|
+
|
|
|
|
+ var nodes = tree.nodes;
|
|
|
|
+ var scene = new THREE.Scene();
|
|
|
|
+
|
|
|
|
+ // first iteration: build nodemap based on DEF statements
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = nodes.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var node = nodes[ i ];
|
|
|
|
+
|
|
|
|
+ buildNodeMap( node, scene );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // second iteration: build nodes
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = nodes.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var node = nodes[ i ];
|
|
|
|
+ var object = getNode( node );
|
|
|
|
+
|
|
|
|
+ if ( object instanceof THREE.Object3D ) scene.add( object );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return scene;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function buildNodeMap( node ) {
|
|
|
|
+
|
|
|
|
+ if ( node.DEF ) {
|
|
|
|
+
|
|
|
|
+ nodeMap[ node.DEF ] = node;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- while ( null !== ( parts = float2_pattern.exec( line ) ) ) {
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
|
|
- point = {
|
|
|
|
- x: parseFloat( parts[ 1 ] ),
|
|
|
|
- y: parseFloat( parts[ 2 ] )
|
|
|
|
- };
|
|
|
|
|
|
+ if ( field.type === 'node' ) {
|
|
|
|
|
|
- scope.points.push( point );
|
|
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ for ( var j = 0, jl = fieldValues.length; j < jl; j ++ ) {
|
|
|
|
+
|
|
|
|
+ buildNodeMap( fieldValues[ j ] );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // end
|
|
|
|
- if ( /]/.exec( line ) ) {
|
|
|
|
|
|
|
|
- scope.isRecordingPoints = false;
|
|
|
|
- node.points = scope.points;
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ 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 ) {
|
|
|
|
+
|
|
|
|
+ var nodeName = node.name;
|
|
|
|
+ var build;
|
|
|
|
+
|
|
|
|
+ switch ( nodeName ) {
|
|
|
|
+
|
|
|
|
+ case 'Group':
|
|
|
|
+ case 'Transform':
|
|
|
|
+ build = buildGroupingNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'Background':
|
|
|
|
+ build = buildBackgroundNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'Shape':
|
|
|
|
+ build = buildShapeNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'Appearance':
|
|
|
|
+ build = buildApperanceNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'Material':
|
|
|
|
+ build = buildMaterialNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'ImageTexture':
|
|
|
|
+ build = buildImageTextureNode( 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 'Color':
|
|
|
|
+ case 'Coordinate':
|
|
|
|
+ case 'Normal':
|
|
|
|
+ case 'TextureCoordinate':
|
|
|
|
+ build = buildGeometricNode( node );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'Anchor':
|
|
|
|
+ case 'Billboard':
|
|
|
|
+ case 'Collision':
|
|
|
|
+
|
|
|
|
+ case 'Inline':
|
|
|
|
+ case 'LOD':
|
|
|
|
+ case 'Switch':
|
|
|
|
+
|
|
|
|
+ case 'AudioClip':
|
|
|
|
+ case 'DirectionalLight':
|
|
|
|
+ case 'PointLight':
|
|
|
|
+ case 'Script':
|
|
|
|
+ case 'Sound':
|
|
|
|
+ case 'SpotLight':
|
|
|
|
+ case 'WorldInfo':
|
|
|
|
+
|
|
|
|
+ case 'CylinderSensor':
|
|
|
|
+ case 'PlaneSensor':
|
|
|
|
+ case 'ProximitySensor':
|
|
|
|
+ case 'SphereSensor':
|
|
|
|
+ case 'TimeSensor':
|
|
|
|
+ case 'TouchSensor':
|
|
|
|
+ case 'VisibilitySensor':
|
|
|
|
+
|
|
|
|
+ case 'ElevationGrid':
|
|
|
|
+ case 'Extrusion':
|
|
|
|
+ case 'Text':
|
|
|
|
+
|
|
|
|
+ case 'FontStyle':
|
|
|
|
+ case 'MovieTexture':
|
|
|
|
+ case 'PixelTexture':
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return build;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function buildGroupingNode( node ) {
|
|
|
|
+
|
|
|
|
+ var object = new THREE.Group();
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
+
|
|
|
|
+ case 'center':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'children':
|
|
|
|
+ parseFieldChildren( fieldValues, object );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'rotation':
|
|
|
|
+ var axis = new THREE.Vector3( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
|
|
|
|
+ var 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 'bboxCenter':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'bboxSize':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return object;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function buildBackgroundNode( node ) {
|
|
|
|
+
|
|
|
|
+ var group = new THREE.Group();
|
|
|
|
+
|
|
|
|
+ var groundAngle, groundColor;
|
|
|
|
+ var skyAngle, skyColor;
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var 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;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // sky
|
|
|
|
+
|
|
|
|
+ if ( skyColor ) {
|
|
|
|
+
|
|
|
|
+ var radius = 10000;
|
|
|
|
+
|
|
|
|
+ var skyGeometry = new THREE.SphereBufferGeometry( radius, 32, 16 );
|
|
|
|
+ var 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 = THREE.VertexColors;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ skyMaterial.color.setRGB( skyColor[ 0 ], skyColor[ 1 ], skyColor[ 2 ] );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var sky = new THREE.Mesh( skyGeometry, skyMaterial );
|
|
|
|
+ group.add( sky );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // ground
|
|
|
|
+
|
|
|
|
+ if ( groundColor ) {
|
|
|
|
+
|
|
|
|
+ if ( groundColor.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ var groundGeometry = new THREE.SphereBufferGeometry( radius, 32, 16, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
|
|
|
|
+ var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors, depthWrite: false, depthTest: false } );
|
|
|
|
+
|
|
|
|
+ paintFaces( groundGeometry, radius, groundAngle, toColorArray( groundColor ), false );
|
|
|
|
+
|
|
|
|
+ var ground = new THREE.Mesh( groundGeometry, groundMaterial );
|
|
|
|
+ group.add( ground );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // render background group first
|
|
|
|
+
|
|
|
|
+ group.renderOrder = - Infinity;
|
|
|
|
+
|
|
|
|
+ return group;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function buildShapeNode( node ) {
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ // if the appearance field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)
|
|
|
|
+
|
|
|
|
+ var material = new THREE.MeshBasicMaterial( { color: 0x000000 } );
|
|
|
|
+ var geometry;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var 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
|
|
|
|
+
|
|
|
|
+ var object;
|
|
|
|
+
|
|
|
|
+ if ( geometry ) {
|
|
|
|
+
|
|
|
|
+ var type = geometry._type;
|
|
|
|
+
|
|
|
|
+ if ( type === 'points' ) { // points
|
|
|
|
+
|
|
|
|
+ var pointsMaterial = new THREE.PointsMaterial( { color: 0xffffff } );
|
|
|
|
+
|
|
|
|
+ if ( geometry.attributes.color !== undefined ) {
|
|
|
|
+
|
|
|
|
+ pointsMaterial.vertexColors = THREE.VertexColors;
|
|
|
|
+
|
|
|
|
+ } 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
|
|
|
|
+
|
|
|
|
+ var lineMaterial = new THREE.LineBasicMaterial( { color: 0xffffff } );
|
|
|
|
+
|
|
|
|
+ if ( geometry.attributes.color !== undefined ) {
|
|
|
|
+
|
|
|
|
+ lineMaterial.vertexColors = THREE.VertexColors;
|
|
|
|
+
|
|
|
|
+ } 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 = THREE.VertexColors;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ object = new THREE.Mesh( geometry, material );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ object = new THREE.Object3D();
|
|
|
|
+
|
|
|
|
+ // if the geometry field is NULL the object is not drawn
|
|
|
|
+
|
|
|
|
+ object.visible = false;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return object;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function buildApperanceNode( node ) {
|
|
|
|
+
|
|
|
|
+ var material = new THREE.MeshPhongMaterial();
|
|
|
|
+ var transformData;
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
+
|
|
|
|
+ case 'material':
|
|
|
|
+ if ( fieldValues[ 0 ] !== null ) {
|
|
|
|
+
|
|
|
|
+ var 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':
|
|
|
|
+ var textureNode = fieldValues[ 0 ];
|
|
|
|
+ if ( textureNode !== null ) {
|
|
|
|
+
|
|
|
|
+ if ( textureNode.name === 'ImageTexture' ) {
|
|
|
|
+
|
|
|
|
+ material.map = getNode( textureNode );
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ // MovieTexture and PixelTexture 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 && 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 ) {
|
|
|
|
+
|
|
|
|
+ var materialData = {};
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var 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 buildImageTextureNode( node ) {
|
|
|
|
+
|
|
|
|
+ var texture;
|
|
|
|
+ var wrapS = THREE.RepeatWrapping;
|
|
|
|
+ var wrapT = THREE.RepeatWrapping;
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
+
|
|
|
|
+ case 'url':
|
|
|
|
+ var 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 ) {
|
|
|
|
+
|
|
|
|
+ var transformData = {
|
|
|
|
+ center: new THREE.Vector2(),
|
|
|
|
+ rotation: new THREE.Vector2(),
|
|
|
|
+ scale: new THREE.Vector2(),
|
|
|
|
+ translation: new THREE.Vector2()
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var 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 buildIndexedFaceSetNode( node ) {
|
|
|
|
+
|
|
|
|
+ var color, coord, normal, texCoord;
|
|
|
|
+ var ccw = true, solid = true, creaseAngle;
|
|
|
|
+ var colorIndex, coordIndex, normalIndex, texCoordIndex;
|
|
|
|
+ var colorPerVertex = true, normalPerVertex = true;
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
+
|
|
|
|
+ case 'color':
|
|
|
|
+ var colorNode = fieldValues[ 0 ];
|
|
|
|
+
|
|
|
|
+ if ( colorNode !== null ) {
|
|
|
|
+
|
|
|
|
+ color = getNode( colorNode );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'coord':
|
|
|
|
+ var coordNode = fieldValues[ 0 ];
|
|
|
|
+
|
|
|
|
+ if ( coordNode !== null ) {
|
|
|
|
+
|
|
|
|
+ coord = getNode( coordNode );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'normal':
|
|
|
|
+ var normalNode = fieldValues[ 0 ];
|
|
|
|
+
|
|
|
|
+ if ( normalNode !== null ) {
|
|
|
|
+
|
|
|
|
+ normal = getNode( normalNode );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'texCoord':
|
|
|
|
+ var 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;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var triangulatedCoordIndex = triangulateFaceIndex( coordIndex, ccw );
|
|
|
|
+
|
|
|
|
+ var positionAttribute;
|
|
|
|
+ var colorAttribute;
|
|
|
|
+ var normalAttribute;
|
|
|
|
+ var uvAttribute;
|
|
|
|
+
|
|
|
|
+ if ( color ) {
|
|
|
|
+
|
|
|
|
+ if ( colorPerVertex === true ) {
|
|
|
|
+
|
|
|
|
+ if ( colorIndex.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ // if the colorIndex field is not empty, then it is used to choose colors for each vertex of the IndexedFaceSet.
|
|
|
|
+
|
|
|
|
+ var 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 Color node
|
|
|
|
+
|
|
|
|
+ colorAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( color, 3 ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ if ( colorIndex.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ // if the colorIndex field is not empty, then they are used to choose one color for each face of the IndexedFaceSet
|
|
|
|
+
|
|
|
|
+ var flattenFaceColors = flattenData( color, colorIndex );
|
|
|
|
+ var 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
|
|
|
|
+
|
|
|
|
+ var triangulatedFaceColors = triangulateFaceData( color, coordIndex );
|
|
|
|
+ colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ( normal ) {
|
|
|
|
+
|
|
|
|
+ if ( normalPerVertex === true ) {
|
|
|
|
+
|
|
|
|
+ // consider vertex normals
|
|
|
|
+
|
|
|
|
+ if ( normalIndex.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ // if the normalIndex field is not empty, then it is used to choose normals for each vertex of the IndexedFaceSet.
|
|
|
|
+
|
|
|
|
+ var 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.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ // if the normalIndex field is not empty, then they are used to choose one normal for each face of the IndexedFaceSet
|
|
|
|
+
|
|
|
|
+ var flattenFaceNormals = flattenData( normal, normalIndex );
|
|
|
|
+ var 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
|
|
|
|
+
|
|
|
|
+ var 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.length > 0 ) {
|
|
|
|
+
|
|
|
|
+ // if the texCoordIndex field is not empty, then it is used to choose texture coordinates for each vertex of the IndexedFaceSet.
|
|
|
|
+
|
|
|
|
+ var 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 ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
|
+ positionAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( coord, 3 ) );
|
|
|
|
+
|
|
|
|
+ geometry.addAttribute( 'position', positionAttribute );
|
|
|
|
+ geometry.addAttribute( 'normal', normalAttribute );
|
|
|
|
+
|
|
|
|
+ // optional attributes
|
|
|
|
+
|
|
|
|
+ if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
|
|
|
|
+ if ( uvAttribute ) geometry.addAttribute( '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 ) {
|
|
|
|
+
|
|
|
|
+ var color, coord;
|
|
|
|
+ var colorIndex, coordIndex;
|
|
|
|
+ var colorPerVertex = true;
|
|
|
|
+
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
+
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
+
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
+
|
|
|
|
+ case 'color':
|
|
|
|
+ var colorNode = fieldValues[ 0 ];
|
|
|
|
+
|
|
|
|
+ if ( colorNode !== null ) {
|
|
|
|
+
|
|
|
|
+ color = getNode( colorNode );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case 'coord':
|
|
|
|
+ var 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
|
|
|
|
+
|
|
|
|
+ var colorAttribute;
|
|
|
|
+
|
|
|
|
+ var 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.
|
|
|
|
+
|
|
|
|
+ var 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 {
|
|
|
|
|
|
- } else if ( scope.isRecordingAngles ) {
|
|
|
|
|
|
+ if ( colorIndex.length > 0 ) {
|
|
|
|
|
|
- // the parts hold the angles as strings
|
|
|
|
- if ( parts.length > 0 ) {
|
|
|
|
|
|
+ // if the colorIndex field is not empty, then colors are applied to each vertex of the IndexedLineSet
|
|
|
|
|
|
- for ( var ind = 0; ind < parts.length; ind ++ ) {
|
|
|
|
|
|
+ var flattenLineColors = flattenData( color, colorIndex ); // compute colors for each VRML primitve
|
|
|
|
+ var expandedLineColors = expandLineData( flattenLineColors, coordIndex ); // compute colors for each line segment (rendering primitve)
|
|
|
|
+ colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
|
|
|
|
|
|
- // the part should be a float
|
|
|
|
- if ( ! float_pattern.test( parts[ ind ] ) ) {
|
|
|
|
|
|
|
|
- continue;
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node
|
|
|
|
|
|
- scope.angles.push( parseFloat( parts[ ind ] ) );
|
|
|
|
|
|
+ var expandedLineColors = expandLineData( color, coordIndex ); // compute colors for each line segment (rendering primitve)
|
|
|
|
+ colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // end
|
|
|
|
- if ( /]/.exec( line ) ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- scope.isRecordingAngles = false;
|
|
|
|
- node[ scope.recordingFieldname ] = scope.angles;
|
|
|
|
|
|
+ //
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
|
|
|
- } else if ( scope.isRecordingColors ) {
|
|
|
|
|
|
+ var positionAttribute = toNonIndexedAttribute( expandedLineIndex, new THREE.Float32BufferAttribute( coord, 3 ) );
|
|
|
|
+ geometry.addAttribute( 'position', positionAttribute );
|
|
|
|
|
|
- while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
|
|
|
|
|
|
+ if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
|
|
|
|
|
|
- var color = {
|
|
|
|
- r: parseFloat( parts[ 1 ] ),
|
|
|
|
- g: parseFloat( parts[ 2 ] ),
|
|
|
|
- b: parseFloat( parts[ 3 ] )
|
|
|
|
- };
|
|
|
|
|
|
+ geometry._type = 'line';
|
|
|
|
|
|
- scope.colors.push( color );
|
|
|
|
|
|
+ return geometry;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // end
|
|
|
|
- if ( /]/.exec( line ) ) {
|
|
|
|
|
|
+ function buildPointSetNode( node ) {
|
|
|
|
|
|
- scope.isRecordingColors = false;
|
|
|
|
- node[ scope.recordingFieldname ] = scope.colors;
|
|
|
|
|
|
+ var geometry;
|
|
|
|
+ var color, coord;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var fields = node.fields;
|
|
|
|
+
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- } else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) {
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
|
|
switch ( fieldName ) {
|
|
switch ( fieldName ) {
|
|
|
|
|
|
- case 'diffuseColor':
|
|
|
|
- case 'emissiveColor':
|
|
|
|
- case 'specularColor':
|
|
|
|
case 'color':
|
|
case 'color':
|
|
|
|
+ var colorNode = fieldValues[ 0 ];
|
|
|
|
|
|
- if ( parts.length !== 4 ) {
|
|
|
|
|
|
+ if ( colorNode !== null ) {
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Invalid color format detected for %s.', fieldName );
|
|
|
|
- break;
|
|
|
|
|
|
+ color = getNode( colorNode );
|
|
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
- property = {
|
|
|
|
- r: parseFloat( parts[ 1 ] ),
|
|
|
|
- g: parseFloat( parts[ 2 ] ),
|
|
|
|
- b: parseFloat( parts[ 3 ] )
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
break;
|
|
break;
|
|
|
|
|
|
- case 'location':
|
|
|
|
- case 'direction':
|
|
|
|
- case 'translation':
|
|
|
|
- case 'scale':
|
|
|
|
- case 'size':
|
|
|
|
- if ( parts.length !== 4 ) {
|
|
|
|
|
|
+ case 'coord':
|
|
|
|
+ var coordNode = fieldValues[ 0 ];
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Invalid vector format detected for %s.', fieldName );
|
|
|
|
- break;
|
|
|
|
|
|
+ if ( coordNode !== null ) {
|
|
|
|
+
|
|
|
|
+ coord = getNode( coordNode );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
+ break;
|
|
|
|
|
|
- property = {
|
|
|
|
- x: parseFloat( parts[ 1 ] ),
|
|
|
|
- y: parseFloat( parts[ 2 ] ),
|
|
|
|
- z: parseFloat( parts[ 3 ] )
|
|
|
|
- };
|
|
|
|
|
|
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
break;
|
|
break;
|
|
|
|
|
|
- case 'intensity':
|
|
|
|
- case 'cutOffAngle':
|
|
|
|
- case 'radius':
|
|
|
|
- case 'topRadius':
|
|
|
|
- case 'bottomRadius':
|
|
|
|
- case 'height':
|
|
|
|
- case 'transparency':
|
|
|
|
- case 'shininess':
|
|
|
|
- case 'ambientIntensity':
|
|
|
|
- case 'creaseAngle':
|
|
|
|
- if ( parts.length !== 2 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Invalid single float value specification detected for %s.', fieldName );
|
|
|
|
- break;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var geometry = new THREE.BufferGeometry();
|
|
|
|
|
|
- property = parseFloat( parts[ 1 ] );
|
|
|
|
|
|
+ geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( coord, 3 ) );
|
|
|
|
+ if ( color ) geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( color, 3 ) );
|
|
|
|
|
|
- break;
|
|
|
|
|
|
+ geometry._type = 'points';
|
|
|
|
|
|
- case 'rotation':
|
|
|
|
- if ( parts.length !== 5 ) {
|
|
|
|
|
|
+ return geometry;
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Invalid quaternion format detected for %s.', fieldName );
|
|
|
|
- break;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function buildBoxNode( node ) {
|
|
|
|
|
|
- property = {
|
|
|
|
- x: parseFloat( parts[ 1 ] ),
|
|
|
|
- y: parseFloat( parts[ 2 ] ),
|
|
|
|
- z: parseFloat( parts[ 3 ] ),
|
|
|
|
- w: parseFloat( parts[ 4 ] )
|
|
|
|
- };
|
|
|
|
|
|
+ var size = new THREE.Vector3( 2, 2, 2 );
|
|
|
|
|
|
- break;
|
|
|
|
|
|
+ var fields = node.fields;
|
|
|
|
|
|
- case 'on':
|
|
|
|
- case 'ccw':
|
|
|
|
- case 'solid':
|
|
|
|
- case 'colorPerVertex':
|
|
|
|
- case 'convex':
|
|
|
|
- if ( parts.length !== 2 ) {
|
|
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Invalid format detected for %s.', fieldName );
|
|
|
|
- break;
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
|
|
- property = parts[ 1 ] === 'TRUE' ? true : false;
|
|
|
|
|
|
+ case 'size':
|
|
|
|
+ size.x = fieldValues[ 0 ];
|
|
|
|
+ size.y = fieldValues[ 1 ];
|
|
|
|
+ size.z = fieldValues[ 2 ];
|
|
|
|
+ break;
|
|
|
|
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
break;
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- // VRMLLoader does not support text so it can't process the "string" property yet
|
|
|
|
-
|
|
|
|
- if ( fieldName !== 'string' ) node[ fieldName ] = property;
|
|
|
|
-
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- return property;
|
|
|
|
|
|
+ var geometry = new THREE.BoxBufferGeometry( size.x, size.y, size.z );
|
|
|
|
+
|
|
|
|
+ return geometry;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- function getTree( lines ) {
|
|
|
|
|
|
+ function buildConeNode( node ) {
|
|
|
|
|
|
- var tree = { 'string': 'Scene', children: [] };
|
|
|
|
- var current = tree;
|
|
|
|
- var matches;
|
|
|
|
- var specification;
|
|
|
|
|
|
+ var radius = 1, height = 2, openEnded = false;
|
|
|
|
|
|
- for ( var i = 0; i < lines.length; i ++ ) {
|
|
|
|
|
|
+ var fields = node.fields;
|
|
|
|
|
|
- var comment = '';
|
|
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- var line = lines[ i ];
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
|
|
- // omit whitespace only lines
|
|
|
|
- if ( null !== ( /^\s+?$/g.exec( line ) ) ) {
|
|
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
|
|
- continue;
|
|
|
|
|
|
+ case 'bottom':
|
|
|
|
+ openEnded = ! fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ case 'bottomRadius':
|
|
|
|
+ radius = fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- line = line.trim();
|
|
|
|
|
|
+ case 'height':
|
|
|
|
+ height = fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- // skip empty lines
|
|
|
|
- if ( line === '' ) {
|
|
|
|
|
|
+ case 'side':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
|
|
- continue;
|
|
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
|
|
+ break;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- if ( /#/.exec( line ) ) {
|
|
|
|
-
|
|
|
|
- var parts = line.split( '#' );
|
|
|
|
-
|
|
|
|
- // discard everything after the #, it is a comment
|
|
|
|
- line = parts[ 0 ];
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // well, let's also keep the comment
|
|
|
|
- comment = parts[ 1 ];
|
|
|
|
|
|
+ var geometry = new THREE.ConeBufferGeometry( radius, height, 16, 1, openEnded );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ return geometry;
|
|
|
|
|
|
- if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // first subpattern should match the Node name
|
|
|
|
|
|
+ function buildCylinderNode( node ) {
|
|
|
|
|
|
- var block = { 'nodeType': matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment': comment };
|
|
|
|
- current.children.push( block );
|
|
|
|
- current = block;
|
|
|
|
|
|
+ var radius = 1, height = 2;
|
|
|
|
|
|
- if ( /}/.exec( line ) ) {
|
|
|
|
|
|
+ var fields = node.fields;
|
|
|
|
|
|
- // example: geometry Box { size 1 1 1 } # all on the same line
|
|
|
|
- specification = /{(.*)}/.exec( line )[ 1 ];
|
|
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- // todo: remove once new parsing is complete?
|
|
|
|
- block.children.push( specification );
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
|
|
- parseProperty( current, specification );
|
|
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
|
|
- current = current.parent;
|
|
|
|
|
|
+ case 'bottom':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ case 'radius':
|
|
|
|
+ radius = fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- } else if ( /}/.exec( line ) ) {
|
|
|
|
|
|
+ case 'height':
|
|
|
|
+ height = fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- current = current.parent;
|
|
|
|
|
|
+ case 'side':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
|
|
- } else if ( line !== '' ) {
|
|
|
|
|
|
+ case 'top':
|
|
|
|
+ // field not supported
|
|
|
|
+ break;
|
|
|
|
|
|
- parseProperty( current, line );
|
|
|
|
- // todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way
|
|
|
|
- current.children.push( line );
|
|
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
|
|
+ break;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- return tree;
|
|
|
|
|
|
+ var geometry = new THREE.CylinderBufferGeometry( radius, radius, height, 16, 1 );
|
|
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function parseNode( data, parent ) {
|
|
|
|
|
|
+ return geometry;
|
|
|
|
|
|
- var object;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( typeof data === 'string' ) {
|
|
|
|
|
|
+ function buildSphereNode( node ) {
|
|
|
|
|
|
- if ( /USE/.exec( data ) ) {
|
|
|
|
|
|
+ var radius = 1;
|
|
|
|
|
|
- var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ];
|
|
|
|
|
|
+ var fields = node.fields;
|
|
|
|
|
|
- if ( undefined == defines[ defineKey ] ) {
|
|
|
|
|
|
+ for ( var i = 0, l = fields.length; i < l; i ++ ) {
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: %s is not defined.', defineKey );
|
|
|
|
|
|
+ var field = fields[ i ];
|
|
|
|
+ var fieldName = field.name;
|
|
|
|
+ var fieldValues = field.values;
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ switch ( fieldName ) {
|
|
|
|
|
|
- if ( /appearance/.exec( data ) && defineKey ) {
|
|
|
|
|
|
+ case 'radius':
|
|
|
|
+ radius = fieldValues[ 0 ];
|
|
|
|
+ break;
|
|
|
|
|
|
- parent.material = defines[ defineKey ].clone();
|
|
|
|
|
|
+ default:
|
|
|
|
+ console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
|
|
|
|
+ break;
|
|
|
|
|
|
- } else if ( /geometry/.exec( data ) && defineKey ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- parent.geometry = defines[ defineKey ].clone();
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it
|
|
|
|
- if ( defines[ defineKey ].solid !== undefined && defines[ defineKey ].solid === false ) {
|
|
|
|
|
|
+ var geometry = new THREE.SphereBufferGeometry( radius, 16, 16 );
|
|
|
|
|
|
- parent.geometry.solid = false;
|
|
|
|
- parent.material.side = THREE.DoubleSide;
|
|
|
|
|
|
+ return geometry;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else if ( defineKey ) {
|
|
|
|
|
|
+ // helper functions
|
|
|
|
|
|
- object = defines[ defineKey ].clone();
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ function resolveUSE( identifier ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var node = nodeMap[ identifier ];
|
|
|
|
+ var 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;
|
|
|
|
|
|
- return;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function parseFieldChildren( children, owner ) {
|
|
|
|
|
|
- object = parent;
|
|
|
|
|
|
+ for ( var i = 0, l = children.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( data.string.indexOf( 'AmbientLight' ) > - 1 && data.nodeType === 'PointLight' ) {
|
|
|
|
|
|
+ var object = getNode( children[ i ] );
|
|
|
|
|
|
- data.nodeType = 'AmbientLight';
|
|
|
|
|
|
+ if ( object instanceof THREE.Object3D ) owner.add( object );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- var l_visible = data.on !== undefined ? data.on : true;
|
|
|
|
- var l_intensity = data.intensity !== undefined ? data.intensity : 1;
|
|
|
|
- var l_color = new THREE.Color();
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( data.color ) {
|
|
|
|
|
|
+ function triangulateFaceIndex( index, ccw ) {
|
|
|
|
|
|
- l_color.copy( data.color );
|
|
|
|
|
|
+ var indices = [];
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // since face defintions can have more than three vertices, it's necessary to
|
|
|
|
+ // perform a simple triangulation
|
|
|
|
|
|
- if ( data.nodeType === 'AmbientLight' ) {
|
|
|
|
|
|
+ var start = 0;
|
|
|
|
|
|
- object = new THREE.AmbientLight( l_color, l_intensity );
|
|
|
|
- object.visible = l_visible;
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i ++ ) {
|
|
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ var i1 = index[ start ];
|
|
|
|
+ var i2 = index[ i + ( ccw ? 1 : 2 ) ];
|
|
|
|
+ var i3 = index[ i + ( ccw ? 2 : 1 ) ];
|
|
|
|
|
|
- } else if ( data.nodeType === 'PointLight' ) {
|
|
|
|
|
|
+ indices.push( i1, i2, i3 );
|
|
|
|
|
|
- var l_distance = 0;
|
|
|
|
|
|
+ // an index of -1 indicates that the current face has ended and the next one begins
|
|
|
|
|
|
- if ( data.radius !== undefined && data.radius < 1000 ) {
|
|
|
|
|
|
+ if ( index[ i + 3 ] === - 1 ) {
|
|
|
|
|
|
- l_distance = data.radius;
|
|
|
|
|
|
+ i += 3;
|
|
|
|
+ start = i + 1;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- object = new THREE.PointLight( l_color, l_intensity, l_distance );
|
|
|
|
- object.visible = l_visible;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ return indices;
|
|
|
|
|
|
- } else if ( data.nodeType === 'SpotLight' ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var l_intensity = 1;
|
|
|
|
- var l_distance = 0;
|
|
|
|
- var l_angle = Math.PI / 3;
|
|
|
|
- var l_penumbra = 0;
|
|
|
|
- var l_visible = true;
|
|
|
|
|
|
+ function triangulateFaceData( data, index ) {
|
|
|
|
|
|
- if ( data.radius !== undefined && data.radius < 1000 ) {
|
|
|
|
|
|
+ var triangulatedData = [];
|
|
|
|
|
|
- l_distance = data.radius;
|
|
|
|
|
|
+ var start = 0;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( data.cutOffAngle !== undefined ) {
|
|
|
|
|
|
+ var stride = start * 3;
|
|
|
|
|
|
- l_angle = data.cutOffAngle;
|
|
|
|
|
|
+ var x = data[ stride ];
|
|
|
|
+ var y = data[ stride + 1 ];
|
|
|
|
+ var z = data[ stride + 2 ];
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ triangulatedData.push( x, y, z );
|
|
|
|
|
|
- object = new THREE.SpotLight( l_color, l_intensity, l_distance, l_angle, l_penumbra );
|
|
|
|
- object.visible = l_visible;
|
|
|
|
|
|
+ // an index of -1 indicates that the current face has ended and the next one begins
|
|
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ if ( index[ i + 3 ] === - 1 ) {
|
|
|
|
|
|
- } else if ( data.nodeType === 'Transform' || data.nodeType === 'Group' ) {
|
|
|
|
|
|
+ i += 3;
|
|
|
|
+ start ++;
|
|
|
|
|
|
- object = new THREE.Object3D();
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( /DEF/.exec( data.string ) ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
|
|
|
|
- defines[ object.name ] = object;
|
|
|
|
|
|
+ return triangulatedData;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( data.translation !== undefined ) {
|
|
|
|
|
|
+ function flattenData( data, index ) {
|
|
|
|
|
|
- var t = data.translation;
|
|
|
|
|
|
+ var flattenData = [];
|
|
|
|
|
|
- object.position.set( t.x, t.y, t.z );
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var i1 = index[ i ];
|
|
|
|
|
|
- if ( data.rotation !== undefined ) {
|
|
|
|
|
|
+ var stride = i1 * 3;
|
|
|
|
|
|
- var r = data.rotation;
|
|
|
|
|
|
+ var x = data[ stride ];
|
|
|
|
+ var y = data[ stride + 1 ];
|
|
|
|
+ var z = data[ stride + 2 ];
|
|
|
|
|
|
- object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w );
|
|
|
|
|
|
+ flattenData.push( x, y, z );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( data.scale !== undefined ) {
|
|
|
|
|
|
+ return flattenData;
|
|
|
|
|
|
- var s = data.scale;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- object.scale.set( s.x, s.y, s.z );
|
|
|
|
|
|
+ function expandLineIndex( index ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var indices = [];
|
|
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i ++ ) {
|
|
|
|
|
|
- } else if ( data.nodeType === 'Shape' ) {
|
|
|
|
|
|
+ var i1 = index[ i ];
|
|
|
|
+ var i2 = index[ i + 1 ];
|
|
|
|
|
|
- object = new THREE.Mesh();
|
|
|
|
|
|
+ indices.push( i1, i2 );
|
|
|
|
|
|
- if ( /DEF/.exec( data.string ) ) {
|
|
|
|
|
|
+ // an index of -1 indicates that the current line has ended and the next one begins
|
|
|
|
|
|
- object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
|
|
|
|
|
|
+ if ( index[ i + 2 ] === - 1 ) {
|
|
|
|
|
|
- defines[ object.name ] = object;
|
|
|
|
|
|
+ i += 2;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- parent.add( object );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else if ( data.nodeType === 'Background' ) {
|
|
|
|
|
|
+ return indices;
|
|
|
|
|
|
- var segments = 20;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // sky (full sphere):
|
|
|
|
|
|
+ function expandLineData( data, index ) {
|
|
|
|
|
|
- var radius = 2e4;
|
|
|
|
|
|
+ var triangulatedData = [];
|
|
|
|
|
|
- var skyGeometry = new THREE.SphereBufferGeometry( radius, segments, segments );
|
|
|
|
- var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide } );
|
|
|
|
|
|
+ var start = 0;
|
|
|
|
|
|
- if ( data.skyColor.length > 1 ) {
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i ++ ) {
|
|
|
|
|
|
- paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true );
|
|
|
|
|
|
+ var stride = start * 3;
|
|
|
|
|
|
- skyMaterial.vertexColors = THREE.VertexColors;
|
|
|
|
|
|
+ var x = data[ stride ];
|
|
|
|
+ var y = data[ stride + 1 ];
|
|
|
|
+ var z = data[ stride + 2 ];
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ triangulatedData.push( x, y, z );
|
|
|
|
|
|
- var color = data.skyColor[ 0 ];
|
|
|
|
- skyMaterial.color.setRGB( color.r, color.b, color.g );
|
|
|
|
|
|
+ // an index of -1 indicates that the current line has ended and the next one begins
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( index[ i + 2 ] === - 1 ) {
|
|
|
|
|
|
- scene.add( new THREE.Mesh( skyGeometry, skyMaterial ) );
|
|
|
|
|
|
+ i += 2;
|
|
|
|
+ start ++;
|
|
|
|
|
|
- // ground (half sphere):
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( data.groundColor !== undefined ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- radius = 1.2e4;
|
|
|
|
|
|
+ return triangulatedData;
|
|
|
|
|
|
- var groundGeometry = new THREE.SphereBufferGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
|
|
|
|
- var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors } );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false );
|
|
|
|
|
|
+ var vA = new THREE.Vector3();
|
|
|
|
+ var vB = new THREE.Vector3();
|
|
|
|
+ var vC = new THREE.Vector3();
|
|
|
|
|
|
- scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) );
|
|
|
|
|
|
+ var uvA = new THREE.Vector2();
|
|
|
|
+ var uvB = new THREE.Vector2();
|
|
|
|
+ var uvC = new THREE.Vector2();
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function computeAttributeFromIndexedData( coordIndex, index, data, itemSize ) {
|
|
|
|
|
|
- } else if ( /geometry/.exec( data.string ) ) {
|
|
|
|
|
|
+ var array = [];
|
|
|
|
|
|
- if ( data.nodeType === 'Box' ) {
|
|
|
|
|
|
+ // we use the coordIndex.length as delimiter since normalIndex must contain at least as many indices
|
|
|
|
|
|
- var s = data.size;
|
|
|
|
|
|
+ for ( var i = 0, l = coordIndex.length; i < l; i += 3 ) {
|
|
|
|
|
|
- parent.geometry = new THREE.BoxBufferGeometry( s.x, s.y, s.z );
|
|
|
|
|
|
+ var a = index[ i ];
|
|
|
|
+ var b = index[ i + 1 ];
|
|
|
|
+ var c = index[ i + 2 ];
|
|
|
|
|
|
- } else if ( data.nodeType === 'Cylinder' ) {
|
|
|
|
|
|
+ if ( itemSize === 2 ) {
|
|
|
|
|
|
- parent.geometry = new THREE.CylinderBufferGeometry( data.radius, data.radius, data.height );
|
|
|
|
|
|
+ uvA.fromArray( data, a * itemSize );
|
|
|
|
+ uvB.fromArray( data, b * itemSize );
|
|
|
|
+ uvC.fromArray( data, c * itemSize );
|
|
|
|
|
|
- } else if ( data.nodeType === 'Cone' ) {
|
|
|
|
|
|
+ array.push( uvA.x, uvA.y );
|
|
|
|
+ array.push( uvB.x, uvB.y );
|
|
|
|
+ array.push( uvC.x, uvC.y );
|
|
|
|
|
|
- parent.geometry = new THREE.CylinderBufferGeometry( data.topRadius, data.bottomRadius, data.height );
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
- } else if ( data.nodeType === 'Sphere' ) {
|
|
|
|
|
|
+ vA.fromArray( data, a * itemSize );
|
|
|
|
+ vB.fromArray( data, b * itemSize );
|
|
|
|
+ vC.fromArray( data, c * itemSize );
|
|
|
|
|
|
- parent.geometry = new THREE.SphereBufferGeometry( data.radius );
|
|
|
|
|
|
+ array.push( vA.x, vA.y, vA.z );
|
|
|
|
+ array.push( vB.x, vB.y, vB.z );
|
|
|
|
+ array.push( vC.x, vC.y, vC.z );
|
|
|
|
|
|
- } else if ( data.nodeType === 'IndexedLineSet' ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: IndexedLineSet not supported yet.' );
|
|
|
|
- parent.parent.remove( parent ); // since the loader is not able to parse the geometry, remove the respective 3D object
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else if ( data.nodeType === 'Text' ) {
|
|
|
|
|
|
+ return new THREE.Float32BufferAttribute( array, itemSize );
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: Text not supported yet.' );
|
|
|
|
- parent.parent.remove( parent );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else if ( data.nodeType === 'IndexedFaceSet' ) {
|
|
|
|
|
|
+ function computeAttributeFromFaceData( index, faceData ) {
|
|
|
|
|
|
- var geometry = new THREE.BufferGeometry();
|
|
|
|
|
|
+ var array = [];
|
|
|
|
|
|
- var positions = [];
|
|
|
|
- var colors = [];
|
|
|
|
- var normals = [];
|
|
|
|
- var uvs = [];
|
|
|
|
|
|
+ for ( var i = 0, j = 0, l = index.length; i < l; i += 3, j ++ ) {
|
|
|
|
|
|
- var position, color, normal, uv;
|
|
|
|
|
|
+ vA.fromArray( faceData, j * 3 );
|
|
|
|
|
|
- var i, il, j, jl;
|
|
|
|
|
|
+ array.push( vA.x, vA.y, vA.z );
|
|
|
|
+ array.push( vA.x, vA.y, vA.z );
|
|
|
|
+ array.push( vA.x, vA.y, vA.z );
|
|
|
|
|
|
- for ( i = 0, il = data.children.length; i < il; i ++ ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var child = data.children[ i ];
|
|
|
|
|
|
+ return new THREE.Float32BufferAttribute( array, 3 );
|
|
|
|
|
|
- // uvs
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( child.nodeType === 'TextureCoordinate' ) {
|
|
|
|
|
|
+ function computeAttributeFromLineData( index, lineData ) {
|
|
|
|
|
|
- if ( child.points ) {
|
|
|
|
|
|
+ var array = [];
|
|
|
|
|
|
- for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
|
|
|
|
|
|
+ for ( var i = 0, j = 0, l = index.length; i < l; i += 2, j ++ ) {
|
|
|
|
|
|
- uv = child.points[ j ];
|
|
|
|
- uvs.push( uv.x, uv.y );
|
|
|
|
|
|
+ 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 );
|
|
|
|
|
|
- // normals
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( child.nodeType === 'Normal' ) {
|
|
|
|
|
|
+ function toNonIndexedAttribute( indices, attribute ) {
|
|
|
|
|
|
- if ( child.points ) {
|
|
|
|
|
|
+ var array = attribute.array;
|
|
|
|
+ var itemSize = attribute.itemSize;
|
|
|
|
|
|
- for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
|
|
|
|
|
|
+ var array2 = new array.constructor( indices.length * itemSize );
|
|
|
|
|
|
- normal = child.points[ j ];
|
|
|
|
- normals.push( normal.x, normal.y, normal.z );
|
|
|
|
|
|
+ var index = 0, index2 = 0;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ for ( var i = 0, l = indices.length; i < l; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ index = indices[ i ] * itemSize;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ for ( var j = 0; j < itemSize; j ++ ) {
|
|
|
|
|
|
- // colors
|
|
|
|
|
|
+ array2[ index2 ++ ] = array[ index ++ ];
|
|
|
|
|
|
- if ( child.nodeType === 'Color' ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( child.color ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- for ( j = 0, jl = child.color.length; j < jl; j ++ ) {
|
|
|
|
|
|
+ return new THREE.Float32BufferAttribute( array2, itemSize );
|
|
|
|
|
|
- color = child.color[ j ];
|
|
|
|
- colors.push( color.r, color.g, color.b );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var ab = new THREE.Vector3();
|
|
|
|
+ var cb = new THREE.Vector3();
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function computeNormalAttribute( index, coord, creaseAngle ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var faces = [];
|
|
|
|
+ var vertexNormals = {};
|
|
|
|
|
|
- // positions
|
|
|
|
|
|
+ // prepare face and raw vertex normals
|
|
|
|
|
|
- if ( child.nodeType === 'Coordinate' ) {
|
|
|
|
|
|
+ for ( var i = 0, l = index.length; i < l; i += 3 ) {
|
|
|
|
|
|
- if ( child.points ) {
|
|
|
|
|
|
+ var a = index[ i ];
|
|
|
|
+ var b = index[ i + 1 ];
|
|
|
|
+ var c = index[ i + 2 ];
|
|
|
|
|
|
- for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
|
|
|
|
|
|
+ var face = new Face( a, b, c );
|
|
|
|
|
|
- position = child.points[ j ];
|
|
|
|
- positions.push( position.x, position.y, position.z );
|
|
|
|
|
|
+ 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();
|
|
|
|
|
|
- if ( child.string.indexOf( 'DEF' ) > - 1 ) {
|
|
|
|
|
|
+ face.normal.copy( cb );
|
|
|
|
|
|
- var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ];
|
|
|
|
|
|
+ if ( vertexNormals[ a ] === undefined ) vertexNormals[ a ] = [];
|
|
|
|
+ if ( vertexNormals[ b ] === undefined ) vertexNormals[ b ] = [];
|
|
|
|
+ if ( vertexNormals[ c ] === undefined ) vertexNormals[ c ] = [];
|
|
|
|
|
|
- defines[ name ] = positions.slice( 0 );
|
|
|
|
|
|
+ vertexNormals[ a ].push( face.normal );
|
|
|
|
+ vertexNormals[ b ].push( face.normal );
|
|
|
|
+ vertexNormals[ c ].push( face.normal );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ faces.push( face );
|
|
|
|
|
|
- if ( child.string.indexOf( 'USE' ) > - 1 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ];
|
|
|
|
|
|
+ // compute vertex normals and build final geometry
|
|
|
|
|
|
- positions = defines[ defineKey ];
|
|
|
|
|
|
+ var normals = [];
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ for ( var i = 0, l = faces.length; i < l; i ++ ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var face = faces[ i ];
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var nA = weightedNormal( vertexNormals[ face.a ], face.normal, creaseAngle );
|
|
|
|
+ var nB = weightedNormal( vertexNormals[ face.b ], face.normal, creaseAngle );
|
|
|
|
+ var nC = weightedNormal( vertexNormals[ face.c ], face.normal, creaseAngle );
|
|
|
|
|
|
- // some shapes only have vertices for use in other shapes
|
|
|
|
|
|
+ vA.fromArray( coord, face.a * 3 );
|
|
|
|
+ vB.fromArray( coord, face.b * 3 );
|
|
|
|
+ vC.fromArray( coord, face.c * 3 );
|
|
|
|
|
|
- if ( data.coordIndex ) {
|
|
|
|
|
|
+ normals.push( nA.x, nA.y, nA.z );
|
|
|
|
+ normals.push( nB.x, nB.y, nB.z );
|
|
|
|
+ normals.push( nC.x, nC.y, nC.z );
|
|
|
|
|
|
- function triangulateIndexArray( indexArray, ccw, colorPerVertex ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- if ( ccw === undefined ) {
|
|
|
|
|
|
+ return new THREE.Float32BufferAttribute( normals, 3 );
|
|
|
|
|
|
- // ccw is true by default
|
|
|
|
- ccw = true;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function weightedNormal( normals, vector, creaseAngle ) {
|
|
|
|
|
|
- var triangulatedIndexArray = [];
|
|
|
|
- var skip = 0;
|
|
|
|
|
|
+ var normal = vector.clone();
|
|
|
|
|
|
- for ( i = 0, il = indexArray.length; i < il; i ++ ) {
|
|
|
|
|
|
+ for ( var i = 0, l = normals.length; i < l; i ++ ) {
|
|
|
|
|
|
- if ( colorPerVertex === false ) {
|
|
|
|
|
|
+ if ( normals[ i ].angleTo( vector ) < creaseAngle ) {
|
|
|
|
|
|
- var colorIndices = indexArray[ i ];
|
|
|
|
|
|
+ normal.add( normals[ i ] );
|
|
|
|
|
|
- for ( j = 0, jl = colorIndices.length; j < jl; j ++ ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var index = colorIndices[ j ];
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- triangulatedIndexArray.push( index, index, index );
|
|
|
|
|
|
+ return normal.normalize();
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ function toColorArray( colors ) {
|
|
|
|
|
|
- var indexedFace = indexArray[ i ];
|
|
|
|
|
|
+ var array = [];
|
|
|
|
|
|
- // VRML support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
|
|
|
|
|
|
+ for ( var i = 0, l = colors.length; i < l; i += 3 ) {
|
|
|
|
|
|
- skip = 0;
|
|
|
|
|
|
+ array.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
|
|
|
|
|
|
- while ( indexedFace.length >= 3 && skip < ( indexedFace.length - 2 ) ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- var i1 = indexedFace[ 0 ];
|
|
|
|
- var i2 = indexedFace[ skip + ( ccw ? 1 : 2 ) ];
|
|
|
|
- var i3 = indexedFace[ skip + ( ccw ? 2 : 1 ) ];
|
|
|
|
|
|
+ return array;
|
|
|
|
|
|
- triangulatedIndexArray.push( i1, i2, i3 );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- skip ++;
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 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 ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var direction = ( topDown === true ) ? 1 : - 1;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var coord = [], A = {}, B = {}, applyColor = false;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ for ( var k = 0; k < angles.length; k ++ ) {
|
|
|
|
|
|
- return triangulatedIndexArray;
|
|
|
|
|
|
+ // push the vector at which the color changes
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var vec = {
|
|
|
|
+ x: direction * ( Math.cos( angles[ k ] ) * radius ),
|
|
|
|
+ y: direction * ( Math.sin( angles[ k ] ) * radius )
|
|
|
|
+ };
|
|
|
|
|
|
- var positionIndexes = data.coordIndex ? triangulateIndexArray( data.coordIndex, data.ccw ) : [];
|
|
|
|
- var normalIndexes = data.normalIndex ? triangulateIndexArray( data.normalIndex, data.ccw ) : positionIndexes;
|
|
|
|
- var colorIndexes = data.colorIndex ? triangulateIndexArray( data.colorIndex, data.ccw, data.colorPerVertex ) : [];
|
|
|
|
- var uvIndexes = data.texCoordIndex ? triangulateIndexArray( data.texCoordIndex, data.ccw ) : positionIndexes;
|
|
|
|
|
|
+ coord.push( vec );
|
|
|
|
|
|
- var newIndexes = [];
|
|
|
|
- var newPositions = [];
|
|
|
|
- var newNormals = [];
|
|
|
|
- var newColors = [];
|
|
|
|
- var newUvs = [];
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // if any other index array does not match the coordinate indexes, split any points that differ
|
|
|
|
|
|
+ var index = geometry.index;
|
|
|
|
+ var positionAttribute = geometry.attributes.position;
|
|
|
|
+ var colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
|
|
|
|
|
|
- var pointMap = Object.create( null );
|
|
|
|
|
|
+ var position = new THREE.Vector3();
|
|
|
|
+ var color = new THREE.Color();
|
|
|
|
|
|
- for ( i = 0; i < positionIndexes.length; i ++ ) {
|
|
|
|
|
|
+ for ( var i = 0; i < index.count; i ++ ) {
|
|
|
|
|
|
- var pointAttributes = [];
|
|
|
|
|
|
+ var vertexIndex = index.getX( i );
|
|
|
|
|
|
- var positionIndex = positionIndexes[ i ];
|
|
|
|
- var normalIndex = normalIndexes[ i ];
|
|
|
|
- var colorIndex = colorIndexes[ i ];
|
|
|
|
- var uvIndex = uvIndexes[ i ];
|
|
|
|
|
|
+ position.fromBufferAttribute( positionAttribute, vertexIndex );
|
|
|
|
|
|
- var base = 10; // which base to use to represent each value
|
|
|
|
|
|
+ for ( var j = 0; j < colors.length; j ++ ) {
|
|
|
|
|
|
- pointAttributes.push( positionIndex.toString( base ) );
|
|
|
|
|
|
+ // linear interpolation between aColor and bColor, calculate proportion
|
|
|
|
+ // A is previous point (angle)
|
|
|
|
|
|
- if ( normalIndex !== undefined ) {
|
|
|
|
|
|
+ if ( j === 0 ) {
|
|
|
|
|
|
- pointAttributes.push( normalIndex.toString( base ) );
|
|
|
|
|
|
+ A.x = 0;
|
|
|
|
+ A.y = ( topDown === true ) ? radius : - 1 * radius;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
- if ( colorIndex !== undefined ) {
|
|
|
|
|
|
+ A.x = coord[ j - 1 ].x;
|
|
|
|
+ A.y = coord[ j - 1 ].y;
|
|
|
|
|
|
- pointAttributes.push( colorIndex.toString( base ) );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // B is current point (angle)
|
|
|
|
|
|
- if ( uvIndex !== undefined ) {
|
|
|
|
|
|
+ B = coord[ j ];
|
|
|
|
|
|
- pointAttributes.push( uvIndex.toString( base ) );
|
|
|
|
|
|
+ if ( B !== undefined ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // p has to be between the points A and B which we interpolate
|
|
|
|
|
|
- var pointId = pointAttributes.join( ',' );
|
|
|
|
- var newIndex = pointMap[ pointId ];
|
|
|
|
|
|
+ applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
|
|
|
|
|
|
- if ( newIndex === undefined ) {
|
|
|
|
|
|
+ if ( applyColor === true ) {
|
|
|
|
|
|
- newIndex = newPositions.length / 3;
|
|
|
|
- pointMap[ pointId ] = newIndex;
|
|
|
|
|
|
+ var aColor = colors[ j ];
|
|
|
|
+ var bColor = colors[ j + 1 ];
|
|
|
|
|
|
- newPositions.push(
|
|
|
|
- positions[ positionIndex * 3 ],
|
|
|
|
- positions[ positionIndex * 3 + 1 ],
|
|
|
|
- positions[ positionIndex * 3 + 2 ]
|
|
|
|
- );
|
|
|
|
|
|
+ // below is simple linear interpolation
|
|
|
|
|
|
- if ( normalIndex !== undefined && normals.length > 0 ) {
|
|
|
|
|
|
+ var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
|
|
|
|
|
|
- newNormals.push(
|
|
|
|
- normals[ normalIndex * 3 ],
|
|
|
|
- normals[ normalIndex * 3 + 1 ],
|
|
|
|
- normals[ normalIndex * 3 + 2 ]
|
|
|
|
- );
|
|
|
|
|
|
+ // 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
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ color.copy( aColor ).lerp( bColor, t );
|
|
|
|
|
|
- if ( colorIndex !== undefined && colors.length > 0 ) {
|
|
|
|
|
|
+ colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
|
|
|
|
|
|
- newColors.push(
|
|
|
|
- colors[ colorIndex * 3 ],
|
|
|
|
- colors[ colorIndex * 3 + 1 ],
|
|
|
|
- colors[ colorIndex * 3 + 2 ]
|
|
|
|
- );
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
|
|
|
|
+ var c = colors[ colorIndex ];
|
|
|
|
+ colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
|
|
|
|
|
|
- if ( uvIndex !== undefined && uvs.length > 0 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- newUvs.push(
|
|
|
|
- uvs[ uvIndex * 2 ],
|
|
|
|
- uvs[ uvIndex * 2 + 1 ]
|
|
|
|
- );
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- newIndexes.push( newIndex );
|
|
|
|
|
|
+ geometry.addAttribute( 'color', colorAttribute );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- positions = newPositions;
|
|
|
|
- normals = newNormals;
|
|
|
|
- colors = newColors;
|
|
|
|
- uvs = newUvs;
|
|
|
|
|
|
+ //
|
|
|
|
|
|
- geometry.setIndex( newIndexes );
|
|
|
|
|
|
+ var textureLoader = new THREE.TextureLoader( this.manager );
|
|
|
|
+ textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ // create JSON representing the tree structure of the VRML asset
|
|
|
|
|
|
- // do not add dummy mesh to the scene
|
|
|
|
|
|
+ var tree = generateVRMLTree( data );
|
|
|
|
|
|
- parent.parent.remove( parent );
|
|
|
|
|
|
+ // check version (only 2.0 is supported)
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ if ( tree.version.indexOf( 'V2.0' ) === - 1 ) {
|
|
|
|
|
|
- if ( false === data.solid ) {
|
|
|
|
|
|
+ throw Error( 'THREE.VRMLLexer: Version of VRML asset not supported.' );
|
|
|
|
|
|
- parent.material.side = THREE.DoubleSide;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ // parse the tree structure to a three.js scene
|
|
|
|
|
|
- // we need to store it on the geometry for use with defines
|
|
|
|
- geometry.solid = data.solid;
|
|
|
|
|
|
+ var scene = parseTree( tree );
|
|
|
|
|
|
- geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
|
|
|
|
|
|
+ return scene;
|
|
|
|
|
|
- if ( colors.length > 0 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
|
|
|
|
|
|
+ };
|
|
|
|
|
|
- parent.material.vertexColors = THREE.VertexColors;
|
|
|
|
|
|
+ function VRMLLexer( tokens ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ this.lexer = new chevrotain.Lexer( tokens );
|
|
|
|
|
|
- if ( uvs.length > 0 ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
|
|
|
|
|
|
+ VRMLLexer.prototype = {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ constructor: VRMLLexer,
|
|
|
|
|
|
- if ( normals.length > 0 ) {
|
|
|
|
|
|
+ lex: function ( inputText ) {
|
|
|
|
|
|
- geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
|
|
|
|
|
|
+ var lexingResult = this.lexer.tokenize( inputText );
|
|
|
|
|
|
- } else {
|
|
|
|
|
|
+ if ( lexingResult.errors.length > 0 ) {
|
|
|
|
|
|
- // convert geometry to non-indexed to get sharp normals
|
|
|
|
- geometry = geometry.toNonIndexed();
|
|
|
|
- geometry.computeVertexNormals();
|
|
|
|
|
|
+ console.error( lexingResult.errors );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ throw Error( 'THREE.VRMLLexer: Lexing errors detected.' );
|
|
|
|
|
|
- geometry.computeBoundingSphere();
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- // see if it's a define
|
|
|
|
- if ( /DEF/.exec( data.string ) ) {
|
|
|
|
|
|
+ return lexingResult;
|
|
|
|
|
|
- geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
|
|
|
|
- defines[ geometry.name ] = geometry;
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
|
|
- parent.geometry = geometry;
|
|
|
|
|
|
+ function VRMLParser( tokenVocabulary ) {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ chevrotain.Parser.call( this, tokenVocabulary );
|
|
|
|
|
|
- return;
|
|
|
|
|
|
+ var $ = this;
|
|
|
|
|
|
- } else if ( /appearance/.exec( data.string ) ) {
|
|
|
|
|
|
+ var Version = tokenVocabulary[ 'Version' ];
|
|
|
|
+ var LCurly = tokenVocabulary[ 'LCurly' ];
|
|
|
|
+ var RCurly = tokenVocabulary[ 'RCurly' ];
|
|
|
|
+ var LSquare = tokenVocabulary[ 'LSquare' ];
|
|
|
|
+ var RSquare = tokenVocabulary[ 'RSquare' ];
|
|
|
|
+ var Identifier = tokenVocabulary[ 'Identifier' ];
|
|
|
|
+ var RouteIdentifier = tokenVocabulary[ 'RouteIdentifier' ];
|
|
|
|
+ var StringLiteral = tokenVocabulary[ 'StringLiteral' ];
|
|
|
|
+ var NumberLiteral = tokenVocabulary[ 'NumberLiteral' ];
|
|
|
|
+ var BooleanLiteral = tokenVocabulary[ 'BooleanLiteral' ];
|
|
|
|
+ var NullLiteral = tokenVocabulary[ 'NullLiteral' ];
|
|
|
|
+ var DEF = tokenVocabulary[ 'DEF' ];
|
|
|
|
+ var USE = tokenVocabulary[ 'USE' ];
|
|
|
|
+ var ROUTE = tokenVocabulary[ 'ROUTE' ];
|
|
|
|
+ var TO = tokenVocabulary[ 'TO' ];
|
|
|
|
+ var NodeName = tokenVocabulary[ 'NodeName' ];
|
|
|
|
|
|
- for ( var i = 0; i < data.children.length; i ++ ) {
|
|
|
|
|
|
+ $.RULE( 'vrml', function () {
|
|
|
|
|
|
- var child = data.children[ i ];
|
|
|
|
|
|
+ $.SUBRULE( $.version );
|
|
|
|
+ $.AT_LEAST_ONE( function () {
|
|
|
|
|
|
- if ( child.nodeType === 'Material' ) {
|
|
|
|
|
|
+ $.SUBRULE( $.node );
|
|
|
|
|
|
- var material = new THREE.MeshPhongMaterial();
|
|
|
|
|
|
+ } );
|
|
|
|
+ $.MANY( function () {
|
|
|
|
|
|
- if ( child.diffuseColor !== undefined ) {
|
|
|
|
|
|
+ $.SUBRULE( $.route );
|
|
|
|
|
|
- var d = child.diffuseColor;
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- material.color.setRGB( d.r, d.g, d.b );
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.RULE( 'version', function () {
|
|
|
|
|
|
- if ( child.emissiveColor !== undefined ) {
|
|
|
|
|
|
+ $.CONSUME( Version );
|
|
|
|
|
|
- var e = child.emissiveColor;
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- material.emissive.setRGB( e.r, e.g, e.b );
|
|
|
|
|
|
+ $.RULE( 'node', function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.OPTION( function () {
|
|
|
|
|
|
- if ( child.specularColor !== undefined ) {
|
|
|
|
|
|
+ $.SUBRULE( $.def );
|
|
|
|
|
|
- var s = child.specularColor;
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- material.specular.setRGB( s.r, s.g, s.b );
|
|
|
|
|
|
+ $.CONSUME( NodeName );
|
|
|
|
+ $.CONSUME( LCurly );
|
|
|
|
+ $.MANY( function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.SUBRULE( $.field );
|
|
|
|
|
|
- if ( child.transparency !== undefined ) {
|
|
|
|
|
|
+ } );
|
|
|
|
+ $.CONSUME( RCurly );
|
|
|
|
|
|
- var t = child.transparency;
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- // transparency is opposite of opacity
|
|
|
|
- material.opacity = Math.abs( 1 - t );
|
|
|
|
|
|
+ $.RULE( 'field', function () {
|
|
|
|
|
|
- material.transparent = true;
|
|
|
|
|
|
+ $.CONSUME( Identifier );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.OR2( [
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- if ( /DEF/.exec( data.string ) ) {
|
|
|
|
|
|
+ $.SUBRULE( $.singleFieldValue );
|
|
|
|
|
|
- material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- defines[ material.name ] = material;
|
|
|
|
|
|
+ $.SUBRULE( $.multiFieldValue );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } }
|
|
|
|
+ ] );
|
|
|
|
|
|
- parent.material = material;
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.RULE( 'def', function () {
|
|
|
|
|
|
- if ( child.nodeType === 'ImageTexture' ) {
|
|
|
|
|
|
+ $.CONSUME( DEF );
|
|
|
|
+ $.CONSUME( Identifier );
|
|
|
|
|
|
- var textureName = /"([^"]+)"/.exec( child.children[ 0 ] );
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- if ( textureName ) {
|
|
|
|
|
|
+ $.RULE( 'use', function () {
|
|
|
|
|
|
- parent.material.name = textureName[ 1 ];
|
|
|
|
|
|
+ $.CONSUME( USE );
|
|
|
|
+ $.CONSUME( Identifier );
|
|
|
|
|
|
- parent.material.map = textureLoader.load( textureName[ 1 ] );
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.RULE( 'singleFieldValue', function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.AT_LEAST_ONE( function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.OR( [
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- return;
|
|
|
|
|
|
+ $.SUBRULE( $.node );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- for ( var i = 0, l = data.children.length; i < l; i ++ ) {
|
|
|
|
|
|
+ $.SUBRULE( $.use );
|
|
|
|
|
|
- parseNode( data.children[ i ], object );
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.CONSUME( StringLiteral );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- parseNode( getTree( lines ), scene );
|
|
|
|
|
|
+ $.CONSUME( NumberLiteral );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- var scene = new THREE.Scene();
|
|
|
|
|
|
+ $.CONSUME( BooleanLiteral );
|
|
|
|
|
|
- var lines = data.split( '\n' );
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- // some lines do not have breaks
|
|
|
|
|
|
+ $.CONSUME( NullLiteral );
|
|
|
|
|
|
- for ( var i = lines.length - 1; i > 0; i -- ) {
|
|
|
|
|
|
+ } }
|
|
|
|
+ ] );
|
|
|
|
|
|
- // The # symbol indicates that all subsequent text, until the end of the line is a comment,
|
|
|
|
- // and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html)
|
|
|
|
- lines[ i ] = lines[ i ].replace( /(#.*)/, '' );
|
|
|
|
|
|
|
|
- var line = lines[ i ];
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- // split lines with {..{ or {..[ - some have both
|
|
|
|
- if ( /{.*[{\[]/.test( line ) ) {
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- var parts = line.split( '{' ).join( '{\n' ).split( '\n' );
|
|
|
|
- parts.unshift( 1 );
|
|
|
|
- parts.unshift( i );
|
|
|
|
- lines.splice.apply( lines, parts );
|
|
|
|
|
|
+ $.RULE( 'multiFieldValue', function () {
|
|
|
|
|
|
- } else if ( /\].*}/.test( line ) ) {
|
|
|
|
|
|
+ $.CONSUME( LSquare );
|
|
|
|
+ $.MANY( function () {
|
|
|
|
|
|
- // split lines with ]..}
|
|
|
|
- var parts = line.split( ']' ).join( ']\n' ).split( '\n' );
|
|
|
|
- parts.unshift( 1 );
|
|
|
|
- parts.unshift( i );
|
|
|
|
- lines.splice.apply( lines, parts );
|
|
|
|
|
|
+ $.OR( [
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.SUBRULE( $.node );
|
|
|
|
|
|
- line = lines[ i ];
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- if ( /}.*}/.test( line ) ) {
|
|
|
|
|
|
+ $.SUBRULE( $.use );
|
|
|
|
|
|
- // split lines with }..}
|
|
|
|
- var parts = line.split( '}' ).join( '}\n' ).split( '\n' );
|
|
|
|
- parts.unshift( 1 );
|
|
|
|
- parts.unshift( i );
|
|
|
|
- lines.splice.apply( lines, parts );
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.CONSUME( StringLiteral );
|
|
|
|
|
|
- line = lines[ i ];
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- if ( /^\b[^\s]+\b$/.test( line.trim() ) ) {
|
|
|
|
|
|
+ $.CONSUME( NumberLiteral );
|
|
|
|
|
|
- // prevent lines with single words like "coord" or "geometry", see #12209
|
|
|
|
- lines[ i + 1 ] = line + ' ' + lines[ i + 1 ].trim();
|
|
|
|
- lines.splice( i, 1 );
|
|
|
|
|
|
+ } },
|
|
|
|
+ { ALT: function () {
|
|
|
|
|
|
- } else if ( ( line.indexOf( 'coord' ) > - 1 ) && ( line.indexOf( '[' ) < 0 ) && ( line.indexOf( '{' ) < 0 ) ) {
|
|
|
|
|
|
+ $.CONSUME( NullLiteral );
|
|
|
|
|
|
- // force the parser to create Coordinate node for empty coords
|
|
|
|
- // coord USE something -> coord USE something Coordinate {}
|
|
|
|
|
|
+ } }
|
|
|
|
+ ] );
|
|
|
|
|
|
- lines[ i ] += ' Coordinate {}';
|
|
|
|
|
|
+ } );
|
|
|
|
+ $.CONSUME( RSquare );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ $.RULE( 'route', function () {
|
|
|
|
|
|
- var header = lines.shift();
|
|
|
|
|
|
+ $.CONSUME( ROUTE );
|
|
|
|
+ $.CONSUME( RouteIdentifier );
|
|
|
|
+ $.CONSUME( TO );
|
|
|
|
+ $.CONSUME2( RouteIdentifier );
|
|
|
|
|
|
- if ( /V1.0/.exec( header ) ) {
|
|
|
|
|
|
+ } );
|
|
|
|
|
|
- console.warn( 'THREE.VRMLLoader: V1.0 not supported yet.' );
|
|
|
|
|
|
+ this.performSelfAnalysis();
|
|
|
|
|
|
- } else if ( /V2.0/.exec( header ) ) {
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- parseV2( lines, scene );
|
|
|
|
|
|
+ VRMLParser.prototype = Object.create( chevrotain.Parser.prototype );
|
|
|
|
+ VRMLParser.prototype.constructor = VRMLParser;
|
|
|
|
|
|
- }
|
|
|
|
|
|
+ function Face( a, b, c ) {
|
|
|
|
|
|
- return scene;
|
|
|
|
|
|
+ this.a = a;
|
|
|
|
+ this.b = b;
|
|
|
|
+ this.c = c;
|
|
|
|
+ this.normal = new THREE.Vector3();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-};
|
|
|
|
|
|
+ return VRMLLoader;
|
|
|
|
+
|
|
|
|
+} )();
|