From 73fc407bc1c3bf7f40ba4e2caba993a4c43fd78f Mon Sep 17 00:00:00 2001 From: Skillbert Date: Mon, 8 Dec 2025 18:36:31 +0100 Subject: [PATCH] new material version --- generated/materials.d.ts | 65 +++++ generated/npcs.d.ts | 416 +++++++++++++++--------------- generated/objects.d.ts | 492 ++++++++++++++++++------------------ generated/playerkit.d.ts | 7 - src/3d/jmat.ts | 211 ++++++++-------- src/buildfiletypes.ts | 86 +++---- src/opcodes/materials.jsonc | 292 ++++++++++++--------- 7 files changed, 845 insertions(+), 724 deletions(-) delete mode 100644 generated/playerkit.d.ts diff --git a/generated/materials.d.ts b/generated/materials.d.ts index ae70e64..61589af 100644 --- a/generated/materials.d.ts +++ b/generated/materials.d.ts @@ -117,4 +117,69 @@ export type materials = { unkFFFF: Uint8Array, endbyte: number, } | null, + v2: { + flags: number, + opaque_2: number, + flag3: number, + hasDiffuse: number, + hasNormal: number, + hasCompound: number, + hasUVanimU: number, + hasUVanimV: number, + flag10: number, + flag11: number, + flag12: number, + flag13: number, + flag14: number, + flag15: number, + flag16: number, + ignore_vertexcol_17: number, + flag18: number, + flag19: number, + flag20: number, + flag21: number, + diffuse: { + size: number, + texture: number, + } | null, + normal: { + size: number, + texture: number, + } | null, + compound: { + size: number, + texture: number, + } | null, + flag13value: number | null, + flag14value: [ + number, + number, + ] | null, + flag15value: number | null, + flag18value: number | null, + flag16value: number | null, + flag12value: number | null, + flag11value: [ + number, + number, + number, + ] | null, + flag19value: [ + number, + number, + number, + number, + number, + ] | null, + normalScale: number | null, + flag17value: number | null, + uvanim_u: number | null, + uvanim_v: number | null, + always_0x0901: Uint8Array, + unknownbyte0: number, + alphamode: number, + alphacutoff: number | null, + unkFFFF: Uint8Array, + endbyte: number, + } | null, }; diff --git a/generated/npcs.d.ts b/generated/npcs.d.ts index f45b2d5..c6eb30d 100644 --- a/generated/npcs.d.ts +++ b/generated/npcs.d.ts @@ -1,208 +1,208 @@ -// GENERATED DO NOT EDIT -// This source data is located at '..\src\opcodes\npcs.jsonc' -// run `npm run filetypes` to rebuild - -export type npcs = { - models?: (number|number)[] | null - name?: string | null - examine?: string | null - unknown_08?: number | null - unknown_0B?: number | null - boundSize?: number | null - unk_0D?: number | null - unk_0E?: number | null - unk_11?: number[] | null - actions_0?: string | null - actions_1?: string | null - actions_2?: string | null - actions_3?: string | null - actions_4?: string | null - color_replacements?: [ - number, - number, - ][] | null - material_replacements?: [ - number, - number, - ][] | null - recolourPalette?: number[] | null - recolor_indices?: number | null - retexture_indices?: number | null - headModels?: (number|number)[] | null - drawMapDot?: false | null - combat?: number | null - scaleXZ?: number | null - scaleY?: number | null - unknown_63?: true | null - ambience?: number | null - modelContract?: number | null - head_icon_data?: number | null - unknown_67?: number | null - morphs_1?: { - unk1: number, - unk2: number[], - unk3: (number|number), - } | null - unknown_6B?: false | null - slowWalk?: false | null - animateIdle?: false | null - shadow?: { - SrcColor: number, - DstColor: number, - } | null - shadowAlphaIntensity?: { - Src: number, - Dst: number, - } | null - unknown_73?: [ - number, - number, - ] | null - morphs_2?: { - unk1: number, - unk2: number, - unk3: number[], - unk4: number, - } | null - movementCapabilities?: number | null - unknown_78?: [ - number, - number, - number, - number, - ] | null - translations?: Uint8Array[] | null - unk_7A?: number | null - iconHeight?: number | null - respawnDirection?: number | null - animation_group?: number | null - movementType?: number | null - ambient_sound?: { - unk1: number, - unk2: number, - unk3: number, - unk4: number, - unk45: number, - } | null - oldCursor?: { - Op: number, - Cursor: number, - } | null - oldCursor2?: { - Op: number, - Cursor: number, - } | null - attackCursor?: number | null - armyIcon?: number | null - unknown_8C?: number | null - unknown_8D?: true | null - mapFunction?: number | null - unknown_8F?: true | null - members_actions_0?: string | null - members_actions_1?: string | null - members_actions_2?: string | null - members_actions_3?: string | null - members_actions_4?: string | null - unknown_9B?: { - unknown_1: number, - unknown_2: number, - unknown_3: number, - unknown_4: number, - } | null - aByte3076_set_1?: true | null - aByte3076_set_0?: false | null - quests?: number[] | null - dummy_1?: true | null - unknown_A3?: number | null - unknown_A4?: { - unknown_1: number, - unknown_2: number, - } | null - unknown_A5?: number | null - unknown_A8?: number | null - unknown_A9?: false | null - action_cursors_0?: number | null - action_cursors_1?: number | null - action_cursors_2?: number | null - action_cursors_3?: number | null - action_cursors_4?: number | null - action_cursors_5?: number | null - dummy_2?: true | null - unknown_B3?: { - unknown_1: number, - unknown_2: number, - unknown_3: number, - unknown_4: number, - unknown_5: number, - unknown_6: number, - } | null - unknown_B4?: number | null - unknown_B5?: { - unknown_1: number, - unknown_2: number, - } | null - unknown_B6?: true | null - unknown_B7?: number | null - unknown_B8?: number | null - unknown_B9?: true | null - unknown_BA?: { - unk0: number, - varbit: number, - varp: number, - flags: number, - multimodel: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - extracount: number, - extra1: number | null, - extra2: number | null, - extra3: number | null, - }[], - }[] | null, - multiheadmodel: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - }[], - }[] | null, - multiretex: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - unk4: number, - }[], - }[] | null, - multirecol: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - unk4: number, - }[], - }[] | null, - multiretint: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - }[], - }[] | null, - default: number, - } | null - unknown_DB?: number | null - extra?: { - prop: number, - intvalue: number | null, - stringvalue: string | null, - }[] | null - unknown_FD?: number | null -}; +// GENERATED DO NOT EDIT +// This source data is located at '..\src\opcodes\npcs.jsonc' +// run `npm run filetypes` to rebuild + +export type npcs = { + models?: (number|number)[] | null + name?: string | null + examine?: string | null + unknown_08?: number | null + unknown_0B?: number | null + boundSize?: number | null + unk_0D?: number | null + unk_0E?: number | null + unk_11?: number[] | null + actions_0?: string | null + actions_1?: string | null + actions_2?: string | null + actions_3?: string | null + actions_4?: string | null + color_replacements?: [ + number, + number, + ][] | null + material_replacements?: [ + number, + number, + ][] | null + recolourPalette?: number[] | null + recolor_indices?: number | null + retexture_indices?: number | null + headModels?: (number|number)[] | null + drawMapDot?: false | null + combat?: number | null + scaleXZ?: number | null + scaleY?: number | null + unknown_63?: true | null + ambience?: number | null + modelContract?: number | null + head_icon_data?: number | null + unknown_67?: number | null + morphs_1?: { + unk1: number, + unk2: number[], + unk3: (number|number), + } | null + unknown_6B?: false | null + slowWalk?: false | null + animateIdle?: false | null + shadow?: { + SrcColor: number, + DstColor: number, + } | null + shadowAlphaIntensity?: { + Src: number, + Dst: number, + } | null + unknown_73?: [ + number, + number, + ] | null + morphs_2?: { + unk1: number, + unk2: number, + unk3: number[], + unk4: number, + } | null + movementCapabilities?: number | null + unknown_78?: [ + number, + number, + number, + number, + ] | null + translations?: Uint8Array[] | null + unk_7A?: number | null + iconHeight?: number | null + respawnDirection?: number | null + animation_group?: number | null + movementType?: number | null + ambient_sound?: { + unk1: number, + unk2: number, + unk3: number, + unk4: number, + unk45: number, + } | null + oldCursor?: { + Op: number, + Cursor: number, + } | null + oldCursor2?: { + Op: number, + Cursor: number, + } | null + attackCursor?: number | null + armyIcon?: number | null + unknown_8C?: number | null + unknown_8D?: true | null + mapFunction?: number | null + unknown_8F?: true | null + members_actions_0?: string | null + members_actions_1?: string | null + members_actions_2?: string | null + members_actions_3?: string | null + members_actions_4?: string | null + unknown_9B?: { + unknown_1: number, + unknown_2: number, + unknown_3: number, + unknown_4: number, + } | null + aByte3076_set_1?: true | null + aByte3076_set_0?: false | null + quests?: number[] | null + dummy_1?: true | null + unknown_A3?: number | null + unknown_A4?: { + unknown_1: number, + unknown_2: number, + } | null + unknown_A5?: number | null + unknown_A8?: number | null + unknown_A9?: false | null + action_cursors_0?: number | null + action_cursors_1?: number | null + action_cursors_2?: number | null + action_cursors_3?: number | null + action_cursors_4?: number | null + action_cursors_5?: number | null + dummy_2?: true | null + unknown_B3?: { + unknown_1: number, + unknown_2: number, + unknown_3: number, + unknown_4: number, + unknown_5: number, + unknown_6: number, + } | null + unknown_B4?: number | null + unknown_B5?: { + unknown_1: number, + unknown_2: number, + } | null + unknown_B6?: true | null + unknown_B7?: number | null + unknown_B8?: number | null + unknown_B9?: true | null + unknown_BA?: { + unk0: number, + varbit: number, + varp: number, + flags: number, + multimodel: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + extracount: number, + extra1: number | null, + extra2: number | null, + extra3: number | null, + }[], + }[] | null, + multiheadmodel: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + }[], + }[] | null, + multiretex: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + unk4: number, + }[], + }[] | null, + multirecol: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + unk4: number, + }[], + }[] | null, + multiretint: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + }[], + }[] | null, + default: number, + } | null + unknown_DB?: number | null + extra?: { + prop: number, + intvalue: number | null, + stringvalue: string | null, + }[] | null + unknown_FD?: number | null +}; diff --git a/generated/objects.d.ts b/generated/objects.d.ts index edad89c..59d96f1 100644 --- a/generated/objects.d.ts +++ b/generated/objects.d.ts @@ -1,246 +1,246 @@ -// GENERATED DO NOT EDIT -// This source data is located at '..\src\opcodes\objects.jsonc' -// run `npm run filetypes` to rebuild - -export type objects = { - models?: ({ - type: number, - values: (number|number)[], - }[]|{ - values: (number|number)[], - type: number, - }[]) | null - name?: string | null - examine?: string | null - models_05?: ({ - models: { - type: number, - values: (number|number)[], - }[], - unktail: [ - (number|number), - (number|number), - ][], - }|{ - models: { - type: 10, - values: (number|number)[], - unktail: [ - (number|number), - (number|number), - ][], - }[], - }) | null - width?: number | null - length?: number | null - probably_nocollision?: true | null - maybe_allows_lineofsight?: true | null - deletable?: boolean | null - probably_morphFloor?: true | null - unknown_16?: true | null - occludes_1?: false | null - probably_animation?: number | null - maybe_blocks_movement?: true | null - wallkit_related_1C?: number | null - ambient?: number | null - actions_0?: string | null - actions_1?: string | null - actions_2?: string | null - actions_3?: string | null - actions_4?: string | null - contrast?: number | null - color_replacements?: [ - number, - number, - ][] | null - material_replacements?: [ - number, - number, - ][] | null - recolourPalette?: number[] | null - unknown_2C?: number | null - unknown_2D?: number | null - unknown_36?: true | null - unknown_37?: true | null - unknown_38?: true | null - unknown_39?: true | null - unknown_3c?: number | null - mirror?: true | null - unknown_40?: true | null - scaleX?: number | null - scaleY?: number | null - scaleZ?: number | null - mapscene_old?: number | null - dummy_45?: number | null - translateX?: number | null - translateY?: number | null - translateZ?: number | null - unknown_49?: true | null - unknown_4A?: true | null - unknown_4B?: number | null - morphs_1?: { - unk1: number, - unk2: (number|number)[], - unk3: (number|number), - } | null - light_source_related_4E?: { - maybe_color: number, - maybe_radius: number, - } | null - unknown_4F?: { - unknown_1: number, - unknown_2: number, - unknown_3: number, - unknown_4: number[], - } | null - unknown_51?: number | null - unknown_52?: true | null - is_members?: true | null - unknown_59?: true | null - unknown_5A?: true | null - isMembers?: true | null - morphs_2?: { - unk1: number, - unk2: (number|number), - unk3: (number|number)[], - unk4: (number|number), - } | null - tilt_xz?: [ - number, - number, - ] | null - under_water?: true | null - probably_morphCeilingOffset?: (number|0) | null - unknown_60?: true | null - ground_decoration_related_61?: true | null - has_animated_texture?: true | null - dummy_63?: { - unknown_2: number, - unknown_1: number, - } | null - dummy_64?: { - unknown_2: number, - unknown_1: number, - } | null - unused_65?: number | null - mapscene?: number | null - occludes_2?: false | null - interactable_related_68?: number | null - invertMapScene?: true | null - headModels?: { - model: number, - unknown_2: number, - }[] | null - mapFunction?: number | null - unknown_71?: number | null - members_action_1?: string | null - members_action_2?: string | null - members_action_3?: string | null - members_action_4?: string | null - members_action_5?: string | null - unknown_A0?: number[] | null - singleuse_A2?: number | null - unknown_A3?: { - unknown_1: number, - unknown_2: number, - unknown_3: number, - unknown_4: number, - } | null - singleuse_A4?: number | null - singleuse_A5?: number | null - singleuse_A6?: number | null - floor_thickness?: number | null - unused_a8?: true | null - unused_a9?: true | null - wallkit_related_AA?: number | null - possibly_wallkit_skew_AB?: number | null - lightsource_related_AD?: { - unknown_1: number, - unknown_2: number, - } | null - can_change_color?: true | null - unknown_B2?: number | null - unknown_BA?: number | null - dummy_bc?: true | null - treerockordoor_BD?: true | null - action_cursors_0?: number | null - action_cursors_1?: number | null - action_cursors_2?: number | null - action_cursors_3?: number | null - action_cursors_4?: number | null - action_cursors_5?: number | null - tileplacement_related_c4?: number | null - clan_citadel_C5?: number | null - invisible_c6?: true | null - flooroverlay_c7?: true | null - singleuse_C8?: true | null - unknown_C9?: { - unknown_1: number, - unknown_2: number, - unknown_3: number, - unknown_4: number, - unknown_5: number, - unknown_6: number, - } | null - singleuse_CA?: number | null - unknown_CB?: true | null - unknown_CC?: Uint8Array[] | null - unknown_CD?: { - unk0: number, - varbit: number, - varp: number, - flags: number, - multimodel: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - extracount: number, - extra1: number | null, - extra2: number | null, - extra3: number | null, - }[], - }[] | null, - multiheadmodel: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - }[], - }[] | null, - multiretex: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - unk4: number, - }[], - }[] | null, - multirecol: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - unk4: number, - }[], - }[] | null, - multiretint: { - value: number, - models: { - unk1: number, - unk2: number, - unk3: number, - }[], - }[] | null, - default: number, - } | null - extra?: { - prop: number, - intvalue: number | null, - stringvalue: string | null, - }[] | null -}; +// GENERATED DO NOT EDIT +// This source data is located at '..\src\opcodes\objects.jsonc' +// run `npm run filetypes` to rebuild + +export type objects = { + models?: ({ + type: number, + values: (number|number)[], + }[]|{ + values: (number|number)[], + type: number, + }[]) | null + name?: string | null + examine?: string | null + models_05?: ({ + models: { + type: number, + values: (number|number)[], + }[], + unktail: [ + (number|number), + (number|number), + ][], + }|{ + models: { + type: 10, + values: (number|number)[], + unktail: [ + (number|number), + (number|number), + ][], + }[], + }) | null + width?: number | null + length?: number | null + probably_nocollision?: true | null + maybe_allows_lineofsight?: true | null + deletable?: boolean | null + probably_morphFloor?: true | null + unknown_16?: true | null + occludes_1?: false | null + probably_animation?: number | null + maybe_blocks_movement?: true | null + wallkit_related_1C?: number | null + ambient?: number | null + actions_0?: string | null + actions_1?: string | null + actions_2?: string | null + actions_3?: string | null + actions_4?: string | null + contrast?: number | null + color_replacements?: [ + number, + number, + ][] | null + material_replacements?: [ + number, + number, + ][] | null + recolourPalette?: number[] | null + unknown_2C?: number | null + unknown_2D?: number | null + unknown_36?: true | null + unknown_37?: true | null + unknown_38?: true | null + unknown_39?: true | null + unknown_3c?: number | null + mirror?: true | null + unknown_40?: true | null + scaleX?: number | null + scaleY?: number | null + scaleZ?: number | null + mapscene_old?: number | null + dummy_45?: number | null + translateX?: number | null + translateY?: number | null + translateZ?: number | null + unknown_49?: true | null + unknown_4A?: true | null + unknown_4B?: number | null + morphs_1?: { + unk1: number, + unk2: (number|number)[], + unk3: (number|number), + } | null + light_source_related_4E?: { + maybe_color: number, + maybe_radius: number, + } | null + unknown_4F?: { + unknown_1: number, + unknown_2: number, + unknown_3: number, + unknown_4: number[], + } | null + unknown_51?: number | null + unknown_52?: true | null + is_members?: true | null + unknown_59?: true | null + unknown_5A?: true | null + isMembers?: true | null + morphs_2?: { + unk1: number, + unk2: (number|number), + unk3: (number|number)[], + unk4: (number|number), + } | null + tilt_xz?: [ + number, + number, + ] | null + under_water?: true | null + probably_morphCeilingOffset?: (number|0) | null + unknown_60?: true | null + ground_decoration_related_61?: true | null + has_animated_texture?: true | null + dummy_63?: { + unknown_2: number, + unknown_1: number, + } | null + dummy_64?: { + unknown_2: number, + unknown_1: number, + } | null + unused_65?: number | null + mapscene?: number | null + occludes_2?: false | null + interactable_related_68?: number | null + invertMapScene?: true | null + headModels?: { + model: number, + unknown_2: number, + }[] | null + mapFunction?: number | null + unknown_71?: number | null + members_action_1?: string | null + members_action_2?: string | null + members_action_3?: string | null + members_action_4?: string | null + members_action_5?: string | null + unknown_A0?: number[] | null + singleuse_A2?: number | null + unknown_A3?: { + unknown_1: number, + unknown_2: number, + unknown_3: number, + unknown_4: number, + } | null + singleuse_A4?: number | null + singleuse_A5?: number | null + singleuse_A6?: number | null + floor_thickness?: number | null + unused_a8?: true | null + unused_a9?: true | null + wallkit_related_AA?: number | null + possibly_wallkit_skew_AB?: number | null + lightsource_related_AD?: { + unknown_1: number, + unknown_2: number, + } | null + can_change_color?: true | null + unknown_B2?: number | null + unknown_BA?: number | null + dummy_bc?: true | null + treerockordoor_BD?: true | null + action_cursors_0?: number | null + action_cursors_1?: number | null + action_cursors_2?: number | null + action_cursors_3?: number | null + action_cursors_4?: number | null + action_cursors_5?: number | null + tileplacement_related_c4?: number | null + clan_citadel_C5?: number | null + invisible_c6?: true | null + flooroverlay_c7?: true | null + singleuse_C8?: true | null + unknown_C9?: { + unknown_1: number, + unknown_2: number, + unknown_3: number, + unknown_4: number, + unknown_5: number, + unknown_6: number, + } | null + singleuse_CA?: number | null + unknown_CB?: true | null + unknown_CC?: Uint8Array[] | null + unknown_CD?: { + unk0: number, + varbit: number, + varp: number, + flags: number, + multimodel: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + extracount: number, + extra1: number | null, + extra2: number | null, + extra3: number | null, + }[], + }[] | null, + multiheadmodel: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + }[], + }[] | null, + multiretex: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + unk4: number, + }[], + }[] | null, + multirecol: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + unk4: number, + }[], + }[] | null, + multiretint: { + value: number, + models: { + unk1: number, + unk2: number, + unk3: number, + }[], + }[] | null, + default: number, + } | null + extra?: { + prop: number, + intvalue: number | null, + stringvalue: string | null, + }[] | null +}; diff --git a/generated/playerkit.d.ts b/generated/playerkit.d.ts deleted file mode 100644 index 7d35449..0000000 --- a/generated/playerkit.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -// GENERATED DO NOT EDIT -// This source data is located at '..\src\opcodes\playerkit.jsonc' -// run `npm run filetypes` to rebuild - -export type playerkit = { - name?: string | null -}; diff --git a/src/3d/jmat.ts b/src/3d/jmat.ts index 5cd4327..c1ec4c8 100644 --- a/src/3d/jmat.ts +++ b/src/3d/jmat.ts @@ -1,105 +1,106 @@ -import { HSL2RGBfloat, packedHSL2HSL } from "../utils"; -import { parse } from "../opdecoder"; -import type { materials } from "../../generated/materials"; -import type { CacheFileSource } from "cache"; - -type TextureRepeatMode = "clamp" | "repeat" | "mirror"; - -export type MaterialData = { - textures: { - diffuse?: number, - normal?: number, - compound?: number - }, - texmodes: TextureRepeatMode, - texmodet: TextureRepeatMode, - uvAnim: { u: number, v: number } | undefined, - baseColorFraction: number - baseColor: [number, number, number], - alphamode: "opaque" | "cutoff" | "blend", - alphacutoff: number, - stripDiffuseAlpha: boolean, - raw: materials | null -} - -export function defaultMaterial(): MaterialData { - return { - textures: {}, - texmodes: "repeat", - texmodet: "repeat", - uvAnim: undefined, - baseColorFraction: 0, - baseColor: [1, 1, 1], - alphamode: "opaque", - alphacutoff: 0.1, - stripDiffuseAlpha: false, - raw: null - } -} - -export function materialCacheKey(matid: number, hasVertexAlpha: boolean, minimapVariant: boolean) { - return (matid == -1 ? 0x3fffff : matid) | (hasVertexAlpha ? 0x800000 : 0) | (minimapVariant ? 0x400000 : 0); -} - -export function convertMaterial(data: Buffer, materialid: number, source: CacheFileSource) { - let rawparsed = parse.materials.read(data, source); - - let mat = defaultMaterial(); - mat.raw = rawparsed; - - if (rawparsed.v0) { - let raw = rawparsed.v0; - let olddiffuse = raw.arr.find(q => q.op == 1); - if (olddiffuse) { - //in caches after this the diffuse prop exists, but is ignored... - if (source.getBuildNr() <= 838) { - mat.textures.diffuse = olddiffuse.value; - } else { - mat.textures.diffuse = materialid; - } - } else if (raw.diffuse) { - mat.textures.diffuse = raw.diffuse; - } else if (raw.textureflags & 0x11) { - mat.textures.diffuse = materialid; - } - if (raw.normal) { - mat.textures.normal = raw.normal; - } else if (raw.textureflags & 0x0a) { - mat.textures.normal = materialid; - } - - let repeatu = raw.texrepeatflags & 0x7; - let repeatv = (raw.textureflags >> 2) & 0x7; - mat.texmodes = repeatu == 0 ? "mirror" : repeatu == 1 ? "repeat" : "clamp"; - mat.texmodet = repeatv == 0 ? "mirror" : repeatv == 1 ? "repeat" : "clamp"; - - mat.alphamode = raw.alphamode == 0 ? "opaque" : raw.alphamode == 1 ? "cutoff" : "blend"; - if (raw.alphacutoff) { mat.alphacutoff = raw.alphacutoff / 255; } - - if (raw.animtexU || raw.animtexV) { - let scale = 1 / (1 << 15); - mat.uvAnim = { u: (raw.animtexU ?? 0) * scale, v: (raw.animtexV ?? 0) * scale }; - } - if (raw.extra) { - mat.baseColorFraction = raw.extra.baseColorFraction / 255; - //seems like 0 is a special case, but unclear - mat.baseColor = (raw.extra.baseColor == 0 ? [1, 1, 1] : HSL2RGBfloat(packedHSL2HSL(raw.extra.baseColor))); - } - mat.stripDiffuseAlpha = (mat.alphamode == "opaque"); - } else if (rawparsed.v1) { - let raw = rawparsed.v1; - //this is very wrong - mat.alphamode = (raw.opaque_2 && !raw.hasUVanimU ? "cutoff" : "blend"); - mat.baseColorFraction = 1; - if (raw.diffuse) { mat.textures.diffuse = raw.diffuse.texture; } - if (raw.normal) { mat.textures.normal = raw.normal.texture; } - if (raw.compound) { mat.textures.compound = raw.compound.texture; } - if (raw.uvanim_u || raw.uvanim_v) { - let scale = 1 / (1 << 15); - mat.uvAnim = { u: (raw.uvanim_u ?? 0) * scale, v: (raw.uvanim_v ?? 0) * scale }; - } - } else { - throw new Error("unkown material version " + rawparsed.version); - } - return mat; -} +import { HSL2RGBfloat, packedHSL2HSL } from "../utils"; +import { parse } from "../opdecoder"; +import type { materials } from "../../generated/materials"; +import type { CacheFileSource } from "cache"; + +type TextureRepeatMode = "clamp" | "repeat" | "mirror"; + +export type MaterialData = { + textures: { + diffuse?: number, + normal?: number, + compound?: number + }, + texmodes: TextureRepeatMode, + texmodet: TextureRepeatMode, + uvAnim: { u: number, v: number } | undefined, + baseColorFraction: number + baseColor: [number, number, number], + alphamode: "opaque" | "cutoff" | "blend", + alphacutoff: number, + stripDiffuseAlpha: boolean, + raw: materials | null +} + +export function defaultMaterial(): MaterialData { + return { + textures: {}, + texmodes: "repeat", + texmodet: "repeat", + uvAnim: undefined, + baseColorFraction: 0, + baseColor: [1, 1, 1], + alphamode: "opaque", + alphacutoff: 0.1, + stripDiffuseAlpha: false, + raw: null + } +} + +export function materialCacheKey(matid: number, hasVertexAlpha: boolean, minimapVariant: boolean) { + return (matid == -1 ? 0x3fffff : matid) | (hasVertexAlpha ? 0x800000 : 0) | (minimapVariant ? 0x400000 : 0); +} + +export function convertMaterial(data: Buffer, materialid: number, source: CacheFileSource) { + let rawparsed = parse.materials.read(data, source); + + let mat = defaultMaterial(); + mat.raw = rawparsed; + + if (rawparsed.v0) { + let raw = rawparsed.v0; + let olddiffuse = raw.arr.find(q => q.op == 1); + if (olddiffuse) { + //in caches after this the diffuse prop exists, but is ignored... + if (source.getBuildNr() <= 838) { + mat.textures.diffuse = olddiffuse.value; + } else { + mat.textures.diffuse = materialid; + } + } else if (raw.diffuse) { + mat.textures.diffuse = raw.diffuse; + } else if (raw.textureflags & 0x11) { + mat.textures.diffuse = materialid; + } + if (raw.normal) { + mat.textures.normal = raw.normal; + } else if (raw.textureflags & 0x0a) { + mat.textures.normal = materialid; + } + + let repeatu = raw.texrepeatflags & 0x7; + let repeatv = (raw.textureflags >> 2) & 0x7; + mat.texmodes = repeatu == 0 ? "mirror" : repeatu == 1 ? "repeat" : "clamp"; + mat.texmodet = repeatv == 0 ? "mirror" : repeatv == 1 ? "repeat" : "clamp"; + + mat.alphamode = raw.alphamode == 0 ? "opaque" : raw.alphamode == 1 ? "cutoff" : "blend"; + if (raw.alphacutoff) { mat.alphacutoff = raw.alphacutoff / 255; } + + if (raw.animtexU || raw.animtexV) { + let scale = 1 / (1 << 15); + mat.uvAnim = { u: (raw.animtexU ?? 0) * scale, v: (raw.animtexV ?? 0) * scale }; + } + if (raw.extra) { + mat.baseColorFraction = raw.extra.baseColorFraction / 255; + //seems like 0 is a special case, but unclear + mat.baseColor = (raw.extra.baseColor == 0 ? [1, 1, 1] : HSL2RGBfloat(packedHSL2HSL(raw.extra.baseColor))); + } + mat.stripDiffuseAlpha = (mat.alphamode == "opaque"); + } else if (rawparsed.v1 || rawparsed.v2) { + // currently v1 and v2 have the same structure + let raw = (rawparsed.v1 || rawparsed.v2)!; + //this is very wrong + mat.alphamode = (raw.opaque_2 && !raw.hasUVanimU ? "cutoff" : "blend"); + mat.baseColorFraction = 1; + if (raw.diffuse) { mat.textures.diffuse = raw.diffuse.texture; } + if (raw.normal) { mat.textures.normal = raw.normal.texture; } + if (raw.compound) { mat.textures.compound = raw.compound.texture; } + if (raw.uvanim_u || raw.uvanim_v) { + let scale = 1 / (1 << 15); + mat.uvAnim = { u: (raw.uvanim_u ?? 0) * scale, v: (raw.uvanim_v ?? 0) * scale }; + } + } else { + throw new Error("unkown material version " + rawparsed.version); + } + return mat; +} diff --git a/src/buildfiletypes.ts b/src/buildfiletypes.ts index c9f24eb..5572e0b 100644 --- a/src/buildfiletypes.ts +++ b/src/buildfiletypes.ts @@ -1,44 +1,44 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as opcode_reader from "./opcode_reader"; -import * as commentjson from "comment-json"; -import { maprenderConfigSchema } from "./jsonschemas"; - -async function buildFileTypes() { - let basedir = path.resolve("./src/opcodes"); - let outdir = path.resolve("./generated"); - - //generate config file metas - let files = fs.readdirSync(basedir); - if (files.some(f => !path.basename(f).match(/\.jsonc?$/))) { - console.error("non-json files matched, is path wrong?"); - } - const typedef = commentjson.parse(fs.readFileSync(path.resolve(basedir, "typedef.jsonc"), "utf-8"), undefined, true); - for (let file of files) { - let srcfile = path.resolve(basedir, file); - let objname = path.parse(srcfile).name; - let jsontext = fs.readFileSync(srcfile, "utf8"); - const opcodes = commentjson.parse(jsontext, undefined, true); - var typesfile = - "// GENERATED DO NOT EDIT\n" + - "// This source data is located at '" + path.relative(outdir, srcfile) + "'\n" + - "// run `npm run filetypes` to rebuild\n\n"; - typesfile += "export type " + objname + " = "; - try { - typesfile += opcode_reader.buildParser(null, opcodes as any, typedef as any).getTypescriptType("") + ";\n"; - } catch (e) { - //console.error(e); - typesfile += "any;\n"; - typesfile += "// " + e.toString().replace(/\n/g, "\n//"); - } - //I'm sorry, git made me do this - typesfile = typesfile.replace(/(? !path.basename(f).match(/\.jsonc?$/))) { + console.error("non-json files matched, is path wrong?"); + } + const typedef = commentjson.parse(fs.readFileSync(path.resolve(basedir, "typedef.jsonc"), "utf-8"), undefined, true); + for (let file of files) { + let srcfile = path.resolve(basedir, file); + let objname = path.parse(srcfile).name; + let jsontext = fs.readFileSync(srcfile, "utf8"); + const opcodes = commentjson.parse(jsontext, undefined, true); + var typesfile = + "// GENERATED DO NOT EDIT\n" + + "// This source data is located at '" + path.relative(outdir, srcfile) + "'\n" + + "// run `npm run filetypes` to rebuild\n\n"; + typesfile += "export type " + objname + " = "; + try { + typesfile += opcode_reader.buildParser(null, opcodes as any, typedef as any).getTypescriptType("") + ";\n"; + } catch (e) { + //console.error(e); + typesfile += "any;\n"; + typesfile += "// " + e.toString().replace(/\n/g, "\n//"); + } + //I'm sorry, git made me do this + // typesfile = typesfile.replace(/(?=887":"ubyte",">=0":0}]], - ["v0",["opt",["version",0],["struct", - ["unk0","ubyte"],//always 0-4 - ["texsize","ushort"],//256,512,0, but doesn't always correspond to actualtex - - //always 0000 after 2015 - ["opt0","ubyte"], - ["opt0data",["opt",["opt0",16],["tuple","ubyte","ushort"]]], - ["arr",["nullarray","ubyte",["struct", - ["op",["ref","$opcode"]], - ["value","ushort"] - ]]], - - ["textureflags","ubyte"],//always 1,3,9,16 - ["diffuse",["opt",["textureflags",17,"bitor"],["match","buildnr",{">=887":"uint",">=0":0}]]], - ["normal",["opt",["textureflags",10,"bitor"],["match","buildnr",{">=887":"uint",">=0":0}]]], - - ["texrepeatflags",["match","buildnr",{">=876":"ubyte",">=0":0}]], - ["unk3_skybox",["match","buildnr",{">=876":["buffer",3,"hex"],">=0":"null"}]],//looks like a bitflag thing, bit weird - - ["flags2","ubyte"],//always 12,13,14,28 - ["unkfloats",["opt",["flags2",4,"bitflag"],["tuple","float","float"]]],//always 1; 1,4,32 - - ["unk7","ubyte"],//0,1,62,63,64,255 - ["weirdshit",["opt",["unk7",255],["buffer",19,"hex"]]],//only one single material that has this - - ["diffuse_related1",["match","buildnr",{">=887":["opt",["textureflags",4,"bitflag"],"ubyte"],">=0":"ubyte"}]],//always 1 or null - ["normal_related",["opt",["textureflags",1,"bitflag"],["buffer",4,"hex"]]],//0x80000000 most of the time - ["diffuse_related2",["match","buildnr",{">=887":["opt",["textureflags",0,"bitflag"],"ubyte"],">=0":"ubyte"}]],//always 1,0 or null - ["diffuse_ralated2_data",["opt",["diffuse_related2",2],"ubyte"]],//last used 2015 - - ["alphamode","ubyte"],//0,1 or 2 - ["alphacutoff",["opt",["alphamode",1],"ubyte"]],//128 most of the time - - ["animtex","ubyte"],//0123 - ["animtexU",["opt",["animtex",0,"bitflag"],["match","buildnr",{">=897":"short",">=0":"byte"}]]], - ["animtexV",["opt",["animtex",1,"bitflag"],["match","buildnr",{">=897":"short",">=0":"byte"}]]], - - ["flagextra","bool"], - ["extra",["opt",["flagextra",1],["struct", - ["unk00_flags","ubyte"],//flags 1=alpha channel reuse,2=water/lava/bloom?,4=water/dg water,8=water, all skyboxes have 6 - ["unk01_flagsornumber","ubyte"],//0123 OR a high number (only 20 uses) - ["unk02","ubyte"],//0 if unk01=0123 or number (only 4 uses) - ["unknown",["buffer",4,"hex"]],//mostly 0 except for some ~15 water materials - ["unk07_bool","bool"], - ["unk08_flags","ubyte"],//0=5k,1=1,2=5k - ["unk09_bool","bool"],//true=7 - ["unk0a_bool","bool"], - ["specular","ubyte"], - ["baseColorFraction","ubyte"],//indicates the mix ratio with colorint and vertex colors - ["baseColor","ushort"] - ]]] - ]]], - ["v1",["opt",["version",1],["struct", - ["flags","uint"], - //0,1 unused - ["opaque_2",["ref","flags",[2,1]]],//3061/3250 - ["flag3",["ref","flags",[3,1]]],//3095 most mats without are never used in a model - //4unused - ["hasDiffuse",["ref","flags",[5,1]]],//3250, always true - ["hasNormal",["ref","flags",[6,1]]],//2878 - ["hasCompound",["ref","flags",[7,1]]],//2223 - - ["hasUVanimU",["ref","flags",[8,1]]],//44 uv anim u? - ["hasUVanimV",["ref","flags",[9,1]]],//49 uv anim v? - ["flag10",["ref","flags",[10,1]]],//282 possibly bloom - ["flag11",["ref","flags",[11,1]]],//41 metalic? - - ["flag12",["ref","flags",[12,1]]],//1 use - ["flag13",["ref","flags",[13,1]]],//73 glasslike diffraction/lensing? https://runescape.wiki/w/Altar_of_War - ["flag14",["ref","flags",[14,1]]],//3179 - ["flag15",["ref","flags",[15,1]]],//2 use, skybox and https://runescape.wiki/w/Oathbreaker_Outfit - - ["flag16",["ref","flags",[16,1]]],//3069 - ["ignore_vertexcol_17",["ref","flags",[17,1]]],//338 - ["flag18",["ref","flags",[18,1]]],//24 uses different skybox? ghostlike stuff https://runescape.wiki/w/Closure%27s_robes - ["flag19",["ref","flags",[19,1]]],//5 use gives aurora effect https://runescape.wiki/w/Bohr - - ["flag20",["ref","flags",[20,1]]],//40 seems to be used on flants/leafs - ["flag21",["ref","flags",[21,1]]],//3250 all materials - //22+ unused - - ["diffuse",["opt",["hasDiffuse",1],["struct", - ["size","ubyte"],//actual size (some times) equal to 2^(6+size) - ["texture","uint"] - ]]], - ["normal",["opt",["hasNormal",1],["struct", - ["size","ubyte"],//actual size (some times) equal to 2^(6+size) - ["texture","uint"] - ]]], - ["compound",["opt",["hasCompound",1],["struct", - ["size","ubyte"],//actual size (some times) equal to 2^(6+size) - ["texture","uint"] - ]]], - ["flag13value",["opt",["flag13",1],"float"]], - ["flag14value",["opt",["flag14",1],["tuple","ushort","ushort"]]],//0,8520 or 13083 only, referencing other material? - ["flag15value",["opt",["flag15",1],"float"]], - ["flag18value",["opt",["flag18",1],"float"]],//not sure about location - ["flag16value",["opt",["flag16",1],"float"]], - ["flag12value",["opt",["flag12",1],"float"]],//not sure about location - ["flag11value",["opt",["flag11",1],["tuple","float","float","float"]]], - ["flag19value",["opt",["flag19",1],["tuple","float","float","float","float","float"]]],//location unknown - ["normalScale",["opt",["hasNormal",1],"float"]], - ["flag17value",["opt",["ignore_vertexcol_17",1],"float"]], - ["uvanim_u",["opt",["hasUVanimU",1],"short"]], - ["uvanim_v",["opt",["hasUVanimV",1],"short"]], - - ["always_0x0901",["buffer",2,"hex"]],//only a couple with 0001 - ["unknownbyte0","ubyte"],//always 0 - ["alphamode","ubyte"],//0,1,2 - ["alphacutoff",["opt",["alphamode",1],"ubyte"]],//127 most of the time - ["unkFFFF",["buffer",2,"hex"]], - ["endbyte","ubyte"]//usually equal to max texture size always 0-4 - ]]] +["struct", + ["version",["match","buildnr",{">=887":"ubyte",">=0":0}]], + ["v0",["opt",["version",0],["struct", + ["unk0","ubyte"],//always 0-4 + ["texsize","ushort"],//256,512,0, but doesn't always correspond to actualtex + + //always 0000 after 2015 + ["opt0","ubyte"], + ["opt0data",["opt",["opt0",16],["tuple","ubyte","ushort"]]], + ["arr",["nullarray","ubyte",["struct", + ["op",["ref","$opcode"]], + ["value","ushort"] + ]]], + + ["textureflags","ubyte"],//always 1,3,9,16 + ["diffuse",["opt",["textureflags",17,"bitor"],["match","buildnr",{">=887":"uint",">=0":0}]]], + ["normal",["opt",["textureflags",10,"bitor"],["match","buildnr",{">=887":"uint",">=0":0}]]], + + ["texrepeatflags",["match","buildnr",{">=876":"ubyte",">=0":0}]], + ["unk3_skybox",["match","buildnr",{">=876":["buffer",3,"hex"],">=0":"null"}]],//looks like a bitflag thing, bit weird + + ["flags2","ubyte"],//always 12,13,14,28 + ["unkfloats",["opt",["flags2",4,"bitflag"],["tuple","float","float"]]],//always 1; 1,4,32 + + ["unk7","ubyte"],//0,1,62,63,64,255 + ["weirdshit",["opt",["unk7",255],["buffer",19,"hex"]]],//only one single material that has this + + ["diffuse_related1",["match","buildnr",{">=887":["opt",["textureflags",4,"bitflag"],"ubyte"],">=0":"ubyte"}]],//always 1 or null + ["normal_related",["opt",["textureflags",1,"bitflag"],["buffer",4,"hex"]]],//0x80000000 most of the time + ["diffuse_related2",["match","buildnr",{">=887":["opt",["textureflags",0,"bitflag"],"ubyte"],">=0":"ubyte"}]],//always 1,0 or null + ["diffuse_ralated2_data",["opt",["diffuse_related2",2],"ubyte"]],//last used 2015 + + ["alphamode","ubyte"],//0,1 or 2 + ["alphacutoff",["opt",["alphamode",1],"ubyte"]],//128 most of the time + + ["animtex","ubyte"],//0123 + ["animtexU",["opt",["animtex",0,"bitflag"],["match","buildnr",{">=897":"short",">=0":"byte"}]]], + ["animtexV",["opt",["animtex",1,"bitflag"],["match","buildnr",{">=897":"short",">=0":"byte"}]]], + + ["flagextra","bool"], + ["extra",["opt",["flagextra",1],["struct", + ["unk00_flags","ubyte"],//flags 1=alpha channel reuse,2=water/lava/bloom?,4=water/dg water,8=water, all skyboxes have 6 + ["unk01_flagsornumber","ubyte"],//0123 OR a high number (only 20 uses) + ["unk02","ubyte"],//0 if unk01=0123 or number (only 4 uses) + ["unknown",["buffer",4,"hex"]],//mostly 0 except for some ~15 water materials + ["unk07_bool","bool"], + ["unk08_flags","ubyte"],//0=5k,1=1,2=5k + ["unk09_bool","bool"],//true=7 + ["unk0a_bool","bool"], + ["specular","ubyte"], + ["baseColorFraction","ubyte"],//indicates the mix ratio with colorint and vertex colors + ["baseColor","ushort"] + ]]] + ]]], + ["v1",["opt",["version",1],["struct", + ["flags","uint"], + //0,1 unused + ["opaque_2",["ref","flags",[2,1]]],//3061/3250 + ["flag3",["ref","flags",[3,1]]],//3095 most mats without are never used in a model + //4unused + ["hasDiffuse",["ref","flags",[5,1]]],//3250, always true + ["hasNormal",["ref","flags",[6,1]]],//2878 + ["hasCompound",["ref","flags",[7,1]]],//2223 + + ["hasUVanimU",["ref","flags",[8,1]]],//44 uv anim u? + ["hasUVanimV",["ref","flags",[9,1]]],//49 uv anim v? + ["flag10",["ref","flags",[10,1]]],//282 possibly bloom + ["flag11",["ref","flags",[11,1]]],//41 metalic? + + ["flag12",["ref","flags",[12,1]]],//1 use + ["flag13",["ref","flags",[13,1]]],//73 glasslike diffraction/lensing? https://runescape.wiki/w/Altar_of_War + ["flag14",["ref","flags",[14,1]]],//3179 + ["flag15",["ref","flags",[15,1]]],//2 use, skybox and https://runescape.wiki/w/Oathbreaker_Outfit + + ["flag16",["ref","flags",[16,1]]],//3069 + ["ignore_vertexcol_17",["ref","flags",[17,1]]],//338 + ["flag18",["ref","flags",[18,1]]],//24 uses different skybox? ghostlike stuff https://runescape.wiki/w/Closure%27s_robes + ["flag19",["ref","flags",[19,1]]],//5 use gives aurora effect https://runescape.wiki/w/Bohr + + ["flag20",["ref","flags",[20,1]]],//40 seems to be used on flants/leafs + ["flag21",["ref","flags",[21,1]]],//3250 all materials + //22+ unused + + ["diffuse",["opt",["hasDiffuse",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["normal",["opt",["hasNormal",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["compound",["opt",["hasCompound",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["flag13value",["opt",["flag13",1],"float"]], + ["flag14value",["opt",["flag14",1],["tuple","ushort","ushort"]]],//0,8520 or 13083 only, referencing other material? + ["flag15value",["opt",["flag15",1],"float"]], + ["flag18value",["opt",["flag18",1],"float"]],//not sure about location + ["flag16value",["opt",["flag16",1],"float"]], + ["flag12value",["opt",["flag12",1],"float"]],//not sure about location + ["flag11value",["opt",["flag11",1],["tuple","float","float","float"]]], + ["flag19value",["opt",["flag19",1],["tuple","float","float","float","float","float"]]],//location unknown + ["normalScale",["opt",["hasNormal",1],"float"]], + ["flag17value",["opt",["ignore_vertexcol_17",1],"float"]], + ["uvanim_u",["opt",["hasUVanimU",1],"short"]], + ["uvanim_v",["opt",["hasUVanimV",1],"short"]], + + ["always_0x0901",["buffer",2,"hex"]],//only a couple with 0001 + ["unknownbyte0","ubyte"],//always 0 + ["alphamode","ubyte"],//0,1,2 + ["alphacutoff",["opt",["alphamode",1],"ubyte"]],//127 most of the time + ["unkFFFF",["buffer",2,"hex"]], + ["endbyte","ubyte"]//usually equal to max texture size always 0-4 + ]]], + // seems to be the same as v1 so far + ["v2",["opt",["version",2],["struct", + ["flags","uint"], + //0,1 unused + ["opaque_2",["ref","flags",[2,1]]],//3061/3250 + ["flag3",["ref","flags",[3,1]]],//3095 most mats without are never used in a model + //4unused + ["hasDiffuse",["ref","flags",[5,1]]],//3250, always true + ["hasNormal",["ref","flags",[6,1]]],//2878 + ["hasCompound",["ref","flags",[7,1]]],//2223 + + ["hasUVanimU",["ref","flags",[8,1]]],//44 uv anim u? + ["hasUVanimV",["ref","flags",[9,1]]],//49 uv anim v? + ["flag10",["ref","flags",[10,1]]],//282 possibly bloom + ["flag11",["ref","flags",[11,1]]],//41 metalic? + + ["flag12",["ref","flags",[12,1]]],//1 use + ["flag13",["ref","flags",[13,1]]],//73 glasslike diffraction/lensing? https://runescape.wiki/w/Altar_of_War + ["flag14",["ref","flags",[14,1]]],//3179 + ["flag15",["ref","flags",[15,1]]],//2 use, skybox and https://runescape.wiki/w/Oathbreaker_Outfit + + ["flag16",["ref","flags",[16,1]]],//3069 + ["ignore_vertexcol_17",["ref","flags",[17,1]]],//338 + ["flag18",["ref","flags",[18,1]]],//24 uses different skybox? ghostlike stuff https://runescape.wiki/w/Closure%27s_robes + ["flag19",["ref","flags",[19,1]]],//5 use gives aurora effect https://runescape.wiki/w/Bohr + + ["flag20",["ref","flags",[20,1]]],//40 seems to be used on flants/leafs + ["flag21",["ref","flags",[21,1]]],//3250 all materials + //22+ unused + + ["diffuse",["opt",["hasDiffuse",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["normal",["opt",["hasNormal",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["compound",["opt",["hasCompound",1],["struct", + ["size","ubyte"],//actual size (some times) equal to 2^(6+size) + ["texture","uint"] + ]]], + ["flag13value",["opt",["flag13",1],"float"]], + ["flag14value",["opt",["flag14",1],["tuple","ushort","ushort"]]],//0,8520 or 13083 only, referencing other material? + ["flag15value",["opt",["flag15",1],"float"]], + ["flag18value",["opt",["flag18",1],"float"]],//not sure about location + ["flag16value",["opt",["flag16",1],"float"]], + ["flag12value",["opt",["flag12",1],"float"]],//not sure about location + ["flag11value",["opt",["flag11",1],["tuple","float","float","float"]]], + ["flag19value",["opt",["flag19",1],["tuple","float","float","float","float","float"]]],//location unknown + ["normalScale",["opt",["hasNormal",1],"float"]], + ["flag17value",["opt",["ignore_vertexcol_17",1],"float"]], + ["uvanim_u",["opt",["hasUVanimU",1],"short"]], + ["uvanim_v",["opt",["hasUVanimV",1],"short"]], + + ["always_0x0901",["buffer",2,"hex"]],//only a couple with 0001 + ["unknownbyte0","ubyte"],//always 0 + ["alphamode","ubyte"],//0,1,2 + ["alphacutoff",["opt",["alphamode",1],"ubyte"]],//127 most of the time + ["unkFFFF",["buffer",2,"hex"]], + ["endbyte","ubyte"]//usually equal to max texture size always 0-4 + ]]] ] \ No newline at end of file