|
@@ -0,0 +1,718 @@
|
|
|
|
|
+// 模型编辑控制器
|
|
|
|
|
+class ModelEditor {
|
|
|
|
|
+ constructor(viewer, entity) {
|
|
|
|
|
+ this.viewer = viewer;
|
|
|
|
|
+ this.entity = entity;
|
|
|
|
|
+ this.handler = null;
|
|
|
|
|
+ this.activePrimitive = null;
|
|
|
|
|
+ this.activeAxis = null;
|
|
|
|
|
+ this.startMousePosition = null;
|
|
|
|
|
+ this.startMouseCartesian = null;
|
|
|
|
|
+ this.dragMode = null;
|
|
|
|
|
+
|
|
|
|
|
+ this.translateEnabled = true;
|
|
|
|
|
+ this.rotateEnabled = true;
|
|
|
|
|
+ this.scaleEnabled = true;
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives = [];
|
|
|
|
|
+ this.originPoint = null;
|
|
|
|
|
+ this.moveAxes = [];
|
|
|
|
|
+ this.axisPlanes = [];
|
|
|
|
|
+ this.rotateAxes = [];
|
|
|
|
|
+ this.scalePoints = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 获取 primitives 的辅助方法
|
|
|
|
|
+ this.primitivesCollection = this.viewer.primitives || (this.viewer.scene && this.viewer.scene.primitives);
|
|
|
|
|
+
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+
|
|
|
|
|
+ if (this.entity.model) {
|
|
|
|
|
+ if (this.entity.model.readyPromise) {
|
|
|
|
|
+ this.entity.model.readyPromise.then(() => {
|
|
|
|
|
+ console.log('模型加载完成(通过 readyPromise)');
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ console.log('模型加载失败,使用当前位置初始化枢轴');
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ });
|
|
|
|
|
+ } else if (this.entity.model.boundingSphere) {
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log('模型没有 readyPromise,等待 boundingSphere');
|
|
|
|
|
+ this.entity.model.definitionChanged.addEventListener(() => {
|
|
|
|
|
+ if (this.entity.model.boundingSphere) {
|
|
|
|
|
+ console.log('模型加载完成(通过 definitionChanged)');
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ if (!this.entity.model.boundingSphere) {
|
|
|
|
|
+ console.log('模型加载超时,使用当前位置初始化枢轴');
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 3000);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async waitForModelLoad() {
|
|
|
|
|
+ if (this.initPromise) {
|
|
|
|
|
+ await this.initPromise;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ getCenter() {
|
|
|
|
|
+ // 首先尝试从 entity.position 获取中心点
|
|
|
|
|
+ if (this.entity.position) {
|
|
|
|
|
+ const position = this.entity.position.getValue(this.viewer.clock.currentTime);
|
|
|
|
|
+ if (position) {
|
|
|
|
|
+ return position;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试多种方式获取模型中心点
|
|
|
|
|
+ if (this.entity.model) {
|
|
|
|
|
+ // 尝试直接访问 boundingSphere
|
|
|
|
|
+ if (this.entity.model.boundingSphere) {
|
|
|
|
|
+ return this.entity.model.boundingSphere.center;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 尝试通过 _boundingSphere 访问
|
|
|
|
|
+ if (this.entity.model._boundingSphere) {
|
|
|
|
|
+ return this.entity.model._boundingSphere.center;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 尝试通过 _primitive 访问
|
|
|
|
|
+ if (this.entity.model._primitive && this.entity.model._primitive.boundingSphere) {
|
|
|
|
|
+ return this.entity.model._primitive.boundingSphere.center;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果实体有 position 属性但没有获取到值,尝试获取实体的默认位置
|
|
|
|
|
+ if (this.entity.position) {
|
|
|
|
|
+ return this.entity.position.getValue(this.viewer.clock.currentTime) || this.viewer.camera.position;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 返回相机位置作为默认值
|
|
|
|
|
+ return this.viewer.camera.position;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ getRadius() {
|
|
|
|
|
+ // 首先尝试从模型获取半径
|
|
|
|
|
+ if (this.entity.model) {
|
|
|
|
|
+ // 尝试直接访问 boundingSphere
|
|
|
|
|
+ if (this.entity.model.boundingSphere) {
|
|
|
|
|
+ return this.entity.model.boundingSphere.radius;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 尝试通过 _boundingSphere 访问
|
|
|
|
|
+ if (this.entity.model._boundingSphere) {
|
|
|
|
|
+ return this.entity.model._boundingSphere.radius;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 尝试通过 _primitive 访问
|
|
|
|
|
+ if (this.entity.model._primitive && this.entity.model._primitive.boundingSphere) {
|
|
|
|
|
+ return this.entity.model._primitive.boundingSphere.radius;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 使用较小的默认半径
|
|
|
|
|
+ return 2;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ init() {
|
|
|
|
|
+ // 先清理旧的 primitives
|
|
|
|
|
+ this.clearPrimitives();
|
|
|
|
|
+
|
|
|
|
|
+ this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);
|
|
|
|
|
+ this.createPrimitives();
|
|
|
|
|
+ this.addEventListener();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createPrimitives(isModel = true) {
|
|
|
|
|
+ this.createOriginPoint();
|
|
|
|
|
+
|
|
|
|
|
+ if (this.translateEnabled) {
|
|
|
|
|
+ this.createMoveAxis();
|
|
|
|
|
+ this.createAxisPlane();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isModel && this.rotateEnabled) {
|
|
|
|
|
+ this.createRotateAxis();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isModel && this.scaleEnabled) {
|
|
|
|
|
+ this.createScaleAxis();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ clearPrimitives() {
|
|
|
|
|
+ this.primitives.forEach(p => {
|
|
|
|
|
+ if (this.primitivesCollection) {
|
|
|
|
|
+ this.primitivesCollection.remove(p);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ this.primitives = [];
|
|
|
|
|
+ this.moveAxes = [];
|
|
|
|
|
+ this.axisPlanes = [];
|
|
|
|
|
+ this.rotateAxes = [];
|
|
|
|
|
+ this.scalePoints = [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createOriginPoint() {
|
|
|
|
|
+ if (!this.center) return;
|
|
|
|
|
+
|
|
|
|
|
+ const primitives = this.primitivesCollection;
|
|
|
|
|
+ if (!primitives) return;
|
|
|
|
|
+
|
|
|
|
|
+ const size = Math.max(this.radius * 0.05, 0.1);
|
|
|
|
|
+
|
|
|
|
|
+ const primitive = primitives.add(new Cesium.Primitive({
|
|
|
|
|
+ geometryInstances: new Cesium.GeometryInstance({
|
|
|
|
|
+ geometry: new Cesium.BoxGeometry({
|
|
|
|
|
+ vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR,
|
|
|
|
|
+ maximum: new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + size,
|
|
|
|
|
+ this.center.y + size,
|
|
|
|
|
+ this.center.z + size
|
|
|
|
|
+ ),
|
|
|
|
|
+ minimum: new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x - size,
|
|
|
|
|
+ this.center.y - size,
|
|
|
|
|
+ this.center.z - size
|
|
|
|
|
+ )
|
|
|
|
|
+ }),
|
|
|
|
|
+ attributes: {
|
|
|
|
|
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE.withAlpha(0.8))
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ appearance: new Cesium.PerInstanceColorAppearance({
|
|
|
|
|
+ flat: true
|
|
|
|
|
+ })
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives.push(primitive);
|
|
|
|
|
+ this.originPoint = primitive;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ getModelRotationMatrix() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ console.log('开始获取模型旋转矩阵');
|
|
|
|
|
+ const entity = this.entity;
|
|
|
|
|
+
|
|
|
|
|
+ if (entity.model) {
|
|
|
|
|
+ const model = entity.model;
|
|
|
|
|
+ console.log('model 属性:', Object.keys(model));
|
|
|
|
|
+ console.log('model._nodeTransformations:', model._nodeTransformations);
|
|
|
|
|
+
|
|
|
|
|
+ if (model._nodeTransformations) {
|
|
|
|
|
+ console.log('遍历 nodeTransformations:');
|
|
|
|
|
+ for (const key in model._nodeTransformations) {
|
|
|
|
|
+ const node = model._nodeTransformations[key];
|
|
|
|
|
+ console.log(' 节点:', key, node);
|
|
|
|
|
+ if (node.rotation) {
|
|
|
|
|
+ console.log(' rotation:', node.rotation);
|
|
|
|
|
+ const rot = Cesium.Matrix3.fromQuaternion(node.rotation, new Cesium.Matrix3());
|
|
|
|
|
+ console.log('从 nodeTransformations.rotation 获取旋转矩阵成功');
|
|
|
|
|
+ return rot;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (node.matrix) {
|
|
|
|
|
+ console.log(' matrix:', node.matrix);
|
|
|
|
|
+ const mat = new Cesium.Matrix4(
|
|
|
|
|
+ node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3],
|
|
|
|
|
+ node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7],
|
|
|
|
|
+ node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11],
|
|
|
|
|
+ node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15]
|
|
|
|
|
+ );
|
|
|
|
|
+ const rot = Cesium.Matrix4.getMatrix3(mat, new Cesium.Matrix3());
|
|
|
|
|
+ console.log('从 nodeTransformations.matrix 获取旋转矩阵成功');
|
|
|
|
|
+ return rot;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (model._root) {
|
|
|
|
|
+ console.log('model._root 存在');
|
|
|
|
|
+ const root = model._root;
|
|
|
|
|
+ console.log('root 属性:', Object.keys(root));
|
|
|
|
|
+
|
|
|
|
|
+ if (root.transform) {
|
|
|
|
|
+ console.log('root.transform:', root.transform);
|
|
|
|
|
+ const rot = Cesium.Matrix4.getMatrix3(root.transform, new Cesium.Matrix3());
|
|
|
|
|
+ console.log('从 root.transform 获取旋转矩阵成功');
|
|
|
|
|
+ return rot;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (root._transform) {
|
|
|
|
|
+ console.log('root._transform:', root._transform);
|
|
|
|
|
+ const rot = Cesium.Matrix4.getMatrix3(root._transform, new Cesium.Matrix3());
|
|
|
|
|
+ console.log('从 root._transform 获取旋转矩阵成功');
|
|
|
|
|
+ return rot;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log('无法获取旋转矩阵,使用单位矩阵');
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.log('获取旋转矩阵失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return Cesium.Matrix3.IDENTITY.clone();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createMoveAxis() {
|
|
|
|
|
+ if (!this.center) return;
|
|
|
|
|
+
|
|
|
|
|
+ const length = this.radius * 0.8;
|
|
|
|
|
+ const rotationMatrix = this.getModelRotationMatrix();
|
|
|
|
|
+
|
|
|
|
|
+ const createAxis = (axis, color) => {
|
|
|
|
|
+ let localOffset;
|
|
|
|
|
+ if (axis === 'X') {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(length, 0, 0);
|
|
|
|
|
+ } else if (axis === 'Y') {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(0, length, 0);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(0, 0, length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const worldOffset = Cesium.Matrix3.multiplyByVector(rotationMatrix, localOffset, new Cesium.Cartesian3());
|
|
|
|
|
+
|
|
|
|
|
+ const points = [
|
|
|
|
|
+ new Cesium.Cartesian3(this.center.x, this.center.y, this.center.z),
|
|
|
|
|
+ new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + worldOffset.x,
|
|
|
|
|
+ this.center.y + worldOffset.y,
|
|
|
|
|
+ this.center.z + worldOffset.z
|
|
|
|
|
+ )
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ const primitive = this.primitivesCollection.add(new Cesium.Primitive({
|
|
|
|
|
+ geometryInstances: new Cesium.GeometryInstance({
|
|
|
|
|
+ geometry: new Cesium.PolylineGeometry({
|
|
|
|
|
+ positions: points,
|
|
|
|
|
+ vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR
|
|
|
|
|
+ }),
|
|
|
|
|
+ attributes: {
|
|
|
|
|
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ appearance: new Cesium.PolylineColorAppearance()
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives.push(primitive);
|
|
|
|
|
+ this.moveAxes.push({ primitive, axis, color });
|
|
|
|
|
+ return primitive;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ createAxis('X', Cesium.Color.RED);
|
|
|
|
|
+ createAxis('Y', Cesium.Color.GREEN);
|
|
|
|
|
+ createAxis('Z', Cesium.Color.BLUE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createAxisPlane() {
|
|
|
|
|
+ if (!this.center) return;
|
|
|
|
|
+
|
|
|
|
|
+ const size = this.radius * 0.8;
|
|
|
|
|
+ const halfSize = size / 2;
|
|
|
|
|
+
|
|
|
|
|
+ const createPlane = (axis1, axis2, color) => {
|
|
|
|
|
+ let points = [];
|
|
|
|
|
+
|
|
|
|
|
+ if (axis1 === 'X' && axis2 === 'Y') {
|
|
|
|
|
+ points = [
|
|
|
|
|
+ new Cesium.Cartesian3(-halfSize, -halfSize, 0),
|
|
|
|
|
+ new Cesium.Cartesian3(halfSize, -halfSize, 0),
|
|
|
|
|
+ new Cesium.Cartesian3(halfSize, halfSize, 0),
|
|
|
|
|
+ new Cesium.Cartesian3(-halfSize, halfSize, 0)
|
|
|
|
|
+ ];
|
|
|
|
|
+ } else if (axis1 === 'X' && axis2 === 'Z') {
|
|
|
|
|
+ points = [
|
|
|
|
|
+ new Cesium.Cartesian3(-halfSize, 0, -halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(halfSize, 0, -halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(halfSize, 0, halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(-halfSize, 0, halfSize)
|
|
|
|
|
+ ];
|
|
|
|
|
+ } else if (axis1 === 'Y' && axis2 === 'Z') {
|
|
|
|
|
+ points = [
|
|
|
|
|
+ new Cesium.Cartesian3(0, -halfSize, -halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(0, halfSize, -halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(0, halfSize, halfSize),
|
|
|
|
|
+ new Cesium.Cartesian3(0, -halfSize, halfSize)
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ points = points.map(p => new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + p.x,
|
|
|
|
|
+ this.center.y + p.y,
|
|
|
|
|
+ this.center.z + p.z
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ const primitive = this.primitivesCollection.add(new Cesium.Primitive({
|
|
|
|
|
+ geometryInstances: new Cesium.GeometryInstance({
|
|
|
|
|
+ geometry: new Cesium.PolygonGeometry({
|
|
|
|
|
+ polygonHierarchy: new Cesium.PolygonHierarchy(points),
|
|
|
|
|
+ extrudedHeight: 0.1
|
|
|
|
|
+ }),
|
|
|
|
|
+ attributes: {
|
|
|
|
|
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color.withAlpha(0.3))
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ appearance: new Cesium.PerInstanceColorAppearance({
|
|
|
|
|
+ flat: true
|
|
|
|
|
+ })
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives.push(primitive);
|
|
|
|
|
+ this.axisPlanes.push({ primitive, axis1, axis2, color });
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ createPlane('X', 'Y', Cesium.Color.fromCssColorString('#ffff00'));
|
|
|
|
|
+ createPlane('X', 'Z', Cesium.Color.fromCssColorString('#ff00ff'));
|
|
|
|
|
+ createPlane('Y', 'Z', Cesium.Color.fromCssColorString('#00ffff'));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createRotateAxis() {
|
|
|
|
|
+ if (!this.center) return;
|
|
|
|
|
+
|
|
|
|
|
+ const ringRadius = this.radius * 1.0;
|
|
|
|
|
+ const rotationMatrix = this.getModelRotationMatrix();
|
|
|
|
|
+
|
|
|
|
|
+ const createRotateAxis = (axis, color) => {
|
|
|
|
|
+ const points = [];
|
|
|
|
|
+ const segments = 64;
|
|
|
|
|
+
|
|
|
|
|
+ for (let i = 0; i <= segments; i++) {
|
|
|
|
|
+ const angle = (i / segments) * Math.PI * 2;
|
|
|
|
|
+ let localX, localY, localZ;
|
|
|
|
|
+
|
|
|
|
|
+ if (axis === 'X') {
|
|
|
|
|
+ localX = 0;
|
|
|
|
|
+ localY = Math.cos(angle) * ringRadius;
|
|
|
|
|
+ localZ = Math.sin(angle) * ringRadius;
|
|
|
|
|
+ } else if (axis === 'Y') {
|
|
|
|
|
+ localX = Math.cos(angle) * ringRadius;
|
|
|
|
|
+ localY = 0;
|
|
|
|
|
+ localZ = Math.sin(angle) * ringRadius;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ localX = Math.cos(angle) * ringRadius;
|
|
|
|
|
+ localY = Math.sin(angle) * ringRadius;
|
|
|
|
|
+ localZ = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const worldOffset = Cesium.Matrix3.multiplyByVector(
|
|
|
|
|
+ rotationMatrix,
|
|
|
|
|
+ new Cesium.Cartesian3(localX, localY, localZ),
|
|
|
|
|
+ new Cesium.Cartesian3()
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ points.push(new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + worldOffset.x,
|
|
|
|
|
+ this.center.y + worldOffset.y,
|
|
|
|
|
+ this.center.z + worldOffset.z
|
|
|
|
|
+ ));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const primitive = this.primitivesCollection.add(new Cesium.Primitive({
|
|
|
|
|
+ geometryInstances: new Cesium.GeometryInstance({
|
|
|
|
|
+ geometry: new Cesium.PolylineGeometry({
|
|
|
|
|
+ positions: points,
|
|
|
|
|
+ vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR
|
|
|
|
|
+ }),
|
|
|
|
|
+ attributes: {
|
|
|
|
|
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ appearance: new Cesium.PolylineColorAppearance()
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives.push(primitive);
|
|
|
|
|
+ this.rotateAxes.push({ primitive, axis, color });
|
|
|
|
|
+ return primitive;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ createRotateAxis('X', Cesium.Color.RED);
|
|
|
|
|
+ createRotateAxis('Y', Cesium.Color.GREEN);
|
|
|
|
|
+ createRotateAxis('Z', Cesium.Color.BLUE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ createScaleAxis() {
|
|
|
|
|
+ if (!this.center) return;
|
|
|
|
|
+
|
|
|
|
|
+ const length = this.radius * 0.8;
|
|
|
|
|
+ const rotationMatrix = this.getModelRotationMatrix();
|
|
|
|
|
+
|
|
|
|
|
+ const createScalePoint = (axis, color) => {
|
|
|
|
|
+ let localOffset;
|
|
|
|
|
+ if (axis === 'X') {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(length, 0, 0);
|
|
|
|
|
+ } else if (axis === 'Y') {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(0, length, 0);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ localOffset = new Cesium.Cartesian3(0, 0, length);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const worldOffset = Cesium.Matrix3.multiplyByVector(rotationMatrix, localOffset, new Cesium.Cartesian3());
|
|
|
|
|
+ const size = Math.max(this.radius * 0.08, 0.15);
|
|
|
|
|
+
|
|
|
|
|
+ const primitive = this.primitivesCollection.add(new Cesium.Primitive({
|
|
|
|
|
+ geometryInstances: new Cesium.GeometryInstance({
|
|
|
|
|
+ geometry: new Cesium.BoxGeometry({
|
|
|
|
|
+ vertexFormat: Cesium.VertexFormat.POSITION_AND_COLOR,
|
|
|
|
|
+ maximum: new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + worldOffset.x + size,
|
|
|
|
|
+ this.center.y + worldOffset.y + size,
|
|
|
|
|
+ this.center.z + worldOffset.z + size
|
|
|
|
|
+ ),
|
|
|
|
|
+ minimum: new Cesium.Cartesian3(
|
|
|
|
|
+ this.center.x + worldOffset.x - size,
|
|
|
|
|
+ this.center.y + worldOffset.y - size,
|
|
|
|
|
+ this.center.z + worldOffset.z - size
|
|
|
|
|
+ )
|
|
|
|
|
+ }),
|
|
|
|
|
+ attributes: {
|
|
|
|
|
+ color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ appearance: new Cesium.PerInstanceColorAppearance({
|
|
|
|
|
+ flat: true
|
|
|
|
|
+ })
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ this.primitives.push(primitive);
|
|
|
|
|
+ this.scalePoints.push({ primitive, axis, color });
|
|
|
|
|
+ return primitive;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ createScalePoint('X', Cesium.Color.RED);
|
|
|
|
|
+ createScalePoint('Y', Cesium.Color.GREEN);
|
|
|
|
|
+ createScalePoint('Z', Cesium.Color.BLUE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ addEventListener() {
|
|
|
|
|
+ const handler = this.handler;
|
|
|
|
|
+
|
|
|
|
|
+ handler.setInputAction((event) => {
|
|
|
|
|
+ const picked = this.viewer.scene.pick(event.position);
|
|
|
|
|
+
|
|
|
|
|
+ if (Cesium.defined(picked) && Cesium.defined(picked.primitive)) {
|
|
|
|
|
+ this.activePrimitive = picked.primitive;
|
|
|
|
|
+ this.startMousePosition = event.position;
|
|
|
|
|
+
|
|
|
|
|
+ if (this.originPoint === picked.primitive) {
|
|
|
|
|
+ this.dragMode = 'translate';
|
|
|
|
|
+ this.activeAxis = 'XYZ';
|
|
|
|
|
+ } else if (this.moveAxes.some(a => a.primitive === picked.primitive)) {
|
|
|
|
|
+ const axisInfo = this.moveAxes.find(a => a.primitive === picked.primitive);
|
|
|
|
|
+ this.dragMode = 'translate';
|
|
|
|
|
+ this.activeAxis = axisInfo.axis;
|
|
|
|
|
+ } else if (this.axisPlanes.some(p => p.primitive === picked.primitive)) {
|
|
|
|
|
+ const planeInfo = this.axisPlanes.find(p => p.primitive === picked.primitive);
|
|
|
|
|
+ this.dragMode = 'translate';
|
|
|
|
|
+ this.activeAxis = planeInfo.axis1 + planeInfo.axis2;
|
|
|
|
|
+ } else if (this.rotateAxes.some(r => r.primitive === picked.primitive)) {
|
|
|
|
|
+ const axisInfo = this.rotateAxes.find(r => r.primitive === picked.primitive);
|
|
|
|
|
+ this.dragMode = 'rotate';
|
|
|
|
|
+ this.activeAxis = axisInfo.axis;
|
|
|
|
|
+ } else if (this.scalePoints.some(s => s.primitive === picked.primitive)) {
|
|
|
|
|
+ const axisInfo = this.scalePoints.find(s => s.primitive === picked.primitive);
|
|
|
|
|
+ this.dragMode = 'scale';
|
|
|
|
|
+ this.activeAxis = axisInfo.axis;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (this.dragMode) {
|
|
|
|
|
+ this.startMouseCartesian = this.pickCartesianFromMouse(event.position);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
|
|
|
|
+
|
|
|
|
|
+ handler.setInputAction((event) => {
|
|
|
|
|
+ if (!this.dragMode || !this.activePrimitive) return;
|
|
|
|
|
+
|
|
|
|
|
+ const currentMouseCartesian = this.pickCartesianFromMouse(event.position);
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.startMouseCartesian || !currentMouseCartesian) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (this.dragMode === 'translate') {
|
|
|
|
|
+ this.translate(currentMouseCartesian);
|
|
|
|
|
+ } else if (this.dragMode === 'rotate') {
|
|
|
|
|
+ this.rotate(event.position);
|
|
|
|
|
+ } else if (this.dragMode === 'scale') {
|
|
|
|
|
+ this.scale(currentMouseCartesian);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.startMouseCartesian = currentMouseCartesian;
|
|
|
|
|
+ }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
|
|
|
|
+
|
|
|
|
|
+ handler.setInputAction(() => {
|
|
|
|
|
+ this.activePrimitive = null;
|
|
|
|
|
+ this.dragMode = null;
|
|
|
|
|
+ this.startMousePosition = null;
|
|
|
|
|
+ this.startMouseCartesian = null;
|
|
|
|
|
+ }, Cesium.ScreenSpaceEventType.LEFT_UP);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pickCartesianFromMouse(mousePosition) {
|
|
|
|
|
+ const ray = this.viewer.camera.getRay(mousePosition);
|
|
|
|
|
+ const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene);
|
|
|
|
|
+
|
|
|
|
|
+ if (!cartesian) {
|
|
|
|
|
+ // 如果没有与地面相交,则使用相机前方一定距离的点
|
|
|
|
|
+ const position = this.viewer.camera.position;
|
|
|
|
|
+ const direction = this.viewer.camera.direction;
|
|
|
|
|
+ const distance = 100;
|
|
|
|
|
+ cartesian = new Cesium.Cartesian3(
|
|
|
|
|
+ position.x + direction.x * distance,
|
|
|
|
|
+ position.y + direction.y * distance,
|
|
|
|
|
+ position.z + direction.z * distance
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return cartesian;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ translate(currentMouseCartesian) {
|
|
|
|
|
+ if (!this.startMouseCartesian || !currentMouseCartesian) return;
|
|
|
|
|
+
|
|
|
|
|
+ const offset = new Cesium.Cartesian3(
|
|
|
|
|
+ currentMouseCartesian.x - this.startMouseCartesian.x,
|
|
|
|
|
+ currentMouseCartesian.y - this.startMouseCartesian.y,
|
|
|
|
|
+ currentMouseCartesian.z - this.startMouseCartesian.z
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 根据选中轴过滤偏移量
|
|
|
|
|
+ if (this.activeAxis.indexOf('X') === -1) offset.x = 0;
|
|
|
|
|
+ if (this.activeAxis.indexOf('Y') === -1) offset.y = 0;
|
|
|
|
|
+ if (this.activeAxis.indexOf('Z') === -1) offset.z = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // 更新实体位置
|
|
|
|
|
+ if (this.entity.position) {
|
|
|
|
|
+ const currentPosition = this.entity.position.getValue(this.viewer.clock.currentTime);
|
|
|
|
|
+ if (currentPosition) {
|
|
|
|
|
+ const newPosition = new Cesium.Cartesian3(
|
|
|
|
|
+ currentPosition.x + offset.x,
|
|
|
|
|
+ currentPosition.y + offset.y,
|
|
|
|
|
+ currentPosition.z + offset.z
|
|
|
|
|
+ );
|
|
|
|
|
+ this.entity.position = newPosition;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新中心点
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+
|
|
|
|
|
+ // 重新创建枢轴
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ rotate(mousePosition) {
|
|
|
|
|
+ if (!this.startMousePosition || !mousePosition) return;
|
|
|
|
|
+
|
|
|
|
|
+ const deltaX = mousePosition.x - this.startMousePosition.x;
|
|
|
|
|
+ const angle = deltaX * 0.01;
|
|
|
|
|
+
|
|
|
|
|
+ let axis = new Cesium.Cartesian3(0, 0, 1);
|
|
|
|
|
+ if (this.activeAxis === 'X') {
|
|
|
|
|
+ axis = new Cesium.Cartesian3(1, 0, 0);
|
|
|
|
|
+ } else if (this.activeAxis === 'Y') {
|
|
|
|
|
+ axis = new Cesium.Cartesian3(0, 1, 0);
|
|
|
|
|
+ } else if (this.activeAxis === 'Z') {
|
|
|
|
|
+ axis = new Cesium.Cartesian3(0, 0, 1);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建旋转矩阵
|
|
|
|
|
+ const quaternion = Cesium.Quaternion.fromAxisAngle(axis, angle);
|
|
|
|
|
+ const rotationMatrix = Cesium.Matrix3.fromQuaternion(quaternion);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前位置
|
|
|
|
|
+ if (this.entity.position) {
|
|
|
|
|
+ const currentPosition = this.entity.position.getValue(this.viewer.clock.currentTime);
|
|
|
|
|
+ if (currentPosition) {
|
|
|
|
|
+ // 计算新位置(绕中心点旋转)
|
|
|
|
|
+ const translation = Cesium.Matrix4.fromTranslation(currentPosition);
|
|
|
|
|
+ const rotation = Cesium.Matrix4.fromTranslationRotationScale(
|
|
|
|
|
+ Cesium.Vector3.ZERO,
|
|
|
|
|
+ quaternion,
|
|
|
|
|
+ new Cesium.Cartesian3(1, 1, 1)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ Cesium.Matrix4.multiply(translation, rotation, translation);
|
|
|
|
|
+
|
|
|
|
|
+ const newPosition = Cesium.Matrix4.getTranslation(translation, new Cesium.Cartesian3());
|
|
|
|
|
+ this.entity.position = newPosition;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新中心点
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+
|
|
|
|
|
+ // 重新创建枢轴
|
|
|
|
|
+ this.init();
|
|
|
|
|
+
|
|
|
|
|
+ this.startMousePosition = mousePosition;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ scale(currentMouseCartesian) {
|
|
|
|
|
+ if (!this.startMouseCartesian || !currentMouseCartesian) return;
|
|
|
|
|
+
|
|
|
|
|
+ const offset = new Cesium.Cartesian3(
|
|
|
|
|
+ currentMouseCartesian.x - this.startMouseCartesian.x,
|
|
|
|
|
+ currentMouseCartesian.y - this.startMouseCartesian.y,
|
|
|
|
|
+ currentMouseCartesian.z - this.startMouseCartesian.z
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 根据选中轴计算缩放比例
|
|
|
|
|
+ let scaleFactor = 1.0;
|
|
|
|
|
+ if (this.activeAxis === 'X' && offset.x !== 0) {
|
|
|
|
|
+ scaleFactor = 1 + offset.x * 0.01;
|
|
|
|
|
+ } else if (this.activeAxis === 'Y' && offset.y !== 0) {
|
|
|
|
|
+ scaleFactor = 1 + offset.y * 0.01;
|
|
|
|
|
+ } else if (this.activeAxis === 'Z' && offset.z !== 0) {
|
|
|
|
|
+ scaleFactor = 1 + offset.z * 0.01;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新模型缩放
|
|
|
|
|
+ if (this.entity.model) {
|
|
|
|
|
+ const currentScale = this.entity.model.scale || 1.0;
|
|
|
|
|
+ this.entity.model.scale = currentScale * scaleFactor;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.startMouseCartesian = currentMouseCartesian;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ destroy() {
|
|
|
|
|
+ // 清理事件监听器
|
|
|
|
|
+ if (this.handler) {
|
|
|
|
|
+ this.handler.destroy();
|
|
|
|
|
+ this.handler = null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清理 primitives
|
|
|
|
|
+ if (this.entity.model && this.entity.model.definitionChanged) {
|
|
|
|
|
+ this.entity.model.definitionChanged.removeEventListener(() => {
|
|
|
|
|
+ if (this.entity.model.boundingSphere) {
|
|
|
|
|
+ this.center = this.getCenter();
|
|
|
|
|
+ this.radius = this.getRadius();
|
|
|
|
|
+ this.init();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.clearPrimitives();
|
|
|
|
|
+
|
|
|
|
|
+ this.activePrimitive = null;
|
|
|
|
|
+ this.activeAxis = null;
|
|
|
|
|
+ this.dragMode = null;
|
|
|
|
|
+ this.startMouseCartesian = null;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export default ModelEditor;
|