├── 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 | 
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 | 
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 |
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 | }
--------------------------------------------------------------------------------