ModelUtility.js 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114
  1. import BoundingSphere from '../Core/BoundingSphere.js';
  2. import Cartesian2 from '../Core/Cartesian2.js';
  3. import Cartesian3 from '../Core/Cartesian3.js';
  4. import Cartesian4 from '../Core/Cartesian4.js';
  5. import clone from '../Core/clone.js';
  6. import defined from '../Core/defined.js';
  7. import defineProperties from '../Core/defineProperties.js';
  8. import Matrix2 from '../Core/Matrix2.js';
  9. import Matrix3 from '../Core/Matrix3.js';
  10. import Matrix4 from '../Core/Matrix4.js';
  11. import Quaternion from '../Core/Quaternion.js';
  12. import RuntimeError from '../Core/RuntimeError.js';
  13. import WebGLConstants from '../Core/WebGLConstants.js';
  14. import ShaderSource from '../Renderer/ShaderSource.js';
  15. import addToArray from '../ThirdParty/GltfPipeline/addToArray.js';
  16. import ForEach from '../ThirdParty/GltfPipeline/ForEach.js';
  17. import hasExtension from '../ThirdParty/GltfPipeline/hasExtension.js';
  18. import AttributeType from './AttributeType.js';
  19. import Axis from './Axis.js';
  20. /**
  21. * @private
  22. */
  23. var ModelUtility = {};
  24. /**
  25. * Updates the model's forward axis if the model is not a 2.0 model.
  26. *
  27. * @param {Object} model The model to update.
  28. */
  29. ModelUtility.updateForwardAxis = function(model) {
  30. var cachedSourceVersion = model.gltf.extras.sourceVersion;
  31. if ((defined(cachedSourceVersion) && cachedSourceVersion !== '2.0')
  32. || ModelUtility.getAssetVersion(model.gltf) !== '2.0') {
  33. model._gltfForwardAxis = Axis.X;
  34. }
  35. };
  36. /**
  37. * Gets the string representing the glTF asset version.
  38. *
  39. * @param {Object} gltf A javascript object containing a glTF asset.
  40. * @returns {String} The glTF asset version string.
  41. */
  42. ModelUtility.getAssetVersion = function(gltf) {
  43. // In glTF 1.0 it was valid to omit the version number.
  44. if (!defined(gltf.asset) || !defined(gltf.asset.version)) {
  45. return '1.0';
  46. }
  47. return gltf.asset.version;
  48. };
  49. /**
  50. * Splits primitive materials with values incompatible for generating techniques.
  51. *
  52. * @param {Object} gltf A javascript object containing a glTF asset.
  53. * @returns {Object} The glTF asset with modified materials.
  54. */
  55. ModelUtility.splitIncompatibleMaterials = function(gltf) {
  56. var accessors = gltf.accessors;
  57. var materials = gltf.materials;
  58. var primitiveInfoByMaterial = {};
  59. ForEach.mesh(gltf, function(mesh) {
  60. ForEach.meshPrimitive(mesh, function(primitive) {
  61. var materialIndex = primitive.material;
  62. var material = materials[materialIndex];
  63. var jointAccessorId = primitive.attributes.JOINTS_0;
  64. var componentType;
  65. var type;
  66. if (defined(jointAccessorId)) {
  67. var jointAccessor = accessors[jointAccessorId];
  68. componentType = jointAccessor.componentType;
  69. type = jointAccessor.type;
  70. }
  71. var isSkinned = defined(jointAccessorId);
  72. var hasVertexColors = defined(primitive.attributes.COLOR_0);
  73. var hasMorphTargets = defined(primitive.targets);
  74. var hasNormals = defined(primitive.attributes.NORMAL);
  75. var hasTangents = defined(primitive.attributes.TANGENT);
  76. var hasTexCoords = defined(primitive.attributes.TEXCOORD_0);
  77. var primitiveInfo = primitiveInfoByMaterial[materialIndex];
  78. if (!defined(primitiveInfo)) {
  79. primitiveInfoByMaterial[materialIndex] = {
  80. skinning: {
  81. skinned: isSkinned,
  82. componentType: componentType,
  83. type: type
  84. },
  85. hasVertexColors: hasVertexColors,
  86. hasMorphTargets: hasMorphTargets,
  87. hasNormals: hasNormals,
  88. hasTangents: hasTangents,
  89. hasTexCoords: hasTexCoords
  90. };
  91. } else if ((primitiveInfo.skinning.skinned !== isSkinned) ||
  92. (primitiveInfo.skinning.type !== type) ||
  93. (primitiveInfo.hasVertexColors !== hasVertexColors) ||
  94. (primitiveInfo.hasMorphTargets !== hasMorphTargets) ||
  95. (primitiveInfo.hasNormals !== hasNormals) ||
  96. (primitiveInfo.hasTangents !== hasTangents) ||
  97. (primitiveInfo.hasTexCoords !== hasTexCoords)) {
  98. // This primitive uses the same material as another one that either:
  99. // * Isn't skinned
  100. // * Uses a different type to store joints and weights
  101. // * Doesn't have vertex colors, morph targets, normals, tangents, or texCoords
  102. var clonedMaterial = clone(material, true);
  103. // Split this off as a separate material
  104. materialIndex = addToArray(materials, clonedMaterial);
  105. primitive.material = materialIndex;
  106. primitiveInfoByMaterial[materialIndex] = {
  107. skinning: {
  108. skinned: isSkinned,
  109. componentType: componentType,
  110. type: type
  111. },
  112. hasVertexColors: hasVertexColors,
  113. hasMorphTargets: hasMorphTargets,
  114. hasNormals: hasNormals,
  115. hasTangents: hasTangents,
  116. hasTexCoords: hasTexCoords
  117. };
  118. }
  119. });
  120. });
  121. return primitiveInfoByMaterial;
  122. };
  123. ModelUtility.getShaderVariable = function(type) {
  124. if (type === 'SCALAR') {
  125. return 'float';
  126. }
  127. return type.toLowerCase();
  128. };
  129. ModelUtility.ModelState = {
  130. NEEDS_LOAD: 0,
  131. LOADING: 1,
  132. LOADED: 2, // Renderable, but textures can still be pending when incrementallyLoadTextures is true.
  133. FAILED: 3
  134. };
  135. ModelUtility.getFailedLoadFunction = function(model, type, path) {
  136. return function(error) {
  137. model._state = ModelUtility.ModelState.FAILED;
  138. var message = 'Failed to load ' + type + ': ' + path;
  139. if (defined(error)) {
  140. message += '\n' + error.message;
  141. }
  142. model._readyPromise.reject(new RuntimeError(message));
  143. };
  144. };
  145. ModelUtility.parseBuffers = function(model, bufferLoad) {
  146. var loadResources = model._loadResources;
  147. ForEach.buffer(model.gltf, function(buffer, bufferViewId) {
  148. if (defined(buffer.extras._pipeline.source)) {
  149. loadResources.buffers[bufferViewId] = buffer.extras._pipeline.source;
  150. } else if (defined(bufferLoad)) {
  151. var bufferResource = model._resource.getDerivedResource({
  152. url: buffer.uri
  153. });
  154. ++loadResources.pendingBufferLoads;
  155. bufferResource.fetchArrayBuffer()
  156. .then(bufferLoad(model, bufferViewId))
  157. .otherwise(ModelUtility.getFailedLoadFunction(model, 'buffer', bufferResource.url));
  158. }
  159. });
  160. };
  161. var aMinScratch = new Cartesian3();
  162. var aMaxScratch = new Cartesian3();
  163. ModelUtility.computeBoundingSphere = function(model) {
  164. var gltf = model.gltf;
  165. var gltfNodes = gltf.nodes;
  166. var gltfMeshes = gltf.meshes;
  167. var rootNodes = gltf.scenes[gltf.scene].nodes;
  168. var rootNodesLength = rootNodes.length;
  169. var nodeStack = [];
  170. var min = new Cartesian3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  171. var max = new Cartesian3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  172. for (var i = 0; i < rootNodesLength; ++i) {
  173. var n = gltfNodes[rootNodes[i]];
  174. n._transformToRoot = ModelUtility.getTransform(n);
  175. nodeStack.push(n);
  176. while (nodeStack.length > 0) {
  177. n = nodeStack.pop();
  178. var transformToRoot = n._transformToRoot;
  179. var meshId = n.mesh;
  180. if (defined(meshId)) {
  181. var mesh = gltfMeshes[meshId];
  182. var primitives = mesh.primitives;
  183. var primitivesLength = primitives.length;
  184. for (var m = 0; m < primitivesLength; ++m) {
  185. var positionAccessor = primitives[m].attributes.POSITION;
  186. if (defined(positionAccessor)) {
  187. var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);
  188. var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
  189. var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
  190. if (defined(min) && defined(max)) {
  191. Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
  192. Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
  193. Cartesian3.minimumByComponent(min, aMin, min);
  194. Cartesian3.maximumByComponent(max, aMax, max);
  195. }
  196. }
  197. }
  198. }
  199. var children = n.children;
  200. if (defined(children)) {
  201. var childrenLength = children.length;
  202. for (var k = 0; k < childrenLength; ++k) {
  203. var child = gltfNodes[children[k]];
  204. child._transformToRoot = ModelUtility.getTransform(child);
  205. Matrix4.multiplyTransformation(transformToRoot, child._transformToRoot, child._transformToRoot);
  206. nodeStack.push(child);
  207. }
  208. }
  209. delete n._transformToRoot;
  210. }
  211. }
  212. var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
  213. if (model._forwardAxis === Axis.Z) {
  214. // glTF 2.0 has a Z-forward convention that must be adapted here to X-forward.
  215. BoundingSphere.transformWithoutScale(boundingSphere, Axis.Z_UP_TO_X_UP, boundingSphere);
  216. }
  217. if (model._upAxis === Axis.Y) {
  218. BoundingSphere.transformWithoutScale(boundingSphere, Axis.Y_UP_TO_Z_UP, boundingSphere);
  219. } else if (model._upAxis === Axis.X) {
  220. BoundingSphere.transformWithoutScale(boundingSphere, Axis.X_UP_TO_Z_UP, boundingSphere);
  221. }
  222. return boundingSphere;
  223. };
  224. function techniqueAttributeForSemantic(technique, semantic) {
  225. return ForEach.techniqueAttribute(technique, function(attribute, attributeName) {
  226. if (attribute.semantic === semantic) {
  227. return attributeName;
  228. }
  229. });
  230. }
  231. function ensureSemanticExistenceForPrimitive(gltf, primitive) {
  232. var accessors = gltf.accessors;
  233. var materials = gltf.materials;
  234. var techniquesWebgl = gltf.extensions.KHR_techniques_webgl;
  235. var techniques = techniquesWebgl.techniques;
  236. var programs = techniquesWebgl.programs;
  237. var shaders = techniquesWebgl.shaders;
  238. var targets = primitive.targets;
  239. var attributes = primitive.attributes;
  240. for (var target in targets) {
  241. if (targets.hasOwnProperty(target)) {
  242. var targetAttributes = targets[target];
  243. for (var attribute in targetAttributes) {
  244. if (attribute !== 'extras') {
  245. attributes[attribute + '_' + target] = targetAttributes[attribute];
  246. }
  247. }
  248. }
  249. }
  250. var material = materials[primitive.material];
  251. var technique = techniques[material.extensions.KHR_techniques_webgl.technique];
  252. var program = programs[technique.program];
  253. var vertexShader = shaders[program.vertexShader];
  254. for (var semantic in attributes) {
  255. if (attributes.hasOwnProperty(semantic)) {
  256. if (!defined(techniqueAttributeForSemantic(technique, semantic))) {
  257. var accessorId = attributes[semantic];
  258. var accessor = accessors[accessorId];
  259. var lowerCase = semantic.toLowerCase();
  260. if (lowerCase.charAt(0) === '_') {
  261. lowerCase = lowerCase.slice(1);
  262. }
  263. var attributeName = 'a_' + lowerCase;
  264. technique.attributes[attributeName] = {
  265. semantic: semantic,
  266. type: accessor.componentType
  267. };
  268. var pipelineExtras = vertexShader.extras._pipeline;
  269. var shaderText = pipelineExtras.source;
  270. shaderText = 'attribute ' + ModelUtility.getShaderVariable(accessor.type) + ' ' + attributeName + ';\n' + shaderText;
  271. pipelineExtras.source = shaderText;
  272. }
  273. }
  274. }
  275. }
  276. /**
  277. * Ensures all attributes present on the primitive are present in the technique and
  278. * vertex shader.
  279. *
  280. * @param {Object} gltf A javascript object containing a glTF asset.
  281. * @returns {Object} The glTF asset, including any additional attributes.
  282. */
  283. ModelUtility.ensureSemanticExistence = function (gltf) {
  284. ForEach.mesh(gltf, function(mesh) {
  285. ForEach.meshPrimitive(mesh, function(primitive) {
  286. ensureSemanticExistenceForPrimitive(gltf, primitive);
  287. });
  288. });
  289. return gltf;
  290. };
  291. /**
  292. * Creates attribute location for all attributes required by a technique.
  293. *
  294. * @param {Object} technique A glTF KHR_techniques_webgl technique object.
  295. * @param {Object} precreatedAttributes A dictionary object of pre-created attributes for which to also create locations.
  296. * @returns {Object} A dictionary object containing attribute names and their locations.
  297. */
  298. ModelUtility.createAttributeLocations = function(technique, precreatedAttributes) {
  299. var attributeLocations = {};
  300. var hasIndex0 = false;
  301. var i = 1;
  302. ForEach.techniqueAttribute(technique, function (attribute, attributeName) {
  303. // Set the position attribute to the 0th index. In some WebGL implementations the shader
  304. // will not work correctly if the 0th attribute is not active. For example, some glTF models
  305. // list the normal attribute first but derived shaders like the cast-shadows shader do not use
  306. // the normal attribute.
  307. if (/pos/i.test(attributeName) && !hasIndex0) {
  308. attributeLocations[attributeName] = 0;
  309. hasIndex0 = true;
  310. } else {
  311. attributeLocations[attributeName] = i++;
  312. }
  313. });
  314. if (defined(precreatedAttributes)) {
  315. for (var attributeName in precreatedAttributes) {
  316. if (precreatedAttributes.hasOwnProperty(attributeName)) {
  317. attributeLocations[attributeName] = i++;
  318. }
  319. }
  320. }
  321. return attributeLocations;
  322. };
  323. ModelUtility.getAccessorMinMax = function(gltf, accessorId) {
  324. var accessor = gltf.accessors[accessorId];
  325. var extensions = accessor.extensions;
  326. var accessorMin = accessor.min;
  327. var accessorMax = accessor.max;
  328. // If this accessor is quantized, we should use the decoded min and max
  329. if (defined(extensions)) {
  330. var quantizedAttributes = extensions.WEB3D_quantized_attributes;
  331. if (defined(quantizedAttributes)) {
  332. accessorMin = quantizedAttributes.decodedMin;
  333. accessorMax = quantizedAttributes.decodedMax;
  334. }
  335. }
  336. return {
  337. min : accessorMin,
  338. max : accessorMax
  339. };
  340. };
  341. function getTechniqueAttributeOrUniformFunction(gltf, technique, semantic, ignoreNodes) {
  342. if (hasExtension(gltf, 'KHR_techniques_webgl')) {
  343. return function(attributeOrUniform, attributeOrUniformName) {
  344. if (attributeOrUniform.semantic === semantic && (!ignoreNodes || !defined(attributeOrUniform.node))) {
  345. return attributeOrUniformName;
  346. }
  347. };
  348. }
  349. return function(parameterName, attributeOrUniformName) {
  350. var attributeOrUniform = technique.parameters[parameterName];
  351. if (attributeOrUniform.semantic === semantic && (!ignoreNodes || !defined(attributeOrUniform.node))) {
  352. return attributeOrUniformName;
  353. }
  354. };
  355. }
  356. ModelUtility.getAttributeOrUniformBySemantic = function(gltf, semantic, programId, ignoreNodes) {
  357. return ForEach.technique(gltf, function(technique) {
  358. if (defined(programId) && (technique.program !== programId)) {
  359. return;
  360. }
  361. var value = ForEach.techniqueAttribute(technique, getTechniqueAttributeOrUniformFunction(gltf, technique, semantic, ignoreNodes));
  362. if (defined(value)) {
  363. return value;
  364. }
  365. return ForEach.techniqueUniform(technique, getTechniqueAttributeOrUniformFunction(gltf, technique, semantic, ignoreNodes));
  366. });
  367. };
  368. ModelUtility.getDiffuseAttributeOrUniform = function(gltf, programId) {
  369. var diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'COLOR_0', programId);
  370. if (!defined(diffuseUniformName)) {
  371. diffuseUniformName = ModelUtility.getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE', programId);
  372. }
  373. return diffuseUniformName;
  374. };
  375. var nodeTranslationScratch = new Cartesian3();
  376. var nodeQuaternionScratch = new Quaternion();
  377. var nodeScaleScratch = new Cartesian3();
  378. ModelUtility.getTransform = function(node, result) {
  379. if (defined(node.matrix)) {
  380. return Matrix4.fromColumnMajorArray(node.matrix, result);
  381. }
  382. return Matrix4.fromTranslationQuaternionRotationScale(
  383. Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch),
  384. Quaternion.unpack(node.rotation, 0, nodeQuaternionScratch),
  385. Cartesian3.fromArray(node.scale, 0, nodeScaleScratch),
  386. result);
  387. };
  388. ModelUtility.getUsedExtensions = function(gltf) {
  389. var extensionsUsed = gltf.extensionsUsed;
  390. var cachedExtensionsUsed = {};
  391. if (defined(extensionsUsed)) {
  392. var extensionsUsedLength = extensionsUsed.length;
  393. for (var i = 0; i < extensionsUsedLength; i++) {
  394. var extension = extensionsUsed[i];
  395. cachedExtensionsUsed[extension] = true;
  396. }
  397. }
  398. return cachedExtensionsUsed;
  399. };
  400. ModelUtility.getRequiredExtensions = function(gltf) {
  401. var extensionsRequired = gltf.extensionsRequired;
  402. var cachedExtensionsRequired = {};
  403. if (defined(extensionsRequired)) {
  404. var extensionsRequiredLength = extensionsRequired.length;
  405. for (var i = 0; i < extensionsRequiredLength; i++) {
  406. var extension = extensionsRequired[i];
  407. cachedExtensionsRequired[extension] = true;
  408. }
  409. }
  410. return cachedExtensionsRequired;
  411. };
  412. ModelUtility.supportedExtensions = {
  413. 'AGI_articulations' : true,
  414. 'CESIUM_RTC' : true,
  415. 'EXT_texture_webp' : true,
  416. 'KHR_blend' : true,
  417. 'KHR_binary_glTF' : true,
  418. 'KHR_draco_mesh_compression' : true,
  419. 'KHR_materials_common' : true,
  420. 'KHR_techniques_webgl' : true,
  421. 'KHR_materials_unlit' : true,
  422. 'KHR_materials_pbrSpecularGlossiness' : true,
  423. 'KHR_texture_transform' : true,
  424. 'WEB3D_quantized_attributes' : true
  425. };
  426. ModelUtility.checkSupportedExtensions = function(extensionsRequired, browserSupportsWebp) {
  427. for (var extension in extensionsRequired) {
  428. if (extensionsRequired.hasOwnProperty(extension)) {
  429. if (!ModelUtility.supportedExtensions[extension]) {
  430. throw new RuntimeError('Unsupported glTF Extension: ' + extension);
  431. }
  432. if (extension === 'EXT_texture_webp' && browserSupportsWebp === false) {
  433. throw new RuntimeError('Loaded model requires WebP but browser does not support it.');
  434. }
  435. }
  436. }
  437. };
  438. ModelUtility.checkSupportedGlExtensions = function(extensionsUsed, context) {
  439. if (defined(extensionsUsed)) {
  440. var glExtensionsUsedLength = extensionsUsed.length;
  441. for (var i = 0; i < glExtensionsUsedLength; i++) {
  442. var extension = extensionsUsed[i];
  443. if (extension !== 'OES_element_index_uint') {
  444. throw new RuntimeError('Unsupported WebGL Extension: ' + extension);
  445. } else if (!context.elementIndexUint) {
  446. throw new RuntimeError('OES_element_index_uint WebGL extension is not enabled.');
  447. }
  448. }
  449. }
  450. };
  451. function replaceAllButFirstInString(string, find, replace) {
  452. // Limit search to strings that are not a subset of other tokens.
  453. find += '(?!\\w)';
  454. find = new RegExp(find, 'g');
  455. var index = string.search(find);
  456. return string.replace(find, function(match, offset) {
  457. return index === offset ? match : replace;
  458. });
  459. }
  460. function getQuantizedAttributes(gltf, accessorId) {
  461. var accessor = gltf.accessors[accessorId];
  462. var extensions = accessor.extensions;
  463. if (defined(extensions)) {
  464. return extensions.WEB3D_quantized_attributes;
  465. }
  466. return undefined;
  467. }
  468. function getAttributeVariableName(gltf, primitive, attributeSemantic) {
  469. var materialId = primitive.material;
  470. var material = gltf.materials[materialId];
  471. if (!hasExtension(gltf, 'KHR_techniques_webgl')
  472. || !defined(material.extensions)
  473. || !defined(material.extensions.KHR_techniques_webgl)) {
  474. return;
  475. }
  476. var techniqueId = material.extensions.KHR_techniques_webgl.technique;
  477. var techniquesWebgl = gltf.extensions.KHR_techniques_webgl;
  478. var technique = techniquesWebgl.techniques[techniqueId];
  479. return ForEach.techniqueAttribute(technique, function(attribute, attributeName) {
  480. var semantic = attribute.semantic;
  481. if (semantic === attributeSemantic) {
  482. return attributeName;
  483. }
  484. });
  485. }
  486. ModelUtility.modifyShaderForDracoQuantizedAttributes = function(gltf, primitive, shader, decodedAttributes) {
  487. var quantizedUniforms = {};
  488. for (var attributeSemantic in decodedAttributes) {
  489. if (decodedAttributes.hasOwnProperty(attributeSemantic)) {
  490. var attribute = decodedAttributes[attributeSemantic];
  491. var quantization = attribute.quantization;
  492. if (!defined(quantization)) {
  493. continue;
  494. }
  495. var attributeVarName = getAttributeVariableName(gltf, primitive, attributeSemantic);
  496. if (attributeSemantic.charAt(0) === '_') {
  497. attributeSemantic = attributeSemantic.substring(1);
  498. }
  499. var decodeUniformVarName = 'gltf_u_dec_' + attributeSemantic.toLowerCase();
  500. if (!defined(quantizedUniforms[decodeUniformVarName])) {
  501. var newMain = 'gltf_decoded_' + attributeSemantic;
  502. var decodedAttributeVarName = attributeVarName.replace('a_', 'gltf_a_dec_');
  503. var size = attribute.componentsPerAttribute;
  504. // replace usages of the original attribute with the decoded version, but not the declaration
  505. shader = replaceAllButFirstInString(shader, attributeVarName, decodedAttributeVarName);
  506. // declare decoded attribute
  507. var variableType;
  508. if (quantization.octEncoded) {
  509. variableType = 'vec3';
  510. } else if (size > 1) {
  511. variableType = 'vec' + size;
  512. } else {
  513. variableType = 'float';
  514. }
  515. shader = variableType + ' ' + decodedAttributeVarName + ';\n' + shader;
  516. // The gltf 2.0 COLOR_0 vertex attribute can be VEC4 or VEC3
  517. var vec3Color = size === 3 && attributeSemantic === 'COLOR_0';
  518. if (vec3Color) {
  519. shader = replaceAllButFirstInString(shader, decodedAttributeVarName, 'vec4(' + decodedAttributeVarName + ', 1.0)');
  520. }
  521. // splice decode function into the shader
  522. var decode = '';
  523. if (quantization.octEncoded) {
  524. var decodeUniformVarNameRangeConstant = decodeUniformVarName + '_rangeConstant';
  525. shader = 'uniform float ' + decodeUniformVarNameRangeConstant + ';\n' + shader;
  526. decode = '\n' +
  527. 'void main() {\n' +
  528. // Draco oct-encoding decodes to zxy order
  529. ' ' + decodedAttributeVarName + ' = czm_octDecode(' + attributeVarName + '.xy, ' + decodeUniformVarNameRangeConstant + ').zxy;\n' +
  530. ' ' + newMain + '();\n' +
  531. '}\n';
  532. } else {
  533. var decodeUniformVarNameNormConstant = decodeUniformVarName + '_normConstant';
  534. var decodeUniformVarNameMin = decodeUniformVarName + '_min';
  535. shader = 'uniform float ' + decodeUniformVarNameNormConstant + ';\n' +
  536. 'uniform ' + variableType + ' ' + decodeUniformVarNameMin + ';\n' + shader;
  537. var attributeVarAccess = vec3Color ? '.xyz' : '';
  538. decode = '\n' +
  539. 'void main() {\n' +
  540. ' ' + decodedAttributeVarName + ' = ' + decodeUniformVarNameMin + ' + ' + attributeVarName + attributeVarAccess + ' * ' + decodeUniformVarNameNormConstant + ';\n' +
  541. ' ' + newMain + '();\n' +
  542. '}\n';
  543. }
  544. shader = ShaderSource.replaceMain(shader, newMain);
  545. shader += decode;
  546. }
  547. }
  548. }
  549. return {
  550. shader : shader
  551. };
  552. };
  553. ModelUtility.modifyShaderForQuantizedAttributes = function(gltf, primitive, shader) {
  554. var quantizedUniforms = {};
  555. var attributes = primitive.attributes;
  556. for (var attributeSemantic in attributes) {
  557. if (attributes.hasOwnProperty(attributeSemantic)) {
  558. var attributeVarName = getAttributeVariableName(gltf, primitive, attributeSemantic);
  559. var accessorId = primitive.attributes[attributeSemantic];
  560. if (attributeSemantic.charAt(0) === '_') {
  561. attributeSemantic = attributeSemantic.substring(1);
  562. }
  563. var decodeUniformVarName = 'gltf_u_dec_' + attributeSemantic.toLowerCase();
  564. var decodeUniformVarNameScale = decodeUniformVarName + '_scale';
  565. var decodeUniformVarNameTranslate = decodeUniformVarName + '_translate';
  566. if (!defined(quantizedUniforms[decodeUniformVarName]) && !defined(quantizedUniforms[decodeUniformVarNameScale])) {
  567. var quantizedAttributes = getQuantizedAttributes(gltf, accessorId);
  568. if (defined(quantizedAttributes)) {
  569. var decodeMatrix = quantizedAttributes.decodeMatrix;
  570. var newMain = 'gltf_decoded_' + attributeSemantic;
  571. var decodedAttributeVarName = attributeVarName.replace('a_', 'gltf_a_dec_');
  572. var size = Math.floor(Math.sqrt(decodeMatrix.length));
  573. // replace usages of the original attribute with the decoded version, but not the declaration
  574. shader = replaceAllButFirstInString(shader, attributeVarName, decodedAttributeVarName);
  575. // declare decoded attribute
  576. var variableType;
  577. if (size > 2) {
  578. variableType = 'vec' + (size - 1);
  579. } else {
  580. variableType = 'float';
  581. }
  582. shader = variableType + ' ' + decodedAttributeVarName + ';\n' + shader;
  583. // splice decode function into the shader - attributes are pre-multiplied with the decode matrix
  584. // uniform in the shader (32-bit floating point)
  585. var decode = '';
  586. if (size === 5) {
  587. // separate scale and translate since glsl doesn't have mat5
  588. shader = 'uniform mat4 ' + decodeUniformVarNameScale + ';\n' + shader;
  589. shader = 'uniform vec4 ' + decodeUniformVarNameTranslate + ';\n' + shader;
  590. decode = '\n' +
  591. 'void main() {\n' +
  592. ' ' + decodedAttributeVarName + ' = ' + decodeUniformVarNameScale + ' * ' + attributeVarName + ' + ' + decodeUniformVarNameTranslate + ';\n' +
  593. ' ' + newMain + '();\n' +
  594. '}\n';
  595. quantizedUniforms[decodeUniformVarNameScale] = {mat : 4};
  596. quantizedUniforms[decodeUniformVarNameTranslate] = {vec : 4};
  597. }
  598. else {
  599. shader = 'uniform mat' + size + ' ' + decodeUniformVarName + ';\n' + shader;
  600. decode = '\n' +
  601. 'void main() {\n' +
  602. ' ' + decodedAttributeVarName + ' = ' + variableType + '(' + decodeUniformVarName + ' * vec' + size + '(' + attributeVarName + ',1.0));\n' +
  603. ' ' + newMain + '();\n' +
  604. '}\n';
  605. quantizedUniforms[decodeUniformVarName] = {mat : size};
  606. }
  607. shader = ShaderSource.replaceMain(shader, newMain);
  608. shader += decode;
  609. }
  610. }
  611. }
  612. }
  613. return {
  614. shader : shader,
  615. uniforms : quantizedUniforms
  616. };
  617. };
  618. ModelUtility.toClipCoordinatesGLSL = function(gltf, shader) {
  619. var positionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'POSITION');
  620. var decodedPositionName = positionName.replace('a_', 'gltf_a_dec_');
  621. if (shader.indexOf(decodedPositionName) !== -1) {
  622. positionName = decodedPositionName;
  623. }
  624. var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEWPROJECTION', undefined, true);
  625. if (!defined(modelViewProjectionName) || shader.indexOf(modelViewProjectionName) === -1) {
  626. var projectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'PROJECTION', undefined, true);
  627. var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEW', undefined, true);
  628. if (shader.indexOf('czm_instanced_modelView ') !== -1) {
  629. modelViewName = 'czm_instanced_modelView';
  630. } else if (!defined(modelViewName)) {
  631. modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'CESIUM_RTC_MODELVIEW', undefined, true);
  632. }
  633. modelViewProjectionName = projectionName + ' * ' + modelViewName;
  634. }
  635. return modelViewProjectionName + ' * vec4(' + positionName + '.xyz, 1.0)';
  636. };
  637. ModelUtility.modifyFragmentShaderForLogDepth = function(shader) {
  638. shader = ShaderSource.replaceMain(shader, 'czm_depth_main');
  639. shader +=
  640. '\n' +
  641. 'void main() \n' +
  642. '{ \n' +
  643. ' czm_depth_main(); \n' +
  644. ' czm_writeLogDepth(); \n' +
  645. '} \n';
  646. return shader;
  647. };
  648. ModelUtility.modifyVertexShaderForLogDepth = function(shader, toClipCoordinatesGLSL) {
  649. shader = ShaderSource.replaceMain(shader, 'czm_depth_main');
  650. shader +=
  651. '\n' +
  652. 'void main() \n' +
  653. '{ \n' +
  654. ' czm_depth_main(); \n' +
  655. ' czm_vertexLogDepth(' + toClipCoordinatesGLSL + '); \n' +
  656. '} \n';
  657. return shader;
  658. };
  659. function getScalarUniformFunction(value) {
  660. var that = {
  661. value : value,
  662. clone : function(source, result) {
  663. return source;
  664. },
  665. func : function() {
  666. return that.value;
  667. }
  668. };
  669. return that;
  670. }
  671. function getVec2UniformFunction(value) {
  672. var that = {
  673. value : Cartesian2.fromArray(value),
  674. clone : Cartesian2.clone,
  675. func : function() {
  676. return that.value;
  677. }
  678. };
  679. return that;
  680. }
  681. function getVec3UniformFunction(value) {
  682. var that = {
  683. value : Cartesian3.fromArray(value),
  684. clone : Cartesian3.clone,
  685. func : function() {
  686. return that.value;
  687. }
  688. };
  689. return that;
  690. }
  691. function getVec4UniformFunction(value) {
  692. var that = {
  693. value : Cartesian4.fromArray(value),
  694. clone : Cartesian4.clone,
  695. func : function() {
  696. return that.value;
  697. }
  698. };
  699. return that;
  700. }
  701. function getMat2UniformFunction(value) {
  702. var that = {
  703. value : Matrix2.fromColumnMajorArray(value),
  704. clone : Matrix2.clone,
  705. func : function() {
  706. return that.value;
  707. }
  708. };
  709. return that;
  710. }
  711. function getMat3UniformFunction(value) {
  712. var that = {
  713. value : Matrix3.fromColumnMajorArray(value),
  714. clone : Matrix3.clone,
  715. func : function() {
  716. return that.value;
  717. }
  718. };
  719. return that;
  720. }
  721. function getMat4UniformFunction(value) {
  722. var that = {
  723. value : Matrix4.fromColumnMajorArray(value),
  724. clone : Matrix4.clone,
  725. func : function() {
  726. return that.value;
  727. }
  728. };
  729. return that;
  730. }
  731. ///////////////////////////////////////////////////////////////////////////
  732. function DelayLoadedTextureUniform(value, textures, defaultTexture) {
  733. this._value = undefined;
  734. this._textureId = value.index;
  735. this._textures = textures;
  736. this._defaultTexture = defaultTexture;
  737. }
  738. defineProperties(DelayLoadedTextureUniform.prototype, {
  739. value : {
  740. get : function() {
  741. // Use the default texture (1x1 white) until the model's texture is loaded
  742. if (!defined(this._value)) {
  743. var texture = this._textures[this._textureId];
  744. if (defined(texture)) {
  745. this._value = texture;
  746. } else {
  747. return this._defaultTexture;
  748. }
  749. }
  750. return this._value;
  751. },
  752. set : function(value) {
  753. this._value = value;
  754. }
  755. }
  756. });
  757. DelayLoadedTextureUniform.prototype.clone = function(source) {
  758. return source;
  759. };
  760. DelayLoadedTextureUniform.prototype.func = undefined;
  761. ///////////////////////////////////////////////////////////////////////////
  762. function getTextureUniformFunction(value, textures, defaultTexture) {
  763. var uniform = new DelayLoadedTextureUniform(value, textures, defaultTexture);
  764. // Define function here to access closure since 'this' can't be
  765. // used when the Renderer sets uniforms.
  766. uniform.func = function() {
  767. return uniform.value;
  768. };
  769. return uniform;
  770. }
  771. var gltfUniformFunctions = {};
  772. gltfUniformFunctions[WebGLConstants.FLOAT] = getScalarUniformFunction;
  773. gltfUniformFunctions[WebGLConstants.FLOAT_VEC2] = getVec2UniformFunction;
  774. gltfUniformFunctions[WebGLConstants.FLOAT_VEC3] = getVec3UniformFunction;
  775. gltfUniformFunctions[WebGLConstants.FLOAT_VEC4] = getVec4UniformFunction;
  776. gltfUniformFunctions[WebGLConstants.INT] = getScalarUniformFunction;
  777. gltfUniformFunctions[WebGLConstants.INT_VEC2] = getVec2UniformFunction;
  778. gltfUniformFunctions[WebGLConstants.INT_VEC3] = getVec3UniformFunction;
  779. gltfUniformFunctions[WebGLConstants.INT_VEC4] = getVec4UniformFunction;
  780. gltfUniformFunctions[WebGLConstants.BOOL] = getScalarUniformFunction;
  781. gltfUniformFunctions[WebGLConstants.BOOL_VEC2] = getVec2UniformFunction;
  782. gltfUniformFunctions[WebGLConstants.BOOL_VEC3] = getVec3UniformFunction;
  783. gltfUniformFunctions[WebGLConstants.BOOL_VEC4] = getVec4UniformFunction;
  784. gltfUniformFunctions[WebGLConstants.FLOAT_MAT2] = getMat2UniformFunction;
  785. gltfUniformFunctions[WebGLConstants.FLOAT_MAT3] = getMat3UniformFunction;
  786. gltfUniformFunctions[WebGLConstants.FLOAT_MAT4] = getMat4UniformFunction;
  787. gltfUniformFunctions[WebGLConstants.SAMPLER_2D] = getTextureUniformFunction;
  788. // GLTF_SPEC: Support SAMPLER_CUBE. https://github.com/KhronosGroup/glTF/issues/40
  789. ModelUtility.createUniformFunction = function(type, value, textures, defaultTexture) {
  790. return gltfUniformFunctions[type](value, textures, defaultTexture);
  791. };
  792. function scaleFromMatrix5Array(matrix) {
  793. return [matrix[0], matrix[1], matrix[2], matrix[3],
  794. matrix[5], matrix[6], matrix[7], matrix[8],
  795. matrix[10], matrix[11], matrix[12], matrix[13],
  796. matrix[15], matrix[16], matrix[17], matrix[18]];
  797. }
  798. function translateFromMatrix5Array(matrix) {
  799. return [matrix[20], matrix[21], matrix[22], matrix[23]];
  800. }
  801. ModelUtility.createUniformsForDracoQuantizedAttributes = function(decodedAttributes) {
  802. var uniformMap = {};
  803. for (var attribute in decodedAttributes) {
  804. if (decodedAttributes.hasOwnProperty(attribute)) {
  805. var decodedData = decodedAttributes[attribute];
  806. var quantization = decodedData.quantization;
  807. if (!defined(quantization)) {
  808. continue;
  809. }
  810. if (attribute.charAt(0) === '_'){
  811. attribute = attribute.substring(1);
  812. }
  813. var uniformVarName = 'gltf_u_dec_' + attribute.toLowerCase();
  814. if (quantization.octEncoded) {
  815. var uniformVarNameRangeConstant = uniformVarName + '_rangeConstant';
  816. var rangeConstant = (1 << quantization.quantizationBits) - 1.0;
  817. uniformMap[uniformVarNameRangeConstant] = getScalarUniformFunction(rangeConstant).func;
  818. continue;
  819. }
  820. var uniformVarNameNormConstant = uniformVarName + '_normConstant';
  821. var normConstant = quantization.range / (1 << quantization.quantizationBits);
  822. uniformMap[uniformVarNameNormConstant] = getScalarUniformFunction(normConstant).func;
  823. var uniformVarNameMin = uniformVarName + '_min';
  824. switch (decodedData.componentsPerAttribute) {
  825. case 1:
  826. uniformMap[uniformVarNameMin] = getScalarUniformFunction(quantization.minValues).func;
  827. break;
  828. case 2:
  829. uniformMap[uniformVarNameMin] = getVec2UniformFunction(quantization.minValues).func;
  830. break;
  831. case 3:
  832. uniformMap[uniformVarNameMin] = getVec3UniformFunction(quantization.minValues).func;
  833. break;
  834. case 4:
  835. uniformMap[uniformVarNameMin] = getVec4UniformFunction(quantization.minValues).func;
  836. break;
  837. }
  838. }
  839. }
  840. return uniformMap;
  841. };
  842. ModelUtility.createUniformsForQuantizedAttributes = function(gltf, primitive, quantizedUniforms) {
  843. var accessors = gltf.accessors;
  844. var setUniforms = {};
  845. var uniformMap = {};
  846. var attributes = primitive.attributes;
  847. for (var attribute in attributes) {
  848. if (attributes.hasOwnProperty(attribute)) {
  849. var accessorId = attributes[attribute];
  850. var a = accessors[accessorId];
  851. var extensions = a.extensions;
  852. if (attribute.charAt(0) === '_') {
  853. attribute = attribute.substring(1);
  854. }
  855. if (defined(extensions)) {
  856. var quantizedAttributes = extensions.WEB3D_quantized_attributes;
  857. if (defined(quantizedAttributes)) {
  858. var decodeMatrix = quantizedAttributes.decodeMatrix;
  859. var uniformVariable = 'gltf_u_dec_' + attribute.toLowerCase();
  860. switch (a.type) {
  861. case AttributeType.SCALAR:
  862. uniformMap[uniformVariable] = getMat2UniformFunction(decodeMatrix).func;
  863. setUniforms[uniformVariable] = true;
  864. break;
  865. case AttributeType.VEC2:
  866. uniformMap[uniformVariable] = getMat3UniformFunction(decodeMatrix).func;
  867. setUniforms[uniformVariable] = true;
  868. break;
  869. case AttributeType.VEC3:
  870. uniformMap[uniformVariable] = getMat4UniformFunction(decodeMatrix).func;
  871. setUniforms[uniformVariable] = true;
  872. break;
  873. case AttributeType.VEC4:
  874. // VEC4 attributes are split into scale and translate because there is no mat5 in GLSL
  875. var uniformVariableScale = uniformVariable + '_scale';
  876. var uniformVariableTranslate = uniformVariable + '_translate';
  877. uniformMap[uniformVariableScale] = getMat4UniformFunction(scaleFromMatrix5Array(decodeMatrix)).func;
  878. uniformMap[uniformVariableTranslate] = getVec4UniformFunction(translateFromMatrix5Array(decodeMatrix)).func;
  879. setUniforms[uniformVariableScale] = true;
  880. setUniforms[uniformVariableTranslate] = true;
  881. break;
  882. }
  883. }
  884. }
  885. }
  886. }
  887. // If there are any unset quantized uniforms in this program, they should be set to the identity
  888. for (var quantizedUniform in quantizedUniforms) {
  889. if (quantizedUniforms.hasOwnProperty(quantizedUniform)) {
  890. if (!setUniforms[quantizedUniform]) {
  891. var properties = quantizedUniforms[quantizedUniform];
  892. if (defined(properties.mat)) {
  893. if (properties.mat === 2) {
  894. uniformMap[quantizedUniform] = getMat2UniformFunction(Matrix2.IDENTITY).func;
  895. } else if (properties.mat === 3) {
  896. uniformMap[quantizedUniform] = getMat3UniformFunction(Matrix3.IDENTITY).func;
  897. } else if (properties.mat === 4) {
  898. uniformMap[quantizedUniform] = getMat4UniformFunction(Matrix4.IDENTITY).func;
  899. }
  900. }
  901. if (defined(properties.vec)) {
  902. if (properties.vec === 4) {
  903. uniformMap[quantizedUniform] = getVec4UniformFunction([0, 0, 0, 0]).func;
  904. }
  905. }
  906. }
  907. }
  908. }
  909. return uniformMap;
  910. };
  911. // This doesn't support LOCAL, which we could add if it is ever used.
  912. var scratchTranslationRtc = new Cartesian3();
  913. var gltfSemanticUniforms = {
  914. MODEL : function(uniformState, model) {
  915. return function() {
  916. return uniformState.model;
  917. };
  918. },
  919. VIEW : function(uniformState, model) {
  920. return function() {
  921. return uniformState.view;
  922. };
  923. },
  924. PROJECTION : function(uniformState, model) {
  925. return function() {
  926. return uniformState.projection;
  927. };
  928. },
  929. MODELVIEW : function(uniformState, model) {
  930. return function() {
  931. return uniformState.modelView;
  932. };
  933. },
  934. CESIUM_RTC_MODELVIEW : function(uniformState, model) {
  935. // CESIUM_RTC extension
  936. var mvRtc = new Matrix4();
  937. return function() {
  938. if (defined(model._rtcCenter)) {
  939. Matrix4.getTranslation(uniformState.model, scratchTranslationRtc);
  940. Cartesian3.add(scratchTranslationRtc, model._rtcCenter, scratchTranslationRtc);
  941. Matrix4.multiplyByPoint(uniformState.view, scratchTranslationRtc, scratchTranslationRtc);
  942. return Matrix4.setTranslation(uniformState.modelView, scratchTranslationRtc, mvRtc);
  943. }
  944. return uniformState.modelView;
  945. };
  946. },
  947. MODELVIEWPROJECTION : function(uniformState, model) {
  948. return function() {
  949. return uniformState.modelViewProjection;
  950. };
  951. },
  952. MODELINVERSE : function(uniformState, model) {
  953. return function() {
  954. return uniformState.inverseModel;
  955. };
  956. },
  957. VIEWINVERSE : function(uniformState, model) {
  958. return function() {
  959. return uniformState.inverseView;
  960. };
  961. },
  962. PROJECTIONINVERSE : function(uniformState, model) {
  963. return function() {
  964. return uniformState.inverseProjection;
  965. };
  966. },
  967. MODELVIEWINVERSE : function(uniformState, model) {
  968. return function() {
  969. return uniformState.inverseModelView;
  970. };
  971. },
  972. MODELVIEWPROJECTIONINVERSE : function(uniformState, model) {
  973. return function() {
  974. return uniformState.inverseModelViewProjection;
  975. };
  976. },
  977. MODELINVERSETRANSPOSE : function(uniformState, model) {
  978. return function() {
  979. return uniformState.inverseTransposeModel;
  980. };
  981. },
  982. MODELVIEWINVERSETRANSPOSE : function(uniformState, model) {
  983. return function() {
  984. return uniformState.normal;
  985. };
  986. },
  987. VIEWPORT : function(uniformState, model) {
  988. return function() {
  989. return uniformState.viewportCartesian4;
  990. };
  991. }
  992. // JOINTMATRIX created in createCommand()
  993. };
  994. ModelUtility.getGltfSemanticUniforms = function() {
  995. return gltfSemanticUniforms;
  996. };
  997. export default ModelUtility;