Home > Enterprise >  THREE.js: Set absolute rotation of sphere, rather than cumulative rotation?
THREE.js: Set absolute rotation of sphere, rather than cumulative rotation?

Time:03-17

I'd like to be able to set the rotation of a Three.js sphere to an absolute value, but whenever I set rotateY the value I apply is added or subtracted from the last rotation, rather than becoming a new absolute rotation setting.

In a related answer about a cube (Three.js Set absolute local rotation), the cube has a rotation attribute, and cube.rotation.x = someValue results in the kind of absolute rotation that I'm looking for.

But the SphereGeometry object that I'm using (with a world map as its texture) has no rotation attribute.

I suppose I could keep track of previous rotations, and apply only the difference, but I'd think that would suffer eventually from cumulative round-off errors.

Is there another way to do this? A reset method of some sort?

  async orient(lon: number, lat: number): Promise<void> {
    if (Globe.mapFailed)
      throw new Error('Map not available');
    else if (!Globe.mapImage)
      await new Promise<void>((resolve, reject) => Globe.waitList.push({ resolve, reject }));

    if (!this.initialized) {
      this.camera = new PerspectiveCamera(FIELD_OF_VIEW, 1);
      this.scene = new Scene();
      this.globe = new SphereGeometry(GLOBE_RADIUS, 50, 50);

      const mesh = new Mesh(
        this.globe,
        new MeshBasicMaterial({
          map: new CanvasTexture(Globe.mapCanvas)
        })
      );

      this.renderer = new WebGLRenderer({ alpha: true });
      this.renderer.setSize(GLOBE_PIXEL_SIZE, GLOBE_PIXEL_SIZE);
      this.rendererHost.appendChild(this.renderer.domElement);
      this.scene.add(mesh);
      this.camera.position.z = VIEW_DISTANCE;
      this.camera.rotation.order = 'YXZ';
      this.initialized = true;
    }

    this.globe.rotateY(PI / 20); // Just a sample value I experimented with
    this.camera.rotation.z = (lat >= 0 ? PI : 0);
    requestAnimationFrame(() => this.renderer.render(this.scene, this.camera));
  }

Update:

My workaround for now is this:

    this.globe.rotateX(-this.lat);
    this.globe.rotateY(this.lon);
    this.lon = to_radian(lon);
    this.lat = to_radian(lat);
    this.globe.rotateY(-this.lon);
    this.globe.rotateX(this.lat);

I'm saving the previous rotations which have been done so that I can undo them, then apply new rotations. (Degree/radian conversions, and the sign of the longitude rotation needing to be reversed, obscures the process a bit.)

CodePudding user response:

I think you're confusing geometry.rotateY(rot) with mesh.rotation.y = rot. As explained in the docs:

.rotateY(): Rotate the geometry about the Y axis. This is typically done as a one time operation, and not during a loop. Use Object3D.rotation for typical real-time mesh rotation.

geometry.rotateY(rot) should only be used once because it updates the values of all the vertex positions, so it has to iterate through every vertex and update it. This is useful if you need to modify the "original state" of your geometry, for example a character model that needs to start facing down the z-axis.

mesh.rotation.y = rot; is what you're probably looking for. This is what you use during realtime rotations, so the intrinsic vertex positions are left untouched, you're just rotating the mesh as a whole. For example, when your character is running all over the map.

this.mesh = new Mesh(geometry, material);

// Set rotation to an absolute rotation value
this.mesh.rotation.y = Math.PI / 20;

// Increment rotation a relative amount (like once per frame):
this.mesh.rotation.y  = Math.PI / 20;
  • Related