上篇文章中,我们介绍了使用viewer.entities.add
添加entity
之后的信号传递以及最后entity
对象被传递到GeometryVisualizer
;
这篇文章,我们则介绍如何在逐帧渲染的过程中根据GeometryVisualizer
中的entity
对象创建相应的primitive
这是下文中涉及到的类的类图,从中可以清晰的了解各个对象之间的关系,下面我们结合代码来仔细讲解。
循环的一帧
我们先看下viewer
初始化的时候做了什么,在何处定义了每一帧的循环,并持续的进行渲染,结合时序图(见第三节)和源码,可以将其分为两个部分
Viewer
初始化
viewer
初始化并创建clock
function Viewer(container, options){
let clock;
let clockViewModel;
let destroyClockViewModel = false;
if (defined(options.clockViewModel)) {
clockViewModel = options.clockViewModel;
clock = clockViewModel.clock;
} else {
clock = new Clock();
clockViewModel = new ClockViewModel(clock);
destroyClockViewModel = true;
}
}
- 将
clock
作为参数之一创建cesiumWidget
// 省略其他参数
const cesiumWidget = new CesiumWidget(cesiumWidgetContainer, {clock: clock});
- 添加监听事件,建立事件响应,其效果我们在后面再具体描述
eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);
cesiumWidget
初始化
- 在构造函数中设置渲染循环策略
this.useDefaultRenderLoop
this._useDefaultRenderLoop = undefined;
this.useDefaultRenderLoop = defaultValue(
options.useDefaultRenderLoop,
true
);
结合useDefaultRenderLoop
的set
函数可知其实是调用了startRenderLoop
函数
useDefaultRenderLoop: {
get: function () {
return this._useDefaultRenderLoop;
},
set: function (value) {
if (this._useDefaultRenderLoop !== value) {
this._useDefaultRenderLoop = value;
if (value && !this._renderLoopRunning) {
startRenderLoop(this);
}
}
},
}
- 在
startRenderLoop
中定义了render
函数并每一帧进行调用
function startRenderLoop(widget) {
widget._renderLoopRunning = true;
let lastFrameTime = 0;
function render(frameTime) {
// 此处省略细节
widget.render();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
- 在
render
函数中起实际作用的是函数widget.render
,其内部通过调用this._clock.tick()
发出信号,结合上一节viewer
初始化中提到的事件监听的建立可以知道,进行响应的是Viewer.prototype._onTick
函数
CesiumWidget.prototype.render = function () {
if (this._canRender) {
this._scene.initializeFrame();
const currentTime = this._clock.tick();
this._scene.render(currentTime);
} else {
this._clock.tick();
}
};
Clock.prototype.tick = function () {
this.onTick.raiseEvent(this);
return currentTime;
};
- 在
Viewer.prototype._onTick
函数中,会通过调用函数this._dataSourceDisplay.update(time)
进行实际的primitive
对象的创建
Viewer.prototype._onTick = function (clock) {
const isUpdated = this._dataSourceDisplay.update(time);
};
时序图
- 这里我们附上整个过程的时序图,帮助大家更好的了解整个过程
生成Primitive
通过上面的描述,我们知道了cesium
的每一帧是如何更新的,以及其通过调用this._dataSourceDisplay.update(time)
进行primitive
的创建,下面我们就探究下具体的创建过程
-
在
update
中,获取了this._defaultDataSource
的_visualizers
属性,通过上一篇文章我们知道,其是一个包含了GeometryVisualizer
等多个Visualizer
的列表,其中GeometryVisualizer
是后续创建polygon
对应primitive
的类DataSourceDisplay.prototype.update = function (time) { visualizers = this._defaultDataSource._visualizers; vLength = visualizers.length; for (x = 0; x < vLength; x++) { result = visualizers[x].update(time) && result; } return result; };
-
在
GeometryVisualizer
的update
函数中主要做了如下几件事:-
获取被添加对象,在上一篇文章中我们知道,通过
_onCollectionChanged
函数,将添加的entity
添加到了this._addedObjects
属性中const addedObjects = this._addedObjects; addedObjects.set(id, entity);
-
遍历每一个被添加的对象
-
创建
UpdaterSet
,其内部的updaters
包含了PolygonGeometryUpdater
在内的10个Updater
-
通过
updater
尝试创建instance
(后面详细介绍)
-
-
移除已经被添加的对象
-
在
batch
中创建primitive
(后面详细介绍)
-
代码节选如下:
GeometryVisualizer.prototype.update = function (time) {
// 获取被添加对象
const addedObjects = this._addedObjects;
const added = addedObjects.values;
// 遍历每一个被添加的对象
for (i = added.length - 1; i > -1; i--) {
entity = added[i];
id = entity.id;
// 创建UpdaterSet
updaterSet = new GeometryUpdaterSet(entity, this._scene);
this._updaterSets.set(id, updaterSet);
// 通过每一个updater尝试创建instance 并添加到batch中
updaterSet.forEach(function (updater) {
that._insertUpdaterIntoBatch(time, updater);
});
}
// 移除已经被添加的对象
addedObjects.removeAll();
// 在batch中创建primitive
let isUpdated = true;
const batches = this._batches;
const length = batches.length;
for (i = 0; i < length; i++) {
isUpdated = batches[i].update(time) && isUpdated;
}
return isUpdated;
};
生成instance
-
获取
polygonOutline
对应的instance
- 在函数
GeometryVisualizer.prototype._insertUpdaterIntoBatch
中将updater
传递到StaticOutlineGeometryBatch.prototype.add
函数中
this._outlineBatches[shadows].add(time, updater);
- 在
StaticOutlineGeometryBatch.prototype.add
先创建polygonOutline
对应的instance
const instance = updater.createOutlineGeometryInstance(time);
StaticOutlineGeometryBatch.prototype.add
中,调用batch.add
函数,传入instance
,并写入字典this.geometry
this.geometry.set(id, instance);
- 在函数
-
获取
polygon
对应的instance
-
同样在函数
GeometryVisualizer.prototype._insertUpdaterIntoBatch
中,将updater
传递到StaticGeometryColorBatch.prototype.add
函数中this._closedColorBatches[shadows].add(time, updater);
-
在
StaticGeometryColorBatch.prototype.add
先创建polygon
对应的instance
const instance = updater.createFillGeometryInstance(time);
StaticGeometryColorBatch.prototype.add
中,调用batch.add
函数,传入instance
,并写入字典this.geometry
this.geometry.set(id, instance);
-
生成primitive
在循环中遍历所有的GeometryBatch
对象,并update
-
生成
polygonOutline
对应的primitive
- 通过
StaticOutlineGeometryBatch.prototype.update
遍历solidBatchesLength
属性,并update
- 在
batch.update
中生成primitive
- 通过
-
生成
polygon
对应的primitive
-
通过
StaticGeometryColorBatch.prototype.update
调用updateItems
函数,在其内部,遍历batch
并update
-
在
batch.update
中生成primitive
-
时序图
- 在这里我们附上整个过程的时序图,可以帮助大家更好的了解整个过程
后续
- 后面我们会进一步探索创建得到的
primitive
如何被渲染,并对比其和我们直接添加的primitive
在组织结构上有什么区别