toto10's picture
5abb6291403b19368a3b842ea4f13b1f23694d90948b2da0ae23e85acf2bb1e9
fb54f6e
raw
history blame
14.8 kB
import React, {createContext, useContext, useEffect, useRef, useState} from 'react';
import {
Accordion,
AccordionSummary,
AccordionDetails, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio,
} from '@mui/material';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import Box from '@mui/material/Box';
import {Button} from "@mui/material";
import {TreeItem, TreeView} from "@mui/lab";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
const ObjectContext = createContext([]);
function useObjectUpdate() {
const context = useContext(ObjectContext);
if (!context) {
throw new Error('useObjectUpdate must be used within ObjectProvider');
}
return context;
}
function ObjectProvider({children}) {
const [objects, setObjects] = useState([]);
const updateObjects = (newObjects) => {
setObjects(newObjects);
};
return (<ObjectContext.Provider value={{objects, updateObjects}}>
{children}
</ObjectContext.Provider>);
}
function processNode(node, handlePoseSelectedObject, setSelectedObj, transformControlMap) {
if (!node) {
return null;
}
return (<TreeItem key={node.name} nodeId={node.name} label={node.name} onClick={(objEvent) => {
const objName = objEvent.target.innerHTML;
setSelectedObj(objName);
handlePoseSelectedObject(objName, transformControlMap[objName]);
}}>
{node.children && node.children.map((child) => processNode(child, handlePoseSelectedObject, setSelectedObj, transformControlMap))}
</TreeItem>);
}
const boneNameTransformMap = {
'hips': "none",
'spine': "none",
'chest': "none",
'upperChest': "none",
'neck': "none",
'head': "none",
'leftShoulder': "none",
'leftUpperArm': "none",
'leftLowerArm': "none",
'leftHand': "none",
'leftThumbMetacarpal': "none",
'leftThumbProximal': "none",
'leftThumbDistal': "none",
'leftIndexProximal': "none",
'leftIndexIntermediate': "none",
'leftIndexDistal': "none",
'leftMiddleProximal': "none",
'leftMiddleIntermediate': "none",
'leftMiddleDistal': "none",
'leftRingProximal': "none",
'leftRingIntermediate': "none",
'leftRingDistal': "none",
'leftLittleProximal': "none",
'leftLittleIntermediate': "none",
'leftLittleDistal': "none",
'rightShoulder': "none",
'rightUpperArm': "none",
'rightLowerArm': "none",
'rightHand': "none",
'rightLittleProximal': "none",
'rightLittleIntermediate': "none",
'rightLittleDistal': "none",
'rightRingProximal': "none",
'rightRingIntermediate': "none",
'rightRingDistal': "none",
'rightMiddleProximal': "none",
'rightMiddleIntermediate': "none",
'rightMiddleDistal': "none",
'rightIndexProximal': "none",
'rightIndexIntermediate': "none",
'rightIndexDistal': "none",
'rightThumbMetacarpal': "none",
'rightThumbProximal': "none",
'rightThumbDistal': "none",
'leftUpperLeg': "none",
'leftLowerLeg': "none",
'leftFoot': "none",
'leftToes': "none",
'rightUpperLeg': "none",
'rightLowerLeg': "none",
'rightFoot': "none",
'rightToes': "none"
};
const boneNameList = {
"name": "body",
"children": [
{
"name": "mainBody",
"children": [
{
"name": "hips",
},
{
"name": "spine",
},
{
"name": "chest",
},
{
"name": "upperChest",
},
{
"name": "head",
}
]
},
{
"name": "leftArm",
"children": [
{
"name": "leftShoulder",
},
{
"name": "leftUpperArm",
},
{
"name": "leftLowerArm",
},
{
"name": "leftHand",
"children": [
{
"name": "leftThumb",
"children": [
{
"name": "leftThumbMetacarpal",
},
{
"name": "leftThumbProximal",
},
{
"name": "leftThumbDistal",
},
]
},
{
"name": "leftIndex",
"children": [
{
"name": "leftIndexProximal",
},
{
"name": "leftIndexIntermediate",
},
{
"name": "leftIndexDistal",
},
]
},
{
"name": "leftMiddle",
"children": [
{
"name": "leftMiddleProximal",
},
{
"name": "leftMiddleIntermediate",
},
{
"name": "leftMiddleDistal",
},
]
},
{
"name": "leftRing",
"children": [
{
"name": "leftRingProximal",
},
{
"name": "leftRingIntermediate",
},
{
"name": "leftRingDistal",
},
]
},
{
"name": "leftLittle",
"children": [
{
"name": "leftLittleProximal",
},
{
"name": "leftLittleIntermediate",
},
{
"name": "leftLittleDistal",
}
]
},
]
},
]
},
{
"name": "rightArm",
"children": [
{
"name": "rightShoulder",
},
{
"name": "rightUpperArm",
},
{
"name": "rightLowerArm",
},
{
"name": "rightHand",
"children": [
{
"name": "rightThumb",
"children": [
{
"name": "rightThumbMetacarpal",
},
{
"name": "rightThumbProximal",
},
{
"name": "rightThumbDistal",
},
]
},
{
"name": "rightIndex",
"children": [
{
"name": "rightIndexProximal",
},
{
"name": "rightIndexIntermediate",
},
{
"name": "rightIndexDistal",
},
]
},
{
"name": "rightMiddle",
"children": [
{
"name": "rightMiddleProximal",
},
{
"name": "rightMiddleIntermediate",
},
{
"name": "rightMiddleDistal",
},
]
},
{
"name": "rightRing",
"children": [
{
"name": "rightRingProximal",
},
{
"name": "rightRingIntermediate",
},
{
"name": "rightRingDistal",
},
]
},
{
"name": "rightLittle",
"children": [
{
"name": "rightLittleProximal",
},
{
"name": "rightLittleIntermediate",
},
{
"name": "rightLittleDistal",
},
]
}
]
},
]
},
{
"name": "leftLeg",
"children": [
{
"name": "leftUpperLeg",
},
{
"name": "leftLowerLeg",
},
{
"name": "leftFoot",
},
{
"name": "leftToes",
}
]
},
{
"name": "rightLeg",
"children": [
{
"name": "rightUpperLeg",
},
{
"name": "rightLowerLeg",
},
{
"name": "rightFoot",
},
{
"name": "rightToes",
}
]
}
]
}
function BodyTree({handlePoseSelectedObject, setSelectedObj, transformControlMap}) {
return (<TreeView
defaultCollapseIcon={<ExpandMoreIcon/>}
defaultExpandIcon={<ChevronRightIcon/>}
defaultEndIcon={<div/>}
>
{processNode(boneNameList, handlePoseSelectedObject, setSelectedObj, transformControlMap)}
</TreeView>);
}
export default function PosePanel({
setPoseModelFileName,
handlePoseSelectedObject,
}) {
return (<ObjectProvider>
<BodyTreeWrapper setPoseModelFileName={setPoseModelFileName}
handlePoseSelectedObject={handlePoseSelectedObject}/>
</ObjectProvider>)
}
function BodyTreeWrapper({
setPoseModelFileName,
handlePoseSelectedObject
}) {
const [selectedObj, setSelectedObj] = useState(null);
const loadPoseModel = () => {
setPoseModelFileName("pose.vrm");
};
const {updateObjects} = useObjectUpdate();
const [transformControlMap, setTransformControlMap] = useState(boneNameTransformMap);
const updateObjectsRef = useRef();
updateObjectsRef.current = updateObjects;
useEffect(() => {
window.updateObjects = (newObjects) => {
if (updateObjectsRef.current) {
updateObjectsRef.current(newObjects);
}
};
return () => {
window.updateObjects = null;
};
}, []);
return (<div>
<Box mb={1} mt={1}>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon/>}>
Pose
</AccordionSummary>
<AccordionDetails>
<Button variant="contained" color="primary" fullWidth sx={{margin: '2px'}}
onClick={loadPoseModel}>Load Pose Model (VRM)</Button>
<div><h6>Pose edit only supports VRM file currently.</h6></div>
<BodyTree handlePoseSelectedObject={handlePoseSelectedObject} setSelectedObj={setSelectedObj} transformControlMap={transformControlMap}/>
{ (selectedObj in boneNameTransformMap) && <FormControl>
<FormLabel>Operate</FormLabel>
<RadioGroup
aria-labelledby="operate-radio-buttons-group-label"
defaultValue="none"
name="operate-radio-buttons-group"
row={true}
onChange={(event) => {
handlePoseSelectedObject(selectedObj, event.target.value);
const updatedMap = {...transformControlMap};
updatedMap[selectedObj] = event.target.value;
setTransformControlMap(updatedMap);
}}
>
<FormControlLabel value="none" control={<Radio/>} label="None"
checked={transformControlMap[selectedObj] === "none"}/>
<FormControlLabel value="rotate" control={<Radio/>} label="Rotate"
checked={transformControlMap[selectedObj] === "rotate"}/>
</RadioGroup>
</FormControl>}
</AccordionDetails>
</Accordion>
</Box>
</div>);
}