1

I'm trying to change the length of a cylinder or the extrudedHeight of a circle when it has been added to the primitives and is shown in the cesium widget/viewer. For example this cylinder:

var length = 100;

var cylinderGeometry = new Cesium.CylinderGeometry({
    length : length,
    topRadius : cylinderradius,
    bottomRadius : cylinderradius,
    slices: cylinderslices,
    vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
    geometry: cylinderGeometry,
    modelMatrix: Cesium.Matrix4.multiplyByTranslation(  
        Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))), 
        new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
    attributes: {
        color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
    },
    id: "Cylinder1"
});
var primitive = new Cesium.Primitive({
    geometryInstances : cylinder ,
    appearance : new Cesium.PerInstanceColorAppearance({
        closed : false,
        translucent: true,
        flat: false,
        faceForward: true
    }),
    allow3DOnly: true,
    vertexCacheOptimize: true,
    allowPicking: true,
    releaseGeometryInstances: false
});

widget.scene.primitives.add(primitive);

Because it's added to the primitives array it will be shown in the widget, but after 2 seconds for example I get a notification that the length should be halved (that means set to 50). Is there any way to do this? Simply changing it in cylinderGeometry doesn't seem to do the job.

I kind of have it working by creating a new cylinder with the new height, adding it and removing the old one. This however tends to flicker the cylinder (it's gone for a fraction of a second) before the new one is shown. I fixed this problem by removing the old instance after a set time after the new one is added. This whole solution isn't very elegant and doesn't work very well on devices with a small amount of computing power, hence my search for a better solution.

I don't care if this is achieved using cylinders or extruded circles. If you need any more information don't hesitate to ask in the comments below the question.

EDIT

I implemented the second solution Matthew suggested but after a while of it running perfectly the cylinders stop changing height (which didn't occur when I used my solution. The callback in the interval does get called. Here is some code showing what my new solution is (not working):

primitives.add(prim);

window.nodeValuesInterval = setInterval(function () {
    if (prim._state == Cesium.PrimitiveState.COMPLETE) {
        clearInterval(window.nodeValuesInterval);
        clearTimeout(window.nodeValuesTimeout);
        primitives.remove(primitiveObjects.value);
        primitiveObjects.value = prim;
    }
}, cylindervalueinterval);

window.nodeValuesTimeout = setTimeout(function () {
    clearInterval(window.nodeValuesInterval);
    primitives.remove(primitiveObjects.value);
    primitiveObjects.value = prim;
}, cylindervaluedelay);
4

1 回答 1

1

Cesium 的几何结构目前针对静态数据进行了优化。某些属性(例如可见性、颜色和材质)可以即时更改,但实际修改几何体的项目(如圆柱体高度)需要您移除图元并重新计算几何体。您看到的闪烁是默认情况下异步基元创建的结果。有两种方法可以随心所欲。

  1. [options.asynchronous: false通过传递给Primitive构造函数来禁用异步原语创建。这意味着当你添加一个新的图元时,Cesium 在它准备好之前不会渲染。对于一两个物体,你不会注意到任何东西。对于很多对象,它会锁定浏览器,直到一切准备就绪。这确实保证了您可以删除旧的/添加新的基元而不会出现任何闪烁。

  2. 第二种选择是添加新的基元(不删除旧的基元),然后在每一帧中检查_state新基元的属性(我认为这是公共 API 的一部分,但显然不是)。当_state等于 时,Cesium.PrimitiveState.COMPLETE您可以安全地删除旧图元,并保证新图元将呈现(因此不会闪烁)。

我认为我们有一个错误/功能请求,可以公开公开状态变量或在 Primitive 准备好时以其他方式通知;但_state在可预见的未来使用应该没问题。如果我们很快添加官方方式,我会更新这个问题。

希望有帮助。

编辑:由于需要更多帮助;这是一个完整的例子。您可以使用此链接将以下代码复制并粘贴到 Sandcastle 中。

基本上它使用 scene.preRender 事件而不是超时(preRender 在这里几乎总是更好的答案)。此外,如果您在旧更新完成处理之前收到新更新,则在计算新更新之前删除该更新非常重要。如果您仍有问题,请告诉我。

require(['Cesium'], function(Cesium) {
    "use strict";

    var widget = new Cesium.CesiumWidget('cesiumContainer');

    var ellipsoid = widget.scene.globe.ellipsoid;
    var lon = 0;
    var lat = 0;
    var cylinderradius = 30000;
    var length = 10000000;
    var cylinderslices = 32;

    var newPrimitive;
    var currentPrimitive;

    //This function creates a new cylinder that is half the length of the old one.
    function decreaseLength() {
        //If there's a pending primitive already, remove it.
        if(Cesium.defined(newPrimitive)){
            widget.scene.primitives.remove(newPrimitive);
        }

        length /= 2;

        var cylinderGeometry = new Cesium.CylinderGeometry({
            length : length,
            topRadius : cylinderradius,
            bottomRadius : cylinderradius,
            slices: cylinderslices,
            vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
        });

        var cylinder = new Cesium.GeometryInstance({
            geometry: cylinderGeometry,
            modelMatrix: Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
                                                              new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
            attributes: {
                color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
            },
            id: "Cylinder1"
        });

        newPrimitive = new Cesium.Primitive({
            geometryInstances : cylinder ,
            appearance : new Cesium.PerInstanceColorAppearance({
                closed : false,
                translucent: true,
                flat: false,
                faceForward: true
            }),
            allow3DOnly: true,
            vertexCacheOptimize: true,
            allowPicking: true,
            releaseGeometryInstances: false
        });

        //We add the new cylinder immediately, but don't remove the old one yet.
        widget.scene.primitives.add(newPrimitive);
    }

    //Create the initial cylinder.
    decreaseLength();

    //Subscribe to the preRender event so we can check the primitive every frame.
    widget.scene.preRender.addEventListener(function(scene, time) {
        //Remove the old cylinder once the new one is ready.
        if(Cesium.defined(newPrimitive) && newPrimitive._state === Cesium.PrimitiveState.COMPLETE){
            if(Cesium.defined(currentPrimitive)){
                widget.scene.primitives.remove(currentPrimitive);
            }
            currentPrimitive = newPrimitive;
            newPrimitive = undefined;
        }
    });

    Sandcastle.addToolbarButton('Decrease Length', decreaseLength);
    Sandcastle.finishedLoading();
});
于 2014-07-02T01:18:36.163 回答