THREE.MeshLine.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. ;(function() {
  2. "use strict";
  3. var root = this
  4. var has_require = typeof require !== 'undefined'
  5. var THREE = root.THREE || has_require && require('three')
  6. if( !THREE )
  7. throw new Error( 'MeshLine requires three.js' )
  8. function MeshLine() {
  9. this.positions = [];
  10. this.previous = [];
  11. this.next = [];
  12. this.side = [];
  13. this.width = [];
  14. this.indices_array = [];
  15. this.uvs = [];
  16. this.counters = [];
  17. this.geometry = new THREE.BufferGeometry();
  18. this.widthCallback = null;
  19. }
  20. MeshLine.prototype.setGeometry = function( g, c ) {
  21. this.widthCallback = c;
  22. this.positions = [];
  23. this.counters = [];
  24. if( g instanceof THREE.Geometry ) {
  25. for( var j = 0; j < g.vertices.length; j++ ) {
  26. var v = g.vertices[ j ];
  27. var c = j/g.vertices.length;
  28. this.positions.push( v.x, v.y, v.z );
  29. this.positions.push( v.x, v.y, v.z );
  30. this.counters.push(c);
  31. this.counters.push(c);
  32. }
  33. }
  34. if( g instanceof THREE.BufferGeometry ) {
  35. // read attribute positions ?
  36. }
  37. if( g instanceof Float32Array || g instanceof Array ) {
  38. for( var j = 0; j < g.length; j += 3 ) {
  39. var c = j/g.length;
  40. this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
  41. this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );
  42. this.counters.push(c);
  43. this.counters.push(c);
  44. }
  45. }
  46. this.process();
  47. }
  48. MeshLine.prototype.compareV3 = function( a, b ) {
  49. var aa = a * 6;
  50. var ab = b * 6;
  51. return ( this.positions[ aa ] === this.positions[ ab ] ) && ( this.positions[ aa + 1 ] === this.positions[ ab + 1 ] ) && ( this.positions[ aa + 2 ] === this.positions[ ab + 2 ] );
  52. }
  53. MeshLine.prototype.copyV3 = function( a ) {
  54. var aa = a * 6;
  55. return [ this.positions[ aa ], this.positions[ aa + 1 ], this.positions[ aa + 2 ] ];
  56. }
  57. MeshLine.prototype.process = function() {
  58. var l = this.positions.length / 6;
  59. this.previous = [];
  60. this.next = [];
  61. this.side = [];
  62. this.width = [];
  63. this.indices_array = [];
  64. this.uvs = [];
  65. for( var j = 0; j < l; j++ ) {
  66. this.side.push( 1 );
  67. this.side.push( -1 );
  68. }
  69. var w;
  70. for( var j = 0; j < l; j++ ) {
  71. if( this.widthCallback ) w = this.widthCallback( j / ( l -1 ) );
  72. else w = 1;
  73. this.width.push( w );
  74. this.width.push( w );
  75. }
  76. for( var j = 0; j < l; j++ ) {
  77. this.uvs.push( j / ( l - 1 ), 0 );
  78. this.uvs.push( j / ( l - 1 ), 1 );
  79. }
  80. var v;
  81. if( this.compareV3( 0, l - 1 ) ){
  82. v = this.copyV3( l - 2 );
  83. } else {
  84. v = this.copyV3( 0 );
  85. }
  86. this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  87. this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  88. for( var j = 0; j < l - 1; j++ ) {
  89. v = this.copyV3( j );
  90. this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  91. this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  92. }
  93. for( var j = 1; j < l; j++ ) {
  94. v = this.copyV3( j );
  95. this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  96. this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  97. }
  98. if( this.compareV3( l - 1, 0 ) ){
  99. v = this.copyV3( 1 );
  100. } else {
  101. v = this.copyV3( l - 1 );
  102. }
  103. this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  104. this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );
  105. for( var j = 0; j < l - 1; j++ ) {
  106. var n = j * 2;
  107. this.indices_array.push( n, n + 1, n + 2 );
  108. this.indices_array.push( n + 2, n + 1, n + 3 );
  109. }
  110. if (!this.attributes) {
  111. this.attributes = {
  112. position: new THREE.BufferAttribute( new Float32Array( this.positions ), 3 ),
  113. previous: new THREE.BufferAttribute( new Float32Array( this.previous ), 3 ),
  114. next: new THREE.BufferAttribute( new Float32Array( this.next ), 3 ),
  115. side: new THREE.BufferAttribute( new Float32Array( this.side ), 1 ),
  116. width: new THREE.BufferAttribute( new Float32Array( this.width ), 1 ),
  117. uv: new THREE.BufferAttribute( new Float32Array( this.uvs ), 2 ),
  118. index: new THREE.BufferAttribute( new Uint16Array( this.indices_array ), 1 ),
  119. counters: new THREE.BufferAttribute( new Float32Array( this.counters ), 1 )
  120. }
  121. } else {
  122. this.attributes.position.copyArray(new Float32Array(this.positions));
  123. this.attributes.position.needsUpdate = true;
  124. this.attributes.previous.copyArray(new Float32Array(this.previous));
  125. this.attributes.previous.needsUpdate = true;
  126. this.attributes.next.copyArray(new Float32Array(this.next));
  127. this.attributes.next.needsUpdate = true;
  128. this.attributes.side.copyArray(new Float32Array(this.side));
  129. this.attributes.side.needsUpdate = true;
  130. this.attributes.width.copyArray(new Float32Array(this.width));
  131. this.attributes.width.needsUpdate = true;
  132. this.attributes.uv.copyArray(new Float32Array(this.uvs));
  133. this.attributes.uv.needsUpdate = true;
  134. this.attributes.index.copyArray(new Uint16Array(this.indices_array));
  135. this.attributes.index.needsUpdate = true;
  136. }
  137. this.geometry.addAttribute( 'position', this.attributes.position );
  138. this.geometry.addAttribute( 'previous', this.attributes.previous );
  139. this.geometry.addAttribute( 'next', this.attributes.next );
  140. this.geometry.addAttribute( 'side', this.attributes.side );
  141. this.geometry.addAttribute( 'width', this.attributes.width );
  142. this.geometry.addAttribute( 'uv', this.attributes.uv );
  143. this.geometry.addAttribute( 'counters', this.attributes.counters );
  144. this.geometry.setIndex( this.attributes.index );
  145. }
  146. function memcpy (src, srcOffset, dst, dstOffset, length) {
  147. var i
  148. src = src.subarray || src.slice ? src : src.buffer
  149. dst = dst.subarray || dst.slice ? dst : dst.buffer
  150. src = srcOffset ? src.subarray ?
  151. src.subarray(srcOffset, length && srcOffset + length) :
  152. src.slice(srcOffset, length && srcOffset + length) : src
  153. if (dst.set) {
  154. dst.set(src, dstOffset)
  155. } else {
  156. for (i=0; i<src.length; i++) {
  157. dst[i + dstOffset] = src[i]
  158. }
  159. }
  160. return dst
  161. }
  162. /**
  163. * Fast method to advance the line by one position. The oldest position is removed.
  164. * @param position
  165. */
  166. MeshLine.prototype.advance = function(position) {
  167. var positions = this.attributes.position.array;
  168. var previous = this.attributes.previous.array;
  169. var next = this.attributes.next.array;
  170. var l = positions.length;
  171. // PREVIOUS
  172. memcpy( positions, 0, previous, 0, l );
  173. // POSITIONS
  174. memcpy( positions, 6, positions, 0, l - 6 );
  175. positions[l - 6] = position.x;
  176. positions[l - 5] = position.y;
  177. positions[l - 4] = position.z;
  178. positions[l - 3] = position.x;
  179. positions[l - 2] = position.y;
  180. positions[l - 1] = position.z;
  181. // NEXT
  182. memcpy( positions, 6, next, 0, l - 6 );
  183. next[l - 6] = position.x;
  184. next[l - 5] = position.y;
  185. next[l - 4] = position.z;
  186. next[l - 3] = position.x;
  187. next[l - 2] = position.y;
  188. next[l - 1] = position.z;
  189. this.attributes.position.needsUpdate = true;
  190. this.attributes.previous.needsUpdate = true;
  191. this.attributes.next.needsUpdate = true;
  192. };
  193. function MeshLineMaterial( parameters ) {
  194. var vertexShaderSource = [
  195. 'precision highp float;',
  196. '',
  197. 'attribute vec3 position;',
  198. 'attribute vec3 previous;',
  199. 'attribute vec3 next;',
  200. 'attribute float side;',
  201. 'attribute float width;',
  202. 'attribute vec2 uv;',
  203. 'attribute float counters;',
  204. '',
  205. 'uniform mat4 projectionMatrix;',
  206. 'uniform mat4 modelViewMatrix;',
  207. 'uniform vec2 resolution;',
  208. 'uniform float lineWidth;',
  209. 'uniform vec3 color;',
  210. 'uniform float opacity;',
  211. 'uniform float near;',
  212. 'uniform float far;',
  213. 'uniform float sizeAttenuation;',
  214. '',
  215. 'varying vec2 vUV;',
  216. 'varying vec4 vColor;',
  217. 'varying float vCounters;',
  218. '',
  219. 'vec2 fix( vec4 i, float aspect ) {',
  220. '',
  221. ' vec2 res = i.xy / i.w;',
  222. ' res.x *= aspect;',
  223. ' vCounters = counters;',
  224. ' return res;',
  225. '',
  226. '}',
  227. '',
  228. 'void main() {',
  229. '',
  230. ' float aspect = resolution.x / resolution.y;',
  231. ' float pixelWidthRatio = 1. / (resolution.x * projectionMatrix[0][0]);',
  232. '',
  233. ' vColor = vec4( color, opacity );',
  234. ' vUV = uv;',
  235. '',
  236. ' mat4 m = projectionMatrix * modelViewMatrix;',
  237. ' vec4 finalPosition = m * vec4( position, 1.0 );',
  238. ' vec4 prevPos = m * vec4( previous, 1.0 );',
  239. ' vec4 nextPos = m * vec4( next, 1.0 );',
  240. '',
  241. ' vec2 currentP = fix( finalPosition, aspect );',
  242. ' vec2 prevP = fix( prevPos, aspect );',
  243. ' vec2 nextP = fix( nextPos, aspect );',
  244. '',
  245. ' float pixelWidth = finalPosition.w * pixelWidthRatio;',
  246. ' float w = 1.8 * pixelWidth * lineWidth * width;',
  247. '',
  248. ' if( sizeAttenuation == 1. ) {',
  249. ' w = 1.8 * lineWidth * width;',
  250. ' }',
  251. '',
  252. ' vec2 dir;',
  253. ' if( nextP == currentP ) dir = normalize( currentP - prevP );',
  254. ' else if( prevP == currentP ) dir = normalize( nextP - currentP );',
  255. ' else {',
  256. ' vec2 dir1 = normalize( currentP - prevP );',
  257. ' vec2 dir2 = normalize( nextP - currentP );',
  258. ' dir = normalize( dir1 + dir2 );',
  259. '',
  260. ' vec2 perp = vec2( -dir1.y, dir1.x );',
  261. ' vec2 miter = vec2( -dir.y, dir.x );',
  262. ' //w = clamp( w / dot( miter, perp ), 0., 4. * lineWidth * width );',
  263. '',
  264. ' }',
  265. '',
  266. ' //vec2 normal = ( cross( vec3( dir, 0. ), vec3( 0., 0., 1. ) ) ).xy;',
  267. ' vec2 normal = vec2( -dir.y, dir.x );',
  268. ' normal.x /= aspect;',
  269. ' normal *= .5 * w;',
  270. '',
  271. ' vec4 offset = vec4( normal * side, 0.0, 1.0 );',
  272. ' finalPosition.xy += offset.xy;',
  273. '',
  274. ' gl_Position = finalPosition;',
  275. '',
  276. '}' ];
  277. var fragmentShaderSource = [
  278. '#extension GL_OES_standard_derivatives : enable',
  279. 'precision mediump float;',
  280. '',
  281. 'uniform sampler2D map;',
  282. 'uniform sampler2D alphaMap;',
  283. 'uniform float useMap;',
  284. 'uniform float useAlphaMap;',
  285. 'uniform float useDash;',
  286. 'uniform float dashArray;',
  287. 'uniform float dashOffset;',
  288. 'uniform float dashRatio;',
  289. 'uniform float visibility;',
  290. 'uniform float alphaTest;',
  291. 'uniform vec2 repeat;',
  292. '',
  293. 'varying vec2 vUV;',
  294. 'varying vec4 vColor;',
  295. 'varying float vCounters;',
  296. '',
  297. 'void main() {',
  298. '',
  299. ' vec4 c = vColor;',
  300. ' if( useMap == 1. ) c *= texture2D( map, vUV * repeat );',
  301. ' if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUV * repeat ).a;',
  302. ' if( c.a < alphaTest ) discard;',
  303. ' if( useDash == 1. ){',
  304. ' c.a *= ceil(mod(vCounters + dashOffset, dashArray) - (dashArray * dashRatio));',
  305. ' }',
  306. ' gl_FragColor = c;',
  307. ' gl_FragColor.a *= step(vCounters, visibility);',
  308. '}' ];
  309. function check( v, d ) {
  310. if( v === undefined ) return d;
  311. return v;
  312. }
  313. THREE.Material.call( this );
  314. parameters = parameters || {};
  315. this.lineWidth = check( parameters.lineWidth, 1 );
  316. this.map = check( parameters.map, null );
  317. this.useMap = check( parameters.useMap, 0 );
  318. this.alphaMap = check( parameters.alphaMap, null );
  319. this.useAlphaMap = check( parameters.useAlphaMap, 0 );
  320. this.color = check( parameters.color, new THREE.Color( 0xffffff ) );
  321. this.opacity = check( parameters.opacity, 1 );
  322. this.resolution = check( parameters.resolution, new THREE.Vector2( 1, 1 ) );
  323. this.sizeAttenuation = check( parameters.sizeAttenuation, 1 );
  324. this.near = check( parameters.near, 1 );
  325. this.far = check( parameters.far, 1 );
  326. this.dashArray = check( parameters.dashArray, 0 );
  327. this.dashOffset = check( parameters.dashOffset, 0 );
  328. this.dashRatio = check( parameters.dashRatio, 0.5 );
  329. this.useDash = ( this.dashArray !== 0 ) ? 1 : 0;
  330. this.visibility = check( parameters.visibility, 1 );
  331. this.alphaTest = check( parameters.alphaTest, 0 );
  332. this.repeat = check( parameters.repeat, new THREE.Vector2( 1, 1 ) );
  333. var material = new THREE.RawShaderMaterial( {
  334. uniforms:{
  335. lineWidth: { type: 'f', value: this.lineWidth },
  336. map: { type: 't', value: this.map },
  337. useMap: { type: 'f', value: this.useMap },
  338. alphaMap: { type: 't', value: this.alphaMap },
  339. useAlphaMap: { type: 'f', value: this.useAlphaMap },
  340. color: { type: 'c', value: this.color },
  341. opacity: { type: 'f', value: this.opacity },
  342. resolution: { type: 'v2', value: this.resolution },
  343. sizeAttenuation: { type: 'f', value: this.sizeAttenuation },
  344. near: { type: 'f', value: this.near },
  345. far: { type: 'f', value: this.far },
  346. dashArray: { type: 'f', value: this.dashArray },
  347. dashOffset: { type: 'f', value: this.dashOffset },
  348. dashRatio: { type: 'f', value: this.dashRatio },
  349. useDash: { type: 'f', value: this.useDash },
  350. visibility: {type: 'f', value: this.visibility},
  351. alphaTest: {type: 'f', value: this.alphaTest},
  352. repeat: { type: 'v2', value: this.repeat }
  353. },
  354. vertexShader: vertexShaderSource.join( '\r\n' ),
  355. fragmentShader: fragmentShaderSource.join( '\r\n' )
  356. });
  357. delete parameters.lineWidth;
  358. delete parameters.map;
  359. delete parameters.useMap;
  360. delete parameters.alphaMap;
  361. delete parameters.useAlphaMap;
  362. delete parameters.color;
  363. delete parameters.opacity;
  364. delete parameters.resolution;
  365. delete parameters.sizeAttenuation;
  366. delete parameters.near;
  367. delete parameters.far;
  368. delete parameters.dashArray;
  369. delete parameters.dashOffset;
  370. delete parameters.dashRatio;
  371. delete parameters.visibility;
  372. delete parameters.alphaTest;
  373. delete parameters.repeat;
  374. material.type = 'MeshLineMaterial';
  375. material.setValues( parameters );
  376. return material;
  377. };
  378. MeshLineMaterial.prototype = Object.create( THREE.Material.prototype );
  379. MeshLineMaterial.prototype.constructor = MeshLineMaterial;
  380. MeshLineMaterial.prototype.copy = function ( source ) {
  381. THREE.Material.prototype.copy.call( this, source );
  382. this.lineWidth = source.lineWidth;
  383. this.map = source.map;
  384. this.useMap = source.useMap;
  385. this.alphaMap = source.alphaMap;
  386. this.useAlphaMap = source.useAlphaMap;
  387. this.color.copy( source.color );
  388. this.opacity = source.opacity;
  389. this.resolution.copy( source.resolution );
  390. this.sizeAttenuation = source.sizeAttenuation;
  391. this.near = source.near;
  392. this.far = source.far;
  393. this.dashArray.copy( source.dashArray );
  394. this.dashOffset.copy( source.dashOffset );
  395. this.dashRatio.copy( source.dashRatio );
  396. this.useDash = source.useDash;
  397. this.visibility = source.visibility;
  398. this.alphaTest = source.alphaTest;
  399. this.repeat.copy( source.repeat );
  400. return this;
  401. };
  402. if( typeof exports !== 'undefined' ) {
  403. if( typeof module !== 'undefined' && module.exports ) {
  404. exports = module.exports = { MeshLine: MeshLine, MeshLineMaterial: MeshLineMaterial };
  405. }
  406. exports.MeshLine = MeshLine;
  407. exports.MeshLineMaterial = MeshLineMaterial;
  408. }
  409. else {
  410. root.MeshLine = MeshLine;
  411. root.MeshLineMaterial = MeshLineMaterial;
  412. }
  413. }).call(this);