Primitive.js 79 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531
  1. import BoundingSphere from "../Core/BoundingSphere.js";
  2. import Cartesian2 from "../Core/Cartesian2.js";
  3. import Cartesian3 from "../Core/Cartesian3.js";
  4. import Cartesian4 from "../Core/Cartesian4.js";
  5. import Cartographic from "../Core/Cartographic.js";
  6. import clone from "../Core/clone.js";
  7. import Color from "../Core/Color.js";
  8. import combine from "../Core/combine.js";
  9. import ComponentDatatype from "../Core/ComponentDatatype.js";
  10. import defaultValue from "../Core/defaultValue.js";
  11. import defer from "../Core/defer.js";
  12. import defined from "../Core/defined.js";
  13. import destroyObject from "../Core/destroyObject.js";
  14. import DeveloperError from "../Core/DeveloperError.js";
  15. import EncodedCartesian3 from "../Core/EncodedCartesian3.js";
  16. import FeatureDetection from "../Core/FeatureDetection.js";
  17. import Geometry from "../Core/Geometry.js";
  18. import GeometryAttribute from "../Core/GeometryAttribute.js";
  19. import GeometryAttributes from "../Core/GeometryAttributes.js";
  20. import GeometryOffsetAttribute from "../Core/GeometryOffsetAttribute.js";
  21. import Intersect from "../Core/Intersect.js";
  22. import Matrix4 from "../Core/Matrix4.js";
  23. import Plane from "../Core/Plane.js";
  24. import RuntimeError from "../Core/RuntimeError.js";
  25. import subdivideArray from "../Core/subdivideArray.js";
  26. import TaskProcessor from "../Core/TaskProcessor.js";
  27. import BufferUsage from "../Renderer/BufferUsage.js";
  28. import ContextLimits from "../Renderer/ContextLimits.js";
  29. import DrawCommand from "../Renderer/DrawCommand.js";
  30. import Pass from "../Renderer/Pass.js";
  31. import RenderState from "../Renderer/RenderState.js";
  32. import ShaderProgram from "../Renderer/ShaderProgram.js";
  33. import ShaderSource from "../Renderer/ShaderSource.js";
  34. import VertexArray from "../Renderer/VertexArray.js";
  35. import BatchTable from "./BatchTable.js";
  36. import CullFace from "./CullFace.js";
  37. import DepthFunction from "./DepthFunction.js";
  38. import PrimitivePipeline from "./PrimitivePipeline.js";
  39. import PrimitiveState from "./PrimitiveState.js";
  40. import SceneMode from "./SceneMode.js";
  41. import ShadowMode from "./ShadowMode.js";
  42. /**
  43. * A primitive represents geometry in the {@link Scene}. The geometry can be from a single {@link GeometryInstance}
  44. * as shown in example 1 below, or from an array of instances, even if the geometry is from different
  45. * geometry types, e.g., an {@link RectangleGeometry} and an {@link EllipsoidGeometry} as shown in Code Example 2.
  46. * <p>
  47. * A primitive combines geometry instances with an {@link Appearance} that describes the full shading, including
  48. * {@link Material} and {@link RenderState}. Roughly, the geometry instance defines the structure and placement,
  49. * and the appearance defines the visual characteristics. Decoupling geometry and appearance allows us to mix
  50. * and match most of them and add a new geometry or appearance independently of each other.
  51. * </p>
  52. * <p>
  53. * Combining multiple instances into one primitive is called batching, and significantly improves performance for static data.
  54. * Instances can be individually picked; {@link Scene#pick} returns their {@link GeometryInstance#id}. Using
  55. * per-instance appearances like {@link PerInstanceColorAppearance}, each instance can also have a unique color.
  56. * </p>
  57. * <p>
  58. * {@link Geometry} can either be created and batched on a web worker or the main thread. The first two examples
  59. * show geometry that will be created on a web worker by using the descriptions of the geometry. The third example
  60. * shows how to create the geometry on the main thread by explicitly calling the <code>createGeometry</code> method.
  61. * </p>
  62. *
  63. * @alias Primitive
  64. * @constructor
  65. *
  66. * @param {Object} [options] Object with the following properties:
  67. * @param {GeometryInstance[]|GeometryInstance} [options.geometryInstances] The geometry instances - or a single geometry instance - to render.
  68. * @param {Appearance} [options.appearance] The appearance used to render the primitive.
  69. * @param {Appearance} [options.depthFailAppearance] The appearance used to shade this primitive when it fails the depth test.
  70. * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
  71. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates.
  72. * @param {Boolean} [options.vertexCacheOptimize=false] When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  73. * @param {Boolean} [options.interleave=false] When <code>true</code>, geometry vertex attributes are interleaved, which can slightly improve rendering performance but increases load time.
  74. * @param {Boolean} [options.compressVertices=true] When <code>true</code>, the geometry vertices are compressed, which will save memory.
  75. * @param {Boolean} [options.releaseGeometryInstances=true] When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  76. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  77. * @param {Boolean} [options.cull=true] When <code>true</code>, the renderer frustum culls and horizon culls the primitive's commands based on their bounding volume. Set this to <code>false</code> for a small performance gain if you are manually culling the primitive.
  78. * @param {Boolean} [options.asynchronous=true] Determines if the primitive will be created asynchronously or block until ready.
  79. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  80. * @param {ShadowMode} [options.shadows=ShadowMode.DISABLED] Determines whether this primitive casts or receives shadows from light sources.
  81. *
  82. * @example
  83. * // 1. Draw a translucent ellipse on the surface with a checkerboard pattern
  84. * const instance = new Cesium.GeometryInstance({
  85. * geometry : new Cesium.EllipseGeometry({
  86. * center : Cesium.Cartesian3.fromDegrees(-100.0, 20.0),
  87. * semiMinorAxis : 500000.0,
  88. * semiMajorAxis : 1000000.0,
  89. * rotation : Cesium.Math.PI_OVER_FOUR,
  90. * vertexFormat : Cesium.VertexFormat.POSITION_AND_ST
  91. * }),
  92. * id : 'object returned when this instance is picked and to get/set per-instance attributes'
  93. * });
  94. * scene.primitives.add(new Cesium.Primitive({
  95. * geometryInstances : instance,
  96. * appearance : new Cesium.EllipsoidSurfaceAppearance({
  97. * material : Cesium.Material.fromType('Checkerboard')
  98. * })
  99. * }));
  100. *
  101. * @example
  102. * // 2. Draw different instances each with a unique color
  103. * const rectangleInstance = new Cesium.GeometryInstance({
  104. * geometry : new Cesium.RectangleGeometry({
  105. * rectangle : Cesium.Rectangle.fromDegrees(-140.0, 30.0, -100.0, 40.0),
  106. * vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
  107. * }),
  108. * id : 'rectangle',
  109. * attributes : {
  110. * color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
  111. * }
  112. * });
  113. * const ellipsoidInstance = new Cesium.GeometryInstance({
  114. * geometry : new Cesium.EllipsoidGeometry({
  115. * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0),
  116. * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
  117. * }),
  118. * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
  119. * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()),
  120. * id : 'ellipsoid',
  121. * attributes : {
  122. * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA)
  123. * }
  124. * });
  125. * scene.primitives.add(new Cesium.Primitive({
  126. * geometryInstances : [rectangleInstance, ellipsoidInstance],
  127. * appearance : new Cesium.PerInstanceColorAppearance()
  128. * }));
  129. *
  130. * @example
  131. * // 3. Create the geometry on the main thread.
  132. * scene.primitives.add(new Cesium.Primitive({
  133. * geometryInstances : new Cesium.GeometryInstance({
  134. * geometry : Cesium.EllipsoidGeometry.createGeometry(new Cesium.EllipsoidGeometry({
  135. * radii : new Cesium.Cartesian3(500000.0, 500000.0, 1000000.0),
  136. * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
  137. * })),
  138. * modelMatrix : Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(
  139. * Cesium.Cartesian3.fromDegrees(-95.59777, 40.03883)), new Cesium.Cartesian3(0.0, 0.0, 500000.0), new Cesium.Matrix4()),
  140. * id : 'ellipsoid',
  141. * attributes : {
  142. * color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.AQUA)
  143. * }
  144. * }),
  145. * appearance : new Cesium.PerInstanceColorAppearance(),
  146. * asynchronous : false
  147. * }));
  148. *
  149. * @see GeometryInstance
  150. * @see Appearance
  151. * @see ClassificationPrimitive
  152. * @see GroundPrimitive
  153. */
  154. function Primitive(options) {
  155. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  156. /**
  157. * The geometry instances rendered with this primitive. This may
  158. * be <code>undefined</code> if <code>options.releaseGeometryInstances</code>
  159. * is <code>true</code> when the primitive is constructed.
  160. * <p>
  161. * Changing this property after the primitive is rendered has no effect.
  162. * </p>
  163. *
  164. * @readonly
  165. * @type GeometryInstance[]|GeometryInstance
  166. *
  167. * @default undefined
  168. */
  169. this.geometryInstances = options.geometryInstances;
  170. /**
  171. * The {@link Appearance} used to shade this primitive. Each geometry
  172. * instance is shaded with the same appearance. Some appearances, like
  173. * {@link PerInstanceColorAppearance} allow giving each instance unique
  174. * properties.
  175. *
  176. * @type Appearance
  177. *
  178. * @default undefined
  179. */
  180. this.appearance = options.appearance;
  181. this._appearance = undefined;
  182. this._material = undefined;
  183. /**
  184. * The {@link Appearance} used to shade this primitive when it fails the depth test. Each geometry
  185. * instance is shaded with the same appearance. Some appearances, like
  186. * {@link PerInstanceColorAppearance} allow giving each instance unique
  187. * properties.
  188. *
  189. * <p>
  190. * When using an appearance that requires a color attribute, like PerInstanceColorAppearance,
  191. * add a depthFailColor per-instance attribute instead.
  192. * </p>
  193. *
  194. * <p>
  195. * Requires the EXT_frag_depth WebGL extension to render properly. If the extension is not supported,
  196. * there may be artifacts.
  197. * </p>
  198. * @type Appearance
  199. *
  200. * @default undefined
  201. */
  202. this.depthFailAppearance = options.depthFailAppearance;
  203. this._depthFailAppearance = undefined;
  204. this._depthFailMaterial = undefined;
  205. /**
  206. * The 4x4 transformation matrix that transforms the primitive (all geometry instances) from model to world coordinates.
  207. * When this is the identity matrix, the primitive is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  208. * Local reference frames can be used by providing a different transformation matrix, like that returned
  209. * by {@link Transforms.eastNorthUpToFixedFrame}.
  210. *
  211. * <p>
  212. * This property is only supported in 3D mode.
  213. * </p>
  214. *
  215. * @type Matrix4
  216. *
  217. * @default Matrix4.IDENTITY
  218. *
  219. * @example
  220. * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  221. * p.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  222. */
  223. this.modelMatrix = Matrix4.clone(
  224. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  225. );
  226. this._modelMatrix = new Matrix4();
  227. /**
  228. * Determines if the primitive will be shown. This affects all geometry
  229. * instances in the primitive.
  230. *
  231. * @type Boolean
  232. *
  233. * @default true
  234. */
  235. this.show = defaultValue(options.show, true);
  236. this._vertexCacheOptimize = defaultValue(options.vertexCacheOptimize, false);
  237. this._interleave = defaultValue(options.interleave, false);
  238. this._releaseGeometryInstances = defaultValue(
  239. options.releaseGeometryInstances,
  240. true
  241. );
  242. this._allowPicking = defaultValue(options.allowPicking, true);
  243. this._asynchronous = defaultValue(options.asynchronous, true);
  244. this._compressVertices = defaultValue(options.compressVertices, true);
  245. /**
  246. * When <code>true</code>, the renderer frustum culls and horizon culls the primitive's commands
  247. * based on their bounding volume. Set this to <code>false</code> for a small performance gain
  248. * if you are manually culling the primitive.
  249. *
  250. * @type {Boolean}
  251. *
  252. * @default true
  253. */
  254. this.cull = defaultValue(options.cull, true);
  255. /**
  256. * This property is for debugging only; it is not for production use nor is it optimized.
  257. * <p>
  258. * Draws the bounding sphere for each draw command in the primitive.
  259. * </p>
  260. *
  261. * @type {Boolean}
  262. *
  263. * @default false
  264. */
  265. this.debugShowBoundingVolume = defaultValue(
  266. options.debugShowBoundingVolume,
  267. false
  268. );
  269. /**
  270. * @private
  271. */
  272. this.rtcCenter = options.rtcCenter;
  273. //>>includeStart('debug', pragmas.debug);
  274. if (
  275. defined(this.rtcCenter) &&
  276. (!defined(this.geometryInstances) ||
  277. (Array.isArray(this.geometryInstances) &&
  278. this.geometryInstances.length !== 1))
  279. ) {
  280. throw new DeveloperError(
  281. "Relative-to-center rendering only supports one geometry instance."
  282. );
  283. }
  284. //>>includeEnd('debug');
  285. /**
  286. * Determines whether this primitive casts or receives shadows from light sources.
  287. *
  288. * @type {ShadowMode}
  289. *
  290. * @default ShadowMode.DISABLED
  291. */
  292. this.shadows = defaultValue(options.shadows, ShadowMode.DISABLED);
  293. this._translucent = undefined;
  294. this._state = PrimitiveState.READY;
  295. this._geometries = [];
  296. this._error = undefined;
  297. this._numberOfInstances = 0;
  298. this._boundingSpheres = [];
  299. this._boundingSphereWC = [];
  300. this._boundingSphereCV = [];
  301. this._boundingSphere2D = [];
  302. this._boundingSphereMorph = [];
  303. this._perInstanceAttributeCache = [];
  304. this._instanceIds = [];
  305. this._lastPerInstanceAttributeIndex = 0;
  306. this._va = [];
  307. this._attributeLocations = undefined;
  308. this._primitiveType = undefined;
  309. this._frontFaceRS = undefined;
  310. this._backFaceRS = undefined;
  311. this._sp = undefined;
  312. this._depthFailAppearance = undefined;
  313. this._spDepthFail = undefined;
  314. this._frontFaceDepthFailRS = undefined;
  315. this._backFaceDepthFailRS = undefined;
  316. this._pickIds = [];
  317. this._colorCommands = [];
  318. this._pickCommands = [];
  319. this._createBoundingVolumeFunction = options._createBoundingVolumeFunction;
  320. this._createRenderStatesFunction = options._createRenderStatesFunction;
  321. this._createShaderProgramFunction = options._createShaderProgramFunction;
  322. this._createCommandsFunction = options._createCommandsFunction;
  323. this._updateAndQueueCommandsFunction =
  324. options._updateAndQueueCommandsFunction;
  325. this._createPickOffsets = options._createPickOffsets;
  326. this._pickOffsets = undefined;
  327. this._createGeometryResults = undefined;
  328. this._ready = false;
  329. this._readyPromise = defer();
  330. this._batchTable = undefined;
  331. this._batchTableAttributeIndices = undefined;
  332. this._offsetInstanceExtend = undefined;
  333. this._batchTableOffsetAttribute2DIndex = undefined;
  334. this._batchTableOffsetsUpdated = false;
  335. this._instanceBoundingSpheres = undefined;
  336. this._instanceBoundingSpheresCV = undefined;
  337. this._tempBoundingSpheres = undefined;
  338. this._recomputeBoundingSpheres = false;
  339. this._batchTableBoundingSpheresUpdated = false;
  340. this._batchTableBoundingSphereAttributeIndices = undefined;
  341. }
  342. Object.defineProperties(Primitive.prototype, {
  343. /**
  344. * When <code>true</code>, geometry vertices are optimized for the pre and post-vertex-shader caches.
  345. *
  346. * @memberof Primitive.prototype
  347. *
  348. * @type {Boolean}
  349. * @readonly
  350. *
  351. * @default true
  352. */
  353. vertexCacheOptimize: {
  354. get: function () {
  355. return this._vertexCacheOptimize;
  356. },
  357. },
  358. /**
  359. * Determines if geometry vertex attributes are interleaved, which can slightly improve rendering performance.
  360. *
  361. * @memberof Primitive.prototype
  362. *
  363. * @type {Boolean}
  364. * @readonly
  365. *
  366. * @default false
  367. */
  368. interleave: {
  369. get: function () {
  370. return this._interleave;
  371. },
  372. },
  373. /**
  374. * When <code>true</code>, the primitive does not keep a reference to the input <code>geometryInstances</code> to save memory.
  375. *
  376. * @memberof Primitive.prototype
  377. *
  378. * @type {Boolean}
  379. * @readonly
  380. *
  381. * @default true
  382. */
  383. releaseGeometryInstances: {
  384. get: function () {
  385. return this._releaseGeometryInstances;
  386. },
  387. },
  388. /**
  389. * When <code>true</code>, each geometry instance will only be pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved. *
  390. *
  391. * @memberof Primitive.prototype
  392. *
  393. * @type {Boolean}
  394. * @readonly
  395. *
  396. * @default true
  397. */
  398. allowPicking: {
  399. get: function () {
  400. return this._allowPicking;
  401. },
  402. },
  403. /**
  404. * Determines if the geometry instances will be created and batched on a web worker.
  405. *
  406. * @memberof Primitive.prototype
  407. *
  408. * @type {Boolean}
  409. * @readonly
  410. *
  411. * @default true
  412. */
  413. asynchronous: {
  414. get: function () {
  415. return this._asynchronous;
  416. },
  417. },
  418. /**
  419. * When <code>true</code>, geometry vertices are compressed, which will save memory.
  420. *
  421. * @memberof Primitive.prototype
  422. *
  423. * @type {Boolean}
  424. * @readonly
  425. *
  426. * @default true
  427. */
  428. compressVertices: {
  429. get: function () {
  430. return this._compressVertices;
  431. },
  432. },
  433. /**
  434. * Determines if the primitive is complete and ready to render. If this property is
  435. * true, the primitive will be rendered the next time that {@link Primitive#update}
  436. * is called.
  437. *
  438. * @memberof Primitive.prototype
  439. *
  440. * @type {Boolean}
  441. * @readonly
  442. */
  443. ready: {
  444. get: function () {
  445. return this._ready;
  446. },
  447. },
  448. /**
  449. * Gets a promise that resolves when the primitive is ready to render.
  450. * @memberof Primitive.prototype
  451. * @type {Promise.<Primitive>}
  452. * @readonly
  453. */
  454. readyPromise: {
  455. get: function () {
  456. return this._readyPromise.promise;
  457. },
  458. },
  459. });
  460. function getCommonPerInstanceAttributeNames(instances) {
  461. const length = instances.length;
  462. const attributesInAllInstances = [];
  463. const attributes0 = instances[0].attributes;
  464. let name;
  465. for (name in attributes0) {
  466. if (attributes0.hasOwnProperty(name) && defined(attributes0[name])) {
  467. const attribute = attributes0[name];
  468. let inAllInstances = true;
  469. // Does this same attribute exist in all instances?
  470. for (let i = 1; i < length; ++i) {
  471. const otherAttribute = instances[i].attributes[name];
  472. if (
  473. !defined(otherAttribute) ||
  474. attribute.componentDatatype !== otherAttribute.componentDatatype ||
  475. attribute.componentsPerAttribute !==
  476. otherAttribute.componentsPerAttribute ||
  477. attribute.normalize !== otherAttribute.normalize
  478. ) {
  479. inAllInstances = false;
  480. break;
  481. }
  482. }
  483. if (inAllInstances) {
  484. attributesInAllInstances.push(name);
  485. }
  486. }
  487. }
  488. return attributesInAllInstances;
  489. }
  490. const scratchGetAttributeCartesian2 = new Cartesian2();
  491. const scratchGetAttributeCartesian3 = new Cartesian3();
  492. const scratchGetAttributeCartesian4 = new Cartesian4();
  493. function getAttributeValue(value) {
  494. const componentsPerAttribute = value.length;
  495. if (componentsPerAttribute === 1) {
  496. return value[0];
  497. } else if (componentsPerAttribute === 2) {
  498. return Cartesian2.unpack(value, 0, scratchGetAttributeCartesian2);
  499. } else if (componentsPerAttribute === 3) {
  500. return Cartesian3.unpack(value, 0, scratchGetAttributeCartesian3);
  501. } else if (componentsPerAttribute === 4) {
  502. return Cartesian4.unpack(value, 0, scratchGetAttributeCartesian4);
  503. }
  504. }
  505. function createBatchTable(primitive, context) {
  506. const geometryInstances = primitive.geometryInstances;
  507. const instances = Array.isArray(geometryInstances)
  508. ? geometryInstances
  509. : [geometryInstances];
  510. const numberOfInstances = instances.length;
  511. if (numberOfInstances === 0) {
  512. return;
  513. }
  514. const names = getCommonPerInstanceAttributeNames(instances);
  515. const length = names.length;
  516. const attributes = [];
  517. const attributeIndices = {};
  518. const boundingSphereAttributeIndices = {};
  519. let offset2DIndex;
  520. const firstInstance = instances[0];
  521. let instanceAttributes = firstInstance.attributes;
  522. let i;
  523. let name;
  524. let attribute;
  525. for (i = 0; i < length; ++i) {
  526. name = names[i];
  527. attribute = instanceAttributes[name];
  528. attributeIndices[name] = i;
  529. attributes.push({
  530. functionName: `czm_batchTable_${name}`,
  531. componentDatatype: attribute.componentDatatype,
  532. componentsPerAttribute: attribute.componentsPerAttribute,
  533. normalize: attribute.normalize,
  534. });
  535. }
  536. if (names.indexOf("distanceDisplayCondition") !== -1) {
  537. attributes.push(
  538. {
  539. functionName: "czm_batchTable_boundingSphereCenter3DHigh",
  540. componentDatatype: ComponentDatatype.FLOAT,
  541. componentsPerAttribute: 3,
  542. },
  543. {
  544. functionName: "czm_batchTable_boundingSphereCenter3DLow",
  545. componentDatatype: ComponentDatatype.FLOAT,
  546. componentsPerAttribute: 3,
  547. },
  548. {
  549. functionName: "czm_batchTable_boundingSphereCenter2DHigh",
  550. componentDatatype: ComponentDatatype.FLOAT,
  551. componentsPerAttribute: 3,
  552. },
  553. {
  554. functionName: "czm_batchTable_boundingSphereCenter2DLow",
  555. componentDatatype: ComponentDatatype.FLOAT,
  556. componentsPerAttribute: 3,
  557. },
  558. {
  559. functionName: "czm_batchTable_boundingSphereRadius",
  560. componentDatatype: ComponentDatatype.FLOAT,
  561. componentsPerAttribute: 1,
  562. }
  563. );
  564. boundingSphereAttributeIndices.center3DHigh = attributes.length - 5;
  565. boundingSphereAttributeIndices.center3DLow = attributes.length - 4;
  566. boundingSphereAttributeIndices.center2DHigh = attributes.length - 3;
  567. boundingSphereAttributeIndices.center2DLow = attributes.length - 2;
  568. boundingSphereAttributeIndices.radius = attributes.length - 1;
  569. }
  570. if (names.indexOf("offset") !== -1) {
  571. attributes.push({
  572. functionName: "czm_batchTable_offset2D",
  573. componentDatatype: ComponentDatatype.FLOAT,
  574. componentsPerAttribute: 3,
  575. });
  576. offset2DIndex = attributes.length - 1;
  577. }
  578. attributes.push({
  579. functionName: "czm_batchTable_pickColor",
  580. componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
  581. componentsPerAttribute: 4,
  582. normalize: true,
  583. });
  584. const attributesLength = attributes.length;
  585. const batchTable = new BatchTable(context, attributes, numberOfInstances);
  586. for (i = 0; i < numberOfInstances; ++i) {
  587. const instance = instances[i];
  588. instanceAttributes = instance.attributes;
  589. for (let j = 0; j < length; ++j) {
  590. name = names[j];
  591. attribute = instanceAttributes[name];
  592. const value = getAttributeValue(attribute.value);
  593. const attributeIndex = attributeIndices[name];
  594. batchTable.setBatchedAttribute(i, attributeIndex, value);
  595. }
  596. const pickObject = {
  597. primitive: defaultValue(instance.pickPrimitive, primitive),
  598. };
  599. if (defined(instance.id)) {
  600. pickObject.id = instance.id;
  601. }
  602. const pickId = context.createPickId(pickObject);
  603. primitive._pickIds.push(pickId);
  604. const pickColor = pickId.color;
  605. const color = scratchGetAttributeCartesian4;
  606. color.x = Color.floatToByte(pickColor.red);
  607. color.y = Color.floatToByte(pickColor.green);
  608. color.z = Color.floatToByte(pickColor.blue);
  609. color.w = Color.floatToByte(pickColor.alpha);
  610. batchTable.setBatchedAttribute(i, attributesLength - 1, color);
  611. }
  612. primitive._batchTable = batchTable;
  613. primitive._batchTableAttributeIndices = attributeIndices;
  614. primitive._batchTableBoundingSphereAttributeIndices = boundingSphereAttributeIndices;
  615. primitive._batchTableOffsetAttribute2DIndex = offset2DIndex;
  616. }
  617. function cloneAttribute(attribute) {
  618. let clonedValues;
  619. if (Array.isArray(attribute.values)) {
  620. clonedValues = attribute.values.slice(0);
  621. } else {
  622. clonedValues = new attribute.values.constructor(attribute.values);
  623. }
  624. return new GeometryAttribute({
  625. componentDatatype: attribute.componentDatatype,
  626. componentsPerAttribute: attribute.componentsPerAttribute,
  627. normalize: attribute.normalize,
  628. values: clonedValues,
  629. });
  630. }
  631. function cloneGeometry(geometry) {
  632. const attributes = geometry.attributes;
  633. const newAttributes = new GeometryAttributes();
  634. for (const property in attributes) {
  635. if (attributes.hasOwnProperty(property) && defined(attributes[property])) {
  636. newAttributes[property] = cloneAttribute(attributes[property]);
  637. }
  638. }
  639. let indices;
  640. if (defined(geometry.indices)) {
  641. const sourceValues = geometry.indices;
  642. if (Array.isArray(sourceValues)) {
  643. indices = sourceValues.slice(0);
  644. } else {
  645. indices = new sourceValues.constructor(sourceValues);
  646. }
  647. }
  648. return new Geometry({
  649. attributes: newAttributes,
  650. indices: indices,
  651. primitiveType: geometry.primitiveType,
  652. boundingSphere: BoundingSphere.clone(geometry.boundingSphere),
  653. });
  654. }
  655. function cloneInstance(instance, geometry) {
  656. return {
  657. geometry: geometry,
  658. attributes: instance.attributes,
  659. modelMatrix: Matrix4.clone(instance.modelMatrix),
  660. pickPrimitive: instance.pickPrimitive,
  661. id: instance.id,
  662. };
  663. }
  664. const positionRegex = /attribute\s+vec(?:3|4)\s+(.*)3DHigh;/g;
  665. Primitive._modifyShaderPosition = function (
  666. primitive,
  667. vertexShaderSource,
  668. scene3DOnly
  669. ) {
  670. let match;
  671. let forwardDecl = "";
  672. let attributes = "";
  673. let computeFunctions = "";
  674. while ((match = positionRegex.exec(vertexShaderSource)) !== null) {
  675. const name = match[1];
  676. const functionName = `vec4 czm_compute${name[0].toUpperCase()}${name.substr(
  677. 1
  678. )}()`;
  679. // Don't forward-declare czm_computePosition because computePosition.glsl already does.
  680. if (functionName !== "vec4 czm_computePosition()") {
  681. forwardDecl += `${functionName};\n`;
  682. }
  683. if (!defined(primitive.rtcCenter)) {
  684. // Use GPU RTE
  685. if (!scene3DOnly) {
  686. attributes +=
  687. `attribute vec3 ${name}2DHigh;\n` + `attribute vec3 ${name}2DLow;\n`;
  688. computeFunctions +=
  689. `${functionName}\n` +
  690. `{\n` +
  691. ` vec4 p;\n` +
  692. ` if (czm_morphTime == 1.0)\n` +
  693. ` {\n` +
  694. ` p = czm_translateRelativeToEye(${name}3DHigh, ${name}3DLow);\n` +
  695. ` }\n` +
  696. ` else if (czm_morphTime == 0.0)\n` +
  697. ` {\n` +
  698. ` p = czm_translateRelativeToEye(${name}2DHigh.zxy, ${name}2DLow.zxy);\n` +
  699. ` }\n` +
  700. ` else\n` +
  701. ` {\n` +
  702. ` p = czm_columbusViewMorph(\n` +
  703. ` czm_translateRelativeToEye(${name}2DHigh.zxy, ${name}2DLow.zxy),\n` +
  704. ` czm_translateRelativeToEye(${name}3DHigh, ${name}3DLow),\n` +
  705. ` czm_morphTime);\n` +
  706. ` }\n` +
  707. ` return p;\n` +
  708. `}\n\n`;
  709. } else {
  710. computeFunctions +=
  711. `${functionName}\n` +
  712. `{\n` +
  713. ` return czm_translateRelativeToEye(${name}3DHigh, ${name}3DLow);\n` +
  714. `}\n\n`;
  715. }
  716. } else {
  717. // Use RTC
  718. vertexShaderSource = vertexShaderSource.replace(
  719. /attribute\s+vec(?:3|4)\s+position3DHigh;/g,
  720. ""
  721. );
  722. vertexShaderSource = vertexShaderSource.replace(
  723. /attribute\s+vec(?:3|4)\s+position3DLow;/g,
  724. ""
  725. );
  726. forwardDecl += "uniform mat4 u_modifiedModelView;\n";
  727. attributes += "attribute vec4 position;\n";
  728. computeFunctions +=
  729. `${functionName}\n` +
  730. `{\n` +
  731. ` return u_modifiedModelView * position;\n` +
  732. `}\n\n`;
  733. vertexShaderSource = vertexShaderSource.replace(
  734. /czm_modelViewRelativeToEye\s+\*\s+/g,
  735. ""
  736. );
  737. vertexShaderSource = vertexShaderSource.replace(
  738. /czm_modelViewProjectionRelativeToEye/g,
  739. "czm_projection"
  740. );
  741. }
  742. }
  743. return [forwardDecl, attributes, vertexShaderSource, computeFunctions].join(
  744. "\n"
  745. );
  746. };
  747. Primitive._appendShowToShader = function (primitive, vertexShaderSource) {
  748. if (!defined(primitive._batchTableAttributeIndices.show)) {
  749. return vertexShaderSource;
  750. }
  751. const renamedVS = ShaderSource.replaceMain(
  752. vertexShaderSource,
  753. "czm_non_show_main"
  754. );
  755. const showMain =
  756. "void main() \n" +
  757. "{ \n" +
  758. " czm_non_show_main(); \n" +
  759. " gl_Position *= czm_batchTable_show(batchId); \n" +
  760. "}";
  761. return `${renamedVS}\n${showMain}`;
  762. };
  763. Primitive._updateColorAttribute = function (
  764. primitive,
  765. vertexShaderSource,
  766. isDepthFail
  767. ) {
  768. // some appearances have a color attribute for per vertex color.
  769. // only remove if color is a per instance attribute.
  770. if (
  771. !defined(primitive._batchTableAttributeIndices.color) &&
  772. !defined(primitive._batchTableAttributeIndices.depthFailColor)
  773. ) {
  774. return vertexShaderSource;
  775. }
  776. if (vertexShaderSource.search(/attribute\s+vec4\s+color;/g) === -1) {
  777. return vertexShaderSource;
  778. }
  779. //>>includeStart('debug', pragmas.debug);
  780. if (
  781. isDepthFail &&
  782. !defined(primitive._batchTableAttributeIndices.depthFailColor)
  783. ) {
  784. throw new DeveloperError(
  785. "A depthFailColor per-instance attribute is required when using a depth fail appearance that uses a color attribute."
  786. );
  787. }
  788. //>>includeEnd('debug');
  789. let modifiedVS = vertexShaderSource;
  790. modifiedVS = modifiedVS.replace(/attribute\s+vec4\s+color;/g, "");
  791. if (!isDepthFail) {
  792. modifiedVS = modifiedVS.replace(
  793. /(\b)color(\b)/g,
  794. "$1czm_batchTable_color(batchId)$2"
  795. );
  796. } else {
  797. modifiedVS = modifiedVS.replace(
  798. /(\b)color(\b)/g,
  799. "$1czm_batchTable_depthFailColor(batchId)$2"
  800. );
  801. }
  802. return modifiedVS;
  803. };
  804. function appendPickToVertexShader(source) {
  805. const renamedVS = ShaderSource.replaceMain(source, "czm_non_pick_main");
  806. const pickMain =
  807. "varying vec4 v_pickColor; \n" +
  808. "void main() \n" +
  809. "{ \n" +
  810. " czm_non_pick_main(); \n" +
  811. " v_pickColor = czm_batchTable_pickColor(batchId); \n" +
  812. "}";
  813. return `${renamedVS}\n${pickMain}`;
  814. }
  815. function appendPickToFragmentShader(source) {
  816. return `varying vec4 v_pickColor;\n${source}`;
  817. }
  818. Primitive._updatePickColorAttribute = function (source) {
  819. let vsPick = source.replace(/attribute\s+vec4\s+pickColor;/g, "");
  820. vsPick = vsPick.replace(
  821. /(\b)pickColor(\b)/g,
  822. "$1czm_batchTable_pickColor(batchId)$2"
  823. );
  824. return vsPick;
  825. };
  826. Primitive._appendOffsetToShader = function (primitive, vertexShaderSource) {
  827. if (!defined(primitive._batchTableAttributeIndices.offset)) {
  828. return vertexShaderSource;
  829. }
  830. let attr = "attribute float batchId;\n";
  831. attr += "attribute float applyOffset;";
  832. let modifiedShader = vertexShaderSource.replace(
  833. /attribute\s+float\s+batchId;/g,
  834. attr
  835. );
  836. let str = "vec4 $1 = czm_computePosition();\n";
  837. str += " if (czm_sceneMode == czm_sceneMode3D)\n";
  838. str += " {\n";
  839. str +=
  840. " $1 = $1 + vec4(czm_batchTable_offset(batchId) * applyOffset, 0.0);";
  841. str += " }\n";
  842. str += " else\n";
  843. str += " {\n";
  844. str +=
  845. " $1 = $1 + vec4(czm_batchTable_offset2D(batchId) * applyOffset, 0.0);";
  846. str += " }\n";
  847. modifiedShader = modifiedShader.replace(
  848. /vec4\s+([A-Za-z0-9_]+)\s+=\s+czm_computePosition\(\);/g,
  849. str
  850. );
  851. return modifiedShader;
  852. };
  853. Primitive._appendDistanceDisplayConditionToShader = function (
  854. primitive,
  855. vertexShaderSource,
  856. scene3DOnly
  857. ) {
  858. if (
  859. !defined(primitive._batchTableAttributeIndices.distanceDisplayCondition)
  860. ) {
  861. return vertexShaderSource;
  862. }
  863. const renamedVS = ShaderSource.replaceMain(
  864. vertexShaderSource,
  865. "czm_non_distanceDisplayCondition_main"
  866. );
  867. let distanceDisplayConditionMain =
  868. "void main() \n" +
  869. "{ \n" +
  870. " czm_non_distanceDisplayCondition_main(); \n" +
  871. " vec2 distanceDisplayCondition = czm_batchTable_distanceDisplayCondition(batchId);\n" +
  872. " vec3 boundingSphereCenter3DHigh = czm_batchTable_boundingSphereCenter3DHigh(batchId);\n" +
  873. " vec3 boundingSphereCenter3DLow = czm_batchTable_boundingSphereCenter3DLow(batchId);\n" +
  874. " float boundingSphereRadius = czm_batchTable_boundingSphereRadius(batchId);\n";
  875. if (!scene3DOnly) {
  876. distanceDisplayConditionMain +=
  877. " vec3 boundingSphereCenter2DHigh = czm_batchTable_boundingSphereCenter2DHigh(batchId);\n" +
  878. " vec3 boundingSphereCenter2DLow = czm_batchTable_boundingSphereCenter2DLow(batchId);\n" +
  879. " vec4 centerRTE;\n" +
  880. " if (czm_morphTime == 1.0)\n" +
  881. " {\n" +
  882. " centerRTE = czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow);\n" +
  883. " }\n" +
  884. " else if (czm_morphTime == 0.0)\n" +
  885. " {\n" +
  886. " centerRTE = czm_translateRelativeToEye(boundingSphereCenter2DHigh.zxy, boundingSphereCenter2DLow.zxy);\n" +
  887. " }\n" +
  888. " else\n" +
  889. " {\n" +
  890. " centerRTE = czm_columbusViewMorph(\n" +
  891. " czm_translateRelativeToEye(boundingSphereCenter2DHigh.zxy, boundingSphereCenter2DLow.zxy),\n" +
  892. " czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow),\n" +
  893. " czm_morphTime);\n" +
  894. " }\n";
  895. } else {
  896. distanceDisplayConditionMain +=
  897. " vec4 centerRTE = czm_translateRelativeToEye(boundingSphereCenter3DHigh, boundingSphereCenter3DLow);\n";
  898. }
  899. distanceDisplayConditionMain +=
  900. " float radiusSq = boundingSphereRadius * boundingSphereRadius; \n" +
  901. " float distanceSq; \n" +
  902. " if (czm_sceneMode == czm_sceneMode2D) \n" +
  903. " { \n" +
  904. " distanceSq = czm_eyeHeight2D.y - radiusSq; \n" +
  905. " } \n" +
  906. " else \n" +
  907. " { \n" +
  908. " distanceSq = dot(centerRTE.xyz, centerRTE.xyz) - radiusSq; \n" +
  909. " } \n" +
  910. " distanceSq = max(distanceSq, 0.0); \n" +
  911. " float nearSq = distanceDisplayCondition.x * distanceDisplayCondition.x; \n" +
  912. " float farSq = distanceDisplayCondition.y * distanceDisplayCondition.y; \n" +
  913. " float show = (distanceSq >= nearSq && distanceSq <= farSq) ? 1.0 : 0.0; \n" +
  914. " gl_Position *= show; \n" +
  915. "}";
  916. return `${renamedVS}\n${distanceDisplayConditionMain}`;
  917. };
  918. function modifyForEncodedNormals(primitive, vertexShaderSource) {
  919. if (!primitive.compressVertices) {
  920. return vertexShaderSource;
  921. }
  922. const containsNormal =
  923. vertexShaderSource.search(/attribute\s+vec3\s+normal;/g) !== -1;
  924. const containsSt =
  925. vertexShaderSource.search(/attribute\s+vec2\s+st;/g) !== -1;
  926. if (!containsNormal && !containsSt) {
  927. return vertexShaderSource;
  928. }
  929. const containsTangent =
  930. vertexShaderSource.search(/attribute\s+vec3\s+tangent;/g) !== -1;
  931. const containsBitangent =
  932. vertexShaderSource.search(/attribute\s+vec3\s+bitangent;/g) !== -1;
  933. let numComponents = containsSt && containsNormal ? 2.0 : 1.0;
  934. numComponents += containsTangent || containsBitangent ? 1 : 0;
  935. const type = numComponents > 1 ? `vec${numComponents}` : "float";
  936. const attributeName = "compressedAttributes";
  937. const attributeDecl = `attribute ${type} ${attributeName};`;
  938. let globalDecl = "";
  939. let decode = "";
  940. if (containsSt) {
  941. globalDecl += "vec2 st;\n";
  942. const stComponent =
  943. numComponents > 1 ? `${attributeName}.x` : attributeName;
  944. decode += ` st = czm_decompressTextureCoordinates(${stComponent});\n`;
  945. }
  946. if (containsNormal && containsTangent && containsBitangent) {
  947. globalDecl += "vec3 normal;\n" + "vec3 tangent;\n" + "vec3 bitangent;\n";
  948. decode += ` czm_octDecode(${attributeName}.${
  949. containsSt ? "yz" : "xy"
  950. }, normal, tangent, bitangent);\n`;
  951. } else {
  952. if (containsNormal) {
  953. globalDecl += "vec3 normal;\n";
  954. decode += ` normal = czm_octDecode(${attributeName}${
  955. numComponents > 1 ? `.${containsSt ? "y" : "x"}` : ""
  956. });\n`;
  957. }
  958. if (containsTangent) {
  959. globalDecl += "vec3 tangent;\n";
  960. decode += ` tangent = czm_octDecode(${attributeName}.${
  961. containsSt && containsNormal ? "z" : "y"
  962. });\n`;
  963. }
  964. if (containsBitangent) {
  965. globalDecl += "vec3 bitangent;\n";
  966. decode += ` bitangent = czm_octDecode(${attributeName}.${
  967. containsSt && containsNormal ? "z" : "y"
  968. });\n`;
  969. }
  970. }
  971. let modifiedVS = vertexShaderSource;
  972. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+normal;/g, "");
  973. modifiedVS = modifiedVS.replace(/attribute\s+vec2\s+st;/g, "");
  974. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+tangent;/g, "");
  975. modifiedVS = modifiedVS.replace(/attribute\s+vec3\s+bitangent;/g, "");
  976. modifiedVS = ShaderSource.replaceMain(modifiedVS, "czm_non_compressed_main");
  977. const compressedMain =
  978. `${"void main() \n" + "{ \n"}${decode} czm_non_compressed_main(); \n` +
  979. `}`;
  980. return [attributeDecl, globalDecl, modifiedVS, compressedMain].join("\n");
  981. }
  982. function depthClampVS(vertexShaderSource) {
  983. let modifiedVS = ShaderSource.replaceMain(
  984. vertexShaderSource,
  985. "czm_non_depth_clamp_main"
  986. );
  987. modifiedVS +=
  988. "void main() {\n" +
  989. " czm_non_depth_clamp_main();\n" +
  990. " gl_Position = czm_depthClamp(gl_Position);" +
  991. "}\n";
  992. return modifiedVS;
  993. }
  994. function depthClampFS(fragmentShaderSource) {
  995. let modifiedFS = ShaderSource.replaceMain(
  996. fragmentShaderSource,
  997. "czm_non_depth_clamp_main"
  998. );
  999. modifiedFS +=
  1000. "void main() {\n" +
  1001. " czm_non_depth_clamp_main();\n" +
  1002. "#if defined(GL_EXT_frag_depth)\n" +
  1003. " #if defined(LOG_DEPTH)\n" +
  1004. " czm_writeLogDepth();\n" +
  1005. " #else\n" +
  1006. " czm_writeDepthClamp();\n" +
  1007. " #endif\n" +
  1008. "#endif\n" +
  1009. "}\n";
  1010. modifiedFS = `${
  1011. "#ifdef GL_EXT_frag_depth\n" +
  1012. "#extension GL_EXT_frag_depth : enable\n" +
  1013. "#endif\n"
  1014. }${modifiedFS}`;
  1015. return modifiedFS;
  1016. }
  1017. function validateShaderMatching(shaderProgram, attributeLocations) {
  1018. // For a VAO and shader program to be compatible, the VAO must have
  1019. // all active attribute in the shader program. The VAO may have
  1020. // extra attributes with the only concern being a potential
  1021. // performance hit due to extra memory bandwidth and cache pollution.
  1022. // The shader source could have extra attributes that are not used,
  1023. // but there is no guarantee they will be optimized out.
  1024. //
  1025. // Here, we validate that the VAO has all attributes required
  1026. // to match the shader program.
  1027. const shaderAttributes = shaderProgram.vertexAttributes;
  1028. //>>includeStart('debug', pragmas.debug);
  1029. for (const name in shaderAttributes) {
  1030. if (shaderAttributes.hasOwnProperty(name)) {
  1031. if (!defined(attributeLocations[name])) {
  1032. throw new DeveloperError(
  1033. `Appearance/Geometry mismatch. The appearance requires vertex shader attribute input '${name}', which was not computed as part of the Geometry. Use the appearance's vertexFormat property when constructing the geometry.`
  1034. );
  1035. }
  1036. }
  1037. }
  1038. //>>includeEnd('debug');
  1039. }
  1040. function getUniformFunction(uniforms, name) {
  1041. return function () {
  1042. return uniforms[name];
  1043. };
  1044. }
  1045. const numberOfCreationWorkers = Math.max(
  1046. FeatureDetection.hardwareConcurrency - 1,
  1047. 1
  1048. );
  1049. let createGeometryTaskProcessors;
  1050. const combineGeometryTaskProcessor = new TaskProcessor("combineGeometry");
  1051. function loadAsynchronous(primitive, frameState) {
  1052. let instances;
  1053. let geometry;
  1054. let i;
  1055. let j;
  1056. const instanceIds = primitive._instanceIds;
  1057. if (primitive._state === PrimitiveState.READY) {
  1058. instances = Array.isArray(primitive.geometryInstances)
  1059. ? primitive.geometryInstances
  1060. : [primitive.geometryInstances];
  1061. const length = (primitive._numberOfInstances = instances.length);
  1062. const promises = [];
  1063. let subTasks = [];
  1064. for (i = 0; i < length; ++i) {
  1065. geometry = instances[i].geometry;
  1066. instanceIds.push(instances[i].id);
  1067. //>>includeStart('debug', pragmas.debug);
  1068. if (!defined(geometry._workerName)) {
  1069. throw new DeveloperError(
  1070. "_workerName must be defined for asynchronous geometry."
  1071. );
  1072. }
  1073. //>>includeEnd('debug');
  1074. subTasks.push({
  1075. moduleName: geometry._workerName,
  1076. geometry: geometry,
  1077. });
  1078. }
  1079. if (!defined(createGeometryTaskProcessors)) {
  1080. createGeometryTaskProcessors = new Array(numberOfCreationWorkers);
  1081. for (i = 0; i < numberOfCreationWorkers; i++) {
  1082. createGeometryTaskProcessors[i] = new TaskProcessor("createGeometry");
  1083. }
  1084. }
  1085. let subTask;
  1086. subTasks = subdivideArray(subTasks, numberOfCreationWorkers);
  1087. for (i = 0; i < subTasks.length; i++) {
  1088. let packedLength = 0;
  1089. const workerSubTasks = subTasks[i];
  1090. const workerSubTasksLength = workerSubTasks.length;
  1091. for (j = 0; j < workerSubTasksLength; ++j) {
  1092. subTask = workerSubTasks[j];
  1093. geometry = subTask.geometry;
  1094. if (defined(geometry.constructor.pack)) {
  1095. subTask.offset = packedLength;
  1096. packedLength += defaultValue(
  1097. geometry.constructor.packedLength,
  1098. geometry.packedLength
  1099. );
  1100. }
  1101. }
  1102. let subTaskTransferableObjects;
  1103. if (packedLength > 0) {
  1104. const array = new Float64Array(packedLength);
  1105. subTaskTransferableObjects = [array.buffer];
  1106. for (j = 0; j < workerSubTasksLength; ++j) {
  1107. subTask = workerSubTasks[j];
  1108. geometry = subTask.geometry;
  1109. if (defined(geometry.constructor.pack)) {
  1110. geometry.constructor.pack(geometry, array, subTask.offset);
  1111. subTask.geometry = array;
  1112. }
  1113. }
  1114. }
  1115. promises.push(
  1116. createGeometryTaskProcessors[i].scheduleTask(
  1117. {
  1118. subTasks: subTasks[i],
  1119. },
  1120. subTaskTransferableObjects
  1121. )
  1122. );
  1123. }
  1124. primitive._state = PrimitiveState.CREATING;
  1125. Promise.all(promises)
  1126. .then(function (results) {
  1127. primitive._createGeometryResults = results;
  1128. primitive._state = PrimitiveState.CREATED;
  1129. })
  1130. .catch(function (error) {
  1131. setReady(primitive, frameState, PrimitiveState.FAILED, error);
  1132. });
  1133. } else if (primitive._state === PrimitiveState.CREATED) {
  1134. const transferableObjects = [];
  1135. instances = Array.isArray(primitive.geometryInstances)
  1136. ? primitive.geometryInstances
  1137. : [primitive.geometryInstances];
  1138. const scene3DOnly = frameState.scene3DOnly;
  1139. const projection = frameState.mapProjection;
  1140. const promise = combineGeometryTaskProcessor.scheduleTask(
  1141. PrimitivePipeline.packCombineGeometryParameters(
  1142. {
  1143. createGeometryResults: primitive._createGeometryResults,
  1144. instances: instances,
  1145. ellipsoid: projection.ellipsoid,
  1146. projection: projection,
  1147. elementIndexUintSupported: frameState.context.elementIndexUint,
  1148. scene3DOnly: scene3DOnly,
  1149. vertexCacheOptimize: primitive.vertexCacheOptimize,
  1150. compressVertices: primitive.compressVertices,
  1151. modelMatrix: primitive.modelMatrix,
  1152. createPickOffsets: primitive._createPickOffsets,
  1153. },
  1154. transferableObjects
  1155. ),
  1156. transferableObjects
  1157. );
  1158. primitive._createGeometryResults = undefined;
  1159. primitive._state = PrimitiveState.COMBINING;
  1160. Promise.resolve(promise)
  1161. .then(function (packedResult) {
  1162. const result = PrimitivePipeline.unpackCombineGeometryResults(
  1163. packedResult
  1164. );
  1165. primitive._geometries = result.geometries;
  1166. primitive._attributeLocations = result.attributeLocations;
  1167. primitive.modelMatrix = Matrix4.clone(
  1168. result.modelMatrix,
  1169. primitive.modelMatrix
  1170. );
  1171. primitive._pickOffsets = result.pickOffsets;
  1172. primitive._offsetInstanceExtend = result.offsetInstanceExtend;
  1173. primitive._instanceBoundingSpheres = result.boundingSpheres;
  1174. primitive._instanceBoundingSpheresCV = result.boundingSpheresCV;
  1175. if (
  1176. defined(primitive._geometries) &&
  1177. primitive._geometries.length > 0
  1178. ) {
  1179. primitive._recomputeBoundingSpheres = true;
  1180. primitive._state = PrimitiveState.COMBINED;
  1181. } else {
  1182. setReady(primitive, frameState, PrimitiveState.FAILED, undefined);
  1183. }
  1184. })
  1185. .catch(function (error) {
  1186. setReady(primitive, frameState, PrimitiveState.FAILED, error);
  1187. });
  1188. }
  1189. }
  1190. function loadSynchronous(primitive, frameState) {
  1191. const instances = Array.isArray(primitive.geometryInstances)
  1192. ? primitive.geometryInstances
  1193. : [primitive.geometryInstances];
  1194. const length = (primitive._numberOfInstances = instances.length);
  1195. const clonedInstances = new Array(length);
  1196. const instanceIds = primitive._instanceIds;
  1197. let instance;
  1198. let i;
  1199. let geometryIndex = 0;
  1200. for (i = 0; i < length; i++) {
  1201. instance = instances[i];
  1202. const geometry = instance.geometry;
  1203. let createdGeometry;
  1204. if (defined(geometry.attributes) && defined(geometry.primitiveType)) {
  1205. createdGeometry = cloneGeometry(geometry);
  1206. } else {
  1207. createdGeometry = geometry.constructor.createGeometry(geometry);
  1208. }
  1209. clonedInstances[geometryIndex++] = cloneInstance(instance, createdGeometry);
  1210. instanceIds.push(instance.id);
  1211. }
  1212. clonedInstances.length = geometryIndex;
  1213. const scene3DOnly = frameState.scene3DOnly;
  1214. const projection = frameState.mapProjection;
  1215. const result = PrimitivePipeline.combineGeometry({
  1216. instances: clonedInstances,
  1217. ellipsoid: projection.ellipsoid,
  1218. projection: projection,
  1219. elementIndexUintSupported: frameState.context.elementIndexUint,
  1220. scene3DOnly: scene3DOnly,
  1221. vertexCacheOptimize: primitive.vertexCacheOptimize,
  1222. compressVertices: primitive.compressVertices,
  1223. modelMatrix: primitive.modelMatrix,
  1224. createPickOffsets: primitive._createPickOffsets,
  1225. });
  1226. primitive._geometries = result.geometries;
  1227. primitive._attributeLocations = result.attributeLocations;
  1228. primitive.modelMatrix = Matrix4.clone(
  1229. result.modelMatrix,
  1230. primitive.modelMatrix
  1231. );
  1232. primitive._pickOffsets = result.pickOffsets;
  1233. primitive._offsetInstanceExtend = result.offsetInstanceExtend;
  1234. primitive._instanceBoundingSpheres = result.boundingSpheres;
  1235. primitive._instanceBoundingSpheresCV = result.boundingSpheresCV;
  1236. if (defined(primitive._geometries) && primitive._geometries.length > 0) {
  1237. primitive._recomputeBoundingSpheres = true;
  1238. primitive._state = PrimitiveState.COMBINED;
  1239. } else {
  1240. setReady(primitive, frameState, PrimitiveState.FAILED, undefined);
  1241. }
  1242. }
  1243. function recomputeBoundingSpheres(primitive, frameState) {
  1244. const offsetIndex = primitive._batchTableAttributeIndices.offset;
  1245. if (!primitive._recomputeBoundingSpheres || !defined(offsetIndex)) {
  1246. primitive._recomputeBoundingSpheres = false;
  1247. return;
  1248. }
  1249. let i;
  1250. const offsetInstanceExtend = primitive._offsetInstanceExtend;
  1251. const boundingSpheres = primitive._instanceBoundingSpheres;
  1252. const length = boundingSpheres.length;
  1253. let newBoundingSpheres = primitive._tempBoundingSpheres;
  1254. if (!defined(newBoundingSpheres)) {
  1255. newBoundingSpheres = new Array(length);
  1256. for (i = 0; i < length; i++) {
  1257. newBoundingSpheres[i] = new BoundingSphere();
  1258. }
  1259. primitive._tempBoundingSpheres = newBoundingSpheres;
  1260. }
  1261. for (i = 0; i < length; ++i) {
  1262. let newBS = newBoundingSpheres[i];
  1263. const offset = primitive._batchTable.getBatchedAttribute(
  1264. i,
  1265. offsetIndex,
  1266. new Cartesian3()
  1267. );
  1268. newBS = boundingSpheres[i].clone(newBS);
  1269. transformBoundingSphere(newBS, offset, offsetInstanceExtend[i]);
  1270. }
  1271. const combinedBS = [];
  1272. const combinedWestBS = [];
  1273. const combinedEastBS = [];
  1274. for (i = 0; i < length; ++i) {
  1275. const bs = newBoundingSpheres[i];
  1276. const minX = bs.center.x - bs.radius;
  1277. if (
  1278. minX > 0 ||
  1279. BoundingSphere.intersectPlane(bs, Plane.ORIGIN_ZX_PLANE) !==
  1280. Intersect.INTERSECTING
  1281. ) {
  1282. combinedBS.push(bs);
  1283. } else {
  1284. combinedWestBS.push(bs);
  1285. combinedEastBS.push(bs);
  1286. }
  1287. }
  1288. let resultBS1 = combinedBS[0];
  1289. let resultBS2 = combinedEastBS[0];
  1290. let resultBS3 = combinedWestBS[0];
  1291. for (i = 1; i < combinedBS.length; i++) {
  1292. resultBS1 = BoundingSphere.union(resultBS1, combinedBS[i]);
  1293. }
  1294. for (i = 1; i < combinedEastBS.length; i++) {
  1295. resultBS2 = BoundingSphere.union(resultBS2, combinedEastBS[i]);
  1296. }
  1297. for (i = 1; i < combinedWestBS.length; i++) {
  1298. resultBS3 = BoundingSphere.union(resultBS3, combinedWestBS[i]);
  1299. }
  1300. const result = [];
  1301. if (defined(resultBS1)) {
  1302. result.push(resultBS1);
  1303. }
  1304. if (defined(resultBS2)) {
  1305. result.push(resultBS2);
  1306. }
  1307. if (defined(resultBS3)) {
  1308. result.push(resultBS3);
  1309. }
  1310. for (i = 0; i < result.length; i++) {
  1311. const boundingSphere = result[i].clone(primitive._boundingSpheres[i]);
  1312. primitive._boundingSpheres[i] = boundingSphere;
  1313. primitive._boundingSphereCV[i] = BoundingSphere.projectTo2D(
  1314. boundingSphere,
  1315. frameState.mapProjection,
  1316. primitive._boundingSphereCV[i]
  1317. );
  1318. }
  1319. Primitive._updateBoundingVolumes(
  1320. primitive,
  1321. frameState,
  1322. primitive.modelMatrix,
  1323. true
  1324. );
  1325. primitive._recomputeBoundingSpheres = false;
  1326. }
  1327. const scratchBoundingSphereCenterEncoded = new EncodedCartesian3();
  1328. const scratchBoundingSphereCartographic = new Cartographic();
  1329. const scratchBoundingSphereCenter2D = new Cartesian3();
  1330. const scratchBoundingSphere = new BoundingSphere();
  1331. function updateBatchTableBoundingSpheres(primitive, frameState) {
  1332. const hasDistanceDisplayCondition = defined(
  1333. primitive._batchTableAttributeIndices.distanceDisplayCondition
  1334. );
  1335. if (
  1336. !hasDistanceDisplayCondition ||
  1337. primitive._batchTableBoundingSpheresUpdated
  1338. ) {
  1339. return;
  1340. }
  1341. const indices = primitive._batchTableBoundingSphereAttributeIndices;
  1342. const center3DHighIndex = indices.center3DHigh;
  1343. const center3DLowIndex = indices.center3DLow;
  1344. const center2DHighIndex = indices.center2DHigh;
  1345. const center2DLowIndex = indices.center2DLow;
  1346. const radiusIndex = indices.radius;
  1347. const projection = frameState.mapProjection;
  1348. const ellipsoid = projection.ellipsoid;
  1349. const batchTable = primitive._batchTable;
  1350. const boundingSpheres = primitive._instanceBoundingSpheres;
  1351. const length = boundingSpheres.length;
  1352. for (let i = 0; i < length; ++i) {
  1353. let boundingSphere = boundingSpheres[i];
  1354. if (!defined(boundingSphere)) {
  1355. continue;
  1356. }
  1357. const modelMatrix = primitive.modelMatrix;
  1358. if (defined(modelMatrix)) {
  1359. boundingSphere = BoundingSphere.transform(
  1360. boundingSphere,
  1361. modelMatrix,
  1362. scratchBoundingSphere
  1363. );
  1364. }
  1365. const center = boundingSphere.center;
  1366. const radius = boundingSphere.radius;
  1367. let encodedCenter = EncodedCartesian3.fromCartesian(
  1368. center,
  1369. scratchBoundingSphereCenterEncoded
  1370. );
  1371. batchTable.setBatchedAttribute(i, center3DHighIndex, encodedCenter.high);
  1372. batchTable.setBatchedAttribute(i, center3DLowIndex, encodedCenter.low);
  1373. if (!frameState.scene3DOnly) {
  1374. const cartographic = ellipsoid.cartesianToCartographic(
  1375. center,
  1376. scratchBoundingSphereCartographic
  1377. );
  1378. const center2D = projection.project(
  1379. cartographic,
  1380. scratchBoundingSphereCenter2D
  1381. );
  1382. encodedCenter = EncodedCartesian3.fromCartesian(
  1383. center2D,
  1384. scratchBoundingSphereCenterEncoded
  1385. );
  1386. batchTable.setBatchedAttribute(i, center2DHighIndex, encodedCenter.high);
  1387. batchTable.setBatchedAttribute(i, center2DLowIndex, encodedCenter.low);
  1388. }
  1389. batchTable.setBatchedAttribute(i, radiusIndex, radius);
  1390. }
  1391. primitive._batchTableBoundingSpheresUpdated = true;
  1392. }
  1393. const offsetScratchCartesian = new Cartesian3();
  1394. const offsetCenterScratch = new Cartesian3();
  1395. function updateBatchTableOffsets(primitive, frameState) {
  1396. const hasOffset = defined(primitive._batchTableAttributeIndices.offset);
  1397. if (
  1398. !hasOffset ||
  1399. primitive._batchTableOffsetsUpdated ||
  1400. frameState.scene3DOnly
  1401. ) {
  1402. return;
  1403. }
  1404. const index2D = primitive._batchTableOffsetAttribute2DIndex;
  1405. const projection = frameState.mapProjection;
  1406. const ellipsoid = projection.ellipsoid;
  1407. const batchTable = primitive._batchTable;
  1408. const boundingSpheres = primitive._instanceBoundingSpheres;
  1409. const length = boundingSpheres.length;
  1410. for (let i = 0; i < length; ++i) {
  1411. let boundingSphere = boundingSpheres[i];
  1412. if (!defined(boundingSphere)) {
  1413. continue;
  1414. }
  1415. const offset = batchTable.getBatchedAttribute(
  1416. i,
  1417. primitive._batchTableAttributeIndices.offset
  1418. );
  1419. if (Cartesian3.equals(offset, Cartesian3.ZERO)) {
  1420. batchTable.setBatchedAttribute(i, index2D, Cartesian3.ZERO);
  1421. continue;
  1422. }
  1423. const modelMatrix = primitive.modelMatrix;
  1424. if (defined(modelMatrix)) {
  1425. boundingSphere = BoundingSphere.transform(
  1426. boundingSphere,
  1427. modelMatrix,
  1428. scratchBoundingSphere
  1429. );
  1430. }
  1431. let center = boundingSphere.center;
  1432. center = ellipsoid.scaleToGeodeticSurface(center, offsetCenterScratch);
  1433. let cartographic = ellipsoid.cartesianToCartographic(
  1434. center,
  1435. scratchBoundingSphereCartographic
  1436. );
  1437. const center2D = projection.project(
  1438. cartographic,
  1439. scratchBoundingSphereCenter2D
  1440. );
  1441. const newPoint = Cartesian3.add(offset, center, offsetScratchCartesian);
  1442. cartographic = ellipsoid.cartesianToCartographic(newPoint, cartographic);
  1443. const newPointProjected = projection.project(
  1444. cartographic,
  1445. offsetScratchCartesian
  1446. );
  1447. const newVector = Cartesian3.subtract(
  1448. newPointProjected,
  1449. center2D,
  1450. offsetScratchCartesian
  1451. );
  1452. const x = newVector.x;
  1453. newVector.x = newVector.z;
  1454. newVector.z = newVector.y;
  1455. newVector.y = x;
  1456. batchTable.setBatchedAttribute(i, index2D, newVector);
  1457. }
  1458. primitive._batchTableOffsetsUpdated = true;
  1459. }
  1460. function createVertexArray(primitive, frameState) {
  1461. const attributeLocations = primitive._attributeLocations;
  1462. const geometries = primitive._geometries;
  1463. const scene3DOnly = frameState.scene3DOnly;
  1464. const context = frameState.context;
  1465. const va = [];
  1466. const length = geometries.length;
  1467. for (let i = 0; i < length; ++i) {
  1468. const geometry = geometries[i];
  1469. va.push(
  1470. VertexArray.fromGeometry({
  1471. context: context,
  1472. geometry: geometry,
  1473. attributeLocations: attributeLocations,
  1474. bufferUsage: BufferUsage.STATIC_DRAW,
  1475. interleave: primitive._interleave,
  1476. })
  1477. );
  1478. if (defined(primitive._createBoundingVolumeFunction)) {
  1479. primitive._createBoundingVolumeFunction(frameState, geometry);
  1480. } else {
  1481. primitive._boundingSpheres.push(
  1482. BoundingSphere.clone(geometry.boundingSphere)
  1483. );
  1484. primitive._boundingSphereWC.push(new BoundingSphere());
  1485. if (!scene3DOnly) {
  1486. const center = geometry.boundingSphereCV.center;
  1487. const x = center.x;
  1488. const y = center.y;
  1489. const z = center.z;
  1490. center.x = z;
  1491. center.y = x;
  1492. center.z = y;
  1493. primitive._boundingSphereCV.push(
  1494. BoundingSphere.clone(geometry.boundingSphereCV)
  1495. );
  1496. primitive._boundingSphere2D.push(new BoundingSphere());
  1497. primitive._boundingSphereMorph.push(new BoundingSphere());
  1498. }
  1499. }
  1500. }
  1501. primitive._va = va;
  1502. primitive._primitiveType = geometries[0].primitiveType;
  1503. if (primitive.releaseGeometryInstances) {
  1504. primitive.geometryInstances = undefined;
  1505. }
  1506. primitive._geometries = undefined;
  1507. setReady(primitive, frameState, PrimitiveState.COMPLETE, undefined);
  1508. }
  1509. function createRenderStates(primitive, context, appearance, twoPasses) {
  1510. let renderState = appearance.getRenderState();
  1511. let rs;
  1512. if (twoPasses) {
  1513. rs = clone(renderState, false);
  1514. rs.cull = {
  1515. enabled: true,
  1516. face: CullFace.BACK,
  1517. };
  1518. primitive._frontFaceRS = RenderState.fromCache(rs);
  1519. rs.cull.face = CullFace.FRONT;
  1520. primitive._backFaceRS = RenderState.fromCache(rs);
  1521. } else {
  1522. primitive._frontFaceRS = RenderState.fromCache(renderState);
  1523. primitive._backFaceRS = primitive._frontFaceRS;
  1524. }
  1525. rs = clone(renderState, false);
  1526. if (defined(primitive._depthFailAppearance)) {
  1527. rs.depthTest.enabled = false;
  1528. }
  1529. if (defined(primitive._depthFailAppearance)) {
  1530. renderState = primitive._depthFailAppearance.getRenderState();
  1531. rs = clone(renderState, false);
  1532. rs.depthTest.func = DepthFunction.GREATER;
  1533. if (twoPasses) {
  1534. rs.cull = {
  1535. enabled: true,
  1536. face: CullFace.BACK,
  1537. };
  1538. primitive._frontFaceDepthFailRS = RenderState.fromCache(rs);
  1539. rs.cull.face = CullFace.FRONT;
  1540. primitive._backFaceDepthFailRS = RenderState.fromCache(rs);
  1541. } else {
  1542. primitive._frontFaceDepthFailRS = RenderState.fromCache(rs);
  1543. primitive._backFaceDepthFailRS = primitive._frontFaceRS;
  1544. }
  1545. }
  1546. }
  1547. function createShaderProgram(primitive, frameState, appearance) {
  1548. const context = frameState.context;
  1549. const attributeLocations = primitive._attributeLocations;
  1550. let vs = primitive._batchTable.getVertexShaderCallback()(
  1551. appearance.vertexShaderSource
  1552. );
  1553. vs = Primitive._appendOffsetToShader(primitive, vs);
  1554. vs = Primitive._appendShowToShader(primitive, vs);
  1555. vs = Primitive._appendDistanceDisplayConditionToShader(
  1556. primitive,
  1557. vs,
  1558. frameState.scene3DOnly
  1559. );
  1560. vs = appendPickToVertexShader(vs);
  1561. vs = Primitive._updateColorAttribute(primitive, vs, false);
  1562. vs = modifyForEncodedNormals(primitive, vs);
  1563. vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly);
  1564. let fs = appearance.getFragmentShaderSource();
  1565. fs = appendPickToFragmentShader(fs);
  1566. primitive._sp = ShaderProgram.replaceCache({
  1567. context: context,
  1568. shaderProgram: primitive._sp,
  1569. vertexShaderSource: vs,
  1570. fragmentShaderSource: fs,
  1571. attributeLocations: attributeLocations,
  1572. });
  1573. validateShaderMatching(primitive._sp, attributeLocations);
  1574. if (defined(primitive._depthFailAppearance)) {
  1575. vs = primitive._batchTable.getVertexShaderCallback()(
  1576. primitive._depthFailAppearance.vertexShaderSource
  1577. );
  1578. vs = Primitive._appendShowToShader(primitive, vs);
  1579. vs = Primitive._appendDistanceDisplayConditionToShader(
  1580. primitive,
  1581. vs,
  1582. frameState.scene3DOnly
  1583. );
  1584. vs = appendPickToVertexShader(vs);
  1585. vs = Primitive._updateColorAttribute(primitive, vs, true);
  1586. vs = modifyForEncodedNormals(primitive, vs);
  1587. vs = Primitive._modifyShaderPosition(primitive, vs, frameState.scene3DOnly);
  1588. vs = depthClampVS(vs);
  1589. fs = primitive._depthFailAppearance.getFragmentShaderSource();
  1590. fs = appendPickToFragmentShader(fs);
  1591. fs = depthClampFS(fs);
  1592. primitive._spDepthFail = ShaderProgram.replaceCache({
  1593. context: context,
  1594. shaderProgram: primitive._spDepthFail,
  1595. vertexShaderSource: vs,
  1596. fragmentShaderSource: fs,
  1597. attributeLocations: attributeLocations,
  1598. });
  1599. validateShaderMatching(primitive._spDepthFail, attributeLocations);
  1600. }
  1601. }
  1602. const modifiedModelViewScratch = new Matrix4();
  1603. const rtcScratch = new Cartesian3();
  1604. function getUniforms(primitive, appearance, material, frameState) {
  1605. // Create uniform map by combining uniforms from the appearance and material if either have uniforms.
  1606. const materialUniformMap = defined(material) ? material._uniforms : undefined;
  1607. const appearanceUniformMap = {};
  1608. const appearanceUniforms = appearance.uniforms;
  1609. if (defined(appearanceUniforms)) {
  1610. // Convert to uniform map of functions for the renderer
  1611. for (const name in appearanceUniforms) {
  1612. if (appearanceUniforms.hasOwnProperty(name)) {
  1613. //>>includeStart('debug', pragmas.debug);
  1614. if (defined(materialUniformMap) && defined(materialUniformMap[name])) {
  1615. // Later, we could rename uniforms behind-the-scenes if needed.
  1616. throw new DeveloperError(
  1617. `Appearance and material have a uniform with the same name: ${name}`
  1618. );
  1619. }
  1620. //>>includeEnd('debug');
  1621. appearanceUniformMap[name] = getUniformFunction(
  1622. appearanceUniforms,
  1623. name
  1624. );
  1625. }
  1626. }
  1627. }
  1628. let uniforms = combine(appearanceUniformMap, materialUniformMap);
  1629. uniforms = primitive._batchTable.getUniformMapCallback()(uniforms);
  1630. if (defined(primitive.rtcCenter)) {
  1631. uniforms.u_modifiedModelView = function () {
  1632. const viewMatrix = frameState.context.uniformState.view;
  1633. Matrix4.multiply(
  1634. viewMatrix,
  1635. primitive._modelMatrix,
  1636. modifiedModelViewScratch
  1637. );
  1638. Matrix4.multiplyByPoint(
  1639. modifiedModelViewScratch,
  1640. primitive.rtcCenter,
  1641. rtcScratch
  1642. );
  1643. Matrix4.setTranslation(
  1644. modifiedModelViewScratch,
  1645. rtcScratch,
  1646. modifiedModelViewScratch
  1647. );
  1648. return modifiedModelViewScratch;
  1649. };
  1650. }
  1651. return uniforms;
  1652. }
  1653. function createCommands(
  1654. primitive,
  1655. appearance,
  1656. material,
  1657. translucent,
  1658. twoPasses,
  1659. colorCommands,
  1660. pickCommands,
  1661. frameState
  1662. ) {
  1663. const uniforms = getUniforms(primitive, appearance, material, frameState);
  1664. let depthFailUniforms;
  1665. if (defined(primitive._depthFailAppearance)) {
  1666. depthFailUniforms = getUniforms(
  1667. primitive,
  1668. primitive._depthFailAppearance,
  1669. primitive._depthFailAppearance.material,
  1670. frameState
  1671. );
  1672. }
  1673. const pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
  1674. let multiplier = twoPasses ? 2 : 1;
  1675. multiplier *= defined(primitive._depthFailAppearance) ? 2 : 1;
  1676. colorCommands.length = primitive._va.length * multiplier;
  1677. const length = colorCommands.length;
  1678. let vaIndex = 0;
  1679. for (let i = 0; i < length; ++i) {
  1680. let colorCommand;
  1681. if (twoPasses) {
  1682. colorCommand = colorCommands[i];
  1683. if (!defined(colorCommand)) {
  1684. colorCommand = colorCommands[i] = new DrawCommand({
  1685. owner: primitive,
  1686. primitiveType: primitive._primitiveType,
  1687. });
  1688. }
  1689. colorCommand.vertexArray = primitive._va[vaIndex];
  1690. colorCommand.renderState = primitive._backFaceRS;
  1691. colorCommand.shaderProgram = primitive._sp;
  1692. colorCommand.uniformMap = uniforms;
  1693. colorCommand.pass = pass;
  1694. ++i;
  1695. }
  1696. colorCommand = colorCommands[i];
  1697. if (!defined(colorCommand)) {
  1698. colorCommand = colorCommands[i] = new DrawCommand({
  1699. owner: primitive,
  1700. primitiveType: primitive._primitiveType,
  1701. });
  1702. }
  1703. colorCommand.vertexArray = primitive._va[vaIndex];
  1704. colorCommand.renderState = primitive._frontFaceRS;
  1705. colorCommand.shaderProgram = primitive._sp;
  1706. colorCommand.uniformMap = uniforms;
  1707. colorCommand.pass = pass;
  1708. if (defined(primitive._depthFailAppearance)) {
  1709. if (twoPasses) {
  1710. ++i;
  1711. colorCommand = colorCommands[i];
  1712. if (!defined(colorCommand)) {
  1713. colorCommand = colorCommands[i] = new DrawCommand({
  1714. owner: primitive,
  1715. primitiveType: primitive._primitiveType,
  1716. });
  1717. }
  1718. colorCommand.vertexArray = primitive._va[vaIndex];
  1719. colorCommand.renderState = primitive._backFaceDepthFailRS;
  1720. colorCommand.shaderProgram = primitive._spDepthFail;
  1721. colorCommand.uniformMap = depthFailUniforms;
  1722. colorCommand.pass = pass;
  1723. }
  1724. ++i;
  1725. colorCommand = colorCommands[i];
  1726. if (!defined(colorCommand)) {
  1727. colorCommand = colorCommands[i] = new DrawCommand({
  1728. owner: primitive,
  1729. primitiveType: primitive._primitiveType,
  1730. });
  1731. }
  1732. colorCommand.vertexArray = primitive._va[vaIndex];
  1733. colorCommand.renderState = primitive._frontFaceDepthFailRS;
  1734. colorCommand.shaderProgram = primitive._spDepthFail;
  1735. colorCommand.uniformMap = depthFailUniforms;
  1736. colorCommand.pass = pass;
  1737. }
  1738. ++vaIndex;
  1739. }
  1740. }
  1741. Primitive._updateBoundingVolumes = function (
  1742. primitive,
  1743. frameState,
  1744. modelMatrix,
  1745. forceUpdate
  1746. ) {
  1747. let i;
  1748. let length;
  1749. let boundingSphere;
  1750. if (forceUpdate || !Matrix4.equals(modelMatrix, primitive._modelMatrix)) {
  1751. Matrix4.clone(modelMatrix, primitive._modelMatrix);
  1752. length = primitive._boundingSpheres.length;
  1753. for (i = 0; i < length; ++i) {
  1754. boundingSphere = primitive._boundingSpheres[i];
  1755. if (defined(boundingSphere)) {
  1756. primitive._boundingSphereWC[i] = BoundingSphere.transform(
  1757. boundingSphere,
  1758. modelMatrix,
  1759. primitive._boundingSphereWC[i]
  1760. );
  1761. if (!frameState.scene3DOnly) {
  1762. primitive._boundingSphere2D[i] = BoundingSphere.clone(
  1763. primitive._boundingSphereCV[i],
  1764. primitive._boundingSphere2D[i]
  1765. );
  1766. primitive._boundingSphere2D[i].center.x = 0.0;
  1767. primitive._boundingSphereMorph[i] = BoundingSphere.union(
  1768. primitive._boundingSphereWC[i],
  1769. primitive._boundingSphereCV[i]
  1770. );
  1771. }
  1772. }
  1773. }
  1774. }
  1775. // Update bounding volumes for primitives that are sized in pixels.
  1776. // The pixel size in meters varies based on the distance from the camera.
  1777. const pixelSize = primitive.appearance.pixelSize;
  1778. if (defined(pixelSize)) {
  1779. length = primitive._boundingSpheres.length;
  1780. for (i = 0; i < length; ++i) {
  1781. boundingSphere = primitive._boundingSpheres[i];
  1782. const boundingSphereWC = primitive._boundingSphereWC[i];
  1783. const pixelSizeInMeters = frameState.camera.getPixelSize(
  1784. boundingSphere,
  1785. frameState.context.drawingBufferWidth,
  1786. frameState.context.drawingBufferHeight
  1787. );
  1788. const sizeInMeters = pixelSizeInMeters * pixelSize;
  1789. boundingSphereWC.radius = boundingSphere.radius + sizeInMeters;
  1790. }
  1791. }
  1792. };
  1793. function updateAndQueueCommands(
  1794. primitive,
  1795. frameState,
  1796. colorCommands,
  1797. pickCommands,
  1798. modelMatrix,
  1799. cull,
  1800. debugShowBoundingVolume,
  1801. twoPasses
  1802. ) {
  1803. //>>includeStart('debug', pragmas.debug);
  1804. if (
  1805. frameState.mode !== SceneMode.SCENE3D &&
  1806. !Matrix4.equals(modelMatrix, Matrix4.IDENTITY)
  1807. ) {
  1808. throw new DeveloperError(
  1809. "Primitive.modelMatrix is only supported in 3D mode."
  1810. );
  1811. }
  1812. //>>includeEnd('debug');
  1813. Primitive._updateBoundingVolumes(primitive, frameState, modelMatrix);
  1814. let boundingSpheres;
  1815. if (frameState.mode === SceneMode.SCENE3D) {
  1816. boundingSpheres = primitive._boundingSphereWC;
  1817. } else if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  1818. boundingSpheres = primitive._boundingSphereCV;
  1819. } else if (
  1820. frameState.mode === SceneMode.SCENE2D &&
  1821. defined(primitive._boundingSphere2D)
  1822. ) {
  1823. boundingSpheres = primitive._boundingSphere2D;
  1824. } else if (defined(primitive._boundingSphereMorph)) {
  1825. boundingSpheres = primitive._boundingSphereMorph;
  1826. }
  1827. const commandList = frameState.commandList;
  1828. const passes = frameState.passes;
  1829. if (passes.render || passes.pick) {
  1830. const allowPicking = primitive.allowPicking;
  1831. const castShadows = ShadowMode.castShadows(primitive.shadows);
  1832. const receiveShadows = ShadowMode.receiveShadows(primitive.shadows);
  1833. const colorLength = colorCommands.length;
  1834. let factor = twoPasses ? 2 : 1;
  1835. factor *= defined(primitive._depthFailAppearance) ? 2 : 1;
  1836. for (let j = 0; j < colorLength; ++j) {
  1837. const sphereIndex = Math.floor(j / factor);
  1838. const colorCommand = colorCommands[j];
  1839. colorCommand.modelMatrix = modelMatrix;
  1840. colorCommand.boundingVolume = boundingSpheres[sphereIndex];
  1841. colorCommand.cull = cull;
  1842. colorCommand.debugShowBoundingVolume = debugShowBoundingVolume;
  1843. colorCommand.castShadows = castShadows;
  1844. colorCommand.receiveShadows = receiveShadows;
  1845. if (allowPicking) {
  1846. colorCommand.pickId = "v_pickColor";
  1847. } else {
  1848. colorCommand.pickId = undefined;
  1849. }
  1850. commandList.push(colorCommand);
  1851. }
  1852. }
  1853. }
  1854. /**
  1855. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  1856. * get the draw commands needed to render this primitive.
  1857. * <p>
  1858. * Do not call this function directly. This is documented just to
  1859. * list the exceptions that may be propagated when the scene is rendered:
  1860. * </p>
  1861. *
  1862. * @exception {DeveloperError} All instance geometries must have the same primitiveType.
  1863. * @exception {DeveloperError} Appearance and material have a uniform with the same name.
  1864. * @exception {DeveloperError} Primitive.modelMatrix is only supported in 3D mode.
  1865. * @exception {RuntimeError} Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero.
  1866. */
  1867. Primitive.prototype.update = function (frameState) {
  1868. if (
  1869. (!defined(this.geometryInstances) && this._va.length === 0) ||
  1870. (defined(this.geometryInstances) &&
  1871. Array.isArray(this.geometryInstances) &&
  1872. this.geometryInstances.length === 0) ||
  1873. !defined(this.appearance) ||
  1874. (frameState.mode !== SceneMode.SCENE3D && frameState.scene3DOnly) ||
  1875. (!frameState.passes.render && !frameState.passes.pick)
  1876. ) {
  1877. return;
  1878. }
  1879. if (defined(this._error)) {
  1880. throw this._error;
  1881. }
  1882. //>>includeStart('debug', pragmas.debug);
  1883. if (defined(this.rtcCenter) && !frameState.scene3DOnly) {
  1884. throw new DeveloperError(
  1885. "RTC rendering is only available for 3D only scenes."
  1886. );
  1887. }
  1888. //>>includeEnd('debug');
  1889. if (this._state === PrimitiveState.FAILED) {
  1890. return;
  1891. }
  1892. const context = frameState.context;
  1893. if (!defined(this._batchTable)) {
  1894. createBatchTable(this, context);
  1895. }
  1896. if (this._batchTable.attributes.length > 0) {
  1897. if (ContextLimits.maximumVertexTextureImageUnits === 0) {
  1898. throw new RuntimeError(
  1899. "Vertex texture fetch support is required to render primitives with per-instance attributes. The maximum number of vertex texture image units must be greater than zero."
  1900. );
  1901. }
  1902. this._batchTable.update(frameState);
  1903. }
  1904. if (
  1905. this._state !== PrimitiveState.COMPLETE &&
  1906. this._state !== PrimitiveState.COMBINED
  1907. ) {
  1908. if (this.asynchronous) {
  1909. loadAsynchronous(this, frameState);
  1910. } else {
  1911. loadSynchronous(this, frameState);
  1912. }
  1913. }
  1914. if (this._state === PrimitiveState.COMBINED) {
  1915. updateBatchTableBoundingSpheres(this, frameState);
  1916. updateBatchTableOffsets(this, frameState);
  1917. createVertexArray(this, frameState);
  1918. }
  1919. if (!this.show || this._state !== PrimitiveState.COMPLETE) {
  1920. return;
  1921. }
  1922. if (!this._batchTableOffsetsUpdated) {
  1923. updateBatchTableOffsets(this, frameState);
  1924. }
  1925. if (this._recomputeBoundingSpheres) {
  1926. recomputeBoundingSpheres(this, frameState);
  1927. }
  1928. // Create or recreate render state and shader program if appearance/material changed
  1929. const appearance = this.appearance;
  1930. const material = appearance.material;
  1931. let createRS = false;
  1932. let createSP = false;
  1933. if (this._appearance !== appearance) {
  1934. this._appearance = appearance;
  1935. this._material = material;
  1936. createRS = true;
  1937. createSP = true;
  1938. } else if (this._material !== material) {
  1939. this._material = material;
  1940. createSP = true;
  1941. }
  1942. const depthFailAppearance = this.depthFailAppearance;
  1943. const depthFailMaterial = defined(depthFailAppearance)
  1944. ? depthFailAppearance.material
  1945. : undefined;
  1946. if (this._depthFailAppearance !== depthFailAppearance) {
  1947. this._depthFailAppearance = depthFailAppearance;
  1948. this._depthFailMaterial = depthFailMaterial;
  1949. createRS = true;
  1950. createSP = true;
  1951. } else if (this._depthFailMaterial !== depthFailMaterial) {
  1952. this._depthFailMaterial = depthFailMaterial;
  1953. createSP = true;
  1954. }
  1955. const translucent = this._appearance.isTranslucent();
  1956. if (this._translucent !== translucent) {
  1957. this._translucent = translucent;
  1958. createRS = true;
  1959. }
  1960. if (defined(this._material)) {
  1961. this._material.update(context);
  1962. }
  1963. const twoPasses = appearance.closed && translucent;
  1964. if (createRS) {
  1965. const rsFunc = defaultValue(
  1966. this._createRenderStatesFunction,
  1967. createRenderStates
  1968. );
  1969. rsFunc(this, context, appearance, twoPasses);
  1970. }
  1971. if (createSP) {
  1972. const spFunc = defaultValue(
  1973. this._createShaderProgramFunction,
  1974. createShaderProgram
  1975. );
  1976. spFunc(this, frameState, appearance);
  1977. }
  1978. if (createRS || createSP) {
  1979. const commandFunc = defaultValue(
  1980. this._createCommandsFunction,
  1981. createCommands
  1982. );
  1983. commandFunc(
  1984. this,
  1985. appearance,
  1986. material,
  1987. translucent,
  1988. twoPasses,
  1989. this._colorCommands,
  1990. this._pickCommands,
  1991. frameState
  1992. );
  1993. }
  1994. const updateAndQueueCommandsFunc = defaultValue(
  1995. this._updateAndQueueCommandsFunction,
  1996. updateAndQueueCommands
  1997. );
  1998. updateAndQueueCommandsFunc(
  1999. this,
  2000. frameState,
  2001. this._colorCommands,
  2002. this._pickCommands,
  2003. this.modelMatrix,
  2004. this.cull,
  2005. this.debugShowBoundingVolume,
  2006. twoPasses
  2007. );
  2008. };
  2009. const offsetBoundingSphereScratch1 = new BoundingSphere();
  2010. const offsetBoundingSphereScratch2 = new BoundingSphere();
  2011. function transformBoundingSphere(boundingSphere, offset, offsetAttribute) {
  2012. if (offsetAttribute === GeometryOffsetAttribute.TOP) {
  2013. const origBS = BoundingSphere.clone(
  2014. boundingSphere,
  2015. offsetBoundingSphereScratch1
  2016. );
  2017. const offsetBS = BoundingSphere.clone(
  2018. boundingSphere,
  2019. offsetBoundingSphereScratch2
  2020. );
  2021. offsetBS.center = Cartesian3.add(offsetBS.center, offset, offsetBS.center);
  2022. boundingSphere = BoundingSphere.union(origBS, offsetBS, boundingSphere);
  2023. } else if (offsetAttribute === GeometryOffsetAttribute.ALL) {
  2024. boundingSphere.center = Cartesian3.add(
  2025. boundingSphere.center,
  2026. offset,
  2027. boundingSphere.center
  2028. );
  2029. }
  2030. return boundingSphere;
  2031. }
  2032. function createGetFunction(batchTable, instanceIndex, attributeIndex) {
  2033. return function () {
  2034. const attributeValue = batchTable.getBatchedAttribute(
  2035. instanceIndex,
  2036. attributeIndex
  2037. );
  2038. const attribute = batchTable.attributes[attributeIndex];
  2039. const componentsPerAttribute = attribute.componentsPerAttribute;
  2040. const value = ComponentDatatype.createTypedArray(
  2041. attribute.componentDatatype,
  2042. componentsPerAttribute
  2043. );
  2044. if (defined(attributeValue.constructor.pack)) {
  2045. attributeValue.constructor.pack(attributeValue, value, 0);
  2046. } else {
  2047. value[0] = attributeValue;
  2048. }
  2049. return value;
  2050. };
  2051. }
  2052. function createSetFunction(
  2053. batchTable,
  2054. instanceIndex,
  2055. attributeIndex,
  2056. primitive,
  2057. name
  2058. ) {
  2059. return function (value) {
  2060. //>>includeStart('debug', pragmas.debug);
  2061. if (
  2062. !defined(value) ||
  2063. !defined(value.length) ||
  2064. value.length < 1 ||
  2065. value.length > 4
  2066. ) {
  2067. throw new DeveloperError(
  2068. "value must be and array with length between 1 and 4."
  2069. );
  2070. }
  2071. //>>includeEnd('debug');
  2072. const attributeValue = getAttributeValue(value);
  2073. batchTable.setBatchedAttribute(
  2074. instanceIndex,
  2075. attributeIndex,
  2076. attributeValue
  2077. );
  2078. if (name === "offset") {
  2079. primitive._recomputeBoundingSpheres = true;
  2080. primitive._batchTableOffsetsUpdated = false;
  2081. }
  2082. };
  2083. }
  2084. const offsetScratch = new Cartesian3();
  2085. function createBoundingSphereProperties(primitive, properties, index) {
  2086. properties.boundingSphere = {
  2087. get: function () {
  2088. let boundingSphere = primitive._instanceBoundingSpheres[index];
  2089. if (defined(boundingSphere)) {
  2090. boundingSphere = boundingSphere.clone();
  2091. const modelMatrix = primitive.modelMatrix;
  2092. const offset = properties.offset;
  2093. if (defined(offset)) {
  2094. transformBoundingSphere(
  2095. boundingSphere,
  2096. Cartesian3.fromArray(offset.get(), 0, offsetScratch),
  2097. primitive._offsetInstanceExtend[index]
  2098. );
  2099. }
  2100. if (defined(modelMatrix)) {
  2101. boundingSphere = BoundingSphere.transform(
  2102. boundingSphere,
  2103. modelMatrix
  2104. );
  2105. }
  2106. }
  2107. return boundingSphere;
  2108. },
  2109. };
  2110. properties.boundingSphereCV = {
  2111. get: function () {
  2112. return primitive._instanceBoundingSpheresCV[index];
  2113. },
  2114. };
  2115. }
  2116. function createPickIdProperty(primitive, properties, index) {
  2117. properties.pickId = {
  2118. get: function () {
  2119. return primitive._pickIds[index];
  2120. },
  2121. };
  2122. }
  2123. /**
  2124. * Returns the modifiable per-instance attributes for a {@link GeometryInstance}.
  2125. *
  2126. * @param {*} id The id of the {@link GeometryInstance}.
  2127. * @returns {Object} The typed array in the attribute's format or undefined if the is no instance with id.
  2128. *
  2129. * @exception {DeveloperError} must call update before calling getGeometryInstanceAttributes.
  2130. *
  2131. * @example
  2132. * const attributes = primitive.getGeometryInstanceAttributes('an id');
  2133. * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA);
  2134. * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true);
  2135. * attributes.distanceDisplayCondition = Cesium.DistanceDisplayConditionGeometryInstanceAttribute.toValue(100.0, 10000.0);
  2136. * attributes.offset = Cesium.OffsetGeometryInstanceAttribute.toValue(Cartesian3.IDENTITY);
  2137. */
  2138. Primitive.prototype.getGeometryInstanceAttributes = function (id) {
  2139. //>>includeStart('debug', pragmas.debug);
  2140. if (!defined(id)) {
  2141. throw new DeveloperError("id is required");
  2142. }
  2143. if (!defined(this._batchTable)) {
  2144. throw new DeveloperError(
  2145. "must call update before calling getGeometryInstanceAttributes"
  2146. );
  2147. }
  2148. //>>includeEnd('debug');
  2149. let index = -1;
  2150. const lastIndex = this._lastPerInstanceAttributeIndex;
  2151. const ids = this._instanceIds;
  2152. const length = ids.length;
  2153. for (let i = 0; i < length; ++i) {
  2154. const curIndex = (lastIndex + i) % length;
  2155. if (id === ids[curIndex]) {
  2156. index = curIndex;
  2157. break;
  2158. }
  2159. }
  2160. if (index === -1) {
  2161. return undefined;
  2162. }
  2163. let attributes = this._perInstanceAttributeCache[index];
  2164. if (defined(attributes)) {
  2165. return attributes;
  2166. }
  2167. const batchTable = this._batchTable;
  2168. const perInstanceAttributeIndices = this._batchTableAttributeIndices;
  2169. attributes = {};
  2170. const properties = {};
  2171. for (const name in perInstanceAttributeIndices) {
  2172. if (perInstanceAttributeIndices.hasOwnProperty(name)) {
  2173. const attributeIndex = perInstanceAttributeIndices[name];
  2174. properties[name] = {
  2175. get: createGetFunction(batchTable, index, attributeIndex),
  2176. set: createSetFunction(batchTable, index, attributeIndex, this, name),
  2177. };
  2178. }
  2179. }
  2180. createBoundingSphereProperties(this, properties, index);
  2181. createPickIdProperty(this, properties, index);
  2182. Object.defineProperties(attributes, properties);
  2183. this._lastPerInstanceAttributeIndex = index;
  2184. this._perInstanceAttributeCache[index] = attributes;
  2185. return attributes;
  2186. };
  2187. /**
  2188. * Returns true if this object was destroyed; otherwise, false.
  2189. * <p>
  2190. * If this object was destroyed, it should not be used; calling any function other than
  2191. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  2192. * </p>
  2193. *
  2194. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  2195. *
  2196. * @see Primitive#destroy
  2197. */
  2198. Primitive.prototype.isDestroyed = function () {
  2199. return false;
  2200. };
  2201. /**
  2202. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  2203. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  2204. * <p>
  2205. * Once an object is destroyed, it should not be used; calling any function other than
  2206. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  2207. * assign the return value (<code>undefined</code>) to the object as done in the example.
  2208. * </p>
  2209. *
  2210. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  2211. *
  2212. *
  2213. * @example
  2214. * e = e && e.destroy();
  2215. *
  2216. * @see Primitive#isDestroyed
  2217. */
  2218. Primitive.prototype.destroy = function () {
  2219. let length;
  2220. let i;
  2221. this._sp = this._sp && this._sp.destroy();
  2222. this._spDepthFail = this._spDepthFail && this._spDepthFail.destroy();
  2223. const va = this._va;
  2224. length = va.length;
  2225. for (i = 0; i < length; ++i) {
  2226. va[i].destroy();
  2227. }
  2228. this._va = undefined;
  2229. const pickIds = this._pickIds;
  2230. length = pickIds.length;
  2231. for (i = 0; i < length; ++i) {
  2232. pickIds[i].destroy();
  2233. }
  2234. this._pickIds = undefined;
  2235. this._batchTable = this._batchTable && this._batchTable.destroy();
  2236. //These objects may be fairly large and reference other large objects (like Entities)
  2237. //We explicitly set them to undefined here so that the memory can be freed
  2238. //even if a reference to the destroyed Primitive has been kept around.
  2239. this._instanceIds = undefined;
  2240. this._perInstanceAttributeCache = undefined;
  2241. this._attributeLocations = undefined;
  2242. return destroyObject(this);
  2243. };
  2244. function setReady(primitive, frameState, state, error) {
  2245. primitive._error = error;
  2246. primitive._state = state;
  2247. frameState.afterRender.push(function () {
  2248. primitive._ready =
  2249. primitive._state === PrimitiveState.COMPLETE ||
  2250. primitive._state === PrimitiveState.FAILED;
  2251. if (!defined(error)) {
  2252. primitive._readyPromise.resolve(primitive);
  2253. } else {
  2254. primitive._readyPromise.reject(error);
  2255. }
  2256. });
  2257. }
  2258. export default Primitive;