/*global define*/ define([ '../Core/clone', '../Core/Color', '../Core/ComponentDatatype', '../Core/createGuid', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/destroyObject', '../Core/DeveloperError', '../Core/Geometry', '../Core/GeometryAttribute', '../Core/IndexDatatype', '../Core/Math', '../Core/Matrix4', '../Core/PixelFormat', '../Core/PrimitiveType', '../Core/RuntimeError', '../Shaders/ViewportQuadVS', './Buffer', './BufferUsage', './ClearCommand', './CubeMap', './DrawCommand', './Framebuffer', './PassState', './PickFramebuffer', './PixelDatatype', './Renderbuffer', './RenderbufferFormat', './RenderState', './ShaderCache', './Texture', './TextureMagnificationFilter', './TextureMinificationFilter', './TextureWrap', './UniformState', './VertexArray' ], function( clone, Color, ComponentDatatype, createGuid, defaultValue, defined, defineProperties, destroyObject, DeveloperError, Geometry, GeometryAttribute, IndexDatatype, CesiumMath, Matrix4, PixelFormat, PrimitiveType, RuntimeError, ViewportQuadVS, Buffer, BufferUsage, ClearCommand, CubeMap, DrawCommand, Framebuffer, PassState, PickFramebuffer, PixelDatatype, Renderbuffer, RenderbufferFormat, RenderState, ShaderCache, Texture, TextureMagnificationFilter, TextureMinificationFilter, TextureWrap, UniformState, VertexArray) { "use strict"; /*global WebGLRenderingContext*/ function errorToString(gl, error) { var message = 'WebGL Error: '; switch (error) { case gl.INVALID_ENUM: message += 'INVALID_ENUM'; break; case gl.INVALID_VALUE: message += 'INVALID_VALUE'; break; case gl.INVALID_OPERATION: message += 'INVALID_OPERATION'; break; case gl.OUT_OF_MEMORY: message += 'OUT_OF_MEMORY'; break; case gl.CONTEXT_LOST_WEBGL: message += 'CONTEXT_LOST_WEBGL lost'; break; default: message += 'Unknown (' + error + ')'; } return message; } function createErrorMessage(gl, glFunc, glFuncArguments, error) { var message = errorToString(gl, error) + ': ' + glFunc.name + '('; for ( var i = 0; i < glFuncArguments.length; ++i) { if (i !== 0) { message += ', '; } message += glFuncArguments[i]; } message += ');'; return message; } function throwOnError(gl, glFunc, glFuncArguments) { var error = gl.getError(); if (error !== gl.NO_ERROR) { throw new RuntimeError(createErrorMessage(gl, glFunc, glFuncArguments, error)); } } function makeGetterSetter(gl, propertyName, logFunc) { return { get : function() { var value = gl[propertyName]; logFunc(gl, 'get: ' + propertyName, value); return gl[propertyName]; }, set : function(value) { gl[propertyName] = value; logFunc(gl, 'set: ' + propertyName, value); } }; } function wrapGL(gl, logFunc) { if (!logFunc) { return gl; } function wrapFunction(property) { return function() { var result = property.apply(gl, arguments); logFunc(gl, property, arguments); return result; }; } var glWrapper = {}; /*jslint forin: true*/ /*jshint forin: false*/ // JSLint normally demands that a for..in loop must directly contain an if, // but in our loop below, we actually intend to iterate all properties, including // those in the prototype. for ( var propertyName in gl) { var property = gl[propertyName]; // wrap any functions we encounter, otherwise just copy the property to the wrapper. if (typeof property === 'function') { glWrapper[propertyName] = wrapFunction(property); } else { Object.defineProperty(glWrapper, propertyName, makeGetterSetter(gl, propertyName, logFunc)); } } return glWrapper; } function getExtension(gl, names) { var length = names.length; for (var i = 0; i < length; ++i) { var extension = gl.getExtension(names[i]); if (extension) { return extension; } } return undefined; } /** * @private */ var Context = function(canvas, options) { // this check must use typeof, not defined, because defined doesn't work with undeclared variables. if (typeof WebGLRenderingContext === 'undefined') { throw new RuntimeError('The browser does not support WebGL. Visit http://get.webgl.org.'); } //>>includeStart('debug', pragmas.debug); if (!defined(canvas)) { throw new DeveloperError('canvas is required.'); } //>>includeEnd('debug'); this._canvas = canvas; options = clone(options, true); options = defaultValue(options, {}); options.allowTextureFilterAnisotropic = defaultValue(options.allowTextureFilterAnisotropic, true); var webglOptions = defaultValue(options.webgl, {}); // Override select WebGL defaults webglOptions.alpha = defaultValue(webglOptions.alpha, false); // WebGL default is true // TODO: WebGL default is false. This works around a bug in Canary and can be removed when fixed: https://code.google.com/p/chromium/issues/detail?id=335273 webglOptions.stencil = defaultValue(webglOptions.stencil, false); webglOptions.failIfMajorPerformanceCaveat = defaultValue(webglOptions.failIfMajorPerformanceCaveat, true); // WebGL default is false this._originalGLContext = canvas.getContext('webgl', webglOptions) || canvas.getContext('experimental-webgl', webglOptions) || undefined; if (!defined(this._originalGLContext)) { throw new RuntimeError('The browser supports WebGL, but initialization failed.'); } this._id = createGuid(); // Validation and logging disabled by default for speed. this.validateFramebuffer = false; this.validateShaderProgram = false; this.logShaderCompilation = false; this._throwOnWebGLError = false; this._shaderCache = new ShaderCache(this); var gl = this._gl = this._originalGLContext; this._version = gl.getParameter(gl.VERSION); this._shadingLanguageVersion = gl.getParameter(gl.SHADING_LANGUAGE_VERSION); this._vendor = gl.getParameter(gl.VENDOR); this._renderer = gl.getParameter(gl.RENDERER); this._redBits = gl.getParameter(gl.RED_BITS); this._greenBits = gl.getParameter(gl.GREEN_BITS); this._blueBits = gl.getParameter(gl.BLUE_BITS); this._alphaBits = gl.getParameter(gl.ALPHA_BITS); this._depthBits = gl.getParameter(gl.DEPTH_BITS); this._stencilBits = gl.getParameter(gl.STENCIL_BITS); this._maximumCombinedTextureImageUnits = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); // min: 8 this._maximumCubeMapSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE); // min: 16 this._maximumFragmentUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); // min: 16 this._maximumTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); // min: 8 this._maximumRenderbufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE); // min: 1 this._maximumTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); // min: 64 this._maximumVaryingVectors = gl.getParameter(gl.MAX_VARYING_VECTORS); // min: 8 this._maximumVertexAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); // min: 8 this._maximumVertexTextureImageUnits = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS); // min: 0 this._maximumVertexUniformVectors = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS); // min: 128 this._aliasedLineWidthRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE); // must include 1 this._aliasedPointSizeRange = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); // must include 1 this._maximumViewportDimensions = gl.getParameter(gl.MAX_VIEWPORT_DIMS); this._antialias = gl.getContextAttributes().antialias; // Query and initialize extensions this._standardDerivatives = getExtension(gl, ['OES_standard_derivatives']); this._elementIndexUint = getExtension(gl, ['OES_element_index_uint']); this._depthTexture = getExtension(gl, ['WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture']); this._textureFloat = getExtension(gl, ['OES_texture_float']); var textureFilterAnisotropic = options.allowTextureFilterAnisotropic ? getExtension(gl, ['EXT_texture_filter_anisotropic', 'WEBKIT_EXT_texture_filter_anisotropic']) : undefined; this._textureFilterAnisotropic = textureFilterAnisotropic; this._maximumTextureFilterAnisotropy = defined(textureFilterAnisotropic) ? gl.getParameter(textureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 1.0; this._vertexArrayObject = getExtension(gl, ['OES_vertex_array_object']); this._fragDepth = getExtension(gl, ['EXT_frag_depth']); this._drawBuffers = getExtension(gl, ['WEBGL_draw_buffers']); this._maximumDrawBuffers = defined(this._drawBuffers) ? gl.getParameter(this._drawBuffers.MAX_DRAW_BUFFERS_WEBGL) : 1; this._maximumColorAttachments = defined(this._drawBuffers) ? gl.getParameter(this._drawBuffers.MAX_COLOR_ATTACHMENTS_WEBGL) : 1; // min when supported: 4 this._debugShaders = getExtension(gl, ['WEBGL_debug_shaders']); var cc = gl.getParameter(gl.COLOR_CLEAR_VALUE); this._clearColor = new Color(cc[0], cc[1], cc[2], cc[3]); this._clearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE); this._clearStencil = gl.getParameter(gl.STENCIL_CLEAR_VALUE); var us = new UniformState(); var ps = new PassState(this); var rs = this.createRenderState(); this._defaultPassState = ps; this._defaultRenderState = rs; this._defaultTexture = undefined; this._defaultCubeMap = undefined; this._us = us; this._currentRenderState = rs; this._currentFramebuffer = undefined; this._maxFrameTextureUnitIndex = 0; this._pickObjects = {}; this._nextPickColor = new Uint32Array(1); /** * @example * { * webgl : { * alpha : false, * depth : true, * stencil : false, * antialias : true, * premultipliedAlpha : true, * preserveDrawingBuffer : false * failIfMajorPerformanceCaveat : true * }, * allowTextureFilterAnisotropic : true * } */ this.options = options; /** * A cache of objects tied to this context. Just before the Context is destroyed, * destroy will be invoked on each object in this object literal that has * such a method. This is useful for caching any objects that might otherwise * be stored globally, except they're tied to a particular context, and to manage * their lifetime. * * @type {Object} */ this.cache = {}; RenderState.apply(gl, rs, ps); }; var defaultFramebufferMarker = {}; defineProperties(Context.prototype, { id : { get : function() { return this._id; } }, canvas : { get : function() { return this._canvas; } }, shaderCache : { get : function() { return this._shaderCache; } }, uniformState : { get : function() { return this._us; } }, /** * The WebGL version or release number of the form <WebGL><space><version number><space><vendor-specific information>. * @memberof Context.prototype * @type {String} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetString.xml|glGetString} with VERSION. */ version : { get : function() { return this._version; } }, /** * The version or release number for the shading language of the form WebGL<space>GLSL<space>ES<space><version number><space><vendor-specific information>. * @memberof Context.prototype * @type {String} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetString.xml|glGetString} with SHADING_LANGUAGE_VERSION. */ shadingLanguageVersion : { get : function() { return this._shadingLanguageVersion; } }, /** * The company responsible for the WebGL implementation. * @memberof Context.prototype * @type {String} */ vendor : { get : function() { return this._vendor; } }, /** * The name of the renderer/configuration/hardware platform. For example, this may be the model of the * video card, e.g., 'GeForce 8800 GTS/PCI/SSE2', or the browser-dependent name of the GL implementation, e.g. * 'Mozilla' or 'ANGLE.' * @memberof Context.prototype * @type {String} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGetString.xml|glGetString} with RENDERER. * @see {@link http://code.google.com/p/angleproject/|ANGLE} */ renderer : { get : function() { return this._renderer; } }, /** * The number of red bits per component in the default framebuffer's color buffer. The minimum is eight. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with RED_BITS. */ redBits : { get : function() { return this._redBits; } }, /** * The number of green bits per component in the default framebuffer's color buffer. The minimum is eight. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with GREEN_BITS. */ greenBits : { get : function() { return this._greenBits; } }, /** * The number of blue bits per component in the default framebuffer's color buffer. The minimum is eight. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with BLUE_BITS. */ blueBits : { get : function() { return this._blueBits; } }, /** * The number of alpha bits per component in the default framebuffer's color buffer. The minimum is eight. *

* The alpha channel is used for GL destination alpha operations and by the HTML compositor to combine the color buffer * with the rest of the page. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALPHA_BITS. */ alphaBits : { get : function() { return this._alphaBits; } }, /** * The number of depth bits per pixel in the default bound framebuffer. The minimum is 16 bits; most * implementations will have 24 bits. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with DEPTH_BITS. */ depthBits : { get : function() { return this._depthBits; } }, /** * The number of stencil bits per pixel in the default bound framebuffer. The minimum is eight bits. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with STENCIL_BITS. */ stencilBits : { get : function() { return this._stencilBits; } }, /** * The maximum number of texture units that can be used from the vertex and fragment * shader with this WebGL implementation. The minimum is eight. If both shaders access the * same texture unit, this counts as two texture units. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_COMBINED_TEXTURE_IMAGE_UNITS. */ maximumCombinedTextureImageUnits : { get : function() { return this._maximumCombinedTextureImageUnits; } }, /** * The approximate maximum cube mape width and height supported by this WebGL implementation. * The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_CUBE_MAP_TEXTURE_SIZE. */ maximumCubeMapSize : { get : function() { return this._maximumCubeMapSize; } }, /** * The maximum number of vec4, ivec4, and bvec4 * uniforms that can be used by a fragment shader with this WebGL implementation. The minimum is 16. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_FRAGMENT_UNIFORM_VECTORS. */ maximumFragmentUniformVectors : { get : function() { return this._maximumFragmentUniformVectors; } }, /** * The maximum number of texture units that can be used from the fragment shader with this WebGL implementation. The minimum is eight. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_TEXTURE_IMAGE_UNITS. */ maximumTextureImageUnits : { get : function() { return this._maximumTextureImageUnits; } }, /** * The maximum renderbuffer width and height supported by this WebGL implementation. * The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_RENDERBUFFER_SIZE. */ maximumRenderbufferSize : { get : function() { return this._maximumRenderbufferSize; } }, /** * The approximate maximum texture width and height supported by this WebGL implementation. * The minimum is 64, but most desktop and laptop implementations will support much larger sizes like 8,192. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_TEXTURE_SIZE. */ maximumTextureSize : { get : function() { return this._maximumTextureSize; } }, /** * The maximum number of vec4 varying variables supported by this WebGL implementation. * The minimum is eight. Matrices and arrays count as multiple vec4s. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VARYING_VECTORS. */ maximumVaryingVectors : { get : function() { return this._maximumVaryingVectors; } }, /** * The maximum number of vec4 vertex attributes supported by this WebGL implementation. The minimum is eight. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_ATTRIBS. */ maximumVertexAttributes : { get : function() { return this._maximumVertexAttributes; } }, /** * The maximum number of texture units that can be used from the vertex shader with this WebGL implementation. * The minimum is zero, which means the GL does not support vertex texture fetch. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_TEXTURE_IMAGE_UNITS. */ maximumVertexTextureImageUnits : { get : function() { return this._maximumVertexTextureImageUnits; } }, /** * The maximum number of vec4, ivec4, and bvec4 * uniforms that can be used by a vertex shader with this WebGL implementation. The minimum is 16. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VERTEX_UNIFORM_VECTORS. */ maximumVertexUniformVectors : { get : function() { return this._maximumVertexUniformVectors; } }, /** * The minimum aliased line width, in pixels, supported by this WebGL implementation. It will be at most one. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_LINE_WIDTH_RANGE. */ minimumAliasedLineWidth : { get : function() { return this._aliasedLineWidthRange[0]; } }, /** * The maximum aliased line width, in pixels, supported by this WebGL implementation. It will be at least one. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_LINE_WIDTH_RANGE. */ maximumAliasedLineWidth : { get : function() { return this._aliasedLineWidthRange[1]; } }, /** * The minimum aliased point size, in pixels, supported by this WebGL implementation. It will be at most one. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_POINT_SIZE_RANGE. */ minimumAliasedPointSize : { get : function() { return this._aliasedPointSizeRange[0]; } }, /** * The maximum aliased point size, in pixels, supported by this WebGL implementation. It will be at least one. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with ALIASED_POINT_SIZE_RANGE. */ maximumAliasedPointSize : { get : function() { return this._aliasedPointSizeRange[1]; } }, /** * The maximum supported width of the viewport. It will be at least as large as the visible width of the associated canvas. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VIEWPORT_DIMS. */ maximumViewportWidth : { get : function() { return this._maximumViewportDimensions[0]; } }, /** * The maximum supported height of the viewport. It will be at least as large as the visible height of the associated canvas. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGet.xml|glGet} with MAX_VIEWPORT_DIMS. */ maximumViewportHeight : { get : function() { return this._maximumViewportDimensions[1]; } }, /** * true if the WebGL context supports antialiasing. By default * antialiasing is requested, but it is not supported by all systems. * @memberof Context.prototype * @type {Boolean} */ antialias : { get : function() { return this._antialias; } }, /** * true if the OES_standard_derivatives extension is supported. This * extension provides access to dFdx, dFdy, and fwidth * functions from GLSL. A shader using these functions still needs to explicitly enable the * extension with #extension GL_OES_standard_derivatives : enable. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/gles/extensions/OES/OES_standard_derivatives.txt|OES_standard_derivatives} */ standardDerivatives : { get : function() { return !!this._standardDerivatives; } }, /** * true if the OES_element_index_uint extension is supported. This * extension allows the use of unsigned int indices, which can improve performance by * eliminating batch breaking caused by unsigned short indices. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/webgl/extensions/OES_element_index_uint/|OES_element_index_uint} */ elementIndexUint : { get : function() { return !!this._elementIndexUint; } }, /** * true if WEBGL_depth_texture is supported. This extension provides * access to depth textures that, for example, can be attached to framebuffers for shadow mapping. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/|WEBGL_depth_texture} */ depthTexture : { get : function() { return !!this._depthTexture; } }, /** * true if OES_texture_float is supported. This extension provides * access to floating point textures that, for example, can be attached to framebuffers for high dynamic range. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/gles/extensions/OES/OES_texture_float.txt|OES_texture_float} */ floatingPointTexture : { get : function() { return !!this._textureFloat; } }, textureFilterAnisotropic : { get : function() { return !!this._textureFilterAnisotropic; } }, maximumTextureFilterAnisotropy : { get : function() { return this._maximumTextureFilterAnisotropy; } }, /** * true if the OES_vertex_array_object extension is supported. This * extension can improve performance by reducing the overhead of switching vertex arrays. * When enabled, this extension is automatically used by {@link VertexArray}. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/|OES_vertex_array_object} */ vertexArrayObject : { get : function() { return !!this._vertexArrayObject; } }, /** * true if the EXT_frag_depth extension is supported. This * extension provides access to the gl_FragDepthEXT built-in output variable * from GLSL fragment shaders. A shader using these functions still needs to explicitly enable the * extension with #extension GL_EXT_frag_depth : enable. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/webgl/extensions/EXT_frag_depth/|EXT_frag_depth} */ fragmentDepth : { get : function() { return !!this._fragDepth; } }, /** * true if the WEBGL_draw_buffers extension is supported. This * extensions provides support for multiple render targets. The framebuffer object can have mutiple * color attachments and the GLSL fragment shader can write to the built-in output array gl_FragData. * A shader using this feature needs to explicitly enable the extension with * #extension GL_EXT_draw_buffers : enable. * @memberof Context.prototype * @type {Boolean} * @see {@link http://www.khronos.org/registry/webgl/extensions/WEBGL_draw_buffers/|WEBGL_draw_buffers} */ drawBuffers : { get : function() { return !!this._drawBuffers; } }, /** * The maximum number of simultaneous outputs that may be written in a fragment shader. * @memberof Context.prototype * @type {Number} */ maximumDrawBuffers : { get : function() { return this._maximumDrawBuffers; } }, /** * The maximum number of color attachments supported. * @memberof Context.prototype * @type {Number} */ maximumColorAttachments : { get : function() { return this._maximumColorAttachments; } }, debugShaders : { get : function() { return this._debugShaders; } }, throwOnWebGLError : { get : function() { return this._throwOnWebGLError; }, set : function(value) { this._throwOnWebGLError = value; this._gl = wrapGL(this._originalGLContext, value ? throwOnError : null); } }, /** * A 1x1 RGBA texture initialized to [255, 255, 255, 255]. This can * be used as a placeholder texture while other textures are downloaded. * @memberof Context.prototype * @type {Texture} */ defaultTexture : { get : function() { if (this._defaultTexture === undefined) { this._defaultTexture = this.createTexture2D({ source : { width : 1, height : 1, arrayBufferView : new Uint8Array([255, 255, 255, 255]) } }); } return this._defaultTexture; } }, /** * A cube map, where each face is a 1x1 RGBA texture initialized to * [255, 255, 255, 255]. This can be used as a placeholder cube map while * other cube maps are downloaded. * @memberof Context.prototype * @type {CubeMap} */ defaultCubeMap : { get : function() { if (this._defaultCubeMap === undefined) { var face = { width : 1, height : 1, arrayBufferView : new Uint8Array([255, 255, 255, 255]) }; this._defaultCubeMap = this.createCubeMap({ source : { positiveX : face, negativeX : face, positiveY : face, negativeY : face, positiveZ : face, negativeZ : face } }); } return this._defaultCubeMap; } }, /** * The drawingBufferWidth of the underlying GL context. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferWidth|drawingBufferWidth} */ drawingBufferHeight : { get : function() { return this._gl.drawingBufferHeight; } }, /** * The drawingBufferHeight of the underlying GL context. * @memberof Context.prototype * @type {Number} * @see {@link https://www.khronos.org/registry/webgl/specs/1.0/#DOM-WebGLRenderingContext-drawingBufferHeight|drawingBufferHeight} */ drawingBufferWidth : { get : function() { return this._gl.drawingBufferWidth; } }, /** * Gets an object representing the currently bound framebuffer. While this instance is not an actual * {@link Framebuffer}, it is used to represent the default framebuffer in calls to * {@link Context.createTexture2DFromFramebuffer}. * @memberof Context.prototype * @type {Object} */ defaultFramebuffer : { get : function() { return defaultFramebufferMarker; } } }); Context.prototype.replaceShaderProgram = function(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations) { return this._shaderCache.replaceShaderProgram(shaderProgram, vertexShaderSource, fragmentShaderSource, attributeLocations); }; Context.prototype.createShaderProgram = function(vertexShaderSource, fragmentShaderSource, attributeLocations) { return this._shaderCache.getShaderProgram(vertexShaderSource, fragmentShaderSource, attributeLocations); }; function createBuffer(gl, bufferTarget, typedArrayOrSizeInBytes, usage) { var sizeInBytes; if (typeof typedArrayOrSizeInBytes === 'number') { sizeInBytes = typedArrayOrSizeInBytes; } else if (typeof typedArrayOrSizeInBytes === 'object' && typeof typedArrayOrSizeInBytes.byteLength === 'number') { sizeInBytes = typedArrayOrSizeInBytes.byteLength; } else { //>>includeStart('debug', pragmas.debug); throw new DeveloperError('typedArrayOrSizeInBytes must be either a typed array or a number.'); //>>includeEnd('debug'); } //>>includeStart('debug', pragmas.debug); if (sizeInBytes <= 0) { throw new DeveloperError('typedArrayOrSizeInBytes must be greater than zero.'); } if (!BufferUsage.validate(usage)) { throw new DeveloperError('usage is invalid.'); } //>>includeEnd('debug'); var buffer = gl.createBuffer(); gl.bindBuffer(bufferTarget, buffer); gl.bufferData(bufferTarget, typedArrayOrSizeInBytes, usage); gl.bindBuffer(bufferTarget, null); return new Buffer(gl, bufferTarget, sizeInBytes, usage, buffer); } /** * Creates a vertex buffer, which contains untyped vertex data in GPU-controlled memory. *

* A vertex array defines the actual makeup of a vertex, e.g., positions, normals, texture coordinates, * etc., by interpreting the raw data in one or more vertex buffers. * * @param {ArrayBufferView|Number} typedArrayOrSizeInBytes A typed array containing the data to copy to the buffer, or a Number defining the size of the buffer in bytes. * @param {BufferUsage} usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @returns {VertexBuffer} The vertex buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} The size in bytes must be greater than zero. * @exception {DeveloperError} Invalid usage. * * @see Context#createVertexArray * @see Context#createIndexBuffer * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with ARRAY_BUFFER * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with ARRAY_BUFFER * * @example * // Example 1. Create a dynamic vertex buffer 16 bytes in size. * var buffer = context.createVertexBuffer(16, BufferUsage.DYNAMIC_DRAW); * * @example * // Example 2. Create a dynamic vertex buffer from three floating-point values. * // The data copied to the vertex buffer is considered raw bytes until it is * // interpreted as vertices using a vertex array. * var positionBuffer = context.createVertexBuffer(new Float32Array([0, 0, 0]), * BufferUsage.STATIC_DRAW); */ Context.prototype.createVertexBuffer = function(typedArrayOrSizeInBytes, usage) { return createBuffer(this._gl, this._gl.ARRAY_BUFFER, typedArrayOrSizeInBytes, usage); }; /** * Creates an index buffer, which contains typed indices in GPU-controlled memory. *

* An index buffer can be attached to a vertex array to select vertices for rendering. * Context.draw can render using the entire index buffer or a subset * of the index buffer defined by an offset and count. * * @param {ArrayBufferView|Number} typedArrayOrSizeInBytes A typed array containing the data to copy to the buffer, or a Number defining the size of the buffer in bytes. * @param {BufferUsage} usage Specifies the expected usage pattern of the buffer. On some GL implementations, this can significantly affect performance. See {@link BufferUsage}. * @param {IndexDatatype} indexDatatype The datatype of indices in the buffer. * @returns {IndexBuffer} The index buffer, ready to be attached to a vertex array. * * @exception {DeveloperError} IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system. Check context.elementIndexUint. * @exception {DeveloperError} The size in bytes must be greater than zero. * @exception {DeveloperError} Invalid usage. * @exception {DeveloperError} Invalid indexDatatype. * * @see Context#createVertexArray * @see Context#createVertexBuffer * @see Context#draw * @see VertexArray * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glGenBuffer.xml|glGenBuffer} * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindBuffer.xml|glBindBuffer} with ELEMENT_ARRAY_BUFFER * @see {@link https://www.khronos.org/opengles/sdk/docs/man/xhtml/glBufferData.xml|glBufferData} with ELEMENT_ARRAY_BUFFER * * @example * // Example 1. Create a stream index buffer of unsigned shorts that is * // 16 bytes in size. * var buffer = context.createIndexBuffer(16, BufferUsage.STREAM_DRAW, * IndexDatatype.UNSIGNED_SHORT); * * @example * // Example 2. Create a static index buffer containing three unsigned shorts. * var buffer = context.createIndexBuffer(new Uint16Array([0, 1, 2]), * BufferUsage.STATIC_DRAW, IndexDatatype.UNSIGNED_SHORT) */ Context.prototype.createIndexBuffer = function(typedArrayOrSizeInBytes, usage, indexDatatype) { //>>includeStart('debug', pragmas.debug); if (!IndexDatatype.validate(indexDatatype)) { throw new DeveloperError('Invalid indexDatatype.'); } //>>includeEnd('debug'); if ((indexDatatype === IndexDatatype.UNSIGNED_INT) && !this.elementIndexUint) { throw new DeveloperError('IndexDatatype.UNSIGNED_INT requires OES_element_index_uint, which is not supported on this system. Check context.elementIndexUint.'); } var bytesPerIndex = IndexDatatype.getSizeInBytes(indexDatatype); var gl = this._gl; var buffer = createBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, typedArrayOrSizeInBytes, usage); var numberOfIndices = buffer.sizeInBytes / bytesPerIndex; defineProperties(buffer, { indexDatatype: { get : function() { return indexDatatype; } }, bytesPerIndex : { get : function() { return bytesPerIndex; } }, numberOfIndices : { get : function() { return numberOfIndices; } } }); return buffer; }; /** * Creates a vertex array, which defines the attributes making up a vertex, and contains an optional index buffer * to select vertices for rendering. Attributes are defined using object literals as shown in Example 1 below. * * @param {Object[]} [attributes] An optional array of attributes. * @param {IndexBuffer} [indexBuffer] An optional index buffer. * * @returns {VertexArray} The vertex array, ready for use with drawing. * * @exception {DeveloperError} Attribute must have a vertexBuffer. * @exception {DeveloperError} Attribute must have a componentsPerAttribute. * @exception {DeveloperError} Attribute must have a valid componentDatatype or not specify it. * @exception {DeveloperError} Attribute must have a strideInBytes less than or equal to 255 or not specify it. * @exception {DeveloperError} Index n is used by more than one attribute. * * @see Context#createVertexArrayFromGeometry * @see Context#createVertexBuffer * @see Context#createIndexBuffer * @see Context#draw * * @example * // Example 1. Create a vertex array with vertices made up of three floating point * // values, e.g., a position, from a single vertex buffer. No index buffer is used. * var positionBuffer = context.createVertexBuffer(12, BufferUsage.STATIC_DRAW); * var attributes = [ * { * index : 0, * enabled : true, * vertexBuffer : positionBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * normalize : false, * offsetInBytes : 0, * strideInBytes : 0 // tightly packed * } * ]; * var va = context.createVertexArray(attributes); * * @example * // Example 2. Create a vertex array with vertices from two different vertex buffers. * // Each vertex has a three-component position and three-component normal. * var positionBuffer = context.createVertexBuffer(12, BufferUsage.STATIC_DRAW); * var normalBuffer = context.createVertexBuffer(12, BufferUsage.STATIC_DRAW); * var attributes = [ * { * index : 0, * vertexBuffer : positionBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT * }, * { * index : 1, * vertexBuffer : normalBuffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT * } * ]; * var va = context.createVertexArray(attributes); * * @example * // Example 3. Creates the same vertex layout as Example 2 using a single * // vertex buffer, instead of two. * var buffer = context.createVertexBuffer(24, BufferUsage.STATIC_DRAW); * var attributes = [ * { * vertexBuffer : buffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * offsetInBytes : 0, * strideInBytes : 24 * }, * { * vertexBuffer : buffer, * componentsPerAttribute : 3, * componentDatatype : ComponentDatatype.FLOAT, * normalize : true, * offsetInBytes : 12, * strideInBytes : 24 * } * ]; * var va = context.createVertexArray(attributes); */ Context.prototype.createVertexArray = function(attributes, indexBuffer) { return new VertexArray(this._gl, this._vertexArrayObject, attributes, indexBuffer); }; /** * options.source can be {@link ImageData}, {@link Image}, {@link Canvas}, or {@link Video}. * * @exception {RuntimeError} When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, this WebGL implementation must support WEBGL_depth_texture. Check context.depthTexture. * @exception {RuntimeError} When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension. Check context.floatingPointTexture. * @exception {DeveloperError} options requires a source field to create an initialized texture or width and height fields to create a blank texture. * @exception {DeveloperError} Width must be greater than zero. * @exception {DeveloperError} Width must be less than or equal to the maximum texture size. * @exception {DeveloperError} Height must be greater than zero. * @exception {DeveloperError} Height must be less than or equal to the maximum texture size. * @exception {DeveloperError} Invalid options.pixelFormat. * @exception {DeveloperError} Invalid options.pixelDatatype. * @exception {DeveloperError} When options.pixelFormat is DEPTH_COMPONENT, options.pixelDatatype must be UNSIGNED_SHORT or UNSIGNED_INT. * @exception {DeveloperError} When options.pixelFormat is DEPTH_STENCIL, options.pixelDatatype must be UNSIGNED_INT_24_8_WEBGL. * @exception {DeveloperError} When options.pixelFormat is DEPTH_COMPONENT or DEPTH_STENCIL, source cannot be provided. * * @see Context#createTexture2DFromFramebuffer * @see Context#createCubeMap * @see Context#createSampler */ Context.prototype.createTexture2D = function(options) { return new Texture(this, options); }; /** * Creates a texture, and copies a subimage of the framebuffer to it. When called without arguments, * the texture is the same width and height as the framebuffer and contains its contents. * * @param {PixelFormat} [pixelFormat=PixelFormat.RGB] The texture's internal pixel format. * @param {Number} [framebufferXOffset=0] An offset in the x direction in the framebuffer where copying begins from. * @param {Number} [framebufferYOffset=0] An offset in the y direction in the framebuffer where copying begins from. * @param {Number} [width=canvas.clientWidth] The width of the texture in texels. * @param {Number} [height=canvas.clientHeight] The height of the texture in texels. * @param {Framebuffer} [framebuffer=defaultFramebuffer] The framebuffer from which to create the texture. If this * parameter is not specified, the default framebuffer is used. * @returns {Texture} A texture with contents from the framebuffer. * * @exception {DeveloperError} Invalid pixelFormat. * @exception {DeveloperError} pixelFormat cannot be DEPTH_COMPONENT or DEPTH_STENCIL. * @exception {DeveloperError} framebufferXOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferYOffset must be greater than or equal to zero. * @exception {DeveloperError} framebufferXOffset + width must be less than or equal to canvas.clientWidth. * @exception {DeveloperError} framebufferYOffset + height must be less than or equal to canvas.clientHeight. * * @see Context#createTexture2D * @see Context#createCubeMap * @see Context#createSampler * * @example * // Create a texture with the contents of the framebuffer. * var t = context.createTexture2DFromFramebuffer(); */ Context.prototype.createTexture2DFromFramebuffer = function(pixelFormat, framebufferXOffset, framebufferYOffset, width, height, framebuffer) { var gl = this._gl; pixelFormat = defaultValue(pixelFormat, PixelFormat.RGB); framebufferXOffset = defaultValue(framebufferXOffset, 0); framebufferYOffset = defaultValue(framebufferYOffset, 0); width = defaultValue(width, gl.drawingBufferWidth); height = defaultValue(height, gl.drawingBufferHeight); //>>includeStart('debug', pragmas.debug); if (!PixelFormat.validate(pixelFormat)) { throw new DeveloperError('Invalid pixelFormat.'); } if (PixelFormat.isDepthFormat(pixelFormat)) { throw new DeveloperError('pixelFormat cannot be DEPTH_COMPONENT or DEPTH_STENCIL.'); } if (framebufferXOffset < 0) { throw new DeveloperError('framebufferXOffset must be greater than or equal to zero.'); } if (framebufferYOffset < 0) { throw new DeveloperError('framebufferYOffset must be greater than or equal to zero.'); } if (framebufferXOffset + width > gl.drawingBufferWidth) { throw new DeveloperError('framebufferXOffset + width must be less than or equal to drawingBufferWidth'); } if (framebufferYOffset + height > gl.drawingBufferHeight) { throw new DeveloperError('framebufferYOffset + height must be less than or equal to drawingBufferHeight.'); } //>>includeEnd('debug'); var texture = new Texture(this, { width : width, height : height, pixelFormat : pixelFormat, source : { framebuffer : defined(framebuffer) ? framebuffer : this.defaultFramebuffer, xOffset : framebufferXOffset, yOffset : framebufferYOffset, width : width, height : height } }); return texture; }; /** * options.source can be {@link ImageData}, {@link Image}, {@link Canvas}, or {@link Video}. * * @returns {CubeMap} The newly created cube map. * * @exception {RuntimeError} When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension. Check context.floatingPointTexture. * @exception {DeveloperError} options.source requires positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ faces. * @exception {DeveloperError} Each face in options.sources must have the same width and height. * @exception {DeveloperError} options requires a source field to create an initialized cube map or width and height fields to create a blank cube map. * @exception {DeveloperError} Width must equal height. * @exception {DeveloperError} Width and height must be greater than zero. * @exception {DeveloperError} Width and height must be less than or equal to the maximum cube map size. * @exception {DeveloperError} Invalid options.pixelFormat. * @exception {DeveloperError} options.pixelFormat cannot be DEPTH_COMPONENT or DEPTH_STENCIL. * @exception {DeveloperError} Invalid options.pixelDatatype. * * @see Context#createTexture2D * @see Context#createTexture2DFromFramebuffer * @see Context#createSampler */ Context.prototype.createCubeMap = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var source = options.source; var width; var height; if (defined(source)) { var faces = [source.positiveX, source.negativeX, source.positiveY, source.negativeY, source.positiveZ, source.negativeZ]; //>>includeStart('debug', pragmas.debug); if (!faces[0] || !faces[1] || !faces[2] || !faces[3] || !faces[4] || !faces[5]) { throw new DeveloperError('options.source requires positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ faces.'); } //>>includeEnd('debug'); width = faces[0].width; height = faces[0].height; //>>includeStart('debug', pragmas.debug); for ( var i = 1; i < 6; ++i) { if ((Number(faces[i].width) !== width) || (Number(faces[i].height) !== height)) { throw new DeveloperError('Each face in options.source must have the same width and height.'); } } //>>includeEnd('debug'); } else { width = options.width; height = options.height; } var size = width; var pixelFormat = defaultValue(options.pixelFormat, PixelFormat.RGBA); var pixelDatatype = defaultValue(options.pixelDatatype, PixelDatatype.UNSIGNED_BYTE); //>>includeStart('debug', pragmas.debug); if (!defined(width) || !defined(height)) { throw new DeveloperError('options requires a source field to create an initialized cube map or width and height fields to create a blank cube map.'); } if (width !== height) { throw new DeveloperError('Width must equal height.'); } if (size <= 0) { throw new DeveloperError('Width and height must be greater than zero.'); } if (size > this._maximumCubeMapSize) { throw new DeveloperError('Width and height must be less than or equal to the maximum cube map size (' + this._maximumCubeMapSize + '). Check maximumCubeMapSize.'); } if (!PixelFormat.validate(pixelFormat)) { throw new DeveloperError('Invalid options.pixelFormat.'); } if (PixelFormat.isDepthFormat(pixelFormat)) { throw new DeveloperError('options.pixelFormat cannot be DEPTH_COMPONENT or DEPTH_STENCIL.'); } if (!PixelDatatype.validate(pixelDatatype)) { throw new DeveloperError('Invalid options.pixelDatatype.'); } //>>includeEnd('debug'); if ((pixelDatatype === PixelDatatype.FLOAT) && !this.floatingPointTexture) { throw new DeveloperError('When options.pixelDatatype is FLOAT, this WebGL implementation must support the OES_texture_float extension.'); } // Use premultiplied alpha for opaque textures should perform better on Chrome: // http://media.tojicode.com/webglCamp4/#20 var preMultiplyAlpha = options.preMultiplyAlpha || ((pixelFormat === PixelFormat.RGB) || (pixelFormat === PixelFormat.LUMINANCE)); var flipY = defaultValue(options.flipY, true); var gl = this._gl; var textureTarget = gl.TEXTURE_CUBE_MAP; var texture = gl.createTexture(); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(textureTarget, texture); function createFace(target, sourceFace) { if (sourceFace.arrayBufferView) { gl.texImage2D(target, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, sourceFace.arrayBufferView); } else { gl.texImage2D(target, 0, pixelFormat, pixelFormat, pixelDatatype, sourceFace); } } if (defined(source)) { // TODO: _gl.pixelStorei(_gl._UNPACK_ALIGNMENT, 4); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, preMultiplyAlpha); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_X, source.positiveX); createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, source.negativeX); createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, source.positiveY); createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, source.negativeY); createFace(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, source.positiveZ); createFace(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, source.negativeZ); } else { gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, pixelFormat, size, size, 0, pixelFormat, pixelDatatype, null); } gl.bindTexture(textureTarget, null); return new CubeMap(gl, this._textureFilterAnisotropic, textureTarget, texture, pixelFormat, pixelDatatype, size, preMultiplyAlpha, flipY); }; /** * Creates a framebuffer with optional initial color, depth, and stencil attachments. * Framebuffers are used for render-to-texture effects; they allow us to render to * textures in one pass, and read from it in a later pass. * * @param {Object} [options] The initial framebuffer attachments as shown in the examplebelow. The possible properties are colorTextures, colorRenderbuffers, depthTexture, depthRenderbuffer, stencilRenderbuffer, depthStencilTexture, and depthStencilRenderbuffer. * @returns {Framebuffer} The created framebuffer. * * @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments. * @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment. * @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer. * @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer. * @exception {DeveloperError} The color-texture pixel-format must be a color format. * @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT. * @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL. * @exception {DeveloperError} The number of color attachments exceeds the number supported. * * @see Context#createTexture2D * @see Context#createCubeMap * @see Context#createRenderbuffer * * @example * // Create a framebuffer with color and depth texture attachments. * var width = context.canvas.clientWidth; * var height = context.canvas.clientHeight; * var framebuffer = context.createFramebuffer({ * colorTextures : [context.createTexture2D({ * width : width, * height : height, * pixelFormat : PixelFormat.RGBA * })], * depthTexture : context.createTexture2D({ * width : width, * height : height, * pixelFormat : PixelFormat.DEPTH_COMPONENT, * pixelDatatype : PixelDatatype.UNSIGNED_SHORT * }) * }); */ Context.prototype.createFramebuffer = function(options) { return new Framebuffer(this._gl, this._maximumColorAttachments, options); }; Context.prototype.createRenderbuffer = function(options) { var gl = this._gl; options = defaultValue(options, defaultValue.EMPTY_OBJECT); var format = defaultValue(options.format, RenderbufferFormat.RGBA4); var width = defined(options.width) ? options.width : gl.drawingBufferWidth; var height = defined(options.height) ? options.height : gl.drawingBufferHeight; //>>includeStart('debug', pragmas.debug); if (!RenderbufferFormat.validate(format)) { throw new DeveloperError('Invalid format.'); } if (width <= 0) { throw new DeveloperError('Width must be greater than zero.'); } if (width > this.maximumRenderbufferSize) { throw new DeveloperError('Width must be less than or equal to the maximum renderbuffer size (' + this.maximumRenderbufferSize + '). Check maximumRenderbufferSize.'); } if (height <= 0) { throw new DeveloperError('Height must be greater than zero.'); } if (height > this.maximumRenderbufferSize) { throw new DeveloperError('Height must be less than or equal to the maximum renderbuffer size (' + this.maximumRenderbufferSize + '). Check maximumRenderbufferSize.'); } //>>includeEnd('debug'); return new Renderbuffer(gl, format, width, height); }; var nextRenderStateId = 0; var renderStateCache = {}; /** * Validates and then finds or creates an immutable render state, which defines the pipeline * state for a {@link DrawCommand} or {@link ClearCommand}. All inputs states are optional. Omitted states * use the defaults shown in the example below. * * @param {Object} [renderState] The states defining the render state as shown in the example below. * * @exception {RuntimeError} renderState.lineWidth is out of range. * @exception {DeveloperError} Invalid renderState.frontFace. * @exception {DeveloperError} Invalid renderState.cull.face. * @exception {DeveloperError} scissorTest.rectangle.width and scissorTest.rectangle.height must be greater than or equal to zero. * @exception {DeveloperError} renderState.depthRange.near can't be greater than renderState.depthRange.far. * @exception {DeveloperError} renderState.depthRange.near must be greater than or equal to zero. * @exception {DeveloperError} renderState.depthRange.far must be less than or equal to zero. * @exception {DeveloperError} Invalid renderState.depthTest.func. * @exception {DeveloperError} renderState.blending.color components must be greater than or equal to zero and less than or equal to one * @exception {DeveloperError} Invalid renderState.blending.equationRgb. * @exception {DeveloperError} Invalid renderState.blending.equationAlpha. * @exception {DeveloperError} Invalid renderState.blending.functionSourceRgb. * @exception {DeveloperError} Invalid renderState.blending.functionSourceAlpha. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationRgb. * @exception {DeveloperError} Invalid renderState.blending.functionDestinationAlpha. * @exception {DeveloperError} Invalid renderState.stencilTest.frontFunction. * @exception {DeveloperError} Invalid renderState.stencilTest.backFunction. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.fail. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zFail. * @exception {DeveloperError} Invalid renderState.stencilTest.frontOperation.zPass. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.fail. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zFail. * @exception {DeveloperError} Invalid renderState.stencilTest.backOperation.zPass. * @exception {DeveloperError} renderState.viewport.width must be greater than or equal to zero. * @exception {DeveloperError} renderState.viewport.width must be less than or equal to the maximum viewport width. * @exception {DeveloperError} renderState.viewport.height must be greater than or equal to zero. * @exception {DeveloperError} renderState.viewport.height must be less than or equal to the maximum viewport height. * * @see DrawCommand * @see ClearCommand * * @example * var defaults = { * frontFace : WindingOrder.COUNTER_CLOCKWISE, * cull : { * enabled : false, * face : CullFace.BACK * }, * lineWidth : 1, * polygonOffset : { * enabled : false, * factor : 0, * units : 0 * }, * scissorTest : { * enabled : false, * rectangle : { * x : 0, * y : 0, * width : 0, * height : 0 * } * }, * depthRange : { * near : 0, * far : 1 * }, * depthTest : { * enabled : false, * func : DepthFunction.LESS * }, * colorMask : { * red : true, * green : true, * blue : true, * alpha : true * }, * depthMask : true, * stencilMask : ~0, * blending : { * enabled : false, * color : { * red : 0.0, * green : 0.0, * blue : 0.0, * alpha : 0.0 * }, * equationRgb : BlendEquation.ADD, * equationAlpha : BlendEquation.ADD, * functionSourceRgb : BlendFunction.ONE, * functionSourceAlpha : BlendFunction.ONE, * functionDestinationRgb : BlendFunction.ZERO, * functionDestinationAlpha : BlendFunction.ZERO * }, * stencilTest : { * enabled : false, * frontFunction : StencilFunction.ALWAYS, * backFunction : StencilFunction.ALWAYS, * reference : 0, * mask : ~0, * frontOperation : { * fail : StencilOperation.KEEP, * zFail : StencilOperation.KEEP, * zPass : StencilOperation.KEEP * }, * backOperation : { * fail : StencilOperation.KEEP, * zFail : StencilOperation.KEEP, * zPass : StencilOperation.KEEP * } * }, * sampleCoverage : { * enabled : false, * value : 1.0, * invert : false * } * }; * * // Same as just context.createRenderState(). * var rs = context.createRenderState(defaults); */ Context.prototype.createRenderState = function(renderState) { var partialKey = JSON.stringify(renderState); var cachedState = renderStateCache[partialKey]; if (defined(cachedState)) { return cachedState; } // Cache miss. Fully define render state and try again. var states = new RenderState(this, renderState); var fullKey = JSON.stringify(states); cachedState = renderStateCache[fullKey]; if (!defined(cachedState)) { states.id = nextRenderStateId++; cachedState = states; // Cache full render state. Multiple partially defined render states may map to this. renderStateCache[fullKey] = cachedState; } // Cache partial render state so we can skip validation on a cache hit for a partially defined render state renderStateCache[partialKey] = cachedState; return cachedState; }; Context.prototype.createSampler = function(sampler) { var s = { wrapS : defaultValue(sampler.wrapS, TextureWrap.CLAMP_TO_EDGE), wrapT : defaultValue(sampler.wrapT, TextureWrap.CLAMP_TO_EDGE), minificationFilter : defaultValue(sampler.minificationFilter, TextureMinificationFilter.LINEAR), magnificationFilter : defaultValue(sampler.magnificationFilter, TextureMagnificationFilter.LINEAR), maximumAnisotropy : (defined(sampler.maximumAnisotropy)) ? sampler.maximumAnisotropy : 1.0 }; //>>includeStart('debug', pragmas.debug); if (!TextureWrap.validate(s.wrapS)) { throw new DeveloperError('Invalid sampler.wrapS.'); } if (!TextureWrap.validate(s.wrapT)) { throw new DeveloperError('Invalid sampler.wrapT.'); } if (!TextureMinificationFilter.validate(s.minificationFilter)) { throw new DeveloperError('Invalid sampler.minificationFilter.'); } if (!TextureMagnificationFilter.validate(s.magnificationFilter)) { throw new DeveloperError('Invalid sampler.magnificationFilter.'); } if (s.maximumAnisotropy < 1.0) { throw new DeveloperError('sampler.maximumAnisotropy must be greater than or equal to one.'); } //>>includeEnd('debug'); return s; }; function validateFramebuffer(context, framebuffer) { if (context.validateFramebuffer) { var gl = context._gl; var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (status !== gl.FRAMEBUFFER_COMPLETE) { var message; switch (status) { case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: message = 'Framebuffer is not complete. Incomplete attachment: at least one attachment point with a renderbuffer or texture attached has its attached object no longer in existence or has an attached image with a width or height of zero, or the color attachment point has a non-color-renderable image attached, or the depth attachment point has a non-depth-renderable image attached, or the stencil attachment point has a non-stencil-renderable image attached. Color-renderable formats include GL_RGBA4, GL_RGB5_A1, and GL_RGB565. GL_DEPTH_COMPONENT16 is the only depth-renderable format. GL_STENCIL_INDEX8 is the only stencil-renderable format.'; break; case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: message = 'Framebuffer is not complete. Incomplete dimensions: not all attached images have the same width and height.'; break; case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: message = 'Framebuffer is not complete. Missing attachment: no images are attached to the framebuffer.'; break; case gl.FRAMEBUFFER_UNSUPPORTED: message = 'Framebuffer is not complete. Unsupported: the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.'; break; } throw new DeveloperError(message); } } } function applyRenderState(context, renderState, passState) { var previousState = context._currentRenderState; if (previousState !== renderState) { context._currentRenderState = renderState; RenderState.partialApply(context._gl, previousState, renderState, passState); } // else same render state as before so state is already applied. } var scratchBackBufferArray; // this check must use typeof, not defined, because defined doesn't work with undeclared variables. if (typeof WebGLRenderingContext !== 'undefined') { scratchBackBufferArray = [WebGLRenderingContext.BACK]; } function bindFramebuffer(context, framebuffer) { if (framebuffer !== context._currentFramebuffer) { context._currentFramebuffer = framebuffer; var buffers = scratchBackBufferArray; if (defined(framebuffer)) { framebuffer._bind(); validateFramebuffer(context, framebuffer); // TODO: Need a way for a command to give what draw buffers are active. buffers = framebuffer._getActiveColorAttachments(); } else { var gl = context._gl; gl.bindFramebuffer(gl.FRAMEBUFFER, null); } if (context.drawBuffers) { context._drawBuffers.drawBuffersWEBGL(buffers); } } } var defaultClearCommand = new ClearCommand(); Context.prototype.clear = function(clearCommand, passState) { clearCommand = defaultValue(clearCommand, defaultClearCommand); passState = defaultValue(passState, this._defaultPassState); var gl = this._gl; var bitmask = 0; var c = clearCommand.color; var d = clearCommand.depth; var s = clearCommand.stencil; if (defined(c)) { if (!Color.equals(this._clearColor, c)) { Color.clone(c, this._clearColor); gl.clearColor(c.red, c.green, c.blue, c.alpha); } bitmask |= gl.COLOR_BUFFER_BIT; } if (defined(d)) { if (d !== this._clearDepth) { this._clearDepth = d; gl.clearDepth(d); } bitmask |= gl.DEPTH_BUFFER_BIT; } if (defined(s)) { if (s !== this._clearStencil) { this._clearStencil = s; gl.clearStencil(s); } bitmask |= gl.STENCIL_BUFFER_BIT; } var rs = defaultValue(clearCommand.renderState, this._defaultRenderState); applyRenderState(this, rs, passState); // The command's framebuffer takes presidence over the pass' framebuffer, e.g., for off-screen rendering. var framebuffer = defaultValue(clearCommand.framebuffer, passState.framebuffer); bindFramebuffer(this, framebuffer); gl.clear(bitmask); }; function beginDraw(context, framebuffer, drawCommand, passState, renderState, shaderProgram) { var rs = defaultValue(defaultValue(renderState, drawCommand.renderState), context._defaultRenderState); //>>includeStart('debug', pragmas.debug); if (defined(framebuffer) && rs.depthTest) { if (rs.depthTest.enabled && !framebuffer.hasDepthAttachment) { throw new DeveloperError('The depth test can not be enabled (drawCommand.renderState.depthTest.enabled) because the framebuffer (drawCommand.framebuffer) does not have a depth or depth-stencil renderbuffer.'); } } //>>includeEnd('debug'); bindFramebuffer(context, framebuffer); var sp = defaultValue(shaderProgram, drawCommand.shaderProgram); sp._bind(); context._maxFrameTextureUnitIndex = Math.max(context._maxFrameTextureUnitIndex, sp.maximumTextureUnitIndex); applyRenderState(context, rs, passState); } function continueDraw(context, drawCommand, shaderProgram) { var primitiveType = drawCommand.primitiveType; var va = drawCommand.vertexArray; var offset = drawCommand.offset; var count = drawCommand.count; //>>includeStart('debug', pragmas.debug); if (!PrimitiveType.validate(primitiveType)) { throw new DeveloperError('drawCommand.primitiveType is required and must be valid.'); } if (!defined(va)) { throw new DeveloperError('drawCommand.vertexArray is required.'); } if (offset < 0) { throw new DeveloperError('drawCommand.offset must be omitted or greater than or equal to zero.'); } if (count < 0) { throw new DeveloperError('drawCommand.count must be omitted or greater than or equal to zero.'); } //>>includeEnd('debug'); context._us.model = defaultValue(drawCommand.modelMatrix, Matrix4.IDENTITY); var sp = defaultValue(shaderProgram, drawCommand.shaderProgram); sp._setUniforms(drawCommand.uniformMap, context._us, context.validateShaderProgram); var indexBuffer = va.indexBuffer; if (defined(indexBuffer)) { offset = offset * indexBuffer.bytesPerIndex; // offset in vertices to offset in bytes count = defaultValue(count, indexBuffer.numberOfIndices); va._bind(); context._gl.drawElements(primitiveType, count, indexBuffer.indexDatatype, offset); va._unBind(); } else { count = defaultValue(count, va.numberOfVertices); va._bind(); context._gl.drawArrays(primitiveType, offset, count); va._unBind(); } } Context.prototype.draw = function(drawCommand, passState, renderState, shaderProgram) { //>>includeStart('debug', pragmas.debug); if (!defined(drawCommand)) { throw new DeveloperError('drawCommand is required.'); } if (!defined(drawCommand.shaderProgram)) { throw new DeveloperError('drawCommand.shaderProgram is required.'); } //>>includeEnd('debug'); passState = defaultValue(passState, this._defaultPassState); // The command's framebuffer takes presidence over the pass' framebuffer, e.g., for off-screen rendering. var framebuffer = defaultValue(drawCommand.framebuffer, passState.framebuffer); beginDraw(this, framebuffer, drawCommand, passState, renderState, shaderProgram); continueDraw(this, drawCommand, shaderProgram); }; Context.prototype.endFrame = function() { var gl = this._gl; gl.useProgram(null); this._currentFramebuffer = undefined; gl.bindFramebuffer(gl.FRAMEBUFFER, null); var buffers = scratchBackBufferArray; if (this.drawBuffers) { this._drawBuffers.drawBuffersWEBGL(scratchBackBufferArray); } var length = this._maxFrameTextureUnitIndex; this._maxFrameTextureUnitIndex = 0; for (var i = 0; i < length; ++i) { gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); } }; Context.prototype.readPixels = function(readState) { var gl = this._gl; readState = readState || {}; var x = Math.max(readState.x || 0, 0); var y = Math.max(readState.y || 0, 0); var width = readState.width || gl.drawingBufferWidth; var height = readState.height || gl.drawingBufferHeight; var framebuffer = readState.framebuffer; //>>includeStart('debug', pragmas.debug); if (width <= 0) { throw new DeveloperError('readState.width must be greater than zero.'); } if (height <= 0) { throw new DeveloperError('readState.height must be greater than zero.'); } //>>includeEnd('debug'); var pixels = new Uint8Array(4 * width * height); bindFramebuffer(this, framebuffer); gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); return pixels; }; ////////////////////////////////////////////////////////////////////////////////////////// function computeNumberOfVertices(attribute) { return attribute.values.length / attribute.componentsPerAttribute; } function computeAttributeSizeInBytes(attribute) { return ComponentDatatype.getSizeInBytes(attribute.componentDatatype) * attribute.componentsPerAttribute; } function interleaveAttributes(attributes) { var j; var name; var attribute; // Extract attribute names. var names = []; for (name in attributes) { // Attribute needs to have per-vertex values; not a constant value for all vertices. if (attributes.hasOwnProperty(name) && defined(attributes[name]) && defined(attributes[name].values)) { names.push(name); if (attributes[name].componentDatatype === ComponentDatatype.DOUBLE) { attributes[name].componentDatatype = ComponentDatatype.FLOAT; attributes[name].values = ComponentDatatype.createTypedArray(ComponentDatatype.FLOAT, attributes[name].values); } } } // Validation. Compute number of vertices. var numberOfVertices; var namesLength = names.length; if (namesLength > 0) { numberOfVertices = computeNumberOfVertices(attributes[names[0]]); for (j = 1; j < namesLength; ++j) { var currentNumberOfVertices = computeNumberOfVertices(attributes[names[j]]); if (currentNumberOfVertices !== numberOfVertices) { throw new RuntimeError( 'Each attribute list must have the same number of vertices. ' + 'Attribute ' + names[j] + ' has a different number of vertices ' + '(' + currentNumberOfVertices.toString() + ')' + ' than attribute ' + names[0] + ' (' + numberOfVertices.toString() + ').'); } } } // Sort attributes by the size of their components. From left to right, a vertex stores floats, shorts, and then bytes. names.sort(function(left, right) { return ComponentDatatype.getSizeInBytes(attributes[right].componentDatatype) - ComponentDatatype.getSizeInBytes(attributes[left].componentDatatype); }); // Compute sizes and strides. var vertexSizeInBytes = 0; var offsetsInBytes = {}; for (j = 0; j < namesLength; ++j) { name = names[j]; attribute = attributes[name]; offsetsInBytes[name] = vertexSizeInBytes; vertexSizeInBytes += computeAttributeSizeInBytes(attribute); } if (vertexSizeInBytes > 0) { // Pad each vertex to be a multiple of the largest component datatype so each // attribute can be addressed using typed arrays. var maxComponentSizeInBytes = ComponentDatatype.getSizeInBytes(attributes[names[0]].componentDatatype); // Sorted large to small var remainder = vertexSizeInBytes % maxComponentSizeInBytes; if (remainder !== 0) { vertexSizeInBytes += (maxComponentSizeInBytes - remainder); } // Total vertex buffer size in bytes, including per-vertex padding. var vertexBufferSizeInBytes = numberOfVertices * vertexSizeInBytes; // Create array for interleaved vertices. Each attribute has a different view (pointer) into the array. var buffer = new ArrayBuffer(vertexBufferSizeInBytes); var views = {}; for (j = 0; j < namesLength; ++j) { name = names[j]; var sizeInBytes = ComponentDatatype.getSizeInBytes(attributes[name].componentDatatype); views[name] = { pointer : ComponentDatatype.createTypedArray(attributes[name].componentDatatype, buffer), index : offsetsInBytes[name] / sizeInBytes, // Offset in ComponentType strideInComponentType : vertexSizeInBytes / sizeInBytes }; } // Copy attributes into one interleaved array. // PERFORMANCE_IDEA: Can we optimize these loops? for (j = 0; j < numberOfVertices; ++j) { for ( var n = 0; n < namesLength; ++n) { name = names[n]; attribute = attributes[name]; var values = attribute.values; var view = views[name]; var pointer = view.pointer; var numberOfComponents = attribute.componentsPerAttribute; for ( var k = 0; k < numberOfComponents; ++k) { pointer[view.index + k] = values[(j * numberOfComponents) + k]; } view.index += view.strideInComponentType; } } return { buffer : buffer, offsetsInBytes : offsetsInBytes, vertexSizeInBytes : vertexSizeInBytes }; } // No attributes to interleave. return undefined; } /** * Creates a vertex array from a geometry. A geometry contains vertex attributes and optional index data * in system memory, whereas a vertex array contains vertex buffers and an optional index buffer in WebGL * memory for use with rendering. *

* The geometry argument should use the standard layout like the geometry returned by {@link BoxGeometry}. *

* options can have four properties: *
    *
  • geometry: The source geometry containing data used to create the vertex array.
  • *
  • attributeLocations: An object that maps geometry attribute names to vertex shader attribute locations.
  • *
  • bufferUsage: The expected usage pattern of the vertex array's buffers. On some WebGL implementations, this can significantly affect performance. See {@link BufferUsage}. Default: BufferUsage.DYNAMIC_DRAW.
  • *
  • interleave: Determines if all attributes are interleaved in a single vertex buffer or if each attribute is stored in a separate vertex buffer. Default: false.
  • *
*
* If options is not specified or the geometry contains no data, the returned vertex array is empty. * * @param {Object} [options] An object defining the geometry, attribute indices, buffer usage, and vertex layout used to create the vertex array. * * @exception {RuntimeError} Each attribute list must have the same number of vertices. * @exception {DeveloperError} The geometry must have zero or one index lists. * @exception {DeveloperError} Index n is used by more than one attribute. * * @see Context#createVertexArray * @see Context#createVertexBuffer * @see Context#createIndexBuffer * @see GeometryPipeline.createAttributeLocations * @see ShaderProgram * * @example * // Example 1. Creates a vertex array for rendering a box. The default dynamic draw * // usage is used for the created vertex and index buffer. The attributes are not * // interleaved by default. * var geometry = new BoxGeometry(); * var va = context.createVertexArrayFromGeometry({ * geometry : geometry, * attributeLocations : GeometryPipeline.createAttributeLocations(geometry), * }); * * @example * // Example 2. Creates a vertex array with interleaved attributes in a * // single vertex buffer. The vertex and index buffer have static draw usage. * var va = context.createVertexArrayFromGeometry({ * geometry : geometry, * attributeLocations : GeometryPipeline.createAttributeLocations(geometry), * bufferUsage : BufferUsage.STATIC_DRAW, * interleave : true * }); * * @example * // Example 3. When the caller destroys the vertex array, it also destroys the * // attached vertex buffer(s) and index buffer. * va = va.destroy(); */ Context.prototype.createVertexArrayFromGeometry = function(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); var geometry = defaultValue(options.geometry, defaultValue.EMPTY_OBJECT); var bufferUsage = defaultValue(options.bufferUsage, BufferUsage.DYNAMIC_DRAW); var attributeLocations = defaultValue(options.attributeLocations, defaultValue.EMPTY_OBJECT); var interleave = defaultValue(options.interleave, false); var createdVAAttributes = options.vertexArrayAttributes; var name; var attribute; var vertexBuffer; var vaAttributes = (defined(createdVAAttributes)) ? createdVAAttributes : []; var attributes = geometry.attributes; if (interleave) { // Use a single vertex buffer with interleaved vertices. var interleavedAttributes = interleaveAttributes(attributes); if (defined(interleavedAttributes)) { vertexBuffer = this.createVertexBuffer(interleavedAttributes.buffer, bufferUsage); var offsetsInBytes = interleavedAttributes.offsetsInBytes; var strideInBytes = interleavedAttributes.vertexSizeInBytes; for (name in attributes) { if (attributes.hasOwnProperty(name) && defined(attributes[name])) { attribute = attributes[name]; if (defined(attribute.values)) { // Common case: per-vertex attributes vaAttributes.push({ index : attributeLocations[name], vertexBuffer : vertexBuffer, componentDatatype : attribute.componentDatatype, componentsPerAttribute : attribute.componentsPerAttribute, normalize : attribute.normalize, offsetInBytes : offsetsInBytes[name], strideInBytes : strideInBytes }); } else { // Constant attribute for all vertices vaAttributes.push({ index : attributeLocations[name], value : attribute.value, componentDatatype : attribute.componentDatatype, normalize : attribute.normalize }); } } } } } else { // One vertex buffer per attribute. for (name in attributes) { if (attributes.hasOwnProperty(name) && defined(attributes[name])) { attribute = attributes[name]; var componentDatatype = attribute.componentDatatype; if (componentDatatype === ComponentDatatype.DOUBLE) { componentDatatype = ComponentDatatype.FLOAT; } vertexBuffer = undefined; if (defined(attribute.values)) { vertexBuffer = this.createVertexBuffer(ComponentDatatype.createTypedArray(componentDatatype, attribute.values), bufferUsage); } vaAttributes.push({ index : attributeLocations[name], vertexBuffer : vertexBuffer, value : attribute.value, componentDatatype : componentDatatype, componentsPerAttribute : attribute.componentsPerAttribute, normalize : attribute.normalize }); } } } var indexBuffer; var indices = geometry.indices; if (defined(indices)) { if ((Geometry.computeNumberOfVertices(geometry) > CesiumMath.SIXTY_FOUR_KILOBYTES) && this.elementIndexUint) { indexBuffer = this.createIndexBuffer(new Uint32Array(indices), bufferUsage, IndexDatatype.UNSIGNED_INT); } else{ indexBuffer = this.createIndexBuffer(new Uint16Array(indices), bufferUsage, IndexDatatype.UNSIGNED_SHORT); } } return this.createVertexArray(vaAttributes, indexBuffer); }; var viewportQuadAttributeLocations = { position : 0, textureCoordinates : 1 }; Context.prototype.createViewportQuadCommand = function(fragmentShaderSource, overrides) { // Per-context cache for viewport quads var vertexArray = this.cache.viewportQuad_vertexArray; if (!defined(vertexArray)) { var geometry = new Geometry({ attributes : { position : new GeometryAttribute({ componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 2, values : [ -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ] }), textureCoordinates : new GeometryAttribute({ componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 2, values : [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ] }) }, // Workaround Internet Explorer 11.0.8 lack of TRIANGLE_FAN indices : new Uint16Array([0, 1, 2, 0, 2, 3]), primitiveType : PrimitiveType.TRIANGLES }); vertexArray = this.createVertexArrayFromGeometry({ geometry : geometry, attributeLocations : { position : 0, textureCoordinates : 1 }, bufferUsage : BufferUsage.STATIC_DRAW, interleave : true }); this.cache.viewportQuad_vertexArray = vertexArray; } overrides = defaultValue(overrides, defaultValue.EMPTY_OBJECT); return new DrawCommand({ vertexArray : vertexArray, primitiveType : PrimitiveType.TRIANGLES, renderState : overrides.renderState, shaderProgram : this.createShaderProgram(ViewportQuadVS, fragmentShaderSource, viewportQuadAttributeLocations), uniformMap : overrides.uniformMap, owner : overrides.owner, framebuffer : overrides.framebuffer }); }; Context.prototype.createPickFramebuffer = function() { return new PickFramebuffer(this); }; /** * Gets the object associated with a pick color. * * @param {Color} pickColor The pick color. * @returns {Object} The object associated with the pick color, or undefined if no object is associated with that color. * * @see Context#createPickId * * @example * var object = context.getObjectByPickColor(pickColor); */ Context.prototype.getObjectByPickColor = function(pickColor) { //>>includeStart('debug', pragmas.debug); if (!defined(pickColor)) { throw new DeveloperError('pickColor is required.'); } //>>includeEnd('debug'); return this._pickObjects[pickColor.toRgba()]; }; function PickId(pickObjects, key, color) { this._pickObjects = pickObjects; this.key = key; this.color = color; } defineProperties(PickId.prototype, { object : { get : function() { return this._pickObjects[this.key]; }, set : function(value) { this._pickObjects[this.key] = value; } } }); PickId.prototype.destroy = function() { delete this._pickObjects[this.key]; return undefined; }; /** * Creates a unique ID associated with the input object for use with color-buffer picking. * The ID has an RGBA color value unique to this context. You must call destroy() * on the pick ID when destroying the input object. * * @param {Object} object The object to associate with the pick ID. * @returns {Object} A PickId object with a color property. * * @exception {RuntimeError} Out of unique Pick IDs. * * @see Context#getObjectByPickColor * * @example * this._pickId = context.createPickId({ * primitive : this, * id : this.id * }); */ Context.prototype.createPickId = function(object) { //>>includeStart('debug', pragmas.debug); if (!defined(object)) { throw new DeveloperError('object is required.'); } //>>includeEnd('debug'); // the increment and assignment have to be separate statements to // actually detect overflow in the Uint32 value ++this._nextPickColor[0]; var key = this._nextPickColor[0]; if (key === 0) { // In case of overflow throw new RuntimeError('Out of unique Pick IDs.'); } this._pickObjects[key] = object; return new PickId(this._pickObjects, key, Color.fromRgba(key)); }; Context.prototype.isDestroyed = function() { return false; }; Context.prototype.destroy = function() { // Destroy all objects in the cache that have a destroy method. var cache = this.cache; for (var property in cache) { if (cache.hasOwnProperty(property)) { var propertyValue = cache[property]; if (defined(propertyValue.destroy)) { propertyValue.destroy(); } } } this._shaderCache = this._shaderCache.destroy(); this._defaultTexture = this._defaultTexture && this._defaultTexture.destroy(); this._defaultCubeMap = this._defaultCubeMap && this._defaultCubeMap.destroy(); return destroyObject(this); }; return Context; });