300 lines
11 KiB
JavaScript
300 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
var Extensions = require('../../../../extensions/Extensions.js');
|
|
var getAttributeInfoFromFormat = require('../../shared/geometry/utils/getAttributeInfoFromFormat.js');
|
|
var ensureAttributes = require('../shader/program/ensureAttributes.js');
|
|
var getGlTypeFromFormat = require('./utils/getGlTypeFromFormat.js');
|
|
|
|
"use strict";
|
|
const topologyToGlMap = {
|
|
"point-list": 0,
|
|
"line-list": 1,
|
|
"line-strip": 3,
|
|
"triangle-list": 4,
|
|
"triangle-strip": 5
|
|
};
|
|
class GlGeometrySystem {
|
|
/** @param renderer - The renderer this System works for. */
|
|
constructor(renderer) {
|
|
this._geometryVaoHash = /* @__PURE__ */ Object.create(null);
|
|
this._renderer = renderer;
|
|
this._activeGeometry = null;
|
|
this._activeVao = null;
|
|
this.hasVao = true;
|
|
this.hasInstance = true;
|
|
}
|
|
/** Sets up the renderer context and necessary buffers. */
|
|
contextChange() {
|
|
const gl = this.gl = this._renderer.gl;
|
|
if (!this._renderer.context.supports.vertexArrayObject) {
|
|
throw new Error("[PixiJS] Vertex Array Objects are not supported on this device");
|
|
}
|
|
const nativeVaoExtension = this._renderer.context.extensions.vertexArrayObject;
|
|
if (nativeVaoExtension) {
|
|
gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES();
|
|
gl.bindVertexArray = (vao) => nativeVaoExtension.bindVertexArrayOES(vao);
|
|
gl.deleteVertexArray = (vao) => nativeVaoExtension.deleteVertexArrayOES(vao);
|
|
}
|
|
const nativeInstancedExtension = this._renderer.context.extensions.vertexAttribDivisorANGLE;
|
|
if (nativeInstancedExtension) {
|
|
gl.drawArraysInstanced = (a, b, c, d) => {
|
|
nativeInstancedExtension.drawArraysInstancedANGLE(a, b, c, d);
|
|
};
|
|
gl.drawElementsInstanced = (a, b, c, d, e) => {
|
|
nativeInstancedExtension.drawElementsInstancedANGLE(a, b, c, d, e);
|
|
};
|
|
gl.vertexAttribDivisor = (a, b) => nativeInstancedExtension.vertexAttribDivisorANGLE(a, b);
|
|
}
|
|
this._activeGeometry = null;
|
|
this._activeVao = null;
|
|
this._geometryVaoHash = /* @__PURE__ */ Object.create(null);
|
|
}
|
|
/**
|
|
* Binds geometry so that is can be drawn. Creating a Vao if required
|
|
* @param geometry - Instance of geometry to bind.
|
|
* @param program - Instance of program to use vao for.
|
|
*/
|
|
bind(geometry, program) {
|
|
const gl = this.gl;
|
|
this._activeGeometry = geometry;
|
|
const vao = this.getVao(geometry, program);
|
|
if (this._activeVao !== vao) {
|
|
this._activeVao = vao;
|
|
gl.bindVertexArray(vao);
|
|
}
|
|
this.updateBuffers();
|
|
}
|
|
/** Reset and unbind any active VAO and geometry. */
|
|
reset() {
|
|
this.unbind();
|
|
}
|
|
/** Update buffers of the currently bound geometry. */
|
|
updateBuffers() {
|
|
const geometry = this._activeGeometry;
|
|
const bufferSystem = this._renderer.buffer;
|
|
for (let i = 0; i < geometry.buffers.length; i++) {
|
|
const buffer = geometry.buffers[i];
|
|
bufferSystem.updateBuffer(buffer);
|
|
}
|
|
}
|
|
/**
|
|
* Check compatibility between a geometry and a program
|
|
* @param geometry - Geometry instance.
|
|
* @param program - Program instance.
|
|
*/
|
|
checkCompatibility(geometry, program) {
|
|
const geometryAttributes = geometry.attributes;
|
|
const shaderAttributes = program._attributeData;
|
|
for (const j in shaderAttributes) {
|
|
if (!geometryAttributes[j]) {
|
|
throw new Error(`shader and geometry incompatible, geometry missing the "${j}" attribute`);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Takes a geometry and program and generates a unique signature for them.
|
|
* @param geometry - To get signature from.
|
|
* @param program - To test geometry against.
|
|
* @returns - Unique signature of the geometry and program
|
|
*/
|
|
getSignature(geometry, program) {
|
|
const attribs = geometry.attributes;
|
|
const shaderAttributes = program._attributeData;
|
|
const strings = ["g", geometry.uid];
|
|
for (const i in attribs) {
|
|
if (shaderAttributes[i]) {
|
|
strings.push(i, shaderAttributes[i].location);
|
|
}
|
|
}
|
|
return strings.join("-");
|
|
}
|
|
getVao(geometry, program) {
|
|
return this._geometryVaoHash[geometry.uid]?.[program._key] || this.initGeometryVao(geometry, program);
|
|
}
|
|
/**
|
|
* Creates or gets Vao with the same structure as the geometry and stores it on the geometry.
|
|
* If vao is created, it is bound automatically. We use a shader to infer what and how to set up the
|
|
* attribute locations.
|
|
* @param geometry - Instance of geometry to to generate Vao for.
|
|
* @param program
|
|
* @param _incRefCount - Increment refCount of all geometry buffers.
|
|
*/
|
|
initGeometryVao(geometry, program, _incRefCount = true) {
|
|
const gl = this._renderer.gl;
|
|
const bufferSystem = this._renderer.buffer;
|
|
this._renderer.shader._getProgramData(program);
|
|
this.checkCompatibility(geometry, program);
|
|
const signature = this.getSignature(geometry, program);
|
|
if (!this._geometryVaoHash[geometry.uid]) {
|
|
this._geometryVaoHash[geometry.uid] = /* @__PURE__ */ Object.create(null);
|
|
geometry.on("destroy", this.onGeometryDestroy, this);
|
|
}
|
|
const vaoObjectHash = this._geometryVaoHash[geometry.uid];
|
|
let vao = vaoObjectHash[signature];
|
|
if (vao) {
|
|
vaoObjectHash[program._key] = vao;
|
|
return vao;
|
|
}
|
|
ensureAttributes.ensureAttributes(geometry, program._attributeData);
|
|
const buffers = geometry.buffers;
|
|
vao = gl.createVertexArray();
|
|
gl.bindVertexArray(vao);
|
|
for (let i = 0; i < buffers.length; i++) {
|
|
const buffer = buffers[i];
|
|
bufferSystem.bind(buffer);
|
|
}
|
|
this.activateVao(geometry, program);
|
|
vaoObjectHash[program._key] = vao;
|
|
vaoObjectHash[signature] = vao;
|
|
gl.bindVertexArray(null);
|
|
return vao;
|
|
}
|
|
/**
|
|
* Disposes geometry.
|
|
* @param geometry - Geometry with buffers. Only VAO will be disposed
|
|
* @param [contextLost=false] - If context was lost, we suppress deleteVertexArray
|
|
*/
|
|
onGeometryDestroy(geometry, contextLost) {
|
|
const vaoObjectHash = this._geometryVaoHash[geometry.uid];
|
|
const gl = this.gl;
|
|
if (vaoObjectHash) {
|
|
if (contextLost) {
|
|
for (const i in vaoObjectHash) {
|
|
if (this._activeVao !== vaoObjectHash[i]) {
|
|
this.unbind();
|
|
}
|
|
gl.deleteVertexArray(vaoObjectHash[i]);
|
|
}
|
|
}
|
|
this._geometryVaoHash[geometry.uid] = null;
|
|
}
|
|
}
|
|
/**
|
|
* Dispose all WebGL resources of all managed geometries.
|
|
* @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls
|
|
*/
|
|
destroyAll(contextLost = false) {
|
|
const gl = this.gl;
|
|
for (const i in this._geometryVaoHash) {
|
|
if (contextLost) {
|
|
for (const j in this._geometryVaoHash[i]) {
|
|
const vaoObjectHash = this._geometryVaoHash[i];
|
|
if (this._activeVao !== vaoObjectHash) {
|
|
this.unbind();
|
|
}
|
|
gl.deleteVertexArray(vaoObjectHash[j]);
|
|
}
|
|
}
|
|
this._geometryVaoHash[i] = null;
|
|
}
|
|
}
|
|
/**
|
|
* Activate vertex array object.
|
|
* @param geometry - Geometry instance.
|
|
* @param program - Shader program instance.
|
|
*/
|
|
activateVao(geometry, program) {
|
|
const gl = this._renderer.gl;
|
|
const bufferSystem = this._renderer.buffer;
|
|
const attributes = geometry.attributes;
|
|
if (geometry.indexBuffer) {
|
|
bufferSystem.bind(geometry.indexBuffer);
|
|
}
|
|
let lastBuffer = null;
|
|
for (const j in attributes) {
|
|
const attribute = attributes[j];
|
|
const buffer = attribute.buffer;
|
|
const glBuffer = bufferSystem.getGlBuffer(buffer);
|
|
const programAttrib = program._attributeData[j];
|
|
if (programAttrib) {
|
|
if (lastBuffer !== glBuffer) {
|
|
bufferSystem.bind(buffer);
|
|
lastBuffer = glBuffer;
|
|
}
|
|
const location = programAttrib.location;
|
|
gl.enableVertexAttribArray(location);
|
|
const attributeInfo = getAttributeInfoFromFormat.getAttributeInfoFromFormat(attribute.format);
|
|
const type = getGlTypeFromFormat.getGlTypeFromFormat(attribute.format);
|
|
if (programAttrib.format?.substring(1, 4) === "int") {
|
|
gl.vertexAttribIPointer(
|
|
location,
|
|
attributeInfo.size,
|
|
type,
|
|
attribute.stride,
|
|
attribute.offset
|
|
);
|
|
} else {
|
|
gl.vertexAttribPointer(
|
|
location,
|
|
attributeInfo.size,
|
|
type,
|
|
attributeInfo.normalised,
|
|
attribute.stride,
|
|
attribute.offset
|
|
);
|
|
}
|
|
if (attribute.instance) {
|
|
if (this.hasInstance) {
|
|
const divisor = attribute.divisor ?? 1;
|
|
gl.vertexAttribDivisor(location, divisor);
|
|
} else {
|
|
throw new Error("geometry error, GPU Instancing is not supported on this device");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Draws the currently bound geometry.
|
|
* @param topology - The type primitive to render.
|
|
* @param size - The number of elements to be rendered. If not specified, all vertices after the
|
|
* starting vertex will be drawn.
|
|
* @param start - The starting vertex in the geometry to start drawing from. If not specified,
|
|
* drawing will start from the first vertex.
|
|
* @param instanceCount - The number of instances of the set of elements to execute. If not specified,
|
|
* all instances will be drawn.
|
|
*/
|
|
draw(topology, size, start, instanceCount) {
|
|
const { gl } = this._renderer;
|
|
const geometry = this._activeGeometry;
|
|
const glTopology = topologyToGlMap[geometry.topology || topology];
|
|
instanceCount || (instanceCount = geometry.instanceCount);
|
|
if (geometry.indexBuffer) {
|
|
const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT;
|
|
const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
|
|
if (instanceCount > 1) {
|
|
gl.drawElementsInstanced(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount);
|
|
} else {
|
|
gl.drawElements(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize);
|
|
}
|
|
} else if (instanceCount > 1) {
|
|
gl.drawArraysInstanced(glTopology, start || 0, size || geometry.getSize(), instanceCount);
|
|
} else {
|
|
gl.drawArrays(glTopology, start || 0, size || geometry.getSize());
|
|
}
|
|
return this;
|
|
}
|
|
/** Unbind/reset everything. */
|
|
unbind() {
|
|
this.gl.bindVertexArray(null);
|
|
this._activeVao = null;
|
|
this._activeGeometry = null;
|
|
}
|
|
destroy() {
|
|
this._renderer = null;
|
|
this.gl = null;
|
|
this._activeVao = null;
|
|
this._activeGeometry = null;
|
|
}
|
|
}
|
|
/** @ignore */
|
|
GlGeometrySystem.extension = {
|
|
type: [
|
|
Extensions.ExtensionType.WebGLSystem
|
|
],
|
|
name: "geometry"
|
|
};
|
|
|
|
exports.GlGeometrySystem = GlGeometrySystem;
|
|
//# sourceMappingURL=GlGeometrySystem.js.map
|