Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6c71ebc
support BENTLEY_materials_point_style
markschlosseratbentley Dec 10, 2025
0432ac5
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
markschlosseratbentley Dec 10, 2025
7b2920c
CHANGES.md
markschlosseratbentley Dec 10, 2025
ada5813
unit test
markschlosseratbentley Dec 10, 2025
200d086
use unsigned short indices so CI can work if OES_element_index_uint i…
markschlosseratbentley Dec 11, 2025
24025b5
make the sandcastle example be dev
markschlosseratbentley Dec 11, 2025
9ebb71b
make the sandcastle example be dev
markschlosseratbentley Dec 11, 2025
e3e4ab0
Remove Showcases label
markschlosseratbentley Dec 11, 2025
c62fed7
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
danielzhong Dec 24, 2025
81bfcf3
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
ggetz Jan 9, 2026
f93f622
only use one copy of the .gltf sample file
markschlosseratbentley Jan 9, 2026
a01e317
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
markschlosseratbentley Jan 12, 2026
0f9f332
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
markschlosseratbentley Jan 16, 2026
bc84e06
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
markschlosseratbentley Jan 21, 2026
551b898
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
ggetz Jan 27, 2026
65195bd
Update packages/engine/Source/Scene/ModelComponents.js
markschlosseratbentley Jan 27, 2026
80f1717
Update packages/sandcastle/gallery/styled-gltf-points-dev/sandcastle.…
markschlosseratbentley Jan 27, 2026
ce05472
fix html
markschlosseratbentley Jan 27, 2026
6916fd0
floor non-integer diameters instead of skipping
markschlosseratbentley Jan 27, 2026
a7ecafa
make sure the floor doesn't floor to zero
markschlosseratbentley Jan 27, 2026
9d1cb0c
Merge branch 'main' into markschlosser/BENTLEY_materials_point_style
markschlosseratbentley Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@

- Beginning in CesiumJS 1.140, billboards and labels will require device support for WebGL 2, or WebGL 1 with ANGLE_instanced_arrays and MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0. For more information or to share feedback, please see [#13053](https://github.com/CesiumGS/cesium/issues/13053). [#13067](https://github.com/CesiumGS/cesium/issues/13067)

#### Additions :tada:

- Added support for the proposed [BENTLEY_materials_point_style](https://github.com/CesiumGS/glTF/pull/91) glTF extension. This allows point primitives to have a diameter property specified and respected when loaded via glTF.

## 1.136 - 2025-12-01

### @cesium/engine
Expand Down
222 changes: 222 additions & 0 deletions Specs/Data/Models/glTF-2.0/StyledPoints/points-r5-g8-b14-y10.gltf
Original file line number Diff line number Diff line change
@@ -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": 5123,
"count": 1,
"type": "SCALAR",
"max": [
0
],
"min": [
0
]
},
{
"bufferView": 1,
"byteOffset": 4,
"componentType": 5123,
"count": 1,
"type": "SCALAR",
"max": [
1
],
"min": [
1
]
},
{
"bufferView": 1,
"byteOffset": 8,
"componentType": 5123,
"count": 1,
"type": "SCALAR",
"max": [
2
],
"min": [
2
]
},
{
"bufferView": 1,
"byteOffset": 12,
"componentType": 5123,
"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
}
10 changes: 10 additions & 0 deletions packages/engine/Source/Scene/GltfLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
17 changes: 17 additions & 0 deletions packages/engine/Source/Scene/Model/MaterialPipelineStage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions packages/engine/Source/Scene/ModelComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/engine/Source/Shaders/Model/ModelFS.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions packages/engine/Source/Shaders/Model/ModelVS.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions packages/engine/Specs/Scene/GltfLoaderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ describe(
"./Data/Models/glTF-2.0/BoxAnisotropy/glTF/BoxAnisotropy.gltf";
const clearcoatTestData =
"./Data/Models/glTF-2.0/BoxClearcoat/glTF/BoxClearcoat.gltf";
const pointStyleTestData =
"./Data/Models/glTF-2.0/StyledPoints/points-r5-g8-b14-y10.gltf";
const meshPrimitiveRestartTestData =
"./Data/Models/glTF-2.0/MeshPrimitiveRestart/glTF/MeshPrimitiveRestart.gltf";
const edgeVisibilityTestData =
Expand Down Expand Up @@ -4176,6 +4178,57 @@ describe(
expect(clearcoatNormalTexture.texture.width).toBe(256);
});

it("loads model with BENTLEY_materials_point_style extension", async function () {
const gltfLoader = await loadGltf(pointStyleTestData);

// The test model has 4 primitives with different materials. Let's verify they all exist.
const primitives = gltfLoader.components.nodes[0].primitives;
expect(primitives.length).toBe(4);

// Check that pointDiameter was loaded correctly for each material. Each primitive has a different diameter.
expect(primitives[0].material.pointDiameter).toBe(5);
expect(primitives[1].material.pointDiameter).toBe(8);
expect(primitives[2].material.pointDiameter).toBe(14);
expect(primitives[3].material.pointDiameter).toBe(10);
});

it("ignores BENTLEY_materials_point_style with invalid negative diameter", async function () {
function modifyGltf(gltf) {
// Set an invalid negative diameter (diameters must be >0).
gltf.materials[0].extensions.BENTLEY_materials_point_style.diameter =
-5;
return gltf;
}

const gltfLoader = await loadModifiedGltfAndTest(
pointStyleTestData,
undefined,
modifyGltf,
);

// The invalid negative diameter should be ignored; property should be undefined once the glTF is loaded.
const material = gltfLoader.components.nodes[0].primitives[0].material;
expect(material.pointDiameter).toBeUndefined();
});

it("ignores BENTLEY_materials_point_style with non-integer diameter", async function () {
function modifyGltf(gltf) {
// Set an invalid non-integer diameter (diameters must be integers).
gltf.materials[0].extensions.BENTLEY_materials_point_style.diameter = 5.5;
return gltf;
}

const gltfLoader = await loadModifiedGltfAndTest(
pointStyleTestData,
undefined,
modifyGltf,
);

// Invalid non-integer diameter should be ignored; property should be undefined once the glTF is loaded.
const material = gltfLoader.components.nodes[0].primitives[0].material;
expect(material.pointDiameter).toBeUndefined();
});

it("loads model with EXT_mesh_primitive_restart extension", async function () {
const gltf = await Resource.fetchJson({
url: meshPrimitiveRestartTestData,
Expand Down
Loading