mirror of
https://github.com/exogen/t2-model-skinner.git
synced 2026-01-19 19:24:44 +00:00
Update export UI, allow selecting exported materials
This commit is contained in:
parent
911eecd4c3
commit
11b8a03ad9
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
2
docs/_next/static/chunks/cb355538-e538db8a1761f402.js
Normal file
2
docs/_next/static/chunks/cb355538-e538db8a1761f402.js
Normal file
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
2
docs/_next/static/chunks/pages/index-1a2b7dc61d221db6.js
Normal file
2
docs/_next/static/chunks/pages/index-1a2b7dc61d221db6.js
Normal file
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,2 +1,2 @@
|
|||
!function(){"use strict";var e,r,_,t,n,i,u,c={},o={};function __webpack_require__(e){var r=o[e];if(void 0!==r)return r.exports;var _=o[e]={id:e,loaded:!1,exports:{}},t=!0;try{c[e].call(_.exports,_,_.exports,__webpack_require__),t=!1}finally{t&&delete o[e]}return _.loaded=!0,_.exports}__webpack_require__.m=c,e=[],__webpack_require__.O=function(r,_,t,n){if(_){n=n||0;for(var i=e.length;i>0&&e[i-1][2]>n;i--)e[i]=e[i-1];e[i]=[_,t,n];return}for(var u=1/0,i=0;i<e.length;i++){for(var _=e[i][0],t=e[i][1],n=e[i][2],c=!0,o=0;o<_.length;o++)u>=n&&Object.keys(__webpack_require__.O).every(function(e){return __webpack_require__.O[e](_[o])})?_.splice(o--,1):(c=!1,n<u&&(u=n));if(c){e.splice(i--,1);var a=t()}}return a},__webpack_require__.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(r,{a:r}),r},__webpack_require__.d=function(e,r){for(var _ in r)__webpack_require__.o(r,_)&&!__webpack_require__.o(e,_)&&Object.defineProperty(e,_,{enumerable:!0,get:r[_]})},__webpack_require__.f={},__webpack_require__.e=function(e){return Promise.all(Object.keys(__webpack_require__.f).reduce(function(r,_){return __webpack_require__.f[_](e,r),r},[]))},__webpack_require__.u=function(e){return"static/chunks/"+(737===e?"fb7d5399":e)+"."+({250:"10c119307e239c98",737:"bc4a70b34221e8c8",767:"0dd6b240996f3455",848:"fc0fe21cdc2e6431"})[e]+".js"},__webpack_require__.miniCssF=function(e){return"static/css/"+({214:"922e89893536f2f9",888:"4f6df96970b4e27f"})[e]+".css"},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},r={},_="_N_E:",__webpack_require__.l=function(e,t,n,i){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var u,c,o=document.getElementsByTagName("script"),a=0;a<o.length;a++){var p=o[a];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==_+n){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,__webpack_require__.nc&&u.setAttribute("nonce",__webpack_require__.nc),u.setAttribute("data-webpack",_+n),u.src=__webpack_require__.tu(e)),r[e]=[t];var onScriptComplete=function(_,t){u.onerror=u.onload=null,clearTimeout(f);var n=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),n&&n.forEach(function(e){return e(t)}),_)return _(t)},f=setTimeout(onScriptComplete.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=onScriptComplete.bind(null,u.onerror),u.onload=onScriptComplete.bind(null,u.onload),c&&document.head.appendChild(u)},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},__webpack_require__.tt=function(){return void 0===t&&(t={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(t=trustedTypes.createPolicy("nextjs#bundler",t))),t},__webpack_require__.tu=function(e){return __webpack_require__.tt().createScriptURL(e)},__webpack_require__.p="/t2-model-skinner/_next/",n={272:0},__webpack_require__.f.j=function(e,r){var _=__webpack_require__.o(n,e)?n[e]:void 0;if(0!==_){if(_)r.push(_[2]);else if(272!=e){var t=new Promise(function(r,t){_=n[e]=[r,t]});r.push(_[2]=t);var i=__webpack_require__.p+__webpack_require__.u(e),u=Error();__webpack_require__.l(i,function(r){if(__webpack_require__.o(n,e)&&(0!==(_=n[e])&&(n[e]=void 0),_)){var t=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;u.message="Loading chunk "+e+" failed.\n("+t+": "+i+")",u.name="ChunkLoadError",u.type=t,u.request=i,_[1](u)}},"chunk-"+e,e)}else n[e]=0}},__webpack_require__.O.j=function(e){return 0===n[e]},i=function(e,r){var _,t,i=r[0],u=r[1],c=r[2],o=0;if(i.some(function(e){return 0!==n[e]})){for(_ in u)__webpack_require__.o(u,_)&&(__webpack_require__.m[_]=u[_]);if(c)var a=c(__webpack_require__)}for(e&&e(r);o<i.length;o++)t=i[o],__webpack_require__.o(n,t)&&n[t]&&n[t][0](),n[t]=0;return __webpack_require__.O(a)},(u=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(i.bind(null,0)),u.push=i.bind(null,u.push.bind(u))}();
|
||||
//# sourceMappingURL=webpack-052f8e41ec24217f.js.map
|
||||
!function(){"use strict";var e,r,_,t,n,i,u,c={},o={};function __webpack_require__(e){var r=o[e];if(void 0!==r)return r.exports;var _=o[e]={id:e,loaded:!1,exports:{}},t=!0;try{c[e].call(_.exports,_,_.exports,__webpack_require__),t=!1}finally{t&&delete o[e]}return _.loaded=!0,_.exports}__webpack_require__.m=c,e=[],__webpack_require__.O=function(r,_,t,n){if(_){n=n||0;for(var i=e.length;i>0&&e[i-1][2]>n;i--)e[i]=e[i-1];e[i]=[_,t,n];return}for(var u=1/0,i=0;i<e.length;i++){for(var _=e[i][0],t=e[i][1],n=e[i][2],c=!0,o=0;o<_.length;o++)u>=n&&Object.keys(__webpack_require__.O).every(function(e){return __webpack_require__.O[e](_[o])})?_.splice(o--,1):(c=!1,n<u&&(u=n));if(c){e.splice(i--,1);var a=t()}}return a},__webpack_require__.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return __webpack_require__.d(r,{a:r}),r},__webpack_require__.d=function(e,r){for(var _ in r)__webpack_require__.o(r,_)&&!__webpack_require__.o(e,_)&&Object.defineProperty(e,_,{enumerable:!0,get:r[_]})},__webpack_require__.f={},__webpack_require__.e=function(e){return Promise.all(Object.keys(__webpack_require__.f).reduce(function(r,_){return __webpack_require__.f[_](e,r),r},[]))},__webpack_require__.u=function(e){return"static/chunks/"+(737===e?"fb7d5399":e)+"."+({250:"10c119307e239c98",737:"bc4a70b34221e8c8",767:"0dd6b240996f3455",848:"fc0fe21cdc2e6431"})[e]+".js"},__webpack_require__.miniCssF=function(e){return"static/css/"+({214:"922e89893536f2f9",888:"fa467350f3c0ad7f"})[e]+".css"},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},r={},_="_N_E:",__webpack_require__.l=function(e,t,n,i){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var u,c,o=document.getElementsByTagName("script"),a=0;a<o.length;a++){var p=o[a];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==_+n){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",u.timeout=120,__webpack_require__.nc&&u.setAttribute("nonce",__webpack_require__.nc),u.setAttribute("data-webpack",_+n),u.src=__webpack_require__.tu(e)),r[e]=[t];var onScriptComplete=function(_,t){u.onerror=u.onload=null,clearTimeout(f);var n=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),n&&n.forEach(function(e){return e(t)}),_)return _(t)},f=setTimeout(onScriptComplete.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=onScriptComplete.bind(null,u.onerror),u.onload=onScriptComplete.bind(null,u.onload),c&&document.head.appendChild(u)},__webpack_require__.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},__webpack_require__.tt=function(){return void 0===t&&(t={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(t=trustedTypes.createPolicy("nextjs#bundler",t))),t},__webpack_require__.tu=function(e){return __webpack_require__.tt().createScriptURL(e)},__webpack_require__.p="/t2-model-skinner/_next/",n={272:0},__webpack_require__.f.j=function(e,r){var _=__webpack_require__.o(n,e)?n[e]:void 0;if(0!==_){if(_)r.push(_[2]);else if(272!=e){var t=new Promise(function(r,t){_=n[e]=[r,t]});r.push(_[2]=t);var i=__webpack_require__.p+__webpack_require__.u(e),u=Error();__webpack_require__.l(i,function(r){if(__webpack_require__.o(n,e)&&(0!==(_=n[e])&&(n[e]=void 0),_)){var t=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;u.message="Loading chunk "+e+" failed.\n("+t+": "+i+")",u.name="ChunkLoadError",u.type=t,u.request=i,_[1](u)}},"chunk-"+e,e)}else n[e]=0}},__webpack_require__.O.j=function(e){return 0===n[e]},i=function(e,r){var _,t,i=r[0],u=r[1],c=r[2],o=0;if(i.some(function(e){return 0!==n[e]})){for(_ in u)__webpack_require__.o(u,_)&&(__webpack_require__.m[_]=u[_]);if(c)var a=c(__webpack_require__)}for(e&&e(r);o<i.length;o++)t=i[o],__webpack_require__.o(n,t)&&n[t]&&n[t][0](),n[t]=0;return __webpack_require__.O(a)},(u=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(i.bind(null,0)),u.push=i.bind(null,u.push.bind(u))}();
|
||||
//# sourceMappingURL=webpack-dd07d6e501d595e1.js.map
|
||||
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
2
docs/_next/static/css/fa467350f3c0ad7f.css
Normal file
2
docs/_next/static/css/fa467350f3c0ad7f.css
Normal file
File diff suppressed because one or more lines are too long
1
docs/_next/static/css/fa467350f3c0ad7f.css.map
Normal file
1
docs/_next/static/css/fa467350f3c0ad7f.css.map
Normal file
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-4060c642ab2f1a1b.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-1a2b7dc61d221db6.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-e538db8a1761f402.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,4 +1,5 @@
|
|||
import { InputHTMLAttributes, useEffect, useRef, useState } from "react";
|
||||
import getConfig from "next/config";
|
||||
import useCanvas from "./useCanvas";
|
||||
import useTools from "./useTools";
|
||||
import { usePopper } from "react-popper";
|
||||
|
|
@ -6,6 +7,7 @@ import Slider from "rc-slider";
|
|||
import { RiFileCopyFill } from "react-icons/ri";
|
||||
import {
|
||||
FaTrashAlt,
|
||||
FaAngleDown,
|
||||
FaLock,
|
||||
FaUnlock,
|
||||
FaArrowUp,
|
||||
|
|
@ -14,11 +16,16 @@ import {
|
|||
import { GiArrowCursor } from "react-icons/gi";
|
||||
import { IoMdBrush } from "react-icons/io";
|
||||
import { ImPlus, ImUndo2, ImRedo2, ImContrast } from "react-icons/im";
|
||||
import useWarrior from "./useWarrior";
|
||||
import { MaterialDefinition } from "./Material";
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
const { materials } = publicRuntimeConfig;
|
||||
|
||||
export default function CanvasTools() {
|
||||
const nameInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const fileTypeRef = useRef<HTMLSelectElement | null>(null);
|
||||
const [exportFileType, setExportFileType] = useState("vl2");
|
||||
const {
|
||||
activeCanvas,
|
||||
backgroundColor,
|
||||
|
|
@ -50,7 +57,11 @@ export default function CanvasTools() {
|
|||
activeCanvasType,
|
||||
addImages,
|
||||
exportSkin,
|
||||
selectedExportMaterials,
|
||||
setSelectedExportMaterials,
|
||||
} = useTools();
|
||||
const { actualModel } = useWarrior();
|
||||
const materialDefs: MaterialDefinition[] = materials[actualModel];
|
||||
const { canvas, isDrawingMode, setDrawingMode } = useCanvas(activeCanvas);
|
||||
const [isMac, setIsMac] = useState(false);
|
||||
const commandKeyPrefix = isMac ? "⌘" : "Ctrl ";
|
||||
|
|
@ -61,12 +72,10 @@ export default function CanvasTools() {
|
|||
null
|
||||
);
|
||||
const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
|
||||
const [arrowElement, setArrowElement] = useState<HTMLElement | null>(null);
|
||||
const [isBrushToolsOpen, setBrushToolsOpen] = useState(false);
|
||||
const [isFilterToolsOpen, setFilterToolsOpen] = useState(false);
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
modifiers: [
|
||||
{ name: "arrow", options: { element: arrowElement } },
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
|
|
@ -76,6 +85,25 @@ export default function CanvasTools() {
|
|||
],
|
||||
});
|
||||
|
||||
// Export popup
|
||||
const [isExportOptionsOpen, setExportOptionsOpen] = useState(false);
|
||||
const [exportReferenceElement, setExportReferenceElement] =
|
||||
useState<HTMLElement | null>(null);
|
||||
const { styles: exportStyles, attributes: exportAttributes } = usePopper(
|
||||
exportReferenceElement,
|
||||
popperElement,
|
||||
{
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, 10],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
const isSelectionLocked = selectedObjects.length
|
||||
? selectedObjects.every((object) => lockedObjects.has(object))
|
||||
: false;
|
||||
|
|
@ -394,12 +422,6 @@ export default function CanvasTools() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="PopupArrow"
|
||||
ref={setArrowElement}
|
||||
style={styles.arrow}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<button
|
||||
|
|
@ -587,12 +609,6 @@ export default function CanvasTools() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="PopupArrow"
|
||||
ref={setArrowElement}
|
||||
style={styles.arrow}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
|
|
@ -606,22 +622,104 @@ export default function CanvasTools() {
|
|||
placeholder="Skin Name"
|
||||
size={12}
|
||||
/>
|
||||
<button
|
||||
className="ExportOptionsButton"
|
||||
type="button"
|
||||
ref={setExportReferenceElement}
|
||||
data-active={isExportOptionsOpen ? "" : undefined}
|
||||
aria-label="Export Options"
|
||||
title="Export Options"
|
||||
onClick={() => {
|
||||
setExportOptionsOpen((isOpen) => !isOpen);
|
||||
}}
|
||||
>
|
||||
.{exportFileType}
|
||||
<FaAngleDown />
|
||||
</button>
|
||||
|
||||
{isExportOptionsOpen ? (
|
||||
<div
|
||||
className="ExportOptionsPopup"
|
||||
ref={setPopperElement}
|
||||
style={exportStyles.popper}
|
||||
tabIndex={-1}
|
||||
onBlur={(event) => {
|
||||
const newFocusElement = event.relatedTarget;
|
||||
const isFocusLeaving =
|
||||
!newFocusElement ||
|
||||
!event.currentTarget.contains(newFocusElement);
|
||||
if (isFocusLeaving) {
|
||||
setExportOptionsOpen(false);
|
||||
}
|
||||
}}
|
||||
{...exportAttributes.popper}
|
||||
>
|
||||
<div className="Fields">
|
||||
<div className="Field">
|
||||
<label>Export Materials</label>
|
||||
<ul className="ExportOptionsList">
|
||||
{materialDefs.map((material, i) => {
|
||||
if (
|
||||
material &&
|
||||
material.selectable !== false &&
|
||||
!material.hidden
|
||||
) {
|
||||
return (
|
||||
<li key={material.name}>
|
||||
<input
|
||||
id={`MaterialSelect-${material.name}`}
|
||||
type="checkbox"
|
||||
checked={selectedExportMaterials[i] !== false}
|
||||
onChange={(event) => {
|
||||
setSelectedExportMaterials(
|
||||
(selectedExportMaterials) => {
|
||||
const newSelectedExportMaterials =
|
||||
selectedExportMaterials.slice();
|
||||
newSelectedExportMaterials[i] =
|
||||
event.target.checked;
|
||||
return newSelectedExportMaterials;
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={`MaterialSelect-${material.name}`}>
|
||||
{material.label}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="Field">
|
||||
<label htmlFor="ExportFormat">Export Format</label>
|
||||
<select
|
||||
id="ExportFormat"
|
||||
value={exportFileType}
|
||||
onChange={(event) => {
|
||||
setExportFileType(event.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="png">.png</option>
|
||||
<option value="vl2">.vl2</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const name = nameInputRef.current ? nameInputRef.current.value : "";
|
||||
const format = fileTypeRef.current
|
||||
? fileTypeRef.current.value
|
||||
: ".png";
|
||||
exportSkin({ name, format });
|
||||
exportSkin({ name, format: exportFileType });
|
||||
}}
|
||||
>
|
||||
Export
|
||||
</button>
|
||||
<select ref={fileTypeRef} defaultValue="vl2">
|
||||
<option value="png">.png</option>
|
||||
<option value="vl2">.vl2</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -46,13 +46,15 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
const { actualModel, selectedModelType } = useWarrior();
|
||||
const [selectedMaterialIndex, setSelectedMaterialIndex] = useState(0);
|
||||
const [selectedFrameIndex, setSelectedFrameIndex] = useState(0);
|
||||
const materialDefs = materials[actualModel];
|
||||
const materialDefs: MaterialDefinition[] = materials[actualModel];
|
||||
const materialDef = materialDefs[selectedMaterialIndex] ?? null;
|
||||
|
||||
const frameCount = materialDef.frameCount ?? 1;
|
||||
const hasAnimation = frameCount > 1;
|
||||
const [selectedExportMaterials, setSelectedExportMaterials] = useState<
|
||||
boolean[]
|
||||
>([]);
|
||||
|
||||
const textureSize = useMemo(
|
||||
const textureSize: [number, number] = useMemo(
|
||||
() => materialDef.size ?? [512, 512],
|
||||
[materialDef]
|
||||
);
|
||||
|
|
@ -71,6 +73,14 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
setSelectedFrameIndex(0);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedExportMaterials(
|
||||
materialDefs.map((material) =>
|
||||
Boolean(material && material.selectable !== false && !material.hidden)
|
||||
)
|
||||
);
|
||||
}, [materialDefs]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedFrameIndex(0);
|
||||
}, [materialDef]);
|
||||
|
|
@ -357,10 +367,8 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
const materialExports = await Promise.all(
|
||||
materialDefs
|
||||
.filter(
|
||||
(materialDef: MaterialDefinition) =>
|
||||
materialDef &&
|
||||
!materialDef.hidden &&
|
||||
materialDef.selectable !== false
|
||||
(materialDef: MaterialDefinition, i) =>
|
||||
selectedExportMaterials[i] !== false
|
||||
)
|
||||
.map((materialDef: MaterialDefinition) => {
|
||||
const frameCount = materialDef.frameCount ?? 1;
|
||||
|
|
@ -396,7 +404,7 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
outputImageUrl = colorImageUrl;
|
||||
}
|
||||
|
||||
let filename;
|
||||
let filename: string;
|
||||
switch (selectedModelType) {
|
||||
case "player":
|
||||
filename = `${name}.${actualModel}.png`;
|
||||
|
|
@ -423,6 +431,9 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
} else {
|
||||
filename = `${actualModel}.png`;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown model type");
|
||||
}
|
||||
|
||||
return { imageUrl: outputImageUrl, filename };
|
||||
|
|
@ -433,8 +444,10 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
switch (format) {
|
||||
case "png": {
|
||||
const { imageUrl, filename } = materialExports[selectedMaterialIndex];
|
||||
savePngFile(imageUrl, filename);
|
||||
materialExports.forEach((materialExport) => {
|
||||
const { imageUrl, filename } = materialExport;
|
||||
savePngFile(imageUrl, filename);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "vl2": {
|
||||
|
|
@ -472,8 +485,8 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
canvases,
|
||||
combineColorAndAlphaImageUrls,
|
||||
materialDefs,
|
||||
selectedMaterialIndex,
|
||||
selectedModelType,
|
||||
selectedExportMaterials,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -521,6 +534,8 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
setSelectedFrameIndex,
|
||||
hasAnimation,
|
||||
frameCount,
|
||||
selectedExportMaterials,
|
||||
setSelectedExportMaterials,
|
||||
}),
|
||||
[
|
||||
activeCanvas,
|
||||
|
|
@ -556,6 +571,7 @@ export default function ToolsProvider({ children }: { children: ReactNode }) {
|
|||
selectedFrameIndex,
|
||||
hasAnimation,
|
||||
frameCount,
|
||||
selectedExportMaterials,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,18 @@ select {
|
|||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.Export .ExportOptionsButton {
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding-right: 6px;
|
||||
}
|
||||
|
||||
.Export .ExportOptionsButton svg {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.Export select {
|
||||
width: 6em;
|
||||
min-width: 6em;
|
||||
|
|
@ -184,6 +196,37 @@ select {
|
|||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.ExportOptionsPopup {
|
||||
background: rgba(5, 22, 21, 0.9);
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgb(0, 255, 200);
|
||||
padding: 10px 12px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ExportOptionsPopup .Fields {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.ExportOptionsList {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ExportOptionsList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin: 4px 0;
|
||||
padding: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.ExportOptionsPopup select {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.SliderContainer {
|
||||
min-width: 200px;
|
||||
min-height: 30px;
|
||||
|
|
@ -322,10 +365,6 @@ select {
|
|||
user-select: none;
|
||||
}
|
||||
|
||||
.PopupArrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@ interface ToolsContextValue {
|
|||
hasMetallic: boolean;
|
||||
hasAnimation: boolean;
|
||||
frameCount: number;
|
||||
selectedExportMaterials: boolean[];
|
||||
setSelectedExportMaterials: (
|
||||
selectedExportMaterials:
|
||||
| boolean[]
|
||||
| ((selectedExportMaterials: boolean[]) => boolean[])
|
||||
) => void;
|
||||
}
|
||||
|
||||
const ToolsContext = React.createContext<ToolsContextValue | null>(null);
|
||||
|
|
|
|||
Loading…
Reference in a new issue