EllipsoidPrimitive.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. import BoundingSphere from '../Core/BoundingSphere.js';
  2. import BoxGeometry from '../Core/BoxGeometry.js';
  3. import Cartesian3 from '../Core/Cartesian3.js';
  4. import combine from '../Core/combine.js';
  5. import defaultValue from '../Core/defaultValue.js';
  6. import defined from '../Core/defined.js';
  7. import destroyObject from '../Core/destroyObject.js';
  8. import DeveloperError from '../Core/DeveloperError.js';
  9. import Matrix4 from '../Core/Matrix4.js';
  10. import VertexFormat from '../Core/VertexFormat.js';
  11. import BufferUsage from '../Renderer/BufferUsage.js';
  12. import DrawCommand from '../Renderer/DrawCommand.js';
  13. import Pass from '../Renderer/Pass.js';
  14. import RenderState from '../Renderer/RenderState.js';
  15. import ShaderProgram from '../Renderer/ShaderProgram.js';
  16. import ShaderSource from '../Renderer/ShaderSource.js';
  17. import VertexArray from '../Renderer/VertexArray.js';
  18. import EllipsoidFS from '../Shaders/EllipsoidFS.js';
  19. import EllipsoidVS from '../Shaders/EllipsoidVS.js';
  20. import BlendingState from './BlendingState.js';
  21. import CullFace from './CullFace.js';
  22. import Material from './Material.js';
  23. import SceneMode from './SceneMode.js';
  24. var attributeLocations = {
  25. position : 0
  26. };
  27. /**
  28. * A renderable ellipsoid. It can also draw spheres when the three {@link EllipsoidPrimitive#radii} components are equal.
  29. * <p>
  30. * This is only supported in 3D. The ellipsoid is not shown in 2D or Columbus view.
  31. * </p>
  32. *
  33. * @alias EllipsoidPrimitive
  34. * @constructor
  35. *
  36. * @param {Object} [options] Object with the following properties:
  37. * @param {Cartesian3} [options.center=Cartesian3.ZERO] The center of the ellipsoid in the ellipsoid's model coordinates.
  38. * @param {Cartesian3} [options.radii] The radius of the ellipsoid along the <code>x</code>, <code>y</code>, and <code>z</code> axes in the ellipsoid's model coordinates.
  39. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the ellipsoid from model to world coordinates.
  40. * @param {Boolean} [options.show=true] Determines if this primitive will be shown.
  41. * @param {Material} [options.material=Material.ColorType] The surface appearance of the primitive.
  42. * @param {Object} [options.id] A user-defined object to return when the instance is picked with {@link Scene#pick}
  43. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
  44. *
  45. * @private
  46. */
  47. function EllipsoidPrimitive(options) {
  48. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  49. /**
  50. * The center of the ellipsoid in the ellipsoid's model coordinates.
  51. * <p>
  52. * The default is {@link Cartesian3.ZERO}.
  53. * </p>
  54. *
  55. * @type {Cartesian3}
  56. * @default {@link Cartesian3.ZERO}
  57. *
  58. * @see EllipsoidPrimitive#modelMatrix
  59. */
  60. this.center = Cartesian3.clone(defaultValue(options.center, Cartesian3.ZERO));
  61. this._center = new Cartesian3();
  62. /**
  63. * The radius of the ellipsoid along the <code>x</code>, <code>y</code>, and <code>z</code> axes in the ellipsoid's model coordinates.
  64. * When these are the same, the ellipsoid is a sphere.
  65. * <p>
  66. * The default is <code>undefined</code>. The ellipsoid is not drawn until a radii is provided.
  67. * </p>
  68. *
  69. * @type {Cartesian3}
  70. * @default undefined
  71. *
  72. *
  73. * @example
  74. * // A sphere with a radius of 2.0
  75. * e.radii = new Cesium.Cartesian3(2.0, 2.0, 2.0);
  76. *
  77. * @see EllipsoidPrimitive#modelMatrix
  78. */
  79. this.radii = Cartesian3.clone(options.radii);
  80. this._radii = new Cartesian3();
  81. this._oneOverEllipsoidRadiiSquared = new Cartesian3();
  82. this._boundingSphere = new BoundingSphere();
  83. /**
  84. * The 4x4 transformation matrix that transforms the ellipsoid from model to world coordinates.
  85. * When this is the identity matrix, the ellipsoid is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  86. * Local reference frames can be used by providing a different transformation matrix, like that returned
  87. * by {@link Transforms.eastNorthUpToFixedFrame}.
  88. *
  89. * @type {Matrix4}
  90. * @default {@link Matrix4.IDENTITY}
  91. *
  92. * @example
  93. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  94. * e.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  95. */
  96. this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
  97. this._modelMatrix = new Matrix4();
  98. this._computedModelMatrix = new Matrix4();
  99. /**
  100. * Determines if the ellipsoid primitive will be shown.
  101. *
  102. * @type {Boolean}
  103. * @default true
  104. */
  105. this.show = defaultValue(options.show, true);
  106. /**
  107. * The surface appearance of the ellipsoid. This can be one of several built-in {@link Material} objects or a custom material, scripted with
  108. * {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}.
  109. * <p>
  110. * The default material is <code>Material.ColorType</code>.
  111. * </p>
  112. *
  113. * @type {Material}
  114. * @default Material.fromType(Material.ColorType)
  115. *
  116. *
  117. * @example
  118. * // 1. Change the color of the default material to yellow
  119. * e.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
  120. *
  121. * // 2. Change material to horizontal stripes
  122. * e.material = Cesium.Material.fromType(Cesium.Material.StripeType);
  123. *
  124. * @see {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}
  125. */
  126. this.material = defaultValue(options.material, Material.fromType(Material.ColorType));
  127. this._material = undefined;
  128. this._translucent = undefined;
  129. /**
  130. * User-defined object returned when the ellipsoid is picked.
  131. *
  132. * @type Object
  133. *
  134. * @default undefined
  135. *
  136. * @see Scene#pick
  137. */
  138. this.id = options.id;
  139. this._id = undefined;
  140. /**
  141. * This property is for debugging only; it is not for production use nor is it optimized.
  142. * <p>
  143. * Draws the bounding sphere for each draw command in the primitive.
  144. * </p>
  145. *
  146. * @type {Boolean}
  147. *
  148. * @default false
  149. */
  150. this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
  151. /**
  152. * @private
  153. */
  154. this.onlySunLighting = defaultValue(options.onlySunLighting, false);
  155. this._onlySunLighting = false;
  156. /**
  157. * @private
  158. */
  159. this._depthTestEnabled = defaultValue(options.depthTestEnabled, true);
  160. this._useLogDepth = false;
  161. this._sp = undefined;
  162. this._rs = undefined;
  163. this._va = undefined;
  164. this._pickSP = undefined;
  165. this._pickId = undefined;
  166. this._colorCommand = new DrawCommand({
  167. owner : defaultValue(options._owner, this)
  168. });
  169. this._pickCommand = new DrawCommand({
  170. owner : defaultValue(options._owner, this),
  171. pickOnly : true
  172. });
  173. var that = this;
  174. this._uniforms = {
  175. u_radii : function() {
  176. return that.radii;
  177. },
  178. u_oneOverEllipsoidRadiiSquared : function() {
  179. return that._oneOverEllipsoidRadiiSquared;
  180. }
  181. };
  182. this._pickUniforms = {
  183. czm_pickColor : function() {
  184. return that._pickId.color;
  185. }
  186. };
  187. }
  188. function getVertexArray(context) {
  189. var vertexArray = context.cache.ellipsoidPrimitive_vertexArray;
  190. if (defined(vertexArray)) {
  191. return vertexArray;
  192. }
  193. var geometry = BoxGeometry.createGeometry(BoxGeometry.fromDimensions({
  194. dimensions : new Cartesian3(2.0, 2.0, 2.0),
  195. vertexFormat : VertexFormat.POSITION_ONLY
  196. }));
  197. vertexArray = VertexArray.fromGeometry({
  198. context : context,
  199. geometry : geometry,
  200. attributeLocations : attributeLocations,
  201. bufferUsage : BufferUsage.STATIC_DRAW,
  202. interleave : true
  203. });
  204. context.cache.ellipsoidPrimitive_vertexArray = vertexArray;
  205. return vertexArray;
  206. }
  207. var logDepthExtension =
  208. '#ifdef GL_EXT_frag_depth \n' +
  209. '#extension GL_EXT_frag_depth : enable \n' +
  210. '#endif \n\n';
  211. /**
  212. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  213. * get the draw commands needed to render this primitive.
  214. * <p>
  215. * Do not call this function directly. This is documented just to
  216. * list the exceptions that may be propagated when the scene is rendered:
  217. * </p>
  218. *
  219. * @exception {DeveloperError} this.material must be defined.
  220. */
  221. EllipsoidPrimitive.prototype.update = function(frameState) {
  222. if (!this.show ||
  223. (frameState.mode !== SceneMode.SCENE3D) ||
  224. (!defined(this.center)) ||
  225. (!defined(this.radii))) {
  226. return;
  227. }
  228. //>>includeStart('debug', pragmas.debug);
  229. if (!defined(this.material)) {
  230. throw new DeveloperError('this.material must be defined.');
  231. }
  232. //>>includeEnd('debug');
  233. var context = frameState.context;
  234. var translucent = this.material.isTranslucent();
  235. var translucencyChanged = this._translucent !== translucent;
  236. if (!defined(this._rs) || translucencyChanged) {
  237. this._translucent = translucent;
  238. // If this render state is ever updated to use a non-default
  239. // depth range, the hard-coded values in EllipsoidVS.glsl need
  240. // to be updated as well.
  241. this._rs = RenderState.fromCache({
  242. // Cull front faces - not back faces - so the ellipsoid doesn't
  243. // disappear if the viewer enters the bounding box.
  244. cull : {
  245. enabled : true,
  246. face : CullFace.FRONT
  247. },
  248. depthTest : {
  249. enabled : this._depthTestEnabled
  250. },
  251. // Only write depth when EXT_frag_depth is supported since the depth for
  252. // the bounding box is wrong; it is not the true depth of the ray casted ellipsoid.
  253. depthMask : !translucent && context.fragmentDepth,
  254. blending : translucent ? BlendingState.ALPHA_BLEND : undefined
  255. });
  256. }
  257. if (!defined(this._va)) {
  258. this._va = getVertexArray(context);
  259. }
  260. var boundingSphereDirty = false;
  261. var radii = this.radii;
  262. if (!Cartesian3.equals(this._radii, radii)) {
  263. Cartesian3.clone(radii, this._radii);
  264. var r = this._oneOverEllipsoidRadiiSquared;
  265. r.x = 1.0 / (radii.x * radii.x);
  266. r.y = 1.0 / (radii.y * radii.y);
  267. r.z = 1.0 / (radii.z * radii.z);
  268. boundingSphereDirty = true;
  269. }
  270. if (!Matrix4.equals(this.modelMatrix, this._modelMatrix) || !Cartesian3.equals(this.center, this._center)) {
  271. Matrix4.clone(this.modelMatrix, this._modelMatrix);
  272. Cartesian3.clone(this.center, this._center);
  273. // Translate model coordinates used for rendering such that the origin is the center of the ellipsoid.
  274. Matrix4.multiplyByTranslation(this.modelMatrix, this.center, this._computedModelMatrix);
  275. boundingSphereDirty = true;
  276. }
  277. if (boundingSphereDirty) {
  278. Cartesian3.clone(Cartesian3.ZERO, this._boundingSphere.center);
  279. this._boundingSphere.radius = Cartesian3.maximumComponent(radii);
  280. BoundingSphere.transform(this._boundingSphere, this._computedModelMatrix, this._boundingSphere);
  281. }
  282. var materialChanged = this._material !== this.material;
  283. this._material = this.material;
  284. this._material.update(context);
  285. var lightingChanged = this.onlySunLighting !== this._onlySunLighting;
  286. this._onlySunLighting = this.onlySunLighting;
  287. var useLogDepth = frameState.useLogDepth;
  288. var useLogDepthChanged = this._useLogDepth !== useLogDepth;
  289. this._useLogDepth = useLogDepth;
  290. var colorCommand = this._colorCommand;
  291. var vs;
  292. var fs;
  293. // Recompile shader when material, lighting, or translucency changes
  294. if (materialChanged || lightingChanged || translucencyChanged || useLogDepthChanged) {
  295. vs = new ShaderSource({
  296. sources : [EllipsoidVS]
  297. });
  298. fs = new ShaderSource({
  299. sources : [this.material.shaderSource, EllipsoidFS]
  300. });
  301. if (this.onlySunLighting) {
  302. fs.defines.push('ONLY_SUN_LIGHTING');
  303. }
  304. if (!translucent && context.fragmentDepth) {
  305. fs.defines.push('WRITE_DEPTH');
  306. }
  307. if (this._useLogDepth) {
  308. vs.defines.push('LOG_DEPTH', 'DISABLE_GL_POSITION_LOG_DEPTH');
  309. fs.defines.push('LOG_DEPTH');
  310. fs.sources.push(logDepthExtension);
  311. }
  312. this._sp = ShaderProgram.replaceCache({
  313. context : context,
  314. shaderProgram : this._sp,
  315. vertexShaderSource : vs,
  316. fragmentShaderSource : fs,
  317. attributeLocations : attributeLocations
  318. });
  319. colorCommand.vertexArray = this._va;
  320. colorCommand.renderState = this._rs;
  321. colorCommand.shaderProgram = this._sp;
  322. colorCommand.uniformMap = combine(this._uniforms, this.material._uniforms);
  323. colorCommand.executeInClosestFrustum = translucent;
  324. }
  325. var commandList = frameState.commandList;
  326. var passes = frameState.passes;
  327. if (passes.render) {
  328. colorCommand.boundingVolume = this._boundingSphere;
  329. colorCommand.debugShowBoundingVolume = this.debugShowBoundingVolume;
  330. colorCommand.modelMatrix = this._computedModelMatrix;
  331. colorCommand.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
  332. commandList.push(colorCommand);
  333. }
  334. if (passes.pick) {
  335. var pickCommand = this._pickCommand;
  336. if (!defined(this._pickId) || (this._id !== this.id)) {
  337. this._id = this.id;
  338. this._pickId = this._pickId && this._pickId.destroy();
  339. this._pickId = context.createPickId({
  340. primitive : this,
  341. id : this.id
  342. });
  343. }
  344. // Recompile shader when material changes
  345. if (materialChanged || lightingChanged || !defined(this._pickSP) || useLogDepthChanged) {
  346. vs = new ShaderSource({
  347. sources : [EllipsoidVS]
  348. });
  349. fs = new ShaderSource({
  350. sources : [this.material.shaderSource, EllipsoidFS],
  351. pickColorQualifier : 'uniform'
  352. });
  353. if (this.onlySunLighting) {
  354. fs.defines.push('ONLY_SUN_LIGHTING');
  355. }
  356. if (!translucent && context.fragmentDepth) {
  357. fs.defines.push('WRITE_DEPTH');
  358. }
  359. if (this._useLogDepth) {
  360. vs.defines.push('LOG_DEPTH');
  361. fs.defines.push('LOG_DEPTH');
  362. fs.sources.push(logDepthExtension);
  363. }
  364. this._pickSP = ShaderProgram.replaceCache({
  365. context : context,
  366. shaderProgram : this._pickSP,
  367. vertexShaderSource : vs,
  368. fragmentShaderSource : fs,
  369. attributeLocations : attributeLocations
  370. });
  371. pickCommand.vertexArray = this._va;
  372. pickCommand.renderState = this._rs;
  373. pickCommand.shaderProgram = this._pickSP;
  374. pickCommand.uniformMap = combine(combine(this._uniforms, this._pickUniforms), this.material._uniforms);
  375. pickCommand.executeInClosestFrustum = translucent;
  376. }
  377. pickCommand.boundingVolume = this._boundingSphere;
  378. pickCommand.modelMatrix = this._computedModelMatrix;
  379. pickCommand.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
  380. commandList.push(pickCommand);
  381. }
  382. };
  383. /**
  384. * Returns true if this object was destroyed; otherwise, false.
  385. * <br /><br />
  386. * If this object was destroyed, it should not be used; calling any function other than
  387. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  388. *
  389. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  390. *
  391. * @see EllipsoidPrimitive#destroy
  392. */
  393. EllipsoidPrimitive.prototype.isDestroyed = function() {
  394. return false;
  395. };
  396. /**
  397. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  398. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  399. * <br /><br />
  400. * Once an object is destroyed, it should not be used; calling any function other than
  401. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  402. * assign the return value (<code>undefined</code>) to the object as done in the example.
  403. *
  404. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  405. *
  406. *
  407. * @example
  408. * e = e && e.destroy();
  409. *
  410. * @see EllipsoidPrimitive#isDestroyed
  411. */
  412. EllipsoidPrimitive.prototype.destroy = function() {
  413. this._sp = this._sp && this._sp.destroy();
  414. this._pickSP = this._pickSP && this._pickSP.destroy();
  415. this._pickId = this._pickId && this._pickId.destroy();
  416. return destroyObject(this);
  417. };
  418. export default EllipsoidPrimitive;