├── LICENSE ├── README.md ├── demo.gif ├── index.html └── js ├── WebGL.js ├── three.min.js └── trails.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 svartmc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trails 2 | 3 | ![trails gif](demo.gif) 4 | 5 | Simple geometrical trail on ( x, z ) axis to attach to your Three.js objects. 6 | 7 | Create the trail: 8 | ``` 9 | createTrail( object, length, width, resolution ); 10 | ``` 11 | Update the trail calling in your animation loop: 12 | ``` 13 | updateTrails(); 14 | ``` 15 | Link to demo, note that you’ll need a web browser which support WebGL in order to see it. 16 |
17 |
18 | ![twitter icon](https://img.icons8.com/color/48/000000/twitter-squared.png) 19 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svartmc/trails/906bc549afa0f852c56594b12b1dec98ae7aef50/demo.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TRAILS 5 | 6 | 7 | 8 | 9 | 10 | 77 | 78 | 79 | 80 |
Infos 81 |
82 | 83 | 84 | 85 | 86 | 87 | 278 | 279 | -------------------------------------------------------------------------------- /js/WebGL.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @author mrdoob / http://mrdoob.com/ 4 | */ 5 | 6 | THREE.WEBGL = { 7 | 8 | isWebGLAvailable: function () { 9 | 10 | try { 11 | 12 | var canvas = document.createElement( 'canvas' ); 13 | return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) ); 14 | 15 | } catch ( e ) { 16 | 17 | return false; 18 | 19 | } 20 | 21 | }, 22 | 23 | isWebGL2Available: function () { 24 | 25 | try { 26 | 27 | var canvas = document.createElement( 'canvas' ); 28 | return !! ( window.WebGL2RenderingContext && canvas.getContext( 'webgl2' ) ); 29 | 30 | } catch ( e ) { 31 | 32 | return false; 33 | 34 | } 35 | 36 | }, 37 | 38 | getWebGLErrorMessage: function () { 39 | 40 | return this.getErrorMessage( 1 ); 41 | 42 | }, 43 | 44 | getWebGL2ErrorMessage: function () { 45 | 46 | return this.getErrorMessage( 2 ); 47 | 48 | }, 49 | 50 | getErrorMessage: function ( version ) { 51 | 52 | var names = { 53 | 1: 'WebGL', 54 | 2: 'WebGL 2' 55 | }; 56 | 57 | var contexts = { 58 | 1: window.WebGLRenderingContext, 59 | 2: window.WebGL2RenderingContext 60 | }; 61 | 62 | var message = 'Your $0 does not seem to support $1'; 63 | 64 | var element = document.createElement( 'div' ); 65 | element.id = 'webglmessage'; 66 | element.style.fontFamily = 'monospace'; 67 | element.style.fontSize = '13px'; 68 | element.style.fontWeight = 'normal'; 69 | element.style.textAlign = 'center'; 70 | element.style.background = '#fff'; 71 | element.style.color = '#000'; 72 | element.style.padding = '1.5em'; 73 | element.style.width = '400px'; 74 | element.style.margin = '5em auto 0'; 75 | 76 | if ( contexts[ version ] ) { 77 | 78 | message = message.replace( '$0', 'graphics card' ); 79 | 80 | } else { 81 | 82 | message = message.replace( '$0', 'browser' ); 83 | 84 | } 85 | 86 | message = message.replace( '$1', names[ version ] ); 87 | 88 | element.innerHTML = message; 89 | 90 | return element; 91 | 92 | } 93 | 94 | }; 95 | -------------------------------------------------------------------------------- /js/trails.js: -------------------------------------------------------------------------------- 1 | var trails = { 2 | 3 | haveTrails: [], 4 | 5 | } 6 | 7 | function createTrail( object, length, width, resolution ) { 8 | 9 | // resolution must be less than the length 10 | 11 | if ( resolution > length ) { 12 | 13 | resolution = length; 14 | 15 | } 16 | 17 | object.userData.trail = { 18 | 19 | length: Math.round( length ), 20 | width: width, 21 | resolution: Math.round( resolution ), 22 | trailHistory: [], 23 | trailVertices: [], 24 | worldDirection: new THREE.Vector3(), 25 | 26 | } 27 | 28 | // trail geo 29 | 30 | var geometry = new THREE.PlaneGeometry( 1, length, 1, resolution ); 31 | 32 | var material = new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.DoubleSide, wireframe: false, transparent: true, opacity: 0.2 } ); // opacity: 0.2 33 | object.userData.trail.mesh = new THREE.Mesh( geometry, material ); 34 | 35 | scene.add( object.userData.trail.mesh ); 36 | 37 | trails.haveTrails.push( object ); 38 | 39 | // setting frustumCulled to false is important because we move the vertices outside the frustum, not the geometry itself 40 | 41 | object.userData.trail.mesh.frustumCulled = false; 42 | 43 | // create history and store vertices 44 | 45 | object.userData.trail.trailHistory = []; 46 | 47 | object.userData.trail.trailVertices = []; 48 | 49 | for ( var i = 0; i < resolution + 1; i++ ) { 50 | 51 | object.userData.trail.trailVertices[i] = []; 52 | 53 | } 54 | 55 | // store vertices based on left or right 56 | 57 | for ( var i = 0; i < object.userData.trail.trailVertices.length; i ++ ) { 58 | 59 | object.userData.trail.trailVertices[i][0] = object.userData.trail.mesh.geometry.vertices[i*2]; 60 | object.userData.trail.trailVertices[i][1] = object.userData.trail.mesh.geometry.vertices[i*2+1]; 61 | 62 | } 63 | 64 | } 65 | 66 | function updateTrailHistory( object ) { 67 | 68 | object.getWorldDirection( object.userData.trail.worldDirection ); 69 | 70 | object.userData.trail.trailHistory.push( [ object.position.x, object.position.y, object.position.z, object.userData.trail.worldDirection.x, object.userData.trail.worldDirection.z ] ); 71 | 72 | if ( object.userData.trail.trailHistory.length > object.userData.trail.length ) { 73 | 74 | object.userData.trail.trailHistory.shift(); 75 | 76 | } 77 | 78 | } 79 | 80 | function updateTrails() { 81 | 82 | for ( var i = 0; i < trails.haveTrails.length; i++ ) { 83 | 84 | var object = trails.haveTrails[i]; 85 | var trail = object.userData.trail; 86 | 87 | updateTrailHistory( object ); 88 | 89 | for ( var j = 0; j < trail.trailVertices.length; j++ ) { 90 | 91 | var index = Math.round( trail.trailHistory.length / trail.resolution * j ); 92 | 93 | if ( index === trail.trailHistory.length ) { 94 | 95 | index = trail.trailHistory.length - 1; 96 | 97 | } 98 | 99 | var pos = trail.trailHistory[index]; 100 | 101 | // custom the shape changing this width parameter 102 | 103 | var width = THREE.Math.mapLinear( j, 0, trail.trailVertices.length, 0, 1 ) * trail.width / 2; 104 | 105 | if ( typeof pos != "undefined" ) { 106 | 107 | // update vertices using a "2D cross product" 108 | // one side of the trail, left or right 109 | 110 | trail.trailVertices[j][0].x = pos[0] - pos[4] * width; 111 | trail.trailVertices[j][0].y = pos[1]; 112 | trail.trailVertices[j][0].z = pos[2] + pos[3] * width; 113 | 114 | // the other side of the trail 115 | 116 | trail.trailVertices[j][1].x = pos[0] + pos[4] * width; 117 | trail.trailVertices[j][1].y = pos[1]; 118 | trail.trailVertices[j][1].z = pos[2] - pos[3] * width; 119 | 120 | } 121 | 122 | } 123 | 124 | trail.mesh.geometry.verticesNeedUpdate = true; 125 | 126 | } 127 | 128 | } 129 | 130 | function resetTrail( object ) { 131 | 132 | object.userData.trail.trailHistory = []; 133 | 134 | } --------------------------------------------------------------------------------