function Float32BufferAttribute( array, itemSize, normalized ) { THREE.BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); } Float32BufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; THREE.Float32BufferAttribute = Float32BufferAttribute; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * linewidth: , * dashed: , * dashScale: , * dashSize: , * gapSize: , * resolution: , // to be set by renderer * } */ THREE.UniformsLib.line = { linewidth: { value: 1 , type:'f'}, resolution: { value: new THREE.Vector2( 1, 1 ), type:'v2' }, dashScale: { value: 1 , type:'f'}, dashSize: { value: 1 , type:'f'}, gapSize: { value: 1 , type:'f'} // todo FIX - maybe change to totalSize }; THREE.ShaderLib[ 'line' ] = { uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.line ] ), /* vertexShader: ` #include #include #include uniform float linewidth; uniform vec2 resolution; attribute vec3 instanceStart; attribute vec3 instanceEnd; attribute vec3 instanceColorStart; attribute vec3 instanceColorEnd; varying vec2 vUv; #ifdef USE_DASH uniform float dashScale; attribute float instanceDistanceStart; attribute float instanceDistanceEnd; varying float vLineDistance; #endif void trimSegment( const in vec4 start, inout vec4 end ) { // trim end segment so it terminates between the camera plane and the near plane // conservative estimate of the near plane float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column float nearEstimate = - 0.5 * b / a; float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ); end.xyz = mix( start.xyz, end.xyz, alpha ); } void main() { #ifdef USE_COLOR vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd; #endif #ifdef USE_DASH vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd; #endif float aspect = resolution.x / resolution.y; vUv = uv; // camera space vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ); vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ); // special case for perspective projection, and segments that terminate either in, or behind, the camera plane // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space // but we need to perform ndc-space calculations in the shader, so we must address this issue directly // perhaps there is a more elegant solution -- WestLangley bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column if ( perspective ) { if ( start.z < 0.0 && end.z >= 0.0 ) { trimSegment( start, end ); } else if ( end.z < 0.0 && start.z >= 0.0 ) { trimSegment( end, start ); } } // clip space vec4 clipStart = projectionMatrix * start; vec4 clipEnd = projectionMatrix * end; // ndc space vec2 ndcStart = clipStart.xy / clipStart.w; vec2 ndcEnd = clipEnd.xy / clipEnd.w; // direction vec2 dir = ndcEnd - ndcStart; // account for clip-space aspect ratio dir.x *= aspect; dir = normalize( dir ); // perpendicular to dir vec2 offset = vec2( dir.y, - dir.x ); // undo aspect ratio adjustment dir.x /= aspect; offset.x /= aspect; // sign flip if ( position.x < 0.0 ) offset *= - 1.0; // endcaps if ( position.y < 0.0 ) { offset += - dir; } else if ( position.y > 1.0 ) { offset += dir; } // adjust for linewidth offset *= linewidth; // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ... offset /= resolution.y; // select end vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd; // back to clip space offset *= clip.w; clip.xy += offset; gl_Position = clip; vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation #include } `, fragmentShader: ` uniform vec3 diffuse; uniform float opacity; #ifdef USE_DASH uniform float dashSize; uniform float gapSize; #endif varying float vLineDistance; #include #include #include #include varying vec2 vUv; void main() { #ifdef USE_DASH if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX #endif if ( abs( vUv.y ) > 1.0 ) { float a = vUv.x; float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; float len2 = a * a + b * b; if ( len2 > 1.0 ) discard; } vec4 diffuseColor = vec4( diffuse, opacity ); #include #include gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ); #include #include #include #include } ` */ //---------------去掉注释的版本-:---- /* vertexShader: ` #include #include #include uniform float linewidth; uniform vec2 resolution; attribute vec3 instanceStart; attribute vec3 instanceEnd; attribute vec3 instanceColorStart; attribute vec3 instanceColorEnd; varying vec2 vUv; #ifdef USE_DASH uniform float dashScale; attribute float instanceDistanceStart; attribute float instanceDistanceEnd; varying float vLineDistance; #endif void trimSegment( const in vec4 start, inout vec4 end ) { float a = projectionMatrix[ 2 ][ 2 ]; float b = projectionMatrix[ 3 ][ 2 ]; float nearEstimate = - 0.5 * b / a; float alpha = ( nearEstimate - start.z ) / ( end.z - start.z ); end.xyz = mix( start.xyz, end.xyz, alpha ); } void main() { #ifdef USE_COLOR vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd; #endif #ifdef USE_DASH vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd; #endif float aspect = resolution.x / resolution.y; vUv = uv; vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 ); vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 ); bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); if ( perspective ) { if ( start.z < 0.0 && end.z >= 0.0 ) { trimSegment( start, end ); } else if ( end.z < 0.0 && start.z >= 0.0 ) { trimSegment( end, start ); } } vec4 clipStart = projectionMatrix * start; vec4 clipEnd = projectionMatrix * end; vec2 ndcStart = clipStart.xy / clipStart.w; vec2 ndcEnd = clipEnd.xy / clipEnd.w; vec2 dir = ndcEnd - ndcStart; dir.x *= aspect; dir = normalize( dir ); vec2 offset = vec2( dir.y, - dir.x ); dir.x /= aspect; offset.x /= aspect; if ( position.x < 0.0 ) offset *= - 1.0; if ( position.y < 0.0 ) { offset += - dir; } else if ( position.y > 1.0 ) { offset += dir; } offset *= linewidth; offset /= resolution.y; vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd; offset *= clip.w; clip.xy += offset; gl_Position = clip; vec4 mvPosition = ( position.y < 0.5 ) ? start : end; #include } `, fragmentShader: ` uniform vec3 diffuse; uniform float opacity; #ifdef USE_DASH uniform float dashSize; uniform float gapSize; #endif varying float vLineDistance; #include #include #include #include varying vec2 vUv; void main() { #ifdef USE_DASH if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; #endif if ( abs( vUv.y ) > 1.0 ) { float a = vUv.x; float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0; float len2 = a * a + b * b; if ( len2 > 1.0 ) discard; } vec4 diffuseColor = vec4( diffuse, opacity ); #include #include gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a ); #include #include #include #include } ` */ //ie不支持模板字符串所以转成: vertexShader : "#include \n#include \n \n#include \n \nuniform float linewidth;\nuniform vec2 resolution;\nattribute vec3 instanceStart;\nattribute vec3 instanceEnd;\nattribute vec3 instanceColorStart;\nattribute vec3 instanceColorEnd;\nvarying vec2 vUv;\n#ifdef USE_DASH\n uniform float dashScale;\n attribute float instanceDistanceStart;\n attribute float instanceDistanceEnd;\n varying float vLineDistance;\n#endif\nvoid trimSegment( const in vec4 start, inout vec4 end ) {\n float a = projectionMatrix[ 2 ][ 2 ]; \n float b = projectionMatrix[ 3 ][ 2 ]; \n float nearEstimate = - 0.5 * b / a;\n float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n end.xyz = mix( start.xyz, end.xyz, alpha );\n}\nvoid main() {\n #ifdef USE_COLOR\n vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n #endif\n #ifdef USE_DASH\n vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;\n #endif\n float aspect = resolution.x / resolution.y;\n vUv = uv;\n vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); \n if ( perspective ) {\n if ( start.z < 0.0 && end.z >= 0.0 ) {\n trimSegment( start, end );\n } else if ( end.z < 0.0 && start.z >= 0.0 ) {\n trimSegment( end, start );\n }\n }\n vec4 clipStart = projectionMatrix * start;\n vec4 clipEnd = projectionMatrix * end;\n vec2 ndcStart = clipStart.xy / clipStart.w;\n vec2 ndcEnd = clipEnd.xy / clipEnd.w;\n vec2 dir = ndcEnd - ndcStart;\n dir.x *= aspect;\n dir = normalize( dir );\n vec2 offset = vec2( dir.y, - dir.x );\n dir.x /= aspect;\n offset.x /= aspect;\n if ( position.x < 0.0 ) offset *= - 1.0;\n if ( position.y < 0.0 ) {\n offset += - dir;\n } else if ( position.y > 1.0 ) {\n offset += dir;\n }\n offset *= linewidth;\n offset /= resolution.y;\n vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n offset *= clip.w;\n clip.xy += offset;\n gl_Position = clip;\n vec4 mvPosition = ( position.y < 0.5 ) ? start : end; \n #include \n \n \n} ", fragmentShader: "uniform vec3 diffuse;\n uniform float opacity;\n #ifdef USE_DASH\n uniform float dashSize;\n uniform float gapSize;\n #endif\n varying float vLineDistance;\n #include \n #include \n #include \n #include \n \n varying vec2 vUv;\n void main() {\n \n #ifdef USE_DASH\n\nif ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; \n\nif ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; \n #endif\n if ( abs( vUv.y ) > 1.0 ) {\n\nfloat a = vUv.x;\n\nfloat b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n\nfloat len2 = a * a + b * b;\n\nif ( len2 > 1.0 ) discard;\n }\n vec4 diffuseColor = vec4( diffuse, opacity );\n #include \n #include \n gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );\n #include \n #include \n #include \n #include \n }" }; THREE.LineMaterial = function ( parameters ) { THREE.ShaderMaterial.call( this, { type: 'LineMaterial', uniforms: THREE.UniformsUtils.clone( THREE.ShaderLib[ 'line' ].uniforms ), vertexShader: THREE.ShaderLib[ 'line' ].vertexShader, fragmentShader: THREE.ShaderLib[ 'line' ].fragmentShader } ); this.dashed = false; Object.defineProperties( this, { color: { enumerable: true, get: function () { return this.uniforms.diffuse.value; }, set: function ( value ) { this.uniforms.diffuse.value = value; } }, linewidth: { enumerable: true, get: function () { return this.uniforms.linewidth.value; }, set: function ( value ) { this.uniforms.linewidth.value = value; } }, dashScale: { enumerable: true, get: function () { return this.uniforms.dashScale.value; }, set: function ( value ) { this.uniforms.dashScale.value = value; } }, dashSize: { enumerable: true, get: function () { return this.uniforms.dashSize.value; }, set: function ( value ) { this.uniforms.dashSize.value = value; } }, gapSize: { enumerable: true, get: function () { return this.uniforms.gapSize.value; }, set: function ( value ) { this.uniforms.gapSize.value = value; } }, resolution: { enumerable: true, get: function () { return this.uniforms.resolution.value; }, set: function ( value ) { this.uniforms.resolution.value.copy( value ); } } } ); this.setValues( parameters ); }; THREE.LineMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); THREE.LineMaterial.prototype.constructor = THREE.LineMaterial; THREE.LineMaterial.prototype.isLineMaterial = true; THREE.LineMaterial.prototype.copy = function ( source ) { THREE.ShaderMaterial.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.resolution = source.resolution; // todo return this; }; /** * @author WestLangley / http://github.com/WestLangley * */ THREE.LineSegmentsGeometry = function () { THREE.InstancedBufferGeometry.call( this ); this.type = 'LineSegmentsGeometry'; var plane = new THREE.BufferGeometry(); var positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ]; var uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ]; var index = new THREE.BufferAttribute(new Uint16Array([ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ]), 1) this.setIndex( index ); this.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) ); this.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) ); }; THREE.LineSegmentsGeometry.prototype = Object.assign( Object.create( THREE.InstancedBufferGeometry.prototype ), { constructor: THREE.LineSegmentsGeometry, isLineSegmentsGeometry: true, applyMatrix: function ( matrix ) { var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if ( start !== undefined ) { matrix.applyToBufferAttribute( start ); matrix.applyToBufferAttribute( end ); start.data.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; }, setPositions: function ( array ) { var lineSegments; if ( array instanceof Float32Array ) { lineSegments = array; } else if ( Array.isArray( array ) ) { lineSegments = new Float32Array( array ); } var instanceBuffer = new THREE.InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz this.addAttribute( 'instanceStart', new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz this.addAttribute( 'instanceEnd', new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz // this.computeBoundingBox(); this.computeBoundingSphere(); return this; }, setColors: function ( array ) { var colors; if ( array instanceof Float32Array ) { colors = array; } else if ( Array.isArray( array ) ) { colors = new Float32Array( array ); } var instanceColorBuffer = new THREE.InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb this.addAttribute( 'instanceColorStart', new THREE.InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb this.addAttribute( 'instanceColorEnd', new THREE.InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb return this; }, fromWireframeGeometry: function ( geometry ) { this.setPositions( geometry.attributes.position.array ); return this; }, fromEdgesGeometry: function ( geometry ) { this.setPositions( geometry.attributes.position.array ); return this; }, fromMesh: function ( mesh ) { this.fromWireframeGeometry( new THREE.WireframeGeometry( mesh.geometry ) ); // set colors, maybe return this; }, fromLineSegements: function ( lineSegments ) { var geometry = lineSegments.geometry; if ( geometry.isGeometry ) { this.setPositions( geometry.vertices ); } else if ( geometry.isBufferGeometry ) { this.setPositions( geometry.position.array ); // assumes non-indexed } // set colors, maybe return this; }, computeBoundingBox: function () { var box = new THREE.Box3(); return function computeBoundingBox() { if ( this.boundingBox === null ) { this.boundingBox = new THREE.Box3(); } var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if ( start !== undefined && end !== undefined ) { this.boundingBox.setFromBufferAttribute( start ); box.setFromBufferAttribute( end ); this.boundingBox.union( box ); } }; }(), computeBoundingSphere: function () { var vector = new THREE.Vector3(); return function computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new THREE.Sphere(); } if ( this.boundingBox === null ) { this.computeBoundingBox(); } var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if ( start !== undefined && end !== undefined ) { var center = this.boundingSphere.center; this.boundingBox.getCenter( center ); var maxRadiusSq = 0; for ( var i = 0, il = start.count; i < il; i ++ ) { vector.fromBufferAttribute( start, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); vector.fromBufferAttribute( end, i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this ); } } }; }(), toJSON: function () { // todo }, clone: function () { // todo }, copy: function ( source ) { // todo return this; } } ); /** * @author WestLangley / http://github.com/WestLangley * */ THREE.LineGeometry = function () { THREE.LineSegmentsGeometry.call( this ); this.type = 'LineGeometry'; }; THREE.LineGeometry.prototype = Object.assign( Object.create( THREE.LineSegmentsGeometry.prototype ), { constructor: THREE.LineGeometry, isLineGeometry: true, setPositions: function ( array ) { // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format var length = array.length - 3; var points = new Float32Array( 2 * length ); for ( var i = 0; i < length; i += 3 ) { points[ 2 * i ] = array[ i ]; points[ 2 * i + 1 ] = array[ i + 1 ]; points[ 2 * i + 2 ] = array[ i + 2 ]; points[ 2 * i + 3 ] = array[ i + 3 ]; points[ 2 * i + 4 ] = array[ i + 4 ]; points[ 2 * i + 5 ] = array[ i + 5 ]; } THREE.LineSegmentsGeometry.prototype.setPositions.call( this, points ); return this; }, setColors: function ( array ) { // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format var length = array.length - 3; var colors = new Float32Array( 2 * length ); for ( var i = 0; i < length; i += 3 ) { colors[ 2 * i ] = array[ i ]; colors[ 2 * i + 1 ] = array[ i + 1 ]; colors[ 2 * i + 2 ] = array[ i + 2 ]; colors[ 2 * i + 3 ] = array[ i + 3 ]; colors[ 2 * i + 4 ] = array[ i + 4 ]; colors[ 2 * i + 5 ] = array[ i + 5 ]; } THREE.LineSegmentsGeometry.prototype.setColors.call( this, colors ); return this; }, fromLine: function ( line ) { var geometry = line.geometry; if ( geometry.isGeometry ) { this.setPositions( geometry.vertices ); } else if ( geometry.isBufferGeometry ) { this.setPositions( geometry.position.array ); // assumes non-indexed } // set colors, maybe return this; }, copy: function ( source ) { // todo return this; } } ); /** * @author WestLangley / http://github.com/WestLangley * */ THREE.LineSegments2 = function ( geometry, material ) { THREE.Mesh.call( this ); this.type = 'LineSegments2'; this.geometry = geometry !== undefined ? geometry : new THREE.LineSegmentsGeometry(); this.material = material !== undefined ? material : new THREE.LineMaterial( { color: Math.random() * 0xffffff } ); }; THREE.LineSegments2.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), { constructor: THREE.LineSegments2, isLineSegments2: true, computeLineDistances: ( function () { // for backwards-compatability, but could be a method of LineSegmentsGeometry... var start = new THREE.Vector3(); var end = new THREE.Vector3(); return function computeLineDistances() { var geometry = this.geometry; var instanceStart = geometry.attributes.instanceStart; var instanceEnd = geometry.attributes.instanceEnd; var lineDistances = new Float32Array( 2 * instanceStart.data.count ); for ( var i = 0, j = 0, l = instanceStart.data.count; i < l; i ++, j += 2 ) { start.fromBufferAttribute( instanceStart, i ); end.fromBufferAttribute( instanceEnd, i ); lineDistances[ j ] = ( j === 0 ) ? 0 : lineDistances[ j - 1 ]; lineDistances[ j + 1 ] = lineDistances[ j ] + start.distanceTo( end ); } var instanceDistanceBuffer = new THREE.InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1 geometry.addAttribute( 'instanceDistanceStart', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0 geometry.addAttribute( 'instanceDistanceEnd', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1 return this; }; }() ), copy: function ( source ) { // todo return this; } } ); //fat line THREE.Fatline = function ( geometry, material ) { THREE.LineSegments2.call( this ); this.type = 'Fatline'; this.geometry = geometry !== undefined ? geometry : new THREE.LineGeometry(); this.material = material !== undefined ? material : new THREE.LineMaterial( { color: Math.random() * 0xffffff } ); }; THREE.Fatline.prototype = Object.assign( Object.create( THREE.LineSegments2.prototype ), { constructor: THREE.Fatline, isFatline: true, copy: function ( source ) { // todo return this; } } ); //////////CSS3DObject//////////// /** * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs * @author mrdoob / http://mrdoob.com/ * @author yomotsu / https://yomotsu.net/ */ (function(){ /* import { Matrix4, Object3D, Vector3 } from "../../../build/three.module.js"; */ var Matrix4 = THREE.Matrix4, Object3D = THREE.Object3D, Vector3 = THREE.Vector3 var CSS3DObject = THREE.CSS3DObject = function ( element ) { Object3D.call( this ); this.element = element; this.element.style.position = 'absolute'; this.element.style.pointerEvents = 'auto'; this.addEventListener( 'removed', function () { this.traverse( function ( object ) { if ( object.element instanceof Element && object.element.parentNode !== null ) { object.element.parentNode.removeChild( object.element ); } } ); } ); }; CSS3DObject.prototype = Object.create( Object3D.prototype ); CSS3DObject.prototype.constructor = CSS3DObject; var CSS3DSprite = THREE.CSS3DSprite = function ( element ) { CSS3DObject.call( this, element ); }; CSS3DSprite.prototype = Object.create( CSS3DObject.prototype ); CSS3DSprite.prototype.constructor = CSS3DSprite; // var CSS3DRenderer = THREE.CSS3DRenderer = function () { var _width, _height; var _widthHalf, _heightHalf; var matrix = new Matrix4(); var cache = { camera: { fov: 0, style: '' }, objects: new WeakMap() }; var domElement = document.createElement( 'div' ); domElement.style.overflow = 'hidden'; this.domElement = domElement; var cameraElement = document.createElement( 'div' ); cameraElement.style.WebkitTransformStyle = 'preserve-3d'; cameraElement.style.transformStyle = 'preserve-3d'; cameraElement.style.pointerEvents = 'none'; domElement.appendChild( cameraElement ); var isIE = /Trident/i.test( navigator.userAgent ); this.getSize = function () { return { width: _width, height: _height }; }; this.setSize = function ( width, height ) { _width = width; _height = height; _widthHalf = _width / 2; _heightHalf = _height / 2; domElement.style.width = width + 'px'; domElement.style.height = height + 'px'; cameraElement.style.width = width + 'px'; cameraElement.style.height = height + 'px'; }; function epsilon( value ) { return Math.abs( value ) < 1e-10 ? 0 : value; } function getCameraCSSMatrix( matrix ) { var elements = matrix.elements; return 'matrix3d(' + epsilon( elements[ 0 ] ) + ',' + epsilon( - elements[ 1 ] ) + ',' + epsilon( elements[ 2 ] ) + ',' + epsilon( elements[ 3 ] ) + ',' + epsilon( elements[ 4 ] ) + ',' + epsilon( - elements[ 5 ] ) + ',' + epsilon( elements[ 6 ] ) + ',' + epsilon( elements[ 7 ] ) + ',' + epsilon( elements[ 8 ] ) + ',' + epsilon( - elements[ 9 ] ) + ',' + epsilon( elements[ 10 ] ) + ',' + epsilon( elements[ 11 ] ) + ',' + epsilon( elements[ 12 ] ) + ',' + epsilon( - elements[ 13 ] ) + ',' + epsilon( elements[ 14 ] ) + ',' + epsilon( elements[ 15 ] ) + ')'; } function getObjectCSSMatrix( matrix, cameraCSSMatrix ) { var elements = matrix.elements; var matrix3d = 'matrix3d(' + epsilon( elements[ 0 ] ) + ',' + epsilon( elements[ 1 ] ) + ',' + epsilon( elements[ 2 ] ) + ',' + epsilon( elements[ 3 ] ) + ',' + epsilon( - elements[ 4 ] ) + ',' + epsilon( - elements[ 5 ] ) + ',' + epsilon( - elements[ 6 ] ) + ',' + epsilon( - elements[ 7 ] ) + ',' + epsilon( elements[ 8 ] ) + ',' + epsilon( elements[ 9 ] ) + ',' + epsilon( elements[ 10 ] ) + ',' + epsilon( elements[ 11 ] ) + ',' + epsilon( elements[ 12 ] ) + ',' + epsilon( elements[ 13 ] ) + ',' + epsilon( elements[ 14 ] ) + ',' + epsilon( elements[ 15 ] ) + ')'; if ( isIE ) { return 'translate(-50%,-50%)' + 'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)' + cameraCSSMatrix + matrix3d; } return 'translate(-50%,-50%)' + matrix3d; } function renderObject( object, camera, cameraCSSMatrix ) { if ( object instanceof CSS3DObject ) { var style; if ( object instanceof CSS3DSprite ) { // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/ matrix.copy( camera.matrixWorldInverse ); matrix.transpose(); matrix.copyPosition( object.matrixWorld ); matrix.scale( object.scale ); matrix.elements[ 3 ] = 0; matrix.elements[ 7 ] = 0; matrix.elements[ 11 ] = 0; matrix.elements[ 15 ] = 1; style = getObjectCSSMatrix( matrix, cameraCSSMatrix ); } else { //style = getObjectCSSMatrix( object.matrixWorld, cameraCSSMatrix ); style = getObjectCSSMatrix( object.matrixWorld, cameraCSSMatrix ) + (isIE ? "" : object.baseScale); //许钟文 加 object.baseScale针对小数,先在elem放大,然后这里缩小 } var element = object.element; var cachedObject = cache.objects.get( object ); if ( cachedObject === undefined || cachedObject.style !== style ) { element.style.WebkitTransform = style; element.style.transform = style; var objectData = { style: style }; if ( isIE ) { objectData.distanceToCameraSquared = getDistanceToSquared( camera, object ); } cache.objects.set( object, objectData ); } if ( element.parentNode !== cameraElement ) { cameraElement.appendChild( element ); } } for ( var i = 0, l = object.children.length; i < l; i ++ ) { renderObject( object.children[ i ], camera, cameraCSSMatrix ); } } var getDistanceToSquared = function () { var a = new Vector3(); var b = new Vector3(); return function ( object1, object2 ) { a.setFromMatrixPosition( object1.matrixWorld ); b.setFromMatrixPosition( object2.matrixWorld ); return a.distanceToSquared( b ); }; }(); function filterAndFlatten( scene ) { var result = []; scene.traverse( function ( object ) { if ( object instanceof CSS3DObject ) result.push( object ); } ); return result; } function zOrder( scene ) { var sorted = filterAndFlatten( scene ).sort( function ( a, b ) { var distanceA = cache.objects.get( a ).distanceToCameraSquared; var distanceB = cache.objects.get( b ).distanceToCameraSquared; return distanceA - distanceB; } ); var zMax = sorted.length; for ( var i = 0, l = sorted.length; i < l; i ++ ) { sorted[ i ].element.style.zIndex = zMax - i; } } this.render = function ( scene, camera ) { var fov = camera.projectionMatrix.elements[ 5 ] * _heightHalf; if ( cache.camera.fov !== fov ) { if ( camera.isPerspectiveCamera ) { domElement.style.WebkitPerspective = fov + 'px'; domElement.style.perspective = fov + 'px'; } else { domElement.style.WebkitPerspective = ''; domElement.style.perspective = ''; } cache.camera.fov = fov; } if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); if ( camera.parent === null ) camera.updateMatrixWorld(); if ( camera.isOrthographicCamera ) { var tx = - ( camera.right + camera.left ) / 2; var ty = ( camera.top + camera.bottom ) / 2; } var cameraCSSMatrix = camera.isOrthographicCamera ? 'scale(' + fov + ')' + 'translate(' + epsilon( tx ) + 'px,' + epsilon( ty ) + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ) : 'translateZ(' + fov + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ); var style = cameraCSSMatrix + 'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)'; if ( cache.camera.style !== style && ! isIE ) { cameraElement.style.WebkitTransform = style; cameraElement.style.transform = style; cache.camera.style = style; } renderObject( scene, camera, cameraCSSMatrix ); if ( isIE ) { // IE10 and 11 does not support 'preserve-3d'. // Thus, z-order in 3D will not work. // We have to calc z-order manually and set CSS z-index for IE. // FYI: z-index can't handle object intersection zOrder( scene ); } }; }; //export { CSS3DObject, CSS3DSprite, CSS3DRenderer }; })()