Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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