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

Primitive.js 79KB

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