Model.js 105 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690
  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/Cartesian2',
  5. '../Core/Cartesian3',
  6. '../Core/Cartesian4',
  7. '../Core/clone',
  8. '../Core/combine',
  9. '../Core/defaultValue',
  10. '../Core/defined',
  11. '../Core/defineProperties',
  12. '../Core/destroyObject',
  13. '../Core/DeveloperError',
  14. '../Core/Event',
  15. '../Core/IndexDatatype',
  16. '../Core/loadArrayBuffer',
  17. '../Core/loadImage',
  18. '../Core/loadText',
  19. '../Core/Math',
  20. '../Core/Matrix2',
  21. '../Core/Matrix3',
  22. '../Core/Matrix4',
  23. '../Core/PrimitiveType',
  24. '../Core/Quaternion',
  25. '../Core/Queue',
  26. '../Core/RuntimeError',
  27. '../Renderer/BufferUsage',
  28. '../Renderer/DrawCommand',
  29. '../Renderer/ShaderSource',
  30. '../Renderer/TextureMinificationFilter',
  31. '../Renderer/TextureWrap',
  32. '../ThirdParty/gltfDefaults',
  33. '../ThirdParty/Uri',
  34. './getModelAccessor',
  35. './ModelAnimationCache',
  36. './ModelAnimationCollection',
  37. './ModelMaterial',
  38. './ModelMesh',
  39. './ModelNode',
  40. './Pass',
  41. './SceneMode'
  42. ], function(
  43. BoundingSphere,
  44. Cartesian2,
  45. Cartesian3,
  46. Cartesian4,
  47. clone,
  48. combine,
  49. defaultValue,
  50. defined,
  51. defineProperties,
  52. destroyObject,
  53. DeveloperError,
  54. Event,
  55. IndexDatatype,
  56. loadArrayBuffer,
  57. loadImage,
  58. loadText,
  59. CesiumMath,
  60. Matrix2,
  61. Matrix3,
  62. Matrix4,
  63. PrimitiveType,
  64. Quaternion,
  65. Queue,
  66. RuntimeError,
  67. BufferUsage,
  68. DrawCommand,
  69. ShaderSource,
  70. TextureMinificationFilter,
  71. TextureWrap,
  72. gltfDefaults,
  73. Uri,
  74. getModelAccessor,
  75. ModelAnimationCache,
  76. ModelAnimationCollection,
  77. ModelMaterial,
  78. ModelMesh,
  79. ModelNode,
  80. Pass,
  81. SceneMode) {
  82. "use strict";
  83. /*global WebGLRenderingContext*/
  84. var yUpToZUp = Matrix4.fromRotationTranslation(Matrix3.fromRotationX(CesiumMath.PI_OVER_TWO));
  85. var ModelState = {
  86. NEEDS_LOAD : 0,
  87. LOADING : 1,
  88. LOADED : 2,
  89. FAILED : 3
  90. };
  91. function LoadResources() {
  92. this.buffersToCreate = new Queue();
  93. this.buffers = {};
  94. this.pendingBufferLoads = 0;
  95. this.programsToCreate = new Queue();
  96. this.shaders = {};
  97. this.pendingShaderLoads = 0;
  98. this.texturesToCreate = new Queue();
  99. this.pendingTextureLoads = 0;
  100. this.createSamplers = true;
  101. this.createSkins = true;
  102. this.createRuntimeAnimations = true;
  103. this.createVertexArrays = true;
  104. this.createRenderStates = true;
  105. this.createUniformMaps = true;
  106. this.createRuntimeNodes = true;
  107. this.skinnedNodesNames = [];
  108. }
  109. LoadResources.prototype.finishedPendingLoads = function() {
  110. return ((this.pendingBufferLoads === 0) &&
  111. (this.pendingShaderLoads === 0) &&
  112. (this.pendingTextureLoads === 0));
  113. };
  114. LoadResources.prototype.finishedResourceCreation = function() {
  115. return ((this.buffersToCreate.length === 0) &&
  116. (this.programsToCreate.length === 0) &&
  117. (this.texturesToCreate.length === 0));
  118. };
  119. LoadResources.prototype.finishedBuffersCreation = function() {
  120. return ((this.pendingBufferLoads === 0) && (this.buffersToCreate.length === 0));
  121. };
  122. LoadResources.prototype.finishedProgramCreation = function() {
  123. return ((this.pendingShaderLoads === 0) && (this.programsToCreate.length === 0));
  124. };
  125. LoadResources.prototype.finishedTextureCreation = function() {
  126. return ((this.pendingTextureLoads === 0) && (this.texturesToCreate.length === 0));
  127. };
  128. ///////////////////////////////////////////////////////////////////////////
  129. // glTF JSON can be big given embedded geometry, textures, and animations, so we
  130. // cache it across all models using the same url/cache-key. This also reduces the
  131. // slight overhead in assigning defaults to missing values.
  132. //
  133. // Note that this is a global cache, compared to renderer resources, which
  134. // are cached per context.
  135. var CachedGltf = function(options) {
  136. this._gltf = gltfDefaults(options.gltf);
  137. this.ready = options.ready;
  138. this.modelsToLoad = [];
  139. this.count = 0;
  140. };
  141. defineProperties(CachedGltf.prototype, {
  142. gltf : {
  143. set : function(value) {
  144. this._gltf = gltfDefaults(value);
  145. },
  146. get : function() {
  147. return this._gltf;
  148. }
  149. }
  150. });
  151. var gltfCache = {};
  152. function getAnimationIds(cachedGltf) {
  153. var animationIds = [];
  154. if (defined(cachedGltf) && defined(cachedGltf.gltf)) {
  155. var animations = cachedGltf.gltf.animations;
  156. for (var name in animations) {
  157. if (animations.hasOwnProperty(name)) {
  158. animationIds.push(name);
  159. }
  160. }
  161. }
  162. return animationIds;
  163. }
  164. ///////////////////////////////////////////////////////////////////////////
  165. /**
  166. * A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL.
  167. * <p>
  168. * Cesium includes support for geometry and materials, glTF animations, and glTF skinning.
  169. * In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable
  170. * with {@link Model#getNode}. glTF cameras and lights are not currently supported.
  171. * </p>
  172. * <p>
  173. * An external glTF asset is created with {@link Model.fromGltf}. glTF JSON can also be
  174. * created at runtime and passed to this constructor function. In either case, the
  175. * {@link Model#readyToRender} event is fired when the model is ready to render, i.e.,
  176. * when the external binary, image, and shader files are downloaded and the WebGL
  177. * resources are created.
  178. * </p>
  179. *
  180. * @alias Model
  181. * @constructor
  182. *
  183. * @param {Object} [options] Object with the following properties:
  184. * @param {Object} [options.gltf] The object for the glTF JSON.
  185. * @param {String} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
  186. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  187. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  188. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  189. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  190. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
  191. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  192. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
  193. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  194. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  195. *
  196. * @see Model.fromGltf
  197. * @see Model#readyToRender
  198. *
  199. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo}
  200. */
  201. var Model = function(options) {
  202. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  203. var cacheKey = options.cacheKey;
  204. this._cacheKey = cacheKey;
  205. this._cachedGltf = undefined;
  206. this._releaseGltfJson = defaultValue(options.releaseGltfJson, false);
  207. this._animationIds = undefined;
  208. var cachedGltf;
  209. if (defined(cacheKey) && defined(gltfCache[cacheKey]) && gltfCache[cacheKey].ready) {
  210. // glTF JSON is in cache and ready
  211. cachedGltf = gltfCache[cacheKey];
  212. ++cachedGltf.count;
  213. } else {
  214. // glTF was explicitly provided, e.g., when a user uses the Model constructor directly
  215. if (defined(options.gltf)) {
  216. cachedGltf = new CachedGltf({
  217. gltf : options.gltf,
  218. ready : true
  219. });
  220. cachedGltf.count = 1;
  221. if (defined(cacheKey)) {
  222. gltfCache[cacheKey] = cachedGltf;
  223. }
  224. }
  225. }
  226. setCachedGltf(this, cachedGltf);
  227. this._basePath = defaultValue(options.basePath, '');
  228. var docUri = new Uri(document.location.href);
  229. var modelUri = new Uri(this._basePath);
  230. this._baseUri = modelUri.resolve(docUri);
  231. /**
  232. * Determines if the model primitive will be shown.
  233. *
  234. * @type {Boolean}
  235. *
  236. * @default true
  237. */
  238. this.show = defaultValue(options.show, true);
  239. /**
  240. * The 4x4 transformation matrix that transforms the model from model to world coordinates.
  241. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  242. * Local reference frames can be used by providing a different transformation matrix, like that returned
  243. * by {@link Transforms.eastNorthUpToFixedFrame}.
  244. *
  245. * @type {Matrix4}
  246. *
  247. * @default {@link Matrix4.IDENTITY}
  248. *
  249. * @example
  250. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  251. * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  252. */
  253. this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
  254. this._modelMatrix = Matrix4.clone(this.modelMatrix);
  255. /**
  256. * A uniform scale applied to this model before the {@link Model#modelMatrix}.
  257. * Values greater than <code>1.0</code> increase the size of the model; values
  258. * less than <code>1.0</code> decrease.
  259. *
  260. * @type {Number}
  261. *
  262. * @default 1.0
  263. */
  264. this.scale = defaultValue(options.scale, 1.0);
  265. this._scale = this.scale;
  266. /**
  267. * The approximate minimum pixel size of the model regardless of zoom.
  268. * This can be used to ensure that a model is visible even when the viewer
  269. * zooms out. When <code>0.0</code>, no minimum size is enforced.
  270. *
  271. * @type {Number}
  272. *
  273. * @default 0.0
  274. */
  275. this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);
  276. this._minimumPixelSize = this.minimumPixelSize;
  277. /**
  278. * User-defined object returned when the model is picked.
  279. *
  280. * @type Object
  281. *
  282. * @default undefined
  283. *
  284. * @see Scene#pick
  285. */
  286. this.id = options.id;
  287. this._id = options.id;
  288. /**
  289. * Used for picking primitives that wrap a model.
  290. *
  291. * @private
  292. */
  293. this.pickPrimitive = options.pickPrimitive;
  294. this._allowPicking = defaultValue(options.allowPicking, true);
  295. /**
  296. * The event fired when this model is ready to render, i.e., when the external binary, image,
  297. * and shader files were downloaded and the WebGL resources were created.
  298. * <p>
  299. * This event is fired at the end of the frame before the first frame the model is rendered in.
  300. * </p>
  301. *
  302. * @type {Event}
  303. * @default new Event()
  304. *
  305. * @example
  306. * // Play all animations at half-speed when the model is ready to render
  307. * model.readyToRender.addEventListener(function(model) {
  308. * model.activeAnimations.addAll({
  309. * speedup : 0.5
  310. * });
  311. * });
  312. *
  313. * @see Model#ready
  314. */
  315. this.readyToRender = new Event();
  316. this._ready = false;
  317. /**
  318. * The currently playing glTF animations.
  319. *
  320. * @type {ModelAnimationCollection}
  321. */
  322. this.activeAnimations = new ModelAnimationCollection(this);
  323. this._asynchronous = defaultValue(options.asynchronous, true);
  324. /**
  325. * This property is for debugging only; it is not for production use nor is it optimized.
  326. * <p>
  327. * Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds
  328. * to one draw command. A glTF mesh has an array of primitives, often of length one.
  329. * </p>
  330. *
  331. * @type {Boolean}
  332. *
  333. * @default false
  334. */
  335. this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
  336. this._debugShowBoudingVolume = false;
  337. /**
  338. * This property is for debugging only; it is not for production use nor is it optimized.
  339. * <p>
  340. * Draws the model in wireframe.
  341. * </p>
  342. *
  343. * @type {Boolean}
  344. *
  345. * @default false
  346. */
  347. this.debugWireframe = defaultValue(options.debugWireframe, false);
  348. this._debugWireframe = false;
  349. this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale
  350. this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
  351. this._boundingSphere = undefined;
  352. this._state = ModelState.NEEDS_LOAD;
  353. this._loadError = undefined;
  354. this._loadResources = undefined;
  355. this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property
  356. this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform
  357. this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal
  358. this._runtime = {
  359. animations : undefined,
  360. rootNodes : undefined,
  361. nodes : undefined, // Indexed with the node property's name, i.e., glTF id
  362. nodesByName : undefined, // Indexed with name property in the node
  363. skinnedNodes : undefined,
  364. meshesByName : undefined, // Indexed with the name property in the mesh
  365. materialsByName : undefined, // Indexed with the name property in the material
  366. materialsById : undefined // Indexed with the material's property name
  367. };
  368. this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation
  369. this._rendererResources = { // Cached between models with the same url/cache-key
  370. buffers : {},
  371. vertexArrays : {},
  372. programs : {},
  373. pickPrograms : {},
  374. textures : {},
  375. samplers : {},
  376. renderStates : {}
  377. };
  378. this._cachedRendererResources = undefined;
  379. this._loadRendererResourcesFromCache = false;
  380. this._nodeCommands = [];
  381. this._pickIds = [];
  382. };
  383. function setCachedGltf(model, cachedGltf) {
  384. model._cachedGltf = cachedGltf;
  385. model._animationIds = getAnimationIds(cachedGltf);
  386. }
  387. defineProperties(Model.prototype, {
  388. /**
  389. * The object for the glTF JSON, including properties with default values omitted
  390. * from the JSON provided to this model.
  391. *
  392. * @memberof Model.prototype
  393. *
  394. * @type {Object}
  395. * @readonly
  396. *
  397. * @default undefined
  398. */
  399. gltf : {
  400. get : function() {
  401. return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined;
  402. }
  403. },
  404. /**
  405. * When <code>true</code>, the glTF JSON is not stored with the model once the model is
  406. * loaded (when {@link Model#ready} is <code>true</code>). This saves memory when
  407. * geometry, textures, and animations are embedded in the .gltf file, which is the
  408. * default for the {@link http://cesiumjs.org/convertmodel.html|Cesium model converter}.
  409. * This is especially useful for cases like 3D buildings, where each .gltf model is unique
  410. * and caching the glTF JSON is not effective.
  411. *
  412. * @memberof Model.prototype
  413. *
  414. * @type {Boolean}
  415. * @readonly
  416. *
  417. * @default false
  418. *
  419. * @private
  420. */
  421. releaseGltfJson : {
  422. get : function() {
  423. return this._releaseGltfJson;
  424. }
  425. },
  426. /**
  427. * The key identifying this model in the model cache for glTF JSON, renderer resources, and animations.
  428. * Caching saves memory and improves loading speed when several models with the same url are created.
  429. * <p>
  430. * This key is automatically generated when the model is created with {@link Model.fromGltf}. If the model
  431. * is created directly from glTF JSON using the {@link Model} constructor, this key can be manually
  432. * provided; otherwise, the model will not be changed.
  433. * </p>
  434. *
  435. * @memberof Model.prototype
  436. *
  437. * @type {String}
  438. * @readonly
  439. *
  440. * @private
  441. */
  442. cacheKey : {
  443. get : function() {
  444. return this._cacheKey;
  445. }
  446. },
  447. /**
  448. * The base path that paths in the glTF JSON are relative to. The base
  449. * path is the same path as the path containing the .json file
  450. * minus the .json file, when binary, image, and shader files are
  451. * in the same directory as the .json. When this is <code>''</code>,
  452. * the app's base path is used.
  453. *
  454. * @memberof Model.prototype
  455. *
  456. * @type {String}
  457. * @readonly
  458. *
  459. * @default ''
  460. */
  461. basePath : {
  462. get : function() {
  463. return this._basePath;
  464. }
  465. },
  466. /**
  467. * The model's bounding sphere in its local coordinate system. This does not take into
  468. * account glTF animation and skins or {@link Model#scale}.
  469. *
  470. * @memberof Model.prototype
  471. *
  472. * @type {BoundingSphere}
  473. * @readonly
  474. *
  475. * @default undefined
  476. *
  477. * @exception {DeveloperError} The model is not loaded. Wait for the model's readyToRender event or ready property.
  478. *
  479. * @example
  480. * // Center in WGS84 coordinates
  481. * var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
  482. */
  483. boundingSphere : {
  484. get : function() {
  485. //>>includeStart('debug', pragmas.debug);
  486. if (this._state !== ModelState.LOADED) {
  487. throw new DeveloperError('The model is not loaded. Wait for the model\'s readyToRender event or ready property.');
  488. }
  489. //>>includeEnd('debug');
  490. this._boundingSphere.radius = (this.scale * Matrix4.getMaximumScale(this.modelMatrix)) * this._initialRadius;
  491. return this._boundingSphere;
  492. }
  493. },
  494. /**
  495. * When <code>true</code>, this model is ready to render, i.e., the external binary, image,
  496. * and shader files were downloaded and the WebGL resources were created. This is set to
  497. * <code>true</code> right before {@link Model#readyToRender} is fired.
  498. *
  499. * @memberof Model.prototype
  500. *
  501. * @type {Boolean}
  502. * @readonly
  503. *
  504. * @default false
  505. *
  506. * @see Model#readyToRender
  507. */
  508. ready : {
  509. get : function() {
  510. return this._ready;
  511. }
  512. },
  513. /**
  514. * Determines if model WebGL resource creation will be spread out over several frames or
  515. * block until completion once all glTF files are loaded.
  516. *
  517. * @memberof Model.prototype
  518. *
  519. * @type {Boolean}
  520. * @readonly
  521. *
  522. * @default true
  523. */
  524. asynchronous : {
  525. get : function() {
  526. return this._asynchronous;
  527. }
  528. },
  529. /**
  530. * When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  531. *
  532. * @memberof Model.prototype
  533. *
  534. * @type {Boolean}
  535. * @readonly
  536. *
  537. * @default true
  538. */
  539. allowPicking : {
  540. get : function() {
  541. return this._allowPicking;
  542. }
  543. }
  544. });
  545. /**
  546. * Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
  547. * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyToRender} event is fired.
  548. *
  549. * @param {Object} options Object with the following properties:
  550. * @param {String} options.url The url to the .gltf file.
  551. * @param {Object} [options.headers] HTTP headers to send with the request.
  552. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  553. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  554. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  555. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  556. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  557. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
  558. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each {@link DrawCommand} in the model.
  559. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  560. * @returns {Model} The newly created model.
  561. *
  562. * @see Model#readyToRender
  563. *
  564. * @example
  565. * // Example 1. Create a model from a glTF asset
  566. * var model = scene.primitives.add(Cesium.Model.fromGltf({
  567. * url : './duck/duck.json'
  568. * }));
  569. *
  570. * @example
  571. * // Example 2. Create model and provide all properties and events
  572. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  573. * var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  574. *
  575. * var model = scene.primitives.add(Model.fromGltf({
  576. * url : './duck/duck.json',
  577. * show : true, // default
  578. * modelMatrix : modelMatrix,
  579. * scale : 2.0, // double size
  580. * minimumPixelSize : 128, // never smaller than 128 pixels
  581. * allowPicking : false, // not pickable
  582. * debugShowBoundingVolume : false, // default
  583. * debugWireframe : false
  584. * }));
  585. *
  586. * model.readyToRender.addEventListener(function(model) {
  587. * // Play all animations when the model is ready to render
  588. * model.activeAnimations.addAll();
  589. * });
  590. */
  591. Model.fromGltf = function(options) {
  592. //>>includeStart('debug', pragmas.debug);
  593. if (!defined(options) || !defined(options.url)) {
  594. throw new DeveloperError('options.url is required');
  595. }
  596. //>>includeEnd('debug');
  597. var url = options.url;
  598. var basePath = '';
  599. var i = url.lastIndexOf('/');
  600. if (i !== -1) {
  601. basePath = url.substring(0, i + 1);
  602. }
  603. var cacheKey = options.cacheKey;
  604. if (!defined(cacheKey)) {
  605. // Use absolute URL, since two URLs with different relative paths could point to the same model.
  606. var docUri = new Uri(document.location.href);
  607. var modelUri = new Uri(url);
  608. cacheKey = modelUri.resolve(docUri).toString();
  609. }
  610. options = clone(options);
  611. options.basePath = basePath;
  612. options.cacheKey = cacheKey;
  613. var model = new Model(options);
  614. var cachedGltf = gltfCache[cacheKey];
  615. if (!defined(cachedGltf)) {
  616. cachedGltf = new CachedGltf({
  617. ready : false
  618. });
  619. cachedGltf.count = 1;
  620. cachedGltf.modelsToLoad.push(model);
  621. setCachedGltf(model, cachedGltf);
  622. gltfCache[cacheKey] = cachedGltf;
  623. loadText(url, options.headers).then(function(data) {
  624. cachedGltf.gltf = JSON.parse(data);
  625. var models = cachedGltf.modelsToLoad;
  626. var length = models.length;
  627. for (var i = 0; i < length; ++i) {
  628. var m = models[i];
  629. if (!m.isDestroyed()) {
  630. setCachedGltf(m, cachedGltf);
  631. }
  632. }
  633. cachedGltf.modelsToLoad = undefined;
  634. cachedGltf.ready = true;
  635. }).otherwise(getFailedLoadFunction(model, 'gltf', url));
  636. } else if (!cachedGltf.ready) {
  637. // Cache hit but the loadText() request is still pending
  638. ++cachedGltf.count;
  639. cachedGltf.modelsToLoad.push(model);
  640. }
  641. // else if the cached glTF is defined and ready, the
  642. // model constructor will pick it up using the cache key.
  643. return model;
  644. };
  645. /**
  646. * For the unit tests to verify model caching.
  647. *
  648. * @private
  649. */
  650. Model._gltfCache = gltfCache;
  651. function getRuntime(model, runtimeName, name) {
  652. //>>includeStart('debug', pragmas.debug);
  653. if (model._state !== ModelState.LOADED) {
  654. throw new DeveloperError('The model is not loaded. Wait for the model\'s readyToRender event or ready property.');
  655. }
  656. if (!defined(name)) {
  657. throw new DeveloperError('name is required.');
  658. }
  659. //>>includeEnd('debug');
  660. return (model._runtime[runtimeName])[name];
  661. }
  662. /**
  663. * Returns the glTF node with the given <code>name</code> property. This is used to
  664. * modify a node's transform for animation outside of glTF animations.
  665. *
  666. * @param {String} name The glTF name of the node.
  667. * @returns {ModelNode} The node or <code>undefined</code> if no node with <code>name</code> exists.
  668. *
  669. * @exception {DeveloperError} The model is not loaded. Wait for the model's readyToRender event or ready property.
  670. *
  671. * @example
  672. * // Apply non-uniform scale to node LOD3sp
  673. * var node = model.getNode('LOD3sp');
  674. * node.matrix =Cesium. Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix);
  675. */
  676. Model.prototype.getNode = function(name) {
  677. var node = getRuntime(this, 'nodesByName', name);
  678. return defined(node) ? node.publicNode : undefined;
  679. };
  680. /**
  681. * Returns the glTF mesh with the given <code>name</code> property.
  682. *
  683. * @param {String} name The glTF name of the mesh.
  684. *
  685. * @returns {ModelMesh} The mesh or <code>undefined</code> if no mesh with <code>name</code> exists.
  686. *
  687. * @exception {DeveloperError} The model is not loaded. Wait for the model's readyToRender event or ready property.
  688. */
  689. Model.prototype.getMesh = function(name) {
  690. return getRuntime(this, 'meshesByName', name);
  691. };
  692. /**
  693. * Returns the glTF material with the given <code>name</code> property.
  694. *
  695. * @param {String} name The glTF name of the material.
  696. * @returns {ModelMaterial} The material or <code>undefined</code> if no material with <code>name</code> exists.
  697. *
  698. * @exception {DeveloperError} The model is not loaded. Wait for the model's readyToRender event or ready property.
  699. */
  700. Model.prototype.getMaterial = function(name) {
  701. return getRuntime(this, 'materialsByName', name);
  702. };
  703. var aMinScratch = new Cartesian3();
  704. var aMaxScratch = new Cartesian3();
  705. function computeBoundingSphere(gltf) {
  706. var gltfNodes = gltf.nodes;
  707. var gltfMeshes = gltf.meshes;
  708. var gltfAccessors = gltf.accessors;
  709. var rootNodes = gltf.scenes[gltf.scene].nodes;
  710. var rootNodesLength = rootNodes.length;
  711. var nodeStack = [];
  712. var min = new Cartesian3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  713. var max = new Cartesian3(Number.MIN_VALUE, Number.MIN_VALUE, Number.MIN_VALUE);
  714. for (var i = 0; i < rootNodesLength; ++i) {
  715. var n = gltfNodes[rootNodes[i]];
  716. n._transformToRoot = getTransform(n);
  717. nodeStack.push(n);
  718. while (nodeStack.length > 0) {
  719. n = nodeStack.pop();
  720. var transformToRoot = n._transformToRoot;
  721. var meshes = defaultValue(n.meshes, defined(n.instanceSkin) ? n.instanceSkin.meshes : undefined);
  722. if (defined(meshes)) {
  723. var meshesLength = meshes.length;
  724. for (var j = 0; j < meshesLength; ++j) {
  725. var primitives = gltfMeshes[meshes[j]].primitives;
  726. var primitivesLength = primitives.length;
  727. for (var m = 0; m < primitivesLength; ++m) {
  728. var position = primitives[m].attributes.POSITION;
  729. if (defined(position)) {
  730. var accessor = gltfAccessors[position];
  731. var aMin = Cartesian3.fromArray(accessor.min, 0, aMinScratch);
  732. var aMax = Cartesian3.fromArray(accessor.max, 0, aMaxScratch);
  733. if (defined(min) && defined(max)) {
  734. Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
  735. Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
  736. Cartesian3.minimumByComponent(min, aMin, min);
  737. Cartesian3.maximumByComponent(max, aMax, max);
  738. }
  739. }
  740. }
  741. }
  742. }
  743. var children = n.children;
  744. var childrenLength = children.length;
  745. for (var k = 0; k < childrenLength; ++k) {
  746. var child = gltfNodes[children[k]];
  747. child._transformToRoot = getTransform(child);
  748. Matrix4.multiplyTransformation(transformToRoot, child._transformToRoot, child._transformToRoot);
  749. nodeStack.push(child);
  750. }
  751. delete n._transformToRoot;
  752. }
  753. }
  754. var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
  755. return BoundingSphere.transformWithoutScale(boundingSphere, yUpToZUp, boundingSphere);
  756. }
  757. ///////////////////////////////////////////////////////////////////////////
  758. function getFailedLoadFunction(model, type, path) {
  759. return function() {
  760. model._loadError = new RuntimeError('Failed to load external ' + type + ': ' + path);
  761. model._state = ModelState.FAILED;
  762. };
  763. }
  764. function bufferLoad(model, name) {
  765. return function(arrayBuffer) {
  766. var loadResources = model._loadResources;
  767. loadResources.buffers[name] = arrayBuffer;
  768. --loadResources.pendingBufferLoads;
  769. };
  770. }
  771. function parseBuffers(model) {
  772. var buffers = model.gltf.buffers;
  773. for (var name in buffers) {
  774. if (buffers.hasOwnProperty(name)) {
  775. ++model._loadResources.pendingBufferLoads;
  776. var buffer = buffers[name];
  777. var uri = new Uri(buffer.uri);
  778. var bufferPath = uri.resolve(model._baseUri).toString();
  779. loadArrayBuffer(bufferPath).then(bufferLoad(model, name)).otherwise(getFailedLoadFunction(model, 'buffer', bufferPath));
  780. }
  781. }
  782. }
  783. function parseBufferViews(model) {
  784. var bufferViews = model.gltf.bufferViews;
  785. for (var name in bufferViews) {
  786. if (bufferViews.hasOwnProperty(name)) {
  787. model._loadResources.buffersToCreate.enqueue(name);
  788. }
  789. }
  790. }
  791. function shaderLoad(model, name) {
  792. return function(source) {
  793. var loadResources = model._loadResources;
  794. loadResources.shaders[name] = source;
  795. --loadResources.pendingShaderLoads;
  796. };
  797. }
  798. function parseShaders(model) {
  799. var shaders = model.gltf.shaders;
  800. for (var name in shaders) {
  801. if (shaders.hasOwnProperty(name)) {
  802. ++model._loadResources.pendingShaderLoads;
  803. var shader = shaders[name];
  804. var uri = new Uri(shader.uri);
  805. var shaderPath = uri.resolve(model._baseUri).toString();
  806. loadText(shaderPath).then(shaderLoad(model, name)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath));
  807. }
  808. }
  809. }
  810. function parsePrograms(model) {
  811. var programs = model.gltf.programs;
  812. for (var name in programs) {
  813. if (programs.hasOwnProperty(name)) {
  814. model._loadResources.programsToCreate.enqueue(name);
  815. }
  816. }
  817. }
  818. function imageLoad(model, name) {
  819. return function(image) {
  820. var loadResources = model._loadResources;
  821. --loadResources.pendingTextureLoads;
  822. loadResources.texturesToCreate.enqueue({
  823. name : name,
  824. image : image
  825. });
  826. };
  827. }
  828. function parseTextures(model) {
  829. var images = model.gltf.images;
  830. var textures = model.gltf.textures;
  831. for (var name in textures) {
  832. if (textures.hasOwnProperty(name)) {
  833. ++model._loadResources.pendingTextureLoads;
  834. var texture = textures[name];
  835. var uri = new Uri(images[texture.source].uri);
  836. var imagePath = uri.resolve(model._baseUri).toString();
  837. loadImage(imagePath).then(imageLoad(model, name)).otherwise(getFailedLoadFunction(model, 'image', imagePath));
  838. }
  839. }
  840. }
  841. var nodeAxisScratch = new Cartesian3();
  842. var nodeTranslationScratch = new Cartesian3();
  843. var nodeQuaternionScratch = new Quaternion();
  844. var nodeScaleScratch = new Cartesian3();
  845. function getTransform(node) {
  846. if (defined(node.matrix)) {
  847. return Matrix4.fromArray(node.matrix);
  848. }
  849. var axis = Cartesian3.fromArray(node.rotation, 0, nodeAxisScratch);
  850. return Matrix4.fromTranslationQuaternionRotationScale(
  851. Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch),
  852. Quaternion.fromAxisAngle(axis, node.rotation[3], nodeQuaternionScratch),
  853. Cartesian3.fromArray(node.scale, 0 , nodeScaleScratch));
  854. }
  855. function parseNodes(model) {
  856. var runtimeNodes = {};
  857. var runtimeNodesByName = {};
  858. var skinnedNodes = [];
  859. var skinnedNodesNames = model._loadResources.skinnedNodesNames;
  860. var nodes = model.gltf.nodes;
  861. for (var name in nodes) {
  862. if (nodes.hasOwnProperty(name)) {
  863. var node = nodes[name];
  864. var runtimeNode = {
  865. // Animation targets
  866. matrix : undefined,
  867. translation : undefined,
  868. rotation : undefined,
  869. scale : undefined,
  870. // Per-node show inherited from parent
  871. computedShow : true,
  872. // Computed transforms
  873. transformToRoot : new Matrix4(),
  874. computedMatrix : new Matrix4(),
  875. dirtyNumber : 0, // The frame this node was made dirty by an animation; for graph traversal
  876. // Rendering
  877. commands : [], // empty for transform, light, and camera nodes
  878. // Skinned node
  879. inverseBindMatrices : undefined, // undefined when node is not skinned
  880. bindShapeMatrix : undefined, // undefined when node is not skinned or identity
  881. joints : [], // empty when node is not skinned
  882. computedJointMatrices : [], // empty when node is not skinned
  883. // Joint node
  884. jointName : node.jointName, // undefined when node is not a joint
  885. // Graph pointers
  886. children : [], // empty for leaf nodes
  887. parents : [], // empty for root nodes
  888. // Publicly-accessible ModelNode instance to modify animation targets
  889. publicNode : undefined
  890. };
  891. runtimeNode.publicNode = new ModelNode(model, node, runtimeNode, name, getTransform(node));
  892. runtimeNodes[name] = runtimeNode;
  893. runtimeNodesByName[node.name] = runtimeNode;
  894. if (defined(node.instanceSkin)) {
  895. skinnedNodesNames.push(name);
  896. skinnedNodes.push(runtimeNode);
  897. }
  898. }
  899. }
  900. model._runtime.nodes = runtimeNodes;
  901. model._runtime.nodesByName = runtimeNodesByName;
  902. model._runtime.skinnedNodes = skinnedNodes;
  903. }
  904. function parseMaterials(model) {
  905. var runtimeMaterials = {};
  906. var runtimeMaterialsById = {};
  907. var materials = model.gltf.materials;
  908. var uniformMaps = model._uniformMaps;
  909. for (var name in materials) {
  910. if (materials.hasOwnProperty(name)) {
  911. // Allocated now so ModelMaterial can keep a reference to it.
  912. uniformMaps[name] = {
  913. uniformMap : undefined,
  914. values : undefined,
  915. jointMatrixUniformName : undefined
  916. };
  917. var material = materials[name];
  918. var modelMaterial = new ModelMaterial(model, material, name);
  919. runtimeMaterials[material.name] = modelMaterial;
  920. runtimeMaterialsById[name] = modelMaterial;
  921. }
  922. }
  923. model._runtime.materialsByName = runtimeMaterials;
  924. model._runtime.materialsById = runtimeMaterialsById;
  925. }
  926. function parseMeshes(model) {
  927. var runtimeMeshes = {};
  928. var runtimeMaterialsById = model._runtime.materialsById;
  929. var meshes = model.gltf.meshes;
  930. for (var name in meshes) {
  931. if (meshes.hasOwnProperty(name)) {
  932. var mesh = meshes[name];
  933. runtimeMeshes[mesh.name] = new ModelMesh(mesh, runtimeMaterialsById, name);
  934. }
  935. }
  936. model._runtime.meshesByName = runtimeMeshes;
  937. }
  938. function parse(model) {
  939. if (!model._loadRendererResourcesFromCache) {
  940. parseBuffers(model);
  941. parseBufferViews(model);
  942. parseShaders(model);
  943. parsePrograms(model);
  944. parseTextures(model);
  945. }
  946. parseMaterials(model);
  947. parseMeshes(model);
  948. parseNodes(model);
  949. }
  950. ///////////////////////////////////////////////////////////////////////////
  951. function createBuffers(model, context) {
  952. var loadResources = model._loadResources;
  953. if (loadResources.pendingBufferLoads !== 0) {
  954. return;
  955. }
  956. var raw;
  957. var bufferView;
  958. var bufferViews = model.gltf.bufferViews;
  959. var buffers = loadResources.buffers;
  960. var rendererBuffers = model._rendererResources.buffers;
  961. while (loadResources.buffersToCreate.length > 0) {
  962. var bufferViewName = loadResources.buffersToCreate.dequeue();
  963. bufferView = bufferViews[bufferViewName];
  964. if (bufferView.target === WebGLRenderingContext.ARRAY_BUFFER) {
  965. // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below.
  966. raw = new Uint8Array(buffers[bufferView.buffer], bufferView.byteOffset, bufferView.byteLength);
  967. var vertexBuffer = context.createVertexBuffer(raw, BufferUsage.STATIC_DRAW);
  968. vertexBuffer.vertexArrayDestroyable = false;
  969. rendererBuffers[bufferViewName] = vertexBuffer;
  970. }
  971. // bufferViews referencing animations are ignored here and handled in createRuntimeAnimations.
  972. // bufferViews referencing skins are ignored here and handled in createSkins.
  973. }
  974. // The Cesium Renderer requires knowing the datatype for an index buffer
  975. // at creation type, which is not part of the glTF bufferview so loop
  976. // through glTF accessors to create the bufferview's index buffer.
  977. var accessors = model.gltf.accessors;
  978. for (var name in accessors) {
  979. if (accessors.hasOwnProperty(name)) {
  980. var accessor = accessors[name];
  981. bufferView = bufferViews[accessor.bufferView];
  982. if ((bufferView.target === WebGLRenderingContext.ELEMENT_ARRAY_BUFFER) && !defined(rendererBuffers[accessor.bufferView])) {
  983. raw = new Uint8Array(buffers[bufferView.buffer], bufferView.byteOffset, bufferView.byteLength);
  984. var indexBuffer = context.createIndexBuffer(raw, BufferUsage.STATIC_DRAW, accessor.componentType);
  985. indexBuffer.vertexArrayDestroyable = false;
  986. rendererBuffers[accessor.bufferView] = indexBuffer;
  987. // In theory, several glTF accessors with different componentTypes could
  988. // point to the same glTF bufferView, which would break this.
  989. // In practice, it is unlikely as it will be UNSIGNED_SHORT.
  990. }
  991. }
  992. }
  993. }
  994. function createAttributeLocations(attributes) {
  995. var attributeLocations = {};
  996. var length = attributes.length;
  997. for (var i = 0; i < length; ++i) {
  998. attributeLocations[attributes[i]] = i;
  999. }
  1000. return attributeLocations;
  1001. }
  1002. function createProgram(name, model, context) {
  1003. var programs = model.gltf.programs;
  1004. var shaders = model._loadResources.shaders;
  1005. var program = programs[name];
  1006. var attributeLocations = createAttributeLocations(program.attributes);
  1007. var vs = shaders[program.vertexShader];
  1008. var fs = shaders[program.fragmentShader];
  1009. model._rendererResources.programs[name] = context.createShaderProgram(vs, fs, attributeLocations);
  1010. if (model.allowPicking) {
  1011. // PERFORMANCE_IDEA: Can optimize this shader with a glTF hint. https://github.com/KhronosGroup/glTF/issues/181
  1012. var pickFS = new ShaderSource({
  1013. sources : [fs],
  1014. pickColorQualifier : 'uniform'
  1015. });
  1016. model._rendererResources.pickPrograms[name] = context.createShaderProgram(vs, pickFS, attributeLocations);
  1017. }
  1018. }
  1019. function createPrograms(model, context) {
  1020. var loadResources = model._loadResources;
  1021. var name;
  1022. if (loadResources.pendingShaderLoads !== 0) {
  1023. return;
  1024. }
  1025. if (model.asynchronous) {
  1026. // Create one program per frame
  1027. if (loadResources.programsToCreate.length > 0) {
  1028. name = loadResources.programsToCreate.dequeue();
  1029. createProgram(name, model, context);
  1030. }
  1031. } else {
  1032. // Create all loaded programs this frame
  1033. while (loadResources.programsToCreate.length > 0) {
  1034. name = loadResources.programsToCreate.dequeue();
  1035. createProgram(name, model, context);
  1036. }
  1037. }
  1038. }
  1039. function createSamplers(model, context) {
  1040. var loadResources = model._loadResources;
  1041. if (loadResources.createSamplers) {
  1042. loadResources.createSamplers = false;
  1043. var rendererSamplers = model._rendererResources.samplers;
  1044. var samplers = model.gltf.samplers;
  1045. for (var name in samplers) {
  1046. if (samplers.hasOwnProperty(name)) {
  1047. var sampler = samplers[name];
  1048. rendererSamplers[name] = context.createSampler({
  1049. wrapS : sampler.wrapS,
  1050. wrapT : sampler.wrapT,
  1051. minificationFilter : sampler.minFilter,
  1052. magnificationFilter : sampler.magFilter
  1053. });
  1054. }
  1055. }
  1056. }
  1057. }
  1058. function createTexture(gltfTexture, model, context) {
  1059. var textures = model.gltf.textures;
  1060. var texture = textures[gltfTexture.name];
  1061. var rendererSamplers = model._rendererResources.samplers;
  1062. var sampler = rendererSamplers[texture.sampler];
  1063. var mipmap =
  1064. (sampler.minificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST) ||
  1065. (sampler.minificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR) ||
  1066. (sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST) ||
  1067. (sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);
  1068. var requiresNpot = mipmap ||
  1069. (sampler.wrapS === TextureWrap.REPEAT) ||
  1070. (sampler.wrapS === TextureWrap.MIRRORED_REPEAT) ||
  1071. (sampler.wrapT === TextureWrap.REPEAT) ||
  1072. (sampler.wrapT === TextureWrap.MIRRORED_REPEAT);
  1073. var source = gltfTexture.image;
  1074. var npot = !CesiumMath.isPowerOfTwo(source.width) || !CesiumMath.isPowerOfTwo(source.height);
  1075. if (requiresNpot && npot) {
  1076. // WebGL requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes.
  1077. var canvas = document.createElement('canvas');
  1078. canvas.width = CesiumMath.nextPowerOfTwo(source.width);
  1079. canvas.height = CesiumMath.nextPowerOfTwo(source.height);
  1080. var canvasContext = canvas.getContext('2d');
  1081. canvasContext.drawImage(source, 0, 0, source.width, source.height, 0, 0, canvas.width, canvas.height);
  1082. source = canvas;
  1083. }
  1084. var tx;
  1085. if (texture.target === WebGLRenderingContext.TEXTURE_2D) {
  1086. tx = context.createTexture2D({
  1087. source : source,
  1088. pixelFormat : texture.internalFormat,
  1089. pixelDatatype : texture.type,
  1090. flipY : false
  1091. });
  1092. }
  1093. // GLTF_SPEC: Support TEXTURE_CUBE_MAP. https://github.com/KhronosGroup/glTF/issues/40
  1094. if (mipmap) {
  1095. tx.generateMipmap();
  1096. }
  1097. tx.sampler = sampler;
  1098. model._rendererResources.textures[gltfTexture.name] = tx;
  1099. }
  1100. function createTextures(model, context) {
  1101. var loadResources = model._loadResources;
  1102. var gltfTexture;
  1103. if (model.asynchronous) {
  1104. // Create one texture per frame
  1105. if (loadResources.texturesToCreate.length > 0) {
  1106. gltfTexture = loadResources.texturesToCreate.dequeue();
  1107. createTexture(gltfTexture, model, context);
  1108. }
  1109. } else {
  1110. // Create all loaded textures this frame
  1111. while (loadResources.texturesToCreate.length > 0) {
  1112. gltfTexture = loadResources.texturesToCreate.dequeue();
  1113. createTexture(gltfTexture, model, context);
  1114. }
  1115. }
  1116. }
  1117. function getAttributeLocations(model, primitive) {
  1118. var gltf = model.gltf;
  1119. var programs = gltf.programs;
  1120. var techniques = gltf.techniques;
  1121. var materials = gltf.materials;
  1122. // Retrieve the compiled shader program to assign index values to attributes
  1123. var attributeLocations = {};
  1124. var technique = techniques[materials[primitive.material].instanceTechnique.technique];
  1125. var parameters = technique.parameters;
  1126. var pass = technique.passes[technique.pass];
  1127. var instanceProgram = pass.instanceProgram;
  1128. var attributes = instanceProgram.attributes;
  1129. var programAttributeLocations = model._rendererResources.programs[instanceProgram.program].vertexAttributes;
  1130. // Note: WebGL shader compiler may have optimized and removed some attributes from programAttributeLocations
  1131. for (var location in programAttributeLocations){
  1132. if (programAttributeLocations.hasOwnProperty(location)) {
  1133. var parameter = parameters[attributes[location]];
  1134. attributeLocations[parameter.semantic] = programAttributeLocations[location].index;
  1135. }
  1136. }
  1137. return attributeLocations;
  1138. }
  1139. function searchForest(forest, jointName) {
  1140. var length = forest.length;
  1141. for (var i = 0; i < length; ++i) {
  1142. var stack = [forest[i]]; // Push root node of tree
  1143. while (stack.length > 0) {
  1144. var n = stack.pop();
  1145. if (n.jointName === jointName) {
  1146. return n;
  1147. }
  1148. var children = n.children;
  1149. var childrenLength = children.length;
  1150. for (var k = 0; k < childrenLength; ++k) {
  1151. stack.push(children[k]);
  1152. }
  1153. }
  1154. }
  1155. // This should never happen; the skeleton should have a node for all joints in the skin.
  1156. return undefined;
  1157. }
  1158. function createJoints(model, runtimeSkins) {
  1159. var gltf = model.gltf;
  1160. var skins = gltf.skins;
  1161. var nodes = gltf.nodes;
  1162. var runtimeNodes = model._runtime.nodes;
  1163. var skinnedNodesNames = model._loadResources.skinnedNodesNames;
  1164. var length = skinnedNodesNames.length;
  1165. for (var j = 0; j < length; ++j) {
  1166. var name = skinnedNodesNames[j];
  1167. var skinnedNode = runtimeNodes[name];
  1168. var instanceSkin = nodes[name].instanceSkin;
  1169. var runtimeSkin = runtimeSkins[instanceSkin.skin];
  1170. skinnedNode.inverseBindMatrices = runtimeSkin.inverseBindMatrices;
  1171. skinnedNode.bindShapeMatrix = runtimeSkin.bindShapeMatrix;
  1172. // 1. Find nodes with the names in instanceSkin.skeletons (the node's skeletons)
  1173. // 2. These nodes form the root nodes of the forest to search for each joint in skin.jointNames. This search uses jointName, not the node's name.
  1174. var forest = [];
  1175. var gltfSkeletons = instanceSkin.skeletons;
  1176. var skeletonsLength = gltfSkeletons.length;
  1177. for (var k = 0; k < skeletonsLength; ++k) {
  1178. forest.push(runtimeNodes[gltfSkeletons[k]]);
  1179. }
  1180. var gltfJointNames = skins[instanceSkin.skin].jointNames;
  1181. var jointNamesLength = gltfJointNames.length;
  1182. for (var i = 0; i < jointNamesLength; ++i) {
  1183. var jointName = gltfJointNames[i];
  1184. skinnedNode.joints.push(searchForest(forest, jointName));
  1185. }
  1186. }
  1187. }
  1188. function createSkins(model) {
  1189. var loadResources = model._loadResources;
  1190. if (!loadResources.finishedBuffersCreation()) {
  1191. return;
  1192. }
  1193. if (!loadResources.createSkins) {
  1194. return;
  1195. }
  1196. loadResources.createSkins = false;
  1197. var gltf = model.gltf;
  1198. var accessors = gltf.accessors;
  1199. var skins = gltf.skins;
  1200. var runtimeSkins = {};
  1201. for (var name in skins) {
  1202. if (skins.hasOwnProperty(name)) {
  1203. var skin = skins[name];
  1204. var accessor = accessors[skin.inverseBindMatrices];
  1205. var bindShapeMatrix;
  1206. if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {
  1207. bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);
  1208. }
  1209. runtimeSkins[name] = {
  1210. inverseBindMatrices : ModelAnimationCache.getSkinInverseBindMatrices(model, accessor),
  1211. bindShapeMatrix : bindShapeMatrix // not used when undefined
  1212. };
  1213. }
  1214. }
  1215. createJoints(model, runtimeSkins);
  1216. }
  1217. function getChannelEvaluator(model, runtimeNode, targetPath, spline) {
  1218. return function(localAnimationTime) {
  1219. // Workaround for https://github.com/KhronosGroup/glTF/issues/219
  1220. /*
  1221. if (targetPath === 'translation') {
  1222. return;
  1223. }
  1224. */
  1225. runtimeNode[targetPath] = spline.evaluate(localAnimationTime, runtimeNode[targetPath]);
  1226. runtimeNode.dirtyNumber = model._maxDirtyNumber;
  1227. };
  1228. }
  1229. function createRuntimeAnimations(model) {
  1230. var loadResources = model._loadResources;
  1231. if (!loadResources.finishedPendingLoads()) {
  1232. return;
  1233. }
  1234. if (!loadResources.createRuntimeAnimations) {
  1235. return;
  1236. }
  1237. loadResources.createRuntimeAnimations = false;
  1238. model._runtime.animations = {
  1239. };
  1240. var runtimeNodes = model._runtime.nodes;
  1241. var animations = model.gltf.animations;
  1242. var accessors = model.gltf.accessors;
  1243. var name;
  1244. for (var animationName in animations) {
  1245. if (animations.hasOwnProperty(animationName)) {
  1246. var animation = animations[animationName];
  1247. var channels = animation.channels;
  1248. var parameters = animation.parameters;
  1249. var samplers = animation.samplers;
  1250. var parameterValues = {};
  1251. for (name in parameters) {
  1252. if (parameters.hasOwnProperty(name)) {
  1253. parameterValues[name] = ModelAnimationCache.getAnimationParameterValues(model, accessors[parameters[name]]);
  1254. }
  1255. }
  1256. // Find start and stop time for the entire animation
  1257. var startTime = Number.MAX_VALUE;
  1258. var stopTime = -Number.MAX_VALUE;
  1259. var length = channels.length;
  1260. var channelEvaluators = new Array(length);
  1261. for (var i = 0; i < length; ++i) {
  1262. var channel = channels[i];
  1263. var target = channel.target;
  1264. var sampler = samplers[channel.sampler];
  1265. var times = parameterValues[sampler.input];
  1266. startTime = Math.min(startTime, times[0]);
  1267. stopTime = Math.max(stopTime, times[times.length - 1]);
  1268. var spline = ModelAnimationCache.getAnimationSpline(model, animationName, animation, channel.sampler, sampler, parameterValues);
  1269. // GLTF_SPEC: Support more targets like materials. https://github.com/KhronosGroup/glTF/issues/142
  1270. channelEvaluators[i] = getChannelEvaluator(model, runtimeNodes[target.id], target.path, spline);
  1271. }
  1272. model._runtime.animations[animationName] = {
  1273. startTime : startTime,
  1274. stopTime : stopTime,
  1275. channelEvaluators : channelEvaluators
  1276. };
  1277. }
  1278. }
  1279. }
  1280. function createVertexArrays(model, context) {
  1281. var loadResources = model._loadResources;
  1282. if (!loadResources.finishedBuffersCreation() || !loadResources.finishedProgramCreation()) {
  1283. return;
  1284. }
  1285. if (!loadResources.createVertexArrays) {
  1286. return;
  1287. }
  1288. loadResources.createVertexArrays = false;
  1289. var rendererBuffers = model._rendererResources.buffers;
  1290. var rendererVertexArrays = model._rendererResources.vertexArrays;
  1291. var gltf = model.gltf;
  1292. var accessors = gltf.accessors;
  1293. var meshes = gltf.meshes;
  1294. for (var meshName in meshes) {
  1295. if (meshes.hasOwnProperty(meshName)) {
  1296. var primitives = meshes[meshName].primitives;
  1297. var primitivesLength = primitives.length;
  1298. for (var i = 0; i < primitivesLength; ++i) {
  1299. var primitive = primitives[i];
  1300. // GLTF_SPEC: This does not take into account attribute arrays,
  1301. // indicated by when an attribute points to a parameter with a
  1302. // count property.
  1303. //
  1304. // https://github.com/KhronosGroup/glTF/issues/258
  1305. var attributeLocations = getAttributeLocations(model, primitive);
  1306. var attrs = [];
  1307. var primitiveAttributes = primitive.attributes;
  1308. for (var attrName in primitiveAttributes) {
  1309. if (primitiveAttributes.hasOwnProperty(attrName)) {
  1310. var attributeLocation = attributeLocations[attrName];
  1311. // Skip if the attribute is not used by the material, e.g., because the asset was exported
  1312. // with an attribute that wasn't used and the asset wasn't optimized.
  1313. if (defined(attributeLocation)) {
  1314. var a = accessors[primitiveAttributes[attrName]];
  1315. attrs.push({
  1316. index : attributeLocation,
  1317. vertexBuffer : rendererBuffers[a.bufferView],
  1318. componentsPerAttribute : getModelAccessor(a).componentsPerAttribute,
  1319. componentDatatype : a.componentType,
  1320. normalize : false,
  1321. offsetInBytes : a.byteOffset,
  1322. strideInBytes : a.byteStride
  1323. });
  1324. }
  1325. }
  1326. }
  1327. var accessor = accessors[primitive.indices];
  1328. var indexBuffer = rendererBuffers[accessor.bufferView];
  1329. rendererVertexArrays[meshName + '.primitive.' + i] = context.createVertexArray(attrs, indexBuffer);
  1330. }
  1331. }
  1332. }
  1333. }
  1334. function getBooleanStates(states) {
  1335. // GLTF_SPEC: SAMPLE_ALPHA_TO_COVERAGE not used by Cesium
  1336. var booleanStates = {};
  1337. booleanStates[WebGLRenderingContext.BLEND] = false;
  1338. booleanStates[WebGLRenderingContext.CULL_FACE] = false;
  1339. booleanStates[WebGLRenderingContext.DEPTH_TEST] = false;
  1340. booleanStates[WebGLRenderingContext.POLYGON_OFFSET_FILL] = false;
  1341. booleanStates[WebGLRenderingContext.SCISSOR_TEST] = false;
  1342. var enable = states.enable;
  1343. var length = enable.length;
  1344. var i;
  1345. for (i = 0; i < length; ++i) {
  1346. booleanStates[enable[i]] = true;
  1347. }
  1348. return booleanStates;
  1349. }
  1350. function createRenderStates(model, context) {
  1351. var loadResources = model._loadResources;
  1352. if (loadResources.createRenderStates) {
  1353. loadResources.createRenderStates = false;
  1354. var rendererRenderStates = model._rendererResources.renderStates;
  1355. var techniques = model.gltf.techniques;
  1356. for (var name in techniques) {
  1357. if (techniques.hasOwnProperty(name)) {
  1358. var technique = techniques[name];
  1359. var pass = technique.passes[technique.pass];
  1360. var states = pass.states;
  1361. var booleanStates = getBooleanStates(states);
  1362. var statesFunctions = defaultValue(states.functions, defaultValue.EMPTY_OBJECT);
  1363. var blendColor = defaultValue(statesFunctions.blendColor, [0.0, 0.0, 0.0, 0.0]);
  1364. var blendEquationSeparate = defaultValue(statesFunctions.blendEquationSeparate, [
  1365. WebGLRenderingContext.FUNC_ADD,
  1366. WebGLRenderingContext.FUNC_ADD]);
  1367. var blendFuncSeparate = defaultValue(statesFunctions.blendFuncSeparate, [
  1368. WebGLRenderingContext.ONE,
  1369. WebGLRenderingContext.ONE,
  1370. WebGLRenderingContext.ZERO,
  1371. WebGLRenderingContext.ZERO]);
  1372. var colorMask = defaultValue(statesFunctions.colorMask, [true, true, true, true]);
  1373. var depthRange = defaultValue(statesFunctions.depthRange, [0.0, 1.0]);
  1374. var polygonOffset = defaultValue(statesFunctions.polygonOffset, [0.0, 0.0]);
  1375. var scissor = defaultValue(statesFunctions.scissor, [0.0, 0.0, 0.0, 0.0]);
  1376. rendererRenderStates[name] = context.createRenderState({
  1377. frontFace : defined(statesFunctions.frontFace) ? statesFunctions.frontFace[0] : WebGLRenderingContext.CCW,
  1378. cull : {
  1379. enabled : booleanStates[WebGLRenderingContext.CULL_FACE],
  1380. face : defined(statesFunctions.cullFace) ? statesFunctions.cullFace[0] : WebGLRenderingContext.BACK
  1381. },
  1382. lineWidth : defined(statesFunctions.lineWidth) ? statesFunctions.lineWidth[0] : 1.0,
  1383. polygonOffset : {
  1384. enabled : booleanStates[WebGLRenderingContext.POLYGON_OFFSET_FILL],
  1385. factor : polygonOffset[0],
  1386. units : polygonOffset[1]
  1387. },
  1388. scissorTest : {
  1389. enabled : booleanStates[WebGLRenderingContext.SCISSOR_TEST],
  1390. rectangle : {
  1391. x : scissor[0],
  1392. y : scissor[1],
  1393. width : scissor[2],
  1394. height : scissor[3]
  1395. }
  1396. },
  1397. depthRange : {
  1398. near : depthRange[0],
  1399. far : depthRange[1]
  1400. },
  1401. depthTest : {
  1402. enabled : booleanStates[WebGLRenderingContext.DEPTH_TEST],
  1403. func : defined(statesFunctions.depthFunc) ? statesFunctions.depthFunc[0] : WebGLRenderingContext.LESS
  1404. },
  1405. colorMask : {
  1406. red : colorMask[0],
  1407. green : colorMask[1],
  1408. blue : colorMask[2],
  1409. alpha : colorMask[3]
  1410. },
  1411. depthMask : defined(statesFunctions.depthMask) ? statesFunctions.depthMask[0] : true,
  1412. blending : {
  1413. enabled : booleanStates[WebGLRenderingContext.BLEND],
  1414. color : {
  1415. red : blendColor[0],
  1416. green : blendColor[1],
  1417. blue : blendColor[2],
  1418. alpha : blendColor[3]
  1419. },
  1420. equationRgb : blendEquationSeparate[0],
  1421. equationAlpha : blendEquationSeparate[1],
  1422. functionSourceRgb : blendFuncSeparate[0],
  1423. functionSourceAlpha : blendFuncSeparate[1],
  1424. functionDestinationRgb : blendFuncSeparate[2],
  1425. functionDestinationAlpha : blendFuncSeparate[3]
  1426. }
  1427. });
  1428. }
  1429. }
  1430. }
  1431. }
  1432. // This doesn't support LOCAL, which we could add if it is ever used.
  1433. var gltfSemanticUniforms = {
  1434. MODEL : function(uniformState) {
  1435. return function() {
  1436. return uniformState.model;
  1437. };
  1438. },
  1439. VIEW : function(uniformState) {
  1440. return function() {
  1441. return uniformState.view;
  1442. };
  1443. },
  1444. PROJECTION : function(uniformState) {
  1445. return function() {
  1446. return uniformState.projection;
  1447. };
  1448. },
  1449. MODELVIEW : function(uniformState) {
  1450. return function() {
  1451. return uniformState.modelView;
  1452. };
  1453. },
  1454. MODELVIEWPROJECTION : function(uniformState) {
  1455. return function() {
  1456. return uniformState.modelViewProjection;
  1457. };
  1458. },
  1459. MODELINVERSE : function(uniformState) {
  1460. return function() {
  1461. return uniformState.inverseModel;
  1462. };
  1463. },
  1464. VIEWINVERSE : function(uniformState) {
  1465. return function() {
  1466. return uniformState.inverseView;
  1467. };
  1468. },
  1469. PROJECTIONINVERSE : function(uniformState) {
  1470. return function() {
  1471. return uniformState.inverseProjection;
  1472. };
  1473. },
  1474. MODELVIEWINVERSE : function(uniformState) {
  1475. return function() {
  1476. return uniformState.inverseModelView;
  1477. };
  1478. },
  1479. MODELVIEWPROJECTIONINVERSE : function(uniformState) {
  1480. return function() {
  1481. return uniformState.inverseModelViewProjection;
  1482. };
  1483. },
  1484. MODELINVERSETRANSPOSE : function(uniformState) {
  1485. return function() {
  1486. return uniformState.inverseTranposeModel;
  1487. };
  1488. },
  1489. MODELVIEWINVERSETRANSPOSE : function(uniformState) {
  1490. return function() {
  1491. return uniformState.normal;
  1492. };
  1493. },
  1494. VIEWPORT : function(uniformState) {
  1495. return function() {
  1496. return uniformState.viewportCartesian4;
  1497. };
  1498. }
  1499. // JOINTMATRIX created in createCommands()
  1500. };
  1501. ///////////////////////////////////////////////////////////////////////////
  1502. function getScalarUniformFunction(value, model) {
  1503. var that = {
  1504. value : value,
  1505. clone : function(source, result) {
  1506. return source;
  1507. },
  1508. func : function() {
  1509. return that.value;
  1510. }
  1511. };
  1512. return that;
  1513. }
  1514. function getVec2UniformFunction(value, model) {
  1515. var that = {
  1516. value : Cartesian2.fromArray(value),
  1517. clone : Cartesian2.clone,
  1518. func : function() {
  1519. return that.value;
  1520. }
  1521. };
  1522. return that;
  1523. }
  1524. function getVec3UniformFunction(value, model) {
  1525. var that = {
  1526. value : Cartesian3.fromArray(value),
  1527. clone : Cartesian3.clone,
  1528. func : function() {
  1529. return that.value;
  1530. }
  1531. };
  1532. return that;
  1533. }
  1534. function getVec4UniformFunction(value, model) {
  1535. var that = {
  1536. value : Cartesian4.fromArray(value),
  1537. clone : Cartesian4.clone,
  1538. func : function() {
  1539. return that.value;
  1540. }
  1541. };
  1542. return that;
  1543. }
  1544. function getMat2UniformFunction(value, model) {
  1545. var that = {
  1546. value : Matrix2.fromColumnMajorArray(value),
  1547. clone : Matrix2.clone,
  1548. func : function() {
  1549. return that.value;
  1550. }
  1551. };
  1552. return that;
  1553. }
  1554. function getMat3UniformFunction(value, model) {
  1555. var that = {
  1556. value : Matrix3.fromColumnMajorArray(value),
  1557. clone : Matrix3.clone,
  1558. func : function() {
  1559. return that.value;
  1560. }
  1561. };
  1562. return that;
  1563. }
  1564. function getMat4UniformFunction(value, model) {
  1565. var that = {
  1566. value : Matrix4.fromColumnMajorArray(value),
  1567. clone : Matrix4.clone,
  1568. func : function() {
  1569. return that.value;
  1570. }
  1571. };
  1572. return that;
  1573. }
  1574. function getTextureUniformFunction(value, model) {
  1575. var that = {
  1576. value : model._rendererResources.textures[value],
  1577. clone : function(source, result) {
  1578. return source;
  1579. },
  1580. func : function() {
  1581. return that.value;
  1582. }
  1583. };
  1584. return that;
  1585. }
  1586. var gltfUniformFunctions = {};
  1587. // this check must use typeof, not defined, because defined doesn't work with undeclared variables.
  1588. if (typeof WebGLRenderingContext !== 'undefined') {
  1589. gltfUniformFunctions[WebGLRenderingContext.FLOAT] = getScalarUniformFunction;
  1590. gltfUniformFunctions[WebGLRenderingContext.FLOAT_VEC2] = getVec2UniformFunction;
  1591. gltfUniformFunctions[WebGLRenderingContext.FLOAT_VEC3] = getVec3UniformFunction;
  1592. gltfUniformFunctions[WebGLRenderingContext.FLOAT_VEC4] = getVec4UniformFunction;
  1593. gltfUniformFunctions[WebGLRenderingContext.INT] = getScalarUniformFunction;
  1594. gltfUniformFunctions[WebGLRenderingContext.INT_VEC2] = getVec2UniformFunction;
  1595. gltfUniformFunctions[WebGLRenderingContext.INT_VEC3] = getVec3UniformFunction;
  1596. gltfUniformFunctions[WebGLRenderingContext.INT_VEC4] = getVec4UniformFunction;
  1597. gltfUniformFunctions[WebGLRenderingContext.BOOL] = getScalarUniformFunction;
  1598. gltfUniformFunctions[WebGLRenderingContext.BOOL_VEC2] = getVec2UniformFunction;
  1599. gltfUniformFunctions[WebGLRenderingContext.BOOL_VEC3] = getVec3UniformFunction;
  1600. gltfUniformFunctions[WebGLRenderingContext.BOOL_VEC4] = getVec4UniformFunction;
  1601. gltfUniformFunctions[WebGLRenderingContext.FLOAT_MAT2] = getMat2UniformFunction;
  1602. gltfUniformFunctions[WebGLRenderingContext.FLOAT_MAT3] = getMat3UniformFunction;
  1603. gltfUniformFunctions[WebGLRenderingContext.FLOAT_MAT4] = getMat4UniformFunction;
  1604. gltfUniformFunctions[WebGLRenderingContext.SAMPLER_2D] = getTextureUniformFunction;
  1605. // GLTF_SPEC: Support SAMPLER_CUBE. https://github.com/KhronosGroup/glTF/issues/40
  1606. }
  1607. function getUniformFunctionFromSource(source, model) {
  1608. var runtimeNode = model._runtime.nodes[source];
  1609. return function() {
  1610. return runtimeNode.computedMatrix;
  1611. };
  1612. }
  1613. function createUniformMaps(model, context) {
  1614. var loadResources = model._loadResources;
  1615. if (!loadResources.finishedTextureCreation() || !loadResources.finishedProgramCreation()) {
  1616. return;
  1617. }
  1618. if (!loadResources.createUniformMaps) {
  1619. return;
  1620. }
  1621. loadResources.createUniformMaps = false;
  1622. var gltf = model.gltf;
  1623. var materials = gltf.materials;
  1624. var techniques = gltf.techniques;
  1625. var programs = gltf.programs;
  1626. var uniformMaps = model._uniformMaps;
  1627. for (var materialName in materials) {
  1628. if (materials.hasOwnProperty(materialName)) {
  1629. var material = materials[materialName];
  1630. var instanceTechnique = material.instanceTechnique;
  1631. var instanceParameters = instanceTechnique.values;
  1632. var technique = techniques[instanceTechnique.technique];
  1633. var parameters = technique.parameters;
  1634. var pass = technique.passes[technique.pass];
  1635. var instanceProgram = pass.instanceProgram;
  1636. var uniforms = instanceProgram.uniforms;
  1637. var uniformMap = {};
  1638. var uniformValues = {};
  1639. var jointMatrixUniformName;
  1640. // Uniform parameters for this pass
  1641. for (var name in uniforms) {
  1642. if (uniforms.hasOwnProperty(name)) {
  1643. var parameterName = uniforms[name];
  1644. var parameter = parameters[parameterName];
  1645. // GLTF_SPEC: This does not take into account uniform arrays,
  1646. // indicated by parameters with a count property.
  1647. //
  1648. // https://github.com/KhronosGroup/glTF/issues/258
  1649. // GLTF_SPEC: In this implementation, material parameters with a
  1650. // semantic or targeted via a source (for animation) are not
  1651. // targetable for material animations. Is this too strict?
  1652. //
  1653. // https://github.com/KhronosGroup/glTF/issues/142
  1654. if (defined(instanceParameters[parameterName])) {
  1655. // Parameter overrides by the instance technique
  1656. var uv = gltfUniformFunctions[parameter.type](instanceParameters[parameterName], model);
  1657. uniformMap[name] = uv.func;
  1658. uniformValues[parameterName] = uv;
  1659. } else if (defined(parameter.semantic)) {
  1660. if (parameter.semantic !== 'JOINTMATRIX') {
  1661. // Map glTF semantic to Cesium automatic uniform
  1662. uniformMap[name] = gltfSemanticUniforms[parameter.semantic](context.uniformState);
  1663. } else {
  1664. jointMatrixUniformName = name;
  1665. }
  1666. } else if (defined(parameter.source)) {
  1667. uniformMap[name] = getUniformFunctionFromSource(parameter.source, model);
  1668. } else if (defined(parameter.value)) {
  1669. // Technique value that isn't overridden by a material
  1670. var uv2 = gltfUniformFunctions[parameter.type](parameter.value, model);
  1671. uniformMap[name] = uv2.func;
  1672. uniformValues[parameterName] = uv2;
  1673. }
  1674. }
  1675. }
  1676. var u = uniformMaps[materialName];
  1677. u.uniformMap = uniformMap; // uniform name -> function for the renderer
  1678. u.values = uniformValues; // material parameter name -> ModelMaterial for modifying the parameter at runtime
  1679. u.jointMatrixUniformName = jointMatrixUniformName;
  1680. }
  1681. }
  1682. }
  1683. function createPickColorFunction(color) {
  1684. return function() {
  1685. return color;
  1686. };
  1687. }
  1688. function createJointMatricesFunction(runtimeNode) {
  1689. return function() {
  1690. return runtimeNode.computedJointMatrices;
  1691. };
  1692. }
  1693. function createCommand(model, gltfNode, runtimeNode, context) {
  1694. var nodeCommands = model._nodeCommands;
  1695. var pickIds = model._pickIds;
  1696. var allowPicking = model.allowPicking;
  1697. var runtimeMeshes = model._runtime.meshesByName;
  1698. var debugShowBoundingVolume = model.debugShowBoundingVolume;
  1699. var resources = model._rendererResources;
  1700. var rendererVertexArrays = resources.vertexArrays;
  1701. var rendererPrograms = resources.programs;
  1702. var rendererPickPrograms = resources.pickPrograms;
  1703. var rendererRenderStates = resources.renderStates;
  1704. var uniformMaps = model._uniformMaps;
  1705. var gltf = model.gltf;
  1706. var accessors = gltf.accessors;
  1707. var gltfMeshes = gltf.meshes;
  1708. var techniques = gltf.techniques;
  1709. var materials = gltf.materials;
  1710. var meshes = defined(gltfNode.meshes) ? gltfNode.meshes : gltfNode.instanceSkin.meshes;
  1711. var meshesLength = meshes.length;
  1712. for (var j = 0; j < meshesLength; ++j) {
  1713. var name = meshes[j];
  1714. var mesh = gltfMeshes[name];
  1715. var primitives = mesh.primitives;
  1716. var length = primitives.length;
  1717. // The glTF node hierarchy is a DAG so a node can have more than one
  1718. // parent, so a node may already have commands. If so, append more
  1719. // since they will have a different model matrix.
  1720. for (var i = 0; i < length; ++i) {
  1721. var primitive = primitives[i];
  1722. var ix = accessors[primitive.indices];
  1723. var instanceTechnique = materials[primitive.material].instanceTechnique;
  1724. var technique = techniques[instanceTechnique.technique];
  1725. var pass = technique.passes[technique.pass];
  1726. var instanceProgram = pass.instanceProgram;
  1727. var boundingSphere;
  1728. var positionAttribute = primitive.attributes.POSITION;
  1729. if (defined(positionAttribute)) {
  1730. var a = accessors[positionAttribute];
  1731. boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.fromArray(a.min), Cartesian3.fromArray(a.max));
  1732. }
  1733. var vertexArray = rendererVertexArrays[name + '.primitive.' + i];
  1734. var count = ix.count;
  1735. var offset = (ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType)); // glTF has offset in bytes. Cesium has offsets in indices
  1736. var um = uniformMaps[primitive.material];
  1737. var uniformMap = um.uniformMap;
  1738. if (defined(um.jointMatrixUniformName)) {
  1739. var jointUniformMap = {};
  1740. jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(runtimeNode);
  1741. uniformMap = combine(uniformMap, jointUniformMap);
  1742. }
  1743. var rs = rendererRenderStates[instanceTechnique.technique];
  1744. // GLTF_SPEC: Offical means to determine translucency. https://github.com/KhronosGroup/glTF/issues/105
  1745. var isTranslucent = rs.blending.enabled;
  1746. var owner = {
  1747. primitive : defaultValue(model.pickPrimitive, model),
  1748. id : model.id,
  1749. node : runtimeNode.publicNode,
  1750. mesh : runtimeMeshes[mesh.name]
  1751. };
  1752. var command = new DrawCommand({
  1753. boundingVolume : new BoundingSphere(), // updated in update()
  1754. modelMatrix : new Matrix4(), // computed in update()
  1755. primitiveType : primitive.primitive,
  1756. vertexArray : vertexArray,
  1757. count : count,
  1758. offset : offset,
  1759. shaderProgram : rendererPrograms[instanceProgram.program],
  1760. uniformMap : uniformMap,
  1761. renderState : rs,
  1762. owner : owner,
  1763. pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
  1764. });
  1765. var pickCommand;
  1766. if (allowPicking) {
  1767. var pickId = context.createPickId(owner);
  1768. pickIds.push(pickId);
  1769. var pickUniformMap = combine(
  1770. uniformMap, {
  1771. czm_pickColor : createPickColorFunction(pickId.color)
  1772. });
  1773. pickCommand = new DrawCommand({
  1774. boundingVolume : new BoundingSphere(), // updated in update()
  1775. modelMatrix : new Matrix4(), // computed in update()
  1776. primitiveType : primitive.primitive,
  1777. vertexArray : vertexArray,
  1778. count : count,
  1779. offset : offset,
  1780. shaderProgram : rendererPickPrograms[instanceProgram.program],
  1781. uniformMap : pickUniformMap,
  1782. renderState : rs,
  1783. owner : owner,
  1784. pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
  1785. });
  1786. }
  1787. var nodeCommand = {
  1788. show : true,
  1789. boundingSphere : boundingSphere,
  1790. command : command,
  1791. pickCommand : pickCommand
  1792. };
  1793. runtimeNode.commands.push(nodeCommand);
  1794. nodeCommands.push(nodeCommand);
  1795. }
  1796. }
  1797. }
  1798. function createRuntimeNodes(model, context) {
  1799. var loadResources = model._loadResources;
  1800. if (!loadResources.finishedPendingLoads() || !loadResources.finishedResourceCreation()) {
  1801. return;
  1802. }
  1803. if (!loadResources.createRuntimeNodes) {
  1804. return;
  1805. }
  1806. loadResources.createRuntimeNodes = false;
  1807. var rootNodes = [];
  1808. var runtimeNodes = model._runtime.nodes;
  1809. var gltf = model.gltf;
  1810. var nodes = gltf.nodes;
  1811. var scene = gltf.scenes[gltf.scene];
  1812. var sceneNodes = scene.nodes;
  1813. var length = sceneNodes.length;
  1814. var stack = [];
  1815. var axis = new Cartesian3();
  1816. for (var i = 0; i < length; ++i) {
  1817. stack.push({
  1818. parentRuntimeNode : undefined,
  1819. gltfNode : nodes[sceneNodes[i]],
  1820. id : sceneNodes[i]
  1821. });
  1822. while (stack.length > 0) {
  1823. var n = stack.pop();
  1824. var parentRuntimeNode = n.parentRuntimeNode;
  1825. var gltfNode = n.gltfNode;
  1826. // Node hierarchy is a DAG so a node can have more than one parent so it may already exist
  1827. var runtimeNode = runtimeNodes[n.id];
  1828. if (runtimeNode.parents.length === 0) {
  1829. if (defined(gltfNode.matrix)) {
  1830. runtimeNode.matrix = Matrix4.fromColumnMajorArray(gltfNode.matrix);
  1831. } else {
  1832. // TRS converted to Cesium types
  1833. axis = Cartesian3.fromArray(gltfNode.rotation, 0, axis);
  1834. runtimeNode.translation = Cartesian3.fromArray(gltfNode.translation);
  1835. runtimeNode.rotation = Quaternion.fromAxisAngle(axis, gltfNode.rotation[3]);
  1836. runtimeNode.scale = Cartesian3.fromArray(gltfNode.scale);
  1837. }
  1838. }
  1839. if (defined(parentRuntimeNode)) {
  1840. parentRuntimeNode.children.push(runtimeNode);
  1841. runtimeNode.parents.push(parentRuntimeNode);
  1842. } else {
  1843. rootNodes.push(runtimeNode);
  1844. }
  1845. if (defined(gltfNode.meshes) || defined(gltfNode.instanceSkin)) {
  1846. createCommand(model, gltfNode, runtimeNode, context);
  1847. }
  1848. var children = gltfNode.children;
  1849. var childrenLength = children.length;
  1850. for (var k = 0; k < childrenLength; ++k) {
  1851. stack.push({
  1852. parentRuntimeNode : runtimeNode,
  1853. gltfNode : nodes[children[k]],
  1854. id : children[k]
  1855. });
  1856. }
  1857. }
  1858. }
  1859. model._runtime.rootNodes = rootNodes;
  1860. model._runtime.nodes = runtimeNodes;
  1861. }
  1862. function createResources(model, context) {
  1863. if (model._loadRendererResourcesFromCache) {
  1864. var resources = model._rendererResources;
  1865. var cachedResources = model._cachedRendererResources;
  1866. resources.buffers = cachedResources.buffers;
  1867. resources.vertexArrays = cachedResources.vertexArrays;
  1868. resources.programs = cachedResources.programs;
  1869. resources.pickPrograms = cachedResources.pickPrograms;
  1870. resources.textures = cachedResources.textures;
  1871. resources.samplers = cachedResources.samplers;
  1872. resources.renderStates = cachedResources.renderStates;
  1873. } else {
  1874. createBuffers(model, context); // using glTF bufferViews
  1875. createPrograms(model, context);
  1876. createSamplers(model, context);
  1877. createTextures(model, context);
  1878. }
  1879. createSkins(model);
  1880. createRuntimeAnimations(model);
  1881. if (!model._loadRendererResourcesFromCache) {
  1882. createVertexArrays(model, context); // using glTF meshes
  1883. createRenderStates(model, context); // using glTF materials/techniques/passes/states
  1884. // Long-term, we might not cache render states if they could change
  1885. // due to an animation, e.g., a uniform going from opaque to transparent.
  1886. // Could use copy-on-write if it is worth it. Probably overkill.
  1887. }
  1888. createUniformMaps(model, context); // using glTF materials/techniques/passes/instanceProgram
  1889. createRuntimeNodes(model, context); // using glTF scene
  1890. }
  1891. ///////////////////////////////////////////////////////////////////////////
  1892. function getNodeMatrix(node, result) {
  1893. var publicNode = node.publicNode;
  1894. var publicMatrix = publicNode.matrix;
  1895. if (publicNode.useMatrix && defined(publicMatrix)) {
  1896. // Public matrix overrides orginial glTF matrix and glTF animations
  1897. Matrix4.clone(publicMatrix, result);
  1898. } else if (defined(node.matrix)) {
  1899. Matrix4.clone(node.matrix, result);
  1900. } else {
  1901. Matrix4.fromTranslationQuaternionRotationScale(node.translation, node.rotation, node.scale, result);
  1902. // Keep matrix returned by the node in-sync if the node is targeted by an animation. Only TRS nodes can be targeted.
  1903. publicNode.setMatrix(result);
  1904. }
  1905. }
  1906. var scratchNodeStack = [];
  1907. function updateNodeHierarchyModelMatrix(model, modelTransformChanged, justLoaded) {
  1908. var maxDirtyNumber = model._maxDirtyNumber;
  1909. var allowPicking = model.allowPicking;
  1910. var rootNodes = model._runtime.rootNodes;
  1911. var length = rootNodes.length;
  1912. var nodeStack = scratchNodeStack;
  1913. var computedModelMatrix = model._computedModelMatrix;
  1914. for (var i = 0; i < length; ++i) {
  1915. var n = rootNodes[i];
  1916. getNodeMatrix(n, n.transformToRoot);
  1917. nodeStack.push(n);
  1918. while (nodeStack.length > 0) {
  1919. n = nodeStack.pop();
  1920. var transformToRoot = n.transformToRoot;
  1921. var commands = n.commands;
  1922. if ((n.dirtyNumber === maxDirtyNumber) || modelTransformChanged || justLoaded) {
  1923. var commandsLength = commands.length;
  1924. if (commandsLength > 0) {
  1925. // Node has meshes, which has primitives. Update their commands.
  1926. for (var j = 0 ; j < commandsLength; ++j) {
  1927. var primitiveCommand = commands[j];
  1928. var command = primitiveCommand.command;
  1929. Matrix4.multiplyTransformation(computedModelMatrix, transformToRoot, command.modelMatrix);
  1930. // PERFORMANCE_IDEA: Can use transformWithoutScale if no node up to the root has scale (inclug animation)
  1931. BoundingSphere.transform(primitiveCommand.boundingSphere, command.modelMatrix, command.boundingVolume);
  1932. if (allowPicking) {
  1933. var pickCommand = primitiveCommand.pickCommand;
  1934. Matrix4.clone(command.modelMatrix, pickCommand.modelMatrix);
  1935. BoundingSphere.clone(command.boundingVolume, pickCommand.boundingVolume);
  1936. }
  1937. }
  1938. } else {
  1939. // Node has a light or camera
  1940. n.computedMatrix = Matrix4.multiplyTransformation(computedModelMatrix, transformToRoot, n.computedMatrix);
  1941. }
  1942. }
  1943. var children = n.children;
  1944. var childrenLength = children.length;
  1945. for (var k = 0; k < childrenLength; ++k) {
  1946. var child = children[k];
  1947. // A node's transform needs to be updated if
  1948. // - It was targeted for animation this frame, or
  1949. // - Any of its ancestors were targeted for animation this frame
  1950. // PERFORMANCE_IDEA: if a child has multiple parents and only one of the parents
  1951. // is dirty, all the subtrees for each child instance will be dirty; we probably
  1952. // won't see this in the wild often.
  1953. child.dirtyNumber = Math.max(child.dirtyNumber, n.dirtyNumber);
  1954. if ((child.dirtyNumber === maxDirtyNumber) || justLoaded) {
  1955. // Don't check for modelTransformChanged since if only the model's model matrix changed,
  1956. // we do not need to rebuild the local transform-to-root, only the final
  1957. // [model's-model-matrix][transform-to-root] above.
  1958. getNodeMatrix(child, child.transformToRoot);
  1959. Matrix4.multiplyTransformation(transformToRoot, child.transformToRoot, child.transformToRoot);
  1960. }
  1961. nodeStack.push(child);
  1962. }
  1963. }
  1964. }
  1965. ++model._maxDirtyNumber;
  1966. }
  1967. var scratchObjectSpace = new Matrix4();
  1968. function applySkins(model) {
  1969. var skinnedNodes = model._runtime.skinnedNodes;
  1970. var length = skinnedNodes.length;
  1971. for (var i = 0; i < length; ++i) {
  1972. var node = skinnedNodes[i];
  1973. scratchObjectSpace = Matrix4.inverseTransformation(node.transformToRoot, scratchObjectSpace);
  1974. var computedJointMatrices = node.computedJointMatrices;
  1975. var joints = node.joints;
  1976. var bindShapeMatrix = node.bindShapeMatrix;
  1977. var inverseBindMatrices = node.inverseBindMatrices;
  1978. var inverseBindMatricesLength = inverseBindMatrices.length;
  1979. for (var m = 0; m < inverseBindMatricesLength; ++m) {
  1980. // [joint-matrix] = [node-to-root^-1][joint-to-root][inverse-bind][bind-shape]
  1981. if (!defined(computedJointMatrices[m])) {
  1982. computedJointMatrices[m] = new Matrix4();
  1983. }
  1984. computedJointMatrices[m] = Matrix4.multiplyTransformation(scratchObjectSpace, joints[m].transformToRoot, computedJointMatrices[m]);
  1985. computedJointMatrices[m] = Matrix4.multiplyTransformation(computedJointMatrices[m], inverseBindMatrices[m], computedJointMatrices[m]);
  1986. if (defined(bindShapeMatrix)) {
  1987. // Optimization for when bind shape matrix is the identity.
  1988. computedJointMatrices[m] = Matrix4.multiplyTransformation(computedJointMatrices[m], bindShapeMatrix, computedJointMatrices[m]);
  1989. }
  1990. }
  1991. }
  1992. }
  1993. function updatePerNodeShow(model) {
  1994. // Totally not worth it, but we could optimize this:
  1995. // http://blogs.agi.com/insight3d/index.php/2008/02/13/deletion-in-bounding-volume-hierarchies/
  1996. var rootNodes = model._runtime.rootNodes;
  1997. var length = rootNodes.length;
  1998. var nodeStack = scratchNodeStack;
  1999. for (var i = 0; i < length; ++i) {
  2000. var n = rootNodes[i];
  2001. n.computedShow = n.publicNode.show;
  2002. nodeStack.push(n);
  2003. while (nodeStack.length > 0) {
  2004. n = nodeStack.pop();
  2005. var show = n.computedShow;
  2006. var nodeCommands = n.commands;
  2007. var nodeCommandsLength = nodeCommands.length;
  2008. for (var j = 0 ; j < nodeCommandsLength; ++j) {
  2009. nodeCommands[j].show = show;
  2010. }
  2011. // if commandsLength is zero, the node has a light or camera
  2012. var children = n.children;
  2013. var childrenLength = children.length;
  2014. for (var k = 0; k < childrenLength; ++k) {
  2015. var child = children[k];
  2016. // Parent needs to be shown for child to be shown.
  2017. child.computedShow = show && child.publicNode.show;
  2018. nodeStack.push(child);
  2019. }
  2020. }
  2021. }
  2022. }
  2023. function updatePickIds(model, context) {
  2024. var id = model.id;
  2025. if (model._id !== id) {
  2026. model._id = id;
  2027. var pickIds = model._pickIds;
  2028. var length = pickIds.length;
  2029. for (var i = 0; i < length; ++i) {
  2030. pickIds[i].object.id = id;
  2031. }
  2032. }
  2033. }
  2034. function updateWireframe(model) {
  2035. if (model._debugWireframe !== model.debugWireframe) {
  2036. model._debugWireframe = model.debugWireframe;
  2037. // This assumes the original primitive was TRIANGLES and that the triangles
  2038. // are connected for the wireframe to look perfect.
  2039. var primitiveType = model.debugWireframe ? PrimitiveType.LINES : PrimitiveType.TRIANGLES;
  2040. var nodeCommands = model._nodeCommands;
  2041. var length = nodeCommands.length;
  2042. for (var i = 0; i < length; ++i) {
  2043. nodeCommands[i].command.primitiveType = primitiveType;
  2044. }
  2045. }
  2046. }
  2047. function updateShowBoundingVolume(model) {
  2048. if (model.debugShowBoundingVolume !== model._debugShowBoundingVolume) {
  2049. model._debugShowBoundingVolume = model.debugShowBoundingVolume;
  2050. var debugShowBoundingVolume = model.debugShowBoundingVolume;
  2051. var nodeCommands = model._nodeCommands;
  2052. var length = nodeCommands.length;
  2053. for (var i = 0; i < length; i++) {
  2054. nodeCommands[i].command.debugShowBoundingVolume = debugShowBoundingVolume;
  2055. }
  2056. }
  2057. }
  2058. var scratchDrawingBufferDimensions = new Cartesian2();
  2059. var scratchToCenter = new Cartesian3();
  2060. var scratchProj = new Cartesian3();
  2061. function scaleInPixels(positionWC, radius, context, frameState) {
  2062. var camera = frameState.camera;
  2063. var frustum = camera.frustum;
  2064. var toCenter = Cartesian3.subtract(camera.positionWC, positionWC, scratchToCenter);
  2065. var proj = Cartesian3.multiplyByScalar(camera.directionWC, Cartesian3.dot(toCenter, camera.directionWC), scratchProj);
  2066. var distance = Math.max(frustum.near, Cartesian3.magnitude(proj) - radius);
  2067. scratchDrawingBufferDimensions.x = context.drawingBufferWidth;
  2068. scratchDrawingBufferDimensions.y = context.drawingBufferHeight;
  2069. var pixelSize = frustum.getPixelSize(scratchDrawingBufferDimensions, distance);
  2070. var pixelScale = Math.max(pixelSize.x, pixelSize.y);
  2071. return pixelScale;
  2072. }
  2073. var scratchPosition = new Cartesian3();
  2074. function getScale(model, context, frameState) {
  2075. var scale = model.scale;
  2076. if (model.minimumPixelSize !== 0.0) {
  2077. // Compute size of bounding sphere in pixels
  2078. var maxPixelSize = Math.max(context.drawingBufferWidth, context.drawingBufferHeight);
  2079. var m = model.modelMatrix;
  2080. scratchPosition.x = m[12];
  2081. scratchPosition.y = m[13];
  2082. scratchPosition.z = m[14];
  2083. var radius = model.boundingSphere.radius;
  2084. var metersPerPixel = scaleInPixels(scratchPosition, radius, context, frameState);
  2085. // metersPerPixel is always > 0.0
  2086. var pixelsPerMeter = 1.0 / metersPerPixel;
  2087. var diameterInPixels = Math.min(pixelsPerMeter * (2.0 * radius), maxPixelSize);
  2088. // Maintain model's minimum pixel size
  2089. if (diameterInPixels < model.minimumPixelSize) {
  2090. scale = (model.minimumPixelSize * metersPerPixel) / (2.0 * model._initialRadius);
  2091. }
  2092. }
  2093. return scale;
  2094. }
  2095. function releaseCachedGltf(model) {
  2096. if (defined(model._cacheKey) && defined(model._cachedGltf) && (--model._cachedGltf.count === 0)) {
  2097. delete gltfCache[model._cacheKey];
  2098. }
  2099. model._cachedGltf = undefined;
  2100. }
  2101. ///////////////////////////////////////////////////////////////////////////
  2102. var CachedRendererResources = function(context, cacheKey) {
  2103. this.buffers = undefined;
  2104. this.vertexArrays = undefined;
  2105. this.programs = undefined;
  2106. this.pickPrograms = undefined;
  2107. this.textures = undefined;
  2108. this.samplers = undefined;
  2109. this.renderStates = undefined;
  2110. this.ready = false;
  2111. this.context = context;
  2112. this.cacheKey = cacheKey;
  2113. this.count = 0;
  2114. };
  2115. function destroy(property) {
  2116. for (var name in property) {
  2117. if (property.hasOwnProperty(name)) {
  2118. property[name].destroy();
  2119. }
  2120. }
  2121. }
  2122. function destroyCachedRendererResources(resources) {
  2123. destroy(resources.buffers);
  2124. destroy(resources.vertexArrays);
  2125. destroy(resources.programs);
  2126. destroy(resources.pickPrograms);
  2127. destroy(resources.textures);
  2128. }
  2129. CachedRendererResources.prototype.release = function() {
  2130. if (--this.count === 0) {
  2131. if (defined(this.cacheKey)) {
  2132. // Remove if this was cached
  2133. delete this.context.cache.modelRendererResourceCache[this.cacheKey];
  2134. }
  2135. destroyCachedRendererResources(this);
  2136. return destroyObject(this);
  2137. }
  2138. return undefined;
  2139. };
  2140. ///////////////////////////////////////////////////////////////////////////
  2141. /**
  2142. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  2143. * get the draw commands needed to render this primitive.
  2144. * <p>
  2145. * Do not call this function directly. This is documented just to
  2146. * list the exceptions that may be propagated when the scene is rendered:
  2147. * </p>
  2148. *
  2149. * @exception {RuntimeError} Failed to load external reference.
  2150. */
  2151. Model.prototype.update = function(context, frameState, commandList) {
  2152. if (frameState.mode !== SceneMode.SCENE3D) {
  2153. return;
  2154. }
  2155. if ((this._state === ModelState.NEEDS_LOAD) && defined(this.gltf)) {
  2156. // Use renderer resources from cache instead of loading/creating them?
  2157. var cachedRendererResources;
  2158. var cacheKey = this.cacheKey;
  2159. if (defined(cacheKey)) {
  2160. context.cache.modelRendererResourceCache = defaultValue(context.cache.modelRendererResourceCache, {});
  2161. var modelCaches = context.cache.modelRendererResourceCache;
  2162. cachedRendererResources = modelCaches[this.cacheKey];
  2163. if (defined(cachedRendererResources)) {
  2164. if (!cachedRendererResources.ready) {
  2165. // Cached resources for the model are not loaded yet. We'll
  2166. // try again every frame until they are.
  2167. return;
  2168. }
  2169. ++cachedRendererResources.count;
  2170. this._loadRendererResourcesFromCache = true;
  2171. } else {
  2172. cachedRendererResources = new CachedRendererResources(context, cacheKey);
  2173. cachedRendererResources.count = 1;
  2174. modelCaches[this.cacheKey] = cachedRendererResources;
  2175. }
  2176. this._cachedRendererResources = cachedRendererResources;
  2177. } else {
  2178. cachedRendererResources = new CachedRendererResources(context);
  2179. cachedRendererResources.count = 1;
  2180. this._cachedRendererResources = cachedRendererResources;
  2181. }
  2182. this._state = ModelState.LOADING;
  2183. this._boundingSphere = computeBoundingSphere(this.gltf);
  2184. this._initialRadius = this._boundingSphere.radius;
  2185. this._loadResources = new LoadResources();
  2186. parse(this);
  2187. }
  2188. var justLoaded = false;
  2189. if (this._state === ModelState.FAILED) {
  2190. throw this._loadError;
  2191. }
  2192. if (this._state === ModelState.LOADING) {
  2193. // Incrementally create WebGL resources as buffers/shaders/textures are downloaded
  2194. createResources(this, context);
  2195. var loadResources = this._loadResources;
  2196. if (loadResources.finishedPendingLoads() && loadResources.finishedResourceCreation()) {
  2197. this._state = ModelState.LOADED;
  2198. this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.
  2199. var resources = this._rendererResources;
  2200. var cachedResources = this._cachedRendererResources;
  2201. cachedResources.buffers = resources.buffers;
  2202. cachedResources.vertexArrays = resources.vertexArrays;
  2203. cachedResources.programs = resources.programs;
  2204. cachedResources.pickPrograms = resources.pickPrograms;
  2205. cachedResources.textures = resources.textures;
  2206. cachedResources.samplers = resources.samplers;
  2207. cachedResources.renderStates = resources.renderStates;
  2208. cachedResources.ready = true;
  2209. if (this.releaseGltfJson) {
  2210. releaseCachedGltf(this);
  2211. }
  2212. justLoaded = true;
  2213. }
  2214. }
  2215. var show = this.show && (this.scale !== 0.0);
  2216. if ((show && this._state === ModelState.LOADED) || justLoaded) {
  2217. var animated = this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty;
  2218. this._cesiumAnimationsDirty = false;
  2219. // Model's model matrix needs to be updated
  2220. var modelTransformChanged = !Matrix4.equals(this._modelMatrix, this.modelMatrix) ||
  2221. (this._scale !== this.scale) ||
  2222. (this._minimumPixelSize !== this.minimumPixelSize) || (this.minimumPixelSize !== 0.0); // Minimum pixel size changed or is enabled
  2223. if (modelTransformChanged || justLoaded) {
  2224. Matrix4.clone(this.modelMatrix, this._modelMatrix);
  2225. this._scale = this.scale;
  2226. this._minimumPixelSize = this.minimumPixelSize;
  2227. var scale = getScale(this, context, frameState);
  2228. var computedModelMatrix = this._computedModelMatrix;
  2229. Matrix4.multiplyByUniformScale(this.modelMatrix, scale, computedModelMatrix);
  2230. Matrix4.multiplyTransformation(computedModelMatrix, yUpToZUp, computedModelMatrix);
  2231. }
  2232. // Update modelMatrix throughout the graph as needed
  2233. if (animated || modelTransformChanged || justLoaded) {
  2234. updateNodeHierarchyModelMatrix(this, modelTransformChanged, justLoaded);
  2235. if (animated || justLoaded) {
  2236. // Apply skins if animation changed any node transforms
  2237. applySkins(this);
  2238. }
  2239. }
  2240. if (this._perNodeShowDirty) {
  2241. this._perNodeShowDirty = false;
  2242. updatePerNodeShow(this);
  2243. }
  2244. updatePickIds(this, context);
  2245. updateWireframe(this);
  2246. updateShowBoundingVolume(this);
  2247. }
  2248. if (justLoaded) {
  2249. // Called after modelMatrix update.
  2250. var model = this;
  2251. frameState.afterRender.push(function() {
  2252. model._ready = true;
  2253. model.readyToRender.raiseEvent(model);
  2254. });
  2255. return;
  2256. }
  2257. // We don't check show at the top of the function since we
  2258. // want to be able to progressively load models when they are not shown,
  2259. // and then have them visible immediately when show is set to true.
  2260. if (show) {
  2261. // PERFORMANCE_IDEA: This is terrible
  2262. var passes = frameState.passes;
  2263. var nodeCommands = this._nodeCommands;
  2264. var length = nodeCommands.length;
  2265. var i;
  2266. var nc;
  2267. if (passes.render) {
  2268. for (i = 0; i < length; ++i) {
  2269. nc = nodeCommands[i];
  2270. if (nc.show) {
  2271. commandList.push(nc.command);
  2272. }
  2273. }
  2274. }
  2275. if (passes.pick) {
  2276. for (i = 0; i < length; ++i) {
  2277. nc = nodeCommands[i];
  2278. if (nc.show) {
  2279. commandList.push(nc.pickCommand);
  2280. }
  2281. }
  2282. }
  2283. }
  2284. };
  2285. /**
  2286. * Returns true if this object was destroyed; otherwise, false.
  2287. * <br /><br />
  2288. * If this object was destroyed, it should not be used; calling any function other than
  2289. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  2290. *
  2291. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  2292. *
  2293. * @see Model#destroy
  2294. */
  2295. Model.prototype.isDestroyed = function() {
  2296. return false;
  2297. };
  2298. /**
  2299. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  2300. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  2301. * <br /><br />
  2302. * Once an object is destroyed, it should not be used; calling any function other than
  2303. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  2304. * assign the return value (<code>undefined</code>) to the object as done in the example.
  2305. *
  2306. * @returns {undefined}
  2307. *
  2308. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  2309. *
  2310. * @see Model#isDestroyed
  2311. *
  2312. * @example
  2313. * model = model && model.destroy();
  2314. */
  2315. Model.prototype.destroy = function() {
  2316. this._rendererResources = undefined;
  2317. this._cachedRendererResources = this._cachedRendererResources && this._cachedRendererResources.release();
  2318. var pickIds = this._pickIds;
  2319. var length = pickIds.length;
  2320. for (var i = 0; i < length; ++i) {
  2321. pickIds[i].destroy();
  2322. }
  2323. releaseCachedGltf(this);
  2324. return destroyObject(this);
  2325. };
  2326. return Model;
  2327. });