Print - 3D Roller Coaster Space Flight in JavaScript - JavaScriptBank.com

Full version: jsB@nk » Game » 3D Roller Coaster Space Flight in JavaScript
URL: https://www.javascriptbank.com/3d-roller-coaster-space-flight-in-javascript.html

3D Roller Coaster Space Flight in JavaScript © JavaScriptBank.comThis JavaScript code example is awesome, it's riding a roller coaster, or chasing a TIE fighter to the Death Star. This is a pretty complex JavaScript engine, supporting arbitrary camera and object transformations, and simple triangle clipping. The animation uses catmull-rom splines.A very nice simulating JavaScript code to practice more.More 3D JavaScript games to play: - The 3DAT Maker

Full version: jsB@nk » Game » 3D Roller Coaster Space Flight in JavaScript
URL: https://www.javascriptbank.com/3d-roller-coaster-space-flight-in-javascript.html

<!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]--><script src="rollercoaster.js"></script>

<div>      Modes:      <input type="button" value="That's no moon..." onclick="DRAW_LINE=false;DRAW_TRIS=false;DRAW_TIE=true;DRAW_GROUND=false;DRAW_STARFIELD=true;DRAW_BACK_WALL=true;">      <input type="button" value="Whee!" onclick="DRAW_LINE=true;DRAW_TRIS=true;DRAW_TIE=false;DRAW_GROUND=true;DRAW_STARFIELD=false;DRAW_BACK_WALL=false;">      <a href="#" onclick="document.getElementById('debugCrap').style.display='block';document.getElementById('debugCrap2').style.display='inline';">Show advanced controls</a><br/>      <input type="button" value="New track" onclick="go()"/>    </div>    <div id="debugCrap" style="display: none;">      <input type="button" onclick="if (timer) {clearInterval(timer); timer = null;this.value='Start'} else {timer = setInterval(function() {update(false);}, 20);this.value='Stop'}" value="Stop"/>      <input type="button" onclick="update(true);" value="Step"/>      <input type="button" onclick="if(document.getElementById('status')) document.getElementById('status').innerHTML='[CLEAR]\n';" value="Clear debug text"/>      <input type="button" onclick="document.getElementById('debugCrap').style.display='none';document.getElementById('debugCrap2').style.display='none';" value="Hide this debug junk"><br/>      <input type="button" onclick="alpha=!alpha" value="Toggle transparency"/>      <input type="button" onclick="DRAW_LINE=!DRAW_LINE" value="Toggle line"/>      <input type="button" onclick="DRAW_TRIS=!DRAW_TRIS" value="Toggle triangles"/>      <input type="button" onclick="DRAW_TIE=!DRAW_TIE" value="Toggle TIE fighter"/>      <input type="button" onclick="DRAW_GROUND=!DRAW_GROUND" value="Toggle ground"/>      <input type="button" onclick="DRAW_STARFIELD=!DRAW_STARFIELD" value="Toggle stars"/>      <input type="button" onclick="DRAW_BACK_WALL=!DRAW_BACK_WALL" value="Toggle death star"/>      <br/>      <span id="fps"></span>      <br/>      Interpolated points (fewer is faster): <input id="smooth" type="text" value="5" size="2"/><input type="button" value="update" onclick="interpPoints = 1+Math.min(50, Math.max(0, parseInt(document.getElementById('smooth').value))); if (isNaN(interpPoints)) interpPoints = 1; document.getElementById('smooth').value = interpPoints-1"/><br/>      TIE distance: <input id="dist" type="text" value="15" size="2"/><input type="button" value="update" onclick="TIE_DIST = Math.min(100, Math.max(3, parseInt(document.getElementById('dist').value))); if (isNaN(TIE_DIST)) TIE_DIST = 3; document.getElementById('dist').value = TIE_DIST"/><br/>      Scale (affects field of view; interacts with width and height): <input id="scaleval" type="text" value="250" size="2"/><input type="button" value="update" onclick="SCALE = Math.min(5000, Math.max(1, parseInt(document.getElementById('scaleval').value))); if (isNaN(SCALE)) SCALE = 250; document.getElementById('scaleval').value = SCALE"/><br/>      Width: <input id="widthval" type="text" value="800" size="2"/><input type="button" value="update" onclick="WIDTH = Math.min(2000, Math.max(10, parseInt(document.getElementById('widthval').value))); if (isNaN(WIDTH)) WIDTH = 250; document.getElementById('widthval').value = WIDTH;canvas.setAttribute('width', WIDTH);"/>       Height: <input id="heightval" type="text" value="600" size="2"/><input type="button" value="update" onclick="HEIGHT = Math.min(2000, Math.max(10, parseInt(document.getElementById('heightval').value))); if (isNaN(HEIGHT)) HEIGHT = 250; document.getElementById('heightval').value = HEIGHT;canvas.setAttribute('height', HEIGHT);"/><br/>      <input type="button" value="Reset car" onclick="time=-1"/><br/>    </div>    <canvas id="theCanvas" width="800" height="600" style="border: 1px solid blue;"></canvas>    <spn id="debugCrap2" style="display: none;">      <textarea id="status" rows="30" cols="40"></textarea>    </span>      <p>Explanation<br>      <ul><li>See also my <a href="3d.html">z-buffering 3d engine</a>.</li><li>The track is a randomly generate set of points going into the distance</li><li>Catmull-Rom splines are used for interpolation</li><li>Catmull-Rom splines use 4 points to interpolate between 2.  This keeps the track smooth, but it means that we need an extra point before the track starts and one after the end.  That's why you don't go all the way to the last triangle before restarting at the beginning</li><li>I'm not actually Z-buffering here.  With reasonable confidence, I know the order ahead of time (ground is far away, the death star is far away, and the spline is (almost) z-sorted by construction) so I just paint back to front.  This means some things would be more complex to implement</li><li>The engine isn't particularly general-purpose.  Ideally, it would accept a tree of objects, each with their own transformation matricies.  Unfortunately, due to the lack of z-buffering, every type of object has its own requirements for proper painting (for example, the TIE fighter is painted in one of three different ways depending on how you're looking at it).  I may eventually generalize it a bit more and use wireframe drawing....</li><li>The line fades as it gets close to you.  The fade of a segment is determined by the distance to part of that segment rather than the time it'll take to reach that segment.  This means that around sharp turns, it's possible for part of the line that you won't actually reach for a while will be faded.  Since the same logic is used to clip the spline, semgents could potentially disappear too early.</li><li>I'm fairly sure there is still at least one bug - I've noticed occasional flickering of the ground, and occasional oddly-warped triangles.  If I happen to figure out when exactly the issues occur, I might try to fix it</li><li>The diagonal line on the ground arises from anitaliasing.</li><li>Painting the ground is <i>incredibly</i> expensive.  I'm not entirely sure why - maybe I'm still asking canvas to paint a lot of offscreen garbage when I clip the triangles or something.</li><li>Canvas apparently uses 16-bit coordinates.  This causes ugliness if pixel positions get too big.  <b>It may also cause your browser (or, on *nix, your X server) to gobble HUGE amounts of RAM sometimes.</b>  Don't set the scale to be too big.</li>      </ul>    </p>