Skip to content

Commit 222212c

Browse files
committed
add material edit
1 parent e54ad64 commit 222212c

File tree

10 files changed

+104
-26
lines changed

10 files changed

+104
-26
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
- [x] Model selection
2828
- [x] Ollama support ([electron version](https://github.com/AmyangXYZ/MiKaPo-Electron))
2929
- [ ] VMD export
30+
- [x] MMD editor: bone, material, mesh edit
31+
- [ ] Custom model, vmd import
32+
- [ ] Multi-user co-editing
3033

3134
## Project Setup
3235

src/Animation.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ function Animation({ setSelectedAnimation }: { setSelectedAnimation: (animation:
1414
>
1515
{availableAnimations.map((animation) => (
1616
<FormControlLabel
17+
key={animation}
1718
value={animation}
18-
control={<Radio sx={{ color: "lightgray" }} size="small" />}
19+
control={<Radio sx={{ color: "#a2c9f5", "&.Mui-checked": { color: "#a2c9f5" } }} size="small" />}
1920
label={<Typography sx={{ fontSize: ".9rem" }}>{animation}</Typography>}
2021
/>
2122
))}

src/App.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from "react"
22
import Motion from "./Motion"
33
import MMDScene from "./MMDScene"
4-
import Outfit from "./Outfit"
4+
import Materials from "./Materials"
55
import Model from "./Model"
66
import Animation from "./Animation"
77
import Header from "./Header"
@@ -27,7 +27,8 @@ function App(): JSX.Element {
2727
const [selectedAnimation, setSelectedAnimation] = useState<string>("")
2828

2929
const [boneRotation, setBoneRotation] = useState<{ name: string; axis: string; value: number } | null>(null)
30-
30+
const [materials, setMaterials] = useState<string[]>([])
31+
const [materialVisible, setMaterialVisible] = useState<{ name: string; visible: boolean } | null>(null)
3132
return (
3233
<>
3334
<Header fps={fps}></Header>
@@ -44,6 +45,8 @@ function App(): JSX.Element {
4445
lerpFactor={lerpFactor}
4546
setFps={setFps}
4647
boneRotation={boneRotation}
48+
setMaterials={setMaterials}
49+
materialVisible={materialVisible}
4750
></MMDScene>
4851
<Drawer
4952
variant="persistent"
@@ -68,7 +71,9 @@ function App(): JSX.Element {
6871
setLerpFactor={setLerpFactor}
6972
style={{ display: activeTab === "motion" ? "block" : "none" }}
7073
></Motion>
71-
{activeTab === "outfit" && <Outfit></Outfit>}
74+
{activeTab === "material" && (
75+
<Materials materials={materials} setMaterialVisible={setMaterialVisible}></Materials>
76+
)}
7277
{activeTab === "skeleton" && <Skeleton setBoneRotation={setBoneRotation}></Skeleton>}
7378
{activeTab === "animation" && <Animation setSelectedAnimation={setSelectedAnimation}></Animation>}
7479
{activeTab === "model" && <Model setSelectedModel={setSelectedModel}></Model>}

src/Footer.tsx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
22
import { faBone, faFilm, faPanorama, faRunning, faShirt, faUser } from "@fortawesome/free-solid-svg-icons"
33
import { Fab, Tooltip } from "@mui/material"
44

5-
function Footer({ setOpenDrawer, setActiveTab }: { setOpenDrawer: (open: boolean) => void, setActiveTab: (tab: string) => void }): JSX.Element {
5+
function Footer({
6+
setOpenDrawer,
7+
setActiveTab,
8+
}: {
9+
setOpenDrawer: (open: boolean) => void
10+
setActiveTab: (tab: string) => void
11+
}): JSX.Element {
612
const colorPalette = {
7-
motion: "#4A90E2", // Soft Blue
8-
skeleton: "#3498DB", // Peter River Blue
9-
outfit: "#2ECC71", // Emerald Green
13+
motion: "#4A90E2", // Soft Blue
14+
skeleton: "#3498DB", // Peter River Blue
15+
material: "#2ECC71", // Emerald Green
1016
background: "#9B59B6", // Amethyst
11-
model: "#FF8C00", // Dark Orange
12-
animation: "#E74C3C" // Alizarin Red
13-
};
17+
model: "#FF8C00", // Dark Orange
18+
animation: "#E74C3C", // Alizarin Red
19+
}
1420

1521
return (
1622
<div className="footer">
@@ -24,14 +30,17 @@ function Footer({ setOpenDrawer, setActiveTab }: { setOpenDrawer: (open: boolean
2430
height: "106px",
2531
backgroundColor: colorPalette.motion,
2632
}}
27-
onClick={() => { setActiveTab("motion"); setOpenDrawer(true) }}
33+
onClick={() => {
34+
setActiveTab("motion")
35+
setOpenDrawer(true)
36+
}}
2837
>
2938
<FontAwesomeIcon icon={faRunning} color="white" size="6x" />
3039
</Fab>
3140
</Tooltip>
3241
{[
3342
{ name: "Model", icon: faUser, angle: -20, color: colorPalette.model },
34-
{ name: "Outfit", icon: faShirt, angle: 10, color: colorPalette.outfit },
43+
{ name: "Material", icon: faShirt, angle: 10, color: colorPalette.material },
3544
{ name: "Background", icon: faPanorama, angle: 40, color: colorPalette.background },
3645
{ name: "Skeleton", icon: faBone, angle: 70, color: colorPalette.skeleton },
3746
{ name: "Animation", icon: faFilm, angle: 100, color: colorPalette.animation },
@@ -46,7 +55,10 @@ function Footer({ setOpenDrawer, setActiveTab }: { setOpenDrawer: (open: boolean
4655
height: "36px",
4756
backgroundColor: color,
4857
}}
49-
onClick={() => { setActiveTab(name.toLowerCase()); setOpenDrawer(true) }}
58+
onClick={() => {
59+
setActiveTab(name.toLowerCase())
60+
setOpenDrawer(true)
61+
}}
5062
>
5163
<FontAwesomeIcon icon={icon} color="white" size="lg" />
5264
</Fab>
@@ -56,4 +68,4 @@ function Footer({ setOpenDrawer, setActiveTab }: { setOpenDrawer: (open: boolean
5668
)
5769
}
5870

59-
export default Footer
71+
export default Footer

src/MMDScene.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
DirectionalLight,
88
Engine,
99
HemisphericLight,
10+
Material,
1011
Matrix,
1112
Mesh,
1213
MeshBuilder,
@@ -104,6 +105,8 @@ function MMDScene({
104105
selectedAnimation,
105106
setSelectedAnimation,
106107
boneRotation,
108+
setMaterials,
109+
materialVisible,
107110
}: {
108111
pose: NormalizedLandmark[] | null
109112
face: NormalizedLandmark[] | null
@@ -116,6 +119,8 @@ function MMDScene({
116119
selectedAnimation: string
117120
setSelectedAnimation: (animation: string) => void
118121
boneRotation: { name: string; axis: string; value: number } | null
122+
setMaterials: (materials: string[]) => void
123+
materialVisible: { name: string; visible: boolean } | null
119124
}): JSX.Element {
120125
const canvasRef = useRef<HTMLCanvasElement>(null)
121126
const sceneRef = useRef<Scene | null>(null)
@@ -148,6 +153,19 @@ function MMDScene({
148153
}
149154
}, [boneRotation])
150155

156+
useEffect(() => {
157+
if (materialVisible) {
158+
const material = mmdModelRef.current!.mesh.metadata.materials.find(
159+
(m: Material) => m.name === materialVisible.name
160+
)
161+
const mesh = mmdModelRef.current!.mesh.metadata.meshes.find((m: Mesh) => m.name === materialVisible.name)
162+
if (material && mesh) {
163+
material.alpha = materialVisible.visible ? 1 : 0
164+
mesh.visibility = materialVisible.visible ? 1 : 0
165+
}
166+
}
167+
}, [materialVisible])
168+
151169
useEffect(() => {
152170
const createScene = async (canvas: HTMLCanvasElement): Promise<Scene> => {
153171
const engine = new Engine(canvas, true, {}, true)
@@ -260,6 +278,7 @@ function MMDScene({
260278
for (const m of mesh.metadata.meshes) {
261279
m.receiveShadows = true
262280
}
281+
setMaterials(mesh.metadata.materials.map((m: Material) => m.name))
263282
shadowGeneratorRef.current!.addShadowCaster(mesh)
264283
mmdModelRef.current = mmdRuntimeRef.current!.createMmdModel(mesh as Mesh, {
265284
buildPhysics: {
@@ -275,7 +294,7 @@ function MMDScene({
275294
)
276295
}
277296
loadMMD()
278-
}, [sceneRendered, sceneRef, mmdWasmInstanceRef, mmdRuntimeRef, selectedModel, setSelectedAnimation])
297+
}, [sceneRendered, sceneRef, mmdWasmInstanceRef, mmdRuntimeRef, selectedModel, setSelectedAnimation, setMaterials])
279298

280299
useEffect(() => {
281300
if (

src/Materials.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Checkbox, List, ListItem, Typography } from "@mui/material"
2+
3+
function Materials({
4+
materials,
5+
setMaterialVisible,
6+
}: {
7+
materials: string[]
8+
setMaterialVisible: (material: { name: string; visible: boolean }) => void
9+
}): JSX.Element {
10+
return (
11+
<List className="material" dense>
12+
{materials.map((material) => (
13+
<ListItem
14+
key={material}
15+
secondaryAction={
16+
<Checkbox
17+
defaultChecked
18+
onChange={(e) => {
19+
setMaterialVisible({ name: material, visible: e.target.checked })
20+
}}
21+
size="small"
22+
sx={{
23+
color: "#a2c9f5",
24+
"&.Mui-checked": {
25+
color: "#a2c9f5",
26+
},
27+
}}
28+
/>
29+
}
30+
>
31+
<Typography>{material}</Typography>
32+
</ListItem>
33+
))}
34+
</List>
35+
)
36+
}
37+
38+
export default Materials

src/Model.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ function Model({ setSelectedModel }: { setSelectedModel: (model: string) => void
1717
<FormControlLabel
1818
key={model}
1919
value={model}
20-
control={<Radio sx={{ color: "lightgray", marginLeft: 2, marginBottom: 2 }} size="small" />}
20+
control={
21+
<Radio
22+
sx={{ color: "#a2c9f5", "&.Mui-checked": { color: "#a2c9f5" }, marginLeft: 2, marginBottom: 2 }}
23+
size="small"
24+
/>
25+
}
2126
label={
2227
<Box sx={{ display: "flex", alignItems: "center", marginBottom: 2 }}>
2328
<Avatar src={`/avatar/${model}.png`} alt={model} sx={{ width: 64, height: 64, marginRight: 1 }} />

src/Outfit.tsx

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/Skeleton.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Dispatch, SetStateAction, useState } from "react"
1+
import React, { useState } from "react"
22
import { List, ListItem, ListItemText, Collapse, IconButton, Slider, Typography, ListItemButton } from "@mui/material"
33
import { ExpandLess, ExpandMore } from "@mui/icons-material"
44

@@ -94,7 +94,7 @@ const categories = {
9494
function Skeleton({
9595
setBoneRotation,
9696
}: {
97-
setBoneRotation: Dispatch<SetStateAction<{ name: string; axis: string; value: number } | null>>
97+
setBoneRotation: (boneRotation: { name: string; axis: string; value: number }) => void
9898
}): JSX.Element {
9999
const [openCategory, setOpenCategory] = useState<string | null>(null)
100100
const [openBones, setOpenBones] = useState<Record<string, boolean>>({})
@@ -160,7 +160,7 @@ function Skeleton({
160160
step={0.001}
161161
min={-Math.PI}
162162
max={Math.PI}
163-
sx={{ ml: 2, width: "80%" }}
163+
sx={{ ml: 2, width: "80%", color: "#a2c9f5" }}
164164
/>
165165
</ListItem>
166166
))}

src/assets/main.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ video {
157157

158158
.animation,
159159
.model,
160-
.outfit,
160+
.material,
161161
.background {
162162
position: absolute;
163163
top: 4rem;

0 commit comments

Comments
 (0)