Extract Vector glTF tile data into BufferPrimitiveCollection#13271
Extract Vector glTF tile data into BufferPrimitiveCollection#13271danielzhong wants to merge 7 commits intodonmccurdy/feat/bufferprimitivecollection-render-glfrom
Conversation
…ollection-render-gl' into DanielZhong/VectorTiles
|
Thank you for the pull request, @danielzhong! ✅ We can confirm we have a CLA on file for you. |
donmccurdy
left a comment
There was a problem hiding this comment.
@danielzhong Great progress here, I like how this is coming together! I left a few initial comments, mainly since we're writing the code and the spec in parallel just trying to make sure things align.
| const values = new Float64Array(indices.length * 3); | ||
| for (let i = 0; i < indices.length; i++) { | ||
| const vertexIndex = indices[i]; | ||
| const srcOffset = vertexIndex * 3; | ||
| values[i * 3] = positions[srcOffset]; | ||
| values[i * 3 + 1] = positions[srcOffset + 1]; | ||
| values[i * 3 + 2] = positions[srcOffset + 2]; | ||
| } | ||
|
|
||
| polylineCollection.add({ positions: values }, polylineView); | ||
| setPrimitiveFeatureId(polylineView, featureIdSource, indices[0]); |
There was a problem hiding this comment.
I'm hoping we'll be able to upload a range of the position array directly to the collection, but I guess the collection needs to allow more than float64array here? Opened:
| if (primitiveType === PrimitiveType.TRIANGLE_STRIP) { | ||
| triangleIndices = | ||
| ModelReader.convertTriangleStripToTriangleIndices(indices); | ||
| } else if (primitiveType === PrimitiveType.TRIANGLE_FAN) { | ||
| triangleIndices = ModelReader.convertTriangleFanToTriangleIndices(indices); | ||
| } |
There was a problem hiding this comment.
I think it's OK just to error out if the primitive uses the vector extension with TRIANGLE_STRIP or TRIANGLE_FAN topology, since we won't be able to identify the original polygon connectivity in those cases anyway.
| polygonCollection.add( | ||
| { | ||
| positions: positions, | ||
| triangles: toUint32Array(triangleIndices), |
There was a problem hiding this comment.
See above, with #13277 it should be OK to use whatever the source array type is.
| polygonView, | ||
| ) { | ||
| const primitiveType = primitive.primitiveType; | ||
| const positions = readTransformedPositions(primitive, nodeTransform); |
There was a problem hiding this comment.
Do we need to transform the vertex positions? I'm hoping that we can apply the node transform to collection.modelMatrix or something like that, but not 100% sure how this all fits with the rest of the loading process!
| if (PrimitiveType.isLines(primitiveType) && defined(polylineCollection)) { | ||
| if (primitiveType === PrimitiveType.LINES) { | ||
| for (let i = 0; i + 1 < indices.length; i += 2) { | ||
| appendPolylinePrimitive( | ||
| polylineCollection, | ||
| polylineView, | ||
| featureIdSource, | ||
| positions, | ||
| [indices[i], indices[i + 1]], | ||
| ); | ||
| } | ||
| } else if (primitiveType === PrimitiveType.LINE_STRIP) { | ||
| appendPolylinePrimitive( | ||
| polylineCollection, | ||
| polylineView, | ||
| featureIdSource, | ||
| positions, | ||
| indices, | ||
| ); | ||
| } else if (primitiveType === PrimitiveType.LINE_LOOP) { | ||
| const loopIndices = new Uint32Array(indices.length + 1); | ||
| for (let i = 0; i < indices.length; i++) { | ||
| loopIndices[i] = indices[i]; | ||
| } | ||
| loopIndices[indices.length] = indices[0]; | ||
| appendPolylinePrimitive( | ||
| polylineCollection, | ||
| polylineView, | ||
| featureIdSource, | ||
| positions, | ||
| loopIndices, | ||
| ); | ||
| } | ||
| return; |
There was a problem hiding this comment.
Here I think it's OK if we throw an error if we see any primitive types not allowed by the vector extension.
| const rootNodes = components.scene.nodes; | ||
| for (let i = 0; i < rootNodes.length; i++) { | ||
| gatherNodeStats(rootNodes[i], stats); | ||
| } | ||
|
|
||
| const points = | ||
| stats.pointPrimitiveCount > 0 | ||
| ? new BufferPointCollection({ | ||
| primitiveCountMax: stats.pointPrimitiveCount, | ||
| vertexCountMax: stats.pointVertexCount, | ||
| }) | ||
| : undefined; | ||
| const polylines = | ||
| stats.polylinePrimitiveCount > 0 | ||
| ? new BufferPolylineCollection({ | ||
| primitiveCountMax: stats.polylinePrimitiveCount, | ||
| vertexCountMax: stats.polylineVertexCount, | ||
| }) | ||
| : undefined; | ||
| const polygons = | ||
| stats.polygonPrimitiveCount > 0 | ||
| ? new BufferPolygonCollection({ | ||
| primitiveCountMax: stats.polygonPrimitiveCount, | ||
| vertexCountMax: stats.polygonVertexCount, | ||
| holeCountMax: 0, | ||
| triangleCountMax: stats.polygonTriangleCount, | ||
| }) | ||
| : undefined; | ||
|
|
||
| const pointView = defined(points) ? new BufferPoint() : undefined; | ||
| const polylineView = defined(polylines) ? new BufferPolyline() : undefined; | ||
| const polygonView = defined(polygons) ? new BufferPolygon() : undefined; | ||
|
|
||
| for (let i = 0; i < rootNodes.length; i++) { | ||
| appendNodeToBuffers( | ||
| rootNodes[i], | ||
| Matrix4.IDENTITY, | ||
| points, | ||
| pointView, | ||
| polylines, | ||
| polylineView, | ||
| polygons, | ||
| polygonView, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Ah, important decision here! If the GLB has 50 mesh primitives, do we want 50 BufferPrimitiveCollections, or just 1 of each topology type? I'd been assuming that the tiler would combine mesh primitives as much as it safely can, so the runtime we could output one collection per mesh primitive and just apply the node transform with collection.modelMatrix. But there might be more to this, @lilleyse do you have a preference?
There was a problem hiding this comment.
I'd been assuming that the tiler would combine mesh primitives as much as it safely can, so the runtime we could output one collection per mesh primitive and just apply the node transform with
collection.modelMatrix
That was my assumption as well. One primitive maps to one buffer collection.
Description
This draft PR is initial work ahead of the final vector tiles spec (TBD).
For now, it assumes a
CESIUM_vector_tilesflag on vector tile datasets, then extracts the required data from glTF intoBufferPrimitiveCollectiontypes, including primitive topology (POINTS/LINES/TRIANGLES), indices, transformed positions, and feature IDs (maybe more?).Those buffers are then rendered through the existing collection renderer path.
.glbtile.VectorGltf3DTileContent.fromGltf(), we callModel.fromGltfAsync()for decode only, without rendering.update(), once the decode model is ready, it callsinitializeVectorPrimitives().initializeVectorPrimitives(),createVectorTileBuffersFromModelComponents()converts glTF primitives into:update()setsmodelMatrixon each collection, then calls eachcollection.update(frameState).Issue number and link
Testing plan
Author checklist
CONTRIBUTORS.mdCHANGES.mdwith a short summary of my change