This sample shows how to create an exploded view effect in the viewport.

预览地址:https://www.itwinjs.org/sample-showcase/?group=Viewer+Features&sample=explode-sample&imodel=House+Sample


1. Purpose

The main purpose is giving an example of how to create the exploded view effect. To achieve this, it also shows how to:

  • Request specific elements and tiles from the backend.
  • Work with Transforms.
  • Render customized graphics using a TileTree.
  • Add a simple Animator to a viewport.
  • NOT as an example of generalized animation

2. Description

To create the exploded view effect, we request specific element graphics from the backend and draw them with a translation in order to move them apart. Most of this is handled by the TileTree and Tile APIs, and communicated through a TiledGraphicsProvider and TileTreeReference. I am omitting most of the logic about the tile hierarchy. See the inline comments and documentation for more information.

  1. The first goal is to load graphics content. This is mostly handled by the TileTree logic and our custom Tile objects. Our graphics tiles use the IModelTileRpcInterface.requestElementGraphics via the TileAdmin to request graphics from the backend and read it using a ImdlReader. The request requires a tolerance (how detailed the element's geometry is). This is calculated in the element tile based on the tile's distance from the camera. We add our tile tree to the viewport by using the TiledGraphicsProvider.forEachTileTreeRef method.
第一步是加载graphics内容。这主要由TileTree逻辑和自定义Tile对象来处理。Graphics tiles使用TileAdmin中的IModelTileRpcInterface.requestElementGraphic从后端请求graphics,并使用 ImdlReader读取。请求需要一个tolerance容区(描述元素的几何图形有多细致)。这在element tile中根据tile与摄影机的距离进行计算。我们通过使用TiledGraphicsProvider.forEachTileTreeRef向viewport添加tile tree。
  1. The second goal is to displace the elements based on the explode scaling. While loading the tile tree, we query the backend for the origin and the element-space bounding box of each element using the iModelConnection.query. We create transform into world space using the Placement3d API with the element's origin and apply (multiply) it by the queried bounding box. This moves the bounding box from element space to world space. In our root tile, we then combine each bounding box using the Range3d.extendRange method. In each graphics tile, we create a vector by subtracting the center of each element's bounding box from the center of the box combining them all. This vector is the direction the element should be displaced. This is then scaled by the explode scaling and used to create a translation transform using the Transform API.

    第二步是基于explode scaling爆炸比例置换元素。在加载tile tree时,我们使用iModelConnection.query查询每个原始的元素及其element-space绑定的box。我们使用Placement3d API创建原始element的local space到world space的转换,并将其应用(乘以)查询到的bounding box。这将bounding box从local space移动到world space。在root tile中,然后使用Range3d.extendRange方法组合所有bounding box。在每个graphics tile中,我们通过从总和bounding box的中心减去每个元素的bounding box的中心,从而创建一个向量。该向量是元素应移位的方向。然后通过explode scaling缩放,并用于使用Transform API创建转换。

  2. The third goal is to draw the graphics displaced by the previously calculated transform. This is done by overriding the Tile.drawGraphics method of our graphics tile. Create a new GraphicsBranch using RenderContext.createGraphicBranch and pass in the translation and an argument. These graphics are drawn in the TileTree.draw by the TileDrawArgs.drawGraphics method. Note: here that the TileDrawArgs used have been extended to include the scaling for the translation matrix.

    第三步是绘制先前被计算的替换graphics。通过使用我们的graphics tile覆盖Tile.drawGraphics来完成。使用RenderContext.createGraphicBranch创建新的GraphicsBranch,并传入变换和参数。这些graphics通过TileDrawArgs.drawGraphics方法在TileTree.draw下绘制。注意:这里使用的TileDrawArgs已经扩展为包含了矩阵变换的scaling。

  3. Now that the modified elements are being drawn, the old, static elements need to be hidden. This is done with two parts working together. First, a FeatureOverrideProvider added to the viewport monitors the modified elements. Once they have tile content loaded, the ElementIds are marked to never be drawn using the FeatureOverride.setNeverDrawnSet method. Second part is a FeatureAppearanceProvider being applied in the perviously mentioned call to RenderContext.createGraphicBranch. This provider ensures the elements relative to our tile tree are drawn by return a default FeatureAppearance for our modified elements.

    现在修改后的元素已被绘制,需要隐藏旧的静态元素。这由两部分工作共同完成。首先,向viewport添加FeatureOverrideProvider监听被修改的元素。一旦加载了tile content,ElementIds就会被FeatureOverride.setNeverDrawnSet方法标记为从不绘制。第二部分是在前面提到的调用RenderContext.createGraphicBranch时传入的FeatureAppearanceProvider. 此provider通过返回我们修改过的元素的默认FeatureAppearance,确保与我们定义的tile tree相关的Elements会被绘制。

  4. The EmphasizeElements API is used to Isolate the objects in the sample. For more information see the Emphasize Elements Sample of that topic.

    EmphasizeElements API用于隔离示例中的对象。更多信息请参见该主题的“强调元素”示例。

3. Notes

Many of the APIs used in this sample are still in beta and alpha, especially those related to the tiles. Please check the latest documentation for changes before adapting for your own purposes.

Much of this logic is based on the DynamicIModelTile of iModel.js. The workflow used in this sample does not scale. While doing this with a low number of elements works well, the performance can quickly fall off when effecting hundreds of elements.


4. Class

class ExplodeProvider implements TiledGraphicsProvider, FeatureOverrideProvider

  • 单例模式、维护单个ExplodeTileTreeRef
  • setData方法可以改变ref的name、elementIds、explodeScaling
  • TiledGraphicsProvider:添加自定义TileTree到ViewPort中
  • FeatureOverrideProvider:监听元素修改事件并隐藏原静态元素

class ExplodeTreeReference extends TileTreeReference

  • 维护TreeDataMap、维护onTreeDataUpdate事件
  • 维护ExplodeTreeSupplier
  • TileTreeReference: 通过getTileTreeOwner方法传入id和supplier拿到tree,用tree重写createDrawArgs方法,创建绘制参数数组

class ExplodeTreeSupplier implements TileTreeSupplier

  • 从后端查询Element数据,包括elementId、origin、boundingBox、transformWorld,这些数据被用以构建ExplodeTileTree
  • TileTreeSupplier: 实现createTileTree方法生成与指定id对应的TileTree,并在Map中关联Id和Tree
  • TileTreeSupplier:实现compareTileTreeIds方法对比id,以优化查询效率

class ExplodeTileTree extends TileTree

  • 维护单个rootTile,构造时生成
  • 维护elementContentLoaded
  • this.iModel.expandDisplayedExtents(this.range),保证动画时不会被切割
  • ExplodeTreeReference.setTreeRange()、ExplodeTreeReference.setTreeLoadedId(),通过这两个方法将range和elementId传入ref中
  • TileTree: 重写rootTile 返回自身维护的rootTile
  • TileTree: 重写maxDepth 因层次结构为 RootTile -> ElementTile -> GraphicsTile,故定义为3
  • TileTree: 重写draw 该方法使用传入的ExplodeTileDrawArgs,传递给rootTile下的selectTiles,并依次调用它们的drawGraphics方法

class RootTile extends Tile implements FeatureAppearanceProvider

  • 维护centerOfMass(计算后的爆炸中心点)
  • 维护的range先是所有ElementTile的Range,后又叠加了Element变换后的Range
  • selectTiles方法调用children(ElementTile)的selectTile,拿到了GraphicsTile数组
  • Tile:重写loadChildren方法,将centerOfMass传入每一个ElementTile中
  • FeatureAppearanceProvider:实现getFeatureAppearance,确保相关的Element被绘制

class ElementTile extends Tile

  • 计算了Element变换后的Range
  • Tile:重写了loadChildren方法,不过返回了空数组,因为只有在必要时才加载GraphicsTile,即selectTile时
  • selectTile方法对GraphicsTile做了一次缓存,强行改变了this.children

class ExplodedGraphicsTile extends Tile

  • Tile: 重写requetContent方法,使用TileAdmin中的requestElementGraphics从后端请求graphics
  • Tile:重写readerContent方法,使用ImdlReader读取请求到的graphics
  • Tile:重写drawGraphics方法,使用ExplodeTileDrawArgs计算Transform,创建图形分支GraphicBranch并应用变换

results matching ""

    No results matching ""