import ArrayBufferSlice from '../ArrayBufferSlice' ; import { align , assert , hexzero , assertExists , nArray } from '../util' ; import * as GX from './gx_enum' ; import { Endianness , getSystemEndianness } from '../endian' ; import { GfxFormat , FormatCompFlags , FormatTypeFlags , getFormatCompByteSize , getFormatCompFlagsComponentCount , getFormatTypeFlags , getFormatComponentCount , getFormatFlags , FormatFlags , makeFormat , setFormatFlags } from '../gfx/platform/GfxPlatformFormat' ; import { HashMap , nullHashFunc } from '../HashMap' ; import { arrayCopy , arrayEqual } from '../gfx/platform/GfxPlatformUtil' ; export interface GX_VtxAttrFmt { compType : GX . CompType ; compCnt : GX . CompCnt ; compShift : number ; } export interface GX_VtxDesc { type : GX . AttrType ; enableOutput ? : boolean ; } export interface GX_Array { buffer : ArrayBufferSlice ; offs : number ; stride : number ; } export const enum VertexAttributeInput { TEX0123MTXIDX , TEX4567MTXIDX , POS , NRM , BINRM , TANGENT , CLR0 , CLR1 , TEX01 , TEX23 , TEX45 , TEX67 , COUNT } function getAttrInputForAttr ( attrib ) { if ( attrib === GX . Attr . POS ) return VertexAttributeInput . POS ; else if ( attrib === GX . Attr . NRM ) return VertexAttributeInput . NRM ; else if ( attrib === GX . Attr . CLR0 ) return VertexAttributeInput . CLR0 ; else if ( attrib === GX . Attr . CLR1 ) return VertexAttributeInput . CLR1 ; else throw "whoops" ; } export interface SingleVertexInputLayout { attrInput : VertexAttributeInput ; bufferOffset : number ; bufferIndex : number ; format : GfxFormat ; } interface VatLayout { srcVertexSize : number ; vatFormat : GX_VtxAttrFmt [ ] ; vcd : GX_VtxDesc [ ] ; } export interface LoadedVertexLayout { indexFormat : GfxFormat ; vertexBufferStrides : number [ ] ; singleVertexInputLayouts : SingleVertexInputLayout [ ] ; vertexAttributeOffsets : number [ ] ; vertexAttributeFormats : GfxFormat [ ] ; } interface VertexLayout extends LoadedVertexLayout , VtxLoaderDesc { vatLayouts : ( VatLayout | undefined ) [ ] ; } export interface LoadedVertexDraw { indexOffset : number ; indexCount : number ; posMatrixTable : number [ ] ; texMatrixTable : number [ ] ; } export interface LoadedVertexData { indexData : ArrayBufferLike ; vertexBuffers : ArrayBufferLike [ ] ; totalIndexCount : number ; totalVertexCount : number ; vertexId : number ; draws : LoadedVertexDraw [ ] ; dlView : DataView | null ; drawCalls : DrawCall [ ] | null ; } export interface LoadOptions { firstVertexId ? : number ; } export interface VtxLoader { loadedVertexLayout : LoadedVertexLayout ; parseDisplayList : ( srcBuffer : ArrayBufferSlice , loadOptions ? : LoadOptions ) => LoadedVertexData ; loadVertexDataInto : ( dst : DataView , dstOffs : number , loadedVertexData : LoadedVertexData , vtxArrays : GX_Array [ ] ) => void ; loadVertexData : ( loadedVertexData : LoadedVertexData , vtxArrays : GX_Array [ ] ) => void ; runVertices : ( vtxArrays : GX_Array [ ] , srcBuffer : ArrayBufferSlice , loadOptions ? : LoadOptions ) => LoadedVertexData ; } type CompSize = 1 | 2 | 4 ; export function getAttributeComponentByteSizeRaw ( compType ) { switch ( compType ) { case GX . CompType . U8 : case GX . CompType . S8 : case GX . CompType . RGBA8 : return 1 ; case GX . CompType . U16 : case GX . CompType . S16 : return 2 ; case GX . CompType . F32 : return 4 ; } } function isVtxAttribMtxIdx ( vtxAttrib ) { return vtxAttrib === GX . Attr . PNMTXIDX || isVtxAttribTexMtxIdx ( vtxAttrib ) ; } function isVtxAttribTexMtxIdx ( vtxAttrib ) { switch ( vtxAttrib ) { case GX . Attr . TEX0MTXIDX : case GX . Attr . TEX1MTXIDX : case GX . Attr . TEX2MTXIDX : case GX . Attr . TEX3MTXIDX : case GX . Attr . TEX4MTXIDX : case GX . Attr . TEX5MTXIDX : case GX . Attr . TEX6MTXIDX : case GX . Attr . TEX7MTXIDX : return true ; default : return false ; } } function isVtxAttribColor ( vtxAttrib ) { switch ( vtxAttrib ) { case GX . Attr . CLR0 : case GX . Attr . CLR1 : return true ; default : return false ; } } function isVtxAttribTex ( vtxAttrib ) { switch ( vtxAttrib ) { case GX . Attr . TEX0 : case GX . Attr . TEX1 : case GX . Attr . TEX2 : case GX . Attr . TEX3 : case GX . Attr . TEX4 : case GX . Attr . TEX5 : case GX . Attr . TEX6 : case GX . Attr . TEX7 : return true ; default : return false ; } } function getAttributeComponentByteSize ( vtxAttrib , vatFormat ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return 1 ; return getAttributeComponentByteSizeRaw ( vatFormat . compType ) ; } function getAttributeByteSizeRaw ( vtxAttrib , vatFormat ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return 1 ; if ( isVtxAttribColor ( vtxAttrib ) ) { switch ( vatFormat . compType ) { case GX . CompType . RGB565 : return 2 ; case GX . CompType . RGB8 : return 3 ; case GX . CompType . RGBX8 : return 4 ; case GX . CompType . RGBA4 : return 2 ; case GX . CompType . RGBA6 : return 3 ; case GX . CompType . RGBA8 : return 4 ; } } const compSize = getAttributeComponentByteSize ( vtxAttrib , vatFormat ) ; const compCount = getAttributeComponentCount ( vtxAttrib , vatFormat ) ; return compSize * compCount ; } export function getAttributeByteSize ( vat , vtxAttrib ) { return getAttributeByteSizeRaw ( vtxAttrib , vat [ vtxAttrib ] ) ; } export function getAttributeFormatCompFlagsRaw ( vtxAttrib , compCnt ) { switch ( vtxAttrib ) { case GX . Attr . POS : if ( compCnt === GX . CompCnt . POS_XY ) return FormatCompFlags . RG ; else if ( compCnt === GX . CompCnt . POS_XYZ ) return FormatCompFlags . RGB ; case GX . Attr . NRM : return FormatCompFlags . RGB ; case GX . Attr . CLR0 : case GX . Attr . CLR1 : if ( compCnt === GX . CompCnt . CLR_RGB ) return FormatCompFlags . RGB ; else if ( compCnt === GX . CompCnt . CLR_RGBA ) return FormatCompFlags . RGBA ; case GX . Attr . TEX0 : case GX . Attr . TEX1 : case GX . Attr . TEX2 : case GX . Attr . TEX3 : case GX . Attr . TEX4 : case GX . Attr . TEX5 : case GX . Attr . TEX6 : case GX . Attr . TEX7 : if ( compCnt === GX . CompCnt . TEX_S ) return FormatCompFlags . R ; else if ( compCnt === GX . CompCnt . TEX_ST ) return FormatCompFlags . RG ; case GX . Attr . NULL : default : throw new Error ( "whoops" ) ; } } function getAttributeFormatCompFlags ( vtxAttrib , vatFormat ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return FormatCompFlags . R ; return getAttributeFormatCompFlagsRaw ( vtxAttrib , vatFormat . compCnt ) ; } function getAttributeComponentCount ( vtxAttrib , vatFormat ) { if ( vtxAttrib === GX . Attr . NRM && vatFormat . compCnt === GX . CompCnt . NRM_NBT ) return 9 ; else return getFormatCompFlagsComponentCount ( getAttributeFormatCompFlags ( vtxAttrib , vatFormat ) ) ; } function getComponentShiftRaw ( compType , compShift ) { switch ( compType ) { case GX . CompType . F32 : case GX . CompType . RGBA8 : return 0 ; case GX . CompType . U8 : case GX . CompType . U16 : case GX . CompType . S8 : case GX . CompType . S16 : return compShift ; } } function getComponentShift ( vtxAttrib , vatFormat ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return 0 ; if ( vtxAttrib === GX . Attr . NRM ) { if ( vatFormat . compType === GX . CompType . U8 || vatFormat . compType === GX . CompType . S8 ) return 6 ; else if ( vatFormat . compType === GX . CompType . U16 || vatFormat . compType === GX . CompType . S16 ) return 14 ; else throw "whoops" ; } return getComponentShiftRaw ( vatFormat . compType , vatFormat . compShift ) ; } function getComponentType ( vtxAttrib , vatFormat ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return GX . CompType . U8 ; return vatFormat . compType ; } function getIndexNumComponents ( vtxAttrib , vatFormat ) { if ( vtxAttrib === GX . Attr . NRM && vatFormat . compCnt === GX . CompCnt . NRM_NBT3 ) return 3 ; else return 1 ; } function getAttrName ( vtxAttrib ) { switch ( vtxAttrib ) { case GX . Attr . PNMTXIDX : return `PNMTXIDX` ; case GX . Attr . TEX0MTXIDX : return `TEX0MTXIDX` ; case GX . Attr . TEX1MTXIDX : return `TEX1MTXIDX` ; case GX . Attr . TEX2MTXIDX : return `TEX2MTXIDX` ; case GX . Attr . TEX3MTXIDX : return `TEX3MTXIDX` ; case GX . Attr . TEX4MTXIDX : return `TEX4MTXIDX` ; case GX . Attr . TEX5MTXIDX : return `TEX5MTXIDX` ; case GX . Attr . TEX6MTXIDX : return `TEX6MTXIDX` ; case GX . Attr . TEX7MTXIDX : return `TEX7MTXIDX` ; case GX . Attr . POS : return `POS` ; case GX . Attr . NRM : return `NRM` ; case GX . Attr . CLR0 : return `CLR0` ; case GX . Attr . CLR1 : return `CLR1` ; case GX . Attr . TEX0 : return `TEX0` ; case GX . Attr . TEX1 : return `TEX1` ; case GX . Attr . TEX2 : return `TEX2` ; case GX . Attr . TEX3 : return `TEX3` ; case GX . Attr . TEX4 : return `TEX4` ; case GX . Attr . TEX5 : return `TEX5` ; case GX . Attr . TEX6 : return `TEX6` ; case GX . Attr . TEX7 : return `TEX7` ; default : throw new Error ( "whoops" ) ; } } function getAttributeBaseFormat ( vtxAttrib ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) return GfxFormat . U8_R_NORM ; if ( vtxAttrib === GX . Attr . CLR0 || vtxAttrib === GX . Attr . CLR1 ) return GfxFormat . U8_R_NORM ; return GfxFormat . F32_R ; } function getAttributeFormat ( vatLayouts , vtxAttrib ) { let formatCompFlags = 0 ; const baseFormat = getAttributeBaseFormat ( vtxAttrib ) ; if ( vtxAttrib === GX . Attr . POS ) { formatCompFlags = FormatCompFlags . RGBA ; } else if ( isVtxAttribColor ( vtxAttrib ) ) { formatCompFlags = FormatCompFlags . RGBA ; } else if ( isVtxAttribTexMtxIdx ( vtxAttrib ) ) { formatCompFlags = FormatCompFlags . RGBA ; } else if ( isVtxAttribTex ( vtxAttrib ) ) { assert ( baseFormat === GfxFormat . F32_R ) ; formatCompFlags = FormatCompFlags . RGBA ; } else { for ( let i = 0 ; i < vatLayouts . length ; i ++ ) { const vatLayout = vatLayouts [ i ] ; if ( vatLayout !== undefined ) formatCompFlags = Math . max ( formatCompFlags , getAttributeFormatCompFlags ( vtxAttrib , vatLayout . vatFormat [ vtxAttrib ] ) ) ; } } return makeFormat ( getFormatTypeFlags ( baseFormat ) , formatCompFlags , getFormatFlags ( baseFormat ) ) ; } function translateVatLayout ( vatFormat , vcd ) { if ( vatFormat === undefined ) return undefined ; let srcVertexSize = 0 ; for ( let vtxAttrib = 0 ; vtxAttrib < vcd . length ; vtxAttrib ++ ) { const vtxAttrDesc = vcd [ vtxAttrib ] ; const vtxAttrFmt = vatFormat [ vtxAttrib ] ; if ( ! vtxAttrDesc || vtxAttrDesc . type === GX . AttrType . NONE ) continue ; const srcIndexComponentCount = getIndexNumComponents ( vtxAttrib , vtxAttrFmt ) ; if ( isVtxAttribMtxIdx ( vtxAttrib ) ) assert ( vtxAttrDesc . type === GX . AttrType . DIRECT ) ; if ( vtxAttrDesc . type === GX . AttrType . DIRECT ) srcVertexSize += getAttributeByteSizeRaw ( vtxAttrib , vtxAttrFmt ) ; else if ( vtxAttrDesc . type === GX . AttrType . INDEX8 ) srcVertexSize += 1 * srcIndexComponentCount ; else if ( vtxAttrDesc . type === GX . AttrType . INDEX16 ) srcVertexSize += 2 * srcIndexComponentCount ; } return { srcVertexSize , vatFormat , vcd } ; } function isVatLayoutNBT ( vatLayout ) { const compCnt = vatLayout . vatFormat [ GX . Attr . NRM ] . compCnt ; return compCnt === GX . CompCnt . NRM_NBT || compCnt === GX . CompCnt . NRM_NBT3 ; } export function compileLoadedVertexLayout ( vat , vcd ) { vat = arrayCopy ( vat , vatCopy ) ; vcd = arrayCopy ( vcd , vcdCopy ) as GX_VtxDesc [ ] ; const vatLayouts = vat . map ( ( vatFormat ) => translateVatLayout ( vatFormat , vcd ) ) ; const bufferIndex = 0 ; function allocateVertexInput ( attrInput , format ) { const existingInput = singleVertexInputLayouts . find ( ( layout ) => layout . attrInput === attrInput ) ; if ( existingInput !== undefined ) { return existingInput ; } else { const formatComponentSize = getFormatCompByteSize ( format ) ; const formatComponentCount = getFormatComponentCount ( format ) ; dstVertexSize = align ( dstVertexSize , formatComponentSize ) ; const bufferOffset = dstVertexSize ; dstVertexSize += formatComponentSize * formatComponentCount ; const input = { attrInput , bufferIndex , bufferOffset , format } ; singleVertexInputLayouts . push ( input ) ; return input ; } } let dstVertexSize = 0 ; const singleVertexInputLayouts = [ ] ; const vertexAttributeOffsets = [ ] ; const vertexAttributeFormats = [ ] ; for ( let vtxAttrib = 0 ; vtxAttrib < vcd . length ; vtxAttrib ++ ) { const vtxAttrDesc = vcd [ vtxAttrib ] ; if ( ! vtxAttrDesc || vtxAttrDesc . type === GX . AttrType . NONE ) continue ; const enableOutput = ( vtxAttrDesc . enableOutput === undefined || vtxAttrDesc . enableOutput ) ; if ( ! enableOutput ) continue ; const inputFormat = getAttributeFormat ( vatLayouts , vtxAttrib ) ; let input ; let fieldFormat ; let fieldCompOffset = 0 ; if ( isVtxAttribTexMtxIdx ( vtxAttrib ) ) { const attrInput = ( vtxAttrib < GX . Attr . TEX4MTXIDX ) ? VertexAttributeInput . TEX0123MTXIDX : VertexAttributeInput . TEX4567MTXIDX ; input = allocateVertexInput ( attrInput , inputFormat ) ; fieldCompOffset = ( vtxAttrib - GX . Attr . TEX0MTXIDX ) & 0x03 ; fieldFormat = input . format ; } else if ( vtxAttrib === GX . Attr . PNMTXIDX ) { const inputFormat = getAttributeFormat ( vatLayouts , GX . Attr . POS ) ; input = allocateVertexInput ( VertexAttributeInput . POS , inputFormat ) ; fieldCompOffset = 3 ; fieldFormat = input . format ; } else if ( vtxAttrib === GX . Attr . NRM && vatLayouts . some ( ( vatLayout ) => vatLayout !== undefined && isVatLayoutNBT ( vatLayout ) ) ) { input = allocateVertexInput ( VertexAttributeInput . NRM , inputFormat ) ; allocateVertexInput ( VertexAttributeInput . BINRM , inputFormat ) ; allocateVertexInput ( VertexAttributeInput . TANGENT , inputFormat ) ; fieldFormat = input . format ; } else if ( isVtxAttribTex ( vtxAttrib ) ) { const texAttr = vtxAttrib - GX . Attr . TEX0 ; const attrInput = VertexAttributeInput . TEX01 + ( texAttr >>> 1 ) ; input = allocateVertexInput ( attrInput , inputFormat ) ; fieldCompOffset = ( texAttr & 0x01 ) * 2 ; assert ( input . format === GfxFormat . F32_RGBA ) ; fieldFormat = GfxFormat . F32_RG ; } else { const attrInput = getAttrInputForAttr ( vtxAttrib ) ; input = allocateVertexInput ( attrInput , inputFormat ) ; fieldFormat = input . format ; } if ( isVtxAttribMtxIdx ( vtxAttrib ) ) { fieldFormat = setFormatFlags ( fieldFormat , FormatFlags . None ) ; } const fieldByteOffset = getFormatCompByteSize ( input . format ) * fieldCompOffset ; vertexAttributeOffsets [ vtxAttrib ] = input . bufferOffset + fieldByteOffset ; vertexAttributeFormats [ vtxAttrib ] = fieldFormat ; } dstVertexSize = align ( dstVertexSize , 4 ) ; const vertexBufferStrides = [ dstVertexSize ] ; const indexFormat = GfxFormat . U16_R ; return { indexFormat , vertexBufferStrides , singleVertexInputLayouts , vertexAttributeOffsets , vertexAttributeFormats , vatLayouts , vat , vcd } ; } type SingleVtxLoaderFunc = ( dstVertexDataView : DataView , dstVertexDataOffs : number , dlView : DataView , dlOffs : number , vtxArrayViews : DataView [ ] , vtxArrayStrides : number [ ] ) => number ; type SingleVatLoaderFunc = ( dstVertexDataView : DataView , dstVertexDataOffs : number , loadedVertexLayout : LoadedVertexLayout , dlView : DataView , drawCalls : DrawCall [ ] , vtxArrayViews : DataView [ ] , vtxArrayStrides : number [ ] ) => number ; function generateRunVertices ( loadedVertexLayout , vatLayout ) { function compileVtxArrayViewName ( vtxAttrib ) { return `vtxArrayViews[${ vtxAttrib }]` ; } function compileVatLayoutAttribute ( vatLayout , vtxAttrib ) { const vtxAttrFmt = vatLayout . vatFormat [ vtxAttrib ] ; const vtxAttrDesc = vatLayout . vcd [ vtxAttrib ] ; if ( ! vtxAttrDesc || vtxAttrDesc . type === GX . AttrType . NONE ) return '' ; const enableOutput = ( vtxAttrDesc . enableOutput === undefined || vtxAttrDesc . enableOutput ) ; const dstFormat = loadedVertexLayout . vertexAttributeFormats [ vtxAttrib ] ; const dstBaseOffs = loadedVertexLayout . vertexAttributeOffsets [ vtxAttrib ] ; let srcAttrByteSize = - 1 ; if ( vtxAttrDesc . type === GX . AttrType . DIRECT ) srcAttrByteSize = getAttributeByteSizeRaw ( vtxAttrib , vtxAttrFmt ) ; function compileShift ( n ) { const srcAttrCompShift = getComponentShift ( vtxAttrib , vtxAttrFmt ) ; const divisor = 1 << srcAttrCompShift ; if ( divisor === 1 ) return n ; else return `(${ n } / ${ divisor })` ; } function compileReadOneComponent ( viewName , attrOffset ) { switch ( getComponentType ( vtxAttrib , vtxAttrFmt ) ) { case GX . CompType . F32 : return `${ viewName }.getFloat32(${ attrOffset })` ; case GX . CompType . U8 : return compileShift ( `${ viewName }.getUint8(${ attrOffset })` ) ; case GX . CompType . U16 : return compileShift ( `${ viewName }.getUint16(${ attrOffset })` ) ; case GX . CompType . S8 : return compileShift ( `${ viewName }.getInt8(${ attrOffset })` ) ; case GX . CompType . S16 : return compileShift ( `${ viewName }.getInt16(${ attrOffset })` ) ; default : throw "whoops" ; } } function compileWriteOneComponentF32 ( offs , value ) { const littleEndian = ( getSystemEndianness ( ) === Endianness . LITTLE_ENDIAN ) ; const dstOffs = `dstVertexDataOffs + ${ offs }` ; return `dstVertexDataView.setFloat32(${ dstOffs }, ${ value }, ${ littleEndian })` ; } function compileWriteOneComponentU8Norm ( offs , value ) { const dstOffs = `dstVertexDataOffs + ${ offs }` ; return `dstVertexDataView.setUint8(${ dstOffs }, ${ value } * 0xFF)` ; } function compileWriteOneComponentU8 ( offs , value ) { const dstOffs = `dstVertexDataOffs + ${ offs }` ; return `dstVertexDataView.setUint8(${ dstOffs }, ${ value })` ; } function compileWriteOneComponent ( offs , value ) { const typeFlags = getFormatTypeFlags ( dstFormat ) ; const isNorm = getFormatFlags ( dstFormat ) & FormatFlags . Normalized ; if ( typeFlags === FormatTypeFlags . F32 ) return compileWriteOneComponentF32 ( offs , value ) ; else if ( typeFlags === FormatTypeFlags . U8 && isNorm ) return compileWriteOneComponentU8Norm ( offs , value ) ; else if ( typeFlags === FormatTypeFlags . U8 ) return compileWriteOneComponentU8 ( offs , value ) ; else throw "whoops" ; } function compileOneAttribColor ( viewName , attrOffs ) { const dstComponentCount = getFormatComponentCount ( dstFormat ) ; const dstOffs = dstBaseOffs ; assert ( dstComponentCount === 4 ) ; const temp = `_T${ vtxAttrib }` ; const componentType = getComponentType ( vtxAttrib , vtxAttrFmt ) ; if ( componentType === GX . CompType . RGB565 ) { return `
    var ${ temp } = ${ viewName }.getUint16(${ attrOffs });
    ${ compileWriteOneComponent ( dstOffs + 0 , `(((${ temp } >>> 11) & 0x1F) / 0x1F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 1 , `(((${ temp } >>>  5) & 0x3F) / 0x3F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 2 , `(((${ temp } >>>  0) & 0x1F) / 0x1F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 3 , `1.0` ) };
` ; } else if ( componentType === GX . CompType . RGB8 || componentType === GX . CompType . RGBX8 ) { return `
    ${ compileWriteOneComponent ( dstOffs + 0 , `${ viewName }.getUint8(${ attrOffs } + 0) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 1 , `${ viewName }.getUint8(${ attrOffs } + 1) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 2 , `${ viewName }.getUint8(${ attrOffs } + 2) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 3 , `1.0` ) };
` ; } else if ( componentType === GX . CompType . RGBA4 ) { return `
    var ${ temp } = ${ viewName }.getUint16(${ attrOffs });
    ${ compileWriteOneComponent ( dstOffs + 0 , `(((${ temp } >>> 12) & 0x0F) / 0x0F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 1 , `(((${ temp } >>>  8) & 0x0F) / 0x0F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 2 , `(((${ temp } >>>  4) & 0x0F) / 0x0F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 3 , `(((${ temp } >>>  0) & 0x0F) / 0x0F)` ) };
` ; } else if ( componentType === GX . CompType . RGBA6 ) { return `
    var ${ temp } = (${ viewName }.getUint8(${ attrOffs } + 0) << 16) | (${ viewName }.getUint8(${ attrOffs } + 1) << 8) | (${ viewName }.getUint8(${ attrOffs } + 2));
    ${ compileWriteOneComponent ( dstOffs + 0 , `(((${ temp } >>> 18) & 0x3F) / 0x3F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 1 , `(((${ temp } >>> 12) & 0x3F) / 0x3F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 2 , `(((${ temp } >>>  6) & 0x3F) / 0x3F)` ) };
    ${ compileWriteOneComponent ( dstOffs + 3 , `(((${ temp } >>>  0) & 0x3F) / 0x3F)` ) };
` ; } else if ( componentType === GX . CompType . RGBA8 ) { return `
    ${ compileWriteOneComponent ( dstOffs + 0 , `${ viewName }.getUint8(${ attrOffs } + 0) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 1 , `${ viewName }.getUint8(${ attrOffs } + 1) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 2 , `${ viewName }.getUint8(${ attrOffs } + 2) / 0xFF` ) };
    ${ compileWriteOneComponent ( dstOffs + 3 , `${ viewName }.getUint8(${ attrOffs } + 3) / 0xFF` ) };
` ; } else { throw "whoops" ; } } function compileOneAttribMtxIdx ( viewName , attrOffs ) { let S = `` ; const srcAttrCompSize = getAttributeComponentByteSize ( vtxAttrib , vtxAttrFmt ) ; const srcAttrCompCount = getAttributeComponentCount ( vtxAttrib , vtxAttrFmt ) ; assertExists ( srcAttrCompSize === 1 && srcAttrCompCount === 1 ) ; const dstOffs = dstBaseOffs ; const srcOffs = `${ attrOffs }` ; const value = compileReadOneComponent ( viewName , srcOffs ) ; S += `
    ${ compileWriteOneComponent ( dstOffs , `(${ value } / 3)` ) };` ; return S ; } function compileOneAttribOther ( viewName , attrOffs ) { let S = `` ; const srcAttrCompSize = getAttributeComponentByteSize ( vtxAttrib , vtxAttrFmt ) ; const srcAttrCompCount = getAttributeComponentCount ( vtxAttrib , vtxAttrFmt ) ; const dstComponentSize = getFormatCompByteSize ( dstFormat ) ; for ( let i = 0 ; i < srcAttrCompCount ; i ++ ) { const dstOffs = dstBaseOffs + ( i * dstComponentSize ) ; const srcOffs = `${ attrOffs } + ${ i * srcAttrCompSize }` ; const value = compileReadOneComponent ( viewName , srcOffs ) ; S += `
    ${ compileWriteOneComponent ( dstOffs , value ) };` ; } return S ; } function compileOneAttrib ( viewName , attrOffsetBase , drawCallIdxIncr ) { let S = `` ; if ( enableOutput ) { if ( isVtxAttribMtxIdx ( vtxAttrib ) ) S += compileOneAttribMtxIdx ( viewName , attrOffsetBase ) ; else if ( isVtxAttribColor ( vtxAttrib ) ) S += compileOneAttribColor ( viewName , attrOffsetBase ) ; else S += compileOneAttribOther ( viewName , attrOffsetBase ) ; } S += `
    drawCallIdx += ${ drawCallIdxIncr };
` ; return S ; } function compileOneIndex ( viewName , readIndex , drawCallIdxIncr , uniqueSuffix = '' ) { const stride = `vtxArrayStrides[${ vtxAttrib }]` ; const attrOffsetBase = `(${ readIndex }) * ${ stride }` ; const arrayOffsetVarName = `arrayOffset${ vtxAttrib }${ uniqueSuffix }` ; if ( enableOutput ) { return `const ${ arrayOffsetVarName } = ${ attrOffsetBase };${ compileOneAttrib ( viewName , arrayOffsetVarName , drawCallIdxIncr ) }` ; } else { return compileOneAttrib ( '' , '' , drawCallIdxIncr ) ; } } function compileAttribIndex ( viewName , readIndex , drawCallIdxIncr ) { if ( vtxAttrib === GX . Attr . NRM && vtxAttrFmt . compCnt === GX . CompCnt . NRM_NBT3 ) { return `
    // NRM
    ${ compileOneIndex ( viewName , readIndex , drawCallIdxIncr , `_N` ) }
    // BINRM
    ${ compileOneIndex ( viewName , readIndex , drawCallIdxIncr , `_B` ) }
    // TANGENT
    ${ compileOneIndex ( viewName , readIndex , drawCallIdxIncr , `_T` ) }` ; } else { return `
    // ${ getAttrName ( vtxAttrib ) }
    ${ compileOneIndex ( viewName , readIndex , drawCallIdxIncr ) }` ; } } switch ( vtxAttrDesc . type ) { case GX . AttrType . DIRECT : return `
    // ${ getAttrName ( vtxAttrib ) }
    ${ compileOneAttrib ( `dlView` , `drawCallIdx` , srcAttrByteSize ) }` ; case GX . AttrType . INDEX8 : return compileAttribIndex ( compileVtxArrayViewName ( vtxAttrib ) , `dlView.getUint8(drawCallIdx)` , 1 ) ; case GX . AttrType . INDEX16 : return compileAttribIndex ( compileVtxArrayViewName ( vtxAttrib ) , `dlView.getUint16(drawCallIdx)` , 2 ) ; default : throw "whoops" ; } } function compileVatLayout ( vatLayout ) { let S = '' ; for ( let vtxAttrib = 0 ; vtxAttrib <= GX . Attr . MAX ; vtxAttrib ++ ) S += compileVatLayoutAttribute ( vatLayout , vtxAttrib ) ; return S ; } return compileVatLayout ( vatLayout ) ; } function compileFunction < T extends Function > ( source , entryPoint ) { const fullSource = `
"use strict";

${ source }

return function() {
    return ${ entryPoint };
}();
` ; const generator = new Function ( fullSource ) ; const func = generator ( ) as T ; return func ; } function compileSingleVtxLoader ( loadedVertexLayout , vatLayout ) { const runVertices = generateRunVertices ( loadedVertexLayout , vatLayout ) ; const source = `
function runVertices(dstVertexDataView, dstVertexDataOffs, dlView, drawCallIdx, vtxArrayViews, vtxArrayStrides) {
    ${ runVertices }
    return drawCallIdx;
}
` ; return compileFunction ( source , `runVertices` ) ; } function compileSingleVatLoader ( loadedVertexLayout , vatLayout ) { const runVertices = generateRunVertices ( loadedVertexLayout , vatLayout ) ; const source = `
function runVertices(dstVertexDataView, dstVertexDataOffs, loadedVertexLayout, dlView, drawCalls, vtxArrayViews, vtxArrayStrides) {
    for (let i = 0; i < drawCalls.length; i++) {
        const drawCall = drawCalls[i];

        let drawCallIdx = drawCall.srcOffs;
        for (let j = 0; j < drawCall.vertexCount; j++) {
            ${ runVertices }
            dstVertexDataOffs += loadedVertexLayout.vertexBufferStrides[0];
        }
    }
}
` ; return compileFunction ( source , `runVertices` ) ; } interface DrawCall { primType : number ; vertexFormat : GX . VtxFmt ; srcOffs : number ; vertexCount : number ; } class VtxLoaderImpl implements VtxLoader { public vtxLoaders : SingleVtxLoaderFunc [ ] = [ ] ; public singleVatLoader : SingleVatLoaderFunc | null = null ; constructor ( public loadedVertexLayout ) { let singleVat = - 1 ; for ( let i = 0 ; i < loadedVertexLayout . vatLayouts . length ; i ++ ) { const vatLayout = loadedVertexLayout . vatLayouts [ i ] ; if ( vatLayout ) { if ( singleVat === - 1 ) singleVat = i ; else singleVat = - 2 ; } } if ( singleVat >= 0 ) { this . singleVatLoader = compileSingleVatLoader ( loadedVertexLayout , loadedVertexLayout . vatLayouts [ singleVat ] ! ) ; } else { for ( let i = 0 ; i < loadedVertexLayout . vatLayouts . length ; i ++ ) { const vatLayout = loadedVertexLayout . vatLayouts [ i ] ; if ( vatLayout !== undefined ) this . vtxLoaders [ i ] = compileSingleVtxLoader ( loadedVertexLayout , vatLayout ) ; } } } public parseDisplayList ( srcBuffer , loadOptions ? ) { function newDraw ( indexOffset ) { return { indexOffset , indexCount : 0 , posMatrixTable : Array ( 10 ) . fill ( 0xFFFF ) , texMatrixTable : Array ( 10 ) . fill ( 0xFFFF ) , } ; } const dlView = srcBuffer . createDataView ( ) ; const drawCalls = [ ] ; const draws = [ ] ; let totalVertexCount = 0 ; let totalIndexCount = 0 ; let drawCallIdx = 0 ; let currentDraw = null ; let currentXfmem = null ; while ( true ) { if ( drawCallIdx >= srcBuffer . byteLength ) break ; const cmd = dlView . getUint8 ( drawCallIdx ) ; if ( cmd === 0 ) break ; switch ( cmd ) { case GX . Command . LOAD_INDX_A : { currentDraw = null ; if ( currentXfmem === null ) currentXfmem = newDraw ( totalIndexCount ) ; const memoryElemSize = 3 * 4 ; const memoryBaseAddr = 0x0000 ; const table = currentXfmem . posMatrixTable ; const arrayIndex = dlView . getUint16 ( drawCallIdx + 0x01 ) ; const addrLen = dlView . getUint16 ( drawCallIdx + 0x03 ) ; const len = ( addrLen >>> 12 ) + 1 ; const addr = addrLen & 0x0FFF ; const tableIndex = ( ( addr - memoryBaseAddr ) / memoryElemSize ) | 0 ; if ( len !== memoryElemSize ) throw Error ( ) ; table [ tableIndex ] = arrayIndex ; drawCallIdx += 0x05 ; continue ; } case GX . Command . LOAD_INDX_C : { currentDraw = null ; if ( currentXfmem === null ) currentXfmem = newDraw ( totalIndexCount ) ; const memoryElemSize = 3 * 4 ; const memoryBaseAddr = 0x0078 ; const table = currentXfmem . texMatrixTable ; const arrayIndex = dlView . getUint16 ( drawCallIdx + 0x01 ) ; const addrLen = dlView . getUint16 ( drawCallIdx + 0x03 ) ; const len = ( addrLen >>> 12 ) + 1 ; const addr = addrLen & 0x0FFF ; const tableIndex = ( ( addr - memoryBaseAddr ) / memoryElemSize ) | 0 ; if ( len !== memoryElemSize ) throw Error ( ) ; table [ tableIndex ] = arrayIndex ; drawCallIdx += 0x05 ; continue ; } case GX . Command . LOAD_INDX_B : case GX . Command . LOAD_INDX_D : drawCallIdx += 0x05 ; continue ; } const primType = cmd & 0xF8 ; const vertexFormat = cmd & 0x07 ; const vertexCount = dlView . getUint16 ( drawCallIdx + 0x01 ) ; drawCallIdx += 0x03 ; const srcOffs = drawCallIdx ; totalVertexCount += vertexCount ; if ( currentDraw === null ) { if ( currentXfmem !== null ) { currentDraw = currentXfmem ; currentXfmem = null ; } else { currentDraw = newDraw ( totalIndexCount ) ; } draws . push ( currentDraw ) ; } let indexCount = 0 ; switch ( primType ) { case GX . Command . DRAW_TRIANGLES : indexCount = vertexCount ; break ; case GX . Command . DRAW_TRIANGLE_FAN : case GX . Command . DRAW_TRIANGLE_STRIP : indexCount = ( vertexCount - 2 ) * 3 ; break ; case GX . Command . DRAW_QUADS : case GX . Command . DRAW_QUADS_2 : indexCount = ( ( vertexCount * 6 ) / 4 ) * 3 ; break ; default : throw new Error ( `Invalid data at ${ hexzero ( srcBuffer . byteOffset , 0x08 ) } / ${ hexzero ( drawCallIdx - 0x03 , 0x04 ) } cmd ${ hexzero ( cmd , 0x02 ) }` ) ; } drawCalls . push ( { primType , vertexFormat , srcOffs , vertexCount } ) ; currentDraw . indexCount += indexCount ; totalIndexCount += indexCount ; const vatFormat = this . loadedVertexLayout . vatLayouts [ vertexFormat ] ; if ( vatFormat === undefined ) throw new Error ( "No VAT for VTXFMT" + vertexFormat ) ; drawCallIdx += vatFormat . srcVertexSize * vertexCount ; } const firstVertexId = ( loadOptions !== undefined && loadOptions . firstVertexId !== undefined ) ? loadOptions . firstVertexId : 0 ; let indexDataIdx = 0 ; const dstIndexData = new Uint16Array ( totalIndexCount ) ; let vertexId = firstVertexId ; for ( let z = 0 ; z < drawCalls . length ; z ++ ) { const drawCall = drawCalls [ z ] ; switch ( drawCall . primType ) { case GX . Command . DRAW_TRIANGLES : for ( let i = 0 ; i < drawCall . vertexCount ; i ++ ) { dstIndexData [ indexDataIdx ++ ] = vertexId ++ ; } break ; case GX . Command . DRAW_TRIANGLE_STRIP : for ( let i = 0 ; i < 3 ; i ++ ) { dstIndexData [ indexDataIdx ++ ] = vertexId ++ ; } for ( let i = 3 ; i < drawCall . vertexCount ; i ++ ) { dstIndexData [ indexDataIdx ++ ] = vertexId - ( ( i & 1 ) ? 1 : 2 ) ; dstIndexData [ indexDataIdx ++ ] = vertexId - ( ( i & 1 ) ? 2 : 1 ) ; dstIndexData [ indexDataIdx ++ ] = vertexId ++ ; } break ; case GX . Command . DRAW_TRIANGLE_FAN : const firstVertex = vertexId ; for ( let i = 0 ; i < 3 ; i ++ ) { dstIndexData [ indexDataIdx ++ ] = vertexId ++ ; } for ( let i = 3 ; i < drawCall . vertexCount ; i ++ ) { dstIndexData [ indexDataIdx ++ ] = firstVertex ; dstIndexData [ indexDataIdx ++ ] = vertexId - 1 ; dstIndexData [ indexDataIdx ++ ] = vertexId ++ ; } break ; case GX . Command . DRAW_QUADS : case GX . Command . DRAW_QUADS_2 : for ( let i = 0 ; i < drawCall . vertexCount ; i += 4 ) { dstIndexData [ indexDataIdx ++ ] = vertexId + 0 ; dstIndexData [ indexDataIdx ++ ] = vertexId + 1 ; dstIndexData [ indexDataIdx ++ ] = vertexId + 2 ; dstIndexData [ indexDataIdx ++ ] = vertexId + 0 ; dstIndexData [ indexDataIdx ++ ] = vertexId + 2 ; dstIndexData [ indexDataIdx ++ ] = vertexId + 3 ; vertexId += 4 ; } } } const dstVertexDataSize = this . loadedVertexLayout . vertexBufferStrides [ 0 ] * totalVertexCount ; const dstVertexData = new ArrayBuffer ( dstVertexDataSize ) ; const vertexBuffers = [ dstVertexData ] ; const indexData = dstIndexData . buffer ; return { indexData , totalIndexCount , totalVertexCount , draws : draws , vertexId , vertexBuffers , dlView , drawCalls } ; } public loadVertexDataInto ( dst , dstOffs , loadedVertexData , vtxArrays ) { const vtxArrayViews = [ ] ; const vtxArrayStrides = [ ] ; for ( let i = 0 ; i <= GX . Attr . MAX ; i ++ ) { if ( vtxArrays [ i ] !== undefined ) { vtxArrayViews [ i ] = vtxArrays [ i ] . buffer . createDataView ( vtxArrays [ i ] . offs ) ; vtxArrayStrides [ i ] = vtxArrays [ i ] . stride ; } } const dlView = assertExists ( loadedVertexData . dlView ) ; const drawCalls = assertExists ( loadedVertexData . drawCalls ) ; const dstVertexDataSize = this . loadedVertexLayout . vertexBufferStrides [ 0 ] * loadedVertexData . totalVertexCount ; assert ( dst . byteLength >= dstVertexDataSize ) ; let dstVertexDataOffs = dstOffs ; if ( this . singleVatLoader !== null ) { this . singleVatLoader ( dst , dstVertexDataOffs , this . loadedVertexLayout , dlView , drawCalls , vtxArrayViews , vtxArrayStrides ) ; } else { for ( let i = 0 ; i < drawCalls . length ; i ++ ) { const drawCall = drawCalls [ i ] ; let drawCallIdx = drawCall . srcOffs ; for ( let j = 0 ; j < drawCall . vertexCount ; j ++ ) { drawCallIdx = this . vtxLoaders [ drawCall . vertexFormat ] ( dst , dstVertexDataOffs , dlView , drawCallIdx , vtxArrayViews , vtxArrayStrides ) ; dstVertexDataOffs += this . loadedVertexLayout . vertexBufferStrides [ 0 ] ; } } } } public loadVertexData ( loadedVertexData , vtxArrays ) { const dstVertexData = assertExists ( loadedVertexData . vertexBuffers [ 0 ] ) ; const dstVertexDataView = new DataView ( dstVertexData ) ; return this . loadVertexDataInto ( dstVertexDataView , 0 , loadedVertexData , vtxArrays ) ; } public runVertices ( vtxArrays , srcBuffer , loadOptions ? ) { const loadedVertexData = this . parseDisplayList ( srcBuffer , loadOptions ) ; this . loadVertexData ( loadedVertexData , vtxArrays ) ; return loadedVertexData ; } } interface VtxLoaderDesc { vat : GX_VtxAttrFmt [ ] [ ] ; vcd : GX_VtxDesc [ ] ; } function vtxAttrFmtCopy ( a ) { if ( a === undefined ) return undefined ; else return { compCnt : a . compCnt , compShift : a . compShift , compType : a . compType } ; } function vtxAttrFmtEqual ( a , b ) { if ( a === undefined || b === undefined ) return a === b ; return a . compCnt === b . compCnt && a . compShift === b . compShift && a . compType === b . compType ; } function vatCopy ( a ) { if ( a === undefined ) return undefined as unknown as GX_VtxAttrFmt [ ] ; else return arrayCopy ( a , vtxAttrFmtCopy ) as GX_VtxAttrFmt [ ] ; } function vatEqual ( a , b ) { if ( a === undefined || b === undefined ) return a === b ; return arrayEqual ( a , b , vtxAttrFmtEqual ) ; } function vcdCopy ( a ) { if ( a === undefined ) return undefined ; else return { enableOutput : a . enableOutput , type : a . type } ; } function vcdEqual ( a , b ) { if ( a === undefined || b === undefined ) return a === b ; return a . enableOutput === b . enableOutput && a . type === b . type ; } function vtxLoaderDescEqual ( a , b ) { if ( ! arrayEqual ( a . vat , b . vat , vatEqual ) ) return false ; if ( ! arrayEqual ( a . vcd , b . vcd , vcdEqual ) ) return false ; return true ; } const cache = new HashMap < VtxLoaderDesc , VtxLoader > ( vtxLoaderDescEqual , nullHashFunc ) ; function compileVtxLoaderDesc ( desc ) { let loader = cache . get ( desc ) ; if ( loader === null ) { const vat = desc . vat ; const vcd = desc . vcd ; const loadedVertexLayout = compileLoadedVertexLayout ( vat , vcd ) ; loader = new VtxLoaderImpl ( loadedVertexLayout ) ; cache . add ( loadedVertexLayout , loader ) ; } return loader ; } export function compileVtxLoaderMultiVat ( vat , vcd ) { const desc = { vat , vcd } ; return compileVtxLoaderDesc ( desc ) ; } export function compileVtxLoader ( vatFormat , vcd ) { const vat = [ vatFormat ] ; const desc = { vat , vcd } ; return compileVtxLoaderDesc ( desc ) ; } export function compilePartialVtxLoader ( vtxLoader , loadedVertexData ) { const vertexLayout = ( vtxLoader as VtxLoaderImpl ) . loadedVertexLayout ; const vat = [ ] ; const vcd = vertexLayout . vcd ; const vatsUsed = nArray ( 8 , ( ) => false ) ; for ( let i = 0 ; i < loadedVertexData . drawCalls ! . length ; i ++ ) { const drawCall = loadedVertexData . drawCalls ! [ i ] ; vatsUsed [ drawCall . vertexFormat ] = true ; } for ( let i = 0 ; i < vatsUsed . length ; i ++ ) if ( vatsUsed [ i ] ) vat [ i ] = vertexLayout . vat [ i ] ; return compileVtxLoaderMultiVat ( vat , vcd ) ; } export class DisplayListRegisters { public bp : Uint32Array = new Uint32Array ( 0x100 ) ; public cp : Uint32Array = new Uint32Array ( 0x100 ) ; private xf : Uint32Array = new Uint32Array ( 0x1000 ) ; public kc : Uint32Array = new Uint32Array ( 4 * 2 * 2 ) ; constructor ( ) { this . bp [ GX . BPRegister . SS_MASK ] = 0x00FFFFFF ; } public bps ( regBag ) { const regAddr = regBag >>> 24 ; const regWMask = this . bp [ GX . BPRegister . SS_MASK ] ; const regValue = ( this . bp [ regAddr ] & ~ regWMask ) | ( regBag & regWMask ) ; if ( regAddr !== GX . BPRegister . SS_MASK ) this . bp [ GX . BPRegister . SS_MASK ] = 0x00FFFFFF ; this . bp [ regAddr ] = regValue ; if ( regAddr >= GX . BPRegister . TEV_REGISTERL_0_ID && regAddr <= GX . BPRegister . TEV_REGISTERL_0_ID + 4 * 2 ) { const kci = regAddr - GX . BPRegister . TEV_REGISTERL_0_ID ; const bank = ( regValue >>> 23 ) & 0x01 ; this . kc [ bank * 4 * 2 + kci ] = regValue ; } } public xfs ( idx , sub , v ) { assert ( idx >= 0x1000 ) ; idx -= 0x1000 ; this . xf [ idx * 0x10 + sub ] = v ; } public xfg ( idx , sub = 0 ) { assert ( idx >= 0x1000 ) ; idx -= 0x1000 ; return this . xf [ idx * 0x10 + sub ] ; } } export function displayListToString ( buffer ) { const view = buffer . createDataView ( ) ; let dlString = '' ; let ssMask = 0x00FFFFFF ; function toHexString ( n ) { return `0x${ n . toString ( 16 ) }` ; } const enum RegisterBlock { XF , BP , CP } ; const blockTables = [ GX . XFRegister , GX . BPRegister , GX . CPRegister ] ; const blockNames = [ 'XF' , 'BP' , 'CP' ] ; function toDlString ( block , regAddr , regValue ) { const table = blockTables [ block ] ; const name = blockNames [ block ] ; const strName = table [ regAddr ] ; const strAddr = toHexString ( regAddr ) ; return `Set ${ name } ${ strName ? strName : strAddr } to ${ toHexString ( regValue ) }\n` ; } for ( let i = 0 ; i < buffer . byteLength ; ) { const cmd = view . getUint8 ( i ++ ) ; switch ( cmd ) { case GX . Command . NOOP : continue ; case GX . Command . LOAD_BP_REG : { const regBag = view . getUint32 ( i ) ; i += 4 ; const regAddr = regBag >>> 24 as GX . BPRegister ; const regValue = regBag & ssMask ; if ( regAddr !== GX . BPRegister . SS_MASK ) { ssMask = 0x00FFFFFF ; } else { ssMask = regValue ; } dlString += toDlString ( RegisterBlock . BP , regAddr , regValue ) ; break ; } case GX . Command . LOAD_CP_REG : { const regAddr = view . getUint8 ( i ) ; i ++ ; const regValue = view . getUint32 ( i ) ; i += 4 ; dlString += toDlString ( RegisterBlock . CP , regAddr , regValue ) ; break ; } case GX . Command . LOAD_XF_REG : { const len = view . getUint16 ( i ) + 1 ; i += 2 ; assert ( len <= 0x10 ) ; const regAddr = view . getUint16 ( i ) ; i += 2 ; for ( let j = 0 ; j < len ; j ++ ) { dlString += toDlString ( RegisterBlock . XF , regAddr + j , view . getUint32 ( i ) ) ; i += 4 ; } break ; } default : console . error ( `Unknown command ${ cmd } at ${ i } (buffer: 0x${ buffer . byteOffset . toString ( 16 ) })` ) ; throw "whoops 1" ; } } return dlString ; } export function displayListRegistersRun ( r , buffer ) { const view = buffer . createDataView ( ) ; for ( let i = 0 ; i < buffer . byteLength ; ) { const cmd = view . getUint8 ( i ++ ) ; switch ( cmd ) { case GX . Command . NOOP : continue ; case GX . Command . LOAD_BP_REG : { const regBag = view . getUint32 ( i ) ; i += 4 ; r . bps ( regBag ) ; break ; } case GX . Command . LOAD_CP_REG : { const regAddr = view . getUint8 ( i ) ; i ++ ; const regValue = view . getUint32 ( i ) ; i += 4 ; r . cp [ regAddr ] = regValue ; break ; } case GX . Command . LOAD_XF_REG : { const len = view . getUint16 ( i ) + 1 ; i += 2 ; assert ( len <= 0x10 ) ; const regAddr = view . getUint16 ( i ) ; i += 2 ; for ( let j = 0 ; j < len ; j ++ ) { r . xfs ( regAddr , j , view . getUint32 ( i ) ) ; i += 4 ; } for ( let j = len ; j < 16 ; j ++ ) { r . xfs ( regAddr , j , 0 ) ; } break ; } default : console . error ( `Unknown command ${ cmd } at ${ i } (buffer: 0x${ buffer . byteOffset . toString ( 16 ) })` ) ; throw "whoops 1" ; } } } function setBPReg ( addr , value ) { return ( addr << 24 ) | ( value & 0x00FFFFFF ) ; } export function displayListRegistersInitGX ( r ) { for ( let i = 0 ; i < 8 ; i += 2 ) { r . bps ( setBPReg ( GX . BPRegister . TEV_KSEL_0_ID + i + 0 , 0b0100 ) ) ; r . bps ( setBPReg ( GX . BPRegister . TEV_KSEL_0_ID + i + 1 , 0b1110 ) ) ; } } function canMergeDraws ( a , b ) { if ( a . indexOffset !== b . indexOffset ) return false ; if ( ! arrayEqual ( a . posMatrixTable , b . posMatrixTable , ( i , j ) => i === j ) ) return false ; if ( ! arrayEqual ( a . texMatrixTable , b . texMatrixTable , ( i , j ) => i === j ) ) return false ; return true ; } export function coalesceLoadedDatas ( loadedDatas ) { let totalIndexCount = 0 ; let totalVertexCount = 0 ; let indexDataSize = 0 ; let packedVertexDataSize = 0 ; const draws = [ ] ; for ( let i = 0 ; i < loadedDatas . length ; i ++ ) { const loadedData = loadedDatas [ i ] ; assert ( loadedData . vertexBuffers . length === 1 ) ; for ( let j = 0 ; j < loadedData . draws . length ; j ++ ) { const draw = loadedData . draws [ j ] ; const existingDraw = draws . length > 0 ? draws [ draws . length - 1 ] : null ; if ( existingDraw !== null && canMergeDraws ( draw , existingDraw ) ) { existingDraw . indexCount += draw . indexCount ; } else { const indexOffset = totalIndexCount + draw . indexOffset ; const indexCount = draw . indexCount ; const posNrmMatrixTable = draw . posMatrixTable ; const texMatrixTable = draw . texMatrixTable ; draws . push ( { indexOffset , indexCount , posMatrixTable : posNrmMatrixTable , texMatrixTable } ) ; } } totalIndexCount += loadedData . totalIndexCount ; totalVertexCount += loadedData . totalVertexCount ; indexDataSize += loadedData . indexData . byteLength ; packedVertexDataSize += loadedData . vertexBuffers [ 0 ] . byteLength ; } const indexData = new Uint8Array ( indexDataSize ) ; const packedVertexData = new Uint8Array ( packedVertexDataSize ) ; let indexDataOffs = 0 ; let packedVertexDataOffs = 0 ; for ( let i = 0 ; i < loadedDatas . length ; i ++ ) { const loadedData = loadedDatas [ i ] ; indexData . set ( new Uint8Array ( loadedData . indexData ) , indexDataOffs ) ; packedVertexData . set ( new Uint8Array ( loadedData . vertexBuffers [ 0 ] ) , packedVertexDataOffs ) ; indexDataOffs += loadedData . indexData . byteLength ; packedVertexDataOffs += loadedData . vertexBuffers [ 0 ] . byteLength ; } return { indexData : indexData . buffer , vertexBuffers : [ packedVertexData . buffer ] , totalIndexCount , totalVertexCount , vertexId : 0 , draws , drawCalls : null , dlView : null , } ; }