-
-
Save CorvusCorax/d6e6e98c946b8bfad56563b514df23a9 to your computer and use it in GitHub Desktop.
// ==UserScript== | |
// @name SpaceX ISS-SIM-Customizer | |
// @version 0.000016 | |
// @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-backward-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 | |
GMBurnTimer=0 | |
GMBurn=0 | |
GMTimeLapse=1 | |
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.000016 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 |
I created a fork and added the code fragments :-
https://gist.github.com/LeighDarby/2bb251105119ff27cdff80a9312cf96a
Was considering a couple of (possibly mad) ideas ...
Add an external camera view of Dragon (track relative and/or fixed point to view docking etc.) - there are quite a few 3D (.glb) models available but the ones with docking nose cone open all require $$ I think ...
Found a decent .glb of Hubble, could put that into orbit and have a different rendezvous option?
Wikipedia seems to have some orbital data but perhaps NASA has better not had a chance to look yet ...
I created a fork and added the code fragments :- https://gist.github.com/LeighDarby/2bb251105119ff27cdff80a9312cf96a
I pulled your changes and integrated this in the main code. Since Dragon's main thrusters are pointed forward - located under the nose cone - I changed the main thrusters to firing backwards instead of forward. I also added code to reset burn timer and thruster status whenever the simulation is reset to not "spawn" with firing main engine ;)
After playing a bit, I think that the thrust is probably way too much. Dragon has 4 forward draco thrusters with 400 N each. Assuming a mass of aprox 10 tons, that would lead to an acceleration of 1600N / 10000kg = 0.16 m/s² when firing continuously.
When hammering the controls, a user can easily achieve 10 times as much and your "main engine" script in course mode achieves close to 1 g - 100 times the thrust dragon would actually have.
but it is of course quite convenient ;) I think I leave it in ;)
You could fork the gist on your own github, then I can pull the fragments :) It's basically a repository. where each comment or edit is a commit. or we turn it into a proper source code repository for development, what do you think?