mirror of
https://github.com/exogen/t2-model-skinner.git
synced 2026-01-19 19:24:44 +00:00
Add imported skins to dropdown and auto-select
This commit is contained in:
parent
997f9226f8
commit
044d9886fb
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
self.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,"static/chunks/e21e5bbe-b28e0079b469d4e8.js","static/chunks/ebc70433-4eccd1cb3af29a3e.js","static/chunks/6eb5140f-31a2b2da7903b885.js","static/chunks/85d7bc83-1ca530d7d3f44153.js","static/chunks/3a17f596-9aeae038dfa51955.js","static/chunks/f580fadb-2911e2fbf64aae5a.js","static/chunks/515-13ff0773d41722ae.js","static/chunks/pages/index-7060ed19c3a48d4d.js"],"/_error":["static/chunks/pages/_error-54b9fcf45cb5bc62.js"],"/gallery":[s,"static/chunks/737a5600-aea383aaa2061cc6.js","static/chunks/918-3c6747f76df39072.js","static/css/922e89893536f2f9.css","static/chunks/pages/gallery-af1406fdc1af13f5.js"],sortedPages:["/","/_app","/_error","/gallery"]}}("static/chunks/cb355538-dbf1c108320fc6a1.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||
self.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":[s,"static/chunks/e21e5bbe-b28e0079b469d4e8.js","static/chunks/ebc70433-4eccd1cb3af29a3e.js","static/chunks/6eb5140f-31a2b2da7903b885.js","static/chunks/85d7bc83-1ca530d7d3f44153.js","static/chunks/3a17f596-9aeae038dfa51955.js","static/chunks/f580fadb-2911e2fbf64aae5a.js","static/chunks/515-13ff0773d41722ae.js","static/chunks/pages/index-5b5a7028e5a18507.js"],"/_error":["static/chunks/pages/_error-54b9fcf45cb5bc62.js"],"/gallery":[s,"static/chunks/737a5600-aea383aaa2061cc6.js","static/chunks/918-3c6747f76df39072.js","static/css/922e89893536f2f9.css","static/chunks/pages/gallery-af1406fdc1af13f5.js"],sortedPages:["/","/_app","/_error","/gallery"]}}("static/chunks/cb355538-dbf1c108320fc6a1.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,29 @@
|
|||
import { ReactNode, useEffect, useMemo, useState } from "react";
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import getConfig from "next/config";
|
||||
import useSettings from "./useSettings";
|
||||
import { WarriorContext } from "./useWarrior";
|
||||
import type { MaterialDefinition } from "./Material";
|
||||
import type { Skin } from "./importUtils";
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { materials, modelDefaults } = publicRuntimeConfig;
|
||||
const baseSkinPath = `https://exogen.github.io/t2-skins/skins`;
|
||||
|
||||
let IMPORTED_SKINS: Map<string, Map<string | null, Skin>> = new Map();
|
||||
|
||||
function mergeNewImportedSkins(newSkins: typeof IMPORTED_SKINS) {
|
||||
const newImportedSkins = new Map(IMPORTED_SKINS.entries());
|
||||
newSkins.forEach((newSkinsByName, modelName) => {
|
||||
const skinsByName =
|
||||
newImportedSkins.get(modelName) ?? new Map<string | null, Skin>();
|
||||
newSkinsByName.forEach((skin, skinName) => {
|
||||
skinsByName.set(skinName, skin);
|
||||
});
|
||||
newImportedSkins.set(modelName, skinsByName);
|
||||
});
|
||||
IMPORTED_SKINS = newImportedSkins;
|
||||
}
|
||||
|
||||
function getFrameNames(frameZeroFile: string, frameCount: number) {
|
||||
if (frameCount < 2) {
|
||||
return [frameZeroFile];
|
||||
|
|
@ -36,6 +52,17 @@ export function getSkinImageUrls({
|
|||
selectedSkinType: string | null;
|
||||
}): Record<string, string[]> {
|
||||
const materialDefs = materials[actualModel];
|
||||
if (selectedSkin && selectedSkinType === "import") {
|
||||
const skinsByName = IMPORTED_SKINS.get(actualModel);
|
||||
if (skinsByName) {
|
||||
const key = selectedSkin === "__untitled__" ? null : selectedSkin;
|
||||
const skin = skinsByName.get(key);
|
||||
if (skin && skin.isComplete) {
|
||||
return Object.fromEntries(skin.materials);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
switch (selectedModelType) {
|
||||
case "player":
|
||||
switch (selectedSkinType) {
|
||||
|
|
@ -146,7 +173,12 @@ export default function WarriorProvider({ children }: { children: ReactNode }) {
|
|||
actualModel,
|
||||
selectedAnimation
|
||||
);
|
||||
const [importedSkins, setImportedSkins] = useState(() => new Map());
|
||||
const [importedSkins, setImportedSkins] = useState(IMPORTED_SKINS);
|
||||
|
||||
const addImportedSkins = useCallback((newSkins: typeof IMPORTED_SKINS) => {
|
||||
mergeNewImportedSkins(newSkins);
|
||||
setImportedSkins(IMPORTED_SKINS);
|
||||
}, []);
|
||||
|
||||
const [skinImageUrls, setSkinImageUrls] = useState<Record<string, string[]>>(
|
||||
() =>
|
||||
|
|
@ -193,7 +225,7 @@ export default function WarriorProvider({ children }: { children: ReactNode }) {
|
|||
slowModeEnabled,
|
||||
setSlowModeEnabled,
|
||||
importedSkins,
|
||||
setImportedSkins,
|
||||
addImportedSkins,
|
||||
};
|
||||
}, [
|
||||
selectedModel,
|
||||
|
|
@ -215,7 +247,7 @@ export default function WarriorProvider({ children }: { children: ReactNode }) {
|
|||
defaultSkinImageUrls,
|
||||
slowModeEnabled,
|
||||
importedSkins,
|
||||
setImportedSkins,
|
||||
addImportedSkins,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { FaFolderOpen } from "react-icons/fa";
|
|||
import { BsFillGrid3X3GapFill } from "react-icons/bs";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useTools from "./useTools";
|
||||
import { importMultipleFilesToModels } from "./importUtils";
|
||||
import { importMultipleFilesToModels, modelToModelType } from "./importUtils";
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { defaultSkins, modelDefaults /*materials*/ } = publicRuntimeConfig;
|
||||
|
|
@ -12,6 +12,8 @@ const { defaultSkins, modelDefaults /*materials*/ } = publicRuntimeConfig;
|
|||
const baseManifestPath = `https://exogen.github.io/t2-skins`;
|
||||
const defaultCustomSkins = {};
|
||||
|
||||
const emptyMap = new Map();
|
||||
|
||||
export default function WarriorSelector() {
|
||||
const {
|
||||
selectedModel,
|
||||
|
|
@ -23,10 +25,9 @@ export default function WarriorSelector() {
|
|||
setSelectedSkinType,
|
||||
actualModel,
|
||||
setSelectedAnimation,
|
||||
setSkinImageUrls,
|
||||
setAnimationPaused,
|
||||
// importedSkins,
|
||||
// setImportedSkins,
|
||||
importedSkins,
|
||||
addImportedSkins,
|
||||
} = useWarrior();
|
||||
const { /*selectedMaterialIndex,*/ setSelectedMaterialIndex } = useTools();
|
||||
// const materialDefs = materials[actualModel];
|
||||
|
|
@ -40,6 +41,12 @@ export default function WarriorSelector() {
|
|||
);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const importedSkinsForModel = importedSkins.get(actualModel) ?? emptyMap;
|
||||
|
||||
const selectableImportedSkins = Array.from(
|
||||
importedSkinsForModel.values()
|
||||
).filter((skin) => skin.isComplete);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
|
@ -193,6 +200,20 @@ export default function WarriorSelector() {
|
|||
);
|
||||
})}
|
||||
</optgroup>
|
||||
{selectableImportedSkins.length ? (
|
||||
<optgroup label="Imported Skins" data-skin-type="import">
|
||||
{selectableImportedSkins.map((skin) => {
|
||||
return (
|
||||
<option
|
||||
key={`import/${skin.name ?? "__untitled__"}`}
|
||||
value={`import/${skin.name ?? "__untitled__"}`}
|
||||
>
|
||||
{skin.name || "Untitled Imported Skin"}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</optgroup>
|
||||
) : null}
|
||||
{newSkins[actualModel]?.length ? (
|
||||
<optgroup label="New Skins ✨" data-skin-type="custom">
|
||||
{newSkins[actualModel]?.map((name: string) => {
|
||||
|
|
@ -229,6 +250,20 @@ export default function WarriorSelector() {
|
|||
<option value={modelDefaults[actualModel]}>Default</option>
|
||||
</optgroup>
|
||||
) : null}
|
||||
{selectableImportedSkins.length ? (
|
||||
<optgroup label="Imported Skins" data-skin-type="import">
|
||||
{selectableImportedSkins.map((skin) => {
|
||||
return (
|
||||
<option
|
||||
key={`import/${skin.name ?? "__untitled__"}`}
|
||||
value={`import/${skin.name ?? "__untitled__"}`}
|
||||
>
|
||||
{skin.name || "Untitled Imported Skin"}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</optgroup>
|
||||
) : null}
|
||||
{customSkins[actualModel]?.length ? (
|
||||
<optgroup label="Custom Skins" data-skin-type="custom">
|
||||
{customSkins[actualModel].map((name: string) => (
|
||||
|
|
@ -259,16 +294,34 @@ export default function WarriorSelector() {
|
|||
const foundModels = await importMultipleFilesToModels(
|
||||
event.target.files ?? []
|
||||
);
|
||||
const selectedModelSkins = foundModels.get(actualModel);
|
||||
if (selectedModelSkins) {
|
||||
const skins = Array.from(selectedModelSkins.values());
|
||||
for (const skin of skins) {
|
||||
addImportedSkins(foundModels);
|
||||
const currentModelSkins = foundModels.get(actualModel);
|
||||
if (currentModelSkins) {
|
||||
const completeSkins = Array.from(
|
||||
currentModelSkins.values()
|
||||
).filter((skin) => skin.isComplete);
|
||||
if (completeSkins.length) {
|
||||
const skin = completeSkins[0];
|
||||
setSelectedSkinType("import");
|
||||
setSelectedSkinSection("import");
|
||||
setSelectedSkin(skin.name ?? "__untitled__");
|
||||
setSelectedMaterialIndex(0);
|
||||
setSelectedAnimation(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const [modelName, skinsByName] of Array.from(
|
||||
foundModels.entries()
|
||||
)) {
|
||||
for (const skin of Array.from(skinsByName.values())) {
|
||||
if (skin.isComplete) {
|
||||
setSelectedSkin(null);
|
||||
setSelectedSkinSection(null);
|
||||
setSkinImageUrls(
|
||||
Object.fromEntries(skin.materials.entries())
|
||||
);
|
||||
setSelectedModel(modelName);
|
||||
setSelectedModelType(modelToModelType(modelName));
|
||||
setSelectedSkinType("import");
|
||||
setSelectedSkinSection("import");
|
||||
setSelectedSkin(skin.name ?? "__untitled__");
|
||||
setSelectedMaterialIndex(0);
|
||||
setSelectedAnimation(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,43 @@ export function clearImportedSkins() {
|
|||
|
||||
const ignoreFilePattern = /^(\.|__MACOSX)/;
|
||||
|
||||
export function modelToModelType(modelName: string) {
|
||||
switch (modelName) {
|
||||
case "lmale":
|
||||
case "mmale":
|
||||
case "hmale":
|
||||
case "lfemale":
|
||||
case "mfemale":
|
||||
case "hfemale":
|
||||
case "lbioderm":
|
||||
case "mbioderm":
|
||||
case "hbioderm":
|
||||
return "player";
|
||||
case "disc":
|
||||
case "chaingun":
|
||||
case "grenade_launcher":
|
||||
case "sniper":
|
||||
case "plasmathrower":
|
||||
case "energy":
|
||||
case "shocklance":
|
||||
case "elf":
|
||||
case "missile":
|
||||
case "mortar":
|
||||
case "repair":
|
||||
case "targeting":
|
||||
return "weapon";
|
||||
case "vehicle_grav_scout":
|
||||
case "vehicle_grav_tank":
|
||||
case "vehicle_land_mpbbase":
|
||||
case "vehicle_air_scout":
|
||||
case "vehicle_air_bomber":
|
||||
case "vehicle_air_hapc":
|
||||
return "vehicle";
|
||||
default:
|
||||
throw new Error("Unknown model");
|
||||
}
|
||||
}
|
||||
|
||||
export async function readZipFile(inputFile: File) {
|
||||
const content = await JSZip.loadAsync(inputFile);
|
||||
const skins = await Promise.all(
|
||||
|
|
@ -175,7 +212,7 @@ function pathToModels(path: string, skinName: string | null = null) {
|
|||
return null;
|
||||
}
|
||||
|
||||
type Skin = {
|
||||
export type Skin = {
|
||||
name: string | null;
|
||||
isComplete: null | boolean;
|
||||
materials: Map<string, string[]>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useContext } from "react";
|
||||
import { Skin } from "./importUtils";
|
||||
|
||||
type WarriorContextValue = {
|
||||
actualModel: string;
|
||||
|
|
@ -28,6 +29,8 @@ type WarriorContextValue = {
|
|||
setSelectedModelType: (selectedModelType: string) => void;
|
||||
slowModeEnabled: boolean;
|
||||
setSlowModeEnabled: (slowModeEnabled: boolean) => void;
|
||||
importedSkins: Map<string, Map<string | null, Skin>>;
|
||||
addImportedSkins: (newSkins: Map<string, Map<string | null, Skin>>) => void;
|
||||
};
|
||||
|
||||
const WarriorContext = React.createContext<WarriorContextValue | null>(null);
|
||||
|
|
|
|||
Loading…
Reference in a new issue