mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-10-09 11:05:24 +00:00
初始化
This commit is contained in:
415
engine/cocos2d/renderer/core/base-renderer.js
Normal file
415
engine/cocos2d/renderer/core/base-renderer.js
Normal file
@@ -0,0 +1,415 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { RecyclePool } from '../memop';
|
||||
import enums from '../enums';
|
||||
import { Vec2, Vec4, Mat3, Mat4, Color, Vec3 } from '../../core/value-types';
|
||||
import ProgramLib from './program-lib';
|
||||
import View from './view';
|
||||
import gfx from '../gfx';
|
||||
|
||||
let _m4_tmp = new Mat4();
|
||||
|
||||
let _stageInfos = new RecyclePool(() => {
|
||||
return {
|
||||
stage: null,
|
||||
items: null,
|
||||
};
|
||||
}, 8);
|
||||
|
||||
let _float2_pool = new RecyclePool(() => {
|
||||
return new Float32Array(2);
|
||||
}, 8);
|
||||
|
||||
let _float3_pool = new RecyclePool(() => {
|
||||
return new Float32Array(3);
|
||||
}, 8);
|
||||
|
||||
let _float4_pool = new RecyclePool(() => {
|
||||
return new Float32Array(4);
|
||||
}, 8);
|
||||
|
||||
let _float9_pool = new RecyclePool(() => {
|
||||
return new Float32Array(9);
|
||||
}, 8);
|
||||
|
||||
let _float16_pool = new RecyclePool(() => {
|
||||
return new Float32Array(16);
|
||||
}, 8);
|
||||
|
||||
let _float64_pool = new RecyclePool(() => {
|
||||
return new Float32Array(64);
|
||||
}, 8);
|
||||
|
||||
let _int2_pool = new RecyclePool(() => {
|
||||
return new Int32Array(2);
|
||||
}, 8);
|
||||
|
||||
let _int3_pool = new RecyclePool(() => {
|
||||
return new Int32Array(3);
|
||||
}, 8);
|
||||
|
||||
let _int4_pool = new RecyclePool(() => {
|
||||
return new Int32Array(4);
|
||||
}, 8);
|
||||
|
||||
let _int64_pool = new RecyclePool(() => {
|
||||
return new Int32Array(64);
|
||||
}, 8);
|
||||
|
||||
export default class Base {
|
||||
/**
|
||||
* @param {gfx.Device} device
|
||||
* @param {Object} opts
|
||||
* @param {gfx.Texture2D} opts.defaultTexture
|
||||
* @param {gfx.TextureCube} opts.defaultTextureCube
|
||||
*/
|
||||
constructor (device, opts) {
|
||||
this._device = device;
|
||||
this._programLib = new ProgramLib(device);
|
||||
this._opts = opts;
|
||||
this._type2defaultValue = {
|
||||
[enums.PARAM_INT]: 0,
|
||||
[enums.PARAM_INT2]: new Vec2(0, 0),
|
||||
[enums.PARAM_INT3]: new Vec3(0, 0, 0),
|
||||
[enums.PARAM_INT4]: new Vec4(0, 0, 0, 0),
|
||||
[enums.PARAM_FLOAT]: 0.0,
|
||||
[enums.PARAM_FLOAT2]: new Vec2(0, 0),
|
||||
[enums.PARAM_FLOAT3]: new Vec3(0, 0, 0),
|
||||
[enums.PARAM_FLOAT4]: new Vec4(0, 0, 0, 0),
|
||||
[enums.PARAM_COLOR4]: new Color(0, 0, 0, 1),
|
||||
[enums.PARAM_MAT3]: new Mat3(),
|
||||
[enums.PARAM_MAT4]: new Mat4(),
|
||||
[enums.PARAM_TEXTURE_2D]: opts.defaultTexture,
|
||||
[enums.PARAM_TEXTURE_CUBE]: opts.defaultTextureCube,
|
||||
};
|
||||
this._stage2fn = {};
|
||||
this._usedTextureUnits = 0;
|
||||
|
||||
this._viewPools = new RecyclePool(() => {
|
||||
return new View();
|
||||
}, 8);
|
||||
|
||||
this._drawItemsPools = new RecyclePool(() => {
|
||||
return {
|
||||
model: null,
|
||||
node: null,
|
||||
ia: null,
|
||||
effect: null,
|
||||
defines: null,
|
||||
uniforms: null
|
||||
};
|
||||
}, 100);
|
||||
|
||||
this._stageItemsPools = new RecyclePool(() => {
|
||||
return new RecyclePool(() => {
|
||||
return {
|
||||
model: null,
|
||||
node: null,
|
||||
ia: null,
|
||||
effect: null,
|
||||
defines: null,
|
||||
passes: [],
|
||||
sortKey: -1,
|
||||
uniforms: null
|
||||
};
|
||||
}, 100);
|
||||
}, 16);
|
||||
|
||||
this._definesChanged = false;
|
||||
}
|
||||
|
||||
_resetTextuerUnit () {
|
||||
this._usedTextureUnits = 0;
|
||||
}
|
||||
|
||||
_allocTextureUnit () {
|
||||
const device = this._device;
|
||||
|
||||
let unit = this._usedTextureUnits;
|
||||
if (unit >= device._caps.maxTextureUnits) {
|
||||
console.warn(`Trying to use ${unit} texture units while this GPU supports only ${device._caps.maxTextureUnits}`);
|
||||
}
|
||||
|
||||
this._usedTextureUnits += 1;
|
||||
return unit;
|
||||
}
|
||||
|
||||
_registerStage (name, fn) {
|
||||
this._stage2fn[name] = fn;
|
||||
}
|
||||
|
||||
clear () {
|
||||
this._programLib.clear();
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset () {
|
||||
this._viewPools.reset();
|
||||
this._stageItemsPools.reset();
|
||||
|
||||
this._definesChanged = false;
|
||||
}
|
||||
|
||||
_requestView () {
|
||||
return this._viewPools.add();
|
||||
}
|
||||
|
||||
_render (view, scene) {
|
||||
const device = this._device;
|
||||
|
||||
// setup framebuffer
|
||||
device.setFrameBuffer(view._framebuffer);
|
||||
|
||||
// setup viewport
|
||||
device.setViewport(
|
||||
view._rect.x,
|
||||
view._rect.y,
|
||||
view._rect.w,
|
||||
view._rect.h
|
||||
);
|
||||
|
||||
// setup clear
|
||||
let clearOpts = {};
|
||||
if (view._clearFlags & enums.CLEAR_COLOR) {
|
||||
clearOpts.color = Vec4.toArray([], view._color);
|
||||
}
|
||||
if (view._clearFlags & enums.CLEAR_DEPTH) {
|
||||
clearOpts.depth = view._depth;
|
||||
}
|
||||
if (view._clearFlags & enums.CLEAR_STENCIL) {
|
||||
clearOpts.stencil = view._stencil;
|
||||
}
|
||||
device.clear(clearOpts);
|
||||
|
||||
// get all draw items
|
||||
this._drawItemsPools.reset();
|
||||
|
||||
for (let i = 0; i < scene._models.length; ++i) {
|
||||
let model = scene._models.data[i];
|
||||
|
||||
// filter model by view
|
||||
if ((model._cullingMask & view._cullingMask) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let drawItem = this._drawItemsPools.add();
|
||||
model.extractDrawItem(drawItem);
|
||||
}
|
||||
|
||||
// dispatch draw items to different stage
|
||||
_stageInfos.reset();
|
||||
|
||||
for (let i = 0; i < view._stages.length; ++i) {
|
||||
let stage = view._stages[i];
|
||||
let stageItems = this._stageItemsPools.add();
|
||||
stageItems.reset();
|
||||
|
||||
for (let j = 0; j < this._drawItemsPools.length; ++j) {
|
||||
let drawItem = this._drawItemsPools.data[j];
|
||||
let passes = drawItem.effect.stagePasses[stage];
|
||||
if (!passes || passes.length === 0) continue;
|
||||
|
||||
let stageItem = stageItems.add();
|
||||
stageItem.passes = passes;
|
||||
stageItem.model = drawItem.model;
|
||||
stageItem.node = drawItem.node;
|
||||
stageItem.ia = drawItem.ia;
|
||||
stageItem.effect = drawItem.effect;
|
||||
stageItem.defines = drawItem.defines;
|
||||
stageItem.sortKey = -1;
|
||||
stageItem.uniforms = drawItem.uniforms;
|
||||
}
|
||||
|
||||
let stageInfo = _stageInfos.add();
|
||||
stageInfo.stage = stage;
|
||||
stageInfo.items = stageItems;
|
||||
}
|
||||
|
||||
// render stages
|
||||
for (let i = 0; i < _stageInfos.length; ++i) {
|
||||
let info = _stageInfos.data[i];
|
||||
let fn = this._stage2fn[info.stage];
|
||||
fn(view, info.items);
|
||||
}
|
||||
}
|
||||
|
||||
_setProperty (prop) {
|
||||
const device = this._device;
|
||||
let param = prop.value;
|
||||
|
||||
if (param === undefined) {
|
||||
param = prop.val;
|
||||
}
|
||||
|
||||
if (param === undefined) {
|
||||
param = this._type2defaultValue[prop.type];
|
||||
}
|
||||
|
||||
if (param === undefined) {
|
||||
console.warn(`Failed to set technique property ${prop.name}, value not found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
prop.type === enums.PARAM_TEXTURE_2D ||
|
||||
prop.type === enums.PARAM_TEXTURE_CUBE
|
||||
) {
|
||||
if (Array.isArray(param)) {
|
||||
if (param.length > prop.count) {
|
||||
console.error(`Failed to set property [${prop.name}] : The length of texture array [${param.length}] is bigger than [${prop.count}].`);
|
||||
return;
|
||||
}
|
||||
let slots = _int64_pool.add();
|
||||
for (let index = 0; index < param.length; ++index) {
|
||||
slots[index] = this._allocTextureUnit();
|
||||
}
|
||||
device.setTextureArray(prop.name, param, slots);
|
||||
} else {
|
||||
device.setTexture(prop.name, param, this._allocTextureUnit());
|
||||
}
|
||||
} else {
|
||||
if (prop.directly) {
|
||||
device.setUniformDirectly(prop.name, param);
|
||||
}
|
||||
else {
|
||||
device.setUniform(prop.name, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_draw (item) {
|
||||
const device = this._device;
|
||||
const programLib = this._programLib;
|
||||
const { node, ia, passes, effect } = item;
|
||||
|
||||
// reset the pool
|
||||
// NOTE: we can use drawCounter optimize this
|
||||
// TODO: should be configurable
|
||||
_float2_pool.reset();
|
||||
_float3_pool.reset();
|
||||
_float4_pool.reset();
|
||||
_float9_pool.reset();
|
||||
_float16_pool.reset();
|
||||
_float64_pool.reset();
|
||||
_int2_pool.reset();
|
||||
_int3_pool.reset();
|
||||
_int4_pool.reset();
|
||||
_int64_pool.reset();
|
||||
|
||||
// set common uniforms
|
||||
// TODO: try commit this depends on effect
|
||||
// {
|
||||
node.getWorldMatrix(_m4_tmp);
|
||||
device.setUniform('cc_matWorld', Mat4.toArray(_float16_pool.add(), _m4_tmp));
|
||||
|
||||
// let wq = node.getWorldRotation(cc.quat());
|
||||
Mat4.invert(_m4_tmp, _m4_tmp);
|
||||
Mat4.transpose(_m4_tmp, _m4_tmp);
|
||||
device.setUniform('cc_matWorldIT', Mat4.toArray(_float16_pool.add(), _m4_tmp));
|
||||
// }
|
||||
|
||||
let defines = this._defines;
|
||||
|
||||
// for each pass
|
||||
for (let i = 0; i < passes.length; ++i) {
|
||||
let pass = passes[i];
|
||||
|
||||
if (this._definesChanged) {
|
||||
pass._programKey = null;
|
||||
}
|
||||
|
||||
let count = ia.count;
|
||||
|
||||
// set vertex buffer
|
||||
if (ia._vertexBuffer) {
|
||||
device.setVertexBuffer(0, ia._vertexBuffer);
|
||||
}
|
||||
|
||||
// set index buffer
|
||||
if (ia._indexBuffer) {
|
||||
device.setIndexBuffer(ia._indexBuffer);
|
||||
}
|
||||
|
||||
// set primitive type
|
||||
device.setPrimitiveType(ia._primitiveType);
|
||||
|
||||
// set program
|
||||
Object.setPrototypeOf(defines, pass._defines);
|
||||
|
||||
let program = programLib.getProgram(pass, defines, effect.name);
|
||||
device.setProgram(program);
|
||||
|
||||
let uniforms = program._uniforms;
|
||||
let variants = pass._properties;
|
||||
for (let j = 0; j < uniforms.length; j++) {
|
||||
let prop = variants[uniforms[j].name];
|
||||
if (prop !== undefined)
|
||||
this._setProperty(prop);
|
||||
}
|
||||
|
||||
|
||||
// cull mode
|
||||
device.setCullMode(pass._cullMode);
|
||||
|
||||
// blend
|
||||
if (pass._blend) {
|
||||
device.enableBlend();
|
||||
device.setBlendFuncSep(
|
||||
pass._blendSrc,
|
||||
pass._blendDst,
|
||||
pass._blendSrcAlpha,
|
||||
pass._blendDstAlpha
|
||||
);
|
||||
device.setBlendEqSep(
|
||||
pass._blendEq,
|
||||
pass._blendAlphaEq
|
||||
);
|
||||
device.setBlendColor32(pass._blendColor);
|
||||
}
|
||||
|
||||
// depth test & write
|
||||
if (pass._depthTest) {
|
||||
device.enableDepthTest();
|
||||
device.setDepthFunc(pass._depthFunc);
|
||||
}
|
||||
if (pass._depthWrite) {
|
||||
device.enableDepthWrite();
|
||||
}
|
||||
|
||||
// stencil
|
||||
device.setStencilTest(pass._stencilTest);
|
||||
if (pass._stencilTest === gfx.STENCIL_ENABLE) {
|
||||
// front
|
||||
device.setStencilFuncFront(
|
||||
pass._stencilFuncFront,
|
||||
pass._stencilRefFront,
|
||||
pass._stencilMaskFront
|
||||
);
|
||||
device.setStencilOpFront(
|
||||
pass._stencilFailOpFront,
|
||||
pass._stencilZFailOpFront,
|
||||
pass._stencilZPassOpFront,
|
||||
pass._stencilWriteMaskFront
|
||||
);
|
||||
|
||||
// back
|
||||
device.setStencilFuncBack(
|
||||
pass._stencilFuncBack,
|
||||
pass._stencilRefBack,
|
||||
pass._stencilMaskBack
|
||||
);
|
||||
device.setStencilOpBack(
|
||||
pass._stencilFailOpBack,
|
||||
pass._stencilZFailOpBack,
|
||||
pass._stencilZPassOpBack,
|
||||
pass._stencilWriteMaskBack
|
||||
);
|
||||
}
|
||||
|
||||
// draw pass
|
||||
device.draw(ia._start, count);
|
||||
|
||||
this._resetTextuerUnit();
|
||||
}
|
||||
}
|
||||
}
|
13
engine/cocos2d/renderer/core/constants.js
Normal file
13
engine/cocos2d/renderer/core/constants.js
Normal file
@@ -0,0 +1,13 @@
|
||||
let RenderQueue = {
|
||||
OPAQUE: 0,
|
||||
TRANSPARENT: 1,
|
||||
OVERLAY: 2
|
||||
};
|
||||
|
||||
let PassStage = {
|
||||
DEFAULT: 1,
|
||||
FORWARD: 2,
|
||||
SHADOWCAST: 4
|
||||
};
|
||||
|
||||
export { RenderQueue, PassStage };
|
35
engine/cocos2d/renderer/core/input-assembler.js
Normal file
35
engine/cocos2d/renderer/core/input-assembler.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import gfx from '../gfx';
|
||||
|
||||
export default class InputAssembler {
|
||||
constructor(vb, ib, pt = gfx.PT_TRIANGLES) {
|
||||
this._vertexBuffer = vb;
|
||||
this._indexBuffer = ib;
|
||||
this._primitiveType = pt;
|
||||
this._start = 0;
|
||||
this._count = -1;
|
||||
|
||||
// TODO: instancing data
|
||||
// this._stream = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {Number} count The number of indices or vertices to dispatch in the draw call.
|
||||
*/
|
||||
get count() {
|
||||
if (this._count !== -1) {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
if (this._indexBuffer) {
|
||||
return this._indexBuffer.count;
|
||||
}
|
||||
|
||||
if (this._vertexBuffer) {
|
||||
return this._vertexBuffer.count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
246
engine/cocos2d/renderer/core/pass.js
Normal file
246
engine/cocos2d/renderer/core/pass.js
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import gfx from '../gfx';
|
||||
import enums from '../enums';
|
||||
import ValueType from '../../core/value-types/value-type';
|
||||
|
||||
export default class Pass {
|
||||
constructor (name, detailName, programName, stage, properties = {}, defines = {}) {
|
||||
this._name = name;
|
||||
this._detailName = detailName;
|
||||
this._programName = programName;
|
||||
this._programKey = null;
|
||||
this._stage = stage;
|
||||
this._properties = properties;
|
||||
this._defines = defines;
|
||||
|
||||
this._propertyNames = Object.keys(properties)
|
||||
this._defineNames = Object.keys(defines)
|
||||
|
||||
// cullmode
|
||||
this._cullMode = gfx.CULL_BACK;
|
||||
|
||||
// blending
|
||||
this._blend = false;
|
||||
this._blendEq = gfx.BLEND_FUNC_ADD;
|
||||
this._blendAlphaEq = gfx.BLEND_FUNC_ADD;
|
||||
this._blendSrc = gfx.BLEND_SRC_ALPHA;
|
||||
this._blendDst = gfx.BLEND_ONE_MINUS_SRC_ALPHA;
|
||||
this._blendSrcAlpha = gfx.BLEND_SRC_ALPHA;
|
||||
this._blendDstAlpha = gfx.BLEND_ONE_MINUS_SRC_ALPHA;
|
||||
this._blendColor = 0xffffffff;
|
||||
|
||||
// depth
|
||||
this._depthTest = false;
|
||||
this._depthWrite = false;
|
||||
this._depthFunc = gfx.DS_FUNC_LESS,
|
||||
|
||||
// stencil
|
||||
this._stencilTest = gfx.STENCIL_INHERIT;
|
||||
|
||||
// front
|
||||
this._stencilFuncFront = gfx.DS_FUNC_ALWAYS;
|
||||
this._stencilRefFront = 0;
|
||||
this._stencilMaskFront = 0xff;
|
||||
this._stencilFailOpFront = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilZFailOpFront = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilZPassOpFront = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilWriteMaskFront = 0xff;
|
||||
// back
|
||||
this._stencilFuncBack = gfx.DS_FUNC_ALWAYS;
|
||||
this._stencilRefBack = 0;
|
||||
this._stencilMaskBack = 0xff;
|
||||
this._stencilFailOpBack = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilZFailOpBack = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilZPassOpBack = gfx.STENCIL_OP_KEEP;
|
||||
this._stencilWriteMaskBack = 0xff;
|
||||
}
|
||||
|
||||
setCullMode (cullMode = gfx.CULL_BACK) {
|
||||
this._cullMode = cullMode;
|
||||
}
|
||||
|
||||
setBlend (
|
||||
enabled = false,
|
||||
blendEq = gfx.BLEND_FUNC_ADD,
|
||||
blendSrc = gfx.BLEND_SRC_ALPHA,
|
||||
blendDst = gfx.BLEND_ONE_MINUS_SRC_ALPHA,
|
||||
blendAlphaEq = gfx.BLEND_FUNC_ADD,
|
||||
blendSrcAlpha = gfx.BLEND_SRC_ALPHA,
|
||||
blendDstAlpha = gfx.BLEND_ONE_MINUS_SRC_ALPHA,
|
||||
blendColor = 0xffffffff
|
||||
) {
|
||||
this._blend = enabled;
|
||||
this._blendEq = blendEq;
|
||||
this._blendSrc = blendSrc;
|
||||
this._blendDst = blendDst;
|
||||
this._blendAlphaEq = blendAlphaEq;
|
||||
this._blendSrcAlpha = blendSrcAlpha;
|
||||
this._blendDstAlpha = blendDstAlpha;
|
||||
this._blendColor = blendColor;
|
||||
}
|
||||
|
||||
setDepth (
|
||||
depthTest = false,
|
||||
depthWrite = false,
|
||||
depthFunc = gfx.DS_FUNC_LESS
|
||||
) {
|
||||
this._depthTest = depthTest;
|
||||
this._depthWrite = depthWrite;
|
||||
this._depthFunc = depthFunc;
|
||||
}
|
||||
|
||||
setStencilFront (
|
||||
enabled = gfx.STENCIL_INHERIT,
|
||||
stencilFunc = gfx.DS_FUNC_ALWAYS,
|
||||
stencilRef = 0,
|
||||
stencilMask = 0xff,
|
||||
stencilFailOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilZFailOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilZPassOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilWriteMask = 0xff
|
||||
) {
|
||||
this._stencilTest = enabled;
|
||||
this._stencilFuncFront = stencilFunc;
|
||||
this._stencilRefFront = stencilRef;
|
||||
this._stencilMaskFront = stencilMask;
|
||||
this._stencilFailOpFront = stencilFailOp;
|
||||
this._stencilZFailOpFront = stencilZFailOp;
|
||||
this._stencilZPassOpFront = stencilZPassOp;
|
||||
this._stencilWriteMaskFront = stencilWriteMask;
|
||||
}
|
||||
|
||||
setStencilEnabled (stencilTest = gfx.STENCIL_INHERIT) {
|
||||
this._stencilTest = stencilTest;
|
||||
}
|
||||
|
||||
setStencilBack (
|
||||
stencilTest = gfx.STENCIL_INHERIT,
|
||||
stencilFunc = gfx.DS_FUNC_ALWAYS,
|
||||
stencilRef = 0,
|
||||
stencilMask = 0xff,
|
||||
stencilFailOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilZFailOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilZPassOp = gfx.STENCIL_OP_KEEP,
|
||||
stencilWriteMask = 0xff
|
||||
) {
|
||||
this._stencilTest = stencilTest;
|
||||
this._stencilFuncBack = stencilFunc;
|
||||
this._stencilRefBack = stencilRef;
|
||||
this._stencilMaskBack = stencilMask;
|
||||
this._stencilFailOpBack = stencilFailOp;
|
||||
this._stencilZFailOpBack = stencilZFailOp;
|
||||
this._stencilZPassOpBack = stencilZPassOp;
|
||||
this._stencilWriteMaskBack = stencilWriteMask;
|
||||
}
|
||||
|
||||
setStage (stage) {
|
||||
this._stage = stage;
|
||||
}
|
||||
|
||||
setProperties (properties) {
|
||||
this._properties = properties;
|
||||
}
|
||||
|
||||
getProperty (name) {
|
||||
if (!this._properties[name]) {
|
||||
return;
|
||||
}
|
||||
return this._properties[name].value;
|
||||
}
|
||||
|
||||
setProperty (name, value, directly) {
|
||||
let prop = this._properties[name];
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prop.directly = directly;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
let array = prop.value;
|
||||
if (array.length !== value.length) {
|
||||
cc.warnID(9105, this._name, name);
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
array[i] = value[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value && !ArrayBuffer.isView(value)) {
|
||||
if (prop.type === enums.PARAM_TEXTURE_2D) {
|
||||
prop.value = value.getImpl();
|
||||
}
|
||||
else if (value instanceof ValueType) {
|
||||
value.constructor.toArray(prop.value, value);
|
||||
}
|
||||
else {
|
||||
if (typeof value === 'object') {
|
||||
cc.warnID(9106, this._name, name);
|
||||
}
|
||||
prop.value = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prop.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getDefine (name) {
|
||||
return this._defines[name];
|
||||
}
|
||||
|
||||
define (name, value, force) {
|
||||
let oldValue = this._defines[name];
|
||||
|
||||
if (!force && oldValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldValue !== value) {
|
||||
this._defines[name] = value;
|
||||
this._programKey = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
clone () {
|
||||
let pass = new Pass(this._programName);
|
||||
Object.assign(pass, this);
|
||||
|
||||
let newProperties = {};
|
||||
let properties = this._properties;
|
||||
for (let name in properties) {
|
||||
let prop = properties[name];
|
||||
let newProp = newProperties[name] = {};
|
||||
|
||||
let value = prop.value;
|
||||
if (Array.isArray(value)) {
|
||||
newProp.value = value.concat();
|
||||
}
|
||||
else if (ArrayBuffer.isView(value)) {
|
||||
newProp.value = new value.__proto__.constructor(value);
|
||||
}
|
||||
else {
|
||||
newProp.value = value;
|
||||
}
|
||||
|
||||
for (let name in prop) {
|
||||
if (name === 'value') continue;
|
||||
newProp[name] = prop[name];
|
||||
}
|
||||
}
|
||||
|
||||
pass._properties = newProperties;
|
||||
pass._defines = Object.assign({}, this._defines);
|
||||
pass._propertyNames = this._propertyNames;
|
||||
pass._defineNames = this._defineNames;
|
||||
|
||||
return pass;
|
||||
}
|
||||
}
|
262
engine/cocos2d/renderer/core/program-lib.js
Normal file
262
engine/cocos2d/renderer/core/program-lib.js
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import gfx from '../gfx';
|
||||
|
||||
let _shdID = 0;
|
||||
|
||||
function _generateDefines(tmpDefines, defines) {
|
||||
let results = [];
|
||||
for (let i = 0; i < tmpDefines.length; i++) {
|
||||
let name = tmpDefines[i].name;
|
||||
let value = defines[name];
|
||||
if (typeof value !== 'number') {
|
||||
value = value ? 1 : 0;
|
||||
}
|
||||
results.push(`#define ${name} ${value}`);
|
||||
}
|
||||
return results.join('\n') + '\n';
|
||||
}
|
||||
|
||||
function _replaceMacroNums(string, tmpDefines, defines) {
|
||||
let tmp = string;
|
||||
|
||||
for (let i = 0; i < tmpDefines.length; i++) {
|
||||
let name = tmpDefines[i].name;
|
||||
let value = defines[name];
|
||||
if (Number.isInteger(value)) {
|
||||
let reg = new RegExp(name, 'g');
|
||||
tmp = tmp.replace(reg, value);
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
function _unrollLoops(string) {
|
||||
let pattern = /#pragma for (\w+) in range\(\s*(\d+)\s*,\s*(\d+)\s*\)([\s\S]+?)#pragma endFor/g;
|
||||
function replace(match, index, begin, end, snippet) {
|
||||
let unroll = '';
|
||||
let parsedBegin = parseInt(begin);
|
||||
let parsedEnd = parseInt(end);
|
||||
if (parsedBegin.isNaN || parsedEnd.isNaN) {
|
||||
console.error('Unroll For Loops Error: begin and end of range must be an int num.');
|
||||
}
|
||||
for (let i = parsedBegin; i < parsedEnd; ++i) {
|
||||
unroll += snippet.replace(new RegExp(`{${index}}`, 'g'), i);
|
||||
}
|
||||
return unroll;
|
||||
}
|
||||
return string.replace(pattern, replace);
|
||||
}
|
||||
|
||||
function _replaceHighp(string) {
|
||||
return string.replace(/\bhighp\b/g, 'mediump');
|
||||
}
|
||||
|
||||
export default class ProgramLib {
|
||||
/**
|
||||
* @param {gfx.Device} device
|
||||
*/
|
||||
constructor(device) {
|
||||
this._device = device;
|
||||
|
||||
// register templates
|
||||
this._templates = {};
|
||||
this._cache = {};
|
||||
|
||||
this._checkPrecision();
|
||||
}
|
||||
|
||||
clear () {
|
||||
this._templates = {};
|
||||
this._cache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} vert
|
||||
* @param {string} frag
|
||||
* @param {Object[]} defines
|
||||
*
|
||||
* @example:
|
||||
* // this object is auto-generated from your actual shaders
|
||||
* let program = {
|
||||
* name: 'foobar',
|
||||
* vert: vertTmpl,
|
||||
* frag: fragTmpl,
|
||||
* defines: [
|
||||
* { name: 'shadow', type: 'boolean' },
|
||||
* { name: 'lightCount', type: 'number', min: 1, max: 4 }
|
||||
* ],
|
||||
* attributes: [{ name: 'a_position', type: 'vec3' }],
|
||||
* uniforms: [{ name: 'color', type: 'vec4' }],
|
||||
* extensions: ['GL_OES_standard_derivatives'],
|
||||
* };
|
||||
* programLib.define(program);
|
||||
*/
|
||||
define(prog) {
|
||||
let { name, defines, glsl1 } = prog;
|
||||
let { vert, frag } = glsl1 || prog;
|
||||
if (this._templates[name]) {
|
||||
// console.warn(`Failed to define shader ${name}: already exists.`);
|
||||
return;
|
||||
}
|
||||
|
||||
let id = ++_shdID;
|
||||
|
||||
// calculate option mask offset
|
||||
let offset = 0;
|
||||
for (let i = 0; i < defines.length; ++i) {
|
||||
let def = defines[i];
|
||||
let cnt = 1;
|
||||
|
||||
if (def.type === 'number') {
|
||||
let range = def.range || [];
|
||||
def.min = range[0] || 0;
|
||||
def.max = range[1] || 4;
|
||||
cnt = Math.ceil(Math.log2(def.max - def.min));
|
||||
|
||||
def._map = function (value) {
|
||||
return (value - this.min) << this._offset;
|
||||
}.bind(def);
|
||||
} else { // boolean
|
||||
def._map = function (value) {
|
||||
if (value) {
|
||||
return 1 << this._offset;
|
||||
}
|
||||
return 0;
|
||||
}.bind(def);
|
||||
}
|
||||
|
||||
def._offset = offset;
|
||||
offset += cnt;
|
||||
}
|
||||
|
||||
let uniforms = prog.uniforms || [];
|
||||
|
||||
if (prog.samplers) {
|
||||
for (let i = 0; i < prog.samplers.length; i++) {
|
||||
uniforms.push(prog.samplers[i])
|
||||
}
|
||||
}
|
||||
if (prog.blocks) {
|
||||
for (let i = 0; i < prog.blocks.length; i++) {
|
||||
let defines = prog.blocks[i].defines;
|
||||
let members = prog.blocks[i].members;
|
||||
for (let j = 0; j < members.length; j++) {
|
||||
uniforms.push({
|
||||
defines,
|
||||
name: members[j].name,
|
||||
type: members[j].type,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// store it
|
||||
this._templates[name] = {
|
||||
id,
|
||||
name,
|
||||
vert,
|
||||
frag,
|
||||
defines,
|
||||
attributes: prog.attributes,
|
||||
uniforms,
|
||||
extensions: prog.extensions
|
||||
};
|
||||
}
|
||||
|
||||
getTemplate(name) {
|
||||
return this._templates[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this library has the specified program?
|
||||
* @param {string} name
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasProgram(name) {
|
||||
return this._templates[name] !== undefined;
|
||||
}
|
||||
|
||||
getKey(name, defines) {
|
||||
let tmpl = this._templates[name];
|
||||
let key = 0;
|
||||
for (let i = 0; i < tmpl.defines.length; ++i) {
|
||||
let tmplDefs = tmpl.defines[i];
|
||||
|
||||
let value = defines[tmplDefs.name];
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
key |= tmplDefs._map(value);
|
||||
}
|
||||
|
||||
// return key << 8 | tmpl.id;
|
||||
// key number maybe bigger than 32 bit, need use string to store value.
|
||||
return tmpl.id + ':' + key;
|
||||
}
|
||||
|
||||
getProgram(pass, defines, errPrefix) {
|
||||
let key = pass._programKey = pass._programKey || this.getKey(pass._programName, defines);
|
||||
let program = this._cache[key];
|
||||
if (program) {
|
||||
return program;
|
||||
}
|
||||
|
||||
// get template
|
||||
let tmpl = this._templates[pass._programName];
|
||||
let customDef = _generateDefines(tmpl.defines, defines);
|
||||
let vert = _replaceMacroNums(tmpl.vert, tmpl.defines, defines);
|
||||
vert = customDef + _unrollLoops(vert);
|
||||
if (!this._highpSupported) {
|
||||
vert = _replaceHighp(vert);
|
||||
}
|
||||
|
||||
let frag = _replaceMacroNums(tmpl.frag, tmpl.defines, defines);
|
||||
frag = customDef + _unrollLoops(frag);
|
||||
if (!this._highpSupported) {
|
||||
frag = _replaceHighp(frag);
|
||||
}
|
||||
|
||||
program = new gfx.Program(this._device, {
|
||||
vert,
|
||||
frag
|
||||
});
|
||||
let errors = program.link();
|
||||
if (errors) {
|
||||
let vertLines = vert.split('\n');
|
||||
let fragLines = frag.split('\n');
|
||||
let defineLength = tmpl.defines.length;
|
||||
errors.forEach(err => {
|
||||
let line = err.line - 1;
|
||||
let originLine = err.line - defineLength;
|
||||
|
||||
let lines = err.type === 'vs' ? vertLines : fragLines;
|
||||
// let source = ` ${lines[line-1]}\n>${lines[line]}\n ${lines[line+1]}`;
|
||||
let source = lines[line];
|
||||
|
||||
let info = err.info || `Failed to compile ${err.type} ${err.fileID} (ln ${originLine}): \n ${err.message}: \n ${source}`;
|
||||
cc.error(`${errPrefix} : ${info}`);
|
||||
})
|
||||
}
|
||||
this._cache[key] = program;
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
_checkPrecision () {
|
||||
let gl = this._device._gl;
|
||||
let highpSupported = false;
|
||||
if (gl.getShaderPrecisionFormat) {
|
||||
let vertHighp = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT);
|
||||
let fragHighp = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
|
||||
highpSupported = (vertHighp && vertHighp.precision > 0) &&
|
||||
(fragHighp && fragHighp.precision > 0);
|
||||
}
|
||||
if (!highpSupported) {
|
||||
cc.warnID(9102);
|
||||
}
|
||||
this._highpSupported = highpSupported;
|
||||
}
|
||||
}
|
24
engine/cocos2d/renderer/core/technique.js
Normal file
24
engine/cocos2d/renderer/core/technique.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
export default class Technique {
|
||||
constructor(name, passes) {
|
||||
this._name = name;
|
||||
this._passes = passes;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get passes() {
|
||||
return this._passes;
|
||||
}
|
||||
|
||||
clone () {
|
||||
let passes = [];
|
||||
for (let i = 0; i < this._passes.length; i++) {
|
||||
passes.push(this._passes[i].clone());
|
||||
}
|
||||
return new Technique(this._name, passes);
|
||||
}
|
||||
}
|
80
engine/cocos2d/renderer/core/view.js
Normal file
80
engine/cocos2d/renderer/core/view.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { Vec3, Mat4, Vec4 } from '../../core/value-types';
|
||||
import enums from '../enums';
|
||||
|
||||
let _m4_tmp = new Mat4();
|
||||
let _genID = 0;
|
||||
|
||||
/**
|
||||
* A representation of a single camera view
|
||||
*/
|
||||
export default class View {
|
||||
/**
|
||||
* Setup a default view
|
||||
*/
|
||||
constructor() {
|
||||
this._id = _genID++;
|
||||
|
||||
// priority. the smaller one will be rendered first
|
||||
this._priority = 0;
|
||||
|
||||
// viewport
|
||||
this._rect = {
|
||||
x: 0, y: 0, w: 1, h: 1
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// this._scissor = {
|
||||
// x: 0, y: 0, w: 1, h: 1
|
||||
// };
|
||||
|
||||
// clear options
|
||||
this._color = new Vec4(0.3, 0.3, 0.3, 1);
|
||||
this._depth = 1;
|
||||
this._stencil = 0;
|
||||
this._clearFlags = enums.CLEAR_COLOR | enums.CLEAR_DEPTH;
|
||||
this._clearModel = null;
|
||||
|
||||
// matrix
|
||||
this._matView = cc.mat4();
|
||||
this._matViewInv = cc.mat4();
|
||||
this._matProj = cc.mat4();
|
||||
this._matViewProj = cc.mat4();
|
||||
this._matInvViewProj = cc.mat4();
|
||||
|
||||
// stages & framebuffer
|
||||
this._stages = [];
|
||||
this._cullingByID = false;
|
||||
this._framebuffer = null;
|
||||
|
||||
this._shadowLight = null; // TODO: should not refer light in view.
|
||||
|
||||
this._cullingMask = 0xffffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view's forward direction
|
||||
* @param {Vec3} out the receiving vector
|
||||
* @returns {Vec3} the receiving vector
|
||||
*/
|
||||
getForward(out) {
|
||||
let m = this._matView.m;
|
||||
return Vec3.set(
|
||||
out,
|
||||
-m[2],
|
||||
-m[6],
|
||||
-m[10]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view's observing location
|
||||
* @param {Vec3} out the receiving vector
|
||||
* @returns {Vec3} the receiving vector
|
||||
*/
|
||||
getPosition(out) {
|
||||
Mat4.invert(_m4_tmp, this._matView);
|
||||
return Mat4.getTranslation(out, _m4_tmp);
|
||||
}
|
||||
}
|
50
engine/cocos2d/renderer/enums.js
Normal file
50
engine/cocos2d/renderer/enums.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { typeMap } from './build/mappings'
|
||||
|
||||
export default {
|
||||
// projection
|
||||
PROJ_PERSPECTIVE: 0,
|
||||
PROJ_ORTHO: 1,
|
||||
|
||||
// lights
|
||||
LIGHT_DIRECTIONAL: 0,
|
||||
LIGHT_POINT: 1,
|
||||
LIGHT_SPOT: 2,
|
||||
LIGHT_AMBIENT: 3,
|
||||
|
||||
// shadows
|
||||
SHADOW_NONE: 0,
|
||||
SHADOW_HARD: 1,
|
||||
SHADOW_SOFT: 2,
|
||||
|
||||
// parameter type
|
||||
PARAM_INT: typeMap.int,
|
||||
PARAM_INT2: typeMap.ivec2,
|
||||
PARAM_INT3: typeMap.ivec3,
|
||||
PARAM_INT4: typeMap.ivec4,
|
||||
PARAM_FLOAT: typeMap.float,
|
||||
PARAM_FLOAT2: typeMap.vec2,
|
||||
PARAM_FLOAT3: typeMap.vec3,
|
||||
PARAM_FLOAT4: typeMap.vec4,
|
||||
PARAM_MAT2: typeMap.mat2,
|
||||
PARAM_MAT3: typeMap.mat3,
|
||||
PARAM_MAT4: typeMap.mat4,
|
||||
PARAM_TEXTURE_2D: typeMap.sampler2D,
|
||||
PARAM_TEXTURE_CUBE: typeMap.samplerCube,
|
||||
|
||||
// clear flags
|
||||
CLEAR_COLOR: 1,
|
||||
CLEAR_DEPTH: 2,
|
||||
CLEAR_STENCIL: 4,
|
||||
CLEAR_SKYBOX: 8,
|
||||
|
||||
//
|
||||
BUFFER_VIEW_INT8: 0,
|
||||
BUFFER_VIEW_UINT8: 1,
|
||||
BUFFER_VIEW_INT16: 2,
|
||||
BUFFER_VIEW_UINT16: 3,
|
||||
BUFFER_VIEW_INT32: 4,
|
||||
BUFFER_VIEW_UINT32: 5,
|
||||
BUFFER_VIEW_FLOAT32: 6,
|
||||
};
|
1396
engine/cocos2d/renderer/gfx/device.js
Normal file
1396
engine/cocos2d/renderer/gfx/device.js
Normal file
File diff suppressed because it is too large
Load Diff
363
engine/cocos2d/renderer/gfx/enums.js
Normal file
363
engine/cocos2d/renderer/gfx/enums.js
Normal 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;
|
||||
}
|
45
engine/cocos2d/renderer/gfx/frame-buffer.js
Normal file
45
engine/cocos2d/renderer/gfx/frame-buffer.js
Normal 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;
|
||||
}
|
||||
}
|
104
engine/cocos2d/renderer/gfx/index-buffer.js
Normal file
104
engine/cocos2d/renderer/gfx/index-buffer.js
Normal 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;
|
46
engine/cocos2d/renderer/gfx/index.js
Normal file
46
engine/cocos2d/renderer/gfx/index.js
Normal 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;
|
3
engine/cocos2d/renderer/gfx/misc.js
Normal file
3
engine/cocos2d/renderer/gfx/misc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isPow2(v) {
|
||||
return !(v & (v - 1)) && (!!v);
|
||||
}
|
172
engine/cocos2d/renderer/gfx/program.js
Normal file
172
engine/cocos2d/renderer/gfx/program.js
Normal 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;
|
||||
}
|
43
engine/cocos2d/renderer/gfx/render-buffer.js
Normal file
43
engine/cocos2d/renderer/gfx/render-buffer.js
Normal 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;
|
||||
}
|
||||
}
|
133
engine/cocos2d/renderer/gfx/state.js
Normal file
133
engine/cocos2d/renderer/gfx/state.js
Normal 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;
|
||||
}
|
||||
}
|
330
engine/cocos2d/renderer/gfx/texture-2d.js
Normal file
330
engine/cocos2d/renderer/gfx/texture-2d.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
338
engine/cocos2d/renderer/gfx/texture-cube.js
Normal file
338
engine/cocos2d/renderer/gfx/texture-cube.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
56
engine/cocos2d/renderer/gfx/texture.js
Normal file
56
engine/cocos2d/renderer/gfx/texture.js
Normal 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;
|
||||
}
|
||||
}
|
99
engine/cocos2d/renderer/gfx/vertex-buffer.js
Normal file
99
engine/cocos2d/renderer/gfx/vertex-buffer.js
Normal 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;
|
70
engine/cocos2d/renderer/gfx/vertex-format.js
Normal file
70
engine/cocos2d/renderer/gfx/vertex-format.js
Normal 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;
|
||||
}
|
||||
}
|
17
engine/cocos2d/renderer/memop/circular-pool.js
Normal file
17
engine/cocos2d/renderer/memop/circular-pool.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export default class CircularPool {
|
||||
constructor(fn, size) {
|
||||
this._cursor = 0;
|
||||
this._data = new Array(size);
|
||||
|
||||
for (let i = 0; i < size; ++i) {
|
||||
this._data[i] = fn();
|
||||
}
|
||||
}
|
||||
|
||||
request() {
|
||||
let item = this._data[this._cursor];
|
||||
this._cursor = (this._cursor + 1) % this._data.length;
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
73
engine/cocos2d/renderer/memop/fixed-array.js
Normal file
73
engine/cocos2d/renderer/memop/fixed-array.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import sort from './timsort';
|
||||
|
||||
export default class FixedArray {
|
||||
constructor(size) {
|
||||
this._count = 0;
|
||||
this._data = new Array(size);
|
||||
}
|
||||
|
||||
_resize(size) {
|
||||
if (size > this._data.length) {
|
||||
for (let i = this._data.length; i < size; ++i) {
|
||||
this._data[i] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
reset() {
|
||||
for (let i = 0; i < this._count; ++i) {
|
||||
this._data[i] = undefined;
|
||||
}
|
||||
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
push(val) {
|
||||
if (this._count >= this._data.length) {
|
||||
this._resize(this._data.length * 2);
|
||||
}
|
||||
|
||||
this._data[this._count] = val;
|
||||
++this._count;
|
||||
}
|
||||
|
||||
pop() {
|
||||
--this._count;
|
||||
|
||||
if (this._count < 0) {
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
let ret = this._data[this._count];
|
||||
this._data[this._count] = undefined;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fastRemove(idx) {
|
||||
if (idx >= this._count || idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let last = this._count - 1;
|
||||
this._data[idx] = this._data[last];
|
||||
this._data[last] = undefined;
|
||||
this._count -= 1;
|
||||
}
|
||||
|
||||
indexOf(val) {
|
||||
return this._data.indexOf(val);
|
||||
}
|
||||
|
||||
sort(cmp) {
|
||||
return sort(this._data, 0, this._count, cmp);
|
||||
}
|
||||
}
|
6
engine/cocos2d/renderer/memop/index.js
Normal file
6
engine/cocos2d/renderer/memop/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export { default as CircularPool } from './circular-pool';
|
||||
export { default as FixedArray } from './fixed-array';
|
||||
export { default as LinkedArray } from './linked-array';
|
||||
export { default as Pool } from './pool';
|
||||
export { default as RecyclePool } from './recycle-pool';
|
||||
export { default as TypedArrayPool } from './typed-array-pool';
|
82
engine/cocos2d/renderer/memop/linked-array.js
Normal file
82
engine/cocos2d/renderer/memop/linked-array.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import Pool from './pool';
|
||||
|
||||
// NOTE: you must have `_prev` and `_next` field in the object returns by `fn`
|
||||
|
||||
export default class LinkedArray {
|
||||
constructor(fn, size) {
|
||||
this._fn = fn;
|
||||
this._count = 0;
|
||||
this._head = null;
|
||||
this._tail = null;
|
||||
|
||||
this._pool = new Pool(fn, size);
|
||||
}
|
||||
|
||||
get head() {
|
||||
return this._head;
|
||||
}
|
||||
|
||||
get tail() {
|
||||
return this._tail;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
add() {
|
||||
let node = this._pool.alloc();
|
||||
|
||||
if (!this._tail) {
|
||||
this._head = node;
|
||||
} else {
|
||||
this._tail._next = node;
|
||||
node._prev = this._tail;
|
||||
}
|
||||
this._tail = node;
|
||||
this._count += 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
remove(node) {
|
||||
if (node._prev) {
|
||||
node._prev._next = node._next;
|
||||
} else {
|
||||
this._head = node._next;
|
||||
}
|
||||
|
||||
if (node._next) {
|
||||
node._next._prev = node._prev;
|
||||
} else {
|
||||
this._tail = node._prev;
|
||||
}
|
||||
|
||||
node._next = null;
|
||||
node._prev = null;
|
||||
this._pool.free(node);
|
||||
this._count -= 1;
|
||||
}
|
||||
|
||||
forEach(fn, binder) {
|
||||
let cursor = this._head;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (binder) {
|
||||
fn = fn.bind(binder);
|
||||
}
|
||||
|
||||
let idx = 0;
|
||||
let next = cursor;
|
||||
|
||||
while (cursor) {
|
||||
next = cursor._next;
|
||||
fn(cursor, idx, this);
|
||||
|
||||
cursor = next;
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
}
|
187
engine/cocos2d/renderer/memop/optimized-array.js
Normal file
187
engine/cocos2d/renderer/memop/optimized-array.js
Normal file
@@ -0,0 +1,187 @@
|
||||
export class OptimizedArray {
|
||||
/**
|
||||
* Initialize this array with specified capacity.
|
||||
* @param {Number} [size] The size.
|
||||
*/
|
||||
constructor(size = 0) {
|
||||
this._size = size;
|
||||
this._data = new Array(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of this array.
|
||||
* @return {Number}
|
||||
*/
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The underlying Array of this array.
|
||||
* @return {Array}
|
||||
*/
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capacity of this array.
|
||||
*/
|
||||
get capacity() {
|
||||
return this._data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a value to back of this array.
|
||||
* @param {any} value
|
||||
*/
|
||||
push(value) {
|
||||
if (this._size > this._data.length)
|
||||
this._extends(this._data.length);
|
||||
this._data[this._size] = value;
|
||||
++this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element and return it, if exists.
|
||||
*/
|
||||
pop() {
|
||||
if (this._size == 0)
|
||||
return;
|
||||
--this._size;
|
||||
let ret = this._data[this._size];
|
||||
this._data[this._size] = undefined;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all elements.
|
||||
*/
|
||||
clear() {
|
||||
for (let i = 0; i < this._data.length; ++i)
|
||||
this._data[i] = undefined;
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @param {Number} size
|
||||
*/
|
||||
_extends(size) {
|
||||
let finalSize = this._data.length + size;
|
||||
for (let i = this._data.length; i < finalSize; ++i)
|
||||
this._data[i] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class OptimizedValueArray {
|
||||
/**
|
||||
* Initialize this array with specified capacity.
|
||||
* @param {any} ctor The constructor to create the value.
|
||||
* @param {Number} [size] The size.
|
||||
*/
|
||||
constructor(ctor, dtor, size = 0) {
|
||||
this._size = size;
|
||||
this._data = new Array();
|
||||
this._ctor = ctor;
|
||||
this._dtor = dtor;
|
||||
this._extends(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of this array.
|
||||
* @return {Number}
|
||||
*/
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* The underlying Array of this array.
|
||||
* @return {Array}
|
||||
*/
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capacity of this array.
|
||||
*/
|
||||
get capacity() {
|
||||
return this._data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a value to back of this array.
|
||||
*/
|
||||
push() {
|
||||
if (this._size >= this._data.length)
|
||||
this._extends(this._data.length + 1);
|
||||
let retval = this._data[this._size];
|
||||
++this._size;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element, if exists.
|
||||
* Since that element is not erased, so we cannot return it.
|
||||
*/
|
||||
pop() {
|
||||
if (this._size == 0)
|
||||
return;
|
||||
--this._size;
|
||||
this._dtor(this._data[this._size]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all elements.
|
||||
*/
|
||||
clear() {
|
||||
this._size = 0;
|
||||
for (let i = 0; i < this._data.length; ++i)
|
||||
this._dtor(this._data[i]);
|
||||
}
|
||||
|
||||
splice(from, number) {
|
||||
if (number == 0)
|
||||
return;
|
||||
|
||||
if (from >= this.size)
|
||||
return; // throw
|
||||
|
||||
number = Math.min(this.size - from, number);
|
||||
|
||||
let originalSize = this._size;
|
||||
this._size -= number;
|
||||
|
||||
for (let i = 0, moveStart = from + number, moveNumber = originalSize - moveStart; i < moveNumber; ++i) {
|
||||
let temp = this._data[from + i];
|
||||
this._data[from + i] = this._data[moveStart + i];
|
||||
this._data[moveStart + i] = temp;
|
||||
}
|
||||
for (let i = this._size; i != originalSize; ++i)
|
||||
this._dtor(this._data[i]);
|
||||
}
|
||||
|
||||
forEach(fx) {
|
||||
for (let i = 0; i < this.size; ++i)
|
||||
fx(this.data[i], i, this);
|
||||
}
|
||||
|
||||
map(fx) {
|
||||
let result = new Array();
|
||||
for (let i = 0; i < this.size; ++i)
|
||||
result.push(fx(this.data[i], i, this));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @param {Number} size
|
||||
*/
|
||||
_extends(size) {
|
||||
let finalSize = this._data.length + size;
|
||||
for (let i = this._data.length; i < finalSize; ++i)
|
||||
this._data[i] = this._ctor();
|
||||
}
|
||||
}
|
59
engine/cocos2d/renderer/memop/pool.js
Normal file
59
engine/cocos2d/renderer/memop/pool.js
Normal file
@@ -0,0 +1,59 @@
|
||||
export default class Pool {
|
||||
constructor(fn, size) {
|
||||
this._fn = fn;
|
||||
this._idx = size - 1;
|
||||
this._frees = new Array(size);
|
||||
|
||||
for (let i = 0; i < size; ++i) {
|
||||
this._frees[i] = fn();
|
||||
}
|
||||
}
|
||||
|
||||
_expand(size) {
|
||||
let old = this._frees;
|
||||
this._frees = new Array(size);
|
||||
|
||||
let len = size - old.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
this._frees[i] = this._fn();
|
||||
}
|
||||
|
||||
for (let i = len, j = 0; i < size; ++i, ++j) {
|
||||
this._frees[i] = old[j];
|
||||
}
|
||||
|
||||
this._idx += len;
|
||||
}
|
||||
|
||||
alloc() {
|
||||
// create some more space (expand by 20%, minimum 1)
|
||||
if (this._idx < 0) {
|
||||
this._expand(Math.round(this._frees.length * 1.2) + 1);
|
||||
}
|
||||
|
||||
let ret = this._frees[this._idx];
|
||||
this._frees[this._idx] = null;
|
||||
--this._idx;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
free(obj) {
|
||||
++this._idx;
|
||||
this._frees[this._idx] = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除对象池。
|
||||
* @param fn 清除回调,对每个释放的对象调用一次。
|
||||
*/
|
||||
clear (fn) {
|
||||
for (let i = 0; i <= this._idx; i++) {
|
||||
if (fn) {
|
||||
fn(this._frees[i]);
|
||||
}
|
||||
}
|
||||
this._frees.length = 0;
|
||||
this._idx = -1;
|
||||
}
|
||||
}
|
63
engine/cocos2d/renderer/memop/recycle-pool.js
Normal file
63
engine/cocos2d/renderer/memop/recycle-pool.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import sort from './timsort';
|
||||
|
||||
/**
|
||||
* Recycle Pool
|
||||
* @class RecyclePool
|
||||
*/
|
||||
export default class RecyclePool {
|
||||
constructor(fn, size) {
|
||||
this._fn = fn;
|
||||
this._count = 0;
|
||||
this._data = new Array(size);
|
||||
|
||||
for (let i = 0; i < size; ++i) {
|
||||
this._data[i] = fn();
|
||||
}
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this._count = 0;
|
||||
}
|
||||
|
||||
resize(size) {
|
||||
if (size > this._data.length) {
|
||||
for (let i = this._data.length; i < size; ++i) {
|
||||
this._data[i] = this._fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add() {
|
||||
if (this._count >= this._data.length) {
|
||||
this.resize(this._data.length * 2);
|
||||
}
|
||||
|
||||
return this._data[this._count++];
|
||||
}
|
||||
|
||||
remove(idx) {
|
||||
if (idx >= this._count) {
|
||||
return;
|
||||
}
|
||||
|
||||
let last = this._count - 1;
|
||||
let tmp = this._data[idx];
|
||||
this._data[idx] = this._data[last];
|
||||
this._data[last] = tmp;
|
||||
this._count -= 1;
|
||||
}
|
||||
|
||||
sort(cmp) {
|
||||
return sort(this._data, 0, this._count, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
cc.RecyclePool = RecyclePool;
|
166
engine/cocos2d/renderer/memop/sort.js
Normal file
166
engine/cocos2d/renderer/memop/sort.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// reference: https://github.com/v8/v8/blob/master/src/js/array.js
|
||||
|
||||
function _compare(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function _sort(array, from, to, comparefn) {
|
||||
|
||||
function _insertionSort(a, from, to) {
|
||||
for (let i = from + 1; i < to; i++) {
|
||||
let element = a[i];
|
||||
let j;
|
||||
|
||||
for (j = i - 1; j >= from; j--) {
|
||||
let tmp = a[j];
|
||||
let order = comparefn(tmp, element);
|
||||
if (order > 0) {
|
||||
a[j + 1] = tmp;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
a[j + 1] = element;
|
||||
}
|
||||
}
|
||||
|
||||
// function _getThirdIndex(a, from, to) {
|
||||
// let t_array = new Array();
|
||||
// // Use both 'from' and 'to' to determine the pivot candidates.
|
||||
// let increment = 200 + ((to - from) & 15);
|
||||
// let j = 0;
|
||||
// from += 1;
|
||||
// to -= 1;
|
||||
// for (let i = from; i < to; i += increment) {
|
||||
// t_array[j] = [i, a[i]];
|
||||
// j++;
|
||||
// }
|
||||
// t_array.sort(function (a, b) {
|
||||
// return comparefn(a[1], b[1]);
|
||||
// });
|
||||
// let third_index = t_array[t_array.length >> 1][0];
|
||||
// return third_index;
|
||||
// }
|
||||
|
||||
function _quickSort(a, from, to) {
|
||||
let third_index = 0;
|
||||
|
||||
while (true) {
|
||||
// Insertion sort is faster for short arrays.
|
||||
if (to - from <= 10) {
|
||||
_insertionSort(a, from, to);
|
||||
return;
|
||||
}
|
||||
// if (to - from > 1000) {
|
||||
// third_index = _getThirdIndex(a, from, to);
|
||||
// } else {
|
||||
third_index = from + ((to - from) >> 1);
|
||||
// }
|
||||
// Find a pivot as the median of first, last and middle element.
|
||||
let v0 = a[from];
|
||||
let v1 = a[to - 1];
|
||||
let v2 = a[third_index];
|
||||
let c01 = comparefn(v0, v1);
|
||||
if (c01 > 0) {
|
||||
// v1 < v0, so swap them.
|
||||
let tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = tmp;
|
||||
} // v0 <= v1.
|
||||
let c02 = comparefn(v0, v2);
|
||||
if (c02 >= 0) {
|
||||
// v2 <= v0 <= v1.
|
||||
let tmp = v0;
|
||||
v0 = v2;
|
||||
v2 = v1;
|
||||
v1 = tmp;
|
||||
} else {
|
||||
// v0 <= v1 && v0 < v2
|
||||
let c12 = comparefn(v1, v2);
|
||||
if (c12 > 0) {
|
||||
// v0 <= v2 < v1
|
||||
let tmp = v1;
|
||||
v1 = v2;
|
||||
v2 = tmp;
|
||||
}
|
||||
}
|
||||
// v0 <= v1 <= v2
|
||||
a[from] = v0;
|
||||
a[to - 1] = v2;
|
||||
let pivot = v1;
|
||||
let low_end = from + 1; // Upper bound of elements lower than pivot.
|
||||
let high_start = to - 1; // Lower bound of elements greater than pivot.
|
||||
a[third_index] = a[low_end];
|
||||
a[low_end] = pivot;
|
||||
|
||||
// From low_end to i are elements equal to pivot.
|
||||
// From i to high_start are elements that haven't been compared yet.
|
||||
partition: for (let i = low_end + 1; i < high_start; i++) {
|
||||
let element = a[i];
|
||||
let order = comparefn(element, pivot);
|
||||
if (order < 0) {
|
||||
a[i] = a[low_end];
|
||||
a[low_end] = element;
|
||||
low_end++;
|
||||
} else if (order > 0) {
|
||||
do {
|
||||
high_start--;
|
||||
if (high_start == i) break partition;
|
||||
let top_elem = a[high_start];
|
||||
order = comparefn(top_elem, pivot);
|
||||
} while (order > 0);
|
||||
a[i] = a[high_start];
|
||||
a[high_start] = element;
|
||||
if (order < 0) {
|
||||
element = a[i];
|
||||
a[i] = a[low_end];
|
||||
a[low_end] = element;
|
||||
low_end++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (to - high_start < low_end - from) {
|
||||
_quickSort(a, high_start, to);
|
||||
to = low_end;
|
||||
} else {
|
||||
_quickSort(a, from, low_end);
|
||||
from = high_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (to - from < 2) {
|
||||
return array;
|
||||
}
|
||||
|
||||
_quickSort(array, from, to);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the _quickSort function with it's initial values.
|
||||
*
|
||||
* @public
|
||||
* @param {array} array The input array which should be sorted
|
||||
* @param {Number} from
|
||||
* @param {Number} to
|
||||
* @param {Function} cmp
|
||||
* @returns {array} array Sorted array
|
||||
*/
|
||||
export default function (array, from, to, cmp) {
|
||||
if (from === undefined) {
|
||||
from = 0;
|
||||
}
|
||||
|
||||
if (to === undefined) {
|
||||
to = array.length;
|
||||
}
|
||||
|
||||
if (cmp === undefined) {
|
||||
cmp = _compare;
|
||||
}
|
||||
|
||||
return _sort(array, from, to, cmp);
|
||||
}
|
967
engine/cocos2d/renderer/memop/timsort.js
Normal file
967
engine/cocos2d/renderer/memop/timsort.js
Normal file
@@ -0,0 +1,967 @@
|
||||
// reference: https://github.com/mziccard/node-timsort
|
||||
|
||||
/**
|
||||
* Default minimum size of a run.
|
||||
*/
|
||||
const DEFAULT_MIN_MERGE = 32;
|
||||
|
||||
/**
|
||||
* Minimum ordered subsequece required to do galloping.
|
||||
*/
|
||||
const DEFAULT_MIN_GALLOPING = 7;
|
||||
|
||||
/**
|
||||
* Default tmp storage length. Can increase depending on the size of the
|
||||
* smallest run to merge.
|
||||
*/
|
||||
const DEFAULT_TMP_STORAGE_LENGTH = 256;
|
||||
|
||||
/**
|
||||
* Pre-computed powers of 10 for efficient lexicographic comparison of
|
||||
* small integers.
|
||||
*/
|
||||
const POWERS_OF_TEN = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]
|
||||
|
||||
/**
|
||||
* Estimate the logarithm base 10 of a small integer.
|
||||
*
|
||||
* @param {number} x - The integer to estimate the logarithm of.
|
||||
* @return {number} - The estimated logarithm of the integer.
|
||||
*/
|
||||
function log10(x) {
|
||||
if (x < 1e5) {
|
||||
if (x < 1e2) {
|
||||
return x < 1e1 ? 0 : 1;
|
||||
}
|
||||
|
||||
if (x < 1e4) {
|
||||
return x < 1e3 ? 2 : 3;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (x < 1e7) {
|
||||
return x < 1e6 ? 5 : 6;
|
||||
}
|
||||
|
||||
if (x < 1e9) {
|
||||
return x < 1e8 ? 7 : 8;
|
||||
}
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default alphabetical comparison of items.
|
||||
*
|
||||
* @param {string|object|number} a - First element to compare.
|
||||
* @param {string|object|number} b - Second element to compare.
|
||||
* @return {number} - A positive number if a.toString() > b.toString(), a
|
||||
* negative number if .toString() < b.toString(), 0 otherwise.
|
||||
*/
|
||||
function alphabeticalCompare(a, b) {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (~~a === a && ~~b === b) {
|
||||
if (a === 0 || b === 0) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
if (a < 0 || b < 0) {
|
||||
if (b >= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a >= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
a = -a;
|
||||
b = -b;
|
||||
}
|
||||
|
||||
const al = log10(a);
|
||||
const bl = log10(b);
|
||||
|
||||
let t = 0;
|
||||
|
||||
if (al < bl) {
|
||||
a *= POWERS_OF_TEN[bl - al - 1];
|
||||
b /= 10;
|
||||
t = -1;
|
||||
} else if (al > bl) {
|
||||
b *= POWERS_OF_TEN[al - bl - 1];
|
||||
a /= 10;
|
||||
t = 1;
|
||||
}
|
||||
|
||||
if (a === b) {
|
||||
return t;
|
||||
}
|
||||
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
let aStr = String(a);
|
||||
let bStr = String(b);
|
||||
|
||||
if (aStr === bStr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return aStr < bStr ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute minimum run length for TimSort
|
||||
*
|
||||
* @param {number} n - The size of the array to sort.
|
||||
*/
|
||||
function minRunLength(n) {
|
||||
let r = 0;
|
||||
|
||||
while (n >= DEFAULT_MIN_MERGE) {
|
||||
r |= (n & 1);
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
return n + r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the length of a monotonically ascending or strictly monotonically
|
||||
* descending sequence (run) starting at array[lo] in the range [lo, hi). If
|
||||
* the run is descending it is made ascending.
|
||||
*
|
||||
* @param {array} array - The array to reverse.
|
||||
* @param {number} lo - First element in the range (inclusive).
|
||||
* @param {number} hi - Last element in the range.
|
||||
* @param {function} compare - Item comparison function.
|
||||
* @return {number} - The length of the run.
|
||||
*/
|
||||
function makeAscendingRun(array, lo, hi, compare) {
|
||||
let runHi = lo + 1;
|
||||
|
||||
if (runHi === hi) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Descending
|
||||
if (compare(array[runHi++], array[lo]) < 0) {
|
||||
while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
|
||||
runHi++;
|
||||
}
|
||||
|
||||
reverseRun(array, lo, runHi);
|
||||
// Ascending
|
||||
} else {
|
||||
while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
|
||||
runHi++;
|
||||
}
|
||||
}
|
||||
|
||||
return runHi - lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse an array in the range [lo, hi).
|
||||
*
|
||||
* @param {array} array - The array to reverse.
|
||||
* @param {number} lo - First element in the range (inclusive).
|
||||
* @param {number} hi - Last element in the range.
|
||||
*/
|
||||
function reverseRun(array, lo, hi) {
|
||||
hi--;
|
||||
|
||||
while (lo < hi) {
|
||||
let t = array[lo];
|
||||
array[lo++] = array[hi];
|
||||
array[hi--] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the binary sort of the array in the range [lo, hi) where start is
|
||||
* the first element possibly out of order.
|
||||
*
|
||||
* @param {array} array - The array to sort.
|
||||
* @param {number} lo - First element in the range (inclusive).
|
||||
* @param {number} hi - Last element in the range.
|
||||
* @param {number} start - First element possibly out of order.
|
||||
* @param {function} compare - Item comparison function.
|
||||
*/
|
||||
function binaryInsertionSort(array, lo, hi, start, compare) {
|
||||
if (start === lo) {
|
||||
start++;
|
||||
}
|
||||
|
||||
for (; start < hi; start++) {
|
||||
let pivot = array[start];
|
||||
|
||||
// Ranges of the array where pivot belongs
|
||||
let left = lo;
|
||||
let right = start;
|
||||
|
||||
/*
|
||||
* pivot >= array[i] for i in [lo, left)
|
||||
* pivot < array[i] for i in in [right, start)
|
||||
*/
|
||||
while (left < right) {
|
||||
let mid = (left + right) >>> 1;
|
||||
|
||||
if (compare(pivot, array[mid]) < 0) {
|
||||
right = mid;
|
||||
} else {
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Move elements right to make room for the pivot. If there are elements
|
||||
* equal to pivot, left points to the first slot after them: this is also
|
||||
* a reason for which TimSort is stable
|
||||
*/
|
||||
let n = start - left;
|
||||
// Switch is just an optimization for small arrays
|
||||
switch (n) {
|
||||
case 3:
|
||||
array[left + 3] = array[left + 2];
|
||||
/* falls through */
|
||||
case 2:
|
||||
array[left + 2] = array[left + 1];
|
||||
/* falls through */
|
||||
case 1:
|
||||
array[left + 1] = array[left];
|
||||
break;
|
||||
default:
|
||||
while (n > 0) {
|
||||
array[left + n] = array[left + n - 1];
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
array[left] = pivot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the position at which to insert a value in a sorted range. If the range
|
||||
* contains elements equal to the value the leftmost element index is returned
|
||||
* (for stability).
|
||||
*
|
||||
* @param {number} value - Value to insert.
|
||||
* @param {array} array - The array in which to insert value.
|
||||
* @param {number} start - First element in the range.
|
||||
* @param {number} length - Length of the range.
|
||||
* @param {number} hint - The index at which to begin the search.
|
||||
* @param {function} compare - Item comparison function.
|
||||
* @return {number} - The index where to insert value.
|
||||
*/
|
||||
function gallopLeft(value, array, start, length, hint, compare) {
|
||||
let lastOffset = 0;
|
||||
let maxOffset = 0;
|
||||
let offset = 1;
|
||||
|
||||
if (compare(value, array[start + hint]) > 0) {
|
||||
maxOffset = length - hint;
|
||||
|
||||
while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
|
||||
lastOffset = offset;
|
||||
offset = (offset << 1) + 1;
|
||||
|
||||
if (offset <= 0) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > maxOffset) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
|
||||
// Make offsets relative to start
|
||||
lastOffset += hint;
|
||||
offset += hint;
|
||||
|
||||
// value <= array[start + hint]
|
||||
} else {
|
||||
maxOffset = hint + 1;
|
||||
while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
|
||||
lastOffset = offset;
|
||||
offset = (offset << 1) + 1;
|
||||
|
||||
if (offset <= 0) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
}
|
||||
if (offset > maxOffset) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
|
||||
// Make offsets relative to start
|
||||
let tmp = lastOffset;
|
||||
lastOffset = hint - offset;
|
||||
offset = hint - tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now array[start+lastOffset] < value <= array[start+offset], so value
|
||||
* belongs somewhere in the range (start + lastOffset, start + offset]. Do a
|
||||
* binary search, with invariant array[start + lastOffset - 1] < value <=
|
||||
* array[start + offset].
|
||||
*/
|
||||
lastOffset++;
|
||||
while (lastOffset < offset) {
|
||||
let m = lastOffset + ((offset - lastOffset) >>> 1);
|
||||
|
||||
if (compare(value, array[start + m]) > 0) {
|
||||
lastOffset = m + 1;
|
||||
|
||||
} else {
|
||||
offset = m;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the position at which to insert a value in a sorted range. If the range
|
||||
* contains elements equal to the value the rightmost element index is returned
|
||||
* (for stability).
|
||||
*
|
||||
* @param {number} value - Value to insert.
|
||||
* @param {array} array - The array in which to insert value.
|
||||
* @param {number} start - First element in the range.
|
||||
* @param {number} length - Length of the range.
|
||||
* @param {number} hint - The index at which to begin the search.
|
||||
* @param {function} compare - Item comparison function.
|
||||
* @return {number} - The index where to insert value.
|
||||
*/
|
||||
function gallopRight(value, array, start, length, hint, compare) {
|
||||
let lastOffset = 0;
|
||||
let maxOffset = 0;
|
||||
let offset = 1;
|
||||
|
||||
if (compare(value, array[start + hint]) < 0) {
|
||||
maxOffset = hint + 1;
|
||||
|
||||
while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
|
||||
lastOffset = offset;
|
||||
offset = (offset << 1) + 1;
|
||||
|
||||
if (offset <= 0) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > maxOffset) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
|
||||
// Make offsets relative to start
|
||||
let tmp = lastOffset;
|
||||
lastOffset = hint - offset;
|
||||
offset = hint - tmp;
|
||||
|
||||
// value >= array[start + hint]
|
||||
} else {
|
||||
maxOffset = length - hint;
|
||||
|
||||
while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
|
||||
lastOffset = offset;
|
||||
offset = (offset << 1) + 1;
|
||||
|
||||
if (offset <= 0) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > maxOffset) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
|
||||
// Make offsets relative to start
|
||||
lastOffset += hint;
|
||||
offset += hint;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now array[start+lastOffset] < value <= array[start+offset], so value
|
||||
* belongs somewhere in the range (start + lastOffset, start + offset]. Do a
|
||||
* binary search, with invariant array[start + lastOffset - 1] < value <=
|
||||
* array[start + offset].
|
||||
*/
|
||||
lastOffset++;
|
||||
|
||||
while (lastOffset < offset) {
|
||||
let m = lastOffset + ((offset - lastOffset) >>> 1);
|
||||
|
||||
if (compare(value, array[start + m]) < 0) {
|
||||
offset = m;
|
||||
|
||||
} else {
|
||||
lastOffset = m + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
class TimSort {
|
||||
|
||||
constructor(array, compare) {
|
||||
this.array = array;
|
||||
this.compare = compare;
|
||||
this.minGallop = DEFAULT_MIN_GALLOPING;
|
||||
this.length = array.length;
|
||||
|
||||
this.tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH;
|
||||
if (this.length < 2 * DEFAULT_TMP_STORAGE_LENGTH) {
|
||||
this.tmpStorageLength = this.length >>> 1;
|
||||
}
|
||||
|
||||
this.tmp = new Array(this.tmpStorageLength);
|
||||
|
||||
this.stackLength =
|
||||
(this.length < 120 ? 5 :
|
||||
this.length < 1542 ? 10 :
|
||||
this.length < 119151 ? 19 : 40);
|
||||
|
||||
this.runStart = new Array(this.stackLength);
|
||||
this.runLength = new Array(this.stackLength);
|
||||
this.stackSize = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a new run on TimSort's stack.
|
||||
*
|
||||
* @param {number} runStart - Start index of the run in the original array.
|
||||
* @param {number} runLength - Length of the run;
|
||||
*/
|
||||
pushRun(runStart, runLength) {
|
||||
this.runStart[this.stackSize] = runStart;
|
||||
this.runLength[this.stackSize] = runLength;
|
||||
this.stackSize += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge runs on TimSort's stack so that the following holds for all i:
|
||||
* 1) runLength[i - 3] > runLength[i - 2] + runLength[i - 1]
|
||||
* 2) runLength[i - 2] > runLength[i - 1]
|
||||
*/
|
||||
mergeRuns() {
|
||||
while (this.stackSize > 1) {
|
||||
let n = this.stackSize - 2;
|
||||
|
||||
if ((n >= 1 &&
|
||||
this.runLength[n - 1] <= this.runLength[n] + this.runLength[n + 1]) ||
|
||||
(n >= 2 &&
|
||||
this.runLength[n - 2] <= this.runLength[n] + this.runLength[n - 1])) {
|
||||
|
||||
if (this.runLength[n - 1] < this.runLength[n + 1]) {
|
||||
n--;
|
||||
}
|
||||
|
||||
} else if (this.runLength[n] > this.runLength[n + 1]) {
|
||||
break;
|
||||
}
|
||||
this.mergeAt(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge all runs on TimSort's stack until only one remains.
|
||||
*/
|
||||
forceMergeRuns() {
|
||||
while (this.stackSize > 1) {
|
||||
let n = this.stackSize - 2;
|
||||
|
||||
if (n > 0 && this.runLength[n - 1] < this.runLength[n + 1]) {
|
||||
n--;
|
||||
}
|
||||
|
||||
this.mergeAt(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the runs on the stack at positions i and i+1. Must be always be called
|
||||
* with i=stackSize-2 or i=stackSize-3 (that is, we merge on top of the stack).
|
||||
*
|
||||
* @param {number} i - Index of the run to merge in TimSort's stack.
|
||||
*/
|
||||
mergeAt(i) {
|
||||
let compare = this.compare;
|
||||
let array = this.array;
|
||||
|
||||
let start1 = this.runStart[i];
|
||||
let length1 = this.runLength[i];
|
||||
let start2 = this.runStart[i + 1];
|
||||
let length2 = this.runLength[i + 1];
|
||||
|
||||
this.runLength[i] = length1 + length2;
|
||||
|
||||
if (i === this.stackSize - 3) {
|
||||
this.runStart[i + 1] = this.runStart[i + 2];
|
||||
this.runLength[i + 1] = this.runLength[i + 2];
|
||||
}
|
||||
|
||||
this.stackSize--;
|
||||
|
||||
/*
|
||||
* Find where the first element in the second run goes in run1. Previous
|
||||
* elements in run1 are already in place
|
||||
*/
|
||||
let k = gallopRight(array[start2], array, start1, length1, 0, compare);
|
||||
start1 += k;
|
||||
length1 -= k;
|
||||
|
||||
if (length1 === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find where the last element in the first run goes in run2. Next elements
|
||||
* in run2 are already in place
|
||||
*/
|
||||
length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
|
||||
|
||||
if (length2 === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge remaining runs. A tmp array with length = min(length1, length2) is
|
||||
* used
|
||||
*/
|
||||
if (length1 <= length2) {
|
||||
this.mergeLow(start1, length1, start2, length2);
|
||||
|
||||
} else {
|
||||
this.mergeHigh(start1, length1, start2, length2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two adjacent runs in a stable way. The runs must be such that the
|
||||
* first element of run1 is bigger than the first element in run2 and the
|
||||
* last element of run1 is greater than all the elements in run2.
|
||||
* The method should be called when run1.length <= run2.length as it uses
|
||||
* TimSort temporary array to store run1. Use mergeHigh if run1.length >
|
||||
* run2.length.
|
||||
*
|
||||
* @param {number} start1 - First element in run1.
|
||||
* @param {number} length1 - Length of run1.
|
||||
* @param {number} start2 - First element in run2.
|
||||
* @param {number} length2 - Length of run2.
|
||||
*/
|
||||
mergeLow(start1, length1, start2, length2) {
|
||||
|
||||
let compare = this.compare;
|
||||
let array = this.array;
|
||||
let tmp = this.tmp;
|
||||
let i = 0;
|
||||
|
||||
for (i = 0; i < length1; i++) {
|
||||
tmp[i] = array[start1 + i];
|
||||
}
|
||||
|
||||
let cursor1 = 0;
|
||||
let cursor2 = start2;
|
||||
let dest = start1;
|
||||
|
||||
array[dest++] = array[cursor2++];
|
||||
|
||||
if (--length2 === 0) {
|
||||
for (i = 0; i < length1; i++) {
|
||||
array[dest + i] = tmp[cursor1 + i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (length1 === 1) {
|
||||
for (i = 0; i < length2; i++) {
|
||||
array[dest + i] = array[cursor2 + i];
|
||||
}
|
||||
array[dest + length2] = tmp[cursor1];
|
||||
return;
|
||||
}
|
||||
|
||||
let minGallop = this.minGallop;
|
||||
|
||||
while (true) {
|
||||
let count1 = 0;
|
||||
let count2 = 0;
|
||||
let exit = false;
|
||||
|
||||
do {
|
||||
if (compare(array[cursor2], tmp[cursor1]) < 0) {
|
||||
array[dest++] = array[cursor2++];
|
||||
count2++;
|
||||
count1 = 0;
|
||||
|
||||
if (--length2 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
array[dest++] = tmp[cursor1++];
|
||||
count1++;
|
||||
count2 = 0;
|
||||
if (--length1 === 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while ((count1 | count2) < minGallop);
|
||||
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
|
||||
|
||||
if (count1 !== 0) {
|
||||
for (i = 0; i < count1; i++) {
|
||||
array[dest + i] = tmp[cursor1 + i];
|
||||
}
|
||||
|
||||
dest += count1;
|
||||
cursor1 += count1;
|
||||
length1 -= count1;
|
||||
if (length1 <= 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
array[dest++] = array[cursor2++];
|
||||
|
||||
if (--length2 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
|
||||
|
||||
if (count2 !== 0) {
|
||||
for (i = 0; i < count2; i++) {
|
||||
array[dest + i] = array[cursor2 + i];
|
||||
}
|
||||
|
||||
dest += count2;
|
||||
cursor2 += count2;
|
||||
length2 -= count2;
|
||||
|
||||
if (length2 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
array[dest++] = tmp[cursor1++];
|
||||
|
||||
if (--length1 === 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
minGallop--;
|
||||
|
||||
} while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
|
||||
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (minGallop < 0) {
|
||||
minGallop = 0;
|
||||
}
|
||||
|
||||
minGallop += 2;
|
||||
}
|
||||
|
||||
this.minGallop = minGallop;
|
||||
|
||||
if (minGallop < 1) {
|
||||
this.minGallop = 1;
|
||||
}
|
||||
|
||||
if (length1 === 1) {
|
||||
for (i = 0; i < length2; i++) {
|
||||
array[dest + i] = array[cursor2 + i];
|
||||
}
|
||||
array[dest + length2] = tmp[cursor1];
|
||||
|
||||
} else if (length1 === 0) {
|
||||
throw new Error('mergeLow preconditions were not respected');
|
||||
|
||||
} else {
|
||||
for (i = 0; i < length1; i++) {
|
||||
array[dest + i] = tmp[cursor1 + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two adjacent runs in a stable way. The runs must be such that the
|
||||
* first element of run1 is bigger than the first element in run2 and the
|
||||
* last element of run1 is greater than all the elements in run2.
|
||||
* The method should be called when run1.length > run2.length as it uses
|
||||
* TimSort temporary array to store run2. Use mergeLow if run1.length <=
|
||||
* run2.length.
|
||||
*
|
||||
* @param {number} start1 - First element in run1.
|
||||
* @param {number} length1 - Length of run1.
|
||||
* @param {number} start2 - First element in run2.
|
||||
* @param {number} length2 - Length of run2.
|
||||
*/
|
||||
mergeHigh(start1, length1, start2, length2) {
|
||||
let compare = this.compare;
|
||||
let array = this.array;
|
||||
let tmp = this.tmp;
|
||||
let i = 0;
|
||||
|
||||
for (i = 0; i < length2; i++) {
|
||||
tmp[i] = array[start2 + i];
|
||||
}
|
||||
|
||||
let cursor1 = start1 + length1 - 1;
|
||||
let cursor2 = length2 - 1;
|
||||
let dest = start2 + length2 - 1;
|
||||
let customCursor = 0;
|
||||
let customDest = 0;
|
||||
|
||||
array[dest--] = array[cursor1--];
|
||||
|
||||
if (--length1 === 0) {
|
||||
customCursor = dest - (length2 - 1);
|
||||
|
||||
for (i = 0; i < length2; i++) {
|
||||
array[customCursor + i] = tmp[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (length2 === 1) {
|
||||
dest -= length1;
|
||||
cursor1 -= length1;
|
||||
customDest = dest + 1;
|
||||
customCursor = cursor1 + 1;
|
||||
|
||||
for (i = length1 - 1; i >= 0; i--) {
|
||||
array[customDest + i] = array[customCursor + i];
|
||||
}
|
||||
|
||||
array[dest] = tmp[cursor2];
|
||||
return;
|
||||
}
|
||||
|
||||
let minGallop = this.minGallop;
|
||||
|
||||
while (true) {
|
||||
let count1 = 0;
|
||||
let count2 = 0;
|
||||
let exit = false;
|
||||
|
||||
do {
|
||||
if (compare(tmp[cursor2], array[cursor1]) < 0) {
|
||||
array[dest--] = array[cursor1--];
|
||||
count1++;
|
||||
count2 = 0;
|
||||
if (--length1 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
array[dest--] = tmp[cursor2--];
|
||||
count2++;
|
||||
count1 = 0;
|
||||
if (--length2 === 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while ((count1 | count2) < minGallop);
|
||||
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
|
||||
|
||||
if (count1 !== 0) {
|
||||
dest -= count1;
|
||||
cursor1 -= count1;
|
||||
length1 -= count1;
|
||||
customDest = dest + 1;
|
||||
customCursor = cursor1 + 1;
|
||||
|
||||
for (i = count1 - 1; i >= 0; i--) {
|
||||
array[customDest + i] = array[customCursor + i];
|
||||
}
|
||||
|
||||
if (length1 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
array[dest--] = tmp[cursor2--];
|
||||
|
||||
if (--length2 === 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
|
||||
|
||||
if (count2 !== 0) {
|
||||
dest -= count2;
|
||||
cursor2 -= count2;
|
||||
length2 -= count2;
|
||||
customDest = dest + 1;
|
||||
customCursor = cursor2 + 1;
|
||||
|
||||
for (i = 0; i < count2; i++) {
|
||||
array[customDest + i] = tmp[customCursor + i];
|
||||
}
|
||||
|
||||
if (length2 <= 1) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
array[dest--] = array[cursor1--];
|
||||
|
||||
if (--length1 === 0) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
minGallop--;
|
||||
|
||||
} while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
|
||||
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (minGallop < 0) {
|
||||
minGallop = 0;
|
||||
}
|
||||
|
||||
minGallop += 2;
|
||||
}
|
||||
|
||||
this.minGallop = minGallop;
|
||||
|
||||
if (minGallop < 1) {
|
||||
this.minGallop = 1;
|
||||
}
|
||||
|
||||
if (length2 === 1) {
|
||||
dest -= length1;
|
||||
cursor1 -= length1;
|
||||
customDest = dest + 1;
|
||||
customCursor = cursor1 + 1;
|
||||
|
||||
for (i = length1 - 1; i >= 0; i--) {
|
||||
array[customDest + i] = array[customCursor + i];
|
||||
}
|
||||
|
||||
array[dest] = tmp[cursor2];
|
||||
|
||||
} else if (length2 === 0) {
|
||||
throw new Error('mergeHigh preconditions were not respected');
|
||||
|
||||
} else {
|
||||
customCursor = dest - (length2 - 1);
|
||||
for (i = 0; i < length2; i++) {
|
||||
array[customCursor + i] = tmp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort an array in the range [lo, hi) using TimSort.
|
||||
*
|
||||
* @param {array} array - The array to sort.
|
||||
* @param {number} lo - First element in the range (inclusive).
|
||||
* @param {number} hi - Last element in the range.
|
||||
* @param {function=} compare - Item comparison function. Default is alphabetical.
|
||||
*/
|
||||
export default function (array, lo, hi, compare) {
|
||||
if (!Array.isArray(array)) {
|
||||
throw new TypeError('Can only sort arrays');
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the case where a comparison function is not provided. We do
|
||||
* lexicographic sorting
|
||||
*/
|
||||
|
||||
if (lo === undefined) {
|
||||
lo = 0;
|
||||
}
|
||||
|
||||
if (hi === undefined) {
|
||||
hi = array.length;
|
||||
}
|
||||
|
||||
if (compare === undefined) {
|
||||
compare = alphabeticalCompare;
|
||||
}
|
||||
|
||||
let remaining = hi - lo;
|
||||
|
||||
// The array is already sorted
|
||||
if (remaining < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let runLength = 0;
|
||||
// On small arrays binary sort can be used directly
|
||||
if (remaining < DEFAULT_MIN_MERGE) {
|
||||
runLength = makeAscendingRun(array, lo, hi, compare);
|
||||
binaryInsertionSort(array, lo, hi, lo + runLength, compare);
|
||||
return;
|
||||
}
|
||||
|
||||
let ts = new TimSort(array, compare);
|
||||
|
||||
let minRun = minRunLength(remaining);
|
||||
|
||||
do {
|
||||
runLength = makeAscendingRun(array, lo, hi, compare);
|
||||
if (runLength < minRun) {
|
||||
let force = remaining;
|
||||
if (force > minRun) {
|
||||
force = minRun;
|
||||
}
|
||||
|
||||
binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
|
||||
runLength = force;
|
||||
}
|
||||
// Push new run and merge if necessary
|
||||
ts.pushRun(lo, runLength);
|
||||
ts.mergeRuns();
|
||||
|
||||
// Go find next run
|
||||
remaining -= runLength;
|
||||
lo += runLength;
|
||||
|
||||
} while (remaining !== 0);
|
||||
|
||||
// Force merging of remaining runs
|
||||
ts.forceMergeRuns();
|
||||
}
|
129
engine/cocos2d/renderer/memop/typed-array-pool.js
Normal file
129
engine/cocos2d/renderer/memop/typed-array-pool.js
Normal file
@@ -0,0 +1,129 @@
|
||||
let _bufferPools = Array(8);
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
_bufferPools[i] = [];
|
||||
}
|
||||
|
||||
function _nextPow16(v) {
|
||||
for (let i = 16; i <= (1 << 28); i *= 16) {
|
||||
if (v <= i) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _log2(v) {
|
||||
let r, shift;
|
||||
r = (v > 0xFFFF) << 4; v >>>= r;
|
||||
shift = (v > 0xFF) << 3; v >>>= shift; r |= shift;
|
||||
shift = (v > 0xF) << 2; v >>>= shift; r |= shift;
|
||||
shift = (v > 0x3) << 1; v >>>= shift; r |= shift;
|
||||
return r | (v >> 1);
|
||||
}
|
||||
|
||||
function _alloc(n) {
|
||||
let sz = _nextPow16(n);
|
||||
let bin = _bufferPools[_log2(sz) >> 2];
|
||||
if (bin.length > 0) {
|
||||
return bin.pop();
|
||||
}
|
||||
return new ArrayBuffer(sz);
|
||||
}
|
||||
|
||||
function _free(buf) {
|
||||
_bufferPools[_log2(buf.byteLength) >> 2].push(buf);
|
||||
}
|
||||
|
||||
export default {
|
||||
alloc_int8(n) {
|
||||
let result = new Int8Array(_alloc(n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_uint8(n) {
|
||||
let result = new Uint8Array(_alloc(n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_int16(n) {
|
||||
let result = new Int16Array(_alloc(2 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_uint16(n) {
|
||||
let result = new Uint16Array(_alloc(2 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_int32(n) {
|
||||
let result = new Int32Array(_alloc(4 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_uint32(n) {
|
||||
let result = new Uint32Array(_alloc(4 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_float32(n) {
|
||||
let result = new Float32Array(_alloc(4 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_float64(n) {
|
||||
let result = new Float64Array(_alloc(8 * n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
alloc_dataview(n) {
|
||||
let result = new DataView(_alloc(n), 0, n);
|
||||
if (result.length !== n) {
|
||||
return result.subarray(0, n);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
free(array) {
|
||||
_free(array.buffer);
|
||||
},
|
||||
|
||||
reset() {
|
||||
let _bufferPools = Array(8);
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
_bufferPools[i] = [];
|
||||
}
|
||||
},
|
||||
};
|
50
engine/cocos2d/renderer/murmurhash2_gc.js
Normal file
50
engine/cocos2d/renderer/murmurhash2_gc.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* JS Implementation of MurmurHash2
|
||||
*
|
||||
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
|
||||
* @see http://github.com/garycourt/murmurhash-js
|
||||
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
|
||||
* @see http://sites.google.com/site/murmurhash/
|
||||
*
|
||||
* @param {string} str ASCII only
|
||||
* @param {number} seed Positive integer only
|
||||
* @return {number} 32-bit positive integer hash
|
||||
*/
|
||||
|
||||
export default function murmurhash2_32_gc(str, seed) {
|
||||
var
|
||||
l = str.length,
|
||||
h = seed ^ l,
|
||||
i = 0,
|
||||
k;
|
||||
|
||||
while (l >= 4) {
|
||||
k =
|
||||
((str.charCodeAt(i) & 0xff)) |
|
||||
((str.charCodeAt(++i) & 0xff) << 8) |
|
||||
((str.charCodeAt(++i) & 0xff) << 16) |
|
||||
((str.charCodeAt(++i) & 0xff) << 24);
|
||||
|
||||
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
||||
k ^= k >>> 24;
|
||||
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
||||
|
||||
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
|
||||
|
||||
l -= 4;
|
||||
++i;
|
||||
}
|
||||
|
||||
switch (l) {
|
||||
case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
|
||||
case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
|
||||
case 1: h ^= (str.charCodeAt(i) & 0xff);
|
||||
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
||||
}
|
||||
|
||||
h ^= h >>> 13;
|
||||
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
||||
h ^= h >>> 15;
|
||||
|
||||
return h >>> 0;
|
||||
}
|
336
engine/cocos2d/renderer/renderers/forward-renderer.js
Normal file
336
engine/cocos2d/renderer/renderers/forward-renderer.js
Normal file
@@ -0,0 +1,336 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { Vec3, Vec4, Mat4 } from '../../core/value-types';
|
||||
import BaseRenderer from '../core/base-renderer';
|
||||
import enums from '../enums';
|
||||
import { RecyclePool } from '../memop';
|
||||
|
||||
let _a16_view = new Float32Array(16);
|
||||
let _a16_view_inv = new Float32Array(16);
|
||||
let _a16_proj = new Float32Array(16);
|
||||
let _a16_viewProj = new Float32Array(16);
|
||||
let _a4_camPos = new Float32Array(4);
|
||||
|
||||
let _a64_shadow_lightViewProj = new Float32Array(64);
|
||||
let _a16_shadow_lightViewProjs = [];
|
||||
let _a4_shadow_info = new Float32Array(4);
|
||||
|
||||
let _camPos = new Vec4(0, 0, 0, 0);
|
||||
let _camFwd = new Vec3(0, 0, 0);
|
||||
let _v3_tmp1 = new Vec3(0, 0, 0);
|
||||
|
||||
const CC_MAX_LIGHTS = 4;
|
||||
const CC_MAX_SHADOW_LIGHTS = 2;
|
||||
|
||||
let _float16_pool = new RecyclePool(() => {
|
||||
return new Float32Array(16);
|
||||
}, 8);
|
||||
|
||||
function sortView (a, b) {
|
||||
return (a._priority - b._priority);
|
||||
}
|
||||
|
||||
export default class ForwardRenderer extends BaseRenderer {
|
||||
constructor(device, builtin) {
|
||||
super(device, builtin);
|
||||
|
||||
this._time = new Float32Array(4);
|
||||
|
||||
this._lights = [];
|
||||
this._shadowLights = [];
|
||||
|
||||
this._numLights = 0;
|
||||
|
||||
this._defines = {
|
||||
};
|
||||
|
||||
this._registerStage('shadowcast', this._shadowStage.bind(this));
|
||||
this._registerStage('opaque', this._opaqueStage.bind(this));
|
||||
this._registerStage('transparent', this._transparentStage.bind(this));
|
||||
}
|
||||
|
||||
reset () {
|
||||
_float16_pool.reset();
|
||||
super.reset();
|
||||
}
|
||||
|
||||
render (scene, dt) {
|
||||
this.reset();
|
||||
|
||||
if (!CC_EDITOR) {
|
||||
if (dt) {
|
||||
this._time[0] += dt;
|
||||
this._time[1] = dt;
|
||||
this._time[2] ++;
|
||||
}
|
||||
this._device.setUniform('cc_time', this._time);
|
||||
}
|
||||
|
||||
this._updateLights(scene);
|
||||
|
||||
const canvas = this._device._gl.canvas;
|
||||
for (let i = 0; i < scene._cameras.length; ++i) {
|
||||
let view = this._requestView();
|
||||
let width = canvas.width;
|
||||
let height = canvas.height;
|
||||
let camera = scene._cameras.data[i];
|
||||
camera.extractView(view, width, height);
|
||||
}
|
||||
|
||||
// render by cameras
|
||||
this._viewPools.sort(sortView);
|
||||
|
||||
for (let i = 0; i < this._viewPools.length; ++i) {
|
||||
let view = this._viewPools.data[i];
|
||||
this._render(view, scene);
|
||||
}
|
||||
}
|
||||
|
||||
// direct render a single camera
|
||||
renderCamera (camera, scene) {
|
||||
this.reset();
|
||||
|
||||
this._updateLights(scene);
|
||||
|
||||
const canvas = this._device._gl.canvas;
|
||||
let width = canvas.width;
|
||||
let height = canvas.height;
|
||||
|
||||
let view = this._requestView();
|
||||
camera.extractView(view, width, height);
|
||||
|
||||
// render by cameras
|
||||
this._viewPools.sort(sortView);
|
||||
|
||||
for (let i = 0; i < this._viewPools.length; ++i) {
|
||||
let view = this._viewPools.data[i];
|
||||
this._render(view, scene);
|
||||
}
|
||||
}
|
||||
|
||||
_updateLights (scene) {
|
||||
this._lights.length = 0;
|
||||
this._shadowLights.length = 0;
|
||||
|
||||
let lights = scene._lights;
|
||||
for (let i = 0; i < lights.length; ++i) {
|
||||
let light = lights.data[i];
|
||||
light.update(this._device);
|
||||
|
||||
if (light.shadowType !== enums.SHADOW_NONE) {
|
||||
if (this._shadowLights.length < CC_MAX_SHADOW_LIGHTS) {
|
||||
this._shadowLights.unshift(light);
|
||||
}
|
||||
let view = this._requestView();
|
||||
light.extractView(view, ['shadowcast']);
|
||||
|
||||
this._lights.splice(0, 0, light);
|
||||
}
|
||||
else {
|
||||
this._lights.push(light);
|
||||
}
|
||||
}
|
||||
|
||||
this._updateLightDefines();
|
||||
this._numLights = lights._count;
|
||||
}
|
||||
|
||||
_updateLightDefines () {
|
||||
let defines = this._defines;
|
||||
|
||||
for (let i = 0; i < this._lights.length; ++i) {
|
||||
let light = this._lights[i];
|
||||
let lightKey = `CC_LIGHT_${i}_TYPE`;
|
||||
let shadowKey = `CC_SHADOW_${i}_TYPE`;
|
||||
if (defines[lightKey] !== light._type){
|
||||
defines[lightKey] = light._type;
|
||||
this._definesChanged = true;
|
||||
}
|
||||
if (defines[shadowKey] !== light._shadowType){
|
||||
defines[shadowKey] = light._shadowType;
|
||||
this._definesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
let newCount = Math.min(CC_MAX_LIGHTS, this._lights.length);
|
||||
if (defines.CC_NUM_LIGHTS !== newCount) {
|
||||
defines.CC_NUM_LIGHTS = newCount;
|
||||
this._definesChanged = true;
|
||||
}
|
||||
newCount = Math.min(CC_MAX_LIGHTS, this._shadowLights.length);
|
||||
if (defines.CC_NUM_SHADOW_LIGHTS !== newCount) {
|
||||
defines.CC_NUM_SHADOW_LIGHTS = newCount;
|
||||
this._definesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
_submitLightsUniforms () {
|
||||
let device = this._device;
|
||||
|
||||
if (this._lights.length > 0) {
|
||||
let positionAndRanges = _float16_pool.add();
|
||||
let directions = _float16_pool.add();
|
||||
let colors = _float16_pool.add();
|
||||
let lightNum = Math.min(CC_MAX_LIGHTS, this._lights.length);
|
||||
for (let i = 0; i < lightNum; ++i) {
|
||||
let light = this._lights[i];
|
||||
let index = i * 4;
|
||||
|
||||
colors.set(light._colorUniform, index);
|
||||
directions.set(light._directionUniform, index);
|
||||
positionAndRanges.set(light._positionUniform, index);
|
||||
positionAndRanges[index+3] = light._range;
|
||||
|
||||
if (light._type === enums.LIGHT_SPOT) {
|
||||
directions[index+3] = light._spotUniform[0];
|
||||
colors[index+3] = light._spotUniform[1];
|
||||
}
|
||||
else {
|
||||
directions[index+3] = 0;
|
||||
colors[index+3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
device.setUniform('cc_lightDirection', directions);
|
||||
device.setUniform('cc_lightColor', colors);
|
||||
device.setUniform('cc_lightPositionAndRange', positionAndRanges);
|
||||
}
|
||||
}
|
||||
|
||||
_submitShadowStageUniforms(view) {
|
||||
|
||||
let light = view._shadowLight;
|
||||
|
||||
let shadowInfo = _a4_shadow_info;
|
||||
shadowInfo[0] = light.shadowMinDepth;
|
||||
shadowInfo[1] = light.shadowMaxDepth;
|
||||
shadowInfo[2] = light.shadowDepthScale;
|
||||
shadowInfo[3] = light.shadowDarkness;
|
||||
|
||||
this._device.setUniform('cc_shadow_map_lightViewProjMatrix', Mat4.toArray(_a16_viewProj, view._matViewProj));
|
||||
this._device.setUniform('cc_shadow_map_info', shadowInfo);
|
||||
this._device.setUniform('cc_shadow_map_bias', light.shadowBias);
|
||||
|
||||
this._defines.CC_SHADOW_TYPE = light._shadowType;
|
||||
}
|
||||
|
||||
_submitOtherStagesUniforms() {
|
||||
let shadowInfo = _float16_pool.add();
|
||||
|
||||
for (let i = 0; i < this._shadowLights.length; ++i) {
|
||||
let light = this._shadowLights[i];
|
||||
let view = _a16_shadow_lightViewProjs[i];
|
||||
if (!view) {
|
||||
view = _a16_shadow_lightViewProjs[i] = new Float32Array(_a64_shadow_lightViewProj.buffer, i * 64, 16);
|
||||
}
|
||||
Mat4.toArray(view, light.viewProjMatrix);
|
||||
|
||||
let index = i*4;
|
||||
shadowInfo[index] = light.shadowMinDepth;
|
||||
shadowInfo[index+1] = light.shadowMaxDepth;
|
||||
shadowInfo[index+2] = light._shadowResolution;
|
||||
shadowInfo[index+3] = light.shadowDarkness;
|
||||
}
|
||||
|
||||
this._device.setUniform(`cc_shadow_lightViewProjMatrix`, _a64_shadow_lightViewProj);
|
||||
this._device.setUniform(`cc_shadow_info`, shadowInfo);
|
||||
// this._device.setUniform(`cc_frustumEdgeFalloff_${index}`, light.frustumEdgeFalloff);
|
||||
}
|
||||
|
||||
_sortItems (items) {
|
||||
// sort items
|
||||
items.sort((a, b) => {
|
||||
// if (a.layer !== b.layer) {
|
||||
// return a.layer - b.layer;
|
||||
// }
|
||||
|
||||
if (a.passes.length !== b.passes.length) {
|
||||
return a.passes.length - b.passes.length;
|
||||
}
|
||||
|
||||
return a.sortKey - b.sortKey;
|
||||
});
|
||||
}
|
||||
|
||||
_shadowStage (view, items) {
|
||||
// update rendering
|
||||
this._submitShadowStageUniforms(view);
|
||||
|
||||
// this._sortItems(items);
|
||||
|
||||
// draw it
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items.data[i];
|
||||
if (item.effect.getDefine('CC_CASTING_SHADOW')) {
|
||||
this._draw(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_drawItems (view, items) {
|
||||
let shadowLights = this._shadowLights;
|
||||
if (shadowLights.length === 0 && this._numLights === 0) {
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items.data[i];
|
||||
this._draw(item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items.data[i];
|
||||
|
||||
for (let shadowIdx = 0; shadowIdx < shadowLights.length; ++shadowIdx) {
|
||||
this._device.setTexture('cc_shadow_map_'+shadowIdx, shadowLights[shadowIdx].shadowMap, this._allocTextureUnit());
|
||||
}
|
||||
|
||||
this._draw(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_opaqueStage (view, items) {
|
||||
view.getPosition(_camPos);
|
||||
|
||||
// update uniforms
|
||||
this._device.setUniform('cc_matView', Mat4.toArray(_a16_view, view._matView));
|
||||
this._device.setUniform('cc_matViewInv', Mat4.toArray(_a16_view_inv, view._matViewInv));
|
||||
this._device.setUniform('cc_matProj', Mat4.toArray(_a16_proj, view._matProj));
|
||||
this._device.setUniform('cc_matViewProj', Mat4.toArray(_a16_viewProj, view._matViewProj));
|
||||
this._device.setUniform('cc_cameraPos', Vec4.toArray(_a4_camPos, _camPos));
|
||||
|
||||
// update rendering
|
||||
this._submitLightsUniforms();
|
||||
this._submitOtherStagesUniforms();
|
||||
|
||||
this._drawItems(view, items);
|
||||
}
|
||||
|
||||
_transparentStage (view, items) {
|
||||
view.getPosition(_camPos);
|
||||
view.getForward(_camFwd);
|
||||
|
||||
// update uniforms
|
||||
this._device.setUniform('cc_matView', Mat4.toArray(_a16_view, view._matView));
|
||||
this._device.setUniform('cc_matViewInv', Mat4.toArray(_a16_view_inv, view._matViewInv));
|
||||
this._device.setUniform('cc_matProj', Mat4.toArray(_a16_proj, view._matProj));
|
||||
this._device.setUniform('cc_matViewProj', Mat4.toArray(_a16_viewProj, view._matViewProj));
|
||||
this._device.setUniform('cc_cameraPos', Vec4.toArray(_a4_camPos, _camPos));
|
||||
|
||||
this._submitLightsUniforms();
|
||||
this._submitOtherStagesUniforms();
|
||||
|
||||
// calculate zdist
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
let item = items.data[i];
|
||||
|
||||
// TODO: we should use mesh center instead!
|
||||
item.node.getWorldPosition(_v3_tmp1);
|
||||
|
||||
Vec3.sub(_v3_tmp1, _v3_tmp1, _camPos);
|
||||
item.sortKey = -Vec3.dot(_v3_tmp1, _camFwd);
|
||||
}
|
||||
|
||||
this._sortItems(items);
|
||||
this._drawItems(view, items);
|
||||
}
|
||||
}
|
503
engine/cocos2d/renderer/scene/camera.js
Normal file
503
engine/cocos2d/renderer/scene/camera.js
Normal file
@@ -0,0 +1,503 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { Vec3, Mat4, lerp, Vec4 } from '../../core/value-types';
|
||||
import { Ray } from '../../core/geom-utils';
|
||||
import enums from '../enums';
|
||||
|
||||
let _tmp_mat4 = new Mat4();
|
||||
|
||||
let _matView = new Mat4();
|
||||
let _matViewInv = new Mat4();
|
||||
let _matProj = new Mat4();
|
||||
let _matViewProj = new Mat4();
|
||||
let _matInvViewProj = new Mat4();
|
||||
let _tmp_v3 = new Vec3();
|
||||
let _tmp2_v3 = new Vec3();
|
||||
|
||||
/**
|
||||
* A representation of a camera instance
|
||||
*/
|
||||
export default class Camera {
|
||||
_poolID = -1;
|
||||
_node = null;
|
||||
_projection = enums.PROJ_PERSPECTIVE;
|
||||
|
||||
// priority. the smaller one will be rendered first
|
||||
_priority = 0;
|
||||
|
||||
// clear options
|
||||
_color = new Vec4(0.2, 0.3, 0.47, 1);
|
||||
_depth = 1;
|
||||
_stencil = 0;
|
||||
_clearFlags = enums.CLEAR_COLOR | enums.CLEAR_DEPTH;
|
||||
_clearModel = null;
|
||||
|
||||
// stages & framebuffer
|
||||
_stages = [];
|
||||
_framebuffer = null;
|
||||
|
||||
// projection properties
|
||||
_near = 0.01;
|
||||
_far = 1000.0;
|
||||
_fov = Math.PI / 4.0; // vertical fov
|
||||
_rect = {
|
||||
x: 0, y: 0, w: 1, h: 1
|
||||
};
|
||||
|
||||
// ortho properties
|
||||
_orthoHeight = 10;
|
||||
|
||||
_cullingMask = 0xffffffff;
|
||||
|
||||
|
||||
// culling mask
|
||||
get cullingMask () {
|
||||
return this._cullingMask;
|
||||
}
|
||||
|
||||
set cullingMask (mask) {
|
||||
this._cullingMask = mask;
|
||||
}
|
||||
|
||||
setCullingMask (mask) {
|
||||
this._cullingMask = mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hosting node of this camera
|
||||
* @returns {Node} the hosting node
|
||||
*/
|
||||
getNode () {
|
||||
return this._node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hosting node of this camera
|
||||
* @param {Node} node the hosting node
|
||||
*/
|
||||
setNode (node) {
|
||||
this._node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the projection type of the camera
|
||||
* @returns {number} camera projection type
|
||||
*/
|
||||
getType () {
|
||||
return this._projection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the projection type of the camera
|
||||
* @param {number} type camera projection type
|
||||
*/
|
||||
setType (type) {
|
||||
this._projection = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the priority of the camera
|
||||
* @returns {number} camera priority
|
||||
*/
|
||||
getPriority () {
|
||||
return this._priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the priority of the camera
|
||||
* @param {number} priority camera priority
|
||||
*/
|
||||
setPriority (priority) {
|
||||
this._priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the orthogonal height of the camera
|
||||
* @returns {number} camera height
|
||||
*/
|
||||
getOrthoHeight () {
|
||||
return this._orthoHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the orthogonal height of the camera
|
||||
* @param {number} val camera height
|
||||
*/
|
||||
setOrthoHeight (val) {
|
||||
this._orthoHeight = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field of view of the camera
|
||||
* @returns {number} camera field of view
|
||||
*/
|
||||
getFov () {
|
||||
return this._fov;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field of view of the camera
|
||||
* @param {number} fov camera field of view
|
||||
*/
|
||||
setFov (fov) {
|
||||
this._fov = fov;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the near clipping distance of the camera
|
||||
* @returns {number} camera near clipping distance
|
||||
*/
|
||||
getNear () {
|
||||
return this._near;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the near clipping distance of the camera
|
||||
* @param {number} near camera near clipping distance
|
||||
*/
|
||||
setNear (near) {
|
||||
this._near = near;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the far clipping distance of the camera
|
||||
* @returns {number} camera far clipping distance
|
||||
*/
|
||||
getFar () {
|
||||
return this._far;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the far clipping distance of the camera
|
||||
* @param {number} far camera far clipping distance
|
||||
*/
|
||||
setFar (far) {
|
||||
this._far = far;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clear color of the camera
|
||||
* @returns {Vec4} out the receiving color vector
|
||||
*/
|
||||
getColor (out) {
|
||||
return Vec4.copy(out, this._color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the clear color of the camera
|
||||
* @param {number} r red channel of camera clear color
|
||||
* @param {number} g green channel of camera clear color
|
||||
* @param {number} b blue channel of camera clear color
|
||||
* @param {number} a alpha channel of camera clear color
|
||||
*/
|
||||
setColor (r, g, b, a) {
|
||||
Vec4.set(this._color, r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clear depth of the camera
|
||||
* @returns {number} camera clear depth
|
||||
*/
|
||||
getDepth () {
|
||||
return this._depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the clear depth of the camera
|
||||
* @param {number} depth camera clear depth
|
||||
*/
|
||||
setDepth (depth) {
|
||||
this._depth = depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clearing stencil value of the camera
|
||||
* @returns {number} camera clearing stencil value
|
||||
*/
|
||||
getStencil () {
|
||||
return this._stencil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the clearing stencil value of the camera
|
||||
* @param {number} stencil camera clearing stencil value
|
||||
*/
|
||||
setStencil (stencil) {
|
||||
this._stencil = stencil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clearing flags of the camera
|
||||
* @returns {number} camera clearing flags
|
||||
*/
|
||||
getClearFlags () {
|
||||
return this._clearFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the clearing flags of the camera
|
||||
* @param {number} flags camera clearing flags
|
||||
*/
|
||||
setClearFlags (flags) {
|
||||
this._clearFlags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rect of the camera
|
||||
* @param {Object} out the receiving object
|
||||
* @returns {Object} camera rect
|
||||
*/
|
||||
getRect (out) {
|
||||
out.x = this._rect.x;
|
||||
out.y = this._rect.y;
|
||||
out.w = this._rect.w;
|
||||
out.h = this._rect.h;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rect of the camera
|
||||
* @param {Number} x - [0,1]
|
||||
* @param {Number} y - [0,1]
|
||||
* @param {Number} w - [0,1]
|
||||
* @param {Number} h - [0,1]
|
||||
*/
|
||||
setRect (x, y, w, h) {
|
||||
this._rect.x = x;
|
||||
this._rect.y = y;
|
||||
this._rect.w = w;
|
||||
this._rect.h = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stages of the camera
|
||||
* @returns {string[]} camera stages
|
||||
*/
|
||||
getStages () {
|
||||
return this._stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stages of the camera
|
||||
* @param {string[]} stages camera stages
|
||||
*/
|
||||
setStages (stages) {
|
||||
this._stages = stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the framebuffer of the camera
|
||||
* @returns {FrameBuffer} camera framebuffer
|
||||
*/
|
||||
getFramebuffer () {
|
||||
return this._framebuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the framebuffer of the camera
|
||||
* @param {FrameBuffer} framebuffer camera framebuffer
|
||||
*/
|
||||
setFrameBuffer (framebuffer) {
|
||||
this._framebuffer = framebuffer;
|
||||
}
|
||||
|
||||
_calcMatrices (width, height) {
|
||||
// view matrix
|
||||
this._node.getWorldRT(_matViewInv);
|
||||
Mat4.invert(_matView, _matViewInv);
|
||||
|
||||
// projection matrix
|
||||
let aspect = width / height;
|
||||
if (this._projection === enums.PROJ_PERSPECTIVE) {
|
||||
Mat4.perspective(_matProj,
|
||||
this._fov,
|
||||
aspect,
|
||||
this._near,
|
||||
this._far
|
||||
);
|
||||
} else {
|
||||
let x = this._orthoHeight * aspect;
|
||||
let y = this._orthoHeight;
|
||||
Mat4.ortho(_matProj,
|
||||
-x, x, -y, y, this._near, this._far
|
||||
);
|
||||
}
|
||||
|
||||
// view-projection
|
||||
Mat4.mul(_matViewProj, _matProj, _matView);
|
||||
// inv view-projection
|
||||
Mat4.invert(_matInvViewProj, _matViewProj);
|
||||
}
|
||||
|
||||
/**
|
||||
* extract a view of this camera
|
||||
* @param {View} out the receiving view
|
||||
* @param {number} width framebuffer width
|
||||
* @param {number} height framebuffer height
|
||||
*/
|
||||
extractView (out, width, height) {
|
||||
if (this._framebuffer) {
|
||||
width = this._framebuffer._width;
|
||||
height = this._framebuffer._height;
|
||||
}
|
||||
|
||||
// priority
|
||||
out._priority = this._priority;
|
||||
|
||||
// rect
|
||||
out._rect.x = this._rect.x * width;
|
||||
out._rect.y = this._rect.y * height;
|
||||
out._rect.w = this._rect.w * width;
|
||||
out._rect.h = this._rect.h * height;
|
||||
|
||||
// clear opts
|
||||
this.getColor(out._color);
|
||||
out._depth = this._depth;
|
||||
out._stencil = this._stencil;
|
||||
out._clearFlags = this._clearFlags;
|
||||
out._clearModel = this._clearModel;
|
||||
|
||||
// stages & framebuffer
|
||||
out._stages = this._stages;
|
||||
out._framebuffer = this._framebuffer;
|
||||
|
||||
this._calcMatrices(width, height);
|
||||
Mat4.copy(out._matView, _matView);
|
||||
Mat4.copy(out._matViewInv, _matViewInv);
|
||||
Mat4.copy(out._matProj, _matProj);
|
||||
Mat4.copy(out._matViewProj, _matViewProj);
|
||||
Mat4.copy(out._matInvViewProj, _matInvViewProj);
|
||||
|
||||
out._cullingMask = this._cullingMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* transform a screen position to a world space ray
|
||||
* @param {number} x the screen x position to be transformed
|
||||
* @param {number} y the screen y position to be transformed
|
||||
* @param {number} width framebuffer width
|
||||
* @param {number} height framebuffer height
|
||||
* @param {Ray} out the resulting ray
|
||||
* @returns {Ray} the resulting ray
|
||||
*/
|
||||
screenPointToRay (x, y, width, height, out) {
|
||||
if (!cc.geomUtils) return out;
|
||||
|
||||
out = out || new Ray();
|
||||
this._calcMatrices(width, height);
|
||||
|
||||
let cx = this._rect.x * width;
|
||||
let cy = this._rect.y * height;
|
||||
let cw = this._rect.w * width;
|
||||
let ch = this._rect.h * height;
|
||||
|
||||
// far plane intersection
|
||||
Vec3.set(_tmp2_v3, (x - cx) / cw * 2 - 1, (y - cy) / ch * 2 - 1, 1);
|
||||
Vec3.transformMat4(_tmp2_v3, _tmp2_v3, _matInvViewProj);
|
||||
|
||||
if (this._projection === enums.PROJ_PERSPECTIVE) {
|
||||
// camera origin
|
||||
this._node.getWorldPosition(_tmp_v3);
|
||||
} else {
|
||||
// near plane intersection
|
||||
Vec3.set(_tmp_v3, (x - cx) / cw * 2 - 1, (y - cy) / ch * 2 - 1, -1);
|
||||
Vec3.transformMat4(_tmp_v3, _tmp_v3, _matInvViewProj);
|
||||
}
|
||||
|
||||
return Ray.fromPoints(out, _tmp_v3, _tmp2_v3);
|
||||
}
|
||||
|
||||
/**
|
||||
* transform a screen position to world space
|
||||
* @param {Vec3} out the resulting vector
|
||||
* @param {Vec3} screenPos the screen position to be transformed
|
||||
* @param {number} width framebuffer width
|
||||
* @param {number} height framebuffer height
|
||||
* @returns {Vec3} the resulting vector
|
||||
*/
|
||||
screenToWorld (out, screenPos, width, height) {
|
||||
this._calcMatrices(width, height);
|
||||
|
||||
let cx = this._rect.x * width;
|
||||
let cy = this._rect.y * height;
|
||||
let cw = this._rect.w * width;
|
||||
let ch = this._rect.h * height;
|
||||
|
||||
if (this._projection === enums.PROJ_PERSPECTIVE) {
|
||||
// calculate screen pos in far clip plane
|
||||
Vec3.set(out,
|
||||
(screenPos.x - cx) / cw * 2 - 1,
|
||||
(screenPos.y - cy) / ch * 2 - 1,
|
||||
0.9999
|
||||
);
|
||||
|
||||
// transform to world
|
||||
Vec3.transformMat4(out, out, _matInvViewProj);
|
||||
|
||||
// lerp to depth z
|
||||
this._node.getWorldPosition(_tmp_v3);
|
||||
|
||||
Vec3.lerp(out, _tmp_v3, out, lerp(this._near / this._far, 1, screenPos.z));
|
||||
} else {
|
||||
Vec3.set(out,
|
||||
(screenPos.x - cx) / cw * 2 - 1,
|
||||
(screenPos.y - cy) / ch * 2 - 1,
|
||||
screenPos.z * 2 - 1
|
||||
);
|
||||
|
||||
// transform to world
|
||||
Vec3.transformMat4(out, out, _matInvViewProj);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* transform a world space position to screen space
|
||||
* @param {Vec3} out the resulting vector
|
||||
* @param {Vec3} worldPos the world space position to be transformed
|
||||
* @param {number} width framebuffer width
|
||||
* @param {number} height framebuffer height
|
||||
* @returns {Vec3} the resulting vector
|
||||
*/
|
||||
worldToScreen (out, worldPos, width, height) {
|
||||
this._calcMatrices(width, height);
|
||||
|
||||
let cx = this._rect.x * width;
|
||||
let cy = this._rect.y * height;
|
||||
let cw = this._rect.w * width;
|
||||
let ch = this._rect.h * height;
|
||||
|
||||
Vec3.transformMat4(out, worldPos, _matViewProj);
|
||||
out.x = cx + (out.x + 1) * 0.5 * cw;
|
||||
out.y = cy + (out.y + 1) * 0.5 * ch;
|
||||
out.z = out.z * 0.5 + 0.5;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* transform a world space matrix to screen space
|
||||
* @param {Mat4} out the resulting vector
|
||||
* @param {Mat4} worldMatrix the world space matrix to be transformed
|
||||
* @param {number} width framebuffer width
|
||||
* @param {number} height framebuffer height
|
||||
* @returns {Mat4} the resulting vector
|
||||
*/
|
||||
worldMatrixToScreen (out, worldMatrix, width, height) {
|
||||
this._calcMatrices(width, height);
|
||||
|
||||
Mat4.mul(out, _matViewProj, worldMatrix);
|
||||
|
||||
let halfWidth = width / 2;
|
||||
let halfHeight = height / 2;
|
||||
Mat4.identity(_tmp_mat4);
|
||||
Mat4.transform(_tmp_mat4, _tmp_mat4, Vec3.set(_tmp_v3, halfWidth, halfHeight, 0));
|
||||
Mat4.scale(_tmp_mat4, _tmp_mat4, Vec3.set(_tmp_v3, halfWidth, halfHeight, 1));
|
||||
|
||||
Mat4.mul(out, _tmp_mat4, out);
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
485
engine/cocos2d/renderer/scene/light.js
Normal file
485
engine/cocos2d/renderer/scene/light.js
Normal file
@@ -0,0 +1,485 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { Mat4, Mat3, Vec3, toRadian } from '../../core/value-types';
|
||||
import gfx from '../gfx';
|
||||
|
||||
import enums from '../enums';
|
||||
|
||||
const _forward = cc.v3(0, 0, -1);
|
||||
|
||||
let _m4_tmp = cc.mat4();
|
||||
let _m3_tmp = Mat3.create();
|
||||
let _transformedLightDirection = cc.v3(0, 0, 0);
|
||||
|
||||
// compute light viewProjMat for shadow.
|
||||
function _computeSpotLightViewProjMatrix(light, outView, outProj) {
|
||||
// view matrix
|
||||
light._node.getWorldRT(outView);
|
||||
Mat4.invert(outView, outView);
|
||||
|
||||
// proj matrix
|
||||
Mat4.perspective(outProj, light._spotAngle * light._spotAngleScale, 1, light._shadowMinDepth, light._shadowMaxDepth);
|
||||
}
|
||||
|
||||
function _computeDirectionalLightViewProjMatrix(light, outView, outProj) {
|
||||
// view matrix
|
||||
light._node.getWorldRT(outView);
|
||||
Mat4.invert(outView, outView);
|
||||
|
||||
// TODO: should compute directional light frustum based on rendered meshes in scene.
|
||||
// proj matrix
|
||||
let halfSize = light._shadowFrustumSize / 2;
|
||||
Mat4.ortho(outProj, -halfSize, halfSize, -halfSize, halfSize, light._shadowMinDepth, light._shadowMaxDepth);
|
||||
}
|
||||
|
||||
function _computePointLightViewProjMatrix(light, outView, outProj) {
|
||||
// view matrix
|
||||
light._node.getWorldRT(outView);
|
||||
Mat4.invert(outView, outView);
|
||||
|
||||
// The transformation from Cartesian to polar coordinates is not a linear function,
|
||||
// so it cannot be achieved by means of a fixed matrix multiplication.
|
||||
// Here we just use a nearly 180 degree perspective matrix instead.
|
||||
Mat4.perspective(outProj, toRadian(179), 1, light._shadowMinDepth, light._shadowMaxDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a light source.
|
||||
* Could be a point light, a spot light or a directional light.
|
||||
*/
|
||||
export default class Light {
|
||||
/**
|
||||
* Setup a default directional light with no shadows
|
||||
*/
|
||||
constructor() {
|
||||
this._poolID = -1;
|
||||
this._node = null;
|
||||
|
||||
this._type = enums.LIGHT_DIRECTIONAL;
|
||||
|
||||
this._color = new Vec3(1, 1, 1);
|
||||
this._intensity = 1;
|
||||
|
||||
// used for spot and point light
|
||||
this._range = 1;
|
||||
// used for spot light, default to 60 degrees
|
||||
this._spotAngle = toRadian(60);
|
||||
this._spotExp = 1;
|
||||
// cached for uniform
|
||||
this._directionUniform = new Float32Array(3);
|
||||
this._positionUniform = new Float32Array(3);
|
||||
this._colorUniform = new Float32Array([this._color.x * this._intensity, this._color.y * this._intensity, this._color.z * this._intensity]);
|
||||
this._spotUniform = new Float32Array([Math.cos(this._spotAngle * 0.5), this._spotExp]);
|
||||
|
||||
// shadow params
|
||||
this._shadowType = enums.SHADOW_NONE;
|
||||
this._shadowFrameBuffer = null;
|
||||
this._shadowMap = null;
|
||||
this._shadowMapDirty = false;
|
||||
this._shadowDepthBuffer = null;
|
||||
this._shadowResolution = 1024;
|
||||
this._shadowBias = 0.0005;
|
||||
this._shadowDarkness = 1;
|
||||
this._shadowMinDepth = 1;
|
||||
this._shadowMaxDepth = 1000;
|
||||
this._frustumEdgeFalloff = 0; // used by directional and spot light.
|
||||
this._viewProjMatrix = cc.mat4();
|
||||
this._spotAngleScale = 1; // used for spot light.
|
||||
this._shadowFrustumSize = 50; // used for directional light.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hosting node of this camera
|
||||
* @returns {Node} the hosting node
|
||||
*/
|
||||
getNode() {
|
||||
return this._node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hosting node of this camera
|
||||
* @param {Node} node the hosting node
|
||||
*/
|
||||
setNode(node) {
|
||||
this._node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the color of the light source
|
||||
* @param {number} r red channel of the light color
|
||||
* @param {number} g green channel of the light color
|
||||
* @param {number} b blue channel of the light color
|
||||
*/
|
||||
setColor(r, g, b) {
|
||||
Vec3.set(this._color, r, g, b);
|
||||
this._colorUniform[0] = r * this._intensity;
|
||||
this._colorUniform[1] = g * this._intensity;
|
||||
this._colorUniform[2] = b * this._intensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the color of the light source
|
||||
* @returns {Vec3} the light color
|
||||
*/
|
||||
get color() {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the intensity of the light source
|
||||
* @param {number} val the light intensity
|
||||
*/
|
||||
setIntensity(val) {
|
||||
this._intensity = val;
|
||||
this._colorUniform[0] = val * this._color.x;
|
||||
this._colorUniform[1] = val * this._color.y;
|
||||
this._colorUniform[2] = val * this._color.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the intensity of the light source
|
||||
* @returns {number} the light intensity
|
||||
*/
|
||||
get intensity() {
|
||||
return this._intensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the type of the light source
|
||||
* @param {number} type light source type
|
||||
*/
|
||||
setType(type) {
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the type of the light source
|
||||
* @returns {number} light source type
|
||||
*/
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the spot light angle
|
||||
* @param {number} val spot light angle
|
||||
*/
|
||||
setSpotAngle(val) {
|
||||
this._spotAngle = val;
|
||||
this._spotUniform[0] = Math.cos(this._spotAngle * 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the spot light angle
|
||||
* @returns {number} spot light angle
|
||||
*/
|
||||
get spotAngle() {
|
||||
return this._spotAngle;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the spot light exponential
|
||||
* @param {number} val spot light exponential
|
||||
*/
|
||||
setSpotExp(val) {
|
||||
this._spotExp = val;
|
||||
this._spotUniform[1] = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the spot light exponential
|
||||
* @returns {number} spot light exponential
|
||||
*/
|
||||
get spotExp() {
|
||||
return this._spotExp;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the range of the light source
|
||||
* @param {number} val light source range
|
||||
*/
|
||||
setRange(val) {
|
||||
this._range = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the range of the light source
|
||||
* @returns {number} range of the light source
|
||||
*/
|
||||
get range() {
|
||||
return this._range;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow type of the light source
|
||||
* @param {number} type light source shadow type
|
||||
*/
|
||||
setShadowType(type) {
|
||||
if (this._shadowType === enums.SHADOW_NONE && type !== enums.SHADOW_NONE) {
|
||||
this._shadowMapDirty = true;
|
||||
}
|
||||
this._shadowType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow type of the light source
|
||||
* @returns {number} light source shadow type
|
||||
*/
|
||||
get shadowType() {
|
||||
return this._shadowType;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadowmap of the light source
|
||||
* @returns {Texture2D} light source shadowmap
|
||||
*/
|
||||
get shadowMap() {
|
||||
return this._shadowMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the view-projection matrix of the light source
|
||||
* @returns {Mat4} light source view-projection matrix
|
||||
*/
|
||||
get viewProjMatrix() {
|
||||
return this._viewProjMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow resolution of the light source
|
||||
* @param {number} val light source shadow resolution
|
||||
*/
|
||||
setShadowResolution(val) {
|
||||
if (this._shadowResolution !== val) {
|
||||
this._shadowMapDirty = true;
|
||||
}
|
||||
this._shadowResolution = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow resolution of the light source
|
||||
* @returns {number} light source shadow resolution
|
||||
*/
|
||||
get shadowResolution() {
|
||||
return this._shadowResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow bias of the light source
|
||||
* @param {number} val light source shadow bias
|
||||
*/
|
||||
setShadowBias(val) {
|
||||
this._shadowBias = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow bias of the light source
|
||||
* @returns {number} light source shadow bias
|
||||
*/
|
||||
get shadowBias() {
|
||||
return this._shadowBias;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow darkness of the light source
|
||||
* @param {number} val light source shadow darkness
|
||||
*/
|
||||
setShadowDarkness(val) {
|
||||
this._shadowDarkness = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow darkness of the light source
|
||||
* @returns {number} light source shadow darkness
|
||||
*/
|
||||
get shadowDarkness() {
|
||||
return this._shadowDarkness;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow min depth of the light source
|
||||
* @param {number} val light source shadow min depth
|
||||
*/
|
||||
setShadowMinDepth(val) {
|
||||
this._shadowMinDepth = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow min depth of the light source
|
||||
* @returns {number} light source shadow min depth
|
||||
*/
|
||||
get shadowMinDepth() {
|
||||
if (this._type === enums.LIGHT_DIRECTIONAL) {
|
||||
return 1.0;
|
||||
}
|
||||
return this._shadowMinDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow max depth of the light source
|
||||
* @param {number} val light source shadow max depth
|
||||
*/
|
||||
setShadowMaxDepth(val) {
|
||||
this._shadowMaxDepth = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow max depth of the light source
|
||||
* @returns {number} light source shadow max depth
|
||||
*/
|
||||
get shadowMaxDepth() {
|
||||
if (this._type === enums.LIGHT_DIRECTIONAL) {
|
||||
return 1.0;
|
||||
}
|
||||
return this._shadowMaxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the frustum edge falloff of the light source
|
||||
* @param {number} val light source frustum edge falloff
|
||||
*/
|
||||
setFrustumEdgeFalloff(val) {
|
||||
this._frustumEdgeFalloff = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the frustum edge falloff of the light source
|
||||
* @returns {number} light source frustum edge falloff
|
||||
*/
|
||||
get frustumEdgeFalloff() {
|
||||
return this._frustumEdgeFalloff;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the shadow frustum size of the light source
|
||||
* @param {number} val light source shadow frustum size
|
||||
*/
|
||||
setShadowFrustumSize(val) {
|
||||
this._shadowFrustumSize = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the shadow frustum size of the light source
|
||||
* @returns {number} light source shadow frustum size
|
||||
*/
|
||||
get shadowFrustumSize() {
|
||||
return this._shadowFrustumSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* extract a view of this light source
|
||||
* @param {View} out the receiving view
|
||||
* @param {string[]} stages the stages using the view
|
||||
*/
|
||||
extractView(out, stages) {
|
||||
// TODO: view should not handle light.
|
||||
out._shadowLight = this;
|
||||
|
||||
// priority. TODO: use varying value for shadow view?
|
||||
out._priority = -1;
|
||||
|
||||
// rect
|
||||
out._rect.x = 0;
|
||||
out._rect.y = 0;
|
||||
out._rect.w = this._shadowResolution;
|
||||
out._rect.h = this._shadowResolution;
|
||||
|
||||
// clear opts
|
||||
Vec3.set(out._color, 1, 1, 1);
|
||||
out._depth = 1;
|
||||
out._stencil = 1;
|
||||
out._clearFlags = enums.CLEAR_COLOR | enums.CLEAR_DEPTH;
|
||||
|
||||
// stages & framebuffer
|
||||
out._stages = stages;
|
||||
out._framebuffer = this._shadowFrameBuffer;
|
||||
|
||||
// view projection matrix
|
||||
switch(this._type) {
|
||||
case enums.LIGHT_SPOT:
|
||||
_computeSpotLightViewProjMatrix(this, out._matView, out._matProj);
|
||||
break;
|
||||
|
||||
case enums.LIGHT_DIRECTIONAL:
|
||||
_computeDirectionalLightViewProjMatrix(this, out._matView, out._matProj);
|
||||
break;
|
||||
|
||||
case enums.LIGHT_POINT:
|
||||
_computePointLightViewProjMatrix(this, out._matView, out._matProj);
|
||||
break;
|
||||
case enums.LIGHT_AMBIENT:
|
||||
break;
|
||||
default:
|
||||
console.warn('shadow of this light type is not supported');
|
||||
}
|
||||
|
||||
// view-projection
|
||||
Mat4.mul(out._matViewProj, out._matProj, out._matView);
|
||||
this._viewProjMatrix = out._matViewProj;
|
||||
Mat4.invert(out._matInvViewProj, out._matViewProj);
|
||||
|
||||
// update view's frustum
|
||||
// out._frustum.update(out._matViewProj, out._matInvViewProj);
|
||||
|
||||
out._cullingMask = 0xffffffff;
|
||||
}
|
||||
|
||||
_updateLightPositionAndDirection() {
|
||||
this._node.getWorldMatrix(_m4_tmp);
|
||||
Mat3.fromMat4(_m3_tmp, _m4_tmp);
|
||||
Vec3.transformMat3(_transformedLightDirection, _forward, _m3_tmp);
|
||||
Vec3.toArray(this._directionUniform, _transformedLightDirection);
|
||||
let pos = this._positionUniform;
|
||||
let m = _m4_tmp.m;
|
||||
pos[0] = m[12];
|
||||
pos[1] = m[13];
|
||||
pos[2] = m[14];
|
||||
}
|
||||
|
||||
_generateShadowMap(device) {
|
||||
this._shadowMap = new gfx.Texture2D(device, {
|
||||
width: this._shadowResolution,
|
||||
height: this._shadowResolution,
|
||||
format: gfx.TEXTURE_FMT_RGBA8,
|
||||
wrapS: gfx.WRAP_CLAMP,
|
||||
wrapT: gfx.WRAP_CLAMP,
|
||||
});
|
||||
this._shadowDepthBuffer = new gfx.RenderBuffer(device,
|
||||
gfx.RB_FMT_D16,
|
||||
this._shadowResolution,
|
||||
this._shadowResolution
|
||||
);
|
||||
this._shadowFrameBuffer = new gfx.FrameBuffer(device, this._shadowResolution, this._shadowResolution, {
|
||||
colors: [this._shadowMap],
|
||||
depth: this._shadowDepthBuffer,
|
||||
});
|
||||
}
|
||||
|
||||
_destroyShadowMap() {
|
||||
if (this._shadowMap) {
|
||||
this._shadowMap.destroy();
|
||||
this._shadowDepthBuffer.destroy();
|
||||
this._shadowFrameBuffer.destroy();
|
||||
this._shadowMap = null;
|
||||
this._shadowDepthBuffer = null;
|
||||
this._shadowFrameBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* update the light source
|
||||
* @param {Device} device the rendering device
|
||||
*/
|
||||
update(device) {
|
||||
this._updateLightPositionAndDirection();
|
||||
|
||||
if (this._shadowType === enums.SHADOW_NONE) {
|
||||
this._destroyShadowMap();
|
||||
} else if (this._shadowMapDirty) {
|
||||
this._destroyShadowMap();
|
||||
this._generateShadowMap(device);
|
||||
this._shadowMapDirty = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
65
engine/cocos2d/renderer/scene/model.js
Normal file
65
engine/cocos2d/renderer/scene/model.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
/**
|
||||
* A representation of a model
|
||||
*/
|
||||
export default class Model {
|
||||
/**
|
||||
* Setup a default empty model
|
||||
*/
|
||||
constructor() {
|
||||
this._type = 'default';
|
||||
this._poolID = -1;
|
||||
this._node = null;
|
||||
this._inputAssembler = null;
|
||||
this._effect = null;
|
||||
this._viewID = -1;
|
||||
this._cameraID = -1;
|
||||
this._userKey = -1;
|
||||
this._castShadow = false;
|
||||
this._boundingShape = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hosting node of this model
|
||||
* @param {Node} node the hosting node
|
||||
*/
|
||||
setNode(node) {
|
||||
this._node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the input assembler
|
||||
* @param {InputAssembler} ia
|
||||
*/
|
||||
setInputAssembler(ia) {
|
||||
this._inputAssembler = ia;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the model effect
|
||||
* @param {?Effect} effect the effect to use
|
||||
*/
|
||||
setEffect(effect) {
|
||||
this._effect = effect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user key
|
||||
* @param {number} key
|
||||
*/
|
||||
setUserKey(key) {
|
||||
this._userKey = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a drawing item
|
||||
* @param {Object} out the receiving item
|
||||
*/
|
||||
extractDrawItem(out) {
|
||||
out.model = this;
|
||||
out.node = this._node;
|
||||
out.ia = this._inputAssembler;
|
||||
out.effect = this._effect;
|
||||
}
|
||||
}
|
181
engine/cocos2d/renderer/scene/scene.js
Normal file
181
engine/cocos2d/renderer/scene/scene.js
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
import { FixedArray } from '../memop';
|
||||
|
||||
/**
|
||||
* A representation of the scene
|
||||
*/
|
||||
class Scene {
|
||||
/**
|
||||
* Setup a default empty scene
|
||||
*/
|
||||
constructor(app) {
|
||||
this._lights = new FixedArray(16);
|
||||
this._models = new FixedArray(16);
|
||||
this._cameras = new FixedArray(16);
|
||||
this._debugCamera = null;
|
||||
this._app = app;
|
||||
|
||||
// NOTE: we don't use pool for views (because it's less changed and it doesn't have poolID)
|
||||
this._views = [];
|
||||
}
|
||||
|
||||
_add(pool, item) {
|
||||
if (item._poolID !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
pool.push(item);
|
||||
item._poolID = pool.length - 1;
|
||||
}
|
||||
|
||||
_remove(pool, item) {
|
||||
if (item._poolID === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
pool.data[pool.length-1]._poolID = item._poolID;
|
||||
pool.fastRemove(item._poolID);
|
||||
item._poolID = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the model viewIDs
|
||||
*/
|
||||
reset() {
|
||||
for (let i = 0; i < this._models.length; ++i) {
|
||||
let model = this._models.data[i];
|
||||
model._viewID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the debug camera
|
||||
* @param {Camera} cam the debug camera
|
||||
*/
|
||||
setDebugCamera(cam) {
|
||||
this._debugCamera = cam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of registered cameras
|
||||
* @returns {number} camera count
|
||||
*/
|
||||
getCameraCount() {
|
||||
return this._cameras.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified camera
|
||||
* @param {number} idx camera index
|
||||
* @returns {Camera} the specified camera
|
||||
*/
|
||||
getCamera(idx) {
|
||||
return this._cameras.data[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* register a camera
|
||||
* @param {Camera} camera the new camera
|
||||
*/
|
||||
addCamera(camera) {
|
||||
this._add(this._cameras, camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a camera
|
||||
* @param {Camera} camera the camera to be removed
|
||||
*/
|
||||
removeCamera(camera) {
|
||||
this._remove(this._cameras, camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of registered model
|
||||
* @returns {number} model count
|
||||
*/
|
||||
getModelCount() {
|
||||
return this._models.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified model
|
||||
* @param {number} idx model index
|
||||
* @returns {Model} the specified model
|
||||
*/
|
||||
getModel(idx) {
|
||||
return this._models.data[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* register a model
|
||||
* @param {Model} model the new model
|
||||
*/
|
||||
addModel(model) {
|
||||
this._add(this._models, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a model
|
||||
* @param {Model} model the model to be removed
|
||||
*/
|
||||
removeModel(model) {
|
||||
this._remove(this._models, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of registered light
|
||||
* @returns {number} light count
|
||||
*/
|
||||
getLightCount() {
|
||||
return this._lights.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified light
|
||||
* @param {number} idx light index
|
||||
* @returns {Light} the specified light
|
||||
*/
|
||||
getLight(idx) {
|
||||
return this._lights.data[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* register a light
|
||||
* @param {Light} light the new light
|
||||
*/
|
||||
addLight(light) {
|
||||
this._add(this._lights, light);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a light
|
||||
* @param {Light} light the light to be removed
|
||||
*/
|
||||
removeLight(light) {
|
||||
this._remove(this._lights, light);
|
||||
}
|
||||
|
||||
/**
|
||||
* register a view
|
||||
* @param {View} view the new view
|
||||
*/
|
||||
addView(view) {
|
||||
if (this._views.indexOf(view) === -1) {
|
||||
this._views.push(view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a view
|
||||
* @param {View} view the view to be removed
|
||||
*/
|
||||
removeView(view) {
|
||||
let idx = this._views.indexOf(view);
|
||||
if (idx !== -1) {
|
||||
this._views.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Scene;
|
117
engine/cocos2d/renderer/types.js
Normal file
117
engine/cocos2d/renderer/types.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import enums from './enums';
|
||||
import { Vec2, Vec3, Vec4, Color, Mat4 } from '../core/value-types';
|
||||
import Texture2D from '../core/assets/CCTexture2D';
|
||||
|
||||
let gfxTexture2D = null, gfxTextureCube = null;
|
||||
if (CC_JSB && CC_NATIVERENDERER) {
|
||||
gfxTexture2D = gfx.Texture2D;
|
||||
} else {
|
||||
gfxTexture2D = require('./gfx/texture-2d');
|
||||
}
|
||||
|
||||
const CCObject = cc.Object;
|
||||
|
||||
let ctor2default = {
|
||||
[Boolean]: v => v || false,
|
||||
[Number]: v => v ? (ArrayBuffer.isView(v) ? v[0] : v) : 0,
|
||||
[Vec2]: v => v ? cc.v2(v[0], v[1]) : cc.v2(),
|
||||
[Vec3]: v => v ? cc.v3(v[0], v[1], v[2]) : cc.v3(),
|
||||
[Vec4]: v => v ? cc.v4(v[0], v[1], v[2], v[3]) : cc.v4(),
|
||||
[Color]: v => v ? cc.color(v[0] * 255, v[1] * 255, v[2] * 255,
|
||||
(v[3] || 1) * 255) : cc.color(),
|
||||
[Mat4]: v => v ? cc.mat4(
|
||||
v[0], v[1], v[2], v[3],
|
||||
v[4], v[5], v[6], v[7],
|
||||
v[8], v[9], v[10], v[11],
|
||||
v[12], v[13], v[14], v[15],
|
||||
) : cc.mat4(),
|
||||
[Texture2D]: () => null,
|
||||
[CCObject]: () => null
|
||||
};
|
||||
|
||||
|
||||
let enums2ctor = {
|
||||
[enums.PARAM_INT]: Number,
|
||||
[enums.PARAM_INT2]: Vec2,
|
||||
[enums.PARAM_INT3]: Vec3,
|
||||
[enums.PARAM_INT4]: Vec4,
|
||||
[enums.PARAM_FLOAT]: Number,
|
||||
[enums.PARAM_FLOAT2]: Vec2,
|
||||
[enums.PARAM_FLOAT3]: Vec3,
|
||||
[enums.PARAM_FLOAT4]: Vec4,
|
||||
[enums.PARAM_MAT4]: Mat4,
|
||||
[enums.PARAM_TEXTURE_2D]: Texture2D,
|
||||
|
||||
color: Color,
|
||||
number: Number,
|
||||
boolean: Boolean,
|
||||
default: CCObject
|
||||
};
|
||||
|
||||
export let ctor2enums = {
|
||||
[Number]: enums.PARAM_FLOAT,
|
||||
[Vec2]: enums.PARAM_FLOAT2,
|
||||
[Vec3]: enums.PARAM_FLOAT3,
|
||||
[Vec4]: enums.PARAM_FLOAT4,
|
||||
[Color]: enums.PARAM_COLOR3,
|
||||
[Color]: enums.PARAM_COLOR4,
|
||||
[Mat4]: enums.PARAM_MAT4,
|
||||
[Texture2D]: enums.PARAM_TEXTURE_2D,
|
||||
[gfxTexture2D]: enums.PARAM_TEXTURE_2D,
|
||||
};
|
||||
|
||||
export let enums2default = {
|
||||
[enums.PARAM_INT]: new Uint32Array([0]),
|
||||
[enums.PARAM_INT2]: new Uint32Array([0, 0]),
|
||||
[enums.PARAM_INT3]: new Uint32Array([0, 0, 0]),
|
||||
[enums.PARAM_INT4]: new Uint32Array([0, 0, 0, 0]),
|
||||
[enums.PARAM_FLOAT]: new Float32Array([0]),
|
||||
[enums.PARAM_FLOAT2]: new Float32Array([0, 0]),
|
||||
[enums.PARAM_FLOAT3]: new Float32Array([0, 0, 0]),
|
||||
[enums.PARAM_FLOAT4]: new Float32Array([0, 0, 0, 0]),
|
||||
[enums.PARAM_MAT4]: new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
[enums.PARAM_TEXTURE_2D]: null,
|
||||
|
||||
number: 0,
|
||||
boolean: false,
|
||||
}
|
||||
|
||||
export let getInstanceType = function (t) {
|
||||
return enums2ctor[t] || enums2ctor.default;
|
||||
};
|
||||
export let getInstanceCtor = function (t) {
|
||||
return ctor2default[getInstanceType(t)];
|
||||
};
|
||||
export let getClassName = function (t) {
|
||||
return cc.js.getClassName(getInstanceType(t));
|
||||
};
|
||||
|
||||
let className2InspectorName = {
|
||||
Number: 'number',
|
||||
Boolean: 'boolean'
|
||||
};
|
||||
export function getInspectorProps (prop) {
|
||||
let editor = {
|
||||
type: prop.type
|
||||
};
|
||||
|
||||
Object.assign(editor, prop.editor || prop.inspector);
|
||||
|
||||
editor.defines = prop.defines;
|
||||
editor.value = getInstanceCtor(editor.type)(prop.value);
|
||||
if (prop.range) {
|
||||
editor.range = prop.range;
|
||||
}
|
||||
|
||||
let className = getClassName(editor.type);
|
||||
editor.typeName = className2InspectorName[className] || className;
|
||||
|
||||
editor.valueCtor = enums2ctor[editor.type];
|
||||
|
||||
if (editor.typeName == 'cc.Texture2D') {
|
||||
editor.typeName = 'cc.Asset';
|
||||
editor.assetType = 'cc.Texture2D';
|
||||
}
|
||||
|
||||
return editor;
|
||||
};
|
Reference in New Issue
Block a user