You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
16 KiB
JavaScript

import BoundingSphere from "../Core/BoundingSphere.js";
import BoxGeometry from "../Core/BoxGeometry.js";
import Cartesian3 from "../Core/Cartesian3.js";
import combine from "../Core/combine.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import Matrix4 from "../Core/Matrix4.js";
import VertexFormat from "../Core/VertexFormat.js";
import BufferUsage from "../Renderer/BufferUsage.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import Pass from "../Renderer/Pass.js";
import RenderState from "../Renderer/RenderState.js";
import ShaderProgram from "../Renderer/ShaderProgram.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import VertexArray from "../Renderer/VertexArray.js";
import EllipsoidFS from "../Shaders/EllipsoidFS.js";
import EllipsoidVS from "../Shaders/EllipsoidVS.js";
import BlendingState from "./BlendingState.js";
import CullFace from "./CullFace.js";
import Material from "./Material.js";
import SceneMode from "./SceneMode.js";
var attributeLocations = {
position: 0,
};
/**
* A renderable ellipsoid. It can also draw spheres when the three {@link EllipsoidPrimitive#radii} components are equal.
* <p>
* This is only supported in 3D. The ellipsoid is not shown in 2D or Columbus view.
* </p>
*
* @alias EllipsoidPrimitive
* @constructor
*
* @param {Object} [options] Object with the following properties:
* @param {Cartesian3} [options.center=Cartesian3.ZERO] The center of the ellipsoid in the ellipsoid's model coordinates.
* @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.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the ellipsoid from model to world coordinates.
* @param {Boolean} [options.show=true] Determines if this primitive will be shown.
* @param {Material} [options.material=Material.ColorType] The surface appearance of the primitive.
* @param {Object} [options.id] A user-defined object to return when the instance is picked with {@link Scene#pick}
* @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Determines if this primitive's commands' bounding spheres are shown.
*
* @private
*/
function EllipsoidPrimitive(options) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
/**
* The center of the ellipsoid in the ellipsoid's model coordinates.
* <p>
* The default is {@link Cartesian3.ZERO}.
* </p>
*
* @type {Cartesian3}
* @default {@link Cartesian3.ZERO}
*
* @see EllipsoidPrimitive#modelMatrix
*/
this.center = Cartesian3.clone(defaultValue(options.center, Cartesian3.ZERO));
this._center = new Cartesian3();
/**
* The radius of the ellipsoid along the <code>x</code>, <code>y</code>, and <code>z</code> axes in the ellipsoid's model coordinates.
* When these are the same, the ellipsoid is a sphere.
* <p>
* The default is <code>undefined</code>. The ellipsoid is not drawn until a radii is provided.
* </p>
*
* @type {Cartesian3}
* @default undefined
*
*
* @example
* // A sphere with a radius of 2.0
* e.radii = new Cesium.Cartesian3(2.0, 2.0, 2.0);
*
* @see EllipsoidPrimitive#modelMatrix
*/
this.radii = Cartesian3.clone(options.radii);
this._radii = new Cartesian3();
this._oneOverEllipsoidRadiiSquared = new Cartesian3();
this._boundingSphere = new BoundingSphere();
/**
* The 4x4 transformation matrix that transforms the ellipsoid from model to world coordinates.
* When this is the identity matrix, the ellipsoid is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
* Local reference frames can be used by providing a different transformation matrix, like that returned
* by {@link Transforms.eastNorthUpToFixedFrame}.
*
* @type {Matrix4}
* @default {@link Matrix4.IDENTITY}
*
* @example
* var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
* e.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
*/
this.modelMatrix = Matrix4.clone(
defaultValue(options.modelMatrix, Matrix4.IDENTITY)
);
this._modelMatrix = new Matrix4();
this._computedModelMatrix = new Matrix4();
/**
* Determines if the ellipsoid primitive will be shown.
*
* @type {Boolean}
* @default true
*/
this.show = defaultValue(options.show, true);
/**
* The surface appearance of the ellipsoid. This can be one of several built-in {@link Material} objects or a custom material, scripted with
* {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric}.
* <p>
* The default material is <code>Material.ColorType</code>.
* </p>
*
* @type {Material}
* @default Material.fromType(Material.ColorType)
*
*
* @example
* // 1. Change the color of the default material to yellow
* e.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
*
* // 2. Change material to horizontal stripes
* e.material = Cesium.Material.fromType(Cesium.Material.StripeType);
*
* @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric}
*/
this.material = defaultValue(
options.material,
Material.fromType(Material.ColorType)
);
this._material = undefined;
this._translucent = undefined;
/**
* User-defined object returned when the ellipsoid is picked.
*
* @type Object
*
* @default undefined
*
* @see Scene#pick
*/
this.id = options.id;
this._id = undefined;
/**
* This property is for debugging only; it is not for production use nor is it optimized.
* <p>
* Draws the bounding sphere for each draw command in the primitive.
* </p>
*
* @type {Boolean}
*
* @default false
*/
this.debugShowBoundingVolume = defaultValue(
options.debugShowBoundingVolume,
false
);
/**
* @private
*/
this.onlySunLighting = defaultValue(options.onlySunLighting, false);
this._onlySunLighting = false;
/**
* @private
*/
this._depthTestEnabled = defaultValue(options.depthTestEnabled, true);
this._useLogDepth = false;
this._sp = undefined;
this._rs = undefined;
this._va = undefined;
this._pickSP = undefined;
this._pickId = undefined;
this._colorCommand = new DrawCommand({
owner: defaultValue(options._owner, this),
});
this._pickCommand = new DrawCommand({
owner: defaultValue(options._owner, this),
pickOnly: true,
});
var that = this;
this._uniforms = {
u_radii: function () {
return that.radii;
},
u_oneOverEllipsoidRadiiSquared: function () {
return that._oneOverEllipsoidRadiiSquared;
},
};
this._pickUniforms = {
czm_pickColor: function () {
return that._pickId.color;
},
};
}
function getVertexArray(context) {
var vertexArray = context.cache.ellipsoidPrimitive_vertexArray;
if (defined(vertexArray)) {
return vertexArray;
}
var geometry = BoxGeometry.createGeometry(
BoxGeometry.fromDimensions({
dimensions: new Cartesian3(2.0, 2.0, 2.0),
vertexFormat: VertexFormat.POSITION_ONLY,
})
);
vertexArray = VertexArray.fromGeometry({
context: context,
geometry: geometry,
attributeLocations: attributeLocations,
bufferUsage: BufferUsage.STATIC_DRAW,
interleave: true,
});
context.cache.ellipsoidPrimitive_vertexArray = vertexArray;
return vertexArray;
}
var logDepthExtension =
"#ifdef GL_EXT_frag_depth \n" +
"#extension GL_EXT_frag_depth : enable \n" +
"#endif \n\n";
/**
* Called when {@link Viewer} or {@link CesiumWidget} render the scene to
* get the draw commands needed to render this primitive.
* <p>
* Do not call this function directly. This is documented just to
* list the exceptions that may be propagated when the scene is rendered:
* </p>
*
* @exception {DeveloperError} this.material must be defined.
*/
EllipsoidPrimitive.prototype.update = function (frameState) {
if (
!this.show ||
frameState.mode !== SceneMode.SCENE3D ||
!defined(this.center) ||
!defined(this.radii)
) {
return;
}
//>>includeStart('debug', pragmas.debug);
if (!defined(this.material)) {
throw new DeveloperError("this.material must be defined.");
}
//>>includeEnd('debug');
var context = frameState.context;
var translucent = this.material.isTranslucent();
var translucencyChanged = this._translucent !== translucent;
if (!defined(this._rs) || translucencyChanged) {
this._translucent = translucent;
// If this render state is ever updated to use a non-default
// depth range, the hard-coded values in EllipsoidVS.glsl need
// to be updated as well.
this._rs = RenderState.fromCache({
// Cull front faces - not back faces - so the ellipsoid doesn't
// disappear if the viewer enters the bounding box.
cull: {
enabled: true,
face: CullFace.FRONT,
},
depthTest: {
enabled: this._depthTestEnabled,
},
// Only write depth when EXT_frag_depth is supported since the depth for
// the bounding box is wrong; it is not the true depth of the ray casted ellipsoid.
depthMask: !translucent && context.fragmentDepth,
blending: translucent ? BlendingState.ALPHA_BLEND : undefined,
});
}
if (!defined(this._va)) {
this._va = getVertexArray(context);
}
var boundingSphereDirty = false;
var radii = this.radii;
if (!Cartesian3.equals(this._radii, radii)) {
Cartesian3.clone(radii, this._radii);
var r = this._oneOverEllipsoidRadiiSquared;
r.x = 1.0 / (radii.x * radii.x);
r.y = 1.0 / (radii.y * radii.y);
r.z = 1.0 / (radii.z * radii.z);
boundingSphereDirty = true;
}
if (
!Matrix4.equals(this.modelMatrix, this._modelMatrix) ||
!Cartesian3.equals(this.center, this._center)
) {
Matrix4.clone(this.modelMatrix, this._modelMatrix);
Cartesian3.clone(this.center, this._center);
// Translate model coordinates used for rendering such that the origin is the center of the ellipsoid.
Matrix4.multiplyByTranslation(
this.modelMatrix,
this.center,
this._computedModelMatrix
);
boundingSphereDirty = true;
}
if (boundingSphereDirty) {
Cartesian3.clone(Cartesian3.ZERO, this._boundingSphere.center);
this._boundingSphere.radius = Cartesian3.maximumComponent(radii);
BoundingSphere.transform(
this._boundingSphere,
this._computedModelMatrix,
this._boundingSphere
);
}
var materialChanged = this._material !== this.material;
this._material = this.material;
this._material.update(context);
var lightingChanged = this.onlySunLighting !== this._onlySunLighting;
this._onlySunLighting = this.onlySunLighting;
var useLogDepth = frameState.useLogDepth;
var useLogDepthChanged = this._useLogDepth !== useLogDepth;
this._useLogDepth = useLogDepth;
var colorCommand = this._colorCommand;
var vs;
var fs;
// Recompile shader when material, lighting, or translucency changes
if (
materialChanged ||
lightingChanged ||
translucencyChanged ||
useLogDepthChanged
) {
vs = new ShaderSource({
sources: [EllipsoidVS],
});
fs = new ShaderSource({
sources: [this.material.shaderSource, EllipsoidFS],
});
if (this.onlySunLighting) {
fs.defines.push("ONLY_SUN_LIGHTING");
}
if (!translucent && context.fragmentDepth) {
fs.defines.push("WRITE_DEPTH");
}
if (this._useLogDepth) {
vs.defines.push("LOG_DEPTH");
fs.defines.push("LOG_DEPTH");
fs.sources.push(logDepthExtension);
}
this._sp = ShaderProgram.replaceCache({
context: context,
shaderProgram: this._sp,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
colorCommand.vertexArray = this._va;
colorCommand.renderState = this._rs;
colorCommand.shaderProgram = this._sp;
colorCommand.uniformMap = combine(this._uniforms, this.material._uniforms);
colorCommand.executeInClosestFrustum = translucent;
}
var commandList = frameState.commandList;
var passes = frameState.passes;
if (passes.render) {
colorCommand.boundingVolume = this._boundingSphere;
colorCommand.debugShowBoundingVolume = this.debugShowBoundingVolume;
colorCommand.modelMatrix = this._computedModelMatrix;
colorCommand.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
commandList.push(colorCommand);
}
if (passes.pick) {
var pickCommand = this._pickCommand;
if (!defined(this._pickId) || this._id !== this.id) {
this._id = this.id;
this._pickId = this._pickId && this._pickId.destroy();
this._pickId = context.createPickId({
primitive: this,
id: this.id,
});
}
// Recompile shader when material changes
if (
materialChanged ||
lightingChanged ||
!defined(this._pickSP) ||
useLogDepthChanged
) {
vs = new ShaderSource({
sources: [EllipsoidVS],
});
fs = new ShaderSource({
sources: [this.material.shaderSource, EllipsoidFS],
pickColorQualifier: "uniform",
});
if (this.onlySunLighting) {
fs.defines.push("ONLY_SUN_LIGHTING");
}
if (!translucent && context.fragmentDepth) {
fs.defines.push("WRITE_DEPTH");
}
if (this._useLogDepth) {
vs.defines.push("LOG_DEPTH");
fs.defines.push("LOG_DEPTH");
fs.sources.push(logDepthExtension);
}
this._pickSP = ShaderProgram.replaceCache({
context: context,
shaderProgram: this._pickSP,
vertexShaderSource: vs,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
pickCommand.vertexArray = this._va;
pickCommand.renderState = this._rs;
pickCommand.shaderProgram = this._pickSP;
pickCommand.uniformMap = combine(
combine(this._uniforms, this._pickUniforms),
this.material._uniforms
);
pickCommand.executeInClosestFrustum = translucent;
}
pickCommand.boundingVolume = this._boundingSphere;
pickCommand.modelMatrix = this._computedModelMatrix;
pickCommand.pass = translucent ? Pass.TRANSLUCENT : Pass.OPAQUE;
commandList.push(pickCommand);
}
};
/**
* Returns true if this object was destroyed; otherwise, false.
* <br /><br />
* If this object was destroyed, it should not be used; calling any function other than
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
*
* @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
*
* @see EllipsoidPrimitive#destroy
*/
EllipsoidPrimitive.prototype.isDestroyed = function () {
return false;
};
/**
* Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
* release of WebGL resources, instead of relying on the garbage collector to destroy this object.
* <br /><br />
* Once an object is destroyed, it should not be used; calling any function other than
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
* assign the return value (<code>undefined</code>) to the object as done in the example.
*
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
*
*
* @example
* e = e && e.destroy();
*
* @see EllipsoidPrimitive#isDestroyed
*/
EllipsoidPrimitive.prototype.destroy = function () {
this._sp = this._sp && this._sp.destroy();
this._pickSP = this._pickSP && this._pickSP.destroy();
this._pickId = this._pickId && this._pickId.destroy();
return destroyObject(this);
};
export default EllipsoidPrimitive;