/******************************************************************************* * * Nx JS扩展 * * * * 2021.12.10 ******************************************************************************/ const NxObject = require( "nx.object" ); // DataView扩展 const exDataView = function() { // DataView 64位UINT解析 DataView.prototype.getUint64 = function( byteOffset, littleEndian ) { const left = this.getUint32( byteOffset, littleEndian ); const right = this.getUint32( byteOffset + 4, littleEndian ); const combine = littleEndian ? ( left + 2 ** 32 * right ) : ( 2 ** 32 * left + right ); if( !Number.isSafeInteger( combine ) ) { cc.warn( "64Int精度丢失!" ); } return combine; } }; // MeshBuffer扩展 const exMeshBuffer = function() { const FIX_IOS14_BUFFER = ( cc.sys.os === cc.sys.OS_IOS || cc.sys.os === cc.sys.MACOS ) && cc.sys.isBrowser && /(OS 1[4-9])|(Version\/1[4-9])/.test( window.navigator.userAgent ); if( FIX_IOS14_BUFFER ) { cc.MeshBuffer.prototype.checkAndSwitchBuffer = function( vertexCount ) { if( this.vertexOffset + vertexCount > 65535 ) { this.uploadData(); this._batcher._flush(); } }; } }; // RenderFlower扩展 const exRenderFlower = function() { let FlagOfset = 0; const DONOTHING = 1 << FlagOfset++; const BREAK_FLOW = 1 << FlagOfset++; const LOCAL_TRANSFORM = 1 << FlagOfset++; const WORLD_TRANSFORM = 1 << FlagOfset++; const TRANSFORM = LOCAL_TRANSFORM | WORLD_TRANSFORM; const UPDATE_RENDER_DATA = 1 << FlagOfset++; const OPACITY = 1 << FlagOfset++; const COLOR = 1 << FlagOfset++; const OPACITY_COLOR = OPACITY | COLOR; const RENDER = 1 << FlagOfset++; const CHILDREN = 1 << FlagOfset++; const POST_RENDER = 1 << FlagOfset++; const FINAL = 1 << FlagOfset++; const renderFlow = cc[ "RenderFlow" ]; const _batcher = renderFlow.getBachther(); let __levelBatcher = []; let __renderQueue = []; Object.defineProperty( renderFlow.prototype, "_opacity", { value: function( node ) { _batcher.parentOpacityDirty++; if( node[ "__levelRender" ] && __levelBatcher[ node[ "__itemIndex" ] ] ) { __levelBatcher[ node[ "__itemIndex" ] ].parentOpacityDirty = 1; } this._next._func( node ); node._renderFlag &= ~OPACITY; if( node[ "__levelRender" ] && __levelBatcher[ node[ "__itemIndex" ] ] && node[ "__lastChildren" ] ) { __levelBatcher[ node.__itemIndex ].parentOpacityDirty = 0; } _batcher.parentOpacityDirty--; } } ) Object.defineProperty( renderFlow.prototype, "_worldTransform", { value: function( node ) { _batcher.worldMatDirty++; if( node[ "__levelRender" ] && __levelBatcher[ node[ "__itemIndex" ] ] ) { __levelBatcher[ node[ "__itemIndex" ] ].worldMatDirty = 1; } let t = node._matrix; let trs = node._trs; let tm = t.m; tm[ 12 ] = trs[ 0 ]; tm[ 13 ] = trs[ 1 ]; tm[ 14 ] = trs[ 2 ]; node._mulMat( node._worldMatrix, node._parent._worldMatrix, t ); node._renderFlag &= ~WORLD_TRANSFORM; this._next._func( node ); if( node[ "__levelRender" ] && __levelBatcher[ node[ "__itemIndex" ] ] && node[ "__lastChildren" ] ) { __levelBatcher[ node.__itemIndex ].worldMatDirty = 0; } _batcher.worldMatDirty--; } } ); const levelSplit = ( node, lv, itemIndex ) => { if( !__renderQueue[ lv ] ) { __renderQueue[ lv ] = []; } __renderQueue[ lv ].push( node ); lv++; node[ "__renderLv" ] = lv; node[ "__levelRender" ] = true; node[ "__itemIndex" ] = itemIndex; const cs = node.children; for( let i = 0; i < cs.length; ++i ) { const c = cs[ i ]; if( !__renderQueue[ lv ] ) { __renderQueue[ lv ] = []; } lv = levelSplit( c, lv, itemIndex ); } return lv; } const checkLevelRender = ( levelRenderNode ) => { const cs = levelRenderNode.children; let rootOpacityInHierarchy = levelRenderNode[ "opacity" ] / 255; __levelBatcher = []; for( let i = 0; i < cs.length; ++i ) { __levelBatcher.push( { worldMatDirty: 0, parentOpacityDirty: 0 } ); levelSplit( cs[ i ], 0, i ); } while( __renderQueue.length > 0 ) { const list = __renderQueue.shift(); if( list.length > 0 ) { while( list.length > 0 ) { const n = list.shift(); n[ "__lastChildren" ] = __renderQueue.length == 0; n[ "__levelRender" ] = true; let opacityInHierarchy = n.parent[ "__opacityInHierarchy" ]; if( opacityInHierarchy === undefined ) { opacityInHierarchy = rootOpacityInHierarchy; } let opacity = ( opacityInHierarchy * ( n[ "_opacity" ] / 255 ) ); n[ "__opacityInHierarchy" ] = opacity; let cullingMask = n[ "_cullingMask" ]; let worldMatDirty = 0; if( __levelBatcher[ n[ "__itemIndex" ] ] ) { worldMatDirty = __levelBatcher[ n[ "__itemIndex" ] ].worldMatDirty || 0; } let parentOpacityDirty = 0; if( __levelBatcher[ n[ "__itemIndex" ] ] ) { parentOpacityDirty = __levelBatcher[ n[ "__itemIndex" ] ].parentOpacityDirty || 0; } let worldTransformFlag = ( worldMatDirty || _batcher.worldMatDirty ) ? WORLD_TRANSFORM : 0; let worldOpacityFlag = ( parentOpacityDirty || _batcher.parentOpacityDirty ) ? OPACITY_COLOR : 0; let worldDirtyFlag = worldTransformFlag | worldOpacityFlag; n[ "_renderFlag" ] |= worldDirtyFlag; if( !n[ "_activeInHierarchy" ] || n[ "_opacity" ] === 0 ) continue; n[ "_cullingMask" ] = n.groupIndex === 0 ? cullingMask : 1 << n.groupIndex; // TODO: Maybe has better way to implement cascade opacity let colorVal = n[ "_color" ]._val; n[ "_color" ]._fastSetA( n[ "_opacity" ] * opacity ); renderFlow.flows[ n[ "_renderFlag" ] ]._func( n ); n[ "_color" ]._val = colorVal; } } } } Object.defineProperty( renderFlow.prototype, "_children", { value: function( node ) { if( node.__levelRender ) return; let cullingMask = node._cullingMask; let enableLevelRender = node[ "__enableLevelRender" ]; const parentOpacityInHierarchy = node.parent ? node.parent[ "__opacityInHierarchy" ] : undefined; let parentOpacity = parentOpacityInHierarchy !== undefined ? parentOpacityInHierarchy : _batcher.parentOpacity; if( !enableLevelRender && !node.__levelRender ) { let opacity = ( parentOpacity *= ( node._opacity / 255 ) ); node[ "__opacityInHierarchy" ] = opacity; let worldTransformFlag = _batcher.worldMatDirty ? WORLD_TRANSFORM : 0; let worldOpacityFlag = _batcher.parentOpacityDirty ? OPACITY_COLOR : 0; let worldDirtyFlag = worldTransformFlag | worldOpacityFlag; let children = node._children; for( let i = 0, l = children.length; i < l; i++ ) { let c = children[ i ]; // Advance the modification of the flag to avoid node attribute modification is invalid when opacity === 0. c._renderFlag |= worldDirtyFlag; c[ "__opacityInHierarchy" ] = c._opacity * opacity / 255; if( !c._activeInHierarchy || c._opacity === 0 ) continue; c._cullingMask = c.groupIndex === 0 ? cullingMask : 1 << c.groupIndex; // TODO: Maybe has better way to implement cascade opacity let colorVal = c._color._val; c._color._fastSetA( c._opacity * opacity ); renderFlow.flows[ c._renderFlag ]._func( c ) c._color._val = colorVal; } } else { checkLevelRender( node ); } _batcher.parentOpacity = parentOpacity; this._next._func( node ); } } ) }; const nxJS = cc.Class( { extends: NxObject, name: "nxJS", // 初始化 initialize: function( _args ) { // USPER this._super( _args ); // DataView扩展 exDataView(); // MeshBuffer扩展 exMeshBuffer(); // RenderFlower扩展 exRenderFlower(); return true; }, // 销毁 uninitialize: function() { // USPER return this._super(); }, } ); // 模块导出 module.exports = nxJS;