diff --git a/config/models.mjs b/config/models.mjs index 966a6c6..34cf13d 100644 --- a/config/models.mjs +++ b/config/models.mjs @@ -28,6 +28,8 @@ const weaponModels = [ "targeting", ]; +const vehicleModels = ["vehicle_air_scout"]; + export async function getSkinConfig() { const [defaultSkins, customSkins, customWeaponSkins] = await Promise.all([ Promise.all( @@ -101,6 +103,8 @@ export async function getSkinConfig() { shocklance: "weapon_shocklance", sniper: "weapon_sniper", targeting: "weapon_targeting", + // Vehicles + vehicle_air_scout: "vehicle_air_scout", }, animationLabels: { Forward: "Run Forward", @@ -259,6 +263,34 @@ export async function getSkinConfig() { mortar: [{ label: "Weapon", name: "weapon_mortar" }], repair: [{ label: "Weapon", name: "weapon_repair" }], targeting: [{ label: "Weapon", name: "weapon_targeting" }], + // Vehicles + vehicle_air_scout: [ + { + label: "Vehicle", + name: "vehicle_air_scout0", + file: "vehicle_air_scout", + }, + { + name: "Unassigned", + hidden: true, + hasDefault: false, + }, + { + name: "vehicle_air_scout", + hidden: true, + hasDefault: false, + }, + { + label: "Flare", + name: "shrikeflare2", + emissiveFactor: [0, 0, 0], + alphaMode: "BLEND", + emissiveTexture: true, + metallicFactor: 0, + roughnessFactor: 1, + size: [256, 256], + }, + ], }, animations: { global: [ diff --git a/public/textures/shrikeflare2.png b/public/textures/shrikeflare2.png new file mode 100644 index 0000000..a57441b Binary files /dev/null and b/public/textures/shrikeflare2.png differ diff --git a/public/textures/vehicle_air_scout.png b/public/textures/vehicle_air_scout.png new file mode 100644 index 0000000..801f4f3 Binary files /dev/null and b/public/textures/vehicle_air_scout.png differ diff --git a/public/vehicle_air_scout.glb b/public/vehicle_air_scout.glb new file mode 100644 index 0000000..00778dd Binary files /dev/null and b/public/vehicle_air_scout.glb differ diff --git a/src/MaterialSelector.tsx b/src/MaterialSelector.tsx index a0a5c8e..aa5f483 100644 --- a/src/MaterialSelector.tsx +++ b/src/MaterialSelector.tsx @@ -20,7 +20,7 @@ export default function MaterialSelector() { }} > {materialDefs.map((materialDef, i) => - materialDef ? ( + materialDef && !materialDef.hidden ? ( diff --git a/src/ToolsProvider.tsx b/src/ToolsProvider.tsx index 8b1dd58..44fc0fa 100644 --- a/src/ToolsProvider.tsx +++ b/src/ToolsProvider.tsx @@ -208,45 +208,59 @@ export default function ToolsProvider({ children }: { children: ReactNode }) { name = name.trim() || "MyCustomSkin"; const materialExports = await Promise.all( - materialDefs.map(async (materialDef: MaterialDefinition) => { - const colorCanvas = canvases[`${materialDef.name}:color`]?.canvas; - const metallicCanvas = - canvases[`${materialDef.name}:metallic`]?.canvas; + materialDefs + .filter( + (materialDef: MaterialDefinition) => + materialDef && !materialDef.hidden + ) + .map(async (materialDef: MaterialDefinition) => { + const colorCanvas = canvases[`${materialDef.name}:color`]?.canvas; + const metallicCanvas = + canvases[`${materialDef.name}:metallic`]?.canvas; - const textureSize = materialDef.size ?? [512, 512]; - let outputImageUrl; + const textureSize = materialDef.size ?? [512, 512]; + let outputImageUrl; - const colorImageUrl = colorCanvas.toDataURL({ - top: canvasPadding, - left: canvasPadding, - width: textureSize[0], - height: textureSize[1], - }); - - if (metallicCanvas) { - const metallicImageUrl = metallicCanvas.toDataURL({ + const colorImageUrl = colorCanvas.toDataURL({ top: canvasPadding, left: canvasPadding, width: textureSize[0], height: textureSize[1], }); - outputImageUrl = await combineColorAndAlphaImageUrls({ - colorImageUrl, - metallicImageUrl, - }); - } else { - outputImageUrl = colorImageUrl; - } - const filename = - selectedModelType === "player" - ? `${name}.${actualModel}.png` - : materialDef - ? `${materialDef.file ?? materialDef.name}.png` - : `weapon_${actualModel}.png`; + if (metallicCanvas) { + const metallicImageUrl = metallicCanvas.toDataURL({ + top: canvasPadding, + left: canvasPadding, + width: textureSize[0], + height: textureSize[1], + }); + outputImageUrl = await combineColorAndAlphaImageUrls({ + colorImageUrl, + metallicImageUrl, + }); + } else { + outputImageUrl = colorImageUrl; + } - return { imageUrl: outputImageUrl, filename }; - }) + let filename; + switch (selectedModelType) { + case "player": + filename = `${name}.${actualModel}.png`; + break; + case "weapon": + case "vehicle": + if (materialDef) { + filename = `${materialDef.file ?? materialDef.name}.png`; + } else if (selectedModelType === "weapon") { + filename = `weapon_${actualModel}.png`; + } else { + filename = `${actualModel}.png`; + } + } + + return { imageUrl: outputImageUrl, filename }; + }) ); switch (format) { diff --git a/src/WarriorProvider.tsx b/src/WarriorProvider.tsx index 59f3db0..dce15e7 100644 --- a/src/WarriorProvider.tsx +++ b/src/WarriorProvider.tsx @@ -34,6 +34,7 @@ export function getSkinImageUrls({ } break; case "weapon": + case "vehicle": return materialDefs.reduce( ( skinImageUrls: Record, diff --git a/src/WarriorSelector.tsx b/src/WarriorSelector.tsx index bcbce71..b1a1715 100644 --- a/src/WarriorSelector.tsx +++ b/src/WarriorSelector.tsx @@ -84,6 +84,9 @@ export default function WarriorSelector() { + + +
@@ -125,7 +128,8 @@ export default function WarriorSelector() { ) : null} - {selectedModelType === "weapon" ? ( + {selectedModelType === "weapon" || + selectedModelType === "vehicle" ? ( <> {modelDefaults[actualModel] ? (