Cesium從入門放棄8:模型編輯Demo

就是這個東西
在這裏插入圖片描述

1.添加模型

const url = "Apps/model/leida.glb";
const pos = Cesium.Cartesian3.fromDegrees(110, 40, 150);
const matrix = Cesium.Transforms.eastNorthUpToFixedFrame(pos);
const model = viewer.scene.primitive.add(
     Cesium.Model.fromGltf({
       url: url,
       modelMatrix: matrix
     })
    );
});
viewer.camera.lookAtTransform(matrix, new Cesium.Cartesian3(-50, 0, 800));

在這裏插入圖片描述

2.畫座標軸

2.1 定義箭頭線

座標軸看上去是個帶箭頭的線,但是Cesium並沒有定義箭頭線的幾何對象,Entity倒是可以給一個箭頭材質,但是模型旋轉平移涉及複雜的矩陣變換,所以Entity並不是一個好的選擇,用Entity也許你能很方便的畫出座標軸,但是矩陣變換估計能把你整暈了,因此我們寧願畫座標軸的時候多花點心思。下面是我基本Primitive封裝的一個箭頭線,實現的原理是一個圓柱加一個圓錐。

export default class ArrowPolyline {
    /**
     * 箭頭線
     */
    constructor(option = {}) {
        this._color = option.color || Cesium.Color.RED;
        this._width = option.width || 3;
        this._headWidth = option.headWidth || 2 * this._width;
        this._length = option.length || 300
        this._headLength = option.headLength || 10
        this._inverse = option.inverse || false
        this.position = option.position
        const id = option.id 
        //這裏用的是圓錐幾何對象,當topRadius和bottomRadius相同時,它就是一個圓柱
        const line = Cesium.CylinderGeometry.createGeometry(new Cesium.CylinderGeometry({
            length: this._length,
            topRadius: this._width,
            bottomRadius: this._width
        }));
        const arrow = Cesium.CylinderGeometry.createGeometry(new Cesium.CylinderGeometry({
            length: this._headLength,
            topRadius: 0,
            bottomRadius: this._headWidth
        }));
        let offset = (this._length + this._headLength) / 2
        if (this._inverse) {
            offset = -offset
        }

        ArrowPolyline.translate(arrow, [0, 0, offset]);

        return new Cesium.Primitive({
            modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(this.position),
            geometryInstances: [new Cesium.GeometryInstance(
                {
                    id: id + '-line',
                    geometry: line,
                }
            ),
            new Cesium.GeometryInstance({
                id: id + '-arrow',
                geometry: arrow,
            })],
            appearance: new Cesium.MaterialAppearance({
                material: Cesium.Material.fromType('Color', { color: this._color })
            })
        });
    }
    /**
    * 按上面的方法畫出的箭頭在線的中間,我們需要把它平移到線的一端
    */
    static translate = function (geometry, offset) {
        const scratchOffset = new Cesium.Cartesian3();
        if (Cesium.isArray(offset)) {
            scratchOffset.x = offset[0];
            scratchOffset.y = offset[1];
            scratchOffset.z = offset[2];
        } else {
            Cesium.Cartesian3.clone(offset, scratchOffset);
        }

        for (let i = 0; i < geometry.attributes.position.values.length; i += 3) {
            geometry.attributes.position.values[i] += scratchOffset.x;
            geometry.attributes.position.values[i + 1] += scratchOffset.y;
            geometry.attributes.position.values[i + 2] += scratchOffset.z;
        }
    }
}

2.2 以模型中心爲原點創建座標軸

model.readyPromise.then(m => {
    const center1 = Cesium.Matrix4.getTranslation(
        m.modelMatrix,
        new Cesium.Cartesian3()
    );
    //必須在模型加載完成後才能讀到boundingSphere屬性
    const boundingShpere = m.boundingSphere;
    const radius = boundingShpere.radius
    const axisZ = new GV.ArrowPolyline({
        id: "axisZ",
        color: Cesium.Color.RED,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,//座標軸的長度應該視模型的直徑而定
        headLength: 10
    });
    const axisX = new GV.ArrowPolyline({
        id: "axisX",
        color: Cesium.Color.GREEN,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,
        headLength: 10
    });
    const axisY = new GV.ArrowPolyline({
        id: "axisY",
        color: Cesium.Color.BLUE,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,
        headLength: 10
    });
    viewer.scene.primitives.add(axisZ)
    viewer.scene.primitives.add(axisX)
    viewer.scene.primitives.add(axisY)
});

三個座標軸已經創建完了,但是如果你打開瀏覽器應該只看到一條軸,應該它們是重疊在一起的,並且方向朝上,也就是說我們畫了三條Z軸,以右手座標系爲例,Z軸繞Y軸逆時針旋轉90度到到X軸,Z軸繞X軸旋轉逆時針旋轉90度得到Y軸。因此對X、Y軸做矩陣變換 。

model.readyPromise.then(m => {
    const center1 = Cesium.Matrix4.getTranslation(
        m.modelMatrix,
        new Cesium.Cartesian3()
    );
    const boundingShpere = m.boundingSphere;
    const radius = boundingShpere.radius
    const axisZ = new GV.ArrowPolyline({
        id: "axisZ",
        color: Cesium.Color.RED,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,
        headLength: 10
    });
    const axisX = new GV.ArrowPolyline({
        id: "axisX",
        color: Cesium.Color.GREEN,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,
        headLength: 10
    });
    const axisY = new GV.ArrowPolyline({
        id: "axisY",
        color: Cesium.Color.BLUE,
        position: center1,
        width: 3,
        headWidth: 5,
        length: radius * 2 + 50,
        headLength: 10
    });

    const mx = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(90));
    const rotationX = Cesium.Matrix4.fromRotationTranslation(mx);
    Cesium.Matrix4.multiply(
        axisX.geometryInstances[0].modelMatrix,
        rotationX,
        axisX.geometryInstances[0].modelMatrix
    );
    Cesium.Matrix4.multiply(
        axisX.geometryInstances[1].modelMatrix,
        rotationX,
        axisX.geometryInstances[1].modelMatrix
    );
    const my = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(90));
    const rotationY = Cesium.Matrix4.fromRotationTranslation(my);
    Cesium.Matrix4.multiply(
        axisY.geometryInstances[0].modelMatrix,
        rotationY,
        axisY.geometryInstances[0].modelMatrix
    );
    Cesium.Matrix4.multiply(
        axisY.geometryInstances[1].modelMatrix,
        rotationY,
        axisY.geometryInstances[1].modelMatrix
    );
    viewer.scene.primitives.add(axisZ)
    viewer.scene.primitives.add(axisX)
    viewer.scene.primitives.add(axisY)
});

在這裏插入圖片描述

3.創建圍繞座標軸的圓

這裏肯定有很多方法,我的實現思路以模型中心爲圓心,模型半徑爲半徑畫圓。

3.1 定義創建座標圓的方法

function createAxisSphere(id, position, matrix, color) {
    const geometry = new Cesium.PolylineGeometry({
        positions: position,
        width: 10
    });
    const instnce = new Cesium.GeometryInstance({
        geometry: geometry,
        id: id,
        attributes: {
            color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
        }
    });
    return new Cesium.Primitive({
        geometryInstances: instnce,
        appearance: new Cesium.PolylineColorAppearance({
            translucent: false
        }),
        modelMatrix: matrix
    });
}

3.2 計算圓周座標

const position = [];
for (let i = 0; i <= 360; i += 3) {
    const sin = Math.sin(Cesium.Math.toRadians(i));
    const cos = Math.cos(Cesium.Math.toRadians(i));
    const x = radius * cos;
    const y = radius * sin;
    position.push(new Cartesian3(x, y, 0));
}

3.3 創建座標軸圓弧

const axisSphereZ = self.createAxisSphere(
    "axisSphereZ",
    position,
    matrix,
    Cesium.Color.RED
);
viewer.scene.primitives.add(axisSphereZ);
const axisSphereY = self.createAxisSphere(
    "axisSphereY",
    position,
    matrix,
    Cesium.Color.GREEN
);
viewer.scene.primitives.add(axisSphereY);
Cesium.Matrix4.multiply(
    axisSphereY.geometryInstances.modelMatrix,
    rotationY,
    axisSphereY.geometryInstances.modelMatrix
);
const axisSphereX = self.createAxisSphere(
    "axisSphereX",
    position,
    matrix,
    Cesium.Color.BLUE
);
viewer.scene.primitives.add(axisSphereX);
Cesium.Matrix4.multiply(
    axisSphereX.geometryInstances.modelMatrix,
    rotationX,
    axisSphereX.geometryInstances.modelMatrix
);

在這裏插入圖片描述

4.結語

至此,最前面展示的內容就已經完成了,剩下的就是交互了,前面已經寫過了,具體請參考
Cesium從入門放棄7:模型矩陣變換

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章