mapbox-gl-js icon indicating copy to clipboard operation
mapbox-gl-js copied to clipboard

Unable to implement THREEjs Transform Control

Open suryakumara opened this issue 1 year ago • 1 comments

mapbox-gl-js version is 2.6.1:

Question

I'd like to implement transformcontrol threejs in mapbox. First I add cube to the scene and attach it to the control. In current mapbox I have some other layer, Icon, 3d building etc. The problem is I can't hover transformcontrol threejs properly and can't move the cube. Seems this happened because layer of mapbox.

image

Links to related documentation

export class ModelLayerAOA implements mapboxgl.CustomLayerInterface {

  constructor(options: SpriteOption) {
    this.modelOrigin = options.origin;
    this.modelAltitude = options.altitude;
    this.onLoad = options.onLoad;
    this.id = options.id;
    this.type = 'custom';
    this.renderingMode = '3d';
    this.color = options.color;
    const rotate = [Math.PI / 2, THREE.MathUtils.degToRad(options.rotateY), 0];
    const mercator = mapboxgl.MercatorCoordinate.fromLngLat(
      this.modelOrigin,
      this.modelAltitude,
    );

    const scale = mercator.meterInMercatorCoordinateUnits();

    this.modelTransform = {
      translateX: mercator.x,
      translateY: mercator.y,
      translateZ: mercator.z ?? 0,
      rotateX: rotate[0],
      rotateY: rotate[1],
      rotateZ: rotate[2],
      scale,
    };
  }

  async onAdd(map: mapboxgl.Map, gl: WebGLRenderingContext) {
    this.map = map;

    this.camera = new THREE.PerspectiveCamera(75);
    this.camera.position.z = 1;
    this.scene = new THREE.Scene();

    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    this.scene.add(ambientLight);

    const geometry = new THREE.BoxGeometry(1, 1, 1);
    const material = new THREE.MeshBasicMaterial({
      color: 0x00ff00,
      transparent: true,
      opacity: 0.3,
    });

    this.renderer = new THREE.WebGLRenderer({
      canvas: map.getCanvas(),
      context: gl,
      antialias: true,
    });

    if (this.renderer?.domElement && this.camera) {
      this.transformControl = new TransformControls(
        this.camera,
        this.renderer?.domElement,
      );
      this.transformControl.size = 5;
      this.transformControl.addEventListener('change', this.updateRender);
    }

    const cube = new THREE.Mesh(geometry, material);

    let pos = new THREE.Vector3();
    cube.getWorldPosition(pos);

    this.model = cube;

    this.renderer.autoClear = false;
    this.addTransformControl();
  }

  addTransformControl = () => {
    if (this.transformControl && this.model) {
      this.transformControl?.attach(this.model);
      this.scene?.add(this.transformControl);
      this.scene?.add(this.model);
    }
  };

  onRemove(map: mapboxgl.Map) {
    map.removeLayer(this.id);
    if (this.model) {
      this.scene?.remove(this.model);
    }
    this.camera?.clear();
    this.scene?.clear();
  }

  render(gl: WebGLRenderingContext, matrix: number[]) {
    const modelTransform = this.modelTransform;
    const rotationX = new THREE.Matrix4().makeRotationAxis(
      new Vector3(1, 0, 0),
      modelTransform.rotateX,
    );
    const rotationY = new THREE.Matrix4().makeRotationAxis(
      new Vector3(0, 1, 0),
      modelTransform.rotateY,
    );
    const rotationZ = new THREE.Matrix4().makeRotationAxis(
      new Vector3(0, 0, 1),
      modelTransform.rotateZ,
    );

    const m = new THREE.Matrix4().fromArray(matrix);
    const l = new THREE.Matrix4()
      .makeTranslation(
        modelTransform.translateX,
        modelTransform.translateY,
        modelTransform.translateZ,
      )
      .scale(
        new Vector3(
          modelTransform.scale,
          -modelTransform.scale,
          modelTransform.scale,
        ),
      )
      .multiply(rotationX)
      .multiply(rotationY)
      .multiply(rotationZ);

    if (this.camera && this.scene && this.map) {
      this.camera.projectionMatrix.elements = matrix;
      this.camera.projectionMatrix = m.multiply(l);
      this.renderer?.resetState();
      this.updateRender();
      this.map.triggerRepaint();
    }
  }

  updateRender = () => {
    if (this.scene && this.camera) {
      this.renderer?.render(this.scene, this.camera);
    }
  };
}

suryakumara avatar Sep 07 '22 06:09 suryakumara

Can you please provide a live reproducible test case?

mourner avatar Sep 08 '22 08:09 mourner

I am closing this issue due to inactivity. Please feel free to re-open with a minimal reproducible code example with jsbin or codepen. Thanks

avpeery avatar Nov 08 '22 16:11 avpeery

@suryakumara Did you have solution? I have same problem :((

Amela-Son avatar Aug 01 '23 07:08 Amela-Son

I have no Idea to solve this..sp I open 2 viewer threejs and mapbox. if you want to transform any 3d. @Amela-Son

suryakumara avatar Aug 01 '23 08:08 suryakumara

@suryakumara In my case, I display model location at specific location on map. So if load 2 viewer, it is not correct about coordinate. Do you have any suggest for my case :((

Amela-Son avatar Aug 01 '23 08:08 Amela-Son

@Amela-Son You can make a function to convert Geolocation + altitude to 3D world coordinate.

suryakumara avatar Aug 01 '23 08:08 suryakumara

@suryakumara Do you have any example about this. I try but it not work

Amela-Son avatar Aug 02 '23 01:08 Amela-Son