919 lines
27 KiB
JavaScript
919 lines
27 KiB
JavaScript
|
var check = require('./util/check')
|
||
|
var values = require('./util/values')
|
||
|
var extend = require('./util/extend')
|
||
|
|
||
|
// We store these constants so that the minifier can inline them
|
||
|
var GL_FRAMEBUFFER = 0x8D40
|
||
|
var GL_RENDERBUFFER = 0x8D41
|
||
|
|
||
|
var GL_TEXTURE_2D = 0x0DE1
|
||
|
var GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515
|
||
|
|
||
|
var GL_COLOR_ATTACHMENT0 = 0x8CE0
|
||
|
var GL_DEPTH_ATTACHMENT = 0x8D00
|
||
|
var GL_STENCIL_ATTACHMENT = 0x8D20
|
||
|
var GL_DEPTH_STENCIL_ATTACHMENT = 0x821A
|
||
|
|
||
|
var GL_FRAMEBUFFER_COMPLETE = 0x8CD5
|
||
|
var GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6
|
||
|
var GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7
|
||
|
var GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9
|
||
|
var GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDD
|
||
|
|
||
|
var GL_HALF_FLOAT_OES = 0x8D61
|
||
|
var GL_UNSIGNED_BYTE = 0x1401
|
||
|
var GL_FLOAT = 0x1406
|
||
|
|
||
|
var GL_RGB = 0x1907
|
||
|
var GL_RGBA = 0x1908
|
||
|
|
||
|
var GL_DEPTH_COMPONENT = 0x1902
|
||
|
|
||
|
var colorTextureFormatEnums = [
|
||
|
GL_RGB,
|
||
|
GL_RGBA
|
||
|
]
|
||
|
|
||
|
// for every texture format, store
|
||
|
// the number of channels
|
||
|
var textureFormatChannels = []
|
||
|
textureFormatChannels[GL_RGBA] = 4
|
||
|
textureFormatChannels[GL_RGB] = 3
|
||
|
|
||
|
// for every texture type, store
|
||
|
// the size in bytes.
|
||
|
var textureTypeSizes = []
|
||
|
textureTypeSizes[GL_UNSIGNED_BYTE] = 1
|
||
|
textureTypeSizes[GL_FLOAT] = 4
|
||
|
textureTypeSizes[GL_HALF_FLOAT_OES] = 2
|
||
|
|
||
|
var GL_RGBA4 = 0x8056
|
||
|
var GL_RGB5_A1 = 0x8057
|
||
|
var GL_RGB565 = 0x8D62
|
||
|
var GL_DEPTH_COMPONENT16 = 0x81A5
|
||
|
var GL_STENCIL_INDEX8 = 0x8D48
|
||
|
var GL_DEPTH_STENCIL = 0x84F9
|
||
|
|
||
|
var GL_SRGB8_ALPHA8_EXT = 0x8C43
|
||
|
|
||
|
var GL_RGBA32F_EXT = 0x8814
|
||
|
|
||
|
var GL_RGBA16F_EXT = 0x881A
|
||
|
var GL_RGB16F_EXT = 0x881B
|
||
|
|
||
|
var colorRenderbufferFormatEnums = [
|
||
|
GL_RGBA4,
|
||
|
GL_RGB5_A1,
|
||
|
GL_RGB565,
|
||
|
GL_SRGB8_ALPHA8_EXT,
|
||
|
GL_RGBA16F_EXT,
|
||
|
GL_RGB16F_EXT,
|
||
|
GL_RGBA32F_EXT
|
||
|
]
|
||
|
|
||
|
var statusCode = {}
|
||
|
statusCode[GL_FRAMEBUFFER_COMPLETE] = 'complete'
|
||
|
statusCode[GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT] = 'incomplete attachment'
|
||
|
statusCode[GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS] = 'incomplete dimensions'
|
||
|
statusCode[GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT] = 'incomplete, missing attachment'
|
||
|
statusCode[GL_FRAMEBUFFER_UNSUPPORTED] = 'unsupported'
|
||
|
|
||
|
module.exports = function wrapFBOState (
|
||
|
gl,
|
||
|
extensions,
|
||
|
limits,
|
||
|
textureState,
|
||
|
renderbufferState,
|
||
|
stats) {
|
||
|
var framebufferState = {
|
||
|
cur: null,
|
||
|
next: null,
|
||
|
dirty: false,
|
||
|
setFBO: null
|
||
|
}
|
||
|
|
||
|
var colorTextureFormats = ['rgba']
|
||
|
var colorRenderbufferFormats = ['rgba4', 'rgb565', 'rgb5 a1']
|
||
|
|
||
|
if (extensions.ext_srgb) {
|
||
|
colorRenderbufferFormats.push('srgba')
|
||
|
}
|
||
|
|
||
|
if (extensions.ext_color_buffer_half_float) {
|
||
|
colorRenderbufferFormats.push('rgba16f', 'rgb16f')
|
||
|
}
|
||
|
|
||
|
if (extensions.webgl_color_buffer_float) {
|
||
|
colorRenderbufferFormats.push('rgba32f')
|
||
|
}
|
||
|
|
||
|
var colorTypes = ['uint8']
|
||
|
if (extensions.oes_texture_half_float) {
|
||
|
colorTypes.push('half float', 'float16')
|
||
|
}
|
||
|
if (extensions.oes_texture_float) {
|
||
|
colorTypes.push('float', 'float32')
|
||
|
}
|
||
|
|
||
|
function FramebufferAttachment (target, texture, renderbuffer) {
|
||
|
this.target = target
|
||
|
this.texture = texture
|
||
|
this.renderbuffer = renderbuffer
|
||
|
|
||
|
var w = 0
|
||
|
var h = 0
|
||
|
if (texture) {
|
||
|
w = texture.width
|
||
|
h = texture.height
|
||
|
} else if (renderbuffer) {
|
||
|
w = renderbuffer.width
|
||
|
h = renderbuffer.height
|
||
|
}
|
||
|
this.width = w
|
||
|
this.height = h
|
||
|
}
|
||
|
|
||
|
function decRef (attachment) {
|
||
|
if (attachment) {
|
||
|
if (attachment.texture) {
|
||
|
attachment.texture._texture.decRef()
|
||
|
}
|
||
|
if (attachment.renderbuffer) {
|
||
|
attachment.renderbuffer._renderbuffer.decRef()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function incRefAndCheckShape (attachment, width, height) {
|
||
|
if (!attachment) {
|
||
|
return
|
||
|
}
|
||
|
if (attachment.texture) {
|
||
|
var texture = attachment.texture._texture
|
||
|
var tw = Math.max(1, texture.width)
|
||
|
var th = Math.max(1, texture.height)
|
||
|
check(tw === width && th === height,
|
||
|
'inconsistent width/height for supplied texture')
|
||
|
texture.refCount += 1
|
||
|
} else {
|
||
|
var renderbuffer = attachment.renderbuffer._renderbuffer
|
||
|
check(
|
||
|
renderbuffer.width === width && renderbuffer.height === height,
|
||
|
'inconsistent width/height for renderbuffer')
|
||
|
renderbuffer.refCount += 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function attach (location, attachment) {
|
||
|
if (attachment) {
|
||
|
if (attachment.texture) {
|
||
|
gl.framebufferTexture2D(
|
||
|
GL_FRAMEBUFFER,
|
||
|
location,
|
||
|
attachment.target,
|
||
|
attachment.texture._texture.texture,
|
||
|
0)
|
||
|
} else {
|
||
|
gl.framebufferRenderbuffer(
|
||
|
GL_FRAMEBUFFER,
|
||
|
location,
|
||
|
GL_RENDERBUFFER,
|
||
|
attachment.renderbuffer._renderbuffer.renderbuffer)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function parseAttachment (attachment) {
|
||
|
var target = GL_TEXTURE_2D
|
||
|
var texture = null
|
||
|
var renderbuffer = null
|
||
|
|
||
|
var data = attachment
|
||
|
if (typeof attachment === 'object') {
|
||
|
data = attachment.data
|
||
|
if ('target' in attachment) {
|
||
|
target = attachment.target | 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
check.type(data, 'function', 'invalid attachment data')
|
||
|
|
||
|
var type = data._reglType
|
||
|
if (type === 'texture2d') {
|
||
|
texture = data
|
||
|
check(target === GL_TEXTURE_2D)
|
||
|
} else if (type === 'textureCube') {
|
||
|
texture = data
|
||
|
check(
|
||
|
target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
|
||
|
target < GL_TEXTURE_CUBE_MAP_POSITIVE_X + 6,
|
||
|
'invalid cube map target')
|
||
|
} else if (type === 'renderbuffer') {
|
||
|
renderbuffer = data
|
||
|
target = GL_RENDERBUFFER
|
||
|
} else {
|
||
|
check.raise('invalid regl object for attachment')
|
||
|
}
|
||
|
|
||
|
return new FramebufferAttachment(target, texture, renderbuffer)
|
||
|
}
|
||
|
|
||
|
function allocAttachment (
|
||
|
width,
|
||
|
height,
|
||
|
isTexture,
|
||
|
format,
|
||
|
type) {
|
||
|
if (isTexture) {
|
||
|
var texture = textureState.create2D({
|
||
|
width: width,
|
||
|
height: height,
|
||
|
format: format,
|
||
|
type: type
|
||
|
})
|
||
|
texture._texture.refCount = 0
|
||
|
return new FramebufferAttachment(GL_TEXTURE_2D, texture, null)
|
||
|
} else {
|
||
|
var rb = renderbufferState.create({
|
||
|
width: width,
|
||
|
height: height,
|
||
|
format: format
|
||
|
})
|
||
|
rb._renderbuffer.refCount = 0
|
||
|
return new FramebufferAttachment(GL_RENDERBUFFER, null, rb)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function unwrapAttachment (attachment) {
|
||
|
return attachment && (attachment.texture || attachment.renderbuffer)
|
||
|
}
|
||
|
|
||
|
function resizeAttachment (attachment, w, h) {
|
||
|
if (attachment) {
|
||
|
if (attachment.texture) {
|
||
|
attachment.texture.resize(w, h)
|
||
|
} else if (attachment.renderbuffer) {
|
||
|
attachment.renderbuffer.resize(w, h)
|
||
|
}
|
||
|
attachment.width = w
|
||
|
attachment.height = h
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var framebufferCount = 0
|
||
|
var framebufferSet = {}
|
||
|
|
||
|
function REGLFramebuffer () {
|
||
|
this.id = framebufferCount++
|
||
|
framebufferSet[this.id] = this
|
||
|
|
||
|
this.framebuffer = gl.createFramebuffer()
|
||
|
this.width = 0
|
||
|
this.height = 0
|
||
|
|
||
|
this.colorAttachments = []
|
||
|
this.depthAttachment = null
|
||
|
this.stencilAttachment = null
|
||
|
this.depthStencilAttachment = null
|
||
|
}
|
||
|
|
||
|
function decFBORefs (framebuffer) {
|
||
|
framebuffer.colorAttachments.forEach(decRef)
|
||
|
decRef(framebuffer.depthAttachment)
|
||
|
decRef(framebuffer.stencilAttachment)
|
||
|
decRef(framebuffer.depthStencilAttachment)
|
||
|
}
|
||
|
|
||
|
function destroy (framebuffer) {
|
||
|
var handle = framebuffer.framebuffer
|
||
|
check(handle, 'must not double destroy framebuffer')
|
||
|
gl.deleteFramebuffer(handle)
|
||
|
framebuffer.framebuffer = null
|
||
|
stats.framebufferCount--
|
||
|
delete framebufferSet[framebuffer.id]
|
||
|
}
|
||
|
|
||
|
function updateFramebuffer (framebuffer) {
|
||
|
var i
|
||
|
|
||
|
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer.framebuffer)
|
||
|
var colorAttachments = framebuffer.colorAttachments
|
||
|
for (i = 0; i < colorAttachments.length; ++i) {
|
||
|
attach(GL_COLOR_ATTACHMENT0 + i, colorAttachments[i])
|
||
|
}
|
||
|
for (i = colorAttachments.length; i < limits.maxColorAttachments; ++i) {
|
||
|
gl.framebufferTexture2D(
|
||
|
GL_FRAMEBUFFER,
|
||
|
GL_COLOR_ATTACHMENT0 + i,
|
||
|
GL_TEXTURE_2D,
|
||
|
null,
|
||
|
0)
|
||
|
}
|
||
|
|
||
|
gl.framebufferTexture2D(
|
||
|
GL_FRAMEBUFFER,
|
||
|
GL_DEPTH_STENCIL_ATTACHMENT,
|
||
|
GL_TEXTURE_2D,
|
||
|
null,
|
||
|
0)
|
||
|
gl.framebufferTexture2D(
|
||
|
GL_FRAMEBUFFER,
|
||
|
GL_DEPTH_ATTACHMENT,
|
||
|
GL_TEXTURE_2D,
|
||
|
null,
|
||
|
0)
|
||
|
gl.framebufferTexture2D(
|
||
|
GL_FRAMEBUFFER,
|
||
|
GL_STENCIL_ATTACHMENT,
|
||
|
GL_TEXTURE_2D,
|
||
|
null,
|
||
|
0)
|
||
|
|
||
|
attach(GL_DEPTH_ATTACHMENT, framebuffer.depthAttachment)
|
||
|
attach(GL_STENCIL_ATTACHMENT, framebuffer.stencilAttachment)
|
||
|
attach(GL_DEPTH_STENCIL_ATTACHMENT, framebuffer.depthStencilAttachment)
|
||
|
|
||
|
// Check status code
|
||
|
var status = gl.checkFramebufferStatus(GL_FRAMEBUFFER)
|
||
|
if (!gl.isContextLost() && status !== GL_FRAMEBUFFER_COMPLETE) {
|
||
|
check.raise('framebuffer configuration not supported, status = ' +
|
||
|
statusCode[status])
|
||
|
}
|
||
|
|
||
|
gl.bindFramebuffer(GL_FRAMEBUFFER, framebufferState.next ? framebufferState.next.framebuffer : null)
|
||
|
framebufferState.cur = framebufferState.next
|
||
|
|
||
|
// FIXME: Clear error code here. This is a work around for a bug in
|
||
|
// headless-gl
|
||
|
gl.getError()
|
||
|
}
|
||
|
|
||
|
function createFBO (a0, a1) {
|
||
|
var framebuffer = new REGLFramebuffer()
|
||
|
stats.framebufferCount++
|
||
|
|
||
|
function reglFramebuffer (a, b) {
|
||
|
var i
|
||
|
|
||
|
check(framebufferState.next !== framebuffer,
|
||
|
'can not update framebuffer which is currently in use')
|
||
|
|
||
|
var width = 0
|
||
|
var height = 0
|
||
|
|
||
|
var needsDepth = true
|
||
|
var needsStencil = true
|
||
|
|
||
|
var colorBuffer = null
|
||
|
var colorTexture = true
|
||
|
var colorFormat = 'rgba'
|
||
|
var colorType = 'uint8'
|
||
|
var colorCount = 1
|
||
|
|
||
|
var depthBuffer = null
|
||
|
var stencilBuffer = null
|
||
|
var depthStencilBuffer = null
|
||
|
var depthStencilTexture = false
|
||
|
|
||
|
if (typeof a === 'number') {
|
||
|
width = a | 0
|
||
|
height = (b | 0) || width
|
||
|
} else if (!a) {
|
||
|
width = height = 1
|
||
|
} else {
|
||
|
check.type(a, 'object', 'invalid arguments for framebuffer')
|
||
|
var options = a
|
||
|
|
||
|
if ('shape' in options) {
|
||
|
var shape = options.shape
|
||
|
check(Array.isArray(shape) && shape.length >= 2,
|
||
|
'invalid shape for framebuffer')
|
||
|
width = shape[0]
|
||
|
height = shape[1]
|
||
|
} else {
|
||
|
if ('radius' in options) {
|
||
|
width = height = options.radius
|
||
|
}
|
||
|
if ('width' in options) {
|
||
|
width = options.width
|
||
|
}
|
||
|
if ('height' in options) {
|
||
|
height = options.height
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('color' in options ||
|
||
|
'colors' in options) {
|
||
|
colorBuffer =
|
||
|
options.color ||
|
||
|
options.colors
|
||
|
if (Array.isArray(colorBuffer)) {
|
||
|
check(
|
||
|
colorBuffer.length === 1 || extensions.webgl_draw_buffers,
|
||
|
'multiple render targets not supported')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!colorBuffer) {
|
||
|
if ('colorCount' in options) {
|
||
|
colorCount = options.colorCount | 0
|
||
|
check(colorCount > 0, 'invalid color buffer count')
|
||
|
}
|
||
|
|
||
|
if ('colorTexture' in options) {
|
||
|
colorTexture = !!options.colorTexture
|
||
|
colorFormat = 'rgba4'
|
||
|
}
|
||
|
|
||
|
if ('colorType' in options) {
|
||
|
colorType = options.colorType
|
||
|
if (!colorTexture) {
|
||
|
if (colorType === 'half float' || colorType === 'float16') {
|
||
|
check(extensions.ext_color_buffer_half_float,
|
||
|
'you must enable EXT_color_buffer_half_float to use 16-bit render buffers')
|
||
|
colorFormat = 'rgba16f'
|
||
|
} else if (colorType === 'float' || colorType === 'float32') {
|
||
|
check(extensions.webgl_color_buffer_float,
|
||
|
'you must enable WEBGL_color_buffer_float in order to use 32-bit floating point renderbuffers')
|
||
|
colorFormat = 'rgba32f'
|
||
|
}
|
||
|
} else {
|
||
|
check(extensions.oes_texture_float ||
|
||
|
!(colorType === 'float' || colorType === 'float32'),
|
||
|
'you must enable OES_texture_float in order to use floating point framebuffer objects')
|
||
|
check(extensions.oes_texture_half_float ||
|
||
|
!(colorType === 'half float' || colorType === 'float16'),
|
||
|
'you must enable OES_texture_half_float in order to use 16-bit floating point framebuffer objects')
|
||
|
}
|
||
|
check.oneOf(colorType, colorTypes, 'invalid color type')
|
||
|
}
|
||
|
|
||
|
if ('colorFormat' in options) {
|
||
|
colorFormat = options.colorFormat
|
||
|
if (colorTextureFormats.indexOf(colorFormat) >= 0) {
|
||
|
colorTexture = true
|
||
|
} else if (colorRenderbufferFormats.indexOf(colorFormat) >= 0) {
|
||
|
colorTexture = false
|
||
|
} else {
|
||
|
check.optional(function () {
|
||
|
if (colorTexture) {
|
||
|
check.oneOf(
|
||
|
options.colorFormat, colorTextureFormats,
|
||
|
'invalid color format for texture')
|
||
|
} else {
|
||
|
check.oneOf(
|
||
|
options.colorFormat, colorRenderbufferFormats,
|
||
|
'invalid color format for renderbuffer')
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('depthTexture' in options || 'depthStencilTexture' in options) {
|
||
|
depthStencilTexture = !!(options.depthTexture ||
|
||
|
options.depthStencilTexture)
|
||
|
check(!depthStencilTexture || extensions.webgl_depth_texture,
|
||
|
'webgl_depth_texture extension not supported')
|
||
|
}
|
||
|
|
||
|
if ('depth' in options) {
|
||
|
if (typeof options.depth === 'boolean') {
|
||
|
needsDepth = options.depth
|
||
|
} else {
|
||
|
depthBuffer = options.depth
|
||
|
needsStencil = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('stencil' in options) {
|
||
|
if (typeof options.stencil === 'boolean') {
|
||
|
needsStencil = options.stencil
|
||
|
} else {
|
||
|
stencilBuffer = options.stencil
|
||
|
needsDepth = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('depthStencil' in options) {
|
||
|
if (typeof options.depthStencil === 'boolean') {
|
||
|
needsDepth = needsStencil = options.depthStencil
|
||
|
} else {
|
||
|
depthStencilBuffer = options.depthStencil
|
||
|
needsDepth = false
|
||
|
needsStencil = false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// parse attachments
|
||
|
var colorAttachments = null
|
||
|
var depthAttachment = null
|
||
|
var stencilAttachment = null
|
||
|
var depthStencilAttachment = null
|
||
|
|
||
|
// Set up color attachments
|
||
|
if (Array.isArray(colorBuffer)) {
|
||
|
colorAttachments = colorBuffer.map(parseAttachment)
|
||
|
} else if (colorBuffer) {
|
||
|
colorAttachments = [parseAttachment(colorBuffer)]
|
||
|
} else {
|
||
|
colorAttachments = new Array(colorCount)
|
||
|
for (i = 0; i < colorCount; ++i) {
|
||
|
colorAttachments[i] = allocAttachment(
|
||
|
width,
|
||
|
height,
|
||
|
colorTexture,
|
||
|
colorFormat,
|
||
|
colorType)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
check(extensions.webgl_draw_buffers || colorAttachments.length <= 1,
|
||
|
'you must enable the WEBGL_draw_buffers extension in order to use multiple color buffers.')
|
||
|
check(colorAttachments.length <= limits.maxColorAttachments,
|
||
|
'too many color attachments, not supported')
|
||
|
|
||
|
width = width || colorAttachments[0].width
|
||
|
height = height || colorAttachments[0].height
|
||
|
|
||
|
if (depthBuffer) {
|
||
|
depthAttachment = parseAttachment(depthBuffer)
|
||
|
} else if (needsDepth && !needsStencil) {
|
||
|
depthAttachment = allocAttachment(
|
||
|
width,
|
||
|
height,
|
||
|
depthStencilTexture,
|
||
|
'depth',
|
||
|
'uint32')
|
||
|
}
|
||
|
|
||
|
if (stencilBuffer) {
|
||
|
stencilAttachment = parseAttachment(stencilBuffer)
|
||
|
} else if (needsStencil && !needsDepth) {
|
||
|
stencilAttachment = allocAttachment(
|
||
|
width,
|
||
|
height,
|
||
|
false,
|
||
|
'stencil',
|
||
|
'uint8')
|
||
|
}
|
||
|
|
||
|
if (depthStencilBuffer) {
|
||
|
depthStencilAttachment = parseAttachment(depthStencilBuffer)
|
||
|
} else if (!depthBuffer && !stencilBuffer && needsStencil && needsDepth) {
|
||
|
depthStencilAttachment = allocAttachment(
|
||
|
width,
|
||
|
height,
|
||
|
depthStencilTexture,
|
||
|
'depth stencil',
|
||
|
'depth stencil')
|
||
|
}
|
||
|
|
||
|
check(
|
||
|
(!!depthBuffer) + (!!stencilBuffer) + (!!depthStencilBuffer) <= 1,
|
||
|
'invalid framebuffer configuration, can specify exactly one depth/stencil attachment')
|
||
|
|
||
|
var commonColorAttachmentSize = null
|
||
|
|
||
|
for (i = 0; i < colorAttachments.length; ++i) {
|
||
|
incRefAndCheckShape(colorAttachments[i], width, height)
|
||
|
check(!colorAttachments[i] ||
|
||
|
(colorAttachments[i].texture &&
|
||
|
colorTextureFormatEnums.indexOf(colorAttachments[i].texture._texture.format) >= 0) ||
|
||
|
(colorAttachments[i].renderbuffer &&
|
||
|
colorRenderbufferFormatEnums.indexOf(colorAttachments[i].renderbuffer._renderbuffer.format) >= 0),
|
||
|
'framebuffer color attachment ' + i + ' is invalid')
|
||
|
|
||
|
if (colorAttachments[i] && colorAttachments[i].texture) {
|
||
|
var colorAttachmentSize =
|
||
|
textureFormatChannels[colorAttachments[i].texture._texture.format] *
|
||
|
textureTypeSizes[colorAttachments[i].texture._texture.type]
|
||
|
|
||
|
if (commonColorAttachmentSize === null) {
|
||
|
commonColorAttachmentSize = colorAttachmentSize
|
||
|
} else {
|
||
|
// We need to make sure that all color attachments have the same number of bitplanes
|
||
|
// (that is, the same numer of bits per pixel)
|
||
|
// This is required by the GLES2.0 standard. See the beginning of Chapter 4 in that document.
|
||
|
check(commonColorAttachmentSize === colorAttachmentSize,
|
||
|
'all color attachments much have the same number of bits per pixel.')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
incRefAndCheckShape(depthAttachment, width, height)
|
||
|
check(!depthAttachment ||
|
||
|
(depthAttachment.texture &&
|
||
|
depthAttachment.texture._texture.format === GL_DEPTH_COMPONENT) ||
|
||
|
(depthAttachment.renderbuffer &&
|
||
|
depthAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_COMPONENT16),
|
||
|
'invalid depth attachment for framebuffer object')
|
||
|
incRefAndCheckShape(stencilAttachment, width, height)
|
||
|
check(!stencilAttachment ||
|
||
|
(stencilAttachment.renderbuffer &&
|
||
|
stencilAttachment.renderbuffer._renderbuffer.format === GL_STENCIL_INDEX8),
|
||
|
'invalid stencil attachment for framebuffer object')
|
||
|
incRefAndCheckShape(depthStencilAttachment, width, height)
|
||
|
check(!depthStencilAttachment ||
|
||
|
(depthStencilAttachment.texture &&
|
||
|
depthStencilAttachment.texture._texture.format === GL_DEPTH_STENCIL) ||
|
||
|
(depthStencilAttachment.renderbuffer &&
|
||
|
depthStencilAttachment.renderbuffer._renderbuffer.format === GL_DEPTH_STENCIL),
|
||
|
'invalid depth-stencil attachment for framebuffer object')
|
||
|
|
||
|
// decrement references
|
||
|
decFBORefs(framebuffer)
|
||
|
|
||
|
framebuffer.width = width
|
||
|
framebuffer.height = height
|
||
|
|
||
|
framebuffer.colorAttachments = colorAttachments
|
||
|
framebuffer.depthAttachment = depthAttachment
|
||
|
framebuffer.stencilAttachment = stencilAttachment
|
||
|
framebuffer.depthStencilAttachment = depthStencilAttachment
|
||
|
|
||
|
reglFramebuffer.color = colorAttachments.map(unwrapAttachment)
|
||
|
reglFramebuffer.depth = unwrapAttachment(depthAttachment)
|
||
|
reglFramebuffer.stencil = unwrapAttachment(stencilAttachment)
|
||
|
reglFramebuffer.depthStencil = unwrapAttachment(depthStencilAttachment)
|
||
|
|
||
|
reglFramebuffer.width = framebuffer.width
|
||
|
reglFramebuffer.height = framebuffer.height
|
||
|
|
||
|
updateFramebuffer(framebuffer)
|
||
|
|
||
|
return reglFramebuffer
|
||
|
}
|
||
|
|
||
|
function resize (w_, h_) {
|
||
|
check(framebufferState.next !== framebuffer,
|
||
|
'can not resize a framebuffer which is currently in use')
|
||
|
|
||
|
var w = Math.max(w_ | 0, 1)
|
||
|
var h = Math.max((h_ | 0) || w, 1)
|
||
|
if (w === framebuffer.width && h === framebuffer.height) {
|
||
|
return reglFramebuffer
|
||
|
}
|
||
|
|
||
|
// resize all buffers
|
||
|
var colorAttachments = framebuffer.colorAttachments
|
||
|
for (var i = 0; i < colorAttachments.length; ++i) {
|
||
|
resizeAttachment(colorAttachments[i], w, h)
|
||
|
}
|
||
|
resizeAttachment(framebuffer.depthAttachment, w, h)
|
||
|
resizeAttachment(framebuffer.stencilAttachment, w, h)
|
||
|
resizeAttachment(framebuffer.depthStencilAttachment, w, h)
|
||
|
|
||
|
framebuffer.width = reglFramebuffer.width = w
|
||
|
framebuffer.height = reglFramebuffer.height = h
|
||
|
|
||
|
updateFramebuffer(framebuffer)
|
||
|
|
||
|
return reglFramebuffer
|
||
|
}
|
||
|
|
||
|
reglFramebuffer(a0, a1)
|
||
|
|
||
|
return extend(reglFramebuffer, {
|
||
|
resize: resize,
|
||
|
_reglType: 'framebuffer',
|
||
|
_framebuffer: framebuffer,
|
||
|
destroy: function () {
|
||
|
destroy(framebuffer)
|
||
|
decFBORefs(framebuffer)
|
||
|
},
|
||
|
use: function (block) {
|
||
|
framebufferState.setFBO({
|
||
|
framebuffer: reglFramebuffer
|
||
|
}, block)
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function createCubeFBO (options) {
|
||
|
var faces = Array(6)
|
||
|
|
||
|
function reglFramebufferCube (a) {
|
||
|
var i
|
||
|
|
||
|
check(faces.indexOf(framebufferState.next) < 0,
|
||
|
'can not update framebuffer which is currently in use')
|
||
|
|
||
|
var params = {
|
||
|
color: null
|
||
|
}
|
||
|
|
||
|
var radius = 0
|
||
|
|
||
|
var colorBuffer = null
|
||
|
var colorFormat = 'rgba'
|
||
|
var colorType = 'uint8'
|
||
|
var colorCount = 1
|
||
|
|
||
|
if (typeof a === 'number') {
|
||
|
radius = a | 0
|
||
|
} else if (!a) {
|
||
|
radius = 1
|
||
|
} else {
|
||
|
check.type(a, 'object', 'invalid arguments for framebuffer')
|
||
|
var options = a
|
||
|
|
||
|
if ('shape' in options) {
|
||
|
var shape = options.shape
|
||
|
check(
|
||
|
Array.isArray(shape) && shape.length >= 2,
|
||
|
'invalid shape for framebuffer')
|
||
|
check(
|
||
|
shape[0] === shape[1],
|
||
|
'cube framebuffer must be square')
|
||
|
radius = shape[0]
|
||
|
} else {
|
||
|
if ('radius' in options) {
|
||
|
radius = options.radius | 0
|
||
|
}
|
||
|
if ('width' in options) {
|
||
|
radius = options.width | 0
|
||
|
if ('height' in options) {
|
||
|
check(options.height === radius, 'must be square')
|
||
|
}
|
||
|
} else if ('height' in options) {
|
||
|
radius = options.height | 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('color' in options ||
|
||
|
'colors' in options) {
|
||
|
colorBuffer =
|
||
|
options.color ||
|
||
|
options.colors
|
||
|
if (Array.isArray(colorBuffer)) {
|
||
|
check(
|
||
|
colorBuffer.length === 1 || extensions.webgl_draw_buffers,
|
||
|
'multiple render targets not supported')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!colorBuffer) {
|
||
|
if ('colorCount' in options) {
|
||
|
colorCount = options.colorCount | 0
|
||
|
check(colorCount > 0, 'invalid color buffer count')
|
||
|
}
|
||
|
|
||
|
if ('colorType' in options) {
|
||
|
check.oneOf(
|
||
|
options.colorType, colorTypes,
|
||
|
'invalid color type')
|
||
|
colorType = options.colorType
|
||
|
}
|
||
|
|
||
|
if ('colorFormat' in options) {
|
||
|
colorFormat = options.colorFormat
|
||
|
check.oneOf(
|
||
|
options.colorFormat, colorTextureFormats,
|
||
|
'invalid color format for texture')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ('depth' in options) {
|
||
|
params.depth = options.depth
|
||
|
}
|
||
|
|
||
|
if ('stencil' in options) {
|
||
|
params.stencil = options.stencil
|
||
|
}
|
||
|
|
||
|
if ('depthStencil' in options) {
|
||
|
params.depthStencil = options.depthStencil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var colorCubes
|
||
|
if (colorBuffer) {
|
||
|
if (Array.isArray(colorBuffer)) {
|
||
|
colorCubes = []
|
||
|
for (i = 0; i < colorBuffer.length; ++i) {
|
||
|
colorCubes[i] = colorBuffer[i]
|
||
|
}
|
||
|
} else {
|
||
|
colorCubes = [ colorBuffer ]
|
||
|
}
|
||
|
} else {
|
||
|
colorCubes = Array(colorCount)
|
||
|
var cubeMapParams = {
|
||
|
radius: radius,
|
||
|
format: colorFormat,
|
||
|
type: colorType
|
||
|
}
|
||
|
for (i = 0; i < colorCount; ++i) {
|
||
|
colorCubes[i] = textureState.createCube(cubeMapParams)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check color cubes
|
||
|
params.color = Array(colorCubes.length)
|
||
|
for (i = 0; i < colorCubes.length; ++i) {
|
||
|
var cube = colorCubes[i]
|
||
|
check(
|
||
|
typeof cube === 'function' && cube._reglType === 'textureCube',
|
||
|
'invalid cube map')
|
||
|
radius = radius || cube.width
|
||
|
check(
|
||
|
cube.width === radius && cube.height === radius,
|
||
|
'invalid cube map shape')
|
||
|
params.color[i] = {
|
||
|
target: GL_TEXTURE_CUBE_MAP_POSITIVE_X,
|
||
|
data: colorCubes[i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 6; ++i) {
|
||
|
for (var j = 0; j < colorCubes.length; ++j) {
|
||
|
params.color[j].target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i
|
||
|
}
|
||
|
// reuse depth-stencil attachments across all cube maps
|
||
|
if (i > 0) {
|
||
|
params.depth = faces[0].depth
|
||
|
params.stencil = faces[0].stencil
|
||
|
params.depthStencil = faces[0].depthStencil
|
||
|
}
|
||
|
if (faces[i]) {
|
||
|
(faces[i])(params)
|
||
|
} else {
|
||
|
faces[i] = createFBO(params)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return extend(reglFramebufferCube, {
|
||
|
width: radius,
|
||
|
height: radius,
|
||
|
color: colorCubes
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function resize (radius_) {
|
||
|
var i
|
||
|
var radius = radius_ | 0
|
||
|
check(radius > 0 && radius <= limits.maxCubeMapSize,
|
||
|
'invalid radius for cube fbo')
|
||
|
|
||
|
if (radius === reglFramebufferCube.width) {
|
||
|
return reglFramebufferCube
|
||
|
}
|
||
|
|
||
|
var colors = reglFramebufferCube.color
|
||
|
for (i = 0; i < colors.length; ++i) {
|
||
|
colors[i].resize(radius)
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 6; ++i) {
|
||
|
faces[i].resize(radius)
|
||
|
}
|
||
|
|
||
|
reglFramebufferCube.width = reglFramebufferCube.height = radius
|
||
|
|
||
|
return reglFramebufferCube
|
||
|
}
|
||
|
|
||
|
reglFramebufferCube(options)
|
||
|
|
||
|
return extend(reglFramebufferCube, {
|
||
|
faces: faces,
|
||
|
resize: resize,
|
||
|
_reglType: 'framebufferCube',
|
||
|
destroy: function () {
|
||
|
faces.forEach(function (f) {
|
||
|
f.destroy()
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function restoreFramebuffers () {
|
||
|
framebufferState.cur = null
|
||
|
framebufferState.next = null
|
||
|
framebufferState.dirty = true
|
||
|
values(framebufferSet).forEach(function (fb) {
|
||
|
fb.framebuffer = gl.createFramebuffer()
|
||
|
updateFramebuffer(fb)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return extend(framebufferState, {
|
||
|
getFramebuffer: function (object) {
|
||
|
if (typeof object === 'function' && object._reglType === 'framebuffer') {
|
||
|
var fbo = object._framebuffer
|
||
|
if (fbo instanceof REGLFramebuffer) {
|
||
|
return fbo
|
||
|
}
|
||
|
}
|
||
|
return null
|
||
|
},
|
||
|
create: createFBO,
|
||
|
createCube: createCubeFBO,
|
||
|
clear: function () {
|
||
|
values(framebufferSet).forEach(destroy)
|
||
|
},
|
||
|
restore: restoreFramebuffers
|
||
|
})
|
||
|
}
|