Diep.IO 3D

Turns diep.io into real 3D

目前為 2021-09-23 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Diep.IO 3D
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.1
  5. // @description Turns diep.io into real 3D
  6. // @author Zertalious (Zert)
  7. // @match *://diep.io/*
  8. // @icon https://www.google.com/s2/favicons?domain=diep.io
  9. // @grant none
  10. // @require https://unpkg.com/three@latest/build/three.min.js
  11. // @require https://unpkg.com/three@latest/examples/js/controls/OrbitControls.js
  12. // ==/UserScript==
  13.  
  14. const OUTLINE_LAYER = 0;
  15. const MAIN_LAYER = 1;
  16.  
  17. let renderer, scene, camera, canvas;
  18.  
  19. init();
  20.  
  21. const tempObject = new THREE.Object3D();
  22. const tempColor = new THREE.Color();
  23.  
  24. const material = new THREE.MeshToonMaterial( { transparent: true } );
  25. const outlineMaterial = new THREE.MeshBasicMaterial( { transparent: true } );
  26.  
  27. material.onBeforeCompile = outlineMaterial.onBeforeCompile = function ( shader ) {
  28.  
  29. shader.vertexShader = shader.vertexShader.replace( 'void', `
  30.  
  31. attribute vec2 scale;
  32. attribute float alpha;
  33.  
  34. varying float vAlpha;
  35.  
  36. void` ).replace( '<begin_vertex>', `<begin_vertex>
  37.  
  38. if ( scale.x != 0.0 && scale.y != 0.0 ) {
  39.  
  40. if ( transformed.x == 1.0 || transformed.x == 0.5 ) {
  41.  
  42. transformed.yz *= scale.x;
  43.  
  44. } else if ( transformed.x == - 1.0 || transformed.x == - 0.5 ) {
  45.  
  46. transformed.yz *= scale.y;
  47.  
  48. }
  49.  
  50. }
  51.  
  52. vAlpha = alpha;
  53.  
  54. ` );
  55.  
  56. shader.fragmentShader = shader.fragmentShader.replace( 'void', `
  57.  
  58. varying float vAlpha;
  59.  
  60. void` ).replace( '}', `
  61.  
  62. gl_FragColor.a *= vAlpha;
  63.  
  64. }` );
  65.  
  66. }
  67.  
  68. const instances = {};
  69.  
  70. const array = [ {
  71. name: 'sphere',
  72. geometry: new THREE.SphereGeometry( 1, 16 ),
  73. count: 150
  74. }, {
  75. name: 'cylinder',
  76. geometry: new THREE.CylinderGeometry( 0.5, 0.5, 1, 16 ).rotateZ( Math.PI / 2 ),
  77. count: 75,
  78. hasScaling: true
  79. }, {
  80. name: 'poly3',
  81. geometry: new THREE.CylinderGeometry( 1, 1, 1, 3, 1, false, - Math.PI / 6 ).rotateX( Math.PI / 2 ),
  82. count: 75
  83. }, {
  84. name: 'poly4',
  85. geometry: new THREE.BoxGeometry( 1, 1, 1 ),
  86. count: 75
  87. }, {
  88. name: 'poly5',
  89. geometry: new THREE.CylinderGeometry( 1, 1, 1, 5, 1, false, Math.PI / 10 ).rotateX( Math.PI / 2 ),
  90. count: 40
  91. }, {
  92. name: 'poly6',
  93. geometry: new THREE.CylinderGeometry( 1, 1, 1, 6, 1, false, - Math.PI / 12 ).rotateX( Math.PI / 2 ),
  94. count: 10
  95. } ];
  96.  
  97. for ( let i = 0; i < array.length; i ++ ) {
  98.  
  99. const { name, geometry, count, hasScaling } = array[ i ];
  100.  
  101. if ( hasScaling ) {
  102.  
  103. geometry.setAttribute( 'scale', new THREE.InstancedBufferAttribute( new Float32Array( count * 2 ), 2 ) );
  104.  
  105. }
  106.  
  107. geometry.setAttribute( 'alpha', new THREE.InstancedBufferAttribute( new Float32Array( count ), 1 ) );
  108.  
  109. const main = new THREE.InstancedMesh( geometry, material, count );
  110. main.layers.set( MAIN_LAYER );
  111. scene.add( main );
  112.  
  113. const outline = new THREE.InstancedMesh( geometry, outlineMaterial, count );
  114. outline.layers.set( OUTLINE_LAYER );
  115. scene.add( outline );
  116.  
  117. main.setColorAt( 0, tempColor );
  118. outline.setColorAt( 0, tempColor );
  119.  
  120. instances[ name ] = {
  121. main,
  122. outline,
  123. count,
  124. hasScaling,
  125. index: 0
  126. };
  127.  
  128. }
  129.  
  130. const stack = [];
  131.  
  132. function getStack( index ) {
  133.  
  134. const result = stack[ stack.length - 1 - index ];
  135.  
  136. if ( result ) {
  137.  
  138. return result;
  139.  
  140. }
  141.  
  142. return { name: 'none' };
  143.  
  144. }
  145.  
  146. function setObject( name, x, y, z, sx, sy, sz, angle, color, alpha = 1, scaleX = 1, scaleY = 1 ) {
  147.  
  148. tempObject.position.set( x, y, z );
  149. tempObject.scale.set( sx, sy, sz );
  150. tempObject.rotation.set( 0, 0, angle );
  151.  
  152. tempObject.updateMatrix();
  153.  
  154. tempColor.set( color );
  155.  
  156. const instance = instances[ name ];
  157.  
  158. instance.main.setMatrixAt( instance.index, tempObject.matrix );
  159. instance.main.setColorAt( instance.index, tempColor );
  160.  
  161. instance.main.geometry.attributes.alpha.setX( instance.index, alpha );
  162. instance.outline.geometry.attributes.alpha.setX( instance.index, alpha );
  163.  
  164. const outlineSize = 4 / window.innerHeight * ( name === 'sphere' ? 0.7 : 1 );
  165.  
  166. if ( instance.hasScaling ) {
  167.  
  168. tempObject.scale.x += outlineSize;
  169. tempObject.scale.y += outlineSize / scaleY;
  170. tempObject.scale.z += outlineSize / scaleY;
  171.  
  172. } else {
  173.  
  174. tempObject.scale.addScalar( outlineSize );
  175.  
  176. }
  177.  
  178. tempObject.updateMatrix();
  179.  
  180. tempColor.multiplyScalar( 0.6 );
  181.  
  182. instance.outline.setMatrixAt( instance.index, tempObject.matrix );
  183. instance.outline.setColorAt( instance.index, tempColor );
  184.  
  185. if ( instance.hasScaling ) {
  186.  
  187. instance.main.geometry.attributes.scale.setXY( instance.index, scaleX, scaleY );
  188. instance.outline.geometry.attributes.scale.setXY( instance.index, scaleX, scaleY );
  189.  
  190. }
  191.  
  192. instance.index ++;
  193.  
  194. stack.push( { name, x, y, z, sx, sy, sz, angle, color, outlineSize, alpha } );
  195.  
  196. }
  197.  
  198. function init() {
  199.  
  200. renderer = new THREE.WebGLRenderer( {
  201. antialias: true,
  202. alpha: true
  203. } );
  204.  
  205. renderer.autoClear = false;
  206.  
  207. renderer.setPixelRatio( window.devicePixelRatio );
  208. renderer.setSize( window.innerWidth, window.innerHeight );
  209.  
  210. renderer.domElement.style.position = 'absolute';
  211. renderer.domElement.style.left = '0';
  212. renderer.domElement.style.top = '0';
  213. renderer.domElement.style.pointerEvents = 'none';
  214.  
  215. canvas = document.getElementById( 'canvas' );
  216.  
  217. canvas.parentNode.insertBefore( renderer.domElement, canvas.nextSibling );
  218.  
  219. scene = new THREE.Scene();
  220.  
  221. camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000 );
  222.  
  223. const oldZ = Math.sin( Math.PI / 3 );
  224. camera.position.z = oldZ;
  225.  
  226. const ambLight = new THREE.AmbientLight( 0xffffff, 0.5 );
  227. ambLight.layers.set( MAIN_LAYER );
  228. scene.add( ambLight );
  229.  
  230. const dirLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
  231. dirLight.layers.set( MAIN_LAYER );
  232. dirLight.position.z = 1;
  233. scene.add( dirLight );
  234.  
  235. const controls = new THREE.OrbitControls( camera, canvas );
  236.  
  237. controls.enabled = false;
  238.  
  239. window.addEventListener( 'keyup', function ( event ) {
  240.  
  241. if ( String.fromCharCode( event.keyCode ) === 'V' ) {
  242.  
  243. controls.enabled = ! controls.enabled;
  244.  
  245. if ( ! controls.enabled ) {
  246.  
  247. camera.position.set( 0, 0, oldZ );
  248. camera.rotation.set( 0, 0, 0 );
  249.  
  250. }
  251.  
  252. }
  253.  
  254. } );
  255.  
  256. window.addEventListener( 'resize', onWindowResize );
  257.  
  258. }
  259.  
  260. function onWindowResize() {
  261.  
  262. renderer.setSize( window.innerWidth, window.innerHeight );
  263. camera.aspect = window.innerWidth / window.innerHeight;
  264. camera.updateProjectionMatrix();
  265.  
  266. }
  267.  
  268. window.requestAnimationFrame = new Proxy( window.requestAnimationFrame, {
  269. apply( target, thisArgs, args ) {
  270.  
  271. args[ 0 ] = new Proxy( args[ 0 ], {
  272. apply( target, thisArgs, args ) {
  273.  
  274. stack.length = 0;
  275.  
  276. tempObject.position.setScalar( 0 );
  277. tempObject.scale.setScalar( 0 );
  278. tempObject.rotation.set( 0, 0, 0 );
  279.  
  280. tempObject.updateMatrix();
  281.  
  282. tempColor.setRGB( 0, 0, 0 );
  283.  
  284. for ( let key in instances ) {
  285.  
  286. const { main, outline, count, hasScaling } = instances[ key ];
  287.  
  288. for ( let i = 0; i < count; i ++ ) {
  289.  
  290. main.setMatrixAt( i, tempObject.matrix );
  291. outline.setMatrixAt( i, tempObject.matrix );
  292.  
  293. }
  294.  
  295. main.instanceMatrix.needsUpdate = true;
  296. main.instanceColor.needsUpdate = true;
  297.  
  298. outline.instanceMatrix.needsUpdate = true;
  299. outline.instanceColor.needsUpdate = true;
  300.  
  301. if ( hasScaling ) {
  302.  
  303. main.geometry.attributes.scale.needsUpdate = true;
  304. outline.geometry.attributes.scale.needsUpdate = true;
  305.  
  306. }
  307.  
  308. main.geometry.attributes.alpha.needsUpdate = true;
  309. outline.geometry.attributes.alpha.needsUpdate = true;
  310.  
  311. instances[ key ].index = 0;
  312.  
  313. }
  314.  
  315. arcCounter = 0;
  316.  
  317. Reflect.apply( ...arguments );
  318.  
  319. renderer.clear();
  320.  
  321. camera.layers.set( OUTLINE_LAYER );
  322.  
  323. renderer.render( scene, camera );
  324.  
  325. renderer.clearDepth();
  326.  
  327. camera.layers.set( MAIN_LAYER );
  328.  
  329. renderer.render( scene, camera );
  330.  
  331. }
  332. } );
  333.  
  334. return Reflect.apply( ...arguments );
  335.  
  336. }
  337. } );
  338.  
  339. const Context2D = CanvasRenderingContext2D.prototype;
  340.  
  341. let arcCounter = 0;
  342.  
  343. Context2D.arc = new Proxy( Context2D.arc, {
  344. apply( target, thisArgs, args ) {
  345.  
  346. if ( args[ 4 ] === Math.PI * 2 ) {
  347.  
  348. if ( arcCounter === 0 ) {
  349.  
  350. const matrix = thisArgs.getTransform();
  351.  
  352. const r = matrix.a / canvas.height;
  353.  
  354. const x = ( matrix.e / window.innerWidth - 0.5 ) * camera.aspect;
  355. const y = 0.5 - matrix.f / window.innerHeight;
  356.  
  357. let z = 0;
  358.  
  359. const s0 = getStack( 0 );
  360. const s1 = getStack( 1 );
  361.  
  362. if ( s0.name === 'cylinder' && s1.name === 'sphere' && Math.hypot( x - s1.x, y - s1.y ) < 0.001 ) {
  363.  
  364. z = s1.sz;
  365.  
  366. const index = ( instances.cylinder.index - 1 ) * 16 + 14;
  367.  
  368. const newDepth = z + r - s0.sz / 2;
  369.  
  370. instances.cylinder.main.instanceMatrix.array[ index ] = newDepth;
  371. instances.cylinder.outline.instanceMatrix.array[ index ] = newDepth;
  372.  
  373. } else myBlock: {
  374.  
  375. for ( let i = 0; i < 5; i ++ ) {
  376.  
  377. if ( getStack( i ).name !== 'cylinder' ) {
  378.  
  379. break myBlock;
  380.  
  381. }
  382.  
  383. }
  384.  
  385. if ( getStack( 0 ).angle !== getStack( 2 ).angle ) {
  386.  
  387. break myBlock;
  388.  
  389. }
  390.  
  391. const a = r - getStack( 0 ).sy;
  392.  
  393. for ( let i = 0; i < 5; i ++ ) {
  394.  
  395. const index = ( instances.cylinder.index - 1 - i ) * 16 + 14;
  396.  
  397. const newDepth = a - a * 2 * i / 4;
  398.  
  399. instances.cylinder.main.instanceMatrix.array[ index ] = newDepth;
  400. instances.cylinder.outline.instanceMatrix.array[ index ] = newDepth;
  401.  
  402. }
  403.  
  404. }
  405.  
  406. checkIfIsMainCanvas( thisArgs, 'sphere' );
  407.  
  408. setObject(
  409. 'sphere',
  410. x,
  411. y,
  412. z,
  413. r,
  414. r,
  415. r,
  416. 0,
  417. thisArgs.fillStyle,
  418. thisArgs.globalAlpha
  419. );
  420.  
  421. } else if ( arcCounter === 1 ) {
  422.  
  423. tempColor.set( thisArgs.fillStyle );
  424. instances.sphere.main.setColorAt( instances.sphere.index - 1, tempColor );
  425.  
  426. tempColor.multiplyScalar( 0.6 );
  427. instances.sphere.outline.setColorAt( instances.sphere.index - 1, tempColor );
  428.  
  429. }
  430.  
  431. arcCounter = ( arcCounter + 1 ) % 3;
  432.  
  433. }
  434.  
  435. return Reflect.apply( ...arguments );
  436.  
  437. }
  438. } );
  439.  
  440. Context2D.rect = new Proxy( Context2D.rect, {
  441. apply( target, thisArgs, args ) {
  442.  
  443. const matrix = thisArgs.getTransform();
  444.  
  445. if ( matrix.b !== 0 && matrix.c !== 0 ) {
  446.  
  447. const center = new DOMPoint( 0.5, 0.5 ).matrixTransform( matrix );
  448.  
  449. const scaleYZ = Math.hypot( matrix.c, matrix.d ) / canvas.height;
  450.  
  451. checkIfIsMainCanvas( thisArgs, 'cylinder' );
  452.  
  453. setObject(
  454. 'cylinder',
  455. ( center.x / canvas.width - 0.5 ) * camera.aspect,
  456. 0.5 - center.y / canvas.height,
  457. 0,
  458. Math.hypot( matrix.a, matrix.b ) / canvas.height,
  459. scaleYZ,
  460. scaleYZ,
  461. Math.atan2( matrix.c, matrix.d ),
  462. thisArgs.fillStyle,
  463. thisArgs.globalAlpha
  464. );
  465.  
  466. }
  467.  
  468. return Reflect.apply( ...arguments );
  469.  
  470. }
  471. } );
  472.  
  473. const points = [];
  474. let hasCurve = true;
  475.  
  476. Context2D.beginPath = new Proxy( Context2D.beginPath, {
  477. apply( target, thisArgs, args ) {
  478.  
  479. points.length = 0;
  480. hasCurve = false;
  481.  
  482. return Reflect.apply( ...arguments );
  483.  
  484. }
  485. } );
  486.  
  487. const addPoint = {
  488. apply( target, thisArgs, [ x, y ] ) {
  489.  
  490. points.push( new DOMPoint( x, y ).matrixTransform( thisArgs.getTransform() ) );
  491.  
  492. return Reflect.apply( ...arguments );
  493.  
  494. }
  495. };
  496.  
  497. Context2D.moveTo = new Proxy( Context2D.moveTo, addPoint );
  498. Context2D.lineTo = new Proxy( Context2D.lineTo, addPoint );
  499.  
  500. Context2D.arc = new Proxy( Context2D.arc, {
  501. apply( target, thisArgs, args ) {
  502.  
  503. hasCurve = true;
  504.  
  505. return Reflect.apply( ...arguments );
  506.  
  507. }
  508. } );
  509.  
  510. Context2D.fill = new Proxy( Context2D.fill, {
  511. apply( target, thisArgs, args ) {
  512.  
  513. if ( ! hasCurve ) {
  514.  
  515. let shouldCreate = true;
  516.  
  517. if ( points.length === 6 ) {
  518.  
  519. const [ a, b, c ] = points;
  520.  
  521. shouldCreate = Math.abs( Math.hypot( a.x - b.x, a.y - b.y ) - Math.hypot( b.x - c.x, b.y - c.y ) ) < 0.01;
  522.  
  523. }
  524.  
  525. if ( points.length > 2 && points.length < 7 && shouldCreate ) {
  526.  
  527. const center = { x: 0, y: 0 };
  528.  
  529. const count = points.length;
  530.  
  531. for ( let i = 0; i < count; i ++ ) {
  532.  
  533. center.x += points[ i ].x;
  534. center.y += points[ i ].y;
  535.  
  536. }
  537.  
  538. center.x /= count;
  539. center.y /= count;
  540.  
  541. let s, sx, angle, scaleX, scaleY;
  542.  
  543. let name = 'poly' + points.length;
  544.  
  545. if ( points.length === 4 ) {
  546.  
  547. const [ p0, p1, p2 ] = points;
  548. const pl = points[ points.length - 1 ];
  549.  
  550. scaleX = Math.hypot( p1.x - p2.x, p1.y - p2.y ) / canvas.height;
  551. scaleY = Math.hypot( p0.x - pl.x, p0.y - pl.y ) / canvas.height;
  552.  
  553. const dx = ( p1.x + p2.x ) / 2 - ( p0.x + pl.x ) / 2;
  554. const dy = ( p1.y + p2.y ) / 2 - ( p0.y + pl.y ) / 2;
  555.  
  556. sx = Math.hypot( dx, dy ) / canvas.height;
  557. angle = Math.atan2( dx, dy ) - Math.PI / 2;
  558.  
  559. if ( Math.abs( scaleX - scaleY ) > 0.001 ) {
  560.  
  561. s = 1;
  562. name = 'cylinder';
  563.  
  564. } else {
  565.  
  566. s = sx = scaleY;
  567.  
  568. }
  569.  
  570. } else {
  571.  
  572. s = sx = Math.hypot( points[ 0 ].x - center.x, points[ 0 ].y - center.y ) / canvas.height;
  573.  
  574. angle = - Math.atan2( points[ 0 ].y - center.y, points[ 0 ].x - center.x );
  575.  
  576. }
  577.  
  578. checkIfIsMainCanvas( thisArgs, name );
  579.  
  580. setObject(
  581. name,
  582. ( center.x / canvas.width - 0.5 ) * camera.aspect,
  583. 0.5 - center.y / canvas.height,
  584. 0,
  585. sx,
  586. s,
  587. s,
  588. angle,
  589. thisArgs.fillStyle,
  590. thisArgs.globalAlpha,
  591. scaleX,
  592. scaleY
  593. );
  594.  
  595. }
  596.  
  597. }
  598.  
  599. return Reflect.apply( ...arguments );
  600.  
  601. }
  602. } );
  603.  
  604. Context2D.drawImage = new Proxy( Context2D.drawImage, {
  605. apply( target, thisArgs, args ) {
  606.  
  607. if ( thisArgs.canvas === canvas && args[ 0 ].objects ) {
  608.  
  609. const matrix = thisArgs.getTransform();
  610.  
  611. const x = matrix.e / canvas.width;
  612. const y = matrix.f / canvas.height;
  613.  
  614. const sx = Math.hypot( matrix.a, matrix.b );
  615. const sy = Math.hypot( matrix.c, matrix.d );
  616.  
  617. for ( let i = 0; i < args[ 0 ].objects.length; i ++ ) {
  618.  
  619. const { name, index } = args[ 0 ].objects[ i ];
  620.  
  621. const instance = instances[ name ];
  622.  
  623. const ma = instance.main.instanceMatrix.array;
  624. const oa = instance.outline.instanceMatrix.array;
  625.  
  626. const idx = index * 16;
  627.  
  628. const ox = ma[ idx + 12 ] / camera.aspect + 0.5;
  629. const oy = - ma[ idx + 13 ] + 0.5;
  630.  
  631. const outlineOldSx = Math.hypot( oa[ idx + 0 ], oa[ idx + 1 ] );
  632. const outlineOldSy = Math.hypot( oa[ idx + 4 ], oa[ idx + 5 ] );
  633.  
  634. const outlineSizeX = outlineOldSx - Math.hypot( ma[ idx + 0 ], ma[ idx + 1 ] );
  635. const outlineSizeY = outlineOldSy - Math.hypot( ma[ idx + 4 ], ma[ idx + 5 ] );
  636.  
  637. ma[ idx + 0 ] *= sx;
  638. ma[ idx + 1 ] *= sx;
  639. ma[ idx + 4 ] *= sy;
  640. ma[ idx + 5 ] *= sy;
  641. ma[ idx + 10 ] *= sy;
  642.  
  643. const nsx = Math.hypot( ma[ idx + 0 ], ma[ idx + 1 ] ) + outlineSizeX;
  644. const nsy = Math.hypot( ma[ idx + 4 ], ma[ idx + 5 ] ) + outlineSizeY;
  645.  
  646. oa[ idx + 0 ] *= nsx / outlineOldSx;
  647. oa[ idx + 1 ] *= nsx / outlineOldSx;
  648. oa[ idx + 4 ] *= nsy / outlineOldSy;
  649. oa[ idx + 5 ] *= nsy / outlineOldSy;
  650. oa[ idx + 10 ] *= sy;
  651.  
  652. ma[ idx + 12 ] = oa[ idx + 12 ] = ( ( ox * sx + x ) - 0.5 ) * camera.aspect;
  653. ma[ idx + 13 ] = oa[ idx + 13 ] = 0.5 - ( oy * sy + y );
  654.  
  655. instance.main.geometry.attributes.alpha.array[ index ] = thisArgs.globalAlpha;
  656. instance.outline.geometry.attributes.alpha.array[ index ] = thisArgs.globalAlpha;
  657.  
  658. }
  659.  
  660. delete args[ 0 ][ 'objects' ];
  661.  
  662. }
  663.  
  664. return Reflect.apply( ...arguments );
  665.  
  666. }
  667. } );
  668.  
  669. function checkIfIsMainCanvas( ctx, name ) {
  670.  
  671. if ( ctx.canvas !== canvas ) {
  672.  
  673. const { index } = instances[ name ];
  674.  
  675. if ( ctx.canvas.objects ) {
  676.  
  677. ctx.canvas.objects.push( { name, index } );
  678.  
  679. } else {
  680.  
  681. ctx.canvas.objects = [ { name, index } ];
  682.  
  683. }
  684.  
  685. }
  686.  
  687. }