From 6c71ebc0c2b19977b7e9fc20e3ab369f4af37465 Mon Sep 17 00:00:00 2001 From: Mark Schlosser <47000437+markschlosseratbentley@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:00:48 -0500 Subject: [PATCH 1/8] support BENTLEY_materials_point_style --- .../StyledPoints/points-r5-g8-b14-y10.gltf | 222 ++++++++++++++++++ packages/engine/Source/Scene/GltfLoader.js | 10 + .../Scene/Model/MaterialPipelineStage.js | 17 ++ .../engine/Source/Scene/ModelComponents.js | 10 + .../engine/Source/Shaders/Model/ModelFS.glsl | 8 + .../engine/Source/Shaders/Model/ModelVS.glsl | 2 + .../gallery/styled-gltf-points/index.html | 36 +++ .../gallery/styled-gltf-points/main.js | 42 ++++ .../styled-gltf-points/sandcastle.yaml | 6 + .../gallery/styled-gltf-points/thumbnail.jpg | Bin 0 -> 5949 bytes 10 files changed, 353 insertions(+) create mode 100755 Apps/SampleData/models/StyledPoints/points-r5-g8-b14-y10.gltf create mode 100644 packages/sandcastle/gallery/styled-gltf-points/index.html create mode 100644 packages/sandcastle/gallery/styled-gltf-points/main.js create mode 100644 packages/sandcastle/gallery/styled-gltf-points/sandcastle.yaml create mode 100644 packages/sandcastle/gallery/styled-gltf-points/thumbnail.jpg diff --git a/Apps/SampleData/models/StyledPoints/points-r5-g8-b14-y10.gltf b/Apps/SampleData/models/StyledPoints/points-r5-g8-b14-y10.gltf new file mode 100755 index 000000000000..fae9d455bb8b --- /dev/null +++ b/Apps/SampleData/models/StyledPoints/points-r5-g8-b14-y10.gltf @@ -0,0 +1,222 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "BENTLEY_materials_point_style" + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5126, + "count": 4, + "type": "VEC3", + "max": [ + 5.0, + 5.0, + 5.0 + ], + "min": [ + -5.0, + -5.0, + -5.0 + ] + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5125, + "count": 1, + "type": "SCALAR", + "max": [ + 0 + ], + "min": [ + 0 + ] + }, + { + "bufferView": 1, + "byteOffset": 4, + "componentType": 5125, + "count": 1, + "type": "SCALAR", + "max": [ + 1 + ], + "min": [ + 1 + ] + }, + { + "bufferView": 1, + "byteOffset": 8, + "componentType": 5125, + "count": 1, + "type": "SCALAR", + "max": [ + 2 + ], + "min": [ + 2 + ] + }, + { + "bufferView": 1, + "byteOffset": 12, + "componentType": 5125, + "count": 1, + "type": "SCALAR", + "max": [ + 3 + ], + "min": [ + 3 + ] + } + ], + "buffers": [ + { + "byteLength": 64, + "uri": "data:application/octet-stream;base64,AACgwAAAoMAAAKDAAACgQAAAoMAAAKDAAAAAAAAAoEAAAKDAAAAAAAAAAAAAAKBAAAAAAAEAAAACAAAAAwAAAA==" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 48, + "target": 34962 + }, + { + "buffer": 0, + "byteOffset": 48, + "byteLength": 16, + "target": 34963 + } + ], + "materials": [ + { + "name": "Red", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "extensions": { + "BENTLEY_materials_point_style": { + "diameter": 5.0 + } + } + }, + { + "name": "Green", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.0, + 1.0, + 0.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "extensions": { + "BENTLEY_materials_point_style": { + "diameter": 8.0 + } + } + }, + { + "name": "Blue", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.0, + 0.0, + 1.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "extensions": { + "BENTLEY_materials_point_style": { + "diameter": 14.0 + } + } + }, + { + "name": "Yellow", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1.0, + 1.0, + 0.0, + 1.0 + ], + "metallicFactor": 0.0 + }, + "extensions": { + "BENTLEY_materials_point_style": { + "diameter": 10.0 + } + } + } + ], + "meshes": [ + { + "name": "PointCloud", + "primitives": [ + { + "mode": 0, + "material": 0, + "indices": 1, + "attributes": { + "POSITION": 0 + } + }, + { + "mode": 0, + "material": 1, + "indices": 2, + "attributes": { + "POSITION": 0 + } + }, + { + "mode": 0, + "material": 2, + "indices": 3, + "attributes": { + "POSITION": 0 + } + }, + { + "mode": 0, + "material": 3, + "indices": 4, + "attributes": { + "POSITION": 0 + } + } + ] + } + ], + "nodes": [ + { + "name": "PointCloudNode", + "mesh": 0 + } + ], + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "scene": 0 +} diff --git a/packages/engine/Source/Scene/GltfLoader.js b/packages/engine/Source/Scene/GltfLoader.js index 800d4a7c2428..c96ca9b82178 100644 --- a/packages/engine/Source/Scene/GltfLoader.js +++ b/packages/engine/Source/Scene/GltfLoader.js @@ -1811,6 +1811,16 @@ function loadMaterial(loader, gltfMaterial, frameState) { material.alphaCutoff = gltfMaterial.alphaCutoff; material.doubleSided = gltfMaterial.doubleSided; + // BENTLEY_materials_point_style extension + const pointStyleExtension = extensions.BENTLEY_materials_point_style; + if (defined(pointStyleExtension) && defined(pointStyleExtension.diameter)) { + const diameter = pointStyleExtension.diameter; + // Validate that diameter is a positive integer as the extension specification requires. + if (diameter > 0 && Math.floor(diameter) === diameter) { + material.pointDiameter = diameter; + } + } + return material; } diff --git a/packages/engine/Source/Scene/Model/MaterialPipelineStage.js b/packages/engine/Source/Scene/Model/MaterialPipelineStage.js index ed2f9e2f1f94..58136200d6aa 100644 --- a/packages/engine/Source/Scene/Model/MaterialPipelineStage.js +++ b/packages/engine/Source/Scene/Model/MaterialPipelineStage.js @@ -167,6 +167,23 @@ MaterialPipelineStage.process = function ( alphaOptions.alphaCutoff = material.alphaCutoff; } + // Configure and handle point diameter for POINTS primitives (BENTLEY_materials_point_style extension). + if (defined(material.pointDiameter)) { + shaderBuilder.addDefine( + "HAS_POINT_DIAMETER", + undefined, + ShaderDestination.VERTEX, + ); + shaderBuilder.addUniform( + "float", + "u_pointDiameter", + ShaderDestination.VERTEX, + ); + uniformMap.u_pointDiameter = function () { + return material.pointDiameter * frameState.pixelRatio; + }; + } + shaderBuilder.addFragmentLines(MaterialStageFS); if (material.doubleSided) { diff --git a/packages/engine/Source/Scene/ModelComponents.js b/packages/engine/Source/Scene/ModelComponents.js index 71d7fb088076..b63d5f20741b 100644 --- a/packages/engine/Source/Scene/ModelComponents.js +++ b/packages/engine/Source/Scene/ModelComponents.js @@ -1623,6 +1623,16 @@ function Material() { * @private */ this.unlit = false; + + /** + * The point diameter in pixels for POINTS primitives. This is set by the + * BENTLEY_materials_point_style extension. + * + * @type {number} + * @default undefined + * @private + */ + this.pointDiameter = undefined; } /** diff --git a/packages/engine/Source/Shaders/Model/ModelFS.glsl b/packages/engine/Source/Shaders/Model/ModelFS.glsl index 76d3469ef094..4be74fa86844 100644 --- a/packages/engine/Source/Shaders/Model/ModelFS.glsl +++ b/packages/engine/Source/Shaders/Model/ModelFS.glsl @@ -29,6 +29,14 @@ SelectedFeature selectedFeature; void main() { + #ifdef PRIMITIVE_TYPE_POINTS + // Render points as circles + float distanceToCenter = length(gl_PointCoord - vec2(0.5)); + if (distanceToCenter > 0.5) { + discard; + } + #endif + #ifdef HAS_POINT_CLOUD_SHOW_STYLE if (v_pointCloudShow == 0.0) { diff --git a/packages/engine/Source/Shaders/Model/ModelVS.glsl b/packages/engine/Source/Shaders/Model/ModelVS.glsl index 96cee64b06e1..8b11b84d05bf 100644 --- a/packages/engine/Source/Shaders/Model/ModelVS.glsl +++ b/packages/engine/Source/Shaders/Model/ModelVS.glsl @@ -142,6 +142,8 @@ void main() gl_PointSize = vsOutput.pointSize; #elif defined(HAS_POINT_CLOUD_POINT_SIZE_STYLE) || defined(HAS_POINT_CLOUD_ATTENUATION) gl_PointSize = pointCloudPointSizeStylingStage(attributes, metadata); + #elif defined(HAS_POINT_DIAMETER) + gl_PointSize = u_pointDiameter; #else gl_PointSize = 1.0; #endif diff --git a/packages/sandcastle/gallery/styled-gltf-points/index.html b/packages/sandcastle/gallery/styled-gltf-points/index.html new file mode 100644 index 000000000000..abd8f4b3276d --- /dev/null +++ b/packages/sandcastle/gallery/styled-gltf-points/index.html @@ -0,0 +1,36 @@ + + +
+ + + + + +