import clone from "../Core/clone.js"; import combine from "../Core/combine.js"; import defaultValue from "../Core/defaultValue.js"; import defined from "../Core/defined.js"; import BlendingState from "./BlendingState.js"; import CullFace from "./CullFace.js"; /** * An appearance defines the full GLSL vertex and fragment shaders and the * render state used to draw a {@link Primitive}. All appearances implement * this base Appearance interface. * * @alias Appearance * @constructor * * @param {object} [options] Object with the following properties: * @param {boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link Appearance#renderState} has alpha blending enabled. * @param {boolean} [options.closed=false] When true, the geometry is expected to be closed so {@link Appearance#renderState} has backface culling enabled. * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color. * @param {string} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader. * @param {string} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader. * @param {object} [options.renderState] Optional render state to override the default render state. * * @see MaterialAppearance * @see EllipsoidSurfaceAppearance * @see PerInstanceColorAppearance * @see DebugAppearance * @see PolylineColorAppearance * @see PolylineMaterialAppearance * * @demo {@link https://sandcastle.cesium.com/index.html?src=Geometry%20and%20Appearances.html|Geometry and Appearances Demo} */ function Appearance(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); /** * The material used to determine the fragment color. Unlike other {@link Appearance} * properties, this is not read-only, so an appearance's material can change on the fly. * * @type Material * * @see {@link https://github.com/CesiumGS/cesium/wiki/Fabric|Fabric} */ this.material = options.material; /** * When true, the geometry is expected to appear translucent. * * @type {boolean} * * @default true */ this.translucent = defaultValue(options.translucent, true); this._vertexShaderSource = options.vertexShaderSource; this._fragmentShaderSource = options.fragmentShaderSource; this._renderState = options.renderState; this._closed = defaultValue(options.closed, false); } Object.defineProperties(Appearance.prototype, { /** * The GLSL source code for the vertex shader. * * @memberof Appearance.prototype * * @type {string} * @readonly */ vertexShaderSource: { get: function () { return this._vertexShaderSource; }, }, /** * The GLSL source code for the fragment shader. The full fragment shader * source is built procedurally taking into account the {@link Appearance#material}. * Use {@link Appearance#getFragmentShaderSource} to get the full source. * * @memberof Appearance.prototype * * @type {string} * @readonly */ fragmentShaderSource: { get: function () { return this._fragmentShaderSource; }, }, /** * The WebGL fixed-function state to use when rendering the geometry. * * @memberof Appearance.prototype * * @type {object} * @readonly */ renderState: { get: function () { return this._renderState; }, }, /** * When true, the geometry is expected to be closed. * * @memberof Appearance.prototype * * @type {boolean} * @readonly * * @default false */ closed: { get: function () { return this._closed; }, }, }); /** * Procedurally creates the full GLSL fragment shader source for this appearance * taking into account {@link Appearance#fragmentShaderSource} and {@link Appearance#material}. * * @returns {string} The full GLSL fragment shader source. */ Appearance.prototype.getFragmentShaderSource = function () { const parts = []; if (this.flat) { parts.push("#define FLAT"); } if (this.faceForward) { parts.push("#define FACE_FORWARD"); } if (defined(this.material)) { parts.push(this.material.shaderSource); } parts.push(this.fragmentShaderSource); return parts.join("\n"); }; /** * Determines if the geometry is translucent based on {@link Appearance#translucent} and {@link Material#isTranslucent}. * * @returns {boolean} true if the appearance is translucent. */ Appearance.prototype.isTranslucent = function () { return ( (defined(this.material) && this.material.isTranslucent()) || (!defined(this.material) && this.translucent) ); }; /** * Creates a render state. This is not the final render state instance; instead, * it can contain a subset of render state properties identical to the render state * created in the context. * * @returns {object} The render state. */ Appearance.prototype.getRenderState = function () { const translucent = this.isTranslucent(); const rs = clone(this.renderState, false); if (translucent) { rs.depthMask = false; rs.blending = BlendingState.ALPHA_BLEND; } else { rs.depthMask = true; } return rs; }; /** * @private */ Appearance.getDefaultRenderState = function (translucent, closed, existing) { let rs = { depthTest: { enabled: true, }, }; if (translucent) { rs.depthMask = false; rs.blending = BlendingState.ALPHA_BLEND; } if (closed) { rs.cull = { enabled: true, face: CullFace.BACK, }; } if (defined(existing)) { rs = combine(existing, rs, true); } return rs; }; export default Appearance;