Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper handling of offset and scale in metadata picking #12237

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
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 @@ -14,6 +14,10 @@

- Fix flickering issue caused by bounding sphere retrieval being blocked by the bounding sphere of another entity. [#12230](https://github.com/CesiumGS/cesium/pull/12230)

##### Fixes :wrench:

- Properly handle `offset` and `scale` properties when picking metadata from property textures. [#12237](https://github.com/CesiumGS/cesium/pull/12237)

### 1.122 - 2024-10-01

#### @cesium/engine
Expand Down
125 changes: 109 additions & 16 deletions packages/engine/Source/Scene/DerivedCommand.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MetadataComponentType } from "@cesium/engine";
import defined from "../Core/defined.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import RenderState from "../Renderer/RenderState.js";
Expand Down Expand Up @@ -416,6 +417,98 @@ function getGlslType(classProperty) {
return `ivec${componentCount}`;
}

/**
* Returns a shader statement that applies the inverse of the
* value transform to the given value, based on the given offset
* and scale.
*
* @param {string} input The input value
* @param {string} offset The offset
* @param {string} scale The scale
* @returns {string} The statement
*/
function unapplyValueTransform(input, offset, scale) {
return `((${input} - float(${offset})) / float(${scale}))`;
}

/**
* Returns a shader statement that applies the inverse of the
* normalization, based on the given component type
*
* @param {string} input The input value
* @param {string} componentType The component type
* @returns {string} The statement
*/
function unnormalize(input, componentType) {
const max = MetadataComponentType.getMaximum(componentType);
return `(${input}) / float(${max})`;
}

/**
* Creates a shader statement that returns the value of the specified
* property, normalized to the range [0, 1].
*
* @param {MetadataClassProperty} classProperty The class property
* @param {object} metadataProperty The metadata property, either
* a `PropertyTextureProperty` or a `PropertyAttributeProperty`
* @returns {string} The string
*/
function getSourceValueStringScalar(classProperty, metadataProperty) {
let result = `float(value)`;

// The 'hasValueTransform' indicates whether the property
// (or its class property) did define an 'offset' or 'scale'.
// Even when they had not been defined in the JSON, they are
// defined in the object, with default values.
if (metadataProperty.hasValueTransform) {
const offset = metadataProperty.offset;
const scale = metadataProperty.scale;
result = unapplyValueTransform(result, offset, scale);
}
if (!classProperty.normalized) {
result = unnormalize(result, classProperty.componentType);
}
return result;
}

/**
* Creates a shader statement that returns the value of the specified
* component of the given property, normalized to the range [0, 1].
*
* @param {MetadataClassProperty} classProperty The class property
* @param {object} metadataProperty The metadata property, either
* a `PropertyTextureProperty` or a `PropertyAttributeProperty`
* @param {string} componentName The name, in ["x", "y", "z", "w"]
* @returns {string} The string
*/
function getSourceValueStringComponent(
classProperty,
metadataProperty,
componentName,
) {
const valueString = `value.${componentName}`;
let result = `float(${valueString})`;

// The 'hasValueTransform' indicates whether the property
// (or its class property) did define an 'offset' or 'scale'.
// Even when they had not been defined in the JSON, they are
// defined in the object, with default values
// Note that in the 'PropertyTextureProperty' and the
// 'PropertyAttributeProperty', these values are
// stored as "object types" (like 'Cartesian2'), whereas
// in the 'MetadataClassProperty', they are stored as
// "array types", e.g. a `[number, number]`
if (metadataProperty.hasValueTransform) {
const offset = metadataProperty.offset[componentName];
const scale = metadataProperty.scale[componentName];
result = unapplyValueTransform(result, offset, scale);
}
if (!classProperty.normalized) {
result = unnormalize(result, classProperty.componentType);
}
return result;
}

/**
* Creates a new `ShaderProgram` from the given input that renders metadata
* values into the frame buffer, according to the given picked metadata info.
Expand Down Expand Up @@ -452,34 +545,34 @@ function getPickMetadataShaderProgram(
return shader;
}

const metadataProperty = pickedMetadataInfo.metadataProperty;
const classProperty = pickedMetadataInfo.classProperty;
const glslType = getGlslType(classProperty);

// Define the components that will go into the output `metadataValues`.
// This will be the 'color' that is written into the frame buffer,
// meaning that the values should be in [0.0, 1.0], and will become
// values in [0, 255] in the frame buffer.
// By default, all of them are 0.0.
const sourceValueStrings = ["0.0", "0.0", "0.0", "0.0"];
const componentCount = getComponentCount(classProperty);
if (componentCount === 1) {
// When the property is a scalar, store its value directly
// in `metadataValues.x`
sourceValueStrings[0] = `float(value)`;
// When the property is a scalar, store the source value
// string directly in `metadataValues.x`
sourceValueStrings[0] = getSourceValueStringScalar(
classProperty,
metadataProperty,
);
} else {
// When the property is an array, store the array elements
// in `metadataValues.x/y/z/w`
const components = ["x", "y", "z", "w"];
for (let i = 0; i < componentCount; i++) {
const component = components[i];
const valueString = `value.${component}`;
sourceValueStrings[i] = `float(${valueString})`;
}
}

// Make sure that the `metadataValues` components are all in
// the range [0, 1] (which will result in RGBA components
// in [0, 255] during rendering)
if (!classProperty.normalized) {
const componentNames = ["x", "y", "z", "w"];
for (let i = 0; i < componentCount; i++) {
sourceValueStrings[i] += " / 255.0";
sourceValueStrings[i] = getSourceValueStringComponent(
classProperty,
metadataProperty,
componentNames[i],
);
}
}

Expand Down
26 changes: 26 additions & 0 deletions packages/engine/Source/Scene/MetadataClassProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,10 @@ Object.defineProperties(MetadataClassProperty.prototype, {
/**
* The offset to be added to property values as part of the value transform.
*
* This is always defined, even when `hasValueTransform` is `false`. If
* the class property JSON itself did not define it, then it will be
* initialized to the default value.
*
* @memberof MetadataClassProperty.prototype
* @type {number|number[]|number[][]}
* @readonly
Expand All @@ -451,6 +455,10 @@ Object.defineProperties(MetadataClassProperty.prototype, {
/**
* The scale to be multiplied to property values as part of the value transform.
*
* This is always defined, even when `hasValueTransform` is `false`. If
* the class property JSON itself did not define it, then it will be
* initialized to the default value.
*
* @memberof MetadataClassProperty.prototype
* @type {number|number[]|number[][]}
* @readonly
Expand Down Expand Up @@ -1139,6 +1147,24 @@ function normalizeInPlace(values, valueType, normalizeFunction) {
}

/**
* Applies the value transform that is defined with the given offsets
* and scales to the given values.
*
* If the given values are not an array, then the given transformation
* function will be applied directly.
*
* If the values are an array, then this function will be called recursively
* with the array elements, boiling down to a component-wise application
* of the transformation function to the innermost array elements.
*
* @param {number|number[]|number[][]} values The input values
* @param {number|number[]|number[][]} offsets The offsets
* @param {number|number[]|number[][]} scales The scales
javagl marked this conversation as resolved.
Show resolved Hide resolved
* @param {Function} transformationFunction The function with the signature
* `(value:number, offset:number, scale:number) : number` that will be
* applied to the innermost elements
* @returns The input values (or the result of applying the transformation
* function to a single value if the values have not been an array).
* @private
*/
MetadataClassProperty.valueTransformInPlace = function (
Expand Down
Loading
Loading