securityos/node_modules/regl/lib/attribute.js

419 lines
12 KiB
JavaScript
Raw Normal View History

2024-09-06 15:32:35 +00:00
var check = require('./util/check')
var values = require('./util/values')
var bufferTypes = require('./constants/dtypes.json')
var isTypedArray = require('./util/is-typed-array')
var isNDArrayLike = require('./util/is-ndarray')
var primitives = require('./constants/primitives.json')
var GL_FLOAT = 5126
var GL_ARRAY_BUFFER = 34962
var GL_ELEMENT_ARRAY_BUFFER = 34963
var VAO_OPTIONS = [
'attributes',
'elements',
'offset',
'count',
'primitive',
'instances'
]
function AttributeRecord () {
this.state = 0
this.x = 0.0
this.y = 0.0
this.z = 0.0
this.w = 0.0
this.buffer = null
this.size = 0
this.normalized = false
this.type = GL_FLOAT
this.offset = 0
this.stride = 0
this.divisor = 0
}
module.exports = function wrapAttributeState (
gl,
extensions,
limits,
stats,
bufferState,
elementState,
drawState) {
var NUM_ATTRIBUTES = limits.maxAttributes
var attributeBindings = new Array(NUM_ATTRIBUTES)
for (var i = 0; i < NUM_ATTRIBUTES; ++i) {
attributeBindings[i] = new AttributeRecord()
}
var vaoCount = 0
var vaoSet = {}
var state = {
Record: AttributeRecord,
scope: {},
state: attributeBindings,
currentVAO: null,
targetVAO: null,
restore: extVAO() ? restoreVAO : function () {},
createVAO: createVAO,
getVAO: getVAO,
destroyBuffer: destroyBuffer,
setVAO: extVAO() ? setVAOEXT : setVAOEmulated,
clear: extVAO() ? destroyVAOEXT : function () {}
}
function destroyBuffer (buffer) {
for (var i = 0; i < attributeBindings.length; ++i) {
var record = attributeBindings[i]
if (record.buffer === buffer) {
gl.disableVertexAttribArray(i)
record.buffer = null
}
}
}
function extVAO () {
return extensions.oes_vertex_array_object
}
function extInstanced () {
return extensions.angle_instanced_arrays
}
function getVAO (vao) {
if (typeof vao === 'function' && vao._vao) {
return vao._vao
}
return null
}
function setVAOEXT (vao) {
if (vao === state.currentVAO) {
return
}
var ext = extVAO()
if (vao) {
ext.bindVertexArrayOES(vao.vao)
} else {
ext.bindVertexArrayOES(null)
}
state.currentVAO = vao
}
function setVAOEmulated (vao) {
if (vao === state.currentVAO) {
return
}
if (vao) {
vao.bindAttrs()
} else {
var exti = extInstanced()
for (var i = 0; i < attributeBindings.length; ++i) {
var binding = attributeBindings[i]
if (binding.buffer) {
gl.enableVertexAttribArray(i)
binding.buffer.bind()
gl.vertexAttribPointer(i, binding.size, binding.type, binding.normalized, binding.stride, binding.offfset)
if (exti && binding.divisor) {
exti.vertexAttribDivisorANGLE(i, binding.divisor)
}
} else {
gl.disableVertexAttribArray(i)
gl.vertexAttrib4f(i, binding.x, binding.y, binding.z, binding.w)
}
}
if (drawState.elements) {
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawState.elements.buffer.buffer)
} else {
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, null)
}
}
state.currentVAO = vao
}
function destroyVAOEXT () {
values(vaoSet).forEach(function (vao) {
vao.destroy()
})
}
function REGLVAO () {
this.id = ++vaoCount
this.attributes = []
this.elements = null
this.ownsElements = false
this.count = 0
this.offset = 0
this.instances = -1
this.primitive = 4
var extension = extVAO()
if (extension) {
this.vao = extension.createVertexArrayOES()
} else {
this.vao = null
}
vaoSet[this.id] = this
this.buffers = []
}
REGLVAO.prototype.bindAttrs = function () {
var exti = extInstanced()
var attributes = this.attributes
for (var i = 0; i < attributes.length; ++i) {
var attr = attributes[i]
if (attr.buffer) {
gl.enableVertexAttribArray(i)
gl.bindBuffer(GL_ARRAY_BUFFER, attr.buffer.buffer)
gl.vertexAttribPointer(i, attr.size, attr.type, attr.normalized, attr.stride, attr.offset)
if (exti && attr.divisor) {
exti.vertexAttribDivisorANGLE(i, attr.divisor)
}
} else {
gl.disableVertexAttribArray(i)
gl.vertexAttrib4f(i, attr.x, attr.y, attr.z, attr.w)
}
}
for (var j = attributes.length; j < NUM_ATTRIBUTES; ++j) {
gl.disableVertexAttribArray(j)
}
var elements = elementState.getElements(this.elements)
if (elements) {
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, elements.buffer.buffer)
} else {
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, null)
}
}
REGLVAO.prototype.refresh = function () {
var ext = extVAO()
if (ext) {
ext.bindVertexArrayOES(this.vao)
this.bindAttrs()
state.currentVAO = null
ext.bindVertexArrayOES(null)
}
}
REGLVAO.prototype.destroy = function () {
if (this.vao) {
var extension = extVAO()
if (this === state.currentVAO) {
state.currentVAO = null
extension.bindVertexArrayOES(null)
}
extension.deleteVertexArrayOES(this.vao)
this.vao = null
}
if (this.ownsElements) {
this.elements.destroy()
this.elements = null
this.ownsElements = false
}
if (vaoSet[this.id]) {
delete vaoSet[this.id]
stats.vaoCount -= 1
}
}
function restoreVAO () {
var ext = extVAO()
if (ext) {
values(vaoSet).forEach(function (vao) {
vao.refresh()
})
}
}
function createVAO (_attr) {
var vao = new REGLVAO()
stats.vaoCount += 1
function updateVAO (options) {
var attributes
if (Array.isArray(options)) {
attributes = options
if (vao.elements && vao.ownsElements) {
vao.elements.destroy()
}
vao.elements = null
vao.ownsElements = false
vao.offset = 0
vao.count = 0
vao.instances = -1
vao.primitive = 4
} else {
check(typeof options === 'object', 'invalid arguments for create vao')
check('attributes' in options, 'must specify attributes for vao')
if (options.elements) {
var elements = options.elements
if (vao.ownsElements) {
if (typeof elements === 'function' && elements._reglType === 'elements') {
vao.elements.destroy()
vao.ownsElements = false
} else {
vao.elements(elements)
vao.ownsElements = false
}
} else if (elementState.getElements(options.elements)) {
vao.elements = options.elements
vao.ownsElements = false
} else {
vao.elements = elementState.create(options.elements)
vao.ownsElements = true
}
} else {
vao.elements = null
vao.ownsElements = false
}
attributes = options.attributes
// set default vao
vao.offset = 0
vao.count = -1
vao.instances = -1
vao.primitive = 4
// copy element properties
if (vao.elements) {
vao.count = vao.elements._elements.vertCount
vao.primitive = vao.elements._elements.primType
}
if ('offset' in options) {
vao.offset = options.offset | 0
}
if ('count' in options) {
vao.count = options.count | 0
}
if ('instances' in options) {
vao.instances = options.instances | 0
}
if ('primitive' in options) {
check(options.primitive in primitives, 'bad primitive type: ' + options.primitive)
vao.primitive = primitives[options.primitive]
}
check.optional(() => {
var keys = Object.keys(options)
for (var i = 0; i < keys.length; ++i) {
check(VAO_OPTIONS.indexOf(keys[i]) >= 0, 'invalid option for vao: "' + keys[i] + '" valid options are ' + VAO_OPTIONS)
}
})
check(Array.isArray(attributes), 'attributes must be an array')
}
check(attributes.length < NUM_ATTRIBUTES, 'too many attributes')
check(attributes.length > 0, 'must specify at least one attribute')
var bufUpdated = {}
var nattributes = vao.attributes
nattributes.length = attributes.length
for (var i = 0; i < attributes.length; ++i) {
var spec = attributes[i]
var rec = nattributes[i] = new AttributeRecord()
var data = spec.data || spec
if (Array.isArray(data) || isTypedArray(data) || isNDArrayLike(data)) {
var buf
if (vao.buffers[i]) {
buf = vao.buffers[i]
if (isTypedArray(data) && buf._buffer.byteLength >= data.byteLength) {
buf.subdata(data)
} else {
buf.destroy()
vao.buffers[i] = null
}
}
if (!vao.buffers[i]) {
buf = vao.buffers[i] = bufferState.create(spec, GL_ARRAY_BUFFER, false, true)
}
rec.buffer = bufferState.getBuffer(buf)
rec.size = rec.buffer.dimension | 0
rec.normalized = false
rec.type = rec.buffer.dtype
rec.offset = 0
rec.stride = 0
rec.divisor = 0
rec.state = 1
bufUpdated[i] = 1
} else if (bufferState.getBuffer(spec)) {
rec.buffer = bufferState.getBuffer(spec)
rec.size = rec.buffer.dimension | 0
rec.normalized = false
rec.type = rec.buffer.dtype
rec.offset = 0
rec.stride = 0
rec.divisor = 0
rec.state = 1
} else if (bufferState.getBuffer(spec.buffer)) {
rec.buffer = bufferState.getBuffer(spec.buffer)
rec.size = ((+spec.size) || rec.buffer.dimension) | 0
rec.normalized = !!spec.normalized || false
if ('type' in spec) {
check.parameter(spec.type, bufferTypes, 'invalid buffer type')
rec.type = bufferTypes[spec.type]
} else {
rec.type = rec.buffer.dtype
}
rec.offset = (spec.offset || 0) | 0
rec.stride = (spec.stride || 0) | 0
rec.divisor = (spec.divisor || 0) | 0
rec.state = 1
check(rec.size >= 1 && rec.size <= 4, 'size must be between 1 and 4')
check(rec.offset >= 0, 'invalid offset')
check(rec.stride >= 0 && rec.stride <= 255, 'stride must be between 0 and 255')
check(rec.divisor >= 0, 'divisor must be positive')
check(!rec.divisor || !!extensions.angle_instanced_arrays, 'ANGLE_instanced_arrays must be enabled to use divisor')
} else if ('x' in spec) {
check(i > 0, 'first attribute must not be a constant')
rec.x = +spec.x || 0
rec.y = +spec.y || 0
rec.z = +spec.z || 0
rec.w = +spec.w || 0
rec.state = 2
} else {
check(false, 'invalid attribute spec for location ' + i)
}
}
// retire unused buffers
for (var j = 0; j < vao.buffers.length; ++j) {
if (!bufUpdated[j] && vao.buffers[j]) {
vao.buffers[j].destroy()
vao.buffers[j] = null
}
}
vao.refresh()
return updateVAO
}
updateVAO.destroy = function () {
for (var j = 0; j < vao.buffers.length; ++j) {
if (vao.buffers[j]) {
vao.buffers[j].destroy()
}
}
vao.buffers.length = 0
if (vao.ownsElements) {
vao.elements.destroy()
vao.elements = null
vao.ownsElements = false
}
vao.destroy()
}
updateVAO._vao = vao
updateVAO._reglType = 'vao'
return updateVAO(_attr)
}
return state
}