';
if (results[i].isIntlDes) {
html += sat.OBJECT_NAME;
} else {
html += sat.OBJECT_NAME.substring(0, results[i].strIndex);
html += '
';
html += sat.OBJECT_NAME.substring(results[i].strIndex, results[i].strIndex + searchStr.length);
html += '';
html += sat.OBJECT_NAME.substring(results[i].strIndex + searchStr.length);
}
html += '
';
if (results[i].isIntlDes) {
html += sat.intlDes.substring(0, results[i].strIndex);
html += '';
html += sat.intlDes.substring(results[i].strIndex, results[i].strIndex + searchStr.length);
html += '';
html += sat.intlDes.substring(results[i].strIndex + searchStr.length);
} else {
html += sat.intlDes;
}
html += '
';
}
var resultStart = performance.now();
// resultBox.append(html);
resultBox[0].innerHTML = html;
resultBox.slideDown();
resultsOpen = true;
};
searchBox.init = function (_satData) {
satData = _satData;
$('#search-results').on('click', '.search-result', function (evt) {
var satId = $(this).data('sat-id');
selectSat(satId);
// hideResults();
});
$('#search-results').on('mouseover', '.search-result', function (evt) {
var satId = $(this).data('sat-id');
orbitDisplay.setHoverOrbit(satId);
satSet.setHover(satId);
hovering = true;
hoverSatId = satId;
});
$('#search-results').mouseout(function () {
orbitDisplay.clearHoverOrbit();
satSet.setHover(-1);
// hoverBoxOnSat(-1);
hovering = false;
});
$('#search').on('input', function () {
var initStart = performance.now();
var searchStr = $('#search').val()
searchBox.doSearch(searchStr);
});
$('#all-objects-link').click(function () {
var intldes = satSet.getSat(selectedSat).intlDes;
var searchStr = intldes.slice(0, 8);
searchBox.doSearch(searchStr);
$('#search').val(searchStr);
});
};
window.searchBox = searchBox;
})();
// **** end search-box.js ***
// **** orbit-display.js ***
/* global groups */
/* global satSet */
/* global mat4 */
/* global shaderLoader */
/* global gl */
(function () {
var NUM_SEGS = 255;
var glBuffers = [];
var inProgress = [];
var orbitDisplay = {};
var pathShader;
var selectOrbitBuf;
var hoverOrbitBuf;
var currentHoverId = -1;
var currentSelectId = -1;
var orbitMvMat = mat4.create();
var orbitWorker = new Worker('/scripts/orbit-calculation-worker.js');
var initialized = false;
var selectColor = [0.0, 1.0, 0.0, 1.0];
var hoverColor = [0.5, 0.5, 1.0, 1.0];
var groupColor = [0.3, 0.5, 1.0, 0.4];
orbitDisplay.init = function () {
var startTime = performance.now();
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, shaderLoader.getShaderCode('path-vertex.glsl'));
gl.compileShader(vs);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, shaderLoader.getShaderCode('path-fragment.glsl'));
gl.compileShader(fs);
pathShader = gl.createProgram();
gl.attachShader(pathShader, vs);
gl.attachShader(pathShader, fs);
gl.linkProgram(pathShader);
pathShader.aPos = gl.getAttribLocation(pathShader, 'aPos');
pathShader.uMvMatrix = gl.getUniformLocation(pathShader, 'uMvMatrix');
pathShader.uCamMatrix = gl.getUniformLocation(pathShader, 'uCamMatrix');
pathShader.uPMatrix = gl.getUniformLocation(pathShader, 'uPMatrix');
pathShader.uColor = gl.getUniformLocation(pathShader, 'uColor');
selectOrbitBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, selectOrbitBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array((NUM_SEGS + 1) * 3), gl.STATIC_DRAW);
hoverOrbitBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, hoverOrbitBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array((NUM_SEGS + 1) * 3), gl.STATIC_DRAW);
for (var i = 0; i < satSet.numSats; i++) {
glBuffers.push(allocateBuffer());
}
orbitWorker.postMessage({
isInit: true,
satData: satSet.satDataString,
numSegs: NUM_SEGS
});
initialized = true;
var time = performance.now() - startTime;
console.log('orbitDisplay init: ' + time + ' ms');
};
orbitDisplay.updateOrbitBuffer = function (satId) {
if (!inProgress[satId]) {
orbitWorker.postMessage({
isInit: false,
satId: satId
});
inProgress[satId] = true;
}
};
orbitWorker.onmessage = function (m) {
var satId = m.data.satId;
var pointsOut = new Float32Array(m.data.pointsOut);
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers[satId]);
gl.bufferData(gl.ARRAY_BUFFER, pointsOut, gl.DYNAMIC_DRAW);
inProgress[satId] = false;
};
/*orbitDisplay.setOrbit = function(satId) {
var sat = satSet.getSat(satId);
mat4.identity(orbitMvMat);
//apply steps in reverse order because matrix multiplication
// (last multiplied in is first applied to vertex)
//step 5. rotate to RAAN
mat4.rotateZ(orbitMvMat, orbitMvMat, sat.raan + Math.PI/2);
//step 4. incline the plane
mat4.rotateY(orbitMvMat, orbitMvMat, -sat.inclination);
//step 3. rotate to argument of periapsis
mat4.rotateZ(orbitMvMat, orbitMvMat, sat.argPe - Math.PI/2);
//step 2. put earth at the focus
mat4.translate(orbitMvMat, orbitMvMat, [sat.semiMajorAxis - sat.apogee - 6371, 0, 0]);
//step 1. stretch to ellipse
mat4.scale(orbitMvMat, orbitMvMat, [sat.semiMajorAxis, sat.semiMinorAxis, 0]);
};
orbitDisplay.clearOrbit = function() {
mat4.identity(orbitMvMat);
}*/
orbitDisplay.setSelectOrbit = function (satId) {
// var start = performance.now();
currentSelectId = satId;
orbitDisplay.updateOrbitBuffer(satId);
// console.log('setOrbit(): ' + (performance.now() - start) + ' ms');
};
orbitDisplay.clearSelectOrbit = function () {
currentSelectId = -1;
gl.bindBuffer(gl.ARRAY_BUFFER, selectOrbitBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array((NUM_SEGS + 1) * 3), gl.DYNAMIC_DRAW);
};
orbitDisplay.setHoverOrbit = function (satId) {
if (satId === currentHoverId) return;
currentHoverId = satId;
orbitDisplay.updateOrbitBuffer(satId);
};
orbitDisplay.clearHoverOrbit = function (satId) {
if (currentHoverId === -1) return;
currentHoverId = -1;
gl.bindBuffer(gl.ARRAY_BUFFER, hoverOrbitBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array((NUM_SEGS + 1) * 3), gl.DYNAMIC_DRAW);
};
orbitDisplay.draw = function (pMatrix, camMatrix) { //lol what do I do here
if (!initialized) return;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(pathShader);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
// gl.depthMask(false);
gl.uniformMatrix4fv(pathShader.uMvMatrix, false, orbitMvMat);
gl.uniformMatrix4fv(pathShader.uCamMatrix, false, camMatrix);
gl.uniformMatrix4fv(pathShader.uPMatrix, false, pMatrix);
if (currentSelectId !== -1) {
gl.uniform4fv(pathShader.uColor, selectColor);
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers[currentSelectId]);
gl.vertexAttribPointer(pathShader.aPos, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, NUM_SEGS + 1);
}
if (currentHoverId !== -1 && currentHoverId !== currentSelectId) { //avoid z-fighting
gl.uniform4fv(pathShader.uColor, hoverColor);
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers[currentHoverId]);
gl.vertexAttribPointer(pathShader.aPos, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, NUM_SEGS + 1);
}
if (groups.selectedGroup !== null) {
gl.uniform4fv(pathShader.uColor, groupColor);
groups.selectedGroup.forEach(function (id) {
gl.bindBuffer(gl.ARRAY_BUFFER, glBuffers[id]);
gl.vertexAttribPointer(pathShader.aPos, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINE_STRIP, 0, NUM_SEGS + 1);
});
}
// gl.depthMask(true);
gl.disable(gl.BLEND);
};
function allocateBuffer() {
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array((NUM_SEGS + 1) * 3), gl.STATIC_DRAW);
return buf;
}
orbitDisplay.getPathShader = function () {
return pathShader;
};
window.orbitDisplay = orbitDisplay;
})();
// **** end orbit-display.js ***
// **** line.js ***
/* global orbitDisplay */
(function () {
function Line() {
this.vertBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(6), gl.STREAM_DRAW);
}
Line.prototype.set = function (pt1, pt2) {
var buf = [];
buf.push(pt1[0]);
buf.push(pt1[1]);
buf.push(pt1[2]);
buf.push(pt2[0]);
buf.push(pt2[1]);
buf.push(pt2[2]);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(buf), gl.STREAM_DRAW);
};
Line.prototype.draw = function () {
var shader = orbitDisplay.getPathShader();
gl.useProgram(shader);
gl.uniform4fv(shader.uColor, [0.1, 0.0, 1.0, 0.1]);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertBuf);
gl.vertexAttribPointer(shader.aPos, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINES, 0, 2);
};
window.Line = Line;
})();
// **** end line.js ***
// **** earth.js ***
/* global satellite */
//earth.js
(function() {
var earth = {};
var R2D = 180 / Math.PI;
var D2R = Math.PI / 180;
var NUM_LAT_SEGS = 64;
var NUM_LON_SEGS = 64;
var pos = [3.0, 0.0, 1.0];
var radius = 6371.0;
var vertPosBuf, vertNormBuf, texCoordBuf, vertIndexBuf; //GPU mem buffers, data and stuff?
var vertCount;
var earthShader;
earth.pos = [0, 0, 0];
var texture, nightTexture;
var texLoaded = false,
nightLoaded = false;
var loaded = false;
function onImageLoaded() {
if (texLoaded && nightLoaded) {
loaded = true;
$('#loader-text').text('系统加载中.....');
}
}
earth.init = function() {
var startTime = new Date().getTime();
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
var fragCode = shaderLoader.getShaderCode('earth-fragment.glsl');
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);
var vertShader = gl.createShader(gl.VERTEX_SHADER);
var vertCode = shaderLoader.getShaderCode('earth-vertex.glsl');
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
earthShader = gl.createProgram();
gl.attachShader(earthShader, vertShader);
gl.attachShader(earthShader, fragShader);
gl.linkProgram(earthShader);
earthShader.aVertexPosition = gl.getAttribLocation(earthShader, 'aVertexPosition');
earthShader.aTexCoord = gl.getAttribLocation(earthShader, 'aTexCoord');
earthShader.aVertexNormal = gl.getAttribLocation(earthShader, 'aVertexNormal');
earthShader.uPMatrix = gl.getUniformLocation(earthShader, 'uPMatrix');
earthShader.uCamMatrix = gl.getUniformLocation(earthShader, 'uCamMatrix');
earthShader.uMvMatrix = gl.getUniformLocation(earthShader, 'uMvMatrix');
earthShader.uNormalMatrix = gl.getUniformLocation(earthShader, 'uNormalMatrix');
earthShader.uLightDirection = gl.getUniformLocation(earthShader, 'uLightDirection');
earthShader.uAmbientLightColor = gl.getUniformLocation(earthShader, 'uAmbientLightColor');
earthShader.uDirectionalLightColor = gl.getUniformLocation(earthShader, 'uDirectionalLightColor');
earthShader.uSampler = gl.getUniformLocation(earthShader, 'uSampler');
earthShader.uNightSampler = gl.getUniformLocation(earthShader, 'uNightSampler');
texture = gl.createTexture();
var img = new Image();
img.onload = function() {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
console.log('earth.js loaded texture');
texLoaded = true;
onImageLoaded();
};
img.src = '../mercator-tex.jpg';
// img.src = '/mercator-tex-512.jpg';
nightTexture = gl.createTexture();
var nightImg = new Image();
nightImg.onload = function() {
gl.bindTexture(gl.TEXTURE_2D, nightTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, nightImg);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
console.log('earth.js loaded nightearth');
nightLoaded = true;
onImageLoaded();
};
nightImg.src = '../nightearth-4096.png';
// nightImg.src = '/nightearth-512.jpg';
//generate a uvsphere bottom up, CCW order
var vertPos = [];
var vertNorm = [];
var texCoord = [];
var i = 0;
for (var lat = 0; lat <= NUM_LAT_SEGS; lat++) {
var latAngle = (Math.PI / NUM_LAT_SEGS) * lat - (Math.PI / 2);
var diskRadius = Math.cos(Math.abs(latAngle));
var z = Math.sin(latAngle);
// console.log('LAT: ' + latAngle * R2D + ' , Z: ' + z);
for (var lon = 0; lon <= NUM_LON_SEGS; lon++) { //add an extra vertex for texture funness
var lonAngle = (Math.PI * 2 / NUM_LON_SEGS) * lon;
var x = Math.cos(lonAngle) * diskRadius;
var y = Math.sin(lonAngle) * diskRadius;
// console.log('i: ' + i + ' LON: ' + lonAngle * R2D + ' X: ' + x + ' Y: ' + y)
//mercator cylindrical projection (simple angle interpolation)
var v = 1 - (lat / NUM_LAT_SEGS);
var u = 0.5 + (lon / NUM_LON_SEGS); //may need to change to move map
// console.log('u: ' + u + ' v: ' + v);
//normals: should just be a vector from center to point (aka the point itself!
vertPos.push(x * radius);
vertPos.push(y * radius);
vertPos.push(z * radius);
texCoord.push(u);
texCoord.push(v);
vertNorm.push(x);
vertNorm.push(y);
vertNorm.push(z);
i++;
}
}
//ok let's calculate vertex draw orders.... indiv triangles
var vertIndex = [];
for (var lat = 0; lat < NUM_LAT_SEGS; lat++) { //this is for each QUAD, not each vertex, so <
for (var lon = 0; lon < NUM_LON_SEGS; lon++) {
var blVert = lat * (NUM_LON_SEGS + 1) + lon; //there's NUM_LON_SEGS + 1 verts in each horizontal band
var brVert = blVert + 1;
var tlVert = (lat + 1) * (NUM_LON_SEGS + 1) + lon;
var trVert = tlVert + 1;
// console.log('bl: ' + blVert + ' br: ' + brVert + ' tl: ' + tlVert + ' tr: ' + trVert);
vertIndex.push(blVert);
vertIndex.push(brVert);
vertIndex.push(tlVert);
vertIndex.push(tlVert);
vertIndex.push(trVert);
vertIndex.push(brVert);
}
}
vertCount = vertIndex.length;
vertPosBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertPos), gl.STATIC_DRAW);
vertNormBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertNormBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertNorm), gl.STATIC_DRAW);
texCoordBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoord), gl.STATIC_DRAW);
vertIndexBuf = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertIndexBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(vertIndex), gl.STATIC_DRAW);
var end = new Date().getTime() - startTime;
console.log('earth init: ' + end + ' ms');
};
earth.draw = function(pMatrix, camMatrix) {
if (!loaded) return;
var now = new Date();
var j = jday(now.getUTCFullYear(),
now.getUTCMonth() + 1, // Note, this function requires months in range 1-12.
now.getUTCDate(),
now.getUTCHours(),
now.getUTCMinutes(),
now.getUTCSeconds());
j += now.getUTCMilliseconds() * 1.15741e-8; //days per millisecond
var era = satellite.gstime_from_jday(j);
var lightDirection = sun.currentDirection();
vec3.normalize(lightDirection, lightDirection);
var mvMatrix = mat4.create();
mat4.identity(mvMatrix);
mat4.rotateZ(mvMatrix, mvMatrix, era);
mat4.translate(mvMatrix, mvMatrix, earth.pos);
var nMatrix = mat3.create();
mat3.normalFromMat4(nMatrix, mvMatrix);
gl.useProgram(earthShader);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.uniformMatrix3fv(earthShader.uNormalMatrix, false, nMatrix);
gl.uniformMatrix4fv(earthShader.uMvMatrix, false, mvMatrix);
gl.uniformMatrix4fv(earthShader.uPMatrix, false, pMatrix);
gl.uniformMatrix4fv(earthShader.uCamMatrix, false, camMatrix);
gl.uniform3fv(earthShader.uLightDirection, lightDirection);
gl.uniform3fv(earthShader.uAmbientLightColor, [0.03, 0.03, 0.03]); //RGB ambient light
gl.uniform3fv(earthShader.uDirectionalLightColor, [1, 1, 0.9]); //RGB directional light
gl.uniform1i(earthShader.uSampler, 0); //point sampler to TEXTURE0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture); //bind texture to TEXTURE0
gl.uniform1i(earthShader.uNightSampler, 1); //point sampler to TEXTURE1
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, nightTexture); //bind tex to TEXTURE1
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuf);
gl.enableVertexAttribArray(earthShader.aTexCoord);
gl.vertexAttribPointer(earthShader.aTexCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuf);
gl.enableVertexAttribArray(earthShader.aVertexPosition);
gl.vertexAttribPointer(earthShader.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.vertexAttribPointer(gl.pickShaderProgram.aPos, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertNormBuf);
gl.enableVertexAttribArray(earthShader.aVertexNormal);
gl.vertexAttribPointer(earthShader.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertIndexBuf);
gl.drawElements(gl.TRIANGLES, vertCount, gl.UNSIGNED_SHORT, 0);
gl.useProgram(gl.pickShaderProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.uniformMatrix4fv(gl.pickShaderProgram.uMvMatrix, false, mvMatrix); //set up picking
gl.disableVertexAttribArray(gl.pickShaderProgram.aColor);
gl.enableVertexAttribArray(gl.pickShaderProgram.aPos);
gl.drawElements(gl.TRIANGLES, vertCount, gl.UNSIGNED_SHORT, 0);
}
function jday(year, mon, day, hr, minute, sec) { //from satellite.js
'use strict';
return (367.0 * year -
Math.floor((7 * (year + Math.floor((mon + 9) / 12.0))) * 0.25) +
Math.floor(275 * mon / 9.0) +
day + 1721013.5 +
((sec / 60.0 + minute) / 60.0 + hr) / 24.0 // ut in days
//# - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5;
);
}
window.earth = earth;
})();
// **** end earth.js ***
// **** sun.js ***
(function() {
var D2R = Math.PI / 180.0;
function currentDirection() {
var now = new Date();
var j = jday(now.getUTCFullYear(),
now.getUTCMonth() + 1, // Note, this function requires months in range 1-12.
now.getUTCDate(),
now.getUTCHours(),
now.getUTCMinutes(),
now.getUTCSeconds());
j += now.getUTCMilliseconds() * 1.15741e-8; //days per millisecond
return getDirection(j);
}
function getDirection(jd) {
var n = jd - 2451545;
var L = (280.460) + (0.9856474 * n); //mean longitude of sun
var g = (357.528) + (0.9856003 * n); //mean anomaly
L = L % 360.0;
g = g % 360.0;
var ecLon = L + 1.915 * Math.sin(g * D2R) + 0.020 * Math.sin(2 * g * D2R);
var ob = getObliquity(jd);
var x = Math.cos(ecLon * D2R);
var y = Math.cos(ob * D2R) * Math.sin(ecLon * D2R);
var z = Math.sin(ob * D2R) * Math.sin(ecLon * D2R);
return [x, y, z];
//return [1, 0, 0];
}
function getObliquity(jd) {
var t = (jd - 2451545) / 3652500;
var ob = //arcseconds
84381.448
- 4680.93 * t
- 1.55 * Math.pow(t, 2)
+ 1999.25 * Math.pow(t, 3)
- 51.38 * Math.pow(t, 4)
- 249.67 * Math.pow(t, 5)
- 39.05 * Math.pow(t, 6)
+ 7.12 * Math.pow(t, 7)
+ 27.87 * Math.pow(t, 8)
+ 5.79 * Math.pow(t, 9)
+ 2.45 * Math.pow(t, 10);
return ob / 3600.0;
}
function jday(year, mon, day, hr, minute, sec){ //from satellite.js
'use strict';
return (367.0 * year -
Math.floor((7 * (year + Math.floor((mon + 9) / 12.0))) * 0.25) +
Math.floor( 275 * mon / 9.0 ) +
day + 1721013.5 +
((sec / 60.0 + minute) / 60.0 + hr) / 24.0 // ut in days
//# - 0.5*sgn(100.0*year + mon - 190002.5) + 0.5;
);
}
window.sun = {
getDirection: getDirection,
currentDirection: currentDirection
};
})();
// **** end sun.js ***
// **** sat.js ***
/* global browserUnsupported */
/* global satellite */
/* global mat4 */
/* global shaderLoader */
/* global gl */
/* global ColorScheme */
/* global $ */
(function () {
var satSet = {};
var dotShader;
var satPosBuf;
var satColorBuf;
var pickColorBuf;
var pickableBuf;
var currentColorScheme;
var shadersReady = false;
var satPos;
var satVel;
var satAlt;
var satData;
var satExtraData;
var hoveringSat = -1;
var selectedSat = -1;
try {
var satCruncher = new Worker('/scripts/sat-cruncher.js');
} catch (E) {
browserUnsupported();
}
var cruncherReady = false;
var lastDrawTime = 0;
var cruncherReadyCallback;
var gotExtraData = false;
satCruncher.onmessage = function (m) {
if (!gotExtraData) { // store extra data that comes from crunching
var start = performance.now();
satExtraData = JSON.parse(m.data.extraData);
for (var i = 0; i < satSet.numSats; i++) {
satData[i].inclination = satExtraData[i].inclination;
satData[i].eccentricity = satExtraData[i].eccentricity;
satData[i].raan = satExtraData[i].raan;
satData[i].argPe = satExtraData[i].argPe;
satData[i].meanMotion = satExtraData[i].meanMotion;
satData[i].semiMajorAxis = satExtraData[i].semiMajorAxis;
satData[i].semiMinorAxis = satExtraData[i].semiMinorAxis;
satData[i].apogee = satExtraData[i].apogee;
satData[i].perigee = satExtraData[i].perigee;
satData[i].period = satExtraData[i].period;
}
console.log('sat.js copied extra data in ' + (performance.now() - start) + ' ms');
gotExtraData = true;
return;
}
satPos = new Float32Array(m.data.satPos);
satVel = new Float32Array(m.data.satVel);
satAlt = new Float32Array(m.data.satAlt);
if (!cruncherReady) {
$('#load-cover').fadeOut();
satSet.setColorScheme(currentColorScheme); //force color recalc
cruncherReady = true;
if (cruncherReadyCallback) {
cruncherReadyCallback(satData);
}
}
};
satSet.init = function (satsReadyCallback) {
dotShader = gl.createProgram();
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, shaderLoader.getShaderCode('dot-vertex.glsl'));
gl.compileShader(vertShader);
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, shaderLoader.getShaderCode('dot-fragment.glsl'));
gl.compileShader(fragShader);
gl.attachShader(dotShader, vertShader);
gl.attachShader(dotShader, fragShader);
gl.linkProgram(dotShader);
dotShader.aPos = gl.getAttribLocation(dotShader, 'aPos');
dotShader.aColor = gl.getAttribLocation(dotShader, 'aColor');
dotShader.uMvMatrix = gl.getUniformLocation(dotShader, 'uMvMatrix');
dotShader.uCamMatrix = gl.getUniformLocation(dotShader, 'uCamMatrix');
dotShader.uPMatrix = gl.getUniformLocation(dotShader, 'uPMatrix');
$.get('/TLE.json?fakeparameter=to_avoid_browser_cache2', function (resp) {
var startTime = new Date().getTime();
console.log('sat.js downloaded data');
$('#loader-text').text('Crunching numbers...');
satData = resp;
satSet.satDataString = JSON.stringify(satData);
var postStart = performance.now();
satCruncher.postMessage(satSet.satDataString); //kick off satCruncher
var postEnd = performance.now();
//do some processing on our satData response
for (var i = 0; i < satData.length; i++) {
if (satData[i].INTLDES !== null) {
var year = satData[i].INTLDES.substring(0, 2); //clean up intl des for display
// var prefix = (year > 50) ? '19' : '20';
// year = prefix + year;
var rest = satData[i].INTLDES.substring(2);
satData[i].intlDes = year + rest;
} else {
satData[i].intlDes = '(unknown)';
}
satData[i].id = i;
}
//populate GPU mem buffers, now that we know how many sats there are
satPosBuf = gl.createBuffer();
satPos = new Float32Array(satData.length * 3);
var pickColorData = [];
pickColorBuf = gl.createBuffer();
for (var i = 0; i < satData.length; i++) {
var byteR = (i + 1) & 0xff;
var byteG = ((i + 1) & 0xff00) >> 8;
var byteB = ((i + 1) & 0xff0000) >> 16;
pickColorData.push(byteR / 255.0);
pickColorData.push(byteG / 255.0);
pickColorData.push(byteB / 255.0);
}
gl.bindBuffer(gl.ARRAY_BUFFER, pickColorBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pickColorData), gl.STATIC_DRAW);
satSet.numSats = satData.length;
satSet.setColorScheme(ColorScheme.default);
// satSet.setColorScheme(ColorScheme.apogee);
// satSet.setColorScheme(ColorScheme.velocity);
var end = new Date().getTime();
console.log('sat.js init: ' + (end - startTime) + ' ms (incl post: ' + (postEnd - postStart) + ' ms)');
shadersReady = true;
if (satsReadyCallback) {
satsReadyCallback(satData);
}
});
};
satSet.setColorScheme = function (scheme) {
currentColorScheme = scheme;
var buffers = scheme.calculateColorBuffers();
satColorBuf = buffers.colorBuf;
pickableBuf = buffers.pickableBuf;
};
satSet.draw = function (pMatrix, camMatrix) {
if (!shadersReady || !cruncherReady) return;
var now = Date.now();
var dt = Math.min((now - lastDrawTime) / 1000.0, 1.0);
for (var i = 0; i < (satData.length * 3); i++) {
satPos[i] += satVel[i] * dt;
}
gl.useProgram(dotShader);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
gl.uniformMatrix4fv(dotShader.uMvMatrix, false, mat4.create());
gl.uniformMatrix4fv(dotShader.uCamMatrix, false, camMatrix);
gl.uniformMatrix4fv(dotShader.uPMatrix, false, pMatrix);
gl.bindBuffer(gl.ARRAY_BUFFER, satPosBuf);
gl.bufferData(gl.ARRAY_BUFFER, satPos, gl.STREAM_DRAW);
gl.vertexAttribPointer(dotShader.aPos, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, satColorBuf);
gl.enableVertexAttribArray(dotShader.aColor);
gl.vertexAttribPointer(dotShader.aColor, 4, gl.FLOAT, false, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
gl.depthMask(false);
gl.drawArrays(gl.POINTS, 0, satData.length);
gl.depthMask(true);
gl.disable(gl.BLEND);
// now pickbuffer stuff......
gl.useProgram(gl.pickShaderProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.uniformMatrix4fv(gl.pickShaderProgram.uMvMatrix, false, mat4.create());
gl.uniformMatrix4fv(gl.pickShaderProgram.uCamMatrix, false, camMatrix);
gl.uniformMatrix4fv(gl.pickShaderProgram.uPMatrix, false, pMatrix);
gl.bindBuffer(gl.ARRAY_BUFFER, satPosBuf);
gl.enableVertexAttribArray(gl.pickShaderProgram.aPos);
gl.vertexAttribPointer(gl.pickShaderProgram.aPos, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.pickShaderProgram.aColor);
gl.bindBuffer(gl.ARRAY_BUFFER, pickColorBuf);
gl.vertexAttribPointer(gl.pickShaderProgram.aColor, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, pickableBuf);
gl.enableVertexAttribArray(gl.pickShaderProgram.aPickable);
gl.vertexAttribPointer(gl.pickShaderProgram.aPickable, 1, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, satData.length); //draw pick
lastDrawTime = now;
};
satSet.getSat = function (i) {
if (!satData) return null;
var ret = satData[i];
if (!ret) return null;
if (gotExtraData) {
ret.altitude = satAlt[i];
ret.velocity = Math.sqrt(
satVel[i * 3] * satVel[i * 3] +
satVel[i * 3 + 1] * satVel[i * 3 + 1] +
satVel[i * 3 + 2] * satVel[i * 3 + 2]
);
ret.position = {
x: satPos[i * 3],
y: satPos[i * 3 + 1],
z: satPos[i * 3 + 2]
};
}
return ret;
};
satSet.getIdFromIntlDes = function (intlDes) {
for (var i = 0; i < satData.length; i++) {
if (satData[i].INTLDES === intlDes || satData[i].intlDes === intlDes) {
return i;
}
}
return null;
};
satSet.getScreenCoords = function (i, pMatrix, camMatrix) {
var pos = satSet.getSat(i).position;
var posVec4 = vec4.fromValues(pos.x, pos.y, pos.z, 1);
var transform = mat4.create();
vec4.transformMat4(posVec4, posVec4, camMatrix);
vec4.transformMat4(posVec4, posVec4, pMatrix);
var glScreenPos = {
x: (posVec4[0] / posVec4[3]),
y: (posVec4[1] / posVec4[3]),
z: (posVec4[2] / posVec4[3]),
};
return {
x: (glScreenPos.x + 1) * 0.5 * window.innerWidth,
y: (-glScreenPos.y + 1) * 0.5 * window.innerHeight,
};
}
satSet.searchNameRegex = function (regex) {
var res = [];
for (var i = 0; i < satData.length; i++) {
if (regex.test(satData[i].OBJECT_NAME)) {
res.push(i);
}
}
return res;
};
satSet.setHover = function (i) {
if (i === hoveringSat) return;
gl.bindBuffer(gl.ARRAY_BUFFER, satColorBuf);
if (hoveringSat != -1 && hoveringSat != selectedSat) {
gl.bufferSubData(gl.ARRAY_BUFFER, hoveringSat * 4 * 4, new Float32Array(currentColorScheme.colorizer(hoveringSat).color));
}
if (i != -1) {
var sat = satSet.getSat(i);
var hoverColor = window.getSatelliteColor(sat);
gl.bufferSubData(gl.ARRAY_BUFFER, i * 4 * 4, new Float32Array(hoverColor));
}
hoveringSat = i;
};
satSet.selectSat = function (i) {
if (i === selectedSat) return;
gl.bindBuffer(gl.ARRAY_BUFFER, satColorBuf);
if (selectedSat != -1) {
gl.bufferSubData(gl.ARRAY_BUFFER, selectedSat * 4 * 4, new Float32Array(currentColorScheme.colorizer(selectedSat).color));
}
if (i != -1) {
var sat = satSet.getSat(i);
var selectedColor = window.getSatelliteColor(sat);
gl.bufferSubData(gl.ARRAY_BUFFER, i * 4 * 4, new Float32Array(selectedColor));
}
selectedSat = i;
};
satSet.onCruncherReady = function (cb) {
cruncherReadyCallback = cb;
if (cruncherReady) cb;
}
window.satSet = satSet;
})();
// **** end sat.js ***
// **** main.js ***
/* global groups */
/* global ColorScheme */
/* global satSet */
/* global $ */
/* global shaderLoader */
/* global Line */
/* global vec4 */
/* global mat4 */
/* global vec3 */
/* global mat3 */
/* global earth */
/* global searchBox */
/* global Spinner */
/* global sun */
/* global orbitDisplay */
var gl;
var cubeVertIndexBuffer;
var R2D = 180 / Math.PI;
var camYaw = 0;
var camPitch = 0.5;
var camYawTarget = 0;
var camPitchTarget = 0;
var camSnapMode = false;
var camZoomSnappedOnSat = false;
var camAngleSnappedOnSat = false;
var camDistTarget = 10000;
var zoomLevel = 0.5;
var zoomTarget = 0.5;
var ZOOM_EXP = 3;
var DIST_MIN = 6400;
var DIST_MAX = 200000;
var camPitchSpeed = 0;
var camYawSpeed = 0;
var pickFb, pickTex;
var pickColorBuf;
var pMatrix = mat4.create(), camMatrix = mat4.create();
var selectedSat = -1;
var mouseX = 0, mouseY = 0, mouseSat = -1;
var dragPoint = [0, 0, 0];
var screenDragPoint = [0, 0];
var dragStartPitch = 0;
var dragStartYaw = 0;
var isDragging = false;
var dragHasMoved = false;
var initialRotation = true;
var initialRotSpeed = 0.000075;
var debugContext, debugImageData;
var debugLine, debugLine2, debugLine3;
var spinner;
var typeList = [
{
name: '人造卫星',
switchFlag: true,
srcUrl: 1,
Num: 0,
srcName: 'PAYLOAD',
// background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(19,252,1,1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(255, 51, 0, 1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
boxShadow: `0 0 10px 4px #ff3300`,
// boxShadow: `0 0 10px 4px #00ff59`,
},
{
name: '火箭残骸',
switchFlag: true,
srcUrl: 2,
Num: 0,
srcName: 'ROCKETBODY',
// background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(1,56,252,1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(51, 128, 255, 0.85) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
boxShadow: `0 0 10px 4px #3380ff`,
// boxShadow: `0 0 10px 4px #4000ff`,
},
{
name: '空间碎片',
switchFlag: true,
srcUrl: 3,
Num: 0,
srcName: 'DEBRIS',
// background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(252,13,1,1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(128, 128, 128, 0.85) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
boxShadow: `0 0 10px 4px #808080`,
// boxShadow: `0 0 10px 4px #ff0c00`,
},
// {
// name: '待定目标',
// switchFlag: true,
// srcUrl: 4,
// Num: 0,
// srcName: 'TBA',
// background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(119,129,131,1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
// boxShadow: `0 0 10px 4px #828086`,
// },
{
name: '待定目标',
switchFlag: true,
srcUrl: 5,
Num: 0,
srcName: 'UNKNOWN',
// background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(255,158,0,1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
background: `linear-gradient(294deg, rgba(255,255,255,0.3701855742296919) 0%, rgba(255, 255, 0, 1) 47%, rgba(255,255,255,0.4542191876750701) 100%)`,
// boxShadow: `0 0 10px 4px #ff9e00`,
boxShadow: `0 0 10px 4px #ffff00`,
},
]
$(document).ready(function () {
var opts = {
lines: 11, // The number of lines to draw
length: 8, // The length of each line
width: 5, // The line thickness
radius: 8, // The radius of the inner circle
corners: 1, // Corner roundness (0..1)
rotate: 0, // The rotation offset
direction: 1, // 1: clockwise, -1: counterclockwise
color: '#fff', // #rgb or #rrggbb or array of colors
speed: 1, // Rounds per second
trail: 50, // Afterglow percentage
shadow: false, // Whether to render a shadow
hwaccel: false, // Whether to use hardware acceleration
className: 'spinner', // The CSS class to assign to the spinner
zIndex: 2e9, // The z-index (defaults to 2000000000)
top: '50%', // Top position relative to parent
left: '50%' // Left position relative to parent
};
var target = document.getElementById('spinner');
spinner = new Spinner(opts).spin(target);
$('#search-results').perfectScrollbar();
// 渲染类型列表
renderTypeList();
var resizing = false;
setTimeout(() => {
initialRotation = true
}, 30000);
$(window).resize(function () {
if (!resizing) {
window.setTimeout(function () {
resizing = false;
webGlInit();
}, 500);
}
resizing = true;
});
webGlInit();
earth.init();
ColorScheme.init();
satSet.init(function (satData) {
orbitDisplay.init();
groups.init();
searchBox.init(satData);
debugLine = new Line();
debugLine2 = new Line();
debugLine3 = new Line();
});
satSet.onCruncherReady(function (satData) {
//do querystring stuff
var queryStr = window.location.search.substring(1);
var params = queryStr.split('&');
for (var i = 0; i < params.length; i++) {
var key = params[i].split('=')[0];
var val = params[i].split('=')[1];
if (key === 'intldes') {
var urlSatId = satSet.getIdFromIntlDes(val.toUpperCase());
if (urlSatId !== null) {
selectSat(urlSatId);
}
} else if (key === 'search') {
searchBox.doSearch(val);
$('#search').val(val);
}
}
searchBox.init(satData);
});
$('#canvas').on('touchmove', function (evt) {
evt.preventDefault();
if (isDragging) {
dragHasMoved = true;
camAngleSnappedOnSat = false;
camZoomSnappedOnSat = false;
}
mouseX = evt.originalEvent.touches[0].clientX;
mouseY = evt.originalEvent.touches[0].clientY;
});
$('#canvas').mousemove(function (evt) {
if (isDragging) {
dragHasMoved = true;
camAngleSnappedOnSat = false;
camZoomSnappedOnSat = false;
}
mouseX = evt.clientX;
mouseY = evt.clientY;
});
$('#canvas').on('wheel', function (evt) {
var delta = evt.originalEvent.deltaY;
if (evt.originalEvent.deltaMode === 1) {
delta *= 33.3333333;
}
zoomTarget += delta * 0.0002;
if (zoomTarget > 1) zoomTarget = 1;
if (zoomTarget < 0) zoomTarget = 0;
initialRotation = false;
camZoomSnappedOnSat = false;
});
$('#canvas').click(function (evt) {
});
$('#canvas').contextmenu(function () {
return false; //stop right-click menu
});
$('#canvas').mousedown(function (evt) {
// if(evt.which === 3) {//RMB
dragPoint = getEarthScreenPoint(evt.clientX, evt.clientY);
screenDragPoint = [evt.clientX, evt.clientY];
dragStartPitch = camPitch;
dragStartYaw = camYaw;
// debugLine.set(dragPoint, getCamPos());
isDragging = true;
camSnapMode = false;
initialRotation = false;
// }
});
$('#canvas').on('touchstart', function (evt) {
var x = evt.originalEvent.touches[0].clientX;
var y = evt.originalEvent.touches[0].clientY;
dragPoint = getEarthScreenPoint(x, y);
screenDragPoint = [x, y];
dragStartPitch = camPitch;
dragStartYaw = camYaw;
// debugLine.set(dragPoint, getCamPos());
isDragging = true;
camSnapMode = false;
initialRotation = false;
});
$('#canvas').mouseup(function (evt) {
// if(evt.which === 3) {//RMB
if (!dragHasMoved) {
var clickedSat = getSatIdFromCoord(evt.clientX, evt.clientY);
if (clickedSat === -1) searchBox.hideResults();
selectSat(clickedSat);
}
dragHasMoved = false;
isDragging = false;
initialRotation = false;
// }
});
$('#canvas').on('touchend', function (evt) {
dragHasMoved = false;
isDragging = false;
initialRotation = false;
});
$('.menu-item').mouseover(function (evt) {
$(this).children('.submenu').css({
display: 'block'
});
});
$('.menu-item').mouseout(function (evt) {
$(this).children('.submenu').css({
display: 'none'
});
});
$('#zoom-in').click(function () {
zoomTarget -= 0.04;
if (zoomTarget < 0) zoomTarget = 0;
initialRotation = false;
camZoomSnappedOnSat = false;
});
$('#zoom-out').click(function () {
zoomTarget += 0.04;
if (zoomTarget > 1) zoomTarget = 1;
initialRotation = false;
camZoomSnappedOnSat = false;
});
// debugContext = $('#debug-canvas')[0].getContext('2d');
// debugImageData = debugContext.createImageData(debugContext.canvas.width, debugContext.canvas.height);
drawLoop(); //kick off the animationFrame()s
});
function selectSat(satId) {
selectedSat = satId;
if (satId === -1) {
$('#sat-infobox').fadeOut();
orbitDisplay.clearSelectOrbit();
} else {
camZoomSnappedOnSat = true;
camAngleSnappedOnSat = true;
satSet.selectSat(satId);
// camSnapToSat(satId);
var sat = satSet.getSat(satId);
if (!sat) return;
orbitDisplay.setSelectOrbit(satId);
$('#sat-infobox').fadeIn();
$('#sat-info-title').html(sat.OBJECT_NAME);
$('#sat-intl-des').html(sat.intlDes);
$('#sat-type').html(sat.OBJECT_TYPE);
$('#sat-apogee').html(sat.apogee.toFixed(0) + ' km');
$('#sat-perigee').html(sat.perigee.toFixed(0) + ' km');
$('#sat-inclination').html((sat.inclination * R2D).toFixed(2) + '°');
$('#sat-period').html(sat.period.toFixed(2) + ' min');
}
updateUrl();
}
function browserUnsupported() {
$('#canvas-holder').hide();
$('#no-webgl').css('display', 'block');
}
function webGlInit() {
var can = $('#canvas')[0];
can.width = window.innerWidth;
can.height = window.innerHeight;
var gl = can.getContext('webgl', { alpha: false }) || can.getContext('experimental-webgl', { alpha: false });
if (!gl) {
browserUnsupported();
}
gl.viewport(0, 0, can.width, can.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(0x8642); //enable point sprites(?!) This might get browsers with
// underlying OpenGL to behave
//although it's not technically a part of the WebGL standard
var pFragShader = gl.createShader(gl.FRAGMENT_SHADER);
var pFragCode = shaderLoader.getShaderCode('pick-fragment.glsl');
gl.shaderSource(pFragShader, pFragCode);
gl.compileShader(pFragShader);
var pVertShader = gl.createShader(gl.VERTEX_SHADER);
var pVertCode = shaderLoader.getShaderCode('pick-vertex.glsl');
gl.shaderSource(pVertShader, pVertCode);
gl.compileShader(pVertShader);
var pickShaderProgram = gl.createProgram();
gl.attachShader(pickShaderProgram, pVertShader);
gl.attachShader(pickShaderProgram, pFragShader);
gl.linkProgram(pickShaderProgram);
pickShaderProgram.aPos = gl.getAttribLocation(pickShaderProgram, 'aPos');
pickShaderProgram.aColor = gl.getAttribLocation(pickShaderProgram, 'aColor');
pickShaderProgram.aPickable = gl.getAttribLocation(pickShaderProgram, 'aPickable');
pickShaderProgram.uCamMatrix = gl.getUniformLocation(pickShaderProgram, 'uCamMatrix');
pickShaderProgram.uMvMatrix = gl.getUniformLocation(pickShaderProgram, 'uMvMatrix');
pickShaderProgram.uPMatrix = gl.getUniformLocation(pickShaderProgram, 'uPMatrix');
gl.pickShaderProgram = pickShaderProgram;
pickFb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, pickFb);
pickTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, pickTex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); //makes clearing work
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
var rb = gl.createRenderbuffer(); //create RB to store the depth buffer
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, pickTex, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb);
gl.pickFb = pickFb;
pickColorBuf = new Uint8Array(4);
pMatrix = mat4.create();
mat4.perspective(pMatrix, 1.01, gl.drawingBufferWidth / gl.drawingBufferHeight, 20.0, 600000.0);
var eciToOpenGlMat = [
1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1
];
mat4.mul(pMatrix, pMatrix, eciToOpenGlMat); //pMat = pMat * ecioglMat
window.gl = gl;
}
function getCamPos() {
var r = getCamDist();
var z = r * Math.sin(camPitch);
var rYaw = r * Math.cos(camPitch);
var x = rYaw * Math.sin(camYaw);
var y = rYaw * Math.cos(camYaw) * -1;
return [x, y, z];
}
function unProject(mx, my) {
var glScreenX = (mx / gl.drawingBufferWidth * 2) - 1.0;
var glScreenY = 1.0 - (my / gl.drawingBufferHeight * 2);
var screenVec = [glScreenX, glScreenY, -0.01, 1.0]; //gl screen coords
var comboPMat = mat4.create();
mat4.mul(comboPMat, pMatrix, camMatrix);
var invMat = mat4.create();
mat4.invert(invMat, comboPMat);
var worldVec = vec4.create();
vec4.transformMat4(worldVec, screenVec, invMat);
return [worldVec[0] / worldVec[3], worldVec[1] / worldVec[3], worldVec[2] / worldVec[3]];
}
function getEarthScreenPoint(x, y) {
// var start = performance.now();
var rayOrigin = getCamPos();
var ptThru = unProject(x, y);
var rayDir = vec3.create();
vec3.subtract(rayDir, ptThru, rayOrigin); //rayDir = ptThru - rayOrigin
vec3.normalize(rayDir, rayDir);
var toCenterVec = vec3.create();
vec3.scale(toCenterVec, rayOrigin, -1); //toCenter is just -camera pos because center is at [0,0,0]
var dParallel = vec3.dot(rayDir, toCenterVec);
var longDir = vec3.create();
vec3.scale(longDir, rayDir, dParallel); //longDir = rayDir * distParallel
vec3.add(ptThru, rayOrigin, longDir); //ptThru is now on the plane going through the center of sphere
var dPerp = vec3.len(ptThru);
var dSubSurf = Math.sqrt(6371 * 6371 - dPerp * dPerp);
var dSurf = dParallel - dSubSurf;
var ptSurf = vec3.create();
vec3.scale(ptSurf, rayDir, dSurf);
vec3.add(ptSurf, ptSurf, rayOrigin);
return ptSurf;
}
function getCamDist() {
return Math.pow(zoomLevel, ZOOM_EXP) * (DIST_MAX - DIST_MIN) + DIST_MIN;
}
function camSnapToSat(satId) {
/* this function runs every frame that a satellite is seleected. However, the user might have broken out of the
zoom snap or angle snap. If so, don't change those targets. */
var sat = satSet.getSat(satId);
if (camAngleSnappedOnSat) {
var pos = sat.position;
var r = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
var yaw = Math.atan2(pos.y, pos.x) + Math.PI / 2;
var pitch = Math.atan2(pos.z, r);
camSnap(pitch, yaw);
}
if (camZoomSnappedOnSat) {
var camDistTarget = sat.altitude + 6371 + 2000;
zoomTarget = Math.pow((camDistTarget - DIST_MIN) / (DIST_MAX - DIST_MIN), 1 / ZOOM_EXP);
}
}
function camSnap(pitch, yaw) {
camPitchTarget = pitch;
camYawTarget = normalizeAngle(yaw);
camSnapMode = true;
}
function normalizeAngle(angle) {
angle %= Math.PI * 2;
if (angle > Math.PI) angle -= Math.PI * 2;
if (angle < -Math.PI) angle += Math.PI * 2;
return angle;
}
var oldT = new Date();
function drawLoop() {
var newT = new Date();
var dt = Math.min(newT - oldT, 1000);
oldT = newT;
var dragTarget = getEarthScreenPoint(mouseX, mouseY);
if (isDragging) {
if (isNaN(dragTarget[0]) || isNaN(dragTarget[1]) || isNaN(dragTarget[2])
|| isNaN(dragPoint[0]) || isNaN(dragPoint[1]) || isNaN(dragPoint[2])) { //random screen drag
var xDif = screenDragPoint[0] - mouseX;
var yDif = screenDragPoint[1] - mouseY;
var yawTarget = dragStartYaw + xDif * 0.005;
var pitchTarget = dragStartPitch + yDif * -0.005;
camPitchSpeed = normalizeAngle(camPitch - pitchTarget) * -0.005;
camYawSpeed = normalizeAngle(camYaw - yawTarget) * -0.005;
} else { //earth surface point drag
var dragPointR = Math.sqrt(dragPoint[0] * dragPoint[0] + dragPoint[1] * dragPoint[1]);
var dragTargetR = Math.sqrt(dragTarget[0] * dragTarget[0] + dragTarget[1] * dragTarget[1]);
var dragPointLon = Math.atan2(dragPoint[1], dragPoint[0]);
var dragTargetLon = Math.atan2(dragTarget[1], dragTarget[0]);
var dragPointLat = Math.atan2(dragPoint[2], dragPointR);
var dragTargetLat = Math.atan2(dragTarget[2], dragTargetR);
var pitchDif = dragPointLat - dragTargetLat;
var yawDif = normalizeAngle(dragPointLon - dragTargetLon);
camPitchSpeed = pitchDif * 0.015;
camYawSpeed = yawDif * 0.015;
}
camSnapMode = false;
} else {
camPitchSpeed -= (camPitchSpeed * dt * 0.005); //decay speeds when globe is "thrown"
camYawSpeed -= (camYawSpeed * dt * 0.005);
}
camPitch += camPitchSpeed * dt;
camYaw += camYawSpeed * dt;
if (initialRotation) {
camYaw += initialRotSpeed * dt;
}
if (camSnapMode) {
camPitch += (camPitchTarget - camPitch) * 0.003 * dt;
var yawErr = normalizeAngle(camYawTarget - camYaw);
camYaw += yawErr * 0.003 * dt;
/* if(Math.abs(camPitchTarget - camPitch) < 0.002 && Math.abs(camYawTarget - camYaw) < 0.002 && Math.abs(zoomTarget - zoomLevel) < 0.002) {
camSnapMode = false; Stay in camSnapMode forever. Is this a good idea? dunno....
}*/
zoomLevel = zoomLevel + (zoomTarget - zoomLevel) * dt * 0.0025;
} else {
zoomLevel = zoomLevel + (zoomTarget - zoomLevel) * dt * 0.0075;
}
if (camPitch > Math.PI / 2) camPitch = Math.PI / 2;
if (camPitch < -Math.PI / 2) camPitch = -Math.PI / 2;
// camYaw = (camYaw % (Math.PI*2));
camYaw = normalizeAngle(camYaw);
if (selectedSat !== -1) {
var sat = satSet.getSat(selectedSat);
debugLine.set(sat, [0, 0, 0]);
camSnapToSat(selectedSat);
}
drawScene();
updateHover();
updateSelectBox();
requestAnimationFrame(drawLoop);
}
function drawScene() {
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
// gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
camMatrix = mat4.create();
mat4.identity(camMatrix);
mat4.translate(camMatrix, camMatrix, [0, getCamDist(), 0]);
mat4.rotateX(camMatrix, camMatrix, camPitch);
mat4.rotateZ(camMatrix, camMatrix, -camYaw);
gl.useProgram(gl.pickShaderProgram);
gl.uniformMatrix4fv(gl.pickShaderProgram.uPMatrix, false, pMatrix);
gl.uniformMatrix4fv(gl.pickShaderProgram.camMatrix, false, camMatrix);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if (debugLine) debugLine.draw();
if (debugLine2) debugLine2.draw();
if (debugLine3) debugLine3.draw();
earth.draw(pMatrix, camMatrix);
satSet.draw(pMatrix, camMatrix);
orbitDisplay.draw(pMatrix, camMatrix);
/* DEBUG - show the pickbuffer on a canvas */
// debugImageData.data = pickColorMap;
/* debugImageData.data.set(pickColorMap);
debugContext.putImageData(debugImageData, 0, 0);*/
}
function updateSelectBox() {
if (selectedSat === -1) return;
var satData = satSet.getSat(selectedSat);
$('#sat-altitude').html(satData.altitude.toFixed(2) + ' km');
$('#sat-velocity').html(satData.velocity.toFixed(2) + ' km/s');
}
function updateHover() {
if (searchBox.isHovering()) {
var satId = searchBox.getHoverSat();
var satPos = satSet.getScreenCoords(satId, pMatrix, camMatrix);
if (!earthHitTest(satPos.x, satPos.y)) {
hoverBoxOnSat(satId, satPos.x, satPos.y);
} else {
hoverBoxOnSat(-1, 0, 0);
}
} else {
mouseSat = getSatIdFromCoord(mouseX, mouseY);
if (mouseSat !== -1) {
orbitDisplay.setHoverOrbit(mouseSat);
} else {
orbitDisplay.clearHoverOrbit();
}
satSet.setHover(mouseSat);
hoverBoxOnSat(mouseSat, mouseX, mouseY);
}
}
/**
* @description 动态增加分组
* @param {groupName} 分组名称
* @param {groupCode} 分组编码名称
* @param {type} 类型 nameRegex/intlDes
* @property {result} 类型-数据或者检索名称/GLONASS/
*/
function creatGroups(groupName, groupCode, type, result) {
if (!groups) return
if (!groups.SatGroup) return
groups[groupCode] = new groups.SatGroup(type, result)
const ul = document.getElementById('groups-display');
const li = document.createElement('li');
li.setAttribute('data-group', groupCode);
li.textContent = groupName;
ul.appendChild(li);
}
function hoverBoxOnSat(satId, satX, satY) {
if (satId === -1) {
$('#sat-hoverbox').html('(none)');
$('#sat-hoverbox').css({ display: 'none' });
$('#canvas').css({ cursor: 'default' });
} else {
try {
$('#sat-hoverbox').html(satSet.getSat(satId).OBJECT_NAME);
$('#sat-hoverbox').css({
display: 'block',
position: 'absolute',
left: satX + 20,
top: satY - 10
});
$('#canvas').css({ cursor: 'pointer' });
} catch (e) { }
}
}
function getSatIdFromCoord(x, y) {
// var start = performance.now();
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
gl.readPixels(x, gl.drawingBufferHeight - y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pickColorBuf);
var pickR = pickColorBuf[0];
var pickG = pickColorBuf[1];
var pickB = pickColorBuf[2];
return ((pickB << 16) | (pickG << 8) | (pickR)) - 1;
}
function earthHitTest(x, y) {
gl.bindFramebuffer(gl.FRAMEBUFFER, gl.pickFb);
gl.readPixels(x, gl.drawingBufferHeight - y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pickColorBuf);
return (pickColorBuf[0] === 0 &&
pickColorBuf[1] === 0 &&
pickColorBuf[2] === 0);
}
function updateUrl() {
var url = '/';
var paramSlices = [];
if (selectedSat !== -1) {
paramSlices.push('intldes=' + satSet.getSat(selectedSat).intlDes);
}
var currentSearch = searchBox.getCurrentSearch();
if (currentSearch != null) {
paramSlices.push('search=' + currentSearch);
}
if (paramSlices.length > 0) {
url += '?' + paramSlices.join('&');
}
window.history.replaceState(null, 'Stuff in Space', url);
}
// 渲染类型列表
function renderTypeList() {
var container = document.getElementById('tableNum');
if (!container) return;
container.innerHTML = '';
typeList.forEach(function (type) {
var li = document.createElement('li');
li.className = 'liNum';
// 创建颜色指示器
var colorActive = document.createElement('div');
colorActive.className = 'colorActive';
// 设置与类型对应的背景和阴影颜色
colorActive.style.background = type.background;
colorActive.style.boxShadow = type.boxShadow;
// 创建内容容器
var content = document.createElement('div');
content.style.display = 'flex';
content.style.justifyContent = 'space-between';
content.style.alignItems = 'center';
content.innerHTML = `