PropertyTextureProperty.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import Check from "../Core/Check.js";
  2. import defaultValue from "../Core/defaultValue.js";
  3. import defined from "../Core/defined.js";
  4. import GltfLoaderUtil from "./GltfLoaderUtil.js";
  5. import MetadataType from "./MetadataType.js";
  6. import MetadataComponentType from "./MetadataComponentType.js";
  7. /**
  8. * A property in a property texture.
  9. *
  10. * <p>
  11. * See the {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata|EXT_structural_metadata Extension} as well as the
  12. * previous {@link https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata|EXT_feature_metadata Extension} for glTF.
  13. * </p>
  14. *
  15. * @param {object} options Object with the following properties:
  16. * @param {object} options.property The property JSON object.
  17. * @param {MetadataClassProperty} options.classProperty The class property.
  18. * @param {Object<number, Texture>} options.textures An object mapping texture IDs to {@link Texture} objects.
  19. *
  20. * @alias PropertyTextureProperty
  21. * @constructor
  22. *
  23. * @private
  24. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  25. */
  26. function PropertyTextureProperty(options) {
  27. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  28. const property = options.property;
  29. const classProperty = options.classProperty;
  30. const textures = options.textures;
  31. //>>includeStart('debug', pragmas.debug);
  32. Check.typeOf.object("options.property", property);
  33. Check.typeOf.object("options.classProperty", classProperty);
  34. Check.typeOf.object("options.textures", textures);
  35. //>>includeEnd('debug');
  36. // in EXT_structural_metadata, the property is a valid glTF textureInfo
  37. const channels = defined(property.channels) ? property.channels : [0];
  38. const textureInfo = property;
  39. const textureReader = GltfLoaderUtil.createModelTextureReader({
  40. textureInfo: textureInfo,
  41. channels: reformatChannels(channels),
  42. texture: textures[textureInfo.index],
  43. });
  44. this._min = property.min;
  45. this._max = property.max;
  46. let offset = property.offset;
  47. let scale = property.scale;
  48. // This needs to be set before handling default values
  49. const hasValueTransform =
  50. classProperty.hasValueTransform || defined(offset) || defined(scale);
  51. // If the property attribute does not define an offset/scale, it inherits from
  52. // the class property. The class property handles setting the default of
  53. // identity: (offset 0, scale 1) with the same scalar/vector/matrix types.
  54. // array types are disallowed by the spec.
  55. offset = defaultValue(offset, classProperty.offset);
  56. scale = defaultValue(scale, classProperty.scale);
  57. // offset and scale are applied on the GPU, so unpack the values
  58. // as math types we can use in uniform callbacks.
  59. offset = classProperty.unpackVectorAndMatrixTypes(offset);
  60. scale = classProperty.unpackVectorAndMatrixTypes(scale);
  61. this._offset = offset;
  62. this._scale = scale;
  63. this._hasValueTransform = hasValueTransform;
  64. this._textureReader = textureReader;
  65. this._classProperty = classProperty;
  66. this._extras = property.extras;
  67. this._extensions = property.extensions;
  68. }
  69. Object.defineProperties(PropertyTextureProperty.prototype, {
  70. /**
  71. * The texture reader.
  72. *
  73. * @memberof PropertyTextureProperty.prototype
  74. * @type {ModelComponents.TextureReader}
  75. * @readonly
  76. * @private
  77. */
  78. textureReader: {
  79. get: function () {
  80. return this._textureReader;
  81. },
  82. },
  83. /**
  84. * True if offset/scale should be applied. If both offset/scale were
  85. * undefined, they default to identity so this property is set false
  86. *
  87. * @memberof PropertyTextureProperty.prototype
  88. * @type {boolean}
  89. * @readonly
  90. * @private
  91. */
  92. hasValueTransform: {
  93. get: function () {
  94. return this._hasValueTransform;
  95. },
  96. },
  97. /**
  98. * The offset to be added to property values as part of the value transform.
  99. *
  100. * @memberof PropertyTextureProperty.prototype
  101. * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4}
  102. * @readonly
  103. * @private
  104. */
  105. offset: {
  106. get: function () {
  107. return this._offset;
  108. },
  109. },
  110. /**
  111. * The scale to be multiplied to property values as part of the value transform.
  112. *
  113. * @memberof PropertyTextureProperty.prototype
  114. * @type {number|Cartesian2|Cartesian3|Cartesian4|Matrix2|Matrix3|Matrix4}
  115. * @readonly
  116. * @private
  117. */
  118. scale: {
  119. get: function () {
  120. return this._scale;
  121. },
  122. },
  123. /**
  124. * The properties inherited from this property's class
  125. *
  126. * @memberof PropertyTextureProperty.prototype
  127. * @type {MetadataClassProperty}
  128. * @readonly
  129. * @private
  130. */
  131. classProperty: {
  132. get: function () {
  133. return this._classProperty;
  134. },
  135. },
  136. /**
  137. * Extra user-defined properties.
  138. *
  139. * @memberof PropertyTextureProperty.prototype
  140. * @type {*}
  141. * @readonly
  142. * @private
  143. */
  144. extras: {
  145. get: function () {
  146. return this._extras;
  147. },
  148. },
  149. /**
  150. * An object containing extensions.
  151. *
  152. * @memberof PropertyTextureProperty.prototype
  153. * @type {*}
  154. * @readonly
  155. * @private
  156. */
  157. extensions: {
  158. get: function () {
  159. return this._extensions;
  160. },
  161. },
  162. });
  163. PropertyTextureProperty.prototype.isGpuCompatible = function () {
  164. const classProperty = this._classProperty;
  165. const type = classProperty.type;
  166. const componentType = classProperty.componentType;
  167. if (classProperty.isArray) {
  168. // only support arrays of 1-4 UINT8 scalars (normalized or unnormalized)
  169. return (
  170. !classProperty.isVariableLengthArray &&
  171. classProperty.arrayLength <= 4 &&
  172. type === MetadataType.SCALAR &&
  173. componentType === MetadataComponentType.UINT8
  174. );
  175. }
  176. if (MetadataType.isVectorType(type) || type === MetadataType.SCALAR) {
  177. return componentType === MetadataComponentType.UINT8;
  178. }
  179. // For this initial implementation, only UINT8-based properties
  180. // are supported.
  181. return false;
  182. };
  183. const floatTypesByComponentCount = [undefined, "float", "vec2", "vec3", "vec4"];
  184. const integerTypesByComponentCount = [
  185. undefined,
  186. "int",
  187. "ivec2",
  188. "ivec3",
  189. "ivec4",
  190. ];
  191. PropertyTextureProperty.prototype.getGlslType = function () {
  192. const classProperty = this._classProperty;
  193. let componentCount = MetadataType.getComponentCount(classProperty.type);
  194. if (classProperty.isArray) {
  195. // fixed-sized arrays of length 2-4 UINT8s are represented as vectors as the
  196. // shader since those are more useful in GLSL.
  197. componentCount = classProperty.arrayLength;
  198. }
  199. // Normalized UINT8 properties are float types in the shader
  200. if (classProperty.normalized) {
  201. return floatTypesByComponentCount[componentCount];
  202. }
  203. // other UINT8-based properties are represented as integer types.
  204. return integerTypesByComponentCount[componentCount];
  205. };
  206. PropertyTextureProperty.prototype.unpackInShader = function (packedValueGlsl) {
  207. const classProperty = this._classProperty;
  208. // no unpacking needed if for normalized types
  209. if (classProperty.normalized) {
  210. return packedValueGlsl;
  211. }
  212. // integer types are read from the texture as normalized float values.
  213. // these need to be rescaled to [0, 255] and cast to the appropriate integer
  214. // type.
  215. const glslType = this.getGlslType();
  216. return `${glslType}(255.0 * ${packedValueGlsl})`;
  217. };
  218. /**
  219. * Reformat from an array of channel indices like <code>[0, 1]</code> to a
  220. * string of channels as would be used in GLSL swizzling (e.g. "rg")
  221. *
  222. * @param {number[]} channels the channel indices
  223. * @return {string} The channels as a string of "r", "g", "b" or "a" characters.
  224. * @private
  225. */
  226. function reformatChannels(channels) {
  227. return channels
  228. .map(function (channelIndex) {
  229. return "rgba".charAt(channelIndex);
  230. })
  231. .join("");
  232. }
  233. export default PropertyTextureProperty;