初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,363 @@
const GL_NEAREST = 9728; // gl.NEAREST
const GL_LINEAR = 9729; // gl.LINEAR
const GL_NEAREST_MIPMAP_NEAREST = 9984; // gl.NEAREST_MIPMAP_NEAREST
const GL_LINEAR_MIPMAP_NEAREST = 9985; // gl.LINEAR_MIPMAP_NEAREST
const GL_NEAREST_MIPMAP_LINEAR = 9986; // gl.NEAREST_MIPMAP_LINEAR
const GL_LINEAR_MIPMAP_LINEAR = 9987; // gl.LINEAR_MIPMAP_LINEAR
// const GL_BYTE = 5120; // gl.BYTE
const GL_UNSIGNED_BYTE = 5121; // gl.UNSIGNED_BYTE
// const GL_SHORT = 5122; // gl.SHORT
const GL_UNSIGNED_SHORT = 5123; // gl.UNSIGNED_SHORT
const GL_UNSIGNED_INT = 5125; // gl.UNSIGNED_INT
const GL_FLOAT = 5126; // gl.FLOAT
const GL_UNSIGNED_SHORT_5_6_5 = 33635; // gl.UNSIGNED_SHORT_5_6_5
const GL_UNSIGNED_SHORT_4_4_4_4 = 32819; // gl.UNSIGNED_SHORT_4_4_4_4
const GL_UNSIGNED_SHORT_5_5_5_1 = 32820; // gl.UNSIGNED_SHORT_5_5_5_1
const GL_HALF_FLOAT_OES = 36193; // gl.HALF_FLOAT_OES
const GL_DEPTH_COMPONENT = 6402; // gl.DEPTH_COMPONENT
const GL_ALPHA = 6406; // gl.ALPHA
const GL_RGB = 6407; // gl.RGB
const GL_RGBA = 6408; // gl.RGBA
const GL_LUMINANCE = 6409; // gl.LUMINANCE
const GL_LUMINANCE_ALPHA = 6410; // gl.LUMINANCE_ALPHA
const GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; // ext.COMPRESSED_RGB_S3TC_DXT1_EXT
const GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; // ext.COMPRESSED_RGBA_S3TC_DXT1_EXT
const GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; // ext.COMPRESSED_RGBA_S3TC_DXT3_EXT
const GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; // ext.COMPRESSED_RGBA_S3TC_DXT5_EXT
const GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; // ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG
const GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; // ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG
const GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; // ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
const GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; // ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
const GL_COMPRESSED_RGB_ETC1_WEBGL = 0x8D64; // ext.COMPRESSED_RGB_ETC1_WEBGL
const GL_COMPRESSED_RGB8_ETC2 = 0x9274; // ext.COMPRESSED_RGB8_ETC2
const GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278; // ext.COMPRESSED_RGBA8_ETC2_EAC
const _filterGL = [
[ GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR ],
[ GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR ],
];
const _textureFmtGL = [
// TEXTURE_FMT_RGB_DXT1: 0
{ format: GL_RGB, internalFormat: GL_COMPRESSED_RGB_S3TC_DXT1_EXT, pixelType: null },
// TEXTURE_FMT_RGBA_DXT1: 1
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, pixelType: null },
// TEXTURE_FMT_RGBA_DXT3: 2
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, pixelType: null },
// TEXTURE_FMT_RGBA_DXT5: 3
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, pixelType: null },
// TEXTURE_FMT_RGB_ETC1: 4
{ format: GL_RGB, internalFormat: GL_COMPRESSED_RGB_ETC1_WEBGL, pixelType: null },
// TEXTURE_FMT_RGB_PVRTC_2BPPV1: 5
{ format: GL_RGB, internalFormat: GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, pixelType: null },
// TEXTURE_FMT_RGBA_PVRTC_2BPPV1: 6
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, pixelType: null },
// TEXTURE_FMT_RGB_PVRTC_4BPPV1: 7
{ format: GL_RGB, internalFormat: GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, pixelType: null },
// TEXTURE_FMT_RGBA_PVRTC_4BPPV1: 8
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, pixelType: null },
// TEXTURE_FMT_A8: 9
{ format: GL_ALPHA, internalFormat: GL_ALPHA, pixelType: GL_UNSIGNED_BYTE },
// TEXTURE_FMT_L8: 10
{ format: GL_LUMINANCE, internalFormat: GL_LUMINANCE, pixelType: GL_UNSIGNED_BYTE },
// TEXTURE_FMT_L8_A8: 11
{ format: GL_LUMINANCE_ALPHA, internalFormat: GL_LUMINANCE_ALPHA, pixelType: GL_UNSIGNED_BYTE },
// TEXTURE_FMT_R5_G6_B5: 12
{ format: GL_RGB, internalFormat: GL_RGB, pixelType: GL_UNSIGNED_SHORT_5_6_5 },
// TEXTURE_FMT_R5_G5_B5_A1: 13
{ format: GL_RGBA, internalFormat: GL_RGBA, pixelType: GL_UNSIGNED_SHORT_5_5_5_1 },
// TEXTURE_FMT_R4_G4_B4_A4: 14
{ format: GL_RGBA, internalFormat: GL_RGBA, pixelType: GL_UNSIGNED_SHORT_4_4_4_4 },
// TEXTURE_FMT_RGB8: 15
{ format: GL_RGB, internalFormat: GL_RGB, pixelType: GL_UNSIGNED_BYTE },
// TEXTURE_FMT_RGBA8: 16
{ format: GL_RGBA, internalFormat: GL_RGBA, pixelType: GL_UNSIGNED_BYTE },
// TEXTURE_FMT_RGB16F: 17
{ format: GL_RGB, internalFormat: GL_RGB, pixelType: GL_HALF_FLOAT_OES },
// TEXTURE_FMT_RGBA16F: 18
{ format: GL_RGBA, internalFormat: GL_RGBA, pixelType: GL_HALF_FLOAT_OES },
// TEXTURE_FMT_RGB32F: 19
{ format: GL_RGB, internalFormat: GL_RGB, pixelType: GL_FLOAT },
// TEXTURE_FMT_RGBA32F: 20
{ format: GL_RGBA, internalFormat: GL_RGBA, pixelType: GL_FLOAT },
// TEXTURE_FMT_R32F: 21
{ format: null, internalFormat: null, pixelType: null },
// TEXTURE_FMT_111110F: 22
{ format: null, internalFormat: null, pixelType: null },
// TEXTURE_FMT_SRGB: 23
{ format: null, internalFormat: null, pixelType: null },
// TEXTURE_FMT_SRGBA: 24
{ format: null, internalFormat: null, pixelType: null },
// TEXTURE_FMT_D16: 25
{ format: GL_DEPTH_COMPONENT, internalFormat: GL_DEPTH_COMPONENT, pixelType: GL_UNSIGNED_SHORT },
// TEXTURE_FMT_D32: 26
{ format: GL_DEPTH_COMPONENT, internalFormat: GL_DEPTH_COMPONENT, pixelType: GL_UNSIGNED_INT },
// TEXTURE_FMT_D24S8: 27
{ format: GL_DEPTH_COMPONENT, internalFormat: GL_DEPTH_COMPONENT, pixelType: GL_UNSIGNED_INT },
// TEXTURE_FMT_RGB_ETC2: 28
{ format: GL_RGB, internalFormat: GL_COMPRESSED_RGB8_ETC2, pixelType: null },
// TEXTURE_FMT_RGBA_ETC2: 29
{ format: GL_RGBA, internalFormat: GL_COMPRESSED_RGBA8_ETC2_EAC, pixelType: null },
];
/**
* enums
*/
export const enums = {
// buffer usage
USAGE_STATIC: 35044, // gl.STATIC_DRAW
USAGE_DYNAMIC: 35048, // gl.DYNAMIC_DRAW
USAGE_STREAM: 35040, // gl.STREAM_DRAW
// index buffer format
INDEX_FMT_UINT8: 5121, // gl.UNSIGNED_BYTE
INDEX_FMT_UINT16: 5123, // gl.UNSIGNED_SHORT
INDEX_FMT_UINT32: 5125, // gl.UNSIGNED_INT (OES_element_index_uint)
// vertex attribute semantic
ATTR_POSITION: 'a_position',
ATTR_NORMAL: 'a_normal',
ATTR_TANGENT: 'a_tangent',
ATTR_BITANGENT: 'a_bitangent',
ATTR_WEIGHTS: 'a_weights',
ATTR_JOINTS: 'a_joints',
ATTR_COLOR: 'a_color',
ATTR_COLOR0: 'a_color0',
ATTR_COLOR1: 'a_color1',
ATTR_UV: 'a_uv',
ATTR_UV0: 'a_uv0',
ATTR_UV1: 'a_uv1',
ATTR_UV2: 'a_uv2',
ATTR_UV3: 'a_uv3',
ATTR_UV4: 'a_uv4',
ATTR_UV5: 'a_uv5',
ATTR_UV6: 'a_uv6',
ATTR_UV7: 'a_uv7',
ATTR_TEX_COORD: 'a_texCoord',
ATTR_TEX_COORD1: 'a_texCoord1',
ATTR_TEX_COORD2: 'a_texCoord2',
ATTR_TEX_COORD3: 'a_texCoord3',
ATTR_TEX_COORD4: 'a_texCoord4',
ATTR_TEX_COORD5: 'a_texCoord5',
ATTR_TEX_COORD6: 'a_texCoord6',
ATTR_TEX_COORD7: 'a_texCoord7',
ATTR_TEX_COORD8: 'a_texCoord8',
// vertex attribute type
ATTR_TYPE_INT8: 5120, // gl.BYTE
ATTR_TYPE_UINT8: 5121, // gl.UNSIGNED_BYTE
ATTR_TYPE_INT16: 5122, // gl.SHORT
ATTR_TYPE_UINT16: 5123, // gl.UNSIGNED_SHORT
ATTR_TYPE_INT32: 5124, // gl.INT
ATTR_TYPE_UINT32: 5125, // gl.UNSIGNED_INT
ATTR_TYPE_FLOAT32: 5126, // gl.FLOAT
// texture filter
FILTER_NEAREST: 0,
FILTER_LINEAR: 1,
// texture wrap mode
WRAP_REPEAT: 10497, // gl.REPEAT
WRAP_CLAMP: 33071, // gl.CLAMP_TO_EDGE
WRAP_MIRROR: 33648, // gl.MIRRORED_REPEAT
// texture format
// compress formats
TEXTURE_FMT_RGB_DXT1: 0,
TEXTURE_FMT_RGBA_DXT1: 1,
TEXTURE_FMT_RGBA_DXT3: 2,
TEXTURE_FMT_RGBA_DXT5: 3,
TEXTURE_FMT_RGB_ETC1: 4,
TEXTURE_FMT_RGB_PVRTC_2BPPV1: 5,
TEXTURE_FMT_RGBA_PVRTC_2BPPV1: 6,
TEXTURE_FMT_RGB_PVRTC_4BPPV1: 7,
TEXTURE_FMT_RGBA_PVRTC_4BPPV1: 8,
// normal formats
TEXTURE_FMT_A8: 9,
TEXTURE_FMT_L8: 10,
TEXTURE_FMT_L8_A8: 11,
TEXTURE_FMT_R5_G6_B5: 12,
TEXTURE_FMT_R5_G5_B5_A1: 13,
TEXTURE_FMT_R4_G4_B4_A4: 14,
TEXTURE_FMT_RGB8: 15,
TEXTURE_FMT_RGBA8: 16,
TEXTURE_FMT_RGB16F: 17,
TEXTURE_FMT_RGBA16F: 18,
TEXTURE_FMT_RGB32F: 19,
TEXTURE_FMT_RGBA32F: 20,
TEXTURE_FMT_R32F: 21,
TEXTURE_FMT_111110F: 22,
TEXTURE_FMT_SRGB: 23,
TEXTURE_FMT_SRGBA: 24,
// depth formats
TEXTURE_FMT_D16: 25,
TEXTURE_FMT_D32: 26,
TEXTURE_FMT_D24S8: 27,
// etc2 format
TEXTURE_FMT_RGB_ETC2: 28,
TEXTURE_FMT_RGBA_ETC2: 29,
// depth and stencil function
DS_FUNC_NEVER: 512, // gl.NEVER
DS_FUNC_LESS: 513, // gl.LESS
DS_FUNC_EQUAL: 514, // gl.EQUAL
DS_FUNC_LEQUAL: 515, // gl.LEQUAL
DS_FUNC_GREATER: 516, // gl.GREATER
DS_FUNC_NOTEQUAL: 517, // gl.NOTEQUAL
DS_FUNC_GEQUAL: 518, // gl.GEQUAL
DS_FUNC_ALWAYS: 519, // gl.ALWAYS
// render-buffer format
RB_FMT_RGBA4: 32854, // gl.RGBA4
RB_FMT_RGB5_A1: 32855, // gl.RGB5_A1
RB_FMT_RGB565: 36194, // gl.RGB565
RB_FMT_D16: 33189, // gl.DEPTH_COMPONENT16
RB_FMT_S8: 36168, // gl.STENCIL_INDEX8
RB_FMT_D24S8: 34041, // gl.DEPTH_STENCIL
// blend-equation
BLEND_FUNC_ADD: 32774, // gl.FUNC_ADD
BLEND_FUNC_SUBTRACT: 32778, // gl.FUNC_SUBTRACT
BLEND_FUNC_REVERSE_SUBTRACT: 32779, // gl.FUNC_REVERSE_SUBTRACT
// blend
BLEND_ZERO: 0, // gl.ZERO
BLEND_ONE: 1, // gl.ONE
BLEND_SRC_COLOR: 768, // gl.SRC_COLOR
BLEND_ONE_MINUS_SRC_COLOR: 769, // gl.ONE_MINUS_SRC_COLOR
BLEND_DST_COLOR: 774, // gl.DST_COLOR
BLEND_ONE_MINUS_DST_COLOR: 775, // gl.ONE_MINUS_DST_COLOR
BLEND_SRC_ALPHA: 770, // gl.SRC_ALPHA
BLEND_ONE_MINUS_SRC_ALPHA: 771, // gl.ONE_MINUS_SRC_ALPHA
BLEND_DST_ALPHA: 772, // gl.DST_ALPHA
BLEND_ONE_MINUS_DST_ALPHA: 773, // gl.ONE_MINUS_DST_ALPHA
BLEND_CONSTANT_COLOR: 32769, // gl.CONSTANT_COLOR
BLEND_ONE_MINUS_CONSTANT_COLOR: 32770, // gl.ONE_MINUS_CONSTANT_COLOR
BLEND_CONSTANT_ALPHA: 32771, // gl.CONSTANT_ALPHA
BLEND_ONE_MINUS_CONSTANT_ALPHA: 32772, // gl.ONE_MINUS_CONSTANT_ALPHA
BLEND_SRC_ALPHA_SATURATE: 776, // gl.SRC_ALPHA_SATURATE
// stencil operation
STENCIL_DISABLE: 0, // disable stencil
STENCIL_ENABLE: 1, // enable stencil
STENCIL_INHERIT: 2, // inherit stencil states
STENCIL_OP_KEEP: 7680, // gl.KEEP
STENCIL_OP_ZERO: 0, // gl.ZERO
STENCIL_OP_REPLACE: 7681, // gl.REPLACE
STENCIL_OP_INCR: 7682, // gl.INCR
STENCIL_OP_INCR_WRAP: 34055, // gl.INCR_WRAP
STENCIL_OP_DECR: 7683, // gl.DECR
STENCIL_OP_DECR_WRAP: 34056, // gl.DECR_WRAP
STENCIL_OP_INVERT: 5386, // gl.INVERT
// cull
CULL_NONE: 0,
CULL_FRONT: 1028,
CULL_BACK: 1029,
CULL_FRONT_AND_BACK: 1032,
// primitive type
PT_POINTS: 0, // gl.POINTS
PT_LINES: 1, // gl.LINES
PT_LINE_LOOP: 2, // gl.LINE_LOOP
PT_LINE_STRIP: 3, // gl.LINE_STRIP
PT_TRIANGLES: 4, // gl.TRIANGLES
PT_TRIANGLE_STRIP: 5, // gl.TRIANGLE_STRIP
PT_TRIANGLE_FAN: 6, // gl.TRIANGLE_FAN
};
/**
* @method attrTypeBytes
* @param {ATTR_TYPE_*} attrType
*/
export function attrTypeBytes(attrType) {
if (attrType === enums.ATTR_TYPE_INT8) {
return 1;
} else if (attrType === enums.ATTR_TYPE_UINT8) {
return 1;
} else if (attrType === enums.ATTR_TYPE_INT16) {
return 2;
} else if (attrType === enums.ATTR_TYPE_UINT16) {
return 2;
} else if (attrType === enums.ATTR_TYPE_INT32) {
return 4;
} else if (attrType === enums.ATTR_TYPE_UINT32) {
return 4;
} else if (attrType === enums.ATTR_TYPE_FLOAT32) {
return 4;
}
console.warn(`Unknown ATTR_TYPE: ${attrType}`);
return 0;
}
/**
* @method glFilter
* @param {WebGLContext} gl
* @param {FILTER_*} filter
* @param {FILTER_*} mipFilter
*/
export function glFilter(gl, filter, mipFilter = -1) {
let result = _filterGL[filter][mipFilter+1];
if (result === undefined) {
console.warn(`Unknown FILTER: ${filter}`);
return mipFilter === -1 ? gl.LINEAR : gl.LINEAR_MIPMAP_LINEAR;
}
return result;
}
/**
* @method glTextureFmt
* @param {TEXTURE_FMT_*} fmt
*/
export function glTextureFmt(fmt) {
let result = _textureFmtGL[fmt];
if (result === undefined) {
console.warn(`Unknown TEXTURE_FMT: ${fmt}`);
return _textureFmtGL[enums.TEXTURE_FMT_RGBA8];
}
return result;
}

View File

@@ -0,0 +1,45 @@
export default class FrameBuffer {
/**
* @constructor
* @param {Device} device
* @param {Number} width
* @param {Number} height
* @param {Object} options
* @param {Array} options.colors
* @param {RenderBuffer|Texture2D|TextureCube} options.depth
* @param {RenderBuffer|Texture2D|TextureCube} options.stencil
* @param {RenderBuffer|Texture2D|TextureCube} options.depthStencil
*/
constructor(device, width, height, options) {
this._device = device;
this._width = width;
this._height = height;
this._colors = options.colors || [];
this._depth = options.depth || null;
this._stencil = options.stencil || null;
this._depthStencil = options.depthStencil || null;
this._glID = device._gl.createFramebuffer();
}
/**
* @method destroy
*/
destroy() {
if (this._glID === null) {
console.error('The frame-buffer already destroyed');
return;
}
const gl = this._device._gl;
gl.deleteFramebuffer(this._glID);
this._glID = null;
}
getHandle() {
return this._glID;
}
}

View File

@@ -0,0 +1,104 @@
import { enums } from './enums';
const BYTES_PER_INDEX = {
[enums.INDEX_FMT_UINT8]: 1,
[enums.INDEX_FMT_UINT16]: 2,
[enums.INDEX_FMT_UINT32]: 4,
}
class IndexBuffer {
/**
* @constructor
* @param {Device} device
* @param {INDEX_FMT_*} format
* @param {USAGE_*} usage
* @param {ArrayBuffer | Uint8Array} data
*/
constructor(device, format, usage, data) {
this._device = device;
this._format = format;
this._usage = usage;
this._bytesPerIndex = BYTES_PER_INDEX[format];
this._bytes = data.byteLength;
this._numIndices = this._bytes / this._bytesPerIndex;
this._needExpandDataStore = true;
// update
this._glID = device._gl.createBuffer();
this.update(0, data);
// stats
device._stats.ib += this._bytes;
}
/**
* @method destroy
*/
destroy() {
if (this._glID === -1) {
console.error('The buffer already destroyed');
return;
}
let gl = this._device._gl;
gl.deleteBuffer(this._glID);
this._device._stats.ib -= this.bytes;
this._glID = -1;
}
/**
* @method update
* @param {Number} byteOffset
* @param {ArrayBuffer} data
*/
update(byteOffset, data) {
if (this._glID === -1) {
console.error('The buffer is destroyed');
return;
}
if (data.byteLength === 0) return;
// Need to create new buffer object when bytes exceed
if (byteOffset + data.byteLength > this._bytes) {
if (byteOffset) {
// Lost data between [0, byteOffset] which is need for new buffer
console.error('Failed to update data, bytes exceed.');
return;
}
else {
this._needExpandDataStore = true;
this._bytes = byteOffset + data.byteLength;
this._numIndices = this._bytes / this._bytesPerIndex;
}
}
/** @type{WebGLRenderingContext} */
let gl = this._device._gl;
let glUsage = this._usage;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._glID);
if (this._needExpandDataStore) {
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, glUsage);
this._needExpandDataStore = false;
}
else {
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, byteOffset, data);
}
this._device._restoreIndexBuffer();
}
get count () {
return this._numIndices;
}
setUsage (usage) {
this._usage = usage;
}
}
IndexBuffer.BYTES_PER_INDEX = BYTES_PER_INDEX;
export default IndexBuffer;

View File

@@ -0,0 +1,46 @@
import {
enums,
attrTypeBytes,
glFilter,
glTextureFmt,
} from './enums';
let gfx = null;
if (CC_JSB && CC_NATIVERENDERER) {
gfx = window.gfx;
} else {
let VertexFormat = require('./vertex-format');
let IndexBuffer = require('./index-buffer');
let VertexBuffer = require('./vertex-buffer');
let Program = require('./program');
let Texture = require('./texture');
let Texture2D = require('./texture-2d');
let TextureCube = require('./texture-cube');
let RenderBuffer = require('./render-buffer');
let FrameBuffer = require('./frame-buffer');
let Device = require('./device');
gfx = {
// classes
VertexFormat,
IndexBuffer,
VertexBuffer,
Program,
Texture,
Texture2D,
TextureCube,
RenderBuffer,
FrameBuffer,
Device,
// functions
attrTypeBytes,
glFilter,
glTextureFmt,
};
Object.assign(gfx, enums);
}
export default gfx;
cc.gfx = gfx;

View File

@@ -0,0 +1,3 @@
export function isPow2(v) {
return !(v & (v - 1)) && (!!v);
}

View File

@@ -0,0 +1,172 @@
let _genID = 0;
function _parseError(out, type, errorLog) {
if(!errorLog){
return;
}
errorLog.split('\n').forEach(msg => {
if (msg.length < 5) {
return;
}
let parts = /^ERROR:\s+(\d+):(\d+):\s*(.*)$/.exec(msg);
if (parts) {
out.push({
type: type,
fileID: parts[1] | 0,
line: parts[2] | 0,
message: parts[3].trim()
})
} else if (msg.length > 0) {
out.push({
type: type,
fileID: -1,
line: 0,
message: msg
});
}
});
}
export default class Program {
/**
* @param {ef.GraphicsDevice} device - graphic device
* @param {object} options - shader definition
* @param {string} options.vert - vertex shader source code
* @param {string} options.frag - fragment shader shader source code
* @example
* let prog = new Program(device, {
* vert: `
* attribute vec3 a_position;
* void main() {
* gl_Position = vec4( a_position, 1.0 );
* }
* `,
* frag: `
* precision mediump float;
* void main() {
* gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 );
* }
* `
* });
*/
constructor(device, options) {
this._device = device;
// stores gl information: { location, type }
this._attributes = [];
this._uniforms = [];
this._samplers = [];
this._errors = [];
this._linked = false;
this._vertSource = options.vert;
this._fragSource = options.frag;
this._glID = null;
this._id = _genID++;
}
get id() {
return this._id;
}
link() {
if (this._linked) {
return;
}
let gl = this._device._gl;
let vertShader = _createShader(gl, gl.VERTEX_SHADER, this._vertSource);
let fragShader = _createShader(gl, gl.FRAGMENT_SHADER, this._fragSource);
let program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
let failed = false;
let errors = this._errors;
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {
_parseError(errors, 'vs', gl.getShaderInfoLog(vertShader));
failed = true;
}
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {
_parseError(errors, 'fs', gl.getShaderInfoLog(fragShader));
failed = true;
}
gl.deleteShader(vertShader);
gl.deleteShader(fragShader);
if (failed) {
return errors;
}
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
errors.push({info: `Failed to link shader program: ${gl.getProgramInfoLog(program)}`});
return errors;
}
this._glID = program;
// parse attribute
let numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
for (let i = 0; i < numAttributes; ++i) {
let info = gl.getActiveAttrib(program, i);
let location = gl.getAttribLocation(program, info.name);
this._attributes.push({
name: info.name,
location: location,
type: info.type,
});
}
// parse uniform
let numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < numUniforms; ++i) {
let info = gl.getActiveUniform(program, i);
let name = info.name;
let location = gl.getUniformLocation(program, name);
let isArray = name.substr(name.length - 3) === '[0]';
if (isArray) {
name = name.substr(0, name.length - 3);
}
let uniform = {
name: name,
location: location,
type: info.type,
size: isArray ? info.size : undefined, // used when uniform is an array
};
this._uniforms.push(uniform);
}
this._linked = true;
}
destroy() {
let gl = this._device._gl;
gl.deleteProgram(this._glID);
this._linked = false;
this._glID = null;
this._attributes = [];
this._uniforms = [];
this._samplers = [];
}
}
// ====================
// internal
// ====================
function _createShader(gl, type, src) {
let shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
return shader;
}

View File

@@ -0,0 +1,43 @@
export default class RenderBuffer {
/**
* @constructor
* @param {Device} device
* @param {RB_FMT_*} format
* @param {Number} width
* @param {Number} height
*/
constructor(device, format, width, height) {
this._device = device;
this._format = format;
this._glID = device._gl.createRenderbuffer();
this.update(width, height);
}
update (width, height) {
this._width = width;
this._height = height;
const gl = this._device._gl;
gl.bindRenderbuffer(gl.RENDERBUFFER, this._glID);
gl.renderbufferStorage(gl.RENDERBUFFER, this._format, width, height);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}
/**
* @method destroy
*/
destroy() {
if (this._glID === null) {
console.error('The render-buffer already destroyed');
return;
}
const gl = this._device._gl;
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.deleteRenderbuffer(this._glID);
this._glID = null;
}
}

View File

@@ -0,0 +1,133 @@
import { enums } from './enums';
const _default = {
// blend
blend: false,
blendSep: false,
blendColor: 0xffffffff,
blendEq: enums.BLEND_FUNC_ADD,
blendAlphaEq: enums.BLEND_FUNC_ADD,
blendSrc: enums.BLEND_ONE,
blendDst: enums.BLEND_ZERO,
blendSrcAlpha: enums.BLEND_ONE,
blendDstAlpha: enums.BLEND_ZERO,
// depth
depthTest: false,
depthWrite: false,
depthFunc: enums.DS_FUNC_LESS,
// stencil
stencilTest: false,
stencilSep: false,
stencilFuncFront: enums.DS_FUNC_ALWAYS,
stencilRefFront: 0,
stencilMaskFront: 0xff,
stencilFailOpFront: enums.STENCIL_OP_KEEP,
stencilZFailOpFront: enums.STENCIL_OP_KEEP,
stencilZPassOpFront: enums.STENCIL_OP_KEEP,
stencilWriteMaskFront: 0xff,
stencilFuncBack: enums.DS_FUNC_ALWAYS,
stencilRefBack: 0,
stencilMaskBack: 0xff,
stencilFailOpBack: enums.STENCIL_OP_KEEP,
stencilZFailOpBack: enums.STENCIL_OP_KEEP,
stencilZPassOpBack: enums.STENCIL_OP_KEEP,
stencilWriteMaskBack: 0xff,
// cull-mode
cullMode: enums.CULL_BACK,
// primitive-type
primitiveType: enums.PT_TRIANGLES,
// bindings
maxStream: -1,
vertexBuffers: [],
vertexBufferOffsets: [],
indexBuffer: null,
maxTextureSlot: -1,
textureUnits: [],
program: null,
};
export default class State {
constructor(device) {
// bindings
this.vertexBuffers = new Array(device._caps.maxVertexStreams);
this.vertexBufferOffsets = new Array(device._caps.maxVertexStreams);
this.textureUnits = new Array(device._caps.maxTextureUnits);
this.set(_default);
}
static initDefault(device) {
_default.vertexBuffers = new Array(device._caps.maxVertexStreams);
_default.vertexBufferOffsets = new Array(device._caps.maxVertexStreams);
_default.textureUnits = new Array(device._caps.maxTextureUnits);
}
reset () {
this.set(_default);
}
set (cpy) {
// blending
this.blend = cpy.blend;
this.blendSep = cpy.blendSep;
this.blendColor = cpy.blendColor;
this.blendEq = cpy.blendEq;
this.blendAlphaEq = cpy.blendAlphaEq;
this.blendSrc = cpy.blendSrc;
this.blendDst = cpy.blendDst;
this.blendSrcAlpha = cpy.blendSrcAlpha;
this.blendDstAlpha = cpy.blendDstAlpha;
// depth
this.depthTest = cpy.depthTest;
this.depthWrite = cpy.depthWrite;
this.depthFunc = cpy.depthFunc;
// stencil
this.stencilTest = cpy.stencilTest;
this.stencilSep = cpy.stencilSep;
this.stencilFuncFront = cpy.stencilFuncFront;
this.stencilRefFront = cpy.stencilRefFront;
this.stencilMaskFront = cpy.stencilMaskFront;
this.stencilFailOpFront = cpy.stencilFailOpFront;
this.stencilZFailOpFront = cpy.stencilZFailOpFront;
this.stencilZPassOpFront = cpy.stencilZPassOpFront;
this.stencilWriteMaskFront = cpy.stencilWriteMaskFront;
this.stencilFuncBack = cpy.stencilFuncBack;
this.stencilRefBack = cpy.stencilRefBack;
this.stencilMaskBack = cpy.stencilMaskBack;
this.stencilFailOpBack = cpy.stencilFailOpBack;
this.stencilZFailOpBack = cpy.stencilZFailOpBack;
this.stencilZPassOpBack = cpy.stencilZPassOpBack;
this.stencilWriteMaskBack = cpy.stencilWriteMaskBack;
// cull-mode
this.cullMode = cpy.cullMode;
// primitive-type
this.primitiveType = cpy.primitiveType;
// buffer bindings
this.maxStream = cpy.maxStream;
for (let i = 0; i < cpy.vertexBuffers.length; ++i) {
this.vertexBuffers[i] = cpy.vertexBuffers[i];
}
for (let i = 0; i < cpy.vertexBufferOffsets.length; ++i) {
this.vertexBufferOffsets[i] = cpy.vertexBufferOffsets[i];
}
this.indexBuffer = cpy.indexBuffer;
// texture bindings
this.maxTextureSlot = cpy.maxTextureSlot;
for (let i = 0; i < cpy.textureUnits.length; ++i) {
this.textureUnits[i] = cpy.textureUnits[i];
}
this.program = cpy.program;
}
}

View File

@@ -0,0 +1,330 @@
// @ts-check
import Texture from './texture';
import { enums, glFilter, glTextureFmt } from './enums';
import { isPow2 } from './misc';
/**
* @typedef {HTMLImageElement | HTMLCanvasElement} HTMLImageSource
* @typedef {HTMLImageSource | ArrayBufferView} ImageSource
* @typedef {{width?: number, height?: number, minFilter?: number, magFilter?: number, mipFilter?: number, wrapS?: number, wrapT?: number, format?: number, genMipmaps?: boolean, images?: ImageSource[], image?: ImageSource, flipY?: boolean, premultiplyAlpha?: boolean, anisotropy?: number}} TextureUpdateOpts
* @typedef {import("../gfx/device").default} Device
*/
export default class Texture2D extends Texture {
/**
* @constructor
* @param {Device} device
* @param {TextureUpdateOpts} options
*/
constructor(device, options) {
super(device);
let gl = this._device._gl;
this._target = gl.TEXTURE_2D;
this._glID = gl.createTexture();
// always alloc texture in GPU when we create it.
options.images = options.images || [null];
this.update(options);
}
/**
* @method update
* @param {TextureUpdateOpts} options
*/
update(options) {
let gl = this._device._gl;
let genMipmaps = this._genMipmap;
if (options) {
if (options.width !== undefined) {
this._width = options.width;
}
if (options.height !== undefined) {
this._height = options.height;
}
if (options.anisotropy !== undefined) {
this._anisotropy = options.anisotropy;
}
if (options.minFilter !== undefined) {
this._minFilter = options.minFilter;
}
if (options.magFilter !== undefined) {
this._magFilter = options.magFilter;
}
if (options.mipFilter !== undefined) {
this._mipFilter = options.mipFilter;
}
if (options.wrapS !== undefined) {
this._wrapS = options.wrapS;
}
if (options.wrapT !== undefined) {
this._wrapT = options.wrapT;
}
if (options.format !== undefined) {
this._format = options.format;
this._compressed =
(this._format >= enums.TEXTURE_FMT_RGB_DXT1 && this._format <= enums.TEXTURE_FMT_RGBA_PVRTC_4BPPV1) ||
(this._format >= enums.TEXTURE_FMT_RGB_ETC2 && this._format <= enums.TEXTURE_FMT_RGBA_ETC2)
;
}
// check if generate mipmap
if (options.genMipmaps !== undefined) {
this._genMipmap = options.genMipmaps;
genMipmaps = options.genMipmaps;
}
let maxSize = this._device.caps.maxTextureSize || Number.MAX_VALUE;
let textureMaxSize = Math.max(options.width || 0, options.height || 0);
if (maxSize < textureMaxSize)
console.warn(`The current texture size ${textureMaxSize} exceeds the maximum size [${maxSize}] supported on the device.`);
if (options.images !== undefined) {
if (options.images.length > 1) {
genMipmaps = false;
let maxLength = options.width > options.height ? options.width : options.height;
if (maxLength >> (options.images.length - 1) !== 1) {
console.error('texture-2d mipmap is invalid, should have a 1x1 mipmap.');
}
}
}
}
// NOTE: get pot after this._width, this._height has been assigned.
let pot = isPow2(this._width) && isPow2(this._height);
if (!pot) {
genMipmaps = false;
}
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._glID);
if (options.images !== undefined && options.images.length > 0) {
this._setMipmap(options.images, options.flipY, options.premultiplyAlpha);
if (options.images.length > 1) this._genMipmap = true;
}
if (genMipmaps) {
gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
gl.generateMipmap(gl.TEXTURE_2D);
this._genMipmap = true;
}
this._setTexInfo();
this._device._restoreTexture(0);
}
/**
* @method updateSubImage
* @param {Object} options
* @param {Number} options.x
* @param {Number} options.y
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateSubImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._glID);
this._setSubImage(glFmt, options);
this._device._restoreTexture(0);
}
/**
* @method updateImage
* @param {Object} options
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this._glID);
this._setImage(glFmt, options);
this._device._restoreTexture(0);
}
_setSubImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let img = options.image;
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
gl.texSubImage2D(gl.TEXTURE_2D, options.level, options.x, options.y, glFmt.format, glFmt.pixelType, img);
} else {
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (this._compressed) {
gl.compressedTexSubImage2D(gl.TEXTURE_2D,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
img
);
} else {
gl.texSubImage2D(
gl.TEXTURE_2D,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
_setImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let img = options.image;
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
gl.texImage2D(
gl.TEXTURE_2D,
options.level,
glFmt.internalFormat,
glFmt.format,
glFmt.pixelType,
img
);
} else {
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (this._compressed) {
gl.compressedTexImage2D(
gl.TEXTURE_2D,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
img
);
} else {
gl.texImage2D(
gl.TEXTURE_2D,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
_setMipmap(images, flipY, premultiplyAlpha) {
let glFmt = glTextureFmt(this._format);
let options = {
width: this._width,
height: this._height,
flipY: flipY,
premultiplyAlpha: premultiplyAlpha,
level: 0,
image: null
};
for (let i = 0; i < images.length; ++i) {
options.level = i;
options.width = this._width >> i;
options.height = this._height >> i;
options.image = images[i];
this._setImage(glFmt, options);
}
}
_setTexInfo() {
let gl = this._device._gl;
let pot = isPow2(this._width) && isPow2(this._height);
// WebGL1 doesn't support all wrap modes with NPOT textures
if (!pot && (this._wrapS !== enums.WRAP_CLAMP || this._wrapT !== enums.WRAP_CLAMP)) {
console.warn('WebGL1 doesn\'t support all wrap modes with NPOT textures');
this._wrapS = enums.WRAP_CLAMP;
this._wrapT = enums.WRAP_CLAMP;
}
let mipFilter = this._genMipmap ? this._mipFilter : -1;
if (!pot && mipFilter !== -1) {
console.warn('NPOT textures do not support mipmap filter');
mipFilter = -1;
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, glFilter(gl, this._minFilter, mipFilter));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, glFilter(gl, this._magFilter, -1));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this._wrapS);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this._wrapT);
let ext = this._device.ext('EXT_texture_filter_anisotropic');
if (ext) {
gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, this._anisotropy);
}
}
}

View File

@@ -0,0 +1,338 @@
import Texture from './texture';
import { enums, glFilter, glTextureFmt } from './enums';
import { isPow2 } from './misc';
export default class TextureCube extends Texture {
/**
* @constructor
* @param {Device} device
* @param {Object} options
* @param {Array} options.images
* @param {Boolean} options.genMipmaps
* @param {Number} options.width
* @param {Number} options.height
* @param {TEXTURE_FMT_*} options.format
* @param {Number} options.anisotropy
* @param {FILTER_*} options.minFilter
* @param {FILTER_*} options.magFilter
* @param {FILTER_*} options.mipFilter
* @param {WRAP_*} options.wrapS
* @param {WRAP_*} options.wrapT
* @param {WRAP_*} options.wrapR
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
constructor(device, options) {
super(device);
let gl = this._device._gl;
this._target = gl.TEXTURE_CUBE_MAP;
this._glID = gl.createTexture();
this.update(options);
}
/**
* @method update
* @param {Object} options
* @param {Array} options.images
* @param {Boolean} options.genMipmaps
* @param {Number} options.width
* @param {Number} options.height
* @param {TEXTURE_FMT_*} options.format
* @param {Number} options.anisotropy
* @param {FILTER_*} options.minFilter
* @param {FILTER_*} options.magFilter
* @param {FILTER_*} options.mipFilter
* @param {WRAP_*} options.wrapS
* @param {WRAP_*} options.wrapT
* @param {WRAP_*} options.wrapR
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
update(options) {
let gl = this._device._gl;
let genMipmaps = this._genMipmaps;
if (options) {
if (options.width !== undefined) {
this._width = options.width;
}
if (options.height !== undefined) {
this._height = options.height;
}
if (options.anisotropy !== undefined) {
this._anisotropy = options.anisotropy;
}
if (options.minFilter !== undefined) {
this._minFilter = options.minFilter;
}
if (options.magFilter !== undefined) {
this._magFilter = options.magFilter;
}
if (options.mipFilter !== undefined) {
this._mipFilter = options.mipFilter;
}
if (options.wrapS !== undefined) {
this._wrapS = options.wrapS;
}
if (options.wrapT !== undefined) {
this._wrapT = options.wrapT;
}
// wrapR available in webgl2
// if (options.wrapR !== undefined) {
// this._wrapR = options.wrapR;
// }
if (options.format !== undefined) {
this._format = options.format;
this._compressed =
(this._format >= enums.TEXTURE_FMT_RGB_DXT1 && this._format <= enums.TEXTURE_FMT_RGBA_PVRTC_4BPPV1) ||
(this._format >= enums.TEXTURE_FMT_RGB_ETC2 && this._format <= enums.TEXTURE_FMT_RGBA_ETC2)
;
}
// check if generate mipmap
if (options.genMipmaps !== undefined) {
this._genMipmaps = options.genMipmaps;
genMipmaps = options.genMipmaps;
}
if (options.images !== undefined) {
if (options.images.length > 1) {
genMipmaps = false;
if (options.width !== options.height) {
console.warn('texture-cube width and height should be identical.');
}
if (options.width >> (options.images.length - 1) !== 1) {
console.error('texture-cube mipmap is invalid. please set mipmap as 1x1, 2x2, 4x4 ... nxn');
}
}
}
}
// NOTE: get pot after this._width, this._height has been assigned.
let pot = isPow2(this._width) && isPow2(this._height);
if (!pot) {
genMipmaps = false;
}
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
if (options.images !== undefined && options.images.length > 0) {
this._setMipmap(options.images, options.flipY, options.premultiplyAlpha);
if (options.images.length > 1) this._genMipmaps = true;
}
if (genMipmaps) {
gl.hint(gl.GENERATE_MIPMAP_HINT, gl.NICEST);
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
this._genMipmaps = true;
}
this._setTexInfo();
this._device._restoreTexture(0);
}
/**
* @method updateSubImage
* @param {Object} options
* @param {Number} options.x
* @param {Number} options.y
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {Number} options.faceIndex
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateSubImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
this._setSubImage(glFmt, options);
this._device._restoreTexture(0);
}
/**
* @method updateImage
* @param {Object} options
* @param {Number} options.width
* @param {Number} options.height
* @param {Number} options.level
* @param {Number} options.faceIndex
* @param {HTMLCanvasElement | HTMLImageElement | HTMLVideoElement | ArrayBufferView} options.image
* @param {Boolean} options.flipY
* @param {Boolean} options.premultiplyAlpha
*/
updateImage(options) {
let gl = this._device._gl;
let glFmt = glTextureFmt(this._format);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glID);
this._setImage(glFmt, options);
this._device._restoreTexture(0);
}
_setSubImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let faceIndex = options.faceIndex;
let img = options.image;
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, options.level, options.x, options.y, glFmt.format, glFmt.pixelType, img);
} else {
if (this._compressed) {
gl.compressedTexSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
img
);
} else {
gl.texSubImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
options.x,
options.y,
options.width,
options.height,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
_setImage(glFmt, options) {
let gl = this._device._gl;
let flipY = options.flipY;
let premultiplyAlpha = options.premultiplyAlpha;
let faceIndex = options.faceIndex;
let img = options.image;
if (flipY === undefined) {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
}
if (premultiplyAlpha === undefined) {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
} else {
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha);
}
if (img && !ArrayBuffer.isView(img) && !(img instanceof ArrayBuffer)) {
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
glFmt.format,
glFmt.pixelType,
img
);
} else {
if (this._compressed) {
gl.compressedTexImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
img
);
} else {
gl.texImage2D(
gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex,
options.level,
glFmt.internalFormat,
options.width,
options.height,
0,
glFmt.format,
glFmt.pixelType,
img
);
}
}
}
// levelImages = [imagePosX, imageNegX, imagePosY, imageNegY, imagePosZ, imageNegz]
// images = [levelImages0, levelImages1, ...]
_setMipmap(images, flipY, premultiplyAlpha) {
let glFmt = glTextureFmt(this._format);
let options = {
width: this._width,
height: this._height,
faceIndex: 0,
flipY: flipY,
premultiplyAlpha: premultiplyAlpha,
level: 0,
image: null
};
for (let i = 0; i < images.length; ++i) {
let levelImages = images[i];
options.level = i;
options.width = this._width >> i;
options.height = this._height >> i;
for (let face = 0; face < 6; ++face) {
options.faceIndex = face;
options.image = levelImages[face];
this._setImage(glFmt, options);
}
}
}
_setTexInfo() {
let gl = this._device._gl;
let pot = isPow2(this._width) && isPow2(this._height);
// WebGL1 doesn't support all wrap modes with NPOT textures
if (!pot && (this._wrapS !== enums.WRAP_CLAMP || this._wrapT !== enums.WRAP_CLAMP)) {
console.warn('WebGL1 doesn\'t support all wrap modes with NPOT textures');
this._wrapS = enums.WRAP_CLAMP;
this._wrapT = enums.WRAP_CLAMP;
}
let mipFilter = this._genMipmaps ? this._mipFilter : -1;
if (!pot && mipFilter !== -1) {
console.warn('NPOT textures do not support mipmap filter');
mipFilter = -1;
}
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, glFilter(gl, this._minFilter, mipFilter));
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, glFilter(gl, this._magFilter, -1));
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, this._wrapS);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, this._wrapT);
// wrapR available in webgl2
// gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, this._wrapR);
let ext = this._device.ext('EXT_texture_filter_anisotropic');
if (ext) {
gl.texParameteri(gl.TEXTURE_CUBE_MAP, ext.TEXTURE_MAX_ANISOTROPY_EXT, this._anisotropy);
}
}
}

View File

@@ -0,0 +1,56 @@
import { enums } from './enums';
/**
* @type {WebGLTexture}
*/
const _nullWebGLTexture = null;
let _textureID = 0;
/**
* @typedef {import("../gfx/device").default} Device
*/
export default class Texture {
/**
* @param {Device} device
*/
constructor(device) {
this._device = device;
this._width = 4;
this._height = 4;
this._genMipmaps = false;
this._compressed = false;
this._anisotropy = 1;
this._minFilter = enums.FILTER_LINEAR;
this._magFilter = enums.FILTER_LINEAR;
this._mipFilter = enums.FILTER_LINEAR;
this._wrapS = enums.WRAP_REPEAT;
this._wrapT = enums.WRAP_REPEAT;
// wrapR available in webgl2
// this._wrapR = enums.WRAP_REPEAT;
this._format = enums.TEXTURE_FMT_RGBA8;
this._target = -1;
this._id = _textureID++;
}
/**
* @method destroy
*/
destroy() {
if (this._glID === _nullWebGLTexture) {
console.error('The texture already destroyed');
return;
}
let gl = this._device._gl;
gl.deleteTexture(this._glID);
this._device._stats.tex -= this.bytes;
this._glID = _nullWebGLTexture;
}
}

View File

@@ -0,0 +1,99 @@
import { enums } from './enums';
class VertexBuffer {
/**
* @constructor
* @param {Device} device
* @param {VertexFormat} format
* @param {USAGE_*} usage
* @param {ArrayBuffer | Uint8Array} data
*/
constructor(device, format, usage, data) {
this._device = device;
this._format = format;
this._usage = usage;
this._bytesPerVertex = this._format._bytes;
this._bytes = data.byteLength;
this._numVertices = this._bytes / this._bytesPerVertex;
this._needExpandDataStore = true;
// update
this._glID = device._gl.createBuffer();
this.update(0, data);
// stats
device._stats.vb += this._bytes;
}
/**
* @method destroy
*/
destroy() {
if (this._glID === -1) {
console.error('The buffer already destroyed');
return;
}
let gl = this._device._gl;
gl.deleteBuffer(this._glID);
this._device._stats.vb -= this.bytes;
this._glID = -1;
}
/**
* @method update
* @param {Number} byteOffset
* @param {ArrayBuffer} data
*/
update(byteOffset, data) {
if (this._glID === -1) {
console.error('The buffer is destroyed');
return;
}
if (data.byteLength === 0) return;
// Need to create new buffer object when bytes exceed
if (byteOffset + data.byteLength > this._bytes) {
if (byteOffset) {
// Lost data between [0, byteOffset] which is need for new buffer
console.error('Failed to update data, bytes exceed.');
return;
}
else {
this._needExpandDataStore = true;
this._bytes = byteOffset + data.byteLength;
this._numVertices = this._bytes / this._bytesPerVertex;
}
}
let gl = this._device._gl;
let glUsage = this._usage;
gl.bindBuffer(gl.ARRAY_BUFFER, this._glID);
if (this._needExpandDataStore) {
gl.bufferData(gl.ARRAY_BUFFER, data, glUsage);
this._needExpandDataStore = false;
}
else {
gl.bufferSubData(gl.ARRAY_BUFFER, byteOffset, data);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
get count () {
return this._numVertices;
}
getFormat (name) {
return this._format.element(name);
}
setUsage (usage) {
this._usage = usage;
}
}
export default VertexBuffer;

View File

@@ -0,0 +1,70 @@
import { attrTypeBytes } from './enums';
import murmurhash2 from '../murmurhash2_gc';
// ====================
// exports
// ====================
export default class VertexFormat {
/**
* @constructor
* @param {Array} infos
*
* @example
* let vertexFmt = new VertexFormat([
* { name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
* { name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
* { name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_FLOAT32, num: 4, normalize: true },
* ])
*/
constructor(infos) {
this._attr2el = {};
this._elements = [];
this._bytes = 0;
let hash = "";
for (let i = 0, len = infos.length; i < len; ++i) {
let info = infos[i];
let el = {
name: info.name,
offset: this._bytes,
stride: 0,
stream: -1,
type: info.type,
num: info.num,
normalize: (info.normalize === undefined) ? false : info.normalize,
bytes: info.num * attrTypeBytes(info.type),
};
this._attr2el[el.name] = el;
this._elements.push(el);
this._bytes += el.bytes;
hash += `${el.name}:${el.num}:${el.type}:${el.normalize}`;
}
for (let i = 0, len = this._elements.length; i < len; ++i) {
let el = this._elements[i];
el.stride = this._bytes;
}
this._hash = murmurhash2(hash, 666);
}
/**
* @method element
* @param {string} attrName
*/
element(attrName) {
return this._attr2el[attrName];
}
/**
* @method getHash
*/
getHash () {
return this._hash;
}
}