import { Layer } from "./Layer";
import * as THREE from 'three';
import { llhToCartesianFastSFCT } from '../GeoUtils.js';
const cartesianLocation = new THREE.Vector3();
const Up = new THREE.Vector3();
const East = new THREE.Vector3();
const North = new THREE.Vector3();
const globalNorth = new THREE.Vector3(0,0,1);
const quaternionToEarthNormalOrientation = new THREE.Quaternion();
const quaternionSelfRotation = new THREE.Quaternion();
const rotationMatrix = new THREE.Matrix4();
const rotation = new THREE.Euler(0,0,0, "ZYX");
/**
* A layer for loading and geolocating a three.js Object3D.
* Note that disposing of resources is not done automatically as the meshes, materials and textures may be used elsewhere.
* It's up to the user to dispose of these resources after calling this layer's #dispose method.
* @class
* @extends Layer
*/
class ObjectLayer extends Layer {
/**
*
* @param {Object} properties
* @param {String|Number} properties.id layer id should be unique
* @param {String} properties.name the name can be anything you want and is intended for labeling
* @param {THREE.Object3D} [properties.object] a three.js object to add to the scene and georeference
* @param {Number} [properties.longitude = 0] (optional) longitude of the model's center point in degrees.
* @param {Number} [properties.latitude = 0] (optional) latitude of the model's center point in degrees.
* @param {Number} [properties.height = 0] (optional) height in meters above sea level.
* @param {Number} [properties.yaw = 0] (optional) a yaw means the original z axis points north. ccw rotation about the model's local y axis.
* @param {Number} [properties.pitch = 0] (optional) pitch rotates the model about it's local x axis. a pitch of 0 alligns it with the horizon, negative values tilts it downwards, positive values tilts it up.
* @param {Number} [properties.roll = 0] (optional) roll around the model's local z axis.
* @param {Number} [properties.scaleX = 1] (optional) scale on X axes.
* @param {Number} [properties.scaleY = 1] (optional) scale on Y axes. defaults to the scaleX property if defined.
* @param {Number} [properties.scaleZ = 1] (optional) scale on Z axes. defaults to the scaleX property if defined.
* @param {Boolean} [properties.visible = true] layer will be rendered if true (true by default)
*/
constructor(properties) {
if (!properties || !properties.object) {
throw "Bad instanciation, ObjectLayer misses required properties."
}
super(properties);
this.isObjectLayer = true;
this.object = properties.object;
this.object3D = new THREE.Object3D();
this.object3D.matrixAutoUpdate = false;
this.object3D.add(this.object);
const self = this;
self.properties = properties;
self.move(properties.longitude, properties.latitude, properties.height, properties.yaw, properties.pitch, properties.roll, properties.scaleX, properties.scaleY?properties.scaleY:properties.scaleX, properties.scaleZ?properties.scaleZ:properties.scaleX);
}
/**
* Sets the object position and orientation based on Longitude, Latitude, Height, Yaw, Pitch, Roll
*
* @param {number} [longitude = 0] - a longitude in degrees
* @param {number} [latitude = 0] - a latitude in degrees
* @param {number} [height = 0] - a height in meters above WGS 84 sea level
* @param {number} [yaw = 0] - Yaw angle in degrees. (0 points north ccw rotation)
* @param {number} [pitch = 0] - Pitch angle in degrees (-90 to 90)
* @param {number} [roll = 0] - Roll angle in degrees.
* @param {number} [scaleX = 1] - scale on X axes.
* @param {number} [scaleY = 1] - scale on Y axes. defaults to the scaleX property if defined.
* @param {number} [scaleZ = 1] - scale on Z axes. defaults to the scaleX property if defined.
*/
move(longitude = 0, latitude = 0, height = 0, yaw = 0, pitch = 0, roll = 0, scaleX = 1, scaleY = 1, scaleZ = 1 ) {
// Step 1: Clamp pitch to avoid gimbal lock
//pitch = THREE.MathUtils.clamp(pitch, -89.9, 89.9);
rotation.set(
pitch*0.0174533, yaw*0.0174533, roll*0.0174533, "ZYX");
cartesianLocation.set(longitude, latitude, height);
llhToCartesianFastSFCT(cartesianLocation, false); // Convert LLH to Cartesian in-place
Up.copy(cartesianLocation).normalize();
East.crossVectors(Up, globalNorth).normalize();
if (East.lengthSq() === 0) {
East.set(1, 0, 0);
}
North.crossVectors(East, Up).normalize();
rotationMatrix.makeBasis(East, Up, North);
quaternionToEarthNormalOrientation.setFromRotationMatrix(rotationMatrix);
quaternionSelfRotation.setFromEuler(rotation);
this.object3D.quaternion.copy(quaternionToEarthNormalOrientation).multiply(quaternionSelfRotation);
this.object3D.position.copy(cartesianLocation);
this.object3D.scale.set(scaleX, scaleY, scaleZ);
this._updateMatrices();
}
/**
* Updates the object's local and world matrices.
*/
_updateMatrices() {
this.object3D.updateMatrix();
this.object3D.updateMatrixWorld(true);
}
_setPlanet(planet) {
this.planet = planet;
}
_addToScene(scene) {
this.scene = scene;
scene.add(this.object3D);
}
setVisible(visible) {
if (visible) {
this.object3D.layers.enable(0);
} else {
this.object3D.layers.disable(0);
}
super.setVisible(visible);
}
dispose() {
this.scene.remove(this.object3D);
}
}
export { ObjectLayer }