3DTiles Clipping Planes例子,可以很容易实现对BIM、Point Cloud、Instanced、Model的任意剖切,其中Instanced是到底是一个什么对象,是怎么来的还不清楚。3DTiles Clipping Planes,对于BIM说来应用场景是有的,比如,需要查看一个建筑内部,应用从上到下的剖分,可以查看内部的结构。但对于点云和model有什么用呢?反正到现在我还没想出来。但从学习的角度去看,还是可以的。先从简单的模型剖切开始。
一、创建clippingPlanes并绑定到模型
直接使用 new Cesium.ClippingPlaneCollection创建clippingPlanes。从里面的参数可以看到创建多个剖切面,设置切面与模型相交线的线宽,如果不需要切面边线,可设置为0;设置剖切面的颜色等。创建ClippingPlane时传入的有一个new Cesium.Cartesian3参数,它定义了剖切面的任意性,传入不同参数,得到不同喑切面。如下图是一个(1.0,1.0,-1.0)剖切面:
clippingPlanes绑定到模型只需要在创建模型entity时,把clippingPlanes作为可选参数即可。代码如下:
function loadModel (url) {
clippingPlanes = new Cesium.ClippingPlaneCollection({
planes: [//切面
new Cesium.ClippingPlane(
new Cesium.Cartesian3(0.0, 0.0, -1.0),
0.0
),
],
edgeWidth: 2.0,// 切面与模型相交线的线宽,如果不需要切面边线,可设置为0
edgeColor: planeColor //切面颜色
});
var position = Cesium.Cartesian3.fromDegrees(116.43299999999988, 39.915999999999954, 300.0);
var heading = Cesium.Math.toRadians(135.0);
var pitch = 0.0;
var roll = 0.0;
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
);
var entity = viewer.entities.add({
name: url,
position: position,
orientation: orientation,
model: {
uri: url,
scale: 8,
minimumPixelSize: 100.0,
clippingPlanes: clippingPlanes,//设置模型切面
},
});
viewer.flyTo(entity)
//加载切面到场景中
for (var i = 0; i < clippingPlanes.length; ++i) {
var plane = clippingPlanes.get(i);
viewer.entities.add({
position: position,
orientation: orientation,
plane: {
dimensions: new Cesium.Cartesian2(50.0, 50.0),
material: planeColor.withAlpha(0.1),
plane: new Cesium.CallbackProperty(createPlaneUpdateFunction(plane), false),
outline: true,
outlineColor: planeColor,
},
});
}
}
function createPlaneUpdateFunction (plane) {
return function () {
plane.distance = targetY;
return plane;
};
}
代码中创建 一个垂直于z轴的剖切面,并在创建模型时绑定到模型上,至此,剖切面已经起作用。要注意的是源码中有一个问题,模型的方向矩阵没有应用到剖切面上,即:模型使用了 orientation: orientation,但剖切面没有使用 :orientation: orientation。如果模型有平移、旋转操作时剖切面与被切面不一致。模型旋转了135度,但剖切面没有旋转,如下图:
正常代码绘制结果,如下图:
从正常绘制结果图来看,剖切面的大小并不影响剖切完整性,即剖切面小于模型也能完整切割,其它绘制剖切面也只是显示效果和鼠标的交互操作,并没有别的作用。
二、鼠标交互
鼠标交互就是在加载剖切面时为plane设置一个属性回调函数,用回调函数实时更新plane的distance属性,从而得到不同的剖切面。关键代码: plane: new Cesium.CallbackProperty(createPlaneUpdateFunction(plane), false)。整个鼠标交互代码如下:
function addListener () {
let scene = viewer.scene
var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (down) {
var pickedObject = scene.pick(down.position);
if (
Cesium.defined(pickedObject) && Cesium.defined(pickedObject.id) && Cesium.defined(pickedObject.id.plane)
) {
selectedPlane = pickedObject.id.plane;
selectedPlane.material = planeColor.withAlpha(0.05);
selectedPlane.outlineColor = planeColor;
scene.screenSpaceCameraController.enableInputs = false;
}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(function () {
if (Cesium.defined(selectedPlane)) {
selectedPlane.material = planeColor.withAlpha(0.1);
selectedPlane.outlineColor = planeColor;
selectedPlane = undefined;
}
scene.screenSpaceCameraController.enableInputs = true;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
handler.setInputAction(function (movement) {
if (Cesium.defined(selectedPlane)) {
console.log('movement.endPosition=', movement.endPosition)
var deltaY = movement.startPosition.y - movement.endPosition.y;
console.log('deltaY=', deltaY)
var deltaX = movement.startPosition.x - movement.endPosition.x;
console.log('deltaX=', deltaX)
targetY += deltaY;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
三、问题
从交互代码中可以看到,是利用鼠标在屏幕上的坐标来直接设置,在鼠标拖动函数中
var deltaY = movement.startPosition.y - movement.endPosition.y;
targetY += deltaY;
在属性回调函数中:
plane.distance = targetY;
但问题是,虽然鼠标y值能反应出物体在地表的上下关系,但数值并不是一致的。也就是plane.distance与屏幕坐标的数值不是一回事。plane.distance单位应该是米,而屏幕坐标是px。所以直接加就会出现问题,鼠标与剖切面不在一起。因为鼠标向上移动1px,而剖切面向上移动1米。如下图:
但是现在也没有更好的解决方案。问题先放着吧。