智慧水务管理系统 - 精河县供水工程综合管理平台

MetadataClassProperty.js 42KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import Check from "../Core/Check.js";
  5. import clone from "../Core/clone.js";
  6. import Frozen from "../Core/Frozen.js";
  7. import defined from "../Core/defined.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import Matrix2 from "../Core/Matrix2.js";
  10. import Matrix3 from "../Core/Matrix3.js";
  11. import Matrix4 from "../Core/Matrix4.js";
  12. import MetadataType from "./MetadataType.js";
  13. import MetadataComponentType, {
  14. ScalarCategories,
  15. } from "./MetadataComponentType.js";
  16. /**
  17. * A metadata property, as part of a {@link MetadataClass}.
  18. * <p>
  19. * See the {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata|3D Metadata Specification} for 3D Tiles
  20. * </p>
  21. *
  22. * @param {object} options Object with the following properties:
  23. * @param {string} options.id The ID of the property.
  24. * @param {MetadataType} options.type The type of the property such as SCALAR, VEC2, VEC3.
  25. * @param {MetadataComponentType} [options.componentType] The component type of the property. This includes integer (e.g. INT8 or UINT16), and floating point (FLOAT32 and FLOAT64) values.
  26. * @param {MetadataEnum} [options.enumType] The enum type of the property. Only defined when type is ENUM.
  27. * @param {boolean} [options.isArray=false] True if a property is an array (either fixed length or variable length), false otherwise.
  28. * @param {boolean} [options.isVariableLengthArray=false] True if a property is a variable length array, false otherwise.
  29. * @param {number} [options.arrayLength] The number of array elements. Only defined for fixed length arrays.
  30. * @param {boolean} [options.normalized=false] Whether the property is normalized.
  31. * @param {number|number[]|number[][]} [options.min] A number or an array of numbers storing the minimum allowable value of this property. Only defined when type is a numeric type.
  32. * @param {number|number[]|number[][]} [options.max] A number or an array of numbers storing the maximum allowable value of this property. Only defined when type is a numeric type.
  33. * @param {number|number[]|number[][]} [options.offset] The offset to be added to property values as part of the value transform.
  34. * @param {number|number[]|number[][]} [options.scale] The scale to be multiplied to property values as part of the value transform.
  35. * @param {number|string|Array} [options.noData] The no-data sentinel value that represents null values.
  36. * @param {number|string|Array} [options.default] A default value to use when an entity's property value is not defined.
  37. * @param {boolean} [options.required=false] Whether the property is required.
  38. * @param {string} [options.name] The name of the property.
  39. * @param {string} [options.description] The description of the property.
  40. * @param {string} [options.semantic] An identifier that describes how this property should be interpreted.
  41. * @param {*} [options.extras] Extra user-defined properties.
  42. * @param {object} [options.extensions] An object containing extensions.
  43. *
  44. * @alias MetadataClassProperty
  45. * @constructor
  46. * @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.
  47. */
  48. function MetadataClassProperty(options) {
  49. options = options ?? Frozen.EMPTY_OBJECT;
  50. const id = options.id;
  51. const type = options.type;
  52. //>>includeStart('debug', pragmas.debug);
  53. Check.typeOf.string("options.id", id);
  54. Check.typeOf.string("options.type", type);
  55. //>>includeEnd('debug');
  56. const componentType = options.componentType;
  57. const enumType = options.enumType;
  58. const normalized =
  59. defined(componentType) &&
  60. (options.normalized ?? false) &&
  61. MetadataComponentType.isIntegerType(componentType);
  62. // Basic information about this property
  63. this._id = id;
  64. this._name = options.name;
  65. this._description = options.description;
  66. this._semantic = options.semantic;
  67. // Only for unit testing purposes, not documented in the API
  68. this._isLegacyExtension = options.isLegacyExtension;
  69. // Details about basic types
  70. this._type = type;
  71. this._componentType = componentType;
  72. this._enumType = enumType;
  73. this._valueType = defined(enumType) ? enumType.valueType : componentType;
  74. // Details about arrays
  75. this._isArray = options.isArray ?? false;
  76. this._isVariableLengthArray = options.isVariableLengthArray ?? false;
  77. this._arrayLength = options.arrayLength;
  78. // min and max allowed values
  79. this._min = clone(options.min, true);
  80. this._max = clone(options.max, true);
  81. // properties that adjust the range of metadata values
  82. this._normalized = normalized;
  83. let offset = clone(options.offset, true);
  84. let scale = clone(options.scale, true);
  85. const hasValueTransform = defined(offset) || defined(scale);
  86. const enableNestedArrays = true;
  87. if (!defined(offset)) {
  88. offset = this.expandConstant(0, enableNestedArrays);
  89. }
  90. if (!defined(scale)) {
  91. scale = this.expandConstant(1, enableNestedArrays);
  92. }
  93. this._offset = offset;
  94. this._scale = scale;
  95. this._hasValueTransform = hasValueTransform;
  96. // sentinel value for missing data, and a default value to use
  97. // in its place if needed.
  98. this._noData = clone(options.noData, true);
  99. // For vector and array types, this is stored as an array of values.
  100. this._default = clone(options.default, true);
  101. this._required = options.required ?? true;
  102. // extras and extensions
  103. this._extras = clone(options.extras, true);
  104. this._extensions = clone(options.extensions, true);
  105. }
  106. /**
  107. * Creates a {@link MetadataClassProperty} from either 3D Tiles 1.1, 3DTILES_metadata, EXT_structural_metadata, or EXT_feature_metadata.
  108. *
  109. * @param {object} options Object with the following properties:
  110. * @param {string} options.id The ID of the property.
  111. * @param {object} options.property The property JSON object.
  112. * @param {Object<string, MetadataEnum>} [options.enums] A dictionary of enums.
  113. *
  114. * @returns {MetadataClassProperty} The newly created metadata class property.
  115. *
  116. * @private
  117. * @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.
  118. */
  119. MetadataClassProperty.fromJson = function (options) {
  120. options = options ?? Frozen.EMPTY_OBJECT;
  121. const id = options.id;
  122. const property = options.property;
  123. //>>includeStart('debug', pragmas.debug);
  124. Check.typeOf.string("options.id", id);
  125. Check.typeOf.object("options.property", property);
  126. Check.typeOf.string("options.property.type", property.type);
  127. //>>includeEnd('debug');
  128. // Try to determine if this is the legacy extension. This is not
  129. // always possible, as there are some types that are valid in both
  130. // extensions.
  131. const isLegacyExtension = isLegacy(property);
  132. const parsedType = parseType(property, options.enums);
  133. // EXT_feature_metadata had an optional flag, while EXT_structural_metadata
  134. // has a required flag. The defaults are not the same, and there's some cases
  135. // like {type: BOOLEAN} that are ambiguous. Coalesce this into a single
  136. // required flag
  137. let required;
  138. if (!defined(isLegacyExtension)) {
  139. // Impossible to tell which extension was used, so don't require
  140. // the property
  141. required = false;
  142. } else if (isLegacyExtension) {
  143. required = defined(property.optional) ? !property.optional : true;
  144. } else {
  145. required = property.required ?? false;
  146. }
  147. return new MetadataClassProperty({
  148. id: id,
  149. type: parsedType.type,
  150. componentType: parsedType.componentType,
  151. enumType: parsedType.enumType,
  152. isArray: parsedType.isArray,
  153. isVariableLengthArray: parsedType.isVariableLengthArray,
  154. arrayLength: parsedType.arrayLength,
  155. normalized: property.normalized,
  156. min: property.min,
  157. max: property.max,
  158. offset: property.offset,
  159. scale: property.scale,
  160. noData: property.noData,
  161. default: property.default,
  162. required: required,
  163. name: property.name,
  164. description: property.description,
  165. semantic: property.semantic,
  166. extras: property.extras,
  167. extensions: property.extensions,
  168. isLegacyExtension: isLegacyExtension,
  169. });
  170. };
  171. Object.defineProperties(MetadataClassProperty.prototype, {
  172. /**
  173. * The ID of the property.
  174. *
  175. * @memberof MetadataClassProperty.prototype
  176. * @type {string}
  177. * @readonly
  178. */
  179. id: {
  180. get: function () {
  181. return this._id;
  182. },
  183. },
  184. /**
  185. * The name of the property.
  186. *
  187. * @memberof MetadataClassProperty.prototype
  188. * @type {string}
  189. * @readonly
  190. */
  191. name: {
  192. get: function () {
  193. return this._name;
  194. },
  195. },
  196. /**
  197. * The description of the property.
  198. *
  199. * @memberof MetadataClassProperty.prototype
  200. * @type {string}
  201. * @readonly
  202. */
  203. description: {
  204. get: function () {
  205. return this._description;
  206. },
  207. },
  208. /**
  209. * The type of the property such as SCALAR, VEC2, VEC3
  210. *
  211. * @memberof MetadataClassProperty.prototype
  212. * @type {MetadataType}
  213. * @readonly
  214. */
  215. type: {
  216. get: function () {
  217. return this._type;
  218. },
  219. },
  220. /**
  221. * The enum type of the property. Only defined when type is ENUM.
  222. *
  223. * @memberof MetadataClassProperty.prototype
  224. * @type {MetadataEnum}
  225. * @readonly
  226. */
  227. enumType: {
  228. get: function () {
  229. return this._enumType;
  230. },
  231. },
  232. /**
  233. * The component type of the property. This includes integer
  234. * (e.g. INT8 or UINT16), and floating point (FLOAT32 and FLOAT64) values
  235. *
  236. * @memberof MetadataClassProperty.prototype
  237. * @type {MetadataComponentType}
  238. * @readonly
  239. */
  240. componentType: {
  241. get: function () {
  242. return this._componentType;
  243. },
  244. },
  245. /**
  246. * The datatype used for storing each component of the property. This
  247. * is usually the same as componentType except for ENUM, where this
  248. * returns an integer type
  249. *
  250. * @memberof MetadataClassProperty.prototype
  251. * @type {MetadataComponentType}
  252. * @readonly
  253. * @private
  254. */
  255. valueType: {
  256. get: function () {
  257. return this._valueType;
  258. },
  259. },
  260. /**
  261. * True if a property is an array (either fixed length or variable length),
  262. * false otherwise.
  263. *
  264. * @memberof MetadataClassProperty.prototype
  265. * @type {boolean}
  266. * @readonly
  267. */
  268. isArray: {
  269. get: function () {
  270. return this._isArray;
  271. },
  272. },
  273. /**
  274. * True if a property is a variable length array, false otherwise.
  275. *
  276. * @memberof MetadataClassProperty.prototype
  277. * @type {boolean}
  278. * @readonly
  279. */
  280. isVariableLengthArray: {
  281. get: function () {
  282. return this._isVariableLengthArray;
  283. },
  284. },
  285. /**
  286. * The number of array elements. Only defined for fixed-size
  287. * arrays.
  288. *
  289. * @memberof MetadataClassProperty.prototype
  290. * @type {number}
  291. * @readonly
  292. */
  293. arrayLength: {
  294. get: function () {
  295. return this._arrayLength;
  296. },
  297. },
  298. /**
  299. * Whether the property is normalized.
  300. *
  301. * @memberof MetadataClassProperty.prototype
  302. * @type {boolean}
  303. * @readonly
  304. */
  305. normalized: {
  306. get: function () {
  307. return this._normalized;
  308. },
  309. },
  310. /**
  311. * A number or an array of numbers storing the maximum allowable value of this property. Only defined when type is a numeric type.
  312. *
  313. * @memberof MetadataClassProperty.prototype
  314. * @type {number|number[]|number[][]}
  315. * @readonly
  316. */
  317. max: {
  318. get: function () {
  319. return this._max;
  320. },
  321. },
  322. /**
  323. * A number or an array of numbers storing the minimum allowable value of this property. Only defined when type is a numeric type.
  324. *
  325. * @memberof MetadataClassProperty.prototype
  326. * @type {number|number[]|number[][]}
  327. * @readonly
  328. */
  329. min: {
  330. get: function () {
  331. return this._min;
  332. },
  333. },
  334. /**
  335. * The no-data sentinel value that represents null values
  336. *
  337. * @memberof MetadataClassProperty.prototype
  338. * @type {number|string|Array}
  339. * @readonly
  340. */
  341. noData: {
  342. get: function () {
  343. return this._noData;
  344. },
  345. },
  346. /**
  347. * A default value to use when an entity's property value is not defined.
  348. *
  349. * @memberof MetadataClassProperty.prototype
  350. * @type {number|string|Array}
  351. * @readonly
  352. */
  353. default: {
  354. get: function () {
  355. return this._default;
  356. },
  357. },
  358. /**
  359. * Whether the property is required.
  360. *
  361. * @memberof MetadataClassProperty.prototype
  362. * @type {boolean}
  363. * @readonly
  364. */
  365. required: {
  366. get: function () {
  367. return this._required;
  368. },
  369. },
  370. /**
  371. * An identifier that describes how this property should be interpreted.
  372. *
  373. * @memberof MetadataClassProperty.prototype
  374. * @type {string}
  375. * @readonly
  376. */
  377. semantic: {
  378. get: function () {
  379. return this._semantic;
  380. },
  381. },
  382. /**
  383. * True if offset/scale should be applied. If both offset/scale were
  384. * undefined, they default to identity so this property is set false
  385. *
  386. * @memberof MetadataClassProperty.prototype
  387. * @type {boolean}
  388. * @readonly
  389. * @private
  390. */
  391. hasValueTransform: {
  392. get: function () {
  393. return this._hasValueTransform;
  394. },
  395. },
  396. /**
  397. * The offset to be added to property values as part of the value transform.
  398. *
  399. * This is always defined, even when `hasValueTransform` is `false`. If
  400. * the class property JSON itself did not define it, then it will be
  401. * initialized to the default value.
  402. *
  403. * @memberof MetadataClassProperty.prototype
  404. * @type {number|number[]|number[][]}
  405. * @readonly
  406. */
  407. offset: {
  408. get: function () {
  409. return this._offset;
  410. },
  411. },
  412. /**
  413. * The scale to be multiplied to property values as part of the value transform.
  414. *
  415. * This is always defined, even when `hasValueTransform` is `false`. If
  416. * the class property JSON itself did not define it, then it will be
  417. * initialized to the default value.
  418. *
  419. * @memberof MetadataClassProperty.prototype
  420. * @type {number|number[]|number[][]}
  421. * @readonly
  422. */
  423. scale: {
  424. get: function () {
  425. return this._scale;
  426. },
  427. },
  428. /**
  429. * Extra user-defined properties.
  430. *
  431. * @memberof MetadataClassProperty.prototype
  432. * @type {*}
  433. * @readonly
  434. */
  435. extras: {
  436. get: function () {
  437. return this._extras;
  438. },
  439. },
  440. /**
  441. * An object containing extensions.
  442. *
  443. * @memberof MetadataClassProperty.prototype
  444. * @type {object}
  445. * @readonly
  446. */
  447. extensions: {
  448. get: function () {
  449. return this._extensions;
  450. },
  451. },
  452. });
  453. function isLegacy(property) {
  454. if (property.type === "ARRAY") {
  455. return true;
  456. }
  457. // New property types in EXT_structural_metadata
  458. const type = property.type;
  459. if (
  460. type === MetadataType.SCALAR ||
  461. MetadataType.isMatrixType(type) ||
  462. MetadataType.isVectorType(type)
  463. ) {
  464. return false;
  465. }
  466. // EXT_feature_metadata allowed numeric types as a type. Now they are
  467. // represented as {type: SINGLE, componentType: type}
  468. if (defined(MetadataComponentType[type])) {
  469. return true;
  470. }
  471. // New properties in EXT_structural_metadata
  472. if (
  473. defined(property.noData) ||
  474. defined(property.scale) ||
  475. defined(property.offset) ||
  476. defined(property.required) ||
  477. defined(property.count) ||
  478. defined(property.array)
  479. ) {
  480. return false;
  481. }
  482. // Properties that only exist in EXT_feature_metadata
  483. if (defined(property.optional)) {
  484. return false;
  485. }
  486. // impossible to tell, give up.
  487. return undefined;
  488. }
  489. function parseType(property, enums) {
  490. const type = property.type;
  491. const componentType = property.componentType;
  492. // EXT_feature_metadata had an ARRAY type. This is now handled
  493. // with array + count, so some details need to be transcoded
  494. const isLegacyArray = type === "ARRAY";
  495. let isArray;
  496. let arrayLength;
  497. let isVariableLengthArray;
  498. if (isLegacyArray) {
  499. // definitely EXT_feature_metadata
  500. isArray = true;
  501. arrayLength = property.componentCount;
  502. isVariableLengthArray = !defined(arrayLength);
  503. } else if (property.array) {
  504. isArray = true;
  505. arrayLength = property.count;
  506. isVariableLengthArray = !defined(property.count);
  507. } else {
  508. // Could be either extension. Some cases are impossible to distinguish
  509. // Default to a single value
  510. isArray = false;
  511. arrayLength = undefined;
  512. isVariableLengthArray = false;
  513. }
  514. let enumType;
  515. if (defined(property.enumType)) {
  516. enumType = enums[property.enumType];
  517. }
  518. // In both EXT_feature_metadata and EXT_structural_metadata, ENUM appears
  519. // as a type.
  520. if (type === MetadataType.ENUM) {
  521. return {
  522. type: type,
  523. componentType: undefined,
  524. enumType: enumType,
  525. valueType: enumType.valueType,
  526. isArray: isArray,
  527. isVariableLengthArray: isVariableLengthArray,
  528. arrayLength: arrayLength,
  529. };
  530. }
  531. // In EXT_feature_metadata, ENUM also appears as an ARRAY componentType
  532. if (isLegacyArray && componentType === MetadataType.ENUM) {
  533. return {
  534. type: componentType,
  535. componentType: undefined,
  536. enumType: enumType,
  537. valueType: enumType.valueType,
  538. isArray: isArray,
  539. isVariableLengthArray: isVariableLengthArray,
  540. arrayLength: arrayLength,
  541. };
  542. }
  543. // EXT_structural_metadata only: SCALAR, VECN and MATN
  544. if (
  545. type === MetadataType.SCALAR ||
  546. MetadataType.isMatrixType(type) ||
  547. MetadataType.isVectorType(type)
  548. ) {
  549. return {
  550. type: type,
  551. componentType: componentType,
  552. enumType: undefined,
  553. valueType: componentType,
  554. isArray: isArray,
  555. isVariableLengthArray: isVariableLengthArray,
  556. arrayLength: arrayLength,
  557. };
  558. }
  559. // In both EXT_structural_metadata and EXT_feature_metadata,
  560. // BOOLEAN and STRING appear as types
  561. if (type === MetadataType.BOOLEAN || type === MetadataType.STRING) {
  562. return {
  563. type: type,
  564. componentType: undefined,
  565. enumType: undefined,
  566. valueType: undefined,
  567. isArray: isArray,
  568. isVariableLengthArray: isVariableLengthArray,
  569. arrayLength: arrayLength,
  570. };
  571. }
  572. // EXT_feature_metadata also allows BOOLEAN and STRING as an ARRAY
  573. // componentType
  574. if (
  575. isLegacyArray &&
  576. (componentType === MetadataType.BOOLEAN ||
  577. componentType === MetadataType.STRING)
  578. ) {
  579. return {
  580. type: componentType,
  581. componentType: undefined,
  582. enumType: undefined,
  583. valueType: undefined,
  584. isArray: isArray,
  585. isVariableLengthArray: isVariableLengthArray,
  586. arrayLength: arrayLength,
  587. };
  588. }
  589. // Both EXT_feature_metadata and EXT_structural_metadata allow numeric types like
  590. // INT32 or FLOAT64 as a componentType.
  591. if (defined(componentType) && defined(MetadataComponentType[componentType])) {
  592. return {
  593. type: MetadataType.SCALAR,
  594. componentType: componentType,
  595. enumType: undefined,
  596. valueType: componentType,
  597. isArray: isArray,
  598. isVariableLengthArray: isVariableLengthArray,
  599. arrayLength: arrayLength,
  600. };
  601. }
  602. // EXT_feature_metadata: integer and float types were allowed as types,
  603. // but now these are expressed as {type: SCALAR, componentType: type}
  604. if (defined(MetadataComponentType[type])) {
  605. return {
  606. type: MetadataType.SCALAR,
  607. componentType: type,
  608. enumType: undefined,
  609. valueType: type,
  610. isArray: isArray,
  611. isVariableLengthArray: isVariableLengthArray,
  612. arrayLength: arrayLength,
  613. };
  614. }
  615. //>>includeStart('debug', pragmas.debug);
  616. throw new DeveloperError(
  617. `unknown metadata type {type: ${type}, componentType: ${componentType})`,
  618. );
  619. //>>includeEnd('debug');
  620. }
  621. /**
  622. * Normalizes integer property values. If the property is not normalized
  623. * the value is returned unmodified.
  624. * <p>
  625. * Given the way normalization is defined in {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata#normalized-values|the 3D Metadata Specification},
  626. * normalize and unnormalize are almost, but not quite inverses. In particular,
  627. * the smallest signed integer value will be off by one after normalizing and
  628. * unnormalizing. See
  629. * {@link https://www.desmos.com/calculator/nledg1evut|this Desmos graph} for
  630. * an example using INT8.
  631. * </p>
  632. * <p>
  633. * Furthermore, for 64-bit integer types, there may be a loss of precision
  634. * due to conversion to Number
  635. * </p>
  636. *
  637. * @param {*} value The integer value or array of integer values.
  638. * @returns {*} The normalized value or array of normalized values.
  639. *
  640. * @private
  641. */
  642. MetadataClassProperty.prototype.normalize = function (value) {
  643. if (!this._normalized) {
  644. return value;
  645. }
  646. return normalizeInPlace(
  647. value,
  648. this._valueType,
  649. MetadataComponentType.normalize,
  650. );
  651. };
  652. /**
  653. * Unnormalizes integer property values. If the property is not normalized
  654. * the value is returned unmodified.
  655. * <p>
  656. * Given the way normalization is defined in {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata#normalized-values|the 3D Metadata Specification},
  657. * normalize and unnormalize are almost, but not quite inverses. In particular,
  658. * the smallest signed integer value will be off by one after normalizing and
  659. * unnormalizing. See
  660. * {@link https://www.desmos.com/calculator/nledg1evut|this Desmos graph} for
  661. * an example using INT8.
  662. * </p>
  663. * <p>
  664. * Furthermore, for 64-bit integer types, there may be a loss of precision
  665. * due to conversion to Number
  666. * </p>
  667. *
  668. * @param {*} value The normalized value or array of normalized values.
  669. * @returns {*} The integer value or array of integer values.
  670. *
  671. * @private
  672. */
  673. MetadataClassProperty.prototype.unnormalize = function (value) {
  674. if (!this._normalized) {
  675. return value;
  676. }
  677. return normalizeInPlace(
  678. value,
  679. this._valueType,
  680. MetadataComponentType.unnormalize,
  681. );
  682. };
  683. /**
  684. * @private
  685. */
  686. MetadataClassProperty.prototype.applyValueTransform = function (value) {
  687. // variable length arrays do not have a well-defined offset/scale so this
  688. // is forbidden by the spec
  689. if (!this._hasValueTransform || this._isVariableLengthArray) {
  690. return value;
  691. }
  692. return MetadataClassProperty.valueTransformInPlace(
  693. value,
  694. this._offset,
  695. this._scale,
  696. MetadataComponentType.applyValueTransform,
  697. );
  698. };
  699. /**
  700. * @private
  701. */
  702. MetadataClassProperty.prototype.unapplyValueTransform = function (value) {
  703. // variable length arrays do not have a well-defined offset/scale so this
  704. // is forbidden by the spec
  705. if (!this._hasValueTransform || this._isVariableLengthArray) {
  706. return value;
  707. }
  708. return MetadataClassProperty.valueTransformInPlace(
  709. value,
  710. this._offset,
  711. this._scale,
  712. MetadataComponentType.unapplyValueTransform,
  713. );
  714. };
  715. /**
  716. * @private
  717. */
  718. MetadataClassProperty.prototype.expandConstant = function (
  719. constant,
  720. enableNestedArrays,
  721. ) {
  722. enableNestedArrays = enableNestedArrays ?? false;
  723. const isArray = this._isArray;
  724. const arrayLength = this._arrayLength;
  725. const componentCount = MetadataType.getComponentCount(this._type);
  726. const isNested = isArray && componentCount > 1;
  727. // scalar values can be returned directly
  728. if (!isArray && componentCount === 1) {
  729. return constant;
  730. }
  731. // vector and matrix values
  732. if (!isArray) {
  733. return new Array(componentCount).fill(constant);
  734. }
  735. // arrays of scalars
  736. if (!isNested) {
  737. return new Array(arrayLength).fill(constant);
  738. }
  739. // arrays of vectors/matrices: flattened
  740. if (!enableNestedArrays) {
  741. return new Array(this._arrayLength * componentCount).fill(constant);
  742. }
  743. // array of vectors/matrices: nested
  744. const innerConstant = new Array(componentCount).fill(constant);
  745. // This array fill duplicates the pointer to the inner arrays. Since this is
  746. // intended for use with constants, no need to clone the array.
  747. return new Array(this._arrayLength).fill(innerConstant);
  748. };
  749. /**
  750. * If the value is the noData sentinel, return undefined. Otherwise, return
  751. * the value.
  752. * @param {*} value The raw value
  753. * @returns {*} Either the value or undefined if the value was a no data value.
  754. *
  755. * @private
  756. */
  757. MetadataClassProperty.prototype.handleNoData = function (value) {
  758. const sentinel = this._noData;
  759. if (!defined(sentinel)) {
  760. return value;
  761. }
  762. if (arrayEquals(value, sentinel)) {
  763. return undefined;
  764. }
  765. return value;
  766. };
  767. function arrayEquals(left, right) {
  768. if (!Array.isArray(left)) {
  769. return left === right;
  770. }
  771. if (!Array.isArray(right)) {
  772. return false;
  773. }
  774. if (left.length !== right.length) {
  775. return false;
  776. }
  777. for (let i = 0; i < left.length; i++) {
  778. if (!arrayEquals(left[i], right[i])) {
  779. return false;
  780. }
  781. }
  782. return true;
  783. }
  784. /**
  785. * Unpack VECN values into {@link Cartesian2}, {@link Cartesian3}, or
  786. * {@link Cartesian4} and MATN values into {@link Matrix2}, {@link Matrix3}, or
  787. * {@link Matrix4} depending on N. All other values (including arrays of
  788. * other sizes) are passed through unaltered.
  789. *
  790. * @param {*} value the original, normalized values.
  791. * @param {boolean} [enableNestedArrays=false] If true, arrays of vectors are represented as nested arrays. This is used for JSON encoding but not binary encoding
  792. * @returns {*} The appropriate vector or matrix type if the value is a vector or matrix type, respectively. If the property is an array of vectors or matrices, an array of the appropriate vector or matrix type is returned. Otherwise, the value is returned unaltered.
  793. * @private
  794. */
  795. MetadataClassProperty.prototype.unpackVectorAndMatrixTypes = function (
  796. value,
  797. enableNestedArrays,
  798. ) {
  799. enableNestedArrays = enableNestedArrays ?? false;
  800. const MathType = MetadataType.getMathType(this._type);
  801. const isArray = this._isArray;
  802. const componentCount = MetadataType.getComponentCount(this._type);
  803. const isNested = isArray && componentCount > 1;
  804. if (!defined(MathType)) {
  805. return value;
  806. }
  807. if (enableNestedArrays && isNested) {
  808. return value.map(function (x) {
  809. return MathType.unpack(x);
  810. });
  811. }
  812. if (isArray) {
  813. return MathType.unpackArray(value);
  814. }
  815. return MathType.unpack(value);
  816. };
  817. /**
  818. * Pack a {@link Cartesian2}, {@link Cartesian3}, or {@link Cartesian4} into an
  819. * array if this property is an <code>VECN</code>.
  820. * Pack a {@link Matrix2}, {@link Matrix3}, or {@link Matrix4} into an
  821. * array if this property is an <code>MATN</code>.
  822. * All other values (including arrays of other sizes) are passed through unaltered.
  823. *
  824. * @param {*} value The value of this property
  825. * @param {boolean} [enableNestedArrays=false] If true, arrays of vectors are represented as nested arrays. This is used for JSON encoding but not binary encoding
  826. * @returns {*} An array of the appropriate length if the property is a vector or matrix type. Otherwise, the value is returned unaltered.
  827. * @private
  828. */
  829. MetadataClassProperty.prototype.packVectorAndMatrixTypes = function (
  830. value,
  831. enableNestedArrays,
  832. ) {
  833. enableNestedArrays = enableNestedArrays ?? false;
  834. const MathType = MetadataType.getMathType(this._type);
  835. const isArray = this._isArray;
  836. const componentCount = MetadataType.getComponentCount(this._type);
  837. const isNested = isArray && componentCount > 1;
  838. if (!defined(MathType)) {
  839. return value;
  840. }
  841. if (enableNestedArrays && isNested) {
  842. return value.map(function (x) {
  843. return MathType.pack(x, []);
  844. });
  845. }
  846. if (isArray) {
  847. return MathType.packArray(value, []);
  848. }
  849. return MathType.pack(value, []);
  850. };
  851. /**
  852. * Validates whether the given value conforms to the property.
  853. *
  854. * @param {*} value The value.
  855. * @returns {string|undefined} An error message if the value does not conform to the property, otherwise undefined.
  856. * @private
  857. */
  858. MetadataClassProperty.prototype.validate = function (value) {
  859. if (!defined(value) && defined(this._default)) {
  860. // no value, but we have a default to use.
  861. return undefined;
  862. }
  863. if (this._required && !defined(value)) {
  864. return `required property must have a value`;
  865. }
  866. if (this._isArray) {
  867. return validateArray(this, value);
  868. }
  869. return validateSingleValue(this, value);
  870. };
  871. function validateArray(classProperty, value) {
  872. if (!Array.isArray(value)) {
  873. return `value ${value} must be an array`;
  874. }
  875. const length = value.length;
  876. if (
  877. !classProperty._isVariableLengthArray &&
  878. length !== classProperty._arrayLength
  879. ) {
  880. return "Array length does not match property.arrayLength";
  881. }
  882. for (let i = 0; i < length; i++) {
  883. const message = validateSingleValue(classProperty, value[i]);
  884. if (defined(message)) {
  885. return message;
  886. }
  887. }
  888. }
  889. function validateSingleValue(classProperty, value) {
  890. const type = classProperty._type;
  891. const componentType = classProperty._componentType;
  892. const enumType = classProperty._enumType;
  893. const normalized = classProperty._normalized;
  894. if (MetadataType.isVectorType(type)) {
  895. return validateVector(value, type, componentType);
  896. } else if (MetadataType.isMatrixType(type)) {
  897. return validateMatrix(value, type, componentType);
  898. } else if (type === MetadataType.STRING) {
  899. return validateString(value);
  900. } else if (type === MetadataType.BOOLEAN) {
  901. return validateBoolean(value);
  902. } else if (type === MetadataType.ENUM) {
  903. return validateEnum(value, enumType);
  904. }
  905. return validateScalar(value, componentType, normalized);
  906. }
  907. function validateVector(value, type, componentType) {
  908. if (!MetadataComponentType.isVectorCompatible(componentType)) {
  909. return `componentType ${componentType} is incompatible with vector type ${type}`;
  910. }
  911. if (type === MetadataType.VEC2 && !(value instanceof Cartesian2)) {
  912. return `vector value ${value} must be a Cartesian2`;
  913. }
  914. if (type === MetadataType.VEC3 && !(value instanceof Cartesian3)) {
  915. return `vector value ${value} must be a Cartesian3`;
  916. }
  917. if (type === MetadataType.VEC4 && !(value instanceof Cartesian4)) {
  918. return `vector value ${value} must be a Cartesian4`;
  919. }
  920. }
  921. function validateMatrix(value, type, componentType) {
  922. if (!MetadataComponentType.isVectorCompatible(componentType)) {
  923. return `componentType ${componentType} is incompatible with matrix type ${type}`;
  924. }
  925. if (type === MetadataType.MAT2 && !(value instanceof Matrix2)) {
  926. return `matrix value ${value} must be a Matrix2`;
  927. }
  928. if (type === MetadataType.MAT3 && !(value instanceof Matrix3)) {
  929. return `matrix value ${value} must be a Matrix3`;
  930. }
  931. if (type === MetadataType.MAT4 && !(value instanceof Matrix4)) {
  932. return `matrix value ${value} must be a Matrix4`;
  933. }
  934. }
  935. function validateString(value) {
  936. if (typeof value !== "string") {
  937. return getTypeErrorMessage(value, MetadataType.STRING);
  938. }
  939. }
  940. function validateBoolean(value) {
  941. if (typeof value !== "boolean") {
  942. return getTypeErrorMessage(value, MetadataType.BOOLEAN);
  943. }
  944. }
  945. function validateEnum(value, enumType) {
  946. const javascriptType = typeof value;
  947. if (defined(enumType)) {
  948. if (javascriptType !== "string" || !defined(enumType.valuesByName[value])) {
  949. return `value ${value} is not a valid enum name for ${enumType.id}`;
  950. }
  951. return;
  952. }
  953. }
  954. function validateScalar(value, componentType, normalized) {
  955. const javascriptType = typeof value;
  956. switch (componentType) {
  957. case MetadataComponentType.INT8:
  958. case MetadataComponentType.UINT8:
  959. case MetadataComponentType.INT16:
  960. case MetadataComponentType.UINT16:
  961. case MetadataComponentType.INT32:
  962. case MetadataComponentType.UINT32:
  963. case MetadataComponentType.FLOAT32:
  964. case MetadataComponentType.FLOAT64:
  965. if (javascriptType !== "number") {
  966. return getTypeErrorMessage(value, componentType);
  967. }
  968. if (!isFinite(value)) {
  969. return getNonFiniteErrorMessage(value, componentType);
  970. }
  971. return checkInRange(value, componentType, normalized);
  972. case MetadataComponentType.INT64:
  973. case MetadataComponentType.UINT64:
  974. if (javascriptType !== "number" && javascriptType !== "bigint") {
  975. return getTypeErrorMessage(value, componentType);
  976. }
  977. if (javascriptType === "number" && !isFinite(value)) {
  978. return getNonFiniteErrorMessage(value, componentType);
  979. }
  980. return checkInRange(value, componentType, normalized);
  981. }
  982. }
  983. function getTypeErrorMessage(value, type) {
  984. return `value ${value} does not match type ${type}`;
  985. }
  986. function getOutOfRangeErrorMessage(value, type, normalized) {
  987. let errorMessage = `value ${value} is out of range for type ${type}`;
  988. if (normalized) {
  989. errorMessage += " (normalized)";
  990. }
  991. return errorMessage;
  992. }
  993. function checkInRange(value, componentType, normalized) {
  994. if (normalized) {
  995. const min = MetadataComponentType.isUnsignedIntegerType(componentType)
  996. ? 0.0
  997. : -1.0;
  998. const max = 1.0;
  999. if (value < min || value > max) {
  1000. return getOutOfRangeErrorMessage(value, componentType, normalized);
  1001. }
  1002. return;
  1003. }
  1004. if (
  1005. value < MetadataComponentType.getMinimum(componentType) ||
  1006. value > MetadataComponentType.getMaximum(componentType)
  1007. ) {
  1008. return getOutOfRangeErrorMessage(value, componentType, normalized);
  1009. }
  1010. }
  1011. function getNonFiniteErrorMessage(value, type) {
  1012. return `value ${value} of type ${type} must be finite`;
  1013. }
  1014. function normalizeInPlace(values, valueType, normalizeFunction) {
  1015. if (!Array.isArray(values)) {
  1016. return normalizeFunction(values, valueType);
  1017. }
  1018. for (let i = 0; i < values.length; i++) {
  1019. values[i] = normalizeInPlace(values[i], valueType, normalizeFunction);
  1020. }
  1021. return values;
  1022. }
  1023. /**
  1024. * Applies the value transform that is defined with the given offsets
  1025. * and scales to the given values.
  1026. *
  1027. * If the given values are not an array, then the given transformation
  1028. * function will be applied directly.
  1029. *
  1030. * If the values are an array, then this function will be called recursively
  1031. * with the array elements, boiling down to a component-wise application
  1032. * of the transformation function to the innermost array elements.
  1033. *
  1034. * @param {number|number[]|number[][]} values The input values
  1035. * @param {number|number[]|number[][]} offsets The offsets
  1036. * @param {number|number[]|number[][]} scales The scales
  1037. * @param {Function} transformationFunction The function with the signature
  1038. * `(value:number, offset:number, scale:number) : number` that will be
  1039. * applied to the innermost elements
  1040. * @returns The input values (or the result of applying the transformation
  1041. * function to a single value if the values have not been an array).
  1042. * @private
  1043. */
  1044. MetadataClassProperty.valueTransformInPlace = function (
  1045. values,
  1046. offsets,
  1047. scales,
  1048. transformationFunction,
  1049. ) {
  1050. if (!Array.isArray(values)) {
  1051. // transform a single value
  1052. return transformationFunction(values, offsets, scales);
  1053. }
  1054. for (let i = 0; i < values.length; i++) {
  1055. // offsets and scales must be the same array shape as values.
  1056. values[i] = MetadataClassProperty.valueTransformInPlace(
  1057. values[i],
  1058. offsets[i],
  1059. scales[i],
  1060. transformationFunction,
  1061. );
  1062. }
  1063. return values;
  1064. };
  1065. /**
  1066. * Determines the byte size of a single property element, stored on the CPU.
  1067. * For example, if the metadata type is VEC3 and the component type is FLOAT32, this would return 12 bytes.
  1068. *
  1069. * @returns {number} The byte size of a single property element.
  1070. *
  1071. * @private
  1072. */
  1073. MetadataClassProperty.prototype.cpuBytesPerElement = function () {
  1074. return bytesPerElement(this, this.valueType);
  1075. };
  1076. /**
  1077. * Determines the byte size of a single property element, stored on the GPU.
  1078. * This differs from the CPU byte size if the element type is a 64-bit type that can be
  1079. * downcast to a 32-bit type for texture packing (only relevant for textures created from property tables).
  1080. *
  1081. * @returns {number} The byte size of a single property element on the GPU.
  1082. */
  1083. MetadataClassProperty.prototype.gpuBytesPerElement = function () {
  1084. const packedType = MetadataComponentType.gpuComponentType(this.valueType);
  1085. return bytesPerElement(this, packedType);
  1086. };
  1087. function bytesPerElement(classProperty, valueType) {
  1088. const type = classProperty.type;
  1089. const componentCount = MetadataType.getComponentCount(type);
  1090. const arrayLength = classProperty.isArray ? classProperty.arrayLength : 1;
  1091. const bytesPerComponent = MetadataComponentType.getSizeInBytes(valueType);
  1092. return componentCount * arrayLength * bytesPerComponent;
  1093. }
  1094. /**
  1095. * Determines whether this property can be stored in a texture, given the property's datatype and the number of
  1096. * texture channels dedicated property value.
  1097. *
  1098. * @param {Number} channelsLength The number of texture channels to pack each property value into
  1099. * @returns {boolean} true if the property can be stored in a texture with the given number of channels, false otherwise
  1100. *
  1101. * @private
  1102. */
  1103. MetadataClassProperty.prototype.isGpuCompatible = function (channelsLength) {
  1104. //>>includeStart('debug', pragmas.debug);
  1105. if (!defined(channelsLength) || channelsLength <= 0) {
  1106. throw new DeveloperError("channelsLength must be a positive number.");
  1107. }
  1108. //>>includeEnd('debug');
  1109. const type = this.type;
  1110. if (this.isVariableLengthArray) {
  1111. return false;
  1112. }
  1113. if (type === MetadataType.STRING) {
  1114. return false;
  1115. }
  1116. // For the time being, boolean properties are not considered GPU compatible, because they are
  1117. // packed as bitstreams and the texture-unpacking logic does not currently support this.
  1118. if (type === MetadataType.BOOLEAN) {
  1119. return false;
  1120. }
  1121. // For all other properties, make sure the components fit in the sampled channels.
  1122. // (64-bit types can be downcast to 32-bit types for texture packing. In the future, support for full 64-bit types may be added)
  1123. if (this.gpuBytesPerElement() > channelsLength) {
  1124. return false;
  1125. }
  1126. return true;
  1127. };
  1128. const floatTypesByComponentCount = [undefined, "float", "vec2", "vec3", "vec4"];
  1129. const integerTypesByComponentCount = [
  1130. undefined,
  1131. "int",
  1132. "ivec2",
  1133. "ivec3",
  1134. "ivec4",
  1135. ];
  1136. const unsignedIntegerTypesByComponentCount = [
  1137. undefined,
  1138. "uint",
  1139. "uvec2",
  1140. "uvec3",
  1141. "uvec4",
  1142. ];
  1143. // Map from scalar component type to the GLSL function used to reinterpret from uint bits to the scalar type
  1144. const uintBitsToScalarType = {
  1145. [ScalarCategories.FLOAT]: "uintBitsToFloat",
  1146. [ScalarCategories.INTEGER]: "int",
  1147. [ScalarCategories.UNSIGNED_INTEGER]: "",
  1148. };
  1149. MetadataClassProperty.prototype.getGlslTypeWebGL1 = function () {
  1150. let componentCount = MetadataType.getComponentCount(this.type);
  1151. if (this.isArray) {
  1152. // fixed-sized arrays of length 2-4 UINT8s are represented as vectors as the
  1153. // shader since those are more useful in GLSL.
  1154. componentCount = this.arrayLength;
  1155. }
  1156. // Normalized UINT8 properties are float types in the shader
  1157. if (this.normalized) {
  1158. return floatTypesByComponentCount[componentCount];
  1159. }
  1160. // other UINT8-based properties are represented as integer types.
  1161. return integerTypesByComponentCount[componentCount];
  1162. };
  1163. MetadataClassProperty.prototype.getGlslType = function () {
  1164. const valueType = this.valueType;
  1165. let componentCount = MetadataType.getComponentCount(this.type);
  1166. const arrayLength = this.isArray ? this.arrayLength : 1;
  1167. componentCount *= arrayLength;
  1168. // Normalized fields are integers represented as float types ([0, 1] or [-1, 1] depending if signed)
  1169. if (!MetadataComponentType.isIntegerType(valueType) || this.normalized) {
  1170. return floatTypesByComponentCount[componentCount];
  1171. }
  1172. if (MetadataComponentType.isUnsignedIntegerType(valueType)) {
  1173. return unsignedIntegerTypesByComponentCount[componentCount];
  1174. }
  1175. return integerTypesByComponentCount[componentCount];
  1176. };
  1177. MetadataClassProperty.prototype.unpackTextureInShader = function (
  1178. sampledTextureExpression,
  1179. channelsString,
  1180. metadataVariableName,
  1181. shaderLines,
  1182. ) {
  1183. const glslType = this.getGlslType();
  1184. const valueType = MetadataComponentType.gpuComponentType(this.valueType);
  1185. const numChannels = channelsString.length;
  1186. const type = this.type;
  1187. // Calculate total number of components
  1188. // (e.g. a length-2 fixed-sized array of VEC2 has 4 components - isGpuCompatible checks this fits in the channels)
  1189. const componentCount =
  1190. MetadataType.getComponentCount(type) *
  1191. (this.isArray ? this.arrayLength : 1);
  1192. const channelsPerComponent = Math.floor(numChannels / componentCount);
  1193. const rawChannelsName = `${metadataVariableName}_rawChannels`;
  1194. const rawBitsName = `${metadataVariableName}_rawBits`;
  1195. const unpackedValueName = `${metadataVariableName}_unpackedValue`;
  1196. const declareUnpackedValueLine = `${glslType} ${unpackedValueName};`;
  1197. const declareRawBitsLine = `uint ${rawBitsName};`;
  1198. shaderLines.push(declareUnpackedValueLine);
  1199. shaderLines.push(declareRawBitsLine);
  1200. // Sample all (specified) channels of the texture
  1201. const assignRawValuesLine = `${floatTypesByComponentCount[numChannels]} ${rawChannelsName} = ${sampledTextureExpression};`;
  1202. shaderLines.push(assignRawValuesLine);
  1203. const castFunction =
  1204. uintBitsToScalarType[MetadataComponentType.category(valueType)];
  1205. const hasMultipleComponents = componentCount > 1;
  1206. // Unpack each component of the output property from the raw channel values
  1207. // E.g. if the output type is a vec2, and 4 channels are given, unpack x from `rg` and y from `ba`
  1208. for (let i = 0; i < componentCount; i++) {
  1209. const channelSlice = "rgba".slice(
  1210. i * channelsPerComponent,
  1211. (i + 1) * channelsPerComponent,
  1212. );
  1213. const subChannels = numChannels > 1 ? `.${channelSlice}` : "";
  1214. const assignRawBitsLine = `${rawBitsName} = czm_unpackTexture(${rawChannelsName}${subChannels});`;
  1215. let indexExpression = "";
  1216. if (hasMultipleComponents) {
  1217. indexExpression = `[${i}]`;
  1218. }
  1219. let normalize = "";
  1220. let toFloatIfNormalize = "";
  1221. if (this.normalized) {
  1222. const maxValue = MetadataComponentType.getMaximum(valueType);
  1223. normalize = ` * ${1.0 / Number(maxValue)}`;
  1224. toFloatIfNormalize = "float";
  1225. }
  1226. const assignUnpackedValueLine = `${unpackedValueName}${indexExpression} = ${toFloatIfNormalize}(${castFunction}(${rawBitsName}))${normalize};`;
  1227. shaderLines.push(assignRawBitsLine);
  1228. shaderLines.push(assignUnpackedValueLine);
  1229. }
  1230. return unpackedValueName;
  1231. };
  1232. // In WebGL 1, we limit property texture support to UINT8 properties.
  1233. MetadataClassProperty.prototype.unpackTextureInShaderWebGL1 = function (
  1234. sampledTextureExpression,
  1235. ) {
  1236. // no unpacking needed if for normalized types
  1237. if (this.normalized) {
  1238. return sampledTextureExpression;
  1239. }
  1240. // integer types are read from the texture as normalized float values.
  1241. // these need to be rescaled to [0, 255] and cast to the appropriate integer
  1242. // type.
  1243. const glslType = this.getGlslTypeWebGL1();
  1244. return `${glslType}(255.0 * ${sampledTextureExpression})`;
  1245. };
  1246. export default MetadataClassProperty;