Forked from CorvusCorax/ISS-SIM-Customizer.user.js
Last active
May 5, 2021 14:17
A Greasemonkey user script for SpaceX ISS SIM - useful to enable hard mode (no HUD) - optionally disable collissions, realistic orbital mechanics and other fun stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name SpaceX ISS-SIM-Customizer | |
// @version 0.000015 | |
// @author CorvusCorax | |
// @match https://iss-sim.spacex.com/ | |
// @description A Greasemonkey user script for SpaceX ISS SIM - useful to enable hard mode (no HUD) - optionally disable collissions, realistic orbital mechanics and other fun stuff | |
// @include https://iss-sim.spacex.com/ | |
// @license Creative Commons - CC BY 4.0 | |
// ==/UserScript== | |
// add menu to configure iss-sim to settings DOM tree | |
var JQ=document.querySelector.bind(document) | |
JQ("#settings .modal-inner").innerHTML+='<div id="setting-tesla" class="setting active">TESLA:<br><span>HIDDEN EASTEREGG</span></div><div id="setting-space" class="setting active">SPACE:<br><span>RESTRICTED</span></div><div id="setting-hud" class="setting active">HUD:<br><span>ONLINE</span></div><div id="setting-limits" class="setting active">DOCKING CONSTRAINTS:<br><span>ORIGINAL SIM +-0.2deg</span></div><div id="setting-collision" class="setting active">COLLISIONS:<br><span>ENABLED</span></div><div id="setting-dock" class="setting active">ISS DOCKING PORT:<br><span>IDA-2</span></div><div id="setting-challenge" class="setting active">CHALLENGE:<br><span>DOCK</span></div>' | |
JQ("#hud").innerHTML+='<div id="rendezvous" class="hud-item" style="position: absolute; top: 15%; left: 15%; width: 110px; margin: 0; text-align: center; text-shadow: 0 0 10px #000; display: none;"><div class="label">RNDVZ</div><div id="rendezvous-phase" class="rate"></div><div id="rendezvous-time" class="rate"></div><div id="rendezvous-deltav" class="rate"></div><div id="rendezvous-deltat" class="rate"></div></div><div id="orbit" class="hud-item" style="position: absolute; top: 15%; right: 15%; width: 90px; margin: 0; text-align: center; text-shadow: 0 0 10px #000; display: none;"><div class="label">ORBIT</div><div id="orbit-vel" class="rate"></div><div id="orbit-alt" class="rate"></div><div id="orbit-apogee" class="rate"></div><div id="orbit-perigee" class="rate"></div><div id="orbit-period" class="rate"></div></div>' | |
JQ("#x-range .distance").style.width="100px" | |
JQ("#y-range .distance").style.width="100px" | |
JQ("#z-range .distance").style.width="100px" | |
{ | |
var d=document.createElement('DIV') | |
d.id="propellant" | |
d.class="hud-item" | |
d.style="position: absolute; top: 15%; left: 5%; width: 100px; margin: 0; text-align: left; text-shadow: 0 0 12px #000; display: none; " | |
d.innerHTML='<div class="label">DELTA-V BUDGET</div><div id="prop-used" class="rate"></div><div id="prop-left" class="rate"></div><div class="label">MAIN BURN (M)</div><div id="burn-onoff" class="rate">OFF</div><div class="label">STATS</div><div id="stats-mission" class="rate"></div><div id="stats-real" class="rate"></div><div id="stats-fps" class="rate"></div>' | |
JQ("#hud").after(d) | |
} | |
// The main functionality override must be loaded AFTER ISS-SIM has finished loading. | |
function GMInitializer() { | |
// allow realistic orbital mechanics | |
window.GMisOrbitalMechanics = 1 | |
// override gravity toggler function | |
// Bug: Tampermonkey extension for firefox does not support overriding functions already bound to event listeners | |
// Fix: Unbind event listener, bind our own instead | |
$("#setting-gravity").removeEventListener("click", toggleGravity, !1) | |
$("#setting-gravity").removeEventListener("touchstart", toggleGravity, !1) | |
var GMAmbientLight = new THREE.AmbientLight(0xffffff, 0.1); | |
toggleGravity = function() { | |
if (!GMisChallenge || GMisTrajectoryVisible) return // cannot switch of gravity when trying rendezvous manouver or showing trajectory | |
GMisOrbitalMechanics = GMisOrbitalMechanics<3 ? GMisOrbitalMechanics+1 : 1 | |
switch (GMisOrbitalMechanics) { | |
case 1: | |
(isGravity = !0, $("#setting-gravity").classList.add("active"), $("#setting-gravity span").innerHTML = "ON") | |
break; | |
case 2: | |
(isGravity = !1, $("#setting-gravity span").innerHTML = "PROPER ORBITAL MECHANICS") | |
$("#setting-gravity").classList.add("active") | |
$("#orbit").style.display="block" | |
$("#rendezvous").style.display="block" | |
// Have earth the correct way around, and ISS in correct orbit | |
earthObject.rotation.fromArray([0,0,(-90+51) * Math.PI/180.0]) | |
camera.children.push(GMAmbientLight) | |
GMSpeedMeasure=0 // force end speed measurement - fluctuating deltaT messes up orbits | |
break; | |
case 3: | |
($("#setting-gravity").classList.remove("active"), $("#setting-gravity span").innerHTML = "OFF") | |
$("#orbit").style.display="none" | |
$("#rendezvous").style.display="none" | |
// Revert back to ISS-SIM reverse earth | |
earthObject.rotation.fromArray([0,0,51 * Math.PI/180.0]) | |
starsObject.rotation.fromArray([0,0,0]) | |
lightISS_Primary.position.set(0, 4e10, 4e10) | |
camera.children.pop(GMAmbientLight) | |
issObject.children[3].children[0].children[0].children[6].children[8].children[6].children[6].children[3].rotation.x=60*Math.PI/180.0 | |
issObject.children[3].children[0].children[0].children[6].children[8].children[7].children[6].children[3].rotation.x=60*Math.PI/180.0 | |
break; | |
} | |
} | |
$("#setting-gravity").addEventListener("click", toggleGravity, !1) | |
$("#setting-gravity").addEventListener("touchstart", toggleGravity, !1) | |
// unhide the Tesla | |
window.GMisTeslaHidden = 1 | |
function GMToggleTeslaHidden() { | |
GMisTeslaHidden ? (GMisTeslaHidden = !1, $("#setting-tesla span").innerHTML = "VISIBLE") : (GMisTeslaHidden = !0, $("#setting-tesla span").innerHTML = "HIDDEN EASTEREGG") | |
} | |
$("#setting-tesla").addEventListener("click", GMToggleTeslaHidden, !1) | |
$("#setting-tesla").addEventListener("touchstart", GMToggleTeslaHidden, !1) | |
// unrestrict space | |
window.GMisSpaceRestricted = 1 | |
function GMToggleSpaceRestricted() { | |
GMisSpaceRestricted ? (GMisSpaceRestricted = !1, $("#setting-space span").innerHTML = "INFINITE") : (GMisChallenge && ( GMisSpaceRestricted = !0, $("#setting-space span").innerHTML = "RESTRICTED")) | |
} | |
$("#setting-space").addEventListener("click", GMToggleSpaceRestricted, !1) | |
$("#setting-space").addEventListener("touchstart", GMToggleSpaceRestricted, !1) | |
// HUD visible | |
window.GMHUDStatus = 1 | |
window.GMisHUDVisible = 1 | |
window.GMisTrajectoryVisible = 0 | |
function GMshowHUD() { | |
issObject.children[4].visible=true | |
issObject.children[5].visible=true | |
issObject.children[6].visible=true | |
issObject.children[7].visible=true | |
$("#hud").style.display="block" | |
$("#hud-tips").style.display="block" | |
$("#hud-worms").style.display="block" | |
} | |
function GMhideHUD() { | |
issObject.children[4].visible=false | |
issObject.children[5].visible=false | |
issObject.children[6].visible=false | |
issObject.children[7].visible=false | |
$("#hud").style.display="none" | |
$("#hud-tips").style.display="none" | |
$("#hud-worms").style.display="none" | |
} | |
function GMToggleHUDVisible() { | |
GMHUDStatus = GMHUDStatus<3 ? GMHUDStatus+1 : 1 | |
switch (GMHUDStatus) { | |
case 1: | |
GMisHUDVisible = !0, $("#setting-hud").classList.add("active"), $("#setting-hud span").innerHTML = "ONLINE", GMshowHUD() | |
break; | |
case 2: | |
( GMisOrbitalMechanics==2 || ( GMisOrbitalMechanics=1, toggleGravity() ) ), GMisTrajectoryVisible = !0, $("#setting-hud span").innerHTML = "ONLINE + TRAJECTORY" | |
break; | |
case 3: | |
GMisHUDVisible = !1, GMisTrajectoryVisible = !1, $("#setting-hud").classList.remove("active"), $("#setting-hud span").innerHTML = "OFFLINE", GMhideHUD() | |
break; | |
} | |
} | |
$("#setting-hud").addEventListener("click", GMToggleHUDVisible, !1) | |
$("#setting-hud").addEventListener("touchstart", GMToggleHUDVisible, !1) | |
// relaxed limits | |
window.GMLimits = 1 | |
window.GMisFuelLimit = !1 | |
window.GMisLimitsOriginal = !0 | |
function GMToggleLimits() { | |
GMLimits = GMLimits < 4 ? GMLimits+1 : 1 | |
switch (GMLimits) { | |
case 1: | |
GMisLimitsOriginal = !0, $("#setting-limits span").innerHTML = "ORIGINAL SIM +-0.2deg" | |
GMisFuelLimit = !1 | |
break; | |
case 2: | |
GMisLimitsOriginal = !1, $("#setting-limits span").innerHTML = "IDA SPECS +-4.0deg 0.1m/s" | |
GMisFuelLimit = !1 | |
break; | |
case 3: | |
GMisLimitsOriginal = !0, $("#setting-limits span").innerHTML = "LIMITED PROPELLANT, ORIGINAL SIM +-0.2deg" | |
GMisFuelLimit = !0 | |
break; | |
case 4: | |
GMisLimitsOriginal = !1, $("#setting-limits span").innerHTML = "LIMITED PROPELLANT, IDA SPECS +-4.0deg 0.1m/s" | |
GMisFuelLimit = !0 | |
break; | |
} | |
} | |
$("#setting-limits").addEventListener("click", GMToggleLimits, !1) | |
$("#setting-limits").addEventListener("touchstart", GMToggleLimits, !1) | |
// collision | |
window.GMisCollisions = 1 | |
function GMToggleCollisions() { | |
GMisCollisions ? (GMisCollisions = !1, $("#setting-collision").classList.remove("active"), $("#setting-collision span").innerHTML = "DISABLED") : (GMisCollisions = !0, $("#setting-collision").classList.add("active"), $("#setting-collision span").innerHTML = "ENABLED" ) | |
} | |
$("#setting-collision").addEventListener("click", GMToggleCollisions, !1) | |
$("#setting-collision").addEventListener("touchstart", GMToggleCollisions, !1) | |
// docking ports | |
window.GMisDocking = 1 | |
var GMRotForward = new THREE.Object3D() | |
GMRotForward.position.fromArray([0,0,-0.1]) | |
var GMRotPort = new THREE.Object3D() | |
GMRotPort.rotateY(0.5*Math.PI) | |
GMRotPort.position.fromArray([-0.1,0,0]) | |
var GMRotAft = new THREE.Object3D() | |
GMRotAft.rotateY(Math.PI) | |
GMRotAft.position.fromArray([0,0,0.1]) | |
var GMRotZenithUS = new THREE.Object3D() | |
GMRotZenithUS.rotateX(-0.5*Math.PI) | |
GMRotZenithUS.position.fromArray([0,-0.1,0]) | |
var GMRotNadirUS = new THREE.Object3D() | |
GMRotNadirUS.rotateX(0.5*Math.PI) | |
GMRotNadirUS.position.fromArray([0,0.1,0]) | |
var GMRotZenithRU = GMRotAft.clone() | |
GMRotZenithRU.rotateX(-0.5*Math.PI) | |
GMRotZenithRU.position.fromArray([0,-0.1,0]) | |
var GMRotNadirRU = GMRotAft.clone() | |
GMRotNadirRU.rotateX(0.5*Math.PI) | |
GMRotNadirRU.position.fromArray([0,0.1,0]) | |
var GMPosIDA2 = new THREE.Vector3(0,0,0) | |
var GMPosHarmonyZt = new THREE.Vector3(0,3.5,-4.75) | |
var GMPosHarmonyNd = new THREE.Vector3(0,-2.1,-4.75) | |
var GMPosPMA3 = new THREE.Vector3(12,0,-21) | |
var GMPosZvezda = new THREE.Vector3(0,1.5,-53.9) | |
var GMPosPoisk = new THREE.Vector3(0,7.0,-40.95) | |
var GMPosPirs = new THREE.Vector3(0,-4.1,-40.95) | |
var GMPosRassvet = new THREE.Vector3(0,-6.5,-28.0) | |
var GMIssRestoreOffset = GMPosIDA2.clone() | |
var GMIssApproach = GMRotForward.clone() | |
function GMRestoreISSPos() { | |
issObject.children[0].position.add(GMIssRestoreOffset) | |
issObject.children[1].position.add(GMIssRestoreOffset) | |
issObject.children[2].position.add(GMIssRestoreOffset) | |
issObject.children[3].position.add(GMIssRestoreOffset) | |
} | |
function GMSetISSPos() { | |
var GMnegRestore = GMIssRestoreOffset.clone() | |
GMnegRestore.negate() | |
issObject.children[0].position.add(GMnegRestore) | |
issObject.children[1].position.add(GMnegRestore) | |
issObject.children[2].position.add(GMnegRestore) | |
issObject.children[3].position.add(GMnegRestore) | |
} | |
function GMRingsFront() { | |
issObject.children[4].position.fromArray([0,0,20]) | |
issObject.children[5].position.fromArray([0,0,40]) | |
issObject.children[6].position.fromArray([0,0,80]) | |
issObject.children[4].rotation.fromArray([0,0,0]) | |
issObject.children[5].rotation.fromArray([0,0,0]) | |
issObject.children[6].rotation.fromArray([0,0,0]) | |
} | |
function GMRingsAft() { | |
issObject.children[4].position.fromArray([0,0,-20]) | |
issObject.children[5].position.fromArray([0,0,-40]) | |
issObject.children[6].position.fromArray([0,0,-80]) | |
issObject.children[4].rotation.fromArray([0,Math.PI,0]) | |
issObject.children[5].rotation.fromArray([0,Math.PI,0]) | |
issObject.children[6].rotation.fromArray([0,Math.PI,0]) | |
} | |
function GMRingsZenith() { | |
issObject.children[4].position.fromArray([0,20,0]) | |
issObject.children[5].position.fromArray([0,40,0]) | |
issObject.children[6].position.fromArray([0,80,0]) | |
issObject.children[4].rotation.fromArray([-0.5*Math.PI,0,0]) | |
issObject.children[5].rotation.fromArray([-0.5*Math.PI,0,0]) | |
issObject.children[6].rotation.fromArray([-0.5*Math.PI,0,0]) | |
} | |
function GMRingsNadir() { | |
issObject.children[4].position.fromArray([0,-20,0]) | |
issObject.children[5].position.fromArray([0,-40,0]) | |
issObject.children[6].position.fromArray([0,-80,0]) | |
issObject.children[4].rotation.fromArray([0.5*Math.PI,0,0]) | |
issObject.children[5].rotation.fromArray([0.5*Math.PI,0,0]) | |
issObject.children[6].rotation.fromArray([0.5*Math.PI,0,0]) | |
} | |
function GMToggleDocking() { | |
GMRestoreISSPos() | |
GMisDocking = GMisDocking<8 ? GMisDocking+1 : 1 | |
var name = "" | |
switch (GMisDocking) { | |
case 1: | |
GMIssRestoreOffset.copy(GMPosIDA2) | |
GMIssApproach.copy(GMRotForward) | |
name = "IDA-2" | |
GMRingsFront() | |
break; | |
case 2: | |
GMIssRestoreOffset.copy(GMPosHarmonyZt) | |
GMIssApproach.copy(GMRotZenithUS) | |
name = "HARMONY ZENITH" | |
GMRingsZenith() | |
break; | |
case 3: | |
GMIssRestoreOffset.copy(GMPosHarmonyNd) | |
GMIssApproach.copy(GMRotNadirUS) | |
name = "HARMONY NADIR" | |
GMRingsNadir() | |
break; | |
case 4: | |
GMIssRestoreOffset.copy(GMPosPMA3) | |
GMIssApproach.copy(GMRotPort) | |
name = "PMA3 ( WATCH RADIATOR! )" | |
GMRingsNadir() | |
issObject.children[4].position.x=5 | |
issObject.children[5].position.x=5 | |
issObject.children[6].position.x=5 | |
// rotate radiator to allow approach | |
issObject.children[3].children[0].children[0].children[6].children[8].children[7].children[5].rotateY(-Math.PI/2) | |
break; | |
case 5: | |
GMIssRestoreOffset.copy(GMPosZvezda) | |
GMIssApproach.copy(GMRotAft) | |
name = "ZVEZDA" | |
GMRingsAft() | |
issObject.children[3].children[0].children[0].children[6].children[8].children[7].children[5].rotateY(-Math.PI/2) | |
break; | |
case 6: | |
GMIssRestoreOffset.copy(GMPosPoisk) | |
GMIssApproach.copy(GMRotZenithRU) | |
name = "POISK" | |
GMRingsZenith() | |
break; | |
case 7: | |
GMIssRestoreOffset.copy(GMPosPirs) | |
GMIssApproach.copy(GMRotNadirRU) | |
name = "PIRS" | |
GMRingsNadir() | |
break; | |
case 8: | |
GMIssRestoreOffset.copy(GMPosRassvet) | |
GMIssApproach.copy(GMRotNadirRU) | |
name = "RASSVET" | |
GMRingsNadir() | |
break; | |
} | |
GMSetISSPos() | |
$("#setting-dock span").innerHTML = name | |
} | |
$("#setting-dock").addEventListener("click", GMToggleDocking, !1) | |
$("#setting-dock").addEventListener("touchstart", GMToggleDocking, !1) | |
// challenge | |
window.GMisChallenge = 1 | |
$("#option-restart").removeEventListener("click", resetPosition, !1) | |
$("#option-restart").removeEventListener("touchstart", resetPosition, !1) | |
window.GMPhase = 127 | |
window.GMPerigee = 190 | |
window.GMApogee = 205 | |
window.GMInclination = Math.PI*1000 // 3000 odd meters ;) | |
resetPosition = function() { | |
var px=12, | |
py=30, | |
pz=50, | |
vx=0, | |
vy=0, | |
vz=0 | |
if (!GMisChallenge) { | |
var phi = new THREE.Euler(-GMPhase*Math.PI/180,0,0), | |
r = GMGravBodyRadius+(GMPerigee*1000), | |
rref = GMGravBodyCenter.length(), | |
a = GMGravBodyRadius+((GMPerigee+GMApogee)*500), | |
pos = new THREE.Vector3(0,0,0).sub(GMGravBodyCenter).applyEuler(phi).setLength(r).add(GMGravBodyCenter), | |
v = Math.sqrt(GMG*GMMe*((2.0/r)-(1.0/a))), | |
omega = Math.sqrt((GMG*GMMe)/rref)/rref, | |
vel = new THREE.Vector3(0,0,v).applyEuler(phi).sub(GMCalcPseudoVelocity(pos,omega)).multiplyScalar(GMFixedStepTime) | |
px=pos.x + GMInclination // this adds enough challenge by screwing everything just a little bit up | |
py=pos.y | |
pz=pos.z | |
vx=vel.x | |
vy=vel.y | |
vz=vel.z | |
} | |
isGameOver=!0, resetMovement(), gsap.to(motionVector, 5, { | |
x: vx, | |
y: vy, | |
z: vz, | |
ease: "expo.out" | |
}), gsap.to(translationVector, 5, { | |
x: 0, | |
y: 0, | |
z: 0, | |
ease: "expo.out" | |
}), gsap.to(camera.position, 5, { | |
x: px, | |
y: py, | |
z: pz, | |
ease: "expo.out" | |
}), gsap.to(camera.rotation, 5, { | |
x: -20 * toRAD, | |
y: -10 * toRAD, | |
z: 15 * toRAD, | |
ease: "expo.out", | |
onComplete: GMGameStart | |
}) | |
} | |
function GMToggleChallenge() { | |
GMisChallenge ? ((GMisSpaceRestricted && GMToggleSpaceRestricted()), ( GMisOrbitalMechanics==2 || ( GMisOrbitalMechanics=1, toggleGravity() ) ), GMisChallenge = !1, $("#setting-challenge span").innerHTML = "RENDEZVOUS + DOCK", GMDeltaVBudget=175, resetPosition()) : (GMisChallenge = !0, $("#setting-challenge span").innerHTML = "DOCK", GMDeltaVBudget=25, resetPosition() ) | |
} | |
$("#option-restart").addEventListener("click", resetPosition, !1) | |
$("#option-restart").addEventListener("touchstart", resetPosition, !1) | |
$("#setting-challenge").addEventListener("click", GMToggleChallenge, !1) | |
$("#setting-challenge").addEventListener("touchstart", GMToggleChallenge, !1) | |
// lots of functionality here for proper orbital mechanics - start with globals | |
var GMCameraRotationHelper = new THREE.Object3D() | |
var GMoldTime = 0 | |
window.GMFixedStepTime = 0.05 | |
window.GMCurrentFrameTime = 0.05 | |
window.GMMissionTimer = 0 | |
window.GMGameTimer = 0 | |
// fix motion - ISS-SIM keeps all velocities in a per frame format, even though frame timing is varying | |
// let's fix that. This function undo's all motion computations and reapply's them with correct timing | |
// the alternative would be to reimplement the render() function. | |
function GMfixMotion() { | |
var k = GMCurrentFrameTime/GMFixedStepTime | |
// fix translation | |
!isGameOver && isGravity && (camera.position.y = camera.position.y + gravity) | |
camera.position.sub(motionVector) | |
correctedMotion = motionVector.clone().multiplyScalar(k) | |
camera.position.add(correctedMotion) | |
!isGameOver && isGravity && (camera.position.y = camera.position.y - (gravity*k)) | |
// fix rotation | |
camera.rotateZ(-currentRotationZ) | |
camera.rotateY(-currentRotationY) | |
camera.rotateX(-currentRotationX) | |
camera.rotateX(currentRotationX*k) | |
camera.rotateY(currentRotationY*k) | |
camera.rotateZ(currentRotationZ*k) | |
} | |
window.GMDeltaVUsed = 0 | |
window.GMDeltaVBudget = 25 | |
var GMOldMotionVector = new THREE.Vector3() | |
var GMOldRotation = new THREE.Vector3() | |
// calculate gravity - for proper orbital mechanics - needs coordinate system translation | |
// due to rotating coordinate reference frame used by ISS-SIM | |
window.GMGravBodyCenter = new THREE.Vector3(0,-6821000 ,0) // hardcode in case earth object is not spawned yet 450 km orbit | |
window.GMGravBodyRadius = 6371000 | |
window.GMGravBodyAtmosphereRadius = GMGravBodyRadius + 80000 | |
window.GMG = 6.6743015e-11 | |
window.GMMe = 5.9722e+24 | |
// var GMMe = 5.9722e+28 // black hole earth to test orbital mechanics | |
window.GMIssOrbitAxis = new THREE.Vector3(1,0,0) | |
// energy is sum of gravitational portential and kinematic | |
function GMOrbitEnergy(v,p) { | |
var r = Math.abs(p.distanceTo(GMGravBodyCenter)), | |
eP = -GMG*GMMe / r, | |
eV = 0.5 * (v.length()**2) | |
return eP + eV | |
} | |
// function to calculate pseudo velocity coming from rotating reference frame at any point | |
function GMCalcPseudoVelocity(position,omega) { | |
var pseudoCenter = GMGravBodyCenter.clone() | |
// frame rotates around x axis - this makes this easy | |
pseudoCenter.x = position.x | |
var radius = Math.abs(position.distanceTo(pseudoCenter)) | |
return pseudoCenter.clone().sub(position).normalize().cross(GMIssOrbitAxis).multiplyScalar(radius*omega) | |
} | |
// function to calculate local gravity vector | |
function GMCalcLocalGravityVector(position) { | |
var r = Math.abs(position.distanceTo(GMGravBodyCenter)) | |
var grav = GMG*GMMe / (r**2) | |
var earthVector = GMGravBodyCenter.clone().sub(position).normalize() | |
return earthVector.clone().multiplyScalar(grav) | |
} | |
function GMprintTime(time) { | |
var res="" | |
if (time<0) { | |
res="-" | |
time=-time | |
} | |
res += Math.floor(time/60).toString().padStart(2,"0") | |
res += ":" | |
res += Math.floor(time%60).toString().padStart(2,"0") | |
return res | |
} | |
// this function calculates the orbital mechanics creates in a rotating reference frame | |
// for that we compute a single motion step at orbital velocity with earth gravity acting, | |
// then transform it back into the rotating ref frame | |
function GMOrbitalMotion() { | |
var mu = GMG*GMMe | |
var GMIssAngularVelocity = Math.sqrt(mu/GMGravBodyCenter.length())/GMGravBodyCenter.length() | |
// calculating a motion step in rotating reference frame is now in its own class | |
// - warning hack: in every time step motionVector is added to camera | |
// but we prefer doing that ourselves so we substract motionVector now so the renderer can re-add it | |
var p = camera.position.clone().sub(motionVector.clone().multiplyScalar(GMCurrentFrameTime/GMFixedStepTime)) | |
var o = new GMOrbiter(p, motionVector.clone().multiplyScalar(1.0/GMFixedStepTime)) | |
o.advance(GMCurrentFrameTime) | |
// copy new velocity into motion vector | |
motionVector.copy(o.v).multiplyScalar(GMFixedStepTime) | |
// apply new position to camera | |
camera.position.copy(o.p) | |
// calculate some orbital parameters for display | |
var r = Math.abs(camera.position.distanceTo(GMGravBodyCenter)) | |
var v = o.vs.length() | |
var gammacos = o.vs.clone().normalize().dot(camera.position.clone().sub(GMGravBodyCenter).normalize()) | |
var gamma = Math.acos(gammacos) | |
var gammasin = Math.sin(gamma) | |
// semi major axis and eccentricity | |
var a = 1.0 / ( (2/r) - ((v**2) / mu) ) | |
var e = Math.sqrt( (((r*(v**2)/mu)-1)**2)*gammasin**2 + gammacos**2 ) | |
// periapis and apoapsis | |
var peri = a*(1-e), apo = a*(1+e) | |
// phase angle between ISS and Dragon - as seen from earth | |
var v1 = camera.position.clone().sub(GMGravBodyCenter).normalize() | |
var v2 = new THREE.Vector3(0,0,0).sub(GMGravBodyCenter).normalize() | |
var phase = v1.angleTo(v2) | |
if (v1.clone().cross(v2).dot(GMIssOrbitAxis)<0) phase=-phase | |
// a2 = r2 - circular ISS orbit | |
var a2 = GMGravBodyCenter.length() | |
// v3/a3/e3 hypothetical transfer orbit from current orbit (including gamma) to ISS - solve for DeltaV | |
var v3 = Math.sqrt(-2.0*a2*mu*((a2 - r)/(((r**3)*(gammasin**2)) - ((a2**2)*r)))) | |
var a3 = 1.0 / ( (2/r) - ((v3**2) / mu) ) | |
var e3 = Math.sqrt( (((r*(v3**2)/mu)-1)**2)*gammasin**2 + gammacos**2 ) | |
// calculate true anomaly of transfer orbit | |
var phi = Math.acos(((a3*(1.0-e3**2))-r)/(e3*r)) | |
if (gammacos<0) phi=-phi // correct for descending half orbit | |
// calculate time spent in transfer orbit - using Mean Anomaly M via eccentric anomaly E | |
var n = Math.sqrt(mu/a3**3) | |
var E = Math.acos((e3+Math.cos(phi))/(1.0+(e3*Math.cos(phi)))) | |
var M = E-(e3*Math.sin(E)) | |
if (gammacos<0) M=-M // once again correct for descending half | |
// calculate time of arrival - depends if we make a transfer up (destination at Apogee) | |
// or down (ISS is at perigee) and current Mean Anomaly | |
var rtime | |
if (a2>a3) { | |
rtime = Math.PI - M | |
} else { | |
rtime = (2.0*Math.PI) - M | |
} | |
if (rtime<0) rtime+=2.0*Math.PI | |
if (rtime>2.0*Math.PI) rtime-=2.0*Math.PI | |
rtime=rtime/n | |
// calculate periods | |
var T = 2.0 * Math.PI * Math.sqrt( a**3 / mu ) | |
var T2 = 2.0 * Math.PI * Math.sqrt( a2**3 / mu ) | |
// calculate phase after transfer | |
var pafter | |
if (a2>a3) { | |
pafter = phase+GMIssAngularVelocity*rtime-(Math.PI-phi) | |
} else { | |
pafter = phase+GMIssAngularVelocity*rtime-((2.0*Math.PI)-phi) | |
} | |
if (pafter < -Math.PI) pafter+=2.0*Math.PI | |
if (pafter > Math.PI) pafter-=2.0*Math.PI | |
// burn T=0 is when phase-after-transfer is zero. But rate at which phase is shrinking depends | |
// on current orbit - this timing is inaccurate, we don't take the current orbits Mean Anomaly into account | |
// but its "good enough" to give the hobby astronaut a good idea when to fire thrusters | |
var pdelta = 2.0*Math.PI*(T2-T)/T2 | |
var btime = -T2*(pafter/pdelta) | |
$("#orbit-vel").innerHTML="Vel: "+v.toFixed(1)+" m/s" | |
$("#orbit-alt").innerHTML="Alt: "+((r-GMGravBodyRadius)/1000).toFixed(1)+" km" | |
$("#orbit-apogee").innerHTML="Ap: "+((apo-GMGravBodyRadius)/1000).toFixed(1)+" km" | |
$("#orbit-perigee").innerHTML="Pe: "+((peri-GMGravBodyRadius)/1000).toFixed(1)+" km" | |
$("#orbit-period").innerHTML="Prd: "+GMprintTime(T) | |
$("#rendezvous-phase").innerHTML="Phs: "+(phase*180.0/Math.PI).toFixed(1)+"°" | |
$("#rendezvous-time").innerHTML="Ht: "+GMprintTime(rtime)+"⇒"+(pafter*180.0/Math.PI).toFixed(1)+"°" | |
$("#rendezvous-deltav").innerHTML="Dv: "+((v3-v).toFixed(1))+" m/s" | |
$("#rendezvous-deltat").innerHTML="Dt: "+GMprintTime(btime) | |
// rotate earth more correctly | |
earthMesh.rotateY(-1e-4) // undo default rotation | |
earthMesh.rotateY(((2*Math.PI)/(24*60*60))*GMCurrentFrameTime) // and rotate eastwards (natural earth rotation) | |
starsObject.rotateX(-GMIssAngularVelocity*GMCurrentFrameTime) | |
earthObject.rotateOnWorldAxis(GMIssOrbitAxis,-GMIssAngularVelocity*GMCurrentFrameTime) // while perceived rotation is west | |
lightISS_Primary.position.applyEuler(new THREE.Euler(-GMIssAngularVelocity*GMCurrentFrameTime,0,0)) // rotate sun | |
// follow sun with ISS with solar panels | |
issObject.children[3].children[0].children[0].children[6].children[8].children[6].children[6].children[3].rotateX(-GMIssAngularVelocity*GMCurrentFrameTime) | |
issObject.children[3].children[0].children[0].children[6].children[8].children[7].children[6].children[3].rotateX(-GMIssAngularVelocity*GMCurrentFrameTime) | |
} | |
// helper class to accurately calculate orbital motion of any object in ISS rotating reference frame | |
class GMOrbiter { | |
constructor(p,v) { | |
this.p = (new THREE.Vector3()).copy(p) | |
this.v = (new THREE.Vector3()).copy(v) | |
this.vs = new THREE.Vector3() | |
} | |
clone() { | |
var c = new GMOrbiter(this.p,this.v) | |
c.vs.copy(this.vs) | |
return c | |
} | |
advance(t) { | |
var mu = GMG*GMMe | |
// velocity of rotating reference frame | |
var GMIssAngularVelocity = Math.sqrt(mu/GMGravBodyCenter.length())/GMGravBodyCenter.length() | |
var pseudoCamMotion = GMCalcPseudoVelocity(this.p,GMIssAngularVelocity) | |
// calculate local gravity vector and magnitude | |
var gravityVector = GMCalcLocalGravityVector(this.p) | |
// calculate current effective velocity vector and direction | |
var pseudoVelocity = this.v.clone().add(pseudoCamMotion) | |
// calculate orbital energy | |
var GMEnergy = GMOrbitEnergy(pseudoVelocity, this.p) | |
// calculate change in position and velocity in stationary coordinate system... | |
var pseudoNextVel1 = pseudoVelocity.clone().add(gravityVector.clone().multiplyScalar(t)) | |
var averageVel1 = pseudoVelocity.clone().add(pseudoNextVel1).multiplyScalar(0.5) | |
var pseudoNextPos1 = this.p.clone().add(averageVel1.clone().multiplyScalar(t)) | |
// iterative, 2 step for higher accuracy (that algo has a name, I forgot that name) | |
var gravityVector2 = GMCalcLocalGravityVector(pseudoNextPos1) | |
var averageGrav = gravityVector.clone().add(gravityVector2).multiplyScalar(0.5) | |
var pseudoNextVel = pseudoVelocity.clone().add(averageGrav.clone().multiplyScalar(t)) | |
var averageVel2 = pseudoVelocity.clone().add(pseudoNextVel).multiplyScalar(0.5) | |
var pseudoNextPos = this.p.clone().add(averageVel2.clone().multiplyScalar(t)) | |
// convert new position back into rotating coordinate frame at time t+1 | |
this.p.copy(pseudoNextPos.clone().sub(GMGravBodyCenter).applyAxisAngle(GMIssOrbitAxis,-GMIssAngularVelocity*t).add(GMGravBodyCenter)) | |
// same with velocity | |
var newPseudoMotion = GMCalcPseudoVelocity(this.p,GMIssAngularVelocity) | |
var newPseudoVelocity = pseudoNextVel.clone().applyAxisAngle(GMIssOrbitAxis,-GMIssAngularVelocity*t) | |
// correct orbital energy to prevent drifting | |
var velEnergyRequired = GMEnergy - (-mu / Math.abs(this.p.distanceTo(GMGravBodyCenter))) | |
var velocityRequired = Math.sqrt( 2.0*velEnergyRequired ) | |
this.vs.copy(newPseudoVelocity.clone().normalize().multiplyScalar(velocityRequired)) | |
this.v.copy(this.vs.clone().sub(newPseudoMotion)) | |
} | |
} | |
window.GMTrajectoryStep=20.0 | |
// Show expected trajectory | |
function GMShowTrajectory() { | |
var o = new GMOrbiter(camera.position, motionVector.clone().multiplyScalar(1.0/GMFixedStepTime)) | |
var timestep = GMTrajectoryStep / (motionVector.length()/GMFixedStepTime) // calculate length of time iteration | |
var iters=1 | |
if (!(timestep<1.0)) timestep=1.0; // prevent div by zero or extreme accuracy degradation | |
var ot=o.clone() | |
ot.advance(timestep) | |
var l=ot.clone().p.sub(o.p).length() | |
iters=Math.floor((GMTrajectoryStep/l)+0.5) | |
if (!(iters<10000)) { // prevent div by zero | |
GMTrajectoryObject.visible = false | |
return | |
} | |
if (!(iters<100)) iters=100; // prevent processing time explosions | |
for (var t=0; t<GMTrajectoryLength; t++) { | |
o.advance(timestep) | |
for (var t2=1;t2<iters;t2++) o.advance(timestep) | |
GMTrajectoryObject.children[t].position.copy(o.p) | |
} | |
} | |
// override some functions from ISS SIM with modified versions to implement this functionality | |
// start with some helper functions | |
function GMAddReason(a,b) { | |
return a != "" ? a + ", " + b : b | |
} | |
window.GMTimeLapse = 1 | |
window.GMBurn=0 | |
window.GMBurnTimer=0 | |
checkCollision = function() { | |
// fix time stepping - do not allow big time jumps to ensure smoothness | |
var GMnewTime=Date.now() | |
if ( GMoldTime < GMnewTime - 100 ) { | |
GMoldTime = GMnewTime - 100 | |
} | |
GMCurrentFrameTime = 0.001 * ( GMnewTime - GMoldTime ) | |
GMoldTime=GMnewTime | |
GMfixMotion() | |
GMTrajectoryObject.visible = false | |
if (!0 !== isGameOver) { | |
// Main Burn incl. timer | |
if (GMBurn == 1){ | |
GMBurnTimer+=GMCurrentFrameTime | |
$("#translate-forward-button").click() | |
} | |
$("#burn-onoff").innerHTML = (GMBurn == 1 ? '<span style="color:red">ON ' + GMprintTime(GMBurnTimer)+"."+(GMBurnTimer%1.0).toFixed(2).toString().substring(2) + '</span>' : "OFF") | |
// timing | |
GMMissionTimer+=GMCurrentFrameTime | |
GMGameTimer+=GMCurrentFrameTime | |
$("#stats-mission").innerHTML="Mis: "+GMprintTime(GMMissionTimer)+"."+(GMMissionTimer%1.0).toFixed(2).toString().substring(2) | |
$("#stats-real").innerHTML="Sim: "+GMprintTime(GMGameTimer)+"."+(GMGameTimer%1.0).toFixed(2).toString().substring(2) | |
$("#stats-fps").innerHTML=(1.0/GMCurrentFrameTime).toFixed(1)+" fps" | |
// DeltaV update: | |
GMDeltaVUsed+=motionVector.clone().sub(GMOldMotionVector).length()/GMFixedStepTime | |
var rotationDeltaV=Math.abs(GMOldRotation.x-currentRotationX) + Math.abs(GMOldRotation.y-currentRotationY) + Math.abs(GMOldRotation.z-currentRotationZ) | |
GMDeltaVUsed+=0.02*rotationDeltaV*180/(Math.PI*moveSpeed) | |
$("#prop-used").innerHTML="Used: "+(GMDeltaVUsed*1.0).toFixed(1)+" m/s" | |
$("#prop-left").innerHTML="Budg: "+(GMDeltaVBudget-GMDeltaVUsed).toFixed(1)+" m/s" | |
$("#propellant").style.display="block" | |
// calculate gravity if applicable | |
if (GMisOrbitalMechanics==2) { | |
GMOrbitalMotion() | |
} | |
// time lapse | |
var realFrameTime = GMCurrentFrameTime | |
GMCurrentFrameTime = GMFixedStepTime // force k factor 1.0 for fast forward frames | |
var ti | |
for (ti=1; ti < GMTimeLapse; ti++) { | |
camera.position.add(motionVector) | |
!isGameOver && isGravity && (camera.position.y = camera.position.y - (gravity)) | |
camera.rotateX(currentRotationX) | |
camera.rotateY(currentRotationY) | |
camera.rotateZ(currentRotationZ) | |
earthMesh.rotateY(1e-4) | |
GMMissionTimer+=GMCurrentFrameTime | |
// calculate gravity if applicable | |
if (GMisOrbitalMechanics==2) { | |
GMOrbitalMotion() | |
} | |
} | |
GMCurrentFrameTime = realFrameTime | |
// save motion vectors for deltaV update | |
GMOldMotionVector.copy(motionVector) | |
GMOldRotation.set(currentRotationX,currentRotationY,currentRotationZ) | |
if (GMisOrbitalMechanics==2 && GMisTrajectoryVisible) { | |
GMTrajectoryObject.visible = true | |
GMShowTrajectory() | |
} | |
var GMIssTarget = issObject.position.clone() | |
GMIssTarget.add(GMIssApproach.position) | |
GMCameraRotationHelper.quaternion.copy(camera.quaternion) | |
GMCameraRotationHelper.quaternion.multiply(GMIssApproach.quaternion.clone().inverse()) | |
var e = hitRaycaster.intersectObjects(hitArray, !0), | |
t = Math.abs(camera.position.distanceTo(issObject.position)), | |
t2 = Math.abs(camera.position.distanceTo(GMIssTarget)), | |
a = Math.abs(GMCameraRotationHelper.rotation.x / toRAD), | |
r = Math.abs(GMCameraRotationHelper.rotation.y / toRAD), | |
o = Math.abs(GMCameraRotationHelper.rotation.z / toRAD), | |
i = Math.abs(motionVector.x), | |
n = Math.abs(motionVector.y), | |
s = Math.abs(motionVector.z), | |
ar = Math.sqrt(a*a+r*r), | |
rxy = Math.sqrt(currentRotationX*currentRotationX + currentRotationY*currentRotationY) / toRAD, | |
rz = Math.abs(currentRotationZ) / toRAD | |
if (hitDistance = t > .5 ? 1 : .1, hitRaycaster.far = hitDistance, updateRateColor(i >= .02 || n >= .02 || s >= .02 ? "warning" : i > toleranceRate || n > toleranceRate || s > toleranceRate ? "caution" : "normal"), GMisCollisions && e.length > 0 && ($("#fail-message").innerHTML = "You made contact with the International Space Station.", hideInterface("fail")), GMisCollisions && Math.abs(camera.position.distanceTo(GMGravBodyCenter)) < GMGravBodyAtmosphereRadius && ($("#fail-message").innerHTML = "You re-entered earth atmosphere.", hideInterface("fail")), GMisSpaceRestricted && t > 500 && ($("#fail-message").innerHTML = "You are too far away from the International Space Station.", hideInterface("fail")), GMisFuelLimit && GMDeltaVUsed > GMDeltaVBudget && ($("#fail-message").innerHTML = "You used too much propellant.", hideInterface("fail")), t < .2 && t2 < t) { | |
var GMInLimits = true, l = "" | |
if (GMisLimitsOriginal) { | |
if (!(a <= toleranceRotation && r <= toleranceRotation && o <= toleranceRotation)) { | |
l=GMAddReason(l,"ROTATION ANGLE") | |
GMInLimits = false | |
} | |
if (!(i <= toleranceRate && n <= toleranceRate && s <= toleranceRate)) { | |
l=GMAddReason(l,"SPEED") | |
GMInLimits = false | |
} | |
} else { | |
// correct speeds for m/s instead of m/frame | |
i=i/GMFixedStepTime | |
n=n/GMFixedStepTime | |
s=s/GMFixedStepTime | |
rxy=rxy/GMFixedStepTime | |
rz=rz/GMFixedStepTime | |
var dx = Math.abs(camera.position.x-issObject.position.x), | |
dy = Math.abs(camera.position.y-issObject.position.y), | |
dz = Math.abs(camera.position.z-issObject.position.z) | |
if (dz>=0.1) { | |
l=GMAddReason(l,"OFFSET-X > 0.1") | |
GMInLimits = false | |
} | |
if (dx>=0.1) { | |
l=GMAddReason(l,"OFFSET-Y > 0.1") | |
GMInLimits = false | |
} | |
if (dy>=0.1) { | |
l=GMAddReason(l,"OFFSET-Z > 0.1") | |
GMInLimits = false | |
} | |
if (s>=0.1) { | |
l=GMAddReason(l,"VELOCITY-X > 0.1") | |
GMInLimits = false | |
} | |
if (i>=0.1) { | |
l=GMAddReason(l,"VELOCITY-Y > 0.1") | |
GMInLimits = false | |
} | |
if (n>=0.1) { | |
l=GMAddReason(l,"VELOCITY-Z > 0.1") | |
GMInLimits = false | |
} | |
if (ar>=4) { | |
l=GMAddReason(l,"ANGLE (PITCH/YAW) > 4") | |
GMInLimits = false | |
} | |
if (o>=4) { | |
l=GMAddReason(l,"ROLL ANGLE > 4") | |
GMInLimits = false | |
} | |
if (rxy>=0.2) { | |
l=GMAddReason(l,"ROTATION (PITCH/YAW) > 0.2") | |
GMInLimits = false | |
} | |
if (rz>=0.2) { | |
l=GMAddReason(l,"ROLL RATE > 0.2") | |
GMInLimits = false | |
} | |
} | |
if (GMInLimits) { | |
hideInterface("success") | |
} else { | |
$("#fail-message").innerHTML = "The following errors occurred: <span class='red'>" + l + "<span>" | |
hideInterface("fail") | |
} | |
} | |
} | |
} | |
renderTesla = function() { | |
(!GMisTeslaHidden || camera.rotation.y > 2.5 || camera.rotation.y < -2.5 ? (isTeslaCreated || createTesla(), isTeslaLoaded && (teslaMesh.visible = !0)) : isTeslaLoaded && (teslaMesh.visible = !1)) | |
} | |
// The NAVBall became a bit more complicated, since we would like it to be in Earth frame | |
// but we are in a rotating reference frame in an inclined earth orbit | |
// Helper function to calculate rotation between 2 orthogonal vector pairs | |
function GMRotateVectorPairs(u0, v0, u2, v2) { | |
var q2 = new THREE.Quaternion().setFromUnitVectors(u0, u2) | |
var v1 = v2.clone().applyQuaternion(q2.clone().conjugate()) | |
var v0_proj = v0.projectOnPlane(u0) | |
var v1_proj = v1.projectOnPlane(u0) | |
var angleInPlane = v0_proj.angleTo(v1_proj) | |
if (v1_proj.dot(new THREE.Vector3().crossVectors(u0, v0)) < 0) { | |
angleInPlane *= -1 | |
} | |
var q1 = new THREE.Quaternion().setFromAxisAngle(u0, angleInPlane) | |
var q = new THREE.Quaternion().multiplyQuaternions(q2, q1) | |
return q | |
} | |
// override navball rendering to show earth/orbit instead of ISS relative | |
renderNavball = function() { | |
if (navballCreated) { | |
var rotAxis=new THREE.Vector3(0,1,0) | |
var pAxis=new THREE.Vector3(0,0,1) | |
var camVector = camera.position.clone().sub(GMGravBodyCenter).normalize() | |
var earthRotAxis = rotAxis.clone().applyQuaternion(earthObject.quaternion).normalize() | |
var vectorToEast = camVector.clone().cross(earthRotAxis).normalize() | |
var q = GMRotateVectorPairs(rotAxis,pAxis,camVector,vectorToEast) | |
var e = camera.quaternion.clone().conjugate().multiply(q) | |
navballObject.setRotationFromQuaternion(e) | |
} | |
} | |
renderOrb = function() { | |
if (isOrbCreated) { | |
orbYawObject.rotation.y = GMisHUDVisible ? -camera.rotation.y : 0.0, orbPitchObject.rotation.x = GMisHUDVisible ? -camera.rotation.x : 0.0, orbRollObject.rotation.z = GMisHUDVisible ? -camera.rotation.z : 0.0 | |
var e = orbFadeTarget.getWorldPosition(new THREE.Vector3), i | |
for (i = 0; i < orbSpriteArray.length; i++) { | |
var t = orbSpriteArray[i].getWorldPosition(new THREE.Vector3), | |
a = Math.abs(t.distanceTo(e)) | |
orbSpriteArray[i].material.uniforms.u_opacity.value = GMisHUDVisible ? opacityDistanceClamp(a, 5, 2, 1) : 0.0 | |
} | |
} | |
} | |
// this is just a bugfix for rough versus fine rotation control | |
updatePrecision = function(e) { | |
"rotation" === e && (rotationPulseSize = .5 === rotationPulseSize ? (rateSpeedSize=2,1) : (rateSpeedSize=1,.5), $("#rotation-controls").classList.toggle("large"), $("#precision-rotation-status").classList.toggle("large"), $("#hud-tips").classList.toggle("rotation-large")), "translation" === e && (translationPulseSize = .001 === translationPulseSize ? .005 : .001, $("#translation-controls").classList.toggle("large"), $("#precision-translation-status").classList.toggle("large"), $("#hud-tips").classList.toggle("translation-large")) | |
} | |
// this is overridden to reset some variables each new game/attempt | |
GMGameStart = function() { | |
GMDeltaVUsed=0 | |
GMOldMotionVector.copy(motionVector) | |
GMOldRotation.set(currentRotationX, currentRotationY, currentRotationZ) | |
GMMissionTimer=0 | |
GMGameTimer=0 | |
isGameOver=!1 | |
} | |
$("#fail-button").removeEventListener("click", showInterface, !1) | |
$("#fail-button").removeEventListener("touchstart", showInterface, !1) | |
$("#success-button").removeEventListener("click", showInterface, !1) | |
$("#success-button").removeEventListener("touchstart", showInterface, !1) | |
// the annoying thing is the need to duplicate the entire function | |
// for the change of a single onComplete property | |
window.GMshowInterface = function() { | |
showInterface() | |
setTimeout(GMGameStart,5000) | |
} | |
$("#fail-button").addEventListener("click", GMshowInterface, !1) | |
$("#fail-button").addEventListener("touchstart", GMshowInterface, !1) | |
$("#success-button").addEventListener("click", GMshowInterface, !1) | |
$("#success-button").addEventListener("touchstart", GMshowInterface, !1) | |
// extra keys - z/y for toggle translation precision - x for toggle rotation precision, c,v,b for time lapse | |
document.addEventListener("keyup", function(e) { | |
if (!isGameOver) switch (e.keyCode || e.which) { | |
case 89: | |
case 90: | |
toggleTranslation() | |
break; | |
case 88: | |
toggleRotation() | |
break; | |
case 67: | |
GMTimeLapse=1 | |
break; | |
case 77: //M key - main burn & timer | |
GMBurn == 1 ? GMBurn=0 : GMBurn=1 | |
if (GMBurn == 0) GMBurnTimer=0 | |
break; | |
case 86: | |
GMTimeLapse>=10 ? GMTimeLapse/=10 : GMTimeLapse=1 | |
break; | |
case 66: | |
GMTimeLapse<=100 ? GMTimeLapse*=10 : GMTimeLapse=1000 | |
break; | |
} | |
}) | |
// make 30 spheres for trajectory visualization | |
window.GMTrajectoryObject = new THREE.Object3D() | |
var GMTrajectoryLength = 30 | |
for (var t=0; t<GMTrajectoryLength; t++) { | |
var scale=0.25 | |
if (((t+1)%10)==0) { | |
scale=0.75 | |
} | |
GMTrajectoryObject.add(new THREE.Mesh(new THREE.SphereGeometry(scale,6,6), new THREE.MeshBasicMaterial({ | |
transparent: true, | |
opacity: 0.75, | |
color: 2413309, | |
}))) | |
} | |
GMTrajectoryObject.visible=false | |
scene.add(GMTrajectoryObject) | |
$("#intro-step1 .animate").innerHTML="<span class=\"red\">ISS-SIM-Customizer</span> user script v 0.000015 by <span class=\"green\">CorvusCorax</span> loaded succesfully - check settings page for details.<br/>Use [x] and [y]/[z] keys to toggle precision thrust for translation/rotation.<br/>Use [b],[v] for time warp +-, [c] resets time warp.<br/>"+$("#intro-step1 .animate").innerHTML | |
} | |
// delayed execution after SpaceX sim loads | |
var GMscript = document.createElement('script') | |
GMscript.type = "text/javascript" | |
GMscript.innerHTML = GMInitializer.toString() + "\n" + "window.addEventListener('load', GMInitializer)" + "\n" | |
document.getElementsByTagName('head')[0].appendChild(GMscript) | |
//end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment