Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Satellites should have a rotational motion in addition to its translational motion #71

Open
zen85 opened this issue Jan 31, 2023 · 4 comments

Comments

@zen85
Copy link

zen85 commented Jan 31, 2023

i love way to much playing around with this but now i ran into a problem i can not solve...

I want to change the satellites example so that i can change the satGeometry to
const satGeometry = new THREE.BoxGeometry(10, 5,2);

it becomes very apparent that the meshes of the satellites do not have rotational motion corresponding to the direction they are heading to.

It would be ideal if the meshes of the satellites would behave like animated planes would.

I tried something like this in my frameTicker

var object_needing_rotational_motion = Globe.getObjectByProperty('noradID', 25544);
console.log(object_needing_rotational_motion .position);
var cartesiancords = object_needing_rotational_motion .position;
var Hoveringabove = Globe.toGeoCoords(cartesiancords);
Hoveringabove.altitude = Hoveringabove.altitude + 1;
var lookatposition = Globe.getCoords(Hoveringabove.lat, Hoveringabove.lng, Hoveringabove.altitude );
console.log(lookatposition);
var obenvector = new THREE.Vector3(lookatposition.x, lookatposition.y, lookatposition.z );
console.log(obenvector);
			
const direction_point = new THREE.Vector3().subVectors(looktovector, object_needing_rotational_motion .position).normalize();
object_needing_rotational_motion .quaternion.setFromUnitVectors(object_needing_rotational_motion .up, direction_point);

which works kind of but still rotates around one axis.

so i tried to put a carot in front of the satellite but with the same outcome on a different axis... :(

	var objekt_zum_ausrichten = Globe.getObjectByProperty('noradID', 25544);
			
	const result = satData_strom.find(sat => sat.object_norad_id === 25544);
	var looktovector = new THREE.Vector3(0, 0, 0 );
			
	var futuretime = time;
			
	futuretime.setMinutes(futuretime.getMinutes()+1);
			
	var ecifuture = satellite.propagate(result.satrec, futuretime);
				
	if (ecifuture.position) {
				
	  const gmst = satellite.gstime(time);
					
	  const gdPos = satellite.eciToGeodetic(ecifuture.position, gmst);
	  result.lat = satellite.radiansToDegrees(gdPos.latitude);
	  result.lng = satellite.radiansToDegrees(gdPos.longitude);
	  result.alt = gdPos.height / EARTH_RADIUS_KM
					
	  var future_pos = Globe.getCoords(result.lat, result.lng, result.alt);
	  looktovector = new THREE.Vector3(future_pos.x, future_pos.y, future_pos.z );

   }
				
futuretime.setMinutes(futuretime.getMinutes()-1);
				
ecifuture = satellite.propagate(result.satrec, futuretime);
				
if (ecifuture.position) {
				
const gmst = satellite.gstime(time);
				
const gdPos = satellite.eciToGeodetic(ecifuture.position, gmst);
				
result.lat = satellite.radiansToDegrees(gdPos.latitude);
result.lng = satellite.radiansToDegrees(gdPos.longitude);
result.alt = gdPos.height / EARTH_RADIUS_KM
				 

					
const direction_point = new THREE.Vector3().subVectors(looktovector, objekt_zum_ausrichten.position).normalize();
objekt_zum_ausrichten.quaternion.setFromUnitVectors(objekt_zum_ausrichten.up, direction_point);
				
//objekt_zum_ausrichten.rotateX(199);

Any Ideas?

@zen85
Copy link
Author

zen85 commented Feb 1, 2023

i just created the the shortest code possible to show my problem and to do tryouts:
the bigger cube represents the ISS and like the real ISS it should have rotationial motion so the "cupola" on it always faces the earth. in my case this is "var object_that_should_face_forward"

<head>
  <style>
    body { margin: 0; }

    #time-log {
      position: absolute;
      font-size: 12px;
      font-family: sans-serif;
      padding: 5px;
      border-radius: 3px;
      background-color: rgba(200, 200, 200, 0.1);
      color: lavender;
      bottom: 10px;
      right: 10px;
    }
  </style>

  <script src="//unpkg.com/satellite.js/dist/satellite.min.js"></script>

  <script src="//unpkg.com/three"></script>
  <script src="//unpkg.com/three-globe"></script>
<!--  <script src="../../dist/three-globe.js"></script>-->
</head>

<body>
  <div id="globeViz"></div>
  <div id="time-log"></div>

  <script type="importmap">{ "imports": { "three": "https://unpkg.com/three/build/three.module.js" }}</script>
  <script type="module">
    import { TrackballControls } from '//unpkg.com/three/examples/jsm/controls/TrackballControls.js';
    Object.assign(THREE , { TrackballControls });

    const EARTH_RADIUS_KM = 6371; // km
    const SAT_SIZE = 80; // km
    const TIME_STEP = 3 * 1000; // per frame

    const timeLogger = document.getElementById('time-log');

    const Globe = new ThreeGlobe()
      .globeImageUrl('//unpkg.com/three-globe/example/img/earth-blue-marble.jpg')
      .objectLat('lat')
      .objectLng('lng')
      .objectAltitude('alt');


    const issGeometry = new THREE.BoxGeometry(20, 15,12);
    const satGeometry = new THREE.BoxGeometry(10, 5,2);
    const satMaterial = new THREE.MeshLambertMaterial({ color: 'palegreen', transparent: true, opacity: 0.7 });
          Globe.objectThreeObject(d => {

	let mesh;

	   if (d.name === 'ISS (ZARYA)') {
		mesh = new THREE.Mesh(issGeometry, satMaterial);
		mesh.meshname = d.name;
		console.log(mesh.noradID);
	  } else {
		mesh = new THREE.Mesh(satGeometry, satMaterial);
		mesh.meshname = d.name;
	  }

	  return mesh;
	
      });
	  

    fetch('https://www.josefwagner.net/valentin/satellites2.txt').then(r => r.text()).then(rawData => {
      const tleData = rawData.replace(/\r/g, '').split(/\n(?=[^12])/).map(tle => tle.split('\n'));
      const satData = tleData.map(([name, ...tle]) => ({
        satrec: satellite.twoline2satrec(...tle),
        name: name.trim().replace(/^0 /, '')
      }));

      // time ticker
      let time = new Date();
      (function frameTicker() {
        requestAnimationFrame(frameTicker);

        time = new Date(+time + TIME_STEP);
        timeLogger.innerText = time.toString();

        // Update satellite positions
        const gmst = satellite.gstime(time);
        satData.forEach(d => {
          const eci = satellite.propagate(d.satrec, time);
          if (eci.position) {
            const gdPos = satellite.eciToGeodetic(eci.position, gmst);
            d.lat = satellite.radiansToDegrees(gdPos.latitude);
            d.lng = satellite.radiansToDegrees(gdPos.longitude);
            d.alt = gdPos.height / EARTH_RADIUS_KM;

			if (d.name === "ISS (ZARYA)") {
				var object_that_should_face_forward = Globe.getObjectByProperty('meshname', "ISS (ZARYA)");
			}
			
          }

        });

		Globe.objectsData(satData);
		
      })();
    });


    // Setup renderer
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.getElementById('globeViz').appendChild(renderer.domElement);

    // Setup scene
    const scene = new THREE.Scene();
    scene.add(Globe);
    scene.add(new THREE.AmbientLight(0xbbbbbb));
    scene.add(new THREE.DirectionalLight(0xffffff, 0.6));

    // Setup camera
    const camera = new THREE.PerspectiveCamera();
    camera.aspect = window.innerWidth/window.innerHeight;
    camera.updateProjectionMatrix();
    camera.position.z = 400;

    // Add camera controls
    const tbControls = new THREE.TrackballControls(camera, renderer.domElement);
    tbControls.minDistance = 101;
    tbControls.rotateSpeed = 5;
    tbControls.zoomSpeed = 0.8;

    // Kick-off renderer
    (function animate() { // IIFE
      // Frame cycle
      tbControls.update();
      renderer.render(scene, camera);
      requestAnimationFrame(animate);
    })();
  </script>
</body>

@vasturiano
Copy link
Owner

@zen85 just a heads up that there's two new props on the objects layer that might help with your use case. They are:

  • objectFacesSurface
  • objectRotation

The first one, if enabled changes the object orientation to always follow the curve of the globe. While the second allows you to apply any additional custom rotation to the object.

So for your case, I believe one way to achieve it would be to enable objectFacesSurface and then apply a rotation on the z axis (the one that points away from the globe) that matches the moving direction of the object. This direction needs to be computed by you as the module doesn't have a notion of movement, only individual positions. But you can do it by calculating the angle between two adjacent set of coordinates (lat, lng) in the motion.

Let me know if this works out for you. And an example on https://codepen.io/ would be great. 👍

@zen85
Copy link
Author

zen85 commented Apr 21, 2023

oh that sound amazing! i am addicted to this...

I already built a monster of a solution which worked though already. funny enough it stopped working 2 days ago with 2.26.0 so i took a bit of a dive now and i found out while doing:

(please mind that i added the property "noradID" to the satellitemeshes for easy identification :) );

var objekt_zum_ausrichten = Globe.getObjectByProperty('noradID', params.cam_to_norad);
console.log(objekt_zum_ausrichten.position);

gave me proper value just gives me 0,0,0 now. if i switch back to 2.25.7 i get proper values so something must have happened :)

I dont know yet what changed since i cant see your changes to the new version apparently? why does the .position value not have proper values anymore for meshes in this layer?

@vasturiano
Copy link
Owner

vasturiano commented Apr 21, 2023

Ah, I see. Because of the rotation functionality the structure of the ThreeJS object being added to the globe has changed.

From v2.26 on we wrap your user object in a group, to which we apply the necessary rotation (to face the surface) and position. And the specific local rotation gets applied to the object directly.

Previously rotation didn't exist and positioning was applied directly to the object. That's why you see that change.

So, you could theoretically now extract the position from the parent, but taking a step back, it's better if you don't rely on the inner works and structure of an globe object directly in your app as these are bound to change from time to time and without notice, due to refactors and new functionality, etc. They're internal implementation details, if you will.

And btw, here's the commit with the relevant changes, you should be able to see that:
9f55038

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants